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"
28 #include <gtk/gtkbutton.h>
29 #include <gtk/gtkcheckbutton.h>
30 #include <gtk/gtkimage.h>
31 #include <gtk/gtkentry.h>
33 #include <libanjuta/anjuta-shell.h>
34 #include <libanjuta/anjuta-status.h>
35 #include <libanjuta/anjuta-debug.h>
37 #include <libanjuta/interfaces/ianjuta-editor.h>
38 #include <libanjuta/interfaces/ianjuta-editor-search.h>
39 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
41 #define ANJUTA_STOCK_GOTO_LINE "anjuta-goto-line"
43 typedef struct _SearchBoxPrivate SearchBoxPrivate
;
45 struct _SearchBoxPrivate
47 GtkWidget
* search_entry
;
48 GtkWidget
* case_check
;
49 GtkWidget
* search_button
;
50 GtkWidget
* close_button
;
52 GtkWidget
* goto_entry
;
53 GtkWidget
* goto_button
;
55 IAnjutaEditor
* current_editor
;
58 /* Incremental search */
59 IAnjutaIterable
* last_start
;
65 #define GET_PRIVATE(o) \
66 (G_TYPE_INSTANCE_GET_PRIVATE((o), SEARCH_TYPE_BOX, SearchBoxPrivate))
68 G_DEFINE_TYPE (SearchBox
, search_box
, GTK_TYPE_HBOX
);
71 on_search_box_hide (GtkWidget
* button
, SearchBox
* search_box
)
73 gtk_widget_hide (GTK_WIDGET (search_box
));
77 on_document_changed (AnjutaDocman
* docman
, IAnjutaDocument
* doc
,
78 SearchBox
* search_box
)
80 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
82 if (!doc
|| !IANJUTA_IS_EDITOR (doc
))
84 gtk_widget_hide (GTK_WIDGET (search_box
));
85 private->current_editor
= NULL
;
89 private->current_editor
= IANJUTA_EDITOR (doc
);
94 on_goto_activated (GtkWidget
* widget
, SearchBox
* search_box
)
96 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
97 const gchar
* str_line
= gtk_entry_get_text (GTK_ENTRY (private->goto_entry
));
99 gint line
= atoi (str_line
);
102 ianjuta_editor_goto_line (private->current_editor
, line
, NULL
);
107 search_box_set_entry_color (SearchBox
* search_box
, gboolean found
)
109 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
115 /* FIXME: a11y and theme */
117 gdk_color_parse ("#FF6666", &red
);
118 gdk_color_parse ("white", &white
);
120 gtk_widget_modify_base (private->search_entry
,
123 gtk_widget_modify_text (private->search_entry
,
129 gtk_widget_modify_base (private->search_entry
,
132 gtk_widget_modify_text (private->search_entry
,
139 on_goto_key_pressed (GtkWidget
* entry
, GdkEventKey
* event
, SearchBox
* search_box
)
141 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
142 switch (event
->keyval
)
169 /* This is a number or enter which is ok */
174 gtk_widget_hide (GTK_WIDGET (search_box
));
175 search_box_set_entry_color (search_box
, TRUE
);
176 if (private->current_editor
)
178 ianjuta_document_grab_focus (IANJUTA_DOCUMENT (private->current_editor
),
193 on_entry_key_pressed (GtkWidget
* entry
, GdkEventKey
* event
, SearchBox
* search_box
)
195 SearchBoxPrivate
* private = GET_PRIVATE(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 (private->current_editor
)
204 ianjuta_document_grab_focus (IANJUTA_DOCUMENT (private->current_editor
),
217 on_search_focus_out (GtkWidget
* widget
, GdkEvent
* event
, SearchBox
* search_box
)
219 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
220 if (private->last_start
)
222 g_object_unref (private->last_start
);
223 private->last_start
= NULL
;
225 anjuta_status_pop (private->status
);
231 on_incremental_search (GtkWidget
* widget
, SearchBox
* search_box
)
233 IAnjutaEditorCell
* search_start
;
234 IAnjutaEditorCell
* search_end
;
235 IAnjutaEditorCell
* result_start
;
236 IAnjutaEditorCell
* result_end
;
237 IAnjutaEditorSelection
* selection
;
238 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
239 gboolean case_sensitive
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (private->case_check
));
240 const gchar
* search_text
= gtk_entry_get_text (GTK_ENTRY (private->search_entry
));
241 gboolean found
= FALSE
;
243 if (!private->current_editor
|| !search_text
|| !strlen (search_text
))
246 if (private->last_start
)
248 search_start
= IANJUTA_EDITOR_CELL (private->last_start
);
253 IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (private->current_editor
,
257 search_end
= IANJUTA_EDITOR_CELL (ianjuta_editor_get_end_position (private->current_editor
,
260 if (ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (private->current_editor
),
261 search_text
, case_sensitive
,
262 search_start
, search_end
,
267 anjuta_status_pop (ANJUTA_STATUS (private->status
));
271 selection
= IANJUTA_EDITOR_SELECTION (private->current_editor
);
272 ianjuta_editor_selection_set (selection
,
273 IANJUTA_ITERABLE (result_start
),
274 IANJUTA_ITERABLE (result_end
), NULL
);
275 g_object_unref (result_start
);
276 g_object_unref (result_end
);
278 private->last_start
= IANJUTA_ITERABLE (search_start
);
282 g_object_unref (search_start
);
283 private->last_start
= NULL
;
286 search_box_set_entry_color (search_box
, found
);
287 g_object_unref (search_end
);
291 on_search_activated (GtkWidget
* widget
, SearchBox
* search_box
)
293 IAnjutaEditorCell
* search_start
;
294 IAnjutaIterable
* real_start
;
295 IAnjutaEditorCell
* search_end
;
296 IAnjutaEditorCell
* result_start
;
297 IAnjutaEditorCell
* result_end
;
298 IAnjutaEditorSelection
* selection
;
299 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
300 gboolean case_sensitive
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (private->case_check
));
301 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
))
307 selection
= IANJUTA_EDITOR_SELECTION (private->current_editor
);
309 if (ianjuta_editor_selection_has_selection (selection
, NULL
))
312 IANJUTA_EDITOR_CELL (ianjuta_editor_selection_get_start (selection
, NULL
));
317 IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (private->current_editor
,
321 ianjuta_iterable_clone (IANJUTA_ITERABLE (search_start
), NULL
);
323 search_end
= IANJUTA_EDITOR_CELL (ianjuta_editor_get_end_position (private->current_editor
,
326 /* If a search_result is already selected, move the search start
329 if (ianjuta_editor_selection_has_selection (selection
,
332 IAnjutaIterable
* selection_start
=
333 ianjuta_editor_selection_get_start (selection
, NULL
);
334 if (ianjuta_iterable_compare (IANJUTA_ITERABLE (search_start
),
335 selection_start
, NULL
) == 0)
337 gchar
* selected_text
=
338 ianjuta_editor_selection_get (selection
, NULL
);
341 if (g_str_has_prefix (selected_text
, search_text
))
343 ianjuta_iterable_next (IANJUTA_ITERABLE (search_start
), NULL
);
346 else if (strlen (selected_text
) >= strlen (search_text
))
348 gchar
* selected_up
= g_utf8_casefold (selected_text
, strlen (search_text
));
349 gchar
* search_text_up
= g_utf8_casefold (search_text
, strlen (search_text
));
350 if (g_str_equal (selected_up
, search_text_up
))
352 ianjuta_iterable_next (IANJUTA_ITERABLE (search_start
), NULL
);
354 g_free (selected_up
);
355 g_free (search_text_up
);
357 g_free (selected_text
);
361 if (ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (private->current_editor
),
362 search_text
, case_sensitive
,
363 search_start
, search_end
,
368 anjuta_status_pop (ANJUTA_STATUS (private->status
));
372 /* Try to continue on top */
373 ianjuta_iterable_first (IANJUTA_ITERABLE (search_start
), NULL
);
374 if (ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (private->current_editor
),
375 search_text
, case_sensitive
,
376 search_start
, search_end
,
380 if (ianjuta_iterable_compare (IANJUTA_ITERABLE (result_start
),
381 real_start
, NULL
) != 0)
384 anjuta_status_push (private->status
,
385 _("Search for \"%s\" reached end and was continued on top."), search_text
);
387 else if (ianjuta_editor_selection_has_selection (selection
, NULL
))
389 anjuta_status_pop (private->status
);
390 anjuta_status_push (private->status
,
391 _("Search for \"%s\" reached end and was continued on top but no new match was found."), search_text
);
397 ianjuta_editor_selection_set (selection
,
398 IANJUTA_ITERABLE (result_start
),
399 IANJUTA_ITERABLE (result_end
), NULL
);
400 g_object_unref (result_start
);
401 g_object_unref (result_end
);
403 search_box_set_entry_color (search_box
, found
);
404 g_object_unref (real_start
);
405 g_object_unref (search_end
);
407 // We end the incremental search here
409 if (private->last_start
)
411 g_object_unref (private->last_start
);
412 private->last_start
= NULL
;
416 g_object_unref (search_start
);
421 search_box_init (SearchBox
*object
)
423 SearchBoxPrivate
* private = GET_PRIVATE(object
);
427 gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_MENU
);
429 gtk_image_new_from_stock (GTK_STOCK_FIND
, GTK_ICON_SIZE_SMALL_TOOLBAR
);
430 GtkWidget
* goto_image
=
431 gtk_image_new_from_stock (ANJUTA_STOCK_GOTO_LINE
, GTK_ICON_SIZE_SMALL_TOOLBAR
);
434 private->search_entry
= gtk_entry_new();
435 g_signal_connect (G_OBJECT (private->search_entry
), "activate",
436 G_CALLBACK (on_search_activated
),
438 g_signal_connect (G_OBJECT (private->search_entry
), "key-press-event",
439 G_CALLBACK (on_entry_key_pressed
),
441 g_signal_connect (G_OBJECT (private->search_entry
), "changed",
442 G_CALLBACK (on_incremental_search
),
444 g_signal_connect (G_OBJECT (private->search_entry
), "focus-out-event",
445 G_CALLBACK (on_search_focus_out
),
448 private->case_check
= gtk_check_button_new_with_label (_("Case sensitive"));
450 private->search_button
= gtk_button_new ();
451 gtk_button_set_image (GTK_BUTTON (private->search_button
), search
);
452 gtk_button_set_relief (GTK_BUTTON (private->search_button
), GTK_RELIEF_NONE
);
453 g_signal_connect (G_OBJECT (private->search_button
), "clicked",
454 G_CALLBACK (on_search_activated
),
458 private->close_button
= gtk_button_new();
459 gtk_button_set_image (GTK_BUTTON (private->close_button
), close
);
460 gtk_button_set_relief (GTK_BUTTON (private->close_button
), GTK_RELIEF_NONE
);
462 g_signal_connect (G_OBJECT (private->close_button
), "clicked",
463 G_CALLBACK (on_search_box_hide
), object
);
466 private->goto_entry
= gtk_entry_new ();
467 gtk_entry_set_width_chars (GTK_ENTRY (private->goto_entry
), 5);
468 g_signal_connect (G_OBJECT (private->goto_entry
), "activate",
469 G_CALLBACK (on_goto_activated
),
471 g_signal_connect (G_OBJECT (private->goto_entry
), "key-press-event",
472 G_CALLBACK (on_goto_key_pressed
),
474 private->goto_button
= gtk_button_new();
475 gtk_button_set_image (GTK_BUTTON (private->goto_button
), goto_image
);
476 gtk_button_set_relief (GTK_BUTTON (private->goto_button
), GTK_RELIEF_NONE
);
477 g_signal_connect (G_OBJECT (private->goto_button
), "clicked",
478 G_CALLBACK (on_goto_activated
),
481 gtk_box_pack_start (GTK_BOX (object
), private->goto_entry
, FALSE
, FALSE
, 0);
482 gtk_box_pack_start (GTK_BOX (object
), private->goto_button
, FALSE
, FALSE
, 0);
484 gtk_box_pack_start (GTK_BOX (object
), private->search_entry
, TRUE
, TRUE
, 5);
485 gtk_box_pack_start (GTK_BOX (object
), private->search_button
, FALSE
, FALSE
, 0);
486 gtk_box_pack_start (GTK_BOX (object
), private->case_check
, FALSE
, FALSE
, 0);
487 gtk_box_pack_end (GTK_BOX (object
), private->close_button
, FALSE
, FALSE
, 0);
489 gtk_widget_show_all (GTK_WIDGET (object
));
491 private->last_start
= NULL
;
495 search_box_finalize (GObject
*object
)
498 G_OBJECT_CLASS (search_box_parent_class
)->finalize (object
);
502 search_box_class_init (SearchBoxClass
*klass
)
504 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
506 g_type_class_add_private (klass
, sizeof (SearchBoxPrivate
));
508 object_class
->finalize
= search_box_finalize
;
512 search_box_new (AnjutaDocman
*docman
)
514 GtkWidget
* search_box
;
515 SearchBoxPrivate
* private;
517 search_box
= GTK_WIDGET (g_object_new (SEARCH_TYPE_BOX
, "homogeneous",
519 g_signal_connect (G_OBJECT (docman
), "document-changed",
520 G_CALLBACK (on_document_changed
), search_box
);
522 private = GET_PRIVATE (search_box
);
523 private->status
= anjuta_shell_get_status (docman
->shell
, NULL
);
529 search_box_fill_search_focus (SearchBox
* search_box
)
531 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
532 IAnjutaEditor
* te
= private->current_editor
;
534 if (IANJUTA_IS_EDITOR (te
))
538 buffer
= ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te
), NULL
);
544 gtk_entry_set_text (GTK_ENTRY (private->search_entry
), buffer
);
545 gtk_editable_select_region (GTK_EDITABLE (private->search_entry
), 0, -1);
551 gtk_widget_grab_focus (private->search_entry
);
555 search_box_grab_line_focus (SearchBox
* search_box
)
557 SearchBoxPrivate
* private = GET_PRIVATE(search_box
);
558 gtk_widget_grab_focus (private->goto_entry
);