1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  *  Copyright (C) 2010  Jonathan Matthew <jonathan@d14n.org>
4  *  Copyright (C) 2006  William Jon McCann <mccann@jhu.edu>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
12  *  GStreamer plugins to be used and distributed together with GStreamer
13  *  and Rhythmbox. This permission is above and beyond the permissions granted
14  *  by the GPL license by which Rhythmbox is covered. If you modify this code
15  *  you may extend this exception to your version of the code, but you are not
16  *  obligated to do so. If you do not wish to do so, delete this exception
17  *  statement from your version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
27  *
28  */
29 
30 #include "config.h"
31 
32 #include <string.h>
33 #include <glib.h>
34 #include <glib/gi18n.h>
35 
36 #include "rb-util.h"
37 
38 #include "rb-display-page-group.h"
39 #include "rb-display-page-tree.h"
40 
41 /**
42  * SECTION:rb-display-page-group
43  * @short_description: Display page grouping
44  *
45  * Page groups define sections of the display page tree.  A page group
46  * consists of an internal name, a display name, and a category.
47  * The internal name can be used to locate a registered page group.
48  * The category is used to sort the page groups.
49  *
50  * While #RBDisplayPageGroup is a subclass of #RBDisplayPage, by default page
51  * groups are never selectable so they have no content.
52  */
53 
54 G_LOCK_DEFINE_STATIC (display_page_groups);
55 
56 enum {
57 	PROP_0,
58 	PROP_ID,
59 	PROP_CATEGORY,
60 	PROP_LOADED
61 };
62 
63 struct _RBDisplayPageGroupPrivate
64 {
65 	char *id;
66 	RBDisplayPageGroupCategory category;
67 	gboolean loaded;
68 };
69 
G_DEFINE_TYPE(RBDisplayPageGroup,rb_display_page_group,RB_TYPE_DISPLAY_PAGE)70 G_DEFINE_TYPE (RBDisplayPageGroup, rb_display_page_group, RB_TYPE_DISPLAY_PAGE)
71 
72 static GHashTable *display_page_groups_map;
73 
74 /**
75  * rb_display_page_group_add_core_groups:
76  * @shell: the #RBShell
77  * @page_model: the #RBDisplayPageModel
78  *
79  * Registers core page groups.
80  */
81 void
82 rb_display_page_group_add_core_groups (GObject *shell, RBDisplayPageModel *page_model)
83 {
84 	RBDisplayPageGroup *group;
85 
86 	group = rb_display_page_group_new (shell, "library", _("Library"), RB_DISPLAY_PAGE_GROUP_CATEGORY_FIXED);
87 	rb_display_page_model_add_page (page_model, RB_DISPLAY_PAGE (group), NULL);
88 
89 	group = rb_display_page_group_new (shell, "stores", _("Stores"), RB_DISPLAY_PAGE_GROUP_CATEGORY_FIXED);
90 	rb_display_page_model_add_page (page_model, RB_DISPLAY_PAGE (group), NULL);
91 
92 	group = rb_display_page_group_new (shell, "playlists", _("Playlists"), RB_DISPLAY_PAGE_GROUP_CATEGORY_PERSISTENT);
93 	rb_display_page_model_add_page (page_model, RB_DISPLAY_PAGE (group), NULL);
94 
95 	group = rb_display_page_group_new (shell, "devices", _("Devices"), RB_DISPLAY_PAGE_GROUP_CATEGORY_REMOVABLE);
96 	rb_display_page_model_add_page (page_model, RB_DISPLAY_PAGE (group), NULL);
97 	rb_display_page_group_loaded (group);
98 
99 	group = rb_display_page_group_new (shell, "shared", _("Shared"), RB_DISPLAY_PAGE_GROUP_CATEGORY_TRANSIENT);
100 	rb_display_page_model_add_page (page_model, RB_DISPLAY_PAGE (group), NULL);
101 	rb_display_page_group_loaded (group);
102 }
103 
104 /**
105  * rb_display_page_group_get_by_id:
106  * @id: name of page group to find
107  *
108  * Locates a page group by name.  If the page group has not been registered yet,
109  * returns NULL instead.
110  *
111  * Return value: (transfer none): existing page group, or NULL.
112  */
113 RBDisplayPageGroup *
rb_display_page_group_get_by_id(const char * id)114 rb_display_page_group_get_by_id (const char *id)
115 {
116 	RBDisplayPageGroup *group;
117 
118 	group = NULL;
119 
120 	G_LOCK (display_page_groups);
121 	if (display_page_groups_map) {
122 		group = g_hash_table_lookup (display_page_groups_map, id);
123 	}
124 	G_UNLOCK (display_page_groups);
125 
126 	return group;
127 }
128 
129 /**
130  * rb_display_page_group_loaded:
131  * @group: a #RBDisplayPageGroup
132  *
133  * Called when the page group is fully loaded, that is, all initial pages have
134  * been added.
135  */
136 void
rb_display_page_group_loaded(RBDisplayPageGroup * group)137 rb_display_page_group_loaded (RBDisplayPageGroup *group)
138 {
139 	group->priv->loaded = TRUE;
140 	g_object_notify (G_OBJECT (group), "loaded");
141 }
142 
143 /**
144  * rb_display_page_group_new:
145  * @shell: the #RBShell
146  * @id: name of the page group (untranslated, used in code)
147  * @name: display name of the page group (translated)
148  * @category: category for the page group
149  *
150  * Creates a new page group object.  The group will be registered
151  * before it is returned.
152  *
153  * Return value: new page group
154  */
155 RBDisplayPageGroup *
rb_display_page_group_new(GObject * shell,const char * id,const char * name,RBDisplayPageGroupCategory category)156 rb_display_page_group_new (GObject *shell,
157 			   const char *id,
158 			   const char *name,
159 			   RBDisplayPageGroupCategory category)
160 {
161 	return g_object_new (RB_TYPE_DISPLAY_PAGE_GROUP,
162 			     "shell", shell,
163 			     "id", id,
164 			     "name", name,
165 			     "category", category,
166 			     NULL);
167 }
168 
169 static gboolean
impl_selectable(RBDisplayPage * page)170 impl_selectable (RBDisplayPage *page)
171 {
172 	return FALSE;
173 }
174 
175 static void
impl_activate(RBDisplayPage * page)176 impl_activate (RBDisplayPage *page)
177 {
178 	RBDisplayPageTree *display_page_tree;
179 	RBShell *shell;
180 
181 	g_object_get (page, "shell", &shell, NULL);
182 	g_object_get (shell, "display-page-tree", &display_page_tree, NULL);
183 	rb_display_page_tree_toggle_expanded (display_page_tree, page);
184 	g_object_unref (display_page_tree);
185 	g_object_unref (shell);
186 }
187 
188 /**
189  * RBDisplayPageGroupType:
190  * @RB_DISPLAY_PAGE_GROUP_CATEGORY_FIXED: Fixed single instance sources (e.g., library)
191  * @RB_DISPLAY_PAGE_GROUP_CATEGORY_PERSISTENT: Persistent multiple-instance sources (e.g. playlists)
192  * @RB_DISPLAY_PAGE_GROUP_CATEGORY_REMOVABLE: Sources representing removable devices
193  * @RB_DISPLAY_PAGE_GROUP_CATEGORY_TRANSIENT: Transient sources (e.g. network shares)
194  * @RB_DISPLAY_PAGE_GROUP_CATEGORY_TOOLS: Utility (ie non-source) pages
195  *
196  * Predefined categories of page group. The order they're defined here is the order they
197  * appear in the page tree.
198  */
199 
200 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
201 GType
rb_display_page_group_category_get_type(void)202 rb_display_page_group_category_get_type (void)
203 {
204 	static GType etype = 0;
205 
206 	if (etype == 0) {
207 		static const GEnumValue values[] = {
208 			ENUM_ENTRY (RB_DISPLAY_PAGE_GROUP_CATEGORY_FIXED, "fixed"),
209 			ENUM_ENTRY (RB_DISPLAY_PAGE_GROUP_CATEGORY_PERSISTENT, "persistent"),
210 			ENUM_ENTRY (RB_DISPLAY_PAGE_GROUP_CATEGORY_REMOVABLE, "removable"),
211 			ENUM_ENTRY (RB_DISPLAY_PAGE_GROUP_CATEGORY_TRANSIENT, "transient"),
212 			ENUM_ENTRY (RB_DISPLAY_PAGE_GROUP_CATEGORY_TOOLS, "tools"),
213 			{ 0, 0, 0 }
214 		};
215 
216 		etype = g_enum_register_static ("RBDisplayPageGroupType", values);
217 	}
218 
219 	return etype;
220 }
221 
222 static void
impl_finalize(GObject * object)223 impl_finalize (GObject *object)
224 {
225 	RBDisplayPageGroup *group = RB_DISPLAY_PAGE_GROUP (object);
226 
227 	g_free (group->priv->id);
228 	/* remove from group map?  can this ever happen? */
229 
230 	G_OBJECT_CLASS (rb_display_page_group_parent_class)->finalize (object);
231 }
232 
233 static void
impl_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)234 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
235 {
236 	RBDisplayPageGroup *group = RB_DISPLAY_PAGE_GROUP (object);
237 	switch (prop_id) {
238 	case PROP_ID:
239 		g_value_set_string (value, group->priv->id);
240 		break;
241 	case PROP_CATEGORY:
242 		g_value_set_enum (value, group->priv->category);
243 		break;
244 	case PROP_LOADED:
245 		g_value_set_boolean (value, group->priv->loaded);
246 		break;
247 	default:
248 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
249 		break;
250 	}
251 }
252 
253 static void
impl_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)254 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
255 {
256 	RBDisplayPageGroup *group = RB_DISPLAY_PAGE_GROUP (object);
257 	switch (prop_id) {
258 	case PROP_ID:
259 		group->priv->id = g_value_dup_string (value);
260 		break;
261 	case PROP_CATEGORY:
262 		group->priv->category = g_value_get_enum (value);
263 		break;
264 	default:
265 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
266 		break;
267 	}
268 }
269 
270 static void
impl_constructed(GObject * object)271 impl_constructed (GObject *object)
272 {
273 	RBDisplayPageGroup *group;
274 
275 	RB_CHAIN_GOBJECT_METHOD (rb_display_page_group_parent_class, constructed, object);
276 
277 	group = RB_DISPLAY_PAGE_GROUP (object);
278 
279 	/* register the new group */
280 	G_LOCK (display_page_groups);
281 	g_assert (g_hash_table_lookup (display_page_groups_map, group->priv->id) == NULL);
282 	g_hash_table_insert (display_page_groups_map, g_strdup (group->priv->id), group);
283 	G_UNLOCK (display_page_groups);
284 }
285 
286 static void
rb_display_page_group_init(RBDisplayPageGroup * group)287 rb_display_page_group_init (RBDisplayPageGroup *group)
288 {
289 	group->priv = G_TYPE_INSTANCE_GET_PRIVATE (group,
290 						   RB_TYPE_DISPLAY_PAGE_GROUP,
291 						   RBDisplayPageGroupPrivate);
292 }
293 
294 static void
rb_display_page_group_class_init(RBDisplayPageGroupClass * klass)295 rb_display_page_group_class_init (RBDisplayPageGroupClass *klass)
296 {
297 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
298 	RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (klass);
299 
300 	G_LOCK (display_page_groups);
301 	if (display_page_groups_map == NULL) {
302 		display_page_groups_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
303 	}
304 	G_UNLOCK (display_page_groups);
305 
306 	object_class->constructed = impl_constructed;
307 	object_class->finalize = impl_finalize;
308 	object_class->set_property = impl_set_property;
309 	object_class->get_property = impl_get_property;
310 
311 	page_class->selectable = impl_selectable;
312 	page_class->activate = impl_activate;
313 
314 	/**
315 	 * RBDisplayPageGroup:id:
316 	 *
317 	 * Internal (untranslated) name for the page group
318 	 */
319 	g_object_class_install_property (object_class,
320 					 PROP_ID,
321 					 g_param_spec_string ("id",
322 							      "identifier",
323 							      "identifier",
324 							      NULL,
325 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
326 	/**
327 	 * RBDisplayPageGroup:category:
328 	 *
329 	 * Page group category that the group falls into
330 	 */
331 	g_object_class_install_property (object_class,
332 					 PROP_CATEGORY,
333 					 g_param_spec_enum ("category",
334 							    "category",
335 							    "page group category",
336 							    RB_TYPE_DISPLAY_PAGE_GROUP_CATEGORY,
337 							    RB_DISPLAY_PAGE_GROUP_CATEGORY_FIXED,
338 							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
339 	/**
340 	 * RBDisplayPageProperty:loaded:
341 	 *
342 	 * Set to %TRUE once the initial set of pages have been added to the group
343 	 */
344 	g_object_class_install_property (object_class,
345 					 PROP_LOADED,
346 					 g_param_spec_boolean ("loaded",
347 							       "loaded",
348 							       "Whether the group is loaded",
349 							       FALSE,
350 							       G_PARAM_READABLE));
351 
352 	g_type_class_add_private (klass, sizeof (RBDisplayPageGroupPrivate));
353 }
354