1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* findreplace.c - finding & replacing data
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 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 
31 #include "findreplace.h"
32 #include "ui.h"
33 #include "gtkhex.h"
34 #include "configuration.h"
35 
36 static gint find_delete_event_cb(GtkWidget *w, GdkEventAny *e,
37 								 FindDialog *dialog);
38 static void find_cancel_cb(GtkWidget *w, FindDialog *dialog);
39 static gint advanced_find_delete_event_cb(GtkWidget *w, GdkEventAny *e,
40 										  AdvancedFindDialog *dialog);
41 static void advanced_find_close_cb(GtkWidget *w, AdvancedFindDialog *dialog);
42 
43 static void find_next_cb(GtkButton *button, FindDialog *);
44 static void find_prev_cb(GtkButton *button, FindDialog *);
45 static void replace_next_cb(GtkButton *button, gpointer);
46 static void replace_one_cb(GtkButton *button, gpointer);
47 static void replace_all_cb(GtkButton *button, gpointer);
48 static void goto_byte_cb(GtkButton *button, GtkWidget *);
49 static gint get_search_string(HexDocument *doc, gchar **str);
50 
51 static void advanced_find_add_add_cb(GtkButton *button,
52 									 AdvancedFind_AddDialog *dialog);
53 static void advanced_find_add_cb(GtkButton *button, AdvancedFindDialog *);
54 static void advanced_find_delete_cb(GtkButton *button, AdvancedFindDialog *dialog);
55 static void advanced_find_next_cb(GtkButton *button, AdvancedFindDialog *dialog);
56 static void advanced_find_prev_cb(GtkButton *button, AdvancedFindDialog *dialog);
57 
58 
59 FindDialog *find_dialog = NULL;
60 ReplaceDialog *replace_dialog = NULL;
61 JumpDialog *jump_dialog = NULL;
62 
63 /* basic structure to hold private information to be stored in the
64  * gtk list.
65  */
66 typedef struct
67 {
68 	gchar *str;
69 	gint str_len;
70 	GtkHex_AutoHighlight *auto_highlight;
71 } AdvancedFind_ListData;
72 
create_hex_view(HexDocument * doc)73 static GtkWidget *create_hex_view(HexDocument *doc)
74 {
75     GtkWidget *gh = hex_document_add_view(doc);
76 
77 	gtk_hex_set_group_type(GTK_HEX(gh), def_group_type);
78 	if (def_metrics && def_font_desc) {
79 		gtk_hex_set_font(GTK_HEX(gh), def_metrics, def_font_desc);
80 	}
81 	gtk_hex_set_insert_mode(GTK_HEX(gh), TRUE);
82 	gtk_hex_set_geometry(GTK_HEX(gh), 16, 4);
83     return gh;
84 }
85 
86 /* Helper functions to set up copy/paste keybindings */
87 static gboolean
keypress_cb(GtkWidget * dialog,GdkEventKey * event,gpointer user_data)88 keypress_cb (GtkWidget *dialog, GdkEventKey *event, gpointer user_data)
89 {
90 	GtkWidget *gh = gtk_window_get_focus (GTK_WINDOW(dialog));
91 
92 	/* If there isn't a GtkHex widget with focus, bail out. */
93 	if (! GTK_IS_HEX (gh)) goto out;
94 
95 	/* Otherwise, handle clipboard shortcuts. */
96 	if (event->state & GDK_CONTROL_MASK)
97 	{
98 		switch (event->keyval)
99 		{
100 			case 'c':
101 				gtk_hex_copy_to_clipboard (GTK_HEX(gh));
102 				return GDK_EVENT_STOP;
103 				break;
104 
105 			case 'x':
106 				gtk_hex_cut_to_clipboard (GTK_HEX(gh));
107 				return GDK_EVENT_STOP;
108 				break;
109 
110 			case 'v':
111 				gtk_hex_paste_from_clipboard (GTK_HEX(gh));
112 				return GDK_EVENT_STOP;
113 				break;
114 		}
115 	}
116 out:
117 	return GDK_EVENT_PROPAGATE;
118 }
119 
120 static void
setup_clipboard_keybindings(GtkWidget * dialog)121 setup_clipboard_keybindings (GtkWidget *dialog)
122 {
123 	/* sanity check: all find/replace/etc. dialogs are GtkDialogs at their core */
124 	g_assert (GTK_IS_DIALOG (dialog));
125 
126 	g_signal_connect (dialog, "key-press-event", G_CALLBACK(keypress_cb), NULL);
127 }
128 
create_find_dialog()129 FindDialog *create_find_dialog()
130 {
131 	FindDialog *dialog;
132 	GtkWidget *frame;
133 
134 	dialog = g_new0(FindDialog, 1);
135 
136 	dialog->window = gtk_dialog_new();
137 	g_signal_connect(G_OBJECT(dialog->window), "delete_event",
138 					 G_CALLBACK(find_delete_event_cb), dialog);
139 
140 	create_dialog_title(dialog->window, _("GHex (%s): Find Data"));
141 
142 	dialog->f_doc = hex_document_new();
143 	dialog->f_gh = create_hex_view(dialog->f_doc);
144 	frame = gtk_frame_new(_("Find String"));
145 	gtk_container_add(GTK_CONTAINER(frame), dialog->f_gh);
146 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), frame,
147 					   TRUE, TRUE, 0);
148 	gtk_widget_show(frame);
149 	gtk_widget_show(dialog->f_gh);
150 
151 	dialog->f_next = create_button(dialog->window, GTK_STOCK_GO_FORWARD, _("Find _Next"));
152 	g_signal_connect (G_OBJECT (dialog->f_next), "clicked",
153 					  G_CALLBACK(find_next_cb), dialog);
154 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->f_next,
155 					   TRUE, TRUE, 0);
156 	gtk_widget_set_can_default(dialog->f_next, TRUE);
157 	gtk_widget_show(dialog->f_next);
158 	dialog->f_prev = create_button(dialog->window, GTK_STOCK_GO_BACK, _("Find _Previous"));
159 	g_signal_connect (G_OBJECT (dialog->f_prev), "clicked",
160 					  G_CALLBACK(find_prev_cb), dialog);
161 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->f_prev,
162 					   TRUE, TRUE, 0);
163 
164 	gtk_widget_set_can_default(dialog->f_prev, TRUE);
165 	gtk_widget_show(dialog->f_prev);
166 
167 	dialog->f_close = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
168 	g_signal_connect (G_OBJECT (dialog->f_close),
169 					  "clicked", G_CALLBACK(find_cancel_cb),
170 					  dialog);
171 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->f_close,
172 					   TRUE, TRUE, 0);
173 
174 	gtk_widget_set_can_default(dialog->f_close, TRUE);
175 	gtk_widget_show(dialog->f_close);
176 
177 	gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), 2);
178 	gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), 2);
179 
180 	setup_clipboard_keybindings (dialog->window);
181 
182 	if (GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (dialog->f_gh))) {
183 		add_atk_namedesc (dialog->f_gh, _("Find Data"), _("Enter the hex data or ASCII data to search for"));
184 		add_atk_namedesc (dialog->f_next, _("Find Next"), _("Finds the next occurrence of the search string"));
185 		add_atk_namedesc (dialog->f_prev, _("Find previous"), _("Finds the previous occurrence of the search string "));
186 		add_atk_namedesc (dialog->f_close, _("Cancel"), _("Closes find data window"));
187 	}
188 
189 	return dialog;
190 }
191 
create_advanced_find_add_dialog(AdvancedFindDialog * parent)192 static AdvancedFind_AddDialog *create_advanced_find_add_dialog(AdvancedFindDialog *parent)
193 {
194 	AdvancedFind_AddDialog *dialog = g_new0(AdvancedFind_AddDialog, 1);
195 	GtkWidget *button, *frame, *sep;
196 
197 	dialog->window = gtk_dialog_new();
198 	gtk_widget_hide(dialog->window);
199 	g_signal_connect(G_OBJECT(dialog->window), "delete_event",
200 					 G_CALLBACK(delete_event_cb), dialog->window);
201 
202 	create_dialog_title(dialog->window, _("GHex (%s): Find Data: Add search"));
203 
204 	dialog->f_doc = hex_document_new();
205 	dialog->f_gh = create_hex_view(dialog->f_doc);
206 	frame = gtk_frame_new(_("Find String"));
207 	gtk_container_add(GTK_CONTAINER(frame), dialog->f_gh);
208 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), frame,
209 					   TRUE, TRUE, 0);
210 	gtk_widget_show(frame);
211 	gtk_widget_show(dialog->f_gh);
212 
213 	sep = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
214 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), sep,
215 					   FALSE, FALSE, 0);
216 
217 	dialog->colour = gtk_color_selection_new();
218 	gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(dialog->colour),
219 												FALSE);
220 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))),
221 					   dialog->colour, FALSE, FALSE, 0);
222 	gtk_widget_show(dialog->colour);
223 
224 	button = create_button(dialog->window, GTK_STOCK_ADD, _("Add"));
225 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), button,
226 					   TRUE, TRUE, 0);
227 	g_signal_connect (G_OBJECT (button),
228 					  "clicked", G_CALLBACK(advanced_find_add_add_cb),
229 					  dialog);
230 	gtk_widget_set_can_default(button, TRUE);
231 	gtk_widget_show(button);
232 
233 	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
234 	g_signal_connect (G_OBJECT (button),
235 					  "clicked", G_CALLBACK(cancel_cb),
236 					  dialog->window);
237 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), button,
238 					   TRUE, TRUE, 0);
239 	gtk_widget_set_can_default(button, TRUE);
240 	gtk_widget_show(button);
241 
242 	setup_clipboard_keybindings (dialog->window);
243 
244 	return dialog;
245 }
246 
create_advanced_find_dialog(GHexWindow * parent)247 AdvancedFindDialog *create_advanced_find_dialog(GHexWindow *parent)
248 {
249 	AdvancedFindDialog *dialog;
250 	GtkCellRenderer *renderer;
251 	GtkTreeViewColumn *column;
252 	GtkTreeSelection *selection;
253 
254 	GtkWidget *sep;
255 
256 	dialog = g_new0(AdvancedFindDialog, 1);
257 
258 	dialog->parent = parent;
259 
260 	dialog->addDialog = create_advanced_find_add_dialog(dialog);
261 
262 	dialog->window = gtk_dialog_new();
263 	g_signal_connect(G_OBJECT(dialog->window), "delete_event",
264 					 G_CALLBACK(advanced_find_delete_event_cb), dialog);
265 
266 	gtk_window_set_default_size(GTK_WINDOW(dialog->window), 300, 350);
267 
268 	create_dialog_title(dialog->window, _("GHex (%s): Find Data"));
269 
270 	dialog->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
271 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))),
272 					   dialog->hbox, TRUE, TRUE, 4);
273 	gtk_widget_show(dialog->hbox);
274 
275 	dialog->list = gtk_list_store_new(3,
276 									  G_TYPE_STRING, G_TYPE_STRING,
277 			                          G_TYPE_POINTER, G_TYPE_POINTER);
278 	dialog->tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (dialog->list));
279 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree));
280 	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
281 	renderer = gtk_cell_renderer_text_new ();
282 	column = gtk_tree_view_column_new_with_attributes (_("Search String"),
283 													   renderer,
284 													   "text", 0,
285 													   "foreground", 1,
286 													   NULL);
287 	gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->tree), column);
288 
289 	renderer = gtk_cell_renderer_text_new ();
290 	column = gtk_tree_view_column_new_with_attributes (_("Highlight Colour"),
291 													   renderer,
292 													   "background", 1,
293 													   NULL);
294 	gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->tree), column);
295 
296 	gtk_box_pack_start(GTK_BOX(dialog->hbox), dialog->tree,
297 					   TRUE, TRUE, 4);
298 	gtk_widget_show (dialog->tree);
299 
300 	dialog->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
301 	gtk_box_pack_start(GTK_BOX(dialog->hbox), dialog->vbox,
302 					   FALSE, FALSE, 4);
303 	gtk_widget_show(dialog->vbox);
304 
305 	dialog->f_next = create_button(dialog->window, GTK_STOCK_GO_FORWARD, _("Find _Next"));
306 	gtk_box_pack_start(GTK_BOX(dialog->vbox), dialog->f_next,
307 					   FALSE, FALSE, 0);
308 	g_signal_connect (G_OBJECT (dialog->f_next),
309 					  "clicked", G_CALLBACK(advanced_find_next_cb),
310 					  dialog);
311 	gtk_widget_set_can_default(dialog->f_next, TRUE);
312 	gtk_widget_show(dialog->f_next);
313 
314 	dialog->f_prev = create_button(dialog->window, GTK_STOCK_GO_BACK, _("Find _Previous"));
315 	gtk_box_pack_start(GTK_BOX(dialog->vbox), dialog->f_prev,
316 					   FALSE, FALSE, 0);
317 	g_signal_connect (G_OBJECT (dialog->f_prev),
318 					  "clicked", G_CALLBACK(advanced_find_prev_cb),
319 					  dialog);
320 	gtk_widget_set_can_default(dialog->f_prev, TRUE);
321 	gtk_widget_show(dialog->f_prev);
322 
323 	sep = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
324 	gtk_box_pack_start(GTK_BOX(dialog->vbox), sep, FALSE, FALSE, 4);
325 	gtk_widget_show(sep);
326 
327 	dialog->f_new = create_button(dialog->window, GTK_STOCK_ADD, _("_Add New"));
328 	gtk_box_pack_start(GTK_BOX(dialog->vbox), dialog->f_new,
329 					   FALSE, FALSE, 0);
330 	g_signal_connect (G_OBJECT (dialog->f_new),
331 					  "clicked", G_CALLBACK(advanced_find_add_cb),
332 					  dialog);
333 	gtk_widget_set_can_default(dialog->f_new, TRUE);
334 	gtk_widget_show(dialog->f_new);
335 
336 	dialog->f_remove = create_button(dialog->window, GTK_STOCK_REMOVE, _("_Remove Selected"));
337 	gtk_box_pack_start(GTK_BOX(dialog->vbox), dialog->f_remove,
338 					   FALSE, FALSE, 0);
339 	g_signal_connect (G_OBJECT (dialog->f_remove),
340 					  "clicked", G_CALLBACK(advanced_find_delete_cb),
341 					  dialog);
342 	gtk_widget_set_can_default(dialog->f_remove, TRUE);
343 	gtk_widget_show(dialog->f_remove);
344 
345 	dialog->f_close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
346 	g_signal_connect(G_OBJECT(dialog->f_close),
347 					 "clicked", G_CALLBACK(advanced_find_close_cb),
348 					 dialog);
349 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))),
350 					   dialog->f_close, TRUE, TRUE, 0);
351 	gtk_widget_set_can_default(dialog->f_close, TRUE);
352 	gtk_widget_show(dialog->f_close);
353 
354 	if (GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (dialog->f_close)))
355 	{
356 		add_atk_namedesc(dialog->f_close, _("Close"), _("Closes advanced find window"));
357 	}
358 
359 	return dialog;
360 }
361 
delete_advanced_find_add_dialog(AdvancedFind_AddDialog * dialog)362 static void delete_advanced_find_add_dialog(AdvancedFind_AddDialog *dialog)
363 {
364 	gtk_widget_destroy(GTK_WIDGET(dialog->window));
365 	g_free(dialog);
366 }
367 
advanced_find_foreachfunc_cb(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)368 static gboolean advanced_find_foreachfunc_cb (GtkTreeModel *model,
369                                               GtkTreePath  *path,
370                                               GtkTreeIter  *iter,
371                                               gpointer      data)
372 {
373 	AdvancedFind_ListData *udata;
374 	GtkHex *gh = (GtkHex *)data;
375 	gtk_tree_model_get(model, iter, 2, &udata, -1);
376 	gtk_hex_delete_autohighlight(gh, udata->auto_highlight);
377 	if(NULL != udata->str)
378 		g_free(udata->str);
379 	g_free(udata);
380 	return FALSE;
381 }
382 
delete_advanced_find_dialog(AdvancedFindDialog * dialog)383 void delete_advanced_find_dialog(AdvancedFindDialog *dialog)
384 {
385 	delete_advanced_find_add_dialog(dialog->addDialog);
386 	gtk_tree_model_foreach(GTK_TREE_MODEL(dialog->list),
387 						   advanced_find_foreachfunc_cb, (gpointer *)dialog->parent->gh);
388 	g_free(dialog);
389 }
390 
create_replace_dialog()391 ReplaceDialog *create_replace_dialog()
392 {
393 	ReplaceDialog *dialog;
394 	GtkWidget *frame;
395 
396 	dialog = g_new0(ReplaceDialog, 1);
397 
398 	dialog->window = gtk_dialog_new();
399 	g_signal_connect(G_OBJECT(dialog->window), "delete_event",
400 					 G_CALLBACK(delete_event_cb), dialog->window);
401 
402 	create_dialog_title(dialog->window, _("GHex (%s): Find & Replace Data"));
403 
404 	dialog->f_doc = hex_document_new();
405 	dialog->f_gh = create_hex_view(dialog->f_doc);
406 	frame = gtk_frame_new(_("Find String"));
407 	gtk_container_add(GTK_CONTAINER(frame), dialog->f_gh);
408 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), frame,
409 					   TRUE, TRUE, 0);
410 	gtk_widget_show(frame);
411 	gtk_widget_show(dialog->f_gh);
412 
413 	dialog->r_doc = hex_document_new();
414 	dialog->r_gh = create_hex_view(dialog->r_doc);
415 	frame = gtk_frame_new(_("Replace With"));
416 	gtk_container_add(GTK_CONTAINER(frame), dialog->r_gh);
417 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), frame,
418 					   TRUE, TRUE, 0);
419 	gtk_widget_show(frame);
420 	gtk_widget_show(dialog->r_gh);
421 
422 	dialog->next = create_button(dialog->window, GTK_STOCK_GO_FORWARD, _("Find _next"));
423 	g_signal_connect (G_OBJECT (dialog->next),
424 					  "clicked", G_CALLBACK(replace_next_cb),
425 					  NULL);
426 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->next,
427 					   TRUE, TRUE, 0);
428 	gtk_widget_set_can_default(dialog->next, TRUE);
429 	gtk_widget_show(dialog->next);
430 	dialog->replace = gtk_button_new_with_mnemonic(_("_Replace"));
431 	g_signal_connect (G_OBJECT (dialog->replace),
432 					  "clicked", G_CALLBACK(replace_one_cb),
433 					  NULL);
434 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->replace,
435 					   TRUE, TRUE, 0);
436 	gtk_widget_set_can_default(dialog->replace, TRUE);
437 	gtk_widget_show(dialog->replace);
438 	dialog->replace_all= gtk_button_new_with_mnemonic(_("Replace _All"));
439 	g_signal_connect (G_OBJECT (dialog->replace_all),
440 					  "clicked", G_CALLBACK(replace_all_cb),
441 					  NULL);
442 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->replace_all,
443 					   TRUE, TRUE, 0);
444 	gtk_widget_set_can_default(dialog->replace_all, TRUE);
445 	gtk_widget_show(dialog->replace_all);
446 
447 	dialog->close = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
448 	g_signal_connect (G_OBJECT (dialog->close),
449 					  "clicked", G_CALLBACK(cancel_cb),
450 					  dialog->window);
451 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->close,
452 					   TRUE, TRUE, 0);
453 	gtk_widget_set_can_default(dialog->close, TRUE);
454 	gtk_widget_show(dialog->close);
455 
456 	gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), 2);
457 	gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), 2);
458 
459 	setup_clipboard_keybindings (dialog->window);
460 
461 	if (GTK_IS_ACCESSIBLE(gtk_widget_get_accessible(dialog->f_gh))) {
462 		add_atk_namedesc (dialog->f_gh, _("Find Data"), _("Enter the hex data or ASCII data to search for"));
463 		add_atk_namedesc (dialog->r_gh, _("Replace Data"), _("Enter the hex data or ASCII data to replace with"));
464 		add_atk_namedesc (dialog->next, _("Find next"), _("Finds the next occurrence of the search string"));
465 		add_atk_namedesc (dialog->replace, _("Replace"), _("Replaces the search string with the replace string"));
466 		add_atk_namedesc (dialog->replace_all, _("Replace All"), _("Replaces all occurrences of the search string with the replace string"));
467 		add_atk_namedesc (dialog->close, _("Cancel"), _("Closes find and replace data window"));
468 	}
469 
470 	return dialog;
471 }
472 
create_jump_dialog()473 JumpDialog *create_jump_dialog()
474 {
475 	JumpDialog *dialog;
476 
477 	dialog = g_new0(JumpDialog, 1);
478 
479 	dialog->window = gtk_dialog_new();
480 	g_signal_connect(G_OBJECT(dialog->window), "delete_event",
481 					 G_CALLBACK(delete_event_cb), dialog->window);
482 
483 	create_dialog_title(dialog->window, _("GHex (%s): Jump To Byte"));
484 
485 	dialog->int_entry = gtk_entry_new();
486 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), dialog->int_entry,
487 					   TRUE, TRUE, 0);
488 	g_signal_connect_swapped (G_OBJECT (dialog->int_entry),
489 	                          "activate", G_CALLBACK(gtk_window_activate_default),
490 	                          GTK_WINDOW (dialog->window));
491 	gtk_widget_show(dialog->int_entry);
492 
493 	dialog->ok = gtk_button_new_from_stock (GTK_STOCK_OK);
494 	g_signal_connect (G_OBJECT (dialog->ok),
495 					  "clicked", G_CALLBACK(goto_byte_cb),
496 					  dialog->int_entry);
497 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->ok,
498 					   TRUE, TRUE, 0);
499 
500 	gtk_widget_set_can_default(dialog->ok, TRUE);
501 	gtk_widget_show(dialog->ok);
502 	dialog->cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
503 	g_signal_connect (G_OBJECT (dialog->cancel),
504 					  "clicked", G_CALLBACK(cancel_cb),
505 					  dialog->window);
506 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog->window))), dialog->cancel,
507 					   TRUE, TRUE, 0);
508 
509 	gtk_widget_set_can_default(dialog->cancel, TRUE);
510 	gtk_widget_show(dialog->cancel);
511 
512 	gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), 2);
513 	gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog->window))), 2);
514 
515 	if (GTK_IS_ACCESSIBLE (gtk_widget_get_accessible(dialog->int_entry))) {
516 		add_atk_namedesc (dialog->int_entry, _("Jump to byte"), _("Enter the byte to jump to"));
517 		add_atk_namedesc (dialog->ok, _("OK"), _("Jumps to the specified byte"));
518 		add_atk_namedesc (dialog->cancel, _("Cancel"), _("Closes jump to byte window"));
519 	}
520 
521 	return dialog;
522 }
523 
get_search_string(HexDocument * doc,gchar ** str)524 static gint get_search_string(HexDocument *doc, gchar **str)
525 {
526 	guint size = doc->file_size;
527 
528 	if(size > 0)
529 		*str = (gchar *)hex_document_get_data(doc, 0, size);
530 	else
531 		*str = NULL;
532 	return size;
533 }
534 
535 /* find and advanced find need special close dialogs, since they
536  * need to do stuff with the highlights
537  */
find_delete_event_cb(GtkWidget * w,GdkEventAny * e,FindDialog * dialog)538 static gint find_delete_event_cb(GtkWidget *w, GdkEventAny *e, FindDialog *dialog)
539 {
540 	GHexWindow *win = ghex_window_get_active();
541 	GtkHex *gh = win->gh;
542 
543 	if (dialog->auto_highlight) gtk_hex_delete_autohighlight(gh, dialog->auto_highlight);
544 	dialog->auto_highlight = NULL;
545 	gtk_widget_hide(w);
546 
547 	return TRUE;
548 }
549 
find_cancel_cb(GtkWidget * w,FindDialog * dialog)550 static void find_cancel_cb(GtkWidget *w, FindDialog *dialog)
551 {
552 	GHexWindow *win = ghex_window_get_active();
553 	GtkHex *gh = win->gh;
554 
555 	if (dialog->auto_highlight) gtk_hex_delete_autohighlight(gh, dialog->auto_highlight);
556 	dialog->auto_highlight = NULL;
557 	gtk_widget_hide(dialog->window);
558 }
559 
advanced_find_delete_event_cb(GtkWidget * w,GdkEventAny * e,AdvancedFindDialog * dialog)560 static gint advanced_find_delete_event_cb(GtkWidget *w, GdkEventAny *e,
561 										  AdvancedFindDialog *dialog)
562 {
563 	gtk_widget_hide(w);
564 
565 	return TRUE;
566 }
567 
advanced_find_close_cb(GtkWidget * w,AdvancedFindDialog * dialog)568 static void advanced_find_close_cb(GtkWidget *w, AdvancedFindDialog *dialog)
569 {
570 	gtk_widget_hide(dialog->window);
571 }
572 
find_next_cb(GtkButton * button,FindDialog * dialog)573 static void find_next_cb(GtkButton *button, FindDialog *dialog)
574 {
575 	GtkHex *gh;
576 	guint offset, str_len;
577 	gchar *str;
578 	GHexWindow *win = ghex_window_get_active();
579 
580 	if(win == NULL || win->gh == NULL) {
581 		display_error_dialog (win, _("There is no active document to search!"));
582 		return;
583 	}
584 
585 	gh = win->gh;
586 
587 	if((str_len = get_search_string(find_dialog->f_doc, &str)) == 0) {
588 		display_error_dialog (win, _("There is no string to search for!"));
589 		return;
590 	}
591 	if (dialog->auto_highlight) gtk_hex_delete_autohighlight(gh, dialog->auto_highlight);
592 	dialog->auto_highlight = NULL;
593 	dialog->auto_highlight = gtk_hex_insert_autohighlight(gh, str, str_len, "red");
594 	if(hex_document_find_forward(gh->document,
595 								 gh->cursor_pos+1, str, str_len, &offset))
596 	{
597 		gtk_hex_set_cursor(gh, offset);
598 	}
599 	else {
600 		ghex_window_flash(win, _("End Of File reached"));
601 		display_info_dialog(win, _("String was not found!\n"));
602 	}
603 	if(NULL != str)
604 		g_free(str);
605 }
606 
find_prev_cb(GtkButton * button,FindDialog * dialog)607 static void find_prev_cb(GtkButton *button, FindDialog *dialog)
608 {
609 	GtkHex *gh;
610 	guint offset, str_len;
611 	gchar *str;
612 	GHexWindow *win = ghex_window_get_active();
613 
614 	if(win == NULL || win->gh == NULL) {
615 		display_error_dialog (win, _("There is no active document to search!"));
616 		return;
617 	}
618 
619 	gh = win->gh;
620 
621 	if((str_len = get_search_string(find_dialog->f_doc, &str)) == 0) {
622 		display_error_dialog (win, _("There is no string to search for!"));
623 		return;
624 	}
625 
626 	if (dialog->auto_highlight) gtk_hex_delete_autohighlight(gh, dialog->auto_highlight);
627 	dialog->auto_highlight = NULL;
628 	dialog->auto_highlight = gtk_hex_insert_autohighlight(gh, str, str_len, "red");
629 	if(hex_document_find_backward(gh->document,
630 								  gh->cursor_pos, str, str_len, &offset))
631 		gtk_hex_set_cursor(gh, offset);
632 	else {
633 		ghex_window_flash(win, _("Beginning Of File reached"));
634 		display_info_dialog(win, _("String was not found!\n"));
635 	}
636 	if(NULL != str)
637 		g_free(str);
638 }
639 
goto_byte_cb(GtkButton * button,GtkWidget * w)640 static void goto_byte_cb(GtkButton *button, GtkWidget *w)
641 {
642 	guint byte = 2, len, i;
643 	gint is_relative = 0;
644 	gboolean is_hex;
645 	const gchar *byte_str = gtk_entry_get_text(GTK_ENTRY(jump_dialog->int_entry));
646 	GHexWindow *win = ghex_window_get_active();
647 
648 	if(win == NULL || win->gh == NULL) {
649 		display_error_dialog (win,
650 							  _("There is no active document to move the "
651 								"cursor in!"));
652 		return;
653 	}
654 
655 	len = strlen(byte_str);
656 
657 	if(len > 1 && byte_str[0] == '+') {
658 		is_relative = 1;
659 		byte_str++;
660 		len--;
661 	} else if(len > 1 && byte_str[0] == '-') {
662 		is_relative = -1;
663 		byte_str++;
664 		len--;
665 	}
666 
667 	if(len == 0) {
668 		display_error_dialog (win, _("No offset has been specified!"));
669 		return;
670 	}
671 
672 	is_hex = ((len > 2) && (byte_str[0] == '0') && (byte_str[1] == 'x'));
673 
674 	if(!is_hex) {
675 		for(i = 0; i < len; i++)
676 			if(!(byte_str[i] >= '0' && byte_str[i] <= '9'))
677 				break;
678 	}
679 	else {
680 		for(i = 2; i < len; i++)
681 			if(!((byte_str[i] >= '0' && byte_str[i] <= '9') ||
682 				 (byte_str[i] >= 'A' && byte_str[i] <= 'F') ||
683 				 (byte_str[i] >= 'a' && byte_str[i] <= 'f')))
684 				break;
685 	}
686 
687 	if((i == len) &&
688 	   ((sscanf(byte_str, "0x%x", &byte) == 1) ||
689 		(sscanf(byte_str, "%d", &byte) == 1))) {
690 		if(is_relative) {
691 			if(is_relative == -1 && byte > win->gh->cursor_pos) {
692 				display_error_dialog(win,
693 								 _("The specified offset is beyond the "
694 								" file boundaries!"));
695 				return;
696 			}
697 			byte = byte * is_relative + win->gh->cursor_pos;
698 		}
699 		if(byte >= win->gh->document->file_size)
700 			display_error_dialog(win,
701 								 _("Can not position cursor beyond the "
702 								   "End Of File!"));
703 		else
704 			gtk_hex_set_cursor(win->gh, byte);
705 	}
706 	else
707 		display_error_dialog(win,
708 							 _("You may only give the offset as:\n"
709 							   "  - a positive decimal number, or\n"
710 							   "  - a hex number, beginning with '0x', or\n"
711 							   "  - a '+' or '-' sign, followed by a relative offset"));
712 }
713 
replace_next_cb(GtkButton * button,gpointer unused)714 static void replace_next_cb(GtkButton *button, gpointer unused)
715 {
716 	GtkHex *gh;
717 	guint offset, str_len;
718 	gchar *str = NULL;
719 	GHexWindow *win = ghex_window_get_active();
720 
721 	if(win == NULL || win->gh == NULL) {
722 		display_error_dialog (win, _("There is no active document to search!"));
723 		return;
724 	}
725 
726 	gh = win->gh;
727 
728 	if((str_len = get_search_string(replace_dialog->f_doc, &str)) == 0) {
729 		display_error_dialog (win, _("There is no string to search for!"));
730 		return;
731 	}
732 
733 	if(hex_document_find_forward(gh->document,
734 								 gh->cursor_pos+1, str, str_len, &offset))
735 		gtk_hex_set_cursor(gh, offset);
736 	else {
737 		display_info_dialog(win, _("String was not found!\n"));
738 		ghex_window_flash(win, _("End Of File reached"));
739 	}
740 
741 	if(NULL != str)
742 		g_free(str);
743 }
744 
replace_one_cb(GtkButton * button,gpointer unused)745 static void replace_one_cb(GtkButton *button, gpointer unused)
746 {
747 	gchar *find_str = NULL, *rep_str = NULL;
748 	guint find_len, rep_len, offset;
749 	GtkHex *gh;
750 	HexDocument *doc;
751 	GHexWindow *win = ghex_window_get_active();
752 
753 	if(win == NULL || win->gh == NULL) {
754 		display_error_dialog (win, _("There is no active buffer to replace data in!"));
755 		return;
756 	}
757 
758 	gh = win->gh;
759 
760 	doc = win->gh->document;
761 
762 	if((find_len = get_search_string(replace_dialog->f_doc, &find_str)) == 0) {
763 		display_error_dialog (win, _("There is no string to search for!"));
764 		return;
765 	}
766 	rep_len = get_search_string(replace_dialog->r_doc, &rep_str);
767 
768 	if(find_len > doc->file_size - gh->cursor_pos)
769 		goto clean_up;
770 
771 	if(hex_document_compare_data(doc, find_str, gh->cursor_pos, find_len) == 0)
772 		hex_document_set_data(doc, gh->cursor_pos,
773 							  rep_len, find_len, rep_str, TRUE);
774 
775 	if(hex_document_find_forward(doc, gh->cursor_pos + rep_len, find_str, find_len,
776 								 &offset))
777 		gtk_hex_set_cursor(gh, offset);
778 	else {
779 		display_info_dialog(win, _("End Of File reached!"));
780 		ghex_window_flash(win, _("End Of File reached!"));
781 	}
782 
783  clean_up:
784 	if(NULL != find_str)
785 		g_free(find_str);
786 	if(NULL != rep_str)
787 		g_free(rep_str);
788 }
789 
replace_all_cb(GtkButton * button,gpointer unused)790 static void replace_all_cb(GtkButton *button, gpointer unused)
791 {
792 	gchar *find_str = NULL, *rep_str = NULL, *flash;
793 	guint find_len, rep_len, offset, count, cursor_pos;
794 	GtkHex *gh;
795 	HexDocument *doc;
796 	GHexWindow *win = ghex_window_get_active();
797 
798 	if(win == NULL || win->gh == NULL) {
799 		display_error_dialog (win, _("There is no active document to replace data in!"));
800 		return;
801 	}
802 
803 	gh = win->gh;
804 
805 	doc = gh->document;
806 
807 	if((find_len = get_search_string(replace_dialog->f_doc, &find_str)) == 0) {
808 		display_error_dialog (win, _("There is no string to search for!"));
809 		return;
810 	}
811 	rep_len = get_search_string(replace_dialog->r_doc, &rep_str);
812 
813 	if(find_len > doc->file_size - gh->cursor_pos)
814 		goto clean_up;
815 
816 	count = 0;
817 	cursor_pos = 0;
818 
819 	while(hex_document_find_forward(doc, cursor_pos, find_str, find_len,
820 									&offset)) {
821 		hex_document_set_data(doc, offset, rep_len, find_len, rep_str, TRUE);
822 		cursor_pos = offset + rep_len;
823 		count++;
824 	}
825 
826 	gtk_hex_set_cursor(gh, MIN(offset, doc->file_size));
827 
828 	if(count == 0) {
829 		display_info_dialog(win, _("No occurrences were found."));
830 	}
831 
832 	flash = g_strdup_printf(ngettext("Replaced %d occurrence.",
833 									 "Replaced %d occurrences.",
834 									 count), count);
835 	ghex_window_flash(win, flash);
836 	g_free(flash);
837 
838  clean_up:
839 	if(NULL != find_str)
840 		g_free(find_str);
841 	if(NULL != rep_str)
842 		g_free(rep_str);
843 }
844 
advanced_find_add_add_cb(GtkButton * button,AdvancedFind_AddDialog * dialog)845 static void advanced_find_add_add_cb(GtkButton *button,
846 									 AdvancedFind_AddDialog *dialog)
847 {
848 	gtk_dialog_response(GTK_DIALOG(dialog->window), GTK_RESPONSE_OK);
849 }
850 
advanced_find_add_cb(GtkButton * button,AdvancedFindDialog * dialog)851 static void advanced_find_add_cb(GtkButton *button, AdvancedFindDialog *dialog)
852 {
853 	gint ret;
854 	if(!gtk_widget_get_visible(dialog->addDialog->window)) {
855 		gtk_window_set_position (GTK_WINDOW(dialog->addDialog->window), GTK_WIN_POS_MOUSE);
856 		gtk_widget_show(dialog->addDialog->window);
857 	}
858 
859 	ret = gtk_dialog_run(GTK_DIALOG(dialog->addDialog->window));
860 	gtk_widget_hide(dialog->addDialog->window);
861 	if (ret != GTK_RESPONSE_NONE)
862 	{
863 		gchar *colour;
864 		GdkRGBA rgba;
865 		AdvancedFind_ListData *data = g_new0(AdvancedFind_ListData, 1);
866 		GtkHex *gh = dialog->parent->gh;
867 		GtkTreeIter iter;
868 
869 		g_return_if_fail (gh != NULL);
870 
871 		if((data->str_len = get_search_string(dialog->addDialog->f_doc, &data->str)) == 0) {
872 			display_error_dialog (dialog->parent, _("No string to search for!"));
873 			return;
874 		}
875 		gtk_color_selection_get_current_rgba (GTK_COLOR_SELECTION (dialog->addDialog->colour),
876 		                                      &rgba);
877 		colour = gdk_rgba_to_string (&rgba);
878 		data->auto_highlight = gtk_hex_insert_autohighlight(gh, data->str, data->str_len, colour);
879 		gtk_list_store_append(dialog->list, &iter);
880 		gtk_list_store_set(dialog->list, &iter,
881 						   0, data->str,
882 						   1, colour,
883 						   2, data,
884 						   -1);
885 		g_free(colour);
886 	}
887 }
888 
advanced_find_delete_cb(GtkButton * button,AdvancedFindDialog * dialog)889 static void advanced_find_delete_cb(GtkButton *button, AdvancedFindDialog *dialog)
890 {
891 	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree));
892 	GtkTreeIter iter;
893 	GtkTreeModel *model;
894 	AdvancedFind_ListData *data;
895 	GtkHex *gh = dialog->parent->gh;
896 
897 	if (gtk_tree_selection_get_selected(selection, &model, &iter) != TRUE)
898 		return;
899 
900 	gtk_tree_model_get(model, &iter, 2, &data, -1);
901 	gtk_hex_delete_autohighlight(gh, data->auto_highlight);
902 	if(NULL != data->str)
903 		g_free(data->str);
904 	g_free(data);
905 	gtk_list_store_remove(dialog->list, &iter);
906 }
907 
advanced_find_next_cb(GtkButton * button,AdvancedFindDialog * dialog)908 static void advanced_find_next_cb(GtkButton *button, AdvancedFindDialog *dialog)
909 {
910 	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree));
911 	GtkTreeIter iter;
912 	GtkTreeModel *model;
913 	AdvancedFind_ListData *data;
914 	GtkHex *gh = dialog->parent->gh;
915 	guint offset;
916 	GHexWindow *win = ghex_window_get_active();
917 
918 	if (gtk_tree_selection_get_selected(selection, &model, &iter) != TRUE)
919 		return;
920 
921 	gtk_tree_model_get(model, &iter, 2, &data, -1);
922 	if(hex_document_find_forward(gh->document,
923 								 gh->cursor_pos+1, data->str, data->str_len, &offset))
924 	{
925 		gtk_hex_set_cursor(gh, offset);
926 	}
927 	else {
928 		ghex_window_flash(win, _("End Of File reached"));
929 		display_info_dialog(win, _("String was not found!\n"));
930 	}
931 }
932 
advanced_find_prev_cb(GtkButton * button,AdvancedFindDialog * dialog)933 static void advanced_find_prev_cb(GtkButton *button, AdvancedFindDialog *dialog)
934 {
935 	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree));
936 	GtkTreeIter iter;
937 	GtkTreeModel *model;
938 	AdvancedFind_ListData *data;
939 	GtkHex *gh = dialog->parent->gh;
940 	guint offset;
941 	GHexWindow *win = ghex_window_get_active();
942 
943 	if (gtk_tree_selection_get_selected(selection, &model, &iter) != TRUE)
944 		return;
945 
946 	gtk_tree_model_get(model, &iter, 2, &data, -1);
947 	if(hex_document_find_backward(gh->document,
948 								  gh->cursor_pos, data->str, data->str_len, &offset))
949 		gtk_hex_set_cursor(gh, offset);
950 	else {
951 		ghex_window_flash(win, _("Beginning Of File reached"));
952 		display_info_dialog(win, _("String was not found!\n"));
953 	}
954 }
955 
956