Move plugins across panel by means of middle button drag and drop.
authorAndriy Grytsenko <andrej@rep.kiev.ua>
Mon, 17 Oct 2016 18:28:11 +0000 (21:28 +0300)
committerAndriy Grytsenko <andrej@rep.kiev.ua>
Mon, 17 Oct 2016 18:44:28 +0000 (21:44 +0300)
Middle-click it then drag, and plugin will follow the mouse until
middle button is released.

Some plugins required corrections on button press behavior, such as
release-triggering or explicit test for button == 1.

15 files changed:
ChangeLog
TODO
plugins/batt/batt.c
plugins/cpu/cpu.c
plugins/dclock.c
plugins/dirmenu.c
plugins/monitors/monitors.c
plugins/netstatus/netstatus.c
plugins/wincmd.c
plugins/xkb/xkb-plugin.c
src/Makefile.am
src/panel-plugin-move.c [new file with mode: 0644]
src/panel.c
src/private.h
src/space.c

index ddf06b8..68a4201 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -25,6 +25,9 @@
     is more obvious and sensible.
 * Added stripping scheme from URI menu://applications/... when a launcher
     is created, only menu-id itself should be used.
+* Implemented moving plugins across panel by means of middle button drag
+    and drop, i.e. middle-click it then drag, and plugin will follow the
+    mouse until middle button is released.
 
 0.8.2
 -------------------------------------------------------------------------
diff --git a/TODO b/TODO
index 82b6d8c..342eeaf 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,4 +1,3 @@
-* drag & drop plugins via middle button
 * "Move" option in plugin context menu
 * LXPanelPluginInit::activatable flag to allow activation with hotkey
 * accessibility (keys-only; single-button-mouse; special devices)
index 0dba6dc..a4bffb4 100644 (file)
@@ -460,7 +460,6 @@ static GtkWidget * constructor(LXPanel *panel, config_setting_t *settings)
 
     lx_b->box = GTK_CONTAINER(p);
     lx_b->drawingArea = gtk_drawing_area_new();
-    gtk_widget_add_events( lx_b->drawingArea, GDK_BUTTON_PRESS_MASK );
 
     gtk_container_add(lx_b->box, lx_b->drawingArea);
 
index 0d941d5..a3787f9 100644 (file)
@@ -264,10 +264,9 @@ static GtkWidget *cpu_constructor(LXPanel *panel, config_setting_t *settings)
     gtk_widget_set_has_window(p, FALSE);
     lxpanel_plugin_set_data(p, c, cpu_destructor);
 
-    /* Allocate drawing area as a child of top level widget.  Enable button press events. */
+    /* Allocate drawing area as a child of top level widget. */
     c->da = gtk_drawing_area_new();
     gtk_widget_set_size_request(c->da, 40, PANEL_HEIGHT_DEFAULT);
-    gtk_widget_add_events(c->da, GDK_BUTTON_PRESS_MASK);
     gtk_container_add(GTK_CONTAINER(p), c->da);
 
     /* Clone a graphics context and set "green" as its foreground color.
@@ -276,11 +275,11 @@ static GtkWidget *cpu_constructor(LXPanel *panel, config_setting_t *settings)
 
     /* Connect signals. */
     g_signal_connect(G_OBJECT(c->da), "configure-event", G_CALLBACK(configure_event), (gpointer) c);
-    #if !GTK_CHECK_VERSION(3, 0, 0)
+#if !GTK_CHECK_VERSION(3, 0, 0)
     g_signal_connect(G_OBJECT(c->da), "expose-event", G_CALLBACK(expose_event), (gpointer) c);
-    #else
+#else
     g_signal_connect(G_OBJECT(c->da), "draw", G_CALLBACK(draw), (gpointer) c);
-    #endif
+#endif
 
     /* Show the widget.  Connect a timer to refresh the statistics. */
     gtk_widget_show(c->da);
index 57bf5b7..7045b94 100644 (file)
@@ -8,7 +8,7 @@
  *               2012 Michael Rawson <michaelrawson76@gmail.com>
  *               2012-2014 Henry Gebhardt <hsggebhardt@gmail.com>
  *               2012 Piotr Sipika <Piotr.Sipika@gmail.com>
- *               2014-2015 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
  * This file is a part of LXPanel project.
  *
@@ -114,9 +114,13 @@ static GtkWidget * dclock_create_calendar(DClockPlugin * dc)
 /* Handler for "button-press-event" event from main widget. */
 static gboolean dclock_button_press_event(GtkWidget * widget, GdkEventButton * evt, LXPanel * panel)
 {
-    DClockPlugin * dc = lxpanel_plugin_get_data(widget);
+    DClockPlugin * dc;
+
+    if (evt->button != 1)
+        return FALSE;
 
     /* If an action is set, execute it. */
+    dc = lxpanel_plugin_get_data(widget);
     if (dc->action != NULL)
         fm_launch_command_simple(NULL, NULL, 0, dc->action, NULL);
 
index 14ac92c..4e48cde 100644 (file)
@@ -5,7 +5,7 @@
  *               2009-2010 Marty Jack <martyj19@comcast.net>
  *               2010 Julien Lavergne <julien.lavergne@gmail.com>
  *               2013 Henry Gebhardt <hsggebhardt@gmail.com>
- *               2014 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
  * This file is a part of LXPanel project.
  *
@@ -250,12 +250,19 @@ static gboolean dirmenu_button_press_event(GtkWidget * widget, GdkEventButton *
     if (event->button == 1)
     {
         dirmenu_show_menu(widget, dm, event->button, event->time);
+        return TRUE;
     }
-    else
+    return FALSE;
+}
+
+static gboolean dirmenu_button_release_event(GtkWidget * widget, GdkEventButton * event, DirMenuPlugin * dm)
+{
+    if (event->button == 2)
     {
         fm_terminal_launch(dm->path, NULL);
+        return TRUE;
     }
-    return TRUE;
+    return FALSE;
 }
 
 /* Plugin constructor. */
@@ -291,6 +298,9 @@ static GtkWidget *dirmenu_constructor(LXPanel *panel, config_setting_t *settings
     /* Initialize the widget. */
     dirmenu_apply_configuration(p);
 
+    g_signal_connect(G_OBJECT(p), "button-release-event",
+                     G_CALLBACK(dirmenu_button_release_event), dm);
+
     /* Show the widget and return. */
     return p;
 }
index 403930b..b903bc8 100644 (file)
@@ -9,7 +9,7 @@
  * Copyright (C) 2010 Cyril Roelandt <steap@users.sourceforge.net>
  *               2012-2014 Henry Gebhardt <hsggebhardt@googlemail.com>
  *               2012 Rafał Mużyło <galtgendo@gmail.com>
- *               2014 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *               2015 Rafał Mużyło <galtgendo@gmail.com>
  *
  * <terms>
@@ -172,7 +172,6 @@ monitor_init(MonitorsPlugin *mp, Monitor *m, gchar *color)
 
     m->da = gtk_drawing_area_new();
     gtk_widget_set_size_request(m->da, DEFAULT_WIDTH, panel_get_height(mp->panel));
-    gtk_widget_add_events(m->da, GDK_BUTTON_PRESS_MASK);
 
     monitor_set_foreground_color(mp, m, color);
 
@@ -186,8 +185,6 @@ monitor_init(MonitorsPlugin *mp, Monitor *m, gchar *color)
     g_signal_connect (G_OBJECT(m->da), "draw",
         G_CALLBACK(draw), (gpointer) m);
 #endif
-    /* g_signal_connect(G_OBJECT(m->da), "button-press-event",
-                    G_CALLBACK(plugin_button_press_event), p); */
 
     return m;
 }
@@ -515,8 +512,12 @@ draw(GtkWidget * widget, cairo_t * cr, Monitor *m)
 
 static gboolean monitors_button_press_event(GtkWidget* widget, GdkEventButton* evt, LXPanel *panel)
 {
-    MonitorsPlugin* mp = lxpanel_plugin_get_data(widget);
+    MonitorsPlugin* mp;
 
+    if (evt->button != 1)
+        return FALSE;
+
+    mp = lxpanel_plugin_get_data(widget);
     if (mp->action != NULL)
         fm_launch_command_simple(NULL, NULL, 0, mp->action, NULL);
     else
index f7bfde9..0f9c81d 100644 (file)
@@ -128,7 +128,6 @@ netstatus_constructor(LXPanel *panel, config_setting_t *settings)
     p = netstatus_icon_new( iface );
     lxpanel_plugin_set_data(p, ns, netstatus_destructor);
     netstatus_icon_set_show_signal((NetstatusIcon *)p, TRUE);
-    gtk_widget_add_events( p, GDK_BUTTON_PRESS_MASK );
     g_object_unref( iface );
 
     RET(p);
index 7cfd29c..106d0de 100644 (file)
@@ -6,7 +6,7 @@
  *               2012 Michael Rawson <michaelrawson76@gmail.com>
  *               2012 Julien Lavergne <julien.lavergne@gmail.com>
  *               2013 Henry Gebhardt <hsggebhardt@gmail.com>
- *               2014 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
  * This file is a part of LXPanel project.
  *
@@ -141,13 +141,22 @@ static gboolean wincmd_button_clicked(GtkWidget * widget, GdkEventButton * event
         }
         else
             wincmd_execute(wc, WC_ICONIFY);
+        return TRUE;
     }
 
+    return FALSE;
+}
+
+static gboolean wincmd_button_released(GtkWidget * widget, GdkEventButton * event, WinCmdPlugin * wc)
+{
     /* Middle-click to shade. */
-    else if (event->button == 2)
+    if (event->button == 2)
+    {
         wincmd_execute(wc, WC_SHADE);
+        return TRUE;
+    }
 
-    return TRUE;
+    return FALSE;
 }
 
 /* Plugin constructor. */
@@ -196,6 +205,9 @@ static GtkWidget *wincmd_constructor(LXPanel *panel, config_setting_t *settings)
     lxpanel_plugin_set_data(p, wc, wincmd_destructor);
     gtk_widget_set_tooltip_text(p, _("Left click to iconify all windows.  Middle click to shade them."));
 
+    g_signal_connect(G_OBJECT(p), "button-release-event",
+                     G_CALLBACK(wincmd_button_released), wc);
+
     /* Show the widget and return. */
     return p;
 }
index e630ea2..5b4d4a7 100644 (file)
@@ -3,7 +3,7 @@
  *               2009-2010 Marty Jack <martyj19@comcast.net>
  *               2012-2013 Giuseppe Penone <giuspen@gmail.com>
  *               2013 Henry Gebhardt <hsggebhardt@gmail.com>
- *               2014 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
  *
  * This file is a part of LXPanel project.
  *
@@ -204,6 +204,8 @@ static gboolean on_xkb_button_scroll_event(GtkWidget * widget, GdkEventScroll *
 static gboolean on_xkb_button_press_event(GtkWidget * widget,  GdkEventButton * event, LXPanel * panel)
 {
     /* Change to next group. */
+    if (event->button != 1)
+        return FALSE;
     xkb_change_group(lxpanel_plugin_get_data(widget), 1);
     return TRUE;
 }
index f35d447..4d65ed2 100644 (file)
@@ -28,6 +28,7 @@ liblxpanel_la_SOURCES = \
        ev.c \
        icon-grid.c \
        panel.c \
+       panel-plugin-move.c \
        plugin.c \
        conf.c \
        space.c \
diff --git a/src/panel-plugin-move.c b/src/panel-plugin-move.c
new file mode 100644 (file)
index 0000000..1e7c548
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
+ *
+ * This file is a part of LXPanel project.
+ *
+ * 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 "private.h"
+#include "space.h"
+
+gboolean _lxpanel_button_release(GtkWidget *widget, GdkEventButton *event)
+{
+    Panel *p = PLUGIN_PANEL(widget)->priv;
+
+    if (event->device == p->move_device && event->button == 2 &&
+        p->move_state != PANEL_MOVE_STOP)
+    {
+        if (p->move_state == PANEL_MOVE_MOVING)
+        {
+            /* ungrab device and return back previous cursor */
+#if GTK_CHECK_VERSION(3, 0, 0)
+            gdk_device_ungrab(event->device, event->time);
+#else
+            gdk_pointer_ungrab(event->time);
+#endif
+        }
+        p->move_state = PANEL_MOVE_STOP;
+        p->move_device = NULL;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+gboolean _lxpanel_motion_notify(GtkWidget *widget, GdkEventMotion *event)
+{
+    Panel *p = PLUGIN_PANEL(widget)->priv;
+    GList *plugins, *l;
+    GtkAllocation alloc;
+    GdkGrabStatus s;
+    int x, y, old_pos, new_pos;
+    gboolean expand, rtl;
+    config_setting_t *cfg;
+    PanelPluginMoveData *in, *out;
+
+    if (event->device != p->move_device)
+        return FALSE;
+
+    if (p->move_state == PANEL_MOVE_DETECT)
+    {
+        gdk_window_get_origin(event->window, &x, &y);
+        x += event->x - p->ax;
+        y += event->y - p->ay;
+        /* check threshold, start moving */
+        if (gtk_drag_check_threshold(widget, p->move_x, p->move_y, x, y))
+        {
+            plugins = gtk_container_get_children(GTK_CONTAINER(p->box));
+            for (l = plugins; l; l = l->next)
+            {
+                gtk_widget_get_allocation(l->data, &alloc);
+                if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                {
+                    if (p->move_x >= alloc.x && p->move_x < alloc.x + alloc.width)
+                        break;
+                }
+                else
+                {
+                    if (p->move_y >= alloc.y && p->move_y < alloc.y + alloc.height)
+                        break;
+                }
+            }
+            if (l == NULL || PANEL_IS_SPACE(l->data))
+            {
+                p->move_state = PANEL_MOVE_STOP;
+                p->move_device = NULL;
+                g_list_free(plugins);
+                return TRUE;
+            }
+            /* grab pointer, use cursor "move" */
+#if GTK_CHECK_VERSION(3, 0, 0)
+            s = gdk_device_grab(event->device, gtk_widget_get_window(widget),
+                                GDK_OWNERSHIP_NONE, FALSE,
+                                GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                                gdk_cursor_new_from_name(p->display, "move"),
+                                event->time);
+#else
+            s = gdk_pointer_grab(gtk_widget_get_window(widget), FALSE,
+                                 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                                 NULL, gdk_cursor_new_from_name(p->display, "move"),
+                                 event->time);
+#endif
+            if (s == GDK_GRAB_SUCCESS)
+            {
+                p->move_state = PANEL_MOVE_MOVING;
+                /* load all drag data into panel data */
+                p->move_plugin = l->data;
+                p->move_before.space = NULL;
+                p->move_before.plugin = NULL;
+                p->move_after.space = NULL;
+                p->move_after.plugin = NULL;
+                if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                    p->move_diff = alloc.x + alloc.width / 2 - p->move_x;
+                else
+                    p->move_diff = alloc.y + alloc.height / 2 - p->move_y;
+                /* g_debug("move_diff is %d",p->move_diff); */
+                if (l->prev)
+                {
+                    GList *save = l;
+
+                    l = l->prev;
+                    if (PANEL_IS_SPACE(l->data))
+                    {
+                        p->move_before.space = l->data;
+                        gtk_container_child_get(GTK_CONTAINER(p->box), l->data,
+                                                "expand", &expand, NULL);
+                        if (expand)
+                        {
+                            gtk_widget_get_allocation(l->data, &alloc);
+                            if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                                p->move_before.space_size = alloc.width;
+                            else
+                                p->move_before.space_size = alloc.height;
+                        }
+                        else
+                            p->move_before.space_size = -1;
+                        l = l->prev;
+                    }
+                    if (l)
+                    {
+                        p->move_before.plugin = l->data;
+                        gtk_widget_get_allocation(l->data, &alloc);
+                        if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                            p->move_before.plugin_center = alloc.x + alloc.width / 2;
+                        else
+                            p->move_before.plugin_center = alloc.y + alloc.height / 2;
+                    }
+                    l = save;
+                }
+                if (l->next)
+                {
+                    l = l->next;
+                    if (PANEL_IS_SPACE(l->data))
+                    {
+                        p->move_after.space = l->data;
+                        gtk_container_child_get(GTK_CONTAINER(p->box), l->data,
+                                                "expand", &expand, NULL);
+                        if (expand)
+                        {
+                            gtk_widget_get_allocation(l->data, &alloc);
+                            if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                                p->move_after.space_size = alloc.width;
+                            else
+                                p->move_after.space_size = alloc.height;
+                        }
+                        else
+                            p->move_after.space_size = -1;
+                        l = l->next;
+                    }
+                    if (l)
+                    {
+                        p->move_after.plugin = l->data;
+                        gtk_widget_get_allocation(l->data, &alloc);
+                        if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                            p->move_after.plugin_center = alloc.x + alloc.width / 2;
+                        else
+                            p->move_after.plugin_center = alloc.y + alloc.height / 2;
+                    }
+                }
+                g_list_free(plugins);
+                return TRUE;
+            }
+            g_list_free(plugins);
+        }
+    }
+    else if (p->move_state == PANEL_MOVE_MOVING)
+    {
+        /* calculate current and new positions of moving widget center */
+        gdk_window_get_origin(event->window, &x, &y);
+        gtk_widget_get_allocation(p->move_plugin, &alloc);
+        if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+        {
+            old_pos = alloc.x + alloc.width / 2;
+            new_pos = x + event->x - p->ax + p->move_diff;
+            rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
+        }
+        else
+        {
+            old_pos = alloc.y + alloc.height / 2;
+            new_pos = y + event->y - p->ay + p->move_diff;
+            rtl = FALSE;
+        }
+        /* actually move widget, changing spaces or swapping with others */
+        if (new_pos > old_pos)
+        {
+            x = new_pos - old_pos;
+            /* cursor is right/down */
+            if (rtl)
+                goto _split_or_swap_before;
+            else
+                goto _split_or_swap_after;
+        }
+        else if (new_pos < old_pos)
+        {
+            x = old_pos - new_pos;
+            /* cursor is left/up */
+            if (rtl)
+            {
+                rtl = FALSE; /* it now means before => after */
+_split_or_swap_after:
+                in = &p->move_before;
+                out = &p->move_after;
+            }
+            else
+            {
+                rtl = TRUE; /* it now means after => before */
+_split_or_swap_before:
+                in = &p->move_after;
+                out = &p->move_before;
+            }
+            /* g_debug("moving plugin by %d", new_pos - old_pos); */
+            if (out->space)
+            {
+                /* split space and move plugin */
+                if (in->space)
+                {
+                    /* between spaces */
+                    if (out->space_size < 0)
+                    {
+                        /* fixed size, decrease out */
+                        y = _panel_space_get_size(out->space) - x;
+                        /* g_debug("next is fixed space of %d -> %d", x+y, y); */
+                        if (y >= 2)
+                        {
+                            _panel_space_resize(out->space, y);
+                            if (in->space_size > 0 &&
+                                y > in->space_size - x)
+                            {
+                                /* fixed became bigger than expanded, let swap */
+                                /* g_debug("swap 'expand' on spaces"); */
+                                gtk_container_child_set(GTK_CONTAINER(p->box),
+                                                        out->space,
+                                                        "expand", TRUE, NULL);
+                                cfg = g_object_get_qdata(G_OBJECT(out->space),
+                                                         lxpanel_plugin_qconf);
+                                config_group_set_int(cfg, "expand", 1);
+                                out->space_size = y;
+                                gtk_container_child_set(GTK_CONTAINER(p->box),
+                                                        in->space,
+                                                        "expand", FALSE, NULL);
+                                cfg = g_object_get_qdata(G_OBJECT(in->space),
+                                                         lxpanel_plugin_qconf);
+                                config_group_set_int(cfg, "expand", 0);
+                                _panel_space_resize(in->space, in->space_size + x);
+                                in->space_size = -1;
+                            }
+                            /* in->space_size < 0 will be handled below */
+                        }
+                        else
+                        {
+                            /* remove empty space plugin */
+                            /* g_debug("removing next space %p", out->space); */
+                            lxpanel_remove_plugin(p->topgwin, out->space);
+                            out->space = NULL;
+                            y += x + gtk_box_get_spacing(GTK_BOX(p->box));
+                            if (in->space_size >= 0)
+                                /* correct size of expanded */
+                                in->space_size += y;
+                            else
+                            {
+                                /* both were fixed size - correct size of which left */
+                                y += _panel_space_get_size(in->space);
+                                _panel_space_resize(in->space, y);
+                                /* g_debug("change prev size to %d", y); */
+                            }
+                            /* both spaces were handled so may return */
+                            return TRUE;
+                        }
+                    }
+                    else
+                    {
+                        if (out->space_size < x)
+                            /* expandable size exhausted */
+                            x = out->space_size;
+                            //FIXME: if in->space_size >= 0 then remove out->space
+                        out->space_size -= x;
+                    }
+                    if (in->space_size < 0)
+                    {
+                        /* fixed size, increase before */
+                        y = _panel_space_get_size(in->space) + x;
+                        _panel_space_resize(in->space, y);
+                        /* g_debug("prev is fixed space of %d -> %d", y-x, y); */
+                        if (out->space_size >= 0 &&
+                            y > out->space_size)
+                        {
+                            /* g_debug("swap 'expand' on spaces"); */
+                            /* fixed became bigger than expanded, let swap */
+                            gtk_container_child_set(GTK_CONTAINER(p->box),
+                                                    in->space,
+                                                    "expand", TRUE, NULL);
+                            cfg = g_object_get_qdata(G_OBJECT(in->space),
+                                                     lxpanel_plugin_qconf);
+                            config_group_set_int(cfg, "expand", 1);
+                            in->space_size = y;
+                            if (out->space_size >= 2)
+                            {
+                                gtk_container_child_set(GTK_CONTAINER(p->box),
+                                                        out->space,
+                                                        "expand", FALSE, NULL);
+                                cfg = g_object_get_qdata(G_OBJECT(out->space),
+                                                         lxpanel_plugin_qconf);
+                                config_group_set_int(cfg, "expand", 0);
+                                _panel_space_resize(out->space, out->space_size);
+                                out->space_size = -1;
+                            }
+                            else
+                            {
+                                /* g_debug("removing next space"); */
+                                /* remove empty space plugin */
+                                lxpanel_remove_plugin(p->topgwin, out->space);
+                                out->space = NULL;
+                                in->space_size += out->space_size;
+                                in->space_size += gtk_box_get_spacing(GTK_BOX(p->box));
+                            }
+                        }
+                    }
+                    else
+                        in->space_size += x;
+                    /* end of between spaces case */
+                }
+                else if (x >= 2)
+                {
+                    /* there is only space after */
+                    /* ensure out->space size is at least x+2 */
+                    if (out->space_size < 0)
+                    {
+                        y = _panel_space_get_size(out->space);
+                        if (y - x < 2)
+                            goto _swap_next_space;
+                        /* decrement in->space size by x */
+                        _panel_space_resize(out->space, y - x);
+                        goto _add_prev_space;
+                    }
+                    else if (out->space_size - x < 2)
+                    {
+_swap_next_space:
+                        /* too big change, just swap */
+                        gtk_container_child_get(GTK_CONTAINER(p->box),
+                                                p->move_plugin,
+                                                "position", &y, NULL);
+                        /* reposition space from next to prev */
+                        in->space = out->space;
+                        in->space_size = out->space_size;
+                        out->space = NULL;
+                        gtk_container_child_set(GTK_CONTAINER(p->box),
+                                                in->space, "position",
+                                                y, NULL);
+                        cfg = g_object_get_qdata(G_OBJECT(in->space),
+                                                 lxpanel_plugin_qconf);
+                        config_setting_move_elem(cfg, config_setting_get_parent(cfg),
+                                                 y + 1);
+                        /* g_debug("swapped with next space %p", in->space); */
+                    }
+                    else
+                    {
+                        out->space_size -= x;
+_add_prev_space:
+                        /* create PanelSpace of size x before plugin on move */
+                        gtk_container_child_get(GTK_CONTAINER(p->box),
+                                                p->move_plugin,
+                                                "position", &y, NULL);
+                        cfg = config_group_add_subgroup(config_root_setting(p->config),
+                                                        "Plugin");
+                        config_group_set_string(cfg, "type", "space");
+                        in->space_size = -1;
+                        if (rtl)
+                            /* create space after moving one */
+                            y++;
+                            /* else create space before moving one, i.e. in place */
+                        in->space = lxpanel_add_plugin(p->topgwin, "space", cfg, y);
+                        config_setting_move_elem(cfg, config_setting_get_parent(cfg),
+                                                 y + 1);
+                        if (in->space == NULL)
+                            //FIXME: is it ever possible?
+                            config_setting_destroy(cfg);
+                        else
+                            _panel_space_resize(in->space, x);
+                        /* g_debug("added space %d before plugin", x); */
+                    }
+                }
+            }
+            else if (out->plugin)
+            {
+                /* no space after, check if need swap */
+                if (new_pos < old_pos)
+                {
+                    /* going down */
+                    if (new_pos < out->plugin_center)
+                    {
+                        /* need swap */
+                        gtk_widget_get_allocation(p->move_plugin, &alloc);
+                        if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                            x = alloc.width;
+                        else
+                            x = alloc.height;
+                        x += gtk_box_get_spacing(GTK_BOX(p->box));
+                        goto _swap_next;
+                    }
+                }
+                else if (new_pos > out->plugin_center)
+                {
+                    /* going up, need swap */
+                    gtk_widget_get_allocation(p->move_plugin, &alloc);
+                    if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                        x = -alloc.width;
+                    else
+                        x = -alloc.height;
+                    x -= gtk_box_get_spacing(GTK_BOX(p->box));
+_swap_next:
+                    /* swap with next plugin and reload all data */
+                    plugins = gtk_container_get_children(GTK_CONTAINER(p->box));
+                    l = g_list_find(plugins, p->move_plugin);
+                    y = g_list_position(plugins, l);
+                    if (rtl)
+                    {
+                        /* going down in list */
+                        g_assert(l && l->prev);
+                        l = l->prev->prev;
+                    }
+                    else
+                    {
+                        /* going up in list */
+                        g_assert(l && l->next);
+                        l = l->next->next;
+                    }
+                    /* g_debug("swapping with next plugin %p", out->plugin); */
+                    in->space = NULL;
+                    in->plugin = out->plugin;
+                    in->plugin_center = out->plugin_center + x;
+                    /* swap next plugin with one being moved */
+                    gtk_container_child_set(GTK_CONTAINER(p->box),
+                                            in->plugin, "position", y, NULL);
+                    cfg = g_object_get_qdata(G_OBJECT(in->plugin),
+                                             lxpanel_plugin_qconf);
+                    config_setting_move_elem(cfg, config_setting_get_parent(cfg),
+                                             y + 1);
+                    /* check and set out->plugin and out->space */
+                    if (l && PANEL_IS_SPACE(l->data))
+                    {
+                        out->space = l->data;
+                        gtk_container_child_get(GTK_CONTAINER(p->box), l->data,
+                                                "expand", &expand, NULL);
+                        if (expand)
+                        {
+                            gtk_widget_get_allocation(l->data, &alloc);
+                            if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                                out->space_size = alloc.width;
+                            else
+                                out->space_size = alloc.height;
+                        }
+                        else
+                            out->space_size = -1;
+                        if (rtl)
+                            l = l->prev;
+                        else
+                            l = l->next;
+                    }
+                    else
+                        out->space = NULL;
+                    if (l)
+                    {
+                        out->plugin = l->data;
+                        gtk_widget_get_allocation(l->data, &alloc);
+                        if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
+                            out->plugin_center = alloc.x + alloc.width / 2;
+                        else
+                            out->plugin_center = alloc.y + alloc.height / 2;
+                    }
+                    else
+                        out->plugin = NULL;
+                    g_list_free(plugins);
+                }
+            }
+        }
+        return TRUE;
+    }
+    return FALSE;
+}
index 5530222..295fed2 100644 (file)
@@ -357,12 +357,32 @@ static gboolean lxpanel_map_event(GtkWidget *widget, GdkEventAny *event)
 /* Handler for "button_press_event" signal with Panel as parameter. */
 static gboolean lxpanel_button_press(GtkWidget *widget, GdkEventButton *event)
 {
+    LXPanel *panel = PLUGIN_PANEL(widget);
+
+    if ((event->state & gtk_accelerator_get_default_mod_mask()) != 0)
+        /* ignore clicks with modifiers */
+        return FALSE;
+
     if (event->button == 3) /* right button */
     {
-        GtkMenu* popup = (GtkMenu*) lxpanel_get_plugin_menu(LXPANEL(widget), NULL, FALSE);
+        GtkMenu* popup = (GtkMenu*) lxpanel_get_plugin_menu(panel, NULL, FALSE);
         gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
         return TRUE;
     }
+    else if (event->button == 2) /* middle button */
+    {
+        Panel *p = panel->priv;
+        if (p->move_state == PANEL_MOVE_STOP)
+        {
+            gdk_window_get_origin(event->window, &p->move_x, &p->move_y);
+            p->move_x += event->x - p->ax;
+            p->move_y += event->y - p->ay;
+            p->move_state = PANEL_MOVE_DETECT;
+            p->move_device = event->device;
+            /* rest of work see in panel-plugin-move.c file */
+            return TRUE;
+        }
+    }
     return FALSE;
 }
 
@@ -392,6 +412,8 @@ static void lxpanel_class_init(PanelToplevelClass *klass)
     widget_class->style_set = lxpanel_style_set;
     widget_class->map_event = lxpanel_map_event;
     widget_class->button_press_event = lxpanel_button_press;
+    widget_class->button_release_event = _lxpanel_button_release;
+    widget_class->motion_notify_event = _lxpanel_motion_notify;
 
     signals[ICON_SIZE_CHANGED] =
         g_signal_new("icon-size-changed",
@@ -931,6 +953,10 @@ mouse_watch(LXPanel *panel)
     cw = p->cw;
     ch = p->ch;
 
+    if (p->move_state != PANEL_MOVE_STOP)
+        /* prevent autohide when dragging is on */
+        return TRUE;
+
     if (cw == 1) cw = 0;
     if (ch == 1) ch = 0;
     /* reduce area which will raise panel so it does not interfere with apps */
index 6d9137e..50c5614 100644 (file)
@@ -55,6 +55,19 @@ enum { HEIGHT_NONE, HEIGHT_PIXEL, HEIGHT_REQUEST };
 #define PANEL_HEIGHT_MIN              16       /* Minimum height of panel */
 #define PANEL_ICON_HIGHLIGHT          0x202020 /* Constant to pass to icon loader */
 
+typedef enum {
+    PANEL_MOVE_STOP, /* initial state */
+    PANEL_MOVE_DETECT, /* button pressed, detect drag */
+    PANEL_MOVE_MOVING /* moving the plugin */
+} PanelPluginMoveState;
+
+typedef struct {
+    int space_size;         /* size of space plugin if expandable */
+    int plugin_center;      /* position of center of prev no-space plugin */
+    GtkWidget * space;
+    GtkWidget * plugin;
+} PanelPluginMoveData;
+
 /* to check if we are in LXDE */
 extern gboolean is_in_lxde;
 
@@ -75,7 +88,7 @@ struct _Panel {
 
     GtkRequisition requisition;
     GtkWidget *(*my_box_new) (gboolean, gint);
-    GtkWidget *(*my_separator_new) ();
+    GtkWidget *(*my_separator_new) (void);
 
     void *bg; /* unused since 0.8.0 */
     int alpha;
@@ -149,6 +162,14 @@ struct _Panel {
     //gint dyn_space;                     /* Space for expandable plugins */
     //guint calculate_size_idle;          /* The idle handler for dyn_space calc */
     cairo_surface_t *surface;           /* Panel background */
+
+    PanelPluginMoveState move_state;    /* Plugin movement (drag&drop) support */
+    int move_x, move_y;
+    int move_diff;
+    GdkDevice * move_device;
+    GtkWidget * move_plugin;            /* widgets involved in movement */
+    PanelPluginMoveData move_before;
+    PanelPluginMoveData move_after;
 };
 
 typedef struct {
@@ -243,6 +264,10 @@ void restart(void);
 void logout(void);
 void gtk_run(void);
 
+/* two huge callbacks used for plugins movement within panel */
+gboolean _lxpanel_button_release(GtkWidget *widget, GdkEventButton *event);
+gboolean _lxpanel_motion_notify(GtkWidget *widget, GdkEventMotion *event);
+
 
 /* -----------------------------------------------------------------------------
  *   Deprecated declarations. Kept for compatibility with old code plugins.
index 3e0aaa7..447fdaf 100644 (file)
@@ -143,11 +143,13 @@ static gboolean panel_space_make_launcher(GtkWidget *widget, gint x, gint y, con
     /* g_debug("making launcher at %d on PanelSpace of size %d", x, size); */
     if (x <= icon_size/2 + 4) //leave launchbar at idx (before PS), size -= icon_size+3
     {
+        lxpanel_config_save(panel);
     }
     else if (x >= size - icon_size/2 - 4) //move launchbar to idx+1 (after PS), size -= icon_size+3
     {
         gtk_box_reorder_child(GTK_BOX(panel_box), launchbar, idx + 1);
         config_setting_move_elem(cfg, config_setting_get_parent(cfg), idx + 2);
+        lxpanel_config_save(panel);
     }
     else if (expand && x < size/2) //create another PS at idx of size pos-icon_size/2-2, shifting launchbar
     {
@@ -372,6 +374,7 @@ void _panel_space_resize(GtkWidget *spacer, gint size)
 
     p->size = MAX(0, size);
     space_apply_configuration(p);
+    lxpanel_config_save(PLUGIN_PANEL(spacer));
 }
 
 gint _panel_space_get_size(GtkWidget *spacer)