Adding upstream version 0.9.0.
[debian/lxpanel.git] / src / panel-plugin-move.c
1 /*
2 * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
3 *
4 * This file is a part of LXPanel project.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include "private.h"
26 #include "space.h"
27
28 gboolean _lxpanel_button_release(GtkWidget *widget, GdkEventButton *event)
29 {
30 Panel *p = PLUGIN_PANEL(widget)->priv;
31
32 if (event->device == p->move_device && event->button == 2 &&
33 p->move_state != PANEL_MOVE_STOP)
34 {
35 if (p->move_state == PANEL_MOVE_MOVING)
36 {
37 /* ungrab device and return back previous cursor */
38 #if GTK_CHECK_VERSION(3, 0, 0)
39 gdk_device_ungrab(event->device, event->time);
40 #else
41 gdk_pointer_ungrab(event->time);
42 #endif
43 }
44 p->move_state = PANEL_MOVE_STOP;
45 p->move_device = NULL;
46 return TRUE;
47 }
48 return FALSE;
49 }
50
51 gboolean _lxpanel_motion_notify(GtkWidget *widget, GdkEventMotion *event)
52 {
53 Panel *p = PLUGIN_PANEL(widget)->priv;
54 GList *plugins, *l;
55 GtkAllocation alloc;
56 GdkGrabStatus s;
57 int x, y, old_pos, new_pos;
58 gboolean expand, rtl;
59 config_setting_t *cfg;
60 PanelPluginMoveData *in, *out;
61
62 if (event->device != p->move_device)
63 return FALSE;
64
65 if (p->move_state == PANEL_MOVE_DETECT)
66 {
67 gdk_window_get_origin(event->window, &x, &y);
68 x += event->x - p->ax;
69 y += event->y - p->ay;
70 /* check threshold, start moving */
71 if (gtk_drag_check_threshold(widget, p->move_x, p->move_y, x, y))
72 {
73 plugins = gtk_container_get_children(GTK_CONTAINER(p->box));
74 for (l = plugins; l; l = l->next)
75 {
76 gtk_widget_get_allocation(l->data, &alloc);
77 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
78 {
79 if (p->move_x >= alloc.x && p->move_x < alloc.x + alloc.width)
80 break;
81 }
82 else
83 {
84 if (p->move_y >= alloc.y && p->move_y < alloc.y + alloc.height)
85 break;
86 }
87 }
88 if (l == NULL || PANEL_IS_SPACE(l->data))
89 {
90 p->move_state = PANEL_MOVE_STOP;
91 p->move_device = NULL;
92 g_list_free(plugins);
93 return TRUE;
94 }
95 /* grab pointer, use cursor "move" */
96 #if GTK_CHECK_VERSION(3, 0, 0)
97 s = gdk_device_grab(event->device, gtk_widget_get_window(widget),
98 GDK_OWNERSHIP_NONE, FALSE,
99 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
100 gdk_cursor_new_from_name(p->display, "move"),
101 event->time);
102 #else
103 s = gdk_pointer_grab(gtk_widget_get_window(widget), FALSE,
104 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
105 NULL, gdk_cursor_new_from_name(p->display, "move"),
106 event->time);
107 #endif
108 if (s == GDK_GRAB_SUCCESS)
109 {
110 p->move_state = PANEL_MOVE_MOVING;
111 /* load all drag data into panel data */
112 p->move_plugin = l->data;
113 p->move_before.space = NULL;
114 p->move_before.plugin = NULL;
115 p->move_after.space = NULL;
116 p->move_after.plugin = NULL;
117 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
118 p->move_diff = alloc.x + alloc.width / 2 - p->move_x;
119 else
120 p->move_diff = alloc.y + alloc.height / 2 - p->move_y;
121 /* g_debug("move_diff is %d",p->move_diff); */
122 if (l->prev)
123 {
124 GList *save = l;
125
126 l = l->prev;
127 if (PANEL_IS_SPACE(l->data))
128 {
129 p->move_before.space = l->data;
130 gtk_container_child_get(GTK_CONTAINER(p->box), l->data,
131 "expand", &expand, NULL);
132 if (expand)
133 {
134 gtk_widget_get_allocation(l->data, &alloc);
135 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
136 p->move_before.space_size = alloc.width;
137 else
138 p->move_before.space_size = alloc.height;
139 }
140 else
141 p->move_before.space_size = -1;
142 l = l->prev;
143 }
144 if (l)
145 {
146 p->move_before.plugin = l->data;
147 gtk_widget_get_allocation(l->data, &alloc);
148 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
149 p->move_before.plugin_center = alloc.x + alloc.width / 2;
150 else
151 p->move_before.plugin_center = alloc.y + alloc.height / 2;
152 }
153 l = save;
154 }
155 if (l->next)
156 {
157 l = l->next;
158 if (PANEL_IS_SPACE(l->data))
159 {
160 p->move_after.space = l->data;
161 gtk_container_child_get(GTK_CONTAINER(p->box), l->data,
162 "expand", &expand, NULL);
163 if (expand)
164 {
165 gtk_widget_get_allocation(l->data, &alloc);
166 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
167 p->move_after.space_size = alloc.width;
168 else
169 p->move_after.space_size = alloc.height;
170 }
171 else
172 p->move_after.space_size = -1;
173 l = l->next;
174 }
175 if (l)
176 {
177 p->move_after.plugin = l->data;
178 gtk_widget_get_allocation(l->data, &alloc);
179 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
180 p->move_after.plugin_center = alloc.x + alloc.width / 2;
181 else
182 p->move_after.plugin_center = alloc.y + alloc.height / 2;
183 }
184 }
185 g_list_free(plugins);
186 return TRUE;
187 }
188 g_list_free(plugins);
189 }
190 }
191 else if (p->move_state == PANEL_MOVE_MOVING)
192 {
193 /* calculate current and new positions of moving widget center */
194 gdk_window_get_origin(event->window, &x, &y);
195 gtk_widget_get_allocation(p->move_plugin, &alloc);
196 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
197 {
198 old_pos = alloc.x + alloc.width / 2;
199 new_pos = x + event->x - p->ax + p->move_diff;
200 rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
201 }
202 else
203 {
204 old_pos = alloc.y + alloc.height / 2;
205 new_pos = y + event->y - p->ay + p->move_diff;
206 rtl = FALSE;
207 }
208 /* actually move widget, changing spaces or swapping with others */
209 if (new_pos > old_pos)
210 {
211 x = new_pos - old_pos;
212 /* cursor is right/down */
213 if (rtl)
214 goto _split_or_swap_before;
215 else
216 goto _split_or_swap_after;
217 }
218 else if (new_pos < old_pos)
219 {
220 x = old_pos - new_pos;
221 /* cursor is left/up */
222 if (rtl)
223 {
224 rtl = FALSE; /* it now means before => after */
225 _split_or_swap_after:
226 in = &p->move_before;
227 out = &p->move_after;
228 }
229 else
230 {
231 rtl = TRUE; /* it now means after => before */
232 _split_or_swap_before:
233 in = &p->move_after;
234 out = &p->move_before;
235 }
236 /* g_debug("moving plugin by %d", new_pos - old_pos); */
237 if (out->space)
238 {
239 /* split space and move plugin */
240 if (in->space)
241 {
242 /* between spaces */
243 if (out->space_size < 0)
244 {
245 /* fixed size, decrease out */
246 y = _panel_space_get_size(out->space) - x;
247 /* g_debug("next is fixed space of %d -> %d", x+y, y); */
248 if (y >= 2)
249 {
250 _panel_space_resize(out->space, y);
251 if (in->space_size > 0 &&
252 y > in->space_size - x)
253 {
254 /* fixed became bigger than expanded, let swap */
255 /* g_debug("swap 'expand' on spaces"); */
256 gtk_container_child_set(GTK_CONTAINER(p->box),
257 out->space,
258 "expand", TRUE, NULL);
259 cfg = g_object_get_qdata(G_OBJECT(out->space),
260 lxpanel_plugin_qconf);
261 config_group_set_int(cfg, "expand", 1);
262 out->space_size = y;
263 gtk_container_child_set(GTK_CONTAINER(p->box),
264 in->space,
265 "expand", FALSE, NULL);
266 cfg = g_object_get_qdata(G_OBJECT(in->space),
267 lxpanel_plugin_qconf);
268 config_group_set_int(cfg, "expand", 0);
269 _panel_space_resize(in->space, in->space_size + x);
270 in->space_size = -1;
271 }
272 /* in->space_size < 0 will be handled below */
273 }
274 else
275 {
276 /* remove empty space plugin */
277 /* g_debug("removing next space %p", out->space); */
278 lxpanel_remove_plugin(p->topgwin, out->space);
279 out->space = NULL;
280 y += x + gtk_box_get_spacing(GTK_BOX(p->box));
281 if (in->space_size >= 0)
282 /* correct size of expanded */
283 in->space_size += y;
284 else
285 {
286 /* both were fixed size - correct size of which left */
287 y += _panel_space_get_size(in->space);
288 _panel_space_resize(in->space, y);
289 /* g_debug("change prev size to %d", y); */
290 }
291 /* both spaces were handled so may return */
292 return TRUE;
293 }
294 }
295 else
296 {
297 if (out->space_size < x)
298 /* expandable size exhausted */
299 x = out->space_size;
300 //FIXME: if in->space_size >= 0 then remove out->space
301 out->space_size -= x;
302 }
303 if (in->space_size < 0)
304 {
305 /* fixed size, increase before */
306 y = _panel_space_get_size(in->space) + x;
307 _panel_space_resize(in->space, y);
308 /* g_debug("prev is fixed space of %d -> %d", y-x, y); */
309 if (out->space_size >= 0 &&
310 y > out->space_size)
311 {
312 /* g_debug("swap 'expand' on spaces"); */
313 /* fixed became bigger than expanded, let swap */
314 gtk_container_child_set(GTK_CONTAINER(p->box),
315 in->space,
316 "expand", TRUE, NULL);
317 cfg = g_object_get_qdata(G_OBJECT(in->space),
318 lxpanel_plugin_qconf);
319 config_group_set_int(cfg, "expand", 1);
320 in->space_size = y;
321 if (out->space_size >= 2)
322 {
323 gtk_container_child_set(GTK_CONTAINER(p->box),
324 out->space,
325 "expand", FALSE, NULL);
326 cfg = g_object_get_qdata(G_OBJECT(out->space),
327 lxpanel_plugin_qconf);
328 config_group_set_int(cfg, "expand", 0);
329 _panel_space_resize(out->space, out->space_size);
330 out->space_size = -1;
331 }
332 else
333 {
334 /* g_debug("removing next space"); */
335 /* remove empty space plugin */
336 lxpanel_remove_plugin(p->topgwin, out->space);
337 out->space = NULL;
338 in->space_size += out->space_size;
339 in->space_size += gtk_box_get_spacing(GTK_BOX(p->box));
340 }
341 }
342 }
343 else
344 in->space_size += x;
345 /* end of between spaces case */
346 }
347 else if (x >= 2)
348 {
349 /* there is only space after */
350 /* ensure out->space size is at least x+2 */
351 if (out->space_size < 0)
352 {
353 y = _panel_space_get_size(out->space);
354 if (y - x < 2)
355 goto _swap_next_space;
356 /* decrement in->space size by x */
357 _panel_space_resize(out->space, y - x);
358 goto _add_prev_space;
359 }
360 else if (out->space_size - x < 2)
361 {
362 _swap_next_space:
363 /* too big change, just swap */
364 gtk_container_child_get(GTK_CONTAINER(p->box),
365 p->move_plugin,
366 "position", &y, NULL);
367 /* reposition space from next to prev */
368 in->space = out->space;
369 in->space_size = out->space_size;
370 out->space = NULL;
371 gtk_container_child_set(GTK_CONTAINER(p->box),
372 in->space, "position",
373 y, NULL);
374 cfg = g_object_get_qdata(G_OBJECT(in->space),
375 lxpanel_plugin_qconf);
376 config_setting_move_elem(cfg, config_setting_get_parent(cfg),
377 y + 1);
378 /* g_debug("swapped with next space %p", in->space); */
379 }
380 else
381 {
382 out->space_size -= x;
383 _add_prev_space:
384 /* create PanelSpace of size x before plugin on move */
385 gtk_container_child_get(GTK_CONTAINER(p->box),
386 p->move_plugin,
387 "position", &y, NULL);
388 cfg = config_group_add_subgroup(config_root_setting(p->config),
389 "Plugin");
390 config_group_set_string(cfg, "type", "space");
391 in->space_size = -1;
392 if (rtl)
393 /* create space after moving one */
394 y++;
395 /* else create space before moving one, i.e. in place */
396 in->space = lxpanel_add_plugin(p->topgwin, "space", cfg, y);
397 config_setting_move_elem(cfg, config_setting_get_parent(cfg),
398 y + 1);
399 if (in->space == NULL)
400 //FIXME: is it ever possible?
401 config_setting_destroy(cfg);
402 else
403 _panel_space_resize(in->space, x);
404 /* g_debug("added space %d before plugin", x); */
405 }
406 }
407 }
408 else if (out->plugin)
409 {
410 /* no space after, check if need swap */
411 if (new_pos < old_pos)
412 {
413 /* going down */
414 if (new_pos < out->plugin_center)
415 {
416 /* need swap */
417 gtk_widget_get_allocation(p->move_plugin, &alloc);
418 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
419 x = alloc.width;
420 else
421 x = alloc.height;
422 x += gtk_box_get_spacing(GTK_BOX(p->box));
423 goto _swap_next;
424 }
425 }
426 else if (new_pos > out->plugin_center)
427 {
428 /* going up, need swap */
429 gtk_widget_get_allocation(p->move_plugin, &alloc);
430 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
431 x = -alloc.width;
432 else
433 x = -alloc.height;
434 x -= gtk_box_get_spacing(GTK_BOX(p->box));
435 _swap_next:
436 /* swap with next plugin and reload all data */
437 plugins = gtk_container_get_children(GTK_CONTAINER(p->box));
438 l = g_list_find(plugins, p->move_plugin);
439 y = g_list_position(plugins, l);
440 if (rtl)
441 {
442 /* going down in list */
443 g_assert(l && l->prev);
444 l = l->prev->prev;
445 }
446 else
447 {
448 /* going up in list */
449 g_assert(l && l->next);
450 l = l->next->next;
451 }
452 /* g_debug("swapping with next plugin %p", out->plugin); */
453 in->space = NULL;
454 in->plugin = out->plugin;
455 in->plugin_center = out->plugin_center + x;
456 /* swap next plugin with one being moved */
457 gtk_container_child_set(GTK_CONTAINER(p->box),
458 in->plugin, "position", y, NULL);
459 cfg = g_object_get_qdata(G_OBJECT(in->plugin),
460 lxpanel_plugin_qconf);
461 config_setting_move_elem(cfg, config_setting_get_parent(cfg),
462 y + 1);
463 /* check and set out->plugin and out->space */
464 if (l && PANEL_IS_SPACE(l->data))
465 {
466 out->space = l->data;
467 gtk_container_child_get(GTK_CONTAINER(p->box), l->data,
468 "expand", &expand, NULL);
469 if (expand)
470 {
471 gtk_widget_get_allocation(l->data, &alloc);
472 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
473 out->space_size = alloc.width;
474 else
475 out->space_size = alloc.height;
476 }
477 else
478 out->space_size = -1;
479 if (rtl)
480 l = l->prev;
481 else
482 l = l->next;
483 }
484 else
485 out->space = NULL;
486 if (l)
487 {
488 out->plugin = l->data;
489 gtk_widget_get_allocation(l->data, &alloc);
490 if (p->orientation == GTK_ORIENTATION_HORIZONTAL)
491 out->plugin_center = alloc.x + alloc.width / 2;
492 else
493 out->plugin_center = alloc.y + alloc.height / 2;
494 }
495 else
496 out->plugin = NULL;
497 g_list_free(plugins);
498 }
499 }
500 }
501 return TRUE;
502 }
503 return FALSE;
504 }