1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 Copyright (C) 2000 Naba Kumar
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <libanjuta/interfaces/ianjuta-editor.h>
22 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
23 #include <libanjuta/interfaces/ianjuta-document-manager.h>
24 #include <libanjuta/anjuta-shell.h>
25 #include <libanjuta/anjuta-debug.h>
27 #include <libegg/menu/egg-entry-action.h>
30 #include "search-replace.h"
31 #include "search-replace_backend.h"
34 #define UI_FILE PACKAGE_DATA_DIR"/ui/anjuta-search.ui"
35 #define ICON_FILE "anjuta-search.png"
37 /* Find next occurence of expression in Editor
38 Caching of FileBuffer might be useful here to improve performance
39 Returns: TRUE = found, FALSE = not found
42 static gboolean
find_incremental(IAnjutaEditor
* te
, gchar
* expression
,
45 FileBuffer
* fb
= file_buffer_new_from_te (te
);
46 SearchExpression
* se
= g_new0(SearchExpression
, 1);
50 se
->search_str
= expression
;
53 se
->ignore_case
= TRUE
;
54 se
->whole_word
= FALSE
;
55 se
->whole_line
= FALSE
;
56 se
->word_start
= FALSE
;
61 info
= get_next_match(fb
, dir
, se
);
66 backward
= dir
== SD_BACKWARD
?TRUE
:FALSE
;
67 ianjuta_editor_selection_set (IANJUTA_EDITOR_SELECTION (te
),
68 info
->pos
, info
->pos
+ info
->len
, backward
, NULL
);
74 match_info_free(info
);
82 on_find1_activate (GtkAction
* action
, gpointer user_data
)
84 anjuta_search_replace_activate(FALSE
, FALSE
);
88 on_find_and_replace1_activate (GtkAction
* action
, gpointer user_data
)
90 anjuta_search_replace_activate(TRUE
, FALSE
);
94 on_find_in_files1_activate (GtkAction
* action
, gpointer user_data
)
96 anjuta_search_replace_activate(FALSE
, TRUE
);
99 /* *user_data : TRUE=Forward False=Backward */
101 on_findnext1_activate (GtkAction
* action
, gpointer user_data
)
103 search_replace_next();
107 on_findprevious1_activate (GtkAction
* action
, gpointer user_data
)
109 search_replace_previous();
113 on_enterselection (GtkAction
* action
, gpointer user_data
)
115 GtkAction
*entry_action
;
118 IAnjutaDocumentManager
* docman
;
119 SearchPlugin
* plugin
;
120 gchar
*selectionText
= NULL
;
123 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
124 ui
= anjuta_shell_get_ui (ANJUTA_PLUGIN(plugin
)->shell
, NULL
);
125 docman
= anjuta_shell_get_interface(ANJUTA_PLUGIN(plugin
)->shell
,
126 IAnjutaDocumentManager
, NULL
);
127 te
= ianjuta_document_manager_get_current_editor(docman
, NULL
);
130 entry_action
= anjuta_ui_get_action (ui
, "ActionGroupSearch",
131 "ActionEditSearchEntry");
132 g_return_if_fail (EGG_IS_ENTRY_ACTION (entry_action
));
134 selectionText
= ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te
),
136 if (selectionText
!= NULL
&& selectionText
[0] != '\0')
138 egg_entry_action_set_text (EGG_ENTRY_ACTION (entry_action
), selectionText
);
140 /* Which proxy to focus? For now just focus the first one */
141 proxies
= gtk_action_get_proxies (GTK_ACTION (entry_action
));
145 child
= gtk_bin_get_child (GTK_BIN (proxies
->data
));
146 gtk_widget_grab_focus (GTK_WIDGET (child
));
148 g_free (selectionText
);
152 on_prev_occur(GtkAction
* action
, gpointer user_data
)
155 IAnjutaDocumentManager
*docman
;
156 SearchPlugin
*plugin
;
158 gchar
*buffer
= NULL
;
160 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
161 docman
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
162 IAnjutaDocumentManager
, NULL
);
163 te
= ianjuta_document_manager_get_current_editor (docman
, NULL
);
165 if ((buffer
= ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te
), NULL
)))
176 buffer
= ianjuta_editor_get_current_word(te
, NULL
);
180 return_
= find_incremental(te
, buffer
, SD_BACKWARD
);
186 on_next_occur(GtkAction
* action
, gpointer user_data
)
189 IAnjutaDocumentManager
*docman
;
190 SearchPlugin
*plugin
;
192 gchar
*buffer
= NULL
;
194 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
195 docman
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
196 IAnjutaDocumentManager
, NULL
);
197 te
= ianjuta_document_manager_get_current_editor (docman
, NULL
);
199 if ((buffer
= ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te
), NULL
)))
210 buffer
= ianjuta_editor_get_current_word(te
, NULL
);
214 return_
= find_incremental(te
, buffer
, SD_FORWARD
);
219 /* Incremental search */
231 on_incremental_entry_key_press (GtkWidget
*entry
, GdkEventKey
*event
,
232 SearchPlugin
*plugin
)
234 if (event
->keyval
== GDK_Escape
)
238 te
= ianjuta_document_manager_get_current_editor(plugin
->docman
, NULL
);
240 ianjuta_editor_grab_focus (te
, NULL
);
244 static void on_toolbar_find_start_over(GtkAction
* action
, gpointer user_data
);
246 /* FIXME: Wrapping does not yet work */
249 on_toolbar_find_clicked (GtkAction
*action
, gpointer user_data
)
255 IAnjutaDocumentManager
*docman
;
256 SearchPlugin
*plugin
;
257 IncrementalSearch
*search_params
;
258 gboolean search_wrap
= FALSE
;
259 AnjutaStatus
*status
;
262 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
263 docman
= plugin
->docman
;
264 te
= ianjuta_document_manager_get_current_editor(docman
, NULL
);
265 ui
= anjuta_shell_get_ui (ANJUTA_PLUGIN(plugin
)->shell
, NULL
);
270 search_params
= g_object_get_data (G_OBJECT (te
), "incremental_search");
273 search_params
= g_new0 (IncrementalSearch
, 1);
274 g_object_set_data_full (G_OBJECT (te
), "incremental_search",
275 search_params
, (GDestroyNotify
)g_free
);
277 if (EGG_IS_ENTRY_ACTION (action
))
279 string
= egg_entry_action_get_text (EGG_ENTRY_ACTION (action
));
283 GtkAction
*entry_action
;
284 entry_action
= anjuta_ui_get_action (ui
,
286 "ActionEditSearchEntry");
287 g_return_if_fail (EGG_IS_ENTRY_ACTION (entry_action
));
288 string
= egg_entry_action_get_text (EGG_ENTRY_ACTION (entry_action
));
290 if (search_params
->pos
>= 0 && search_params
->wrap
)
292 /* If incremental search wrap requested, so wrap it. */
296 expression
= g_strdup(string
);
297 if (search_params
->end
&&
298 g_str_has_prefix(expression
, search_params
->last
))
304 search_params
->end
= FALSE
;
307 ianjuta_editor_goto_position(te
, 0, NULL
);
308 ret
= find_incremental(te
, expression
, SD_FORWARD
);
309 search_params
->wrap
= FALSE
;
313 ret
= find_incremental(te
, expression
, SD_FORWARD
);
316 status
= anjuta_shell_get_status (ANJUTA_PLUGIN (user_data
)->shell
, NULL
);
320 if (search_params
->pos
< 0)
325 parent
= GTK_WINDOW (ANJUTA_PLUGIN(user_data
)->shell
);
326 dialog
= gtk_message_dialog_new (parent
,
327 GTK_DIALOG_DESTROY_WITH_PARENT
,
328 GTK_MESSAGE_QUESTION
,
330 _("No matches. Wrap search around the document?"));
331 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_YES
)
332 on_toolbar_find_start_over (action
, user_data
);
333 gtk_widget_destroy (dialog
);
337 if (search_wrap
== FALSE
)
339 anjuta_status_push(status
,
340 _("Incremental search for '%s' failed. Press Enter or click Find to continue searching at the top."),
342 search_params
->wrap
= 1;
347 search_params
->end
= TRUE
;
348 anjuta_status_push (status
, _("Incremental search for '%s' (continued at top) failed."),
350 search_params
->wrap
= 0;
355 anjuta_status_clear_stack (status
);
356 g_free(search_params
->last
);
357 search_params
->last
= expression
;
361 on_toolbar_find_start_over (GtkAction
* action
, gpointer user_data
)
364 IAnjutaDocumentManager
*docman
;
365 SearchPlugin
*plugin
;
367 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
368 docman
= plugin
->docman
;
369 te
= ianjuta_document_manager_get_current_editor(docman
, NULL
);
371 /* search from doc start */
372 ianjuta_editor_goto_position(te
, 0, NULL
);
373 on_toolbar_find_clicked (action
, user_data
);
377 on_toolbar_find_incremental_start (GtkAction
*action
, gpointer user_data
)
380 IAnjutaDocumentManager
*docman
;
381 SearchPlugin
*plugin
;
382 IncrementalSearch
*search_params
;
383 GSList
*entries
, *node
;
384 static GHashTable
*entries_connected
= NULL
;
386 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
387 docman
= plugin
->docman
;
388 te
= ianjuta_document_manager_get_current_editor(docman
, NULL
);
390 if (!te
) return FALSE
;
392 /* Make sure we set up escape for getting out the focus to the editor */
393 if (entries_connected
== NULL
)
395 entries_connected
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
397 entries
= gtk_action_get_proxies (action
);
402 entry
= GTK_WIDGET (node
->data
);
403 if (!g_hash_table_lookup (entries_connected
, entry
))
405 g_signal_connect (G_OBJECT (entry
), "key-press-event",
406 G_CALLBACK (on_incremental_entry_key_press
),
408 g_hash_table_insert (entries_connected
, entry
, entry
);
410 node
= g_slist_next (node
);
413 search_params
= g_object_get_data (G_OBJECT (te
), "incremental_search");
416 search_params
= g_new0 (IncrementalSearch
, 1);
417 g_object_set_data_full (G_OBJECT (te
), "incremental_search",
418 search_params
, (GDestroyNotify
)g_free
);
420 /* Prepare to begin incremental search */
421 search_params
->pos
= ianjuta_editor_get_position(te
, NULL
);
422 search_params
->wrap
= FALSE
;
427 on_toolbar_find_incremental_end (GtkAction
*action
, gpointer user_data
)
430 IAnjutaDocumentManager
*docman
;
431 SearchPlugin
*plugin
;
432 IncrementalSearch
*search_params
;
433 AnjutaStatus
*status
;
435 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
436 docman
= plugin
->docman
;
437 te
= ianjuta_document_manager_get_current_editor(docman
, NULL
);
442 status
= anjuta_shell_get_status (ANJUTA_PLUGIN (user_data
)->shell
, NULL
);
443 anjuta_status_clear_stack (status
);
445 search_params
= g_object_get_data (G_OBJECT (te
), "incremental_search");
448 search_params
->pos
= -1;
449 search_params
->wrap
= FALSE
;
455 on_toolbar_find_incremental (GtkAction
*action
, gpointer user_data
)
457 const gchar
*entry_text
;
459 IAnjutaDocumentManager
*docman
;
460 SearchPlugin
*plugin
;
461 IncrementalSearch
*search_params
;
463 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
464 docman
= plugin
->docman
;
465 te
= ianjuta_document_manager_get_current_editor(docman
, NULL
);
470 search_params
= g_object_get_data (G_OBJECT (te
), "incremental_search");
473 search_params
= g_new0 (IncrementalSearch
, 1);
474 g_object_set_data_full (G_OBJECT (te
), "incremental_search",
475 search_params
, (GDestroyNotify
)g_free
);
478 if (search_params
->pos
< 0)
481 if (EGG_IS_ENTRY_ACTION (action
))
483 entry_text
= egg_entry_action_get_text (EGG_ENTRY_ACTION (action
));
488 GtkAction
*entry_action
;
490 ui
= ANJUTA_UI (g_object_get_data (G_OBJECT (user_data
), "ui"));
491 entry_action
= anjuta_ui_get_action (ui
, "ActionGroupSearch",
492 "ActionEditSearchEntry");
493 g_return_if_fail (EGG_IS_ENTRY_ACTION (entry_action
));
495 egg_entry_action_get_text (EGG_ENTRY_ACTION (entry_action
));
497 if (!entry_text
|| strlen(entry_text
) < 1) return;
499 ianjuta_editor_goto_position(te
, search_params
->pos
, NULL
);
500 on_toolbar_find_clicked (NULL
, user_data
);
503 static GtkActionEntry actions_search
[] = {
504 { "ActionMenuEditSearch", NULL
, N_("_Search"), NULL
, NULL
, NULL
},
505 { "ActionEditSearchFind", GTK_STOCK_FIND
, N_("_Find..."), "<control>f",
506 N_("Search for a string or regular expression in the editor"),
507 G_CALLBACK (on_find1_activate
)},
508 { "ActionEditSearchFindNext", GTK_STOCK_FIND
, N_("Find _Next"), "<control>g",
509 N_("Repeat the last Find command"),
510 G_CALLBACK (on_findnext1_activate
)},
511 { "ActionEditSearchFindPrevious", GTK_STOCK_FIND
, N_("Find _Previous"),
513 N_("Repeat the last Find command"),
514 G_CALLBACK (on_findprevious1_activate
)},
515 { "ActionEditSearchReplace", GTK_STOCK_FIND_AND_REPLACE
, N_("Find and R_eplace..."),
517 N_("Search for and replace a string or regular expression with another string"),
518 G_CALLBACK (on_find_and_replace1_activate
)},
519 { "ActionEditAdvancedSearch", GTK_STOCK_FIND
, N_("Advanced Search And Replace"),
520 NULL
, N_("New advance search And replace stuff"),
521 G_CALLBACK (on_find1_activate
)},
522 { "ActionEditSearchSelectionISearch", NULL
, N_("_Enter Selection/I-Search"),
524 N_("Enter the selected text as the search target"),
525 G_CALLBACK (on_enterselection
)},
526 { "ActionEditSearchInFiles", NULL
, N_("Fin_d in Files..."), "<shift><control>f",
527 N_("Search for a string in multiple files or directories"),
528 G_CALLBACK (on_find_in_files1_activate
)},
529 { "ActionEditGotoOccuranceNext", GTK_STOCK_JUMP_TO
,
530 N_("Ne_xt Occurrence"), NULL
,
531 N_("Find the next occurrence of current word"),
532 G_CALLBACK (on_next_occur
)},
533 { "ActionEditGotoOccurancePrev",GTK_STOCK_JUMP_TO
,
534 N_("Pre_vious Occurrence"), NULL
,
535 N_("Find the previous occurrence of current word"),
536 G_CALLBACK (on_prev_occur
)},
539 gpointer parent_class
;
542 activate_plugin (AnjutaPlugin
*plugin
)
545 GtkActionGroup
* group
;
547 SearchPlugin
* splugin
= ANJUTA_PLUGIN_SEARCH (plugin
);
548 IAnjutaDocumentManager
* docman
= anjuta_shell_get_interface(ANJUTA_PLUGIN(plugin
)->shell
,
549 IAnjutaDocumentManager
, NULL
);
552 ui
= anjuta_shell_get_ui (plugin
->shell
, NULL
);
553 anjuta_ui_add_action_group_entries (ui
, "ActionGroupSearch",
556 G_N_ELEMENTS (actions_search
),
557 GETTEXT_PACKAGE
, TRUE
, plugin
);
559 group
= gtk_action_group_new ("ActionGroupSearch");
560 action
= g_object_new (EGG_TYPE_ENTRY_ACTION
,
561 "name", "ActionEditSearchEntry",
562 "label", _("Search"),
563 "tooltip", _("Incremental search"),
564 "stock_id", GTK_STOCK_JUMP_TO
,
567 g_assert (EGG_IS_ENTRY_ACTION (action
));
568 g_signal_connect (action
, "activate",
569 G_CALLBACK (on_toolbar_find_clicked
), plugin
);
570 g_signal_connect (action
, "changed",
571 G_CALLBACK (on_toolbar_find_incremental
), plugin
);
572 g_signal_connect (action
, "focus-in",
573 G_CALLBACK (on_toolbar_find_incremental_start
), plugin
);
574 g_signal_connect (action
, "focus-out",
575 G_CALLBACK (on_toolbar_find_incremental_end
), plugin
);
576 gtk_action_group_add_action (group
, action
);
578 /* FIXME: For some reason, if can_customize is set TRUE, AnjutaUI
579 * can't find this action
581 anjuta_ui_add_action_group(ui
, "ActionGroupSearch", _("Search Toolbar"),
583 g_object_set (G_OBJECT (action
), "sensitive", TRUE
, NULL
);
586 splugin
->uiid
= anjuta_ui_merge (ui
, UI_FILE
);
587 splugin
->docman
= docman
;
588 search_and_replace_init(docman
);
594 deactivate_plugin (AnjutaPlugin
*plugin
)
601 dispose (GObject
*obj
)
603 //SearchPlugin *plugin = ANJUTA_PLUGIN_SEARCH (obj);
605 GNOME_CALL_PARENT (G_OBJECT_CLASS
, dispose
, (obj
));
609 search_plugin_instance_init (GObject
*obj
)
611 //SearchPlugin *plugin = ANJUTA_PLUGIN_SEARCH (obj);
615 search_plugin_class_init (GObjectClass
*klass
)
617 AnjutaPluginClass
*plugin_class
= ANJUTA_PLUGIN_CLASS (klass
);
619 parent_class
= g_type_class_peek_parent (klass
);
621 plugin_class
->activate
= activate_plugin
;
622 plugin_class
->deactivate
= deactivate_plugin
;
623 klass
->dispose
= dispose
;
625 ANJUTA_PLUGIN_BEGIN (SearchPlugin
, search_plugin
);
627 ANJUTA_SIMPLE_PLUGIN (SearchPlugin
, search_plugin
);