Convert launch button into a widget class for more easy management.
authorAndriy Grytsenko <andrej@rep.kiev.ua>
Mon, 3 Oct 2016 00:02:18 +0000 (03:02 +0300)
committerAndriy Grytsenko <andrej@rep.kiev.ua>
Mon, 3 Oct 2016 00:02:18 +0000 (03:02 +0300)
ChangeLog
plugins/Makefile.am
plugins/launch-button.c [new file with mode: 0644]
plugins/launch-button.h [new file with mode: 0644]
plugins/launchtaskbar.c
src/icon-grid.c

index 1764b4c..22c4784 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,6 +9,7 @@
 * Added support for third-party plugins localized descriptions.
 * Implemented "lxpanelctl command ..." to send message to panel plugin.
 * Added APIs for PanelIconGrid to draw focus on drop.
+* Converted launch button into widget class for more easy management.
 * Adjusted PanelIconGrid: min spacing is now 1, no_window flag is now left
     unset by default.
 
index 2a3b752..edb6e77 100644 (file)
@@ -36,6 +36,7 @@ PLUGINS_SOURCES = \
        dirmenu.c \
        launchtaskbar.c \
        task-button.c \
+       launch-button.c \
        pager.c \
        separator.c \
        tray.c \
diff --git a/plugins/launch-button.c b/plugins/launch-button.c
new file mode 100644 (file)
index 0000000..9c74b47
--- /dev/null
@@ -0,0 +1,242 @@
+/**
+ * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "launch-button.h"
+#include "misc.h"
+
+#include <string.h>
+
+/* Representative of one launch button.
+ * Note that the launch parameters come from the specified desktop file, or from the configuration file.
+ * This structure is also used during the "add to launchtaskbar" dialog to hold menu items. */
+struct _LaunchButton
+{
+    GtkEventBox parent;
+    LXPanel * panel;                    /* Back pointer to panel (grandparent widget) */
+    GtkWidget * plugin;                 /* Back pointer to the plugin */
+    FmJob * job;                        /* Async job to retrieve file info */
+    FmFileInfo * fi;                    /* Launcher application descriptor */
+    config_setting_t * settings;        /* Button settings */
+};
+
+
+static void launch_button_job_finished(FmJob *job, LaunchButton *self)
+{
+    GtkWidget *image;
+
+    if (self->job == NULL)
+        return; // duplicate call? seems a bug in libfm
+
+    if (FM_IS_FILE_INFO_JOB(job))
+    {
+        /* absolute path */
+        self->fi = fm_file_info_list_pop_head(FM_FILE_INFO_JOB(job)->file_infos);
+    }
+    else
+    {
+        /* search for id */
+        self->fi = fm_file_info_list_pop_head(FM_DIR_LIST_JOB(job)->files);
+    }
+    self->job = NULL;
+    g_object_unref(job);
+    if (self->fi == NULL)
+    {
+        g_warning("launchbar: desktop entry does not exist");
+        return;
+    }
+    image = lxpanel_image_new_for_fm_icon(self->panel, fm_file_info_get_icon(self->fi),
+                                          -1, NULL);
+    lxpanel_button_compose(GTK_WIDGET(self), image, NULL, NULL);
+    gtk_widget_set_tooltip_text(GTK_WIDGET(self), fm_file_info_get_disp_name(self->fi));
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Class implementation
+ */
+
+G_DEFINE_TYPE(LaunchButton, launch_button, GTK_TYPE_EVENT_BOX)
+
+static void launch_button_dispose(GObject *object)
+{
+    LaunchButton *self = (LaunchButton *)object;
+
+    if (self->job)
+    {
+        g_signal_handlers_disconnect_by_func(self->job,
+                                             launch_button_job_finished, object);
+        fm_job_cancel(self->job);
+        self->job = NULL;
+    }
+
+    if (self->fi)
+    {
+        fm_file_info_unref(self->fi);
+        self->fi = NULL;
+    }
+
+    G_OBJECT_CLASS(launch_button_parent_class)->dispose(object);
+}
+
+static gboolean launch_button_press_event(GtkWidget *widget, GdkEventButton *event)
+{
+    LaunchButton *btn = PANEL_LAUNCH_BUTTON(widget);
+
+    if (event->button == 1 && event->type == GDK_BUTTON_PRESS) /* left button */
+    {
+        if (btn->job) /* The job is still running */
+            ;
+        else if (btn->fi == NULL)  /* The bootstrap button */
+            lxpanel_plugin_show_config_dialog(btn->plugin);
+        else
+            lxpanel_launch_path(btn->panel, fm_file_info_get_path(btn->fi));
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static void launch_button_init(LaunchButton *self)
+{
+    gtk_container_set_border_width(GTK_CONTAINER(self), 0);
+    gtk_widget_set_can_focus(GTK_WIDGET(self), FALSE);
+}
+
+static void launch_button_class_init(LaunchButtonClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+    object_class->dispose = launch_button_dispose;
+    widget_class->button_press_event = launch_button_press_event;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Interface functions
+ */
+
+/* creates new button */
+LaunchButton *launch_button_new(LXPanel *panel, GtkWidget *plugin, FmPath *id,
+                                config_setting_t *settings)
+{
+    LaunchButton *self = g_object_new(PANEL_TYPE_LAUNCH_BUTTON, NULL);
+    GtkWidget *image;
+
+    self->panel = panel;
+    self->plugin = plugin;
+    self->settings = settings;
+    if (id == NULL)
+    {
+        /* a bootstrap button */
+        image = lxpanel_image_new_for_icon(panel, GTK_STOCK_ADD, -1, NULL);
+        lxpanel_button_compose(GTK_WIDGET(self), image, NULL, NULL);
+    }
+    else
+    {
+        /* g_debug("LaunchButton: trying file %s in scheme %s", fm_path_get_basename(id),
+                fm_path_get_basename(fm_path_get_scheme_path(id))); */
+        if (fm_path_is_native(id) ||
+            strncmp(fm_path_get_basename(fm_path_get_scheme_path(id)), "search:", 7) != 0)
+        {
+            FmFileInfoJob *job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
+
+            fm_file_info_job_add(job, id);
+            self->job = FM_JOB(job);
+        }
+        else /* it is a search job */
+        {
+            FmDirListJob *job = fm_dir_list_job_new2(id, FM_DIR_LIST_JOB_FAST);
+
+            self->job = FM_JOB(job);
+        }
+        g_signal_connect(self->job, "finished",
+                         G_CALLBACK(launch_button_job_finished), self);
+        if (!fm_job_run_async(self->job))
+        {
+            g_object_unref(self->job);
+            self->job = NULL;
+            gtk_widget_destroy(GTK_WIDGET(self));
+            g_warning("launchbar: problem running file search job");
+            return NULL;
+        }
+    }
+    return self;
+}
+
+FmFileInfo *launch_button_get_file_info(LaunchButton *btn)
+{
+    if (PANEL_IS_LAUNCH_BUTTON(btn))
+        return btn->fi;
+    return NULL;
+}
+
+const char *launch_button_get_disp_name(LaunchButton *btn)
+{
+    if (PANEL_IS_LAUNCH_BUTTON(btn) && btn->fi != NULL)
+        return fm_file_info_get_disp_name(btn->fi);
+    return NULL;
+}
+
+FmIcon *launch_button_get_icon(LaunchButton *btn)
+{
+    if (PANEL_IS_LAUNCH_BUTTON(btn) && btn->fi != NULL)
+        return fm_file_info_get_icon(btn->fi);
+    return NULL;
+}
+
+config_setting_t *launch_button_get_settings(LaunchButton *btn)
+{
+    if (PANEL_IS_LAUNCH_BUTTON(btn))
+        return btn->settings;
+    return NULL;
+}
+
+void launch_button_set_settings(LaunchButton *btn, config_setting_t *settings)
+{
+    if (PANEL_IS_LAUNCH_BUTTON(btn))
+        btn->settings = settings;
+}
+
+/**
+ * launch_button_wait_load
+ * @btn: a button instance
+ *
+ * If @btn does not have pending file info then returns. Otherwise waits
+ * for it. If loading the info failed then destroys @btn and associated
+ * settings.
+ *
+ * Returns: %TRUE if button is fully loaded.
+ *
+ * Since: 0.9.0
+ */
+gboolean launch_button_wait_load(LaunchButton *btn)
+{
+    if (!PANEL_IS_LAUNCH_BUTTON(btn) || btn->job == NULL)
+        return TRUE;
+    if (fm_job_run_sync(btn->job))
+        return TRUE;
+
+    if (btn->settings)
+        config_setting_destroy(btn->settings);
+    gtk_widget_destroy(GTK_WIDGET(btn));
+    return FALSE;
+}
diff --git a/plugins/launch-button.h b/plugins/launch-button.h
new file mode 100644 (file)
index 0000000..d84bc9b
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __LAUNCH_BUTTON_H__
+#define __LAUNCH_BUTTON_H__ 1
+
+#include "plugin.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PANEL_TYPE_LAUNCH_BUTTON           (launch_button_get_type())
+#define PANEL_LAUNCH_BUTTON(obj)           (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                                            PANEL_TYPE_LAUNCH_BUTTON, LaunchButton))
+#define PANEL_LAUNCH_BUTTON_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                            PANEL_TYPE_LAUNCH_BUTTON, LaunchButtonClass))
+#define PANEL_IS_LAUNCH_BUTTON(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+                                            PANEL_TYPE_LAUNCH_BUTTON))
+#define PANEL_IS_LAUNCH_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+                                             PANEL_TYPE_LAUNCH_BUTTON))
+
+extern GType launch_button_get_type        (void) G_GNUC_CONST;
+
+typedef struct _LaunchButton                LaunchButton;
+typedef struct _LaunchButtonClass           LaunchButtonClass;
+
+struct _LaunchButtonClass
+{
+    GtkEventBoxClass parent_class;
+};
+
+/* creates new button */
+LaunchButton *launch_button_new(LXPanel *panel, GtkWidget *plugin, FmPath *path, config_setting_t *settings);
+FmFileInfo *launch_button_get_file_info(LaunchButton *btn);
+const char *launch_button_get_disp_name(LaunchButton *btn);
+FmIcon *launch_button_get_icon(LaunchButton *btn);
+config_setting_t *launch_button_get_settings(LaunchButton *btn);
+void launch_button_set_settings(LaunchButton *btn, config_setting_t *settings);
+gboolean launch_button_wait_load(LaunchButton *btn);
+
+G_END_DECLS
+
+#endif /* __LAUNCH_BUTTON_H__ */
index a58755a..c811db6 100644 (file)
@@ -78,6 +78,7 @@
 #include "ev.h"
 #include "plugin.h"
 #include "task-button.h"
+#include "launch-button.h"
 #include "icon-grid.h"
 #ifndef DISABLE_MENU
 # include "menu-policy.h"
@@ -103,24 +104,12 @@ typedef enum {
 
 typedef struct LaunchTaskBarPlugin LaunchTaskBarPlugin;
 
-/* Representative of one launch button.
- * Note that the launch parameters come from the specified desktop file, or from the configuration file.
- * This structure is also used during the "add to launchtaskbar" dialog to hold menu items. */
-typedef struct {
-    LaunchTaskBarPlugin * p;            /* Back pointer to plugin */
-    GtkWidget * widget;                 /* Pointer to button */
-    FmFileInfo * fi;                    /* Launcher application descriptor */
-    config_setting_t * settings;        /* Pointer to settings */
-    FmDndDest * dd;                     /* Drag and drop support */
-} LaunchButton; /* FIXME: convert it into GtkWidget, button itself */
-
 /* Private context for taskbar plugin. */
 struct LaunchTaskBarPlugin {
     /* LAUNCHBAR */
     GtkWidget *lb_icon_grid;         /* Icon grid managing the container */
-    GSList        *buttons;          /* Launchbar buttons */
-    LaunchButton  *bootstrap_button; /* Bootstrapping button for empty launchtaskbar */
     GtkWidget     *p_button_add, *p_button_remove, *p_label_menu_app_exec, *p_label_def_app_exec;
+    FmDndDest *dd;                 /* Drag & drop on launchbar */
     /* TASKBAR */
     GtkWidget * tb_icon_grid;      /* Manager for taskbar buttons */
     int number_of_desktops;        /* Number of desktops, from NET_WM_NUMBER_OF_DESKTOPS */
@@ -136,7 +125,7 @@ struct LaunchTaskBarPlugin {
     gboolean flash_state;       /* One-bit counter to flash taskbar */
     /* COMMON */
 #ifndef DISABLE_MENU
-    FmFileInfo * fi;            /* Current menu item file info */
+    FmPath * path;              /* Current menu item path */
     GtkWidget       *p_menuitem_lock_tbp;
     GtkWidget       *p_menuitem_unlock_tbp;
     GtkWidget       *p_menuitem_new_instance;
@@ -236,7 +225,7 @@ static char *task_get_cmdline(Window win, LaunchTaskBarPlugin *ltbp)
     return cmdline;
 }
 
-static FmFileInfo *f_find_menu_launchbutton_recursive(Window win, LaunchTaskBarPlugin *ltbp)
+static FmPath *f_find_menu_launchbutton_recursive(Window win, LaunchTaskBarPlugin *ltbp)
 {
     MenuCache *mc;
     guint32 flags;
@@ -245,9 +234,7 @@ static FmFileInfo *f_find_menu_launchbutton_recursive(Window win, LaunchTaskBarP
     char *exec_bin = task_get_cmdline(win, ltbp);
     const char *exec, *short_exec;
     char *str_path;
-    FmPath *path;
-    FmFileInfoJob *job;
-    FmFileInfo *fi = NULL;
+    FmPath *path = NULL;
 
     /* FIXME: cache it in Task object */
     mc = panel_menu_cache_new(&flags);
@@ -296,61 +283,44 @@ static FmFileInfo *f_find_menu_launchbutton_recursive(Window win, LaunchTaskBarP
         str_path = menu_cache_dir_make_path(MENU_CACHE_DIR(l->data));
         path = fm_path_new_relative(fm_path_get_apps_menu(), str_path+13); /* skip /Applications */
         g_free(str_path);
-        job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
-        fm_file_info_job_add(job, path);
-        fm_path_unref(path);
-        if (!fm_job_run_sync(FM_JOB(job)))
-            g_warning("launchtaskbar: problem running file info job");
-        else
-            fi = fm_file_info_list_pop_head(job->file_infos);
-        g_object_unref(job);
     }
     g_slist_foreach(apps, (GFunc)menu_cache_item_unref, NULL);
     g_slist_free(apps);
     menu_cache_unref(mc);
-    g_debug("f_find_menu_launchbutton_recursive: search '%s' found=%d", exec_bin, (fi != NULL));
+    g_debug("f_find_menu_launchbutton_recursive: search '%s' found=%d", exec_bin, (path != NULL));
     g_free(exec_bin);
-    return fi;
+    return path;
 }
 #endif
 
-/* Deallocate a LaunchButton. */
-static void launchbutton_free(LaunchButton * btn)
-{
-    if (btn->fi)
-        fm_file_info_unref(btn->fi);
-    if (btn->dd)
-        g_object_unref(btn->dd);
-    g_free(btn);
-}
-
-/* Handler for "button-press-event" event from launchtaskbar button. */
-static gboolean launchbutton_press_event(GtkWidget * widget, GdkEventButton * event, LaunchButton * b)
-{
-    if (event->button == 1 && event->type == GDK_BUTTON_PRESS) /* left button */
-    {
-        if (b->fi == NULL)  /* The bootstrap button */
-            lxpanel_plugin_show_config_dialog(b->p->plugin);
-        else
-            lxpanel_launch_path(b->p->panel, fm_file_info_get_path(b->fi));
-        return TRUE;
-    }
-    return FALSE;
-}
-
 /* Handler for "drag-motion" event from launchtaskbar button. */
-static gboolean launchbutton_drag_motion_event(
+static gboolean on_launchbar_drag_motion(
     GtkWidget * widget,
     GdkDragContext * context,
     gint x,
     gint y,
     guint time,
-    LaunchButton * b)
+    LaunchTaskBarPlugin * b)
 {
     GdkAtom target;
     GdkDragAction action = 0;
+    GtkWidget *btn;
+    PanelIconGridDropPosition pos;
 
-    fm_dnd_dest_set_dest_file(b->dd, b->fi);
+    if (!panel_icon_grid_get_dest_at_pos(PANEL_ICON_GRID(b->lb_icon_grid), x, y,
+                                         &btn, &pos) || !btn)
+    {
+        panel_icon_grid_set_drag_dest(PANEL_ICON_GRID(b->lb_icon_grid), NULL, 0);
+        fm_dnd_dest_set_dest_file(b->dd, NULL);
+        return FALSE;
+    }
+    panel_icon_grid_set_drag_dest(PANEL_ICON_GRID(b->lb_icon_grid), btn, pos);
+    if (!PANEL_IS_LAUNCH_BUTTON(btn) || pos != PANEL_ICON_GRID_DROP_INTO)
+    {
+        fm_dnd_dest_set_dest_file(b->dd, NULL);
+        return FALSE;
+    }
+    fm_dnd_dest_set_dest_file(b->dd, launch_button_get_file_info((LaunchButton *)btn));
     target = fm_dnd_dest_find_target(b->dd, context);
     if (target != GDK_NONE && fm_dnd_dest_is_target_supported(b->dd, target))
         action = fm_dnd_dest_get_default_action(b->dd, context, target);
@@ -359,135 +329,37 @@ static gboolean launchbutton_drag_motion_event(
     return (action != 0);
 }
 
-/* Build the graphic elements for the bootstrap launchtaskbar button. */
-static void launchbutton_build_bootstrap(LaunchTaskBarPlugin *lb)
+static void on_launchbar_drag_leave(GtkWidget * widget, GdkDragContext * drag_context,
+                                    guint time, LaunchTaskBarPlugin * lb)
 {
-    if(lb->bootstrap_button == NULL)
-    {
-        /* Build a button that has the stock "Add" icon.
-         * The "desktop-id" being NULL is the marker that this is the bootstrap button. */
-        lb->bootstrap_button = g_new0(LaunchButton, 1);
-        lb->bootstrap_button->p = lb;
-
-        /* Create an event box. */
-        lb->bootstrap_button->widget = lxpanel_button_new_for_icon(lb->panel,
-                                                                   GTK_STOCK_ADD,
-                                                                   NULL, NULL);
-        g_signal_connect(lb->bootstrap_button->widget, "button-press-event",
-                         G_CALLBACK(launchbutton_press_event), lb->bootstrap_button);
-
-        /* Add the bootstrap button to the icon grid.  By policy it is empty at this point. */
-        gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), lb->bootstrap_button->widget);
-        //plugin_widget_set_background(lb->bootstrap_button->widget, lb->panel);
-    }
-    else
-        gtk_widget_show(lb->bootstrap_button->widget);
+    panel_icon_grid_set_drag_dest(PANEL_ICON_GRID(lb->lb_icon_grid), NULL, 0);
+    fm_dnd_dest_set_dest_file(lb->dd, NULL);
 }
 
 #ifndef DISABLE_MENU
-static LaunchButton *launchbar_exec_bin_exists(LaunchTaskBarPlugin *lb, FmFileInfo *fi)
+static LaunchButton *launchbar_exec_bin_exists(LaunchTaskBarPlugin *lb, FmPath *path)
 {
     LaunchButton *ret_val = NULL;
-    FmPath *path;
-    GSList* l;
+    GList *children, *l;
+    FmFileInfo *fi;
 
-    if (!fi)
+    if (!path)
         return NULL;
-    path = fm_file_info_get_path(fi);
-    for(l = lb->buttons; l != NULL; l = l->next)
+    children = gtk_container_get_children(GTK_CONTAINER(lb->lb_icon_grid));
+    for (l = children; l != NULL; l = l->next)
     {
-        LaunchButton *btn = (LaunchButton *)l->data;
-        if (btn->fi && fm_path_equal(path, fm_file_info_get_path(btn->fi)))
+        fi = launch_button_get_file_info(PANEL_LAUNCH_BUTTON(l->data));
+        if (fi && fm_path_equal(path, fm_file_info_get_path(fi)))
         {
-            ret_val = btn;
+            ret_val = l->data;
             break;
         }
     }
+    g_list_free(children);
     return ret_val;
 }
 #endif
 
-/* Build the graphic elements for a launchtaskbar button.  The desktop_id field is already established. */
-/* NOTE: this func consumes reference on fi */
-static LaunchButton *launchbutton_for_file_info(LaunchTaskBarPlugin * lb, FmFileInfo * fi)
-{
-    LaunchButton *btn;
-    GtkWidget *button;
-
-    if (fi == NULL)
-    {
-        g_warning("launchbar: desktop entry does not exist\n");
-        return NULL;
-    }
-
-    /* Allocate the LaunchButton structure. */
-    btn = g_new0(LaunchButton, 1);
-    btn->p = lb;
-    btn->fi = fi;
-
-    /* Create a button with the specified icon. */
-    button = lxpanel_button_new_for_fm_icon(lb->panel, fm_file_info_get_icon(fi),
-                                            NULL, NULL);
-    btn->widget = button;
-
-    gtk_widget_set_tooltip_text(button, fm_file_info_get_disp_name(fi));
-
-    /* Add the button to the icon grid. */
-    gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), button);
-
-    /* Drag and drop support. */
-    btn->dd = fm_dnd_dest_new_with_handlers(button);
-
-    /* Connect signals. */
-    g_signal_connect(button, "button-press-event", G_CALLBACK(launchbutton_press_event), (gpointer) btn);
-    g_signal_connect(button, "drag-motion", G_CALLBACK(launchbutton_drag_motion_event), btn);
-
-    /* If the list goes from null to non-null, remove the bootstrap button. */
-    if ((lb->buttons == NULL) && (lb->bootstrap_button != NULL))
-        gtk_widget_hide(lb->bootstrap_button->widget);
-
-    /* Append at end of list to preserve configured order. */
-    lb->buttons = g_slist_append(lb->buttons, btn);
-
-    /* Show the widget and return. */
-    //plugin_widget_set_background(button, lb->panel);
-    return btn;
-}
-
-static LaunchButton *launchbutton_build_gui(LaunchTaskBarPlugin * lb, FmPath * id)
-{
-    /* Try to get the file data */
-    FmFileInfoJob *job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
-    FmFileInfo *fi;
-
-    fm_file_info_job_add(job, id);
-    if (!fm_job_run_sync(FM_JOB(job)))
-    {
-        g_warning("launchbar: problem running file info job\n");
-        g_object_unref(job);
-        return NULL;
-    }
-    fi = fm_file_info_list_pop_head(job->file_infos);
-    g_object_unref(job);
-    return launchbutton_for_file_info(lb, fi);
-}
-
-static LaunchButton *launchbutton_search_and_build_gui(LaunchTaskBarPlugin * lb, FmPath * id)
-{
-    FmDirListJob *job = fm_dir_list_job_new2(id, FM_DIR_LIST_JOB_FAST);
-    FmFileInfo *fi;
-
-    if (!fm_job_run_sync(FM_JOB(job)))
-    {
-        g_warning("launchbar: problem running file search job\n");
-        g_object_unref(job);
-        return NULL;
-    }
-    fi = fm_file_info_list_pop_head(job->files);
-    g_object_unref(job);
-    return launchbutton_for_file_info(lb, fi);
-}
-
 /* Read the configuration file entry for a launchtaskbar button and create it. */
 static gboolean launchbutton_constructor(LaunchTaskBarPlugin * lb, config_setting_t * s)
 {
@@ -505,24 +377,22 @@ static gboolean launchbutton_constructor(LaunchTaskBarPlugin * lb, config_settin
     {
         str_path = expand_tilda(str);
         path = fm_path_new_for_path(str_path);
-        btn = launchbutton_build_gui(lb, path);
     }
     else if (strchr(str, '/') != NULL)
     {
         path = fm_path_new_for_str(str);
         /* FIXME: check if str contains invalid path */
-        btn = launchbutton_build_gui(lb, path);
     }
     else
     {
         str_path = g_strdup_printf("search://menu://applications/?recursive=1&show_hidden=1&name=%s", str);
         path = fm_path_new_for_uri(str_path);
-        btn = launchbutton_search_and_build_gui(lb, path);
     }
+    btn = launch_button_new(lb->panel, lb->plugin, path, s);
     g_free(str_path);
     fm_path_unref(path);
     if (btn)
-        btn->settings = s;
+        gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), GTK_WIDGET(btn));
     return (btn != NULL);
 }
 
@@ -587,9 +457,10 @@ static void launchtaskbar_constructor_add_default_special_case(LaunchTaskBarPlug
     g_key_file_set_value(ltbp->p_key_file_special_cases, "special_cases", tk_exec, mb_exec);
 }
 
-static void launchtaskbar_constructor_launch(LaunchTaskBarPlugin *ltbp, gboolean build_bootstrap)
+static void launchtaskbar_constructor_launch(LaunchTaskBarPlugin *ltbp)
 {
     config_setting_t *settings;
+    guint i = 0;
 
     if(!ltbp->lb_built)
     {
@@ -599,9 +470,8 @@ static void launchtaskbar_constructor_launch(LaunchTaskBarPlugin *ltbp, gboolean
         if (settings && config_setting_is_list(settings))
         {
             config_setting_t *s;
-            guint i;
 
-            for (i = 0; (s = config_setting_get_elem(settings, i)) != NULL; )
+            while ((s = config_setting_get_elem(settings, i)) != NULL)
             {
                 if (strcmp(config_setting_get_name(s), "Button") != 0)
                 {
@@ -620,11 +490,19 @@ static void launchtaskbar_constructor_launch(LaunchTaskBarPlugin *ltbp, gboolean
                     i++;
             }
         }
-        if(build_bootstrap)
+        if (i == 0)
         {
-            if(ltbp->buttons == NULL)
-                launchbutton_build_bootstrap(ltbp);
+            /* build bootstrap button */
+            LaunchButton *btn = launch_button_new(ltbp->panel, ltbp->plugin, NULL, NULL);
+            gtk_container_add(GTK_CONTAINER(ltbp->lb_icon_grid), GTK_WIDGET(btn));
+            g_debug("launchtaskbar: added bootstrap button %p", btn);
         }
+        /* Drag and drop support. */
+        ltbp->dd = fm_dnd_dest_new_with_handlers(ltbp->lb_icon_grid);
+        g_signal_connect(ltbp->lb_icon_grid, "drag-motion",
+                         G_CALLBACK(on_launchbar_drag_motion), ltbp);
+        g_signal_connect(ltbp->lb_icon_grid, "drag-leave",
+                         G_CALLBACK(on_launchbar_drag_leave), ltbp);
     }
     gtk_widget_set_visible(ltbp->lb_icon_grid, TRUE);
 }
@@ -757,6 +635,7 @@ static GtkWidget *_launchtaskbar_constructor(LXPanel *panel, config_setting_t *s
 {
     GtkWidget *p;
     LaunchTaskBarPlugin *ltbp;
+    int height, border;
 
     gtk_rc_parse_string(launchtaskbar_rc);
 
@@ -800,22 +679,24 @@ static GtkWidget *_launchtaskbar_constructor(LXPanel *panel, config_setting_t *s
     ltbp->plugin = p = panel_box_new(panel, FALSE, 5);
     lxpanel_plugin_set_data(p, ltbp, launchtaskbar_destructor);
     /* Allocate an icon grid manager to manage the container. */
+    height = panel_get_height(panel);
+    border = (height - ltbp->icon_size) > 1 ? 1 : 0;
     ltbp->lb_icon_grid = panel_icon_grid_new(panel_get_orientation(panel),
                                              ltbp->icon_size, ltbp->icon_size,
-                                             3, 0, panel_get_height(panel));
+                                             3 - 2 * border, border, height);
     gtk_box_pack_start(GTK_BOX(p), ltbp->lb_icon_grid, FALSE, TRUE, 0);
 
     /* Read parameters from the configuration file. */
     config_setting_lookup_int(settings, "LaunchTaskBarMode", &ltbp->mode);
     switch (ltbp->mode) {
     case LAUNCHBAR:
-        launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
+        launchtaskbar_constructor_launch(ltbp);
         gtk_widget_set_name(p, "launchbar");
         break;
     default:
         ltbp->mode = LAUNCHTASKBAR; /* reset invalid value */
     case LAUNCHTASKBAR:
-        launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
+        launchtaskbar_constructor_launch(ltbp);
         gtk_widget_set_name(p, "launchtaskbar");
     case TASKBAR:
         launchtaskbar_constructor_task(ltbp);
@@ -833,15 +714,9 @@ static GtkWidget *launchtaskbar_constructor(LXPanel *panel, config_setting_t *se
 
 static void launchtaskbar_destructor_launch(LaunchTaskBarPlugin *ltbp)
 {
-    /* Free the launchbar. */
-    g_slist_foreach(ltbp->buttons, (GFunc) launchbutton_free, NULL);
-
-    /* Free the bootstrap button if it exists. */
-    if(ltbp->bootstrap_button != NULL)
-    {
-        launchbutton_free(ltbp->bootstrap_button);
-        ltbp->bootstrap_button = NULL;
-    }
+    if (ltbp->dd)
+        g_object_unref(ltbp->dd);
+    /* do not disconnect handler on child widget - it is already destroyed */
 }
 
 static void launchtaskbar_destructor_task(LaunchTaskBarPlugin *ltbp)
@@ -861,8 +736,8 @@ static void launchtaskbar_destructor_task(LaunchTaskBarPlugin *ltbp)
     /* Stop blinking timeout */
     reset_timer_on_task(ltbp);
 #ifndef DISABLE_MENU
-    if (ltbp->fi)
-        fm_file_info_unref(ltbp->fi);
+    if (ltbp->path)
+        fm_path_unref(ltbp->path);
 #endif
 }
 
@@ -885,32 +760,52 @@ static void launchtaskbar_destructor(gpointer user_data)
     g_free(ltbp);
 }
 
+static void launchbar_remove_bootstrap(LaunchTaskBarPlugin *ltbp)
+{
+    GList *btns = gtk_container_get_children(GTK_CONTAINER(ltbp->lb_icon_grid));
+    GList *l;
+
+    for (l = btns; l; l = l->next)
+        if (launch_button_get_settings(l->data) == NULL)
+        {
+            gtk_widget_destroy(l->data);
+            g_debug("launchtaskbar: removed bootstrap button %p", l->data);
+        }
+    g_list_free(btns);
+}
+
 static void _launchbar_configure_add(GtkTreeView *menu_view, LaunchTaskBarPlugin *ltbp)
 {
     GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
     FmPath * sel_path = fm_app_menu_view_dup_selected_app_desktop_path(menu_view);
     LaunchButton * btn;
 
-    if (sel_path != NULL && (btn = launchbutton_build_gui(ltbp, sel_path)) != NULL)
+    if (sel_path != NULL &&
+        (btn = launch_button_new(ltbp->panel, ltbp->plugin, sel_path, NULL)) != NULL &&
+        launch_button_wait_load(btn))
     {
         GtkListStore * list = GTK_LIST_STORE(gtk_tree_view_get_model(defined_view));
         GtkTreeIter it;
         GdkPixbuf* pix;
         char *path;
+        config_setting_t *settings;
+        gtk_container_add(GTK_CONTAINER(ltbp->lb_icon_grid), GTK_WIDGET(btn));
         gtk_list_store_append(list, &it);
-        pix = fm_pixbuf_from_icon(fm_file_info_get_icon(btn->fi), PANEL_ICON_SIZE);
+        pix = fm_pixbuf_from_icon(launch_button_get_icon(btn), PANEL_ICON_SIZE);
         gtk_list_store_set(list, &it,
             COL_ICON, pix,
-            COL_TITLE, fm_file_info_get_disp_name(btn->fi),
+            COL_TITLE, launch_button_get_disp_name(btn),
             COL_BTN, btn,
             -1);
         g_object_unref(pix);
         path = fm_path_to_str(sel_path);
         /* g_debug("*** path '%s'",path); */
-        btn->settings = config_group_add_subgroup(ltbp->settings, "Button");
-        config_group_set_string(btn->settings, "id", path);
+        settings = config_group_add_subgroup(ltbp->settings, "Button");
+        config_group_set_string(settings, "id", path);
+        launch_button_set_settings(btn, settings);
         g_free(path);
         fm_path_unref(sel_path);
+        launchbar_remove_bootstrap(ltbp);
     }
 }
 
@@ -924,13 +819,20 @@ static void launchbar_configure_add_button(GtkButton * widget, LaunchTaskBarPlug
 
 static void  launchbar_remove_button(LaunchTaskBarPlugin *ltbp, LaunchButton *btn)
 {
-    ltbp->buttons = g_slist_remove(ltbp->buttons, btn);
-    gtk_widget_destroy(btn->widget);
-    config_setting_destroy(btn->settings);
-    launchbutton_free(btn);
+    GList *list;
+
+    config_setting_destroy(launch_button_get_settings(btn));
+    gtk_widget_destroy(GTK_WIDGET(btn));
     /* Put the bootstrap button back if the list becomes empty. */
-    if(ltbp->buttons == NULL)
-        launchbutton_build_bootstrap(ltbp);
+    list = gtk_container_get_children(GTK_CONTAINER(ltbp->lb_icon_grid));
+    if (list == NULL)
+    {
+        btn = launch_button_new(ltbp->panel, ltbp->plugin, NULL, NULL);
+        gtk_container_add(GTK_CONTAINER(ltbp->lb_icon_grid), GTK_WIDGET(btn));
+        g_debug("launchtaskbar: added bootstrap button %p", btn);
+    }
+    else
+        g_list_free(list);
 }
 
 /* Handler for "clicked" action on launchtaskbar configuration dialog "Remove" button. */
@@ -973,14 +875,12 @@ static void launchbar_configure_move_up_button(GtkButton * widget, LaunchTaskBar
                 /* We have found a selected button that can be moved.
                  * Reorder it in the icon grid, the data structure, and the view. */
                 int i = gtk_tree_path_get_indices(path)[0];
-                ltbp->buttons = g_slist_remove(ltbp->buttons, btn);
-                ltbp->buttons = g_slist_insert(ltbp->buttons, btn, i);
+                config_setting_t *settings = launch_button_get_settings(btn);
                 gtk_list_store_move_before(GTK_LIST_STORE(list), &it, &it2);
                 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
-                                              btn->widget, i);
-                config_setting_move_elem(btn->settings,
-                                         config_setting_get_parent(btn->settings),
-                                         i);
+                                              GTK_WIDGET(btn), i);
+                config_setting_move_elem(settings,
+                                         config_setting_get_parent(settings), i);
             }
         }
         gtk_tree_path_free(path);
@@ -1008,14 +908,12 @@ static void launchbar_configure_move_down_button(GtkButton * widget, LaunchTaskB
                 /* We have found a selected button that can be moved.
                  * Reorder it in the icon grid, the data structure, and the view. */
                 int i = gtk_tree_path_get_indices(path)[0];
-                ltbp->buttons = g_slist_remove(ltbp->buttons, btn);
-                ltbp->buttons = g_slist_insert(ltbp->buttons, btn, i + 1);
+                config_setting_t *settings = launch_button_get_settings(btn);
                 gtk_list_store_move_after(GTK_LIST_STORE(list), &it, &it2);
                 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
-                                              btn->widget, i);
-                config_setting_move_elem(btn->settings,
-                                         config_setting_get_parent(btn->settings),
-                                         i);
+                                              GTK_WIDGET(btn), i);
+                config_setting_move_elem(settings,
+                                         config_setting_get_parent(settings), i);
             }
         }
         gtk_tree_path_free(path);
@@ -1045,21 +943,25 @@ static void launchbar_configure_initialize_list(LaunchTaskBarPlugin *ltbp, GtkWi
     GtkListStore* list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
 
     /* Initialize from defined launchtaskbar buttons. */
-    GSList* l;
-    for (l = ltbp->buttons; l != NULL; l = l->next)
+    GList *children = gtk_container_get_children(GTK_CONTAINER(ltbp->lb_icon_grid));
+    GList *l;
+    for (l = children; l != NULL; l = l->next)
     {
         LaunchButton * btn = (LaunchButton *) l->data;
         GdkPixbuf * pix;
         GtkTreeIter it;
+        if (launch_button_get_settings(btn) == NULL) /* bootstrap button */
+            continue;
         gtk_list_store_append(list, &it);
-        pix = fm_pixbuf_from_icon(fm_file_info_get_icon(btn->fi), PANEL_ICON_SIZE);
+        pix = fm_pixbuf_from_icon(launch_button_get_icon(btn), PANEL_ICON_SIZE);
         gtk_list_store_set(list, &it,
                            COL_ICON, pix,
-                           COL_TITLE, fm_file_info_get_disp_name(btn->fi),
+                           COL_TITLE, launch_button_get_disp_name(btn),
                            COL_BTN, btn,
                            -1);
         g_object_unref(pix);
     }
+    g_list_free(children);
     g_object_set_data(G_OBJECT(dlg), "defined_view", view);
 }
 
@@ -1111,7 +1013,7 @@ static void on_combobox_mode_changed(GtkComboBox *p_combobox, gpointer p_data)
     case LAUNCHBAR:
         if (ltbp->tb_icon_grid)
             gtk_widget_set_visible(ltbp->tb_icon_grid, FALSE);
-        launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
+        launchtaskbar_constructor_launch(ltbp);
         plugin_set_expand_status(ltbp, FALSE);
         gtk_widget_set_name(ltbp->plugin, "launchbar");
         break;
@@ -1124,7 +1026,7 @@ static void on_combobox_mode_changed(GtkComboBox *p_combobox, gpointer p_data)
     default:
         ltbp->mode = LAUNCHTASKBAR;
     case LAUNCHTASKBAR:
-        launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
+        launchtaskbar_constructor_launch(ltbp);
         launchtaskbar_constructor_task(ltbp);
         plugin_set_expand_status(ltbp, TRUE);
         gtk_widget_set_name(ltbp->plugin, "launchtaskbar");
@@ -1311,11 +1213,12 @@ static void on_defined_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_d
                                        &tree_iter_sel))
     {
         LaunchButton * p_btn;
+        FmFileInfo *fi;
         gtk_tree_model_get(p_treemodel, &tree_iter_sel, COL_BTN, &p_btn, -1);
-        if( (p_btn != NULL) && (p_btn->fi != NULL) )
+        if ((p_btn != NULL) && ((fi = launch_button_get_file_info(p_btn)) != NULL))
         {
             GString *p_gstring = g_string_new("");
-            g_string_printf(p_gstring, "<i>%s</i>", fm_file_info_get_disp_name(p_btn->fi));
+            g_string_printf(p_gstring, "<i>%s</i>", fm_file_info_get_disp_name(fi));
             gtk_label_set_markup(GTK_LABEL(lb->p_label_def_app_exec), p_gstring->str);
             g_string_free(p_gstring, TRUE/*free also gstring->str*/);
             label_set = TRUE;
@@ -1480,12 +1383,14 @@ static void launchtaskbar_panel_configuration_changed(LXPanel *panel, GtkWidget
     /* Set orientation into the icon grid. */
     LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
     int new_icon_size = panel_get_icon_size(panel);
+    int height = panel_get_height(panel);
+    int border = (height - new_icon_size) > 1 ? 1 : 0;
 
     if (ltbp->lb_built)
         panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->lb_icon_grid),
                                      panel_get_orientation(panel),
-                                     new_icon_size, new_icon_size, 3, 0,
-                                     panel_get_height(panel));
+                                     new_icon_size, new_icon_size,
+                                     3 - 2 * border, border, height);
 
     /* Redraw all the labels.  Icon size or font color may have changed. */
     if (ltbp->tb_built)
@@ -1610,8 +1515,8 @@ static void on_task_menu_target_set(TaskButton *btn, gulong win, LaunchTaskBarPl
 #ifndef DISABLE_MENU
             if(ltbp->mode == LAUNCHTASKBAR)
             {
-                FmFileInfo *fi = f_find_menu_launchbutton_recursive(win, ltbp);
-                LaunchButton *btn = launchbar_exec_bin_exists(ltbp, fi);
+                FmPath *path = f_find_menu_launchbutton_recursive(win, ltbp);
+                LaunchButton *btn = launchbar_exec_bin_exists(ltbp, path);
                 /* FIXME: shouldn't we make file info at task button creation? */
 #ifdef DEBUG
                 g_print("\nTB '%s' right-click, in LB: %c\n", tk->exec_bin, btn != NULL ? 'Y':'N');
@@ -1624,14 +1529,14 @@ static void on_task_menu_target_set(TaskButton *btn, gulong win, LaunchTaskBarPl
                 }
                 else
                 {
-                    gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, fi != NULL);
+                    gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, path != NULL);
                     gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, FALSE);
-                    gtk_widget_set_visible(ltbp->p_menuitem_new_instance, fi != NULL);
+                    gtk_widget_set_visible(ltbp->p_menuitem_new_instance, path != NULL);
                 }
                 gtk_widget_set_visible(ltbp->p_menuitem_separator, TRUE);
-                if (ltbp->fi)
-                    fm_file_info_unref(ltbp->fi);
-                ltbp->fi = fi;
+                if (ltbp->path)
+                    fm_path_unref(ltbp->path);
+                ltbp->path = path;
             }
             else
             {
@@ -1978,28 +1883,30 @@ static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, Laun
 #ifndef DISABLE_MENU
 static void  on_menuitem_lock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
 {
-    FmFileInfo *fi = tb->fi;
     LaunchButton *btn;
     char *path;
+    config_setting_t *settings;
 
-    if (fi)
+    if (tb->path)
     {
         /* Create a button and add settings for it */
-        fm_file_info_ref(fi);
-        btn = launchbutton_for_file_info(tb, fi);
-        path = fm_path_to_str(fm_file_info_get_path(fi));
+        path = fm_path_to_str(tb->path);
         /* g_debug("*** path '%s'",path); */
-        btn->settings = config_group_add_subgroup(tb->settings, "Button");
-        config_group_set_string(btn->settings, "id", path);
+        settings = config_group_add_subgroup(tb->settings, "Button");
+        config_group_set_string(settings, "id", path);
         g_free(path);
+        btn = launch_button_new(tb->panel, tb->plugin, tb->path, settings);
+        if (btn)
+            gtk_container_add(GTK_CONTAINER(tb->lb_icon_grid), GTK_WIDGET(btn));
+        else
+            config_setting_destroy(settings);
         lxpanel_config_save(tb->panel);
     }
 }
 
 static void  on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
 {
-    FmFileInfo *fi = tb->fi;
-    LaunchButton *btn = launchbar_exec_bin_exists(tb, fi);
+    LaunchButton *btn = launchbar_exec_bin_exists(tb, tb->path);
     if(btn != NULL)
     {
         launchbar_remove_button(tb, btn);
@@ -2009,11 +1916,9 @@ static void  on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlu
 
 static void  on_menuitem_new_instance_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
 {
-    FmFileInfo *fi = tb->fi;
-
-    if (fi)
+    if (tb->path)
     {
-        lxpanel_launch_path(tb->panel, fm_file_info_get_path(fi));
+        lxpanel_launch_path(tb->panel, tb->path);
     }
 }
 #endif
index 64cbf65..2900557 100644 (file)
@@ -177,13 +177,13 @@ static void panel_icon_grid_size_allocate(GtkWidget *widget,
                 {
                     y = border;
                     if (direction == GTK_TEXT_DIR_RTL)
-                        x -= (x_delta + ig->spacing);
+                        x -= (x_delta + ig->spacing + 2 * border);
                     else
-                        x += (x_delta + ig->spacing);
+                        x += (x_delta + ig->spacing + 2 * border);
                     x_delta = 0;
                     // FIXME: if fill_width and rows = 1 then allocate whole column
                 }
-                next_coord = y + child_height + ig->spacing;
+                next_coord = y + child_height + ig->spacing + 2 * border;
                 x_delta = MAX(x_delta, child_allocation.width);
             }
             else
@@ -198,7 +198,7 @@ static void panel_icon_grid_size_allocate(GtkWidget *widget,
                         if (next_coord < border)
                         {
                             next_coord = allocation->width - border;
-                            y += child_height + ig->spacing;
+                            y += child_height + ig->spacing + 2 * border;
                         }
                     }
                     x = next_coord;
@@ -209,9 +209,9 @@ static void panel_icon_grid_size_allocate(GtkWidget *widget,
                     if (x + child_allocation.width > allocation->width - border && x > border)
                     {
                         x = border;
-                        y += child_height + ig->spacing;
+                        y += child_height + ig->spacing + 2 * border;
                     }
-                    next_coord = x + child_allocation.width + ig->spacing;
+                    next_coord = x + child_allocation.width + ig->spacing + 2 * border;
                 }
             }
             child_allocation.x = x;
@@ -252,7 +252,7 @@ static void panel_icon_grid_size_request(GtkWidget *widget,
         /* In horizontal orientation, fit as many rows into the available height as possible.
          * Then allocate as many columns as necessary.  Guard against zerodivides. */
         if ((ig->child_height + ig->spacing) != 0)
-            ig->rows = (target_dimension + ig->spacing - border * 2) / (ig->child_height + ig->spacing);
+            ig->rows = (target_dimension + ig->spacing) / (ig->child_height + ig->spacing + border * 2);
         if (ig->rows == 0)
             ig->rows = 1;
         /* Count visible children and columns. */
@@ -270,12 +270,12 @@ static void panel_icon_grid_size_request(GtkWidget *widget,
                     row = 0;
                     if (requisition->width > 0)
                         requisition->width += ig->spacing;
-                    requisition->width += w;
+                    requisition->width += w + 2 * border;
                     row = w = 0;
                 }
             }
         if (row > 0)
-            requisition->width += w;
+            requisition->width += w + 2 * border;
         /* if ((ig->columns == 1) && (ig->rows > visible_children))
             ig->rows = visible_children; */
     }
@@ -284,7 +284,7 @@ static void panel_icon_grid_size_request(GtkWidget *widget,
         /* In vertical orientation, fit as many columns into the available width as possible.
          * Then allocate as many rows as necessary.  Guard against zerodivides. */
         if ((ig->child_width + ig->spacing) != 0)
-            ig->columns = (target_dimension + ig->spacing - border * 2) / (ig->child_width + ig->spacing);
+            ig->columns = (target_dimension + ig->spacing) / (ig->child_width + ig->spacing + border * 2);
         if (ig->columns == 0)
             ig->columns = 1;
         /* Count visible children and rows. */
@@ -300,7 +300,7 @@ static void panel_icon_grid_size_request(GtkWidget *widget,
                 }
                 if (w > 0)
                     w += ig->spacing;
-                w += child_requisition.width;
+                w += child_requisition.width + 2 * border;
                 requisition->width = MAX(requisition->width, w);
             }
         if (w > 0)
@@ -311,9 +311,7 @@ static void panel_icon_grid_size_request(GtkWidget *widget,
     if ((ig->columns == 0) || (ig->rows == 0))
         requisition->height = 0;
     else
-        requisition->height = (ig->child_height + ig->spacing) * ig->rows - ig->spacing + 2 * border;
-    if (requisition->width > 0)
-        requisition->width += 2 * border;
+        requisition->height = (ig->child_height + ig->spacing + 2 * border) * ig->rows - ig->spacing;
 
     if (ig->rows != old_rows || ig->columns != old_columns)
         gtk_widget_queue_resize(widget);
@@ -647,13 +645,13 @@ static void panel_icon_grid_queue_draw_child(PanelIconGrid * ig, GtkWidget * chi
     switch (ig->dest_pos)
     {
     case PANEL_ICON_GRID_DROP_LEFT:
-        rect.x = allocation.x - ig->spacing;
+        rect.x = allocation.x - ig->spacing - border;
         rect.width = border + ig->spacing;
         rect.y = allocation.y;
         rect.height = allocation.height;
         break;
     case PANEL_ICON_GRID_DROP_RIGHT:
-        rect.x = allocation.x + allocation.width - border;
+        rect.x = allocation.x + allocation.width;
         rect.width = border + ig->spacing;
         rect.y = allocation.y;
         rect.height = allocation.height;
@@ -661,17 +659,21 @@ static void panel_icon_grid_queue_draw_child(PanelIconGrid * ig, GtkWidget * chi
     case PANEL_ICON_GRID_DROP_BELOW:
         rect.x = allocation.x;
         rect.width = allocation.width;
-        rect.y = allocation.y + allocation.height - border;
+        rect.y = allocation.y + allocation.height;
         rect.height = border + ig->spacing;
         break;
     case PANEL_ICON_GRID_DROP_ABOVE:
         rect.x = allocation.x;
         rect.width = allocation.width;
-        rect.y = allocation.y - ig->spacing;
+        rect.y = allocation.y - ig->spacing - border;
         rect.height = border + ig->spacing;
         break;
     case PANEL_ICON_GRID_DROP_INTO:
-        rect = allocation;
+    default:
+        rect.x = allocation.x - border;
+        rect.width = allocation.width + 2 * border;
+        rect.y = allocation.y - border;
+        rect.height = allocation.height + 2 * border;
     }
 
     if (rect.width > 0 && rect.height > 0)
@@ -932,13 +934,13 @@ static gboolean panel_icon_grid_expose(GtkWidget *widget, GdkEventExpose *event)
             switch(ig->dest_pos)
             {
             case PANEL_ICON_GRID_DROP_LEFT:
-                rect.x = allocation.x - ig->spacing;
+                rect.x = allocation.x - ig->spacing - border;
                 rect.width = border + ig->spacing;
                 rect.y = allocation.y;
                 rect.height = allocation.height;
                 break;
             case PANEL_ICON_GRID_DROP_RIGHT:
-                rect.x = allocation.x + allocation.width - border;
+                rect.x = allocation.x + allocation.width;
                 rect.width = border + ig->spacing;
                 rect.y = allocation.y;
                 rect.height = allocation.height;
@@ -946,18 +948,21 @@ static gboolean panel_icon_grid_expose(GtkWidget *widget, GdkEventExpose *event)
             case PANEL_ICON_GRID_DROP_BELOW:
                 rect.x = allocation.x;
                 rect.width = allocation.width;
-                rect.y = allocation.y + allocation.height - border;
+                rect.y = allocation.y + allocation.height;
                 rect.height = border + ig->spacing;
                 break;
             case PANEL_ICON_GRID_DROP_ABOVE:
                 rect.x = allocation.x;
                 rect.width = allocation.width;
-                rect.y = allocation.y - ig->spacing;
+                rect.y = allocation.y - ig->spacing - border;
                 rect.height = border + ig->spacing;
                 break;
             case PANEL_ICON_GRID_DROP_INTO:
             default:
-                rect = allocation;
+                rect.x = allocation.x - border;
+                rect.width = allocation.width + 2 * border;
+                rect.y = allocation.y - border;
+                rect.height = allocation.height + 2 * border;
             }
 #if GTK_CHECK_VERSION(3, 0, 0)
             context = gtk_widget_get_style_context(widget);