1 /* Copyright (C) 2004 Red Hat, Inc.
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public License as
5 * published by the Free Software Foundation; either version 2 of the
6 * License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
13 * You should have received a copy of the GNU Library General Public
14 * License along with the Gnome Library; see the file COPYING.LIB. If not,
15 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
23 #include <glib/gi18n.h>
25 #include <gdk/gdkkeysyms.h>
27 #include "eggfindbar.h"
29 struct _EggFindBarPrivate
33 GtkToolItem *next_button;
34 GtkToolItem *previous_button;
35 GtkToolItem *status_separator;
36 GtkToolItem *status_item;
37 GtkToolItem *case_button;
39 GtkWidget *find_entry;
40 GtkWidget *status_label;
42 gulong set_focus_handler;
43 guint case_sensitive : 1;
46 #define EGG_FIND_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EGG_TYPE_FIND_BAR, EggFindBarPrivate))
54 static void egg_find_bar_finalize (GObject *object);
55 static void egg_find_bar_get_property (GObject *object,
59 static void egg_find_bar_set_property (GObject *object,
63 static void egg_find_bar_show (GtkWidget *widget);
64 static void egg_find_bar_hide (GtkWidget *widget);
65 static void egg_find_bar_grab_focus (GtkWidget *widget);
67 G_DEFINE_TYPE (EggFindBar, egg_find_bar, GTK_TYPE_TOOLBAR);
78 static guint find_bar_signals[LAST_SIGNAL] = { 0 };
81 egg_find_bar_class_init (EggFindBarClass *klass)
83 GObjectClass *object_class;
84 GtkWidgetClass *widget_class;
85 GtkBindingSet *binding_set;
87 egg_find_bar_parent_class = g_type_class_peek_parent (klass);
89 object_class = (GObjectClass *)klass;
90 widget_class = (GtkWidgetClass *)klass;
92 object_class->set_property = egg_find_bar_set_property;
93 object_class->get_property = egg_find_bar_get_property;
95 object_class->finalize = egg_find_bar_finalize;
97 widget_class->show = egg_find_bar_show;
98 widget_class->hide = egg_find_bar_hide;
100 widget_class->grab_focus = egg_find_bar_grab_focus;
102 find_bar_signals[NEXT] =
103 g_signal_new ("next",
104 G_OBJECT_CLASS_TYPE (object_class),
106 G_STRUCT_OFFSET (EggFindBarClass, next),
108 g_cclosure_marshal_VOID__VOID,
110 find_bar_signals[PREVIOUS] =
111 g_signal_new ("previous",
112 G_OBJECT_CLASS_TYPE (object_class),
114 G_STRUCT_OFFSET (EggFindBarClass, previous),
116 g_cclosure_marshal_VOID__VOID,
118 find_bar_signals[CLOSE] =
119 g_signal_new ("close",
120 G_OBJECT_CLASS_TYPE (object_class),
121 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
122 G_STRUCT_OFFSET (EggFindBarClass, close),
124 g_cclosure_marshal_VOID__VOID,
126 find_bar_signals[SCROLL] =
127 g_signal_new ("scroll",
128 G_OBJECT_CLASS_TYPE (object_class),
129 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
130 G_STRUCT_OFFSET (EggFindBarClass, scroll),
132 g_cclosure_marshal_VOID__ENUM,
134 GTK_TYPE_SCROLL_TYPE);
137 * EggFindBar:search_string:
139 * The current string to search for. NULL or empty string
140 * both mean no current string.
143 g_object_class_install_property (object_class,
145 g_param_spec_string ("search_string",
147 "The name of the string to be found",
152 * EggFindBar:case_sensitive:
154 * TRUE for a case sensitive search.
157 g_object_class_install_property (object_class,
159 g_param_spec_boolean ("case_sensitive",
161 "TRUE for a case sensitive search",
165 g_type_class_add_private (object_class, sizeof (EggFindBarPrivate));
167 binding_set = gtk_binding_set_by_class (klass);
169 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
172 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
174 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD);
175 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
177 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD);
178 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
180 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
181 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
183 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
184 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
186 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
187 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
189 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
193 egg_find_bar_emit_next (EggFindBar *find_bar)
195 g_signal_emit (find_bar, find_bar_signals[NEXT], 0);
199 egg_find_bar_emit_previous (EggFindBar *find_bar)
201 g_signal_emit (find_bar, find_bar_signals[PREVIOUS], 0);
205 next_clicked_callback (GtkButton *button,
208 EggFindBar *find_bar = EGG_FIND_BAR (data);
210 egg_find_bar_emit_next (find_bar);
214 previous_clicked_callback (GtkButton *button,
217 EggFindBar *find_bar = EGG_FIND_BAR (data);
219 egg_find_bar_emit_previous (find_bar);
223 case_sensitive_toggled_callback (GtkCheckButton *button,
226 EggFindBar *find_bar = EGG_FIND_BAR (data);
228 egg_find_bar_set_case_sensitive (find_bar,
229 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
233 entry_activate_callback (GtkEntry *entry,
236 EggFindBar *find_bar = EGG_FIND_BAR (data);
238 if (find_bar->priv->search_string != NULL)
239 egg_find_bar_emit_next (find_bar);
243 entry_changed_callback (GtkEntry *entry,
246 EggFindBar *find_bar = EGG_FIND_BAR (data);
249 /* paranoid strdup because set_search_string() sets
252 text = g_strdup (gtk_entry_get_text (entry));
254 egg_find_bar_set_search_string (find_bar, text);
260 set_focus_cb (GtkWidget *window,
264 GtkWidget *wbar = GTK_WIDGET (bar);
266 while (widget != NULL && widget != wbar)
268 widget = gtk_widget_get_parent (widget);
271 /* if widget == bar, the new focus widget is in the bar, so we
276 g_signal_emit (bar, find_bar_signals[CLOSE], 0);
281 egg_find_bar_init (EggFindBar *find_bar)
283 EggFindBarPrivate *priv;
285 GtkWidget *alignment;
291 priv = EGG_FIND_BAR_GET_PRIVATE (find_bar);
293 find_bar->priv = priv;
294 priv->search_string = NULL;
296 gtk_toolbar_set_style (GTK_TOOLBAR (find_bar), GTK_TOOLBAR_BOTH_HORIZ);
299 item = gtk_tool_item_new ();
300 box = gtk_hbox_new (FALSE, 12);
302 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
303 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 2, 2);
305 label = gtk_label_new_with_mnemonic (_("Find:"));
307 priv->find_entry = gtk_entry_new ();
308 gtk_entry_set_width_chars (GTK_ENTRY (priv->find_entry), 32);
309 gtk_entry_set_max_length (GTK_ENTRY (priv->find_entry), 512);
310 gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->find_entry);
313 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
314 priv->previous_button = gtk_tool_button_new (arrow, Q_("Find Pre_vious"));
315 gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (priv->previous_button), TRUE);
316 gtk_tool_item_set_is_important (priv->previous_button, TRUE);
317 gtk_widget_set_tooltip_text (GTK_WIDGET (priv->previous_button),
318 _("Find previous occurrence of the search string"));
321 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
322 priv->next_button = gtk_tool_button_new (arrow, Q_("Find Ne_xt"));
323 gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (priv->next_button), TRUE);
324 gtk_tool_item_set_is_important (priv->next_button, TRUE);
325 gtk_widget_set_tooltip_text (GTK_WIDGET (priv->next_button),
326 _("Find next occurrence of the search string"));
329 priv->status_separator = gtk_separator_tool_item_new();
332 priv->case_button = gtk_toggle_tool_button_new ();
333 g_object_set (G_OBJECT (priv->case_button), "label", _("C_ase Sensitive"), NULL);
334 gtk_tool_item_set_is_important (priv->case_button, TRUE);
335 gtk_widget_set_tooltip_text (GTK_WIDGET (priv->case_button),
336 _("Toggle case sensitive search"));
339 priv->status_item = gtk_tool_item_new();
340 gtk_tool_item_set_expand (priv->status_item, TRUE);
341 priv->status_label = gtk_label_new (NULL);
342 gtk_label_set_ellipsize (GTK_LABEL (priv->status_label),
343 PANGO_ELLIPSIZE_END);
344 gtk_misc_set_alignment (GTK_MISC (priv->status_label), 0.0, 0.5);
347 g_signal_connect (priv->find_entry, "changed",
348 G_CALLBACK (entry_changed_callback),
350 g_signal_connect (priv->find_entry, "activate",
351 G_CALLBACK (entry_activate_callback),
353 g_signal_connect (priv->next_button, "clicked",
354 G_CALLBACK (next_clicked_callback),
356 g_signal_connect (priv->previous_button, "clicked",
357 G_CALLBACK (previous_clicked_callback),
359 g_signal_connect (priv->case_button, "toggled",
360 G_CALLBACK (case_sensitive_toggled_callback),
363 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
364 gtk_box_pack_start (GTK_BOX (box), priv->find_entry, TRUE, TRUE, 0);
365 gtk_container_add (GTK_CONTAINER (alignment), box);
366 gtk_container_add (GTK_CONTAINER (item), alignment);
367 gtk_toolbar_insert (GTK_TOOLBAR (find_bar), item, -1);
368 gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->previous_button, -1);
369 gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->next_button, -1);
370 gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->case_button, -1);
371 gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->status_separator, -1);
372 gtk_container_add (GTK_CONTAINER (priv->status_item), priv->status_label);
373 gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->status_item, -1);
375 /* don't show status separator/label until they are set */
377 gtk_widget_show_all (GTK_WIDGET (item));
378 gtk_widget_show_all (GTK_WIDGET (priv->next_button));
379 gtk_widget_show_all (GTK_WIDGET (priv->previous_button));
380 gtk_widget_show (priv->status_label);
384 egg_find_bar_finalize (GObject *object)
386 EggFindBar *find_bar = EGG_FIND_BAR (object);
387 EggFindBarPrivate *priv = (EggFindBarPrivate *)find_bar->priv;
389 g_free (priv->search_string);
391 G_OBJECT_CLASS (egg_find_bar_parent_class)->finalize (object);
395 egg_find_bar_set_property (GObject *object,
400 EggFindBar *find_bar = EGG_FIND_BAR (object);
404 case PROP_SEARCH_STRING:
405 egg_find_bar_set_search_string (find_bar, g_value_get_string (value));
407 case PROP_CASE_SENSITIVE:
408 egg_find_bar_set_case_sensitive (find_bar, g_value_get_boolean (value));
411 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417 egg_find_bar_get_property (GObject *object,
422 EggFindBar *find_bar = EGG_FIND_BAR (object);
423 EggFindBarPrivate *priv = (EggFindBarPrivate *)find_bar->priv;
427 case PROP_SEARCH_STRING:
428 g_value_set_string (value, priv->search_string);
430 case PROP_CASE_SENSITIVE:
431 g_value_set_boolean (value, priv->case_sensitive);
434 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
440 egg_find_bar_show (GtkWidget *widget)
442 EggFindBar *bar = EGG_FIND_BAR (widget);
443 EggFindBarPrivate *priv = bar->priv;
445 GTK_WIDGET_CLASS (egg_find_bar_parent_class)->show (widget);
447 if (priv->set_focus_handler == 0)
451 toplevel = gtk_widget_get_toplevel (widget);
453 priv->set_focus_handler =
454 g_signal_connect (toplevel, "set-focus",
455 G_CALLBACK (set_focus_cb), bar);
460 egg_find_bar_hide (GtkWidget *widget)
462 EggFindBar *bar = EGG_FIND_BAR (widget);
463 EggFindBarPrivate *priv = bar->priv;
465 if (priv->set_focus_handler != 0)
469 toplevel = gtk_widget_get_toplevel (widget);
471 g_signal_handlers_disconnect_by_func
472 (toplevel, (void (*)) G_CALLBACK (set_focus_cb), bar);
473 priv->set_focus_handler = 0;
476 GTK_WIDGET_CLASS (egg_find_bar_parent_class)->hide (widget);
480 egg_find_bar_grab_focus (GtkWidget *widget)
482 EggFindBar *find_bar = EGG_FIND_BAR (widget);
483 EggFindBarPrivate *priv = find_bar->priv;
485 gtk_widget_grab_focus (priv->find_entry);
491 * Creates a new #EggFindBar.
493 * Returns: a newly created #EggFindBar
498 egg_find_bar_new (void)
500 EggFindBar *find_bar;
502 find_bar = g_object_new (EGG_TYPE_FIND_BAR, NULL);
504 return GTK_WIDGET (find_bar);
508 * egg_find_bar_set_search_string:
510 * Sets the string that should be found/highlighted in the document.
511 * Empty string is converted to NULL.
516 egg_find_bar_set_search_string (EggFindBar *find_bar,
517 const char *search_string)
519 EggFindBarPrivate *priv;
521 g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
523 priv = (EggFindBarPrivate *)find_bar->priv;
525 g_object_freeze_notify (G_OBJECT (find_bar));
527 if (priv->search_string != search_string)
531 old = priv->search_string;
533 if (search_string && *search_string == '\0')
534 search_string = NULL;
536 /* Only update if the string has changed; setting the entry
537 * will emit changed on the entry which will re-enter
538 * this function, but we'll handle that fine with this
541 if ((old && search_string == NULL) ||
542 (old == NULL && search_string) ||
543 (old && search_string &&
544 strcmp (old, search_string) != 0))
548 priv->search_string = g_strdup (search_string);
551 gtk_entry_set_text (GTK_ENTRY (priv->find_entry),
552 priv->search_string ?
553 priv->search_string :
556 not_empty = (search_string == NULL) ? FALSE : TRUE;
558 gtk_widget_set_sensitive (GTK_WIDGET (find_bar->priv->next_button), not_empty);
559 gtk_widget_set_sensitive (GTK_WIDGET (find_bar->priv->previous_button), not_empty);
561 g_object_notify (G_OBJECT (find_bar),
566 g_object_thaw_notify (G_OBJECT (find_bar));
571 * egg_find_bar_get_search_string:
573 * Gets the string that should be found/highlighted in the document.
575 * Returns: the string
580 egg_find_bar_get_search_string (EggFindBar *find_bar)
582 EggFindBarPrivate *priv;
584 g_return_val_if_fail (EGG_IS_FIND_BAR (find_bar), NULL);
586 priv = find_bar->priv;
588 return priv->search_string ? priv->search_string : "";
592 * egg_find_bar_set_case_sensitive:
594 * Sets whether the search is case sensitive
599 egg_find_bar_set_case_sensitive (EggFindBar *find_bar,
600 gboolean case_sensitive)
602 EggFindBarPrivate *priv;
604 g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
606 priv = (EggFindBarPrivate *)find_bar->priv;
608 g_object_freeze_notify (G_OBJECT (find_bar));
610 case_sensitive = case_sensitive != FALSE;
612 if (priv->case_sensitive != case_sensitive)
614 priv->case_sensitive = case_sensitive;
616 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->case_button),
617 priv->case_sensitive);
619 g_object_notify (G_OBJECT (find_bar),
623 g_object_thaw_notify (G_OBJECT (find_bar));
627 * egg_find_bar_get_case_sensitive:
629 * Gets whether the search is case sensitive
631 * Returns: TRUE if it's case sensitive
636 egg_find_bar_get_case_sensitive (EggFindBar *find_bar)
638 EggFindBarPrivate *priv;
640 g_return_val_if_fail (EGG_IS_FIND_BAR (find_bar), FALSE);
642 priv = (EggFindBarPrivate *)find_bar->priv;
644 return priv->case_sensitive;
648 * egg_find_bar_set_status_text:
650 * Sets some text to display if there's space; typical text would
651 * be something like "5 results on this page" or "No results"
653 * @text: the text to display
658 egg_find_bar_set_status_text (EggFindBar *find_bar,
661 EggFindBarPrivate *priv;
663 g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
665 priv = (EggFindBarPrivate *)find_bar->priv;
667 gtk_label_set_text (GTK_LABEL (priv->status_label), text);
668 g_object_set (priv->status_separator, "visible", text != NULL && *text != '\0', NULL);
669 g_object_set (priv->status_item, "visible", text != NULL && *text !='\0', NULL);