1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) Johannes Schmid 2007 <jhs@gnome.org>
6 * anjuta is free software.
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)
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"
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 /* Time spend to do search in the idle callback */
47 #define CONTINUOUS_SEARCH_TIMEOUT 0.1
49 struct _SearchBoxPrivate
53 GtkWidget
* search_entry
;
54 GtkWidget
* replace_entry
;
56 GtkWidget
* close_button
;
57 GtkWidget
* next_button
;
58 GtkWidget
* previous_button
;
60 GtkWidget
* replace_button
;
61 GtkWidget
* replace_all_button
;
63 GtkWidget
* goto_entry
;
64 GtkWidget
* goto_button
;
66 IAnjutaEditor
* current_editor
;
70 /* Search options popup menu */
71 GtkWidget
* popup_menu
;
72 GtkAction
* case_action
;
73 GtkAction
* highlight_action
;
74 GtkAction
* regex_action
;
76 gboolean case_sensitive
;
77 gboolean highlight_all
;
80 IAnjutaEditorCell
*start_highlight
;
81 IAnjutaEditorCell
*end_highlight
;
84 GtkCssProvider
*provider
;
90 #define GET_PRIVATE(o) \
91 (G_TYPE_INSTANCE_GET_PRIVATE((o), SEARCH_TYPE_BOX, SearchBoxPrivate))
93 G_DEFINE_TYPE (SearchBox
, search_box
, GTK_TYPE_HBOX
);
96 on_search_box_hide (GtkWidget
* button
, SearchBox
* search_box
)
98 search_box_hide (search_box
);
102 on_document_changed (AnjutaDocman
* docman
, IAnjutaDocument
* doc
,
103 SearchBox
* search_box
)
105 if (!doc
|| !IANJUTA_IS_EDITOR (doc
))
107 gtk_widget_hide (GTK_WIDGET (search_box
));
108 search_box
->priv
->current_editor
= NULL
;
112 search_box
->priv
->current_editor
= IANJUTA_EDITOR (doc
);
113 if (search_box
->priv
->highlight_all
) search_box_highlight_all (search_box
);
118 on_goto_activated (GtkWidget
* widget
, SearchBox
* search_box
)
120 const gchar
* str_line
= gtk_entry_get_text (GTK_ENTRY (search_box
->priv
->goto_entry
));
122 gint line
= atoi (str_line
);
125 ianjuta_editor_goto_line (search_box
->priv
->current_editor
, line
, NULL
);
130 search_box_set_entry_color (SearchBox
* search_box
, gboolean found
)
132 GtkStyleContext
*context
;
134 context
= gtk_widget_get_style_context (GTK_WIDGET (search_box
->priv
->search_entry
));
137 gtk_style_context_add_class (context
, "not-found");
141 gtk_style_context_remove_class (context
, "not-found");
146 on_goto_key_pressed (GtkWidget
* entry
, GdkEventKey
* event
, SearchBox
* search_box
)
148 switch (event
->keyval
)
171 case GDK_KEY_KP_Enter
:
172 case GDK_KEY_BackSpace
:
176 /* This is a number or enter which is ok */
181 search_box_hide (search_box
);
194 on_search_box_key_pressed (GtkWidget
* widget
, GdkEventKey
* event
, SearchBox
* search_box
)
196 switch (event
->keyval
)
200 gtk_widget_hide (GTK_WIDGET (search_box
));
201 search_box_set_entry_color (search_box
, TRUE
);
202 if (search_box
->priv
->current_editor
)
204 ianjuta_document_grab_focus (IANJUTA_DOCUMENT (search_box
->priv
->current_editor
),
217 on_search_focus_out (GtkWidget
* widget
, GdkEvent
* event
, SearchBox
* search_box
)
219 anjuta_status_pop (search_box
->priv
->status
);
225 /* Search regular expression in text, return TRUE and matching part as start and
226 * end integer position */
228 search_regex_in_text (const gchar
* search_entry
, const gchar
* editor_text
, gboolean search_forward
, gint
* start_pos
, gint
* end_pos
)
231 GMatchInfo
*match_info
;
235 regex
= g_regex_new (search_entry
, 0, 0, &err
);
238 g_message ("%s",err
->message
);
240 g_regex_unref(regex
);
244 found
= g_regex_match (regex
, editor_text
, 0, &match_info
);
249 g_match_info_fetch_pos(match_info
, 0, start_pos
, end_pos
);
254 g_match_info_fetch_pos(match_info
, 0, start_pos
, end_pos
);
256 while (g_match_info_next(match_info
, NULL
));
259 *start_pos
= g_utf8_pointer_to_offset(editor_text
, &editor_text
[*start_pos
]);
260 *end_pos
= g_utf8_pointer_to_offset(editor_text
, &editor_text
[*end_pos
]);
264 g_regex_unref(regex
);
266 g_match_info_free (match_info
);
272 /* Search string in text, return TRUE and matching part as start and
273 * end integer position */
275 search_str_in_text (const gchar
* search_entry
, const gchar
* editor_text
, gboolean case_sensitive
, gint
* start_pos
, gint
* end_pos
)
279 if (strlen (editor_text
) >= strlen (search_entry
))
281 gchar
* selected_text_case
;
282 gchar
* search_text_case
;
286 selected_text_case
= g_strdup (editor_text
);
287 search_text_case
= g_strdup (search_entry
);
291 selected_text_case
= g_utf8_casefold (editor_text
, strlen (editor_text
));
292 search_text_case
= g_utf8_casefold (search_entry
, strlen (search_entry
));
295 gchar
* strstr
= g_strstr_len (selected_text_case
, -1, search_text_case
);
299 *start_pos
= g_utf8_pointer_to_offset(selected_text_case
, strstr
);
300 *end_pos
= g_utf8_pointer_to_offset(selected_text_case
, strstr
+ strlen (search_entry
));
304 g_free (selected_text_case
);
305 g_free (search_text_case
);
311 /* Search string in editor, return TRUE and matching part as start and
314 editor_search (IAnjutaEditor
*editor
,
315 const gchar
*search_text
,
316 gboolean case_sensitive
,
317 gboolean search_forward
,
319 IAnjutaEditorCell
* search_start
,
320 IAnjutaEditorCell
* search_end
,
321 IAnjutaEditorCell
** result_start
,
322 IAnjutaEditorCell
** result_end
)
330 gchar
*text_to_search
;
332 text_to_search
= ianjuta_editor_get_text (editor
,
333 IANJUTA_ITERABLE (search_start
),
334 IANJUTA_ITERABLE (search_end
), NULL
);
336 found
= search_regex_in_text (search_text
, text_to_search
, search_forward
, &start_pos
, &end_pos
);
338 start_pos
+= ianjuta_iterable_get_position(IANJUTA_ITERABLE (search_start
), NULL
);
339 end_pos
+= ianjuta_iterable_get_position(IANJUTA_ITERABLE (search_start
), NULL
);
341 if (found
&& start_pos
>= 0)
343 *result_start
= IANJUTA_EDITOR_CELL (ianjuta_editor_get_start_position (editor
,
345 *result_end
= IANJUTA_EDITOR_CELL (ianjuta_editor_get_start_position (editor
,
348 if (!ianjuta_iterable_set_position(IANJUTA_ITERABLE(*result_start
), start_pos
, NULL
) ||
349 !ianjuta_iterable_set_position(IANJUTA_ITERABLE(*result_end
), end_pos
, NULL
))
351 g_object_unref(*result_start
);
352 g_object_unref(*result_end
);
357 g_free(text_to_search
);
363 found
= ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (editor
),
364 search_text
, case_sensitive
,
365 search_start
, search_end
,
371 found
= ianjuta_editor_search_backward (IANJUTA_EDITOR_SEARCH (editor
),
372 search_text
, case_sensitive
,
373 search_end
, search_start
,
383 search_box_incremental_search (SearchBox
* search_box
,
384 gboolean search_forward
,
385 gboolean search_next
,
388 IAnjutaIterable
* real_start
;
389 IAnjutaEditorCell
* search_start
;
390 IAnjutaEditorCell
* search_end
;
391 IAnjutaEditorCell
* result_start
;
392 IAnjutaEditorCell
* result_end
;
393 IAnjutaEditorSelection
* selection
;
395 const gchar
* search_text
= gtk_entry_get_text (GTK_ENTRY (search_box
->priv
->search_entry
));
397 gboolean found
= FALSE
;
399 if (!search_box
->priv
->current_editor
|| !search_text
|| !strlen (search_text
))
402 selection
= IANJUTA_EDITOR_SELECTION (search_box
->priv
->current_editor
);
404 if (ianjuta_editor_selection_has_selection (selection
, NULL
))
407 IANJUTA_EDITOR_CELL (ianjuta_editor_selection_get_start (selection
, NULL
));
412 IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (search_box
->priv
->current_editor
,
417 ianjuta_iterable_clone (IANJUTA_ITERABLE (search_start
), NULL
);
419 /* If forward, set search start and end to current position of
420 * cursor and editor end, respectively, or if backward to editor
421 * start and current position of cursor, respectively. Current
422 * position of cursor is selection start if have selection. */
425 search_end
= IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (search_box
->priv
->current_editor
,
427 ianjuta_iterable_last (IANJUTA_ITERABLE (search_end
), NULL
);
431 search_end
= search_start
;
432 search_start
= IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (search_box
->priv
->current_editor
,
434 ianjuta_iterable_first (IANJUTA_ITERABLE (search_start
), NULL
);
437 /* When there's a selection, if forward, set search start and end
438 * to match end and editor end, respectively, or if backward to
439 * editor start and match start, respectively. If forward and
440 * selection starts with a match, look for next match.
442 if (ianjuta_editor_selection_has_selection (selection
,
443 NULL
) && search_next
)
445 gchar
* selected_text
=
446 ianjuta_editor_selection_get (selection
, NULL
);
448 gint start_pos
, end_pos
;
449 gboolean selected_have_search_text
= FALSE
;
451 selected_have_search_text
= search_box
->priv
->regex_mode
?
452 search_regex_in_text (search_text
, selected_text
, TRUE
, &start_pos
, &end_pos
) :
453 search_str_in_text (search_text
, selected_text
, search_box
->priv
->case_sensitive
, &start_pos
, &end_pos
);
455 if (selected_have_search_text
)
457 IAnjutaIterable
* selection_start
=
458 ianjuta_editor_selection_get_start (selection
, NULL
);
460 if (search_forward
&& start_pos
== 0)
462 end_pos
+= ianjuta_iterable_get_position(IANJUTA_ITERABLE (selection_start
), NULL
);
463 ianjuta_iterable_set_position (IANJUTA_ITERABLE(search_start
), end_pos
, NULL
);
464 ianjuta_iterable_last (IANJUTA_ITERABLE (search_end
), NULL
);
466 else if (!search_forward
)
468 start_pos
+= ianjuta_iterable_get_position(IANJUTA_ITERABLE (selection_start
), NULL
);
469 ianjuta_iterable_set_position (IANJUTA_ITERABLE(search_end
), start_pos
, NULL
);
470 ianjuta_iterable_first (IANJUTA_ITERABLE (search_start
), NULL
);
472 g_object_unref (selection_start
);
475 g_free (selected_text
);
478 /* Try searching in current position */
479 found
= editor_search (search_box
->priv
->current_editor
,
481 search_box
->priv
->case_sensitive
,
483 search_box
->priv
->regex_mode
,
491 anjuta_status_pop (ANJUTA_STATUS (search_box
->priv
->status
));
495 /* Try to wrap if not found */
496 ianjuta_iterable_first (IANJUTA_ITERABLE (search_start
), NULL
);
497 ianjuta_iterable_last (IANJUTA_ITERABLE (search_end
), NULL
);
499 /* Try to search again */
500 found
= editor_search (search_box
->priv
->current_editor
,
502 search_box
->priv
->case_sensitive
,
504 search_box
->priv
->regex_mode
,
510 /* Check if successful */
513 if (ianjuta_iterable_compare (IANJUTA_ITERABLE (result_start
),
514 real_start
, NULL
) != 0)
516 anjuta_status_pop (search_box
->priv
->status
);
519 anjuta_status_push (search_box
->priv
->status
,
520 _("Search for \"%s\" reached the end and was continued at the top."), search_text
);
524 anjuta_status_push (search_box
->priv
->status
,
525 _("Search for \"%s\" reached top and was continued at the bottom."), search_text
);
528 else if (ianjuta_editor_selection_has_selection (selection
, NULL
))
531 anjuta_status_pop (search_box
->priv
->status
);
534 anjuta_status_push (search_box
->priv
->status
,
535 _("Search for \"%s\" reached the end and was continued at the top but no new match was found."), search_text
);
539 anjuta_status_push (search_box
->priv
->status
,
540 _("Search for \"%s\" reached top and was continued at the bottom but no new match was found."), search_text
);
552 ianjuta_editor_selection_set (selection
,
553 IANJUTA_ITERABLE (result_start
),
554 IANJUTA_ITERABLE (result_end
), TRUE
, NULL
);
555 g_object_unref (result_start
);
556 g_object_unref (result_end
);
560 if(ianjuta_editor_selection_get (selection
, NULL
)!=NULL
)
562 IAnjutaIterable
* selection_start
=
563 ianjuta_editor_selection_get_start (selection
, NULL
);
564 ianjuta_editor_selection_set (selection
,
565 IANJUTA_ITERABLE (selection_start
),
566 IANJUTA_ITERABLE (selection_start
), TRUE
, NULL
);
567 g_object_unref (selection_start
);
572 search_box_set_entry_color (search_box
, found
);
573 g_object_unref (real_start
);
574 g_object_unref (search_start
);
575 g_object_unref (search_end
);
581 highlight_in_background (SearchBox
*search_box
)
583 gboolean found
= FALSE
;
586 if (search_box
->priv
->start_highlight
!= NULL
)
588 const gchar
* search_text
= gtk_entry_get_text (GTK_ENTRY (search_box
->priv
->search_entry
));
589 GTimer
*timer
= g_timer_new();
591 if (*search_text
!= '\0')
595 IAnjutaEditorCell
* result_start
;
596 IAnjutaEditorCell
* result_end
;
598 found
= editor_search (search_box
->priv
->current_editor
,
600 search_box
->priv
->case_sensitive
,
602 search_box
->priv
->regex_mode
,
603 search_box
->priv
->start_highlight
,
604 search_box
->priv
->end_highlight
,
609 ianjuta_indicable_set(IANJUTA_INDICABLE(search_box
->priv
->current_editor
),
610 IANJUTA_ITERABLE (result_start
),
611 IANJUTA_ITERABLE (result_end
),
612 IANJUTA_INDICABLE_IMPORTANT
, NULL
);
613 g_object_unref (result_start
);
614 g_object_unref (search_box
->priv
->start_highlight
);
615 search_box
->priv
->start_highlight
= result_end
;
618 while (found
&& g_timer_elapsed(timer
, NULL
) < CONTINUOUS_SEARCH_TIMEOUT
);
620 g_timer_destroy (timer
);
625 search_box
->priv
->idle_id
= 0;
626 g_clear_object (&search_box
->priv
->start_highlight
);
627 g_clear_object (&search_box
->priv
->end_highlight
);
634 search_box_highlight_all (SearchBox
*search_box
)
636 if (!search_box
->priv
->current_editor
)
639 ianjuta_indicable_clear(IANJUTA_INDICABLE(search_box
->priv
->current_editor
), NULL
);
640 if (search_box
->priv
->start_highlight
!= NULL
) g_object_unref (search_box
->priv
->start_highlight
);
641 if (search_box
->priv
->end_highlight
!= NULL
) g_object_unref (search_box
->priv
->end_highlight
);
642 search_box
->priv
->start_highlight
= IANJUTA_EDITOR_CELL (ianjuta_editor_get_start_position (search_box
->priv
->current_editor
, NULL
));
643 search_box
->priv
->end_highlight
= IANJUTA_EDITOR_CELL (ianjuta_editor_get_end_position (search_box
->priv
->current_editor
, NULL
));
645 if (search_box
->priv
->idle_id
== 0)
647 search_box
->priv
->idle_id
= g_idle_add_full(G_PRIORITY_DEFAULT_IDLE
,
648 (GSourceFunc
)highlight_in_background
,
655 search_box_clear_highlight (SearchBox
* search_box
)
657 if (!search_box
->priv
->current_editor
)
660 ianjuta_indicable_clear(IANJUTA_INDICABLE(search_box
->priv
->current_editor
), NULL
);
664 search_box_toggle_highlight (SearchBox
* search_box
, gboolean status
)
666 if (!search_box
->priv
->current_editor
)
669 search_box
->priv
->highlight_all
= status
;
670 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(search_box
->priv
->highlight_action
),
675 ianjuta_indicable_clear(IANJUTA_INDICABLE(search_box
->priv
->current_editor
), NULL
);
676 g_clear_object (&search_box
->priv
->start_highlight
);
677 g_clear_object (&search_box
->priv
->end_highlight
);
681 search_box_highlight_all (search_box
);
686 search_box_toggle_case_sensitive (SearchBox
* search_box
, gboolean status
)
688 if (!search_box
->priv
->current_editor
)
690 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(search_box
->priv
->case_action
),
693 search_box
->priv
->case_sensitive
= status
;
694 if (search_box
->priv
->highlight_all
) search_box_highlight_all (search_box
);
698 search_box_toggle_regex (SearchBox
* search_box
, gboolean status
)
700 if (!search_box
->priv
->current_editor
)
703 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(search_box
->priv
->regex_action
),
706 search_box
->priv
->regex_mode
= status
;
707 if (search_box
->priv
->highlight_all
) search_box_highlight_all (search_box
);
711 on_search_box_entry_changed (GtkWidget
* widget
, SearchBox
* search_box
)
713 if (!search_box
->priv
->regex_mode
)
715 GtkEntryBuffer
* buffer
= gtk_entry_get_buffer (GTK_ENTRY(widget
));
716 if (gtk_entry_buffer_get_length (buffer
))
717 search_box_incremental_search (search_box
, TRUE
, FALSE
, TRUE
);
720 /* clear selection */
721 IAnjutaIterable
* cursor
=
722 ianjuta_editor_get_position (IANJUTA_EDITOR (search_box
->priv
->current_editor
),
724 ianjuta_editor_selection_set (IANJUTA_EDITOR_SELECTION (search_box
->priv
->current_editor
),
731 if (search_box
->priv
->highlight_all
) search_box_highlight_all (search_box
);
735 search_box_forward_search (SearchBox
* search_box
, GtkWidget
* widget
)
737 search_box_incremental_search (search_box
, TRUE
, TRUE
, TRUE
);
741 on_search_box_backward_search (GtkWidget
* widget
, SearchBox
* search_box
)
743 search_box_incremental_search (search_box
, FALSE
, TRUE
, TRUE
);
747 search_box_replace (SearchBox
* search_box
, GtkWidget
* widget
,
748 gboolean undo
/* treat as undo action */)
751 IAnjutaEditorSelection
* selection
;
752 gchar
* selection_text
;
753 gboolean replace_successful
= FALSE
;
755 const gchar
* replace_text
= gtk_entry_get_text (GTK_ENTRY (search_box
->priv
->replace_entry
));
756 const gchar
* search_text
= gtk_entry_get_text (GTK_ENTRY (search_box
->priv
->search_entry
));
758 selection
= IANJUTA_EDITOR_SELECTION (search_box
->priv
->current_editor
);
759 selection_text
= ianjuta_editor_selection_get (selection
, NULL
);
761 if (ianjuta_editor_selection_has_selection (selection
, NULL
))
763 if (search_box
->priv
->regex_mode
)
766 gchar
* replacement_text
;
767 gint start_pos
, end_pos
;
769 gboolean result
= search_regex_in_text (search_text
, selection_text
, TRUE
, &start_pos
, &end_pos
);
773 regex
= g_regex_new (search_text
, 0, 0, NULL
);
774 replacement_text
= g_regex_replace(regex
, selection_text
, strlen(selection_text
), 0,
775 replace_text
, 0, &err
);
778 g_message ("%s",err
->message
);
780 g_regex_unref(regex
);
785 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT (selection
), NULL
);
786 ianjuta_editor_selection_replace (selection
, replacement_text
, strlen(replacement_text
), NULL
);
788 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT (selection
), NULL
);
790 replace_successful
= TRUE
;
794 g_regex_unref(regex
);
795 if (replacement_text
)
796 g_free(replacement_text
);
799 else if ((search_box
->priv
->case_sensitive
&& g_str_equal (selection_text
, search_text
)) ||
800 (!search_box
->priv
->case_sensitive
&& strcasecmp (selection_text
, search_text
) == 0))
803 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT (selection
), NULL
);
804 ianjuta_editor_selection_replace (selection
, replace_text
, strlen(replace_text
), NULL
);
806 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT (selection
), NULL
);
808 replace_successful
= TRUE
;
811 g_free(selection_text
);
814 return replace_successful
;
819 on_replace_activated (GtkWidget
* widget
, SearchBox
* search_box
)
821 gboolean successful_replace
;
823 if (!search_box
->priv
->current_editor
)
826 /* Either replace search-term or try to move search forward to next occurence */
828 successful_replace
= search_box_replace (search_box
, widget
, TRUE
);
830 if (successful_replace
)
832 search_box_forward_search (search_box
, widget
);
837 do_popup_menu (GtkWidget
* widget
, GdkEventButton
*event
, SearchBox
* search_box
)
839 int button
, event_time
;
843 button
= event
->button
;
844 event_time
= event
->time
;
849 event_time
= gtk_get_current_event_time ();
852 if (!gtk_menu_get_attach_widget(GTK_MENU (search_box
->priv
->popup_menu
)))
853 gtk_menu_attach_to_widget (GTK_MENU (search_box
->priv
->popup_menu
), widget
, NULL
);
855 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(search_box
->priv
->case_action
), search_box
->priv
->case_sensitive
);
856 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(search_box
->priv
->regex_action
), search_box
->priv
->regex_mode
);
857 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(search_box
->priv
->highlight_action
), search_box
->priv
->highlight_all
);
859 gtk_menu_popup (GTK_MENU (search_box
->priv
->popup_menu
), NULL
, NULL
, NULL
, NULL
,
865 on_search_entry_icon_pressed (GtkWidget
* widget
, GtkEntryIconPosition pos
,
866 GdkEvent
* event
, SearchBox
* search_box
)
868 do_popup_menu (widget
, (GdkEventButton
*)event
, search_box
);
873 on_search_entry_popup_menu (GtkWidget
* widget
, SearchBox
* search_box
)
875 do_popup_menu (widget
, NULL
, search_box
);
880 on_replace_all_activated (GtkWidget
* widget
, SearchBox
* search_box
)
882 IAnjutaIterable
* cursor
;
884 if (!search_box
->priv
->current_editor
)
887 /* Cache current position and search from begin */
888 cursor
= ianjuta_editor_get_position (IANJUTA_EDITOR (search_box
->priv
->current_editor
),
890 ianjuta_editor_goto_start (IANJUTA_EDITOR (search_box
->priv
->current_editor
), NULL
);
892 /* Replace all instances of search_entry with replace_entry text */
893 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT (search_box
->priv
->current_editor
), NULL
);
894 while (search_box_incremental_search (search_box
, TRUE
, TRUE
, FALSE
))
896 search_box_replace (search_box
, widget
, FALSE
);
898 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT (search_box
->priv
->current_editor
), NULL
);
900 /* Back to cached position */
901 ianjuta_editor_selection_set (IANJUTA_EDITOR_SELECTION (search_box
->priv
->current_editor
),
902 cursor
, cursor
, TRUE
, NULL
);
903 g_object_unref (cursor
);
907 search_box_init (SearchBox
*search_box
)
909 search_box
->priv
= GET_PRIVATE(search_box
);
910 GList
* focus_chain
= NULL
;
911 GtkStyleContext
*context
;
915 gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_MENU
);
918 search_box
->priv
->search_entry
= gtk_entry_new();
919 gtk_widget_set_tooltip_text (search_box
->priv
->search_entry
,
920 _("Use the context menu of the \"Find\" icon for more search options"));
921 g_signal_connect_swapped (G_OBJECT (search_box
->priv
->search_entry
), "activate",
922 G_CALLBACK (search_box_forward_search
),
924 g_signal_connect (G_OBJECT (search_box
), "key-press-event",
925 G_CALLBACK (on_search_box_key_pressed
),
927 g_signal_connect (G_OBJECT (search_box
->priv
->search_entry
), "changed",
928 G_CALLBACK (on_search_box_entry_changed
),
930 g_signal_connect (G_OBJECT (search_box
->priv
->search_entry
), "focus-out-event",
931 G_CALLBACK (on_search_focus_out
),
933 g_signal_connect (G_OBJECT (search_box
->priv
->search_entry
), "icon-press",
934 G_CALLBACK (on_search_entry_icon_pressed
),
936 g_signal_connect (G_OBJECT (search_box
->priv
->search_entry
), "popup-menu",
937 G_CALLBACK (on_search_entry_popup_menu
),
940 search_box
->priv
->close_button
= gtk_button_new();
941 gtk_button_set_image (GTK_BUTTON (search_box
->priv
->close_button
), close
);
942 gtk_button_set_relief (GTK_BUTTON (search_box
->priv
->close_button
), GTK_RELIEF_NONE
);
944 g_signal_connect (G_OBJECT (search_box
->priv
->close_button
), "clicked",
945 G_CALLBACK (on_search_box_hide
), search_box
);
947 /* CSS custom style */
948 search_box
->priv
->provider
= gtk_css_provider_new ();
949 gtk_css_provider_load_from_data (search_box
->priv
->provider
,
950 ".not-found {color: @gedit_not_found_fg; background-image: none; background-color: @gedit_not_found_bg;}"
951 ".not-found:selected {color: @theme_selected_fg_color; background-color: @theme_selected_bg_color;}"
953 context
= gtk_widget_get_style_context (GTK_WIDGET (search_box
->priv
->search_entry
));
954 gtk_style_context_add_provider (context
, GTK_STYLE_PROVIDER (search_box
->priv
->provider
), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
957 /* Previous, Next Navigation */
958 search_box
->priv
->next_button
= gtk_button_new ();
959 gtk_container_add (GTK_CONTAINER (search_box
->priv
->next_button
),
960 gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD
,
961 GTK_ICON_SIZE_BUTTON
));
962 gtk_button_set_relief (GTK_BUTTON (search_box
->priv
->next_button
), GTK_RELIEF_NONE
);
963 g_signal_connect_swapped (G_OBJECT(search_box
->priv
->next_button
), "clicked",
964 G_CALLBACK (search_box_forward_search
), search_box
);
965 search_box
->priv
->previous_button
= gtk_button_new ();
966 gtk_container_add (GTK_CONTAINER (search_box
->priv
->previous_button
),
967 gtk_image_new_from_stock (GTK_STOCK_GO_BACK
,
968 GTK_ICON_SIZE_BUTTON
));
969 gtk_button_set_relief (GTK_BUTTON (search_box
->priv
->previous_button
), GTK_RELIEF_NONE
);
970 g_signal_connect (G_OBJECT(search_box
->priv
->previous_button
), "clicked",
971 G_CALLBACK (on_search_box_backward_search
), search_box
);
974 search_box
->priv
->goto_entry
= gtk_entry_new ();
975 gtk_entry_set_width_chars (GTK_ENTRY (search_box
->priv
->goto_entry
), LINE_ENTRY_WIDTH
);
976 gtk_entry_set_icon_from_stock (GTK_ENTRY (search_box
->priv
->goto_entry
),
977 GTK_ENTRY_ICON_SECONDARY
,
978 ANJUTA_STOCK_GOTO_LINE
);
979 g_signal_connect (G_OBJECT (search_box
->priv
->goto_entry
), "activate",
980 G_CALLBACK (on_goto_activated
),
982 g_signal_connect (G_OBJECT (search_box
->priv
->goto_entry
), "key-press-event",
983 G_CALLBACK (on_goto_key_pressed
),
986 search_box
->priv
->replace_entry
= gtk_entry_new();
987 g_signal_connect (G_OBJECT (search_box
->priv
->replace_entry
), "activate",
988 G_CALLBACK (on_replace_activated
),
991 search_box
->priv
->replace_button
= gtk_button_new_with_label(_("Replace"));
992 gtk_button_set_relief (GTK_BUTTON (search_box
->priv
->replace_button
), GTK_RELIEF_NONE
);
993 g_signal_connect (G_OBJECT(search_box
->priv
->replace_button
), "clicked",
994 G_CALLBACK (on_replace_activated
), search_box
);
996 search_box
->priv
->replace_all_button
= gtk_button_new_with_label(_("Replace all"));
997 gtk_button_set_relief (GTK_BUTTON (search_box
->priv
->replace_all_button
), GTK_RELIEF_NONE
);
998 g_signal_connect (G_OBJECT(search_box
->priv
->replace_all_button
), "clicked",
999 G_CALLBACK (on_replace_all_activated
), search_box
);
1001 /* Popup Menu Options */
1002 search_box
->priv
->regex_mode
= FALSE
;
1003 search_box
->priv
->highlight_all
= FALSE
;
1004 search_box
->priv
->case_sensitive
= FALSE
;
1006 /* Highlight iterator */
1007 search_box
->priv
->start_highlight
= NULL
;
1008 search_box
->priv
->end_highlight
= NULL
;
1009 search_box
->priv
->idle_id
= 0;
1011 /* Initialize search_box grid */
1012 search_box
->priv
->grid
= gtk_grid_new();
1013 gtk_orientable_set_orientation (GTK_ORIENTABLE (search_box
->priv
->grid
),
1014 GTK_ORIENTATION_VERTICAL
);
1015 gtk_grid_set_row_spacing (GTK_GRID (search_box
->priv
->grid
), 5);
1017 /* Attach search elements to grid */
1018 gtk_grid_attach (GTK_GRID (search_box
->priv
->grid
), search_box
->priv
->goto_entry
, 0, 0, 1, 1);
1020 gtk_grid_attach (GTK_GRID (search_box
->priv
->grid
), search_box
->priv
->search_entry
, 1, 0, 1, 1);
1022 gtk_grid_attach (GTK_GRID (search_box
->priv
->grid
), search_box
->priv
->previous_button
, 2, 0, 1, 1);
1023 gtk_grid_attach (GTK_GRID (search_box
->priv
->grid
), search_box
->priv
->next_button
, 3, 0, 1, 1);
1025 gtk_grid_attach_next_to (GTK_GRID (search_box
->priv
->grid
),
1026 search_box
->priv
->close_button
,
1027 search_box
->priv
->next_button
,
1028 GTK_POS_RIGHT
, 1, 1);
1029 gtk_widget_set_hexpand(search_box
->priv
->close_button
, TRUE
);
1030 gtk_widget_set_halign(search_box
->priv
->close_button
,
1033 /* Add Replace elements to search box on 2nd level */
1034 gtk_grid_attach (GTK_GRID (search_box
->priv
->grid
), search_box
->priv
->replace_entry
, 1, 1, 1, 1);
1035 gtk_grid_attach (GTK_GRID (search_box
->priv
->grid
), search_box
->priv
->replace_button
, 2, 1, 1, 1);
1036 gtk_grid_attach (GTK_GRID (search_box
->priv
->grid
), search_box
->priv
->replace_all_button
, 3, 1, 1, 1);
1038 /* Expand search entries (a bit) */
1039 gtk_entry_set_width_chars (GTK_ENTRY (search_box
->priv
->search_entry
), SEARCH_ENTRY_WIDTH
);
1040 gtk_entry_set_width_chars (GTK_ENTRY (search_box
->priv
->replace_entry
), SEARCH_ENTRY_WIDTH
);
1042 /* Set nice icons */
1043 gtk_entry_set_icon_from_stock (GTK_ENTRY (search_box
->priv
->search_entry
),
1044 GTK_ENTRY_ICON_PRIMARY
,
1046 gtk_entry_set_icon_from_stock (GTK_ENTRY (search_box
->priv
->replace_entry
),
1047 GTK_ENTRY_ICON_PRIMARY
,
1048 GTK_STOCK_FIND_AND_REPLACE
);
1050 /* Pack grid into search box */
1051 gtk_box_pack_start (GTK_BOX(search_box
), search_box
->priv
->grid
, TRUE
, TRUE
, 0);
1053 /* Set focus chain */
1054 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->search_entry
);
1055 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->replace_entry
);
1056 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->next_button
);
1057 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->previous_button
);
1058 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->replace_button
);
1059 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->replace_all_button
);
1060 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->goto_entry
);
1061 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->close_button
);
1062 focus_chain
= g_list_prepend (focus_chain
, search_box
->priv
->search_entry
);
1063 focus_chain
= g_list_reverse (focus_chain
);
1064 gtk_container_set_focus_chain (GTK_CONTAINER (search_box
->priv
->grid
),
1066 g_list_free (focus_chain
);
1068 /* Show all children but keep the top box hidden. */
1069 gtk_widget_show_all (GTK_WIDGET (search_box
));
1070 gtk_widget_hide (GTK_WIDGET (search_box
));
1074 search_box_finalize (GObject
*object
)
1076 SearchBox
*search_box
= SEARCH_BOX (object
);
1078 if (search_box
->priv
->idle_id
) g_source_remove (search_box
->priv
->idle_id
);
1079 if (search_box
->priv
->start_highlight
) g_object_unref (search_box
->priv
->start_highlight
);
1080 if (search_box
->priv
->end_highlight
) g_object_unref (search_box
->priv
->end_highlight
);
1081 if (search_box
->priv
->provider
) g_object_unref (search_box
->priv
->provider
);
1083 G_OBJECT_CLASS (search_box_parent_class
)->finalize (object
);
1087 search_box_class_init (SearchBoxClass
*klass
)
1089 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
1091 g_type_class_add_private (klass
, sizeof (SearchBoxPrivate
));
1093 object_class
->finalize
= search_box_finalize
;
1097 search_box_new (AnjutaDocman
*docman
)
1099 SearchBox
* search_box
;
1102 search_box
= SEARCH_BOX (g_object_new (SEARCH_TYPE_BOX
, "homogeneous",
1105 g_signal_connect (G_OBJECT (docman
), "document-changed",
1106 G_CALLBACK (on_document_changed
), search_box
);
1108 search_box
->priv
->status
= anjuta_shell_get_status (docman
->shell
, NULL
);
1110 ui
= anjuta_shell_get_ui (docman
->shell
, NULL
);
1111 search_box
->priv
->popup_menu
= gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui
),
1113 g_assert (search_box
->priv
->popup_menu
!= NULL
&& GTK_IS_MENU (search_box
->priv
->popup_menu
));
1115 search_box
->priv
->case_action
=
1116 gtk_ui_manager_get_action (GTK_UI_MANAGER (ui
),
1117 "/SearchboxPopup/CaseCheck");
1119 search_box
->priv
->highlight_action
=
1120 gtk_ui_manager_get_action (GTK_UI_MANAGER (ui
),
1121 "/SearchboxPopup/HighlightAll");
1122 search_box
->priv
->regex_action
=
1123 gtk_ui_manager_get_action (GTK_UI_MANAGER (ui
),
1124 "/SearchboxPopup/RegexSearch");
1126 g_signal_connect (search_box
->priv
->popup_menu
, "deactivate",
1127 G_CALLBACK (gtk_widget_hide
), NULL
);
1129 return GTK_WIDGET (search_box
);
1133 search_box_fill_search_focus (SearchBox
* search_box
, gboolean on_replace
)
1135 IAnjutaEditor
* te
= search_box
->priv
->current_editor
;
1137 if (IANJUTA_IS_EDITOR (te
) && !search_box
->priv
->regex_mode
)
1141 buffer
= ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te
), NULL
);
1144 g_strstrip (buffer
);
1148 gtk_entry_set_text (GTK_ENTRY (search_box
->priv
->search_entry
), buffer
);
1149 gtk_editable_select_region (GTK_EDITABLE (search_box
->priv
->search_entry
), 0, -1);
1156 /* Toggle replace level (replace entry, replace buttons) of search box */
1157 search_box_set_replace (search_box
, on_replace
);
1159 gtk_widget_grab_focus (search_box
->priv
->search_entry
);
1163 search_box_grab_line_focus (SearchBox
* search_box
)
1165 gtk_widget_grab_focus (search_box
->priv
->goto_entry
);
1169 search_box_hide (SearchBox
* search_box
)
1171 gtk_widget_hide (GTK_WIDGET (search_box
));
1172 search_box_set_entry_color (search_box
, TRUE
);
1173 if (search_box
->priv
->current_editor
)
1175 ianjuta_document_grab_focus (IANJUTA_DOCUMENT (search_box
->priv
->current_editor
),
1181 search_box_set_replace (SearchBox
* search_box
, gboolean replace
)
1185 gtk_widget_show (search_box
->priv
->replace_entry
);
1186 gtk_widget_show (search_box
->priv
->replace_button
);
1187 gtk_widget_show (search_box
->priv
->replace_all_button
);
1191 gtk_widget_hide (search_box
->priv
->replace_entry
);
1192 gtk_widget_hide (search_box
->priv
->replace_button
);
1193 gtk_widget_hide (search_box
->priv
->replace_all_button
);
1197 const gchar
* search_box_get_search_string (SearchBox
* search_box
)
1199 g_return_val_if_fail (search_box
!= NULL
&& SEARCH_IS_BOX(search_box
), NULL
);
1201 return gtk_entry_get_text (GTK_ENTRY (search_box
->priv
->search_entry
));
1204 void search_box_set_search_string (SearchBox
* search_box
, const gchar
* search
)
1206 g_return_if_fail (search_box
!= NULL
&& SEARCH_IS_BOX(search_box
));
1208 gtk_entry_set_text (GTK_ENTRY (search_box
->priv
->search_entry
), search
);
1211 const gchar
* search_box_get_replace_string (SearchBox
* search_box
)
1213 g_return_val_if_fail (search_box
!= NULL
&& SEARCH_IS_BOX(search_box
), NULL
);
1215 return gtk_entry_get_text (GTK_ENTRY (search_box
->priv
->replace_entry
));
1219 void search_box_set_replace_string (SearchBox
* search_box
, const gchar
* replace
)
1221 g_return_if_fail (search_box
!= NULL
&& SEARCH_IS_BOX(search_box
));
1223 gtk_entry_set_text (GTK_ENTRY (search_box
->priv
->replace_entry
), replace
);
1227 search_box_session_load (SearchBox
* search_box
, AnjutaSession
* session
)
1229 g_return_if_fail (search_box
!= NULL
&& SEARCH_IS_BOX(search_box
));
1231 search_box
->priv
->case_sensitive
= anjuta_session_get_int (session
, "Search Box", "Case Sensitive") ? TRUE
: FALSE
;
1232 search_box
->priv
->regex_mode
= anjuta_session_get_int (session
, "Search Box", "Regular Expression") ? TRUE
: FALSE
;
1233 search_box
->priv
->highlight_all
= anjuta_session_get_int (session
, "Search Box", "Highlight Match") ? TRUE
: FALSE
;
1237 search_box_session_save (SearchBox
* search_box
, AnjutaSession
* session
)
1239 g_return_if_fail (search_box
!= NULL
&& SEARCH_IS_BOX(search_box
));
1241 anjuta_session_set_int (session
, "Search Box", "Case Sensitive", search_box
->priv
->case_sensitive
? 1 : 0);
1242 anjuta_session_set_int (session
, "Search Box", "Regular Expression", search_box
->priv
->regex_mode
? 1 : 0);
1243 anjuta_session_set_int (session
, "Search Box", "Highlight Match", search_box
->priv
->highlight_all
? 1 : 0);