~ubuntu-branches/ubuntu/raring/geany/raring-proposed

« back to all changes in this revision

Viewing changes to src/search.c

  • Committer: Bazaar Package Importer
  • Author(s): Damián Viano
  • Date: 2008-05-02 11:37:45 UTC
  • mfrom: (1.2.1 upstream) (9 hardy)
  • mto: (3.2.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 12.
  • Revision ID: james.westby@ubuntu.com-20080502113745-xzp4g6dmovrpoj17
Tags: 0.14-1
New upstream release (Closes: #478126)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 *      search.c - this file is part of Geany, a fast and lightweight IDE
3
3
 *
4
 
 *      Copyright 2006 Enrico Troeger <enrico.troeger@uvena.de>
5
 
 *      Copyright 2006 Nick Treleaven <nick.treleaven@btinternet.com>
 
4
 *      Copyright 2006-2008 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
 
5
 *      Copyright 2006-2008 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
6
6
 *
7
7
 *      This program is free software; you can redistribute it and/or modify
8
8
 *      it under the terms of the GNU General Public License as published by
18
18
 *      along with this program; if not, write to the Free Software
19
19
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
20
 *
21
 
 * $Id: search.c 825 2006-09-20 11:03:33Z ntrel $
 
21
 * $Id: search.c 2389 2008-03-23 16:29:43Z eht16 $
 
22
 */
 
23
 
 
24
/*
 
25
 * Find, Replace, Find in Files dialog related functions.
 
26
 * Note that the basic text find functions are in document.c.
22
27
 */
23
28
 
24
29
#include <gdk/gdkkeysyms.h>
25
30
 
26
31
#include "geany.h"
27
32
#include "search.h"
 
33
#include "prefs.h"
28
34
#include "support.h"
29
35
#include "utils.h"
30
36
#include "msgwindow.h"
31
37
#include "document.h"
32
38
#include "sciwrappers.h"
33
 
#include "main.h"
 
39
#include "ui_utils.h"
 
40
#include "editor.h"
34
41
 
35
42
#include <unistd.h>
36
43
#include <string.h>
42
49
 
43
50
 
44
51
enum {
45
 
        GEANY_RESPONSE_REPLACE = 1,
46
 
        GEANY_RESPONSE_REPLACE_ALL,
47
 
        GEANY_RESPONSE_REPLACE_SEL,
48
 
        GEANY_RESPONSE_FIND
 
52
        GEANY_RESPONSE_FIND = 1,
 
53
        GEANY_RESPONSE_FIND_PREVIOUS,
 
54
        GEANY_RESPONSE_FIND_IN_FILE,
 
55
        GEANY_RESPONSE_FIND_IN_SESSION,
 
56
        GEANY_RESPONSE_MARK,
 
57
        GEANY_RESPONSE_REPLACE,
 
58
        GEANY_RESPONSE_REPLACE_AND_FIND,
 
59
        GEANY_RESPONSE_REPLACE_IN_SESSION,
 
60
        GEANY_RESPONSE_REPLACE_IN_FILE,
 
61
        GEANY_RESPONSE_REPLACE_IN_SEL
49
62
};
50
63
 
51
64
 
52
 
typedef enum
53
 
{
54
 
        FIF_CASE_SENSITIVE      = 1 << 0,
55
 
        FIF_WHOLE_WORD          = 1 << 1,
56
 
        FIF_INVERT_MATCH        = 1 << 2
57
 
} fif_options;
58
 
 
59
 
typedef enum
60
 
{
61
 
        FIF_FGREP,
62
 
        FIF_GREP,
63
 
        FIF_EGREP
64
 
} fif_match_type;
65
 
 
66
 
 
67
65
GeanySearchData search_data;
68
66
 
 
67
SearchPrefs search_prefs = {NULL};
 
68
 
69
69
static struct
70
70
{
71
71
        GtkWidget       *find_dialog;
82
82
 
83
83
static gchar **search_get_argv(const gchar **argv_prefix, const gchar *dir);
84
84
 
85
 
static GSList *search_get_file_list(const gchar *path, guint *length);
86
 
 
87
 
 
88
 
static void add_find_checkboxes(GtkDialog *dialog);
89
85
 
90
86
static void
91
87
on_find_replace_checkbutton_toggled(GtkToggleButton *togglebutton, gpointer user_data);
103
99
on_replace_entry_activate(GtkEntry *entry, gpointer user_data);
104
100
 
105
101
static gboolean
106
 
on_combo_entry_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
 
102
on_widget_key_pressed_set_focus(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
107
103
 
108
104
static void
109
105
on_find_in_files_dialog_response(GtkDialog *dialog, gint response, gpointer user_data);
110
106
 
111
107
static gboolean
112
 
search_find_in_files(const gchar *search_text, const gchar *dir, fif_match_type regex_opt,
113
 
                fif_options opts);
114
 
 
115
 
static void on_open_dir_dialog_clicked(GtkButton *button, gpointer user_data);
116
 
 
117
 
 
118
 
void search_init()
 
108
search_find_in_files(const gchar *search_text, const gchar *dir, const gchar *opts);
 
109
 
 
110
 
 
111
void search_init(void)
119
112
{
120
113
        widgets.find_dialog             = NULL;
121
114
        widgets.replace_dialog          = NULL;
127
120
#define FREE_WIDGET(wid) \
128
121
        if (wid && GTK_IS_WIDGET(wid)) gtk_widget_destroy(wid);
129
122
 
130
 
void search_finalize()
 
123
void search_finalize(void)
131
124
{
132
125
        FREE_WIDGET(widgets.find_dialog);
133
126
        FREE_WIDGET(widgets.replace_dialog);
136
129
}
137
130
 
138
131
 
139
 
static void add_find_checkboxes(GtkDialog *dialog)
 
132
static GtkWidget *add_find_checkboxes(GtkDialog *dialog)
140
133
{
141
 
        GtkWidget *checkbox1, *checkbox2, *check_regexp, *checkbox4, *checkbox5,
 
134
        GtkWidget *checkbox1, *checkbox2, *check_regexp, *check_back, *checkbox5,
142
135
                          *checkbox7, *hbox, *fbox, *mbox;
143
136
        GtkTooltips *tooltips = GTK_TOOLTIPS(lookup_widget(app->window, "tooltips"));
144
137
 
151
144
        g_signal_connect(G_OBJECT(check_regexp), "toggled",
152
145
                G_CALLBACK(on_find_replace_checkbutton_toggled), GTK_WIDGET(dialog));
153
146
 
154
 
        checkbox4 = gtk_check_button_new_with_mnemonic(_("_Search backwards"));
155
 
        g_object_set_data_full(G_OBJECT(dialog), "check_back",
156
 
                                        gtk_widget_ref(checkbox4), (GDestroyNotify)gtk_widget_unref);
157
 
        gtk_button_set_focus_on_click(GTK_BUTTON(checkbox4), FALSE);
158
 
 
 
147
        if (dialog != GTK_DIALOG(widgets.find_dialog))
 
148
        {
 
149
                check_back = gtk_check_button_new_with_mnemonic(_("Search _backwards"));
 
150
                g_object_set_data_full(G_OBJECT(dialog), "check_back",
 
151
                                                gtk_widget_ref(check_back), (GDestroyNotify)gtk_widget_unref);
 
152
                gtk_button_set_focus_on_click(GTK_BUTTON(check_back), FALSE);
 
153
        }
 
154
        else
 
155
        {       /* align the two checkboxes at the top of the hbox */
 
156
                GtkSizeGroup *label_size;
 
157
                check_back = gtk_label_new(NULL);
 
158
                label_size = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL);
 
159
                gtk_size_group_add_widget(GTK_SIZE_GROUP(label_size), check_back);
 
160
                gtk_size_group_add_widget(GTK_SIZE_GROUP(label_size), check_regexp);
 
161
                g_object_unref(label_size);
 
162
        }
159
163
        checkbox7 = gtk_check_button_new_with_mnemonic(_("Use _escape sequences"));
160
164
        g_object_set_data_full(G_OBJECT(dialog), "check_escape",
161
165
                                        gtk_widget_ref(checkbox7), (GDestroyNotify)gtk_widget_unref);
164
168
                _("Replace \\\\, \\t, \\n, \\r and \\uXXXX (Unicode chararacters) with the "
165
169
                  "corresponding control characters."), NULL);
166
170
 
167
 
        // Search features
 
171
        /* Search features */
168
172
        fbox = gtk_vbox_new(FALSE, 0);
169
173
        gtk_container_add(GTK_CONTAINER(fbox), check_regexp);
170
174
        gtk_container_add(GTK_CONTAINER(fbox), checkbox7);
171
 
        gtk_container_add(GTK_CONTAINER(fbox), checkbox4);
 
175
        gtk_container_add(GTK_CONTAINER(fbox), check_back);
172
176
 
173
 
        checkbox1 = gtk_check_button_new_with_mnemonic(_("_Case sensitive"));
 
177
        checkbox1 = gtk_check_button_new_with_mnemonic(_("C_ase sensitive"));
174
178
        g_object_set_data_full(G_OBJECT(dialog), "check_case",
175
179
                                        gtk_widget_ref(checkbox1), (GDestroyNotify)gtk_widget_unref);
176
180
        gtk_button_set_focus_on_click(GTK_BUTTON(checkbox1), FALSE);
180
184
                                        gtk_widget_ref(checkbox2), (GDestroyNotify)gtk_widget_unref);
181
185
        gtk_button_set_focus_on_click(GTK_BUTTON(checkbox2), FALSE);
182
186
 
183
 
        checkbox5 = gtk_check_button_new_with_mnemonic(_("Match only word s_tart"));
 
187
        checkbox5 = gtk_check_button_new_with_mnemonic(_("Match from s_tart of word"));
184
188
        g_object_set_data_full(G_OBJECT(dialog), "check_wordstart",
185
189
                                        gtk_widget_ref(checkbox5), (GDestroyNotify)gtk_widget_unref);
186
190
        gtk_button_set_focus_on_click(GTK_BUTTON(checkbox5), FALSE);
187
191
 
188
 
        // Matching options
 
192
        /* Matching options */
189
193
        mbox = gtk_vbox_new(FALSE, 0);
190
194
        gtk_container_add(GTK_CONTAINER(mbox), checkbox1);
191
195
        gtk_container_add(GTK_CONTAINER(mbox), checkbox2);
194
198
        hbox = gtk_hbox_new(TRUE, 6);
195
199
        gtk_container_add(GTK_CONTAINER(hbox), fbox);
196
200
        gtk_container_add(GTK_CONTAINER(hbox), mbox);
197
 
        gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 6);
198
 
}
199
 
 
200
 
 
201
 
void search_show_find_dialog()
 
201
        return hbox;
 
202
}
 
203
 
 
204
 
 
205
static void send_find_dialog_response(GtkButton *button, gpointer user_data)
 
206
{
 
207
        gtk_dialog_response(GTK_DIALOG(widgets.find_dialog), GPOINTER_TO_INT(user_data));
 
208
}
 
209
 
 
210
 
 
211
/* store text, clear search flags so we can use Search->Find Next/Previous */
 
212
static void setup_find_next(const gchar *text)
 
213
{
 
214
        g_free(search_data.text);
 
215
        search_data.text = g_strdup(text);
 
216
        search_data.flags = 0;
 
217
        search_data.backwards = FALSE;
 
218
        search_data.search_bar = FALSE;
 
219
}
 
220
 
 
221
 
 
222
/* Search for next match of the current "selection"
 
223
 * For X11 based systems, this will try to use the system-wide
 
224
 * x-selection first. If it doesn't find anything suitable in
 
225
 * the x-selection (or if we are on Win32) it will try to use
 
226
 * the scintilla selection or current token instead.
 
227
 * Search flags are always zero.
 
228
 */
 
229
void search_find_selection(gint idx, gboolean search_backwards)
 
230
{
 
231
        gchar *s = NULL;
 
232
#ifdef G_OS_UNIX
 
233
        GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
 
234
#endif
 
235
 
 
236
        g_return_if_fail(DOC_IDX_VALID(idx));
 
237
 
 
238
#ifdef G_OS_UNIX
 
239
        s=gtk_clipboard_wait_for_text(clipboard);
 
240
        if (s)
 
241
        {
 
242
                if (strchr(s,'\n') || strchr(s, '\r'))
 
243
                {
 
244
                        g_free(s);
 
245
                        s=NULL;
 
246
                };
 
247
        }
 
248
#endif
 
249
 
 
250
        if (!s)
 
251
                s=editor_get_default_selection(idx, TRUE, NULL);
 
252
 
 
253
        if (s)
 
254
        {
 
255
                setup_find_next(s);     /* allow find next/prev */
 
256
 
 
257
                if (document_find_text(idx, s, 0, search_backwards, FALSE, NULL) > -1)
 
258
                        editor_display_current_line(idx, 0.3F);
 
259
                g_free(s);
 
260
        }
 
261
}
 
262
 
 
263
 
 
264
/* this will load a GTK rc style to set a monospace font for text fields(GtkEntry) in all
 
265
 * search dialogs. This needs to be done only once.
 
266
 * The monospace font should increase readibility of regular expressions containing spaces, points,
 
267
 * commas and similar (#1907117). */
 
268
static void load_monospace_style()
 
269
{
 
270
        static const gchar *rcstyle =
 
271
                "style \"geany-monospace\"\n" \
 
272
                "{\n" \
 
273
                "    font_name=\"Monospace\"\n" \
 
274
                "}\n" \
 
275
                "widget \"GeanyDialogSearch.*.GtkEntry\" style \"geany-monospace\"";
 
276
        static gboolean load = TRUE;
 
277
 
 
278
        if (load)
 
279
        {
 
280
                gtk_rc_parse_string(rcstyle);
 
281
                load = FALSE;
 
282
        }
 
283
}
 
284
 
 
285
 
 
286
void search_show_find_dialog(void)
202
287
{
203
288
        gint idx = document_get_cur_idx();
204
289
        gchar *sel = NULL;
205
290
 
206
 
        if (idx == -1 || ! doc_list[idx].is_valid) return;
 
291
        g_return_if_fail(DOC_IDX_VALID(idx));
207
292
 
208
 
        if (sci_get_lines_selected(doc_list[idx].sci) == 1)
209
 
        {
210
 
                sel = g_malloc(sci_get_selected_text_length(doc_list[idx].sci));
211
 
                sci_get_selected_text(doc_list[idx].sci, sel);
212
 
        }
 
293
        sel = editor_get_default_selection(idx, prefs.search_use_current_word, NULL);
213
294
 
214
295
        if (widgets.find_dialog == NULL)
215
296
        {
216
 
                GtkWidget *label, *entry, *sbox, *align;
 
297
                GtkWidget *label, *entry, *sbox, *vbox;
 
298
                GtkWidget *exp, *bbox, *button, *check_close;
 
299
                GtkTooltips *tooltips = GTK_TOOLTIPS(lookup_widget(app->window, "tooltips"));
 
300
 
 
301
                load_monospace_style();
217
302
 
218
303
                widgets.find_dialog = gtk_dialog_new_with_buttons(_("Find"),
219
304
                        GTK_WINDOW(app->window), GTK_DIALOG_DESTROY_WITH_PARENT,
220
 
                        GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
221
 
                        GTK_STOCK_FIND, GTK_RESPONSE_ACCEPT, NULL);
222
 
 
223
 
                label = gtk_label_new(_("Search for:"));
 
305
                        GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, NULL);
 
306
                vbox = ui_dialog_vbox_new(GTK_DIALOG(widgets.find_dialog));
 
307
                gtk_widget_set_name(widgets.find_dialog, "GeanyDialogSearch");
 
308
                gtk_box_set_spacing(GTK_BOX(vbox), 9);
 
309
 
 
310
                button = ui_button_new_with_image(GTK_STOCK_GO_BACK, _("_Previous"));
 
311
                gtk_dialog_add_action_widget(GTK_DIALOG(widgets.find_dialog), button,
 
312
                        GEANY_RESPONSE_FIND_PREVIOUS);
 
313
                g_object_set_data_full(G_OBJECT(widgets.find_dialog), "btn_previous",
 
314
                                                gtk_widget_ref(button), (GDestroyNotify)gtk_widget_unref);
 
315
 
 
316
                button = ui_button_new_with_image(GTK_STOCK_GO_FORWARD, _("_Next"));
 
317
                gtk_dialog_add_action_widget(GTK_DIALOG(widgets.find_dialog), button,
 
318
                        GEANY_RESPONSE_FIND);
 
319
 
 
320
                label = gtk_label_new_with_mnemonic(_("_Search for:"));
224
321
                gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
225
322
 
226
323
                entry = gtk_combo_box_entry_new_text();
 
324
                gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
227
325
                gtk_entry_set_max_length(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry))), 248);
228
326
                gtk_entry_set_width_chars(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry))), 50);
229
327
                if (sel) gtk_entry_set_text(GTK_ENTRY(GTK_BIN(entry)->child), sel);
235
333
                g_signal_connect((gpointer) widgets.find_dialog, "response",
236
334
                                G_CALLBACK(on_find_dialog_response), entry);
237
335
                g_signal_connect((gpointer) widgets.find_dialog, "delete_event",
238
 
                                G_CALLBACK(gtk_widget_hide), NULL);
 
336
                                G_CALLBACK(gtk_widget_hide_on_delete), NULL);
239
337
 
240
338
                sbox = gtk_hbox_new(FALSE, 6);
241
339
                gtk_box_pack_start(GTK_BOX(sbox), label, FALSE, FALSE, 0);
242
340
                gtk_box_pack_start(GTK_BOX(sbox), entry, TRUE, TRUE, 0);
243
 
                align = gtk_alignment_new(0, 0, 1, 0);
244
 
                gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 0, 0, 0);
245
 
                gtk_container_add(GTK_CONTAINER(align), sbox);
246
 
                gtk_container_add(GTK_CONTAINER(GTK_DIALOG(widgets.find_dialog)->vbox), align);
247
 
 
248
 
                add_find_checkboxes(GTK_DIALOG(widgets.find_dialog));
249
 
 
250
 
                gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(widgets.find_dialog)->vbox), 3);
 
341
                gtk_box_pack_start(GTK_BOX(vbox), sbox, TRUE, FALSE, 0);
 
342
 
 
343
                gtk_container_add(GTK_CONTAINER(vbox),
 
344
                        add_find_checkboxes(GTK_DIALOG(widgets.find_dialog)));
 
345
 
 
346
                /* Now add the multiple match options */
 
347
                exp = gtk_expander_new_with_mnemonic(_("_Find All"));
 
348
                bbox = gtk_hbutton_box_new();
 
349
 
 
350
                button = gtk_button_new_with_mnemonic(_("_Mark"));
 
351
                gtk_tooltips_set_tip(tooltips, button,
 
352
                                _("Mark all matches in the current document."), NULL);
 
353
                gtk_container_add(GTK_CONTAINER(bbox), button);
 
354
                g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(send_find_dialog_response),
 
355
                        GINT_TO_POINTER(GEANY_RESPONSE_MARK));
 
356
 
 
357
                button = gtk_button_new_with_mnemonic(_("In Sessi_on"));
 
358
                gtk_container_add(GTK_CONTAINER(bbox), button);
 
359
                g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(send_find_dialog_response),
 
360
                        GINT_TO_POINTER(GEANY_RESPONSE_FIND_IN_SESSION));
 
361
 
 
362
                button = gtk_button_new_with_mnemonic(_("_In Document"));
 
363
                gtk_container_add(GTK_CONTAINER(bbox), button);
 
364
                g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(send_find_dialog_response),
 
365
                        GINT_TO_POINTER(GEANY_RESPONSE_FIND_IN_FILE));
 
366
 
 
367
                /* close window checkbox */
 
368
                check_close = gtk_check_button_new_with_mnemonic(_("Close _dialog"));
 
369
                g_object_set_data_full(G_OBJECT(widgets.find_dialog), "check_close",
 
370
                                                gtk_widget_ref(check_close), (GDestroyNotify) gtk_widget_unref);
 
371
                gtk_button_set_focus_on_click(GTK_BUTTON(check_close), FALSE);
 
372
                gtk_tooltips_set_tip(tooltips, check_close,
 
373
                                _("Disable this option to keep the dialog open."), NULL);
 
374
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_close), TRUE);
 
375
                gtk_container_add(GTK_CONTAINER(bbox), check_close);
 
376
                gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), check_close, TRUE);
 
377
 
 
378
                ui_hbutton_box_copy_layout(
 
379
                        GTK_BUTTON_BOX(GTK_DIALOG(widgets.find_dialog)->action_area),
 
380
                        GTK_BUTTON_BOX(bbox));
 
381
                gtk_container_add(GTK_CONTAINER(exp), bbox);
 
382
                gtk_container_add(GTK_CONTAINER(vbox), exp);
251
383
 
252
384
                gtk_widget_show_all(widgets.find_dialog);
253
385
        }
254
386
        else
255
387
        {
256
 
                if (sel) gtk_entry_set_text(GTK_ENTRY(GTK_BIN(
 
388
                /* only set selection if the dialog is not already visible */
 
389
                if (! GTK_WIDGET_VISIBLE(widgets.find_dialog) && sel)
 
390
                        gtk_entry_set_text(GTK_ENTRY(GTK_BIN(
257
391
                                                        lookup_widget(widgets.find_dialog, "entry"))->child), sel);
258
392
                gtk_widget_grab_focus(GTK_WIDGET(GTK_BIN(lookup_widget(widgets.find_dialog, "entry"))->child));
259
393
                gtk_widget_show(widgets.find_dialog);
 
394
                /* bring the dialog back in the foreground in case it is already open but the focus is away */
 
395
                gtk_window_present(GTK_WINDOW(widgets.find_dialog));
260
396
        }
261
397
        g_free(sel);
262
398
}
263
399
 
264
400
 
265
 
void search_show_replace_dialog()
 
401
static void send_replace_dialog_response(GtkButton *button, gpointer user_data)
 
402
{
 
403
        gtk_dialog_response(GTK_DIALOG(widgets.replace_dialog), GPOINTER_TO_INT(user_data));
 
404
}
 
405
 
 
406
 
 
407
void search_show_replace_dialog(void)
266
408
{
267
409
        gint idx = document_get_cur_idx();
268
410
        gchar *sel = NULL;
269
411
 
270
412
        if (idx == -1 || ! doc_list[idx].is_valid) return;
271
413
 
272
 
        if (sci_get_lines_selected(doc_list[idx].sci) == 1)
273
 
        {
274
 
                sel = g_malloc(sci_get_selected_text_length(doc_list[idx].sci));
275
 
                sci_get_selected_text(doc_list[idx].sci, sel);
276
 
        }
 
414
        sel = editor_get_default_selection(idx, prefs.search_use_current_word, NULL);
277
415
 
278
416
        if (widgets.replace_dialog == NULL)
279
417
        {
280
418
                GtkWidget *label_find, *label_replace, *entry_find, *entry_replace,
281
 
                        *checkbox6, *checkbox8, *button, *align, *rbox, *fbox, *hbox;
282
 
                GtkSizeGroup *size_group;
 
419
                        *check_close, *button, *rbox, *fbox, *vbox, *exp, *bbox;
 
420
                GtkSizeGroup *label_size;
283
421
                GtkTooltips *tooltips = GTK_TOOLTIPS(lookup_widget(app->window, "tooltips"));
284
422
 
 
423
                load_monospace_style();
 
424
 
285
425
                widgets.replace_dialog = gtk_dialog_new_with_buttons(_("Replace"),
286
426
                        GTK_WINDOW(app->window), GTK_DIALOG_DESTROY_WITH_PARENT,
287
427
                        GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, NULL);
 
428
                vbox = ui_dialog_vbox_new(GTK_DIALOG(widgets.replace_dialog));
 
429
                gtk_box_set_spacing(GTK_BOX(vbox), 9);
 
430
                gtk_widget_set_name(widgets.replace_dialog, "GeanyDialogSearch");
288
431
 
289
 
                button = gtk_button_new_with_mnemonic(_("_In Selection"));
290
 
                gtk_tooltips_set_tip(tooltips, button,
291
 
                        _("Replace all matches found in the currently selected text"), NULL);
292
 
                gtk_widget_show(button);
293
 
                gtk_dialog_add_action_widget(GTK_DIALOG(widgets.replace_dialog), button,
294
 
                        GEANY_RESPONSE_REPLACE_SEL);
295
 
                button = gtk_button_new_with_mnemonic(_("Replace _All"));
296
 
                gtk_widget_show(button);
297
 
                gtk_dialog_add_action_widget(GTK_DIALOG(widgets.replace_dialog), button,
298
 
                        GEANY_RESPONSE_REPLACE_ALL);
299
432
                button = gtk_button_new_from_stock(GTK_STOCK_FIND);
300
 
                gtk_widget_show(button);
301
433
                gtk_dialog_add_action_widget(GTK_DIALOG(widgets.replace_dialog), button,
302
434
                        GEANY_RESPONSE_FIND);
303
435
                button = gtk_button_new_with_mnemonic(_("_Replace"));
304
 
                gtk_widget_show(button);
305
436
                gtk_dialog_add_action_widget(GTK_DIALOG(widgets.replace_dialog), button,
306
437
                        GEANY_RESPONSE_REPLACE);
 
438
                button = gtk_button_new_with_mnemonic(_("Replace & Fi_nd"));
 
439
                gtk_dialog_add_action_widget(GTK_DIALOG(widgets.replace_dialog), button,
 
440
                        GEANY_RESPONSE_REPLACE_AND_FIND);
307
441
 
308
 
                label_find = gtk_label_new(_("Search for:"));
 
442
                label_find = gtk_label_new_with_mnemonic(_("_Search for:"));
309
443
                gtk_misc_set_alignment(GTK_MISC(label_find), 0, 0.5);
310
444
 
311
 
                label_replace = gtk_label_new(_("Replace with:"));
 
445
                label_replace = gtk_label_new_with_mnemonic(_("Replace wit_h:"));
312
446
                gtk_misc_set_alignment(GTK_MISC(label_replace), 0, 0.5);
313
447
 
314
448
                entry_find = gtk_combo_box_entry_new_text();
 
449
                gtk_label_set_mnemonic_widget(GTK_LABEL(label_find), entry_find);
315
450
                gtk_entry_set_max_length(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry_find))), 248);
316
451
                gtk_entry_set_width_chars(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry_find))), 50);
317
452
                if (sel) gtk_entry_set_text(GTK_ENTRY(GTK_BIN(entry_find)->child), sel);
319
454
                                                gtk_widget_ref(entry_find), (GDestroyNotify)gtk_widget_unref);
320
455
 
321
456
                entry_replace = gtk_combo_box_entry_new_text();
 
457
                gtk_label_set_mnemonic_widget(GTK_LABEL(label_replace), entry_replace);
322
458
                gtk_entry_set_max_length(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry_replace))), 248);
323
459
                gtk_entry_set_width_chars(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry_replace))), 50);
324
460
                g_object_set_data_full(G_OBJECT(widgets.replace_dialog), "entry_replace",
325
461
                                                gtk_widget_ref(entry_replace), (GDestroyNotify)gtk_widget_unref);
326
462
 
327
 
                g_signal_connect((gpointer) gtk_bin_get_child(GTK_BIN(entry_find)), "key-press-event",
328
 
                                G_CALLBACK(on_combo_entry_key_pressed), gtk_bin_get_child(GTK_BIN(entry_replace)));
 
463
                g_signal_connect((gpointer) gtk_bin_get_child(GTK_BIN(entry_find)),
 
464
                                "key-press-event", G_CALLBACK(on_widget_key_pressed_set_focus),
 
465
                                gtk_bin_get_child(GTK_BIN(entry_replace)));
329
466
                g_signal_connect((gpointer) gtk_bin_get_child(GTK_BIN(entry_replace)), "activate",
330
467
                                G_CALLBACK(on_replace_entry_activate), NULL);
331
468
                g_signal_connect((gpointer) widgets.replace_dialog, "response",
332
469
                                G_CALLBACK(on_replace_dialog_response), entry_replace);
333
470
                g_signal_connect((gpointer) widgets.replace_dialog, "delete_event",
334
 
                                G_CALLBACK(gtk_widget_hide), NULL);
 
471
                                G_CALLBACK(gtk_widget_hide_on_delete), NULL);
335
472
 
336
473
                fbox = gtk_hbox_new(FALSE, 6);
337
474
                gtk_box_pack_start(GTK_BOX(fbox), label_find, FALSE, FALSE, 0);
338
475
                gtk_box_pack_start(GTK_BOX(fbox), entry_find, TRUE, TRUE, 0);
339
 
                gtk_box_pack_start(GTK_BOX(GTK_DIALOG(widgets.replace_dialog)->vbox), fbox,
340
 
                        FALSE, FALSE, 6);
341
476
 
342
477
                rbox = gtk_hbox_new(FALSE, 6);
343
478
                gtk_box_pack_start(GTK_BOX(rbox), label_replace, FALSE, FALSE, 0);
344
479
                gtk_box_pack_start(GTK_BOX(rbox), entry_replace, TRUE, TRUE, 0);
345
 
                gtk_box_pack_start(GTK_BOX(GTK_DIALOG(widgets.replace_dialog)->vbox), rbox,
346
 
                        FALSE, FALSE, 3);
347
 
 
348
 
                size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
349
 
                gtk_size_group_add_widget(size_group, label_find);
350
 
                gtk_size_group_add_widget(size_group, label_replace);
351
 
                g_object_unref(G_OBJECT(size_group));   // auto destroy the size group
352
 
 
353
 
                add_find_checkboxes(GTK_DIALOG(widgets.replace_dialog));
354
 
 
355
 
                checkbox6 = gtk_check_button_new_with_mnemonic(_("Replace in all _open files"));
356
 
                g_object_set_data_full(G_OBJECT(widgets.replace_dialog), "check_all_buffers",
357
 
                                                gtk_widget_ref(checkbox6), (GDestroyNotify)gtk_widget_unref);
358
 
                gtk_tooltips_set_tip(tooltips, checkbox6,
359
 
                        _("Replaces the search text in all opened files. This option is only useful(and used) if you click on \"Replace All\"."), NULL);
360
 
                gtk_button_set_focus_on_click(GTK_BUTTON(checkbox6), FALSE);
361
 
 
362
 
                // Don't close window checkbox
363
 
                checkbox8 = gtk_check_button_new_with_mnemonic(_("_Don't close this dialog"));
364
 
                g_object_set_data_full(G_OBJECT(widgets.replace_dialog), "check_dont_close_window",
365
 
                                                gtk_widget_ref(checkbox8), (GDestroyNotify) gtk_widget_unref);
366
 
                gtk_button_set_focus_on_click(GTK_BUTTON(checkbox8), FALSE);
367
 
                gtk_tooltips_set_tip(tooltips, checkbox8,
368
 
                                _("The dialog window won't be closed when you start the operation."), NULL);
369
 
 
370
 
                hbox = gtk_hbox_new(TRUE, 6);
371
 
                gtk_container_add(GTK_CONTAINER(hbox), checkbox6);
372
 
                gtk_container_add(GTK_CONTAINER(hbox), checkbox8);
373
 
 
374
 
                align = gtk_alignment_new(0, 0, 1, 1);
375
 
                gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 9, 0, 0);
376
 
                gtk_container_add(GTK_CONTAINER(align), hbox);
377
 
                gtk_container_add(GTK_CONTAINER(GTK_DIALOG(widgets.replace_dialog)->vbox), align);
 
480
 
 
481
                label_size = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
482
                gtk_size_group_add_widget(label_size, label_find);
 
483
                gtk_size_group_add_widget(label_size, label_replace);
 
484
                g_object_unref(G_OBJECT(label_size));   /* auto destroy the size group */
 
485
 
 
486
                gtk_box_pack_start(GTK_BOX(vbox), fbox, TRUE, FALSE, 0);
 
487
                gtk_box_pack_start(GTK_BOX(vbox), rbox, TRUE, FALSE, 0);
 
488
                gtk_container_add(GTK_CONTAINER(vbox),
 
489
                        add_find_checkboxes(GTK_DIALOG(widgets.replace_dialog)));
 
490
 
 
491
                /* Now add the multiple replace options */
 
492
                exp = gtk_expander_new_with_mnemonic(_("Re_place All"));
 
493
                bbox = gtk_hbutton_box_new();
 
494
 
 
495
                button = gtk_button_new_with_mnemonic(_("In Se_lection"));
 
496
                gtk_tooltips_set_tip(tooltips, button,
 
497
                        _("Replace all matches found in the currently selected text"), NULL);
 
498
                gtk_container_add(GTK_CONTAINER(bbox), button);
 
499
                g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(send_replace_dialog_response),
 
500
                        GINT_TO_POINTER(GEANY_RESPONSE_REPLACE_IN_SEL));
 
501
 
 
502
                button = gtk_button_new_with_mnemonic(_("In Sessi_on"));
 
503
                gtk_container_add(GTK_CONTAINER(bbox), button);
 
504
                g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(send_replace_dialog_response),
 
505
                        GINT_TO_POINTER(GEANY_RESPONSE_REPLACE_IN_SESSION));
 
506
 
 
507
                button = gtk_button_new_with_mnemonic(_("_In Document"));
 
508
                gtk_container_add(GTK_CONTAINER(bbox), button);
 
509
                g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(send_replace_dialog_response),
 
510
                        GINT_TO_POINTER(GEANY_RESPONSE_REPLACE_IN_FILE));
 
511
 
 
512
                /* close window checkbox */
 
513
                check_close = gtk_check_button_new_with_mnemonic(_("Close _dialog"));
 
514
                g_object_set_data_full(G_OBJECT(widgets.replace_dialog), "check_close",
 
515
                                                gtk_widget_ref(check_close), (GDestroyNotify) gtk_widget_unref);
 
516
                gtk_button_set_focus_on_click(GTK_BUTTON(check_close), FALSE);
 
517
                gtk_tooltips_set_tip(tooltips, check_close,
 
518
                                _("Disable this option to keep the dialog open."), NULL);
 
519
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_close), TRUE);
 
520
                gtk_container_add(GTK_CONTAINER(bbox), check_close);
 
521
                gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), check_close, TRUE);
 
522
 
 
523
                ui_hbutton_box_copy_layout(
 
524
                        GTK_BUTTON_BOX(GTK_DIALOG(widgets.replace_dialog)->action_area),
 
525
                        GTK_BUTTON_BOX(bbox));
 
526
                gtk_container_add(GTK_CONTAINER(exp), bbox);
 
527
                gtk_container_add(GTK_CONTAINER(vbox), exp);
378
528
 
379
529
                gtk_widget_show_all(widgets.replace_dialog);
380
530
        }
381
531
        else
382
532
        {
383
 
                if (sel) gtk_entry_set_text(GTK_ENTRY(GTK_BIN(
 
533
                /* only set selection if the dialog is not already visible */
 
534
                if (! GTK_WIDGET_VISIBLE(widgets.replace_dialog) && sel)
 
535
                        gtk_entry_set_text(GTK_ENTRY(GTK_BIN(
384
536
                                                        lookup_widget(widgets.replace_dialog, "entry_find"))->child), sel);
385
537
                gtk_widget_grab_focus(GTK_WIDGET(GTK_BIN(lookup_widget(widgets.replace_dialog, "entry_find"))->child));
386
538
                gtk_widget_show(widgets.replace_dialog);
 
539
                /* bring the dialog back in the foreground in case it is already open but the focus is away */
 
540
                gtk_window_present(GTK_WINDOW(widgets.replace_dialog));
387
541
        }
388
542
        g_free(sel);
389
543
}
390
544
 
391
545
 
392
 
void search_show_find_in_files_dialog()
 
546
static void on_extra_options_toggled(GtkToggleButton *togglebutton, gpointer user_data)
 
547
{
 
548
        /* disable extra option entry when checkbutton not checked */
 
549
        gtk_widget_set_sensitive(GTK_WIDGET(user_data),
 
550
                gtk_toggle_button_get_active(togglebutton));
 
551
}
 
552
 
 
553
 
 
554
/* dir is the directory to search in (UTF-8 encoding), maybe NULL to determine it the usual way
 
555
 * by using the current file's path */
 
556
void search_show_find_in_files_dialog(const gchar *dir)
393
557
{
394
558
        static GtkWidget *combo = NULL;
395
 
        static GtkWidget *entry1;
396
 
        GtkWidget *entry2; // the child GtkEntry of combo
397
 
        GtkWidget *dirbtn, *openimg;
 
559
        static GtkWidget *dir_combo;
 
560
        GtkWidget *entry; /* the child GtkEntry of combo (or dir_combo) */
398
561
        gint idx = document_get_cur_idx();
399
562
        gchar *sel = NULL;
400
563
        gchar *cur_dir;
401
564
 
402
 
        if (idx == -1 || ! doc_list[idx].is_valid) return;
403
 
 
404
 
        cur_dir = utils_get_current_file_dir();
405
 
 
406
565
        if (widgets.find_in_files_dialog == NULL)
407
566
        {
408
 
                GtkWidget *label, *label1, *checkbox1, *checkbox2, *check_wholeword;
409
 
                GtkWidget *dbox, *sbox, *cbox, *rbox, *rbtn, *hbox;
 
567
                GtkWidget *label, *label1, *checkbox1, *checkbox2, *check_wholeword,
 
568
                        *check_recursive, *check_extra, *entry_extra;
 
569
                GtkWidget *dbox, *sbox, *cbox, *rbox, *rbtn, *hbox, *vbox;
410
570
                GtkSizeGroup *size_group;
411
571
                GtkTooltips *tooltips = GTK_TOOLTIPS(lookup_widget(app->window, "tooltips"));
412
572
 
 
573
                load_monospace_style();
 
574
 
413
575
                widgets.find_in_files_dialog = gtk_dialog_new_with_buttons(
414
 
                        _("Find in files"), GTK_WINDOW(app->window), GTK_DIALOG_DESTROY_WITH_PARENT,
 
576
                        _("Find in Files"), GTK_WINDOW(app->window), GTK_DIALOG_DESTROY_WITH_PARENT,
415
577
                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
 
578
                vbox = ui_dialog_vbox_new(GTK_DIALOG(widgets.find_in_files_dialog));
 
579
                gtk_box_set_spacing(GTK_BOX(vbox), 9);
 
580
                gtk_widget_set_name(widgets.find_in_files_dialog, "GeanyDialogSearch");
416
581
 
417
582
                gtk_dialog_add_button(GTK_DIALOG(widgets.find_in_files_dialog), "gtk-find", GTK_RESPONSE_ACCEPT);
418
583
                gtk_dialog_set_default_response(GTK_DIALOG(widgets.find_in_files_dialog),
419
584
                        GTK_RESPONSE_ACCEPT);
420
585
 
421
 
                label1 = gtk_label_new(_("Directory:"));
 
586
                label1 = gtk_label_new_with_mnemonic(_("_Directory:"));
422
587
                gtk_misc_set_alignment(GTK_MISC(label1), 0, 0.5);
423
588
 
424
 
                entry1 = gtk_entry_new();
425
 
                gtk_entry_set_max_length(GTK_ENTRY(entry1), 248);
426
 
                gtk_entry_set_width_chars(GTK_ENTRY(entry1), 50);
427
 
                g_object_set_data_full(G_OBJECT(widgets.find_in_files_dialog), "entry_dir",
428
 
                                                gtk_widget_ref(entry1), (GDestroyNotify)gtk_widget_unref);
429
 
 
430
 
                dirbtn = gtk_button_new();
431
 
                openimg = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
432
 
                gtk_container_add(GTK_CONTAINER(dirbtn), openimg);
433
 
                g_signal_connect(G_OBJECT(dirbtn), "clicked", G_CALLBACK(on_open_dir_dialog_clicked),
434
 
                        NULL);
435
 
 
436
 
                dbox = gtk_hbox_new(FALSE, 6);
 
589
                dir_combo = gtk_combo_box_entry_new_text();
 
590
                entry = gtk_bin_get_child(GTK_BIN(dir_combo));
 
591
                gtk_label_set_mnemonic_widget(GTK_LABEL(label1), entry);
 
592
                gtk_entry_set_max_length(GTK_ENTRY(entry), 248);
 
593
                gtk_entry_set_width_chars(GTK_ENTRY(entry), 50);
 
594
                g_object_set_data_full(G_OBJECT(widgets.find_in_files_dialog), "dir_combo",
 
595
                                                gtk_widget_ref(dir_combo), (GDestroyNotify)gtk_widget_unref);
 
596
 
 
597
                dbox = ui_path_box_new(NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
 
598
                        GTK_ENTRY(entry));
437
599
                gtk_box_pack_start(GTK_BOX(dbox), label1, FALSE, FALSE, 0);
438
 
                gtk_box_pack_start(GTK_BOX(dbox), entry1, TRUE, TRUE, 0);
439
 
                gtk_box_pack_start(GTK_BOX(dbox), dirbtn, FALSE, FALSE, 0);
440
600
 
441
 
                label = gtk_label_new(_("Search for:"));
 
601
                label = gtk_label_new_with_mnemonic(_("_Search for:"));
442
602
                gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
443
603
 
444
604
                combo = gtk_combo_box_entry_new_text();
445
 
                entry2 = gtk_bin_get_child(GTK_BIN(combo));
446
 
                gtk_entry_set_max_length(GTK_ENTRY(entry2), 248);
447
 
                gtk_entry_set_width_chars(GTK_ENTRY(entry2), 50);
448
 
                gtk_entry_set_activates_default(GTK_ENTRY(entry2), TRUE);
 
605
                entry = gtk_bin_get_child(GTK_BIN(combo));
 
606
                gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
 
607
                gtk_entry_set_max_length(GTK_ENTRY(entry), 248);
 
608
                gtk_entry_set_width_chars(GTK_ENTRY(entry), 50);
 
609
                gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
449
610
 
450
611
                sbox = gtk_hbox_new(FALSE, 6);
451
612
                gtk_box_pack_start(GTK_BOX(sbox), label, FALSE, FALSE, 0);
454
615
                size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
455
616
                gtk_size_group_add_widget(size_group, label1);
456
617
                gtk_size_group_add_widget(size_group, label);
457
 
                g_object_unref(G_OBJECT(size_group));   // auto destroy the size group
 
618
                g_object_unref(G_OBJECT(size_group));   /* auto destroy the size group */
458
619
 
459
620
                rbox = gtk_vbox_new(FALSE, 0);
460
 
                rbtn = gtk_radio_button_new_with_mnemonic(NULL, _("_Fixed strings"));
461
 
                // Make fixed strings the default to speed up searching all files in directory.
 
621
                rbtn = gtk_radio_button_new_with_mnemonic(NULL, _("Fixed s_trings"));
 
622
                /* Make fixed strings the default to speed up searching all files in directory. */
462
623
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rbtn), TRUE);
463
624
                g_object_set_data_full(G_OBJECT(widgets.find_in_files_dialog), "radio_fgrep",
464
625
                                                gtk_widget_ref(rbtn), (GDestroyNotify)gtk_widget_unref);
481
642
                gtk_button_set_focus_on_click(GTK_BUTTON(rbtn), FALSE);
482
643
                gtk_container_add(GTK_CONTAINER(rbox), rbtn);
483
644
 
484
 
                checkbox1 = gtk_check_button_new_with_mnemonic(_("_Case sensitive"));
 
645
                check_recursive = gtk_check_button_new_with_mnemonic(_("_Recurse in subfolders"));
 
646
                g_object_set_data_full(G_OBJECT(widgets.find_in_files_dialog), "check_recursive",
 
647
                                                gtk_widget_ref(check_recursive), (GDestroyNotify)gtk_widget_unref);
 
648
                gtk_button_set_focus_on_click(GTK_BUTTON(check_recursive), FALSE);
 
649
 
 
650
                checkbox1 = gtk_check_button_new_with_mnemonic(_("C_ase sensitive"));
485
651
                g_object_set_data_full(G_OBJECT(widgets.find_in_files_dialog), "check_case",
486
652
                                                gtk_widget_ref(checkbox1), (GDestroyNotify)gtk_widget_unref);
487
653
                gtk_button_set_focus_on_click(GTK_BUTTON(checkbox1), FALSE);
503
669
                gtk_container_add(GTK_CONTAINER(cbox), checkbox1);
504
670
                gtk_container_add(GTK_CONTAINER(cbox), check_wholeword);
505
671
                gtk_container_add(GTK_CONTAINER(cbox), checkbox2);
 
672
                gtk_container_add(GTK_CONTAINER(cbox), check_recursive);
506
673
 
507
674
                hbox = gtk_hbox_new(FALSE, 6);
508
675
                gtk_container_add(GTK_CONTAINER(hbox), rbox);
509
676
                gtk_container_add(GTK_CONTAINER(hbox), cbox);
510
677
 
511
 
                gtk_box_pack_start(GTK_BOX(GTK_DIALOG(widgets.find_in_files_dialog)->vbox),
512
 
                        dbox, TRUE, FALSE, 6);
513
 
                gtk_box_pack_start(GTK_BOX(GTK_DIALOG(widgets.find_in_files_dialog)->vbox),
514
 
                        sbox, TRUE, FALSE, 0);
515
 
                gtk_box_pack_start(GTK_BOX(GTK_DIALOG(widgets.find_in_files_dialog)->vbox),
516
 
                        hbox, TRUE, TRUE, 6);
517
 
 
518
 
                gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(widgets.find_in_files_dialog)->vbox), 6);
519
 
 
520
 
                g_signal_connect((gpointer) entry1, "key-press-event",
521
 
                                G_CALLBACK(on_combo_entry_key_pressed), gtk_bin_get_child(GTK_BIN(combo)));
 
678
                gtk_box_pack_start(GTK_BOX(vbox), dbox, TRUE, FALSE, 0);
 
679
                gtk_box_pack_start(GTK_BOX(vbox), sbox, TRUE, FALSE, 0);
 
680
                gtk_container_add(GTK_CONTAINER(vbox), hbox);
 
681
 
 
682
                check_extra = gtk_check_button_new_with_mnemonic(_("E_xtra options:"));
 
683
                g_object_set_data_full(G_OBJECT(widgets.find_in_files_dialog), "check_extra",
 
684
                                                gtk_widget_ref(check_extra), (GDestroyNotify)gtk_widget_unref);
 
685
                gtk_button_set_focus_on_click(GTK_BUTTON(check_extra), FALSE);
 
686
 
 
687
                entry_extra = gtk_entry_new();
 
688
                if (search_prefs.fif_extra_options)
 
689
                        gtk_entry_set_text(GTK_ENTRY(entry_extra), search_prefs.fif_extra_options);
 
690
                gtk_widget_set_sensitive(entry_extra, FALSE);
 
691
                g_object_set_data_full(G_OBJECT(widgets.find_in_files_dialog), "entry_extra",
 
692
                                                gtk_widget_ref(entry_extra), (GDestroyNotify)gtk_widget_unref);
 
693
                gtk_tooltips_set_tip(tooltips, entry_extra,
 
694
                                _("Other options to pass to Grep"), NULL);
 
695
 
 
696
                /* enable entry_extra when check_extra is checked */
 
697
                g_signal_connect(G_OBJECT(check_extra), "toggled",
 
698
                        G_CALLBACK(on_extra_options_toggled), entry_extra);
 
699
 
 
700
                hbox = gtk_hbox_new(FALSE, 6);
 
701
                gtk_box_pack_start(GTK_BOX(hbox), check_extra, FALSE, FALSE, 0);
 
702
                gtk_box_pack_start(GTK_BOX(hbox), entry_extra, TRUE, TRUE, 0);
 
703
                gtk_container_add(GTK_CONTAINER(vbox), hbox);
 
704
 
 
705
                g_signal_connect((gpointer) dir_combo, "key-press-event",
 
706
                                G_CALLBACK(on_widget_key_pressed_set_focus), combo);
522
707
                g_signal_connect((gpointer) widgets.find_in_files_dialog, "response",
523
708
                                G_CALLBACK(on_find_in_files_dialog_response), combo);
524
709
                g_signal_connect((gpointer) widgets.find_in_files_dialog, "delete_event",
525
 
                                G_CALLBACK(gtk_widget_hide), NULL);
 
710
                                G_CALLBACK(gtk_widget_hide_on_delete), NULL);
526
711
 
527
712
                gtk_widget_show_all(widgets.find_in_files_dialog);
 
713
                sel = editor_get_default_selection(idx, prefs.search_use_current_word, NULL);
528
714
        }
529
715
 
530
 
        if (sci_get_lines_selected(doc_list[idx].sci) == 1)
 
716
        entry = GTK_BIN(combo)->child;
 
717
        /* only set selection if the dialog is not already visible, or has just been created */
 
718
        if (! sel && ! GTK_WIDGET_VISIBLE(widgets.find_in_files_dialog))
 
719
                sel = editor_get_default_selection(idx, prefs.search_use_current_word, NULL);
 
720
        if (sel)
 
721
                gtk_entry_set_text(GTK_ENTRY(entry), sel);
 
722
        g_free(sel);
 
723
 
 
724
        entry = GTK_BIN(dir_combo)->child;
 
725
        if (NZV(dir))
 
726
                cur_dir = g_strdup(dir);
 
727
        else
 
728
                cur_dir = utils_get_current_file_dir_utf8();
 
729
        if (cur_dir)
531
730
        {
532
 
                sel = g_malloc(sci_get_selected_text_length(doc_list[idx].sci));
533
 
                sci_get_selected_text(doc_list[idx].sci, sel);
534
 
        }
535
 
 
536
 
        entry2 = GTK_BIN(combo)->child;
537
 
        if (sel) gtk_entry_set_text(GTK_ENTRY(entry2), sel);
538
 
        g_free(sel);
539
 
 
540
 
        if (cur_dir) gtk_entry_set_text(GTK_ENTRY(entry1), cur_dir);
541
 
        g_free(cur_dir);
542
 
 
543
 
        // put the focus to the directory entry if it is empty
544
 
        if (utils_strcmp(gtk_entry_get_text(GTK_ENTRY(entry1)), ""))
545
 
                gtk_widget_grab_focus(entry1);
546
 
        else
547
 
                gtk_widget_grab_focus(entry2);
 
731
                gtk_entry_set_text(GTK_ENTRY(entry), cur_dir);
 
732
                g_free(cur_dir);
 
733
        }
 
734
        else
 
735
        {       /* use default_open_path if no directory could be determined (e.g. when no files are open) */
 
736
                gtk_entry_set_text(GTK_ENTRY(entry), prefs.default_open_path);
 
737
        }
 
738
 
 
739
        /* put the focus to the directory entry if it is empty */
 
740
        if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(entry)), ""))
 
741
                gtk_widget_grab_focus(dir_combo);
 
742
        else
 
743
                gtk_widget_grab_focus(combo);
548
744
 
549
745
        gtk_widget_show(widgets.find_in_files_dialog);
550
 
}
551
 
 
552
 
 
553
 
static void on_open_dir_dialog_clicked(GtkButton *button, gpointer user_data)
554
 
{
555
 
        GtkWidget *dialog = gtk_file_chooser_dialog_new(_("Select folder"),
556
 
                GTK_WINDOW(app->window), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
557
 
                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
558
 
                GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
559
 
        GtkWidget *entry_dir = lookup_widget(widgets.find_in_files_dialog, "entry_dir");
560
 
        gchar *dir_locale;
561
 
        const gchar *entry_text;
562
 
 
563
 
        entry_text = gtk_entry_get_text(GTK_ENTRY(entry_dir));
564
 
        dir_locale = utils_get_locale_from_utf8(entry_text);
565
 
        if (g_file_test(dir_locale, G_FILE_TEST_IS_DIR))
566
 
                gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir_locale);
567
 
        g_free(dir_locale);
568
 
 
569
 
        if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
570
 
        {
571
 
                gchar *dir_utf8;
572
 
                dir_locale = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
573
 
                dir_utf8 = utils_get_utf8_from_locale(dir_locale);
574
 
                g_free(dir_locale);
575
 
                gtk_entry_set_text(GTK_ENTRY(entry_dir), dir_utf8);
576
 
                g_free(dir_utf8);
577
 
        }
578
 
        gtk_widget_destroy(dialog);
 
746
        /* bring the dialog back in the foreground in case it is already open but the focus is away */
 
747
        gtk_window_present(GTK_WINDOW(widgets.find_in_files_dialog));
579
748
}
580
749
 
581
750
 
589
758
        if (togglebutton == chk_regexp)
590
759
        {
591
760
                gboolean regex_set = gtk_toggle_button_get_active(chk_regexp);
592
 
                GtkWidget *check_back = lookup_widget(dialog, "check_back");
593
761
                GtkWidget *check_word = lookup_widget(dialog, "check_word");
594
762
                GtkWidget *check_wordstart = lookup_widget(dialog, "check_wordstart");
595
763
                GtkToggleButton *check_case = GTK_TOGGLE_BUTTON(
596
764
                        lookup_widget(dialog, "check_case"));
597
 
                static gboolean case_state = FALSE; // state before regex enabled
598
 
 
599
 
                // hide options that don't apply to regex searches
600
 
                gtk_widget_set_sensitive(check_back, ! regex_set);
 
765
                static gboolean case_state = FALSE; /* state before regex enabled */
 
766
 
 
767
                /* hide options that don't apply to regex searches */
 
768
                if (dialog == widgets.find_dialog)
 
769
                        gtk_widget_set_sensitive(lookup_widget(dialog, "btn_previous"), ! regex_set);
 
770
                else
 
771
                        gtk_widget_set_sensitive(lookup_widget(dialog, "check_back"), ! regex_set);
 
772
 
601
773
                gtk_widget_set_sensitive(check_word, ! regex_set);
602
774
                gtk_widget_set_sensitive(check_wordstart, ! regex_set);
603
775
 
604
 
                if (regex_set)  // regex enabled
 
776
                if (regex_set)  /* regex enabled */
605
777
                {
606
 
                        // Enable case sensitive but remember original case toggle state
 
778
                        /* Enable case sensitive but remember original case toggle state */
607
779
                        case_state = gtk_toggle_button_get_active(check_case);
608
780
                        gtk_toggle_button_set_active(check_case, TRUE);
609
781
                }
610
 
                else    // regex disabled
 
782
                else    /* regex disabled */
611
783
                {
612
 
                        // If case sensitive is still enabled, revert to what it was before we enabled it
 
784
                        /* If case sensitive is still enabled, revert to what it was before we enabled it */
613
785
                        if (gtk_toggle_button_get_active(check_case) == TRUE)
614
786
                                gtk_toggle_button_set_active(check_case, case_state);
615
787
                }
617
789
}
618
790
 
619
791
 
 
792
static gint search_mark(gint idx, const gchar *search_text, gint flags)
 
793
{
 
794
        gint pos, line, count = 0;
 
795
        struct TextToFind ttf;
 
796
 
 
797
        g_return_val_if_fail(DOC_IDX_VALID(idx), 0);
 
798
 
 
799
        ttf.chrg.cpMin = 0;
 
800
        ttf.chrg.cpMax = sci_get_length(doc_list[idx].sci);
 
801
        ttf.lpstrText = (gchar *)search_text;
 
802
        while (1)
 
803
        {
 
804
                pos = sci_find_text(doc_list[idx].sci, flags, &ttf);
 
805
                if (pos == -1) break;
 
806
 
 
807
                line = sci_get_line_from_position(doc_list[idx].sci, pos);
 
808
                sci_set_marker_at_line(doc_list[idx].sci, line, TRUE, 1);
 
809
 
 
810
                ttf.chrg.cpMin = ttf.chrgText.cpMax + 1;
 
811
                count++;
 
812
        }
 
813
        return count;
 
814
}
 
815
 
 
816
 
 
817
static void
 
818
on_find_entry_activate(GtkEntry *entry, gpointer user_data)
 
819
{
 
820
        on_find_dialog_response(NULL, GEANY_RESPONSE_FIND,
 
821
                                lookup_widget(GTK_WIDGET(entry), "entry"));
 
822
}
 
823
 
 
824
 
 
825
static gint get_search_flags(GtkWidget *dialog)
 
826
{
 
827
        gboolean fl1, fl2, fl3, fl4;
 
828
 
 
829
        fl1 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
830
                                lookup_widget(dialog, "check_case")));
 
831
        fl2 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
832
                                lookup_widget(dialog, "check_word")));
 
833
        fl3 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
834
                                lookup_widget(dialog, "check_regexp")));
 
835
        fl4 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
836
                                lookup_widget(dialog, "check_wordstart")));
 
837
 
 
838
        return (fl1 ? SCFIND_MATCHCASE : 0) |
 
839
                (fl2 ? SCFIND_WHOLEWORD : 0) |
 
840
                (fl3 ? SCFIND_REGEXP | SCFIND_POSIX : 0) |
 
841
                (fl4 ? SCFIND_WORDSTART : 0);
 
842
}
 
843
 
 
844
 
620
845
static void
621
846
on_find_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
622
847
{
623
 
        if (response == GTK_RESPONSE_ACCEPT)
 
848
        if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
 
849
                gtk_widget_hide(widgets.find_dialog);
 
850
        else
624
851
        {
625
852
                gint idx = document_get_cur_idx();
626
853
                gboolean search_replace_escape;
627
 
                gboolean
628
 
                        fl1 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
629
 
                                                lookup_widget(GTK_WIDGET(widgets.find_dialog), "check_case"))),
630
 
                        fl2 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
631
 
                                                lookup_widget(GTK_WIDGET(widgets.find_dialog), "check_word"))),
632
 
                        fl3 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
633
 
                                                lookup_widget(GTK_WIDGET(widgets.find_dialog), "check_regexp"))),
634
 
                        fl4 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
635
 
                                                lookup_widget(GTK_WIDGET(widgets.find_dialog), "check_wordstart")));
 
854
                gboolean check_close = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
855
                                                lookup_widget(GTK_WIDGET(widgets.find_dialog), "check_close")));
 
856
 
636
857
                search_replace_escape = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
637
858
                                                lookup_widget(GTK_WIDGET(widgets.find_dialog), "check_escape")));
638
 
                search_data.backwards = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
639
 
                                                lookup_widget(GTK_WIDGET(widgets.find_dialog), "check_back")));
 
859
                search_data.backwards = FALSE;
 
860
                search_data.search_bar = FALSE;
 
861
 
 
862
                if (! DOC_IDX_VALID(idx)) return;
640
863
 
641
864
                g_free(search_data.text);
642
865
                search_data.text = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(user_data)))));
648
871
                        return;
649
872
                }
650
873
 
651
 
                gtk_combo_box_prepend_text(GTK_COMBO_BOX(user_data), search_data.text);
652
 
                search_data.flags = (fl1 ? SCFIND_MATCHCASE : 0) |
653
 
                                (fl2 ? SCFIND_WHOLEWORD : 0) |
654
 
                                (fl3 ? SCFIND_REGEXP | SCFIND_POSIX: 0) |
655
 
                                (fl4 ? SCFIND_WORDSTART : 0);
656
 
                document_find_text(idx, search_data.text, search_data.flags, search_data.backwards);
 
874
                ui_combo_box_add_to_history(GTK_COMBO_BOX(user_data), search_data.text);
 
875
 
 
876
                search_data.flags = get_search_flags(widgets.find_dialog);
 
877
 
 
878
                switch (response)
 
879
                {
 
880
                        case GEANY_RESPONSE_FIND:
 
881
                        case GEANY_RESPONSE_FIND_PREVIOUS:
 
882
                                document_find_text(idx, search_data.text, search_data.flags,
 
883
                                        (response == GEANY_RESPONSE_FIND_PREVIOUS), TRUE, GTK_WIDGET(widgets.find_dialog));
 
884
                                check_close = FALSE;
 
885
                                if (prefs.suppress_search_dialogs)
 
886
                                        check_close = TRUE;
 
887
                                break;
 
888
 
 
889
                        case GEANY_RESPONSE_FIND_IN_FILE:
 
890
                                search_find_usage(search_data.text, search_data.flags, FALSE);
 
891
                                break;
 
892
 
 
893
                        case GEANY_RESPONSE_FIND_IN_SESSION:
 
894
                                search_find_usage(search_data.text, search_data.flags, TRUE);
 
895
                                break;
 
896
 
 
897
                        case GEANY_RESPONSE_MARK:
 
898
                                if (DOC_IDX_VALID(idx))
 
899
                                {
 
900
                                        gint count = search_mark(idx, search_data.text, search_data.flags);
 
901
 
 
902
                                        if (count == 0)
 
903
                                                ui_set_statusbar(FALSE, _("No matches found for \"%s\"."), search_data.text);
 
904
                                        else
 
905
                                                ui_set_statusbar(FALSE, _("Found %d matches for \"%s\"."), count,
 
906
                                                        search_data.text);
 
907
                                }
 
908
                                break;
 
909
                }
 
910
                if (check_close)
 
911
                        gtk_widget_hide(widgets.find_dialog);
657
912
        }
658
 
        else gtk_widget_hide(widgets.find_dialog);
659
913
}
660
914
 
661
915
 
662
916
static void
663
 
on_find_entry_activate(GtkEntry *entry, gpointer user_data)
 
917
on_replace_entry_activate(GtkEntry *entry, gpointer user_data)
664
918
{
665
 
        on_find_dialog_response(NULL, GTK_RESPONSE_ACCEPT,
666
 
                                lookup_widget(GTK_WIDGET(entry), "entry"));
 
919
        on_replace_dialog_response(NULL, GEANY_RESPONSE_REPLACE, NULL);
667
920
}
668
921
 
669
922
 
674
927
        GtkWidget *entry_find = lookup_widget(GTK_WIDGET(widgets.replace_dialog), "entry_find");
675
928
        GtkWidget *entry_replace = lookup_widget(GTK_WIDGET(widgets.replace_dialog), "entry_replace");
676
929
        gint search_flags_re;
677
 
        gboolean search_backwards_re, search_replace_escape_re, search_in_all_buffers_re;
678
 
        gboolean fl1, fl2, fl3, fl4;
 
930
        gboolean search_backwards_re, search_replace_escape_re;
679
931
        gboolean close_window;
680
932
        gchar *find, *replace;
681
933
 
682
 
        if (response == GTK_RESPONSE_CANCEL)
 
934
        if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
683
935
        {
684
936
                gtk_widget_hide(widgets.replace_dialog);
685
937
                return;
686
938
        }
687
939
 
688
 
        fl1 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
689
 
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_case")));
690
 
        fl2 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
691
 
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_word")));
692
 
        fl3 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
693
 
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_regexp")));
694
 
        fl4 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
695
 
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_wordstart")));
696
 
        close_window = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
697
 
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_dont_close_window")));
 
940
        close_window = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
941
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_close")));
698
942
        search_backwards_re = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
699
943
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_back")));
700
944
        search_replace_escape_re = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
701
945
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_escape")));
702
 
        search_in_all_buffers_re = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
703
 
                                lookup_widget(GTK_WIDGET(widgets.replace_dialog), "check_all_buffers")));
704
946
        find = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry_find)))));
705
947
        replace = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(entry_replace)))));
706
948
 
707
 
        if ((response != GEANY_RESPONSE_FIND) && (! fl1) && (strcasecmp(find, replace) == 0))
 
949
        search_flags_re = get_search_flags(widgets.replace_dialog);
 
950
 
 
951
        if ((response != GEANY_RESPONSE_FIND) && (search_flags_re & SCFIND_MATCHCASE)
 
952
                && (strcmp(find, replace) == 0))
708
953
        {
709
954
                utils_beep();
710
955
                gtk_widget_grab_focus(GTK_WIDGET(GTK_BIN(lookup_widget(widgets.replace_dialog, "entry_find"))->child));
711
956
                return;
712
957
        }
713
958
 
714
 
        gtk_combo_box_prepend_text(GTK_COMBO_BOX(entry_find), find);
715
 
        gtk_combo_box_prepend_text(GTK_COMBO_BOX(entry_replace), replace);
 
959
        ui_combo_box_add_to_history(GTK_COMBO_BOX(entry_find), find);
 
960
        ui_combo_box_add_to_history(GTK_COMBO_BOX(entry_replace), replace);
716
961
 
717
962
        if (search_replace_escape_re &&
718
963
                (! utils_str_replace_escape(find) || ! utils_str_replace_escape(replace)))
722
967
                return;
723
968
        }
724
969
 
725
 
        search_flags_re = (fl1 ? SCFIND_MATCHCASE : 0) |
726
 
                                          (fl2 ? SCFIND_WHOLEWORD : 0) |
727
 
                                          (fl3 ? SCFIND_REGEXP | SCFIND_POSIX : 0) |
728
 
                                          (fl4 ? SCFIND_WORDSTART : 0);
729
 
 
730
 
        if (search_in_all_buffers_re && response == GEANY_RESPONSE_REPLACE_ALL)
 
970
        switch (response)
731
971
        {
732
 
                gint i;
733
 
                for (i = 0; i < GEANY_MAX_OPEN_FILES; i++)
734
 
                {
735
 
                        if (! doc_list[i].is_valid) continue;
736
 
 
737
 
                        document_replace_all(i, find, replace, search_flags_re, search_replace_escape_re);
738
 
                }
739
 
                if (close_window) gtk_widget_hide(widgets.replace_dialog);
 
972
                case GEANY_RESPONSE_REPLACE_AND_FIND:
 
973
                {
 
974
                        gint rep = document_replace_text(idx, find, replace, search_flags_re,
 
975
                                search_backwards_re);
 
976
                        if (rep != -1)
 
977
                                document_find_text(idx, find, search_flags_re, search_backwards_re,
 
978
                                        TRUE, NULL);
 
979
                        break;
 
980
                }
 
981
                case GEANY_RESPONSE_REPLACE:
 
982
                {
 
983
                        document_replace_text(idx, find, replace, search_flags_re,
 
984
                                search_backwards_re);
 
985
                        break;
 
986
                }
 
987
                case GEANY_RESPONSE_FIND:
 
988
                {
 
989
                        document_find_text(idx, find, search_flags_re, search_backwards_re, TRUE,
 
990
                                                           GTK_WIDGET(dialog));
 
991
                        break;
 
992
                }
 
993
                case GEANY_RESPONSE_REPLACE_IN_FILE:
 
994
                {
 
995
                        if (! document_replace_all(idx, find, replace, search_flags_re,
 
996
                                search_replace_escape_re))
 
997
                        {
 
998
                                utils_beep();
 
999
                        }
 
1000
                        break;
 
1001
                }
 
1002
                case GEANY_RESPONSE_REPLACE_IN_SESSION:
 
1003
                {
 
1004
                        guint n, count = 0;
 
1005
 
 
1006
                        /* replace in all documents following notebook tab order */
 
1007
                        for (n = 0; (gint) n < gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook)); n++)
 
1008
                        {
 
1009
                                gint ix = document_get_n_idx(n);
 
1010
 
 
1011
                                if (! doc_list[ix].is_valid) continue;
 
1012
 
 
1013
                                if (document_replace_all(ix, find, replace, search_flags_re,
 
1014
                                        search_replace_escape_re)) count++;
 
1015
                        }
 
1016
                        if (count == 0)
 
1017
                                utils_beep();
 
1018
 
 
1019
                        ui_set_statusbar(FALSE, _("Replaced text in %u files."), count);
 
1020
                        /* show which docs had replacements: */
 
1021
                        gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_STATUS);
 
1022
 
 
1023
                        ui_save_buttons_toggle(doc_list[idx].changed);  /* update save all */
 
1024
                        break;
 
1025
                }
 
1026
                case GEANY_RESPONSE_REPLACE_IN_SEL:
 
1027
                {
 
1028
                        document_replace_sel(idx, find, replace, search_flags_re, search_replace_escape_re);
 
1029
                        break;
 
1030
                }
740
1031
        }
741
 
        else
 
1032
        switch (response)
742
1033
        {
743
 
                switch (response)
744
 
                {
745
 
                        case GEANY_RESPONSE_REPLACE:
746
 
                        {
747
 
                                document_replace_text(idx, find, replace, search_flags_re,
748
 
                                        search_backwards_re);
749
 
                                break;
750
 
                        }
751
 
                        case GEANY_RESPONSE_FIND:
752
 
                        {
753
 
                                document_find_text(idx, find, search_flags_re, search_backwards_re);
754
 
                                break;
755
 
                        }
756
 
                        case GEANY_RESPONSE_REPLACE_ALL:
757
 
                        {
758
 
                                document_replace_all(idx, find, replace, search_flags_re, search_replace_escape_re);
759
 
                                if (close_window) gtk_widget_hide(widgets.replace_dialog);
760
 
                                break;
761
 
                        }
762
 
                        case GEANY_RESPONSE_REPLACE_SEL:
763
 
                        {
764
 
                                document_replace_sel(idx, find, replace, search_flags_re, search_replace_escape_re);
765
 
                                if (close_window) gtk_widget_hide(widgets.replace_dialog);
766
 
                                break;
767
 
                        }
768
 
                }
 
1034
                case GEANY_RESPONSE_REPLACE_IN_SEL:
 
1035
                case GEANY_RESPONSE_REPLACE_IN_FILE:
 
1036
                case GEANY_RESPONSE_REPLACE_IN_SESSION:
 
1037
                        if (close_window)
 
1038
                                gtk_widget_hide(widgets.replace_dialog);
769
1039
        }
770
1040
        g_free(find);
771
1041
        g_free(replace);
772
1042
}
773
1043
 
774
1044
 
775
 
static void
776
 
on_replace_entry_activate(GtkEntry *entry, gpointer user_data)
777
 
{
778
 
        on_replace_dialog_response(NULL, GEANY_RESPONSE_REPLACE, NULL);
779
 
}
780
 
 
781
 
 
782
1045
static gboolean
783
 
on_combo_entry_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
 
1046
on_widget_key_pressed_set_focus(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
784
1047
{
785
 
        // catch tabulator key to set the focus in the replace entry instead of
786
 
        // setting it to the combo box
 
1048
        /* catch tabulator key to set the focus in the replace entry instead of
 
1049
         * setting it to the combo box */
787
1050
        if (event->keyval == GDK_Tab)
788
1051
        {
789
1052
                gtk_widget_grab_focus(GTK_WIDGET(user_data));
793
1056
}
794
1057
 
795
1058
 
 
1059
static GString *get_grep_options(void)
 
1060
{
 
1061
        gboolean fgrep = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
1062
                                        lookup_widget(widgets.find_in_files_dialog, "radio_fgrep")));
 
1063
        gboolean grep = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
1064
                                        lookup_widget(widgets.find_in_files_dialog, "radio_grep")));
 
1065
        gboolean invert = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
1066
                                        lookup_widget(widgets.find_in_files_dialog, "check_invert")));
 
1067
        gboolean case_sens = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
1068
                                        lookup_widget(widgets.find_in_files_dialog, "check_case")));
 
1069
        gboolean whole_word = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
1070
                                        lookup_widget(widgets.find_in_files_dialog, "check_wholeword")));
 
1071
        gboolean recursive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
1072
                                        lookup_widget(widgets.find_in_files_dialog, "check_recursive")));
 
1073
        gboolean extra = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
 
1074
                                        lookup_widget(widgets.find_in_files_dialog, "check_extra")));
 
1075
        GString *gstr = g_string_new("-nHI");   /* line numbers, filenames, ignore binaries */
 
1076
 
 
1077
        if (invert)
 
1078
                g_string_append_c(gstr, 'v');
 
1079
        if (! case_sens)
 
1080
                g_string_append_c(gstr, 'i');
 
1081
        if (whole_word)
 
1082
                g_string_append_c(gstr, 'w');
 
1083
        if (recursive)
 
1084
                g_string_append_c(gstr, 'r');
 
1085
 
 
1086
        if (fgrep)
 
1087
                g_string_append_c(gstr, 'F');
 
1088
        else if (! grep)
 
1089
                g_string_append_c(gstr, 'E');
 
1090
 
 
1091
        if (extra)
 
1092
        {
 
1093
                gchar *text = g_strdup(gtk_entry_get_text(GTK_ENTRY(
 
1094
                                        lookup_widget(widgets.find_in_files_dialog, "entry_extra"))));
 
1095
 
 
1096
                text = g_strstrip(text);
 
1097
                if (*text != 0)
 
1098
                {
 
1099
                        g_string_append_c(gstr, ' ');
 
1100
                        g_string_append(gstr, text);
 
1101
                }
 
1102
                g_free(text);
 
1103
        }
 
1104
        return gstr;
 
1105
}
 
1106
 
 
1107
 
796
1108
static void
797
1109
on_find_in_files_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
798
1110
{
800
1112
        {
801
1113
                const gchar *search_text =
802
1114
                        gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(user_data))));
 
1115
                GtkWidget *dir_combo =
 
1116
                        lookup_widget(widgets.find_in_files_dialog, "dir_combo");
803
1117
                const gchar *utf8_dir =
804
 
                        gtk_entry_get_text(GTK_ENTRY(lookup_widget(widgets.find_in_files_dialog, "entry_dir")));
805
 
 
806
 
                if (utf8_dir == NULL || utils_strcmp(utf8_dir, ""))
807
 
                        msgwin_status_add(_("Invalid directory for find in files."));
 
1118
                        gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(dir_combo))));
 
1119
 
 
1120
                /* update extra options pref */
 
1121
                g_free(search_prefs.fif_extra_options);
 
1122
                search_prefs.fif_extra_options = g_strdup(gtk_entry_get_text(GTK_ENTRY(
 
1123
                                        lookup_widget(widgets.find_in_files_dialog, "entry_extra"))));
 
1124
 
 
1125
                if (utf8_dir == NULL || utils_str_equal(utf8_dir, ""))
 
1126
                        ui_set_statusbar(FALSE, _("Invalid directory for find in files."));
808
1127
                else if (search_text && *search_text)
809
1128
                {
810
1129
                        gchar *locale_dir;
811
 
                        gboolean invert = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
812
 
                                                        lookup_widget(widgets.find_in_files_dialog, "check_invert")));
813
 
                        gboolean case_sens = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
814
 
                                                        lookup_widget(widgets.find_in_files_dialog, "check_case")));
815
 
                        gboolean whole_word = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
816
 
                                                        lookup_widget(widgets.find_in_files_dialog, "check_wholeword")));
817
 
                        gint opts = (invert ? FIF_INVERT_MATCH : 0) |
818
 
                                                (case_sens ? FIF_CASE_SENSITIVE : 0) |
819
 
                                                (whole_word ? FIF_WHOLE_WORD : 0);
820
 
                        gboolean fgrep = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
821
 
                                                        lookup_widget(widgets.find_in_files_dialog, "radio_fgrep")));
822
 
                        gboolean grep = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
823
 
                                                        lookup_widget(widgets.find_in_files_dialog, "radio_grep")));
824
 
                        gint regex_opt = fgrep ? FIF_FGREP : (grep ? FIF_GREP : FIF_EGREP);
 
1130
                        GString *opts = get_grep_options();
825
1131
 
826
1132
                        locale_dir = utils_get_locale_from_utf8(utf8_dir);
827
1133
 
828
 
                        if (search_find_in_files(search_text, locale_dir, regex_opt, opts))
 
1134
                        if (search_find_in_files(search_text, locale_dir, opts->str))
829
1135
                        {
830
 
                                gtk_combo_box_prepend_text(GTK_COMBO_BOX(user_data), search_text);
 
1136
                                ui_combo_box_add_to_history(GTK_COMBO_BOX(user_data), search_text);
 
1137
                                ui_combo_box_add_to_history(GTK_COMBO_BOX(dir_combo), utf8_dir);
831
1138
                                gtk_widget_hide(widgets.find_in_files_dialog);
832
1139
                        }
833
1140
                        g_free(locale_dir);
 
1141
                        g_string_free(opts, TRUE);
834
1142
                }
835
1143
                else
836
 
                        msgwin_status_add(_("No text to find."));
 
1144
                        ui_set_statusbar(FALSE, _("No text to find."));
837
1145
        }
838
1146
        else
839
1147
                gtk_widget_hide(widgets.find_in_files_dialog);
841
1149
 
842
1150
 
843
1151
static gboolean
844
 
search_find_in_files(const gchar *search_text, const gchar *dir, fif_match_type regex_opt,
845
 
                fif_options opts)
 
1152
search_find_in_files(const gchar *search_text, const gchar *dir, const gchar *opts)
846
1153
{
847
 
        gchar **argv_prefix;
848
 
        gchar **grep_cmd_argv;
849
 
        gchar **argv;
850
 
        gchar grep_opts[] = "-nHI    ";
 
1154
        gchar **argv_prefix, **argv, **opts_argv;
 
1155
        gchar *command_grep;
 
1156
        guint opts_argv_len, i;
851
1157
        GPid child_pid;
852
 
        gint stdout_fd, stdin_fd, grep_cmd_argv_len, i, grep_opts_len = 4;
 
1158
        gint stdout_fd;
853
1159
        GError *error = NULL;
854
1160
        gboolean ret = FALSE;
855
1161
 
856
1162
        if (! search_text || ! *search_text || ! dir) return TRUE;
857
1163
 
858
 
        // first process the grep command (need to split it in a argv because it can be "grep -I")
859
 
        grep_cmd_argv = g_strsplit(app->tools_grep_cmd, " ", -1);
860
 
        grep_cmd_argv_len = g_strv_length(grep_cmd_argv);
861
 
 
862
 
        if (! g_file_test(grep_cmd_argv[0], G_FILE_TEST_IS_EXECUTABLE))
863
 
        {
864
 
                msgwin_status_add(_("Cannot execute grep tool '%s';"
865
 
                        " check the path setting in Preferences."), app->tools_grep_cmd);
 
1164
        command_grep = g_find_program_in_path(prefs.tools_grep_cmd);
 
1165
        if (command_grep == NULL)
 
1166
        {
 
1167
                ui_set_statusbar(TRUE, _("Cannot execute grep tool '%s';"
 
1168
                        " check the path setting in Preferences."), prefs.tools_grep_cmd);
 
1169
                return FALSE;
 
1170
        }
 
1171
 
 
1172
        opts_argv = g_strsplit(opts, " ", -1);
 
1173
        opts_argv_len = g_strv_length(opts_argv);
 
1174
 
 
1175
        /* set grep command and options */
 
1176
        argv_prefix = g_new0(gchar*, 1 + opts_argv_len + 3 + 1);        /* last +1 for recursive arg */
 
1177
 
 
1178
        argv_prefix[0] = command_grep;
 
1179
        for (i = 0; i < opts_argv_len; i++)
 
1180
        {
 
1181
                argv_prefix[i + 1] = g_strdup(opts_argv[i]);
 
1182
        }
 
1183
        g_strfreev(opts_argv);
 
1184
 
 
1185
        i++;    /* correct for prefs.tools_grep_cmd */
 
1186
        argv_prefix[i++] = g_strdup("--");
 
1187
        argv_prefix[i++] = g_strdup(search_text);
 
1188
 
 
1189
        /* finally add the arguments(files to be searched) */
 
1190
        if (strstr(argv_prefix[1], "r"))        /* recursive option set */
 
1191
        {
 
1192
                argv_prefix[i++] = g_strdup(".");
 
1193
                argv_prefix[i++] = NULL;
 
1194
                argv = argv_prefix;
 
1195
        }
 
1196
        else
 
1197
        {
 
1198
                argv_prefix[i++] = NULL;
 
1199
                argv = search_get_argv((const gchar**)argv_prefix, dir);
 
1200
                g_strfreev(argv_prefix);
 
1201
        }
 
1202
 
 
1203
        if (argv == NULL)       /* no files */
 
1204
        {
 
1205
                g_strfreev(argv);
866
1206
                return FALSE;
867
1207
        }
868
1208
 
869
1209
        gtk_list_store_clear(msgwindow.store_msg);
870
1210
        gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_MESSAGE);
871
1211
 
872
 
        switch (regex_opt)
873
 
        {
874
 
                default:
875
 
                case FIF_FGREP: grep_opts[grep_opts_len++] = 'F'; break;
876
 
                case FIF_GREP:  break;
877
 
                case FIF_EGREP: grep_opts[grep_opts_len++] = 'E'; break;
878
 
        }
879
 
 
880
 
        if (! (opts & FIF_CASE_SENSITIVE))
881
 
                grep_opts[grep_opts_len++] = 'i';
882
 
        if (opts & FIF_WHOLE_WORD)
883
 
                grep_opts[grep_opts_len++] = 'w';
884
 
        if (opts & FIF_INVERT_MATCH)
885
 
                grep_opts[grep_opts_len++] = 'v';
886
 
        grep_opts[grep_opts_len] = '\0';
887
 
 
888
 
        // set grep command and options
889
 
        argv_prefix = g_new0(gchar*, grep_cmd_argv_len + 4);
890
 
        for (i = 0; i < grep_cmd_argv_len; i++)
891
 
        {
892
 
                argv_prefix[i] = g_strdup(grep_cmd_argv[i]);
893
 
        }
894
 
        argv_prefix[grep_cmd_argv_len] = g_strdup(grep_opts);
895
 
        argv_prefix[grep_cmd_argv_len + 1] = g_strdup("--");
896
 
        argv_prefix[grep_cmd_argv_len + 2] = g_strdup(search_text);
897
 
        argv_prefix[grep_cmd_argv_len + 3] = NULL;
898
 
        g_strfreev(grep_cmd_argv);
899
 
 
900
 
        // finally add the arguments(files to be searched)
901
 
        argv = search_get_argv((const gchar**)argv_prefix, dir);
902
 
        g_strfreev(argv_prefix);
903
 
 
904
 
        if (argv == NULL) return FALSE;
905
 
 
906
1212
        if (! g_spawn_async_with_pipes(dir, (gchar**)argv, NULL,
907
1213
                G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
908
1214
                NULL, NULL, &child_pid,
909
 
                &stdin_fd, &stdout_fd, NULL, &error))
 
1215
                NULL, &stdout_fd, NULL, &error))
910
1216
        {
911
1217
                geany_debug("%s: g_spawn_async_with_pipes() failed: %s", __func__, error->message);
912
 
                msgwin_status_add(_("Process failed (%s)"), error->message);
 
1218
                ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
913
1219
                g_error_free(error);
914
1220
                ret = FALSE;
915
1221
        }
916
1222
        else
917
1223
        {
 
1224
                gchar *str, *utf8_str;
 
1225
 
918
1226
                g_free(msgwindow.find_in_files_dir);
919
1227
                msgwindow.find_in_files_dir = g_strdup(dir);
920
1228
                utils_set_up_io_channel(stdout_fd, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
921
 
                        search_read_io, NULL);
 
1229
                        TRUE, search_read_io, NULL);
922
1230
                g_child_watch_add(child_pid, search_close_pid, NULL);
 
1231
 
 
1232
                str = g_strdup_printf(_("%s %s -- %s (in directory: %s)"),
 
1233
                        prefs.tools_grep_cmd, opts, search_text, dir);
 
1234
                utf8_str = utils_get_utf8_from_locale(str);
 
1235
                msgwin_msg_add(COLOR_BLUE, -1, -1, utf8_str);
 
1236
                utils_free_pointers(str, utf8_str, NULL);
923
1237
                ret = TRUE;
924
1238
        }
925
1239
        g_strfreev(argv);
935
1249
        guint prefix_len, list_len, i, j;
936
1250
        gchar **argv;
937
1251
        GSList *list, *item;
 
1252
        GError *error = NULL;
938
1253
 
939
1254
        g_return_val_if_fail(dir != NULL, NULL);
940
1255
 
941
1256
        prefix_len = g_strv_length((gchar**)argv_prefix);
942
 
        list = search_get_file_list(dir, &list_len);
 
1257
        list = utils_get_file_list(dir, &list_len, &error);
 
1258
        if (error)
 
1259
        {
 
1260
                ui_set_statusbar(TRUE, _("Could not open directory (%s)"), error->message);
 
1261
                g_error_free(error);
 
1262
                return NULL;
 
1263
        }
943
1264
        if (list == NULL) return NULL;
944
1265
 
945
1266
        argv = g_new(gchar*, prefix_len + list_len + 1);
960
1281
}
961
1282
 
962
1283
 
963
 
/* Gets a sorted list of files in the current directory.
964
 
 * The list and the data in the list should be freed after use.
965
 
 * Returns: The list or NULL if no files found.
966
 
 * *length is set to the number of non-NULL data items in the list. */
967
 
static GSList *search_get_file_list(const gchar *path, guint *length)
968
 
{
969
 
        GError *error = NULL;
970
 
        GSList *list = NULL;
971
 
        guint len = 0;
972
 
        GDir *dir;
973
 
 
974
 
        g_return_val_if_fail(path != NULL, NULL);
975
 
 
976
 
        dir = g_dir_open(path, 0, &error);
977
 
        if (error)
978
 
        {
979
 
                msgwin_status_add(_("Could not open directory (%s)"), error->message);
980
 
                g_error_free(error);
981
 
                *length = 0;
982
 
                return NULL;
983
 
        }
984
 
 
985
 
        while (1)
986
 
        {
987
 
                const gchar *filename = g_dir_read_name(dir);
988
 
                if (filename == NULL) break;
989
 
 
990
 
                list = g_slist_insert_sorted(list, g_strdup(filename), (GCompareFunc) strcmp);
991
 
                len++;
992
 
        }
993
 
        g_dir_close(dir);
994
 
 
995
 
        *length = len;
996
 
        return list;
997
 
}
998
 
 
999
 
 
1000
1284
static gboolean search_read_io              (GIOChannel *source,
1001
1285
                                             GIOCondition condition,
1002
1286
                                             gpointer data)
1007
1291
 
1008
1292
                while (g_io_channel_read_line(source, &msg, NULL, NULL, NULL) && msg)
1009
1293
                {
1010
 
                        msgwin_msg_add(-1, -1, g_strstrip(msg));
 
1294
                        msgwin_msg_add(COLOR_BLACK, -1, -1, g_strstrip(msg));
1011
1295
                        g_free(msg);
1012
1296
                }
1013
1297
        }
1021
1305
static void search_close_pid(GPid child_pid, gint status, gpointer user_data)
1022
1306
{
1023
1307
#ifdef G_OS_UNIX
1024
 
        gchar *msg = _("Search failed.");
 
1308
        const gchar *msg = _("Search failed.");
 
1309
        gint color = COLOR_DARK_RED;
1025
1310
 
1026
1311
        if (WIFEXITED(status))
1027
1312
        {
1028
1313
                switch (WEXITSTATUS(status))
1029
1314
                {
1030
 
                        case 0: msg = _("Search completed."); break;
1031
 
                        case 1: msg = _("No matches found."); break;
1032
 
                        default: break;
 
1315
                        case 0:
 
1316
                        {
 
1317
                                gint count = gtk_tree_model_iter_n_children(
 
1318
                                        GTK_TREE_MODEL(msgwindow.store_msg), NULL) - 1;
 
1319
 
 
1320
                                msgwin_msg_add_fmt(COLOR_BLUE, -1, -1,
 
1321
                                        _("Search completed with %d matches."), count);
 
1322
                                ui_set_statusbar(FALSE, _("Search completed with %d matches."), count);
 
1323
                                break;
 
1324
                        }
 
1325
                        case 1:
 
1326
                                msg = _("No matches found.");
 
1327
                                color = COLOR_BLUE;
 
1328
                        default:
 
1329
                                msgwin_msg_add(color, -1, -1, msg);
 
1330
                                ui_set_statusbar(FALSE, "%s", msg);
 
1331
                                break;
1033
1332
                }
1034
1333
        }
1035
 
 
1036
 
        msgwin_msg_add(-1, -1, msg);
1037
1334
#endif
 
1335
 
1038
1336
        utils_beep();
1039
1337
        g_spawn_close_pid(child_pid);
1040
1338
}
1041
1339
 
1042
1340
 
 
1341
static gint find_document_usage(gint idx, const gchar *search_text, gint flags)
 
1342
{
 
1343
        gchar *buffer, *short_file_name;
 
1344
        struct TextToFind ttf;
 
1345
        gint count = 0;
 
1346
 
 
1347
        g_return_val_if_fail(DOC_IDX_VALID(idx), 0);
 
1348
 
 
1349
        short_file_name = g_path_get_basename(DOC_FILENAME(idx));
 
1350
 
 
1351
        ttf.chrg.cpMin = 0;
 
1352
        ttf.chrg.cpMax = sci_get_length(doc_list[idx].sci);
 
1353
        ttf.lpstrText = (gchar *)search_text;
 
1354
        while (1)
 
1355
        {
 
1356
                gint pos, line, start, find_len;
 
1357
 
 
1358
                pos = sci_find_text(doc_list[idx].sci, flags, &ttf);
 
1359
                if (pos == -1)
 
1360
                        break;  /* no more matches */
 
1361
                find_len = ttf.chrgText.cpMax - ttf.chrgText.cpMin;
 
1362
                if (find_len == 0)
 
1363
                        break;  /* Ignore regex ^ or $ */
 
1364
 
 
1365
                count++;
 
1366
                line = sci_get_line_from_position(doc_list[idx].sci, pos);
 
1367
                buffer = sci_get_line(doc_list[idx].sci, line);
 
1368
                msgwin_msg_add_fmt(COLOR_BLACK, line + 1, idx,
 
1369
                        "%s:%d : %s", short_file_name, line + 1, g_strstrip(buffer));
 
1370
                g_free(buffer);
 
1371
 
 
1372
                start = ttf.chrgText.cpMax + 1;
 
1373
                ttf.chrg.cpMin = start;
 
1374
        }
 
1375
        g_free(short_file_name);
 
1376
        return count;
 
1377
}
 
1378
 
 
1379
 
 
1380
void search_find_usage(const gchar *search_text, gint flags, gboolean in_session)
 
1381
{
 
1382
        gint idx;
 
1383
        gboolean found = FALSE;
 
1384
 
 
1385
        idx = document_get_cur_idx();
 
1386
        g_return_if_fail(DOC_IDX_VALID(idx));
 
1387
 
 
1388
        gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_MESSAGE);
 
1389
        gtk_list_store_clear(msgwindow.store_msg);
 
1390
 
 
1391
        if (! in_session)
 
1392
        {       /* use current document */
 
1393
                found = (find_document_usage(idx, search_text, flags) > 0);
 
1394
        }
 
1395
        else
 
1396
        {
 
1397
                guint i;
 
1398
                for (i = 0; i < doc_array->len; i++)
 
1399
                {
 
1400
                        if (doc_list[i].is_valid)
 
1401
                                if (find_document_usage(i, search_text, flags) > 0) found = TRUE;
 
1402
                }
 
1403
        }
 
1404
 
 
1405
        if (! found) /* no matches were found */
 
1406
        {
 
1407
                ui_set_statusbar(FALSE, _("No matches found for \"%s\"."), search_text);
 
1408
                msgwin_msg_add_fmt(COLOR_BLUE, -1, -1, _("No matches found for \"%s\"."), search_text);
 
1409
        }
 
1410
        else
 
1411
        {
 
1412
                gint count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_msg), NULL);
 
1413
 
 
1414
                ui_set_statusbar(FALSE, _("Found %d matches for \"%s\"."), count, search_text);
 
1415
                msgwin_msg_add_fmt(COLOR_BLUE, -1, -1, _("Found %d matches for \"%s\"."), count,
 
1416
                        search_text);
 
1417
        }
 
1418
}
 
1419
 
 
1420