Merging upstream version 0.8.2 (Closes: #786485, #784725, #801319).
[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 void indicator_destructor(gpointer user_data);
97
98 static gint
99 name2order (const gchar * name, const gchar * hint) {
100 int i;
101
102 for (i = 0; indicator_order[i][0] != NULL; i++) {
103 if (g_strcmp0(name, indicator_order[i][0]) == 0 &&
104 g_strcmp0(hint, indicator_order[i][1]) == 0) {
105 return i;
106 }
107 }
108
109 return -1;
110 }
111
112 typedef struct _incoming_position_t incoming_position_t;
113 struct _incoming_position_t {
114 gint objposition;
115 gint entryposition;
116 gint menupos;
117 gboolean found;
118 };
119
120 /* This function helps by determining where in the menu list
121 this new entry should be placed. It compares the objects
122 that they're on, and then the individual entries. Each
123 is progressively more expensive. */
124 static void
125 place_in_menu_cb (GtkWidget * widget, gpointer user_data)
126 {
127 incoming_position_t * position = (incoming_position_t *)user_data;
128 if (position->found) {
129 /* We've already been placed, just finish the foreach */
130 return;
131 }
132
133 IndicatorObject * io = INDICATOR_OBJECT(g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_OBJECT));
134 g_return_if_fail(INDICATOR_IS_OBJECT(io));
135
136 gint objposition = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
137 /* We've already passed it, well, then this is where
138 we should be be. Stop! */
139 if (objposition > position->objposition) {
140 position->found = TRUE;
141 return;
142 }
143
144 /* The objects don't match yet, keep looking */
145 if (objposition < position->objposition) {
146 position->menupos++;
147 return;
148 }
149
150 /* The objects are the same, let's start looking at entries. */
151 IndicatorObjectEntry * entry = (IndicatorObjectEntry *)g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
152 gint entryposition = indicator_object_get_location(io, entry);
153
154 if (entryposition > position->entryposition) {
155 position->found = TRUE;
156 return;
157 }
158
159 if (entryposition < position->entryposition) {
160 position->menupos++;
161 return;
162 }
163
164 /* We've got the same object and the same entry. Well,
165 let's just put it right here then. */
166 position->found = TRUE;
167 return;
168 }
169
170 /* Position the entry */
171 static void
172 place_in_menu (GtkWidget *menubar,
173 GtkWidget *menuitem,
174 IndicatorObject *io,
175 IndicatorObjectEntry *entry)
176 {
177 incoming_position_t position;
178
179 /* Start with the default position for this indicator object */
180 gint io_position = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
181
182 /* If name-hint is set, try to find the entry's position */
183 if (entry->name_hint != NULL) {
184 const gchar *name = (const gchar *)g_object_get_data(G_OBJECT(io), IO_DATA_NAME);
185 gint entry_position = name2order(name, entry->name_hint);
186
187 /* If we don't find the entry, fall back to the indicator object's position */
188 if (entry_position > -1)
189 io_position = entry_position;
190 }
191
192 position.objposition = io_position;
193 position.entryposition = indicator_object_get_location(io, entry);
194 position.menupos = 0;
195 position.found = FALSE;
196
197 gtk_container_foreach(GTK_CONTAINER(menubar), place_in_menu_cb, &position);
198
199 gtk_menu_shell_insert(GTK_MENU_SHELL(menubar), menuitem, position.menupos);
200 }
201
202 static void
203 something_shown (GtkWidget * widget, gpointer user_data)
204 {
205 GtkWidget * menuitem = GTK_WIDGET(user_data);
206 gtk_widget_show(menuitem);
207 }
208
209 static void
210 something_hidden (GtkWidget * widget, gpointer user_data)
211 {
212 GtkWidget * menuitem = GTK_WIDGET(user_data);
213 gtk_widget_hide(menuitem);
214 }
215
216 static void
217 sensitive_cb (GObject * obj, GParamSpec * pspec, gpointer user_data)
218 {
219 g_return_if_fail(GTK_IS_WIDGET(obj));
220 g_return_if_fail(GTK_IS_WIDGET(user_data));
221
222 gtk_widget_set_sensitive(GTK_WIDGET(user_data), gtk_widget_get_sensitive(GTK_WIDGET(obj)));
223 return;
224 }
225
226 static void
227 entry_activated (GtkWidget * widget, gpointer user_data)
228 {
229 g_return_if_fail(GTK_IS_WIDGET(widget));
230
231 IndicatorObject *io = g_object_get_data (G_OBJECT (widget), MENU_DATA_INDICATOR_OBJECT);
232 IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (widget), MENU_DATA_INDICATOR_ENTRY);
233
234 g_return_if_fail(INDICATOR_IS_OBJECT(io));
235
236 return indicator_object_entry_activate(io, entry, gtk_get_current_event_time());
237 }
238
239 static gboolean
240 entry_secondary_activated (GtkWidget * widget, GdkEvent * event, gpointer user_data)
241 {
242 g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
243
244 switch (event->type) {
245 case GDK_ENTER_NOTIFY:
246 g_object_set_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM, GINT_TO_POINTER(TRUE));
247 break;
248
249 case GDK_LEAVE_NOTIFY:
250 g_object_set_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM, GINT_TO_POINTER(FALSE));
251 g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(FALSE));
252 break;
253
254 case GDK_BUTTON_PRESS:
255 if (event->button.button == 2) {
256 g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(TRUE));
257 }
258 break;
259
260 case GDK_BUTTON_RELEASE:
261 if (event->button.button == 2) {
262 gboolean in_menuitem = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM));
263 gboolean menuitem_pressed = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED));
264
265 if (in_menuitem && menuitem_pressed) {
266 g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(FALSE));
267
268 IndicatorObject *io = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_OBJECT);
269 IndicatorObjectEntry *entry = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
270
271 g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
272
273 g_signal_emit_by_name(io, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE,
274 entry, event->button.time);
275 }
276 }
277 break;
278 default: ;
279 }
280
281 return FALSE;
282 }
283
284 static gboolean
285 entry_scrolled (GtkWidget *menuitem, GdkEventScroll *event, gpointer data)
286 {
287 g_return_val_if_fail(GTK_IS_WIDGET(menuitem), FALSE);
288
289 IndicatorObject *io = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_OBJECT);
290 IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_ENTRY);
291
292 g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
293
294 g_signal_emit_by_name (io, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry, 1, event->direction);
295
296 return FALSE;
297 }
298
299 static void
300 entry_added (IndicatorObject * io, IndicatorObjectEntry * entry, GtkWidget * menubar)
301 {
302 const char *indicator_name = (const gchar *)g_object_get_data(G_OBJECT(io), IO_DATA_NAME);
303 g_debug("Signal: Entry Added from %s", indicator_name);
304 gboolean something_visible = FALSE;
305 gboolean something_sensitive = FALSE;
306
307 GtkWidget * menuitem = gtk_menu_item_new();
308 GtkWidget * hbox = gtk_hbox_new(FALSE, 3);
309
310 g_object_set_data (G_OBJECT (menuitem), MENU_DATA_BOX, hbox);
311 g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_OBJECT, io);
312 g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_ENTRY, entry);
313
314 g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(entry_activated), NULL);
315 g_signal_connect(G_OBJECT(menuitem), "button-press-event", G_CALLBACK(entry_secondary_activated), NULL);
316 g_signal_connect(G_OBJECT(menuitem), "button-release-event", G_CALLBACK(entry_secondary_activated), NULL);
317 g_signal_connect(G_OBJECT(menuitem), "enter-notify-event", G_CALLBACK(entry_secondary_activated), NULL);
318 g_signal_connect(G_OBJECT(menuitem), "leave-notify-event", G_CALLBACK(entry_secondary_activated), NULL);
319 g_signal_connect(G_OBJECT(menuitem), "scroll-event", G_CALLBACK(entry_scrolled), NULL);
320
321 if (entry->image != NULL)
322 {
323 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry->image), FALSE, FALSE, 1);
324 if (gtk_widget_get_visible(GTK_WIDGET(entry->image))) {
325 something_visible = TRUE;
326 }
327
328 if (gtk_widget_get_sensitive(GTK_WIDGET(entry->image))) {
329 something_sensitive = TRUE;
330 }
331
332 g_signal_connect(G_OBJECT(entry->image), "show", G_CALLBACK(something_shown), menuitem);
333 g_signal_connect(G_OBJECT(entry->image), "hide", G_CALLBACK(something_hidden), menuitem);
334 g_signal_connect(G_OBJECT(entry->image), "notify::sensitive", G_CALLBACK(sensitive_cb), menuitem);
335 }
336 if (entry->label != NULL)
337 {
338 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry->label), FALSE, FALSE, 1);
339
340 if (gtk_widget_get_visible(GTK_WIDGET(entry->label))) {
341 something_visible = TRUE;
342 }
343
344 if (gtk_widget_get_sensitive(GTK_WIDGET(entry->label))) {
345
346 something_sensitive = TRUE;
347 }
348
349 g_signal_connect(G_OBJECT(entry->label), "show", G_CALLBACK(something_shown), menuitem);
350 g_signal_connect(G_OBJECT(entry->label), "hide", G_CALLBACK(something_hidden), menuitem);
351 g_signal_connect(G_OBJECT(entry->label), "notify::sensitive", G_CALLBACK(sensitive_cb), menuitem);
352
353 }
354 gtk_container_add(GTK_CONTAINER(menuitem), hbox);
355 gtk_widget_show(hbox);
356
357 if (entry->menu != NULL)
358 {
359 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(entry->menu));
360 }
361
362 place_in_menu(menubar, menuitem, io, entry);
363
364 if (something_visible) {
365 gtk_widget_show(menuitem);
366 }
367 gtk_widget_set_sensitive(menuitem, something_sensitive);
368
369 return;
370 }
371
372 static void
373 entry_removed_cb (GtkWidget * widget, gpointer userdata)
374 {
375 gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
376
377 if (data != userdata)
378 {
379 return;
380 }
381
382 IndicatorObjectEntry * entry = (IndicatorObjectEntry *)data;
383 if (entry->label != NULL) {
384 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(something_shown), widget);
385 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(something_hidden), widget);
386 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(sensitive_cb), widget);
387 }
388 if (entry->image != NULL) {
389 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(something_shown), widget);
390 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(something_hidden), widget);
391 g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(sensitive_cb), widget);
392 }
393
394 gtk_widget_destroy(widget);
395 return;
396 }
397
398 static void
399 entry_moved_find_cb (GtkWidget * widget, gpointer userdata)
400 {
401 gpointer * array = (gpointer *)userdata;
402 if (array[1] != NULL) {
403 return;
404 }
405
406 gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
407
408 if (data != array[0]) {
409 return;
410 }
411
412 array[1] = widget;
413 return;
414 }
415
416 /* Gets called when an entry for an object was moved. */
417 static void
418 entry_moved (IndicatorObject * io, IndicatorObjectEntry * entry,
419 gint old G_GNUC_UNUSED, gint new G_GNUC_UNUSED, gpointer user_data)
420 {
421 GtkWidget * menubar = GTK_WIDGET(user_data);
422
423 gpointer array[2];
424 array[0] = entry;
425 array[1] = NULL;
426
427 gtk_container_foreach(GTK_CONTAINER(menubar), entry_moved_find_cb, array);
428 if (array[1] == NULL) {
429 g_warning("Moving an entry that isn't in our menus.");
430 return;
431 }
432
433 GtkWidget * mi = GTK_WIDGET(array[1]);
434 g_object_ref(G_OBJECT(mi));
435 gtk_container_remove(GTK_CONTAINER(menubar), mi);
436 place_in_menu(menubar, mi, io, entry);
437 g_object_unref(G_OBJECT(mi));
438
439 return;
440 }
441
442 static void
443 entry_removed (IndicatorObject * io G_GNUC_UNUSED, IndicatorObjectEntry * entry,
444 gpointer user_data)
445 {
446 g_debug("Signal: Entry Removed");
447
448 gtk_container_foreach(GTK_CONTAINER(user_data), entry_removed_cb, entry);
449
450 return;
451 }
452
453 static void
454 menu_show (IndicatorObject * io, IndicatorObjectEntry * entry,
455 guint32 timestamp, gpointer user_data)
456 {
457 GtkWidget * menubar = GTK_WIDGET(user_data);
458
459 if (entry == NULL) {
460 /* Close any open menus instead of opening one */
461 GList * entries = indicator_object_get_entries(io);
462 GList * entry = NULL;
463 for (entry = entries; entry != NULL; entry = g_list_next(entry)) {
464 IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data;
465 gtk_menu_popdown(entrydata->menu);
466 }
467 g_list_free(entries);
468
469 /* And tell the menubar to exit activation mode too */
470 gtk_menu_shell_cancel(GTK_MENU_SHELL(menubar));
471 return;
472 }
473
474 // TODO: do something sensible here
475 }
476
477 static gboolean
478 load_module (const gchar * name, GtkWidget * menubar)
479 {
480 g_debug("Looking at Module: %s", name);
481 g_return_val_if_fail(name != NULL, FALSE);
482
483 if (!g_str_has_suffix(name, G_MODULE_SUFFIX))
484 {
485 return FALSE;
486 }
487
488 g_debug("Loading Module: %s", name);
489
490 /* Build the object for the module */
491 gchar *fullpath = g_build_filename(INDICATOR_DIR, name, NULL);
492 g_debug("Full path: %s", fullpath);
493 IndicatorObject * io = indicator_object_new_from_file(fullpath);
494 g_free(fullpath);
495
496 /* Set the environment it's in */
497 indicator_object_set_environment(io, (const GStrv)indicator_env);
498
499 /* Attach the 'name' to the object */
500 g_object_set_data_full(G_OBJECT(io), IO_DATA_NAME, g_strdup(name), g_free);
501 g_object_set_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER, GINT_TO_POINTER(name2order(name, NULL)));
502
503 /* Connect to it's signals */
504 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(entry_added), menubar);
505 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, G_CALLBACK(entry_removed), menubar);
506 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED, G_CALLBACK(entry_moved), menubar);
507 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_MENU_SHOW, G_CALLBACK(menu_show), menubar);
508
509 /* Work on the entries */
510 GList * entries = indicator_object_get_entries(io);
511 GList * entry = NULL;
512
513 for (entry = entries; entry != NULL; entry = g_list_next(entry))
514 {
515 IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data;
516 entry_added(io, entrydata, menubar);
517 }
518
519 g_list_free(entries);
520
521 return TRUE;
522 }
523
524 #if 0
525 static void
526 log_to_file_cb (GObject * source_obj G_GNUC_UNUSED,
527 GAsyncResult * result G_GNUC_UNUSED, gpointer user_data)
528 {
529 g_free(user_data);
530 return;
531 }
532
533 static void
534 log_to_file (const gchar * domain G_GNUC_UNUSED,
535 GLogLevelFlags level G_GNUC_UNUSED,
536 const gchar * message,
537 gpointer data G_GNUC_UNUSED)
538 {
539 if (log_file == NULL) {
540 GError * error = NULL;
541 gchar * filename = g_build_filename(g_get_user_cache_dir(), LOG_FILE_NAME, NULL);
542 GFile * file = g_file_new_for_path(filename);
543 g_free(filename);
544
545 if (!g_file_test(g_get_user_cache_dir(), G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
546 GFile * cachedir = g_file_new_for_path(g_get_user_cache_dir());
547 g_file_make_directory_with_parents(cachedir, NULL, &error);
548
549 if (error != NULL) {
550 g_error("Unable to make directory '%s' for log file: %s", g_get_user_cache_dir(), error->message);
551 return;
552 }
553 }
554
555 g_file_delete(file, NULL, NULL);
556
557 GFileIOStream * io = g_file_create_readwrite(file,
558 G_FILE_CREATE_REPLACE_DESTINATION, /* flags */
559 NULL, /* cancelable */
560 &error); /* error */
561 if (error != NULL) {
562 g_error("Unable to replace file: %s", error->message);
563 return;
564 }
565
566 log_file = g_io_stream_get_output_stream(G_IO_STREAM(io));
567 }
568
569 gchar * outputstring = g_strdup_printf("%s\n", message);
570 g_output_stream_write_async(log_file,
571 outputstring, /* data */
572 strlen(outputstring), /* length */
573 G_PRIORITY_LOW, /* priority */
574 NULL, /* cancelable */
575 log_to_file_cb, /* callback */
576 outputstring); /* data */
577
578 return;
579 }
580 #endif
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 static gboolean
614 menubar_on_expose (GtkWidget * widget,
615 GdkEventExpose *event G_GNUC_UNUSED,
616 GtkWidget * menubar)
617 {
618 if (GTK_WIDGET_HAS_FOCUS(menubar))
619 gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(menubar),
620 NULL, widget, "menubar-applet", 0, 0, -1, -1);
621
622 return FALSE;
623 }
624
625 static void indicator_load_modules(LXPanel *panel, GtkWidget *p)
626 {
627
628 gint indicators_loaded = 0;
629 IndicatorPlugin * indicator = lxpanel_plugin_get_data(p);
630
631 gtk_container_forall(GTK_CONTAINER(indicator->menubar),
632 (GtkCallback)gtk_widget_destroy, NULL);
633
634 if (g_file_test(INDICATOR_DIR, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
635 {
636 GDir *dir = g_dir_open(INDICATOR_DIR, 0, NULL);
637
638 const gchar *name;
639 while ((name = g_dir_read_name(dir)) != NULL)
640 {
641
642 if (g_strcmp0(name, "libsession.so")== 0) {
643 if (indicator->session == 1){
644 load_module(name, indicator->menubar);
645 indicators_loaded++;
646 }
647 }
648 else if (g_strcmp0(name, "libapplication.so")== 0) {
649 if (indicator->applications == 1){
650 load_module(name, indicator->menubar);
651 indicators_loaded++;
652 }
653 }
654 else if (g_strcmp0(name, "libdatetime.so")== 0) {
655 if (indicator->datetime == 1) {
656 load_module(name, indicator->menubar);
657 indicators_loaded++;
658 }
659 }
660 else if (g_strcmp0(name, "libmessaging.so")== 0) {
661 if (indicator->messages == 1) {
662 load_module(name, indicator->menubar);
663 indicators_loaded++;
664 }
665 }
666 else if (g_strcmp0(name, "libnetworkmenu.so")== 0) {
667 if (indicator->network == 1) {
668 load_module(name, indicator->menubar);
669 indicators_loaded++;
670 }
671 }
672 else if (g_strcmp0(name, "libsoundmenu.so")== 0) {
673 if (indicator->sound == 1) {
674 load_module(name, indicator->menubar);
675 indicators_loaded++;
676 }
677 }
678 /* else if (g_strcmp0(name, "libappmenu.so") == 0) {
679 if (indicator->appmenu == 1) {
680 load_module(name, indicator->menubar);
681 indicators_loaded++;
682 }
683 }*/
684 }
685 g_dir_close (dir);
686 }
687
688 /* Update the plugin container contents */
689 if (gtk_bin_get_child(GTK_BIN(p)))
690 gtk_container_remove(GTK_CONTAINER(p), gtk_bin_get_child(GTK_BIN(p)));
691 if (indicators_loaded == 0)
692 {
693 /* A label to allow for click through */
694 GtkWidget *label = gtk_label_new(_("No Indicators"));
695 gtk_widget_show(label);
696 gtk_container_add(GTK_CONTAINER(p), label);
697 }
698 else
699 {
700 gtk_container_add(GTK_CONTAINER(p), indicator->menubar);
701 /* Enforce background */
702 plugin_widget_set_background(indicator->menubar, panel);
703 }
704 }
705
706 /* Plugin constructor. */
707 static GtkWidget *indicator_constructor(LXPanel *panel, config_setting_t *settings)
708 {
709 /* Allocate and initialize plugin context and set into Plugin private data pointer. */
710 IndicatorPlugin * indicator = g_new0(IndicatorPlugin, 1);
711 GtkWidget *p;
712 int tmp_int;
713
714 indicator->panel = panel;
715 indicator->settings = settings;
716
717 /* Default support for indicators */
718 indicator->applications = TRUE;
719 indicator->datetime = FALSE;
720 indicator->messages = FALSE;
721 indicator->network = FALSE;
722 indicator->session = FALSE;
723 indicator->sound = FALSE;
724 /* indicator->appmenu = FALSE; */
725
726 /* Load parameters from the configuration file. */
727 if (config_setting_lookup_int(settings, "applications", &tmp_int))
728 indicator->applications = tmp_int != 0;
729 if (config_setting_lookup_int(settings, "datetime", &tmp_int))
730 indicator->datetime = tmp_int != 0;
731 if (config_setting_lookup_int(settings, "messages", &tmp_int))
732 indicator->messages = tmp_int != 0;
733 if (config_setting_lookup_int(settings, "network", &tmp_int))
734 indicator->network = tmp_int != 0;
735 if (config_setting_lookup_int(settings, "session", &tmp_int))
736 indicator->session = tmp_int != 0;
737 if (config_setting_lookup_int(settings, "sound", &tmp_int))
738 indicator->sound = tmp_int != 0;
739 /* if (config_setting_lookup_int(settings, "appmenu", &tmp_int))
740 indicator->appmenu = tmp_int != 0;*/
741
742 /* Allocate top level widget and set into Plugin widget pointer. */
743 p = gtk_event_box_new();
744 lxpanel_plugin_set_data(p, indicator, indicator_destructor);
745
746 gtk_rc_parse_string (
747 "style \"indicator-applet-style\"\n"
748 "{\n"
749 " GtkMenuBar::shadow-type = none\n"
750 " GtkMenuBar::internal-padding = 0\n"
751 " GtkWidget::focus-line-width = 0\n"
752 " GtkWidget::focus-padding = 0\n"
753 "}\n"
754 "style \"indicator-applet-menubar-style\"\n"
755 "{\n"
756 " GtkMenuBar::shadow-type = none\n"
757 " GtkMenuBar::internal-padding = 0\n"
758 " GtkWidget::focus-line-width = 0\n"
759 " GtkWidget::focus-padding = 0\n"
760 " GtkMenuItem::horizontal-padding = 0\n"
761 "}\n"
762 "style \"indicator-applet-menuitem-style\"\n"
763 "{\n"
764 " GtkWidget::focus-line-width = 0\n"
765 " GtkWidget::focus-padding = 0\n"
766 " GtkMenuItem::horizontal-padding = 0\n"
767 "}\n"
768 "widget \"*.fast-user-switch-applet\" style \"indicator-applet-style\""
769 "widget \"*.fast-user-switch-menuitem\" style \"indicator-applet-menuitem-style\""
770 "widget \"*.fast-user-switch-menubar\" style \"indicator-applet-menubar-style\"");
771
772 gtk_widget_set_name(p, "fast-user-switch-applet");
773
774 /* Connect signals for container */
775 //g_log_set_default_handler(log_to_file, NULL);
776
777 /* Allocate icon as a child of top level. */
778 indicator->menubar = g_object_ref_sink(gtk_menu_bar_new());
779 gtk_widget_show(indicator->menubar);
780 gtk_widget_set_can_focus(indicator->menubar, TRUE);
781
782 /* Init some theme/icon stuff */
783 gtk_icon_theme_append_search_path(panel_get_icon_theme(panel),
784 INDICATOR_ICONS_DIR);
785 g_debug("Icons directory: %s", INDICATOR_ICONS_DIR);
786
787 gtk_widget_set_name(indicator->menubar, "fast-user-switch-menubar");
788
789 /* Connect signals. */
790 g_signal_connect(indicator->menubar, "button-press-event", G_CALLBACK(menubar_press), NULL);
791 g_signal_connect(indicator->menubar, "scroll-event", G_CALLBACK (menubar_scroll), NULL);
792 g_signal_connect_after(indicator->menubar, "expose-event", G_CALLBACK(menubar_on_expose), indicator->menubar);
793
794 gtk_container_set_border_width(GTK_CONTAINER(indicator->menubar), 0);
795
796 return p;
797 }
798
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_object_unref(indicator->menubar);
806 g_free(indicator);
807 }
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 };