Ticket #3204: As user, I want to use own default colors for viewer.
[midnight-commander.git] / src / viewer / hex.c
blobff7d501f19904186d822488d6809ef68135271f7
1 /*
2 Internal file viewer for the Midnight Commander
3 Function for hex view
5 Copyright (C) 1994-2014
6 Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
11 Jakub Jelinek, 1995
12 Joseph M. Hinkle, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
15 Roland Illig <roland.illig@gmx.de>, 2004, 2005
16 Slava Zanko <slavazanko@google.com>, 2009, 2013
17 Andrew Borodin <aborodin@vmail.ru>, 2009
18 Ilia Maslakov <il.smind@gmail.com>, 2009
20 This file is part of the Midnight Commander.
22 The Midnight Commander is free software: you can redistribute it
23 and/or modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation, either version 3 of the License,
25 or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program. If not, see <http://www.gnu.org/licenses/>.
36 #include <config.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <inttypes.h> /* uintmax_t */
42 #include "lib/global.h"
43 #include "lib/tty/tty.h"
44 #include "lib/skin.h"
45 #include "lib/vfs/vfs.h"
46 #include "lib/lock.h" /* lock_file() and unlock_file() */
47 #include "lib/util.h"
48 #include "lib/widget.h"
49 #ifdef HAVE_CHARSET
50 #include "lib/charsets.h"
51 #endif
53 #include "internal.h"
55 /*** global variables ****************************************************************************/
57 /*** file scope macro definitions ****************************************************************/
59 /*** file scope type declarations ****************************************************************/
61 typedef enum
63 MARK_NORMAL,
64 MARK_SELECTED,
65 MARK_CURSOR,
66 MARK_CHANGED
67 } mark_t;
69 /*** file scope variables ************************************************************************/
71 static const char hex_char[] = "0123456789ABCDEF";
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
76 #ifdef HAVE_CHARSET
77 static int
78 utf8_to_int (char *str, int *char_width, gboolean * result)
80 int res = -1;
81 gunichar ch;
82 gchar *next_ch = NULL;
83 int width = 0;
85 *result = TRUE;
87 if (str == NULL)
89 *result = FALSE;
90 return 0;
93 res = g_utf8_get_char_validated (str, -1);
95 if (res < 0)
96 ch = *str;
97 else
99 ch = res;
100 /* Calculate UTF-8 char width */
101 next_ch = g_utf8_next_char (str);
102 if (next_ch)
103 width = next_ch - str;
104 else
105 ch = 0;
107 *char_width = width;
108 return ch;
110 #endif /* HAVE_CHARSET */
112 /* --------------------------------------------------------------------------------------------- */
113 /** Determine the state of the current byte.
115 * @param view viewer object
116 * @param from offset
117 * @param curr current node
120 static mark_t
121 mcview_hex_calculate_boldflag (mcview_t * view, off_t from, struct hexedit_change_node *curr)
123 return (from == view->hex_cursor) ? MARK_CURSOR
124 : (curr != NULL && from == curr->offset) ? MARK_CHANGED
125 : (view->search_start <= from && from < view->search_end) ? MARK_SELECTED : MARK_NORMAL;
128 /* --------------------------------------------------------------------------------------------- */
129 /*** public functions ****************************************************************************/
130 /* --------------------------------------------------------------------------------------------- */
132 void
133 mcview_display_hex (mcview_t * view)
135 const screen_dimen top = view->data_area.top;
136 const screen_dimen left = view->data_area.left;
137 const screen_dimen height = view->data_area.height;
138 const screen_dimen width = view->data_area.width;
139 const int ngroups = view->bytes_per_line / 4;
140 const screen_dimen text_start = 8 + 13 * ngroups + ((width < 80) ? 0 : (ngroups - 1 + 1));
141 /* 8 characters are used for the file offset, and every hex group
142 * takes 13 characters. On "big" screens, the groups are separated
143 * by an extra vertical line, and there is an extra space before the
144 * text column.
147 screen_dimen row;
148 off_t from;
149 int c;
150 mark_t boldflag = MARK_NORMAL;
151 struct hexedit_change_node *curr = view->change_list;
152 #ifdef HAVE_CHARSET
153 int ch = 0;
154 #endif /* HAVE_CHARSET */
156 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
157 int bytes; /* Number of bytes already printed on the line */
159 mcview_display_clean (view);
161 /* Find the first displayable changed byte */
162 from = view->dpy_start;
163 while (curr && (curr->offset < from))
165 curr = curr->next;
168 for (row = 0; mcview_get_byte (view, from, NULL) == TRUE && row < height; row++)
170 screen_dimen col = 0;
171 size_t i;
173 col = 0;
175 /* Print the hex offset */
176 g_snprintf (hex_buff, sizeof (hex_buff), "%08" PRIXMAX " ", (uintmax_t) from);
177 widget_move (view, top + row, left);
178 tty_setcolor (VIEW_BOLD_COLOR);
179 for (i = 0; col < width && hex_buff[i] != '\0'; i++)
181 tty_print_char (hex_buff[i]);
182 /* tty_print_char(hex_buff[i]); */
183 col += 1;
185 tty_setcolor (VIEW_NORMAL_COLOR);
187 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++)
190 #ifdef HAVE_CHARSET
191 if (view->utf8)
193 int cw = 1;
194 gboolean read_res = TRUE;
196 ch = mcview_get_utf (view, from, &cw, &read_res);
197 if (!read_res)
198 break;
199 /* char width is greater 0 bytes */
200 if (cw != 0)
202 int cnt;
203 char corr_buf[UTF8_CHAR_LEN + 1];
204 struct hexedit_change_node *corr = curr;
205 int res;
207 res = g_unichar_to_utf8 (ch, (char *) corr_buf);
209 for (cnt = 0; cnt < cw; cnt++)
211 if (curr != NULL && from + cnt == curr->offset)
213 /* replace only changed bytes in array of multibyte char */
214 corr_buf[cnt] = curr->value;
215 curr = curr->next;
218 corr_buf[res] = '\0';
219 /* Determine the state of the current multibyte char */
220 ch = utf8_to_int ((char *) corr_buf, &cw, &read_res);
221 curr = corr;
224 #endif /* HAVE_CHARSET */
225 if (!mcview_get_byte (view, from, &c))
226 break;
228 /* Save the cursor position for mcview_place_cursor() */
229 if (from == view->hex_cursor && !view->hexview_in_text)
231 view->cursor_row = row;
232 view->cursor_col = col;
235 /* Determine the state of the current byte */
236 boldflag = mcview_hex_calculate_boldflag (view, from, curr);
238 /* Determine the value of the current byte */
239 if (curr != NULL && from == curr->offset)
241 c = curr->value;
242 curr = curr->next;
245 /* Select the color for the hex number */
246 tty_setcolor (boldflag == MARK_NORMAL ? VIEW_NORMAL_COLOR :
247 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
248 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
249 /* boldflag == MARK_CURSOR */
250 view->hexview_in_text ? VIEW_SELECTED_COLOR : VIEW_UNDERLINED_COLOR);
252 /* Print the hex number */
253 widget_move (view, top + row, left + col);
254 if (col < width)
256 tty_print_char (hex_char[c / 16]);
257 col += 1;
259 if (col < width)
261 tty_print_char (hex_char[c % 16]);
262 col += 1;
265 /* Print the separator */
266 tty_setcolor (VIEW_NORMAL_COLOR);
267 if (bytes != view->bytes_per_line - 1)
269 if (col < width)
271 tty_print_char (' ');
272 col += 1;
275 /* After every four bytes, print a group separator */
276 if (bytes % 4 == 3)
278 if (view->data_area.width >= 80 && col < width)
280 tty_print_one_vline (TRUE);
281 col += 1;
283 if (col < width)
285 tty_print_char (' ');
286 col += 1;
291 /* Select the color for the character; this differs from the
292 * hex color when boldflag == MARK_CURSOR */
293 tty_setcolor (boldflag == MARK_NORMAL ? VIEW_NORMAL_COLOR :
294 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
295 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
296 /* boldflag == MARK_CURSOR */
297 view->hexview_in_text ? VIEW_SELECTED_COLOR : MARKED_SELECTED_COLOR);
300 #ifdef HAVE_CHARSET
301 if (mc_global.utf8_display)
303 if (!view->utf8)
305 c = convert_from_8bit_to_utf_c ((unsigned char) c, view->converter);
307 if (!g_unichar_isprint (c))
308 c = '.';
310 else if (view->utf8)
311 ch = convert_from_utf_to_current_c (ch, view->converter);
312 else
313 #endif
315 #ifdef HAVE_CHARSET
316 c = convert_to_display_c (c);
317 #endif
319 if (!is_printable (c))
320 c = '.';
323 /* Print corresponding character on the text side */
324 if (text_start + bytes < width)
326 widget_move (view, top + row, left + text_start + bytes);
327 #ifdef HAVE_CHARSET
328 if (view->utf8)
329 tty_print_anychar (ch);
330 else
331 #endif
332 tty_print_char (c);
335 /* Save the cursor position for mcview_place_cursor() */
336 if (from == view->hex_cursor && view->hexview_in_text)
338 view->cursor_row = row;
339 view->cursor_col = text_start + bytes;
344 /* Be polite to the other functions */
345 tty_setcolor (VIEW_NORMAL_COLOR);
347 mcview_place_cursor (view);
348 view->dpy_end = from;
351 /* --------------------------------------------------------------------------------------------- */
353 gboolean
354 mcview_hexedit_save_changes (mcview_t * view)
356 int answer = 0;
358 if (view->change_list == NULL)
359 return TRUE;
361 while (answer == 0)
363 int fp;
364 char *text;
365 struct hexedit_change_node *curr, *next;
367 #ifdef HAVE_ASSERT_H
368 assert (view->filename_vpath != NULL);
369 #endif
371 fp = mc_open (view->filename_vpath, O_WRONLY);
372 if (fp != -1)
374 for (curr = view->change_list; curr != NULL; curr = next)
376 next = curr->next;
378 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
379 || mc_write (fp, &(curr->value), 1) != 1)
380 goto save_error;
382 /* delete the saved item from the change list */
383 view->change_list = next;
384 view->dirty++;
385 mcview_set_byte (view, curr->offset, curr->value);
386 g_free (curr);
389 view->change_list = NULL;
391 if (view->locked)
392 view->locked = unlock_file (view->filename_vpath);
394 if (mc_close (fp) == -1)
395 message (D_ERROR, _("Save file"),
396 _("Error while closing the file:\n%s\n"
397 "Data may have been written or not"), unix_error_string (errno));
399 view->dirty++;
400 return TRUE;
403 save_error:
404 text = g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno));
405 (void) mc_close (fp);
407 answer = query_dialog (_("Save file"), text, D_ERROR, 2, _("&Retry"), _("&Cancel"));
408 g_free (text);
411 return FALSE;
414 /* --------------------------------------------------------------------------------------------- */
416 void
417 mcview_toggle_hexedit_mode (mcview_t * view)
419 view->hexedit_mode = !view->hexedit_mode;
420 view->dpy_bbar_dirty = TRUE;
421 view->dirty++;
424 /* --------------------------------------------------------------------------------------------- */
426 void
427 mcview_hexedit_free_change_list (mcview_t * view)
429 struct hexedit_change_node *curr, *next;
431 for (curr = view->change_list; curr != NULL; curr = next)
433 next = curr->next;
434 g_free (curr);
436 view->change_list = NULL;
438 if (view->locked)
439 view->locked = unlock_file (view->filename_vpath);
441 view->dirty++;
444 /* --------------------------------------------------------------------------------------------- */
446 void
447 mcview_enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
449 /* chnode always either points to the head of the list or
450 * to one of the ->next fields in the list. The value at
451 * this location will be overwritten with the new node. */
452 struct hexedit_change_node **chnode = head;
454 while (*chnode != NULL && (*chnode)->offset < node->offset)
455 chnode = &((*chnode)->next);
457 node->next = *chnode;
458 *chnode = node;
461 /* --------------------------------------------------------------------------------------------- */