Updated Spanish translation
[anjuta-git-plugin.git] / plugins / search / plugin.c
blob6768b56b1f1e60e1cf75343273574d8185ac2d02
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 plugin.c
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>
29 #include "plugin.h"
30 #include "search-replace.h"
31 #include "search-replace_backend.h"
32 #include "config.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,
43 SearchDirection dir)
45 FileBuffer* fb = file_buffer_new_from_te (te);
46 SearchExpression* se = g_new0(SearchExpression, 1);
47 MatchInfo* info;
48 gboolean ret;
50 se->search_str = expression;
51 se->regex = FALSE;
52 se->greedy = FALSE;
53 se->ignore_case = TRUE;
54 se->whole_word = FALSE;
55 se->whole_line = FALSE;
56 se->word_start = FALSE;
57 se->no_limit = FALSE;
58 se->actions_max = 1;
59 se->re = NULL;
61 info = get_next_match(fb, dir, se);
63 if (info != NULL)
65 gboolean backward;
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);
69 ret = TRUE;
71 else
72 ret = FALSE;
74 match_info_free(info);
75 file_buffer_free(fb);
76 g_free(se);
78 return ret;
81 static void
82 on_find1_activate (GtkAction * action, gpointer user_data)
84 anjuta_search_replace_activate(FALSE, FALSE);
87 static void
88 on_find_and_replace1_activate (GtkAction * action, gpointer user_data)
90 anjuta_search_replace_activate(TRUE, FALSE);
93 static void
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 */
100 static void
101 on_findnext1_activate (GtkAction * action, gpointer user_data)
103 search_replace_next();
106 static void
107 on_findprevious1_activate (GtkAction * action, gpointer user_data)
109 search_replace_previous();
112 static void
113 on_enterselection (GtkAction * action, gpointer user_data)
115 GtkAction *entry_action;
116 AnjutaUI* ui;
117 IAnjutaEditor *te;
118 IAnjutaDocumentManager* docman;
119 IAnjutaDocument* doc;
120 SearchPlugin* plugin;
121 gchar *selectionText = NULL;
122 GSList *proxies;
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;
130 if (!te) return;
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),
137 NULL);
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));
144 if (proxies)
146 GtkWidget *child;
147 child = gtk_bin_get_child (GTK_BIN (proxies->data));
148 gtk_widget_grab_focus (GTK_WIDGET (child));
150 g_free (selectionText);
153 static void
154 on_prev_occur(GtkAction * action, gpointer user_data)
156 IAnjutaEditor* te;
157 IAnjutaDocumentManager *docman;
158 IAnjutaDocument* doc;
159 SearchPlugin *plugin;
160 gint return_;
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;
168 if(!te) return;
169 if ((buffer = ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te), NULL)))
171 g_strstrip(buffer);
172 if ('\0' == *buffer)
174 g_free(buffer);
175 buffer = NULL;
178 if (NULL == buffer)
180 buffer = ianjuta_editor_get_current_word(te, NULL);
181 if (!buffer)
182 return;
184 return_= find_incremental(te, buffer, SD_BACKWARD);
186 g_free(buffer);
189 static void
190 on_next_occur(GtkAction * action, gpointer user_data)
192 IAnjutaEditor* te;
193 IAnjutaDocumentManager *docman;
194 IAnjutaDocument* doc;
195 SearchPlugin *plugin;
196 gint return_;
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;
204 if(!te) return;
205 if ((buffer = ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te), NULL)))
207 g_strstrip(buffer);
208 if ('\0' == *buffer)
210 g_free(buffer);
211 buffer = NULL;
214 if (NULL == buffer)
216 buffer = ianjuta_editor_get_current_word(te, NULL);
217 if (!buffer)
218 return;
220 return_= find_incremental(te, buffer, SD_FORWARD);
222 g_free(buffer);
225 /* Incremental search */
227 typedef struct
229 gint pos;
230 gboolean wrap;
231 gboolean end;
232 gchar* last;
234 } IncrementalSearch;
236 static void
237 on_incremental_entry_key_press (GtkWidget *entry, GdkEventKey *event,
238 SearchPlugin *plugin)
240 if (event->keyval == GDK_Escape)
242 IAnjutaEditor *te;
243 IAnjutaDocument *doc;
245 doc = ianjuta_document_manager_get_current_document(plugin->docman, NULL);
246 te = IANJUTA_IS_EDITOR(doc) ? IANJUTA_EDITOR(doc) : NULL;
247 if (te)
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 */
256 static void
257 on_toolbar_find_clicked (GtkAction *action, gpointer user_data)
259 const gchar *string;
260 gchar* expression;
261 gint ret;
262 IAnjutaEditor *te;
263 IAnjutaDocument *doc;
264 IAnjutaDocumentManager *docman;
265 SearchPlugin *plugin;
266 IncrementalSearch *search_params;
267 gboolean search_wrap = FALSE;
268 AnjutaStatus *status;
269 AnjutaUI* ui;
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);
277 if (!te)
278 return;
280 search_params = g_object_get_data (G_OBJECT (te), "incremental_search");
281 if (!search_params)
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));
291 else
293 GtkAction *entry_action;
294 entry_action = anjuta_ui_get_action (ui,
295 "ActionGroupSearch",
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. */
303 search_wrap = TRUE;
306 expression = g_strdup(string);
307 if (search_params->end &&
308 g_str_has_prefix(expression, search_params->last))
310 g_free(expression);
311 return;
313 else
314 search_params->end = FALSE;
315 if (search_wrap)
317 ianjuta_editor_goto_position(te, 0, NULL);
318 ret = find_incremental(te, expression, SD_FORWARD);
319 search_params->wrap = FALSE;
321 else
323 ret = find_incremental(te, expression, SD_FORWARD);
326 status = anjuta_shell_get_status (ANJUTA_PLUGIN (user_data)->shell, NULL);
328 if (ret == FALSE)
330 if (search_params->pos < 0)
332 GtkWindow *parent;
333 GtkWidget *dialog;
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,
339 GTK_BUTTONS_YES_NO,
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);
345 else
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."),
351 string);
352 search_params->wrap = 1;
353 gdk_beep();
355 else
357 search_params->end = TRUE;
358 anjuta_status_push (status, _("Incremental search for '%s' (continued at top) failed."),
359 string);
360 search_params->wrap = 0;
364 else
366 anjuta_status_clear_stack (status);
368 g_free(search_params->last);
369 search_params->last = expression;
372 static void
373 on_toolbar_find_start_over (GtkAction * action, gpointer user_data)
375 IAnjutaEditor *te;
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 */
386 if (te)
388 ianjuta_editor_goto_position(te, 0, NULL);
389 on_toolbar_find_clicked (action, user_data);
393 static gboolean
394 on_toolbar_find_incremental_start (GtkAction *action, gpointer user_data)
396 IAnjutaEditor *te;
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);
417 node = entries;
418 while (node)
420 GtkWidget *entry;
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),
426 plugin);
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");
433 if (!search_params)
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;
442 return FALSE;
445 static gboolean
446 on_toolbar_find_incremental_end (GtkAction *action, gpointer user_data)
448 IAnjutaEditor *te;
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;
460 if (!te)
461 return FALSE;
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");
467 if (search_params)
469 search_params->pos = -1;
470 search_params->wrap = FALSE;
472 return FALSE;
475 static void
476 on_toolbar_find_incremental (GtkAction *action, gpointer user_data)
478 const gchar *entry_text;
479 IAnjutaEditor *te;
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;
490 if (!te)
491 return;
493 if (EGG_IS_ENTRY_ACTION (action))
495 entry_text = egg_entry_action_get_text (EGG_ENTRY_ACTION (action));
497 else
499 AnjutaUI *ui;
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));
506 entry_text =
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");
512 if (!search_params)
514 search_params = g_new0 (IncrementalSearch, 1);
515 g_object_set_data_full (G_OBJECT (te), "incremental_search",
516 search_params, (GDestroyNotify)g_free);
517 search_params->pos =
518 ianjuta_editor_get_position(te, NULL);
520 else
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)
531 return;
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"),
546 "<control><shift>g",
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..."),
550 "<control>h",
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"),
557 "<control>e",
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;
575 static gboolean
576 activate_plugin (AnjutaPlugin *plugin)
578 AnjutaUI *ui;
579 GtkActionGroup* group;
580 GtkAction* action;
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",
588 _("Searching..."),
589 actions_search,
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,
599 "width", 150,
600 NULL);
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"),
616 group, TRUE);
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);
624 return TRUE;
627 static gboolean
628 deactivate_plugin (AnjutaPlugin *plugin)
631 return TRUE;
634 static void
635 dispose (GObject *obj)
637 //SearchPlugin *plugin = ANJUTA_PLUGIN_SEARCH (obj);
639 GNOME_CALL_PARENT (G_OBJECT_CLASS, dispose, (obj));
642 static void
643 search_plugin_instance_init (GObject *obj)
645 //SearchPlugin *plugin = ANJUTA_PLUGIN_SEARCH (obj);
648 static void
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);
660 ANJUTA_PLUGIN_END;
661 ANJUTA_SIMPLE_PLUGIN (SearchPlugin, search_plugin);