geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

search.c
Go to the documentation of this file.
1/*
2 * search.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2006 The Geany contributors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/*
22 * Find, Replace, Find in Files dialog related functions.
23 * Note that the basic text find functions are in document.c.
24 */
25
26#ifdef HAVE_CONFIG_H
27# include "config.h"
28#endif
29
30#include "search.h"
31
32#include "app.h"
33#include "document.h"
34#include "encodings.h"
35#include "encodingsprivate.h"
36#include "keyfile.h"
37#include "msgwindow.h"
38#include "prefs.h"
39#include "sciwrappers.h"
40#include "spawn.h"
41#include "stash.h"
42#include "support.h"
43#include "toolbar.h"
44#include "ui_utils.h"
45#include "utils.h"
46
47#include <unistd.h>
48#include <string.h>
49#include <ctype.h>
50
51#include <gtk/gtk.h>
52#include <gdk/gdkkeysyms.h>
53
54enum
55{
66};
67
68
69enum
70{
74};
75
76
77GeanySearchData search_data;
79
80
81static struct
82{
83 gboolean fif_regexp;
87 gboolean fif_recursive;
91 gchar *fif_files;
92 gboolean find_regexp;
107}
109
113
114
115static struct
116{
117 GtkWidget *dialog;
118 GtkWidget *entry;
119 gboolean all_expanded;
120 gint position[2]; /* x, y */
121}
122find_dlg = {NULL, NULL, FALSE, {0, 0}};
123
124static struct
125{
126 GtkWidget *dialog;
127 GtkWidget *find_combobox;
128 GtkWidget *find_entry;
130 GtkWidget *replace_entry;
131 gboolean all_expanded;
132 gint position[2]; /* x, y */
133}
134replace_dlg = {NULL, NULL, NULL, NULL, NULL, FALSE, {0, 0}};
135
136static struct
137{
138 GtkWidget *dialog;
139 GtkWidget *dir_combo;
140 GtkWidget *files_combo;
141 GtkWidget *search_combo;
142 GtkWidget *encoding_combo;
144 gint position[2]; /* x, y */
145}
146fif_dlg = {NULL, NULL, NULL, NULL, NULL, NULL, {0, 0}};
147
148
149static void search_read_io(GString *string, GIOCondition condition, gpointer data);
150static void search_read_io_stderr(GString *string, GIOCondition condition, gpointer data);
151
152static void search_finished(GPid child_pid, gint status, gpointer user_data);
153
154static gchar **search_get_argv(const gchar **argv_prefix, const gchar *dir);
155
156static GRegex *compile_regex(const gchar *str, GeanyFindFlags sflags);
157
158
159static void
160on_find_replace_checkbutton_toggled(GtkToggleButton *togglebutton, gpointer user_data);
161
162static void
163on_find_dialog_response(GtkDialog *dialog, gint response, gpointer user_data);
164
165static void
166on_find_entry_activate(GtkEntry *entry, gpointer user_data);
167
168static void
169on_find_entry_activate_backward(GtkEntry *entry, gpointer user_data);
170
171static void
172on_replace_dialog_response(GtkDialog *dialog, gint response, gpointer user_data);
173
174static void
175on_replace_find_entry_activate(GtkEntry *entry, gpointer user_data);
176
177static void
178on_replace_entry_activate(GtkEntry *entry, gpointer user_data);
179
180static void
181on_find_in_files_dialog_response(GtkDialog *dialog, gint response, gpointer user_data);
182
183static gboolean
184search_find_in_files(const gchar *utf8_search_text, const gchar *dir, const gchar *opts,
185 const gchar *enc);
186
187
188static void init_prefs(void)
189{
191
192 group = stash_group_new("search");
195 "pref_search_hide_find_dialog", FALSE, "check_always_wrap_search");
197 "pref_search_always_wrap", FALSE, "check_hide_find_dialog");
199 "pref_search_current_file_dir", TRUE, "check_fif_current_dir");
200 stash_group_add_boolean(group, &find_dlg.all_expanded, "find_all_expanded", FALSE);
201 stash_group_add_boolean(group, &replace_dlg.all_expanded, "replace_all_expanded", FALSE);
202 /* dialog positions */
203 stash_group_add_integer(group, &find_dlg.position[0], "position_find_x", -1);
204 stash_group_add_integer(group, &find_dlg.position[1], "position_find_y", -1);
205 stash_group_add_integer(group, &replace_dlg.position[0], "position_replace_x", -1);
206 stash_group_add_integer(group, &replace_dlg.position[1], "position_replace_y", -1);
207 stash_group_add_integer(group, &fif_dlg.position[0], "position_fif_x", -1);
208 stash_group_add_integer(group, &fif_dlg.position[1], "position_fif_y", -1);
209
210 memset(&settings, '\0', sizeof(settings));
211
212 group = stash_group_new("search");
216 "fif_regexp", FALSE, "check_regexp");
217 stash_group_add_toggle_button(group, &settings.fif_case_sensitive,
218 "fif_case_sensitive", TRUE, "check_case");
219 stash_group_add_toggle_button(group, &settings.fif_match_whole_word,
220 "fif_match_whole_word", FALSE, "check_wholeword");
221 stash_group_add_toggle_button(group, &settings.fif_invert_results,
222 "fif_invert_results", FALSE, "check_invert");
224 "fif_recursive", FALSE, "check_recursive");
225 stash_group_add_entry(group, &settings.fif_extra_options,
226 "fif_extra_options", "", "entry_extra");
227 stash_group_add_toggle_button(group, &settings.fif_use_extra_options,
228 "fif_use_extra_options", FALSE, "check_extra");
230 "fif_files", "", "entry_files");
232 "fif_files_mode", FILES_MODE_ALL, "combo_files_mode");
233
234 group = stash_group_new("search");
238 "find_regexp", FALSE, "check_regexp");
239 stash_group_add_toggle_button(group, &settings.find_regexp_multiline,
240 "find_regexp_multiline", FALSE, "check_multiline");
241 stash_group_add_toggle_button(group, &settings.find_case_sensitive,
242 "find_case_sensitive", FALSE, "check_case");
243 stash_group_add_toggle_button(group, &settings.find_escape_sequences,
244 "find_escape_sequences", FALSE, "check_escape");
245 stash_group_add_toggle_button(group, &settings.find_match_whole_word,
246 "find_match_whole_word", FALSE, "check_word");
247 stash_group_add_toggle_button(group, &settings.find_match_word_start,
248 "find_match_word_start", FALSE, "check_wordstart");
249 stash_group_add_toggle_button(group, &settings.find_close_dialog,
250 "find_close_dialog", TRUE, "check_close");
251
252 group = stash_group_new("search");
256 "replace_regexp", FALSE, "check_regexp");
257 stash_group_add_toggle_button(group, &settings.replace_regexp_multiline,
258 "replace_regexp_multiline", FALSE, "check_multiline");
259 stash_group_add_toggle_button(group, &settings.replace_case_sensitive,
260 "replace_case_sensitive", FALSE, "check_case");
261 stash_group_add_toggle_button(group, &settings.replace_escape_sequences,
262 "replace_escape_sequences", FALSE, "check_escape");
263 stash_group_add_toggle_button(group, &settings.replace_match_whole_word,
264 "replace_match_whole_word", FALSE, "check_word");
265 stash_group_add_toggle_button(group, &settings.replace_match_word_start,
266 "replace_match_word_start", FALSE, "check_wordstart");
267 stash_group_add_toggle_button(group, &settings.replace_search_backwards,
268 "replace_search_backwards", FALSE, "check_back");
269 stash_group_add_toggle_button(group, &settings.replace_close_dialog,
270 "replace_close_dialog", TRUE, "check_close");
271}
272
273
274void search_init(void)
275{
276 search_data.text = NULL;
277 search_data.original_text = NULL;
278 init_prefs();
279}
280
281
282#define FREE_WIDGET(wid) \
283 if (wid && GTK_IS_WIDGET(wid)) gtk_widget_destroy(wid);
284
286{
287 FREE_WIDGET(find_dlg.dialog);
288 FREE_WIDGET(replace_dlg.dialog);
289 FREE_WIDGET(fif_dlg.dialog);
290 g_free(search_data.text);
291 g_free(search_data.original_text);
292}
293
294
296 GtkToggleButton *togglebutton, gpointer user_data)
297{
298 gtk_widget_set_sensitive(GTK_WIDGET(user_data),
299 !gtk_toggle_button_get_active(togglebutton));
300}
301
302
303static GtkWidget *add_find_checkboxes(GtkDialog *dialog)
304{
305 GtkWidget *checkbox1, *checkbox2, *check_regexp, *checkbox5,
306 *checkbox7, *check_multiline, *hbox, *fbox, *mbox;
307
308 check_regexp = gtk_check_button_new_with_mnemonic(_("_Use regular expressions"));
309 ui_hookup_widget(dialog, check_regexp, "check_regexp");
310 gtk_button_set_focus_on_click(GTK_BUTTON(check_regexp), FALSE);
311 gtk_widget_set_tooltip_text(check_regexp, _("Use Perl-like regular expressions. "
312 "For detailed information about using regular expressions, please refer to the manual."));
313 g_signal_connect(check_regexp, "toggled",
315
316 checkbox7 = gtk_check_button_new_with_mnemonic(_("Use _escape sequences"));
317 ui_hookup_widget(dialog, checkbox7, "check_escape");
318 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox7), FALSE);
319 gtk_widget_set_tooltip_text(checkbox7,
320 _("Replace \\\\, \\t, \\n, \\r and \\uXXXX (Unicode characters) with the "
321 "corresponding control characters"));
322
323 check_multiline = gtk_check_button_new_with_mnemonic(_("Use multi-line matchin_g"));
324 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_multiline), FALSE);
325 gtk_widget_set_sensitive(check_multiline, FALSE);
326 ui_hookup_widget(dialog, check_multiline, "check_multiline");
327 gtk_button_set_focus_on_click(GTK_BUTTON(check_multiline), FALSE);
328 gtk_widget_set_tooltip_text(check_multiline, _("Perform regular expression "
329 "matching on the whole buffer at once rather than line by line, allowing "
330 "matches to span multiple lines. In this mode, newline characters are part "
331 "of the input and can be captured as normal characters by the pattern."));
332
333 /* Search features */
334 fbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
335 gtk_box_pack_start(GTK_BOX(fbox), check_regexp, FALSE, FALSE, 0);
336 gtk_box_pack_start(GTK_BOX(fbox), check_multiline, FALSE, FALSE, 0);
337 gtk_box_pack_start(GTK_BOX(fbox), checkbox7, FALSE, FALSE, 0);
338
339 if (dialog != GTK_DIALOG(find_dlg.dialog))
340 {
341 GtkWidget *check_back = gtk_check_button_new_with_mnemonic(_("Search _backwards"));
342 ui_hookup_widget(dialog, check_back, "check_back");
343 gtk_button_set_focus_on_click(GTK_BUTTON(check_back), FALSE);
344 gtk_container_add(GTK_CONTAINER(fbox), check_back);
345 }
346
347 checkbox1 = gtk_check_button_new_with_mnemonic(_("C_ase sensitive"));
348 ui_hookup_widget(dialog, checkbox1, "check_case");
349 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox1), FALSE);
350
351 checkbox2 = gtk_check_button_new_with_mnemonic(_("Match only a _whole word"));
352 ui_hookup_widget(dialog, checkbox2, "check_word");
353 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox2), FALSE);
354
355 checkbox5 = gtk_check_button_new_with_mnemonic(_("Match from s_tart of word"));
356 ui_hookup_widget(dialog, checkbox5, "check_wordstart");
357 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox5), FALSE);
358
359 /* disable wordstart when wholeword is checked */
360 g_signal_connect(checkbox2, "toggled",
361 G_CALLBACK(on_widget_toggled_set_insensitive), checkbox5);
362
363 /* Matching options */
364 mbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
365 gtk_box_pack_start(GTK_BOX(mbox), checkbox1, FALSE, FALSE, 0);
366 gtk_box_pack_start(GTK_BOX(mbox), checkbox2, FALSE, FALSE, 0);
367 gtk_box_pack_start(GTK_BOX(mbox), checkbox5, FALSE, FALSE, 0);
368
369 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
370 gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE);
371 gtk_container_add(GTK_CONTAINER(hbox), fbox);
372 gtk_container_add(GTK_CONTAINER(hbox), mbox);
373 return hbox;
374}
375
376
377static void send_find_dialog_response(GtkButton *button, gpointer user_data)
378{
379 gtk_dialog_response(GTK_DIALOG(find_dlg.dialog), GPOINTER_TO_INT(user_data));
380}
381
382
383/* store text, clear search flags so we can use Search->Find Next/Previous */
384static void setup_find_next(const gchar *text)
385{
386 g_free(search_data.text);
387 g_free(search_data.original_text);
388 search_data.text = g_strdup(text);
389 search_data.original_text = g_strdup(text);
390 search_data.flags = 0;
391 search_data.backwards = FALSE;
392 search_data.search_bar = FALSE;
393}
394
395
396/* Search for next match of the current "selection".
397 * Optionally for X11 based systems, this will try to use the system-wide
398 * x-selection first.
399 * If it doesn't find a suitable search string it will try to use
400 * the current word instead, or just repeat the last search.
401 * Search flags are always zero.
402 */
403void search_find_selection(GeanyDocument *doc, gboolean search_backwards)
404{
405 gchar *s = NULL;
406
407 g_return_if_fail(DOC_VALID(doc));
408
409#ifdef G_OS_UNIX
411 {
412 GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
413
414 s = gtk_clipboard_wait_for_text(clipboard);
415 if (s && (strchr(s,'\n') || strchr(s, '\r')))
416 {
417 g_free(s);
418 s = NULL;
419 };
420 }
421#endif
422
423 if (!s && sci_has_selection(doc->editor->sci))
425
427 {
428 /* get the current word */
430 }
431
432 if (s)
433 {
434 setup_find_next(s); /* allow find next/prev */
435
436 if (document_find_text(doc, s, NULL, 0, search_backwards, NULL, FALSE, NULL) > -1)
438 g_free(s);
439 }
441 {
442 /* Repeat last search (in case selection was lost) */
443 search_find_again(search_backwards);
444 }
445 else
446 {
447 utils_beep();
448 }
449}
450
451
452static void on_expander_activated(GtkExpander *exp, gpointer data)
453{
454 gboolean *setting = data;
455
456 *setting = gtk_expander_get_expanded(exp);
457}
458
459
460static void create_find_dialog(void)
461{
462 GtkWidget *label, *entry, *sbox, *vbox;
463 GtkWidget *exp, *bbox, *button, *check_close;
464
465 find_dlg.dialog = gtk_dialog_new_with_buttons(_("Find"),
466 GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
467 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, NULL);
468 vbox = ui_dialog_vbox_new(GTK_DIALOG(find_dlg.dialog));
469 gtk_widget_set_name(find_dlg.dialog, "GeanyDialogSearch");
470 gtk_box_set_spacing(GTK_BOX(vbox), 9);
471
472 button = ui_button_new_with_image(GTK_STOCK_GO_BACK, _("_Previous"));
473 gtk_dialog_add_action_widget(GTK_DIALOG(find_dlg.dialog), button,
475 ui_hookup_widget(find_dlg.dialog, button, "btn_previous");
476
477 button = ui_button_new_with_image(GTK_STOCK_GO_FORWARD, _("_Next"));
478 gtk_dialog_add_action_widget(GTK_DIALOG(find_dlg.dialog), button,
480
481 label = gtk_label_new_with_mnemonic(_("_Search for:"));
482 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
483
484 entry = gtk_combo_box_text_new_with_entry();
485 ui_entry_add_clear_icon(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry))));
486 gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
487 gtk_entry_set_width_chars(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry))), 50);
488 find_dlg.entry = gtk_bin_get_child(GTK_BIN(entry));
489
490 g_signal_connect(gtk_bin_get_child(GTK_BIN(entry)), "activate",
491 G_CALLBACK(on_find_entry_activate), entry);
492 ui_entry_add_activate_backward_signal(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry))));
493 g_signal_connect(gtk_bin_get_child(GTK_BIN(entry)), "activate-backward",
495 g_signal_connect(find_dlg.dialog, "response",
496 G_CALLBACK(on_find_dialog_response), entry);
497 g_signal_connect(find_dlg.dialog, "delete-event",
498 G_CALLBACK(gtk_widget_hide_on_delete), NULL);
499
500 sbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
501 gtk_box_pack_start(GTK_BOX(sbox), label, FALSE, FALSE, 0);
502 gtk_box_pack_start(GTK_BOX(sbox), entry, TRUE, TRUE, 0);
503 gtk_box_pack_start(GTK_BOX(vbox), sbox, TRUE, FALSE, 0);
504
505 gtk_container_add(GTK_CONTAINER(vbox),
506 add_find_checkboxes(GTK_DIALOG(find_dlg.dialog)));
507
508 /* Now add the multiple match options */
509 exp = gtk_expander_new_with_mnemonic(_("_Find All"));
510 gtk_expander_set_expanded(GTK_EXPANDER(exp), find_dlg.all_expanded);
511 g_signal_connect_after(exp, "activate",
512 G_CALLBACK(on_expander_activated), &find_dlg.all_expanded);
513
514 bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
515
516 button = gtk_button_new_with_mnemonic(_("_Mark"));
517 gtk_widget_set_tooltip_text(button,
518 _("Mark all matches in the current document"));
519 gtk_container_add(GTK_CONTAINER(bbox), button);
520 g_signal_connect(button, "clicked", G_CALLBACK(send_find_dialog_response),
521 GINT_TO_POINTER(GEANY_RESPONSE_MARK));
522
523 button = gtk_button_new_with_mnemonic(_("In Sessi_on"));
524 gtk_container_add(GTK_CONTAINER(bbox), button);
525 g_signal_connect(button, "clicked", G_CALLBACK(send_find_dialog_response),
526 GINT_TO_POINTER(GEANY_RESPONSE_FIND_IN_SESSION));
527
528 button = gtk_button_new_with_mnemonic(_("_In Document"));
529 gtk_container_add(GTK_CONTAINER(bbox), button);
530 g_signal_connect(button, "clicked", G_CALLBACK(send_find_dialog_response),
531 GINT_TO_POINTER(GEANY_RESPONSE_FIND_IN_FILE));
532
533 /* close window checkbox */
534 check_close = gtk_check_button_new_with_mnemonic(_("Close _dialog"));
535 ui_hookup_widget(find_dlg.dialog, check_close, "check_close");
536 gtk_button_set_focus_on_click(GTK_BUTTON(check_close), FALSE);
537 gtk_widget_set_tooltip_text(check_close,
538 _("Disable this option to keep the dialog open"));
539 gtk_container_add(GTK_CONTAINER(bbox), check_close);
540 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), check_close, TRUE);
541
543 GTK_BUTTON_BOX(gtk_dialog_get_action_area(GTK_DIALOG(find_dlg.dialog))),
544 GTK_BUTTON_BOX(bbox));
545 gtk_container_add(GTK_CONTAINER(exp), bbox);
546 gtk_container_add(GTK_CONTAINER(vbox), exp);
547}
548
549
550static void set_dialog_position(GtkWidget *dialog, gint *position)
551{
552 if (position[0] >= 0)
553 gtk_window_move(GTK_WINDOW(dialog), position[0], position[1]);
554}
555
556
558{
560 gchar *sel = NULL;
561
562 g_return_if_fail(doc != NULL);
563
565
566 if (find_dlg.dialog == NULL)
567 {
570 if (sel)
571 gtk_entry_set_text(GTK_ENTRY(find_dlg.entry), sel);
572
573 set_dialog_position(find_dlg.dialog, find_dlg.position);
575 }
576 else
577 {
578 if (sel != NULL)
579 {
580 /* update the search text from current selection */
581 gtk_entry_set_text(GTK_ENTRY(find_dlg.entry), sel);
582 /* reset the entry widget's background colour */
584 }
585 gtk_widget_grab_focus(find_dlg.entry);
586 set_dialog_position(find_dlg.dialog, find_dlg.position);
587 gtk_widget_show(find_dlg.dialog);
588 /* bring the dialog back in the foreground in case it is already open but the focus is away */
589 gtk_window_present(GTK_WINDOW(find_dlg.dialog));
590 }
591
592 g_free(sel);
593}
594
595
596static void send_replace_dialog_response(GtkButton *button, gpointer user_data)
597{
598 gtk_dialog_response(GTK_DIALOG(replace_dlg.dialog), GPOINTER_TO_INT(user_data));
599}
600
601
602static gboolean
603on_widget_key_pressed_set_focus(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
604{
605 if (event->keyval == GDK_KEY_Tab)
606 {
607 gtk_widget_grab_focus(GTK_WIDGET(user_data));
608 return TRUE;
609 }
610 return FALSE;
611}
612
613
614static void create_replace_dialog(void)
615{
616 GtkWidget *label_find, *label_replace,
617 *check_close, *button, *rbox, *fbox, *vbox, *exp, *bbox;
618 GtkSizeGroup *label_size;
619
620 replace_dlg.dialog = gtk_dialog_new_with_buttons(_("Replace"),
621 GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
622 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, NULL);
623 vbox = ui_dialog_vbox_new(GTK_DIALOG(replace_dlg.dialog));
624 gtk_box_set_spacing(GTK_BOX(vbox), 9);
625 gtk_widget_set_name(replace_dlg.dialog, "GeanyDialogSearch");
626
627 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
628 gtk_dialog_add_action_widget(GTK_DIALOG(replace_dlg.dialog), button,
630 button = gtk_button_new_with_mnemonic(_("_Replace"));
631 gtk_button_set_image(GTK_BUTTON(button),
632 gtk_image_new_from_stock(GTK_STOCK_FIND_AND_REPLACE, GTK_ICON_SIZE_BUTTON));
633 gtk_dialog_add_action_widget(GTK_DIALOG(replace_dlg.dialog), button,
635 button = gtk_button_new_with_mnemonic(_("Replace & Fi_nd"));
636 gtk_button_set_image(GTK_BUTTON(button),
637 gtk_image_new_from_stock(GTK_STOCK_FIND_AND_REPLACE, GTK_ICON_SIZE_BUTTON));
638 gtk_dialog_add_action_widget(GTK_DIALOG(replace_dlg.dialog), button,
640
641 label_find = gtk_label_new_with_mnemonic(_("_Search for:"));
642 gtk_misc_set_alignment(GTK_MISC(label_find), 0, 0.5);
643
644 label_replace = gtk_label_new_with_mnemonic(_("Replace wit_h:"));
645 gtk_misc_set_alignment(GTK_MISC(label_replace), 0, 0.5);
646
647 replace_dlg.find_combobox = gtk_combo_box_text_new_with_entry();
648 replace_dlg.find_entry = gtk_bin_get_child(GTK_BIN(replace_dlg.find_combobox));
649 ui_entry_add_clear_icon(GTK_ENTRY(replace_dlg.find_entry));
650 gtk_label_set_mnemonic_widget(GTK_LABEL(label_find), replace_dlg.find_combobox);
651 gtk_entry_set_width_chars(GTK_ENTRY(replace_dlg.find_entry), 50);
652 ui_hookup_widget(replace_dlg.dialog, replace_dlg.find_combobox, "entry_find");
653
654 replace_dlg.replace_combobox = gtk_combo_box_text_new_with_entry();
655 replace_dlg.replace_entry = gtk_bin_get_child(GTK_BIN(replace_dlg.replace_combobox));
656 ui_entry_add_clear_icon(GTK_ENTRY(replace_dlg.replace_entry));
657 gtk_label_set_mnemonic_widget(GTK_LABEL(label_replace), replace_dlg.replace_combobox);
658 gtk_entry_set_width_chars(GTK_ENTRY(replace_dlg.replace_entry), 50);
659 ui_hookup_widget(replace_dlg.dialog, replace_dlg.replace_combobox, "entry_replace");
660
661 /* tab from find to the replace entry */
662 g_signal_connect(replace_dlg.find_entry,
663 "key-press-event", G_CALLBACK(on_widget_key_pressed_set_focus),
664 replace_dlg.replace_entry);
665 g_signal_connect(replace_dlg.find_entry, "activate",
667 g_signal_connect(replace_dlg.replace_entry, "activate",
668 G_CALLBACK(on_replace_entry_activate), NULL);
669 g_signal_connect(replace_dlg.dialog, "response",
670 G_CALLBACK(on_replace_dialog_response), NULL);
671 g_signal_connect(replace_dlg.dialog, "delete-event",
672 G_CALLBACK(gtk_widget_hide_on_delete), NULL);
673
674 fbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
675 gtk_box_pack_start(GTK_BOX(fbox), label_find, FALSE, FALSE, 0);
676 gtk_box_pack_start(GTK_BOX(fbox), replace_dlg.find_combobox, TRUE, TRUE, 0);
677
678 rbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
679 gtk_box_pack_start(GTK_BOX(rbox), label_replace, FALSE, FALSE, 0);
680 gtk_box_pack_start(GTK_BOX(rbox), replace_dlg.replace_combobox, TRUE, TRUE, 0);
681
682 label_size = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
683 gtk_size_group_add_widget(label_size, label_find);
684 gtk_size_group_add_widget(label_size, label_replace);
685 g_object_unref(G_OBJECT(label_size)); /* auto destroy the size group */
686
687 gtk_box_pack_start(GTK_BOX(vbox), fbox, TRUE, FALSE, 0);
688 gtk_box_pack_start(GTK_BOX(vbox), rbox, TRUE, FALSE, 0);
689 gtk_container_add(GTK_CONTAINER(vbox),
690 add_find_checkboxes(GTK_DIALOG(replace_dlg.dialog)));
691
692 /* Now add the multiple replace options */
693 exp = gtk_expander_new_with_mnemonic(_("Re_place All"));
694 gtk_expander_set_expanded(GTK_EXPANDER(exp), replace_dlg.all_expanded);
695 g_signal_connect_after(exp, "activate",
696 G_CALLBACK(on_expander_activated), &replace_dlg.all_expanded);
697
698 bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
699
700 button = gtk_button_new_with_mnemonic(_("In Sessi_on"));
701 gtk_container_add(GTK_CONTAINER(bbox), button);
702 g_signal_connect(button, "clicked", G_CALLBACK(send_replace_dialog_response),
703 GINT_TO_POINTER(GEANY_RESPONSE_REPLACE_IN_SESSION));
704
705 button = gtk_button_new_with_mnemonic(_("_In Document"));
706 gtk_container_add(GTK_CONTAINER(bbox), button);
707 g_signal_connect(button, "clicked", G_CALLBACK(send_replace_dialog_response),
708 GINT_TO_POINTER(GEANY_RESPONSE_REPLACE_IN_FILE));
709
710 button = gtk_button_new_with_mnemonic(_("In Se_lection"));
711 gtk_widget_set_tooltip_text(button,
712 _("Replace all matches found in the currently selected text"));
713 gtk_container_add(GTK_CONTAINER(bbox), button);
714 g_signal_connect(button, "clicked", G_CALLBACK(send_replace_dialog_response),
715 GINT_TO_POINTER(GEANY_RESPONSE_REPLACE_IN_SEL));
716
717 /* close window checkbox */
718 check_close = gtk_check_button_new_with_mnemonic(_("Close _dialog"));
719 ui_hookup_widget(replace_dlg.dialog, check_close, "check_close");
720 gtk_button_set_focus_on_click(GTK_BUTTON(check_close), FALSE);
721 gtk_widget_set_tooltip_text(check_close,
722 _("Disable this option to keep the dialog open"));
723 gtk_container_add(GTK_CONTAINER(bbox), check_close);
724 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), check_close, TRUE);
725
727 GTK_BUTTON_BOX(gtk_dialog_get_action_area(GTK_DIALOG(replace_dlg.dialog))),
728 GTK_BUTTON_BOX(bbox));
729 gtk_container_add(GTK_CONTAINER(exp), bbox);
730 gtk_container_add(GTK_CONTAINER(vbox), exp);
731}
732
733
735{
737 gchar *sel = NULL;
738
739 if (doc == NULL)
740 return;
741
743
744 if (replace_dlg.dialog == NULL)
745 {
748 if (sel)
749 gtk_entry_set_text(GTK_ENTRY(replace_dlg.find_entry), sel);
750
753 }
754 else
755 {
756 if (sel != NULL)
757 {
758 /* update the search text from current selection */
759 gtk_entry_set_text(GTK_ENTRY(replace_dlg.find_entry), sel);
760 /* reset the entry widget's background colour */
762 }
763 gtk_widget_grab_focus(replace_dlg.find_entry);
765 gtk_widget_show(replace_dlg.dialog);
766 /* bring the dialog back in the foreground in case it is already open but the focus is away */
767 gtk_window_present(GTK_WINDOW(replace_dlg.dialog));
768 }
769
770 g_free(sel);
771}
772
773
774static void on_widget_toggled_set_sensitive(GtkToggleButton *togglebutton, gpointer user_data)
775{
776 /* disable extra option entry when checkbutton not checked */
777 gtk_widget_set_sensitive(GTK_WIDGET(user_data),
778 gtk_toggle_button_get_active(togglebutton));
779}
780
781
782static void update_file_patterns(GtkWidget *mode_combo, GtkWidget *fcombo)
783{
784 gint selection;
785 GtkWidget *entry;
786
787 entry = gtk_bin_get_child(GTK_BIN(fcombo));
788
789 selection = gtk_combo_box_get_active(GTK_COMBO_BOX(mode_combo));
790
791 if (selection == FILES_MODE_ALL)
792 {
793 gtk_entry_set_text(GTK_ENTRY(entry), "");
794 gtk_widget_set_sensitive(fcombo, FALSE);
795 }
796 else if (selection == FILES_MODE_CUSTOM)
797 {
798 gtk_widget_set_sensitive(fcombo, TRUE);
799 }
800 else if (selection == FILES_MODE_PROJECT)
801 {
803 {
804 gchar *patterns;
805
806 patterns = g_strjoinv(" ", app->project->file_patterns);
807 gtk_entry_set_text(GTK_ENTRY(entry), patterns);
808 g_free(patterns);
809 }
810 else
811 {
812 gtk_entry_set_text(GTK_ENTRY(entry), "");
813 }
814
815 gtk_widget_set_sensitive(fcombo, FALSE);
816 }
817}
818
819
820/* creates the combo to choose which files include in the search */
821static GtkWidget *create_fif_file_mode_combo(void)
822{
823 GtkWidget *combo;
824 GtkCellRenderer *renderer;
825 GtkListStore *store;
826 GtkTreeIter iter;
827
828 /* text/sensitive */
829 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN);
830 gtk_list_store_append(store, &iter);
831 gtk_list_store_set(store, &iter, 0, _("all"), 1, TRUE, -1);
832 gtk_list_store_append(store, &iter);
833 gtk_list_store_set(store, &iter, 0, _("project"), 1, app->project != NULL, -1);
834 gtk_list_store_append(store, &iter);
835 gtk_list_store_set(store, &iter, 0, _("custom"), 1, TRUE, -1);
836
837 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
838 g_object_unref(store);
839 gtk_widget_set_tooltip_text(combo, _("All: search all files in the directory\n"
840 "Project: use file patterns defined in the project settings\n"
841 "Custom: specify file patterns manually"));
842
843 renderer = gtk_cell_renderer_text_new();
844 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
845 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "text", 0, "sensitive", 1, NULL);
846
847 return combo;
848}
849
850
851/* updates the sensitivity of the project combo item */
853{
854 GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(fif_dlg.files_mode_combo));
855 GtkTreeIter iter;
856
857 /* "1" refers to the second list entry, project */
858 if (gtk_tree_model_get_iter_from_string(model, &iter, "1"))
859 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, app->project != NULL, -1);
860}
861
862
863static void create_fif_dialog(void)
864{
865 GtkWidget *dir_combo, *combo, *fcombo, *e_combo, *entry;
866 GtkWidget *label, *label1, *label2, *label3, *checkbox1, *checkbox2, *check_wholeword,
867 *check_recursive, *check_extra, *entry_extra, *check_regexp, *combo_files_mode;
868 GtkWidget *dbox, *sbox, *lbox, *rbox, *hbox, *vbox, *ebox;
869 GtkSizeGroup *size_group;
870
871 fif_dlg.dialog = gtk_dialog_new_with_buttons(
872 _("Find in Files"), GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
873 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
874 vbox = ui_dialog_vbox_new(GTK_DIALOG(fif_dlg.dialog));
875 gtk_box_set_spacing(GTK_BOX(vbox), 9);
876 gtk_widget_set_name(fif_dlg.dialog, "GeanyDialogSearch");
877
878 gtk_dialog_add_button(GTK_DIALOG(fif_dlg.dialog), GTK_STOCK_FIND, GTK_RESPONSE_ACCEPT);
879 gtk_dialog_set_default_response(GTK_DIALOG(fif_dlg.dialog),
880 GTK_RESPONSE_ACCEPT);
881
882 label = gtk_label_new_with_mnemonic(_("_Search for:"));
883 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
884
885 combo = gtk_combo_box_text_new_with_entry();
886 entry = gtk_bin_get_child(GTK_BIN(combo));
887 ui_entry_add_clear_icon(GTK_ENTRY(entry));
888 gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
889 gtk_entry_set_width_chars(GTK_ENTRY(entry), 50);
890 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
891 fif_dlg.search_combo = combo;
892
893 sbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
894 gtk_box_pack_start(GTK_BOX(sbox), label, FALSE, FALSE, 0);
895 gtk_box_pack_start(GTK_BOX(sbox), combo, TRUE, TRUE, 0);
896
897 /* make labels same width */
898 size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
899 gtk_size_group_add_widget(size_group, label);
900
901 label3 = gtk_label_new_with_mnemonic(_("File _patterns:"));
902 gtk_misc_set_alignment(GTK_MISC(label3), 0, 0.5);
903
904 combo_files_mode = create_fif_file_mode_combo();
905 gtk_label_set_mnemonic_widget(GTK_LABEL(label3), combo_files_mode);
906 ui_hookup_widget(fif_dlg.dialog, combo_files_mode, "combo_files_mode");
907 fif_dlg.files_mode_combo = combo_files_mode;
908
909 fcombo = gtk_combo_box_text_new_with_entry();
910 entry = gtk_bin_get_child(GTK_BIN(fcombo));
911 ui_entry_add_clear_icon(GTK_ENTRY(entry));
912 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
913 gtk_widget_set_tooltip_text(entry,
914 _("Space separated list of file patterns (e.g. *.c *.h)"));
915 ui_hookup_widget(fif_dlg.dialog, entry, "entry_files");
916 fif_dlg.files_combo = fcombo;
917
918 /* update the entry when selection is changed */
919 g_signal_connect(combo_files_mode, "changed", G_CALLBACK(update_file_patterns), fcombo);
920
921 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
922 gtk_box_pack_start(GTK_BOX(hbox), label3, FALSE, FALSE, 0);
923 gtk_box_pack_start(GTK_BOX(hbox), combo_files_mode, FALSE, FALSE, 0);
924 gtk_box_pack_start(GTK_BOX(hbox), fcombo, TRUE, TRUE, 0);
925
926 label1 = gtk_label_new_with_mnemonic(_("_Directory:"));
927 gtk_misc_set_alignment(GTK_MISC(label1), 0, 0.5);
928
929 dir_combo = gtk_combo_box_text_new_with_entry();
930 entry = gtk_bin_get_child(GTK_BIN(dir_combo));
931 ui_entry_add_clear_icon(GTK_ENTRY(entry));
932 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
933 gtk_label_set_mnemonic_widget(GTK_LABEL(label1), entry);
934 gtk_entry_set_width_chars(GTK_ENTRY(entry), 50);
935 fif_dlg.dir_combo = dir_combo;
936
937 /* tab from files to the dir entry */
938 g_signal_connect(gtk_bin_get_child(GTK_BIN(fcombo)), "key-press-event",
940
941 dbox = ui_path_box_new(NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
942 GTK_ENTRY(entry));
943 gtk_box_pack_start(GTK_BOX(dbox), label1, FALSE, FALSE, 0);
944
945 label2 = gtk_label_new_with_mnemonic(_("E_ncoding:"));
946 gtk_misc_set_alignment(GTK_MISC(label2), 0, 0.5);
947
949 gtk_label_set_mnemonic_widget(GTK_LABEL(label2), e_combo);
950 fif_dlg.encoding_combo = e_combo;
951
952 ebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
953 gtk_box_pack_start(GTK_BOX(ebox), label2, FALSE, FALSE, 0);
954 gtk_box_pack_start(GTK_BOX(ebox), e_combo, TRUE, TRUE, 0);
955
956 gtk_size_group_add_widget(size_group, label1);
957 gtk_size_group_add_widget(size_group, label2);
958 gtk_size_group_add_widget(size_group, label3);
959 g_object_unref(G_OBJECT(size_group)); /* auto destroy the size group */
960
961 gtk_box_pack_start(GTK_BOX(vbox), sbox, TRUE, FALSE, 0);
962 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 0);
963 gtk_box_pack_start(GTK_BOX(vbox), dbox, TRUE, FALSE, 0);
964 gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
965
966 check_regexp = gtk_check_button_new_with_mnemonic(_("_Use regular expressions"));
967 ui_hookup_widget(fif_dlg.dialog, check_regexp, "check_regexp");
968 gtk_button_set_focus_on_click(GTK_BUTTON(check_regexp), FALSE);
969 gtk_widget_set_tooltip_text(check_regexp, _("See grep's manual page for more information"));
970
971 check_recursive = gtk_check_button_new_with_mnemonic(_("_Recurse in subfolders"));
972 ui_hookup_widget(fif_dlg.dialog, check_recursive, "check_recursive");
973 gtk_button_set_focus_on_click(GTK_BUTTON(check_recursive), FALSE);
974
975 checkbox1 = gtk_check_button_new_with_mnemonic(_("C_ase sensitive"));
976 ui_hookup_widget(fif_dlg.dialog, checkbox1, "check_case");
977 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox1), FALSE);
978 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox1), TRUE);
979
980 check_wholeword = gtk_check_button_new_with_mnemonic(_("Match only a _whole word"));
981 ui_hookup_widget(fif_dlg.dialog, check_wholeword, "check_wholeword");
982 gtk_button_set_focus_on_click(GTK_BUTTON(check_wholeword), FALSE);
983
984 checkbox2 = gtk_check_button_new_with_mnemonic(_("_Invert search results"));
985 ui_hookup_widget(fif_dlg.dialog, checkbox2, "check_invert");
986 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox2), FALSE);
987 gtk_widget_set_tooltip_text(checkbox2,
988 _("Invert the sense of matching, to select non-matching lines"));
989
990 lbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
991 gtk_container_add(GTK_CONTAINER(lbox), check_regexp);
992 gtk_container_add(GTK_CONTAINER(lbox), checkbox2);
993 gtk_container_add(GTK_CONTAINER(lbox), check_recursive);
994
995 rbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
996 gtk_container_add(GTK_CONTAINER(rbox), checkbox1);
997 gtk_container_add(GTK_CONTAINER(rbox), check_wholeword);
998 gtk_container_add(GTK_CONTAINER(rbox), gtk_label_new(NULL));
999
1000 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1001 gtk_container_add(GTK_CONTAINER(hbox), lbox);
1002 gtk_container_add(GTK_CONTAINER(hbox), rbox);
1003 gtk_container_add(GTK_CONTAINER(vbox), hbox);
1004
1005 check_extra = gtk_check_button_new_with_mnemonic(_("E_xtra options:"));
1006 ui_hookup_widget(fif_dlg.dialog, check_extra, "check_extra");
1007 gtk_button_set_focus_on_click(GTK_BUTTON(check_extra), FALSE);
1008
1009 entry_extra = gtk_entry_new();
1010 ui_entry_add_clear_icon(GTK_ENTRY(entry_extra));
1011 gtk_entry_set_activates_default(GTK_ENTRY(entry_extra), TRUE);
1012 gtk_widget_set_sensitive(entry_extra, FALSE);
1013 gtk_widget_set_tooltip_text(entry_extra, _("Other options to pass to Grep"));
1014 ui_hookup_widget(fif_dlg.dialog, entry_extra, "entry_extra");
1015
1016 /* enable entry_extra when check_extra is checked */
1017 g_signal_connect(check_extra, "toggled",
1018 G_CALLBACK(on_widget_toggled_set_sensitive), entry_extra);
1019
1020 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1021 gtk_box_pack_start(GTK_BOX(hbox), check_extra, FALSE, FALSE, 0);
1022 gtk_box_pack_start(GTK_BOX(hbox), entry_extra, TRUE, TRUE, 0);
1023 gtk_container_add(GTK_CONTAINER(vbox), hbox);
1024
1025 g_signal_connect(fif_dlg.dialog, "response",
1027 g_signal_connect(fif_dlg.dialog, "delete-event",
1028 G_CALLBACK(gtk_widget_hide_on_delete), NULL);
1029}
1030
1031
1032/**
1033 * Shows the Find in Files dialog.
1034 *
1035 * @param dir The directory to search in (UTF-8 encoding). May be @c NULL
1036 * to determine it the usual way by using the current document's path.
1037 *
1038 * @since 0.14, plugin API 53
1039 */
1040GEANY_API_SYMBOL
1042{
1044}
1045
1046
1047void search_show_find_in_files_dialog_full(const gchar *text, const gchar *dir)
1048{
1049 GtkWidget *entry; /* for child GtkEntry of a GtkComboBoxEntry */
1051 gchar *sel = NULL;
1052 gchar *cur_dir = NULL;
1054
1055 if (fif_dlg.dialog == NULL)
1056 {
1059 if (doc && !text)
1061 }
1063
1064 if (!text)
1065 {
1066 if (doc && ! sel)
1068 text = sel;
1069 }
1070 entry = gtk_bin_get_child(GTK_BIN(fif_dlg.search_combo));
1071 if (text)
1072 gtk_entry_set_text(GTK_ENTRY(entry), text);
1073 g_free(sel);
1074
1075 /* add project's base path directory to the dir list, we do this here once
1076 * (in create_fif_dialog() it would fail if a project is opened after dialog creation) */
1077 if (app->project != NULL && !EMPTY(app->project->base_path))
1078 {
1079 ui_combo_box_prepend_text_once(GTK_COMBO_BOX_TEXT(fif_dlg.dir_combo),
1081 }
1082
1083 entry = gtk_bin_get_child(GTK_BIN(fif_dlg.dir_combo));
1084 if (!EMPTY(dir))
1085 cur_dir = g_strdup(dir); /* custom directory argument passed */
1086 else
1087 {
1089 {
1090 static gchar *last_cur_dir = NULL;
1091 static GeanyDocument *last_doc = NULL;
1092
1093 /* Only set the directory entry once for the current document */
1095 if (doc == last_doc && cur_dir && utils_str_equal(cur_dir, last_cur_dir))
1096 {
1097 /* in case the user now wants the current directory, add it to history */
1098 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(fif_dlg.dir_combo), cur_dir, 0);
1099 SETPTR(cur_dir, NULL);
1100 }
1101 else
1102 SETPTR(last_cur_dir, g_strdup(cur_dir));
1103
1104 last_doc = doc;
1105 }
1106 if (!cur_dir && EMPTY(gtk_entry_get_text(GTK_ENTRY(entry))))
1107 {
1108 /* use default_open_path if no directory could be determined
1109 * (e.g. when no files are open) */
1110 if (!cur_dir)
1111 cur_dir = g_strdup(utils_get_default_dir_utf8());
1112 if (!cur_dir)
1113 cur_dir = g_get_current_dir();
1114 }
1115 }
1116 if (cur_dir)
1117 {
1118 gtk_entry_set_text(GTK_ENTRY(entry), cur_dir);
1119 g_free(cur_dir);
1120 }
1121
1123 update_file_patterns(fif_dlg.files_mode_combo, fif_dlg.files_combo);
1124
1125 /* set the encoding of the current file */
1126 if (doc != NULL)
1128 ui_encodings_combo_box_set_active_encoding(GTK_COMBO_BOX(fif_dlg.encoding_combo), enc_idx);
1129
1130 /* put the focus to the directory entry if it is empty */
1131 if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(entry)), ""))
1132 gtk_widget_grab_focus(fif_dlg.dir_combo);
1133 else
1134 gtk_widget_grab_focus(fif_dlg.search_combo);
1135
1136 /* set dialog window position */
1137 set_dialog_position(fif_dlg.dialog, fif_dlg.position);
1138
1139 gtk_widget_show(fif_dlg.dialog);
1140 /* bring the dialog back in the foreground in case it is already open but the focus is away */
1141 gtk_window_present(GTK_WINDOW(fif_dlg.dialog));
1142}
1143
1144
1145static void
1146on_find_replace_checkbutton_toggled(GtkToggleButton *togglebutton, gpointer user_data)
1147{
1148 GtkWidget *dialog = GTK_WIDGET(user_data);
1149 GtkToggleButton *chk_regexp = GTK_TOGGLE_BUTTON(
1150 ui_lookup_widget(dialog, "check_regexp"));
1151
1152 if (togglebutton == chk_regexp)
1153 {
1154 gboolean regex_set = gtk_toggle_button_get_active(chk_regexp);
1155 GtkWidget *check_word = ui_lookup_widget(dialog, "check_word");
1156 GtkWidget *check_wordstart = ui_lookup_widget(dialog, "check_wordstart");
1157 GtkWidget *check_escape = ui_lookup_widget(dialog, "check_escape");
1158 GtkWidget *check_multiline = ui_lookup_widget(dialog, "check_multiline");
1159 gboolean replace = (dialog != find_dlg.dialog);
1160 const char *back_button[2] = { "btn_previous" , "check_back" };
1161
1162 /* hide options that don't apply to regex searches */
1163 gtk_widget_set_sensitive(check_escape, ! regex_set);
1164 gtk_widget_set_sensitive(ui_lookup_widget(dialog, back_button[replace]), ! regex_set);
1165 gtk_widget_set_sensitive(check_word, ! regex_set);
1166 gtk_widget_set_sensitive(check_wordstart, ! regex_set);
1167 gtk_widget_set_sensitive(check_multiline, regex_set);
1168 }
1169}
1170
1171
1172static GeanyMatchInfo *match_info_new(GeanyFindFlags flags, gint start, gint end)
1173{
1174 GeanyMatchInfo *info = g_slice_alloc(sizeof *info);
1175
1176 info->flags = flags;
1177 info->start = start;
1178 info->end = end;
1179 info->match_text = NULL;
1180
1181 return info;
1182}
1183
1185{
1186 g_free(info->match_text);
1187 g_slice_free1(sizeof *info, info);
1188}
1189
1190
1191/* find all in the given range.
1192 * Returns a list of allocated GeanyMatchInfo, should be freed using:
1193 *
1194 * foreach_slist(node, matches)
1195 * geany_match_info_free(node->data);
1196 * g_slist_free(matches); */
1197static GSList *find_range(ScintillaObject *sci, GeanyFindFlags flags, struct Sci_TextToFind *ttf)
1198{
1199 GSList *matches = NULL;
1200 GeanyMatchInfo *info;
1201
1202 g_return_val_if_fail(sci != NULL && ttf->lpstrText != NULL, NULL);
1203 if (! *ttf->lpstrText)
1204 return NULL;
1205
1206 while (search_find_text(sci, flags, ttf, &info) != -1)
1207 {
1208 if (ttf->chrgText.cpMax > ttf->chrg.cpMax)
1209 {
1210 /* found text is partially out of range */
1212 break;
1213 }
1214
1215 matches = g_slist_prepend(matches, info);
1216 ttf->chrg.cpMin = ttf->chrgText.cpMax;
1217
1218 /* avoid rematching with empty matches like "(?=[a-z])" or "^$".
1219 * note we cannot assume a match will always be empty or not and then break out, since
1220 * matches like "a?(?=b)" will sometimes be empty and sometimes not */
1221 if (ttf->chrgText.cpMax == ttf->chrgText.cpMin)
1222 ttf->chrg.cpMin ++;
1223 }
1224
1225 return g_slist_reverse(matches);
1226}
1227
1228
1229/* Clears markers if text is null/empty.
1230 * @return Number of matches marked. */
1231gint search_mark_all(GeanyDocument *doc, const gchar *search_text, GeanyFindFlags flags)
1232{
1233 gint count = 0;
1234 struct Sci_TextToFind ttf;
1235 GSList *match, *matches;
1236
1237 g_return_val_if_fail(DOC_VALID(doc), 0);
1238
1239 /* clear previous search indicators */
1241
1242 if (G_UNLIKELY(EMPTY(search_text)))
1243 return 0;
1244
1245 ttf.chrg.cpMin = 0;
1246 ttf.chrg.cpMax = sci_get_length(doc->editor->sci);
1247 ttf.lpstrText = (gchar *)search_text;
1248
1249 matches = find_range(doc->editor->sci, flags, &ttf);
1250 foreach_slist (match, matches)
1251 {
1252 GeanyMatchInfo *info = match->data;
1253
1254 if (info->end != info->start)
1256 count++;
1257
1259 }
1260 g_slist_free(matches);
1261
1262 return count;
1263}
1264
1265
1266static void
1267on_find_entry_activate(GtkEntry *entry, gpointer user_data)
1268{
1270}
1271
1272
1273static void
1274on_find_entry_activate_backward(GtkEntry *entry, gpointer user_data)
1275{
1276 /* can't search backwards with a regexp */
1277 if (search_data.flags & GEANY_FIND_REGEXP)
1278 utils_beep();
1279 else
1281}
1282
1283
1284static GeanyFindFlags int_search_flags(gint match_case, gint whole_word, gint regexp, gint multiline, gint word_start)
1285{
1286 return (match_case ? GEANY_FIND_MATCHCASE : 0) |
1287 (regexp ? GEANY_FIND_REGEXP : 0) |
1288 (whole_word ? GEANY_FIND_WHOLEWORD : 0) |
1289 (multiline ? GEANY_FIND_MULTILINE : 0) |
1290 /* SCFIND_WORDSTART overrides SCFIND_WHOLEWORD, but we want the opposite */
1291 (word_start && !whole_word ? GEANY_FIND_WORDSTART : 0);
1292}
1293
1294
1295static void
1296on_find_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
1297{
1298 gtk_window_get_position(GTK_WINDOW(find_dlg.dialog),
1299 &find_dlg.position[0], &find_dlg.position[1]);
1300
1302
1303 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1304 gtk_widget_hide(find_dlg.dialog);
1305 else
1306 {
1308 gboolean check_close = settings.find_close_dialog;
1309
1310 if (doc == NULL)
1311 return;
1312
1313 search_data.backwards = FALSE;
1314 search_data.search_bar = FALSE;
1315
1316 g_free(search_data.text);
1317 g_free(search_data.original_text);
1318 search_data.text = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(user_data)))));
1319 search_data.original_text = g_strdup(search_data.text);
1320 search_data.flags = int_search_flags(settings.find_case_sensitive,
1321 settings.find_match_whole_word, settings.find_regexp, settings.find_regexp_multiline,
1322 settings.find_match_word_start);
1323
1324 if (EMPTY(search_data.text))
1325 {
1326 fail:
1327 utils_beep();
1328 gtk_widget_grab_focus(find_dlg.entry);
1329 return;
1330 }
1331 if (search_data.flags & GEANY_FIND_REGEXP)
1332 {
1333 GRegex *regex = compile_regex(search_data.text, search_data.flags);
1334 if (!regex)
1335 goto fail;
1336 else
1337 g_regex_unref(regex);
1338 }
1339 else if (settings.find_escape_sequences)
1340 {
1341 if (! utils_str_replace_escape(search_data.text, FALSE))
1342 goto fail;
1343 }
1344 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(user_data), search_data.original_text, 0);
1345
1346 switch (response)
1347 {
1350 {
1351 gint result = document_find_text(doc, search_data.text, search_data.original_text, search_data.flags,
1352 (response == GEANY_RESPONSE_FIND_PREVIOUS), NULL, TRUE, GTK_WIDGET(find_dlg.dialog));
1353 ui_set_search_entry_background(find_dlg.entry, (result > -1));
1354 check_close = search_prefs.hide_find_dialog;
1355 break;
1356 }
1358 search_find_usage(search_data.text, search_data.original_text, search_data.flags, FALSE);
1359 break;
1360
1362 search_find_usage(search_data.text, search_data.original_text, search_data.flags, TRUE);
1363 break;
1364
1366 {
1367 gint count = search_mark_all(doc, search_data.text, search_data.flags);
1368
1369 if (count == 0)
1370 ui_set_statusbar(FALSE, _("No matches found for \"%s\"."), search_data.original_text);
1371 else
1372 ui_set_statusbar(FALSE,
1373 ngettext("Found %d match for \"%s\".",
1374 "Found %d matches for \"%s\".", count),
1375 count, search_data.original_text);
1376 }
1377 break;
1378 }
1379 if (check_close)
1380 gtk_widget_hide(find_dlg.dialog);
1381 }
1382}
1383
1384
1385static void
1386on_replace_find_entry_activate(GtkEntry *entry, gpointer user_data)
1387{
1389}
1390
1391
1392static void
1393on_replace_entry_activate(GtkEntry *entry, gpointer user_data)
1394{
1397 NULL);
1398}
1399
1400
1402 GeanyFindFlags search_flags_re, gboolean search_replace_escape_re,
1403 const gchar *find, const gchar *replace,
1404 const gchar *original_find, const gchar *original_replace)
1405{
1406 guint n, page_count, rep_count = 0, file_count = 0;
1407
1408 /* replace in all documents following notebook tab order */
1409 page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook));
1410 for (n = 0; n < page_count; n++)
1411 {
1413 gint reps = 0;
1414
1415 reps = document_replace_all(tmp_doc, find, replace, original_find, original_replace, search_flags_re);
1416 rep_count += reps;
1417 if (reps)
1418 file_count++;
1419 }
1420 if (file_count == 0)
1421 {
1422 utils_beep();
1423 ui_set_statusbar(FALSE, _("No matches found for \"%s\"."), original_find);
1424 return;
1425 }
1426 /* if only one file was changed, don't override that document's status message
1427 * so we don't have to translate 4 messages for ngettext */
1428 if (file_count > 1)
1429 ui_set_statusbar(FALSE, _("Replaced %u matches in %u documents."),
1430 rep_count, file_count);
1431
1432 /* show which docs had replacements: */
1433 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_STATUS);
1434
1435 ui_save_buttons_toggle(doc->changed); /* update save all */
1436}
1437
1438
1439static void
1440on_replace_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
1441{
1443 GeanyFindFlags search_flags_re;
1444 gboolean search_backwards_re, search_replace_escape_re;
1445 gchar *find, *replace, *original_find = NULL, *original_replace = NULL;
1446
1447 gtk_window_get_position(GTK_WINDOW(replace_dlg.dialog),
1448 &replace_dlg.position[0], &replace_dlg.position[1]);
1449
1451
1452 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1453 {
1454 gtk_widget_hide(replace_dlg.dialog);
1455 return;
1456 }
1457
1458 search_backwards_re = settings.replace_search_backwards;
1459 search_replace_escape_re = settings.replace_escape_sequences;
1460 find = g_strdup(gtk_entry_get_text(GTK_ENTRY(replace_dlg.find_entry)));
1461 replace = g_strdup(gtk_entry_get_text(GTK_ENTRY(replace_dlg.replace_entry)));
1462
1463 search_flags_re = int_search_flags(settings.replace_case_sensitive,
1464 settings.replace_match_whole_word, settings.replace_regexp,
1465 settings.replace_regexp_multiline, settings.replace_match_word_start);
1466
1467 if ((response != GEANY_RESPONSE_FIND) && (search_flags_re & GEANY_FIND_MATCHCASE)
1468 && (strcmp(find, replace) == 0))
1469 goto fail;
1470
1471 original_find = g_strdup(find);
1472 original_replace = g_strdup(replace);
1473
1474 if (search_flags_re & GEANY_FIND_REGEXP)
1475 {
1476 GRegex *regex = compile_regex(find, search_flags_re);
1477 if (regex)
1478 g_regex_unref(regex);
1479 /* find escapes will be handled by GRegex */
1480 if (!regex || !utils_str_replace_escape(replace, TRUE))
1481 goto fail;
1482 }
1483 else if (search_replace_escape_re)
1484 {
1485 if (! utils_str_replace_escape(find, FALSE) ||
1486 ! utils_str_replace_escape(replace, FALSE))
1487 goto fail;
1488 }
1489
1490 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(replace_dlg.find_combobox), original_find, 0);
1491 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(replace_dlg.replace_combobox), original_replace, 0);
1492
1493 switch (response)
1494 {
1496 {
1497 gint rep = document_replace_text(doc, find, original_find, replace, search_flags_re,
1498 search_backwards_re);
1499 if (rep != -1)
1500 document_find_text(doc, find, original_find, search_flags_re, search_backwards_re,
1501 NULL, TRUE, NULL);
1502 break;
1503 }
1505 document_replace_text(doc, find, original_find, replace, search_flags_re,
1506 search_backwards_re);
1507 break;
1508
1510 {
1511 gint result = document_find_text(doc, find, original_find, search_flags_re,
1512 search_backwards_re, NULL, TRUE, GTK_WIDGET(dialog));
1513 ui_set_search_entry_background(replace_dlg.find_entry, (result > -1));
1514 break;
1515 }
1517 if (! document_replace_all(doc, find, replace, original_find, original_replace, search_flags_re))
1518 utils_beep();
1519 break;
1520
1522 replace_in_session(doc, search_flags_re, search_replace_escape_re, find, replace, original_find, original_replace);
1523 break;
1524
1526 document_replace_sel(doc, find, replace, original_find, original_replace, search_flags_re);
1527 break;
1528 }
1529 switch (response)
1530 {
1534 if (settings.replace_close_dialog)
1535 gtk_widget_hide(replace_dlg.dialog);
1536 }
1537 g_free(find);
1538 g_free(replace);
1539 g_free(original_find);
1540 g_free(original_replace);
1541 return;
1542
1543fail:
1544 utils_beep();
1545 gtk_widget_grab_focus(replace_dlg.find_entry);
1546 g_free(find);
1547 g_free(replace);
1548 g_free(original_find);
1549 g_free(original_replace);
1550}
1551
1552
1553static GString *get_grep_options(void)
1554{
1555 GString *gstr = g_string_new("-nHI"); /* line numbers, filenames, ignore binaries */
1556
1557 if (settings.fif_invert_results)
1558 g_string_append_c(gstr, 'v');
1559 if (! settings.fif_case_sensitive)
1560 g_string_append_c(gstr, 'i');
1561 if (settings.fif_match_whole_word)
1562 g_string_append_c(gstr, 'w');
1563 if (settings.fif_recursive)
1564 g_string_append_c(gstr, 'r');
1565
1566 if (!settings.fif_regexp)
1567 g_string_append_c(gstr, 'F');
1568 else
1569 g_string_append_c(gstr, 'E');
1570
1571 if (settings.fif_use_extra_options)
1572 {
1573 g_strstrip(settings.fif_extra_options);
1574
1575 if (*settings.fif_extra_options != 0)
1576 {
1577 g_string_append_c(gstr, ' ');
1578 g_string_append(gstr, settings.fif_extra_options);
1579 }
1580 }
1581 g_strstrip(settings.fif_files);
1582 if (settings.fif_files_mode != FILES_MODE_ALL && *settings.fif_files)
1583 {
1584 GString *tmp;
1585
1586 /* put --include= before each pattern */
1587 tmp = g_string_new(settings.fif_files);
1588 do {} while (utils_string_replace_all(tmp, " ", " "));
1589 g_string_prepend_c(tmp, ' ');
1590 utils_string_replace_all(tmp, " ", " --include=");
1591 g_string_append(gstr, tmp->str);
1592 g_string_free(tmp, TRUE);
1593 }
1594 return gstr;
1595}
1596
1597
1598static void
1600 G_GNUC_UNUSED gpointer user_data)
1601{
1602 gtk_window_get_position(GTK_WINDOW(fif_dlg.dialog), &fif_dlg.position[0], &fif_dlg.position[1]);
1603
1605
1606 if (response == GTK_RESPONSE_ACCEPT)
1607 {
1608 GtkWidget *search_combo = fif_dlg.search_combo;
1609 const gchar *search_text =
1610 gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(search_combo))));
1611 GtkWidget *dir_combo = fif_dlg.dir_combo;
1612 const gchar *utf8_dir =
1613 gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(dir_combo))));
1614 GeanyEncodingIndex enc_idx =
1615 ui_encodings_combo_box_get_active_encoding(GTK_COMBO_BOX(fif_dlg.encoding_combo));
1616
1617 if (G_UNLIKELY(EMPTY(utf8_dir)))
1618 ui_set_statusbar(FALSE, _("Invalid directory for find in files."));
1619 else if (!EMPTY(search_text))
1620 {
1621 GString *opts = get_grep_options();
1622 const gchar *enc = (enc_idx == GEANY_ENCODING_UTF_8) ? NULL :
1624
1625 if (search_find_in_files(search_text, utf8_dir, opts->str, enc))
1626 {
1627 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(search_combo), search_text, 0);
1628 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(fif_dlg.files_combo), NULL, 0);
1629 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(dir_combo), utf8_dir, 0);
1630 gtk_widget_hide(fif_dlg.dialog);
1631 }
1632 g_string_free(opts, TRUE);
1633 }
1634 else
1635 ui_set_statusbar(FALSE, _("No text to find."));
1636 }
1637 else
1638 gtk_widget_hide(fif_dlg.dialog);
1639}
1640
1641
1642static gboolean
1643search_find_in_files(const gchar *utf8_search_text, const gchar *utf8_dir, const gchar *opts,
1644 const gchar *enc)
1645{
1646 gchar **argv_prefix, **argv;
1647 gchar *command_grep;
1648 gchar *command_line, *dir;
1649 gchar *search_text = NULL;
1650 GError *error = NULL;
1651 gboolean ret = FALSE;
1652 gssize utf8_text_len;
1653
1654 if (EMPTY(utf8_search_text) || ! utf8_dir) return TRUE;
1655
1656 command_grep = g_find_program_in_path(tool_prefs.grep_cmd);
1657 if (command_grep == NULL)
1658 command_line = g_strdup_printf("%s %s --", tool_prefs.grep_cmd, opts);
1659 else
1660 {
1661 command_line = g_strdup_printf("\"%s\" %s --", command_grep, opts);
1662 g_free(command_grep);
1663 }
1664
1665 /* convert the search text in the preferred encoding (if the text is not valid UTF-8. assume
1666 * it is already in the preferred encoding) */
1667 utf8_text_len = strlen(utf8_search_text);
1668 if (enc != NULL && g_utf8_validate(utf8_search_text, utf8_text_len, NULL))
1669 {
1670 search_text = g_convert(utf8_search_text, utf8_text_len, enc, "UTF-8", NULL, NULL, NULL);
1671 }
1672 if (search_text == NULL)
1673 search_text = g_strdup(utf8_search_text);
1674
1675 argv_prefix = g_new(gchar*, 3);
1676 argv_prefix[0] = search_text;
1677 dir = utils_get_locale_from_utf8(utf8_dir);
1678
1679 /* finally add the arguments(files to be searched) */
1680 if (settings.fif_recursive) /* recursive option set */
1681 {
1682 /* Use '.' so we get relative paths in the output */
1683 argv_prefix[1] = g_strdup(".");
1684 argv_prefix[2] = NULL;
1685 argv = argv_prefix;
1686 }
1687 else
1688 {
1689 argv_prefix[1] = NULL;
1690 argv = search_get_argv((const gchar**)argv_prefix, dir);
1691 g_strfreev(argv_prefix);
1692
1693 if (argv == NULL) /* no files */
1694 {
1695 g_free(command_line);
1696 return FALSE;
1697 }
1698 }
1699
1700 gtk_list_store_clear(msgwindow.store_msg);
1701 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_MESSAGE);
1702
1703 /* we can pass 'enc' without strdup'ing it here because it's a global const string and
1704 * always exits longer than the lifetime of this function */
1705 if (spawn_with_callbacks(dir, command_line, argv, NULL, 0, NULL, NULL, search_read_io,
1706 (gpointer) enc, 0, search_read_io_stderr, (gpointer) enc, 0, search_finished, NULL,
1707 NULL, &error))
1708 {
1709 gchar *utf8_str;
1710
1711 ui_progress_bar_start(_("Searching..."));
1713 utf8_str = g_strdup_printf(_("%s %s -- %s (in directory: %s)"),
1714 tool_prefs.grep_cmd, opts, utf8_search_text, utf8_dir);
1715 msgwin_msg_add_string(COLOR_BLUE, -1, NULL, utf8_str);
1716 g_free(utf8_str);
1717 ret = TRUE;
1718 }
1719 else
1720 {
1721 ui_set_statusbar(TRUE, _("Cannot execute grep tool \"%s\": %s. "
1722 "Check the path setting in Preferences."), tool_prefs.grep_cmd, error->message);
1723 g_error_free(error);
1724 }
1725
1726 utils_free_pointers(2, dir, command_line, NULL);
1727 g_strfreev(argv);
1728 return ret;
1729}
1730
1731
1732static gboolean pattern_list_match(GSList *patterns, const gchar *str)
1733{
1734 GSList *item;
1735
1736 foreach_slist(item, patterns)
1737 {
1738 if (g_pattern_match_string(item->data, str))
1739 return TRUE;
1740 }
1741 return FALSE;
1742}
1743
1744
1745/* Creates an argument vector of strings, copying argv_prefix[] values for
1746 * the first arguments, then followed by filenames found in dir.
1747 * Returns NULL if no files were found, otherwise returned vector should be fully freed. */
1748static gchar **search_get_argv(const gchar **argv_prefix, const gchar *dir)
1749{
1750 guint prefix_len, list_len, i, j;
1751 gchar **argv;
1752 GSList *list, *item, *patterns = NULL;
1753 GError *error = NULL;
1754
1755 g_return_val_if_fail(dir != NULL, NULL);
1756
1757 prefix_len = g_strv_length((gchar**)argv_prefix);
1758 list = utils_get_file_list(dir, &list_len, &error);
1759 if (error)
1760 {
1761 ui_set_statusbar(TRUE, _("Could not open directory (%s)"), error->message);
1762 g_error_free(error);
1763 return NULL;
1764 }
1765 if (list == NULL)
1766 return NULL;
1767
1768 argv = g_new(gchar*, prefix_len + list_len + 1);
1769
1770 for (i = 0, j = 0; i < prefix_len; i++)
1771 {
1772 if (g_str_has_prefix(argv_prefix[i], "--include="))
1773 {
1774 const gchar *pat = &(argv_prefix[i][10]); /* the pattern part of the argument */
1775
1776 patterns = g_slist_prepend(patterns, g_pattern_spec_new(pat));
1777 }
1778 else
1779 argv[j++] = g_strdup(argv_prefix[i]);
1780 }
1781
1782 if (patterns)
1783 {
1784 GSList *pat;
1785
1786 foreach_slist(item, list)
1787 {
1788 if (pattern_list_match(patterns, item->data))
1789 argv[j++] = item->data;
1790 else
1791 g_free(item->data);
1792 }
1793 foreach_slist(pat, patterns)
1794 g_pattern_spec_free(pat->data);
1795 g_slist_free(patterns);
1796 }
1797 else
1798 {
1799 foreach_slist(item, list)
1800 argv[j++] = item->data;
1801 }
1802
1803 argv[j] = NULL;
1804 g_slist_free(list);
1805 return argv;
1806}
1807
1808
1809static void read_fif_io(gchar *msg, GIOCondition condition, gchar *enc, gint msg_color)
1810{
1811 if (condition & (G_IO_IN | G_IO_PRI))
1812 {
1813 gchar *utf8_msg = NULL;
1814
1815 g_strstrip(msg);
1816 /* enc is NULL when encoding is set to UTF-8, so we can skip any conversion */
1817 if (enc != NULL)
1818 {
1819 if (! g_utf8_validate(msg, -1, NULL))
1820 {
1821 utf8_msg = g_convert(msg, -1, "UTF-8", enc, NULL, NULL, NULL);
1822 }
1823 if (utf8_msg == NULL)
1824 utf8_msg = msg;
1825 }
1826 else
1827 utf8_msg = msg;
1828
1829 msgwin_msg_add_string(msg_color, -1, NULL, utf8_msg);
1830
1831 if (utf8_msg != msg)
1832 g_free(utf8_msg);
1833 }
1834}
1835
1836
1837static void search_read_io(GString *string, GIOCondition condition, gpointer data)
1838{
1839 read_fif_io(string->str, condition, data, COLOR_BLACK);
1840}
1841
1842
1843static void search_read_io_stderr(GString *string, GIOCondition condition, gpointer data)
1844{
1845 read_fif_io(string->str, condition, data, COLOR_DARK_RED);
1846}
1847
1848
1849static void search_finished(GPid child_pid, gint status, gpointer user_data)
1850{
1851 const gchar *msg = _("Search failed.");
1852 gint exit_status;
1853
1854 if (SPAWN_WIFEXITED(status))
1855 {
1856 exit_status = SPAWN_WEXITSTATUS(status);
1857 }
1858 else if (SPAWN_WIFSIGNALED(status))
1859 {
1860 exit_status = -1;
1861 g_warning("Find in Files: The command failed unexpectedly (signal received).");
1862 }
1863 else
1864 {
1865 exit_status = 1;
1866 }
1867
1868 switch (exit_status)
1869 {
1870 case 0:
1871 {
1872 gint count = gtk_tree_model_iter_n_children(
1873 GTK_TREE_MODEL(msgwindow.store_msg), NULL) - 1;
1874 gchar *text = ngettext(
1875 "Search completed with %d match.",
1876 "Search completed with %d matches.", count);
1877
1879 ui_set_statusbar(FALSE, text, count);
1880 break;
1881 }
1882 case 1:
1883 msg = _("No matches found.");
1884 /* fall through */
1885 default:
1887 ui_set_statusbar(FALSE, "%s", msg);
1888 break;
1889 }
1890 utils_beep();
1892}
1893
1894
1895static GRegex *compile_regex(const gchar *str, GeanyFindFlags sflags)
1896{
1897 GRegex *regex;
1898 GError *error = NULL;
1899 gint rflags = 0;
1900
1901 if (sflags & GEANY_FIND_MULTILINE)
1902 rflags |= G_REGEX_MULTILINE;
1903 if (~sflags & GEANY_FIND_MATCHCASE)
1904 rflags |= G_REGEX_CASELESS;
1906 {
1907 geany_debug("%s: Unsupported regex flags found!", G_STRFUNC);
1908 }
1909
1910 regex = g_regex_new(str, rflags, 0, &error);
1911 if (!regex)
1912 {
1913 ui_set_statusbar(FALSE, _("Bad regex: %s"), error->message);
1914 g_error_free(error);
1915 }
1916 return regex;
1917}
1918
1919
1920/* groups that don't exist are handled OK as len = end - start = (-1) - (-1) = 0 */
1921static gchar *get_regex_match_string(const gchar *text, const GeanyMatchInfo *match, guint nth)
1922{
1923 const gint start = match->matches[nth].start;
1924 const gint end = match->matches[nth].end;
1925 return g_strndup(&text[start], end - start);
1926}
1927
1928
1929static gint find_regex(ScintillaObject *sci, guint pos, GRegex *regex, gboolean multiline, GeanyMatchInfo *match)
1930{
1931 const gchar *text;
1932 GMatchInfo *minfo;
1933 guint document_length;
1934 gint ret = -1;
1935 gint offset = 0;
1936
1937 document_length = (guint)sci_get_length(sci);
1938 if (document_length <= 0)
1939 return -1; /* skip empty documents */
1940
1941 g_return_val_if_fail(pos <= document_length, -1);
1942
1943 if (multiline)
1944 {
1945 /* Warning: any SCI calls will invalidate 'text' after calling SCI_GETCHARACTERPOINTER */
1946 text = (void*)SSM(sci, SCI_GETCHARACTERPOINTER, 0, 0);
1947 g_regex_match_full(regex, text, -1, pos, 0, &minfo, NULL);
1948 }
1949 else /* single-line mode, manually match against each line */
1950 {
1952
1953 for (;;)
1954 {
1955 gint start = sci_get_position_from_line(sci, line);
1956 gint end = sci_get_line_end_position(sci, line);
1957
1958 text = (void*)SSM(sci, SCI_GETRANGEPOINTER, start, end - start);
1959 if (g_regex_match_full(regex, text, end - start, pos - start, 0, &minfo, NULL))
1960 {
1961 offset = start;
1962 break;
1963 }
1964 else /* not found, try next line */
1965 {
1966 line ++;
1967 if (line >= sci_get_line_count(sci))
1968 break;
1970 /* don't free last info, it's freed below */
1971 g_match_info_free(minfo);
1972 }
1973 }
1974 }
1975
1976 /* Warning: minfo will become invalid when 'text' does! */
1977 if (g_match_info_matches(minfo))
1978 {
1979 guint i;
1980
1981 /* copy whole match text and offsets before they become invalid */
1982 SETPTR(match->match_text, g_match_info_fetch(minfo, 0));
1983
1984 foreach_range(i, G_N_ELEMENTS(match->matches))
1985 {
1986 gint start = -1, end = -1;
1987
1988 g_match_info_fetch_pos(minfo, (gint)i, &start, &end);
1989 match->matches[i].start = offset + start;
1990 match->matches[i].end = offset + end;
1991 }
1992 match->start = match->matches[0].start;
1993 match->end = match->matches[0].end;
1994 ret = match->start;
1995 }
1996 g_match_info_free(minfo);
1997 return ret;
1998}
1999
2000
2002{
2003 g_warn_if_fail(! (flags & GEANY_FIND_REGEXP) || ! (flags & GEANY_FIND_MULTILINE));
2004
2005 return ((flags & GEANY_FIND_MATCHCASE) ? SCFIND_MATCHCASE : 0) |
2006 ((flags & GEANY_FIND_WHOLEWORD) ? SCFIND_WHOLEWORD : 0) |
2007 ((flags & GEANY_FIND_REGEXP) ? SCFIND_REGEXP | SCFIND_POSIX : 0) |
2008 ((flags & GEANY_FIND_WORDSTART) ? SCFIND_WORDSTART : 0);
2009}
2010
2011
2012gint search_find_prev(ScintillaObject *sci, const gchar *str, GeanyFindFlags flags, GeanyMatchInfo **match_)
2013{
2014 gint ret;
2015
2016 g_return_val_if_fail(! (flags & GEANY_FIND_REGEXP), -1);
2017
2019 if (ret != -1 && match_)
2020 *match_ = match_info_new(flags, ret, ret + strlen(str));
2021 return ret;
2022}
2023
2024
2025gint search_find_next(ScintillaObject *sci, const gchar *str, GeanyFindFlags flags, GeanyMatchInfo **match_)
2026{
2028 GRegex *regex;
2029 gint ret = -1;
2030 gint pos;
2031
2032 if (~flags & GEANY_FIND_REGEXP)
2033 {
2035 if (ret != -1 && match_)
2036 *match_ = match_info_new(flags, ret, ret + strlen(str));
2037 return ret;
2038 }
2039
2040 regex = compile_regex(str, flags);
2041 if (!regex)
2042 return -1;
2043
2044 match = match_info_new(flags, 0, 0);
2045
2047 ret = find_regex(sci, pos, regex, flags & GEANY_FIND_MULTILINE, match);
2048 /* avoid re-matching the same position in case of empty matches */
2049 if (ret == pos && match->matches[0].start == match->matches[0].end)
2050 ret = find_regex(sci, pos + 1, regex, flags & GEANY_FIND_MULTILINE, match);
2051 if (ret >= 0)
2052 sci_set_selection(sci, match->start, match->end);
2053
2054 if (ret != -1 && match_)
2055 *match_ = match;
2056 else
2058
2059 g_regex_unref(regex);
2060 return ret;
2061}
2062
2063
2064gint search_replace_match(ScintillaObject *sci, const GeanyMatchInfo *match, const gchar *replace_text)
2065{
2066 GString *str;
2067 gint ret = 0;
2068 gint i = 0;
2069
2072
2073 if (! (match->flags & GEANY_FIND_REGEXP))
2074 return sci_replace_target(sci, replace_text, FALSE);
2075
2076 str = g_string_new(replace_text);
2077 while (str->str[i])
2078 {
2079 gchar *ptr = &str->str[i];
2080 gchar *grp;
2081 gchar c;
2082
2083 if (ptr[0] != '\\')
2084 {
2085 i++;
2086 continue;
2087 }
2088 c = ptr[1];
2089 /* backslash or unnecessary escape */
2090 if (c == '\\' || !isdigit(c))
2091 {
2092 g_string_erase(str, i, 1);
2093 i++;
2094 continue;
2095 }
2096 /* digit escape */
2097 g_string_erase(str, i, 2);
2098 /* fix match offsets by subtracting index of whole match start from the string */
2099 grp = get_regex_match_string(match->match_text - match->matches[0].start, match, c - '0');
2100 g_string_insert(str, i, grp);
2101 i += strlen(grp);
2102 g_free(grp);
2103 }
2104 ret = sci_replace_target(sci, str->str, FALSE);
2105 g_string_free(str, TRUE);
2106 return ret;
2107}
2108
2109
2110gint search_find_text(ScintillaObject *sci, GeanyFindFlags flags, struct Sci_TextToFind *ttf, GeanyMatchInfo **match_)
2111{
2113 GRegex *regex;
2114 gint ret;
2115
2116 if (~flags & GEANY_FIND_REGEXP)
2117 {
2119 if (ret != -1 && match_)
2120 *match_ = match_info_new(flags, ttf->chrgText.cpMin, ttf->chrgText.cpMax);
2121 return ret;
2122 }
2123
2124 regex = compile_regex(ttf->lpstrText, flags);
2125 if (!regex)
2126 return -1;
2127
2128 match = match_info_new(flags, 0, 0);
2129
2130 ret = find_regex(sci, ttf->chrg.cpMin, regex, flags & GEANY_FIND_MULTILINE, match);
2131 if (ret >= ttf->chrg.cpMax)
2132 ret = -1;
2133 else if (ret >= 0)
2134 {
2135 ttf->chrgText.cpMin = match->start;
2136 ttf->chrgText.cpMax = match->end;
2137 }
2138
2139 if (ret != -1 && match_)
2140 *match_ = match;
2141 else
2143
2144 g_regex_unref(regex);
2145 return ret;
2146}
2147
2148
2149static gint find_document_usage(GeanyDocument *doc, const gchar *search_text, GeanyFindFlags flags)
2150{
2151 gchar *buffer, *short_file_name;
2152 struct Sci_TextToFind ttf;
2153 gint count = 0;
2154 gint prev_line = -1;
2155 GSList *match, *matches;
2156
2157 g_return_val_if_fail(DOC_VALID(doc), 0);
2158
2159 short_file_name = g_path_get_basename(DOC_FILENAME(doc));
2160
2161 ttf.chrg.cpMin = 0;
2162 ttf.chrg.cpMax = sci_get_length(doc->editor->sci);
2163 ttf.lpstrText = (gchar *)search_text;
2164
2165 matches = find_range(doc->editor->sci, flags, &ttf);
2166 foreach_slist (match, matches)
2167 {
2168 GeanyMatchInfo *info = match->data;
2169 gint line = sci_get_line_from_position(doc->editor->sci, info->start);
2170
2171 if (line != prev_line)
2172 {
2173 buffer = sci_get_line(doc->editor->sci, line);
2175 "%s:%d: %s", short_file_name, line + 1, g_strstrip(buffer));
2176 g_free(buffer);
2177 prev_line = line;
2178 }
2179 count++;
2180
2182 }
2183 g_slist_free(matches);
2184 g_free(short_file_name);
2185 return count;
2186}
2187
2188
2189void search_find_usage(const gchar *search_text, const gchar *original_search_text,
2190 GeanyFindFlags flags, gboolean in_session)
2191{
2192 GeanyDocument *doc;
2193 gint count = 0;
2194
2195 doc = document_get_current();
2196 g_return_if_fail(doc != NULL);
2197
2198 if (G_UNLIKELY(EMPTY(search_text)))
2199 {
2200 utils_beep();
2201 return;
2202 }
2203
2204 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_MESSAGE);
2205 gtk_list_store_clear(msgwindow.store_msg);
2206
2207 if (! in_session)
2208 { /* use current document */
2209 count = find_document_usage(doc, search_text, flags);
2210 }
2211 else
2212 {
2213 guint i;
2214 for (i = 0; i < documents_array->len; i++)
2215 {
2216 if (documents[i]->is_valid)
2217 {
2218 count += find_document_usage(documents[i], search_text, flags);
2219 }
2220 }
2221 }
2222
2223 if (count == 0) /* no matches were found */
2224 {
2225 ui_set_statusbar(FALSE, _("No matches found for \"%s\"."), original_search_text);
2226 msgwin_msg_add(COLOR_BLUE, -1, NULL, _("No matches found for \"%s\"."), original_search_text);
2227 }
2228 else
2229 {
2231 "Found %d match for \"%s\".", "Found %d matches for \"%s\".", count),
2232 count, original_search_text);
2234 "Found %d match for \"%s\".", "Found %d matches for \"%s\".", count),
2235 count, original_search_text);
2236 }
2237}
2238
2239
2240/* ttf is updated to include the last match position (ttf->chrg.cpMin) and
2241 * the new search range end (ttf->chrg.cpMax).
2242 * Note: Normally you would call sci_start/end_undo_action() around this call. */
2243guint search_replace_range(ScintillaObject *sci, struct Sci_TextToFind *ttf,
2244 GeanyFindFlags flags, const gchar *replace_text)
2245{
2246 gint count = 0;
2247 gint offset = 0; /* difference between search pos and replace pos */
2248 GSList *match, *matches;
2249
2250 g_return_val_if_fail(sci != NULL && ttf->lpstrText != NULL && replace_text != NULL, 0);
2251 if (! *ttf->lpstrText)
2252 return 0;
2253
2254 matches = find_range(sci, flags, ttf);
2255 foreach_slist (match, matches)
2256 {
2257 GeanyMatchInfo *info = match->data;
2258 gint replace_len;
2259
2260 info->start += offset;
2261 info->end += offset;
2262
2263 replace_len = search_replace_match(sci, info, replace_text);
2264 offset += replace_len - (info->end - info->start);
2265 count ++;
2266
2267 /* on last match, update the last match/new range end */
2268 if (! match->next)
2269 {
2270 ttf->chrg.cpMin = info->start;
2271 ttf->chrg.cpMax += offset;
2272 }
2273
2275 }
2276 g_slist_free(matches);
2277
2278 return count;
2279}
2280
2281
2282void search_find_again(gboolean change_direction)
2283{
2285
2286 g_return_if_fail(doc != NULL);
2287
2288 if (search_data.text)
2289 {
2290 gboolean forward = ! search_data.backwards;
2291 gint result = document_find_text(doc, search_data.text, search_data.original_text, search_data.flags,
2292 change_direction ? forward : !forward, NULL, FALSE, NULL);
2293
2294 if (result > -1)
2296
2297 if (search_data.search_bar)
2299 toolbar_get_widget_child_by_name("SearchEntry"), (result > -1));
2300 }
2301}
#define SCFIND_REGEXP
Definition: Scintilla.h:419
#define SCFIND_POSIX
Definition: Scintilla.h:420
#define SCI_GETCHARACTERPOINTER
Definition: Scintilla.h:870
#define SCFIND_WHOLEWORD
Definition: Scintilla.h:416
#define SCFIND_MATCHCASE
Definition: Scintilla.h:417
#define SCI_GETRANGEPOINTER
Definition: Scintilla.h:871
#define SCFIND_WORDSTART
Definition: Scintilla.h:418
Contains the GeanyApp.
GeanyBuildCommand ** ptr
Definition: build.c:2679
const gchar * label
Definition: build.c:2676
GeanyDocument * document_get_current(void)
Finds the current document.
Definition: document.c:371
gint document_find_text(GeanyDocument *doc, const gchar *text, const gchar *original_text, GeanyFindFlags flags, gboolean search_backwards, GeanyMatchInfo **match_, gboolean scroll, GtkWidget *parent)
Definition: document.c:2317
GeanyDocument * document_get_from_page(guint page_num)
Finds the document for the given notebook page page_num.
Definition: document.c:352
gint document_replace_text(GeanyDocument *doc, const gchar *find_text, const gchar *original_find_text, const gchar *replace_text, GeanyFindFlags flags, gboolean search_backwards)
Definition: document.c:2397
GPtrArray * documents_array
Definition: document.c:87
void document_replace_sel(GeanyDocument *doc, const gchar *find_text, const gchar *replace_text, const gchar *original_find_text, const gchar *original_replace_text, GeanyFindFlags flags)
Definition: document.c:2520
gint document_replace_all(GeanyDocument *doc, const gchar *find_text, const gchar *replace_text, const gchar *original_find_text, const gchar *original_replace_text, GeanyFindFlags flags)
Definition: document.c:2623
Document related actions: new, save, open, etc.
#define DOC_VALID(doc_ptr)
Null-safe way to check GeanyDocument::is_valid.
Definition: document.h:162
#define DOC_FILENAME(doc)
Returns the filename of the document passed or GEANY_STRING_UNTITLED (e.g.
Definition: document.h:170
#define documents
Wraps GeanyData::documents_array so it can be used with C array syntax.
Definition: document.h:130
gchar * editor_get_default_selection(GeanyEditor *editor, gboolean use_current_word, const gchar *wordchars)
Definition: editor.c:4057
void editor_indicator_clear(GeanyEditor *editor, gint indic)
Deletes all currently set indicators matching indic in the editor window.
Definition: editor.c:4149
gchar * text
Definition: editor.c:83
void editor_display_current_line(GeanyEditor *editor, gfloat percent_of_view)
Definition: editor.c:4108
ScintillaObject * sci
Definition: editor.c:88
void editor_indicator_set_on_range(GeanyEditor *editor, gint indic, gint start, gint end)
Sets an indicator on the range specified by start and end.
Definition: editor.c:4225
gint pos
Definition: editor.c:87
@ GEANY_INDICATOR_SEARCH
Indicator used to highlight search results in the document.
Definition: editor.h:72
const gchar * encodings_get_charset_from_index(gint idx)
Gets the character set name of the specified index e.g.
Definition: encodings.c:267
GeanyEncodingIndex encodings_get_idx_from_charset(const gchar *charset)
Definition: encodings.c:196
Encoding conversion and Byte Order Mark (BOM) handling.
GeanyEncodingIndex
List of known and supported encodings.
Definition: encodings.h:50
@ GEANY_ENCODING_UTF_8
Definition: encodings.h:68
void error(const errorSelection selection, const char *const format,...)
Definition: error.c:53
vString * line
Definition: geany_cobol.c:133
unsigned int count
tokenInfo * list
static bool match(const unsigned char *line, const char *word)
Definition: geany_tcl.c:55
void configuration_add_pref_group(struct StashGroup *group, gboolean for_prefs_dialog)
Definition: keyfile.c:128
GeanyApp * app
Definition: libmain.c:86
void geany_debug(gchar const *format,...)
Definition: log.c:67
void msgwin_set_messages_dir(const gchar *messages_dir)
Sets the Messages path for opening any parsed filenames without absolute path from message lines.
Definition: msgwindow.c:114
MessageWindow msgwindow
Definition: msgwindow.c:66
void msgwin_msg_add(gint msg_color, gint line, GeanyDocument *doc, const gchar *format,...)
Adds a formatted message in the messages tab treeview in the messages window.
Definition: msgwindow.c:403
void msgwin_msg_add_string(gint msg_color, gint line, GeanyDocument *doc, const gchar *string)
Adds a new message in the messages tab treeview in the messages window.
Definition: msgwindow.c:433
Message window functions (status, compiler, messages windows).
@ MSG_STATUS
Index of the status message tab.
Definition: msgwindow.h:46
@ MSG_MESSAGE
Index of the messages tab.
Definition: msgwindow.h:48
@ COLOR_BLUE
Color blue.
Definition: msgwindow.h:39
@ COLOR_BLACK
Color black.
Definition: msgwindow.h:38
@ COLOR_DARK_RED
Color dark red.
Definition: msgwindow.h:37
GeanyToolPrefs tool_prefs
Definition: prefs.c:67
#define NULL
Definition: rbtree.h:150
gchar * sci_get_line(ScintillaObject *sci, gint line_num)
Gets line contents.
Definition: sciwrappers.c:713
gint sci_get_line_count(ScintillaObject *sci)
Gets the total number of lines.
Definition: sciwrappers.c:555
gint sci_replace_target(ScintillaObject *sci, const gchar *text, gboolean regex)
Definition: sciwrappers.c:1154
void sci_set_target_start(ScintillaObject *sci, gint start)
Definition: sciwrappers.c:1140
gchar * sci_get_selection_contents(ScintillaObject *sci)
Gets selected text.
Definition: sciwrappers.c:778
gint sci_search_next(ScintillaObject *sci, gint flags, const gchar *text)
Definition: sciwrappers.c:967
gint sci_get_length(ScintillaObject *sci)
Gets the length of all text.
Definition: sciwrappers.c:656
gint sci_get_current_position(ScintillaObject *sci)
Gets the cursor position.
Definition: sciwrappers.c:507
void sci_set_selection(ScintillaObject *sci, gint anchorPos, gint currentPos)
Definition: sciwrappers.c:581
gint sci_find_text(ScintillaObject *sci, gint flags, struct Sci_TextToFind *ttf)
Finds text in the document.
Definition: sciwrappers.c:995
gboolean sci_has_selection(ScintillaObject *sci)
Checks if there's a selection.
Definition: sciwrappers.c:920
gint sci_search_prev(ScintillaObject *sci, gint flags, const gchar *text)
Definition: sciwrappers.c:974
void sci_set_target_end(ScintillaObject *sci, gint end)
Definition: sciwrappers.c:1147
gint sci_get_line_end_position(ScintillaObject *sci, gint line)
Gets the position at the end of a line.
Definition: sciwrappers.c:592
gint sci_get_line_from_position(ScintillaObject *sci, gint position)
Gets the line number from position.
Definition: sciwrappers.c:469
gint sci_get_position_from_line(ScintillaObject *sci, gint line)
Gets the position for the start of line.
Definition: sciwrappers.c:497
Wrapper functions for the Scintilla editor widget SCI_* messages.
gboolean find_regexp
Definition: search.c:92
gboolean fif_invert_results
Definition: search.c:86
GtkWidget * entry
Definition: search.c:118
static void create_find_dialog(void)
Definition: search.c:460
@ FILES_MODE_CUSTOM
Definition: search.c:73
@ FILES_MODE_ALL
Definition: search.c:71
@ FILES_MODE_PROJECT
Definition: search.c:72
void search_show_find_in_files_dialog(const gchar *dir)
Shows the Find in Files dialog.
Definition: search.c:1041
static GtkWidget * create_fif_file_mode_combo(void)
Definition: search.c:821
static struct @105 settings
static gboolean pattern_list_match(GSList *patterns, const gchar *str)
Definition: search.c:1732
static void search_read_io(GString *string, GIOCondition condition, gpointer data)
Definition: search.c:1837
static void create_fif_dialog(void)
Definition: search.c:863
GtkWidget * dialog
Definition: search.c:117
gint position[2]
Definition: search.c:120
static void setup_find_next(const gchar *text)
Definition: search.c:384
gboolean replace_escape_sequences
Definition: search.c:101
static gchar * get_regex_match_string(const gchar *text, const GeanyMatchInfo *match, guint nth)
Definition: search.c:1921
static GString * get_grep_options(void)
Definition: search.c:1553
gboolean find_regexp_multiline
Definition: search.c:93
GtkWidget * dir_combo
Definition: search.c:139
static void on_find_entry_activate_backward(GtkEntry *entry, gpointer user_data)
Definition: search.c:1274
static void set_dialog_position(GtkWidget *dialog, gint *position)
Definition: search.c:550
static void on_expander_activated(GtkExpander *exp, gpointer data)
Definition: search.c:452
gboolean replace_match_whole_word
Definition: search.c:103
static void send_replace_dialog_response(GtkButton *button, gpointer user_data)
Definition: search.c:596
gint search_mark_all(GeanyDocument *doc, const gchar *search_text, GeanyFindFlags flags)
Definition: search.c:1231
static GeanyFindFlags int_search_flags(gint match_case, gint whole_word, gint regexp, gint multiline, gint word_start)
Definition: search.c:1284
static struct @108 fif_dlg
gboolean replace_close_dialog
Definition: search.c:106
gboolean find_close_dialog
Definition: search.c:98
static void create_replace_dialog(void)
Definition: search.c:614
static gboolean search_find_in_files(const gchar *utf8_search_text, const gchar *dir, const gchar *opts, const gchar *enc)
Definition: search.c:1643
static void update_file_patterns(GtkWidget *mode_combo, GtkWidget *fcombo)
Definition: search.c:782
void search_find_usage(const gchar *search_text, const gchar *original_search_text, GeanyFindFlags flags, gboolean in_session)
Definition: search.c:2189
gboolean fif_regexp
Definition: search.c:83
GtkWidget * files_mode_combo
Definition: search.c:143
static void on_find_in_files_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
static void on_widget_toggled_set_insensitive(GtkToggleButton *togglebutton, gpointer user_data)
Definition: search.c:295
static void replace_in_session(GeanyDocument *doc, GeanyFindFlags search_flags_re, gboolean search_replace_escape_re, const gchar *find, const gchar *replace, const gchar *original_find, const gchar *original_replace)
Definition: search.c:1401
GtkWidget * replace_entry
Definition: search.c:130
void search_show_find_dialog(void)
Definition: search.c:557
gboolean find_match_word_start
Definition: search.c:97
static gint find_document_usage(GeanyDocument *doc, const gchar *search_text, GeanyFindFlags flags)
Definition: search.c:2149
static GSList * find_range(ScintillaObject *sci, GeanyFindFlags flags, struct Sci_TextToFind *ttf)
Definition: search.c:1197
gboolean fif_match_whole_word
Definition: search.c:85
static gchar ** search_get_argv(const gchar **argv_prefix, const gchar *dir)
Definition: search.c:1748
gboolean replace_regexp_multiline
Definition: search.c:100
gchar * fif_extra_options
Definition: search.c:89
gboolean find_escape_sequences
Definition: search.c:94
GtkWidget * search_combo
Definition: search.c:141
static gint geany_find_flags_to_sci_flags(GeanyFindFlags flags)
Definition: search.c:2001
static void init_prefs(void)
Definition: search.c:188
static GRegex * compile_regex(const gchar *str, GeanyFindFlags sflags)
Definition: search.c:1895
static struct @106 find_dlg
GtkWidget * encoding_combo
Definition: search.c:142
static void search_finished(GPid child_pid, gint status, gpointer user_data)
Definition: search.c:1849
gboolean fif_case_sensitive
Definition: search.c:84
static void on_replace_find_entry_activate(GtkEntry *entry, gpointer user_data)
Definition: search.c:1386
static struct @107 replace_dlg
static void on_replace_entry_activate(GtkEntry *entry, gpointer user_data)
Definition: search.c:1393
void geany_match_info_free(GeanyMatchInfo *info)
Definition: search.c:1184
void search_find_selection(GeanyDocument *doc, gboolean search_backwards)
Definition: search.c:403
gint search_find_text(ScintillaObject *sci, GeanyFindFlags flags, struct Sci_TextToFind *ttf, GeanyMatchInfo **match_)
Definition: search.c:2110
void search_finalize(void)
Definition: search.c:285
guint search_replace_range(ScintillaObject *sci, struct Sci_TextToFind *ttf, GeanyFindFlags flags, const gchar *replace_text)
Definition: search.c:2243
static StashGroup * fif_prefs
Definition: search.c:110
gboolean find_match_whole_word
Definition: search.c:96
void search_show_find_in_files_dialog_full(const gchar *text, const gchar *dir)
Definition: search.c:1047
GeanySearchData search_data
Definition: search.c:77
static void on_find_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
Definition: search.c:1296
gboolean fif_recursive
Definition: search.c:87
void search_find_again(gboolean change_direction)
Definition: search.c:2282
void search_init(void)
Definition: search.c:274
static void read_fif_io(gchar *msg, GIOCondition condition, gchar *enc, gint msg_color)
Definition: search.c:1809
gboolean replace_search_backwards
Definition: search.c:105
static void search_read_io_stderr(GString *string, GIOCondition condition, gpointer data)
Definition: search.c:1843
gint search_replace_match(ScintillaObject *sci, const GeanyMatchInfo *match, const gchar *replace_text)
Definition: search.c:2064
static void send_find_dialog_response(GtkButton *button, gpointer user_data)
Definition: search.c:377
static StashGroup * find_prefs
Definition: search.c:111
gboolean fif_use_extra_options
Definition: search.c:88
@ GEANY_RESPONSE_MARK
Definition: search.c:60
@ GEANY_RESPONSE_REPLACE
Definition: search.c:61
@ GEANY_RESPONSE_FIND
Definition: search.c:56
@ GEANY_RESPONSE_FIND_PREVIOUS
Definition: search.c:57
@ GEANY_RESPONSE_FIND_IN_SESSION
Definition: search.c:59
@ GEANY_RESPONSE_REPLACE_IN_SEL
Definition: search.c:65
@ GEANY_RESPONSE_REPLACE_IN_SESSION
Definition: search.c:63
@ GEANY_RESPONSE_REPLACE_AND_FIND
Definition: search.c:62
@ GEANY_RESPONSE_REPLACE_IN_FILE
Definition: search.c:64
@ GEANY_RESPONSE_FIND_IN_FILE
Definition: search.c:58
static void on_replace_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
Definition: search.c:1440
gint search_find_next(ScintillaObject *sci, const gchar *str, GeanyFindFlags flags, GeanyMatchInfo **match_)
Definition: search.c:2025
static void on_widget_toggled_set_sensitive(GtkToggleButton *togglebutton, gpointer user_data)
Definition: search.c:774
GtkWidget * files_combo
Definition: search.c:140
gint fif_files_mode
Definition: search.c:90
static void on_find_replace_checkbutton_toggled(GtkToggleButton *togglebutton, gpointer user_data)
Definition: search.c:1146
static StashGroup * replace_prefs
Definition: search.c:112
static GeanyMatchInfo * match_info_new(GeanyFindFlags flags, gint start, gint end)
Definition: search.c:1172
GeanySearchPrefs search_prefs
Definition: search.c:78
GtkWidget * replace_combobox
Definition: search.c:129
GtkWidget * find_combobox
Definition: search.c:127
gchar * fif_files
Definition: search.c:91
GtkWidget * find_entry
Definition: search.c:128
gboolean find_case_sensitive
Definition: search.c:95
gboolean replace_regexp
Definition: search.c:99
static void on_find_entry_activate(GtkEntry *entry, gpointer user_data)
Definition: search.c:1267
static void update_fif_file_mode_combo(void)
Definition: search.c:852
gint search_find_prev(ScintillaObject *sci, const gchar *str, GeanyFindFlags flags, GeanyMatchInfo **match_)
Definition: search.c:2012
gboolean all_expanded
Definition: search.c:119
#define FREE_WIDGET(wid)
Definition: search.c:282
gboolean replace_match_word_start
Definition: search.c:104
gboolean replace_case_sensitive
Definition: search.c:102
static gint find_regex(ScintillaObject *sci, guint pos, GRegex *regex, gboolean multiline, GeanyMatchInfo *match)
Definition: search.c:1929
static gboolean on_widget_key_pressed_set_focus(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
Definition: search.c:603
void search_show_replace_dialog(void)
Definition: search.c:734
static GtkWidget * add_find_checkboxes(GtkDialog *dialog)
Definition: search.c:303
Search (prefs).
@ GEANY_FIND_SEL_X
Definition: search.h:50
@ GEANY_FIND_SEL_AGAIN
Definition: search.h:51
GeanyFindFlags
Definition: search.h:36
@ GEANY_FIND_WORDSTART
Definition: search.h:39
@ GEANY_FIND_REGEXP
Definition: search.h:40
@ GEANY_FIND_WHOLEWORD
Definition: search.h:38
@ GEANY_FIND_MATCHCASE
Definition: search.h:37
@ GEANY_FIND_MULTILINE
Definition: search.h:41
gboolean spawn_with_callbacks(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, SpawnFlags spawn_flags, GIOFunc stdin_cb, gpointer stdin_data, SpawnReadFunc stdout_cb, gpointer stdout_data, gsize stdout_max_length, SpawnReadFunc stderr_cb, gpointer stderr_data, gsize stderr_max_length, GChildWatchFunc exit_cb, gpointer exit_data, GPid *child_pid, GError **error)
<simplesect kind="geany:skip"></simplesect> Executes a child program and setups callbacks.
Definition: spawn.c:1158
Portable and convenient process spawning and communication.
#define SPAWN_WIFEXITED(status)
non-zero if the child exited normally
Definition: spawn.h:34
#define SPAWN_WIFSIGNALED(status)
non-zero if the child exited due to signal
Definition: spawn.h:36
#define SPAWN_WEXITSTATUS(status)
exit status of a child if exited normally
Definition: spawn.h:35
StashGroup * group
Definition: stash-example.c:1
stash_group_add_boolean(group, &china_enabled, "china", TRUE)
stash_group_update(group, dialog)
stash_group_add_toggle_button(group, &want_handle, "handle", TRUE, "check_handle")
gtk_container_add(GTK_CONTAINER(dialog->vbox), check_button)
ui_hookup_widget(dialog, check_button, "check_handle")
gtk_widget_show_all(dialog)
stash_group_display(group, dialog)
void stash_group_add_integer(StashGroup *group, gint *setting, const gchar *key_name, gint default_value)
Adds integer setting.
Definition: stash.c:485
void stash_group_add_combo_box(StashGroup *group, gint *setting, const gchar *key_name, gint default_value, StashWidgetID widget_id)
Adds a GtkComboBox widget pref.
Definition: stash.c:912
StashGroup * stash_group_new(const gchar *name)
Creates a new group.
Definition: stash.c:360
void stash_group_add_entry(StashGroup *group, gchar **setting, const gchar *key_name, const gchar *default_value, StashWidgetID widget_id)
Adds a GtkEntry widget pref.
Definition: stash.c:944
Lightweight library for reading/writing GKeyFile settings and synchronizing widgets with C variables.
struct GeanyProject * project
Currently active project or NULL if none is open.
Definition: app.h:49
Structure for representing an open tab with all its properties.
Definition: document.h:81
gboolean changed
Whether this document has been changed since it was last saved.
Definition: document.h:107
gchar * encoding
The encoding of the document, must be a valid string representation of an encoding,...
Definition: document.h:95
GeanyEditor * editor
The editor associated with the document.
Definition: document.h:98
ScintillaObject * sci
The Scintilla editor GtkWidget.
Definition: editor.h:152
GtkWidget * window
Main window.
Definition: ui_utils.h:80
GtkWidget * notebook
Document notebook.
Definition: ui_utils.h:83
gint start
Definition: search.h:71
gchar * match_text
Definition: search.h:73
GeanyFindFlags flags
Definition: search.h:69
gint end
Definition: search.h:71
GStrv file_patterns
Array of filename extension patterns.
Definition: project.h:43
gchar * base_path
Base path of the project directory (in UTF-8, maybe relative).
Definition: project.h:39
Search preferences.
Definition: search.h:57
gboolean hide_find_dialog
Definition: search.h:61
gboolean replace_and_find_by_default
Definition: search.h:62
gboolean always_wrap
Definition: search.h:58
gboolean use_current_word
Use current word for default search text.
Definition: search.h:59
gboolean use_current_file_dir
Definition: search.h:60
GeanyFindSelOptions find_selection_type
Definition: search.h:63
gchar * grep_cmd
grep command
Definition: prefs.h:53
Sci_PositionCR cpMin
Definition: Scintilla.h:1177
Sci_PositionCR cpMax
Definition: Scintilla.h:1178
const char * lpstrText
Definition: Scintilla.h:1188
struct Sci_CharacterRange chrg
Definition: Scintilla.h:1187
struct Sci_CharacterRange chrgText
Definition: Scintilla.h:1189
Defines internationalization macros.
#define _(String)
Definition: support.h:42
#define ngettext(String, PluralString, Number)
Definition: support.h:41
GtkWidget * toolbar_get_widget_child_by_name(const gchar *name)
Definition: toolbar.c:141
Toolbar (prefs).
void ui_progress_bar_start(const gchar *text)
Starts a constantly pulsing progressbar in the right corner of the statusbar (if the statusbar is vis...
Definition: ui_utils.c:2822
GtkWidget * ui_dialog_vbox_new(GtkDialog *dialog)
Makes a fixed border for dialogs without increasing the button box border.
Definition: ui_utils.c:1499
gboolean ui_encodings_combo_box_set_active_encoding(GtkComboBox *combo, gint enc)
Definition: ui_utils.c:3203
void ui_save_buttons_toggle(gboolean enable)
Definition: ui_utils.c:830
GtkWidget * ui_path_box_new(const gchar *title, GtkFileChooserAction action, GtkEntry *entry)
Creates a GtkHBox with entry packed into it and an open button which runs a file chooser,...
Definition: ui_utils.c:1917
void ui_combo_box_prepend_text_once(GtkComboBoxText *combo, const gchar *text)
Definition: ui_utils.c:1730
void ui_hbutton_box_copy_layout(GtkButtonBox *master, GtkButtonBox *copy)
Definition: ui_utils.c:1651
void ui_entry_add_activate_backward_signal(GtkEntry *entry)
Definition: ui_utils.c:1613
GtkWidget * ui_create_encodings_combo_box(gboolean has_detect, gint default_enc)
Definition: ui_utils.c:3165
GeanyMainWidgets main_widgets
Definition: ui_utils.c:72
void ui_entry_add_clear_icon(GtkEntry *entry)
Adds a small clear icon to the right end of the passed entry.
Definition: ui_utils.c:1604
void ui_set_statusbar(gboolean log, const gchar *format,...)
Displays text on the statusbar.
Definition: ui_utils.c:168
gint ui_encodings_combo_box_get_active_encoding(GtkComboBox *combo)
Definition: ui_utils.c:3187
void ui_progress_bar_stop(void)
Stops a running progress bar and hides the widget again.
Definition: ui_utils.c:2842
void ui_combo_box_add_to_history(GtkComboBoxText *combo_entry, const gchar *text, gint history_len)
Prepends text to the drop down list, removing a duplicate element in the list if found.
Definition: ui_utils.c:1697
GtkWidget * ui_button_new_with_image(const gchar *stock_id, const gchar *text)
Creates a GtkButton with custom text and a stock image similar to gtk_button_new_from_stock().
Definition: ui_utils.c:1552
void ui_set_search_entry_background(GtkWidget *widget, gboolean success)
Definition: ui_utils.c:1105
GtkWidget * ui_lookup_widget(GtkWidget *widget, const gchar *widget_name)
Returns a widget from a name in a component, usually created by Glade.
Definition: ui_utils.c:2743
User Interface general utility functions.
guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
Definition: utils.c:1529
gchar * utils_get_current_file_dir_utf8(void)
Definition: utils.c:897
GSList * utils_get_file_list(const gchar *path, guint *length, GError **error)
Gets a sorted list of files from the specified directory.
Definition: utils.c:1441
void utils_beep(void)
Definition: utils.c:918
gboolean utils_str_replace_escape(gchar *string, gboolean keep_backslash)
Definition: utils.c:1071
gboolean utils_str_equal(const gchar *a, const gchar *b)
NULL-safe string comparison.
Definition: utils.c:599
const gchar * utils_get_default_dir_utf8(void)
Definition: utils.c:1615
void utils_free_pointers(gsize arg_count,...)
Definition: utils.c:1293
gchar * utils_get_locale_from_utf8(const gchar *utf8_text)
Converts the given UTF-8 encoded string into locale encoding.
Definition: utils.c:1243
General utility functions, non-GTK related.
#define foreach_slist(node, list)
Iterates all the nodes in list.
Definition: utils.h:121
#define SETPTR(ptr, result)
Assigns result to ptr, then frees the old value.
Definition: utils.h:50
#define EMPTY(ptr)
Returns TRUE if ptr is NULL or *ptr is FALSE.
Definition: utils.h:38
#define foreach_range(i, size)
Iterates from 0 to size.
Definition: utils.h:159