1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* gtkhex.c - a GtkHex widget, modified for use in GHex
3 
4    Copyright (C) 1998 - 2004 Free Software Foundation
5 
6    GHex is free software; you can redistribute it and/or
7    modify it under the terms of the GNU General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10 
11    GHex is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with GHex; see the file COPYING.
18    If not, write to the Free Software Foundation, Inc.,
19    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21    Author: Jaka Mocnik <jaka@gnu.org>
22  */
23 
24 #include <config.h>
25 
26 #include <string.h>
27 
28 #include <gdk/gdkkeysyms.h>
29 
30 #include "hex-document.h"
31 #include "gtkhex.h"
32 #include "gtkhex-private.h"
33 
34 #define DEFAULT_CPL 32
35 #define DEFAULT_LINES 10
36 
37 #define SCROLL_TIMEOUT 100
38 
39 #define DEFAULT_FONT "Monospace 12"
40 
41 #define is_displayable(c) (((c) >= 0x20) && ((c) < 0x7f))
42 
43 #define GTKHEX_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object),\
44                                    GTK_TYPE_HEX,                         \
45                                    GtkHexPrivate))
46 
47 enum {
48 	CURSOR_MOVED_SIGNAL,
49 	DATA_CHANGED_SIGNAL,
50 	CUT_CLIPBOARD_SIGNAL,
51 	COPY_CLIPBOARD_SIGNAL,
52 	PASTE_CLIPBOARD_SIGNAL,
53 	LAST_SIGNAL
54 };
55 
56 enum ClipboardTargets {
57   TARGET_HEXDATA,
58   TARGET_STRING,
59   N_TARGETS
60 };
61 
62 static const GtkTargetEntry clipboard_targets[] = {
63 	{ "HEXDATA", 0, TARGET_HEXDATA },
64 	{ "STRING", 0, TARGET_STRING }
65 };
66 
67 struct _GtkHex_AutoHighlight
68 {
69 	gint search_view;
70 	gchar *search_string;
71 	gint search_len;
72 
73 	gchar *colour;
74 
75 	gint view_min;
76 	gint view_max;
77 
78 	GtkHex_Highlight *highlights;
79 	GtkHex_AutoHighlight *next, *prev;
80 };
81 
82 struct _GtkHexPrivate
83 {
84 	/* Buffer for storing formatted data for rendering;
85 	   dynamically adjusts its size to the display size */
86 	guchar *disp_buffer;
87 
88 	gint default_cpl;
89 	gint default_lines;
90 
91 	guchar *clip_buf;
92 	gint clip_buf_len;
93 };
94 
95 static gint gtkhex_signals[LAST_SIGNAL] = { 0 };
96 
97 static GtkFixedClass *parent_class = NULL;
98 static gchar *char_widths = NULL;
99 
100 static void render_hex_highlights (GtkHex *gh, cairo_t *cr, gint cursor_line);
101 static void render_ascii_highlights (GtkHex *gh, cairo_t *cr, gint cursor_line);
102 static void render_hex_lines (GtkHex *gh, cairo_t *cr, gint, gint);
103 static void render_ascii_lines (GtkHex *gh, cairo_t *cr, gint, gint);
104 
105 static void gtk_hex_validate_highlight(GtkHex *gh, GtkHex_Highlight *hl);
106 static void gtk_hex_invalidate_highlight(GtkHex *gh, GtkHex_Highlight *hl);
107 static void gtk_hex_invalidate_all_highlights(GtkHex *gh);
108 
109 static void gtk_hex_update_all_auto_highlights(GtkHex *gh, gboolean delete,
110 											   gboolean add);
111 
112 static GtkHex_Highlight *gtk_hex_insert_highlight (GtkHex *gh,
113 												   GtkHex_AutoHighlight *ahl,
114 												   gint start, gint end);
115 static void gtk_hex_delete_highlight (GtkHex *gh, GtkHex_AutoHighlight *ahl,
116 									  GtkHex_Highlight *hl);
117 static void gtk_hex_update_auto_highlight(GtkHex *gh, GtkHex_AutoHighlight *ahl,
118 									  gboolean delete, gboolean add);
119 
120 /*
121  * simply forces widget w to redraw itself
122  */
redraw_widget(GtkWidget * w)123 static void redraw_widget(GtkWidget *w) {
124 	if(!gtk_widget_get_realized(w))
125 		return;
126 
127 	gdk_window_invalidate_rect (gtk_widget_get_window(w), NULL, FALSE);
128 }
129 
130 /*
131  * ?_to_pointer translates mouse coordinates in hex/ascii view
132  * to cursor coordinates.
133  */
hex_to_pointer(GtkHex * gh,guint mx,guint my)134 static void hex_to_pointer(GtkHex *gh, guint mx, guint my) {
135 	guint cx, cy, x;
136 
137 	cy = gh->top_line + my/gh->char_height;
138 
139 	cx = 0; x = 0;
140 	while(cx < 2*gh->cpl) {
141 		x += gh->char_width;
142 
143 		if(x > mx) {
144 			gtk_hex_set_cursor_xy(gh, cx/2, cy);
145 			gtk_hex_set_nibble(gh, ((cx%2 == 0)?UPPER_NIBBLE:LOWER_NIBBLE));
146 
147 			cx = 2*gh->cpl;
148 		}
149 
150 		cx++;
151 
152 		if(cx%(2*gh->group_type) == 0)
153 			x += gh->char_width;
154 	}
155 }
156 
ascii_to_pointer(GtkHex * gh,gint mx,gint my)157 static void ascii_to_pointer(GtkHex *gh, gint mx, gint my) {
158 	int cy;
159 
160 	cy = gh->top_line + my/gh->char_height;
161 
162 	gtk_hex_set_cursor_xy(gh, mx/gh->char_width, cy);
163 }
164 
get_max_char_width(GtkHex * gh,PangoFontMetrics * font_metrics)165 static guint get_max_char_width(GtkHex *gh, PangoFontMetrics *font_metrics) {
166 	/* this is, I guess, a rather dirty trick, but
167 	   right now i can't think of anything better */
168 	guint i;
169 	guint maxwidth = 0;
170 	PangoRectangle logical_rect;
171 	PangoLayout *layout;
172 	gchar str[2];
173 
174 	if (char_widths == NULL)
175 		char_widths = (gchar*)g_malloc(0x100);
176 
177 	char_widths[0] = 0;
178 
179 	layout = gtk_widget_create_pango_layout (GTK_WIDGET (gh), "");
180 	pango_layout_set_font_description (layout, gh->font_desc);
181 
182 	for(i = 1; i < 0x100; i++) {
183 		logical_rect.width = 0;
184 		/* Check if the char is displayable. Caused trouble to pango */
185 		if (is_displayable((guchar)i)) {
186 			sprintf (str, "%c", (gchar)i);
187 			pango_layout_set_text(layout, str, -1);
188 			pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
189 		}
190 		char_widths[i] = logical_rect.width;
191 	}
192 
193 	for(i = '0'; i <= 'z'; i++)
194 		maxwidth = MAX(maxwidth, char_widths[i]);
195 
196 	g_object_unref (G_OBJECT (layout));
197 	return maxwidth;
198 }
199 
format_xbyte(GtkHex * gh,gint pos,gchar buf[2])200 void format_xbyte(GtkHex *gh, gint pos, gchar buf[2]) {
201 	guint low, high;
202 	guchar c;
203 
204 	c = gtk_hex_get_byte(gh, pos);
205 	low = c & 0x0F;
206 	high = (c & 0xF0) >> 4;
207 
208 	buf[0] = ((high < 10)?(high + '0'):(high - 10 + 'A'));
209 	buf[1] = ((low < 10)?(low + '0'):(low - 10 + 'A'));
210 }
211 
212 /*
213  * format_[x|a]block() formats contents of the buffer
214  * into displayable text in hex or ascii, respectively
215  */
format_xblock(GtkHex * gh,gchar * out,guint start,guint end)216 gint format_xblock(GtkHex *gh, gchar *out, guint start, guint end) {
217 	int i, j, low, high;
218 	guchar c;
219 
220 	for(i = start + 1, j = 0; i <= end; i++) {
221 		c = gtk_hex_get_byte(gh, i - 1);
222 		low = c & 0x0F;
223 		high = (c & 0xF0) >> 4;
224 
225 		out[j++] = ((high < 10)?(high + '0'):(high - 10 + 'A'));
226 		out[j++] = ((low < 10)?(low + '0'):(low - 10 + 'A'));
227 
228 		if(i%gh->group_type == 0)
229 			out[j++] = ' ';
230 	}
231 
232 	return j;
233 }
234 
format_ablock(GtkHex * gh,gchar * out,guint start,guint end)235 gint format_ablock(GtkHex *gh, gchar *out, guint start, guint end) {
236 	int i, j;
237 	guchar c;
238 
239 	for(i = start, j = 0; i < end; i++, j++) {
240 		c = gtk_hex_get_byte(gh, i);
241 		if(is_displayable(c))
242 			out[j] = c;
243 		else
244 			out[j] = '.';
245 	}
246 
247 	return end - start;
248 }
249 
250 /*
251  * get_[x|a]coords() translates offset from the beginning of
252  * the block into x,y coordinates of the xdisp/adisp, respectively
253  */
get_xcoords(GtkHex * gh,gint pos,gint * x,gint * y)254 static gint get_xcoords(GtkHex *gh, gint pos, gint *x, gint *y) {
255 	gint cx, cy, spaces;
256 
257 	if(gh->cpl == 0)
258 		return FALSE;
259 
260 	cy = pos / gh->cpl;
261 	cy -= gh->top_line;
262 	if(cy < 0)
263 		return FALSE;
264 
265 	cx = 2*(pos % gh->cpl);
266 	spaces = (pos % gh->cpl) / gh->group_type;
267 
268 	cx *= gh->char_width;
269 	cy *= gh->char_height;
270 	spaces *= gh->char_width;
271 	*x = cx + spaces;
272 	*y = cy;
273 
274 	return TRUE;
275 }
276 
get_acoords(GtkHex * gh,gint pos,gint * x,gint * y)277 static gint get_acoords(GtkHex *gh, gint pos, gint *x, gint *y) {
278 	gint cx, cy;
279 
280 	if(gh->cpl == 0)
281 		return FALSE;
282 
283 	cy = pos / gh->cpl;
284 	cy -= gh->top_line;
285 	if(cy < 0)
286 		return FALSE;
287 	cy *= gh->char_height;
288 
289 	cx = gh->char_width*(pos % gh->cpl);
290 
291 	*x = cx;
292 	*y = cy;
293 
294 	return TRUE;
295 }
296 
297 static void
invalidate_xc(GtkHex * gh)298 invalidate_xc (GtkHex *gh)
299 {
300     GtkWidget *widget = gh->xdisp;
301     gint cx, cy;
302 
303     if (get_xcoords (gh, gh->cursor_pos, &cx, &cy)) {
304         if (gh->lower_nibble)
305             cx += gh->char_width;
306 
307         gtk_widget_queue_draw_area (widget,
308                                     cx, cy,
309                                     gh->char_width + 1,
310                                     gh->char_height);
311     }
312 }
313 
314 static void
invalidate_ac(GtkHex * gh)315 invalidate_ac (GtkHex *gh)
316 {
317     GtkWidget *widget = gh->adisp;
318     gint cx, cy;
319 
320     if (get_acoords (gh, gh->cursor_pos, &cx, &cy)) {
321         gtk_widget_queue_draw_area (widget,
322                                     cx, cy,
323                                     gh->char_width + 1,
324                                     gh->char_height);
325     }
326 }
327 
328 /*
329  * the cursor rendering stuff...
330  */
331 static void
render_ac(GtkHex * gh,cairo_t * cr)332 render_ac (GtkHex *gh,
333            cairo_t *cr)
334 {
335 	GdkRGBA bg_color;
336 	GdkRGBA fg_color;
337 	GtkStateFlags state;
338 	GtkStyleContext *context;
339 	gint cx, cy;
340 	static guchar c[2] = "\0\0";
341 
342 	if(!gtk_widget_get_realized(gh->adisp))
343 		return;
344 
345 	context = gtk_widget_get_style_context (gh->adisp);
346 	state = gtk_widget_get_state_flags (gh->adisp);
347 	state |= GTK_STATE_FLAG_SELECTED;
348 
349 	if(get_acoords(gh, gh->cursor_pos, &cx, &cy)) {
350 		c[0] = gtk_hex_get_byte(gh, gh->cursor_pos);
351 		if(!is_displayable(c[0]))
352 			c[0] = '.';
353 
354 		gtk_style_context_get_background_color (context, state, &bg_color);
355 		gdk_cairo_set_source_rgba (cr, &bg_color);
356 
357 		if(gh->active_view == VIEW_ASCII) {
358 			cairo_rectangle (cr, cx, cy, gh->char_width, gh->char_height - 1);
359 			cairo_fill (cr);
360 			gtk_style_context_get_color (context, state, &fg_color);
361 		}
362 		else {
363 			cairo_set_line_width (cr, 1.0);
364 			cairo_rectangle (cr, cx + 0.5, cy + 0.5, gh->char_width, gh->char_height - 1);
365 			cairo_stroke (cr);
366 			gtk_style_context_get_color (context, state & ~GTK_STATE_FLAG_SELECTED, &fg_color);
367 		}
368 		gdk_cairo_set_source_rgba (cr, &fg_color);
369 		cairo_move_to (cr, cx, cy);
370 		pango_layout_set_text (gh->alayout, c, 1);
371 		pango_cairo_show_layout (cr, gh->alayout);
372 	}
373 }
374 
375 static void
render_xc(GtkHex * gh,cairo_t * cr)376 render_xc (GtkHex *gh,
377            cairo_t *cr)
378 {
379 	GdkRGBA bg_color;
380 	GdkRGBA fg_color;
381 	GtkStateFlags state;
382 	GtkStyleContext *context;
383 	gint cx, cy, i;
384 	static guchar c[2];
385 
386 	if(!gtk_widget_get_realized(gh->xdisp))
387 		return;
388 
389 	context = gtk_widget_get_style_context (gh->xdisp);
390 	state = gtk_widget_get_state_flags (gh->xdisp);
391 	state |= GTK_STATE_FLAG_SELECTED;
392 
393 	if(get_xcoords(gh, gh->cursor_pos, &cx, &cy)) {
394 		format_xbyte(gh, gh->cursor_pos, c);
395 		if(gh->lower_nibble) {
396 			cx += gh->char_width;
397 			i = 1;
398 		}
399 		else {
400 			c[1] = 0;
401 			i = 0;
402 		}
403 
404 		gtk_style_context_get_background_color (context, state, &bg_color);
405 		gdk_cairo_set_source_rgba (cr, &bg_color);
406 
407 		if(gh->active_view == VIEW_HEX) {
408 			cairo_rectangle (cr, cx, cy, gh->char_width, gh->char_height - 1);
409 			cairo_fill (cr);
410 			gtk_style_context_get_color (context, state, &fg_color);
411 		}
412 		else {
413 			cairo_set_line_width (cr, 1.0);
414 			cairo_rectangle (cr, cx + 0.5, cy + 0.5, gh->char_width, gh->char_height - 1);
415 			cairo_stroke (cr);
416 			gtk_style_context_get_color (context, state & ~GTK_STATE_FLAG_SELECTED, &fg_color);
417 		}
418 		gdk_cairo_set_source_rgba (cr, &fg_color);
419 		cairo_move_to (cr, cx, cy);
420 		pango_layout_set_text (gh->xlayout, &c[i], 1);
421 		pango_cairo_show_layout (cr, gh->xlayout);
422 	}
423 }
424 
show_cursor(GtkHex * gh)425 static void show_cursor(GtkHex *gh) {
426 	if(!gh->cursor_shown) {
427 		if (gtk_widget_get_realized (gh->xdisp) || gtk_widget_get_realized (gh->adisp)) {
428 			invalidate_xc (gh);
429 			invalidate_ac (gh);
430 		}
431 		gh->cursor_shown = TRUE;
432 	}
433 }
434 
hide_cursor(GtkHex * gh)435 static void hide_cursor(GtkHex *gh) {
436 	if(gh->cursor_shown) {
437 		if (gtk_widget_get_realized (gh->xdisp) || gtk_widget_get_realized (gh->adisp)) {
438 			invalidate_xc (gh);
439 			invalidate_ac (gh);
440 		}
441 		gh->cursor_shown = FALSE;
442 	}
443 }
444 
445 static void
render_hex_highlights(GtkHex * gh,cairo_t * cr,gint cursor_line)446 render_hex_highlights (GtkHex *gh,
447                        cairo_t *cr,
448                        gint cursor_line)
449 {
450 	GtkHex_Highlight *curHighlight = &gh->selection;
451 	gint xcpl = gh->cpl*2 + gh->cpl/gh->group_type;
452 	   /* would be nice if we could cache that */
453 
454 	GtkHex_AutoHighlight *nextList = gh->auto_highlight;
455 	GtkStateFlags state;
456 	GtkStyleContext *context;
457 
458 	context = gtk_widget_get_style_context (gh->xdisp);
459 	state = gtk_widget_get_state_flags (gh->xdisp);
460 
461 	gtk_style_context_save (context);
462 	state |= GTK_STATE_FLAG_SELECTED;
463 	gtk_style_context_set_state (context, state);
464 
465 	cairo_save (cr);
466 
467 	while (curHighlight)
468 	{
469 		if (ABS(curHighlight->start - curHighlight->end) >= curHighlight->min_select)
470 		{
471 			GdkRGBA bg_color;
472 			gint start, end;
473 			gint sl, el;
474 			gint cursor_off = 0;
475 			gint len;
476 
477 			gtk_hex_validate_highlight(gh, curHighlight);
478 
479 			start = MIN(curHighlight->start, curHighlight->end);
480 			end = MAX(curHighlight->start, curHighlight->end);
481 			sl = curHighlight->start_line;
482 			el = curHighlight->end_line;
483 
484 			if (curHighlight->bg_color) {
485 				gdk_cairo_set_source_rgba (cr, curHighlight->bg_color);
486 			} else {
487 				gtk_style_context_get_background_color (context, state, &bg_color);
488 				gdk_cairo_set_source_rgba (cr, &bg_color);
489 			}
490 			if (cursor_line == sl)
491 			{
492 				cursor_off = 2*(start%gh->cpl) + (start%gh->cpl)/gh->group_type;
493 				if (cursor_line == el)
494 					len = 2*(end%gh->cpl + 1) + (end%gh->cpl)/gh->group_type;
495 				else
496 					len = xcpl;
497 				len = len - cursor_off;
498 				if (len > 0)
499 					cairo_rectangle (cr,
500 					                 cursor_off * gh->char_width,
501 					                 cursor_line * gh->char_height,
502 					                 len * gh->char_width,
503 					                 gh->char_height);
504 			}
505 			else if (cursor_line == el)
506 			{
507 				cursor_off = 2*(end%gh->cpl + 1) + (end%gh->cpl)/gh->group_type;
508 				if (cursor_off > 0)
509 					cairo_rectangle (cr,
510 					                 0,
511 					                 cursor_line * gh->char_height,
512 					                 cursor_off * gh->char_width,
513 					                 gh->char_height);
514 			}
515 			else if (cursor_line > sl && cursor_line < el)
516 			{
517 				cairo_rectangle (cr,
518 				                 0,
519 				                 cursor_line * gh->char_height,
520 				                 xcpl * gh->char_width,
521 				                 gh->char_height);
522 			}
523 
524 			cairo_fill (cr);
525 		}
526 		curHighlight = curHighlight->next;
527 		while (curHighlight == NULL && nextList)
528 		{
529 			curHighlight = nextList->highlights;
530 			nextList = nextList->next;
531 		}
532 	}
533 
534 	cairo_restore (cr);
535 	gtk_style_context_restore (context);
536 }
537 
538 static void
render_ascii_highlights(GtkHex * gh,cairo_t * cr,gint cursor_line)539 render_ascii_highlights (GtkHex *gh,
540                          cairo_t *cr,
541                          gint cursor_line)
542 {
543 	GtkHex_Highlight *curHighlight = &gh->selection;
544 	GtkHex_AutoHighlight *nextList = gh->auto_highlight;
545 	GtkStateFlags state;
546 	GtkStyleContext *context;
547 
548 	context = gtk_widget_get_style_context (gh->adisp);
549 	state = gtk_widget_get_state_flags (gh->adisp);
550 
551 	gtk_style_context_save (context);
552 	state |= GTK_STATE_FLAG_SELECTED;
553 	gtk_style_context_set_state (context, state);
554 
555 	cairo_save (cr);
556 
557 	while (curHighlight)
558 	{
559 		if (ABS(curHighlight->start - curHighlight->end) >= curHighlight->min_select)
560 		{
561 			GdkRGBA bg_color;
562 			gint start, end;
563 			gint sl, el;
564 			gint cursor_off = 0;
565 			gint len;
566 
567 			gtk_hex_validate_highlight(gh, curHighlight);
568 
569 			start = MIN(curHighlight->start, curHighlight->end);
570 			end = MAX(curHighlight->start, curHighlight->end);
571 			sl = curHighlight->start_line;
572 			el = curHighlight->end_line;
573 
574 			if (curHighlight->bg_color) {
575 				gdk_cairo_set_source_rgba (cr, curHighlight->bg_color);
576 			} else {
577 				gtk_style_context_get_background_color (context, state, &bg_color);
578 				gdk_cairo_set_source_rgba (cr, &bg_color);
579 			}
580 			if (cursor_line == sl)
581 			{
582 				cursor_off = start % gh->cpl;
583 				if (cursor_line == el)
584 					len = end - start + 1;
585 				else
586 					len = gh->cpl - cursor_off;
587 				if (len > 0)
588 					cairo_rectangle (cr,
589 					                 cursor_off * gh->char_width,
590 					                 cursor_line * gh->char_height,
591 					                 len * gh->char_width,
592 					                 gh->char_height);
593 			}
594 			else if (cursor_line == el)
595 			{
596 				cursor_off = end % gh->cpl + 1;
597 				if (cursor_off > 0)
598 					cairo_rectangle (cr,
599 					                 0,
600 					                 cursor_line * gh->char_height,
601 					                 cursor_off * gh->char_width,
602 					                 gh->char_height);
603 			}
604 			else if (cursor_line > sl && cursor_line < el)
605 			{
606 				cairo_rectangle (cr,
607 				                 0,
608 				                 cursor_line * gh->char_height,
609 				                 gh->cpl * gh->char_width,
610 				                 gh->char_height);
611 			}
612 
613 			cairo_fill (cr);
614 		}
615 		curHighlight = curHighlight->next;
616 		while (curHighlight == NULL && nextList)
617 		{
618 			curHighlight = nextList->highlights;
619 			nextList = nextList->next;
620 		}
621 	}
622 
623 	cairo_restore (cr);
624 	gtk_style_context_restore (context);
625 }
626 
627 /*
628  * when calling invalidate_*_lines() the imin and imax arguments are the
629  * numbers of the first and last line TO BE INVALIDATED in the range
630  * [0 .. gh->vis_lines-1] AND NOT [0 .. gh->lines]!
631  */
632 static void
invalidate_lines(GtkHex * gh,GtkWidget * widget,gint imin,gint imax)633 invalidate_lines (GtkHex *gh,
634                   GtkWidget *widget,
635                   gint imin,
636                   gint imax)
637 {
638     GtkAllocation allocation;
639 
640     gtk_widget_get_allocation (widget, &allocation);
641     gtk_widget_queue_draw_area (widget,
642                                 0,
643                                 imin * gh->char_height,
644                                 allocation.width,
645                                 (imax - imin + 1) * gh->char_height);
646 }
647 
648 static void
invalidate_hex_lines(GtkHex * gh,gint imin,gint imax)649 invalidate_hex_lines (GtkHex *gh,
650                       gint imin,
651                       gint imax)
652 {
653     invalidate_lines (gh, gh->xdisp, imin, imax);
654 }
655 
656 static void
invalidate_ascii_lines(GtkHex * gh,gint imin,gint imax)657 invalidate_ascii_lines (GtkHex *gh,
658                         gint imin,
659                         gint imax)
660 {
661     invalidate_lines (gh, gh->adisp, imin, imax);
662 }
663 
664 static void
invalidate_offsets(GtkHex * gh,gint imin,gint imax)665 invalidate_offsets (GtkHex *gh,
666                     gint imin,
667                     gint imax)
668 {
669     invalidate_lines (gh, gh->offsets, imin, imax);
670 }
671 
672 /*
673  * when calling render_*_lines() the imin and imax arguments are the
674  * numbers of the first and last line TO BE DISPLAYED in the range
675  * [0 .. gh->vis_lines-1] AND NOT [0 .. gh->lines]!
676  */
677 static void
render_hex_lines(GtkHex * gh,cairo_t * cr,gint imin,gint imax)678 render_hex_lines (GtkHex *gh,
679                   cairo_t *cr,
680                   gint imin,
681                   gint imax)
682 {
683 	GtkWidget *w = gh->xdisp;
684 	GdkRGBA bg_color;
685 	GdkRGBA fg_color;
686 	GtkAllocation allocation;
687 	GtkStateFlags state;
688 	GtkStyleContext *context;
689 	gint i, cursor_line;
690 	gint xcpl = gh->cpl*2 + gh->cpl/gh->group_type;
691 	gint frm_len, tmp;
692 
693 	if( (!gtk_widget_get_realized(GTK_WIDGET (gh))) || (gh->cpl == 0) )
694 		return;
695 
696 	context = gtk_widget_get_style_context (w);
697 	state = gtk_widget_get_state_flags (w);
698 
699 	gtk_style_context_get_background_color (context, state, &bg_color);
700 	gtk_style_context_get_color (context, state, &fg_color);
701 
702 	cursor_line = gh->cursor_pos / gh->cpl - gh->top_line;
703 
704 	gtk_widget_get_allocation(w, &allocation);
705 	gdk_cairo_set_source_rgba (cr, &bg_color);
706 	cairo_rectangle (cr, 0, imin * gh->char_height, allocation.width, (imax - imin + 1) * gh->char_height);
707 	cairo_fill (cr);
708 
709 	imax = MIN(imax, gh->vis_lines);
710 	imax = MIN(imax, gh->lines);
711 
712 	gdk_cairo_set_source_rgba (cr, &fg_color);
713 
714 	frm_len = format_xblock (gh, gh->priv->disp_buffer, (gh->top_line+imin)*gh->cpl,
715 							MIN((gh->top_line+imax+1)*gh->cpl, gh->document->file_size) );
716 
717 	for(i = imin; i <= imax; i++) {
718 		tmp = (gint)frm_len - (gint)((i - imin)*xcpl);
719 		if(tmp <= 0)
720 			break;
721 
722 		render_hex_highlights (gh, cr, i);
723 		cairo_move_to (cr, 0, i * gh->char_height);
724 		pango_layout_set_text (gh->xlayout, gh->priv->disp_buffer + (i - imin) * xcpl, MIN(xcpl, tmp));
725 		pango_cairo_show_layout (cr, gh->xlayout);
726 	}
727 
728 	if((cursor_line >= imin) && (cursor_line <= imax) && (gh->cursor_shown))
729 		render_xc (gh, cr);
730 }
731 
732 static void
render_ascii_lines(GtkHex * gh,cairo_t * cr,gint imin,gint imax)733 render_ascii_lines (GtkHex *gh,
734                     cairo_t *cr,
735                     gint imin,
736                     gint imax)
737 {
738 	GtkWidget *w = gh->adisp;
739 	GdkRGBA bg_color;
740 	GdkRGBA fg_color;
741 	GtkAllocation allocation;
742 	GtkStateFlags state;
743 	GtkStyleContext *context;
744 	gint i, tmp, frm_len;
745 	guint cursor_line;
746 
747 	if( (!gtk_widget_get_realized(GTK_WIDGET(gh))) || (gh->cpl == 0) )
748 		return;
749 
750 	context = gtk_widget_get_style_context (w);
751 	state = gtk_widget_get_state_flags (w);
752 
753 	gtk_style_context_get_background_color (context, state, &bg_color);
754 	gtk_style_context_get_color (context, state, &fg_color);
755 
756 	cursor_line = gh->cursor_pos / gh->cpl - gh->top_line;
757 
758 	gtk_widget_get_allocation(w, &allocation);
759 	gdk_cairo_set_source_rgba (cr, &bg_color);
760 	cairo_rectangle (cr, 0, imin * gh->char_height, allocation.width, (imax - imin + 1) * gh->char_height);
761 	cairo_fill (cr);
762 
763 	imax = MIN(imax, gh->vis_lines);
764 	imax = MIN(imax, gh->lines);
765 
766 	gdk_cairo_set_source_rgba (cr, &fg_color);
767 
768 	frm_len = format_ablock (gh, gh->priv->disp_buffer, (gh->top_line+imin)*gh->cpl,
769 							MIN((gh->top_line+imax+1)*gh->cpl, gh->document->file_size) );
770 
771 	for(i = imin; i <= imax; i++) {
772 		tmp = (gint)frm_len - (gint)((i - imin)*gh->cpl);
773 		if(tmp <= 0)
774 			break;
775 
776 		render_ascii_highlights (gh, cr, i);
777 
778 		cairo_move_to (cr, 0, i * gh->char_height);
779 		pango_layout_set_text (gh->alayout, gh->priv->disp_buffer + (i - imin)*gh->cpl, MIN(gh->cpl, tmp));
780 		pango_cairo_show_layout (cr, gh->alayout);
781 	}
782 
783 	if((cursor_line >= imin) && (cursor_line <= imax) && (gh->cursor_shown))
784 		render_ac (gh, cr);
785 }
786 
787 static void
render_offsets(GtkHex * gh,cairo_t * cr,gint imin,gint imax)788 render_offsets (GtkHex *gh,
789                 cairo_t *cr,
790                 gint imin,
791                 gint imax)
792 {
793 	GtkWidget *w = gh->offsets;
794 	GdkRGBA bg_color;
795 	GdkRGBA fg_color;
796 	GtkAllocation allocation;
797 	GtkStateFlags state;
798 	GtkStyleContext *context;
799 	gint i;
800 	gchar offstr[9];
801 
802 	if(!gtk_widget_get_realized(GTK_WIDGET(gh)))
803 		return;
804 
805 	context = gtk_widget_get_style_context (w);
806 	state = gtk_widget_get_state_flags (w);
807 
808 	gtk_style_context_get_background_color (context, state, &bg_color);
809 	gtk_style_context_get_color (context, state, &fg_color);
810 
811 	gtk_widget_get_allocation(w, &allocation);
812 	gdk_cairo_set_source_rgba (cr, &bg_color);
813 	cairo_rectangle (cr, 0, imin * gh->char_height, allocation.width, (imax - imin + 1) * gh->char_height);
814 	cairo_fill (cr);
815 
816 	imax = MIN(imax, gh->vis_lines);
817 	imax = MIN(imax, gh->lines - gh->top_line - 1);
818 
819 	gdk_cairo_set_source_rgba (cr, &fg_color);
820 
821 	for(i = imin; i <= imax; i++) {
822 		sprintf(offstr, "%08X", (gh->top_line + i)*gh->cpl + gh->starting_offset);
823 		cairo_move_to (cr, 0, i * gh->char_height);
824 		pango_layout_set_text (gh->olayout, offstr, 8);
825 		pango_cairo_show_layout (cr, gh->olayout);
826 	}
827 }
828 
829 /*
830  * draw signal handlers for both displays
831  */
832 static void
hex_draw(GtkWidget * w,cairo_t * cr,GtkHex * gh)833 hex_draw (GtkWidget *w,
834           cairo_t *cr,
835           GtkHex *gh)
836 {
837 	GdkRectangle rect;
838 	gint imin, imax;
839 
840 	gdk_cairo_get_clip_rectangle (cr, &rect);
841 
842 	imin = (rect.y) / gh->char_height;
843 	imax = (rect.y + rect.height) / gh->char_height;
844 	if ((rect.y + rect.height) % gh->char_height)
845 		imax++;
846 
847 	imax = MIN(imax, gh->vis_lines);
848 
849 	render_hex_lines (gh, cr, imin, imax);
850 }
851 
852 static void
ascii_draw(GtkWidget * w,cairo_t * cr,GtkHex * gh)853 ascii_draw (GtkWidget *w,
854             cairo_t *cr,
855             GtkHex *gh)
856 {
857 	GdkRectangle rect;
858 	gint imin, imax;
859 
860 	gdk_cairo_get_clip_rectangle (cr, &rect);
861 
862 	imin = (rect.y) / gh->char_height;
863 	imax = (rect.y + rect.height) / gh->char_height;
864 	if ((rect.y + rect.height) % gh->char_height)
865 		imax++;
866 
867 	imax = MIN(imax, gh->vis_lines);
868 
869 	render_ascii_lines (gh, cr, imin, imax);
870 }
871 
872 static void
offsets_draw(GtkWidget * w,cairo_t * cr,GtkHex * gh)873 offsets_draw (GtkWidget *w,
874               cairo_t *cr,
875               GtkHex *gh)
876 {
877 	GdkRectangle rect;
878 	gint imin, imax;
879 
880 	gdk_cairo_get_clip_rectangle (cr, &rect);
881 
882 	imin = (rect.y) / gh->char_height;
883 	imax = (rect.y + rect.height) / gh->char_height;
884 	if ((rect.y + rect.height) % gh->char_height)
885 		imax++;
886 
887 	imax = MIN(imax, gh->vis_lines);
888 
889 	render_offsets (gh, cr, imin, imax);
890 }
891 
892 /*
893  * draw signal handler for the GtkHex itself: draws shadows around both displays
894  */
895 static void
draw_shadow(GtkWidget * widget,cairo_t * cr)896 draw_shadow (GtkWidget *widget,
897              cairo_t *cr)
898 {
899 	GtkHex *gh = GTK_HEX(widget);
900 	GtkRequisition sb_req;
901 	GtkAllocation allocation;
902 	GtkBorder padding;
903 	GtkStateFlags state;
904 	GtkStyleContext *context;
905 	gint border = gtk_container_get_border_width(GTK_CONTAINER(widget));
906 	gint x;
907 
908 	context = gtk_widget_get_style_context (widget);
909 	state = gtk_widget_get_state_flags (widget);
910 	gtk_style_context_get_padding (context, state, &padding);
911 
912 	x = border;
913 	gtk_widget_get_allocation(widget, &allocation);
914 	if(gh->show_offsets) {
915 		gtk_render_frame (context,
916 		                  cr,
917 		                  border,
918 		                  border,
919 		                  9 * gh->char_width + padding.left + padding.right,
920 		                  allocation.height - 2 * border);
921 		x += 9 * gh->char_width + padding.left + padding.right + gh->extra_width/2;
922 	}
923 
924 	gtk_render_frame (context,
925 	                  cr,
926 	                  x,
927 	                  border,
928 	                  gh->xdisp_width + padding.left + padding.right,
929 	                  allocation.height - 2 * border);
930 
931 	/* Draw a frame around the ascii display + scrollbar */
932 	gtk_widget_get_requisition(gh->scrollbar, &sb_req);
933 	gtk_render_frame (context,
934 	                  cr,
935 	                  allocation.width - border - gh->adisp_width - sb_req.width - padding.left - padding.right,
936 	                  border,
937 	                  gh->adisp_width + sb_req.width + padding.left + padding.right,
938 	                  allocation.height - 2 * border);
939 }
940 
941 /*
942  * this calculates how many bytes we can stuff into one line and how many
943  * lines we can display according to the current size of the widget
944  */
recalc_displays(GtkHex * gh,guint width,guint height)945 static void recalc_displays(GtkHex *gh, guint width, guint height) {
946 	gboolean scroll_to_cursor;
947 	gdouble value;
948 	gint total_width = width;
949 	gint total_cpl, xcpl;
950 	gint old_cpl = gh->cpl;
951 	GtkBorder padding;
952 	GtkStateFlags state;
953 	GtkStyleContext *context;
954 	GtkRequisition req;
955 
956 	context = gtk_widget_get_style_context (GTK_WIDGET (gh));
957 	state = gtk_widget_get_state_flags (GTK_WIDGET (gh));
958 	gtk_style_context_get_padding (context, state, &padding);
959 
960 	/*
961 	 * Only change the value of the adjustment to put the cursor on screen
962 	 * if the cursor is currently within the displayed portion.
963 	 */
964 	scroll_to_cursor = (gh->cpl == 0) ||
965 	                   ((gh->cursor_pos / gh->cpl >= gtk_adjustment_get_value (gh->adj)) &&
966 	                    (gh->cursor_pos / gh->cpl <= gtk_adjustment_get_value (gh->adj) + gh->vis_lines - 1));
967 
968 	gtk_widget_get_preferred_size (gh->scrollbar, &req, NULL);
969 
970 	gh->xdisp_width = 1;
971 	gh->adisp_width = 1;
972 
973 	total_width -= 2*gtk_container_get_border_width(GTK_CONTAINER(gh)) +
974 	               2 * padding.left + 2 * padding.right + req.width;
975 
976 	if(gh->show_offsets)
977 		total_width -= padding.left + padding.right + 9 * gh->char_width;
978 
979 	total_cpl = total_width / gh->char_width;
980 
981 	if((total_cpl == 0) || (total_width < 0)) {
982 		gh->cpl = gh->lines = gh->vis_lines = 0;
983 		return;
984 	}
985 
986 	/* calculate how many bytes we can stuff in one line */
987 	gh->cpl = 0;
988 	do {
989 		if(gh->cpl % gh->group_type == 0 && total_cpl < gh->group_type*3)
990 			break;
991 
992 		gh->cpl++;        /* just added one more char */
993 		total_cpl -= 3;   /* 2 for xdisp, 1 for adisp */
994 
995 		if(gh->cpl % gh->group_type == 0) /* just ended a group */
996 			total_cpl--;
997 	} while(total_cpl > 0);
998 
999 	if(gh->cpl == 0)
1000 		return;
1001 
1002 	if(gh->document->file_size == 0)
1003 		gh->lines = 1;
1004 	else {
1005 		gh->lines = gh->document->file_size / gh->cpl;
1006 		if(gh->document->file_size % gh->cpl)
1007 			gh->lines++;
1008 	}
1009 
1010 	gh->vis_lines = ((gint) (height - 2 * gtk_container_get_border_width (GTK_CONTAINER (gh)) - padding.top - padding.bottom)) / ((gint) gh->char_height);
1011 
1012 	gh->adisp_width = gh->cpl*gh->char_width;
1013 	xcpl = gh->cpl*2 + (gh->cpl - 1)/gh->group_type;
1014 	gh->xdisp_width = xcpl*gh->char_width;
1015 
1016 	gh->extra_width = total_width - gh->xdisp_width - gh->adisp_width;
1017 
1018 	if (gh->priv->disp_buffer)
1019 		g_free (gh->priv->disp_buffer);
1020 
1021 	gh->priv->disp_buffer = g_malloc ((xcpl + 1) * (gh->vis_lines + 1));
1022 
1023 	/* calculate new display position */
1024 	value = MIN (gh->top_line * old_cpl / gh->cpl, gh->lines - gh->vis_lines);
1025 	value = MAX (0, value);
1026 
1027 	/* keep cursor on screen if it was on screen before */
1028 	if (scroll_to_cursor &&
1029 	    ((gh->cursor_pos / gh->cpl < value) ||
1030 	     (gh->cursor_pos / gh->cpl > value + gh->vis_lines - 1))) {
1031 		value = MIN (gh->cursor_pos / gh->cpl, gh->lines - gh->vis_lines);
1032 		value = MAX (0, value);
1033 	}
1034 
1035 	/* adjust the scrollbar and display position to new values */
1036 	gtk_adjustment_configure (gh->adj,
1037 	                          value,             /* value */
1038 	                          0,                 /* lower */
1039 	                          gh->lines,         /* upper */
1040 	                          1,                 /* step increment */
1041 	                          gh->vis_lines - 1, /* page increment */
1042 	                          gh->vis_lines      /* page size */);
1043 
1044 	g_signal_emit_by_name(G_OBJECT(gh->adj), "changed");
1045 	g_signal_emit_by_name(G_OBJECT(gh->adj), "value_changed");
1046 }
1047 
1048 /*
1049  * takes care of xdisp and adisp scrolling
1050  * connected to value_changed signal of scrollbar's GtkAdjustment
1051  */
display_scrolled(GtkAdjustment * adj,GtkHex * gh)1052 static void display_scrolled(GtkAdjustment *adj, GtkHex *gh) {
1053 	gint dx;
1054 	gint dy;
1055 
1056 	if ((!gtk_widget_is_drawable(gh->xdisp)) ||
1057 	    (!gtk_widget_is_drawable(gh->adisp)))
1058 		return;
1059 
1060 	dx = 0;
1061 	dy = (gh->top_line - (gint)gtk_adjustment_get_value (adj)) * gh->char_height;
1062 
1063 	gh->top_line = (gint)gtk_adjustment_get_value(adj);
1064 
1065 	gdk_window_scroll (gtk_widget_get_window (gh->xdisp), dx, dy);
1066 	gdk_window_scroll (gtk_widget_get_window (gh->adisp), dx, dy);
1067 	if (gh->offsets)
1068 		gdk_window_scroll (gtk_widget_get_window (gh->offsets), dx, dy);
1069 
1070 	gtk_hex_update_all_auto_highlights(gh, TRUE, TRUE);
1071 	gtk_hex_invalidate_all_highlights(gh);
1072 }
1073 
1074 /*
1075  * mouse signal handlers (button 1 and motion) for both displays
1076  */
scroll_timeout_handler(GtkHex * gh)1077 static gboolean scroll_timeout_handler(GtkHex *gh) {
1078 	if(gh->scroll_dir < 0)
1079 		gtk_hex_set_cursor(gh, MAX(0, (int)(gh->cursor_pos - gh->cpl)));
1080 	else if(gh->scroll_dir > 0)
1081 		gtk_hex_set_cursor(gh, MIN(gh->document->file_size - 1,
1082 								   gh->cursor_pos + gh->cpl));
1083 	return TRUE;
1084 }
1085 
hex_scroll_cb(GtkWidget * w,GdkEventScroll * event,GtkHex * gh)1086 static void hex_scroll_cb(GtkWidget *w, GdkEventScroll *event, GtkHex *gh) {
1087 	gtk_widget_event(gh->scrollbar, (GdkEvent *)event);
1088 }
1089 
hex_button_cb(GtkWidget * w,GdkEventButton * event,GtkHex * gh)1090 static void hex_button_cb(GtkWidget *w, GdkEventButton *event, GtkHex *gh) {
1091 	if( (event->type == GDK_BUTTON_RELEASE) &&
1092 		(event->button == GDK_BUTTON_PRIMARY) ) {
1093 		if(gh->scroll_timeout != -1) {
1094 			g_source_remove(gh->scroll_timeout);
1095 			gh->scroll_timeout = -1;
1096 			gh->scroll_dir = 0;
1097 		}
1098 		gh->selecting = FALSE;
1099 		gtk_grab_remove(w);
1100 		gh->button = 0;
1101 	}
1102 	else if((event->type == GDK_BUTTON_PRESS) && (event->button == GDK_BUTTON_PRIMARY)) {
1103 		if (!gtk_widget_has_focus (GTK_WIDGET (gh)))
1104 			gtk_widget_grab_focus (GTK_WIDGET(gh));
1105 
1106 		gtk_grab_add(w);
1107 
1108 		gh->button = event->button;
1109 
1110 		if(gh->active_view == VIEW_HEX) {
1111 			hex_to_pointer(gh, event->x, event->y);
1112 
1113 			if(!gh->selecting) {
1114 				gh->selecting = TRUE;
1115 				gtk_hex_set_selection(gh, gh->cursor_pos, gh->cursor_pos);
1116 			}
1117 		}
1118 		else {
1119 			hide_cursor(gh);
1120 			gh->active_view = VIEW_HEX;
1121 			show_cursor(gh);
1122 			hex_button_cb(w, event, gh);
1123 		}
1124 	}
1125 	else if((event->type == GDK_BUTTON_PRESS) && (event->button == GDK_BUTTON_MIDDLE)) {
1126 		GtkHexClass *klass = GTK_HEX_CLASS(GTK_WIDGET_GET_CLASS(gh));
1127 		gchar *text;
1128 
1129 		gh->active_view = VIEW_HEX;
1130 		hex_to_pointer(gh, event->x, event->y);
1131 
1132 		text = gtk_clipboard_wait_for_text(klass->primary);
1133 		if(text) {
1134 			hex_document_set_data(gh->document, gh->cursor_pos,
1135 								  strlen(text), 0, text, TRUE);
1136 			gtk_hex_set_cursor(gh, gh->cursor_pos + strlen(text));
1137 			g_free(text);
1138 		}
1139 		gh->button = 0;
1140 	}
1141 	else
1142 		gh->button = 0;
1143 }
1144 
hex_motion_cb(GtkWidget * w,GdkEventMotion * event,GtkHex * gh)1145 static void hex_motion_cb(GtkWidget *w, GdkEventMotion *event, GtkHex *gh) {
1146 	GtkAllocation allocation;
1147 	GdkDeviceManager *device_manager;
1148 	GdkDevice *pointer;
1149 	gint x, y;
1150 
1151 	gtk_widget_get_allocation(w, &allocation);
1152 
1153 	device_manager = gdk_display_get_device_manager (gtk_widget_get_display (w));
1154 	pointer = gdk_device_manager_get_client_pointer (device_manager);
1155 	gdk_window_get_device_position (gtk_widget_get_window (w), pointer, &x, &y, NULL);
1156 
1157 	if(y < 0)
1158 		gh->scroll_dir = -1;
1159 	else if(y >= allocation.height)
1160 		gh->scroll_dir = 1;
1161 	else
1162 		gh->scroll_dir = 0;
1163 
1164 	if(gh->scroll_dir != 0) {
1165 		if(gh->scroll_timeout == -1)
1166 			gh->scroll_timeout =
1167 				g_timeout_add(SCROLL_TIMEOUT,
1168 							  (GSourceFunc)scroll_timeout_handler, gh);
1169 		return;
1170 	}
1171 	else {
1172 		if(gh->scroll_timeout != -1) {
1173 			g_source_remove(gh->scroll_timeout);
1174 			gh->scroll_timeout = -1;
1175 		}
1176 	}
1177 
1178 	if(event->window != gtk_widget_get_window(w))
1179 		return;
1180 
1181 	if((gh->active_view == VIEW_HEX) && (gh->button == 1)) {
1182 		hex_to_pointer(gh, x, y);
1183 	}
1184 }
1185 
ascii_scroll_cb(GtkWidget * w,GdkEventScroll * event,GtkHex * gh)1186 static void ascii_scroll_cb(GtkWidget *w, GdkEventScroll *event, GtkHex *gh) {
1187 	gtk_widget_event(gh->scrollbar, (GdkEvent *)event);
1188 }
1189 
ascii_button_cb(GtkWidget * w,GdkEventButton * event,GtkHex * gh)1190 static void ascii_button_cb(GtkWidget *w, GdkEventButton *event, GtkHex *gh) {
1191 	if( (event->type == GDK_BUTTON_RELEASE) &&
1192 		(event->button == GDK_BUTTON_PRIMARY) ) {
1193 		if(gh->scroll_timeout != -1) {
1194 			g_source_remove(gh->scroll_timeout);
1195 			gh->scroll_timeout = -1;
1196 			gh->scroll_dir = 0;
1197 		}
1198 		gh->selecting = FALSE;
1199 		gtk_grab_remove(w);
1200 		gh->button = 0;
1201 	}
1202 	else if( (event->type == GDK_BUTTON_PRESS) && (event->button == GDK_BUTTON_PRIMARY) ) {
1203 		if (!gtk_widget_has_focus (GTK_WIDGET (gh)))
1204 			gtk_widget_grab_focus (GTK_WIDGET(gh));
1205 
1206 		gtk_grab_add(w);
1207 		gh->button = event->button;
1208 		if(gh->active_view == VIEW_ASCII) {
1209 			ascii_to_pointer(gh, event->x, event->y);
1210 			if(!gh->selecting) {
1211 				gh->selecting = TRUE;
1212 				gtk_hex_set_selection(gh, gh->cursor_pos, gh->cursor_pos);
1213 			}
1214 		}
1215 		else {
1216 			hide_cursor(gh);
1217 			gh->active_view = VIEW_ASCII;
1218 			show_cursor(gh);
1219 			ascii_button_cb(w, event, gh);
1220 		}
1221 	}
1222 	else if((event->type == GDK_BUTTON_PRESS) && (event->button == GDK_BUTTON_MIDDLE)) {
1223 		GtkHexClass *klass = GTK_HEX_CLASS(GTK_WIDGET_GET_CLASS(gh));
1224 		gchar *text;
1225 
1226 		gh->active_view = VIEW_ASCII;
1227 		ascii_to_pointer(gh, event->x, event->y);
1228 
1229 		text = gtk_clipboard_wait_for_text(klass->primary);
1230 		if(text) {
1231 			hex_document_set_data(gh->document, gh->cursor_pos,
1232 								  strlen(text), 0, text, TRUE);
1233 			gtk_hex_set_cursor(gh, gh->cursor_pos + strlen(text));
1234 			g_free(text);
1235 		}
1236 		gh->button = 0;
1237 	}
1238 	else
1239 		gh->button = 0;
1240 }
1241 
ascii_motion_cb(GtkWidget * w,GdkEventMotion * event,GtkHex * gh)1242 static void ascii_motion_cb(GtkWidget *w, GdkEventMotion *event, GtkHex *gh) {
1243 	GtkAllocation allocation;
1244 	GdkDeviceManager *device_manager;
1245 	GdkDevice *pointer;
1246 	gint x, y;
1247 
1248 	gtk_widget_get_allocation(w, &allocation);
1249 
1250 	device_manager = gdk_display_get_device_manager (gtk_widget_get_display (w));
1251 	pointer = gdk_device_manager_get_client_pointer (device_manager);
1252 	gdk_window_get_device_position (gtk_widget_get_window (w), pointer, &x, &y, NULL);
1253 
1254 	if(y < 0)
1255 		gh->scroll_dir = -1;
1256 	else if(y >= allocation.height)
1257 		gh->scroll_dir = 1;
1258 	else
1259 		gh->scroll_dir = 0;
1260 
1261 	if(gh->scroll_dir != 0) {
1262 		if(gh->scroll_timeout == -1)
1263 			gh->scroll_timeout =
1264 				g_timeout_add(SCROLL_TIMEOUT,
1265 							  (GSourceFunc)scroll_timeout_handler, gh);
1266 		return;
1267 	}
1268 	else {
1269 		if(gh->scroll_timeout != -1) {
1270 			g_source_remove(gh->scroll_timeout);
1271 			gh->scroll_timeout = -1;
1272 		}
1273 	}
1274 
1275 	if(event->window != gtk_widget_get_window(w))
1276 		return;
1277 
1278 	if((gh->active_view == VIEW_ASCII) && (gh->button == 1)) {
1279 		ascii_to_pointer(gh, x, y);
1280 	}
1281 }
1282 
show_offsets_widget(GtkHex * gh)1283 static void show_offsets_widget(GtkHex *gh) {
1284 	GtkStyleContext *context;
1285 
1286 	gh->offsets = gtk_drawing_area_new();
1287 
1288 	/* Modify the font for the widget */
1289 	gtk_widget_modify_font (gh->offsets, gh->font_desc);
1290 
1291 	/* Create the pango layout for the widget */
1292 	gh->olayout = gtk_widget_create_pango_layout (gh->offsets, "");
1293 
1294 	gtk_widget_set_events (gh->offsets, GDK_EXPOSURE_MASK);
1295 	g_signal_connect (G_OBJECT(gh->offsets), "draw",
1296 	                  G_CALLBACK (offsets_draw), gh);
1297 
1298 	context = gtk_widget_get_style_context (GTK_WIDGET (gh->xdisp));
1299 	gtk_style_context_add_class (context, GTK_STYLE_CLASS_HEADER);
1300 
1301 	gtk_fixed_put(GTK_FIXED(gh), gh->offsets, 0, 0);
1302 	gtk_widget_show(gh->offsets);
1303 }
1304 
hide_offsets_widget(GtkHex * gh)1305 static void hide_offsets_widget(GtkHex *gh) {
1306 	if(gh->offsets) {
1307 		gtk_container_remove(GTK_CONTAINER(gh), gh->offsets);
1308 		gh->offsets = NULL;
1309 	}
1310 }
1311 
1312 /*
1313  * default data_changed signal handler
1314  */
gtk_hex_real_data_changed(GtkHex * gh,gpointer data)1315 static void gtk_hex_real_data_changed(GtkHex *gh, gpointer data) {
1316 	HexChangeData *change_data = (HexChangeData *)data;
1317 	gint start_line, end_line;
1318 	guint lines;
1319 
1320 	if(gh->cpl == 0)
1321 		return;
1322 
1323 	if(change_data->start - change_data->end + 1 != change_data->rep_len) {
1324 		lines = gh->document->file_size / gh->cpl;
1325 		if(gh->document->file_size % gh->cpl)
1326 			lines++;
1327 		if(lines != gh->lines) {
1328 			gh->lines = lines;
1329 			gtk_adjustment_set_value(gh->adj, MIN(gtk_adjustment_get_value(gh->adj), gh->lines - gh->vis_lines));
1330 			gtk_adjustment_set_value(gh->adj, MAX(0, gtk_adjustment_get_value(gh->adj)));
1331 			if((gh->cursor_pos/gh->cpl < gtk_adjustment_get_value(gh->adj)) ||
1332 			   (gh->cursor_pos/gh->cpl > gtk_adjustment_get_value(gh->adj) + gh->vis_lines - 1)) {
1333 				gtk_adjustment_set_value(gh->adj, MIN(gh->cursor_pos/gh->cpl, gh->lines - gh->vis_lines));
1334 				gtk_adjustment_set_value(gh->adj, MAX(0, gtk_adjustment_get_value(gh->adj)));
1335 			}
1336 			gtk_adjustment_set_lower(gh->adj, 0);
1337 			gtk_adjustment_set_upper(gh->adj, gh->lines);
1338 			gtk_adjustment_set_step_increment(gh->adj, 1);
1339 			gtk_adjustment_set_page_increment(gh->adj, gh->vis_lines - 1);
1340 			gtk_adjustment_set_page_size(gh->adj, gh->vis_lines);
1341 			g_signal_emit_by_name(G_OBJECT(gh->adj), "changed");
1342 			g_signal_emit_by_name(G_OBJECT(gh->adj), "value_changed");
1343 		}
1344 	}
1345 
1346 	start_line = change_data->start/gh->cpl - gh->top_line;
1347 	end_line = change_data->end/gh->cpl - gh->top_line;
1348 
1349 	if(end_line < 0 ||
1350 	   start_line > gh->vis_lines)
1351 		return;
1352 
1353 	start_line = MAX(start_line, 0);
1354 	if(change_data->rep_len - change_data->end + change_data->start - 1 != 0)
1355 		end_line = gh->vis_lines;
1356 	else
1357 		end_line = MIN(end_line, gh->vis_lines);
1358 
1359     invalidate_hex_lines (gh, start_line, end_line);
1360     invalidate_ascii_lines (gh, start_line, end_line);
1361     if (gh->show_offsets)
1362     {
1363         invalidate_offsets (gh, start_line, end_line);
1364     }
1365 }
1366 
bytes_changed(GtkHex * gh,gint start,gint end)1367 static void bytes_changed(GtkHex *gh, gint start, gint end)
1368 {
1369 	gint start_line = start/gh->cpl - gh->top_line;
1370 	gint end_line = end/gh->cpl - gh->top_line;
1371 
1372 	if(end_line < 0 ||
1373 	   start_line > gh->vis_lines)
1374 		return;
1375 
1376 	start_line = MAX(start_line, 0);
1377 
1378     invalidate_hex_lines (gh, start_line, end_line);
1379     invalidate_ascii_lines (gh, start_line, end_line);
1380     if (gh->show_offsets)
1381     {
1382         invalidate_offsets (gh, start_line, end_line);
1383     }
1384 }
1385 
1386 static void
clipboard_get_cb(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer user_data)1387 clipboard_get_cb (GtkClipboard *clipboard,
1388 		GtkSelectionData *selection_data,
1389 		guint info,
1390 		gpointer user_data)
1391 {
1392 	GtkHex *gh = GTK_HEX(user_data);
1393 
1394 	switch (info)
1395 	{
1396 		case TARGET_HEXDATA:
1397 			if (gh->priv->clip_buf)
1398 			{
1399 				gtk_selection_data_set (selection_data,
1400 						gdk_atom_intern ("HEXDATA", FALSE),
1401 						8,	/* ie, 8-bit characters */
1402 						gh->priv->clip_buf,
1403 						gh->priv->clip_buf_len);
1404 			}
1405 			break;
1406 
1407 		case TARGET_STRING:
1408 			if (gh->priv->clip_buf)
1409 			{
1410 				gtk_selection_data_set (selection_data,
1411 						GDK_SELECTION_TYPE_STRING,
1412 						8,	/* ie, 8-bit characters */
1413 						gh->priv->clip_buf,
1414 						gh->priv->clip_buf_len);
1415 			}
1416 			break;
1417 
1418 		default:
1419 			g_critical ("Invalid clipboard data.");
1420 			return;
1421 	}
1422 
1423 }
1424 
primary_get_cb(GtkClipboard * clipboard,GtkSelectionData * data,guint info,gpointer user_data)1425 static void primary_get_cb(GtkClipboard *clipboard,
1426 						   GtkSelectionData *data, guint info,
1427 						   gpointer user_data) {
1428 	GtkHex *gh = GTK_HEX(user_data);
1429 	if(gh->selection.start != gh->selection.end) {
1430 		gint start_pos;
1431 		gint end_pos;
1432 		guchar *text;
1433 
1434 		start_pos = MIN(gh->selection.start, gh->selection.end);
1435 		end_pos = MAX(gh->selection.start, gh->selection.end);
1436 
1437 		text = hex_document_get_data(gh->document, start_pos,
1438 									 end_pos - start_pos);
1439 		gtk_selection_data_set_text(data, text, end_pos - start_pos);
1440 		g_free(text);
1441 	}
1442 }
1443 
gtk_hex_set_selection(GtkHex * gh,gint start,gint end)1444 void gtk_hex_set_selection(GtkHex *gh, gint start, gint end)
1445 {
1446 	gint length = gh->document->file_size;
1447 	gint oe, os, ne, ns;
1448 	GtkHexClass *klass = GTK_HEX_CLASS(GTK_WIDGET_GET_CLASS(gh));
1449 
1450 	if (end < 0)
1451 		end = length;
1452 
1453 	if (gh->selection.start != gh->selection.end)
1454 		gtk_clipboard_clear(klass->primary);
1455 
1456 	os = MIN(gh->selection.start, gh->selection.end);
1457 	oe = MAX(gh->selection.start, gh->selection.end);
1458 
1459 	gh->selection.start = CLAMP(start, 0, length);
1460 	gh->selection.end = MIN(end, length);
1461 
1462 	gtk_hex_invalidate_highlight(gh, &gh->selection);
1463 
1464 	ns = MIN(gh->selection.start, gh->selection.end);
1465 	ne = MAX(gh->selection.start, gh->selection.end);
1466 
1467 	if(ns != os && ne != oe) {
1468 		bytes_changed(gh, MIN(ns, os), MAX(ne, oe));
1469 	}
1470 	else if(ne != oe) {
1471 		bytes_changed(gh, MIN(ne, oe), MAX(ne, oe));
1472 	}
1473 	else if(ns != os) {
1474 		bytes_changed(gh, MIN(ns, os), MAX(ns, os));
1475 	}
1476 
1477 	if(gh->selection.start != gh->selection.end)
1478 		gtk_clipboard_set_with_data(klass->primary, clipboard_targets, N_TARGETS,
1479 									primary_get_cb, NULL,
1480 									gh);
1481 }
1482 
gtk_hex_get_selection(GtkHex * gh,gint * start,gint * end)1483 gboolean gtk_hex_get_selection(GtkHex *gh, gint *start, gint *end)
1484 {
1485 	gint ss, se;
1486 
1487 	if(gh->selection.start > gh->selection.end) {
1488 		se = gh->selection.start;
1489 		ss = gh->selection.end;
1490 	}
1491 	else {
1492 		ss = gh->selection.start;
1493 		se = gh->selection.end;
1494 	}
1495 
1496 	if(NULL != start)
1497 		*start = ss;
1498 	if(NULL != end)
1499 		*end = se;
1500 
1501 	return !(ss == se);
1502 }
1503 
gtk_hex_clear_selection(GtkHex * gh)1504 void gtk_hex_clear_selection(GtkHex *gh)
1505 {
1506 	gtk_hex_set_selection(gh, 0, 0);
1507 }
1508 
gtk_hex_delete_selection(GtkHex * gh)1509 void gtk_hex_delete_selection(GtkHex *gh)
1510 {
1511 	guint start, end, len;
1512 
1513 	start = MIN(gh->selection.start, gh->selection.end);
1514 	end = MAX(gh->selection.start, gh->selection.end);
1515 
1516 	len = end - start + 1;
1517 	g_assert (len);
1518 
1519 	gtk_hex_set_selection(gh, 0, 0);
1520 
1521 	hex_document_delete_data(gh->document, MIN(start, end), len, TRUE);
1522 	gtk_hex_set_cursor(gh, start);
1523 }
1524 
gtk_hex_validate_highlight(GtkHex * gh,GtkHex_Highlight * hl)1525 static void gtk_hex_validate_highlight(GtkHex *gh, GtkHex_Highlight *hl)
1526 {
1527 	if (!hl->valid)
1528 	{
1529 		hl->start_line = MIN(hl->start, hl->end) / gh->cpl - gh->top_line;
1530 		hl->end_line = MAX(hl->start, hl->end) / gh->cpl - gh->top_line;
1531 		hl->valid = TRUE;
1532 	}
1533 }
1534 
gtk_hex_invalidate_highlight(GtkHex * gh,GtkHex_Highlight * hl)1535 static void gtk_hex_invalidate_highlight(GtkHex *gh, GtkHex_Highlight *hl)
1536 {
1537 	hl->valid = FALSE;
1538 }
1539 
gtk_hex_invalidate_all_highlights(GtkHex * gh)1540 static void gtk_hex_invalidate_all_highlights(GtkHex *gh)
1541 {
1542 	GtkHex_Highlight *cur = &gh->selection;
1543 	GtkHex_AutoHighlight *nextList = gh->auto_highlight;
1544 	while (cur)
1545 	{
1546 		gtk_hex_invalidate_highlight(gh, cur);
1547 		cur = cur->next;
1548 		while (cur == NULL && nextList)
1549 		{
1550 			cur = nextList->highlights;
1551 			nextList = nextList->next;
1552 		}
1553 	}
1554 }
1555 
gtk_hex_insert_highlight(GtkHex * gh,GtkHex_AutoHighlight * ahl,gint start,gint end)1556 static GtkHex_Highlight *gtk_hex_insert_highlight (GtkHex *gh,
1557 												   GtkHex_AutoHighlight *ahl,
1558 												   gint start, gint end)
1559 {
1560 	GdkRGBA rgba;
1561 	gint length = gh->document->file_size;
1562 
1563 	GtkHex_Highlight *new = g_malloc0(sizeof(GtkHex_Highlight));
1564 
1565 	new->start = CLAMP(MIN(start, end), 0, length);
1566 	new->end = MIN(MAX(start, end), length);
1567 
1568 	new->valid = FALSE;
1569 
1570 	new->min_select = 0;
1571 
1572 	if (gdk_rgba_parse (&rgba, ahl->colour))
1573 		new->bg_color = gdk_rgba_copy (&rgba);
1574 	else
1575 		new->bg_color = NULL;
1576 
1577 
1578 	new->prev = NULL;
1579 	new->next = ahl->highlights;
1580 	if (new->next) new->next->prev = new;
1581 	ahl->highlights = new;
1582 
1583 	bytes_changed(gh, new->start, new->end);
1584 
1585 	return new;
1586 }
1587 
gtk_hex_delete_highlight(GtkHex * gh,GtkHex_AutoHighlight * ahl,GtkHex_Highlight * hl)1588 static void gtk_hex_delete_highlight (GtkHex *gh, GtkHex_AutoHighlight *ahl,
1589 									  GtkHex_Highlight *hl)
1590 {
1591 	int start, end;
1592 	start = hl->start;
1593 	end = hl->end;
1594 	if (hl->prev) hl->prev->next = hl->next;
1595 	if (hl->next) hl->next->prev = hl->prev;
1596 
1597 	if (hl == ahl->highlights) ahl->highlights = hl->next;
1598 
1599 	if (hl->bg_color)
1600 		gdk_rgba_free (hl->bg_color);
1601 
1602 	g_free(hl);
1603 	bytes_changed(gh, start, end);
1604 }
1605 
1606 /* stolen from hex_document_compare_data - but uses
1607  * gtk_hex_* stuff rather than hex_document_* directly
1608  * and simply returns a gboolean.
1609  */
gtk_hex_compare_data(GtkHex * gh,guchar * cmp,guint pos,gint len)1610 static gboolean gtk_hex_compare_data(GtkHex *gh, guchar *cmp, guint pos, gint len)
1611 {
1612 	int i;
1613 	for (i = 0; i < len; i++)
1614 	{
1615 		guchar c = gtk_hex_get_byte(gh, pos + i);
1616 		if (c != *(cmp + i))
1617 			return FALSE;
1618 	}
1619 	return TRUE;
1620 }
1621 
gtk_hex_find_limited(GtkHex * gh,gchar * find,int findlen,guint lower,guint upper,guint * found)1622 static gboolean gtk_hex_find_limited(GtkHex *gh, gchar *find, int findlen,
1623 									 guint lower, guint upper,
1624 									 guint *found)
1625 {
1626 	guint pos = lower;
1627 	while (pos < upper)
1628 	{
1629 		if (gtk_hex_compare_data(gh, (guchar *)find, pos, findlen))
1630 		{
1631 			*found = pos;
1632 			return TRUE;
1633 		}
1634 		pos++;
1635 	}
1636 	return FALSE;
1637 }
1638 
1639 /* removes any highlights that arn't visible
1640  * adds any new highlights that became visible
1641  */
gtk_hex_update_auto_highlight(GtkHex * gh,GtkHex_AutoHighlight * ahl,gboolean delete,gboolean add)1642 static void gtk_hex_update_auto_highlight(GtkHex *gh, GtkHex_AutoHighlight *ahl,
1643 										  gboolean delete, gboolean add)
1644 {
1645 	gint del_min, del_max;
1646 	gint add_min, add_max;
1647 	guint foundpos = -1;
1648 	gint prev_min = ahl->view_min;
1649 	gint prev_max = ahl->view_max;
1650 	GtkHex_Highlight *cur;
1651 
1652 	ahl->view_min = gh->top_line * gh->cpl;
1653 	ahl->view_max = (gh->top_line + gh->vis_lines) * gh->cpl;
1654 
1655 	if (prev_min < ahl->view_min && prev_max < ahl->view_max)
1656 	{
1657 		del_min = prev_min - ahl->search_len;
1658 		del_max = ahl->view_min - ahl->search_len;
1659 		add_min = prev_max;
1660 		add_max = ahl->view_max;
1661 	}
1662 	else if (prev_min > ahl->view_min && prev_max > ahl->view_max)
1663 	{
1664 		add_min = ahl->view_min - ahl->search_len;
1665 		add_max = prev_min - ahl->search_len;
1666 		del_min = ahl->view_max;
1667 		del_max = prev_max;
1668 	}
1669 	else /* just refresh the lot */
1670 	{
1671 		del_min = 0;
1672 		del_max = gh->cpl * gh->lines;
1673 		add_min = ahl->view_min;
1674 		add_max = ahl->view_max;
1675 	}
1676 
1677 	add_min = MAX(add_min, 0);
1678 	del_min = MAX(del_min, 0);
1679 
1680 	cur = ahl->highlights;
1681 	while (delete && cur)
1682 	{
1683 		if (cur->start >= del_min && cur->start <= del_max)
1684 		{
1685 			GtkHex_Highlight *next = cur->next;
1686 			gtk_hex_delete_highlight(gh, ahl, cur);
1687 			cur = next;
1688 		}
1689 		else cur = cur->next;
1690 	}
1691 	while (add &&
1692 		   gtk_hex_find_limited(gh, ahl->search_string, ahl->search_len,
1693 								MAX(add_min, foundpos+1), add_max, &foundpos))
1694 	{
1695 		gtk_hex_insert_highlight(gh, ahl, foundpos, foundpos+(ahl->search_len)-1);
1696 	}
1697 }
1698 
gtk_hex_update_all_auto_highlights(GtkHex * gh,gboolean delete,gboolean add)1699 static void gtk_hex_update_all_auto_highlights(GtkHex *gh, gboolean delete, gboolean add)
1700 {
1701 	GtkHex_AutoHighlight *cur = gh->auto_highlight;
1702 	while (cur)
1703 	{
1704 		gtk_hex_update_auto_highlight(gh, cur, delete, add);
1705 		cur = cur->next;
1706 	}
1707 }
1708 
gtk_hex_copy_to_clipboard(GtkHex * gh)1709 void gtk_hex_copy_to_clipboard(GtkHex *gh)
1710 {
1711 	g_signal_emit_by_name(G_OBJECT(gh), "copy_clipboard");
1712 }
1713 
gtk_hex_cut_to_clipboard(GtkHex * gh)1714 void gtk_hex_cut_to_clipboard(GtkHex *gh)
1715 {
1716 	g_signal_emit_by_name(G_OBJECT(gh), "cut_clipboard");
1717 }
1718 
gtk_hex_paste_from_clipboard(GtkHex * gh)1719 void gtk_hex_paste_from_clipboard(GtkHex *gh)
1720 {
1721 	g_signal_emit_by_name(G_OBJECT(gh), "paste_clipboard");
1722 }
1723 
gtk_hex_real_copy_to_clipboard(GtkHex * gh)1724 static void gtk_hex_real_copy_to_clipboard(GtkHex *gh)
1725 {
1726 	GtkHexClass *klass = GTK_HEX_CLASS(GTK_WIDGET_GET_CLASS(gh));
1727 
1728 	gint start_pos, end_pos, len;
1729 
1730 	start_pos = MIN(gh->selection.start, gh->selection.end);
1731 	end_pos = MAX(gh->selection.start, gh->selection.end);
1732 
1733 	/* +1 because we're counting the number of characters to grab here.
1734 	 * You have to actually include the first character in the range.
1735 	 */
1736 	len = end_pos - start_pos + 1;
1737 	g_return_if_fail (len);
1738 
1739 	g_clear_pointer (&gh->priv->clip_buf, g_free);
1740 	gh->priv->clip_buf = hex_document_get_data (gh->document,
1741 			start_pos, len);
1742 	gh->priv->clip_buf_len = len;
1743 
1744 	gtk_clipboard_set_with_data (klass->clipboard,
1745 			clipboard_targets,
1746 			N_TARGETS,
1747 			clipboard_get_cb,
1748 			NULL,
1749 			gh);
1750 }
1751 
gtk_hex_real_cut_to_clipboard(GtkHex * gh)1752 static void gtk_hex_real_cut_to_clipboard(GtkHex *gh)
1753 {
1754 	if(gh->selection.start != -1 && gh->selection.end != -1) {
1755 		gtk_hex_real_copy_to_clipboard(gh);
1756 		gtk_hex_delete_selection(gh);
1757 	}
1758 }
1759 
gtk_hex_real_paste_from_clipboard(GtkHex * gh)1760 static void gtk_hex_real_paste_from_clipboard(GtkHex *gh)
1761 {
1762 	GtkHexClass *klass = GTK_HEX_CLASS(GTK_WIDGET_GET_CLASS(gh));
1763 	GtkSelectionData *selection_data = NULL;
1764 	const guchar *text = NULL;
1765 	GdkAtom hex_atom = gdk_atom_intern ("HEXDATA", FALSE);
1766 	int len;
1767 
1768 	if (gtk_clipboard_wait_is_target_available (klass->clipboard,
1769 				hex_atom))
1770 	{
1771 		selection_data = gtk_clipboard_wait_for_contents (
1772 				klass->clipboard, hex_atom);
1773 	}
1774 	else
1775 	{
1776 		selection_data = gtk_clipboard_wait_for_contents (
1777 				klass->clipboard, GDK_TARGET_STRING);
1778 	}
1779 
1780 	if (selection_data)
1781 	{
1782 		text = gtk_selection_data_get_data (selection_data);
1783 		len = gtk_selection_data_get_length (selection_data);
1784 	}
1785 
1786 	if (text && len)
1787 	{
1788 		hex_document_set_data(gh->document, gh->cursor_pos,
1789 				len, 0, text, TRUE);
1790 		gtk_hex_set_cursor(gh, gh->cursor_pos + len);
1791 	}
1792 
1793 	g_clear_pointer (&selection_data, gtk_selection_data_free);
1794 }
1795 
gtk_hex_finalize(GObject * o)1796 static void gtk_hex_finalize(GObject *o) {
1797 	GtkHex *gh = GTK_HEX(o);
1798 
1799 	if (gh->priv->disp_buffer)
1800 		g_free (gh->priv->disp_buffer);
1801 
1802 	if (gh->disp_font_metrics)
1803 		pango_font_metrics_unref (gh->disp_font_metrics);
1804 
1805 	if (gh->font_desc)
1806 		pango_font_description_free (gh->font_desc);
1807 
1808 	if (gh->xlayout)
1809 		g_object_unref (G_OBJECT (gh->xlayout));
1810 
1811 	if (gh->alayout)
1812 		g_object_unref (G_OBJECT (gh->alayout));
1813 
1814 	if (gh->olayout)
1815 		g_object_unref (G_OBJECT (gh->olayout));
1816 
1817 	/* Changes for Gnome 2.0 -- SnM */
1818 	if(G_OBJECT_CLASS(parent_class)->finalize)
1819 		(* G_OBJECT_CLASS(parent_class)->finalize)(G_OBJECT(o));
1820 }
1821 
gtk_hex_key_press(GtkWidget * w,GdkEventKey * event)1822 static gboolean gtk_hex_key_press(GtkWidget *w, GdkEventKey *event) {
1823 	GtkHex *gh = GTK_HEX(w);
1824 	gint ret = TRUE;
1825 
1826 	hide_cursor(gh);
1827 
1828 	if(!(event->state & GDK_SHIFT_MASK)) {
1829 		gh->selecting = FALSE;
1830 	}
1831 	else {
1832 		gh->selecting = TRUE;
1833 	}
1834 	switch(event->keyval) {
1835 	case GDK_KEY_BackSpace:
1836 		if(gh->cursor_pos > 0) {
1837 			hex_document_set_data(gh->document, gh->cursor_pos - 1,
1838 								  0, 1, NULL, TRUE);
1839 			if (gh->selecting)
1840 				gh->selecting = FALSE;
1841 			gtk_hex_set_cursor(gh, gh->cursor_pos - 1);
1842 		}
1843 		break;
1844 	case GDK_KEY_Tab:
1845 	case GDK_KEY_KP_Tab:
1846 		if (gh->active_view == VIEW_ASCII) {
1847 			gh->active_view = VIEW_HEX;
1848 		}
1849 		else {
1850 			gh->active_view = VIEW_ASCII;
1851 		}
1852 		break;
1853 	case GDK_KEY_Delete:
1854 		if(gh->cursor_pos < gh->document->file_size) {
1855 			hex_document_set_data(gh->document, gh->cursor_pos,
1856 								  0, 1, NULL, TRUE);
1857 			gtk_hex_set_cursor(gh, gh->cursor_pos);
1858 		}
1859 		break;
1860 	case GDK_KEY_Up:
1861 		gtk_hex_set_cursor(gh, gh->cursor_pos - gh->cpl);
1862 		break;
1863 	case GDK_KEY_Down:
1864 		gtk_hex_set_cursor(gh, gh->cursor_pos + gh->cpl);
1865 		break;
1866 	case GDK_KEY_Page_Up:
1867 		gtk_hex_set_cursor(gh, MAX(0, (gint)gh->cursor_pos - gh->vis_lines*gh->cpl));
1868 		break;
1869 	case GDK_KEY_Page_Down:
1870 		gtk_hex_set_cursor(gh, MIN((gint)gh->document->file_size, (gint)gh->cursor_pos + gh->vis_lines*gh->cpl));
1871 		break;
1872 	default:
1873 		if (event->state & GDK_MOD1_MASK) {
1874 			show_cursor(gh);
1875 			return FALSE;
1876 		}
1877 		if(gh->active_view == VIEW_HEX)
1878 			switch(event->keyval) {
1879 			case GDK_KEY_Left:
1880 				if(!(event->state & GDK_SHIFT_MASK)) {
1881 					gh->lower_nibble = !gh->lower_nibble;
1882 					if(gh->lower_nibble)
1883 						gtk_hex_set_cursor(gh, gh->cursor_pos - 1);
1884 				}
1885 				else {
1886 					gtk_hex_set_cursor(gh, gh->cursor_pos - 1);
1887 				}
1888 				break;
1889 			case GDK_KEY_Right:
1890 				if(gh->cursor_pos >= gh->document->file_size)
1891 					break;
1892 				if(!(event->state & GDK_SHIFT_MASK)) {
1893 					gh->lower_nibble = !gh->lower_nibble;
1894 					if(!gh->lower_nibble)
1895 						gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1896 				}
1897 				else {
1898 					gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1899 				}
1900 				break;
1901 			default:
1902 				if(event->length != 1)
1903 					ret = FALSE;
1904 				else if((event->keyval >= '0')&&(event->keyval <= '9')) {
1905 					hex_document_set_nibble(gh->document, event->keyval - '0',
1906 											gh->cursor_pos, gh->lower_nibble,
1907 											gh->insert, TRUE);
1908 					if (gh->selecting)
1909 						gh->selecting = FALSE;
1910 					gh->lower_nibble = !gh->lower_nibble;
1911 					if(!gh->lower_nibble)
1912 						gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1913 				}
1914 				else if((event->keyval >= 'A')&&(event->keyval <= 'F')) {
1915 					hex_document_set_nibble(gh->document, event->keyval - 'A' + 10,
1916 											gh->cursor_pos, gh->lower_nibble,
1917 											gh->insert, TRUE);
1918 					if (gh->selecting)
1919 						gh->selecting = FALSE;
1920 					gh->lower_nibble = !gh->lower_nibble;
1921 					if(!gh->lower_nibble)
1922 						gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1923 				}
1924 				else if((event->keyval >= 'a')&&(event->keyval <= 'f')) {
1925 					hex_document_set_nibble(gh->document, event->keyval - 'a' + 10,
1926 											gh->cursor_pos, gh->lower_nibble,
1927 											gh->insert, TRUE);
1928 					if (gh->selecting)
1929 						gh->selecting = FALSE;
1930 					gh->lower_nibble = !gh->lower_nibble;
1931 					if(!gh->lower_nibble)
1932 						gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1933 				}
1934 				else if((event->keyval >= GDK_KEY_KP_0)&&(event->keyval <= GDK_KEY_KP_9)) {
1935 					hex_document_set_nibble(gh->document, event->keyval - GDK_KEY_KP_0,
1936 											gh->cursor_pos, gh->lower_nibble,
1937 											gh->insert, TRUE);
1938 					if (gh->selecting)
1939 						gh->selecting = FALSE;
1940 					gh->lower_nibble = !gh->lower_nibble;
1941 					if(!gh->lower_nibble)
1942 						gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1943 				}
1944 				else
1945 					ret = FALSE;
1946 
1947 				break;
1948 			}
1949 		else if(gh->active_view == VIEW_ASCII)
1950 			switch(event->keyval) {
1951 			case GDK_KEY_Left:
1952 				gtk_hex_set_cursor(gh, gh->cursor_pos - 1);
1953 				break;
1954 			case GDK_KEY_Right:
1955 				gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1956 				break;
1957 			default:
1958 				if(event->length != 1)
1959 					ret = FALSE;
1960 				else if(is_displayable(event->keyval)) {
1961 					hex_document_set_byte(gh->document, event->keyval,
1962 										  gh->cursor_pos, gh->insert, TRUE);
1963 					if (gh->selecting)
1964 						gh->selecting = FALSE;
1965 					gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1966 				}
1967 				else if((event->keyval >= GDK_KEY_KP_0)&&(event->keyval <= GDK_KEY_KP_9)) {
1968 					hex_document_set_byte(gh->document, event->keyval - GDK_KEY_KP_0 + '0',
1969 											gh->cursor_pos, gh->insert, TRUE);
1970 					if (gh->selecting)
1971 						gh->selecting = FALSE;
1972 					gtk_hex_set_cursor(gh, gh->cursor_pos + 1);
1973 				}
1974 				else
1975 					ret = FALSE;
1976 
1977 				break;
1978 			}
1979 		break;
1980 	}
1981 
1982 	show_cursor(gh);
1983 
1984 	return ret;
1985 }
1986 
gtk_hex_key_release(GtkWidget * w,GdkEventKey * event)1987 static gboolean gtk_hex_key_release(GtkWidget *w, GdkEventKey *event) {
1988 	GtkHex *gh = GTK_HEX(w);
1989 
1990 	if (event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R) {
1991 		gh->selecting = FALSE;
1992 	}
1993 
1994 	return TRUE;
1995 }
1996 
gtk_hex_button_release(GtkWidget * w,GdkEventButton * event)1997 static gboolean gtk_hex_button_release(GtkWidget *w, GdkEventButton *event) {
1998 	GtkHex *gh = GTK_HEX(w);
1999 
2000 	if(event->state & GDK_SHIFT_MASK) {
2001 		gh->selecting = FALSE;
2002 	}
2003 
2004 	return TRUE;
2005 }
2006 
2007 /*
2008  * recalculate the width of both displays and reposition and resize all
2009  * the children widgets and adjust the scrollbar after resizing
2010  * connects to the size_allocate signal of the GtkHex widget
2011  */
gtk_hex_size_allocate(GtkWidget * w,GtkAllocation * alloc)2012 static void gtk_hex_size_allocate(GtkWidget *w, GtkAllocation *alloc) {
2013 	GtkHex *gh;
2014 	GtkAllocation my_alloc;
2015 	GtkBorder padding;
2016 	GtkRequisition sb_req;
2017 	GtkStateFlags state;
2018 	GtkStyleContext *context;
2019 	gint border_width;
2020 
2021 	gh = GTK_HEX(w);
2022 	hide_cursor(gh);
2023 
2024 	recalc_displays(gh, alloc->width, alloc->height);
2025 
2026 	gtk_widget_set_allocation(w, alloc);
2027 	if(gtk_widget_get_realized(w))
2028 		gdk_window_move_resize (gtk_widget_get_window(w),
2029 								alloc->x,
2030 								alloc->y,
2031 								alloc->width,
2032 								alloc->height);
2033 
2034 	border_width = gtk_container_get_border_width(GTK_CONTAINER(w));
2035 
2036 	context = gtk_widget_get_style_context (w);
2037 	state = gtk_widget_get_state_flags (w);
2038 	gtk_style_context_get_padding (context, state, &padding);
2039 
2040 	my_alloc.x = border_width + padding.left;
2041 	my_alloc.y = border_width + padding.top;
2042 	my_alloc.height = MAX (alloc->height - 2 * border_width - padding.top - padding.bottom, 1);
2043 	if(gh->show_offsets) {
2044 		my_alloc.width = 9*gh->char_width;
2045 		gtk_widget_size_allocate(gh->offsets, &my_alloc);
2046 		gtk_widget_queue_draw(gh->offsets);
2047 		my_alloc.x += padding.left + padding.right + my_alloc.width + gh->extra_width/2;
2048 	}
2049 
2050 	gtk_widget_get_requisition(gh->scrollbar, &sb_req);
2051 
2052 	my_alloc.width = gh->xdisp_width;
2053 	gtk_widget_size_allocate(gh->xdisp, &my_alloc);
2054 	my_alloc.x = alloc->width - border_width - sb_req.width;
2055 	my_alloc.y = border_width;
2056 	my_alloc.width = sb_req.width;
2057 	my_alloc.height = MAX(alloc->height - 2*border_width, 1);
2058 	gtk_widget_size_allocate(gh->scrollbar, &my_alloc);
2059 	my_alloc.x -= gh->adisp_width + padding.left;
2060 	my_alloc.y = border_width + padding.top;
2061 	my_alloc.width = gh->adisp_width;
2062 	my_alloc.height = MAX (alloc->height - 2 * border_width - padding.top - padding.bottom, 1);
2063 	gtk_widget_size_allocate(gh->adisp, &my_alloc);
2064 
2065 	show_cursor(gh);
2066 }
2067 
2068 static gboolean
gtk_hex_draw(GtkWidget * w,cairo_t * cr)2069 gtk_hex_draw (GtkWidget *w,
2070               cairo_t *cr)
2071 {
2072 	if (GTK_WIDGET_CLASS (parent_class)->draw)
2073 		(* GTK_WIDGET_CLASS (parent_class)->draw) (w, cr);
2074 
2075 	draw_shadow (w, cr);
2076 
2077 	return TRUE;
2078 }
2079 
gtk_hex_document_changed(HexDocument * doc,gpointer change_data,gboolean push_undo,gpointer data)2080 static void gtk_hex_document_changed(HexDocument* doc, gpointer change_data,
2081         gboolean push_undo, gpointer data)
2082 {
2083     gtk_hex_real_data_changed (GTK_HEX(data), change_data);
2084 }
2085 
2086 
gtk_hex_size_request(GtkWidget * w,GtkRequisition * req)2087 static void gtk_hex_size_request(GtkWidget *w, GtkRequisition *req) {
2088 	GtkBorder padding;
2089 	GtkHex *gh = GTK_HEX(w);
2090 	GtkRequisition sb_req;
2091 	GtkStateFlags state;
2092 	GtkStyleContext *context;
2093 
2094 	context = gtk_widget_get_style_context (w);
2095 	state = gtk_widget_get_state_flags (w);
2096 	gtk_style_context_get_padding (context, state, &padding);
2097 
2098 	gtk_widget_get_preferred_size (gh->scrollbar, &sb_req, NULL);
2099 	req->width = 2 * padding.left + 2 * padding.right + 2 * gtk_container_get_border_width (GTK_CONTAINER (w)) +
2100 		sb_req.width + gh->char_width * (gh->priv->default_cpl + (gh->priv->default_cpl - 1) /
2101 										 gh->group_type);
2102 	if(gh->show_offsets)
2103 		req->width += padding.left + padding.right + 9 * gh->char_width;
2104 	req->height = gh->priv->default_lines * gh->char_height + padding.top + padding.bottom +
2105 		2*gtk_container_get_border_width(GTK_CONTAINER(w));
2106 }
2107 
2108 static void
gtk_hex_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)2109 gtk_hex_get_preferred_width (GtkWidget *widget,
2110                              gint      *minimal_width,
2111                              gint      *natural_width)
2112 {
2113     GtkRequisition requisition;
2114 
2115     gtk_hex_size_request (widget, &requisition);
2116 
2117     *minimal_width = *natural_width = requisition.width;
2118 }
2119 
2120 static void
gtk_hex_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)2121 gtk_hex_get_preferred_height (GtkWidget *widget,
2122                               gint      *minimal_height,
2123                               gint      *natural_height)
2124 {
2125     GtkRequisition requisition;
2126 
2127     gtk_hex_size_request (widget, &requisition);
2128 
2129     *minimal_height = *natural_height = requisition.height;
2130 }
2131 
gtk_hex_class_init(GtkHexClass * klass,gpointer data)2132 static void gtk_hex_class_init(GtkHexClass *klass, gpointer data) {
2133 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
2134 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
2135 
2136 	parent_class = g_type_class_peek_parent(klass);
2137 
2138 	gtkhex_signals[CURSOR_MOVED_SIGNAL] =
2139 		g_signal_new ("cursor_moved",
2140 					  G_TYPE_FROM_CLASS (widget_class),
2141 					  G_SIGNAL_RUN_FIRST,
2142 					  G_STRUCT_OFFSET (GtkHexClass, cursor_moved),
2143 					  NULL, NULL, NULL, G_TYPE_NONE, 0);
2144 
2145 	gtkhex_signals[DATA_CHANGED_SIGNAL] =
2146 		g_signal_new ("data_changed",
2147 					  G_TYPE_FROM_CLASS (widget_class),
2148 					  G_SIGNAL_RUN_FIRST,
2149 					  G_STRUCT_OFFSET (GtkHexClass, data_changed),
2150 					  NULL, NULL, NULL, G_TYPE_NONE, 1,
2151 					  G_TYPE_POINTER);
2152 
2153 	gtkhex_signals[CUT_CLIPBOARD_SIGNAL] =
2154 		g_signal_new ("cut_clipboard",
2155 					  G_TYPE_FROM_CLASS (widget_class),
2156 					  G_SIGNAL_RUN_FIRST,
2157 					  G_STRUCT_OFFSET (GtkHexClass, cut_clipboard),
2158 					  NULL, NULL, NULL, G_TYPE_NONE, 0);
2159 
2160 	gtkhex_signals[COPY_CLIPBOARD_SIGNAL] =
2161 		g_signal_new ("copy_clipboard",
2162 					  G_TYPE_FROM_CLASS (widget_class),
2163 					  G_SIGNAL_RUN_FIRST,
2164 					  G_STRUCT_OFFSET (GtkHexClass, copy_clipboard),
2165 					  NULL, NULL, NULL, G_TYPE_NONE, 0);
2166 
2167 
2168 	gtkhex_signals[PASTE_CLIPBOARD_SIGNAL] =
2169 		g_signal_new ("paste_clipboard",
2170 					  G_TYPE_FROM_CLASS (widget_class),
2171 					  G_SIGNAL_RUN_FIRST,
2172 					  G_STRUCT_OFFSET (GtkHexClass, paste_clipboard),
2173 					  NULL, NULL, NULL, G_TYPE_NONE, 0);
2174 
2175 	klass->cursor_moved = NULL;
2176 	klass->data_changed = gtk_hex_real_data_changed;
2177 	klass->cut_clipboard = gtk_hex_real_cut_to_clipboard;
2178 	klass->copy_clipboard = gtk_hex_real_copy_to_clipboard;
2179 	klass->paste_clipboard = gtk_hex_real_paste_from_clipboard;
2180 
2181 	klass->primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2182 	klass->clipboard = gtk_clipboard_get(GDK_NONE);
2183 
2184 	GTK_WIDGET_CLASS(klass)->size_allocate = gtk_hex_size_allocate;
2185 	GTK_WIDGET_CLASS(klass)->get_preferred_width = gtk_hex_get_preferred_width;
2186 	GTK_WIDGET_CLASS(klass)->get_preferred_height = gtk_hex_get_preferred_height;
2187 	GTK_WIDGET_CLASS(klass)->draw = gtk_hex_draw;
2188 	GTK_WIDGET_CLASS(klass)->key_press_event = gtk_hex_key_press;
2189 	GTK_WIDGET_CLASS(klass)->key_release_event = gtk_hex_key_release;
2190 	GTK_WIDGET_CLASS(klass)->button_release_event = gtk_hex_button_release;
2191 
2192 	object_class->finalize = gtk_hex_finalize;
2193 
2194 	parent_class = g_type_class_ref (gtk_fixed_get_type ());
2195 
2196 	g_type_class_add_private (object_class, sizeof (GtkHexPrivate));
2197 }
2198 
gtk_hex_init(GtkHex * gh,gpointer klass)2199 static void gtk_hex_init(GtkHex *gh, gpointer klass) {
2200 	GtkCssProvider *provider;
2201 	GtkStyleContext *context;
2202 
2203 	gh->priv = GTKHEX_GET_PRIVATE (gh);
2204 	gh->priv->disp_buffer = NULL;
2205 	gh->priv->default_cpl = DEFAULT_CPL;
2206 	gh->priv->default_lines = DEFAULT_LINES;
2207 
2208 	gh->scroll_timeout = -1;
2209 
2210 	gh->document = NULL;
2211 	gh->starting_offset = 0;
2212 
2213 	gh->xdisp_width = gh->adisp_width = 200;
2214 	gh->extra_width = 0;
2215 	gh->active_view = VIEW_HEX;
2216 	gh->group_type = GROUP_BYTE;
2217 	gh->lines = gh->vis_lines = gh->top_line = gh->cpl = 0;
2218 	gh->cursor_pos = 0;
2219 	gh->lower_nibble = FALSE;
2220 	gh->cursor_shown = FALSE;
2221 	gh->button = 0;
2222 	gh->insert = FALSE; /* default to overwrite mode */
2223 	gh->selecting = FALSE;
2224 
2225 	gh->selection.start = gh->selection.end = 0;
2226 	gh->selection.bg_color = NULL;
2227 	gh->selection.min_select = 1;
2228 	gh->selection.next = gh->selection.prev = NULL;
2229 	gh->selection.valid = FALSE;
2230 
2231 	gh->auto_highlight = NULL;
2232 
2233 	/* get ourselves a decent monospaced font for rendering text */
2234 	gh->disp_font_metrics = gtk_hex_load_font (DEFAULT_FONT);
2235 	gh->font_desc = pango_font_description_from_string (DEFAULT_FONT);
2236 
2237 	gh->char_width = get_max_char_width(gh, gh->disp_font_metrics);
2238 	gh->char_height = PANGO_PIXELS (pango_font_metrics_get_ascent (gh->disp_font_metrics)) + PANGO_PIXELS (pango_font_metrics_get_descent (gh->disp_font_metrics)) + 2;
2239 
2240 
2241 	gtk_widget_set_can_focus(GTK_WIDGET(gh), TRUE);
2242 	gtk_widget_set_events(GTK_WIDGET(gh), GDK_KEY_PRESS_MASK);
2243 
2244 	context = gtk_widget_get_style_context (GTK_WIDGET (gh));
2245 	provider = gtk_css_provider_new ();
2246 	gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (provider),
2247 	                                 "GtkHex {\n"
2248 	                                 "   border-style: solid;\n"
2249 	                                 "   border-width: 1px;\n"
2250 	                                 "   padding: 1px;\n"
2251 	                                 "}\n", -1, NULL);
2252 	gtk_style_context_add_provider (context,
2253 	                                GTK_STYLE_PROVIDER (provider),
2254 	                                GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
2255 
2256 
2257 	gh->adj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
2258 	gh->xdisp = gtk_drawing_area_new();
2259 
2260 	/* Modify the font for the widget */
2261 	gtk_widget_modify_font (gh->xdisp, gh->font_desc);
2262 
2263 	/* Create the pango layout for the widget */
2264 	gh->xlayout = gtk_widget_create_pango_layout (gh->xdisp, "");
2265 
2266 	gtk_widget_set_events (gh->xdisp, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
2267 						   GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK);
2268 	g_signal_connect(G_OBJECT(gh->xdisp), "draw",
2269 					 G_CALLBACK(hex_draw), gh);
2270 	g_signal_connect(G_OBJECT(gh->xdisp), "button_press_event",
2271 					 G_CALLBACK(hex_button_cb), gh);
2272 	g_signal_connect(G_OBJECT(gh->xdisp), "button_release_event",
2273 					 G_CALLBACK(hex_button_cb), gh);
2274 	g_signal_connect(G_OBJECT(gh->xdisp), "motion_notify_event",
2275 					 G_CALLBACK(hex_motion_cb), gh);
2276 	g_signal_connect(G_OBJECT(gh->xdisp), "scroll_event",
2277 					 G_CALLBACK(hex_scroll_cb), gh);
2278 
2279 	context = gtk_widget_get_style_context (GTK_WIDGET (gh->xdisp));
2280 	gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
2281 
2282 	gtk_fixed_put(GTK_FIXED(gh), gh->xdisp, 0, 0);
2283 	gtk_widget_show(gh->xdisp);
2284 
2285 	gh->adisp = gtk_drawing_area_new();
2286 
2287 	/* Modify the font for the widget */
2288 	gtk_widget_modify_font (gh->adisp, gh->font_desc);
2289 
2290 	/* Create the pango layout for the widget */
2291 	gh->alayout = gtk_widget_create_pango_layout (gh->adisp, "");
2292 
2293 	gtk_widget_set_events (gh->adisp, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
2294 						   GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK);
2295 	g_signal_connect(G_OBJECT(gh->adisp), "draw",
2296 					 G_CALLBACK(ascii_draw), gh);
2297 	g_signal_connect(G_OBJECT(gh->adisp), "button_press_event",
2298 					 G_CALLBACK(ascii_button_cb), gh);
2299 	g_signal_connect(G_OBJECT(gh->adisp), "button_release_event",
2300 					 G_CALLBACK(ascii_button_cb), gh);
2301 	g_signal_connect(G_OBJECT(gh->adisp), "motion_notify_event",
2302 					 G_CALLBACK(ascii_motion_cb), gh);
2303 	g_signal_connect(G_OBJECT(gh->adisp), "scroll_event",
2304 					 G_CALLBACK(ascii_scroll_cb), gh);
2305 
2306 	context = gtk_widget_get_style_context (GTK_WIDGET (gh->adisp));
2307 	gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
2308 
2309 	gtk_fixed_put(GTK_FIXED(gh), gh->adisp, 0, 0);
2310 	gtk_widget_show(gh->adisp);
2311 
2312 	g_signal_connect(G_OBJECT(gh->adj), "value_changed",
2313 					 G_CALLBACK(display_scrolled), gh);
2314 
2315 	gh->scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, gh->adj);
2316 	gtk_fixed_put(GTK_FIXED(gh), gh->scrollbar, 0, 0);
2317 	gtk_widget_show(gh->scrollbar);
2318 }
2319 
gtk_hex_get_type()2320 GType gtk_hex_get_type() {
2321 	static GType gh_type = 0;
2322 
2323 	if(!gh_type) {
2324 		GTypeInfo gh_info = {
2325 			sizeof (GtkHexClass),
2326 			NULL,		/* base_init */
2327 			NULL,		/* base_finalize */
2328 			(GClassInitFunc) gtk_hex_class_init,
2329 			NULL,		/* class_finalize */
2330 			NULL,		/* class_data */
2331 			sizeof (GtkHex),
2332 			0,
2333 			(GInstanceInitFunc) gtk_hex_init
2334 		};
2335 
2336 		gh_type = g_type_register_static (gtk_fixed_get_type(),
2337 							"GtkHex",
2338 							&gh_info,
2339 							0);
2340 	}
2341 
2342 	return gh_type;
2343 }
2344 
gtk_hex_new(HexDocument * owner)2345 GtkWidget *gtk_hex_new(HexDocument *owner) {
2346 	GtkHex *gh;
2347 
2348 	gh = GTK_HEX (g_object_new (GTK_TYPE_HEX, NULL));
2349 	g_return_val_if_fail (gh != NULL, NULL);
2350 
2351 	gh->document = owner;
2352     g_signal_connect (G_OBJECT (gh->document), "document_changed",
2353             G_CALLBACK (gtk_hex_document_changed), gh);
2354 
2355 	return GTK_WIDGET(gh);
2356 }
2357 
2358 
2359 /*-------- public API starts here --------*/
2360 
2361 
2362 /*
2363  * moves cursor to UPPER_NIBBLE or LOWER_NIBBLE of the current byte
2364  */
gtk_hex_set_nibble(GtkHex * gh,gint lower_nibble)2365 void gtk_hex_set_nibble(GtkHex *gh, gint lower_nibble) {
2366 	g_return_if_fail(gh != NULL);
2367 	g_return_if_fail(GTK_IS_HEX(gh));
2368 
2369 	if(gh->selecting) {
2370 		bytes_changed(gh, gh->cursor_pos, gh->cursor_pos);
2371 		gh->lower_nibble = lower_nibble;
2372 	}
2373 	else if(gh->selection.end != gh->selection.start) {
2374 		guint start = MIN(gh->selection.start, gh->selection.end);
2375 		guint end = MAX(gh->selection.start, gh->selection.end);
2376 		gh->selection.end = gh->selection.start = 0;
2377 		bytes_changed(gh, start, end);
2378 		gh->lower_nibble = lower_nibble;
2379 	}
2380 	else {
2381 		hide_cursor(gh);
2382 		gh->lower_nibble = lower_nibble;
2383 		show_cursor(gh);
2384 	}
2385 }
2386 
2387 /*
2388  * moves cursor to byte index
2389  */
gtk_hex_set_cursor(GtkHex * gh,gint index)2390 void gtk_hex_set_cursor(GtkHex *gh, gint index) {
2391 	guint y;
2392 	guint old_pos = gh->cursor_pos;
2393 
2394 	g_return_if_fail(gh != NULL);
2395 	g_return_if_fail(GTK_IS_HEX(gh));
2396 
2397 	if((index >= 0) && (index <= gh->document->file_size)) {
2398 		if(!gh->insert && index == gh->document->file_size)
2399 			index--;
2400 
2401 		index = MAX(index, 0);
2402 
2403 		hide_cursor(gh);
2404 
2405 		gh->cursor_pos = index;
2406 
2407 		if(gh->cpl == 0)
2408 			return;
2409 
2410 		y = index / gh->cpl;
2411 		if(y >= gh->top_line + gh->vis_lines) {
2412 			gtk_adjustment_set_value(gh->adj, MIN(y - gh->vis_lines + 1, gh->lines - gh->vis_lines));
2413 			gtk_adjustment_set_value(gh->adj, MAX(gtk_adjustment_get_value(gh->adj), 0));
2414 			g_signal_emit_by_name(G_OBJECT(gh->adj), "value_changed");
2415 		}
2416 		else if (y < gh->top_line) {
2417 			gtk_adjustment_set_value(gh->adj, y);
2418 			g_signal_emit_by_name(G_OBJECT(gh->adj), "value_changed");
2419 		}
2420 
2421 		if(index == gh->document->file_size)
2422 			gh->lower_nibble = FALSE;
2423 
2424 		if(gh->selecting) {
2425 			gtk_hex_set_selection(gh, gh->selection.start, gh->cursor_pos);
2426 			bytes_changed(gh, MIN(gh->cursor_pos, old_pos), MAX(gh->cursor_pos, old_pos));
2427 		}
2428 		else {
2429 			guint start = MIN(gh->selection.start, gh->selection.end);
2430 			guint end = MAX(gh->selection.start, gh->selection.end);
2431 			bytes_changed(gh, start, end);
2432 			gh->selection.end = gh->selection.start = gh->cursor_pos;
2433 		}
2434 
2435 		g_signal_emit_by_name(G_OBJECT(gh), "cursor_moved");
2436 
2437 		bytes_changed(gh, old_pos, old_pos);
2438 		show_cursor(gh);
2439 	}
2440 }
2441 
2442 /*
2443  * moves cursor to column x in line y (in the whole buffer, not just the currently visible part)
2444  */
gtk_hex_set_cursor_xy(GtkHex * gh,gint x,gint y)2445 void gtk_hex_set_cursor_xy(GtkHex *gh, gint x, gint y) {
2446 	gint cp;
2447 	guint old_pos = gh->cursor_pos;
2448 
2449 	g_return_if_fail(gh != NULL);
2450 	g_return_if_fail(GTK_IS_HEX(gh));
2451 
2452 	cp = y*gh->cpl + x;
2453 
2454 	if((y >= 0) && (y < gh->lines) && (x >= 0) &&
2455 	   (x < gh->cpl) && (cp <= gh->document->file_size)) {
2456 		if(!gh->insert && cp == gh->document->file_size)
2457 			cp--;
2458 
2459 		cp = MAX(cp, 0);
2460 
2461 		hide_cursor(gh);
2462 
2463 		gh->cursor_pos = cp;
2464 
2465 		if(y >= gh->top_line + gh->vis_lines) {
2466 			gtk_adjustment_set_value(gh->adj, MIN(y - gh->vis_lines + 1, gh->lines - gh->vis_lines));
2467 			gtk_adjustment_set_value(gh->adj, MAX(0, gtk_adjustment_get_value(gh->adj)));
2468 			g_signal_emit_by_name(G_OBJECT(gh->adj), "value_changed");
2469 		}
2470 		else if (y < gh->top_line) {
2471 			gtk_adjustment_set_value(gh->adj, y);
2472 			g_signal_emit_by_name(G_OBJECT(gh->adj), "value_changed");
2473 		}
2474 
2475 		g_signal_emit_by_name(G_OBJECT(gh), "cursor_moved");
2476 
2477 		if(gh->selecting) {
2478 			gtk_hex_set_selection(gh, gh->selection.start, gh->cursor_pos);
2479 			bytes_changed(gh, MIN(gh->cursor_pos, old_pos), MAX(gh->cursor_pos, old_pos));
2480 		}
2481 		else if(gh->selection.end != gh->selection.start) {
2482 			guint start = MIN(gh->selection.start, gh->selection.end);
2483 			guint end = MAX(gh->selection.start, gh->selection.end);
2484 			gh->selection.end = gh->selection.start = 0;
2485 			bytes_changed(gh, start, end);
2486 		}
2487 		bytes_changed(gh, old_pos, old_pos);
2488 		show_cursor(gh);
2489 	}
2490 }
2491 
2492 /*
2493  * returns cursor position
2494  */
gtk_hex_get_cursor(GtkHex * gh)2495 guint gtk_hex_get_cursor(GtkHex *gh) {
2496 	g_return_val_if_fail(gh != NULL, -1);
2497 	g_return_val_if_fail(GTK_IS_HEX(gh), -1);
2498 
2499 	return gh->cursor_pos;
2500 }
2501 
2502 /*
2503  * returns value of the byte at position offset
2504  */
gtk_hex_get_byte(GtkHex * gh,guint offset)2505 guchar gtk_hex_get_byte(GtkHex *gh, guint offset) {
2506 	g_return_val_if_fail(gh != NULL, 0);
2507 	g_return_val_if_fail(GTK_IS_HEX(gh), 0);
2508 
2509 	if((offset >= 0) && (offset < gh->document->file_size))
2510 		return hex_document_get_byte(gh->document, offset);
2511 
2512 	return 0;
2513 }
2514 
2515 /*
2516  * sets data group type (see GROUP_* defines in gtkhex.h)
2517  */
gtk_hex_set_group_type(GtkHex * gh,guint gt)2518 void gtk_hex_set_group_type(GtkHex *gh, guint gt) {
2519 	GtkAllocation allocation;
2520 
2521 	g_return_if_fail(gh != NULL);
2522 	g_return_if_fail(GTK_IS_HEX(gh));
2523 
2524 	hide_cursor(gh);
2525 	gh->group_type = gt;
2526 	gtk_widget_get_allocation(GTK_WIDGET(gh), &allocation);
2527 	recalc_displays(gh, allocation.width, allocation.height);
2528 	gtk_widget_queue_resize(GTK_WIDGET(gh));
2529 	show_cursor(gh);
2530 }
2531 
2532 /*
2533  * sets font for displaying data
2534  */
gtk_hex_set_font(GtkHex * gh,PangoFontMetrics * font_metrics,const PangoFontDescription * font_desc)2535 void gtk_hex_set_font(GtkHex *gh, PangoFontMetrics *font_metrics, const PangoFontDescription *font_desc) {
2536 	GtkAllocation allocation;
2537 
2538 	g_return_if_fail(gh != NULL);
2539 	g_return_if_fail(GTK_IS_HEX(gh));
2540 
2541 	if (gh->disp_font_metrics)
2542 		pango_font_metrics_unref (gh->disp_font_metrics);
2543 
2544 	if (gh->font_desc)
2545 		pango_font_description_free (gh->font_desc);
2546 
2547 	gh->disp_font_metrics = pango_font_metrics_ref (font_metrics);
2548 	gh->font_desc = pango_font_description_copy (font_desc);
2549 
2550 	if (gh->xdisp)
2551 		gtk_widget_modify_font (gh->xdisp, gh->font_desc);
2552 
2553 	if (gh->adisp)
2554 		gtk_widget_modify_font (gh->adisp, gh->font_desc);
2555 
2556 	if (gh->offsets)
2557 		gtk_widget_modify_font (gh->offsets, gh->font_desc);
2558 
2559 
2560 	gh->char_width = get_max_char_width(gh, gh->disp_font_metrics);
2561 	gh->char_height = PANGO_PIXELS (pango_font_metrics_get_ascent (gh->disp_font_metrics)) + PANGO_PIXELS (pango_font_metrics_get_descent (gh->disp_font_metrics)) + 2;
2562 	gtk_widget_get_allocation(GTK_WIDGET(gh), &allocation);
2563 	recalc_displays(gh, allocation.width, allocation.height);
2564 
2565 	redraw_widget(GTK_WIDGET(gh));
2566 }
2567 
2568 /*
2569  *  do we show the offsets of lines?
2570  */
gtk_hex_show_offsets(GtkHex * gh,gboolean show)2571 void gtk_hex_show_offsets(GtkHex *gh, gboolean show)
2572 {
2573 	g_return_if_fail(gh != NULL);
2574 	g_return_if_fail(GTK_IS_HEX(gh));
2575 
2576 	if(gh->show_offsets == show)
2577 		return;
2578 
2579 	gh->show_offsets = show;
2580 	if(show)
2581 		show_offsets_widget(gh);
2582 	else
2583 		hide_offsets_widget(gh);
2584 }
2585 
gtk_hex_set_starting_offset(GtkHex * gh,gint starting_offset)2586 void gtk_hex_set_starting_offset(GtkHex *gh, gint starting_offset)
2587 {
2588 	g_return_if_fail (gh != NULL);
2589 	g_return_if_fail(GTK_IS_HEX(gh));
2590 	gh->starting_offset = starting_offset;
2591 }
2592 
gtk_hex_set_insert_mode(GtkHex * gh,gboolean insert)2593 void gtk_hex_set_insert_mode(GtkHex *gh, gboolean insert)
2594 {
2595 	g_return_if_fail(gh != NULL);
2596 	g_return_if_fail(GTK_IS_HEX(gh));
2597 
2598 	gh->insert = insert;
2599 
2600 	if(!gh->insert && gh->cursor_pos > 0) {
2601 		if(gh->cursor_pos >= gh->document->file_size)
2602 			gh->cursor_pos = gh->document->file_size - 1;
2603 	}
2604 }
2605 
gtk_hex_load_font(const char * font_name)2606 PangoFontMetrics* gtk_hex_load_font (const char *font_name)
2607 {
2608 	PangoContext *context;
2609 	PangoFont *new_font;
2610 	PangoFontDescription *new_desc;
2611 	PangoFontMetrics *new_metrics;
2612 
2613 	new_desc = pango_font_description_from_string (font_name);
2614 
2615 	context = gdk_pango_context_get();
2616 
2617 	/* FIXME - Should get the locale language here */
2618 	pango_context_set_language (context, gtk_get_default_language());
2619 
2620 	new_font = pango_context_load_font (context, new_desc);
2621 
2622 	new_metrics = pango_font_get_metrics (new_font, pango_context_get_language (context));
2623 
2624 	pango_font_description_free (new_desc);
2625 	g_object_unref (G_OBJECT (context));
2626 	g_object_unref (G_OBJECT (new_font));
2627 
2628 	return new_metrics;
2629 }
2630 
gtk_hex_insert_autohighlight(GtkHex * gh,const gchar * search,gint len,const gchar * colour)2631 GtkHex_AutoHighlight *gtk_hex_insert_autohighlight(GtkHex *gh,
2632 												   const gchar *search,
2633 												   gint len,
2634 												   const gchar *colour)
2635 {
2636 	GtkHex_AutoHighlight *new = g_malloc0(sizeof(GtkHex_AutoHighlight));
2637 
2638 	new->search_string = g_memdup(search, len);
2639 	new->search_len = len;
2640 
2641 	new->colour = g_strdup(colour);
2642 
2643 	new->highlights = NULL;
2644 
2645 	new->next = gh->auto_highlight;
2646 	new->prev = NULL;
2647 	if (new->next) new->next->prev = new;
2648 	gh->auto_highlight = new;
2649 
2650 	new->view_min = 0;
2651 	new->view_max = 0;
2652 
2653 	gtk_hex_update_auto_highlight(gh, new, FALSE, TRUE);
2654 
2655 	return new;
2656 }
2657 
gtk_hex_delete_autohighlight(GtkHex * gh,GtkHex_AutoHighlight * ahl)2658 void gtk_hex_delete_autohighlight(GtkHex *gh, GtkHex_AutoHighlight *ahl)
2659 {
2660 	g_free(ahl->search_string);
2661 	g_free(ahl->colour);
2662 
2663 	while (ahl->highlights)
2664 	{
2665 		gtk_hex_delete_highlight(gh, ahl, ahl->highlights);
2666 	}
2667 
2668 	if (ahl->next) ahl->next->prev = ahl->prev;
2669 	if (ahl->prev) ahl->prev->next = ahl->next;
2670 
2671 	if (gh->auto_highlight == ahl) gh->auto_highlight = ahl->next;
2672 
2673 	g_free(ahl);
2674 }
2675 
gtk_hex_set_geometry(GtkHex * gh,gint cpl,gint vis_lines)2676 void gtk_hex_set_geometry(GtkHex *gh, gint cpl, gint vis_lines)
2677 {
2678     gh->priv->default_cpl = cpl;
2679     gh->priv->default_lines = vis_lines;
2680 }
2681 
add_atk_namedesc(GtkWidget * widget,const gchar * name,const gchar * desc)2682 void add_atk_namedesc (GtkWidget *widget, const gchar *name, const gchar *desc)
2683 {
2684         AtkObject *atk_widget;
2685 
2686         g_return_if_fail (GTK_IS_WIDGET (widget));
2687         atk_widget = gtk_widget_get_accessible (widget);
2688 
2689         if (name)
2690                 atk_object_set_name (atk_widget, name);
2691         if (desc)
2692                 atk_object_set_description (atk_widget, desc);
2693 }
2694 
add_atk_relation(GtkWidget * obj1,GtkWidget * obj2,AtkRelationType type)2695 void add_atk_relation (GtkWidget *obj1, GtkWidget *obj2, AtkRelationType type)
2696 {
2697 
2698         AtkObject *atk_obj1, *atk_obj2;
2699         AtkRelationSet *relation_set;
2700         AtkRelation *relation;
2701 
2702         g_return_if_fail (GTK_IS_WIDGET (obj1));
2703         g_return_if_fail (GTK_IS_WIDGET (obj2));
2704 
2705         atk_obj1 = gtk_widget_get_accessible (obj1);
2706         atk_obj2 = gtk_widget_get_accessible (obj2);
2707 
2708         relation_set = atk_object_ref_relation_set (atk_obj1);
2709         relation = atk_relation_new (&atk_obj2, 1, type);
2710         atk_relation_set_add (relation_set, relation);
2711         g_object_unref (G_OBJECT (relation));
2712 
2713 }
2714