Merge branch 'use-libfm'
[lxde/lxpanel.git] / src / plugin.c
index dfb818b..c95815c 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
+ * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
 #include "config.h"
 #endif
 
-#include "plugin.h"
+#include "private.h"
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
 #include "bg.h"
 
 #include <glib-object.h>
+#include <glib/gi18n.h>
+#include <libfm/fm-gtk.h>
 
 //#define DEBUG
 #include "dbg.h"
 
-static GList *pcl = NULL;
+static void register_plugin_class(PluginClass * pc, gboolean dynamic);
+static void init_plugin_class_list(void);
+static void plugin_class_unref(PluginClass * pc);
 
-#if 0
-/* dummy type plugin for dynamic plugins registering new types */
-static void
-lx_type_plugin_iface_init(gpointer g_iface, gpointer g_iface_data);
+GQuark lxpanel_plugin_qdata;
+static GHashTable *_all_types = NULL;
 
-G_DEFINE_TYPE_EXTENDED ( LXTypePlugin,
-                         lx_type_plugin,
-                         G_TYPE_OBJECT,
-                         0,
-                         G_IMPLEMENT_INTERFACE (G_TYPE_TYPE_PLUGIN,
-                                         lx_type_plugin_iface_init))
+/* Dynamic parameter for static (built-in) plugins must be FALSE so we will not try to unload them */
+#define REGISTER_STATIC_PLUGIN_CLASS(pc) \
+do {\
+    extern PluginClass pc;\
+    register_plugin_class(&pc, FALSE);\
+} while (0)
 
-void lx_type_plugin_init( LXTypePlugin* tp )
-{
-}
+/* The same for new plugins type - they will be not unloaded by FmModule */
+#define REGISTER_STATIC_MODULE(pc) do { \
+    extern LXPanelPluginInit lxpanel_static_plugin_##pc; \
+    lxpanel_register_plugin_type(#pc, &lxpanel_static_plugin_##pc); } while (0)
 
-void lx_type_plugin_class_init(LXTypePluginClass* klass)
+static inline LXPanelPluginInit *_find_plugin(const char *name)
 {
+    return g_hash_table_lookup(_all_types, name);
 }
 
-void lx_type_plugin_iface_init(gpointer g_iface, gpointer g_iface_data)
+static void _old_plugin_config(Panel *panel, GtkWidget *instance, GtkWindow *parent)
 {
-}
+    LXPanelPluginInit *init = PLUGIN_CLASS(instance);
+    Plugin * plugin;
 
-GTypePlugin* lx_type_plugin_get(const char* plugin_name)
-{
-    LXTypePlugin* tp = g_object_new(LX_TYPE_TYPE_PLUGIN, NULL);
-    return tp;
+    g_return_if_fail(init != NULL && init->new_instance == NULL);
+    plugin = lxpanel_plugin_get_data(instance);
+    if (plugin->class->config)
+        plugin->class->config(plugin, parent);
 }
-#endif
 
-/* counter for static (built-in) plugins must be greater then zero
- * so lxpanel will not try to unload them */
-
-#define REGISTER_PLUGIN_CLASS(pc, dynamic) \
-do {\
-    extern PluginClass pc;\
-    register_plugin_class(&pc, dynamic);\
-} while (0)
+static void _old_plugin_reconfigure(Panel *panel, GtkWidget *instance)
+{
+    LXPanelPluginInit *init = PLUGIN_CLASS(instance);
+    Plugin * plugin;
 
+    g_return_if_fail(init != NULL && init->new_instance == NULL);
+    plugin = lxpanel_plugin_get_data(instance);
+    if (plugin->class->panel_configuration_changed)
+        plugin->class->panel_configuration_changed(plugin);
+}
 
-static void
-register_plugin_class(PluginClass *pc, int dynamic)
+/* Register a PluginClass. */
+static void register_plugin_class(PluginClass * pc, gboolean dynamic)
 {
-    pcl = g_list_append(pcl, pc);
+    LXPanelPluginInit *init = g_new0(LXPanelPluginInit, 1);
+    init->_reserved1 = pc;
+    init->name = pc->name;
+    init->description = pc->description;
+    if (pc->config)
+        init->config = _old_plugin_config;
+    if (pc->panel_configuration_changed)
+        init->reconfigure = _old_plugin_reconfigure;
+    init->one_per_system = pc->one_per_system;
+    init->expand_available = pc->expand_available;
+    init->expand_default = pc->expand_default;
     pc->dynamic = dynamic;
-    if (!pc->dynamic)
-        pc->count++;
-    /* reloading netstatus results in segfault due to registering static type in dll.
-     * so keep it always onboard until bug fix */
-    if (!strcmp(pc->type, "netstatus"))
-        pc->count++;
+    g_hash_table_insert(_all_types, g_strdup(pc->type), init);
 }
 
-static void
-init_plugin_class_list()
+/* Initialize the static plugins. */
+static void init_plugin_class_list(void)
 {
-    ENTER;
 #ifdef STATIC_SEPARATOR
-    REGISTER_PLUGIN_CLASS(separator_plugin_class, 0);
+    REGISTER_STATIC_MODULE(separator);
 #endif
 
-/* Remove image plugin since it seems to be useless. */
-/*
-#ifdef STATIC_IMAGE
-    REGISTER_PLUGIN_CLASS(image_plugin_class, 0);
+#ifdef STATIC_LAUNCHBAR
+    REGISTER_STATIC_MODULE(launchbar);
 #endif
-*/
 
-#ifdef STATIC_LAUNCHBAR
-    REGISTER_PLUGIN_CLASS(launchbar_plugin_class, 0);
+#ifdef STATIC_LAUNCHTASKBAR
+    REGISTER_STATIC_PLUGIN_CLASS(launchtaskbar_plugin_class);
 #endif
 
 #ifdef STATIC_DCLOCK
-    REGISTER_PLUGIN_CLASS(dclock_plugin_class, 0);
+    REGISTER_STATIC_MODULE(dclock);
 #endif
 
 #ifdef STATIC_WINCMD
-    REGISTER_PLUGIN_CLASS(wincmd_plugin_class, 0);
+    REGISTER_STATIC_MODULE(wincmd);
 #endif
 
 #ifdef STATIC_DIRMENU
-    REGISTER_PLUGIN_CLASS(dirmenu_plugin_class, 0);
+    REGISTER_STATIC_MODULE(dirmenu);
 #endif
 
 #ifdef STATIC_TASKBAR
-    REGISTER_PLUGIN_CLASS(taskbar_plugin_class, 0);
+    REGISTER_STATIC_PLUGIN_CLASS(taskbar_plugin_class);
 #endif
 
 #ifdef STATIC_PAGER
-    REGISTER_PLUGIN_CLASS(pager_plugin_class, 0);
+    REGISTER_STATIC_PLUGIN_CLASS(pager_plugin_class);
 #endif
 
 #ifdef STATIC_TRAY
-    REGISTER_PLUGIN_CLASS(tray_plugin_class, 0);
+    REGISTER_STATIC_PLUGIN_CLASS(tray_plugin_class);
 #endif
 
+#ifndef DISABLE_MENU
 #ifdef STATIC_MENU
-    REGISTER_PLUGIN_CLASS(menu_plugin_class, 0);
+    REGISTER_STATIC_PLUGIN_CLASS(menu_plugin_class);
+#endif
 #endif
 
 #ifdef STATIC_SPACE
-    REGISTER_PLUGIN_CLASS(space_plugin_class, 0);
+    REGISTER_STATIC_MODULE(space);
 #endif
-
-    RET();
 }
 
-GList* plugin_find_class( const char* type )
+/* Load a dynamic plugin. */
+static void plugin_load_dynamic(const char * type, const gchar * path)
 {
-    GList *tmp;
-    PluginClass *pc = NULL;
-    for (tmp = pcl; tmp; tmp = g_list_next(tmp)) {
-        pc = (PluginClass *) tmp->data;
-        if (!g_ascii_strcasecmp(type, pc->type)) {
-            LOG(LOG_INFO, "   already have it\n");
-            break;
+    PluginClass * pc = NULL;
+
+    /* Load the external module. */
+    GModule * m = g_module_open(path, G_MODULE_BIND_LAZY);
+    if (m != NULL)
+    {
+        /* Formulate the name of the expected external variable of type PluginClass. */
+        char class_name[128];
+        g_snprintf(class_name, sizeof(class_name), "%s_plugin_class", type);
+
+        /* Validate that the external variable is of type PluginClass. */
+        gpointer tmpsym;
+        if (( ! g_module_symbol(m, class_name, &tmpsym))       /* Ensure symbol is present */
+        || ((pc = tmpsym) == NULL)
+        || (pc->structure_size != sizeof(PluginClass))         /* Then check versioning information */
+        || (pc->structure_version != PLUGINCLASS_VERSION)
+        || (strcmp(type, pc->type) != 0))                      /* Then and only then access other fields; check name */
+        {
+            g_module_close(m);
+            ERR("%s.so is not a lxpanel plugin\n", type);
+            return;
         }
+
+        /* Register the newly loaded and valid plugin. */
+        pc->gmodule = m;
+        register_plugin_class(pc, TRUE);
+        pc->count = 1;
     }
-    return tmp;
 }
 
-static PluginClass*
-plugin_load_dynamic( const char* type, const char* path )
+/* Unreference a dynamic plugin. */
+static void plugin_class_unref(PluginClass * pc)
 {
-    PluginClass *pc = NULL;
-    GModule *m;
-    gpointer tmpsym;
-    char class_name[ 128 ];
-    m = g_module_open(path, G_MODULE_BIND_LAZY);
-    if (!m) {
-        /* ERR("error is %s\n", g_module_error()); */
-        RET(NULL);
-    }
-    g_snprintf( class_name, 128, "%s_plugin_class", type );
-
-    if (!g_module_symbol(m, class_name, &tmpsym)
-         || (pc = tmpsym) == NULL
-         || strcmp(type, pc->type)) {
-        g_module_close(m);
-        ERR("%s.so is not a lxpanel plugin\n", type);
-        RET(NULL);
+    pc->count -= 1;
+
+    /* If the reference count drops to zero, unload the plugin if it is dynamic and has declared itself unloadable. */
+    if ((pc->count == 0)
+    && (pc->dynamic)
+    && ( ! pc->not_unloadable))
+    {
+        g_module_close(pc->gmodule);
     }
-    pc->gmodule = m;
-    register_plugin_class(pc, 1);
-    return pc;
 }
 
-Plugin *
-plugin_load(char *type)
+/* Loads all available old type plugins. Should be removed in future releases. */
+static void plugin_get_available_classes(void)
 {
-    GList *tmp;
-    PluginClass *pc = NULL;
-    Plugin *plug = NULL;
+    /* Initialize static plugins on first call. */
+    init_plugin_class_list();
 
-    ENTER;
-    if (!pcl)
-        init_plugin_class_list();
-
-    tmp = plugin_find_class( type );
-
-    if( tmp ) {
-        pc = (PluginClass *) tmp->data;
-    }
 #ifndef DISABLE_PLUGINS_LOADING
-    else if ( g_module_supported() ) {
-        char* path[ PATH_MAX ];
-#if 0   /* put plugins in config dir is too dirty... */
-        g_snprintf(path, PATH_MAX, "%s/.lxpanel/plugins/%s.so", getenv("HOME"), type);
-        pc = plugin_load_dynamic( type, path );
+    GDir * dir = g_dir_open(PACKAGE_LIB_DIR "/lxpanel/plugins", 0, NULL);
+    if (dir != NULL)
+    {
+        const char * file;
+        while ((file = g_dir_read_name(dir)) != NULL)
+        {
+            if (g_str_has_suffix(file, ".so"))
+            {
+                char * type = g_strndup(file, strlen(file) - 3);
+                if (_find_plugin(type) == NULL)
+                {
+                    /* If it has not been loaded, do it.  If successful, add it to the result. */
+                    char * path = g_build_filename(PACKAGE_LIB_DIR "/lxpanel/plugins", file, NULL );
+                    plugin_load_dynamic(type, path);
+                    g_free(path);
+                }
+                g_free(type);
+            }
+        }
+        g_dir_close(dir);
+    }
 #endif
-        if( !pc ) {
-            g_snprintf(path, PATH_MAX, PACKAGE_LIB_DIR "/lxpanel/plugins/%s.so", type);
-            pc = plugin_load_dynamic( type, path );
+}
+
+/* Recursively set the background of all widgets on a panel background configuration change. */
+void plugin_widget_set_background(GtkWidget * w, Panel * p)
+{
+    if (w != NULL)
+    {
+        if ( ! GTK_WIDGET_NO_WINDOW(w))
+        {
+            if ((p->background) || (p->transparent))
+            {
+                if (GTK_WIDGET_REALIZED(w))
+                {
+                    panel_determine_background_pixmap(p, w, w->window);
+                    gdk_window_invalidate_rect(w->window, NULL, TRUE);
+                }
+            }
+            else
+            {
+                /* Set background according to the current GTK style. */
+                gtk_widget_set_app_paintable(w, FALSE);
+                if (GTK_WIDGET_REALIZED(w))
+                {
+                    gdk_window_set_back_pixmap(w->window, NULL, TRUE);
+                    gtk_style_set_background(w->style, w->window, GTK_STATE_NORMAL);
+                }
+            }
         }
+
+        /* Special handling to get tray icons redrawn. */
+        if (GTK_IS_SOCKET(w))
+        {
+            gtk_widget_hide(w);
+            gdk_window_process_all_updates();
+            gtk_widget_show(w);
+            gdk_window_process_all_updates();
+        }
+
+        /* Recursively process all children of a container. */
+        if (GTK_IS_CONTAINER(w))
+            gtk_container_foreach(GTK_CONTAINER(w), (GtkCallback) plugin_widget_set_background, p);
     }
-#endif  /* DISABLE_PLUGINS_LOADING */
+}
 
-    /* nothing was found */
-    if (!pc)
-        RET(NULL);
+/* Handler for "button_press_event" signal with Plugin as parameter.
+ * External so can be used from a plugin. */
+gboolean lxpanel_plugin_button_press_event(GtkWidget *plugin, GdkEventButton *event, Panel *panel)
+{
+    if (event->button == 3) /* right button */
+    {
+        GtkMenu* popup = (GtkMenu*)lxpanel_get_plugin_menu(panel, plugin, FALSE);
+        gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
+        return TRUE;
+    }
+    return FALSE;
+}
 
-    plug = g_new0(Plugin, 1);
-    g_return_val_if_fail (plug != NULL, NULL);
-    plug->class = pc;
-    pc->count++;
-    RET(plug);
+/* for old plugins compatibility */
+gboolean plugin_button_press_event(GtkWidget *widget, GdkEventButton *event, Plugin *plugin)
+{
+    return lxpanel_plugin_button_press_event(plugin->pwid, event, PLUGIN_PANEL(plugin->pwid));
 }
 
+/* Helper for position-calculation callback for popup menus. */
+void lxpanel_plugin_popup_set_position_helper(Panel * p, GtkWidget * near, GtkWidget * popup, GtkRequisition * popup_req, gint * px, gint * py)
+{
+    /* Get the origin of the requested-near widget in screen coordinates. */
+    gint x, y;
+    gdk_window_get_origin(GDK_WINDOW(near->window), &x, &y);
+    if (x != near->allocation.x) x += near->allocation.x;      /* Doesn't seem to be working according to spec; the allocation.x sometimes has the window origin in it */
+    if (y != near->allocation.y) y += near->allocation.y;
+
+    /* Dispatch on edge to lay out the popup menu with respect to the button.
+     * Also set "push-in" to avoid any case where it might flow off screen. */
+    switch (p->edge)
+    {
+        case EDGE_TOP:          y += near->allocation.height;         break;
+        case EDGE_BOTTOM:       y -= popup_req->height;                break;
+        case EDGE_LEFT:         x += near->allocation.width;          break;
+        case EDGE_RIGHT:        x -= popup_req->width;                 break;
+    }
+    *px = x;
+    *py = y;
+}
 
-void plugin_put(Plugin *this)
+/* for old plugins compatibility */
+void plugin_popup_set_position_helper(Plugin * p, GtkWidget * near, GtkWidget * popup, GtkRequisition * popup_req, gint * px, gint * py)
 {
-    PluginClass *pc = this->class;
-    ENTER;
-    plugin_class_unref( pc );
-    g_free(this);
-    RET();
+    lxpanel_plugin_popup_set_position_helper(p->panel, near, popup, popup_req, px, py);
 }
 
-int
-plugin_start(Plugin *this, char** fp)
+/* Adjust the position of a popup window to ensure that it is not hidden by the panel.
+ * It is observed that some window managers do not honor the strut that is set on the panel. */
+void lxpanel_plugin_adjust_popup_position(GtkWidget * popup, GtkWidget * parent)
 {
-    ENTER;
+    /* Initialize. */
+    Panel * p = PLUGIN_PANEL(parent);
 
-    DBG("%s\n", this->class->type);
+    /* Get the coordinates of the plugin top level widget. */
+    int x = p->cx + parent->allocation.x;
+    int y = p->cy + parent->allocation.y;
 
-    if (!this->class->constructor(this, fp)) {
-//        if (!this->class->invisible)
-//            gtk_widget_destroy(this->pwid);
-        RET(0);
+    /* Adjust these coordinates according to the panel edge. */
+    switch (p->edge)
+    {
+        case EDGE_TOP:
+           y += parent->allocation.height;
+            break;
+        case EDGE_BOTTOM:
+            y -= popup->allocation.height;
+            break;
+        case EDGE_LEFT:
+            x += parent->allocation.width;
+            break;
+        case EDGE_RIGHT:
+            x -= popup->allocation.width;
+            break;
     }
 
-    if (!this->class->invisible && this->pwid ) {
-        /* this->pwid is created by the plugin */
-        //this->pwid = gtk_bgbox_new();
-        gtk_widget_set_name(this->pwid, this->class->type);
-        gtk_box_pack_start(GTK_BOX(this->panel->box), this->pwid, this->expand, TRUE,
-              this->padding);
-        gtk_container_set_border_width(GTK_CONTAINER(this->pwid), this->border);
+    /* Clip the coordinates to ensure that the popup remains on screen. */
+    int screen_width = gdk_screen_width();
+    int screen_height = gdk_screen_height();
+    if ((x + popup->allocation.width) > screen_width) x = screen_width - popup->allocation.width;
+    if ((y + popup->allocation.height) > screen_height) y = screen_height - popup->allocation.height;
+
+    /* Move the popup to position. */
+    gdk_window_move(popup->window, x, y);
+}
+
+/* for old plugins compatibility */
+void plugin_adjust_popup_position(GtkWidget * popup, Plugin * plugin)
+{
+    lxpanel_plugin_adjust_popup_position(popup, plugin->pwid);
+}
+
+/* Open a specified path in a file manager. */
+static gboolean _open_dir_in_file_manager(GAppLaunchContext* ctx, GList* folder_infos,
+                                          gpointer user_data, GError** err)
+{
+    FmFileInfo *fi = folder_infos->data; /* only first is used */
+    GAppInfo *app = g_app_info_get_default_for_type("inode/directory", TRUE);
+    GFile *gf;
+    gboolean ret;
 
-        gtk_widget_show(this->pwid);
+    if (app == NULL)
+    {
+        g_set_error_literal(err, G_SHELL_ERROR, G_SHELL_ERROR_EMPTY_STRING,
+                            _("No file manager is configured."));
+        return FALSE;
     }
-    RET(1);
+    gf = fm_path_to_gfile(fm_file_info_get_path(fi));
+    folder_infos = g_list_prepend(NULL, gf);
+    ret = fm_app_info_launch(app, folder_infos, ctx, err);
+    g_list_free(folder_infos);
+    g_object_unref(gf);
+    g_object_unref(app);
+    return ret;
+}
+
+gboolean lxpanel_launch_path(Panel *panel, FmPath *path)
+{
+    return fm_launch_path_simple(NULL, NULL, path, _open_dir_in_file_manager, NULL);
 }
 
+#if GLIB_CHECK_VERSION(2, 32, 0)
+static GRecMutex _mutex;
+#else
+static GStaticRecMutex _mutex = G_STATIC_REC_MUTEX_INIT;
+#endif
+
+#ifndef DISABLE_PLUGINS_LOADING
+FM_MODULE_DEFINE_TYPE(lxpanel_gtk, LXPanelPluginInit, 1)
 
-void plugin_stop(Plugin *this)
+static gboolean fm_module_callback_lxpanel_gtk(const char *name, gpointer init, int ver)
 {
-    ENTER;
-    DBG("%s\n", this->class->type);
-    this->class->destructor(this);
-    this->panel->plug_num--;
-    if (!this->class->invisible && this->pwid )
-        gtk_widget_destroy(this->pwid);
-    /* this->pwid is destroyed in the dtor of plugins */
-    RET();
+    /* ignore ver for now, only 1 exists */
+    return lxpanel_register_plugin_type(name, init);
 }
+#endif
+
+static gboolean old_plugins_loaded = FALSE;
 
-void plugin_class_unref( PluginClass* pc )
+void _prepare_modules(void)
 {
-    --pc->count;
-    if (pc->count == 0 && pc->dynamic) {
-        pcl = g_list_remove(pcl, pc);
-        /* pc points now somewhere inside loaded lib, so if g_module_close
-         * will touch it after dlclose (and 2.6 does) it will result in segfault */
-        g_module_close(pc->gmodule);
-    }
+    _all_types = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+    lxpanel_plugin_qdata = g_quark_from_static_string("LXPanel::plugin-data");
+    lxpanel_plugin_qinit = g_quark_from_static_string("LXPanel::plugin-init");
+    lxpanel_plugin_qconf = g_quark_from_static_string("LXPanel::plugin-conf");
+    lxpanel_plugin_qpanel = g_quark_from_static_string("LXPanel::plugin-panel");
+#ifndef DISABLE_PLUGINS_LOADING
+    fm_modules_add_directory(PACKAGE_LIB_DIR "/lxpanel/plugins");
+    fm_module_register_lxpanel_gtk();
+#endif
 }
 
-/*
-   Get a list of all available plugin classes
-   Return a newly allocated GList which should be freed with
-   plugin_class_list_free( list );
-*/
-GList* plugin_get_available_classes()
+void _unload_modules(void)
 {
-    GList* classes = NULL;
-    char *path, *dir_path;
-    const char* file;
-    GDir* dir;
-    GList* l;
-    PluginClass *pc;
-
-    for( l = pcl; l; l = l->next ) {
-        pc = (PluginClass*)l->data;
-        classes = g_list_prepend( classes, pc );
-        ++pc->count;
-    }
+    GHashTableIter iter;
+    gpointer key, val;
 
 #ifndef DISABLE_PLUGINS_LOADING
-#if 0   /* Put plugins in config dir is too dirty... */
-    dir_path = g_build_filename( g_get_home_dir(), ".lxpanel/plugins", NULL );
-    if( dir = g_dir_open( dir_path, 0, NULL ) ) {
-        while( file = g_dir_read_name( dir ) ) {
-            GModule *m;
-            char* type;
-            if( ! g_str_has_suffix( file, ".so" ) )
-                  continue;
-            type = g_strndup( file, strlen(file) - 3 );
-            l = plugin_find_class( type );
-            if( l == NULL ) { /* If it has not been loaded */
-                path = g_build_filename( dir_path, file, NULL );
-                if( pc = plugin_load_dynamic( type, path ) ) {
-                    ++pc->count;
-                    classes = g_list_prepend( classes, pc );
-                }
-                g_free( path );
-            }
-            g_free( type );
+    fm_module_unregister_type("lxpanel_gtk");
+#endif
+    g_hash_table_iter_init(&iter, _all_types);
+    while(g_hash_table_iter_next(&iter, &key, &val))
+    {
+        register LXPanelPluginInit *init = val;
+        if (init->new_instance == NULL) /* old type of plugin */
+        {
+            plugin_class_unref(init->_reserved1);
+            g_free(val);
         }
-        g_dir_close( dir );
     }
-    g_free( dir_path );
+    g_hash_table_destroy(_all_types);
+    old_plugins_loaded = FALSE;
+}
+
+gboolean lxpanel_register_plugin_type(const char *name, LXPanelPluginInit *init)
+{
+    LXPanelPluginInit *data;
+
+    /* validate it */
+    if (init->new_instance == NULL || name == NULL || name[0] == '\0')
+        return FALSE;
+#if GLIB_CHECK_VERSION(2, 32, 0)
+    g_rec_mutex_lock(&_mutex);
+#else
+    g_static_rec_mutex_lock(&_mutex);
 #endif
-    if( dir = g_dir_open( PACKAGE_LIB_DIR "/lxpanel/plugins", 0, NULL ) ) {
-        while( file = g_dir_read_name( dir ) ) {
-            GModule *m;
-            char* type;
-            if( ! g_str_has_suffix( file, ".so" ) )
-                  continue;
-            type = g_strndup( file, strlen(file) - 3 );
-            l = plugin_find_class( type );
-            if( l == NULL ) { /* If it has not been loaded */
-                path = g_build_filename( PACKAGE_LIB_DIR "/lxpanel/plugins", file, NULL );
-                if( pc = plugin_load_dynamic( type, path ) ) {
-                    ++pc->count;
-                    classes = g_list_prepend( classes, pc );
-                }
-                g_free( path );
-            }
-            g_free( type );
-        }
-        g_dir_close( dir );
+    /* test if it's registered already */
+    data = _find_plugin(name);
+    if (data == NULL)
+    {
+        if (init->init)
+            init->init();
+        g_hash_table_insert(_all_types, g_strdup(name), init);
     }
+#if GLIB_CHECK_VERSION(2, 32, 0)
+    g_rec_mutex_unlock(&_mutex);
+#else
+    g_static_rec_mutex_unlock(&_mutex);
 #endif
-    /* classes = g_list_reverse( classes ); */
-    return classes;
+    return (data == NULL);
 }
 
-void plugin_class_list_free( GList* classes )
+static void _old_plugin_save_hook(const config_setting_t * setting, FILE * f, gpointer user_data)
 {
-   g_list_foreach( classes, plugin_class_unref, NULL );
-   g_list_free( classes );
+    Plugin *pl = user_data;
+    PluginClass *pc = pl->class;
+    if (pc->save)
+        pc->save(pl, f);
 }
 
-void
-plugin_widget_set_background( GtkWidget* w, Panel* p )
+/* This is called right before Plugin instance is destroyed */
+static void _old_plugin_destroy(gpointer data)
 {
-       static gboolean in_tray = FALSE;
-       gboolean is_tray;
+    Plugin *pl = data;
 
-    if( ! w )
-        return;
+    /* Run the destructor. */
+    pl->class->destructor(pl);
+    plugin_class_unref(pl->class);
 
-    is_tray = ( GTK_IS_CONTAINER(w) && strcmp( gtk_widget_get_name( w ), "tray" ) == 0 );
+    /* Free the Plugin structure. */
+    g_free(pl);
+}
+
+//static void on_size_allocate(GtkWidget *widget, GdkRectangle *allocation, Panel *p)
+//{
+//    _queue_panel_calculate_size(p);
+//}
 
-    if( ! GTK_WIDGET_NO_WINDOW( w ) )
+GtkWidget *lxpanel_add_plugin(Panel *p, const char *name, config_setting_t *cfg, gint at)
+{
+    LXPanelPluginInit *init;
+    GtkWidget *widget;
+    config_setting_t *s, *pconf;
+    gint expand, padding = 0, border = 0, i;
+
+    CHECK_MODULES();
+    if (!old_plugins_loaded)
+        plugin_get_available_classes();
+    old_plugins_loaded = TRUE;
+    init = _find_plugin(name);
+    if (init == NULL)
+        return NULL;
+    /* prepare widget settings */
+    if (!init->expand_available)
+        expand = 0;
+    else if ((s = config_setting_get_member(cfg, "expand")))
+        expand = config_setting_get_int(s);
+    else
+        expand = init->expand_default;
+    s = config_setting_get_member(cfg, "padding");
+    if (s)
+        padding = config_setting_get_int(s);
+    s = config_setting_get_member(cfg, "border");
+    if (s)
+        border = config_setting_get_int(s);
+    /* prepare config and create it if need */
+    s = config_setting_add(cfg, "", PANEL_CONF_TYPE_LIST);
+    for (i = 0; (pconf = config_setting_get_elem(s, i)); i++)
+        if (strcmp(config_setting_get_name(pconf), "Config") == 0)
+            break;
+    if (!pconf)
+        pconf = config_setting_add(s, "Config", PANEL_CONF_TYPE_GROUP);
+    /* If this plugin can only be instantiated once, count the instantiation.
+     * This causes the configuration system to avoid displaying the plugin as one that can be added. */
+    if (init->new_instance) /* new style of plugin */
     {
-        if( p->background || p->transparent )
-        {
-            gtk_widget_set_app_paintable( w, TRUE );
-            if( GTK_WIDGET_REALIZED(w) )
-                gdk_window_set_back_pixmap( w->window, NULL, TRUE );
-        }
+        widget = init->new_instance(p, pconf);
+        if (widget == NULL)
+            return widget;
+        if (init->button_press_event)
+            g_signal_connect(widget, "button-press-event",
+                             G_CALLBACK(init->button_press_event), p);
         else
-        {
-            gtk_widget_set_app_paintable( w, FALSE );
-            if( GTK_WIDGET_REALIZED(w) )
-            {
-                gdk_window_set_back_pixmap( w->window, NULL, FALSE );
-
-                               /* dirty hack to fix the background color of tray icons :-( */
-                if( in_tray )
-                                       gdk_window_set_background( w->window, &w->style->bg[ GTK_STATE_NORMAL ] );
-                       }
-        }
-        // g_debug("%s has window (%s)", gtk_widget_get_name(w), G_OBJECT_TYPE_NAME(w) );
+            g_signal_connect(widget, "button-press-event",
+                             G_CALLBACK(lxpanel_plugin_button_press_event), p);
     }
-    /*
     else
-        g_debug("%s has NO window (%s)", gtk_widget_get_name(w), G_OBJECT_TYPE_NAME(w) );
-    */
-    if( GTK_IS_CONTAINER( w ) )
     {
-       if( is_tray )
-               in_tray = TRUE;
-
-        gtk_container_foreach( w, plugin_widget_set_background, p );
-
-        if( is_tray )
-                       in_tray = FALSE;
-    }
+        Plugin *pl = g_new0(Plugin, 1);
+        PluginClass *pc = init->_reserved1;
+        char *conf = config_setting_to_string(pconf), *fp;
+
+        pl->class = pc;
+        pl->panel = p;
+        widget = NULL;
+        fp = &conf[9]; /* skip "Config {\n" */
+        /* g_debug("created conf: %s",conf); */
+    /* Call the constructor.
+     * It is responsible for parsing the parameters, and setting "pwid" to the top level widget. */
+        if (pc->constructor(pl, &fp))
+            widget = pl->pwid;
+        g_free(conf);
+
+        if (widget == NULL) /* failed */
+        {
+            g_free(pl);
+            return widget;
+        }
 
-    /* Dirty hack: Force repaint of tray icons!!
-     * Normal gtk+ repaint cannot work across different processes.
-     * So, we need some dirty tricks here.
-     */
-    if( is_tray )
-    {
-        gtk_widget_set_size_request( w, w->allocation.width, w->allocation.height );
-        gtk_widget_hide (gtk_bin_get_child((GtkBin*)w));
-        if( gtk_events_pending() )
-            gtk_main_iteration();
-        gtk_widget_show (gtk_bin_get_child((GtkBin*)w));
-        if( gtk_events_pending() )
-            gtk_main_iteration();
-        gtk_widget_set_size_request( w, -1, -1 );
+        pc->count += 1;
+        config_setting_set_save_hook(pconf, _old_plugin_save_hook, pl);
+        lxpanel_plugin_set_data(widget, pl, _old_plugin_destroy);
     }
+    gtk_widget_set_name(widget, init->name);
+    gtk_box_pack_start(GTK_BOX(p->box), widget, expand, TRUE, padding);
+    gtk_container_set_border_width(GTK_CONTAINER(widget), border);
+//    g_signal_connect(widget, "size-allocate", G_CALLBACK(on_size_allocate), p);
+    gtk_widget_show(widget);
+    g_object_set_qdata(G_OBJECT(widget), lxpanel_plugin_qconf, cfg);
+    g_object_set_qdata(G_OBJECT(widget), lxpanel_plugin_qinit, init);
+    g_object_set_qdata(G_OBJECT(widget), lxpanel_plugin_qpanel, p);
+    return widget;
 }
 
-void plugin_set_background( Plugin* pl, Panel* p )
+/* transfer none - note that not all fields are valid there */
+GHashTable *lxpanel_get_all_types(void)
 {
-    if( G_UNLIKELY( pl->class->invisible || ! pl->pwid ) )
-        return;
-    plugin_widget_set_background( pl->pwid, p );
+    return _all_types;
 }