~ubuntu-branches/ubuntu/quantal/rhythmbox/quantal-proposed

« back to all changes in this revision

Viewing changes to widgets/rb-search-entry.c

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher, Rico Tzschichholz
  • Date: 2011-12-05 19:31:23 UTC
  • mfrom: (1.1.60)
  • Revision ID: package-import@ubuntu.com-20111205193123-89047p8yplb0w1vx
Tags: 2.90.1~20111126.89c872b0-0ubuntu1
* Upload the new version to Ubuntu, should solve those issues:
  - the lack of rhythmbox-client command (lp: #875064)
  - the music sharing preferences dialog (lp: #894153)
  - several segfaults (lp: #859195, #814614)
* debian/control.in:
  - let the rhythmbox gir depends on gir1.2-peas-1.0 (lp: #874973)

[ Rico Tzschichholz ]
* New upstream git snapshot

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
#include <gtk/gtk.h>
36
36
 
37
37
#include "rb-search-entry.h"
 
38
#include "rb-util.h"
38
39
 
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);
51
56
                                      GtkEntryIconPosition icon_pos,
52
57
                                      GdkEvent *event,
53
58
                                      RBSearchEntry *search_entry);
 
59
static void rb_search_entry_update_icons (RBSearchEntry *entry);
 
60
static void rb_search_entry_widget_grab_focus (GtkWidget *widget);
54
61
 
55
62
struct RBSearchEntryPrivate
56
63
{
57
64
        GtkWidget *entry;
 
65
        GtkWidget *button;
58
66
 
 
67
        gboolean has_popup;
 
68
        gboolean explicit_mode;
59
69
        gboolean clearing;
 
70
        gboolean searching;
60
71
 
61
72
        guint timeout;
62
 
 
63
 
        gboolean is_a11y_theme;
64
73
};
65
74
 
66
75
G_DEFINE_TYPE (RBSearchEntry, rb_search_entry, GTK_TYPE_HBOX)
76
85
 *
77
86
 * Signals are emitted when the search text changes,
78
87
 * arbitrarily rate-limited to one every 300ms.
79
 
 *
80
 
 * When the text entry widget is non-empty, its colours are
81
 
 * changed to display the text in black on yellow.
82
88
 */
83
89
 
84
90
enum
85
91
{
86
92
        SEARCH,
87
93
        ACTIVATE,
 
94
        SHOW_POPUP,
88
95
        LAST_SIGNAL
89
96
};
90
97
 
 
98
enum
 
99
{
 
100
        PROP_0,
 
101
        PROP_EXPLICIT_MODE,
 
102
        PROP_HAS_POPUP
 
103
};
 
104
 
91
105
static guint rb_search_entry_signals[LAST_SIGNAL] = { 0 };
92
106
 
93
107
static void
94
108
rb_search_entry_class_init (RBSearchEntryClass *klass)
95
109
{
96
110
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
111
        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
97
112
 
 
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;
 
117
 
 
118
        widget_class->grab_focus = rb_search_entry_widget_grab_focus;
99
119
 
100
120
        /**
101
121
         * RBSearchEntry::search:
135
155
                              1,
136
156
                              G_TYPE_STRING);
137
157
 
 
158
        /**
 
159
         * RBSearchEntry::show-popup:
 
160
         * @entry: the #RBSearchEntry
 
161
         *
 
162
         * Emitted when a popup menu should be shown
 
163
         */
 
164
        rb_search_entry_signals[SHOW_POPUP] =
 
165
                g_signal_new ("show-popup",
 
166
                              G_OBJECT_CLASS_TYPE (object_class),
 
167
                              G_SIGNAL_RUN_LAST,
 
168
                              G_STRUCT_OFFSET (RBSearchEntryClass, show_popup),
 
169
                              NULL, NULL,
 
170
                              g_cclosure_marshal_VOID__VOID,
 
171
                              G_TYPE_NONE,
 
172
                              0);
 
173
 
 
174
        /**
 
175
         * RBSearchEntry:explicit-mode:
 
176
         *
 
177
         * If TRUE, show a button and only emit the 'search' signal when
 
178
         * the user presses it rather than when they stop typing.
 
179
         */
 
180
        g_object_class_install_property (object_class,
 
181
                                         PROP_EXPLICIT_MODE,
 
182
                                         g_param_spec_boolean ("explicit-mode",
 
183
                                                               "explicit mode",
 
184
                                                               "whether in explicit search mode or not",
 
185
                                                               FALSE,
 
186
                                                               G_PARAM_READWRITE));
 
187
        /**
 
188
         * RBSearchEntry:has-popup:
 
189
         *
 
190
         * If TRUE, show a primary icon and emit the show-popup when clicked.
 
191
         */
 
192
        g_object_class_install_property (object_class,
 
193
                                         PROP_HAS_POPUP,
 
194
                                         g_param_spec_boolean ("has-popup",
 
195
                                                               "has popup",
 
196
                                                               "whether to display the search menu icon",
 
197
                                                               FALSE,
 
198
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
199
 
138
200
        g_type_class_add_private (klass, sizeof (RBSearchEntryPrivate));
139
201
}
140
202
 
141
203
static void
142
204
rb_search_entry_init (RBSearchEntry *entry)
143
205
{
144
 
        GtkWidget *label;
145
 
        GtkSettings *settings;
146
 
        char *theme;
147
 
 
148
206
        entry->priv = RB_SEARCH_ENTRY_GET_PRIVATE (entry);
149
 
 
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;
154
 
        g_free (theme);
155
 
 
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);
160
 
 
 
207
}
 
208
 
 
209
static void
 
210
rb_search_entry_constructed (GObject *object)
 
211
{
 
212
        RBSearchEntry *entry;
 
213
 
 
214
        RB_CHAIN_GOBJECT_METHOD (rb_search_entry_parent_class, constructed, object);
 
215
 
 
216
        entry = RB_SEARCH_ENTRY (object);
 
217
 
 
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,
164
 
                                       GTK_STOCK_CLEAR);
 
220
        g_signal_connect_object (GTK_ENTRY (entry->priv->entry),
 
221
                                 "icon-press",
 
222
                                 G_CALLBACK (rb_search_entry_clear_cb),
 
223
                                 entry, 0);
 
224
 
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),
169
 
                                 "icon-press",
170
 
                                 G_CALLBACK (rb_search_entry_clear_cb),
171
 
                                 entry, 0);
172
 
 
173
 
        gtk_label_set_mnemonic_widget (GTK_LABEL (label),
174
 
                                       entry->priv->entry);
 
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"));
 
235
        } else {
 
236
                gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->entry),
 
237
                                                   GTK_ENTRY_ICON_SECONDARY,
 
238
                                                   "edit-find-symbolic");
 
239
        }
175
240
 
176
241
        gtk_box_pack_start (GTK_BOX (entry), entry->priv->entry, TRUE, TRUE, 0);
177
242
 
187
252
                                 "activate",
188
253
                                 G_CALLBACK (rb_search_entry_activate_cb),
189
254
                                 entry, 0);
 
255
 
 
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,
 
260
                                 "clicked",
 
261
                                 G_CALLBACK (button_clicked_cb),
 
262
                                 entry, 0);
 
263
}
 
264
 
 
265
static void
 
266
rb_search_entry_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 
267
{
 
268
        RBSearchEntry *entry = RB_SEARCH_ENTRY (object);
 
269
 
 
270
        switch (prop_id) {
 
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);
 
275
                break;
 
276
        case PROP_HAS_POPUP:
 
277
                entry->priv->has_popup = g_value_get_boolean (value);
 
278
                break;
 
279
        default:
 
280
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
281
                break;
 
282
        }
 
283
}
 
284
 
 
285
static void
 
286
rb_search_entry_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 
287
{
 
288
        RBSearchEntry *entry = RB_SEARCH_ENTRY (object);
 
289
 
 
290
        switch (prop_id) {
 
291
        case PROP_EXPLICIT_MODE:
 
292
                g_value_set_boolean (value, entry->priv->explicit_mode);
 
293
                break;
 
294
        case PROP_HAS_POPUP:
 
295
                g_value_set_boolean (value, entry->priv->has_popup);
 
296
                break;
 
297
        default:
 
298
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
299
                break;
 
300
        }
190
301
}
191
302
 
192
303
static void
212
323
 * Return value: new search entry widget.
213
324
 */
214
325
RBSearchEntry *
215
 
rb_search_entry_new (void)
 
326
rb_search_entry_new (gboolean has_popup)
216
327
{
217
328
        RBSearchEntry *entry;
218
329
 
219
330
        entry = RB_SEARCH_ENTRY (g_object_new (RB_TYPE_SEARCH_ENTRY,
220
331
                                               "spacing", 5,
 
332
                                               "has-popup", has_popup,
 
333
                                               "hexpand", TRUE,
221
334
                                               NULL));
222
335
 
223
336
        g_return_val_if_fail (entry->priv != NULL, NULL);
262
375
                            text ? text : "");
263
376
}
264
377
 
 
378
/**
 
379
 * rb_search_entry_set_placeholder:
 
380
 * @entry: a #RBSearchEntry
 
381
 * @text: placeholder text
 
382
 *
 
383
 * Sets the placeholder text in the search entry box.
 
384
 */
 
385
void
 
386
rb_search_entry_set_placeholder (RBSearchEntry *entry, const char *text)
 
387
{
 
388
        gtk_entry_set_placeholder_text (GTK_ENTRY (entry->priv->entry), text);
 
389
}
 
390
 
265
391
static void
266
 
rb_search_entry_check_style (RBSearchEntry *entry)
 
392
rb_search_entry_update_icons (RBSearchEntry *entry)
267
393
{
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,};
272
 
        const gchar* text;
273
 
 
274
 
        if (entry->priv->is_a11y_theme)
275
 
                return;
276
 
 
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;
282
 
        }
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;
287
 
        }
288
 
 
289
 
        text = gtk_entry_get_text (GTK_ENTRY (entry->priv->entry));
290
 
        if (text && *text) {
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);
294
 
        } else {
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);
298
 
        }
299
 
 
300
 
        gtk_widget_queue_draw (GTK_WIDGET (entry));
 
394
        const char *text;
 
395
        const char *icon;
 
396
        gboolean searching;
 
397
 
 
398
        if (entry->priv->explicit_mode) {
 
399
                searching = entry->priv->searching;
 
400
        } else {
 
401
                text = gtk_entry_get_text (GTK_ENTRY (entry->priv->entry));
 
402
                searching = (text && *text);
 
403
        }
 
404
 
 
405
        if (searching) {
 
406
                icon = "edit-clear-symbolic";
 
407
        } else if (entry->priv->has_popup) {
 
408
                /* we already use 'find' as the primary icon */
 
409
                icon = NULL;
 
410
        } else {
 
411
                icon = "edit-find-symbolic";
 
412
        }
 
413
        gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->entry),
 
414
                                           GTK_ENTRY_ICON_SECONDARY,
 
415
                                           icon);
301
416
}
302
417
 
303
418
static void
304
419
rb_search_entry_changed_cb (GtkEditable *editable,
305
420
                            RBSearchEntry *entry)
306
421
{
307
 
        rb_search_entry_check_style (entry);
 
422
        const char *text;
308
423
 
309
 
        if (entry->priv->clearing == TRUE)
 
424
        if (entry->priv->clearing == TRUE) {
 
425
                entry->priv->searching = FALSE;
 
426
                rb_search_entry_update_icons (entry);
310
427
                return;
 
428
        }
311
429
 
312
430
        if (entry->priv->timeout != 0) {
313
431
                g_source_remove (entry->priv->timeout);
315
433
        }
316
434
 
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);
320
 
        else
 
440
        } else {
 
441
                entry->priv->searching = FALSE;
 
442
                gtk_widget_set_sensitive (entry->priv->button, FALSE);
321
443
                rb_search_entry_timeout_cb (entry);
 
444
        }
 
445
        rb_search_entry_update_icons (entry);
322
446
}
323
447
 
324
448
static gboolean
325
449
rb_search_entry_timeout_cb (RBSearchEntry *entry)
326
450
{
 
451
        const char *text;
327
452
        gdk_threads_enter ();
328
453
 
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));
 
455
 
 
456
        if (entry->priv->explicit_mode == FALSE) {
 
457
                g_signal_emit (G_OBJECT (entry), rb_search_entry_signals[SEARCH], 0, text);
 
458
        }
331
459
        entry->priv->timeout = 0;
332
460
 
333
461
        gdk_threads_leave ();
346
474
        g_source_remove (entry->priv->timeout);
347
475
        entry->priv->timeout = 0;
348
476
 
349
 
        g_signal_emit (G_OBJECT (entry), rb_search_entry_signals[SEARCH], 0,
350
 
                       gtk_entry_get_text (GTK_ENTRY (entry->priv->entry)));
 
477
        if (entry->priv->explicit_mode == FALSE) {
 
478
                g_signal_emit (G_OBJECT (entry), rb_search_entry_signals[SEARCH], 0,
 
479
                               gtk_entry_get_text (GTK_ENTRY (entry->priv->entry)));
 
480
        }
351
481
 
352
482
        return FALSE;
353
483
}
363
493
gboolean
364
494
rb_search_entry_searching (RBSearchEntry *entry)
365
495
{
366
 
        return strcmp ("", gtk_entry_get_text (GTK_ENTRY (entry->priv->entry))) != 0;
 
496
        if (entry->priv->explicit_mode) {
 
497
                return entry->priv->searching;
 
498
        } else {
 
499
                return strcmp ("", gtk_entry_get_text (GTK_ENTRY (entry->priv->entry))) != 0;
 
500
        }
367
501
}
368
502
 
369
503
static void
370
504
rb_search_entry_activate_cb (GtkEntry *gtkentry,
371
505
                             RBSearchEntry *entry)
372
506
{
 
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)));
375
511
}
376
512
 
 
513
static void
 
514
button_clicked_cb (GtkButton *button, RBSearchEntry *entry)
 
515
{
 
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)));
 
520
}
 
521
 
377
522
/**
378
523
 * rb_search_entry_grab_focus:
379
524
 * @entry: a #RBSearchEntry
387
532
}
388
533
 
389
534
static void
 
535
rb_search_entry_widget_grab_focus (GtkWidget *widget)
 
536
{
 
537
        rb_search_entry_grab_focus (RB_SEARCH_ENTRY (widget));
 
538
}
 
539
 
 
540
static void
390
541
rb_search_entry_clear_cb (GtkEntry *entry,
391
542
                          GtkEntryIconPosition icon_pos,
392
543
                          GdkEvent *event,
393
544
                          RBSearchEntry *search_entry)
394
545
{
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);
 
548
        } else {
 
549
                rb_search_entry_set_text (search_entry, "");
 
550
        }
 
551
}
 
552
 
 
553
/**
 
554
 * rb_search_entry_set_mnemonic:
 
555
 * @entry: a #RBSearchEntry
 
556
 * @enable: if %TRUE, enable the mnemonic
 
557
 *
 
558
 * Adds or removes a mnemonic allowing the user to focus
 
559
 * the search entry.
 
560
 */
 
561
void
 
562
rb_search_entry_set_mnemonic (RBSearchEntry *entry, gboolean enable)
 
563
{
 
564
        GtkWidget *toplevel;
 
565
        guint keyval;
 
566
        gunichar accel = 0;
 
567
 
 
568
        if (pango_parse_markup (_("_Search:"), -1, '_', NULL, NULL, &accel, NULL) && accel != 0) {
 
569
                keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel));
 
570
        } else {
 
571
                keyval = gdk_unicode_to_keyval ('s');
 
572
        }
 
573
 
 
574
        toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry));
 
575
        if (gtk_widget_is_toplevel (toplevel)) {
 
576
                if (enable) {
 
577
                        gtk_window_add_mnemonic (GTK_WINDOW (toplevel), keyval, entry->priv->entry);
 
578
                } else {
 
579
                        gtk_window_remove_mnemonic (GTK_WINDOW (toplevel), keyval, entry->priv->entry);
 
580
                }
 
581
        }
396
582
}