35
35
#include <gtk/gtk.h>
37
37
#include "rb-search-entry.h"
39
40
static void rb_search_entry_class_init (RBSearchEntryClass *klass);
40
41
static void rb_search_entry_init (RBSearchEntry *entry);
42
static void rb_search_entry_constructed (GObject *object);
41
43
static void rb_search_entry_finalize (GObject *object);
42
44
static gboolean rb_search_entry_timeout_cb (RBSearchEntry *entry);
43
45
static void rb_search_entry_changed_cb (GtkEditable *editable,
44
46
RBSearchEntry *entry);
45
47
static void rb_search_entry_activate_cb (GtkEntry *gtkentry,
46
48
RBSearchEntry *entry);
49
static void rb_search_entry_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
50
static void rb_search_entry_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
51
static void button_clicked_cb (GtkButton *button, RBSearchEntry *entry);
47
52
static gboolean rb_search_entry_focus_out_event_cb (GtkWidget *widget,
48
53
GdkEventFocus *event,
49
54
RBSearchEntry *entry);
77
86
* Signals are emitted when the search text changes,
78
87
* arbitrarily rate-limited to one every 300ms.
80
* When the text entry widget is non-empty, its colours are
81
* changed to display the text in black on yellow.
91
105
static guint rb_search_entry_signals[LAST_SIGNAL] = { 0 };
94
108
rb_search_entry_class_init (RBSearchEntryClass *klass)
96
110
GObjectClass *object_class = G_OBJECT_CLASS (klass);
111
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
113
object_class->constructed = rb_search_entry_constructed;
98
114
object_class->finalize = rb_search_entry_finalize;
115
object_class->set_property = rb_search_entry_set_property;
116
object_class->get_property = rb_search_entry_get_property;
118
widget_class->grab_focus = rb_search_entry_widget_grab_focus;
101
121
* RBSearchEntry::search:
159
* RBSearchEntry::show-popup:
160
* @entry: the #RBSearchEntry
162
* Emitted when a popup menu should be shown
164
rb_search_entry_signals[SHOW_POPUP] =
165
g_signal_new ("show-popup",
166
G_OBJECT_CLASS_TYPE (object_class),
168
G_STRUCT_OFFSET (RBSearchEntryClass, show_popup),
170
g_cclosure_marshal_VOID__VOID,
175
* RBSearchEntry:explicit-mode:
177
* If TRUE, show a button and only emit the 'search' signal when
178
* the user presses it rather than when they stop typing.
180
g_object_class_install_property (object_class,
182
g_param_spec_boolean ("explicit-mode",
184
"whether in explicit search mode or not",
188
* RBSearchEntry:has-popup:
190
* If TRUE, show a primary icon and emit the show-popup when clicked.
192
g_object_class_install_property (object_class,
194
g_param_spec_boolean ("has-popup",
196
"whether to display the search menu icon",
198
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
138
200
g_type_class_add_private (klass, sizeof (RBSearchEntryPrivate));
142
204
rb_search_entry_init (RBSearchEntry *entry)
145
GtkSettings *settings;
148
206
entry->priv = RB_SEARCH_ENTRY_GET_PRIVATE (entry);
150
settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (entry)));
151
g_object_get (settings, "gtk-theme-name", &theme, NULL);
152
entry->priv->is_a11y_theme = strncmp (theme, "HighContrast", strlen ("HighContrast")) == 0 ||
153
strncmp (theme, "LowContrast", strlen ("LowContrast")) == 0;
156
/* this string can only be so long, or there wont be a search entry :) */
157
label = gtk_label_new_with_mnemonic (_("_Search:"));
158
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_RIGHT);
159
gtk_box_pack_start (GTK_BOX (entry), label, FALSE, TRUE, 0);
210
rb_search_entry_constructed (GObject *object)
212
RBSearchEntry *entry;
214
RB_CHAIN_GOBJECT_METHOD (rb_search_entry_parent_class, constructed, object);
216
entry = RB_SEARCH_ENTRY (object);
218
gtk_widget_set_can_focus (GTK_WIDGET (entry), TRUE);
161
219
entry->priv->entry = gtk_entry_new ();
162
gtk_entry_set_icon_from_stock (GTK_ENTRY (entry->priv->entry),
163
GTK_ENTRY_ICON_SECONDARY,
220
g_signal_connect_object (GTK_ENTRY (entry->priv->entry),
222
G_CALLBACK (rb_search_entry_clear_cb),
165
225
gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry->priv->entry),
166
226
GTK_ENTRY_ICON_SECONDARY,
167
227
_("Clear the search text"));
168
g_signal_connect_object (GTK_ENTRY (entry->priv->entry),
170
G_CALLBACK (rb_search_entry_clear_cb),
173
gtk_label_set_mnemonic_widget (GTK_LABEL (label),
228
if (entry->priv->has_popup) {
229
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->entry),
230
GTK_ENTRY_ICON_PRIMARY,
231
"edit-find-symbolic");
232
gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry->priv->entry),
233
GTK_ENTRY_ICON_PRIMARY,
234
_("Select the search type"));
236
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->entry),
237
GTK_ENTRY_ICON_SECONDARY,
238
"edit-find-symbolic");
176
241
gtk_box_pack_start (GTK_BOX (entry), entry->priv->entry, TRUE, TRUE, 0);
188
253
G_CALLBACK (rb_search_entry_activate_cb),
256
entry->priv->button = gtk_button_new_with_label (_("Search"));
257
gtk_box_pack_start (GTK_BOX (entry), entry->priv->button, FALSE, FALSE, 0);
258
gtk_widget_set_no_show_all (entry->priv->button, TRUE);
259
g_signal_connect_object (entry->priv->button,
261
G_CALLBACK (button_clicked_cb),
266
rb_search_entry_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
268
RBSearchEntry *entry = RB_SEARCH_ENTRY (object);
271
case PROP_EXPLICIT_MODE:
272
entry->priv->explicit_mode = g_value_get_boolean (value);
273
gtk_widget_set_visible (entry->priv->button, entry->priv->explicit_mode == TRUE);
274
rb_search_entry_update_icons (entry);
277
entry->priv->has_popup = g_value_get_boolean (value);
280
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286
rb_search_entry_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
288
RBSearchEntry *entry = RB_SEARCH_ENTRY (object);
291
case PROP_EXPLICIT_MODE:
292
g_value_set_boolean (value, entry->priv->explicit_mode);
295
g_value_set_boolean (value, entry->priv->has_popup);
298
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
262
375
text ? text : "");
379
* rb_search_entry_set_placeholder:
380
* @entry: a #RBSearchEntry
381
* @text: placeholder text
383
* Sets the placeholder text in the search entry box.
386
rb_search_entry_set_placeholder (RBSearchEntry *entry, const char *text)
388
gtk_entry_set_placeholder_text (GTK_ENTRY (entry->priv->entry), text);
266
rb_search_entry_check_style (RBSearchEntry *entry)
392
rb_search_entry_update_icons (RBSearchEntry *entry)
268
static const GdkRGBA fallback_bg_color = { 0.9686, 0.9686, 0.7451, 1.0}; /* yellow-ish */
269
static const GdkRGBA fallback_fg_color = { 0, 0, 0, 1.0 }; /* black. */
270
GdkRGBA bg_color = {0,};
271
GdkRGBA fg_color = {0,};
274
if (entry->priv->is_a11y_theme)
277
/* allow user style to override the colors */
278
if (gtk_style_context_lookup_color (gtk_widget_get_style_context (GTK_WIDGET (entry)),
279
"rb-search-active-bg",
280
&bg_color) == FALSE) {
281
bg_color = fallback_bg_color;
283
if (gtk_style_context_lookup_color (gtk_widget_get_style_context (GTK_WIDGET (entry)),
284
"rb-search-active-fg",
285
&fg_color) == FALSE) {
286
fg_color = fallback_fg_color;
289
text = gtk_entry_get_text (GTK_ENTRY (entry->priv->entry));
291
gtk_widget_override_color (entry->priv->entry, GTK_STATE_NORMAL, &fg_color);
292
gtk_widget_override_background_color (entry->priv->entry, GTK_STATE_NORMAL, &bg_color);
293
gtk_widget_override_cursor (entry->priv->entry, &fg_color, &fg_color);
295
gtk_widget_override_color (entry->priv->entry, GTK_STATE_NORMAL, NULL);
296
gtk_widget_override_background_color (entry->priv->entry, GTK_STATE_NORMAL, NULL);
297
gtk_widget_override_cursor (entry->priv->entry, NULL, NULL);
300
gtk_widget_queue_draw (GTK_WIDGET (entry));
398
if (entry->priv->explicit_mode) {
399
searching = entry->priv->searching;
401
text = gtk_entry_get_text (GTK_ENTRY (entry->priv->entry));
402
searching = (text && *text);
406
icon = "edit-clear-symbolic";
407
} else if (entry->priv->has_popup) {
408
/* we already use 'find' as the primary icon */
411
icon = "edit-find-symbolic";
413
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->entry),
414
GTK_ENTRY_ICON_SECONDARY,
304
419
rb_search_entry_changed_cb (GtkEditable *editable,
305
420
RBSearchEntry *entry)
307
rb_search_entry_check_style (entry);
309
if (entry->priv->clearing == TRUE)
424
if (entry->priv->clearing == TRUE) {
425
entry->priv->searching = FALSE;
426
rb_search_entry_update_icons (entry);
312
430
if (entry->priv->timeout != 0) {
313
431
g_source_remove (entry->priv->timeout);
317
435
/* emit it now if we're clearing the entry */
318
if (gtk_entry_get_text (GTK_ENTRY (entry->priv->entry)))
436
text = gtk_entry_get_text (GTK_ENTRY (entry->priv->entry));
437
if (text != NULL && text[0] != '\0') {
438
gtk_widget_set_sensitive (entry->priv->button, TRUE);
319
439
entry->priv->timeout = g_timeout_add (300, (GSourceFunc) rb_search_entry_timeout_cb, entry);
441
entry->priv->searching = FALSE;
442
gtk_widget_set_sensitive (entry->priv->button, FALSE);
321
443
rb_search_entry_timeout_cb (entry);
445
rb_search_entry_update_icons (entry);
325
449
rb_search_entry_timeout_cb (RBSearchEntry *entry)
327
452
gdk_threads_enter ();
329
g_signal_emit (G_OBJECT (entry), rb_search_entry_signals[SEARCH], 0,
330
gtk_entry_get_text (GTK_ENTRY (entry->priv->entry)));
454
text = gtk_entry_get_text (GTK_ENTRY (entry->priv->entry));
456
if (entry->priv->explicit_mode == FALSE) {
457
g_signal_emit (G_OBJECT (entry), rb_search_entry_signals[SEARCH], 0, text);
331
459
entry->priv->timeout = 0;
333
461
gdk_threads_leave ();
364
494
rb_search_entry_searching (RBSearchEntry *entry)
366
return strcmp ("", gtk_entry_get_text (GTK_ENTRY (entry->priv->entry))) != 0;
496
if (entry->priv->explicit_mode) {
497
return entry->priv->searching;
499
return strcmp ("", gtk_entry_get_text (GTK_ENTRY (entry->priv->entry))) != 0;
370
504
rb_search_entry_activate_cb (GtkEntry *gtkentry,
371
505
RBSearchEntry *entry)
507
entry->priv->searching = TRUE;
508
rb_search_entry_update_icons (entry);
373
509
g_signal_emit (G_OBJECT (entry), rb_search_entry_signals[ACTIVATE], 0,
374
510
gtk_entry_get_text (GTK_ENTRY (entry->priv->entry)));
514
button_clicked_cb (GtkButton *button, RBSearchEntry *entry)
516
entry->priv->searching = TRUE;
517
rb_search_entry_update_icons (entry);
518
g_signal_emit (G_OBJECT (entry), rb_search_entry_signals[SEARCH], 0,
519
gtk_entry_get_text (GTK_ENTRY (entry->priv->entry)));
378
523
* rb_search_entry_grab_focus:
379
524
* @entry: a #RBSearchEntry
535
rb_search_entry_widget_grab_focus (GtkWidget *widget)
537
rb_search_entry_grab_focus (RB_SEARCH_ENTRY (widget));
390
541
rb_search_entry_clear_cb (GtkEntry *entry,
391
542
GtkEntryIconPosition icon_pos,
393
544
RBSearchEntry *search_entry)
395
rb_search_entry_set_text (search_entry, "");
546
if (icon_pos == GTK_ENTRY_ICON_PRIMARY) {
547
g_signal_emit (G_OBJECT (search_entry), rb_search_entry_signals[SHOW_POPUP], 0);
549
rb_search_entry_set_text (search_entry, "");
554
* rb_search_entry_set_mnemonic:
555
* @entry: a #RBSearchEntry
556
* @enable: if %TRUE, enable the mnemonic
558
* Adds or removes a mnemonic allowing the user to focus
562
rb_search_entry_set_mnemonic (RBSearchEntry *entry, gboolean enable)
568
if (pango_parse_markup (_("_Search:"), -1, '_', NULL, NULL, &accel, NULL) && accel != 0) {
569
keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel));
571
keyval = gdk_unicode_to_keyval ('s');
574
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry));
575
if (gtk_widget_is_toplevel (toplevel)) {
577
gtk_window_add_mnemonic (GTK_WINDOW (toplevel), keyval, entry->priv->entry);
579
gtk_window_remove_mnemonic (GTK_WINDOW (toplevel), keyval, entry->priv->entry);