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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 IAnjutaDocument
* doc
;
120 SearchPlugin
* plugin
;
121 gchar
*selectionText
= NULL
;
124 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
125 ui
= anjuta_shell_get_ui (ANJUTA_PLUGIN(plugin
)->shell
, NULL
);
126 docman
= anjuta_shell_get_interface(ANJUTA_PLUGIN(plugin
)->shell
,
127 IAnjutaDocumentManager
, NULL
);
128 doc
= ianjuta_document_manager_get_current_document(docman
, NULL
);
129 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
132 entry_action
= anjuta_ui_get_action (ui
, "ActionGroupSearch",
133 "ActionEditSearchEntry");
134 g_return_if_fail (EGG_IS_ENTRY_ACTION (entry_action
));
136 selectionText
= ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te
),
138 if (selectionText
!= NULL
&& selectionText
[0] != '\0')
140 egg_entry_action_set_text (EGG_ENTRY_ACTION (entry_action
), selectionText
);
142 /* Which proxy to focus? For now just focus the first one */
143 proxies
= gtk_action_get_proxies (GTK_ACTION (entry_action
));
147 child
= gtk_bin_get_child (GTK_BIN (proxies
->data
));
148 gtk_widget_grab_focus (GTK_WIDGET (child
));
150 g_free (selectionText
);
154 on_prev_occur(GtkAction
* action
, gpointer user_data
)
157 IAnjutaDocumentManager
*docman
;
158 IAnjutaDocument
* doc
;
159 SearchPlugin
*plugin
;
161 gchar
*buffer
= NULL
;
163 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
164 docman
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
165 IAnjutaDocumentManager
, NULL
);
166 doc
= ianjuta_document_manager_get_current_document(docman
, NULL
);
167 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
169 if ((buffer
= ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te
), NULL
)))
180 buffer
= ianjuta_editor_get_current_word(te
, NULL
);
184 return_
= find_incremental(te
, buffer
, SD_BACKWARD
);
190 on_next_occur(GtkAction
* action
, gpointer user_data
)
193 IAnjutaDocumentManager
*docman
;
194 IAnjutaDocument
* doc
;
195 SearchPlugin
*plugin
;
197 gchar
*buffer
= NULL
;
199 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
200 docman
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
201 IAnjutaDocumentManager
, NULL
);
202 doc
= ianjuta_document_manager_get_current_document(docman
, NULL
);
203 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
205 if ((buffer
= ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te
), NULL
)))
216 buffer
= ianjuta_editor_get_current_word(te
, NULL
);
220 return_
= find_incremental(te
, buffer
, SD_FORWARD
);
225 /* Incremental search */
237 on_incremental_entry_key_press (GtkWidget
*entry
, GdkEventKey
*event
,
238 SearchPlugin
*plugin
)
240 if (event
->keyval
== GDK_Escape
)
243 IAnjutaDocument
*doc
;
245 doc
= ianjuta_document_manager_get_current_document(plugin
->docman
, NULL
);
246 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
248 ianjuta_document_grab_focus (IANJUTA_DOCUMENT(te
), NULL
);
252 static void on_toolbar_find_start_over(GtkAction
* action
, gpointer user_data
);
254 /* FIXME: Wrapping does not yet work */
257 on_toolbar_find_clicked (GtkAction
*action
, gpointer user_data
)
263 IAnjutaDocument
*doc
;
264 IAnjutaDocumentManager
*docman
;
265 SearchPlugin
*plugin
;
266 IncrementalSearch
*search_params
;
267 gboolean search_wrap
= FALSE
;
268 AnjutaStatus
*status
;
271 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
272 docman
= plugin
->docman
;
273 doc
= ianjuta_document_manager_get_current_document(docman
, NULL
);
274 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
275 ui
= anjuta_shell_get_ui (ANJUTA_PLUGIN(plugin
)->shell
, NULL
);
280 search_params
= g_object_get_data (G_OBJECT (te
), "incremental_search");
283 search_params
= g_new0 (IncrementalSearch
, 1);
284 g_object_set_data_full (G_OBJECT (te
), "incremental_search",
285 search_params
, (GDestroyNotify
)g_free
);
287 if (EGG_IS_ENTRY_ACTION (action
))
289 string
= egg_entry_action_get_text (EGG_ENTRY_ACTION (action
));
293 GtkAction
*entry_action
;
294 entry_action
= anjuta_ui_get_action (ui
,
296 "ActionEditSearchEntry");
297 g_return_if_fail (EGG_IS_ENTRY_ACTION (entry_action
));
298 string
= egg_entry_action_get_text (EGG_ENTRY_ACTION (entry_action
));
300 if (search_params
->pos
>= 0 && search_params
->wrap
)
302 /* If incremental search wrap requested, so wrap it. */
306 expression
= g_strdup(string
);
307 if (search_params
->end
&&
308 g_str_has_prefix(expression
, search_params
->last
))
314 search_params
->end
= FALSE
;
317 ianjuta_editor_goto_position(te
, 0, NULL
);
318 ret
= find_incremental(te
, expression
, SD_FORWARD
);
319 search_params
->wrap
= FALSE
;
323 ret
= find_incremental(te
, expression
, SD_FORWARD
);
326 status
= anjuta_shell_get_status (ANJUTA_PLUGIN (user_data
)->shell
, NULL
);
330 if (search_params
->pos
< 0)
335 parent
= GTK_WINDOW (ANJUTA_PLUGIN(user_data
)->shell
);
336 dialog
= gtk_message_dialog_new (parent
,
337 GTK_DIALOG_DESTROY_WITH_PARENT
,
338 GTK_MESSAGE_QUESTION
,
340 _("No matches. Wrap search around the document?"));
341 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_YES
)
342 on_toolbar_find_start_over (action
, user_data
);
343 gtk_widget_destroy (dialog
);
347 if (search_wrap
== FALSE
)
349 anjuta_status_push(status
,
350 _("Incremental search for '%s' failed. Press Enter or click Find to continue searching at the top."),
352 search_params
->wrap
= 1;
357 search_params
->end
= TRUE
;
358 anjuta_status_push (status
, _("Incremental search for '%s' (continued at top) failed."),
360 search_params
->wrap
= 0;
366 anjuta_status_clear_stack (status
);
368 g_free(search_params
->last
);
369 search_params
->last
= expression
;
373 on_toolbar_find_start_over (GtkAction
* action
, gpointer user_data
)
376 IAnjutaDocument
*doc
;
377 IAnjutaDocumentManager
*docman
;
378 SearchPlugin
*plugin
;
380 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
381 docman
= plugin
->docman
;
382 doc
= ianjuta_document_manager_get_current_document(docman
, NULL
);
383 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
385 /* search from doc start */
388 ianjuta_editor_goto_position(te
, 0, NULL
);
389 on_toolbar_find_clicked (action
, user_data
);
394 on_toolbar_find_incremental_start (GtkAction
*action
, gpointer user_data
)
397 IAnjutaDocument
*doc
;
398 IAnjutaDocumentManager
*docman
;
399 SearchPlugin
*plugin
;
400 IncrementalSearch
*search_params
;
401 GSList
*entries
, *node
;
402 static GHashTable
*entries_connected
= NULL
;
404 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
405 docman
= plugin
->docman
;
406 doc
= ianjuta_document_manager_get_current_document(docman
, NULL
);
407 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
409 if (!te
) return FALSE
;
411 /* Make sure we set up escape for getting out the focus to the editor */
412 if (entries_connected
== NULL
)
414 entries_connected
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
416 entries
= gtk_action_get_proxies (action
);
421 entry
= GTK_WIDGET (node
->data
);
422 if (!g_hash_table_lookup (entries_connected
, entry
))
424 g_signal_connect (G_OBJECT (entry
), "key-press-event",
425 G_CALLBACK (on_incremental_entry_key_press
),
427 g_hash_table_insert (entries_connected
, entry
, entry
);
429 node
= g_slist_next (node
);
432 search_params
= g_object_get_data (G_OBJECT (te
), "incremental_search");
435 search_params
= g_new0 (IncrementalSearch
, 1);
436 g_object_set_data_full (G_OBJECT (te
), "incremental_search",
437 search_params
, (GDestroyNotify
)g_free
);
439 /* Prepare to begin incremental search */
440 search_params
->pos
= ianjuta_editor_get_position(te
, NULL
);
441 search_params
->wrap
= FALSE
;
446 on_toolbar_find_incremental_end (GtkAction
*action
, gpointer user_data
)
449 IAnjutaDocument
*doc
;
450 IAnjutaDocumentManager
*docman
;
451 SearchPlugin
*plugin
;
452 IncrementalSearch
*search_params
;
453 AnjutaStatus
*status
;
455 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
456 docman
= plugin
->docman
;
457 doc
= ianjuta_document_manager_get_current_document(docman
, NULL
);
458 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
463 status
= anjuta_shell_get_status (ANJUTA_PLUGIN (user_data
)->shell
, NULL
);
464 anjuta_status_clear_stack (status
);
466 search_params
= g_object_get_data (G_OBJECT (te
), "incremental_search");
469 search_params
->pos
= -1;
470 search_params
->wrap
= FALSE
;
476 on_toolbar_find_incremental (GtkAction
*action
, gpointer user_data
)
478 const gchar
*entry_text
;
480 IAnjutaDocument
*doc
;
481 IAnjutaDocumentManager
*docman
;
482 SearchPlugin
*plugin
;
483 IncrementalSearch
*search_params
;
485 plugin
= ANJUTA_PLUGIN_SEARCH (user_data
);
486 docman
= plugin
->docman
;
487 doc
= ianjuta_document_manager_get_current_document(docman
, NULL
);
488 te
= IANJUTA_IS_EDITOR(doc
) ? IANJUTA_EDITOR(doc
) : NULL
;
493 if (EGG_IS_ENTRY_ACTION (action
))
495 entry_text
= egg_entry_action_get_text (EGG_ENTRY_ACTION (action
));
500 GtkAction
*entry_action
;
502 ui
= ANJUTA_UI (g_object_get_data (G_OBJECT (user_data
), "ui"));
503 entry_action
= anjuta_ui_get_action (ui
, "ActionGroupSearch",
504 "ActionEditSearchEntry");
505 g_return_if_fail (EGG_IS_ENTRY_ACTION (entry_action
));
507 egg_entry_action_get_text (EGG_ENTRY_ACTION (entry_action
));
509 if (!entry_text
|| strlen(entry_text
) < 1) return;
511 search_params
= g_object_get_data (G_OBJECT (te
), "incremental_search");
514 search_params
= g_new0 (IncrementalSearch
, 1);
515 g_object_set_data_full (G_OBJECT (te
), "incremental_search",
516 search_params
, (GDestroyNotify
)g_free
);
518 ianjuta_editor_get_position(te
, NULL
);
522 /* Do not wrap around if the user just pressed backspace */
523 if (search_params
->last
&&
524 g_str_has_prefix(search_params
->last
, entry_text
))
526 DEBUG_PRINT ("Do not wrap");
527 search_params
->wrap
= FALSE
;
530 if (search_params
->pos
< 0)
533 ianjuta_editor_goto_position(te
, search_params
->pos
, NULL
);
534 on_toolbar_find_clicked (NULL
, user_data
);
537 static GtkActionEntry actions_search
[] = {
538 { "ActionMenuEditSearch", NULL
, N_("_Search"), NULL
, NULL
, NULL
},
539 { "ActionEditSearchFind", GTK_STOCK_FIND
, N_("_Find..."), "<control>f",
540 N_("Search for a string or regular expression in the editor"),
541 G_CALLBACK (on_find1_activate
)},
542 { "ActionEditSearchFindNext", GTK_STOCK_FIND
, N_("Find _Next"), "<control>g",
543 N_("Repeat the last Find command"),
544 G_CALLBACK (on_findnext1_activate
)},
545 { "ActionEditSearchFindPrevious", GTK_STOCK_FIND
, N_("Find _Previous"),
547 N_("Repeat the last Find command"),
548 G_CALLBACK (on_findprevious1_activate
)},
549 { "ActionEditSearchReplace", GTK_STOCK_FIND_AND_REPLACE
, N_("Find and R_eplace..."),
551 N_("Search for and replace a string or regular expression with another string"),
552 G_CALLBACK (on_find_and_replace1_activate
)},
553 { "ActionEditAdvancedSearch", GTK_STOCK_FIND
, N_("Advanced Search And Replace"),
554 NULL
, N_("New advance search And replace stuff"),
555 G_CALLBACK (on_find1_activate
)},
556 { "ActionEditSearchSelectionISearch", NULL
, N_("_Enter Selection/I-Search"),
558 N_("Enter the selected text as the search target"),
559 G_CALLBACK (on_enterselection
)},
560 { "ActionEditSearchInFiles", NULL
, N_("Fin_d in Files..."), "<shift><control>f",
561 N_("Search for a string in multiple files or directories"),
562 G_CALLBACK (on_find_in_files1_activate
)},
563 { "ActionEditGotoOccuranceNext", GTK_STOCK_JUMP_TO
,
564 N_("Ne_xt Occurrence"), NULL
,
565 N_("Find the next occurrence of current word"),
566 G_CALLBACK (on_next_occur
)},
567 { "ActionEditGotoOccurancePrev",GTK_STOCK_JUMP_TO
,
568 N_("Pre_vious Occurrence"), NULL
,
569 N_("Find the previous occurrence of current word"),
570 G_CALLBACK (on_prev_occur
)},
573 gpointer parent_class
;
576 activate_plugin (AnjutaPlugin
*plugin
)
579 GtkActionGroup
* group
;
581 SearchPlugin
* splugin
= ANJUTA_PLUGIN_SEARCH (plugin
);
582 IAnjutaDocumentManager
* docman
= anjuta_shell_get_interface(ANJUTA_PLUGIN(plugin
)->shell
,
583 IAnjutaDocumentManager
, NULL
);
586 ui
= anjuta_shell_get_ui (plugin
->shell
, NULL
);
587 anjuta_ui_add_action_group_entries (ui
, "ActionGroupSearch",
590 G_N_ELEMENTS (actions_search
),
591 GETTEXT_PACKAGE
, TRUE
, plugin
);
593 group
= gtk_action_group_new ("ActionGroupSearch");
594 action
= g_object_new (EGG_TYPE_ENTRY_ACTION
,
595 "name", "ActionEditSearchEntry",
596 "label", _("Search"),
597 "tooltip", _("Incremental search"),
598 "stock_id", GTK_STOCK_JUMP_TO
,
601 g_assert (EGG_IS_ENTRY_ACTION (action
));
602 g_signal_connect (action
, "activate",
603 G_CALLBACK (on_toolbar_find_clicked
), plugin
);
604 g_signal_connect (action
, "changed",
605 G_CALLBACK (on_toolbar_find_incremental
), plugin
);
606 g_signal_connect (action
, "focus-in",
607 G_CALLBACK (on_toolbar_find_incremental_start
), plugin
);
608 g_signal_connect (action
, "focus-out",
609 G_CALLBACK (on_toolbar_find_incremental_end
), plugin
);
610 gtk_action_group_add_action (group
, action
);
612 /* FIXME: For some reason, if can_customize is set TRUE, AnjutaUI
613 * can't find this action
615 anjuta_ui_add_action_group(ui
, "ActionGroupSearch", _("Search Toolbar"),
617 g_object_set (G_OBJECT (action
), "sensitive", TRUE
, NULL
);
620 splugin
->uiid
= anjuta_ui_merge (ui
, UI_FILE
);
621 splugin
->docman
= docman
;
622 search_and_replace_init(docman
);
628 deactivate_plugin (AnjutaPlugin
*plugin
)
635 dispose (GObject
*obj
)
637 //SearchPlugin *plugin = ANJUTA_PLUGIN_SEARCH (obj);
639 GNOME_CALL_PARENT (G_OBJECT_CLASS
, dispose
, (obj
));
643 search_plugin_instance_init (GObject
*obj
)
645 //SearchPlugin *plugin = ANJUTA_PLUGIN_SEARCH (obj);
649 search_plugin_class_init (GObjectClass
*klass
)
651 AnjutaPluginClass
*plugin_class
= ANJUTA_PLUGIN_CLASS (klass
);
653 parent_class
= g_type_class_peek_parent (klass
);
655 plugin_class
->activate
= activate_plugin
;
656 plugin_class
->deactivate
= deactivate_plugin
;
657 klass
->dispose
= dispose
;
659 ANJUTA_PLUGIN_BEGIN (SearchPlugin
, search_plugin
);
661 ANJUTA_SIMPLE_PLUGIN (SearchPlugin
, search_plugin
);