cdd172ddde59faf353d54b304daca755af7bb50f
[lxde/lxpanel.git] / src / plugin.c
1 /**
2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "private.h"
24
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
27 #include <gdk/gdk.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "misc.h"
32 #include "bg.h"
33
34 #include <glib-object.h>
35 #include <glib/gi18n.h>
36 #include <libfm/fm-gtk.h>
37
38 //#define DEBUG
39 #include "dbg.h"
40
41 static void init_plugin_class_list(void);
42 static void plugin_class_unref(PluginClass * pc);
43
44 GQuark lxpanel_plugin_qinit;
45 GQuark lxpanel_plugin_qconf;
46 GQuark lxpanel_plugin_qdata;
47 static GHashTable *_all_types = NULL;
48
49 /* Dynamic parameter for static (built-in) plugins must be FALSE so we will not try to unload them */
50 #define REGISTER_STATIC_PLUGIN_CLASS(pc) \
51 do {\
52 extern PluginClass pc;\
53 register_plugin_class(&pc, FALSE);\
54 } while (0)
55
56 /* The same for new plugins type - they will be not unloaded by FmModule */
57 #define REGISTER_STATIC_MODULE(pc) do { \
58 extern LXPanelPluginInit lxpanel_static_plugin_##pc; \
59 lxpanel_register_plugin_type(#pc, &lxpanel_static_plugin_##pc); } while (0)
60
61 static inline const LXPanelPluginInit *_find_plugin(const char *name)
62 {
63 return g_hash_table_lookup(_all_types, name);
64 }
65
66 static GtkWidget *_old_plugin_config(LXPanel *panel, GtkWidget *instance)
67 {
68 const LXPanelPluginInit *init = PLUGIN_CLASS(instance);
69 Plugin * plugin;
70
71 g_return_val_if_fail(init != NULL && init->new_instance == NULL, NULL);
72 plugin = lxpanel_plugin_get_data(instance);
73 if (plugin->class->config)
74 plugin->class->config(plugin, GTK_WINDOW(panel));
75 return NULL;
76 }
77
78 static void _old_plugin_reconfigure(LXPanel *panel, GtkWidget *instance)
79 {
80 const LXPanelPluginInit *init = PLUGIN_CLASS(instance);
81 Plugin * plugin;
82
83 g_return_if_fail(init != NULL && init->new_instance == NULL);
84 plugin = lxpanel_plugin_get_data(instance);
85 if (plugin->class->panel_configuration_changed)
86 plugin->class->panel_configuration_changed(plugin);
87 }
88
89 /* Register a PluginClass. */
90 static void register_plugin_class(PluginClass * pc, gboolean dynamic)
91 {
92 LXPanelPluginInit *init = g_new0(LXPanelPluginInit, 1);
93 init->_reserved1 = pc;
94 init->name = pc->name;
95 init->description = pc->description;
96 if (pc->config)
97 init->config = _old_plugin_config;
98 if (pc->panel_configuration_changed)
99 init->reconfigure = _old_plugin_reconfigure;
100 init->one_per_system = pc->one_per_system;
101 init->expand_available = pc->expand_available;
102 init->expand_default = pc->expand_default;
103 pc->dynamic = dynamic;
104 g_hash_table_insert(_all_types, g_strdup(pc->type), init);
105 }
106
107 /* Initialize the static plugins. */
108 static void init_plugin_class_list(void)
109 {
110 #ifdef STATIC_SEPARATOR
111 REGISTER_STATIC_MODULE(separator);
112 #endif
113
114 #ifdef STATIC_LAUNCHTASKBAR
115 REGISTER_STATIC_MODULE(launchtaskbar);
116 #endif
117
118 #ifdef STATIC_DCLOCK
119 REGISTER_STATIC_MODULE(dclock);
120 #endif
121
122 #ifdef STATIC_WINCMD
123 REGISTER_STATIC_MODULE(wincmd);
124 #endif
125
126 #ifdef STATIC_DIRMENU
127 REGISTER_STATIC_MODULE(dirmenu);
128 #endif
129
130 #ifdef STATIC_PAGER
131 REGISTER_STATIC_MODULE(pager);
132 #endif
133
134 #ifdef STATIC_TRAY
135 REGISTER_STATIC_MODULE(tray);
136 #endif
137
138 #ifndef DISABLE_MENU
139 #ifdef STATIC_MENU
140 REGISTER_STATIC_MODULE(menu);
141 #endif
142 #endif
143
144 #ifdef STATIC_SPACE
145 REGISTER_STATIC_MODULE(space);
146 #endif
147 }
148
149 /* Load a dynamic plugin. */
150 static void plugin_load_dynamic(const char * type, const gchar * path)
151 {
152 PluginClass * pc = NULL;
153
154 /* Load the external module. */
155 GModule * m = g_module_open(path, G_MODULE_BIND_LAZY);
156 if (m != NULL)
157 {
158 /* Formulate the name of the expected external variable of type PluginClass. */
159 char class_name[128];
160 g_snprintf(class_name, sizeof(class_name), "%s_plugin_class", type);
161
162 /* Validate that the external variable is of type PluginClass. */
163 gpointer tmpsym;
164 if (( ! g_module_symbol(m, class_name, &tmpsym)) /* Ensure symbol is present */
165 || ((pc = tmpsym) == NULL)
166 || (pc->structure_size != sizeof(PluginClass)) /* Then check versioning information */
167 || (pc->structure_version != PLUGINCLASS_VERSION)
168 || (strcmp(type, pc->type) != 0)) /* Then and only then access other fields; check name */
169 {
170 g_module_close(m);
171 ERR("%s.so is not a lxpanel plugin\n", type);
172 return;
173 }
174
175 /* Register the newly loaded and valid plugin. */
176 pc->gmodule = m;
177 register_plugin_class(pc, TRUE);
178 pc->count = 1;
179 }
180 }
181
182 /* Unreference a dynamic plugin. */
183 static void plugin_class_unref(PluginClass * pc)
184 {
185 pc->count -= 1;
186
187 /* If the reference count drops to zero, unload the plugin if it is dynamic and has declared itself unloadable. */
188 if ((pc->count == 0)
189 && (pc->dynamic)
190 && ( ! pc->not_unloadable))
191 {
192 g_module_close(pc->gmodule);
193 }
194 }
195
196 /* Loads all available old type plugins. Should be removed in future releases. */
197 static void plugin_get_available_classes(void)
198 {
199 /* Initialize static plugins on first call. */
200 init_plugin_class_list();
201
202 #ifndef DISABLE_PLUGINS_LOADING
203 GDir * dir = g_dir_open(PACKAGE_LIB_DIR "/lxpanel/plugins", 0, NULL);
204 if (dir != NULL)
205 {
206 const char * file;
207 while ((file = g_dir_read_name(dir)) != NULL)
208 {
209 if (g_str_has_suffix(file, ".so"))
210 {
211 char * type = g_strndup(file, strlen(file) - 3);
212 if (_find_plugin(type) == NULL)
213 {
214 /* If it has not been loaded, do it. If successful, add it to the result. */
215 char * path = g_build_filename(PACKAGE_LIB_DIR "/lxpanel/plugins", file, NULL );
216 plugin_load_dynamic(type, path);
217 g_free(path);
218 }
219 g_free(type);
220 }
221 }
222 g_dir_close(dir);
223 }
224 #endif
225 }
226
227 /* Recursively set the background of all widgets on a panel background configuration change. */
228 void plugin_widget_set_background(GtkWidget * w, LXPanel * panel)
229 {
230 if (w != NULL)
231 {
232 Panel *p = panel->priv;
233 if (gtk_widget_get_has_window(w))
234 {
235 if ((p->background) || (p->transparent))
236 {
237 #if GTK_CHECK_VERSION(2, 20, 0)
238 if (gtk_widget_get_realized(w))
239 #else
240 if (GTK_WIDGET_REALIZED(w))
241 #endif
242 {
243 _panel_determine_background_pixmap(panel, w);
244 gdk_window_invalidate_rect(gtk_widget_get_window(w), NULL, TRUE);
245 }
246 }
247 else
248 {
249 /* Set background according to the current GTK style. */
250 gtk_widget_set_app_paintable(w, FALSE);
251 #if GTK_CHECK_VERSION(2, 20, 0)
252 if (gtk_widget_get_realized(w))
253 #else
254 if (GTK_WIDGET_REALIZED(w))
255 #endif
256 {
257 gdk_window_set_back_pixmap(gtk_widget_get_window(w), NULL, TRUE);
258 gtk_style_set_background(gtk_widget_get_style(w),
259 gtk_widget_get_window(w),
260 GTK_STATE_NORMAL);
261 }
262 }
263 }
264
265 /* Special handling to get tray icons redrawn. */
266 if (GTK_IS_SOCKET(w))
267 {
268 gtk_widget_hide(w);
269 gdk_window_process_all_updates();
270 gtk_widget_show(w);
271 gdk_window_process_all_updates();
272 }
273
274 /* Recursively process all children of a container. */
275 if (GTK_IS_CONTAINER(w))
276 gtk_container_foreach(GTK_CONTAINER(w), (GtkCallback) plugin_widget_set_background, panel);
277 }
278 }
279
280 /* Handler for "button_press_event" signal with Plugin as parameter.
281 * External so can be used from a plugin. */
282 static gboolean lxpanel_plugin_button_press_event(GtkWidget *plugin, GdkEventButton *event, LXPanel *panel)
283 {
284 if (event->button == 3 && /* right button */
285 (event->state & gtk_accelerator_get_default_mod_mask()) == 0) /* no key */
286 {
287 GtkMenu* popup = (GtkMenu*)lxpanel_get_plugin_menu(panel, plugin, FALSE);
288 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
289 return TRUE;
290 }
291 return FALSE;
292 }
293
294 /* for old plugins compatibility */
295 gboolean plugin_button_press_event(GtkWidget *widget, GdkEventButton *event, Plugin *plugin)
296 {
297 return lxpanel_plugin_button_press_event(plugin->pwid, event, PLUGIN_PANEL(plugin->pwid));
298 }
299
300 /* Helper for position-calculation callback for popup menus. */
301 void lxpanel_plugin_popup_set_position_helper(LXPanel * p, GtkWidget * near, GtkWidget * popup, gint * px, gint * py)
302 {
303 gint x, y;
304 GtkAllocation allocation;
305 GtkRequisition popup_req;
306
307 /* Get the allocation of the popup menu. */
308 gtk_widget_size_request(popup, &popup_req);
309 if (gtk_widget_is_toplevel(popup))
310 {
311 GdkRectangle extents;
312 /* FIXME: can we wait somehow for WM drawing decorations? */
313 gdk_window_get_frame_extents(gtk_widget_get_window(popup), &extents);
314 popup_req.width = extents.width;
315 popup_req.height = extents.height;
316 }
317
318 /* Get the origin of the requested-near widget in screen coordinates. */
319 gtk_widget_get_allocation(near, &allocation);
320 gdk_window_get_origin(gtk_widget_get_window(near), &x, &y);
321 if (x != allocation.x) x += allocation.x; /* Doesn't seem to be working according to spec; the allocation.x sometimes has the window origin in it */
322 if (y != allocation.y) y += allocation.y;
323
324 /* Dispatch on edge to lay out the popup menu with respect to the button.
325 * Also set "push-in" to avoid any case where it might flow off screen. */
326 switch (p->priv->edge)
327 {
328 case EDGE_TOP: y += allocation.height; break;
329 case EDGE_BOTTOM: y -= popup_req.height; break;
330 case EDGE_LEFT: x += allocation.width; break;
331 case EDGE_RIGHT: x -= popup_req.width; break;
332 }
333
334 /* Push onscreen. */
335 int screen_width = gdk_screen_width();
336 int screen_height = gdk_screen_height();
337 if ((x + popup_req.width) > screen_width)
338 x -= (x + popup_req.width) - screen_width;
339 if ((y + popup_req.height) > screen_height)
340 y -= (y + popup_req.height) - screen_height;
341
342 *px = x;
343 *py = y;
344 }
345
346 /* for old plugins compatibility -- popup_req is ignored here */
347 void plugin_popup_set_position_helper(Plugin * p, GtkWidget * near, GtkWidget * popup, GtkRequisition * popup_req, gint * px, gint * py)
348 {
349 lxpanel_plugin_popup_set_position_helper(p->panel->topgwin, near, popup, px, py);
350 }
351
352 /* Adjust the position of a popup window to ensure that it is not hidden by the panel.
353 * It is observed that some window managers do not honor the strut that is set on the panel. */
354 void lxpanel_plugin_adjust_popup_position(GtkWidget * popup, GtkWidget * parent)
355 {
356 /* Initialize. */
357 Panel * p = PLUGIN_PANEL(parent)->priv;
358 GtkAllocation allocation;
359
360 gtk_widget_get_allocation(parent, &allocation);
361 /* Get the coordinates of the plugin top level widget. */
362 int x = p->cx + allocation.x;
363 int y = p->cy + allocation.y;
364
365 /* Adjust these coordinates according to the panel edge. */
366 switch (p->edge)
367 {
368 case EDGE_TOP:
369 y += allocation.height;
370 gtk_widget_get_allocation(popup, &allocation);
371 break;
372 case EDGE_BOTTOM:
373 gtk_widget_get_allocation(popup, &allocation);
374 y -= allocation.height;
375 break;
376 case EDGE_LEFT:
377 x += allocation.width;
378 gtk_widget_get_allocation(popup, &allocation);
379 break;
380 case EDGE_RIGHT:
381 gtk_widget_get_allocation(popup, &allocation);
382 x -= allocation.width;
383 break;
384 }
385
386 /* Clip the coordinates to ensure that the popup remains on screen. */
387 int screen_width = gdk_screen_width();
388 int screen_height = gdk_screen_height();
389 if ((x + allocation.width) > screen_width) x = screen_width - allocation.width;
390 if ((y + allocation.height) > screen_height) y = screen_height - allocation.height;
391 if (y < 0) y = 0;
392 if (x < 0) x = 0;
393
394 /* Move the popup to position. */
395 gdk_window_move(gtk_widget_get_window(popup), x, y);
396 }
397
398 /* for old plugins compatibility */
399 void plugin_adjust_popup_position(GtkWidget * popup, Plugin * plugin)
400 {
401 lxpanel_plugin_adjust_popup_position(popup, plugin->pwid);
402 }
403
404 /* Open a specified path in a file manager. */
405 static gboolean _open_dir_in_file_manager(GAppLaunchContext* ctx, GList* folder_infos,
406 gpointer user_data, GError** err)
407 {
408 FmFileInfo *fi = folder_infos->data; /* only first is used */
409 GAppInfo *app = g_app_info_get_default_for_type("inode/directory", TRUE);
410 GFile *gf;
411 gboolean ret;
412
413 if (app == NULL)
414 {
415 g_set_error_literal(err, G_SHELL_ERROR, G_SHELL_ERROR_EMPTY_STRING,
416 _("No file manager is configured."));
417 return FALSE;
418 }
419 gf = fm_path_to_gfile(fm_file_info_get_path(fi));
420 folder_infos = g_list_prepend(NULL, gf);
421 ret = fm_app_info_launch(app, folder_infos, ctx, err);
422 g_list_free(folder_infos);
423 g_object_unref(gf);
424 g_object_unref(app);
425 return ret;
426 }
427
428 gboolean lxpanel_launch_path(LXPanel *panel, FmPath *path)
429 {
430 return fm_launch_path_simple(NULL, NULL, path, _open_dir_in_file_manager, NULL);
431 }
432
433 void lxpanel_plugin_show_config_dialog(GtkWidget* plugin)
434 {
435 const LXPanelPluginInit *init = PLUGIN_CLASS(plugin);
436 LXPanel *panel = PLUGIN_PANEL(plugin);
437 GtkWidget *dlg = panel->priv->plugin_pref_dialog;
438
439 if (dlg && g_object_get_data(G_OBJECT(dlg), "generic-config-plugin") == plugin)
440 return; /* configuration dialog is already shown for this widget */
441 g_return_if_fail(panel != NULL);
442 dlg = init->config(panel, plugin);
443 if (dlg)
444 _panel_show_config_dialog(panel, plugin, dlg);
445 }
446
447 #if GLIB_CHECK_VERSION(2, 32, 0)
448 static GRecMutex _mutex;
449 #else
450 static GStaticRecMutex _mutex = G_STATIC_REC_MUTEX_INIT;
451 #endif
452
453 #ifndef DISABLE_PLUGINS_LOADING
454 FM_MODULE_DEFINE_TYPE(lxpanel_gtk, LXPanelPluginInit, 1)
455
456 static gboolean fm_module_callback_lxpanel_gtk(const char *name, gpointer init, int ver)
457 {
458 /* ignore ver for now, only 1 exists */
459 return lxpanel_register_plugin_type(name, init);
460 }
461 #endif
462
463 static gboolean old_plugins_loaded = FALSE;
464
465 void _prepare_modules(void)
466 {
467 _all_types = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
468 lxpanel_plugin_qdata = g_quark_from_static_string("LXPanel::plugin-data");
469 lxpanel_plugin_qinit = g_quark_from_static_string("LXPanel::plugin-init");
470 lxpanel_plugin_qconf = g_quark_from_static_string("LXPanel::plugin-conf");
471 #ifndef DISABLE_PLUGINS_LOADING
472 fm_modules_add_directory(PACKAGE_LIB_DIR "/lxpanel/plugins");
473 fm_module_register_lxpanel_gtk();
474 #endif
475 }
476
477 void _unload_modules(void)
478 {
479 GHashTableIter iter;
480 gpointer key, val;
481
482 #ifndef DISABLE_PLUGINS_LOADING
483 fm_module_unregister_type("lxpanel_gtk");
484 #endif
485 g_hash_table_iter_init(&iter, _all_types);
486 while(g_hash_table_iter_next(&iter, &key, &val))
487 {
488 register const LXPanelPluginInit *init = val;
489 if (init->new_instance == NULL) /* old type of plugin */
490 {
491 plugin_class_unref(init->_reserved1);
492 g_free(val);
493 }
494 }
495 g_hash_table_destroy(_all_types);
496 old_plugins_loaded = FALSE;
497 }
498
499 gboolean lxpanel_register_plugin_type(const char *name, const LXPanelPluginInit *init)
500 {
501 const LXPanelPluginInit *data;
502
503 /* validate it */
504 if (init->new_instance == NULL || name == NULL || name[0] == '\0')
505 return FALSE;
506 #if GLIB_CHECK_VERSION(2, 32, 0)
507 g_rec_mutex_lock(&_mutex);
508 #else
509 g_static_rec_mutex_lock(&_mutex);
510 #endif
511 /* test if it's registered already */
512 data = _find_plugin(name);
513 if (data == NULL)
514 {
515 if (init->init)
516 init->init();
517 g_hash_table_insert(_all_types, g_strdup(name), (gpointer)init);
518 }
519 #if GLIB_CHECK_VERSION(2, 32, 0)
520 g_rec_mutex_unlock(&_mutex);
521 #else
522 g_static_rec_mutex_unlock(&_mutex);
523 #endif
524 return (data == NULL);
525 }
526
527 static void _old_plugin_save_hook(const config_setting_t * setting, FILE * f, gpointer user_data)
528 {
529 Plugin *pl = user_data;
530 PluginClass *pc = pl->class;
531 if (pc->save)
532 pc->save(pl, f);
533 }
534
535 /* This is called right before Plugin instance is destroyed */
536 static void _old_plugin_destroy(gpointer data)
537 {
538 Plugin *pl = data;
539
540 plugin_class_unref(pl->class);
541
542 /* Free the Plugin structure. */
543 g_free(pl);
544 }
545
546 static void _on_old_widget_destroy(GtkWidget *widget, Plugin *pl)
547 {
548 /* Never let run it again. */
549 g_signal_handlers_disconnect_by_func(widget, _on_old_widget_destroy, pl);
550 /* Run the destructor before destroying the top level widget.
551 * This prevents problems with the plugin destroying child widgets. */
552 pl->class->destructor(pl);
553 }
554
555 //static void on_size_allocate(GtkWidget *widget, GdkRectangle *allocation, LXPanel *p)
556 //{
557 // _queue_panel_calculate_size(p);
558 //}
559
560 GtkWidget *lxpanel_add_plugin(LXPanel *p, const char *name, config_setting_t *cfg, gint at)
561 {
562 const LXPanelPluginInit *init;
563 GtkWidget *widget;
564 config_setting_t *s, *pconf;
565 gint expand, padding = 0, border = 0, i;
566
567 CHECK_MODULES();
568 if (!old_plugins_loaded)
569 plugin_get_available_classes();
570 old_plugins_loaded = TRUE;
571 init = _find_plugin(name);
572 if (init == NULL)
573 return NULL;
574 /* prepare widget settings */
575 if (!init->expand_available)
576 expand = 0;
577 else if ((s = config_setting_get_member(cfg, "expand")))
578 expand = config_setting_get_int(s);
579 else
580 expand = init->expand_default;
581 s = config_setting_get_member(cfg, "padding");
582 if (s)
583 padding = config_setting_get_int(s);
584 s = config_setting_get_member(cfg, "border");
585 if (s)
586 border = config_setting_get_int(s);
587 /* prepare config and create it if need */
588 s = config_setting_add(cfg, "", PANEL_CONF_TYPE_LIST);
589 for (i = 0; (pconf = config_setting_get_elem(s, i)); i++)
590 if (strcmp(config_setting_get_name(pconf), "Config") == 0)
591 break;
592 if (!pconf)
593 pconf = config_setting_add(s, "Config", PANEL_CONF_TYPE_GROUP);
594 /* If this plugin can only be instantiated once, count the instantiation.
595 * This causes the configuration system to avoid displaying the plugin as one that can be added. */
596 if (init->new_instance) /* new style of plugin */
597 {
598 widget = init->new_instance(p, pconf);
599 if (widget == NULL)
600 return widget;
601 /* always connect lxpanel_plugin_button_press_event() */
602 g_signal_connect(widget, "button-press-event",
603 G_CALLBACK(lxpanel_plugin_button_press_event), p);
604 if (init->button_press_event)
605 g_signal_connect(widget, "button-press-event",
606 G_CALLBACK(init->button_press_event), p);
607 }
608 else
609 {
610 Plugin *pl = g_new0(Plugin, 1);
611 PluginClass *pc = init->_reserved1;
612 char *conf = config_setting_to_string(pconf), *fp;
613
614 pl->class = pc;
615 pl->panel = p->priv;
616 widget = NULL;
617 fp = &conf[9]; /* skip "Config {\n" */
618 /* g_debug("created conf: %s",conf); */
619 /* Call the constructor.
620 * It is responsible for parsing the parameters, and setting "pwid" to the top level widget. */
621 if (pc->constructor(pl, &fp))
622 widget = pl->pwid;
623 g_free(conf);
624
625 if (widget == NULL) /* failed */
626 {
627 g_free(pl);
628 return widget;
629 }
630
631 pc->count += 1;
632 g_signal_connect(widget, "destroy", G_CALLBACK(_on_old_widget_destroy), pl);
633 config_setting_set_save_hook(pconf, _old_plugin_save_hook, pl);
634 lxpanel_plugin_set_data(widget, pl, _old_plugin_destroy);
635 }
636 gtk_widget_set_name(widget, name);
637 gtk_box_pack_start(GTK_BOX(p->priv->box), widget, expand, TRUE, padding);
638 gtk_container_set_border_width(GTK_CONTAINER(widget), border);
639 // g_signal_connect(widget, "size-allocate", G_CALLBACK(on_size_allocate), p);
640 gtk_widget_show(widget);
641 g_object_set_qdata(G_OBJECT(widget), lxpanel_plugin_qconf, cfg);
642 g_object_set_qdata(G_OBJECT(widget), lxpanel_plugin_qinit, (gpointer)init);
643 return widget;
644 }
645
646 /* transfer none - note that not all fields are valid there */
647 GHashTable *lxpanel_get_all_types(void)
648 {
649 return _all_types;
650 }