Ticket #2170: Color collisions
[midnight-commander.git] / src / viewer / hex.c
blob16827bd6eb2c542ea519815c4d4ee933a1e8e955
1 /*
2 Internal file viewer for the Midnight Commander
3 Function for hex view
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
10 1995 Jakub Jelinek
11 1996 Joseph M. Hinkle
12 1997 Norbert Warmuth
13 1998 Pavel Machek
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
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 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 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, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
35 MA 02110-1301, USA.
38 #include <config.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdint.h> /* uintmax_t */
44 #include "lib/global.h"
45 #include "lib/tty/tty.h"
46 #include "lib/skin.h"
47 #include "lib/vfs/mc-vfs/vfs.h"
49 #include "src/main.h"
50 #include "src/wtools.h"
51 #include "src/charsets.h"
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 static int
77 utf8_to_int (char *str, int *char_width, gboolean * result)
79 int res = -1;
80 gunichar ch;
81 gchar *next_ch = NULL;
82 int width = 0;
84 *result = TRUE;
86 if (str == NULL)
88 *result = FALSE;
89 width = 0;
90 return 0;
93 res = g_utf8_get_char_validated (str, -1);
95 if (res < 0)
97 ch = *str;
98 width = 0;
100 else
102 ch = res;
103 /* Calculate UTF-8 char width */
104 next_ch = g_utf8_next_char (str);
105 if (next_ch)
107 width = next_ch - str;
109 else
111 ch = 0;
112 width = 0;
115 *char_width = width;
116 return ch;
119 /* --------------------------------------------------------------------------------------------- */
120 /*** public functions ****************************************************************************/
121 /* --------------------------------------------------------------------------------------------- */
123 void
124 mcview_display_hex (mcview_t * view)
126 const screen_dimen top = view->data_area.top;
127 const screen_dimen left = view->data_area.left;
128 const screen_dimen height = view->data_area.height;
129 const screen_dimen width = view->data_area.width;
130 const int ngroups = view->bytes_per_line / 4;
131 const screen_dimen text_start = 8 + 13 * ngroups + ((width < 80) ? 0 : (ngroups - 1 + 1));
132 /* 8 characters are used for the file offset, and every hex group
133 * takes 13 characters. On ``big'' screens, the groups are separated
134 * by an extra vertical line, and there is an extra space before the
135 * text column.
138 screen_dimen row, col;
139 off_t from;
140 int c;
141 mark_t boldflag = MARK_NORMAL;
142 struct hexedit_change_node *curr = view->change_list;
143 int ch = 0;
145 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
146 int bytes; /* Number of bytes already printed on the line */
148 mcview_display_clean (view);
150 /* Find the first displayable changed byte */
151 from = view->dpy_start;
152 while (curr && (curr->offset < from))
154 curr = curr->next;
157 for (row = 0; mcview_get_byte (view, from, NULL) == TRUE && row < height; row++)
159 size_t i;
160 col = 0;
162 /* Print the hex offset */
163 g_snprintf (hex_buff, sizeof (hex_buff), "%08jX ", (uintmax_t) from);
164 widget_move (view, top + row, left);
165 tty_setcolor (VIEW_BOLD_COLOR);
166 for (i = 0; col < width && hex_buff[i] != '\0'; i++)
168 tty_print_char (hex_buff[i]);
169 /* tty_print_char(hex_buff[i]); */
170 col += 1;
172 tty_setcolor (NORMAL_COLOR);
174 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++)
177 #ifdef HAVE_CHARSET
178 if (view->utf8)
180 char corr_buf[6 + 1];
181 int cnt, cw = 1;
182 gboolean read_res = TRUE;
183 ch = mcview_get_utf (view, from, &cw, &read_res);
184 if (!read_res)
185 break;
186 /* char width is greater 0 bytes */
187 if (cw != 0)
189 struct hexedit_change_node *corr = curr;
190 int res = g_unichar_to_utf8 (ch, (char *) corr_buf);
191 for (cnt = 0; cnt < cw; cnt++)
193 if (curr != NULL && from + cnt == curr->offset)
195 /* replace only changed bytes in array of multibyte char */
196 corr_buf[cnt] = curr->value;
197 curr = curr->next;
200 corr_buf[res] = '\0';
201 /* Determine the state of the current multibyte char */
202 ch = utf8_to_int ((char *) corr_buf, &cw, &read_res);
203 curr = corr;
206 #endif
207 if (!mcview_get_byte (view, from, &c))
208 break;
210 /* Save the cursor position for mcview_place_cursor() */
211 if (from == view->hex_cursor && !view->hexview_in_text)
213 view->cursor_row = row;
214 view->cursor_col = col;
217 /* Determine the state of the current byte */
218 boldflag =
219 (from == view->hex_cursor) ? MARK_CURSOR
220 : (curr != NULL && from == curr->offset) ? MARK_CHANGED
221 : (view->search_start <= from &&
222 from < view->search_end) ? MARK_SELECTED : MARK_NORMAL;
224 /* Determine the value of the current byte */
225 if (curr != NULL && from == curr->offset)
227 c = curr->value;
228 curr = curr->next;
231 /* Select the color for the hex number */
232 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
233 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
234 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
235 /* boldflag == MARK_CURSOR */
236 view->hexview_in_text ? VIEW_SELECTED_COLOR : VIEW_UNDERLINED_COLOR);
238 /* Print the hex number */
239 widget_move (view, top + row, left + col);
240 if (col < width)
242 tty_print_char (hex_char[c / 16]);
243 col += 1;
245 if (col < width)
247 tty_print_char (hex_char[c % 16]);
248 col += 1;
251 /* Print the separator */
252 tty_setcolor (NORMAL_COLOR);
253 if (bytes != view->bytes_per_line - 1)
255 if (col < width)
257 tty_print_char (' ');
258 col += 1;
261 /* After every four bytes, print a group separator */
262 if (bytes % 4 == 3)
264 if (view->data_area.width >= 80 && col < width)
266 tty_print_one_vline (TRUE);
267 col += 1;
269 if (col < width)
271 tty_print_char (' ');
272 col += 1;
277 /* Select the color for the character; this differs from the
278 * hex color when boldflag == MARK_CURSOR */
279 tty_setcolor (boldflag == MARK_NORMAL ? NORMAL_COLOR :
280 boldflag == MARK_SELECTED ? VIEW_BOLD_COLOR :
281 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
282 /* boldflag == MARK_CURSOR */
283 view->hexview_in_text ? VIEW_SELECTED_COLOR : MARKED_SELECTED_COLOR);
286 #ifdef HAVE_CHARSET
287 if (utf8_display)
289 if (!view->utf8)
291 c = convert_from_8bit_to_utf_c ((unsigned char) c, view->converter);
293 if (!g_unichar_isprint (c))
294 c = '.';
296 else if (view->utf8)
297 ch = convert_from_utf_to_current_c (ch, view->converter);
298 else
299 #endif
301 c = convert_to_display_c (c);
303 if (!is_printable (c))
304 c = '.';
307 /* Print corresponding character on the text side */
308 if (text_start + bytes < width)
310 widget_move (view, top + row, left + text_start + bytes);
311 #ifdef HAVE_CHARSET
312 if (view->utf8)
313 tty_print_anychar (ch);
314 else
315 #endif
317 tty_print_char (c);
321 /* Save the cursor position for mcview_place_cursor() */
322 if (from == view->hex_cursor && view->hexview_in_text)
324 view->cursor_row = row;
325 view->cursor_col = text_start + bytes;
330 /* Be polite to the other functions */
331 tty_setcolor (NORMAL_COLOR);
333 mcview_place_cursor (view);
334 view->dpy_end = from;
337 /* --------------------------------------------------------------------------------------------- */
339 gboolean
340 mcview_hexedit_save_changes (mcview_t * view)
342 int answer = 0;
344 if (view->change_list == NULL)
345 return TRUE;
347 while (answer == 0)
349 int fp;
350 char *text;
351 struct hexedit_change_node *curr, *next;
353 assert (view->filename != NULL);
355 fp = mc_open (view->filename, O_WRONLY);
356 if (fp != -1)
358 for (curr = view->change_list; curr != NULL; curr = next)
360 next = curr->next;
362 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
363 || mc_write (fp, &(curr->value), 1) != 1)
364 goto save_error;
366 /* delete the saved item from the change list */
367 view->change_list = next;
368 view->dirty++;
369 mcview_set_byte (view, curr->offset, curr->value);
370 g_free (curr);
373 view->change_list = NULL;
375 if (view->locked)
376 view->locked = mcview_unlock_file (view);
378 if (mc_close (fp) == -1)
379 message (D_ERROR, _("Save file"),
380 _("Error while closing the file:\n%s\n"
381 "Data may have been written or not"), unix_error_string (errno));
383 view->dirty++;
384 return TRUE;
387 save_error:
388 text = g_strdup_printf (_("Cannot save file:\n%s"), unix_error_string (errno));
389 (void) mc_close (fp);
391 answer = query_dialog (_("Save file"), text, D_ERROR, 2, _("&Retry"), _("&Cancel"));
392 g_free (text);
395 return FALSE;
398 /* --------------------------------------------------------------------------------------------- */
400 void
401 mcview_toggle_hexedit_mode (mcview_t * view)
403 view->hexedit_mode = !view->hexedit_mode;
404 view->dpy_bbar_dirty = TRUE;
405 view->dirty++;
408 /* --------------------------------------------------------------------------------------------- */
410 void
411 mcview_hexedit_free_change_list (mcview_t * view)
413 struct hexedit_change_node *curr, *next;
415 for (curr = view->change_list; curr != NULL; curr = next)
417 next = curr->next;
418 g_free (curr);
420 view->change_list = NULL;
422 if (view->locked)
423 view->locked = mcview_unlock_file (view);
425 view->dirty++;
428 /* --------------------------------------------------------------------------------------------- */
430 void
431 mcview_enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
433 /* chnode always either points to the head of the list or
434 * to one of the ->next fields in the list. The value at
435 * this location will be overwritten with the new node. */
436 struct hexedit_change_node **chnode = head;
438 while (*chnode != NULL && (*chnode)->offset < node->offset)
439 chnode = &((*chnode)->next);
441 node->next = *chnode;
442 *chnode = node;
445 /* --------------------------------------------------------------------------------------------- */