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