Enabling multithreaded compilation.
[debian/lxpanel.git] / src / plugins / indicator / indicator.c
1 /*
2 Copyright 2010 Julien Lavergne <gilir@ubuntu.com>
3
4 Based on indicator-applet :
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 TODO Check also http://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/services/panel-service.c
24
25 TODO ? : add hotkey support (r348 + r352)
26
27 TODO : vertical support (r354)
28
29 */
30
31 #include "plugin.h"
32
33 #include "misc.h"
34 #include "panel.h"
35 #include "dbg.h"
36
37 #include <stdlib.h>
38 #include <glib/gi18n.h>
39
40 #include <gtk/gtk.h>
41 #include <libindicator/indicator-object.h>
42
43 static gchar * indicator_order[][2] = {
44 {"libappmenu.so", NULL},
45 {"libapplication.so", NULL},
46 {"libapplication.so", "gst-keyboard-xkb"},
47 {"libmessaging.so", NULL},
48 {"libpower.so", NULL},
49 {"libapplication.so", "bluetooth-manager"},
50 {"libnetwork.so", NULL},
51 {"libnetworkmenu.so", NULL},
52 {"libapplication.so", "nm-applet"},
53 {"libsoundmenu.so", NULL},
54 {"libdatetime.so", NULL},
55 {"libsession.so", NULL},
56 {NULL, NULL}
57 };
58
59 #define MENU_DATA_BOX "box"
60 #define MENU_DATA_INDICATOR_OBJECT "indicator-object"
61 #define MENU_DATA_INDICATOR_ENTRY "indicator-entry"
62 #define MENU_DATA_IN_MENUITEM "in-menuitem"
63 #define MENU_DATA_MENUITEM_PRESSED "menuitem-pressed"
64
65 #define IO_DATA_NAME "indicator-name"
66 #define IO_DATA_ORDER_NUMBER "indicator-order-number"
67
68 #define LOG_FILE_NAME "lxpanel-indicator-plugin.log"
69
70 GOutputStream * log_file = NULL;
71
72 typedef struct {
73
74 Plugin * plugin; /* Back pointer to plugin */
75
76 IndicatorObject *io; /* Indicators applets */
77
78 GList *images; /* List of images of applets */
79 GList *menus; /* List of menus of applets */
80
81 GtkWidget * menubar; /* Displayed menubar */
82
83 gboolean applications; /* Support for differents indicators */
84 gboolean datetime;
85 gboolean me;
86 gboolean messages;
87 gboolean network;
88 gboolean session;
89 gboolean sound;
90 /* gboolean appmenu; */
91
92
93 } IndicatorPlugin;
94
95 static const gchar * indicator_env[] = {
96 "indicator-applet",
97 NULL
98 };
99
100 static gint
101 name2order (const gchar * name, const gchar * hint) {
102 int i;
103
104 for (i = 0; indicator_order[i][0] != NULL; i++) {
105 if (g_strcmp0(name, indicator_order[i][0]) == 0 &&
106 g_strcmp0(hint, indicator_order[i][1]) == 0) {
107 return i;
108 }
109 }
110
111 return -1;
112 }
113
114 typedef struct _incoming_position_t incoming_position_t;
115 struct _incoming_position_t {
116 gint objposition;
117 gint entryposition;
118 gint menupos;
119 gboolean found;
120 };
121
122 /* This function helps by determining where in the menu list
123 this new entry should be placed. It compares the objects
124 that they're on, and then the individual entries. Each
125 is progressively more expensive. */
126 static void
127 place_in_menu_cb (GtkWidget * widget, gpointer user_data)
128 {
129 incoming_position_t * position = (incoming_position_t *)user_data;
130 if (position->found) {
131 /* We've already been placed, just finish the foreach */
132 return;
133 }
134
135 IndicatorObject * io = INDICATOR_OBJECT(g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_OBJECT));
136 g_return_if_fail(INDICATOR_IS_OBJECT(io));
137
138 gint objposition = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
139 /* We've already passed it, well, then this is where
140 we should be be. Stop! */
141 if (objposition > position->objposition) {
142 position->found = TRUE;
143 return;
144 }
145
146 /* The objects don't match yet, keep looking */
147 if (objposition < position->objposition) {
148 position->menupos++;
149 return;
150 }
151
152 /* The objects are the same, let's start looking at entries. */
153 IndicatorObjectEntry * entry = (IndicatorObjectEntry *)g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
154 gint entryposition = indicator_object_get_location(io, entry);
155
156 if (entryposition > position->entryposition) {
157 position->found = TRUE;
158 return;
159 }
160
161 if (entryposition < position->entryposition) {
162 position->menupos++;
163 return;
164 }
165
166 /* We've got the same object and the same entry. Well,
167 let's just put it right here then. */
168 position->found = TRUE;
169 return;
170 }
171
172 /* Position the entry */
173 static void
174 place_in_menu (GtkWidget *menubar,
175 GtkWidget *menuitem,
176 IndicatorObject *io,
177 IndicatorObjectEntry *entry)
178 {
179 incoming_position_t position;
180
181 /* Start with the default position for this indicator object */
182 gint io_position = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
183
184 /* If name-hint is set, try to find the entry's position */
185 if (entry->name_hint != NULL) {
186 const gchar *name = (const gchar *)g_object_get_data(G_OBJECT(io), IO_DATA_NAME);
187 gint entry_position = name2order(name, entry->name_hint);
188
189 /* If we don't find the entry, fall back to the indicator object's position */
190 if (entry_position > -1)
191 io_position = entry_position;
192 }
193
194 position.objposition = io_position;
195 position.entryposition = indicator_object_get_location(io, entry);
196 position.menupos = 0;
197 position.found = FALSE;
198
199 gtk_container_foreach(GTK_CONTAINER(menubar), place_in_menu_cb, &position);
200
201 gtk_menu_shell_insert(GTK_MENU_SHELL(menubar), menuitem, position.menupos);
202 }
203
204 static void
205 something_shown (GtkWidget * widget, gpointer user_data)
206 {
207 GtkWidget * menuitem = GTK_WIDGET(user_data);
208 gtk_widget_show(menuitem);
209 }
210
211 static void
212 something_hidden (GtkWidget * widget, gpointer user_data)
213 {
214 GtkWidget * menuitem = GTK_WIDGET(user_data);
215 gtk_widget_hide(menuitem);
216 }
217
218 static void
219 sensitive_cb (GObject * obj, GParamSpec * pspec, gpointer user_data)
220 {
221 g_return_if_fail(GTK_IS_WIDGET(obj));
222 g_return_if_fail(GTK_IS_WIDGET(user_data));
223
224 gtk_widget_set_sensitive(GTK_WIDGET(user_data), gtk_widget_get_sensitive(GTK_WIDGET(obj)));
225 return;
226 }
227
228 static void
229 entry_activated (GtkWidget * widget, gpointer user_data)
230 {
231 g_return_if_fail(GTK_IS_WIDGET(widget));
232
233 IndicatorObject *io = g_object_get_data (G_OBJECT (widget), MENU_DATA_INDICATOR_OBJECT);
234 IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (widget), MENU_DATA_INDICATOR_ENTRY);
235
236 g_return_if_fail(INDICATOR_IS_OBJECT(io));
237
238 return indicator_object_entry_activate(io, entry, gtk_get_current_event_time());
239 }
240
241 static gboolean
242 entry_secondary_activated (GtkWidget * widget, GdkEvent * event, gpointer user_data)
243 {
244 g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
245
246 switch (event->type) {
247 case GDK_ENTER_NOTIFY:
248 g_object_set_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM, GINT_TO_POINTER(TRUE));
249 break;
250
251 case GDK_LEAVE_NOTIFY:
252 g_object_set_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM, GINT_TO_POINTER(FALSE));
253 g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(FALSE));
254 break;
255
256 case GDK_BUTTON_PRESS:
257 if (event->button.button == 2) {
258 g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(TRUE));
259 }
260 break;
261
262 case GDK_BUTTON_RELEASE:
263 if (event->button.button == 2) {
264 gboolean in_menuitem = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM));
265 gboolean menuitem_pressed = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED));
266
267 if (in_menuitem && menuitem_pressed) {
268 g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(FALSE));
269
270 IndicatorObject *io = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_OBJECT);
271 IndicatorObjectEntry *entry = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
272
273 g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
274
275 g_signal_emit_by_name(io, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE,
276 entry, event->button.time);
277 }
278 }
279 break;
280 }
281
282 return FALSE;
283 }
284
285 static gboolean
286 entry_scrolled (GtkWidget *menuitem, GdkEventScroll *event, gpointer data)
287 {
288 g_return_val_if_fail(GTK_IS_WIDGET(menuitem), FALSE);
289
290 IndicatorObject *io = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_OBJECT);
291 IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_ENTRY);
292
293 g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
294
295 g_signal_emit_by_name (io, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry, 1, event->direction);
296
297 return FALSE;
298 }
299
300 static void
301 entry_added (IndicatorObject * io, IndicatorObjectEntry * entry, GtkWidget * menubar)
302 {
303 const char *indicator_name = (const gchar *)g_object_get_data(G_OBJECT(io), IO_DATA_NAME);
304 g_debug("Signal: Entry Added from %s", indicator_name);
305 gboolean something_visible = FALSE;
306 gboolean something_sensitive = FALSE;
307
308 GtkWidget * menuitem = gtk_menu_item_new();
309 GtkWidget * hbox = gtk_hbox_new(FALSE, 3);
310
311 g_object_set_data (G_OBJECT (menuitem), MENU_DATA_BOX, hbox);
312 g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_OBJECT, io);
313 g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_ENTRY, entry);
314
315 g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(entry_activated), NULL);
316 g_signal_connect(G_OBJECT(menuitem), "button-press-event", G_CALLBACK(entry_secondary_activated), NULL);
317 g_signal_connect(G_OBJECT(menuitem), "button-release-event", G_CALLBACK(entry_secondary_activated), NULL);
318 g_signal_connect(G_OBJECT(menuitem), "enter-notify-event", G_CALLBACK(entry_secondary_activated), NULL);
319 g_signal_connect(G_OBJECT(menuitem), "leave-notify-event", G_CALLBACK(entry_secondary_activated), NULL);
320 g_signal_connect(G_OBJECT(menuitem), "scroll-event", G_CALLBACK(entry_scrolled), NULL);
321
322 if (entry->image != NULL)
323 {
324 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry->image), FALSE, FALSE, 1);
325 if (gtk_widget_get_visible(GTK_WIDGET(entry->image))) {
326 something_visible = TRUE;
327 }
328
329 if (gtk_widget_get_sensitive(GTK_WIDGET(entry->image))) {
330 something_sensitive = TRUE;
331 }
332
333 g_signal_connect(G_OBJECT(entry->image), "show", G_CALLBACK(something_shown), menuitem);
334 g_signal_connect(G_OBJECT(entry->image), "hide", G_CALLBACK(something_hidden), menuitem);
335 g_signal_connect(G_OBJECT(entry->image), "notify::sensitive", G_CALLBACK(sensitive_cb), menuitem);
336
337 }
338 if (entry->label != NULL)
339 {
340 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry->label), FALSE, FALSE, 1);
341
342 if (gtk_widget_get_visible(GTK_WIDGET(entry->label))) {
343 something_visible = TRUE;
344 }
345
346 if (gtk_widget_get_sensitive(GTK_WIDGET(entry->label))) {
347
348 something_sensitive = TRUE;
349 }
350
351 g_signal_connect(G_OBJECT(entry->label), "show", G_CALLBACK(something_shown), menuitem);
352 g_signal_connect(G_OBJECT(entry->label), "hide", G_CALLBACK(something_hidden), menuitem);
353 g_signal_connect(G_OBJECT(entry->label), "notify::sensitive", G_CALLBACK(sensitive_cb), menuitem);
354
355 }
356 gtk_container_add(GTK_CONTAINER(menuitem), hbox);
357 gtk_widget_show(hbox);
358
359 if (entry->menu != NULL)
360 {
361 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(entry->menu));
362 }
363
364 place_in_menu(menubar, menuitem, io, entry);
365
366 if (something_visible) {
367 gtk_widget_show(menuitem);
368 }
369 gtk_widget_set_sensitive(menuitem, something_sensitive);
370
371 return;
372 }
373
374 static void
375 entry_removed_cb (GtkWidget * widget, gpointer userdata)
376 {
377 gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
378
379 if (data != userdata)
380 {
381 return;
382 }
383
384 IndicatorObjectEntry * entry = (IndicatorObjectEntry *)data;
385 if (entry->label != NULL) {
386 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(something_shown), widget);
387 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(something_hidden), widget);
388 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(sensitive_cb), widget);
389 }
390 if (entry->image != NULL) {
391 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(something_shown), widget);
392 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(something_hidden), widget);
393 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(sensitive_cb), widget);
394 }
395
396 gtk_widget_destroy(widget);
397 return;
398 }
399
400 static void
401 entry_moved_find_cb (GtkWidget * widget, gpointer userdata)
402 {
403 gpointer * array = (gpointer *)userdata;
404 if (array[1] != NULL) {
405 return;
406 }
407
408 gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
409
410 if (data != array[0]) {
411 return;
412 }
413
414 array[1] = widget;
415 return;
416 }
417
418 /* Gets called when an entry for an object was moved. */
419 static void
420 entry_moved (IndicatorObject * io, IndicatorObjectEntry * entry,
421 gint old G_GNUC_UNUSED, gint new G_GNUC_UNUSED, gpointer user_data)
422 {
423 GtkWidget * menubar = GTK_WIDGET(user_data);
424
425 gpointer array[2];
426 array[0] = entry;
427 array[1] = NULL;
428
429 gtk_container_foreach(GTK_CONTAINER(menubar), entry_moved_find_cb, array);
430 if (array[1] == NULL) {
431 g_warning("Moving an entry that isn't in our menus.");
432 return;
433 }
434
435 GtkWidget * mi = GTK_WIDGET(array[1]);
436 g_object_ref(G_OBJECT(mi));
437 gtk_container_remove(GTK_CONTAINER(menubar), mi);
438 place_in_menu(menubar, mi, io, entry);
439 g_object_unref(G_OBJECT(mi));
440
441 return;
442 }
443
444 static void
445 entry_removed (IndicatorObject * io G_GNUC_UNUSED, IndicatorObjectEntry * entry,
446 gpointer user_data)
447 {
448 g_debug("Signal: Entry Removed");
449
450 gtk_container_foreach(GTK_CONTAINER(user_data), entry_removed_cb, entry);
451
452 return;
453 }
454
455 static void
456 menu_show (IndicatorObject * io, IndicatorObjectEntry * entry,
457 guint32 timestamp, gpointer user_data)
458 {
459 GtkWidget * menubar = GTK_WIDGET(user_data);
460
461 if (entry == NULL) {
462 /* Close any open menus instead of opening one */
463 GList * entries = indicator_object_get_entries(io);
464 GList * entry = NULL;
465 for (entry = entries; entry != NULL; entry = g_list_next(entry)) {
466 IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data;
467 gtk_menu_popdown(entrydata->menu);
468 }
469 g_list_free(entries);
470
471 /* And tell the menubar to exit activation mode too */
472 gtk_menu_shell_cancel(GTK_MENU_SHELL(menubar));
473 return;
474 }
475
476 // TODO: do something sensible here
477 }
478
479 static gboolean
480 load_module (const gchar * name, GtkWidget * menubar)
481 {
482 g_debug("Looking at Module: %s", name);
483 g_return_val_if_fail(name != NULL, FALSE);
484
485 if (!g_str_has_suffix(name, G_MODULE_SUFFIX))
486 {
487 return FALSE;
488 }
489
490 g_debug("Loading Module: %s", name);
491
492 /* Build the object for the module */
493 gchar *fullpath = g_build_filename(INDICATOR_DIR, name, NULL);
494 g_debug("Full path: %s", fullpath);
495 IndicatorObject * io = indicator_object_new_from_file(fullpath);
496 g_free(fullpath);
497
498 /* Set the environment it's in */
499 indicator_object_set_environment(io, (const GStrv)indicator_env);
500
501 /* Attach the 'name' to the object */
502 g_object_set_data_full(G_OBJECT(io), IO_DATA_NAME, g_strdup(name), g_free);
503 g_object_set_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER, GINT_TO_POINTER(name2order(name, NULL)));
504
505 /* Connect to it's signals */
506 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(entry_added), menubar);
507 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, G_CALLBACK(entry_removed), menubar);
508 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED, G_CALLBACK(entry_moved), menubar);
509 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_MENU_SHOW, G_CALLBACK(menu_show), menubar);
510
511 /* Work on the entries */
512 GList * entries = indicator_object_get_entries(io);
513 GList * entry = NULL;
514
515 for (entry = entries; entry != NULL; entry = g_list_next(entry))
516 {
517 IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data;
518 entry_added(io, entrydata, menubar);
519 }
520
521 g_list_free(entries);
522
523 return TRUE;
524 }
525
526 static void
527 log_to_file_cb (GObject * source_obj G_GNUC_UNUSED,
528 GAsyncResult * result G_GNUC_UNUSED, gpointer user_data)
529 {
530 g_free(user_data);
531 return;
532 }
533
534 static void
535 log_to_file (const gchar * domain G_GNUC_UNUSED,
536 GLogLevelFlags level G_GNUC_UNUSED,
537 const gchar * message,
538 gpointer data G_GNUC_UNUSED)
539 {
540 if (log_file == NULL) {
541 GError * error = NULL;
542 gchar * filename = g_build_filename(g_get_user_cache_dir(), LOG_FILE_NAME, NULL);
543 GFile * file = g_file_new_for_path(filename);
544 g_free(filename);
545
546 if (!g_file_test(g_get_user_cache_dir(), G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
547 GFile * cachedir = g_file_new_for_path(g_get_user_cache_dir());
548 g_file_make_directory_with_parents(cachedir, NULL, &error);
549
550 if (error != NULL) {
551 g_error("Unable to make directory '%s' for log file: %s", g_get_user_cache_dir(), error->message);
552 return;
553 }
554 }
555
556 g_file_delete(file, NULL, NULL);
557
558 GFileIOStream * io = g_file_create_readwrite(file,
559 G_FILE_CREATE_REPLACE_DESTINATION, /* flags */
560 NULL, /* cancelable */
561 &error); /* error */
562 if (error != NULL) {
563 g_error("Unable to replace file: %s", error->message);
564 return;
565 }
566
567 log_file = g_io_stream_get_output_stream(G_IO_STREAM(io));
568 }
569
570 gchar * outputstring = g_strdup_printf("%s\n", message);
571 g_output_stream_write_async(log_file,
572 outputstring, /* data */
573 strlen(outputstring), /* length */
574 G_PRIORITY_LOW, /* priority */
575 NULL, /* cancelable */
576 log_to_file_cb, /* callback */
577 outputstring); /* data */
578
579 return;
580 }
581
582 static gboolean
583 menubar_press (GtkWidget * widget,
584 GdkEventButton *event,
585 gpointer data G_GNUC_UNUSED)
586
587 {
588 if (event->button != 1) {
589 g_signal_stop_emission_by_name(widget, "button-press-event");
590 }
591
592 return FALSE;
593
594 }
595
596 static gboolean
597 menubar_scroll (GtkWidget *widget G_GNUC_UNUSED,
598 GdkEventScroll *event,
599 gpointer data G_GNUC_UNUSED)
600 {
601
602 GtkWidget *menuitem;
603
604 menuitem = gtk_get_event_widget ((GdkEvent *)event);
605
606 IndicatorObject *io = g_object_get_data (G_OBJECT (menuitem), "indicator");
607 g_signal_emit_by_name (io, "scroll", 1, event->direction);
608
609 return FALSE;
610
611 }
612
613
614 static gboolean
615 menubar_on_expose (GtkWidget * widget,
616 GdkEventExpose *event G_GNUC_UNUSED,
617 GtkWidget * menubar)
618 {
619
620 if (GTK_WIDGET_HAS_FOCUS(menubar))
621 gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(menubar),
622 NULL, widget, "menubar-applet", 0, 0, -1, -1);
623
624 return FALSE;
625 }
626
627 static gint indicator_load_modules(Plugin * p)
628 {
629
630 gint indicators_loaded = 0;
631 IndicatorPlugin * indicator = (IndicatorPlugin *) p->priv;
632
633 gtk_widget_hide_all(p->pwid);
634
635 GList *l = NULL;
636 for (l = gtk_container_get_children(GTK_CONTAINER(indicator->menubar)); l; l = l->next)
637 {
638 gtk_widget_destroy(GTK_WIDGET(l->data));
639 }
640
641 if (g_file_test(INDICATOR_DIR, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
642 {
643 GDir *dir = g_dir_open(INDICATOR_DIR, 0, NULL);
644
645 const gchar *name;
646 while ((name = g_dir_read_name(dir)) != NULL)
647 {
648
649 if (g_strcmp0(name, "libsession.so")== 0) {
650 if (indicator->session == 1){
651 load_module(name, indicator->menubar);
652 indicators_loaded++;
653 }
654 }
655 else if (g_strcmp0(name, "libapplication.so")== 0) {
656 if (indicator->applications == 1){
657 load_module(name, indicator->menubar);
658 indicators_loaded++;
659 }
660 }
661 else if (g_strcmp0(name, "libdatetime.so")== 0) {
662 if (indicator->datetime == 1) {
663 load_module(name, indicator->menubar);
664 indicators_loaded++;
665 }
666 }
667 else if (g_strcmp0(name, "libmessaging.so")== 0) {
668 if (indicator->messages == 1) {
669 load_module(name, indicator->menubar);
670 indicators_loaded++;
671 }
672 }
673 else if (g_strcmp0(name, "libnetworkmenu.so")== 0) {
674 if (indicator->network == 1) {
675 load_module(name, indicator->menubar);
676 indicators_loaded++;
677 }
678 }
679 else if (g_strcmp0(name, "libsoundmenu.so")== 0) {
680 if (indicator->sound == 1) {
681 load_module(name, indicator->menubar);
682 indicators_loaded++;
683 }
684 }
685 /* else if (g_strcmp0(name, "libappmenu.so") == 0) {
686 if (indicator->appmenu == 1) {
687 load_module(name, indicator->menubar);
688 indicators_loaded++;
689 }
690 }*/
691 }
692 g_dir_close (dir);
693 }
694
695 if (indicators_loaded == 0)
696 {
697 /* A label to allow for click through */
698 GtkWidget * item = gtk_label_new(_("No Indicators"));
699 gtk_container_add(GTK_CONTAINER(p->pwid), item);
700 gtk_widget_show(item);
701 }
702 else
703 {
704 gtk_container_add(GTK_CONTAINER(p->pwid), indicator->menubar);
705
706 /* Set background to default. */
707 gtk_widget_set_style(indicator->menubar, p->panel->defstyle);
708 gtk_widget_show(indicator->menubar);
709 }
710
711 /* Update the display, show the widget, and return. */
712 gtk_widget_show_all(p->pwid);
713
714 }
715
716 /* Plugin constructor. */
717 static int indicator_constructor(Plugin * p, char ** fp)
718 {
719 /* Allocate and initialize plugin context and set into Plugin private data pointer. */
720 IndicatorPlugin * indicator = g_new0(IndicatorPlugin, 1);
721 indicator->plugin = p;
722 p->priv = indicator;
723
724 /* Default support for indicators */
725 indicator->applications = TRUE;
726 indicator->datetime = FALSE;
727 indicator->messages = FALSE;
728 indicator->network = FALSE;
729 indicator->session = FALSE;
730 indicator->sound = FALSE;
731 /* indicator->appmenu = FALSE; */
732
733 /* Load parameters from the configuration file. */
734
735 line s;
736 s.len = 256;
737 if (fp != NULL)
738 {
739 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END)
740 {
741 if (s.type == LINE_NONE)
742 {
743 ERR( "space: illegal token %s\n", s.str);
744 return 0;
745 }
746 if (s.type == LINE_VAR)
747 {
748 if (g_ascii_strcasecmp(s.t[0], "applications") == 0)
749 indicator->applications = str2num(bool_pair, s.t[1], 0);
750 else if (g_ascii_strcasecmp(s.t[0], "datetime") == 0)
751 indicator->datetime = str2num(bool_pair, s.t[1], 0);
752 else if (g_ascii_strcasecmp(s.t[0], "messages") == 0)
753 indicator->messages = str2num(bool_pair, s.t[1], 0);
754 else if (g_ascii_strcasecmp(s.t[0], "network") == 1)
755 indicator->network = str2num(bool_pair, s.t[1], 0);
756 else if (g_ascii_strcasecmp(s.t[0], "session") == 0)
757 indicator->session = str2num(bool_pair, s.t[1], 0);
758 else if (g_ascii_strcasecmp(s.t[0], "sound") == 0)
759 indicator->sound = str2num(bool_pair, s.t[1], 0);
760 /* else if (g_ascii_strcasecmp(s.t[0], "appmenu") == 0)
761 indicator->appmenu = str2num(bool_pair, s.t[1], 0);*/
762
763 else
764 ERR( "indicator: unknown var %s\n", s.t[0]);
765 }
766 else
767 {
768 ERR( "indicator: illegal in this context %s\n", s.str);
769 return 0;
770 }
771 }
772 }
773
774 /* Allocate top level widget and set into Plugin widget pointer. */
775 p->pwid = gtk_event_box_new();
776
777 gtk_rc_parse_string (
778 "style \"indicator-applet-style\"\n"
779 "{\n"
780 " GtkMenuBar::shadow-type = none\n"
781 " GtkMenuBar::internal-padding = 0\n"
782 " GtkWidget::focus-line-width = 0\n"
783 " GtkWidget::focus-padding = 0\n"
784 "}\n"
785 "style \"indicator-applet-menubar-style\"\n"
786 "{\n"
787 " GtkMenuBar::shadow-type = none\n"
788 " GtkMenuBar::internal-padding = 0\n"
789 " GtkWidget::focus-line-width = 0\n"
790 " GtkWidget::focus-padding = 0\n"
791 " GtkMenuItem::horizontal-padding = 0\n"
792 "}\n"
793 "style \"indicator-applet-menuitem-style\"\n"
794 "{\n"
795 " GtkWidget::focus-line-width = 0\n"
796 " GtkWidget::focus-padding = 0\n"
797 " GtkMenuItem::horizontal-padding = 0\n"
798 "}\n"
799 "widget \"*.fast-user-switch-applet\" style \"indicator-applet-style\""
800 "widget \"*.fast-user-switch-menuitem\" style \"indicator-applet-menuitem-style\""
801 "widget \"*.fast-user-switch-menubar\" style \"indicator-applet-menubar-style\"");
802
803 gtk_widget_set_name(GTK_WIDGET (p->pwid), "fast-user-switch-applet");
804
805 /* Connect signals for container */
806 g_signal_connect(p->pwid, "button-press-event", G_CALLBACK(plugin_button_press_event), p);
807
808 g_log_set_default_handler(log_to_file, NULL);
809
810 /* Allocate icon as a child of top level. */
811 indicator->menubar = gtk_menu_bar_new();
812 GTK_WIDGET_SET_FLAGS (indicator->menubar, GTK_WIDGET_FLAGS(indicator->menubar) | GTK_CAN_FOCUS);
813
814 /* Init some theme/icon stuff */
815 gtk_icon_theme_append_search_path(p->panel->icon_theme,
816 INDICATOR_ICONS_DIR);
817 g_debug("Icons directory: %s", INDICATOR_ICONS_DIR);
818
819 gtk_widget_set_name(GTK_WIDGET (indicator->menubar), "fast-user-switch-menubar");
820
821 /* Connect signals. */
822 g_signal_connect(p->pwid, "button-press-event", G_CALLBACK(plugin_button_press_event), p);
823 g_signal_connect(indicator->menubar, "button-press-event", G_CALLBACK(menubar_press), NULL);
824 g_signal_connect(indicator->menubar, "scroll-event", G_CALLBACK (menubar_scroll), NULL);
825 g_signal_connect_after(indicator->menubar, "expose-event", G_CALLBACK(menubar_on_expose), indicator->menubar);
826
827 gtk_container_set_border_width(GTK_CONTAINER(indicator->menubar), 0);
828
829 /* load 'em */
830 indicator_load_modules(p);
831
832 return 1;
833
834 }
835
836 /* Plugin destructor. */
837 static void indicator_destructor(Plugin * p)
838 {
839 IndicatorPlugin * indicator = (IndicatorPlugin *) p->priv;
840
841 /* Deallocate all memory. */
842 g_free(indicator);
843 }
844
845 /* Callback when panel configuration changes. */
846 static void indicator_panel_configuration_changed(Plugin * p)
847 {
848 /*
849 Update when configuration changed
850 */
851
852 /* load 'em */
853 indicator_load_modules(p);
854
855 /* Determine if the orientation changed in a way that requires action. */
856 /*
857 GtkWidget * sep = gtk_bin_get_child(GTK_BIN(p->pwid));
858 if (GTK_IS_VSEPARATOR(sep))
859 {
860 if (p->panel->orientation == GTK_ORIENTATION_HORIZONTAL)
861 return;
862 }
863 else
864 {
865 if (p->panel->orientation == GTK_ORIENTATION_VERTICAL)
866 return;
867 }
868 */
869 }
870
871 /* Callback when the configuration dialog has recorded a configuration change. */
872 static void indicator_apply_configuration(Plugin * p)
873 {
874
875 /* IndicatorPlugin * indicator = (IndicatorPlugin *) p->priv;*/
876
877 /* load 'em */
878 indicator_load_modules(p);
879
880 /* Apply settings. */
881 /*
882 if (p->panel->orientation == ORIENT_HORIZ)
883 gtk_widget_set_size_request(p->pwid, sp->size, 2);
884 else
885 gtk_widget_set_size_request(p->pwid, 2, sp->size);
886 */
887 }
888
889 /* Callback when the configuration dialog is to be shown. */
890 static void indicator_configure(Plugin * p, GtkWindow * parent)
891 {
892 IndicatorPlugin * indicator = (IndicatorPlugin *) p->priv;
893 GtkWidget * dlg = create_generic_config_dlg(
894 _(p->class->name),
895 GTK_WIDGET(parent),
896 (GSourceFunc) indicator_apply_configuration, (gpointer) p,
897 _("Indicator Applications"), &indicator->applications, CONF_TYPE_BOOL,
898 _("Clock Indicator"), &indicator->datetime, CONF_TYPE_BOOL,
899 _("Messaging Menu"), &indicator->messages, CONF_TYPE_BOOL,
900 _("Network Menu"), &indicator->network, CONF_TYPE_BOOL,
901 _("Session Menu"), &indicator->session, CONF_TYPE_BOOL,
902 _("Sound Menu"), &indicator->sound, CONF_TYPE_BOOL,
903 /* _("Applications menus"), &indicator->appmenu, CONF_TYPE_BOOL,*/
904 NULL);
905 gtk_widget_set_size_request(GTK_WIDGET(dlg), 300, -1);
906 gtk_window_present(GTK_WINDOW(dlg));
907 }
908
909 /* Callback when the configuration is to be saved. */
910 static void indicator_save_configuration(Plugin * p, FILE * fp)
911 {
912 IndicatorPlugin * indicator= (IndicatorPlugin *) p->priv;
913 lxpanel_put_int(fp, "applications", indicator->applications);
914 lxpanel_put_int(fp, "datetime", indicator->datetime);
915 lxpanel_put_int(fp, "messages", indicator->messages);
916 lxpanel_put_int(fp, "network", indicator->network);
917 lxpanel_put_int(fp, "session", indicator->session);
918 lxpanel_put_int(fp, "sound", indicator->sound);
919 /* lxpanel_put_int(fp, "appmenu", indicator->appmenu);*/
920 }
921
922 /* Plugin descriptor. */
923 PluginClass indicator_plugin_class = {
924
925 PLUGINCLASS_VERSIONING,
926
927 type : "indicator",
928 name : N_("Indicator applets"),
929 version: "1.0",
930 description : N_("Add indicator applets to the panel"),
931
932 constructor : indicator_constructor,
933 destructor : indicator_destructor,
934 config : indicator_configure,
935 save : indicator_save_configuration,
936 panel_configuration_changed : indicator_panel_configuration_changed
937 };