hythmbox-2.98/widgets/rb-source-toolbar.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2011 Jonathan Matthew <jonathan@d14n.org>
  4  *
  5  *  This program is free software; you can redistribute it and/or modify
  6  *  it under the terms of the GNU General Public License as published by
  7  *  the Free Software Foundation; either version 2 of the License, or
  8  *  (at your option) any later version.
  9  *
 10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 11  *  GStreamer plugins to be used and distributed together with GStreamer
 12  *  and Rhythmbox. This permission is above and beyond the permissions granted
 13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 14  *  you may extend this exception to your version of the code, but you are not
 15  *  obligated to do so. If you do not wish to do so, delete this exception
 16  *  statement from your version.
 17  *
 18  *  This program is distributed in the hope that it will be useful,
 19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  *  GNU General Public License for more details.
 22  *
 23  *  You should have received a copy of the GNU General Public License
 24  *  along with this program; if not, write to the Free Software
 25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 26  *
 27  */
 28 
 29 #include <config.h>
 30 
 31 #include <widgets/rb-source-toolbar.h>
 32 #include <lib/rb-util.h>
 33 
 34 static void rb_source_toolbar_class_init (RBSourceToolbarClass *klass);
 35 static void rb_source_toolbar_init (RBSourceToolbar *toolbar);
 36 
 37 static void toolbar_add_widget_cb (GtkUIManager *ui_manager, GtkWidget *widget, RBSourceToolbar *toolbar);
 38 static void popup_add_widget_cb (GtkUIManager *ui_manager, GtkWidget *widget, RBSourceToolbar *toolbar);
 39 
 40 struct _RBSourceToolbarPrivate
 41 {
 42 	GtkUIManager *ui_manager;
 43 	RBSource *source;
 44 	RBSearchEntry *search_entry;
 45 	GtkWidget *search_popup;
 46 	GtkWidget *toolbar;
 47 	GBinding *browse_binding;
 48 	GtkAction *browse_action;
 49 	char *popup_path;
 50 
 51 	/* search state */
 52 	int search_value;
 53 	gulong search_change_cb_id;
 54 	RBSourceSearch *active_search;
 55 	char *search_text;
 56 	GtkRadioAction *search_group;
 57 };
 58 
 59 G_DEFINE_TYPE (RBSourceToolbar, rb_source_toolbar, GTK_TYPE_GRID)
 60 
 61 /**
 62  * SECTION:rb-source-toolbar
 63  * @short_description: toolbar+search entry for sources
 64  *
 65  * This class combines a toolbar for custom source actions with a
 66  * search entry.  The toolbar content is specified using a UI path.
 67  * The #RBSourceToolbar takes care of preserving search state when
 68  * the selected page changes, and performs searches when the user
 69  * selects a new search type or changes the search text.
 70  */
 71 
 72 enum
 73 {
 74 	PROP_0,
 75 	PROP_SOURCE,
 76 	PROP_UI_MANAGER,
 77 };
 78 
 79 static void
 80 prepare_toolbar (GtkWidget *toolbar)
 81 {
 82 	static GtkCssProvider *provider = NULL;
 83 
 84 	if (provider == NULL) {
 85 		const char *style =
 86 			"GtkToolbar {\n"
 87                         "       -GtkToolbar-shadow-type: none;\n"
 88                         "       border-style: none;\n"
 89                         "}";
 90 
 91 		provider = gtk_css_provider_new ();
 92 		gtk_css_provider_load_from_data (provider, style, -1, NULL);
 93 	}
 94 
 95 	gtk_style_context_add_provider (gtk_widget_get_style_context (toolbar),
 96 					GTK_STYLE_PROVIDER (provider),
 97 					GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 98 
 99 	gtk_widget_set_hexpand (toolbar, TRUE);
100 
101 	gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_TEXT);
102 }
103 
104 static void
105 search_change_cb (GtkRadioAction *group, GtkRadioAction *current, RBSourceToolbar *toolbar)
106 {
107 	toolbar->priv->active_search = rb_source_search_get_from_action (G_OBJECT (current));
108 
109 	if (toolbar->priv->search_text != NULL) {
110 		rb_source_search (toolbar->priv->source, toolbar->priv->active_search, NULL, toolbar->priv->search_text);
111 	}
112 
113 	rb_search_entry_set_placeholder (toolbar->priv->search_entry, gtk_action_get_label (GTK_ACTION (current)));
114 }
115 
116 static void
117 source_selected_cb (GObject *object, GParamSpec *pspec, RBSourceToolbar *toolbar)
118 {
119 	gboolean selected;
120 
121 	g_object_get (object, "selected", &selected, NULL);
122 
123 	if (selected) {
124 		char *toolbar_path;
125 		char *browse_path;
126 
127 		if (toolbar->priv->toolbar != NULL) {
128 			gtk_grid_attach (GTK_GRID (toolbar), toolbar->priv->toolbar, 0, 0, 2, 1);
129 			gtk_widget_show_all (GTK_WIDGET (toolbar->priv->toolbar));
130 		}
131 
132 		if (toolbar->priv->search_entry != NULL) {
133 			rb_search_entry_set_mnemonic (toolbar->priv->search_entry, TRUE);
134 
135 			gtk_widget_add_accelerator (GTK_WIDGET (toolbar->priv->search_entry),
136 						    "grab-focus",
137 						    gtk_ui_manager_get_accel_group (toolbar->priv->ui_manager),
138 						    gdk_unicode_to_keyval ('f'),
139 						    GDK_CONTROL_MASK,
140 						    0);
141 		}
142 
143 		if (toolbar->priv->search_group != NULL) {
144 			if (toolbar->priv->search_value != -1) {
145 				gtk_radio_action_set_current_value (toolbar->priv->search_group,
146 								    toolbar->priv->search_value);
147 			}
148 
149 			toolbar->priv->search_change_cb_id = g_signal_connect (toolbar->priv->search_group,
150 									       "changed",
151 									       G_CALLBACK (search_change_cb),
152 									       toolbar);
153 		}
154 
155 		g_object_get (toolbar->priv->source, "toolbar-path", &toolbar_path, NULL);
156 		if (toolbar_path != NULL) {
157 
158 			browse_path = g_strdup_printf ("%s/Browse", toolbar_path);
159 			toolbar->priv->browse_action = gtk_ui_manager_get_action (toolbar->priv->ui_manager,
160 										  browse_path);
161 			g_free (browse_path);
162 
163 			if (toolbar->priv->browse_action != NULL) {
164 				toolbar->priv->browse_binding =
165 					g_object_bind_property (toolbar->priv->source, "show-browser",
166 								toolbar->priv->browse_action, "active",
167 								G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
168 				gtk_action_connect_accelerator (toolbar->priv->browse_action);
169 			}
170 			g_free (toolbar_path);
171 		} else {
172 			toolbar->priv->browse_action = NULL;
173 		}
174 	} else {
175 		if (toolbar->priv->toolbar != NULL) {
176 			gtk_container_remove (GTK_CONTAINER (toolbar), toolbar->priv->toolbar);
177 		}
178 
179 		if (toolbar->priv->search_entry != NULL) {
180 			rb_search_entry_set_mnemonic (toolbar->priv->search_entry, FALSE);
181 
182 			gtk_widget_remove_accelerator (GTK_WIDGET (toolbar->priv->search_entry),
183 						       gtk_ui_manager_get_accel_group (toolbar->priv->ui_manager),
184 						       gdk_unicode_to_keyval ('f'),
185 						       GDK_CONTROL_MASK);
186 		}
187 
188 		if (toolbar->priv->search_group != NULL) {
189 			if (toolbar->priv->search_change_cb_id != 0) {
190 				g_signal_handler_disconnect (toolbar->priv->search_group,
191 							     toolbar->priv->search_change_cb_id);
192 			}
193 
194 			toolbar->priv->search_value = gtk_radio_action_get_current_value (toolbar->priv->search_group);
195 		}
196 
197 		if (toolbar->priv->browse_binding != NULL) {
198 			g_object_unref (toolbar->priv->browse_binding);
199 			toolbar->priv->browse_binding = NULL;
200 		}
201 
202 		if (toolbar->priv->browse_action != NULL) {
203 			gtk_action_disconnect_accelerator (toolbar->priv->browse_action);
204 			toolbar->priv->browse_action = NULL;
205 		}
206 	}
207 }
208 
209 static void
210 search_cb (RBSearchEntry *search_entry, const char *text, RBSourceToolbar *toolbar)
211 {
212 	rb_source_search (toolbar->priv->source, toolbar->priv->active_search, toolbar->priv->search_text, text);
213 
214 	g_free (toolbar->priv->search_text);
215 	toolbar->priv->search_text = NULL;
216 	if (text != NULL) {
217 		toolbar->priv->search_text = g_strdup (text);
218 	}
219 }
220 
221 static void
222 show_popup_cb (RBSearchEntry *search_entry, RBSourceToolbar *toolbar)
223 {
224 	gtk_menu_popup (GTK_MENU (toolbar->priv->search_popup),
225 			NULL, NULL, NULL, NULL, 3,
226 			gtk_get_current_event_time ());
227 }
228 
229 
230 static void
231 impl_finalize (GObject *object)
232 {
233 	RBSourceToolbar *toolbar = RB_SOURCE_TOOLBAR (object);
234 
235 	g_free (toolbar->priv->search_text);
236 	g_free (toolbar->priv->popup_path);
237 
238 	G_OBJECT_CLASS (rb_source_toolbar_parent_class)->finalize (object);
239 }
240 
241 static void
242 impl_dispose (GObject *object)
243 {
244 	RBSourceToolbar *toolbar = RB_SOURCE_TOOLBAR (object);
245 
246 	if (toolbar->priv->ui_manager != NULL) {
247 		g_signal_handlers_disconnect_by_func (toolbar->priv->ui_manager, G_CALLBACK (popup_add_widget_cb), toolbar);
248 		g_signal_handlers_disconnect_by_func (toolbar->priv->ui_manager, G_CALLBACK (toolbar_add_widget_cb), toolbar);
249 
250 		g_object_unref (toolbar->priv->ui_manager);
251 		toolbar->priv->ui_manager = NULL;
252 	}
253 	if (toolbar->priv->search_popup != NULL) {
254 		g_object_unref (toolbar->priv->search_popup);
255 		toolbar->priv->search_popup = NULL;
256 	}
257 	if (toolbar->priv->toolbar != NULL) {
258 		g_object_unref (toolbar->priv->toolbar);
259 		toolbar->priv->toolbar = NULL;
260 	}
261 	if (toolbar->priv->browse_binding != NULL) {
262 		g_object_unref (toolbar->priv->browse_binding);
263 		toolbar->priv->browse_binding = NULL;
264 	}
265 
266 	G_OBJECT_CLASS (rb_source_toolbar_parent_class)->dispose (object);
267 }
268 
269 static void
270 toolbar_add_widget_cb (GtkUIManager *ui_manager, GtkWidget *widget, RBSourceToolbar *toolbar)
271 {
272 	char *toolbar_path;
273 	gboolean selected;
274 
275 	g_object_get (toolbar->priv->source, "toolbar-path", &toolbar_path, "selected", &selected, NULL);
276 	toolbar->priv->toolbar = gtk_ui_manager_get_widget (toolbar->priv->ui_manager, toolbar_path);
277 	g_free (toolbar_path);
278 
279 	if (toolbar->priv->toolbar) {
280 		g_object_ref (toolbar->priv->toolbar);
281 		g_signal_handlers_disconnect_by_func (ui_manager, G_CALLBACK (toolbar_add_widget_cb), toolbar);
282 
283 		prepare_toolbar (toolbar->priv->toolbar);
284 
285 		if (selected) {
286 			gtk_grid_attach (GTK_GRID (toolbar), toolbar->priv->toolbar, 0, 0, 2, 1);
287 			gtk_widget_show_all (GTK_WIDGET (toolbar->priv->toolbar));
288 		}
289 	}
290 }
291 
292 static void
293 impl_constructed (GObject *object)
294 {
295 	RBSourceToolbar *toolbar;
296 	char *toolbar_path;
297 	GtkWidget *blank;
298 
299 	RB_CHAIN_GOBJECT_METHOD (rb_source_toolbar_parent_class, constructed, object);
300 
301 	toolbar = RB_SOURCE_TOOLBAR (object);
302 
303 	g_object_get (toolbar->priv->source, "toolbar-path", &toolbar_path, NULL);
304 	if (toolbar_path) {
305 		toolbar->priv->toolbar = gtk_ui_manager_get_widget (toolbar->priv->ui_manager, toolbar_path);
306 		if (toolbar->priv->toolbar == NULL) {
307 			g_signal_connect (toolbar->priv->ui_manager, "add-widget", G_CALLBACK (toolbar_add_widget_cb), toolbar);
308 		} else {
309 			g_object_ref (toolbar->priv->toolbar);
310 			prepare_toolbar (toolbar->priv->toolbar);
311 		}
312 	} else {
313 		blank = gtk_toolbar_new ();
314 		prepare_toolbar (blank);
315 		gtk_grid_attach (GTK_GRID (toolbar), blank, 0, 0, 2 ,1);
316 	}
317 	g_free (toolbar_path);
318 
319 	/* search entry gets created later if required */
320 
321 	g_signal_connect (toolbar->priv->source, "notify::selected", G_CALLBACK (source_selected_cb), toolbar);
322 }
323 
324 static void
325 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
326 {
327 	RBSourceToolbar *toolbar = RB_SOURCE_TOOLBAR (object);
328 
329 	switch (prop_id) {
330 	case PROP_SOURCE:
331 		g_value_set_object (value, toolbar->priv->source);
332 		break;
333 	case PROP_UI_MANAGER:
334 		g_value_set_object (value, toolbar->priv->ui_manager);
335 		break;
336 	default:
337 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
338 		break;
339 	}
340 }
341 
342 static void
343 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
344 {
345 	RBSourceToolbar *toolbar = RB_SOURCE_TOOLBAR (object);
346 
347 	switch (prop_id) {
348 	case PROP_SOURCE:
349 		toolbar->priv->source = g_value_get_object (value);	/* don't take a ref */
350 		break;
351 	case PROP_UI_MANAGER:
352 		toolbar->priv->ui_manager = g_value_dup_object (value);
353 		break;
354 	default:
355 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356 		break;
357 	}
358 }
359 
360 static void
361 rb_source_toolbar_init (RBSourceToolbar *toolbar)
362 {
363 	toolbar->priv = G_TYPE_INSTANCE_GET_PRIVATE (toolbar, RB_TYPE_SOURCE_TOOLBAR, RBSourceToolbarPrivate);
364 
365 	toolbar->priv->search_value = -1;
366 }
367 
368 static void
369 rb_source_toolbar_class_init (RBSourceToolbarClass *klass)
370 {
371 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
372 
373 	object_class->constructed = impl_constructed;
374 	object_class->dispose = impl_dispose;
375 	object_class->finalize = impl_finalize;
376 	object_class->set_property = impl_set_property;
377 	object_class->get_property = impl_get_property;
378 
379 	/**
380 	 * RBSourceToolbar:source:
381 	 *
382 	 * The #RBSource the toolbar is associated with
383 	 */
384 	g_object_class_install_property (object_class,
385 					 PROP_SOURCE,
386 					 g_param_spec_object ("source",
387 							      "source",
388 							      "RBSource instance",
389 							      RB_TYPE_DISPLAY_PAGE,
390 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
391 	/**
392 	 * RBSourceToolbar:ui-manager:
393 	 *
394 	 * The #GtkUIManager instance
395 	 */
396 	g_object_class_install_property (object_class,
397 					 PROP_UI_MANAGER,
398 					 g_param_spec_object ("ui-manager",
399 							      "ui manager",
400 							      "GtkUIManager instance",
401 							      GTK_TYPE_UI_MANAGER,
402 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
403 	g_type_class_add_private (klass, sizeof (RBSourceToolbarPrivate));
404 }
405 
406 /**
407  * rb_source_toolbar_new:
408  * @page: a #RBDisplayPage
409  * @ui_manager: the #GtkUIManager
410  *
411  * Creates a new source toolbar for @page.  The toolbar does not
412  * initially include a search entry.  Call #rb_source_toolbar_add_search_entry
413  * to add one.  The toolbar content comes from the @RBSource:toolbar-path property.
414  *
415  * Return value: the #RBSourceToolbar
416  */
417 RBSourceToolbar *
418 rb_source_toolbar_new (RBDisplayPage *page, GtkUIManager *ui_manager)
419 {
420 	GObject *object;
421 	object = g_object_new (RB_TYPE_SOURCE_TOOLBAR,
422 			       "source", page,
423 			       "ui-manager", ui_manager,
424 			       "column-spacing", 6,
425 			       "column-homogeneous", TRUE,
426 			       "row-spacing", 6,
427 			       "row-homogeneous", TRUE,
428 			       NULL);
429 	return RB_SOURCE_TOOLBAR (object);
430 }
431 
432 static void
433 setup_search_popup (RBSourceToolbar *toolbar, GtkWidget *popup)
434 {
435 	GList *items;
436 	GSList *l;
437 	int active_value;
438 
439 	toolbar->priv->search_popup = g_object_ref (popup);
440 
441 	items = gtk_container_get_children (GTK_CONTAINER (toolbar->priv->search_popup));
442 	toolbar->priv->search_group = GTK_RADIO_ACTION (gtk_activatable_get_related_action (GTK_ACTIVATABLE (items->data)));
443 	g_list_free (items);
444 
445 	active_value = gtk_radio_action_get_current_value (toolbar->priv->search_group);
446 	for (l = gtk_radio_action_get_group (toolbar->priv->search_group); l != NULL; l = l->next) {
447 		int value;
448 		g_object_get (G_OBJECT (l->data), "value", &value, NULL);
449 		if (value == active_value) {
450 			rb_search_entry_set_placeholder (toolbar->priv->search_entry,
451 							 gtk_action_get_label (GTK_ACTION (l->data)));
452 		}
453 	}
454 
455 	g_signal_connect (toolbar->priv->search_entry, "show-popup", G_CALLBACK (show_popup_cb), toolbar);
456 }
457 
458 static void
459 popup_add_widget_cb (GtkUIManager *ui_manager, GtkWidget *widget, RBSourceToolbar *toolbar)
460 {
461 	GtkWidget *popup;
462 	popup = gtk_ui_manager_get_widget (toolbar->priv->ui_manager, toolbar->priv->popup_path);
463 
464 	if (popup) {
465 		setup_search_popup (toolbar, popup);
466 		g_signal_handlers_disconnect_by_func (ui_manager, G_CALLBACK (popup_add_widget_cb), toolbar);
467 	}
468 }
469 
470 
471 /**
472  * rb_source_toolbar_add_search_entry:
473  * @toolbar: a #RBSourceToolbar
474  * @popup_path: the UI path for the search popup (or NULL)
475  * @placeholder: the placeholder text for the search entry (or NULL)
476  *
477  * Adds a search entry to the toolbar.  If a popup path is specified,
478  * clicking on the primary icon will show a menu allowing the user to
479  * select a search type, and the placeholder text for the entry will
480  * be the selected search description.  Otherwise, the specified placeholder
481  * text will be displayed.
482  */
483 void
484 rb_source_toolbar_add_search_entry (RBSourceToolbar *toolbar, const char *popup_path, const char *placeholder)
485 {
486 	g_assert (toolbar->priv->search_entry == NULL);
487 
488 	toolbar->priv->search_entry = rb_search_entry_new (popup_path != NULL);
489 	gtk_widget_set_margin_right (GTK_WIDGET (toolbar->priv->search_entry), 6);
490 	gtk_grid_attach (GTK_GRID (toolbar), GTK_WIDGET (toolbar->priv->search_entry), 2, 0, 1, 1);
491 
492 	if (placeholder) {
493 		rb_search_entry_set_placeholder (toolbar->priv->search_entry, placeholder);
494 	}
495 
496 	g_signal_connect (toolbar->priv->search_entry, "search", G_CALLBACK (search_cb), toolbar);
497 	/* activate? */
498 
499 	if (popup_path != NULL) {
500 		GtkWidget *popup;
501 		toolbar->priv->popup_path = g_strdup (popup_path);
502 
503 		popup = gtk_ui_manager_get_widget (toolbar->priv->ui_manager, popup_path);
504 		if (popup != NULL) {
505 			setup_search_popup (toolbar, popup);
506 		} else {
507 			g_signal_connect (toolbar->priv->ui_manager, "add-widget", G_CALLBACK (popup_add_widget_cb), toolbar);
508 		}
509 	}
510 }
511 
512 /**
513  * rb_source_toolbar_clear_search_entry:
514  * @toolbar: a #RBSourceToolbar
515  *
516  * Clears the search entry text.  Call this from RBSource:impl_reset_filters.
517  */
518 void
519 rb_source_toolbar_clear_search_entry (RBSourceToolbar *toolbar)
520 {
521 	g_assert (toolbar->priv->search_entry != NULL);
522 	rb_search_entry_clear (toolbar->priv->search_entry);
523 }