1 /*
2 An object to represent loadable indicator modules to make loading
3 them easy and objectified.
4 
5 Copyright 2009 Canonical Ltd.
6 
7 Authors:
8     Ted Gould <ted@canonical.com>
9 
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 version 3.0 as published by the Free Software Foundation.
13 
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License version 3.0 for more details.
18 
19 You should have received a copy of the GNU General Public
20 License along with this library. If not, see
21 <http://www.gnu.org/licenses/>.
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "indicator.h"
29 #include "indicator-object.h"
30 #include "indicator-object-marshal.h"
31 #include "indicator-object-enum-types.h"
32 
33 /**
34 	@ENTRY_INIT: The entry hasn't been initialized yet, so its
35 		visibility will depend upon the inital-visibility property.
36 	@ENTRY_VISIBLE: The entry is visible
37 	@ENTRY_INVISIBLE: The entry is invisible
38 */
39 typedef enum {
40     ENTRY_INIT,
41     ENTRY_VISIBLE,
42     ENTRY_INVISIBLE
43 }
44 EntryVisibility;
45 
46 typedef struct _IndicatorObjectEntryPrivate {
47      EntryVisibility visibility;
48 }
49 IndicatorObjectEntryPrivate;
50 
51 /**
52 	IndicatorObjectPrivate:
53 	@module: The loaded module representing the object.  Note to
54 		subclasses: This will not be set when you're initalized.
55 	@entry: A default entry for objects that don't need all the
56 		fancy stuff.  This works with #get_entries_default.
57 	@gotten_entries: A check to see if the @entry has been
58 		populated intelligently yet.
59 
60 	Structure to define the memory for the private area
61 	of the object instance.
62 */
63 struct _IndicatorObjectPrivate {
64 	GModule * module;
65 
66 	/* For get_entries_default */
67 	IndicatorObjectEntry entry;
68 	gboolean gotten_entries;
69 
70 	/* Whether or not entries are visible by default */
71 	gboolean default_visibility;
72 	GHashTable * entry_privates;
73 
74 	GStrv environments;
75 };
76 
77 #define INDICATOR_OBJECT_GET_PRIVATE(o) (INDICATOR_OBJECT(o)->priv)
78 
79 
80 /* Signals Stuff */
81 enum {
82 	ENTRY_ADDED,
83 	ENTRY_REMOVED,
84 	ENTRY_MOVED,
85 	ENTRY_SCROLLED,
86 	MENU_SHOW,
87 	SHOW_NOW_CHANGED,
88 	ACCESSIBLE_DESC_UPDATE,
89 	SECONDARY_ACTIVATE,
90 	LAST_SIGNAL
91 };
92 
93 /* Properties */
94 /* Enum for the properties so that they can be quickly
95    found and looked up. */
96 enum {
97 	PROP_0,
98 	PROP_GSETTINGS_SCHEMA_ID,
99 	PROP_DEFAULT_VISIBILITY,
100 };
101 
102 
103 static guint signals[LAST_SIGNAL] = { 0 };
104 
105 /* GObject stuff */
106 static void indicator_object_class_init (IndicatorObjectClass *klass);
107 static void indicator_object_init       (IndicatorObject *self);
108 static void indicator_object_dispose    (GObject *object);
109 static void indicator_object_finalize   (GObject *object);
110 static void set_property (GObject*, guint prop_id, const GValue*, GParamSpec* );
111 static void get_property (GObject*, guint prop_id,       GValue*, GParamSpec* );
112 
113 /* entries' visibility */
114 static GList * get_entries_default               (IndicatorObject*);
115 static GList * get_all_entries                   (IndicatorObject*);
116 static void indicator_object_entry_being_removed (IndicatorObject*, IndicatorObjectEntry*);
117 static void indicator_object_entry_was_added     (IndicatorObject*, IndicatorObjectEntry*);
118 static IndicatorObjectEntryPrivate * entry_get_private (IndicatorObject*, IndicatorObjectEntry*);
119 
120 G_DEFINE_TYPE (IndicatorObject, indicator_object, G_TYPE_OBJECT);
121 
122 /* Setup the class and put the functions into the
123    class structure */
124 static void
indicator_object_class_init(IndicatorObjectClass * klass)125 indicator_object_class_init (IndicatorObjectClass *klass)
126 {
127 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
128 
129 	g_type_class_add_private (klass, sizeof (IndicatorObjectPrivate));
130 
131 	object_class->dispose = indicator_object_dispose;
132 	object_class->finalize = indicator_object_finalize;
133 	object_class->set_property = set_property;
134 	object_class->get_property = get_property;
135 
136 	klass->get_label =  NULL;
137 	klass->get_menu  =  NULL;
138 	klass->get_image =  NULL;
139 	klass->get_accessible_desc = NULL;
140 	klass->get_entries = get_entries_default;
141 	klass->get_location = NULL;
142 	klass->entry_being_removed = NULL;
143 	klass->entry_was_added = NULL;
144 
145 	klass->entry_activate = NULL;
146 	klass->entry_activate_window = NULL;
147 	klass->entry_close = NULL;
148 
149 	/**
150 		IndicatorObject::entry-added:
151 		@arg0: The #IndicatorObject object
152 		@arg1: A pointer to the #IndicatorObjectEntry that
153 			is being added.
154 
155 		Signaled when a new entry is added and should
156 		be shown by the person using this object.
157 	*/
158 	signals[ENTRY_ADDED] = g_signal_new (INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED,
159 	                                     G_TYPE_FROM_CLASS(klass),
160 	                                     G_SIGNAL_RUN_LAST,
161 	                                     G_STRUCT_OFFSET (IndicatorObjectClass, entry_added),
162 	                                     NULL, NULL,
163 	                                     g_cclosure_marshal_VOID__POINTER,
164 	                                     G_TYPE_NONE, 1, G_TYPE_POINTER, G_TYPE_NONE);
165 
166 	/**
167 		IndicatorObject::entry-removed:
168 		@arg0: The #IndicatorObject object
169 		@arg1: A pointer to the #IndicatorObjectEntry that
170 			is being removed.
171 
172 		Signaled when an entry is removed and should
173 		be removed by the person using this object.
174 	*/
175 	signals[ENTRY_REMOVED] = g_signal_new (INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED,
176 	                                       G_TYPE_FROM_CLASS(klass),
177 	                                       G_SIGNAL_RUN_LAST,
178 	                                       G_STRUCT_OFFSET (IndicatorObjectClass, entry_removed),
179 	                                       NULL, NULL,
180 	                                       g_cclosure_marshal_VOID__POINTER,
181 	                                       G_TYPE_NONE, 1, G_TYPE_POINTER, G_TYPE_NONE);
182 	/**
183 		IndicatorObject::entry-moved:
184 		@arg0: The #IndicatorObject object
185 		@arg1: A pointer to the #IndicatorObjectEntry that
186 			is being moved.
187 		@arg2: The old location of the entry
188 		@arg3: The new location of the entry
189 
190 		When the order of the entries change, then this signal
191 		is sent to tell the new location.
192 	*/
193 	signals[ENTRY_MOVED] = g_signal_new (INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED,
194 	                                     G_TYPE_FROM_CLASS(klass),
195 	                                     G_SIGNAL_RUN_LAST,
196 	                                     G_STRUCT_OFFSET (IndicatorObjectClass, entry_moved),
197 	                                     NULL, NULL,
198 	                                     _indicator_object_marshal_VOID__POINTER_UINT_UINT,
199 	                                     G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_NONE);
200 	/**
201 		IndicatorObject::entry-scrolled:
202 		@arg0: The #IndicatorObject object
203 		@arg1: A pointer to the #IndicatorObjectEntry that
204 			receives the scroll event.
205 		@arg2: The delta of the scroll event
206 		@arg3: The orientation of the scroll event.
207 
208 		When the indicator receives a mouse scroll wheel event
209 		from the user, this signal is emitted.
210 	*/
211 	signals[ENTRY_SCROLLED] = g_signal_new (INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED,
212 	                                G_TYPE_FROM_CLASS(klass),
213 	                                G_SIGNAL_RUN_LAST,
214 	                                G_STRUCT_OFFSET (IndicatorObjectClass, entry_scrolled),
215 	                                NULL, NULL,
216 	                                _indicator_object_marshal_VOID__POINTER_UINT_ENUM,
217 	                                G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_UINT,
218 	                                INDICATOR_OBJECT_TYPE_SCROLL_DIRECTION);
219 	/**
220 		IndicatorObject::secondary-activate:
221 		@arg0: The #IndicatorObject object
222 		@arg1: A pointer to the #IndicatorObjectEntry that
223 			receives the secondary activate event.
224 		@arg2: The timestamp of the event
225 
226 		When the indicator receives a secondary activation event
227 		from the user, this signal is emitted.
228 	*/
229 	signals[SECONDARY_ACTIVATE] = g_signal_new (INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE,
230 	                                G_TYPE_FROM_CLASS(klass),
231 	                                G_SIGNAL_RUN_LAST,
232 	                                G_STRUCT_OFFSET (IndicatorObjectClass, secondary_activate),
233 	                                NULL, NULL,
234 	                                _indicator_object_marshal_VOID__POINTER_UINT,
235 	                                G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
236 
237 	/**
238 		IndicatorObject::menu-show:
239 		@arg0: The #IndicatorObject object
240 		@arg1: A pointer to the #IndicatorObjectEntry that
241 			is being shown.
242 		@arg2: The timestamp of the event
243 
244 		Used when the indicator wants to signal up the stack
245 		that the menu should be shown.
246 	*/
247 	signals[MENU_SHOW] = g_signal_new (INDICATOR_OBJECT_SIGNAL_MENU_SHOW,
248 	                                   G_TYPE_FROM_CLASS(klass),
249 	                                   G_SIGNAL_RUN_LAST,
250 	                                   G_STRUCT_OFFSET (IndicatorObjectClass, menu_show),
251 	                                   NULL, NULL,
252 	                                   _indicator_object_marshal_VOID__POINTER_UINT,
253 	                                   G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
254 
255 	/**
256 		IndicatorObject::show-now-changed:
257 		@arg0: The #IndicatorObject object
258 		@arg1: A pointer to the #IndicatorObjectEntry that
259 			is changing it's state
260 		@arg2: The state of whether the entry should be shown
261 
262 		Whether the entry should be shown or not has changed so we need
263 		to tell whoever is displaying it.
264 	*/
265 	signals[SHOW_NOW_CHANGED] = g_signal_new (INDICATOR_OBJECT_SIGNAL_SHOW_NOW_CHANGED,
266 	                                          G_TYPE_FROM_CLASS(klass),
267 	                                          G_SIGNAL_RUN_LAST,
268 	                                          G_STRUCT_OFFSET (IndicatorObjectClass, show_now_changed),
269 	                                          NULL, NULL,
270 	                                          _indicator_object_marshal_VOID__POINTER_BOOLEAN,
271 	                                          G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
272 
273 	/**
274 		IndicatorObject::accessible-desc-update::
275 		@arg0: The #IndicatorObject object
276 		@arg1: A pointer to the #IndicatorObjectEntry whos
277 			accessible description has been updated.
278 
279 		Signaled when an indicator's accessible description
280 		has been updated, so that the displayer of the
281 		indicator can fetch the new description.
282 	*/
283 	signals[ACCESSIBLE_DESC_UPDATE] = g_signal_new (INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE,
284 	                                     G_TYPE_FROM_CLASS(klass),
285 	                                     G_SIGNAL_RUN_LAST,
286 	                                     G_STRUCT_OFFSET (IndicatorObjectClass, accessible_desc_update),
287 	                                     NULL, NULL,
288 	                                     g_cclosure_marshal_VOID__POINTER,
289 	                                     G_TYPE_NONE, 1, G_TYPE_POINTER, G_TYPE_NONE);
290 
291 	/* Properties */
292 
293 	GParamSpec * pspec = g_param_spec_boolean (INDICATOR_OBJECT_DEFAULT_VISIBILITY,
294 			"default visibility",
295 			"Whether or not entries should initially be visible.",
296 			TRUE,
297 			G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
298 	g_object_class_install_property (object_class, PROP_DEFAULT_VISIBILITY, pspec);
299 }
300 
301 /* Initialize an instance */
302 static void
indicator_object_init(IndicatorObject * self)303 indicator_object_init (IndicatorObject *self)
304 {
305 	IndicatorObjectPrivate * priv = G_TYPE_INSTANCE_GET_PRIVATE (self, INDICATOR_OBJECT_TYPE, IndicatorObjectPrivate);
306 
307 	priv->module = NULL;
308 
309 	priv->entry.parent_object = self;
310 	priv->entry.menu = NULL;
311 	priv->entry.label = NULL;
312 	priv->entry.image = NULL;
313 	priv->entry.accessible_desc = NULL;
314 	priv->entry.name_hint = NULL;
315 
316 	priv->gotten_entries = FALSE;
317 	priv->default_visibility = TRUE;
318 	priv->entry_privates = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
319 
320 	priv->environments = NULL;
321 
322 	self->priv = priv;
323 
324 	GObject * o = G_OBJECT(self);
325 	/* Invoke the entry-being-removed virtual function first */
326 	g_signal_connect (o, INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED,
327 	                  G_CALLBACK(indicator_object_entry_being_removed), NULL);
328 	/* Invoke the entry-was-added virtual function last */
329 	g_signal_connect_after (o, INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED,
330 	                        G_CALLBACK(indicator_object_entry_was_added), NULL);
331 }
332 
333 /* Unref the objects that we're holding on to. */
334 static void
indicator_object_dispose(GObject * object)335 indicator_object_dispose (GObject *object)
336 {
337 	/* Ensure that hidden entries are re-added so their widgetry will
338 	   be cleaned up properly by the client */
339 	indicator_object_set_visible (INDICATOR_OBJECT (object), TRUE);
340 
341 	G_OBJECT_CLASS (indicator_object_parent_class)->dispose (object);
342 }
343 
344 /* A small helper function that closes a module but
345    in the function prototype of a GSourceFunc. */
346 static gboolean
module_unref(gpointer data)347 module_unref (gpointer data)
348 {
349 	if (!g_module_close((GModule *)data)) {
350 		/* All we can do is warn. */
351 		g_warning("Unable to close module!");
352 	}
353 	return FALSE;
354 }
355 
356 /* Free memory */
357 static void
indicator_object_finalize(GObject * object)358 indicator_object_finalize (GObject *object)
359 {
360 	IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(object);
361 
362 	if (priv->entry_privates != NULL) {
363 		g_hash_table_destroy (priv->entry_privates);
364 		priv->entry_privates = NULL;
365 	}
366 
367 	if (priv->environments != NULL) {
368 		g_strfreev(priv->environments);
369 		priv->environments = NULL;
370 	}
371 
372 	if (priv->module != NULL) {
373 		/* Wow, this is convoluted.  So basically we want to unref
374 		   the module which will cause the code it included to be
375 		   removed.  But, since its finalize function is the function
376 		   that called this one, we can't really remove it before
377 		   it finishes being executed.  So we're putting the job into
378 		   the main loop to remove it the next time it gets a chance.
379 		   Slightly non-deterministic, but should work. */
380 		g_idle_add(module_unref, priv->module);
381 		priv->module = NULL;
382 	}
383 
384 	G_OBJECT_CLASS (indicator_object_parent_class)->finalize (object);
385 	return;
386 }
387 
388 /**
389 	indicator_object_new_from_file:
390 	@file: Filename containing a loadable module
391 
392 	This function builds an #IndicatorObject using the symbols
393 	that are found in @file.  The module is loaded and the
394 	references are all kept by the object.  To unload the
395 	module the object must be destroyed.
396 
397 	Return value: A valid #IndicatorObject or #NULL if error.
398 */
399 IndicatorObject *
indicator_object_new_from_file(const gchar * file)400 indicator_object_new_from_file (const gchar * file)
401 {
402 	GObject * object = NULL;
403 	GModule * module = NULL;
404 
405 	/* Check to make sure the name exists and that the
406 	   file itself exists */
407 	if (file == NULL) {
408 		g_warning("Invalid filename.");
409 		return NULL;
410 	}
411 
412 	if (!g_file_test(file, G_FILE_TEST_EXISTS)) {
413 		g_warning("File '%s' does not exist.", file);
414 		return NULL;
415 	}
416 
417 	/* Grab the g_module reference, pull it in but let's
418 	   keep the symbols local to avoid conflicts. */
419 	module = g_module_open(file,
420                            G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
421 	if (module == NULL) {
422 		g_warning("Unable to load module: %s", file);
423 		return NULL;
424 	}
425 
426 	/* Look for the version function, error if not found. */
427 	get_version_t lget_version = NULL;
428 	if (!g_module_symbol(module, INDICATOR_GET_VERSION_S, (gpointer *)(&lget_version))) {
429 		g_warning("Unable to get the symbol for getting the version.");
430 		return NULL;
431 	}
432 
433 	/* Check the version with the macro and make sure we're
434 	   all talking the same language. */
435 	if (!INDICATOR_VERSION_CHECK(lget_version())) {
436 		g_warning("Indicator using API version '%s' we're expecting '%s'", lget_version(), INDICATOR_VERSION);
437 		return NULL;
438 	}
439 
440 	/* The function for grabbing a label from the module
441 	   execute it, and make sure everything is a-okay */
442 	get_type_t lget_type = NULL;
443 	if (!g_module_symbol(module, INDICATOR_GET_TYPE_S, (gpointer *)(&lget_type))) {
444 		g_warning("Unable to get '" INDICATOR_GET_TYPE_S "' symbol from module: %s", file);
445 		goto unrefandout;
446 	}
447 	if (lget_type == NULL) {
448 		g_warning("Symbol '" INDICATOR_GET_TYPE_S "' is (null) in module: %s", file);
449 		goto unrefandout;
450 	}
451 
452 	/* A this point we allocate the object, any code beyond
453 	   here needs to deallocate it if we're returning in an
454 	   error'd state. */
455 	object = g_object_new(lget_type(), NULL);
456 	if (object == NULL) {
457 		g_warning("Unable to build an object if type '%d' in module: %s", (gint)lget_type(), file);
458 		goto unrefandout;
459 	}
460 	if (!INDICATOR_IS_OBJECT(object)) {
461 		g_warning("Type '%d' in file %s is not a subclass of IndicatorObject.", (gint)lget_type(), file);
462 		goto unrefandout;
463 	}
464 
465 	/* Now we can track the module */
466 	INDICATOR_OBJECT_GET_PRIVATE(object)->module = module;
467 
468 	return INDICATOR_OBJECT(object);
469 
470 	/* Error, let's drop the object and return NULL.  Sad when
471 	   this happens. */
472 unrefandout:
473 	g_clear_object (&object);
474 	g_clear_object (&module);
475 	g_warning("Error building IndicatorObject from file: %s", file);
476 	return NULL;
477 }
478 
479 /* The default get entries function uses the other single
480    entries in the class to create an entry structure and
481    put it into a list.  This makes it simple for simple objects
482    to create the list.  Small changes from the way they
483    previously were. */
484 static GList *
get_entries_default(IndicatorObject * io)485 get_entries_default (IndicatorObject * io)
486 {
487 	IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(io);
488 
489 	if (!priv->gotten_entries) {
490 		IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
491 
492 		priv->entry.parent_object = io;
493 
494 		if (class->get_label) {
495 			priv->entry.label = class->get_label(io);
496 		}
497 
498 		if (class->get_image) {
499 			priv->entry.image = class->get_image(io);
500 		}
501 
502 		if (priv->entry.image == NULL && priv->entry.label == NULL) {
503 			g_warning("IndicatorObject class does not create an image or a label.  We need one of those.");
504 			return NULL;
505 		}
506 
507 		if (class->get_menu) {
508 			priv->entry.menu = class->get_menu(io);
509 		}
510 
511 		if (priv->entry.menu == NULL) {
512 			g_warning("IndicatorObject class does not create a menu.  We need one of those.");
513 			return NULL;
514 		}
515 
516 		if (class->get_accessible_desc) {
517 			priv->entry.accessible_desc = class->get_accessible_desc(io);
518 		}
519 
520 		if (priv->entry.accessible_desc == NULL) {
521 			g_warning("IndicatorObject class does not have an accessible description.");
522 		}
523 
524 		if (class->get_name_hint) {
525 			priv->entry.name_hint = class->get_name_hint(io);
526 		}
527 
528 		priv->gotten_entries = TRUE;
529 	}
530 
531 	return g_list_append(NULL, &(priv->entry));
532 }
533 
534 /* returns a list of all IndicatorObjectEntires, visible or not */
535 static GList*
get_all_entries(IndicatorObject * io)536 get_all_entries (IndicatorObject * io)
537 {
538 	GList * all_entries = NULL, *l;
539 
540 	g_return_val_if_fail(INDICATOR_IS_OBJECT(io), NULL);
541 	IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
542 
543 	if (class->get_entries == NULL)
544 		g_error("No get_entries function on object.  It must have been deleted?!?!");
545 	else
546 	{
547 		all_entries = class->get_entries(io);
548 
549 		for (l = all_entries; l; l = l->next)
550 		{
551 			IndicatorObjectEntry *entry = l->data;
552 
553 			if (entry)
554 				entry->parent_object = io;
555 		}
556 	}
557 
558 	return all_entries;
559 }
560 
561 /* get the private structure that corresponds to a caller-specified entry */
562 static IndicatorObjectEntryPrivate *
entry_get_private(IndicatorObject * io,IndicatorObjectEntry * entry)563 entry_get_private (IndicatorObject * io, IndicatorObjectEntry * entry)
564 {
565 	g_return_val_if_fail (INDICATOR_IS_OBJECT(io), NULL);
566 	g_return_val_if_fail (io->priv != NULL, NULL);
567 
568 	GHashTable * h = io->priv->entry_privates;
569 	IndicatorObjectEntryPrivate * priv = g_hash_table_lookup (h, entry);
570 	if (priv == NULL)
571 	{
572 		priv = g_new0 (IndicatorObjectEntryPrivate, 1);
573 		priv->visibility = ENTRY_INIT;
574 		g_hash_table_insert (h, entry, priv);
575 	}
576 
577 	return priv;
578 }
579 
580 /**
581 	indicator_object_get_entries:
582 	@io: #IndicatorObject to query
583 
584 	This function returns a list of visible entries. The list is built
585 	by calling the object's #IndicatorObjectClass::get_entries
586 	virtual function and testing each of the results for visibility.
587 	Callers should free the GList with g_list_free(), but the entries
588 	are owned by the IndicatorObject and should not be freed.
589 
590 	Return value: (element-type IndicatorObjectEntry) (transfer container):
591 		A list if #IndicatorObjectEntry structures or NULL on error.
592 */
593 GList *
indicator_object_get_entries(IndicatorObject * io)594 indicator_object_get_entries (IndicatorObject * io)
595 {
596 	GList * l;
597 	GList * ret = NULL;
598 	GList * all_entries = get_all_entries (io);
599 	const gboolean default_visibility = INDICATOR_OBJECT_GET_PRIVATE(io)->default_visibility;
600 
601 	for (l=all_entries; l!=NULL; l=l->next)
602 	{
603 		gboolean show_me;
604 		IndicatorObjectEntry * entry = l->data;
605 
606 		switch (entry_get_private(io,entry)->visibility) {
607 			case ENTRY_VISIBLE:   show_me = TRUE; break;
608 			case ENTRY_INVISIBLE: show_me = FALSE; break;
609 			case ENTRY_INIT:      show_me = default_visibility; break;
610 			default:              show_me = TRUE; g_warn_if_reached(); break;
611 		}
612 
613 		if (show_me)
614 			ret = g_list_prepend (ret, entry);
615 	}
616 
617 	g_list_free (all_entries);
618 	return g_list_reverse (ret);
619 }
620 
621 /**
622 	indicator_object_get_location:
623 	@io: #IndicatorObject to query
624 	@entry: The #IndicatorObjectEntry to look for.
625 
626 	This function looks on the class for the object and calls
627 	it's #IndicatorObjectClass::get_location function.  If the
628 	function doesn't exist it returns zero.
629 
630 	Return value: Location of the @entry in the display or
631 		zero if no location is specified.
632 */
633 guint
indicator_object_get_location(IndicatorObject * io,IndicatorObjectEntry * entry)634 indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry)
635 {
636 	g_return_val_if_fail(INDICATOR_IS_OBJECT(io), 0);
637 	IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
638 
639 	if (class->get_location) {
640 		return class->get_location(io, entry);
641 	}
642 
643 	return 0;
644 }
645 
646 /**
647 	indicator_object_get_show_now:
648 	@io: #IndicatorObject to query
649 	@entry: The #IndicatorObjectEntry to look for.
650 
651 	This function returns whether the entry should be shown with
652 	priority on the panel.  If the object does not support checking
653 	it assumes that its entries should never have priority.
654 
655 	Return value: Whether the entry should be shown with priority.
656 */
657 guint
indicator_object_get_show_now(IndicatorObject * io,IndicatorObjectEntry * entry)658 indicator_object_get_show_now (IndicatorObject * io, IndicatorObjectEntry * entry)
659 {
660 	g_return_val_if_fail(INDICATOR_IS_OBJECT(io), 0);
661 	IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
662 
663 	if (class->get_show_now) {
664 		return class->get_show_now(io, entry);
665 	}
666 
667 	return FALSE;
668 }
669 
670 /**
671 	indicator_object_entry_activate_window:
672 	@io: #IndicatorObject to query
673 	@entry: The #IndicatorObjectEntry whose entry was shown
674 	@windowid: ID of the window that is currently focused (or will
675 		be very shortly)
676 	@timestamp: The X11 timestamp of the event
677 
678 	Used to signal to the indicator that the menu on an entry has
679 	been clicked on.  This can either be an activate or a showing
680 	of the menu.  Also includes a window ID so that we can know what
681 	application is going to be getting focused soon.  If there is
682 	no override of this function, it is the same as calling
683 	indicator_object_entry_activate and in general is preferable
684 	if you have that information available.
685 */
686 void
indicator_object_entry_activate_window(IndicatorObject * io,IndicatorObjectEntry * entry,guint windowid,guint timestamp)687 indicator_object_entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp)
688 {
689 	g_return_if_fail(INDICATOR_IS_OBJECT(io));
690 	IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
691 
692 	if (class->entry_activate_window != NULL) {
693 		return class->entry_activate_window(io, entry, windowid, timestamp);
694 	} else {
695 		indicator_object_entry_activate(io, entry, timestamp);
696 	}
697 
698 	return;
699 }
700 
701 /**
702 	indicator_object_entry_activate:
703 	@io: #IndicatorObject to query
704 	@entry: The #IndicatorObjectEntry whose entry was shown
705 	@timestamp: The X11 timestamp of the event
706 
707 	Used to signal to the indicator that the menu on an entry has
708 	been clicked on.  This can either be an activate or a showing
709 	of the menu.  Note, this does not actually show the menu that's
710 	left up to the reader.
711 */
712 void
indicator_object_entry_activate(IndicatorObject * io,IndicatorObjectEntry * entry,guint timestamp)713 indicator_object_entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp)
714 {
715 	g_return_if_fail(INDICATOR_IS_OBJECT(io));
716 	IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
717 
718 	if (class->entry_activate != NULL) {
719 		return class->entry_activate(io, entry, timestamp);
720 	}
721 
722 	return;
723 }
724 
725 /**
726 	indicator_object_entry_close:
727 	@io: #IndicatorObject to query
728 	@entry: The #IndicatorObjectEntry whose menu was closed
729 	@timestamp: The X11 timestamp of the event
730 
731 	Used to tell the indicator that a menu has been closed for the
732 	entry that is specified.
733 */
734 void
indicator_object_entry_close(IndicatorObject * io,IndicatorObjectEntry * entry,guint timestamp)735 indicator_object_entry_close (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp)
736 {
737 	g_return_if_fail(INDICATOR_IS_OBJECT(io));
738 	IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
739 
740 	if (class->entry_close != NULL) {
741 		return class->entry_close(io, entry, timestamp);
742 	}
743 
744 	return;
745 }
746 
747 static void
indicator_object_entry_being_removed(IndicatorObject * io,IndicatorObjectEntry * entry)748 indicator_object_entry_being_removed (IndicatorObject * io, IndicatorObjectEntry * entry)
749 {
750 	g_return_if_fail(INDICATOR_IS_OBJECT(io));
751 	IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
752 
753 	entry_get_private (io, entry)->visibility = ENTRY_INVISIBLE;
754 
755 	if (entry)
756 		entry->parent_object = NULL;
757 
758 	if (class->entry_being_removed != NULL)
759 	{
760 		class->entry_being_removed (io, entry);
761 	}
762 }
763 
764 static void
indicator_object_entry_was_added(IndicatorObject * io,IndicatorObjectEntry * entry)765 indicator_object_entry_was_added (IndicatorObject * io, IndicatorObjectEntry * entry)
766 {
767 	g_return_if_fail(INDICATOR_IS_OBJECT(io));
768 	IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io);
769 
770 	entry_get_private (io, entry)->visibility = ENTRY_VISIBLE;
771 
772 	if (entry)
773 		entry->parent_object = io;
774 
775 	if (class->entry_was_added != NULL)
776 	{
777 		class->entry_was_added (io, entry);
778 	}
779 }
780 
781 /**
782 	indicator_object_set_environment:
783 	@io: #IndicatorObject to set on
784 	@env: List of enviroment names to use
785 
786 	Sets the names of the environment that the indicator is being
787 	loaded into.  This allows for indicators to behave differently
788 	in different hosts if need be.
789 */
790 void
indicator_object_set_environment(IndicatorObject * io,GStrv env)791 indicator_object_set_environment (IndicatorObject * io, GStrv env)
792 {
793 	/* FIXME: should this be a property? */
794 	g_return_if_fail(INDICATOR_IS_OBJECT(io));
795 
796 	if (io->priv->environments != NULL) {
797 		g_strfreev(io->priv->environments);
798 		io->priv->environments = NULL;
799 	}
800 
801 	io->priv->environments = g_strdupv(env);
802 
803 	return;
804 }
805 
806 /**
807 	indicator_object_get_environment:
808 	@io: #IndicatorObject to get the environment from
809 
810 	Gets the list of environment strings that this object is
811 	placed into.
812 
813 	Return value: (transfer none): Gets the list of strings that
814 	represent the environment or NULL if none were given.
815 */
816 GStrv
indicator_object_get_environment(IndicatorObject * io)817 indicator_object_get_environment (IndicatorObject * io)
818 {
819 	g_return_val_if_fail(INDICATOR_IS_OBJECT(io), NULL);
820 	return io->priv->environments;
821 }
822 
823 /**
824 	indicator_object_check_environment:
825 	@io: #IndicatorObject to check on
826 	@env: Environment that we're looking for
827 
828 	Convience function to check to see if the specified environment
829 	@env is in our list of environments.
830 
831 	Return Value: Whether we're in environment @env
832 */
833 gboolean
indicator_object_check_environment(IndicatorObject * io,const gchar * env)834 indicator_object_check_environment (IndicatorObject * io, const gchar * env)
835 {
836 	g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
837 	g_return_val_if_fail(env != NULL, FALSE);
838 
839 	if (io->priv->environments == NULL) {
840 		return FALSE;
841 	}
842 
843 	int i;
844 	for (i = 0; io->priv->environments[i] != NULL; i++) {
845 		if (g_strcmp0(env, io->priv->environments[i]) == 0) {
846 			return TRUE;
847 		}
848 	}
849 
850 	return FALSE;
851 }
852 
853 /**
854 	indicator_object_set_visible:
855 	@io: #IndicatorObject to check on
856 	@visible: whether or not the entries should be visible
857 
858 	Used to set all of an indicator's entries to be visible or hidden.
859 */
860 void
indicator_object_set_visible(IndicatorObject * io,gboolean visible)861 indicator_object_set_visible (IndicatorObject * io, gboolean visible)
862 {
863 	g_return_if_fail(INDICATOR_IS_OBJECT(io));
864 
865 	GList * l;
866 	GList * entries = get_all_entries (io);
867 	const guint signal_id = signals[visible ? ENTRY_ADDED : ENTRY_REMOVED];
868 	EntryVisibility visibility = visible ? ENTRY_VISIBLE : ENTRY_INVISIBLE;
869 	const GQuark detail = (GQuark)0;
870 
871 	for (l=entries; l!=NULL; l=l->next) {
872 		IndicatorObjectEntry *entry = l->data;
873 		if (entry_get_private (io, entry)->visibility != visibility)
874 			g_signal_emit(io, signal_id, detail, entry);
875 	}
876 	g_list_free (entries);
877 }
878 
879 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)880 get_property (GObject     * object,
881               guint         prop_id,
882               GValue      * value,
883               GParamSpec  * pspec)
884 {
885         IndicatorObject * self = INDICATOR_OBJECT(object);
886         g_return_if_fail(self != NULL);
887 
888         IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(self);
889         g_return_if_fail(priv != NULL);
890 
891         switch (prop_id) {
892         /* *********************** */
893         case PROP_DEFAULT_VISIBILITY:
894                 if (G_VALUE_HOLDS_BOOLEAN(value)) {
895                         g_value_set_boolean(value, priv->default_visibility);
896                 } else {
897                         g_warning("default-visibility property requires a boolean value.");
898                 }
899                 break;
900         /* *********************** */
901         default:
902                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
903                 break;
904         }
905 }
906 
907 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)908 set_property (GObject       * object,
909               guint           prop_id,
910               const GValue  * value,
911               GParamSpec    * pspec)
912 {
913         IndicatorObject * self = INDICATOR_OBJECT(object);
914         g_return_if_fail (self != NULL);
915 
916         IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(self);
917         g_return_if_fail (priv != NULL);
918 
919 
920         switch (prop_id) {
921 
922         /* *********************** */
923         case PROP_DEFAULT_VISIBILITY:
924                 if (G_VALUE_HOLDS_BOOLEAN(value)) {
925                         priv->default_visibility = g_value_get_boolean (value);
926                 } else {
927                         g_warning("default-visibility property requires a boolean value.");
928                 }
929                 break;
930 
931 
932         default:
933                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
934                 break;
935         }
936 }
937 
938