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