Adding upstream version 0.9.0.
[debian/lxpanel.git] / src / plugin.c
CommitLineData
0688b017
AG
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>
7a1c5048 6 * 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
0688b017
AG
7 *
8 * This file is a part of LXPanel project.
6cc5e1a6
DB
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
6b775dbb 29#include "private.h"
6cc5e1a6
DB
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"
6cc5e1a6
DB
38
39#include <glib-object.h>
6b775dbb
AG
40#include <glib/gi18n.h>
41#include <libfm/fm-gtk.h>
6cc5e1a6
DB
42
43//#define DEBUG
44#include "dbg.h"
f7ecd6ce
AG
45#include "gtk-compat.h"
46
47#if GTK_CHECK_VERSION(3, 0, 0)
48#include <gtk/gtkx.h>
49#endif
6cc5e1a6 50
2ba86315 51static void plugin_class_unref(PluginClass * pc);
6cc5e1a6 52
6b775dbb
AG
53GQuark lxpanel_plugin_qinit;
54GQuark lxpanel_plugin_qconf;
55GQuark lxpanel_plugin_qdata;
7dd482c5 56GQuark lxpanel_plugin_qsize;
6b775dbb
AG
57static GHashTable *_all_types = NULL;
58
2ba86315
DB
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) \
6cc5e1a6
DB
61do {\
62 extern PluginClass pc;\
2ba86315 63 register_plugin_class(&pc, FALSE);\
6cc5e1a6
DB
64} while (0)
65
6b775dbb
AG
66static inline const LXPanelPluginInit *_find_plugin(const char *name)
67{
68 return g_hash_table_lookup(_all_types, name);
69}
70
71static 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
83static 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
2ba86315
DB
94/* Register a PluginClass. */
95static void register_plugin_class(PluginClass * pc, gboolean dynamic)
6cc5e1a6 96{
6b775dbb
AG
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;
6cc5e1a6 108 pc->dynamic = dynamic;
6b775dbb 109 g_hash_table_insert(_all_types, g_strdup(pc->type), init);
6cc5e1a6
DB
110}
111
2ba86315 112/* Load a dynamic plugin. */
6b775dbb 113static void plugin_load_dynamic(const char * type, const gchar * path)
6cc5e1a6 114{
2ba86315
DB
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);
6b775dbb
AG
134 g_warning("%s.so is not a lxpanel plugin", type);
135 return;
2ba86315
DB
136 }
137
138 /* Register the newly loaded and valid plugin. */
139 pc->gmodule = m;
140 register_plugin_class(pc, TRUE);
6b775dbb 141 pc->count = 1;
6cc5e1a6 142 }
6cc5e1a6
DB
143}
144
2ba86315
DB
145/* Unreference a dynamic plugin. */
146static void plugin_class_unref(PluginClass * pc)
6cc5e1a6 147{
2ba86315
DB
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 {
6cc5e1a6
DB
155 g_module_close(pc->gmodule);
156 }
157}
158
6b775dbb
AG
159/* Loads all available old type plugins. Should be removed in future releases. */
160static void plugin_get_available_classes(void)
6cc5e1a6 161{
6cc5e1a6 162#ifndef DISABLE_PLUGINS_LOADING
2ba86315
DB
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);
6b775dbb 172 if (_find_plugin(type) == NULL)
2ba86315
DB
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 );
6b775dbb 176 plugin_load_dynamic(type, path);
2ba86315 177 g_free(path);
6cc5e1a6 178 }
2ba86315 179 g_free(type);
6cc5e1a6 180 }
6cc5e1a6 181 }
2ba86315 182 g_dir_close(dir);
6cc5e1a6
DB
183 }
184#endif
6cc5e1a6
DB
185}
186
2ba86315 187/* Recursively set the background of all widgets on a panel background configuration change. */
6b775dbb 188void plugin_widget_set_background(GtkWidget * w, LXPanel * panel)
6cc5e1a6 189{
2ba86315 190 if (w != NULL)
6cc5e1a6 191 {
6b775dbb 192 if (gtk_widget_get_has_window(w))
6cc5e1a6 193 {
f7ecd6ce
AG
194 Panel *p = panel->priv;
195
196 gtk_widget_set_app_paintable(w, ((p->background) || (p->transparent)));
197 if (gtk_widget_get_realized(w))
2ba86315 198 {
f7ecd6ce
AG
199 GdkWindow *window = gtk_widget_get_window(w);
200#if GTK_CHECK_VERSION(3, 0, 0)
201 gdk_window_set_background_pattern(window, NULL);
6b775dbb 202#else
f7ecd6ce 203 gdk_window_set_back_pixmap(window, NULL, TRUE);
6b775dbb 204#endif
f7ecd6ce
AG
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,
6b775dbb 211 GTK_STATE_NORMAL);
2ba86315 212 }
6cc5e1a6 213 }
6cc5e1a6 214
2ba86315
DB
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 }
6cc5e1a6 223
2ba86315
DB
224 /* Recursively process all children of a container. */
225 if (GTK_IS_CONTAINER(w))
6b775dbb 226 gtk_container_foreach(GTK_CONTAINER(w), (GtkCallback) plugin_widget_set_background, panel);
6cc5e1a6 227 }
2ba86315 228}
6cc5e1a6 229
2ba86315
DB
230/* Handler for "button_press_event" signal with Plugin as parameter.
231 * External so can be used from a plugin. */
6b775dbb 232static gboolean lxpanel_plugin_button_press_event(GtkWidget *plugin, GdkEventButton *event, LXPanel *panel)
2ba86315 233{
6b775dbb
AG
234 if (event->button == 3 && /* right button */
235 (event->state & gtk_accelerator_get_default_mod_mask()) == 0) /* no key */
6cc5e1a6 236 {
6b775dbb 237 GtkMenu* popup = (GtkMenu*)lxpanel_get_plugin_menu(panel, plugin, FALSE);
2ba86315
DB
238 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
239 return TRUE;
6b775dbb 240 }
2ba86315 241 return FALSE;
6cc5e1a6
DB
242}
243
6b775dbb
AG
244/* for old plugins compatibility */
245gboolean 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
2ba86315 250/* Helper for position-calculation callback for popup menus. */
6b775dbb 251void lxpanel_plugin_popup_set_position_helper(LXPanel * p, GtkWidget * near, GtkWidget * popup, gint * px, gint * py)
6cc5e1a6 252{
2ba86315 253 gint x, y;
6b775dbb
AG
254 GtkAllocation allocation;
255 GtkAllocation popup_req;
f7ecd6ce
AG
256 GdkScreen *screen = NULL;
257 gint monitor;
6b775dbb
AG
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 }
2ba86315
DB
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. */
6b775dbb 284 switch (p->priv->edge)
2ba86315 285 {
6b775dbb
AG
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;
2ba86315 290 }
6b775dbb
AG
291
292 /* Push onscreen. */
f7ecd6ce
AG
293 if (gtk_widget_has_screen(near))
294 screen = gtk_widget_get_screen(near);
295 else
7a1c5048
AG
296 /* panel as a GtkWindow always has a screen */
297 screen = gtk_widget_get_screen(GTK_WIDGET(p));
f7ecd6ce
AG
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);
6b775dbb 306
2ba86315
DB
307 *px = x;
308 *py = y;
6cc5e1a6 309}
32a67dc7 310
6b775dbb
AG
311/* for old plugins compatibility -- popup_req is ignored here */
312void 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
32a67dc7
DB
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. */
6b775dbb
AG
319void 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 */
32a67dc7
DB
331void plugin_adjust_popup_position(GtkWidget * popup, Plugin * plugin)
332{
6b775dbb
AG
333 lxpanel_plugin_adjust_popup_position(popup, plugin->pwid);
334}
32a67dc7 335
6b775dbb
AG
336/* Open a specified path in a file manager. */
337static 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;
32a67dc7 344
6b775dbb 345 if (app == NULL)
32a67dc7 346 {
6b775dbb
AG
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
360gboolean 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
365void 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)
380static GRecMutex _mutex;
381#else
382static GStaticRecMutex _mutex = G_STATIC_REC_MUTEX_INIT;
383#endif
384
385#ifndef DISABLE_PLUGINS_LOADING
386FM_MODULE_DEFINE_TYPE(lxpanel_gtk, LXPanelPluginInit, 1)
387
388static 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
395static gboolean old_plugins_loaded = FALSE;
396
f7ecd6ce 397void lxpanel_prepare_modules(void)
6b775dbb
AG
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");
7dd482c5 403 lxpanel_plugin_qsize = g_quark_from_static_string("LXPanel::plugin-size");
6b775dbb
AG
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
f7ecd6ce 410void lxpanel_unload_modules(void)
6b775dbb
AG
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
432gboolean 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
460static 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 */
469static 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
479static 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
7dd482c5
AG
488static 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); */
f7ecd6ce
AG
498 plugin_widget_set_background(widget, p);
499// _panel_queue_update_background(p);
6b775dbb 500// _queue_panel_calculate_size(p);
7dd482c5 501}
6b775dbb
AG
502
503GtkWidget *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");
f7ecd6ce 528 /* FIXME: this is useless setting, it should be 0 or panel becomes weird */
6b775dbb
AG
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)
32a67dc7 535 break;
6b775dbb
AG
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);
32a67dc7 551 }
6b775dbb
AG
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);
32a67dc7 568
6b775dbb
AG
569 if (widget == NULL) /* failed */
570 {
571 g_free(pl);
572 return widget;
573 }
32a67dc7 574
6b775dbb
AG
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);
7a1c5048
AG
582 if (at >= 0)
583 gtk_box_reorder_child(GTK_BOX(p->priv->box), widget, at);
6b775dbb 584 gtk_container_set_border_width(GTK_CONTAINER(widget), border);
7dd482c5 585 g_signal_connect(widget, "size-allocate", G_CALLBACK(on_size_allocate), p);
6b775dbb
AG
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);
7dd482c5
AG
589 g_object_set_qdata_full(G_OBJECT(widget), lxpanel_plugin_qsize,
590 g_new0(GdkRectangle, 1), g_free);
6b775dbb
AG
591 return widget;
592}
593
594/* transfer none - note that not all fields are valid there */
595GHashTable *lxpanel_get_all_types(void)
596{
597 return _all_types;
32a67dc7 598}