search: Search for next occurence when replace succeeds (see bgo#665945)
[anjuta.git] / plugins / document-manager / search-box.c
blobcc0fe5272635b9a2b85b5664cd0ba2cadb527222
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) Johannes Schmid 2007 <jhs@gnome.org>
5 *
6 * anjuta is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
13 * anjuta is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with anjuta. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include <glib/gi18n.h>
26 #include "search-box.h"
28 #include <stdlib.h>
29 #include <gtk/gtk.h>
31 #include <libanjuta/anjuta-shell.h>
32 #include <libanjuta/anjuta-status.h>
33 #include <libanjuta/anjuta-debug.h>
35 #include <libanjuta/interfaces/ianjuta-editor.h>
36 #include <libanjuta/interfaces/ianjuta-editor-search.h>
37 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
38 #include <libanjuta/interfaces/ianjuta-indicable.h>
40 #define ANJUTA_STOCK_GOTO_LINE "anjuta-goto-line"
42 /* width of the entries in chars, that include the space for the icon */
43 #define LINE_ENTRY_WIDTH 7
44 #define SEARCH_ENTRY_WIDTH 45
46 typedef struct _SearchBoxPrivate SearchBoxPrivate;
48 struct _SearchBoxPrivate
50 GtkWidget* grid;
52 GtkWidget* search_entry;
53 GtkWidget* replace_entry;
55 GtkWidget* close_button;
56 GtkWidget* next_button;
57 GtkWidget* previous_button;
59 GtkWidget* replace_button;
60 GtkWidget* replace_all_button;
62 GtkWidget* goto_entry;
63 GtkWidget* goto_button;
65 IAnjutaEditor* current_editor;
66 AnjutaStatus* status;
68 /* Search options popup menu */
69 GtkWidget* popup_menu;
70 gboolean case_sensitive;
71 gboolean highlight_all;
72 gboolean regex_mode;
73 gboolean highlight_complete;
77 #ifdef GET_PRIVATE
78 # undef GET_PRIVATE
79 #endif
80 #define GET_PRIVATE(o) \
81 (G_TYPE_INSTANCE_GET_PRIVATE((o), SEARCH_TYPE_BOX, SearchBoxPrivate))
83 G_DEFINE_TYPE (SearchBox, search_box, GTK_TYPE_HBOX);
85 static void
86 on_search_box_hide (GtkWidget* button, SearchBox* search_box)
88 gtk_widget_hide (GTK_WIDGET (search_box));
91 static void
92 on_document_changed (AnjutaDocman* docman, IAnjutaDocument* doc,
93 SearchBox* search_box)
95 SearchBoxPrivate* private = GET_PRIVATE(search_box);
97 if (!doc || !IANJUTA_IS_EDITOR (doc))
99 gtk_widget_hide (GTK_WIDGET (search_box));
100 private->current_editor = NULL;
102 else
104 private->current_editor = IANJUTA_EDITOR (doc);
108 static void
109 on_goto_activated (GtkWidget* widget, SearchBox* search_box)
111 SearchBoxPrivate* private = GET_PRIVATE(search_box);
112 const gchar* str_line = gtk_entry_get_text (GTK_ENTRY (private->goto_entry));
114 gint line = atoi (str_line);
115 if (line > 0)
117 ianjuta_editor_goto_line (private->current_editor, line, NULL);
121 static void
122 search_box_set_entry_color (SearchBox* search_box, gboolean found)
124 SearchBoxPrivate* private = GET_PRIVATE(search_box);
125 if (!found)
127 GdkColor red;
128 GdkColor white;
130 /* FIXME: a11y and theme */
132 gdk_color_parse ("#FF6666", &red);
133 gdk_color_parse ("white", &white);
135 gtk_widget_modify_base (private->search_entry,
136 GTK_STATE_NORMAL,
137 &red);
138 gtk_widget_modify_text (private->search_entry,
139 GTK_STATE_NORMAL,
140 &white);
142 else
144 gtk_widget_modify_base (private->search_entry,
145 GTK_STATE_NORMAL,
146 NULL);
147 gtk_widget_modify_text (private->search_entry,
148 GTK_STATE_NORMAL,
149 NULL);
153 static gboolean
154 on_goto_key_pressed (GtkWidget* entry, GdkEventKey* event, SearchBox* search_box)
156 SearchBoxPrivate* private = GET_PRIVATE(search_box);
157 switch (event->keyval)
159 case GDK_KEY_0:
160 case GDK_KEY_1:
161 case GDK_KEY_2:
162 case GDK_KEY_3:
163 case GDK_KEY_4:
164 case GDK_KEY_5:
165 case GDK_KEY_6:
166 case GDK_KEY_7:
167 case GDK_KEY_8:
168 case GDK_KEY_9:
169 case GDK_KEY_KP_0:
170 case GDK_KEY_KP_1:
171 case GDK_KEY_KP_2:
172 case GDK_KEY_KP_3:
173 case GDK_KEY_KP_4:
174 case GDK_KEY_KP_5:
175 case GDK_KEY_KP_6:
176 case GDK_KEY_KP_7:
177 case GDK_KEY_KP_8:
178 case GDK_KEY_KP_9:
179 case GDK_KEY_Return:
180 case GDK_KEY_KP_Enter:
181 case GDK_KEY_BackSpace:
182 case GDK_KEY_Delete:
184 /* This is a number or enter which is ok */
185 break;
187 case GDK_KEY_Escape:
189 gtk_widget_hide (GTK_WIDGET (search_box));
190 search_box_set_entry_color (search_box, TRUE);
191 if (private->current_editor)
193 ianjuta_document_grab_focus (IANJUTA_DOCUMENT (private->current_editor),
194 NULL);
197 default:
199 /* Not a number */
200 gdk_beep ();
201 return TRUE;
204 return FALSE;
207 static gboolean
208 on_entry_key_pressed (GtkWidget* entry, GdkEventKey* event, SearchBox* search_box)
211 SearchBoxPrivate* private = GET_PRIVATE(search_box);
212 switch (event->keyval)
214 case GDK_KEY_Escape:
216 gtk_widget_hide (GTK_WIDGET (search_box));
217 search_box_set_entry_color (search_box, TRUE);
218 if (private->current_editor)
220 ianjuta_document_grab_focus (IANJUTA_DOCUMENT (private->current_editor),
221 NULL);
224 default:
226 /* Do nothing... */
229 return FALSE;
232 static gboolean
233 on_search_focus_out (GtkWidget* widget, GdkEvent* event, SearchBox* search_box)
235 SearchBoxPrivate* private = GET_PRIVATE(search_box);
236 anjuta_status_pop (private->status);
238 return FALSE;
243 static gboolean
244 incremental_regex_search (const gchar* search_entry, const gchar* editor_text, gint * start_pos, gint * end_pos, gboolean search_forward)
246 GRegex * regex;
247 GMatchInfo *match_info;
248 gboolean result;
249 GError * err = NULL;
251 regex = g_regex_new (search_entry, 0, 0, &err);
252 if (err)
254 g_message ("%s",err->message);
255 g_error_free (err);
256 g_regex_unref(regex);
257 return FALSE;
260 result = g_regex_match (regex, editor_text, 0, &match_info);
262 if (result)
264 if (search_forward)
265 g_match_info_fetch_pos(match_info, 0, start_pos, end_pos);
266 else
270 g_match_info_fetch_pos(match_info, 0, start_pos, end_pos);
272 while (g_match_info_next(match_info, NULL));
275 *start_pos = g_utf8_pointer_to_offset(editor_text, &editor_text[*start_pos]);
276 *end_pos = g_utf8_pointer_to_offset(editor_text, &editor_text[*end_pos]);
279 if (regex)
280 g_regex_unref(regex);
281 if (match_info)
282 g_match_info_free (match_info);
284 return result;
288 gboolean
289 search_box_incremental_search (SearchBox* search_box, gboolean search_forward,
290 gboolean wrap)
292 IAnjutaIterable* real_start;
293 IAnjutaEditorCell* search_start;
294 IAnjutaEditorCell* search_end;
295 IAnjutaEditorCell* result_start;
296 IAnjutaEditorCell* result_end;
297 IAnjutaEditorSelection* selection;
298 SearchBoxPrivate* private = GET_PRIVATE (search_box);
300 const gchar* search_text = gtk_entry_get_text (GTK_ENTRY (private->search_entry));
302 gboolean found = FALSE;
304 if (!private->current_editor || !search_text || !strlen (search_text))
305 return FALSE;
307 selection = IANJUTA_EDITOR_SELECTION (private->current_editor);
309 if (ianjuta_editor_selection_has_selection (selection, NULL))
311 search_start =
312 IANJUTA_EDITOR_CELL (ianjuta_editor_selection_get_start (selection, NULL));
314 else
316 search_start =
317 IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (private->current_editor,
318 NULL));
321 real_start =
322 ianjuta_iterable_clone (IANJUTA_ITERABLE (search_start), NULL);
324 /* If forward, set search start and end to current position of
325 * cursor and editor end, respectively, or if backward to editor
326 * start and current position of cursor, respectively. Current
327 * position of cursor is selection start if have selection. */
328 if (search_forward)
330 search_end = IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (private->current_editor,
331 NULL));
332 ianjuta_iterable_last (IANJUTA_ITERABLE (search_end), NULL);
334 else
336 search_end = search_start;
337 search_start = IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (private->current_editor,
338 NULL));
339 ianjuta_iterable_first (IANJUTA_ITERABLE (search_start), NULL);
342 /* When there's a selection, if forward, set search start and end
343 * to match end and editor end, respectively, or if backward to
344 * editor start and match start, respectively. If forward and
345 * selection starts with a match, look for next match.
347 if (ianjuta_editor_selection_has_selection (selection,
348 NULL))
350 IAnjutaIterable* selection_start =
351 ianjuta_editor_selection_get_start (selection, NULL);
353 gchar* selected_text =
354 ianjuta_editor_selection_get (selection, NULL);
356 gint start_pos, end_pos;
357 gboolean selected_have_search_text = FALSE;
359 if (private->regex_mode)
361 /* Always look for first match */
362 if (incremental_regex_search (search_text, selected_text, &start_pos, &end_pos, TRUE))
364 selected_have_search_text = TRUE;
367 else if (strlen (selected_text) >= strlen (search_text))
369 gchar* selected_text_case;
370 gchar* search_text_case;
372 if (private->case_sensitive)
374 selected_text_case = g_strdup (selected_text);
375 search_text_case = g_strdup (search_text);
377 else
379 selected_text_case = g_utf8_casefold (selected_text, strlen (selected_text));
380 search_text_case = g_utf8_casefold (search_text, strlen (search_text));
383 gchar* strstr = g_strstr_len (selected_text_case, -1, search_text_case);
385 if (strstr)
387 start_pos = g_utf8_pointer_to_offset(selected_text_case, strstr);
388 end_pos = g_utf8_pointer_to_offset(selected_text_case, strstr + strlen (search_text));
389 selected_have_search_text = TRUE;
392 g_free (selected_text_case);
393 g_free (search_text_case);
396 if (selected_have_search_text)
398 if (search_forward && start_pos == 0)
400 end_pos += ianjuta_iterable_get_position(IANJUTA_ITERABLE (selection_start), NULL);
401 ianjuta_iterable_set_position (IANJUTA_ITERABLE(search_start), end_pos, NULL);
402 ianjuta_iterable_last (IANJUTA_ITERABLE (search_end), NULL);
404 else if (!search_forward)
406 start_pos += ianjuta_iterable_get_position(IANJUTA_ITERABLE (selection_start), NULL);
407 ianjuta_iterable_set_position (IANJUTA_ITERABLE(search_end), start_pos, NULL);
408 ianjuta_iterable_first (IANJUTA_ITERABLE (search_start), NULL);
412 g_free (selected_text);
414 g_object_unref (selection_start);
417 gboolean result_set = FALSE;
419 gint start_pos, end_pos;
420 gchar * text_to_search;
421 gboolean result;
423 if (!found)
425 /* Try searching in current position */
426 if (private->regex_mode)
428 text_to_search = ianjuta_editor_get_text (private->current_editor,
429 IANJUTA_ITERABLE (search_start),
430 IANJUTA_ITERABLE (search_end), NULL);
432 result = incremental_regex_search (search_text, text_to_search, &start_pos, &end_pos, search_forward);
434 start_pos += ianjuta_iterable_get_position(IANJUTA_ITERABLE (search_start), NULL);
435 end_pos += ianjuta_iterable_get_position(IANJUTA_ITERABLE (search_start), NULL);
437 if (result && start_pos >= 0)
439 result_start = IANJUTA_EDITOR_CELL (ianjuta_editor_get_start_position (private->current_editor,
440 NULL));
441 result_end = IANJUTA_EDITOR_CELL (ianjuta_editor_get_start_position (private->current_editor,
442 NULL));
444 if (ianjuta_iterable_set_position(IANJUTA_ITERABLE(result_start), start_pos, NULL) &&
445 ianjuta_iterable_set_position(IANJUTA_ITERABLE(result_end), end_pos, NULL))
447 found = TRUE;
450 if (!found)
452 g_object_unref(result_start);
453 g_object_unref(result_end);
457 g_free(text_to_search);
459 else
461 if (search_forward)
463 if (ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (private->current_editor),
464 search_text, private->case_sensitive,
465 search_start, search_end,
466 &result_start,
467 &result_end, NULL))
469 found = TRUE;
472 else
474 if (ianjuta_editor_search_backward (IANJUTA_EDITOR_SEARCH (private->current_editor),
475 search_text, private->case_sensitive,
476 search_end, search_start,
477 &result_start,
478 &result_end, NULL))
480 found = TRUE;
486 if (found)
488 anjuta_status_pop (ANJUTA_STATUS (private->status));
490 else if (wrap)
492 /* Try to wrap if not found */
493 ianjuta_iterable_first (IANJUTA_ITERABLE (search_start), NULL);
494 ianjuta_iterable_last (IANJUTA_ITERABLE (search_end), NULL);
496 /* Try to search again */
497 if (private->regex_mode)
499 text_to_search = ianjuta_editor_get_text (private->current_editor,
500 IANJUTA_ITERABLE(search_start),
501 IANJUTA_ITERABLE(search_end), NULL);
503 result = incremental_regex_search (search_text, text_to_search, &start_pos, &end_pos, search_forward);
505 start_pos += ianjuta_iterable_get_position(IANJUTA_ITERABLE(search_start), NULL);
506 end_pos += ianjuta_iterable_get_position(IANJUTA_ITERABLE(search_start), NULL);
508 if (result && start_pos >= 0)
510 result_start = IANJUTA_EDITOR_CELL (ianjuta_editor_get_start_position (private->current_editor,
511 NULL));
512 result_end = IANJUTA_EDITOR_CELL (ianjuta_editor_get_start_position (private->current_editor,
513 NULL));
515 if (ianjuta_iterable_set_position(IANJUTA_ITERABLE(result_start), start_pos, NULL) &&
516 ianjuta_iterable_set_position(IANJUTA_ITERABLE(result_end), end_pos, NULL))
518 result_set = TRUE;
521 if (!result_set)
523 g_object_unref(result_start);
524 g_object_unref(result_end);
528 else
530 if (search_forward)
532 if (ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (private->current_editor),
533 search_text, private->case_sensitive,
534 search_start, search_end,
535 &result_start,
536 &result_end, NULL))
538 result_set = TRUE;
541 else
543 if (ianjuta_editor_search_backward (IANJUTA_EDITOR_SEARCH (private->current_editor),
544 search_text, private->case_sensitive,
545 search_end, search_start,
546 &result_start,
547 &result_end, NULL))
549 result_set = TRUE;
554 /* Check if successful */
555 if (result_set)
557 if (ianjuta_iterable_compare (IANJUTA_ITERABLE (result_start),
558 real_start, NULL) != 0)
560 found = TRUE;
561 anjuta_status_pop (private->status);
562 if (search_forward)
564 anjuta_status_push (private->status,
565 _("Search for \"%s\" reached the end and was continued at the top."), search_text);
567 else
569 anjuta_status_push (private->status,
570 _("Search for \"%s\" reached top and was continued at the bottom."), search_text);
573 else if (ianjuta_editor_selection_has_selection (selection, NULL))
575 anjuta_status_pop (private->status);
576 if (search_forward)
578 anjuta_status_push (private->status,
579 _("Search for \"%s\" reached the end and was continued at the top but no new match was found."), search_text);
581 else
583 anjuta_status_push (private->status,
584 _("Search for \"%s\" reached top and was continued at the bottom but no new match was found."), search_text);
590 if (found)
592 ianjuta_editor_selection_set (selection,
593 IANJUTA_ITERABLE (result_start),
594 IANJUTA_ITERABLE (result_end), TRUE, NULL);
595 g_object_unref (result_start);
596 g_object_unref (result_end);
599 search_box_set_entry_color (search_box, found);
600 g_object_unref (real_start);
601 g_object_unref (search_start);
602 g_object_unref (search_end);
604 return found;
607 void
608 search_box_clear_highlight (SearchBox * search_box)
610 SearchBoxPrivate* private = GET_PRIVATE(search_box);
612 if (!private->current_editor)
613 return;
615 ianjuta_indicable_clear(IANJUTA_INDICABLE(private->current_editor), NULL);
616 private->highlight_complete = FALSE;
619 void
620 search_box_toggle_highlight (SearchBox * search_box, gboolean status)
622 SearchBoxPrivate* private = GET_PRIVATE(search_box);
624 if (!private->current_editor)
625 return;
627 private->highlight_all = status;
629 if (!status)
631 ianjuta_indicable_clear(IANJUTA_INDICABLE(private->current_editor), NULL);
632 private->highlight_complete = FALSE;
636 void
637 search_box_toggle_case_sensitive (SearchBox * search_box, gboolean status)
639 SearchBoxPrivate* private = GET_PRIVATE(search_box);
641 if (!private->current_editor)
642 return;
644 private->case_sensitive = status;
645 search_box_clear_highlight(search_box);
648 void
649 search_box_toggle_regex (SearchBox * search_box, gboolean status)
651 SearchBoxPrivate* private = GET_PRIVATE(search_box);
653 if (!private->current_editor)
654 return;
656 private->regex_mode = status;
657 search_box_clear_highlight(search_box);
661 static void
662 search_box_search_highlight_all (SearchBox * search_box, gboolean search_forward)
664 IAnjutaEditorCell * highlight_start;
665 IAnjutaEditorSelection * selection;
666 gboolean entry_found;
668 SearchBoxPrivate* private = GET_PRIVATE(search_box);
670 highlight_start = NULL;
671 ianjuta_indicable_clear(IANJUTA_INDICABLE(private->current_editor), NULL);
673 /* Search through editor and highlight instances of search_entry */
674 while ((entry_found = search_box_incremental_search (search_box, search_forward, TRUE)) == TRUE)
676 IAnjutaEditorCell * result_begin, * result_end;
677 selection = IANJUTA_EDITOR_SELECTION (private->current_editor);
679 result_begin =
680 IANJUTA_EDITOR_CELL (ianjuta_editor_selection_get_start (selection, NULL));
681 result_end =
682 IANJUTA_EDITOR_CELL (ianjuta_editor_selection_get_end (selection, NULL));
684 if (!highlight_start)
686 highlight_start =
687 IANJUTA_EDITOR_CELL (ianjuta_iterable_clone (IANJUTA_ITERABLE (result_begin), NULL));
689 else if (ianjuta_iterable_compare (IANJUTA_ITERABLE (result_begin),
690 IANJUTA_ITERABLE (highlight_start), NULL) == 0)
692 g_object_unref (result_begin);
693 g_object_unref (result_end);
694 g_object_unref (highlight_start);
695 highlight_start = NULL;
696 break;
699 ianjuta_indicable_set(IANJUTA_INDICABLE(private->current_editor),
700 IANJUTA_ITERABLE (result_begin),
701 IANJUTA_ITERABLE (result_end),
702 IANJUTA_INDICABLE_IMPORTANT, NULL);
703 g_object_unref (result_begin);
704 g_object_unref (result_end);
706 if (highlight_start)
707 g_object_unref (highlight_start);
708 private->highlight_complete = TRUE;
712 static void
713 on_search_box_entry_changed (GtkWidget * widget, SearchBox * search_box)
715 SearchBoxPrivate* private = GET_PRIVATE(search_box);
717 if (!private->regex_mode)
719 GtkEntryBuffer* buffer = gtk_entry_get_buffer (GTK_ENTRY(widget));
720 if (gtk_entry_buffer_get_length (buffer))
721 search_box_incremental_search (search_box, TRUE, TRUE);
722 else
724 /* clear selection */
725 IAnjutaIterable* cursor =
726 ianjuta_editor_get_position (IANJUTA_EDITOR (private->current_editor),
727 NULL);
728 ianjuta_editor_selection_set (IANJUTA_EDITOR_SELECTION (private->current_editor),
729 cursor,
730 cursor,
731 FALSE, NULL);
736 static void
737 search_box_forward_search (SearchBox * search_box, GtkWidget* widget)
739 SearchBoxPrivate* private = GET_PRIVATE(search_box);
741 if (private->highlight_all && !private->highlight_complete)
743 search_box_search_highlight_all (search_box, TRUE);
745 else
747 search_box_incremental_search (search_box, TRUE, TRUE);
752 static void
753 on_search_box_backward_search (GtkWidget * widget, SearchBox * search_box)
755 SearchBoxPrivate* private = GET_PRIVATE(search_box);
757 if (private->highlight_all && !private->highlight_complete)
759 search_box_search_highlight_all (search_box, FALSE);
761 else
763 search_box_incremental_search (search_box, FALSE, TRUE);
767 static gboolean
768 search_box_replace (SearchBox * search_box, GtkWidget * widget,
769 gboolean undo /* treat as undo action */)
772 IAnjutaEditorSelection* selection;
773 gchar * selection_text;
774 gboolean replace_successful = FALSE;
775 SearchBoxPrivate* private = GET_PRIVATE(search_box);
777 const gchar* replace_text = gtk_entry_get_text (GTK_ENTRY (private->replace_entry));
778 const gchar* search_text = gtk_entry_get_text (GTK_ENTRY (private->search_entry));
780 selection = IANJUTA_EDITOR_SELECTION (private->current_editor);
781 selection_text = ianjuta_editor_selection_get (selection, NULL);
783 if (ianjuta_editor_selection_has_selection (selection, NULL))
785 if (private->regex_mode)
787 GRegex * regex;
788 gchar * replacement_text;
789 gint start_pos, end_pos;
790 GError * err = NULL;
791 gboolean result = incremental_regex_search (search_text, selection_text, &start_pos, &end_pos, TRUE);
793 if (result)
795 regex = g_regex_new (search_text, 0, 0, NULL);
796 replacement_text = g_regex_replace(regex, selection_text, strlen(selection_text), 0,
797 replace_text, 0, &err);
798 if (err)
800 g_message ("%s",err->message);
801 g_error_free (err);
802 g_regex_unref(regex);
804 else
806 if (undo)
807 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT (selection), NULL);
808 ianjuta_editor_selection_replace (selection, replacement_text, strlen(replacement_text), NULL);
809 if (undo)
810 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT (selection), NULL);
812 replace_successful = TRUE;
815 if (regex)
816 g_regex_unref(regex);
817 if (replacement_text)
818 g_free(replacement_text);
821 else if ((private->case_sensitive && g_str_equal (selection_text, search_text)) ||
822 (!private->case_sensitive && strcasecmp (selection_text, search_text) == 0))
824 if (undo)
825 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT (selection), NULL);
826 ianjuta_editor_selection_replace (selection, replace_text, strlen(replace_text), NULL);
827 if (undo)
828 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT (selection), NULL);
830 replace_successful = TRUE;
833 g_free(selection_text);
836 return replace_successful;
840 static void
841 on_replace_activated (GtkWidget* widget, SearchBox* search_box)
843 gboolean successful_replace;
845 SearchBoxPrivate* private = GET_PRIVATE(search_box);
847 if (!private->current_editor)
848 return;
850 /* Either replace search-term or try to move search forward to next occurence */
852 successful_replace = search_box_replace (search_box, widget, TRUE);
854 if (successful_replace)
856 search_box_forward_search (search_box, widget);
860 static void
861 do_popup_menu (GtkWidget* widget, GdkEventButton *event, SearchBox* search_box)
863 int button, event_time;
864 SearchBoxPrivate* private = GET_PRIVATE(search_box);
866 if (event)
868 button = event->button;
869 event_time = event->time;
871 else
873 button = 0;
874 event_time = gtk_get_current_event_time ();
877 if (!gtk_menu_get_attach_widget(GTK_MENU (private->popup_menu)))
878 gtk_menu_attach_to_widget (GTK_MENU (private->popup_menu), widget, NULL);
879 gtk_menu_popup (GTK_MENU (private->popup_menu), NULL, NULL, NULL, NULL,
880 button, event_time);
884 static gboolean
885 on_search_entry_icon_pressed (GtkWidget* widget, GtkEntryIconPosition pos,
886 GdkEvent* event, SearchBox * search_box)
888 do_popup_menu (widget, (GdkEventButton*)event, search_box);
889 return TRUE;
892 static gboolean
893 on_search_entry_popup_menu (GtkWidget* widget, SearchBox* search_box)
895 do_popup_menu (widget, NULL, search_box);
896 return TRUE;
899 static void
900 on_replace_all_activated (GtkWidget* widget, SearchBox* search_box)
903 SearchBoxPrivate* private = GET_PRIVATE(search_box);
904 IAnjutaIterable* cursor;
906 if (!private->current_editor)
907 return;
909 /* Cache current position and search from begin */
910 cursor = ianjuta_editor_get_position (IANJUTA_EDITOR (private->current_editor),
911 NULL);
912 ianjuta_editor_goto_start (IANJUTA_EDITOR (private->current_editor), NULL);
914 /* Replace all instances of search_entry with replace_entry text */
915 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT (private->current_editor), NULL);
916 while (search_box_incremental_search (search_box, TRUE, FALSE))
918 search_box_replace (search_box, widget, FALSE);
920 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT (private->current_editor), NULL);
922 /* Back to cached position */
923 ianjuta_editor_selection_set (IANJUTA_EDITOR_SELECTION (private->current_editor),
924 cursor, cursor, TRUE, NULL);
925 g_object_unref (cursor);
928 static void
929 search_box_init (SearchBox *object)
931 SearchBoxPrivate* private = GET_PRIVATE(object);
933 /* Button images */
934 GtkWidget* close =
935 gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
937 /* Searching */
938 private->search_entry = gtk_entry_new();
939 g_signal_connect_swapped (G_OBJECT (private->search_entry), "activate",
940 G_CALLBACK (search_box_forward_search),
941 object);
942 g_signal_connect (G_OBJECT (private->search_entry), "key-press-event",
943 G_CALLBACK (on_entry_key_pressed),
944 object);
945 g_signal_connect (G_OBJECT (private->search_entry), "changed",
946 G_CALLBACK (on_search_box_entry_changed),
947 object);
948 g_signal_connect (G_OBJECT (private->search_entry), "focus-out-event",
949 G_CALLBACK (on_search_focus_out),
950 object);
951 g_signal_connect (G_OBJECT (private->search_entry), "icon-press",
952 G_CALLBACK (on_search_entry_icon_pressed),
953 object);
954 g_signal_connect (G_OBJECT (private->search_entry), "popup-menu",
955 G_CALLBACK (on_search_entry_popup_menu),
956 object);
958 private->close_button = gtk_button_new();
959 gtk_button_set_image (GTK_BUTTON (private->close_button), close);
960 gtk_button_set_relief (GTK_BUTTON (private->close_button), GTK_RELIEF_NONE);
962 g_signal_connect (G_OBJECT (private->close_button), "clicked",
963 G_CALLBACK (on_search_box_hide), object);
965 /* Previous, Next Navigation */
966 private->next_button = gtk_button_new ();
967 gtk_container_add (GTK_CONTAINER (private->next_button),
968 gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD,
969 GTK_ICON_SIZE_BUTTON));
970 gtk_button_set_relief (GTK_BUTTON (private->next_button), GTK_RELIEF_NONE);
971 g_signal_connect_swapped (G_OBJECT(private->next_button), "clicked",
972 G_CALLBACK (search_box_forward_search), object);
973 private->previous_button = gtk_button_new ();
974 gtk_container_add (GTK_CONTAINER (private->previous_button),
975 gtk_image_new_from_stock (GTK_STOCK_GO_BACK,
976 GTK_ICON_SIZE_BUTTON));
977 gtk_button_set_relief (GTK_BUTTON (private->previous_button), GTK_RELIEF_NONE);
978 g_signal_connect (G_OBJECT(private->previous_button), "clicked",
979 G_CALLBACK (on_search_box_backward_search), object);
981 /* Goto line */
982 private->goto_entry = gtk_entry_new ();
983 gtk_entry_set_width_chars (GTK_ENTRY (private->goto_entry), LINE_ENTRY_WIDTH);
984 gtk_entry_set_icon_from_stock (GTK_ENTRY (private->goto_entry),
985 GTK_ENTRY_ICON_SECONDARY,
986 ANJUTA_STOCK_GOTO_LINE);
987 g_signal_connect (G_OBJECT (private->goto_entry), "activate",
988 G_CALLBACK (on_goto_activated),
989 object);
990 g_signal_connect (G_OBJECT (private->goto_entry), "key-press-event",
991 G_CALLBACK (on_goto_key_pressed),
992 object);
993 /* Replace */
994 private->replace_entry = gtk_entry_new();
995 g_signal_connect (G_OBJECT (private->replace_entry),
996 "key-press-event", G_CALLBACK (on_entry_key_pressed),
997 object);
998 g_signal_connect (G_OBJECT (private->replace_entry), "activate",
999 G_CALLBACK (on_replace_activated),
1000 object);
1002 private->replace_button = gtk_button_new_with_label(_("Replace"));
1003 gtk_button_set_relief (GTK_BUTTON (private->replace_button), GTK_RELIEF_NONE);
1004 g_signal_connect (G_OBJECT(private->replace_button), "clicked",
1005 G_CALLBACK (on_replace_activated), object);
1007 private->replace_all_button = gtk_button_new_with_label(_("Replace all"));
1008 gtk_button_set_relief (GTK_BUTTON (private->replace_all_button), GTK_RELIEF_NONE);
1009 g_signal_connect (G_OBJECT(private->replace_all_button), "clicked",
1010 G_CALLBACK (on_replace_all_activated), object);
1012 /* Popup Menu Options */
1013 private->regex_mode = FALSE;
1014 private->highlight_all = FALSE;
1015 private->case_sensitive = FALSE;
1016 private->highlight_complete = FALSE;
1018 /* Initialize search_box grid */
1019 private->grid = gtk_grid_new();
1020 gtk_orientable_set_orientation (GTK_ORIENTABLE (private->grid),
1021 GTK_ORIENTATION_VERTICAL);
1022 gtk_grid_set_row_spacing (GTK_GRID (private->grid), 5);
1024 /* Attach search elements to grid */
1025 gtk_grid_attach (GTK_GRID (private->grid), private->goto_entry, 0, 0, 1, 1);
1027 gtk_grid_attach (GTK_GRID (private->grid), private->search_entry, 1, 0, 1, 1);
1029 gtk_grid_attach (GTK_GRID (private->grid), private->previous_button, 2, 0, 1, 1);
1030 gtk_grid_attach (GTK_GRID (private->grid), private->next_button, 3, 0, 1, 1);
1032 gtk_grid_attach (GTK_GRID (private->grid), private->close_button, 4, 0, 1, 1);
1034 /* Add Replace elements to search box on 2nd level */
1035 gtk_grid_attach (GTK_GRID (private->grid), private->replace_entry, 1, 1, 1, 1);
1036 gtk_grid_attach (GTK_GRID (private->grid), private->replace_button, 2, 1, 1, 1);
1037 gtk_grid_attach (GTK_GRID (private->grid), private->replace_all_button, 3, 1, 1, 1);
1039 /* Expand search entries (a bit) */
1040 gtk_entry_set_width_chars (GTK_ENTRY (private->search_entry), SEARCH_ENTRY_WIDTH);
1041 gtk_entry_set_width_chars (GTK_ENTRY (private->replace_entry), SEARCH_ENTRY_WIDTH);
1043 /* Set nice icons */
1044 gtk_entry_set_icon_from_stock (GTK_ENTRY (private->search_entry),
1045 GTK_ENTRY_ICON_PRIMARY,
1046 GTK_STOCK_FIND);
1047 gtk_entry_set_icon_from_stock (GTK_ENTRY (private->replace_entry),
1048 GTK_ENTRY_ICON_PRIMARY,
1049 GTK_STOCK_FIND_AND_REPLACE);
1051 /* Pack grid into search box */
1052 gtk_box_pack_start (GTK_BOX(object), private->grid, TRUE, TRUE, 0);
1053 gtk_widget_show_all (GTK_WIDGET (object));
1057 static void
1058 search_box_finalize (GObject *object)
1061 G_OBJECT_CLASS (search_box_parent_class)->finalize (object);
1064 static void
1065 search_box_class_init (SearchBoxClass *klass)
1067 GObjectClass* object_class = G_OBJECT_CLASS (klass);
1069 g_type_class_add_private (klass, sizeof (SearchBoxPrivate));
1071 object_class->finalize = search_box_finalize;
1074 GtkWidget*
1075 search_box_new (AnjutaDocman *docman)
1077 GtkWidget* search_box;
1078 SearchBoxPrivate* private;
1079 AnjutaUI *ui;
1081 search_box = GTK_WIDGET (g_object_new (SEARCH_TYPE_BOX, "homogeneous",
1082 FALSE, NULL));
1084 g_signal_connect (G_OBJECT (docman), "document-changed",
1085 G_CALLBACK (on_document_changed), search_box);
1087 private = GET_PRIVATE (search_box);
1088 private->status = anjuta_shell_get_status (docman->shell, NULL);
1090 ui = anjuta_shell_get_ui (docman->shell, NULL);
1091 private->popup_menu = gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui),
1092 "/SearchboxPopup");
1093 g_assert (private->popup_menu != NULL && GTK_IS_MENU (private->popup_menu));
1095 g_signal_connect (private->popup_menu, "deactivate",
1096 G_CALLBACK (gtk_widget_hide), NULL);
1098 return search_box;
1101 void
1102 search_box_fill_search_focus (SearchBox* search_box, gboolean on_replace)
1105 SearchBoxPrivate* private = GET_PRIVATE(search_box);
1106 IAnjutaEditor* te = private->current_editor;
1108 if (IANJUTA_IS_EDITOR (te) && !private->regex_mode)
1110 gchar *buffer;
1112 buffer = ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te), NULL);
1113 if (buffer != NULL)
1115 g_strstrip (buffer);
1116 if (*buffer != 0)
1119 gtk_entry_set_text (GTK_ENTRY (private->search_entry), buffer);
1120 gtk_editable_select_region (GTK_EDITABLE (private->search_entry), 0, -1);
1123 g_free (buffer);
1127 /* Toggle replace level (replace entry, replace buttons) of search box */
1128 search_box_set_replace (search_box, on_replace);
1130 gtk_widget_grab_focus (private->search_entry);
1133 void
1134 search_box_grab_line_focus (SearchBox* search_box)
1136 SearchBoxPrivate* private = GET_PRIVATE(search_box);
1137 gtk_widget_grab_focus (private->goto_entry);
1140 void
1141 search_box_set_replace (SearchBox* object, gboolean replace)
1144 SearchBoxPrivate* private = GET_PRIVATE(object);
1146 if (replace)
1148 gtk_widget_show (private->replace_entry);
1149 gtk_widget_show (private->replace_button);
1150 gtk_widget_show (private->replace_all_button);
1152 else
1154 gtk_widget_hide (private->replace_entry);
1155 gtk_widget_hide (private->replace_button);
1156 gtk_widget_hide (private->replace_all_button);