2008-04-22 Johannes Schmid <jhs@gnome.org>
[anjuta-git-plugin.git] / plugins / search / search-replace.c
blob0177eb83d646bbbe61775d66a16ce195c3b50ef2
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 /*
4 ** search-replace.c: Generic Search and Replace
5 ** Author: Biswapesh Chattopadhyay
6 */
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <dirent.h>
19 #include <fnmatch.h>
20 #include <fcntl.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
24 #include <gnome.h>
25 #include <glade/glade.h>
26 #include <libgnomevfs/gnome-vfs.h>
28 #include <libanjuta/anjuta-utils.h>
29 #include <libanjuta/anjuta-plugin.h>
30 #include <libanjuta/anjuta-debug.h>
31 #include <libanjuta/interfaces/ianjuta-message-manager.h>
32 #include <libanjuta/interfaces/ianjuta-message-view.h>
33 #include <libanjuta/interfaces/ianjuta-editor.h>
34 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
35 #include <libanjuta/interfaces/ianjuta-markable.h>
36 #include <libanjuta/interfaces/ianjuta-bookmark.h>
37 #include <libanjuta/interfaces/ianjuta-indicable.h>
39 #include "search-replace_backend.h"
40 #include "search-replace.h"
41 #include "search_preferences.h"
43 #include <libanjuta/interfaces/ianjuta-project-manager.h>
44 #include <glib/gi18n.h>
46 #define GLADE_FILE_SEARCH_REPLACE PACKAGE_DATA_DIR"/glade/anjuta-search.glade"
48 /* LibGlade's auto-signal-connect will connect to these signals.
49 * Do not declare them static.
51 gboolean
52 on_search_dialog_key_press_event(GtkWidget *widget, GdkEventKey *event,
53 gpointer user_data);
54 void on_search_match_whole_word_toggled (GtkToggleButton *togglebutton,
55 gpointer user_data);
56 void on_search_match_whole_line_toggled (GtkToggleButton *togglebutton,
57 gpointer user_data);
58 void on_search_match_word_start_toggled (GtkToggleButton *togglebutton,
59 gpointer user_data);
60 gboolean on_search_replace_delete_event(GtkWidget *window, GdkEvent *event,
61 gboolean user_data);
62 void on_replace_regex_toggled (GtkToggleButton *togglebutton, gpointer user_data);
63 void on_search_regex_toggled (GtkToggleButton *togglebutton, gpointer user_data);
64 void on_search_action_changed (GtkComboBox *combo, gpointer user_data);
65 void on_search_target_changed(GtkComboBox *combo, gpointer user_data);
66 void on_search_expression_changed(GtkComboBox *combo, gpointer user_data);
67 void on_actions_no_limit_clicked(GtkButton *button, gpointer user_data);
68 void on_search_button_close_clicked(GtkButton *button, gpointer user_data);
69 void on_search_button_close_clicked(GtkButton *button, gpointer user_data);
70 void on_search_button_help_clicked(GtkButton *button, gpointer user_data);
71 void on_search_button_next_clicked(GtkButton *button, gpointer user_data);
72 void on_search_button_jump_clicked(GtkButton *button, gpointer user_data);
73 void on_search_expression_activate (GtkEditable *edit, gpointer user_data);
74 void on_search_button_save_clicked(GtkButton *button, gpointer user_data);
75 void on_search_button_stop_clicked(GtkButton *button, gpointer user_data);
77 void on_search_direction_changed (GtkComboBox *combo, gpointer user_data);
78 void on_search_full_buffer_toggled (GtkToggleButton *togglebutton,
79 gpointer user_data);
80 void on_search_forward_toggled (GtkToggleButton *togglebutton,
81 gpointer user_data);
82 void on_search_backward_toggled (GtkToggleButton *togglebutton,
83 gpointer user_data);
84 void on_setting_basic_search_toggled (GtkToggleButton *togglebutton,
85 gpointer user_data);
87 /* GUI dropdown option strings */
88 AnjutaUtilStringMap search_direction_strings[] = {
89 /* the order of these matters - it must match the order of the corresponding
90 radio buttons on another page */
91 {SD_BEGINNING, N_("Full Buffer")},
92 {SD_FORWARD, N_("Forward")},
93 {SD_BACKWARD, N_("Backward")},
94 {-1, NULL}
97 AnjutaUtilStringMap search_target_strings[] = {
98 {SR_BUFFER, N_("Current Buffer")},
99 {SR_SELECTION, N_("Current Selection")},
100 {SR_BLOCK, N_("Current Block")},
101 {SR_FUNCTION, N_("Current Function")},
102 {SR_OPEN_BUFFERS, N_("All Open Buffers")},
103 {SR_PROJECT, N_("All Project Files")},
104 /* {SR_VARIABLE, N_("Specify File List")},*/
105 {SR_FILES, N_("Specify File Patterns")},
106 {-1, NULL}
109 AnjutaUtilStringMap search_action_strings[] = {
110 {SA_SELECT, N_("Select next match")},
111 {SA_BOOKMARK, N_("Bookmark all matched lines")},
112 {SA_HIGHLIGHT, N_("Mark all matches")},
113 {SA_FIND_PANE, N_("List matches in find pane")},
114 {SA_REPLACE, N_("Replace next match")},
115 {SA_REPLACEALL, N_("Replace all matches")},
116 {-1, NULL}
120 typedef struct _SearchReplaceGUI
122 GladeXML *xml;
123 GtkWidget *dialog;
124 gboolean showing;
125 } SearchReplaceGUI;
128 static GladeWidget glade_widgets[] = {
129 /* CLOSE_BUTTON */
130 {GE_BUTTON, "button.close", NULL, NULL},
131 /* STOP_BUTTON */
132 {GE_BUTTON, "button.stop", NULL, NULL},
133 /* SEARCH_BUTTON */
134 {GE_BUTTON, "button.next", NULL, NULL},
135 /* JUMP_BUTTON */
136 {GE_BUTTON, "button.jump", NULL, NULL},
137 /* SEARCH_NOTEBOOK */
138 {GE_NONE, "search.notebook", NULL, NULL},
139 /* SEARCH_EXPR_FRAME */
140 {GE_NONE, "frame.search.expression", NULL, NULL},
141 /* SEARCH_TARGET_FRAME */
142 {GE_NONE, "frame.search.target", NULL, NULL},
143 /* SEARCH_VAR_FRAME */
144 {GE_NONE, "frame.search.var", NULL, NULL},
145 /* FILE_FILTER_FRAME */
146 {GE_NONE, "frame.file.filter", NULL, NULL},
147 /* FRAME_SEARCH_BASIC */
148 {GE_NONE, "frame.search.basic", NULL, NULL},
149 /* LABEL_REPLACE */
150 {GE_NONE, "label.replace", NULL, NULL},
151 /* SEARCH_STRING */
152 {GE_COMBO_ENTRY, "search.string.combo", NULL, NULL},
153 /* SEARCH_VAR */
154 {GE_COMBO_ENTRY, "search.var.combo", NULL, NULL},
155 /* MATCH_FILES */
156 {GE_COMBO_ENTRY, "file.filter.match.combo", NULL, NULL},
157 /* UNMATCH_FILES */
158 {GE_COMBO_ENTRY, "file.filter.unmatch.combo", NULL, NULL},
159 /* MATCH_DIRS */
160 {GE_COMBO_ENTRY, "dir.filter.match.combo", NULL, NULL},
161 /* UNMATCH_DIRS */
162 {GE_COMBO_ENTRY, "dir.filter.unmatch.combo", NULL, NULL},
163 /* REPLACE_STRING */
164 {GE_COMBO_ENTRY, "replace.string.combo", NULL, NULL},
165 /* ACTIONS_MAX */
166 {GE_TEXT, "actions.max", NULL, NULL},
167 /* SETTING_PREF_ENTRY */
168 {GE_TEXT, "setting.pref.entry", NULL, NULL},
169 /* SEARCH_REGEX */
170 {GE_BOOLEAN, "search.regex", NULL, NULL},
171 /* GREEDY */
172 {GE_BOOLEAN, "search.greedy", NULL, NULL},
173 /* IGNORE_CASE */
174 {GE_BOOLEAN, "search.ignore.case", NULL, NULL},
175 /* WHOLE_WORD */
176 {GE_BOOLEAN, "search.match.whole.word", NULL, NULL},
177 /* WORD_START */
178 {GE_BOOLEAN, "search.match.word.start", NULL, NULL},
179 /* WHOLE_LINE */
180 {GE_BOOLEAN, "search.match.whole.line", NULL, NULL},
181 /* IGNORE_HIDDEN_FILES */
182 {GE_BOOLEAN, "ignore.hidden.files", NULL, NULL},
183 /* IGNORE_BINARY_FILES */
184 {GE_BOOLEAN, "ignore.binary.files", NULL, NULL},
185 /* IGNORE_HIDDEN_DIRS */
186 {GE_BOOLEAN, "ignore.hidden.dirs", NULL, NULL},
187 /* SEARCH_RECURSIVE */
188 {GE_BOOLEAN, "search.dir.recursive", NULL, NULL},
189 /* REPLACE_REGEX */
190 {GE_BOOLEAN, "replace.regex", NULL, NULL},
191 /* ACTIONS_NO_LIMIT */
192 {GE_BOOLEAN, "actions.no_limit", NULL, NULL},
193 /* SEARCH_FULL_BUFFER */
194 {GE_BOOLEAN, "search.full_buffer", NULL, NULL},
195 /* SEARCH_FORWARD */
196 {GE_BOOLEAN, "search.forward", NULL, NULL},
197 /* SEARCH_BACKWARD */
198 {GE_BOOLEAN, "search.backward", NULL, NULL},
199 /* SEARCH_BASIC */
200 {GE_BOOLEAN, "search.basic", NULL, NULL},
201 /* SEARCH_STRING_COMBO */
202 {GE_COMBO, "search.string.combo", NULL, NULL},
203 /* SEARCH_TARGET_COMBO */
204 {GE_COMBO, "search.target.combo", search_target_strings, NULL},
205 /* SEARCH_ACTION_COMBO */
206 {GE_COMBO, "search.action.combo", search_action_strings, NULL},
207 /* SEARCH_VAR_COMBO */
208 {GE_COMBO, "search.var.combo", NULL, NULL},
209 /* MATCH_FILES_COMBO */
210 {GE_COMBO, "file.filter.match.combo", NULL, NULL},
211 /* UNMATCH_FILES_COMBO */
212 {GE_COMBO, "file.filter.unmatch.combo", NULL, NULL},
213 /* MATCH_DIRS_COMBO */
214 {GE_COMBO, "dir.filter.match.combo", NULL, NULL},
215 /* UNMATCH_DIRS_COMBO */
216 {GE_COMBO, "dir.filter.unmatch.combo", NULL, NULL},
217 /* REPLACE_STRING_COMBO */
218 {GE_COMBO, "replace.string.combo", NULL, NULL},
219 /* SEARCH_DIRECTION_COMBO */
220 {GE_COMBO, "search.direction.combo", search_direction_strings, NULL},
221 /* SETTING_PREF_TREEVIEW */
222 {GE_NONE, "setting.pref.treeview", NULL, NULL},
223 {GE_NONE, NULL, NULL, NULL}
226 /***********************************************************/
228 static void
229 write_message_pane(IAnjutaMessageView* view, FileBuffer *fb, SearchEntry *se, MatchInfo *mi);
230 static gboolean on_message_clicked (GObject* object, gchar* message, gpointer data);
231 static void on_message_view_destroyed (gpointer unused, GObject* where_the_object_was);
232 static void on_message_buffer_flush (IAnjutaMessageView *view, const gchar *one_line, gpointer data);
233 static void save_not_opened_files(FileBuffer *fb);
234 static gboolean replace_in_not_opened_files(FileBuffer *fb, MatchInfo *mi, gchar *repl_str);
235 static void search_set_action(SearchAction action);
236 static void search_set_target(SearchRangeType target);
237 static void search_set_direction(SearchDirection dir);
238 static void populate_value(GladeWidgetId id, gpointer val_ptr);
239 static void reset_flags(void);
240 static void reset_flags_and_search_button (void);
241 static void search_start_over (SearchDirection direction);
242 static void search_end_alert (gchar *string);
243 static void max_results_alert (void);
244 static void nb_results_alert (gint nb);
245 static void search_show_replace(gboolean hide);
246 static void modify_label_image_button(GladeWidgetId button_name, gchar *name, char *stock_image);
247 static void show_jump_button (gboolean show);
248 static gboolean create_dialog(void);
249 static void show_dialog(void);
250 static gboolean word_in_list(GList *list, gchar *word);
251 static GList* list_max_items(GList *list, guint nb_max);
252 static void search_update_combos (void);
253 static void replace_update_combos (void);
254 static void search_direction_changed(SearchDirection dir);
255 static void search_set_direction(SearchDirection dir);
256 static void search_set_toggle_direction(SearchDirection dir);
257 static void search_disconnect_set_toggle_connect(GladeWidgetId id,
258 GCallback function, gboolean active);
259 static void search_replace_next_previous(SearchDirection dir);
260 static void basic_search_toggled(void);
262 static SearchReplaceGUI *sg = NULL;
264 static SearchReplace *sr = NULL;
266 static gboolean flag_select = FALSE;
267 static gboolean interactive = FALSE;
268 static gboolean end_activity = FALSE;
269 static gboolean labels_translated = FALSE;
271 /***********************************************************/
273 void
274 search_and_replace_init (IAnjutaDocumentManager *dm)
276 sr = create_search_replace_instance (dm);
279 void
280 search_and_replace (void)
282 GList *entries;
283 GList *tmp;
284 SearchEntry *se;
285 FileBuffer *fb;
286 static MatchInfo *mi;
287 Search *s;
288 gint offset;
289 gint found_line = 0;
290 static gint os = 0;
291 gint nb_results;
292 static long start_sel = 0;
293 static long end_sel = 0;
294 static gchar *ch = NULL;
295 gchar *regx_pattern;
296 gboolean save_file = FALSE;
297 IAnjutaMessageManager* msgman;
298 IAnjutaMessageView* view = NULL;
299 gboolean backward;
301 g_return_if_fail(sr);
302 s = &(sr->search);
304 if (s->expr.search_str == NULL)
305 return;
307 entries = create_search_entries(s);
308 if (!entries)
309 return;
311 end_activity = FALSE;
312 backward = (s->range.direction == SD_BACKWARD);
314 search_update_combos ();
315 if (s->action == SA_REPLACE || s->action == SA_REPLACEALL)
316 replace_update_combos ();
318 if (SA_FIND_PANE == s->action)
320 gchar* name = g_strconcat(_("Find: "), s->expr.search_str, NULL);
321 AnjutaShell* shell;
322 g_object_get(G_OBJECT(sr->docman), "shell", &shell, NULL);
323 msgman = anjuta_shell_get_interface(shell,
324 IAnjutaMessageManager, NULL);
325 g_return_if_fail(msgman != NULL);
327 view = ianjuta_message_manager_get_view_by_name(msgman, name, NULL);
328 if (view == NULL)
330 view = ianjuta_message_manager_add_view(msgman, name,
331 GTK_STOCK_FIND_AND_REPLACE, NULL);
332 g_return_if_fail(view != NULL);
333 g_signal_connect (G_OBJECT(view), "buffer_flushed",
334 G_CALLBACK (on_message_buffer_flush), NULL);
335 g_signal_connect (G_OBJECT(view), "message_clicked",
336 G_CALLBACK (on_message_clicked), NULL);
337 g_object_weak_ref (G_OBJECT(view), on_message_view_destroyed, NULL);
339 else
340 ianjuta_message_view_clear(view, NULL);
341 ianjuta_message_manager_set_current_view(msgman, view, NULL);
343 gtk_widget_set_sensitive (sr_get_gladewidget(STOP_BUTTON)->widget, TRUE);
344 nb_results = 0;
345 for (tmp = entries; tmp && (nb_results <= s->expr.actions_max);
346 tmp = g_list_next(tmp))
348 if (end_activity)
349 break;
350 while(gtk_events_pending())
351 gtk_main_iteration();
353 /*to eliminate un-needed moves, when not bookmarking, this could be
354 current line i.e. ianjuta_editor_get_lineno (IANJUTA_EDITOR (se->te), NULL);
355 or sometimes last-line ? */
356 found_line = (s->action == SA_BOOKMARK) ? -1 : 1;
358 se = (SearchEntry *) tmp->data;
359 if (flag_select)
361 se->start_pos = start_sel;
362 se->end_pos = end_sel;
364 else
365 end_sel = se->end_pos;
366 if (SE_BUFFER == se->type)
367 fb = file_buffer_new_from_te(se->te);
368 else /* if (SE_FILE == se->type) */
369 fb = file_buffer_new_from_path(se->path, NULL, -1, 0);
371 if (fb)
373 fb->pos = se->start_pos;
374 offset = 0;
375 /* NO - there's no reason for user to expect existing marks to be removed.
376 And that can easily be done manually by user if so desired.
377 if (s->action == SA_BOOKMARK && IANJUTA_IS_MARKABLE (fb->te))
378 ianjuta_markable_delete_all_markers(IANJUTA_MARKABLE(fb->te),
379 IANJUTA_MARKABLE_LINEMARKER, NULL);
381 //FIXME enable clearing of marks by some means other than a 0-match search
382 if (s->action == SA_HIGHLIGHT)
383 ianjuta_indicable_clear (IANJUTA_INDICABLE(fb->te), NULL);
385 while (interactive ||
386 NULL != (mi = get_next_match(fb, s->range.direction, &(s->expr))))
388 if ((s->range.direction == SD_BACKWARD) && (mi->pos < se->end_pos))
389 break;
390 if ((s->range.direction != SD_BACKWARD) && ((se->end_pos != -1) &&
391 (mi->pos+mi->len > se->end_pos)))
392 break;
393 nb_results++;
394 if (nb_results > sr->search.expr.actions_max)
395 break;
397 /* NOTE - mi->line is "editor-style" 1-based, but some things
398 here use/expect 0-base, so adjustments are made as needed */
400 switch (s->action)
402 case SA_HIGHLIGHT:
403 found_line = mi->line;
405 if (fb->te == NULL)
406 fb->te =
407 IANJUTA_EDITOR (ianjuta_document_manager_get_current_document
408 (sr->docman, NULL));
410 if (IANJUTA_INDICABLE (fb->te))
412 IAnjutaIterable *start_pos, *end_pos;
413 /* end-location is correct for sourceview, 1-too-big for scintilla */
414 start_pos = ianjuta_editor_get_position_from_offset (fb->te, mi->pos, NULL);
415 end_pos = ianjuta_editor_get_position_from_offset (fb->te, mi->pos + mi->len, NULL);
416 ianjuta_indicable_set (IANJUTA_INDICABLE(fb->te),
417 start_pos, end_pos,
418 IANJUTA_INDICABLE_IMPORTANT, NULL);
419 g_object_unref (start_pos);
420 g_object_unref (end_pos);
422 break;
424 case SA_BOOKMARK:
425 if (found_line != mi->line)
427 found_line = mi->line;
429 if (fb->te == NULL)
430 fb->te =
431 IANJUTA_EDITOR (ianjuta_document_manager_get_current_document
432 (sr->docman, NULL));
434 if (IANJUTA_IS_MARKABLE (fb->te) &&
435 !ianjuta_markable_is_marker_set (
436 IANJUTA_MARKABLE(fb->te),
437 mi->line,
438 IANJUTA_MARKABLE_BOOKMARK,
439 NULL))
441 ianjuta_bookmark_toggle (IANJUTA_BOOKMARK(fb->te),
442 mi->line, FALSE, NULL);
445 break;
447 case SA_SELECT:
448 if (found_line != mi->line || fb->te == NULL)
450 if (fb->te)
451 ianjuta_editor_goto_line (fb->te, mi->line, NULL);
452 else
453 fb->te = ianjuta_document_manager_goto_uri_line_mark
454 (sr->docman, fb->uri, mi->line, FALSE, NULL);
455 found_line = mi->line;
458 IAnjutaIterable* start = ianjuta_editor_get_position_from_offset (fb->te, mi->pos, NULL);
459 IAnjutaIterable* end = ianjuta_editor_get_position_from_offset (fb->te, mi->pos + mi->len, NULL);
460 ianjuta_editor_selection_set(IANJUTA_EDITOR_SELECTION (fb->te),
461 start,
462 end,
463 NULL);
464 g_object_unref (start);
465 g_object_unref (end);
466 break;
469 case SA_FIND_PANE:
470 write_message_pane(view, fb, se, mi);
471 break;
473 case SA_REPLACE:
474 if (found_line != mi->line || fb->te == NULL)
476 if (fb->te)
477 ianjuta_editor_goto_line (fb->te, mi->line, NULL);
478 else
479 fb->te = ianjuta_document_manager_goto_uri_line_mark
480 (sr->docman, fb->uri, mi->line, FALSE, NULL);
481 found_line = mi->line;
484 if (!interactive)
486 IAnjutaIterable* start = ianjuta_editor_get_position_from_offset (fb->te, mi->pos - offset, NULL);
487 IAnjutaIterable* end = ianjuta_editor_get_position_from_offset (fb->te, mi->pos - offset + mi->len, NULL);
488 ianjuta_editor_selection_set(IANJUTA_EDITOR_SELECTION (fb->te),
489 start,
490 end,
491 NULL);
492 g_object_unref (start);
493 g_object_unref (end);
494 interactive = TRUE;
495 os = offset;
496 modify_label_image_button(SEARCH_BUTTON, _("Replace"),
497 GTK_STOCK_FIND_AND_REPLACE);
498 show_jump_button(TRUE);
499 if (sr->replace.regex && sr->search.expr.regex)
501 g_free (ch);
502 ch = regex_backref (mi, fb);
505 else
507 if (ch && sr->replace.regex && sr->search.expr.regex)
509 g_free (sr->replace.repl_str);
510 sr->replace.repl_str = g_strdup (ch);
511 g_free (ch);
512 ch = NULL;
515 IAnjutaIterable* start = ianjuta_editor_get_position_from_offset (fb->te, mi->pos - os, NULL);
516 IAnjutaIterable* end = ianjuta_editor_get_position_from_offset (fb->te, mi->pos + mi->len - os, NULL);
517 ianjuta_editor_selection_set(IANJUTA_EDITOR_SELECTION (fb->te),
518 start,
519 end,
520 NULL);
521 ianjuta_editor_selection_replace(IANJUTA_EDITOR_SELECTION (fb->te),
522 sr->replace.repl_str,
523 strlen(sr->replace.repl_str),
524 NULL);
525 g_object_unref (start);
526 g_object_unref (end);
528 if (se->direction != SD_BACKWARD)
529 offset += mi->len - (sr->replace.repl_str?strlen(sr->replace.repl_str):0);
531 interactive = FALSE;
533 break;
535 case SA_REPLACEALL:
536 if (sr->replace.regex && sr->search.expr.regex)
538 regx_pattern = sr->replace.repl_str; /* preserve for later matches */
539 sr->replace.repl_str = regex_backref (mi, fb);
541 else
542 regx_pattern = NULL;
543 if (fb->te == NULL) /* NON OPENED FILES */
545 if (replace_in_not_opened_files(fb, mi, sr->replace.repl_str))
546 save_file = TRUE;
548 else
550 IAnjutaIterable* start = ianjuta_editor_get_position_from_offset (fb->te, mi->pos - offset, NULL);
551 IAnjutaIterable* end = ianjuta_editor_get_position_from_offset (fb->te, mi->pos + mi->len - offset, NULL);
552 ianjuta_editor_selection_set(IANJUTA_EDITOR_SELECTION (fb->te),
553 start,
554 end,
555 NULL);
556 ianjuta_editor_selection_replace(IANJUTA_EDITOR_SELECTION (fb->te),
557 sr->replace.repl_str,
558 strlen(sr->replace.repl_str),
559 NULL);
560 g_object_unref (start);
561 g_object_unref (end);
563 if (se->direction != SD_BACKWARD)
564 offset += mi->len - (sr->replace.repl_str?strlen(sr->replace.repl_str):0);
565 if (regx_pattern)
567 g_free (sr->replace.repl_str);
568 sr->replace.repl_str = regx_pattern;
570 break;
572 default:
573 g_warning ("Not implemented - File %s - Line %d\n", __FILE__, __LINE__);
574 break;
575 } // switch
577 if (se->direction != SD_BACKWARD)
578 start_sel = mi->pos + mi->len - offset;
579 else
580 start_sel = mi->pos - offset;
582 if (SA_REPLACE != s->action || !interactive)
583 match_info_free(mi);
585 if (SA_SELECT == s->action ||
586 ((SA_REPLACE == s->action || SA_REPLACEALL == s->action) && interactive))
587 break;
588 } // while
589 /* NO - leave the current position unchanged when marking-all
590 if (nb_results > 0)
592 switch (s->action)
594 case SA_HIGHLIGHT:
595 case SA_BOOKMARK:
596 ianjuta_editor_goto_line (fb->te, found_line, NULL);
597 default:
598 break;
602 if (save_file)
604 save_not_opened_files (fb);
605 save_file = FALSE;
608 file_buffer_free (fb);
609 } // if (fb)
611 g_free (se->path);
612 g_free (se);
614 if (SA_SELECT == s->action && nb_results > 0)
615 break;
618 gtk_widget_set_sensitive (sr_get_gladewidget(STOP_BUTTON)->widget, FALSE);
620 if (s->range.type == SR_BLOCK || s->range.type == SR_FUNCTION ||
621 s->range.type == SR_SELECTION)
622 flag_select = TRUE;
624 if (entries)
625 g_list_free (entries);
627 if (s->action == SA_FIND_PANE)
629 ianjuta_message_view_append (view, IANJUTA_MESSAGE_VIEW_TYPE_INFO,
630 _("Search complete"), "", NULL);
633 if (nb_results == 0)
635 search_end_alert(sr->search.expr.search_str);
637 else if (nb_results > sr->search.expr.actions_max)
638 max_results_alert();
639 else if (s->action == SA_REPLACEALL)
640 nb_results_alert(nb_results);
642 if ((s->range.direction == SD_BEGINNING) &&
643 ((s->action == SA_SELECT) || (s->action == SA_REPLACE)) )
645 search_set_direction(SD_FORWARD);
649 static void
650 write_message_pane(IAnjutaMessageView* view, FileBuffer *fb, SearchEntry *se,
651 MatchInfo *mi)
653 gchar *match_line;
654 char buf[BUFSIZ];
655 gchar *tmp;
657 match_line = file_match_line_from_pos(fb, mi->pos);
659 if (SE_BUFFER == se->type)
661 /* DEBUG_PRINT ("FBPATH %s\n", fb->path); */
662 const gchar* filename = ianjuta_document_get_filename(IANJUTA_DOCUMENT(se->te), NULL);
663 tmp = g_strrstr(fb->path, "/");
664 tmp = g_strndup(fb->path, tmp + 1 -(fb->path));
665 snprintf(buf, BUFSIZ, "%s%s:%d:%s\n", tmp, filename,
666 mi->line, match_line);
667 g_free(tmp);
669 else /* if (SE_FILE == se->type) */
671 snprintf(buf, BUFSIZ, "%s:%d:%s\n", se->path, mi->line + 1, match_line);
673 g_free(match_line);
674 ianjuta_message_view_buffer_append (view, buf, NULL);
677 static void
678 on_message_view_destroyed (gpointer unused, GObject* where_the_object_was)
680 end_activity = TRUE;
683 static void
684 on_message_buffer_flush (IAnjutaMessageView *view, const gchar *one_line,
685 gpointer data)
687 ianjuta_message_view_append (view, IANJUTA_MESSAGE_VIEW_TYPE_NORMAL,
688 one_line, "", NULL);
691 static gboolean
692 on_message_clicked (GObject* object, gchar* message, gpointer data)
694 gchar *ptr, *ptr2;
695 gchar *path, *nline;
696 gchar *uri;
697 gint line;
699 if (!(ptr = g_strstr_len(message, strlen(message), ":")) )
700 return FALSE;
701 path = g_strndup(message, ptr - message);
703 ptr++;
704 if (!(ptr2 = g_strstr_len(ptr, strlen(ptr), ":")) )
705 return FALSE;
706 nline = g_strndup(ptr, ptr2 - ptr);
707 line = atoi(nline);
709 uri = gnome_vfs_get_uri_from_local_path (path);
710 ianjuta_document_manager_goto_uri_line_mark (sr->docman, uri, line, TRUE, NULL);
711 g_free(uri);
712 g_free(path);
713 g_free(nline);
714 return FALSE;
717 static void
718 save_not_opened_files(FileBuffer *fb)
720 FILE *fp;
722 fp = fopen(fb->path, "wb");
723 if (!fp)
724 return;
725 fwrite(fb->buf, fb->len, 1, fp);
726 fclose(fp);
729 static gboolean
730 replace_in_not_opened_files(FileBuffer *fb, MatchInfo *mi, gchar *repl_str)
732 gint l;
733 g_return_val_if_fail (repl_str != NULL, FALSE);
735 if (strlen(repl_str) > mi->len)
737 l = fb->len - mi->pos;
738 fb->len = fb->len + strlen(repl_str) - mi->len;
739 if ( (fb->buf = g_realloc(fb->buf, fb->len)) == NULL )
740 return FALSE;
741 memmove((fb->buf) + mi->pos + strlen(repl_str) - mi->len, fb->buf + mi->pos,l);
743 if (strlen(repl_str) < mi->len)
745 l = fb->len - mi->pos - mi->len ;
746 memmove((fb->buf) + mi->pos + strlen(repl_str), fb->buf + mi->pos + mi->len,l);
747 fb->len = fb->len + strlen(repl_str) - mi->len;
748 if ( (fb->buf = g_realloc(fb->buf, fb->len)) == NULL)
749 return FALSE;
752 for (l=0; l < strlen(repl_str); l++)
753 (fb->buf)[(mi->pos)+l] = repl_str [l];
755 return TRUE;
759 static void
760 search_replace_next_previous(SearchDirection dir)
762 SearchDirection save_direction;
763 SearchAction save_action;
764 SearchRangeType save_type;
766 if (sr)
768 save_action = sr->search.action;
769 save_type = sr->search.range.type;
770 save_direction = sr->search.range.direction;
771 sr->search.range.direction = dir;
772 if (save_type == SR_OPEN_BUFFERS || save_type == SR_PROJECT ||
773 save_type == SR_FILES)
774 sr->search.range.direction = SR_BUFFER;
775 sr->search.action = SA_SELECT;
777 search_and_replace();
779 sr->search.action = save_action;
780 sr->search.range.type = save_type;
781 sr->search.range.direction = save_direction;
783 else
785 DEBUG_PRINT ("sr null\n");
789 void
790 search_replace_next(void)
792 search_replace_next_previous(SD_FORWARD);
795 void
796 search_replace_previous(void)
798 search_replace_next_previous(SD_BACKWARD);
801 /****************************************************************/
803 GladeWidget *
804 sr_get_gladewidget(GladeWidgetId id)
806 return &glade_widgets[id];
809 static void
810 search_set_popdown_strings (GtkComboBoxEntry *combo, GList* strings)
812 GtkListStore *store;
813 gboolean init;
815 init = gtk_combo_box_get_model (GTK_COMBO_BOX(combo)) == NULL;
817 store = gtk_list_store_new (1, G_TYPE_STRING);
818 for (; strings != NULL; strings = g_list_next(strings))
820 GtkTreeIter iter;
822 gtk_list_store_append (store, &iter);
823 gtk_list_store_set (store, &iter, 0, strings->data, -1);
825 gtk_combo_box_set_model (GTK_COMBO_BOX(combo), GTK_TREE_MODEL (store));
826 g_object_unref (store);
828 if (init) gtk_combo_box_entry_set_text_column (combo, 0);
831 static void
832 search_set_popdown_map (GtkComboBox *combo, AnjutaUtilStringMap *map)
834 GtkListStore *store;
835 gboolean init;
836 gint i;
838 init = gtk_combo_box_get_model (combo) == NULL;
840 store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
841 for (i = 0; map[i].type != -1; ++i)
843 GtkTreeIter iter;
845 gtk_list_store_append (store, &iter);
846 gtk_list_store_set (store, &iter, 0, map[i].name, 1, map[i].type, -1);
848 gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store));
849 g_object_unref (store);
850 gtk_combo_box_set_active (combo, 0);
852 if (init)
854 GtkCellRenderer *cell;
856 cell = gtk_cell_renderer_text_new ();
857 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
858 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
859 "text", 0, NULL);
863 static void
864 search_select_item(GtkComboBox* combo, gint item)
866 GtkTreeModel *model;
867 GtkTreeIter iter;
868 gboolean search;
870 model = gtk_combo_box_get_model(combo);
872 /* Find item corresponding to command */
873 for (search = gtk_tree_model_get_iter_first(model, &iter); search;
874 gtk_tree_model_iter_next (model, &iter))
876 gint id;
878 gtk_tree_model_get (model, &iter, 1, &id, -1);
880 if (id == item)
882 /* Find command */
883 gtk_combo_box_set_active_iter(combo, &iter);
884 break;
890 static void
891 search_set_combo(GladeWidgetId id_combo, gint command)
893 GtkComboBox *combo;
895 combo = GTK_COMBO_BOX(sr_get_gladewidget(id_combo)->widget);
896 search_select_item (combo, command);
899 static void
900 search_set_action(SearchAction action)
902 search_set_combo(SEARCH_ACTION_COMBO, action);
905 static void
906 search_set_target(SearchRangeType target)
908 search_set_combo(SEARCH_TARGET_COMBO, target);
911 static void
912 search_set_direction(SearchDirection dir)
914 search_set_combo(SEARCH_DIRECTION_COMBO, dir);
917 static gint
918 search_get_item_combo(GtkComboBox *combo)
920 gint item;
921 GtkTreeModel *model;
922 GtkTreeIter iter;
923 gboolean sel;
925 sel = gtk_combo_box_get_active_iter (combo, &iter);
926 model = gtk_combo_box_get_model (combo);
927 gtk_tree_model_get (model, &iter, 1, &item, -1);
929 return item;
932 static gint
933 search_get_item_combo_name(GladeWidgetId id)
935 GtkWidget *combo = sr_get_gladewidget(id)->widget;
936 return search_get_item_combo(GTK_COMBO_BOX(combo));
939 static void
940 search_direction_changed(SearchDirection dir)
942 SearchEntryType tgt;
943 SearchAction act;
945 tgt = search_get_item_combo_name(SEARCH_TARGET_COMBO);
946 if (dir != SD_BEGINNING)
948 if (tgt == SR_OPEN_BUFFERS || tgt == SR_PROJECT
949 || tgt == SR_FILES)
950 search_set_target(SR_BUFFER);
952 else
954 if (tgt == SR_BUFFER ||tgt == SR_SELECTION || tgt == SR_BLOCK ||
955 tgt == SR_FUNCTION)
956 search_set_target(SR_BUFFER);
957 else
959 act = search_get_item_combo_name(SEARCH_ACTION_COMBO);
960 if (act == SA_SELECT)
961 search_set_action(SA_BOOKMARK);
962 if (act == SA_REPLACE)
963 search_set_action(SA_REPLACEALL);
968 static void
969 populate_value(GladeWidgetId id, gpointer val_ptr)
971 GladeWidget *gw;
973 g_return_if_fail(id && val_ptr);
975 gw = sr_get_gladewidget(id);
976 g_return_if_fail(gw);
977 switch(gw->type)
979 case GE_COMBO_ENTRY:
980 case GE_TEXT:
981 if (*((char **) val_ptr))
982 g_free(* ((char **) val_ptr));
983 *((char **) val_ptr) = gtk_editable_get_chars(
984 GTK_EDITABLE(gw->widget), 0, -1);
985 break;
986 case GE_BOOLEAN:
987 * ((gboolean *) val_ptr) = gtk_toggle_button_get_active(
988 GTK_TOGGLE_BUTTON(gw->widget));
989 break;
990 case GE_COMBO:
991 g_return_if_fail (gw->extra != NULL);
993 *((int *) val_ptr) = search_get_item_combo (GTK_COMBO_BOX(gw->widget));
994 break;
995 default:
996 g_warning("Bad option %d to populate_value", gw->type);
997 break;
1001 static void
1002 reset_flags(void)
1004 flag_select = FALSE;
1005 interactive = FALSE;
1008 static void
1009 reset_flags_and_search_button(void)
1011 reset_flags();
1012 if (sr->search.action != SA_REPLACEALL)
1013 modify_label_image_button(SEARCH_BUTTON, _("Search"), GTK_STOCK_FIND);
1015 else
1016 modify_label_image_button(SEARCH_BUTTON, _("Replace All"),
1017 GTK_STOCK_FIND_AND_REPLACE);
1019 show_jump_button(FALSE);
1022 static void
1023 search_start_over (SearchDirection direction)
1025 IAnjutaDocument* doc = ianjuta_document_manager_get_current_document(sr->docman,
1026 NULL);
1027 IAnjutaEditor *te = NULL;
1028 if (IANJUTA_IS_EDITOR(doc))
1029 te = IANJUTA_EDITOR(doc);
1031 if (te)
1033 if (direction != SD_BACKWARD)
1034 /* search from doc start */
1035 ianjuta_editor_goto_start (te, NULL);
1036 else
1037 /* search from doc end */
1038 ianjuta_editor_goto_end (te, NULL);
1042 static void
1043 search_end_alert(gchar *string)
1045 GtkWidget *dialog;
1047 if (sr->search.range.direction != SD_BEGINNING && !flag_select)
1049 // Ask if user wants to wrap around the doc
1050 // Dialog to be made HIG compliant.
1051 dialog = gtk_message_dialog_new (GTK_WINDOW (sg->dialog),
1052 GTK_DIALOG_DESTROY_WITH_PARENT,
1053 GTK_MESSAGE_QUESTION,
1054 GTK_BUTTONS_YES_NO,
1055 _("The match \"%s\" was not found. Wrap search around the document?"),
1056 string);
1058 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1059 GTK_RESPONSE_YES);
1060 g_signal_connect(G_OBJECT(dialog), "key-press-event",
1061 G_CALLBACK(on_search_dialog_key_press_event), NULL);
1062 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES)
1064 search_start_over (sr->search.range.direction);
1065 gtk_widget_destroy(dialog);
1066 reset_flags();
1067 search_and_replace ();
1068 return;
1071 else
1073 dialog = gtk_message_dialog_new(GTK_WINDOW (sg->dialog),
1074 GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
1075 _("The match \"%s\" was not found."),
1076 string);
1077 g_signal_connect(G_OBJECT(dialog), "key-press-event",
1078 G_CALLBACK(on_search_dialog_key_press_event), NULL);
1079 gtk_dialog_run(GTK_DIALOG(dialog));
1081 gtk_widget_destroy(dialog);
1082 reset_flags();
1085 static void
1086 max_results_alert(void)
1088 GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW (sg->dialog),
1089 GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
1090 _("The maximum number of results has been reached."));
1091 g_signal_connect(G_OBJECT(dialog), "key-press-event",
1092 G_CALLBACK(on_search_dialog_key_press_event), NULL);
1093 gtk_dialog_run(GTK_DIALOG(dialog));
1094 gtk_widget_destroy(dialog);
1095 reset_flags();
1098 static void
1099 nb_results_alert(gint nb)
1101 GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW (sg->dialog),
1102 GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK,
1103 ngettext("%d match has been replaced.",
1104 "%d matches have been replaced.", nb), nb);
1105 g_signal_connect(G_OBJECT(dialog), "key-press-event",
1106 G_CALLBACK(on_search_dialog_key_press_event), NULL);
1107 gtk_dialog_run(GTK_DIALOG(dialog));
1108 gtk_widget_destroy(dialog);
1109 reset_flags();
1112 static void
1113 search_show_replace(gboolean hide)
1115 static GladeWidgetId hide_widgets[] = {
1116 REPLACE_REGEX, REPLACE_STRING_COMBO, LABEL_REPLACE
1118 int i;
1119 GtkWidget *widget;
1121 for (i=0; i < sizeof(hide_widgets)/sizeof(hide_widgets[0]); ++i)
1123 widget = sr_get_gladewidget(hide_widgets[i])->widget;
1124 if (NULL != widget)
1126 if (hide)
1127 gtk_widget_show(widget);
1128 else
1129 gtk_widget_hide(widget);
1134 static void
1135 modify_label_image_button(GladeWidgetId button_id, gchar *name, char *stock_image)
1137 GList *list, *l;
1138 GtkHBox *hbox;
1139 GtkWidget *alignment;
1140 GtkWidget *button = sr_get_gladewidget(button_id)->widget;
1142 list = gtk_container_get_children(GTK_CONTAINER (button));
1143 alignment = GTK_WIDGET(list->data);
1144 g_list_free(list);
1145 list = gtk_container_get_children(GTK_CONTAINER (alignment));
1146 hbox = GTK_HBOX(list->data);
1147 g_list_free(list);
1148 list = gtk_container_get_children(GTK_CONTAINER (hbox));
1149 for (l=list; l; l = g_list_next(l))
1151 if (GTK_IS_LABEL(l->data))
1152 gtk_label_set_text(GTK_LABEL(l->data), name);
1153 if (GTK_IS_IMAGE(l->data))
1154 gtk_image_set_from_stock(GTK_IMAGE(l->data), stock_image,
1155 GTK_ICON_SIZE_BUTTON);
1157 g_list_free(list);
1161 /********************************************************************/
1163 #define POP_LIST(str, var) populate_value(str, &s);\
1164 if (s) \
1166 sr->search.range.files.var = anjuta_util_glist_from_string(s);\
1169 /********************************************************************/
1171 void
1172 search_replace_populate(void)
1174 char *s = NULL;
1175 char *max = NULL;
1177 /* Now, populate the instance with values from the GUI */
1178 populate_value(SEARCH_STRING, &(sr->search.expr.search_str));
1179 populate_value(SEARCH_REGEX, &(sr->search.expr.regex));
1180 populate_value(GREEDY, &(sr->search.expr.greedy));
1181 populate_value(IGNORE_CASE, &(sr->search.expr.ignore_case));
1182 populate_value(WHOLE_WORD, &(sr->search.expr.whole_word));
1183 populate_value(WHOLE_LINE, &(sr->search.expr.whole_line));
1184 populate_value(WORD_START, &(sr->search.expr.word_start));
1185 populate_value(SEARCH_TARGET_COMBO, &(sr->search.range.type));
1186 populate_value(SEARCH_DIRECTION_COMBO, &(sr->search.range.direction));
1187 populate_value(ACTIONS_NO_LIMIT, &(sr->search.expr.no_limit));
1189 populate_value(SEARCH_BASIC, &(sr->search.basic_search));
1191 if (sr->search.expr.no_limit)
1192 sr->search.expr.actions_max = G_MAXINT;
1193 else
1195 populate_value(ACTIONS_MAX, &(max));
1196 sr->search.expr.actions_max = atoi(max);
1197 if (sr->search.expr.actions_max <= 0)
1198 sr->search.expr.actions_max = 200;
1199 g_free(max);
1202 switch (sr->search.range.type)
1204 case SR_FUNCTION:
1205 case SR_BLOCK:
1206 if (flag_select)
1207 sr->search.range.type = SR_SELECTION;
1208 break;
1209 case SR_FILES:
1210 POP_LIST(MATCH_FILES, match_files);
1211 POP_LIST(UNMATCH_FILES, ignore_files);
1212 POP_LIST(MATCH_DIRS, match_dirs);
1213 POP_LIST(UNMATCH_DIRS, ignore_dirs);
1214 populate_value(IGNORE_HIDDEN_FILES, &(sr->search.range.files.ignore_hidden_files));
1215 populate_value(IGNORE_HIDDEN_DIRS, &(sr->search.range.files.ignore_hidden_dirs));
1216 populate_value(SEARCH_RECURSIVE, &(sr->search.range.files.recurse));
1217 break;
1218 default:
1219 break;
1221 populate_value(SEARCH_ACTION_COMBO, &(sr->search.action));
1222 switch (sr->search.action)
1224 case SA_REPLACE:
1225 case SA_REPLACEALL:
1226 populate_value(REPLACE_STRING, &(sr->replace.repl_str));
1227 populate_value(REPLACE_REGEX, &(sr->replace.regex));
1228 break;
1229 default:
1230 break;
1234 static void
1235 show_jump_button (gboolean show)
1237 GtkWidget *jump_button = sr_get_gladewidget(JUMP_BUTTON)->widget;
1238 if (show)
1239 gtk_widget_show(jump_button);
1240 else
1241 gtk_widget_hide(jump_button);
1244 static
1245 void translate_dialog_strings (AnjutaUtilStringMap labels[])
1247 guint i = 0;
1248 while (labels[i].name != NULL)
1250 labels[i].name = gettext (labels[i].name);
1251 i++;
1255 static gboolean
1256 create_dialog(void)
1258 GladeWidget *w;
1259 GtkWidget *widget;
1260 int i;
1262 g_return_val_if_fail(NULL != sr, FALSE);
1263 if (NULL != sg) return TRUE;
1264 sg = g_new0(SearchReplaceGUI, 1);
1266 if (NULL == (sg->xml = glade_xml_new(GLADE_FILE_SEARCH_REPLACE,
1267 SEARCH_REPLACE_DIALOG, NULL)))
1269 anjuta_util_dialog_error(NULL, _("Unable to build user interface for Search And Replace"));
1270 g_free(sg);
1271 sg = NULL;
1272 return FALSE;
1274 sg->dialog = glade_xml_get_widget(sg->xml, SEARCH_REPLACE_DIALOG);
1275 /* gtk_window_set_transient_for (GTK_WINDOW(sg->dialog)
1276 , GTK_WINDOW(app->widgets.window)); */
1278 if (!labels_translated)
1280 labels_translated = TRUE;
1281 translate_dialog_strings (search_direction_strings);
1282 translate_dialog_strings (search_target_strings);
1283 translate_dialog_strings (search_action_strings);
1286 for (i=0; NULL != glade_widgets[i].name; ++i)
1288 w = &(glade_widgets[i]);
1289 w->widget = glade_xml_get_widget(sg->xml, w->name);
1290 if (GE_COMBO_ENTRY == w->type)
1292 /* Get child of GtkComboBoxEntry */
1293 w->widget = GTK_BIN(w->widget)->child;
1295 gtk_widget_ref(w->widget);
1296 if (GE_COMBO == w->type && NULL != w->extra)
1298 search_set_popdown_map(GTK_COMBO_BOX(w->widget), (AnjutaUtilStringMap *)w->extra);
1302 widget = sr_get_gladewidget(SEARCH_STRING_COMBO)->widget;
1303 g_signal_connect (widget, "changed", G_CALLBACK (on_search_expression_changed), NULL);
1304 widget = sr_get_gladewidget(SEARCH_STRING)->widget;
1305 g_signal_connect (widget, "activate", G_CALLBACK (on_search_expression_activate), NULL);
1306 widget = sr_get_gladewidget(REPLACE_STRING)->widget;
1307 g_signal_connect (widget, "activate", G_CALLBACK (on_search_expression_activate), NULL);
1308 widget = sr_get_gladewidget(SEARCH_ACTION_COMBO)->widget;
1309 g_signal_connect (widget, "changed", G_CALLBACK (on_search_action_changed), NULL);
1310 widget = sr_get_gladewidget(SEARCH_DIRECTION_COMBO)->widget;
1311 g_signal_connect (widget, "changed", G_CALLBACK (on_search_direction_changed), NULL);
1312 widget = sr_get_gladewidget(SEARCH_TARGET_COMBO)->widget;
1313 g_signal_connect (widget, "changed", G_CALLBACK (on_search_target_changed), NULL);
1316 search_preferences_initialize_setting_treeview(sg->dialog);
1317 search_preferences_init();
1319 glade_xml_signal_autoconnect(sg->xml);
1320 return TRUE;
1323 static void
1324 show_dialog(void)
1326 gtk_window_present (GTK_WINDOW (sg->dialog));
1327 sg->showing = TRUE;
1330 static gboolean
1331 word_in_list(GList *list, gchar *word)
1333 GList *l = list;
1335 while (l != NULL)
1337 if (strcmp(l->data, word) == 0)
1338 return TRUE;
1339 l = g_list_next(l);
1341 return FALSE;
1344 /* Remove last item of the list if > nb_max */
1346 static GList*
1347 list_max_items(GList *list, guint nb_max)
1349 GList *last;
1351 if (g_list_length(list) > nb_max)
1353 last = g_list_last(list);
1354 g_free(last->data);
1355 list = g_list_delete_link (list, last);
1357 return list;
1360 #define MAX_ITEMS_SEARCH_COMBO 16
1362 static void
1363 search_update_combos(void)
1365 GtkWidget *search_entry = NULL;
1366 gchar *search_word = NULL;
1367 IAnjutaDocument* doc = ianjuta_document_manager_get_current_document(sr->docman,
1368 NULL);
1369 IAnjutaEditor *te = NULL;
1370 if (IANJUTA_IS_EDITOR(doc))
1371 te = IANJUTA_EDITOR(doc);
1373 search_entry = sr_get_gladewidget(SEARCH_STRING)->widget;
1374 if (search_entry && te)
1376 search_word = g_strdup(gtk_entry_get_text(GTK_ENTRY (search_entry)));
1377 if (search_word && strlen(search_word) > 0)
1379 if (!word_in_list(sr->search.expr_history, search_word))
1381 GtkWidget *search_list =
1382 sr_get_gladewidget(SEARCH_STRING_COMBO)->widget;
1383 sr->search.expr_history = g_list_prepend(sr->search.expr_history,
1384 search_word);
1385 sr->search.expr_history = list_max_items(sr->search.expr_history,
1386 MAX_ITEMS_SEARCH_COMBO);
1387 search_set_popdown_strings(GTK_COMBO_BOX_ENTRY (search_list),
1388 sr->search.expr_history);
1390 //search_toolbar_set_text(search_word);
1391 // FIXME comboentry instead of entry
1392 //~ entry_set_text_n_select (app->widgets.toolbar.main_toolbar.find_entry,
1393 //~ search_word, FALSE);
1399 static void
1400 replace_update_combos(void)
1402 GtkWidget *replace_entry = NULL;
1403 gchar *replace_word = NULL;
1404 IAnjutaDocument* doc = ianjuta_document_manager_get_current_document(sr->docman,
1405 NULL);
1406 IAnjutaEditor *te = NULL;
1407 if (IANJUTA_IS_EDITOR(doc))
1408 te = IANJUTA_EDITOR(doc);
1410 replace_entry = sr_get_gladewidget(REPLACE_STRING)->widget;
1411 if (replace_entry && te)
1413 replace_word = g_strdup(gtk_entry_get_text(GTK_ENTRY (replace_entry)));
1414 if (replace_word && strlen(replace_word) > 0)
1416 if (!word_in_list(sr->replace.expr_history, replace_word))
1418 GtkWidget *replace_list =
1419 sr_get_gladewidget(REPLACE_STRING_COMBO)->widget;
1420 sr->replace.expr_history = g_list_prepend(sr->replace.expr_history,
1421 replace_word);
1422 sr->replace.expr_history = list_max_items(sr->replace.expr_history,
1423 MAX_ITEMS_SEARCH_COMBO);
1424 search_set_popdown_strings(GTK_COMBO_BOX_ENTRY (replace_list),
1425 sr->replace.expr_history);
1431 void
1432 search_update_dialog(void)
1434 GtkWidget *widget;
1435 Search *s;
1437 s = &(sr->search);
1438 widget = sr_get_gladewidget(SEARCH_REGEX)->widget;
1439 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), s->expr.regex);
1440 widget = sr_get_gladewidget(GREEDY)->widget;
1441 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), s->expr.greedy);
1442 widget = sr_get_gladewidget(IGNORE_CASE)->widget;
1443 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), s->expr.ignore_case);
1444 widget = sr_get_gladewidget(WHOLE_WORD)->widget;
1445 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), s->expr.whole_word);
1446 widget = sr_get_gladewidget(WHOLE_LINE)->widget;
1447 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), s->expr.whole_line);
1448 widget = sr_get_gladewidget(WORD_START)->widget;
1449 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), s->expr.word_start);
1451 widget = sr_get_gladewidget(ACTIONS_NO_LIMIT)->widget;
1452 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), s->expr.no_limit);
1453 widget = sr_get_gladewidget(ACTIONS_MAX)->widget;
1454 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), s->expr.actions_max);
1456 widget = sr_get_gladewidget(REPLACE_REGEX)->widget;
1457 gtk_widget_set_sensitive(widget, sr->search.expr.regex);
1459 widget = sr_get_gladewidget(SEARCH_BUTTON)->widget;
1460 gtk_widget_set_sensitive (widget, (s->expr.search_str != NULL) && (*s->expr.search_str != '\0'));
1462 widget = sr_get_gladewidget(SEARCH_STRING)->widget;
1463 if (s->expr.search_str)
1464 gtk_entry_set_text(GTK_ENTRY(widget), s->expr.search_str);
1466 widget = sr_get_gladewidget(SEARCH_DIRECTION_COMBO)->widget;
1467 search_select_item (GTK_COMBO_BOX(widget), s->range.direction);
1469 widget = sr_get_gladewidget(SEARCH_ACTION_COMBO)->widget;
1470 search_select_item (GTK_COMBO_BOX(widget), s->action);
1472 search_show_replace(s->action == SA_REPLACE || s->action == SA_REPLACEALL);
1474 widget = sr_get_gladewidget(SEARCH_TARGET_COMBO)->widget;
1475 search_select_item (GTK_COMBO_BOX(widget), s->range.type);
1477 widget = sr_get_gladewidget(SEARCH_BASIC)->widget;
1478 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), s->basic_search);
1480 widget = sr_get_gladewidget(STOP_BUTTON)->widget;
1481 gtk_widget_set_sensitive (widget, FALSE);
1483 basic_search_toggled();
1486 /* -------------- Callbacks --------------------- */
1488 gboolean
1489 on_search_replace_delete_event(GtkWidget *window, GdkEvent *event,
1490 gboolean user_data)
1492 if (sg->showing)
1494 gtk_widget_hide(sg->dialog);
1495 sg->showing = FALSE;
1497 return TRUE;
1500 gboolean
1501 on_search_dialog_key_press_event(GtkWidget *widget, GdkEventKey *event,
1502 gpointer user_data)
1504 if (event->keyval == GDK_Escape)
1506 if (user_data)
1508 /* Escape pressed in Find window */
1509 gtk_widget_hide(widget);
1510 sg->showing = FALSE;
1512 else
1514 /* Escape pressed in wrap yes/no window */
1515 gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_NO);
1517 return TRUE;
1519 else
1521 if ( (event->state & GDK_CONTROL_MASK) &&
1522 ((event->keyval & 0x5F) == GDK_G))
1524 if (event->state & GDK_SHIFT_MASK)
1525 search_replace_previous();
1526 else
1527 search_replace_next();
1529 return FALSE;
1533 static void
1534 search_disconnect_set_toggle_connect(GladeWidgetId id, GCallback function,
1535 gboolean active)
1537 GtkWidget *button;
1539 button = sr_get_gladewidget(id)->widget;
1540 g_signal_handlers_disconnect_by_func(G_OBJECT(button), function, NULL);
1541 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), active);
1542 g_signal_connect(G_OBJECT(button), "toggled", function, NULL);
1546 void
1547 on_search_match_whole_word_toggled (GtkToggleButton *togglebutton,
1548 gpointer user_data)
1550 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)))
1552 search_disconnect_set_toggle_connect(WHOLE_LINE, (GCallback)
1553 on_search_match_whole_line_toggled, FALSE);
1554 search_disconnect_set_toggle_connect(WORD_START, (GCallback)
1555 on_search_match_word_start_toggled, FALSE);
1559 void
1560 on_search_match_whole_line_toggled (GtkToggleButton *togglebutton,
1561 gpointer user_data)
1563 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)))
1565 search_disconnect_set_toggle_connect(WHOLE_WORD, (GCallback)
1566 on_search_match_whole_word_toggled, FALSE);
1567 search_disconnect_set_toggle_connect(WORD_START, (GCallback)
1568 on_search_match_word_start_toggled, FALSE);
1572 void
1573 on_search_match_word_start_toggled (GtkToggleButton *togglebutton,
1574 gpointer user_data)
1576 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)))
1578 search_disconnect_set_toggle_connect(WHOLE_WORD, (GCallback)
1579 on_search_match_whole_word_toggled, FALSE);
1580 search_disconnect_set_toggle_connect(WHOLE_LINE, (GCallback)
1581 on_search_match_whole_line_toggled, FALSE);
1586 static void
1587 search_make_sensitive(gboolean sensitive)
1589 static char *widgets[] = {
1590 SEARCH_EXPR_FRAME, SEARCH_TARGET_FRAME, CLOSE_BUTTON, SEARCH_BUTTON,
1591 JUMP_BUTTON
1593 gint i;
1594 GtkWidget *widget;
1596 for (i=0; i < sizeof(widgets)/sizeof(widgets[0]); ++i)
1598 widget = sr_get_gladewidget(widgets[i])->widget;
1599 if (NULL != widget)
1600 gtk_widget_set_sensitive(widget, sensitive);
1605 void
1606 on_search_regex_toggled (GtkToggleButton *togglebutton, gpointer user_data)
1608 static GladeWidgetId dependent_widgets[] = {
1609 WHOLE_WORD, WHOLE_LINE, WORD_START,
1610 SEARCH_BACKWARD, SEARCH_FULL_BUFFER
1612 int i;
1613 GtkWidget *dircombo = sr_get_gladewidget(SEARCH_DIRECTION_COMBO)->widget;
1614 GtkWidget *repl_regex = sr_get_gladewidget(REPLACE_REGEX)->widget;
1615 GtkWidget *widget;
1616 gboolean state = gtk_toggle_button_get_active(togglebutton);
1618 if (state)
1620 search_set_direction(SD_FORWARD);
1623 gtk_widget_set_sensitive(dircombo, !state);
1624 gtk_widget_set_sensitive(repl_regex, state);
1626 for (i=0; i < sizeof(dependent_widgets)/sizeof(dependent_widgets[0]); ++i)
1628 widget = sr_get_gladewidget(dependent_widgets[i])->widget;
1629 if (NULL != widget)
1631 gtk_widget_set_sensitive(widget, !state);
1632 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget), FALSE);
1637 static void
1638 search_set_toggle_direction(SearchDirection dir)
1640 switch (dir)
1642 case SD_FORWARD :
1643 search_disconnect_set_toggle_connect(SEARCH_FORWARD, (GCallback)
1644 on_search_forward_toggled, TRUE);
1645 break;
1646 case SD_BACKWARD :
1647 search_disconnect_set_toggle_connect(SEARCH_BACKWARD, (GCallback)
1648 on_search_backward_toggled, TRUE);
1649 break;
1650 case SD_BEGINNING :
1651 search_disconnect_set_toggle_connect(SEARCH_FULL_BUFFER, (GCallback)
1652 on_search_full_buffer_toggled, TRUE);
1653 break;
1657 void
1658 on_search_direction_changed (GtkComboBox *combo, gpointer user_data)
1660 SearchDirection dir;
1662 dir = search_get_item_combo(combo);
1663 search_set_toggle_direction(dir);
1664 search_direction_changed(dir);
1667 void
1668 on_search_action_changed (GtkComboBox *combo, gpointer user_data)
1670 SearchAction act;
1671 SearchRangeType rt;
1673 reset_flags();
1674 act = search_get_item_combo(combo);
1675 rt = search_get_item_combo_name(SEARCH_TARGET_COMBO);
1676 show_jump_button (FALSE);
1677 switch(act)
1679 case SA_SELECT:
1680 search_show_replace(FALSE);
1681 modify_label_image_button(SEARCH_BUTTON, _("Search"), GTK_STOCK_FIND);
1682 if (rt == SR_OPEN_BUFFERS || rt == SR_PROJECT ||
1683 rt == SR_FILES)
1684 search_set_target(SR_BUFFER);
1685 break;
1686 case SA_REPLACE:
1687 search_show_replace(TRUE);
1688 modify_label_image_button(SEARCH_BUTTON, _("Search"), GTK_STOCK_FIND);
1689 if (rt == SR_OPEN_BUFFERS || rt == SR_PROJECT ||
1690 rt == SR_FILES)
1691 search_set_target(SR_BUFFER);
1692 break;
1693 case SA_REPLACEALL:
1694 search_show_replace(TRUE);
1695 modify_label_image_button(SEARCH_BUTTON, _("Replace All"),
1696 GTK_STOCK_FIND_AND_REPLACE);
1697 break;
1698 default:
1699 search_show_replace(FALSE);
1700 modify_label_image_button(SEARCH_BUTTON, _("Search"), GTK_STOCK_FIND);
1701 break;
1705 void
1706 on_search_target_changed(GtkComboBox *combo, gpointer user_data)
1708 SearchRangeType tgt;
1709 SearchDirection dir;
1710 SearchAction act;
1711 GtkWidget *search_var_frame = sr_get_gladewidget(SEARCH_VAR_FRAME)->widget;
1712 GtkWidget *file_filter_frame = sr_get_gladewidget(FILE_FILTER_FRAME)->widget;
1714 tgt = search_get_item_combo(combo);
1715 switch(tgt)
1717 case SR_FILES:
1718 gtk_widget_hide(search_var_frame);
1719 gtk_widget_show(file_filter_frame);
1720 break;
1721 default:
1722 gtk_widget_hide(search_var_frame);
1723 gtk_widget_hide(file_filter_frame);
1724 break;
1727 dir = search_get_item_combo_name(SEARCH_DIRECTION_COMBO);
1729 if (tgt == SR_SELECTION || tgt == SR_BLOCK || tgt == SR_FUNCTION)
1732 if (dir == SD_BEGINNING)
1734 search_set_direction(SD_FORWARD);
1737 if (tgt == SR_OPEN_BUFFERS || tgt == SR_PROJECT ||
1738 tgt == SR_FILES)
1740 search_set_direction(SD_BEGINNING);
1742 act = search_get_item_combo_name(SEARCH_ACTION_COMBO);
1743 if (act != SA_REPLACE && act != SA_REPLACEALL)
1745 if (tgt == SR_OPEN_BUFFERS)
1746 search_set_action(SA_BOOKMARK);
1747 else
1748 search_set_action(SA_FIND_PANE);
1750 else
1752 search_set_action(SA_REPLACEALL);
1753 sr->search.action = SA_REPLACEALL;
1756 reset_flags_and_search_button();
1757 /* Resize dialog */
1758 gtk_window_resize(GTK_WINDOW(sg->dialog), 10, 10);
1761 void
1762 on_search_expression_changed(GtkComboBox *combo, gpointer user_data)
1764 GtkWidget *search_entry = sr_get_gladewidget(SEARCH_STRING)->widget;
1765 GtkWidget *widget = sr_get_gladewidget(SEARCH_BUTTON)->widget;
1766 gboolean sensitive;
1768 sensitive = *gtk_entry_get_text (GTK_ENTRY (search_entry)) == '\0' ? FALSE: TRUE;
1769 gtk_widget_set_sensitive (widget, sensitive);
1773 void
1774 on_actions_no_limit_clicked(GtkButton *button, gpointer user_data)
1776 GtkWidget *actions_max = sr_get_gladewidget(ACTIONS_MAX)->widget;
1778 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
1779 gtk_widget_set_sensitive (actions_max, FALSE);
1780 else
1781 gtk_widget_set_sensitive (actions_max, TRUE);
1784 void
1785 on_search_button_close_clicked(GtkButton *button, gpointer user_data)
1787 if (sg->showing)
1789 gtk_widget_hide(sg->dialog);
1790 sg->showing = FALSE;
1794 void
1795 on_search_button_stop_clicked(GtkButton *button, gpointer user_data)
1797 end_activity = TRUE;
1800 void
1801 on_search_button_next_clicked(GtkButton *button, gpointer user_data)
1803 if (sr->search.expr.regex_info)
1805 g_regex_unref (sr->search.expr.regex_info);
1806 sr->search.expr.regex_info = NULL;
1808 search_replace_populate();
1810 search_and_replace();
1813 void search_replace_find_usage(const gchar *symbol)
1815 gchar *project_root_uri = NULL;
1816 SearchReplace *old_sr = sr;
1817 AnjutaShell* shell;
1819 sr = g_new (SearchReplace, 1);
1821 sr->search.expr.search_str = g_strdup (symbol);
1822 sr->search.expr.regex = FALSE;
1823 sr->search.expr.greedy = FALSE;
1824 sr->search.expr.ignore_case = FALSE;
1825 sr->search.expr.whole_word = TRUE;
1826 sr->search.expr.whole_line = FALSE;
1827 sr->search.expr.word_start = FALSE;
1828 sr->search.expr.no_limit = TRUE;
1829 sr->search.expr.actions_max = G_MAXINT;
1830 sr->search.expr.regex_info = NULL;
1832 g_object_get(G_OBJECT(sr->docman), "shell", &shell, NULL);
1834 anjuta_shell_get (shell,
1835 "project_root_uri", G_TYPE_STRING,
1836 &project_root_uri, NULL);
1838 sr->search.range.type =
1839 project_root_uri != NULL ? SR_PROJECT : SR_OPEN_BUFFERS;
1840 g_free (project_root_uri);
1842 sr->search.range.direction = SD_BEGINNING;
1844 sr->search.range.var = NULL;
1846 sr->search.range.files.top_dir = NULL;
1847 sr->search.range.files.match_files = NULL;
1848 sr->search.range.files.match_dirs = NULL;
1849 sr->search.range.files.ignore_files = NULL;
1850 sr->search.range.files.ignore_dirs = NULL;
1851 sr->search.range.files.ignore_hidden_files = TRUE;
1852 sr->search.range.files.ignore_hidden_dirs = TRUE;
1853 sr->search.range.files.recurse = TRUE;
1855 sr->search.action = SA_FIND_PANE;
1857 sr->search.expr_history = NULL;
1858 sr->search.incremental_pos = 0;
1859 sr->search.incremental_wrap = TRUE;
1861 create_dialog ();
1863 search_and_replace();
1864 g_free (sr);
1865 sr = old_sr;
1868 void
1869 on_search_button_jump_clicked(GtkButton *button, gpointer user_data)
1871 if (sr)
1872 interactive = FALSE;
1873 gtk_widget_hide(GTK_WIDGET(button));
1875 search_replace_populate();
1876 search_and_replace();
1879 void
1880 on_search_expression_activate (GtkEditable *edit, gpointer user_data)
1882 GtkWidget *combo;
1884 search_replace_populate();
1886 search_and_replace();
1887 combo = GTK_WIDGET(edit)->parent;
1888 reset_flags_and_search_button();
1892 void
1893 on_search_full_buffer_toggled (GtkToggleButton *togglebutton,
1894 gpointer user_data)
1896 if (gtk_toggle_button_get_active(togglebutton))
1898 search_set_direction(SD_BEGINNING);
1902 void
1903 on_search_forward_toggled (GtkToggleButton *togglebutton,
1904 gpointer user_data)
1906 if (gtk_toggle_button_get_active(togglebutton))
1908 search_set_direction(SD_FORWARD);
1912 void
1913 on_search_backward_toggled (GtkToggleButton *togglebutton,
1914 gpointer user_data)
1916 if (gtk_toggle_button_get_active(togglebutton))
1918 search_set_direction(SD_BACKWARD);
1922 void
1923 on_setting_basic_search_toggled (GtkToggleButton *togglebutton,
1924 gpointer user_data)
1926 SearchAction act;
1927 GtkWidget *frame_basic = sr_get_gladewidget(FRAME_SEARCH_BASIC)->widget;
1929 if (gtk_toggle_button_get_active(togglebutton))
1931 gtk_widget_show(frame_basic);
1932 search_set_target(SR_BUFFER);
1933 search_set_direction(SD_FORWARD);
1935 act = search_get_item_combo_name(SEARCH_ACTION_COMBO);
1936 if (act == SA_REPLACE || act == SA_REPLACEALL)
1937 search_set_action(SA_REPLACE);
1938 else
1939 search_set_action(SA_SELECT);
1941 else
1942 gtk_widget_hide(frame_basic);
1946 static void
1947 basic_search_toggled(void)
1949 GtkToggleButton *togglebutton;
1951 togglebutton = GTK_TOGGLE_BUTTON(sr_get_gladewidget(SEARCH_BASIC)->widget);
1953 on_setting_basic_search_toggled (togglebutton, NULL);
1956 /***********************************************************************/
1958 #define MAX_LENGTH_SEARCH 64
1960 void
1961 anjuta_search_replace_activate (gboolean replace, gboolean project)
1963 GtkWidget *notebook;
1964 GtkWidget *search_entry;
1965 IAnjutaDocument *doc;
1966 IAnjutaEditor *te;
1968 create_dialog ();
1970 search_update_dialog();
1972 search_replace_populate();
1974 reset_flags_and_search_button();
1976 search_entry = sr_get_gladewidget(SEARCH_STRING)->widget;
1977 doc = ianjuta_document_manager_get_current_document(sr->docman, NULL);
1978 te = (IANJUTA_IS_EDITOR (doc)) ? IANJUTA_EDITOR (doc) : NULL;
1979 if (te && search_entry && sr->search.range.type != SR_SELECTION)
1981 /* Set properties */
1982 gchar *current_word;
1984 current_word = ianjuta_editor_selection_get
1985 (IANJUTA_EDITOR_SELECTION (te), NULL);
1986 if (current_word == NULL)
1987 current_word = ianjuta_editor_get_current_word (te, NULL);
1989 if (current_word && strlen(current_word) > 0 )
1991 if (strlen(current_word) > MAX_LENGTH_SEARCH)
1992 current_word[MAX_LENGTH_SEARCH] = '\0';
1993 gtk_entry_set_text(GTK_ENTRY (search_entry), current_word);
1994 g_free(current_word);
1998 if (replace)
2000 if ( !(sr->search.action == SA_REPLACE ||
2001 sr->search.action == SA_REPLACEALL))
2003 search_set_action(SA_REPLACE);
2004 sr->search.action = SA_REPLACE;
2005 search_show_replace(TRUE);
2008 else
2010 if (sr->search.action == SA_REPLACE || sr->search.action == SA_REPLACEALL)
2012 search_set_action(SA_SELECT);
2013 sr->search.action = SA_SELECT;
2014 search_show_replace(FALSE);
2017 if (sr->search.action != SA_REPLACEALL)
2018 modify_label_image_button(SEARCH_BUTTON, _("Search"), GTK_STOCK_FIND);
2020 if (project)
2022 search_set_target(SR_PROJECT);
2023 if (!replace)
2025 search_set_action (SA_FIND_PANE);
2026 search_set_direction (SD_BEGINNING);
2029 show_jump_button(FALSE);
2031 notebook = sr_get_gladewidget(SEARCH_NOTEBOOK)->widget;
2032 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
2034 /* Show the dialog */
2035 if (search_entry)
2036 gtk_widget_grab_focus (search_entry);
2037 show_dialog();