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