e: move e-tiling in e17.
authorCedric BAIL <cedric.bail@free.fr>
Wed, 2 May 2012 08:20:53 +0000 (08:20 +0000)
committerCedric BAIL <cedric.bail@free.fr>
Wed, 2 May 2012 08:20:53 +0000 (08:20 +0000)
SVN revision: 70614

22 files changed:
configure.ac
po/POTFILES.in
src/modules/Makefile.am
src/modules/tiling/AUTHORS [new file with mode: 0644]
src/modules/tiling/ChangeLog [new file with mode: 0644]
src/modules/tiling/Makefile.am [new file with mode: 0644]
src/modules/tiling/README [new file with mode: 0644]
src/modules/tiling/TODO [new file with mode: 0644]
src/modules/tiling/e-module-tiling.edc [new file with mode: 0644]
src/modules/tiling/e_mod_config.c [new file with mode: 0644]
src/modules/tiling/e_mod_tiling.c [new file with mode: 0644]
src/modules/tiling/e_mod_tiling.h [new file with mode: 0644]
src/modules/tiling/images/arrow_e.png [new file with mode: 0644]
src/modules/tiling/images/arrow_n.png [new file with mode: 0644]
src/modules/tiling/images/arrow_ne.png [new file with mode: 0644]
src/modules/tiling/images/arrow_nw.png [new file with mode: 0644]
src/modules/tiling/images/arrow_s.png [new file with mode: 0644]
src/modules/tiling/images/arrow_se.png [new file with mode: 0644]
src/modules/tiling/images/arrow_sw.png [new file with mode: 0644]
src/modules/tiling/images/arrow_w.png [new file with mode: 0644]
src/modules/tiling/images/module_icon.png [new file with mode: 0644]
src/modules/tiling/module.desktop.in [new file with mode: 0644]

index fb1995a..d19da09 100644 (file)
@@ -798,6 +798,7 @@ AC_E_OPTIONAL_MODULE([backlight], true)
 AC_E_OPTIONAL_MODULE([tasks], true)
 AC_E_OPTIONAL_MODULE([conf_randr], true)
 AC_E_OPTIONAL_MODULE([xkbswitch], true)
+AC_E_OPTIONAL_MODULE([tiling], true)
 
 SUSPEND=""
 HIBERNATE=""
@@ -954,6 +955,8 @@ src/modules/tasks/Makefile
 src/modules/tasks/module.desktop
 src/modules/xkbswitch/Makefile
 src/modules/xkbswitch/module.desktop
+src/modules/tiling/Makefile
+src/modules/tiling/module.desktop
 src/preload/Makefile
 data/Makefile
 data/images/Makefile
index 637b8d1..a0e3349 100644 (file)
@@ -250,6 +250,9 @@ src/modules/systray/e_mod_main.c
 src/modules/shot/e_mod_main.c
 src/modules/tasks/e_mod_main.c
 src/modules/tasks/e_mod_config.c
+src/modules/tiling/e_mod_config.c
+src/modules/tiling/e_mod_tiling.c
+src/modules/tiling/e_mod_tiling.h
 src/modules/xkbswitch/e_mod_main.c
 src/modules/xkbswitch/e_mod_config.c
 src/modules/xkbswitch/e_mod_parse.c
index 77a8943..83bf770 100644 (file)
@@ -195,6 +195,10 @@ if USE_MODULE_XKBSWITCH
 SUBDIRS += xkbswitch
 endif
 
+if USE_MODULE_TILING
+SUBDIRS += tiling
+endif
+
 #if HAVE_WAYLAND_DRM
 # SUBDIRS += wl_drm
 #endif
diff --git a/src/modules/tiling/AUTHORS b/src/modules/tiling/AUTHORS
new file mode 100644 (file)
index 0000000..1ff3540
--- /dev/null
@@ -0,0 +1,2 @@
+Boris Faure <billiob@gmail.com>
+Based on original Tiling module by Michael Stapelberg <michael+e17@stapelberg.de>
diff --git a/src/modules/tiling/ChangeLog b/src/modules/tiling/ChangeLog
new file mode 100644 (file)
index 0000000..1e4d6a9
--- /dev/null
@@ -0,0 +1,15 @@
+v0.9 December 11, 2011
+    * restore border and maximized state when untiling
+    * add setting to stack in rows or columns
+    * toggle between rows and columns
+    * add setting to choose key hints
+    * add setting whether to display the window title
+
+v0.5 July 17, 2011
+    * swap windows
+    * move windows
+    * change focus to identified window
+    * move transitions
+
+v0.1 May 14, 2011
+    * basic tiling
diff --git a/src/modules/tiling/Makefile.am b/src/modules/tiling/Makefile.am
new file mode 100644 (file)
index 0000000..2daa7ad
--- /dev/null
@@ -0,0 +1,38 @@
+MAINTAINERCLEANFILES = Makefile.in module.desktop
+MODULE = tiling
+
+filesdir = $(libdir)/enlightenment/modules/$(MODULE)
+files_DATA = e-module-$(MODULE).edj module.desktop
+
+EXTRA_DIST = $(files_DATA) \
+            e-module-$(MODULE).edc \
+            module.desktop.in \
+            images/module_icon.png
+
+EDJE_CC = @edje_cc@
+EDJE_FLAGS = -id $(top_srcdir)/src/modules/$(MODULE)/images \
+            @EDJE_DEF@
+
+e-module-$(MODULE).edj:  e-module-$(MODULE).edc
+       $(EDJE_CC) $(EDJE_FLAGS) $< $@
+
+# the module .so file
+INCLUDES               = -I. \
+                         -I$(top_srcdir) \
+                         -I$(top_srcdir)/src/modules/$(MODULE) \
+                         -I$(top_srcdir)/src/bin \
+                         -I$(top_builddir)/src/bin \
+                         -I$(top_srcdir)/src/modules \
+                         @e_cflags@
+pkgdir                 = $(libdir)/enlightenment/modules/$(MODULE)/$(MODULE_ARCH)
+pkg_LTLIBRARIES        = module.la
+module_la_SOURCES      = e_mod_tiling.c \
+                         e_mod_tiling.h \
+                         e_mod_config.c
+
+module_la_LIBADD       = @e_libs@ @dlopen_libs@
+module_la_LDFLAGS      = -module -avoid-version
+module_la_DEPENDENCIES = $(top_builddir)/config.h
+
+uninstall:
+       rm -rf $(DESTDIR)$(libdir)/enlightenment/modules/$(MODULE)
diff --git a/src/modules/tiling/README b/src/modules/tiling/README
new file mode 100644 (file)
index 0000000..1f65380
--- /dev/null
@@ -0,0 +1,18 @@
+A tiling module for the Enlightenment Window Manager
+
+Concept
+=======
+
+Each desktop can have from 1 to 8 (vertical) columns.
+Windows are then resized and moved to use as much as columns as configured.
+
+When there are more windows than possible columns, the remaining windows are
+packed in the last column.
+
+Resizing or moving a window will try to resize or move the column it is in.
+
+What is needed?
+===============
+
+An up-to-date enlightenment environment.
+…
diff --git a/src/modules/tiling/TODO b/src/modules/tiling/TODO
new file mode 100644 (file)
index 0000000..75465fb
--- /dev/null
@@ -0,0 +1,12 @@
+
+--- v1.0
+[ ] Respect more ICCCM
+[ ] Show how many columns are configured. (?)
+[ ] Remove remaining bugs ☺.
+
+--- v0.9
+[X] Toggle between horizontal and vertical columns
+[X] Handle correctly multiple screens
+[X] Setting whether to display the window title
+[X] Setting to choose hint keys
+[X] Save original border and restore it when untiling
diff --git a/src/modules/tiling/e-module-tiling.edc b/src/modules/tiling/e-module-tiling.edc
new file mode 100644 (file)
index 0000000..fcda181
--- /dev/null
@@ -0,0 +1,2861 @@
+images {
+   image: "module_icon.png" COMP;
+
+   image: "arrow_n.png" COMP;
+   image: "arrow_nw.png" COMP;
+   image: "arrow_w.png" COMP;
+   image: "arrow_sw.png" COMP;
+   image: "arrow_s.png" COMP;
+   image: "arrow_se.png" COMP;
+   image: "arrow_e.png" COMP;
+   image: "arrow_ne.png" COMP;
+}
+collections {
+    group {
+        name: "icon";
+        parts {
+            part {
+                name: "image";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    aspect: 1.0 1.0;
+                    aspect_preference: BOTH;
+                    image.normal: "module_icon.png";
+                }
+            }
+        }
+    }
+
+    group { name: "modules/tiling/move/left/composite"; // {{{
+        min: 101 54;
+        data {
+            item: "shaped" "1";
+        }
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 101 54;
+                    max: 101 54;
+                    state: "default" 0.0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_nw";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 77 29;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 101 54;
+                        to: "_";
+                    }
+                    image.normal: "arrow_nw.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_w";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 42 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 63 29;
+                        to: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_sw";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 29;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 24 54;
+                        to: "_";
+                    }
+                    image.normal: "arrow_sw.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_nw";
+                target: "arrow_w";
+                target: "arrow_sw";
+                after: "in1a";
+            }
+            program {
+                name: "in1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_nw";
+                after: "in2a";
+                after: "in2b";
+            }
+            program {
+                name: "in1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_sw";
+            }
+            program {
+                name: "in2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_w";
+                after: "in3a";
+                after: "in3b";
+            }
+            program {
+                name: "in2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.4;
+                target: "arrow_nw";
+            }
+            program {
+                name: "in3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_sw";
+                after: "in1a";
+                after: "in1b";
+            }
+            program {
+                name: "in3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_w";
+            }
+        }
+    } // }}}
+    group { name: "modules/tiling/move/left"; // {{{
+        min: 101 54;
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 101 54;
+                    max: 101 54;
+                    state: "default" 0.0;
+                    color: 0 0 0 255;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_nw";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 77 29;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 101 54;
+                        to: "_";
+                    }
+                    image.normal: "arrow_nw.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_w";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 42 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 63 29;
+                        to: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_sw";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 29;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 24 54;
+                        to: "_";
+                    }
+                    image.normal: "arrow_sw.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_nw";
+                target: "arrow_w";
+                target: "arrow_sw";
+                after: "in1a";
+            }
+            program {
+                name: "in1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_nw";
+                after: "in2a";
+                after: "in2b";
+            }
+            program {
+                name: "in1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_sw";
+            }
+            program {
+                name: "in2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_w";
+                after: "in3a";
+                after: "in3b";
+            }
+            program {
+                name: "in2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.4;
+                target: "arrow_nw";
+            }
+            program {
+                name: "in3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_sw";
+                after: "in1a";
+                after: "in1b";
+            }
+            program {
+                name: "in3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_w";
+            }
+        }
+    } // }}}
+
+    group { name: "modules/tiling/move/down/composite"; // {{{
+        min: 54 101;
+        data {
+            item: "shaped" "1";
+        }
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 54 101;
+                    max: 54 101;
+                    state: "default" 0.0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_sw";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 29 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 53 25;
+                        to: "_";
+                    }
+                    image.normal: "arrow_sw.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 38;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset:  29 59;
+                        to: "_";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_se";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 29 76;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 53 101;
+                        to: "_";
+                    }
+                    image.normal: "arrow_se.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_sw";
+                target: "arrow_s";
+                target: "arrow_se";
+                after: "in1a";
+            }
+            program {
+                name: "in1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_sw";
+                after: "in2a";
+                after: "in2b";
+            }
+            program {
+                name: "in1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_se";
+            }
+            program {
+                name: "in2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_s";
+                after: "in3a";
+                after: "in3b";
+            }
+            program {
+                name: "in2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.4;
+                target: "arrow_sw";
+            }
+            program {
+                name: "in3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_se";
+                after: "in1a";
+                after: "in1b";
+            }
+            program {
+                name: "in3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_s";
+            }
+        }
+    } // }}}
+    group { name: "modules/tiling/move/down"; // {{{
+        min: 54 101;
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 54 101;
+                    max: 54 101;
+                    state: "default" 0.0;
+                    color: 0 0 0 255;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_sw";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 29 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 53 25;
+                        to: "_";
+                    }
+                    image.normal: "arrow_sw.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 38;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset:  29 59;
+                        to: "_";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_se";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 29 76;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 53 101;
+                        to: "_";
+                    }
+                    image.normal: "arrow_se.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_sw";
+                target: "arrow_s";
+                target: "arrow_se";
+                after: "in1a";
+            }
+            program {
+                name: "in1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_sw";
+                after: "in2a";
+                after: "in2b";
+            }
+            program {
+                name: "in1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_se";
+            }
+            program {
+                name: "in2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_s";
+                after: "in3a";
+                after: "in3b";
+            }
+            program {
+                name: "in2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.4;
+                target: "arrow_sw";
+            }
+            program {
+                name: "in3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_se";
+                after: "in1a";
+                after: "in1b";
+            }
+            program {
+                name: "in3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_s";
+            }
+        }
+    } // }}}
+
+    group { name: "modules/tiling/move/right/composite"; // {{{
+        min: 101 54;
+        data {
+            item: "shaped" "1";
+        }
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 101 54;
+                    max: 101 54;
+                    state: "default" 0.0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_se";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 24 25;
+                        to: "_";
+                    }
+                    image.normal: "arrow_se.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 38 25;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset:  59 54;
+                        to: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_ne";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset:  77 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 101 24;
+                        to: "_";
+                    }
+                    image.normal: "arrow_ne.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_se";
+                target: "arrow_e";
+                target: "arrow_ne";
+                after: "in1a";
+            }
+            program {
+                name: "in1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_se";
+                after: "in2a";
+                after: "in2b";
+            }
+            program {
+                name: "in1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_ne";
+            }
+            program {
+                name: "in2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_e";
+                after: "in3a";
+                after: "in3b";
+            }
+            program {
+                name: "in2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.4;
+                target: "arrow_se";
+            }
+            program {
+                name: "in3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_ne";
+                after: "in1a";
+                after: "in1b";
+            }
+            program {
+                name: "in3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_e";
+            }
+        }
+    } // }}}
+    group { name: "modules/tiling/move/right"; // {{{
+        min: 101 54;
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 101 54;
+                    max: 101 54;
+                    state: "default" 0.0;
+                    color: 0 0 0 255;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_se";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 24 25;
+                        to: "_";
+                    }
+                    image.normal: "arrow_se.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 38 25;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset:  59 54;
+                        to: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_ne";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset:  77 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 101 24;
+                        to: "_";
+                    }
+                    image.normal: "arrow_ne.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_se";
+                target: "arrow_e";
+                target: "arrow_ne";
+                after: "in1a";
+            }
+            program {
+                name: "in1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_se";
+                after: "in2a";
+                after: "in2b";
+            }
+            program {
+                name: "in1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_ne";
+            }
+            program {
+                name: "in2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_e";
+                after: "in3a";
+                after: "in3b";
+            }
+            program {
+                name: "in2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.4;
+                target: "arrow_se";
+            }
+            program {
+                name: "in3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_ne";
+                after: "in1a";
+                after: "in1b";
+            }
+            program {
+                name: "in3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_e";
+            }
+        }
+    } // }}}
+
+    group { name: "modules/tiling/move/up/composite"; // {{{
+        min: 54 101;
+        data {
+            item: "shaped" "1";
+        }
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 54 101;
+                    max: 54 101;
+                    state: "default" 0.0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_ne";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 76;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 24 101;
+                        to: "_";
+                    }
+                    image.normal: "arrow_ne.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_n";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 25 38;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset:  54 59;
+                        to: "_";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_nw";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset:  0 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 24 25;
+                        to: "_";
+                    }
+                    image.normal: "arrow_nw.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_ne";
+                target: "arrow_n";
+                target: "arrow_nw";
+                after: "in1a";
+            }
+            program {
+                name: "in1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_ne";
+                after: "in2a";
+                after: "in2b";
+            }
+            program {
+                name: "in1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_nw";
+            }
+            program {
+                name: "in2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_n";
+                after: "in3a";
+                after: "in3b";
+            }
+            program {
+                name: "in2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.4;
+                target: "arrow_ne";
+            }
+            program {
+                name: "in3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_nw";
+                after: "in1a";
+                after: "in1b";
+            }
+            program {
+                name: "in3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_n";
+            }
+        }
+    } // }}}
+    group { name: "modules/tiling/move/up"; // {{{
+        min: 54 101;
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 54 101;
+                    max: 54 101;
+                    state: "default" 0.0;
+                    color: 0 0 0 255;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_ne";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 76;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 24 101;
+                        to: "_";
+                    }
+                    image.normal: "arrow_ne.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_n";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 25 38;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset:  54 59;
+                        to: "_";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_nw";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset:  0 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 0;
+                        offset: 24 25;
+                        to: "_";
+                    }
+                    image.normal: "arrow_nw.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_ne";
+                target: "arrow_n";
+                target: "arrow_nw";
+                after: "in1a";
+            }
+            program {
+                name: "in1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_ne";
+                after: "in2a";
+                after: "in2b";
+            }
+            program {
+                name: "in1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_nw";
+            }
+            program {
+                name: "in2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_n";
+                after: "in3a";
+                after: "in3b";
+            }
+            program {
+                name: "in2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.4;
+                target: "arrow_ne";
+            }
+            program {
+                name: "in3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.4;
+                target: "arrow_nw";
+                after: "in1a";
+                after: "in1b";
+            }
+            program {
+                name: "in3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.6;
+                target: "arrow_n";
+            }
+        }
+    } // }}}
+
+    group { name: "modules/tiling/transition/horizontal/composite"; // {{{
+        min: 29 221;
+        data {
+            item: "shaped" "1";
+        }
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 29 221;
+                    max: 29 221;
+                    state: "default" 0.0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_n_3";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 1 0;
+                        offset: 0 21;
+                        to: "_";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_n_2";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_n_3";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_n_3";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_n_1";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_n_2";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_n_2";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_n_0";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_n_1";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_n_1";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s_0";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 10;
+                        to_x: "_";
+                        to_y: "arrow_n_0";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 31;
+                        to_x: "_";
+                        to_y: "arrow_n_0";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s_1";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_s_0";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_s_0";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s_2";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_s_1";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_s_1";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s_3";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_s_2";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_s_2";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_n_0";
+                target: "arrow_n_1";
+                target: "arrow_n_2";
+                target: "arrow_n_3";
+                target: "arrow_s_0";
+                target: "arrow_s_1";
+                target: "arrow_s_2";
+                target: "arrow_s_3";
+                after: "n0a";
+                after: "s0a";
+            }
+            program {
+                name: "n0a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_n_0";
+                after: "n1a";
+                after: "n1b";
+            }
+            program {
+                name: "n0b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_n_3";
+            }
+            program {
+                name: "n1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_n_1";
+                after: "n2a";
+                after: "n2b";
+            }
+            program {
+                name: "n1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_n_0";
+            }
+            program {
+                name: "n2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_n_2";
+                after: "n3a";
+                after: "n3b";
+            }
+            program {
+                name: "n2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_n_1";
+            }
+            program {
+                name: "n3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_n_3";
+                after: "n0a";
+                after: "n0b";
+            }
+            program {
+                name: "n3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_n_2";
+            }
+            program {
+                name: "s0a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_s_0";
+                after: "s1a";
+                after: "s1b";
+            }
+            program {
+                name: "s0b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_s_3";
+            }
+            program {
+                name: "s1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_s_1";
+                after: "s2a";
+                after: "s2b";
+            }
+            program {
+                name: "s1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_s_0";
+            }
+            program {
+                name: "s2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_s_2";
+                after: "s3a";
+                after: "s3b";
+            }
+            program {
+                name: "s2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_s_1";
+            }
+            program {
+                name: "s3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_s_3";
+                after: "s0a";
+                after: "s0b";
+            }
+            program {
+                name: "s3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_s_2";
+            }
+        }
+    } // }}}
+    group { name: "modules/tiling/transition/horizontal"; // {{{
+        min: 29 221;
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 29 221;
+                    max: 29 221;
+                    state: "default" 0.0;
+                    color: 0 0 0 255;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_n_3";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 1 0;
+                        offset: 0 21;
+                        to: "_";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_n_2";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_n_3";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_n_3";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_n_1";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_n_2";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_n_2";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_n_0";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_n_1";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_n_1";
+                    }
+                    image.normal: "arrow_n.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s_0";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 10;
+                        to_x: "_";
+                        to_y: "arrow_n_0";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 31;
+                        to_x: "_";
+                        to_y: "arrow_n_0";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s_1";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_s_0";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_s_0";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s_2";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_s_1";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_s_1";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_s_3";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 1;
+                        offset: 0 6;
+                        to_x: "_";
+                        to_y: "arrow_s_2";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 0 27;
+                        to_x: "_";
+                        to_y: "arrow_s_2";
+                    }
+                    image.normal: "arrow_s.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_n_0";
+                target: "arrow_n_1";
+                target: "arrow_n_2";
+                target: "arrow_n_3";
+                target: "arrow_s_0";
+                target: "arrow_s_1";
+                target: "arrow_s_2";
+                target: "arrow_s_3";
+                after: "n0a";
+                after: "s0a";
+            }
+            program {
+                name: "n0a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_n_0";
+                after: "n1a";
+                after: "n1b";
+            }
+            program {
+                name: "n0b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_n_3";
+            }
+            program {
+                name: "n1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_n_1";
+                after: "n2a";
+                after: "n2b";
+            }
+            program {
+                name: "n1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_n_0";
+            }
+            program {
+                name: "n2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_n_2";
+                after: "n3a";
+                after: "n3b";
+            }
+            program {
+                name: "n2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_n_1";
+            }
+            program {
+                name: "n3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_n_3";
+                after: "n0a";
+                after: "n0b";
+            }
+            program {
+                name: "n3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_n_2";
+            }
+            program {
+                name: "s0a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_s_0";
+                after: "s1a";
+                after: "s1b";
+            }
+            program {
+                name: "s0b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_s_3";
+            }
+            program {
+                name: "s1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_s_1";
+                after: "s2a";
+                after: "s2b";
+            }
+            program {
+                name: "s1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_s_0";
+            }
+            program {
+                name: "s2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_s_2";
+                after: "s3a";
+                after: "s3b";
+            }
+            program {
+                name: "s2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_s_1";
+            }
+            program {
+                name: "s3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_s_3";
+                after: "s0a";
+                after: "s0b";
+            }
+            program {
+                name: "s3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_s_2";
+            }
+        }
+    } // }}}
+
+    group { name: "modules/tiling/transition/vertical/composite"; // {{{
+        min: 221 29;
+        data {
+            item: "shaped" "1";
+        }
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 221 29;
+                    max: 221 29;
+                    state: "default" 0.0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_w_3";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 1;
+                        offset: 21 0;
+                        to: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_w_2";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_w_3";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_w_3";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_w_1";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_w_2";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_w_2";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_w_0";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_w_1";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_w_1";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e_0";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 10 0;
+                        to_x: "arrow_w_0";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 31 0;
+                        to_x: "arrow_w_0";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e_1";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_e_0";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_e_0";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e_2";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_e_1";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_e_1";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e_3";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_e_2";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_e_2";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_w_0";
+                target: "arrow_w_1";
+                target: "arrow_w_2";
+                target: "arrow_w_3";
+                target: "arrow_e_0";
+                target: "arrow_e_1";
+                target: "arrow_e_2";
+                target: "arrow_e_3";
+                after: "w0a";
+                after: "e0a";
+            }
+            program {
+                name: "w0a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_w_0";
+                after: "w1a";
+                after: "w1b";
+            }
+            program {
+                name: "w0b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_w_3";
+            }
+            program {
+                name: "w1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_w_1";
+                after: "w2a";
+                after: "w2b";
+            }
+            program {
+                name: "w1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_w_0";
+            }
+            program {
+                name: "w2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_w_2";
+                after: "w3a";
+                after: "w3b";
+            }
+            program {
+                name: "w2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_w_1";
+            }
+            program {
+                name: "w3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_w_3";
+                after: "w0a";
+                after: "w0b";
+            }
+            program {
+                name: "w3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_w_2";
+            }
+            program {
+                name: "e0a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_e_0";
+                after: "e1a";
+                after: "e1b";
+            }
+            program {
+                name: "e0b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_e_3";
+            }
+            program {
+                name: "e1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_e_1";
+                after: "e2a";
+                after: "e2b";
+            }
+            program {
+                name: "e1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_e_0";
+            }
+            program {
+                name: "e2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_e_2";
+                after: "e3a";
+                after: "e3b";
+            }
+            program {
+                name: "e2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_e_1";
+            }
+            program {
+                name: "e3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_e_3";
+                after: "e0a";
+                after: "e0b";
+            }
+            program {
+                name: "e3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_e_2";
+            }
+        }
+    } // }}}
+    group { name: "modules/tiling/transition/vertical"; // {{{
+        min: 221 29;
+        parts {
+            part {
+                name: "_";
+                type: RECT;
+                mouse_events: 0;
+                description {
+                    min: 221 29;
+                    max: 221 29;
+                    state: "default" 0.0;
+                    color: 0 0 0 255;
+                    rel1 {
+                        relative: 0.0 0.0;
+                        offset: 0 0;
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: -1 -1;
+                    }
+                }
+            }
+            part {
+                name: "arrow_w_3";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 0 0;
+                        offset: 0 0;
+                        to: "_";
+                    }
+                    rel2 {
+                        relative: 0 1;
+                        offset: 21 0;
+                        to: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_w_2";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_w_3";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_w_3";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_w_1";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_w_2";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_w_2";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_w_0";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_w_1";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_w_1";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_w.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e_0";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 10 0;
+                        to_x: "arrow_w_0";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 31 0;
+                        to_x: "arrow_w_0";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e_1";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_e_0";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_e_0";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e_2";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_e_1";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_e_1";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+            part {
+                name: "arrow_e_3";
+                mouse_events: 0;
+                description {
+                    state: "default" 0.0;
+                    visible: 0;
+                    color: 255 255 255 0;
+                    rel1 {
+                        relative: 1 0;
+                        offset: 6 0;
+                        to_x: "arrow_e_2";
+                        to_y: "_";
+                    }
+                    rel2 {
+                        relative: 1 1;
+                        offset: 27 0;
+                        to_x: "arrow_e_2";
+                        to_y: "_";
+                    }
+                    image.normal: "arrow_e.png";
+                    fill.smooth: 0;
+                }
+                description {
+                    state: "visible" 0.0;
+                    inherit: "default" 0.0;
+                    visible: 1;
+                    color: 255 255 255 255;
+                }
+            }
+        }
+        programs {
+            program {
+                name: "init";
+                signal: "load";
+                source: "";
+                action: STATE_SET "default" 0.0;
+                target: "arrow_w_0";
+                target: "arrow_w_1";
+                target: "arrow_w_2";
+                target: "arrow_w_3";
+                target: "arrow_e_0";
+                target: "arrow_e_1";
+                target: "arrow_e_2";
+                target: "arrow_e_3";
+                after: "w0a";
+                after: "e0a";
+            }
+            program {
+                name: "w0a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_w_0";
+                after: "w1a";
+                after: "w1b";
+            }
+            program {
+                name: "w0b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_w_3";
+            }
+            program {
+                name: "w1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_w_1";
+                after: "w2a";
+                after: "w2b";
+            }
+            program {
+                name: "w1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_w_0";
+            }
+            program {
+                name: "w2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_w_2";
+                after: "w3a";
+                after: "w3b";
+            }
+            program {
+                name: "w2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_w_1";
+            }
+            program {
+                name: "w3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_w_3";
+                after: "w0a";
+                after: "w0b";
+            }
+            program {
+                name: "w3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_w_2";
+            }
+            program {
+                name: "e0a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_e_0";
+                after: "e1a";
+                after: "e1b";
+            }
+            program {
+                name: "e0b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_e_3";
+            }
+            program {
+                name: "e1a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_e_1";
+                after: "e2a";
+                after: "e2b";
+            }
+            program {
+                name: "e1b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_e_0";
+            }
+            program {
+                name: "e2a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_e_2";
+                after: "e3a";
+                after: "e3b";
+            }
+            program {
+                name: "e2b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_e_1";
+            }
+            program {
+                name: "e3a";
+                action: STATE_SET "visible" 0.0;
+                transition: SINUSOIDAL 0.25;
+                target: "arrow_e_3";
+                after: "e0a";
+                after: "e0b";
+            }
+            program {
+                name: "e3b";
+                action: STATE_SET "default" 0.0;
+                transition: DECELERATE 0.75;
+                target: "arrow_e_2";
+            }
+        }
+    } // }}}
+}
diff --git a/src/modules/tiling/e_mod_config.c b/src/modules/tiling/e_mod_config.c
new file mode 100644 (file)
index 0000000..0f424f2
--- /dev/null
@@ -0,0 +1,333 @@
+#include "e_mod_tiling.h"
+
+/* HACK: Needed to get subobjs of the widget. Is there a better way? */
+typedef struct _E_Widget_Smart_Data E_Widget_Smart_Data;
+
+struct _E_Widget_Smart_Data
+{
+   Evas_Object *parent_obj;
+   Evas_Coord   x, y, w, h;
+   Evas_Coord   minw, minh;
+   Eina_List   *subobjs;
+};
+
+/* Some defines to make coding with the e_widget_* easier for
+ * configuration panel */
+#define RADIO(title, value, radiogroup) \
+  e_widget_radio_add(evas, _(title), value, radiogroup)
+#define LIST_ADD(list, object) \
+  e_widget_list_object_append(list, object, 1, 1, 0.5)
+
+struct _Config_vdesk *
+get_vdesk(Eina_List *vdesks,
+          int x,
+          int y,
+          unsigned int zone_num)
+{
+    for (Eina_List *l = vdesks; l; l = l->next) {
+        struct _Config_vdesk *vd = l->data;
+
+        if (!vd)
+            continue;
+        if (vd->nb_stacks < 0 || vd->nb_stacks > TILING_MAX_STACKS)
+            vd->nb_stacks = 0;
+        if (vd->x == x && vd->y == y && vd->zone_num == zone_num)
+            return vd;
+    }
+
+    return NULL;
+}
+
+/*
+ * Fills the E_Config_Dialog-struct with the data currently in use
+ *
+ */
+static void *
+_create_data(E_Config_Dialog *cfd)
+{
+    E_Config_Dialog_Data *cfdata = E_NEW(E_Config_Dialog_Data, 1);
+
+    /* Because we save a lot of lines here by using memcpy,
+     * the structs have to be ordered the same */
+    memcpy(cfdata, tiling_g.config, sizeof(Config));
+    cfdata->config.keyhints = strdup(tiling_g.config->keyhints);
+
+    /* Handle things which can't be easily memcpy'd */
+    cfdata->config.vdesks = NULL;
+
+    for (Eina_List *l = tiling_g.config->vdesks; l; l = l->next) {
+        struct _Config_vdesk *vd = l->data,
+                             *newvd;
+
+        if (!vd)
+            continue;
+
+        newvd = E_NEW(struct _Config_vdesk, 1);
+        newvd->x = vd->x;
+        newvd->y = vd->y;
+        newvd->zone_num = vd->zone_num;
+        newvd->nb_stacks = vd->nb_stacks;
+        newvd->use_rows = vd->use_rows;
+
+        cfdata->config.vdesks = eina_list_append(cfdata->config.vdesks,
+                                                 newvd);
+    }
+
+    return cfdata;
+}
+
+static void
+_free_data(E_Config_Dialog      *cfd,
+           E_Config_Dialog_Data *cfdata)
+{
+    eina_list_free(cfdata->config.vdesks);
+    free(cfdata->config.keyhints);
+    free(cfdata);
+}
+
+static void
+_fill_zone_config(E_Zone               *zone,
+                  E_Config_Dialog_Data *cfdata)
+{
+    Evas *evas = cfdata->evas;
+
+    /* Clear old entries first */
+    evas_object_del(cfdata->o_desklist);
+
+    cfdata->o_desklist = e_widget_list_add(evas, 1, 0);
+
+    for (int i = 0; i < zone->desk_y_count * zone->desk_x_count; i++) {
+        E_Desk *desk = zone->desks[i];
+        struct _Config_vdesk *vd;
+        Evas_Object *list, *slider, *radio;
+        E_Radio_Group *rg;
+
+        if (!desk)
+            continue;
+
+        vd = get_vdesk(cfdata->config.vdesks, desk->x, desk->y, zone->num);
+        if (!vd) {
+            vd = E_NEW(struct _Config_vdesk, 1);
+            vd->x = desk->x;
+            vd->y = desk->y;
+            vd->zone_num = zone->num;
+            vd->nb_stacks = 0;
+            vd->use_rows = 0;
+
+            cfdata->config.vdesks = eina_list_append(cfdata->config.vdesks,
+                                                     vd);
+        }
+
+        list = e_widget_list_add(evas, false, true);
+
+        LIST_ADD(list, e_widget_label_add(evas, desk->name));
+        slider = e_widget_slider_add(evas, 1, 0, _("%1.0f"),
+                                     0.0, 8.0, 1.0, 0, NULL,
+                                     &vd->nb_stacks, 150);
+        LIST_ADD(list, slider);
+
+        rg = e_widget_radio_group_new(&vd->use_rows);
+        radio = e_widget_radio_add(evas, _("columns"), 0, rg);
+        LIST_ADD(list, radio);
+        radio = e_widget_radio_add(evas, _("rows"), 1, rg);
+        LIST_ADD(list, radio);
+
+        LIST_ADD(cfdata->o_desklist, list);
+    }
+
+    /* Get the correct sizes of desklist and scrollframe */
+    LIST_ADD(cfdata->osf, cfdata->o_desklist);
+}
+
+static void
+_cb_zone_change(void        *data,
+                Evas_Object *obj)
+{
+    int n;
+    E_Config_Dialog_Data *cfdata = data;
+    E_Zone *zone;
+
+    if (!cfdata || !cfdata->o_zonelist)
+        return;
+
+    n = e_widget_ilist_selected_get(cfdata->o_zonelist);
+    zone = e_widget_ilist_nth_data_get(cfdata->o_zonelist, n);
+    if (!zone)
+        return;
+    _fill_zone_config(zone, cfdata);
+}
+
+static Evas_Object *
+_basic_create_widgets(E_Config_Dialog      *cfd,
+                      Evas                 *evas,
+                      E_Config_Dialog_Data *cfdata)
+{
+    Evas_Object *o, *oc, *of;
+    E_Container *con = e_container_current_get(e_manager_current_get());
+    E_Zone *zone;
+
+    o = e_widget_list_add(evas, 0, 0);
+
+    /* General settings */
+    of = e_widget_framelist_add(evas, _("General"), 0);
+    e_widget_framelist_object_append(of,
+      e_widget_check_add(evas, _("Tile dialog windows as well"),
+                         &cfdata->config.tile_dialogs));
+    e_widget_framelist_object_append(of,
+      e_widget_check_add(evas, _("Show window titles"),
+                         &cfdata->config.show_titles));
+    oc = e_widget_list_add(evas, false, true);
+    e_widget_list_object_append(oc,
+      e_widget_label_add(evas, _("Key hints")), 1, 0, 0.5);
+    e_widget_list_object_append(oc,
+      e_widget_entry_add(evas, &cfdata->config.keyhints, NULL, NULL, NULL),
+      1, 1, 0.5);
+    e_widget_framelist_object_append(of, oc);
+
+    LIST_ADD(o, of);
+
+    /* Virtual desktop settings */
+    of = e_widget_framelist_add(evas, _("Virtual Desktops"), 0);
+    e_widget_label_add(evas,
+                       _("Number of columns used to tile per desk"
+                          " (0 → tiling disabled):"));
+    cfdata->osf = e_widget_list_add(evas, 0, 1);
+
+    /* Zone list */
+    cfdata->o_zonelist = e_widget_ilist_add(evas, 0, 0, NULL);
+    e_widget_ilist_multi_select_set(cfdata->o_zonelist, false);
+    e_widget_size_min_set(cfdata->o_zonelist, 100, 100);
+    e_widget_on_change_hook_set(cfdata->o_zonelist, _cb_zone_change, cfdata);
+    for (Eina_List *l = con->zones; l; l = l->next) {
+        if (!(zone = l->data))
+            continue;
+        e_widget_ilist_append(cfdata->o_zonelist, NULL, zone->name, NULL, zone, NULL);
+    }
+    e_widget_ilist_go(cfdata->o_zonelist);
+    e_widget_ilist_thaw(cfdata->o_zonelist);
+
+    LIST_ADD(cfdata->osf, cfdata->o_zonelist);
+
+    /* List of individual tiling modes */
+    cfdata->evas = evas;
+
+    _fill_zone_config(con->zones->data, cfdata);
+
+    e_widget_ilist_selected_set(cfdata->o_zonelist, 0);
+
+    e_widget_framelist_object_append(of, cfdata->osf);
+
+    LIST_ADD(o, of);
+
+    return o;
+}
+
+static int
+_basic_apply_data(E_Config_Dialog      *cfd,
+                  E_Config_Dialog_Data *cfdata)
+{
+    struct _Config_vdesk *vd;
+
+    tiling_g.config->tile_dialogs = cfdata->config.tile_dialogs;
+    tiling_g.config->show_titles = cfdata->config.show_titles;
+    if (strcmp(tiling_g.config->keyhints, cfdata->config.keyhints)) {
+        free(tiling_g.config->keyhints);
+        if (!cfdata->config.keyhints || !*cfdata->config.keyhints) {
+            tiling_g.config->keyhints = strdup(tiling_g.default_keyhints);
+        } else {
+            char *c = cfdata->config.keyhints;
+            int len = strlen(cfdata->config.keyhints);
+
+            /* Remove duplicates */
+            while (*c) {
+                char *f = c + 1;
+
+                while ((f = strchr(f, *c))) {
+                    *f = cfdata->config.keyhints[--len];
+                    cfdata->config.keyhints[len] = '\0';
+                }
+                c++;
+            }
+            tiling_g.config->keyhints = strdup(cfdata->config.keyhints);
+        }
+    }
+
+    /* Check if the layout for one of the vdesks has changed */
+    for (Eina_List *l = tiling_g.config->vdesks; l; l = l->next) {
+        struct _Config_vdesk *newvd;
+
+        vd = l->data;
+
+        if (!vd)
+            continue;
+        if (!(newvd = get_vdesk(cfdata->config.vdesks,
+                                vd->x, vd->y, vd->zone_num))) {
+            change_desk_conf(vd);
+            continue;
+        }
+
+        if (newvd->nb_stacks != vd->nb_stacks
+        ||  newvd->use_rows != vd->use_rows) {
+            DBG("number of columns for (%d, %d, %d) changed from %d|%d"
+                " to %d|%d",
+                vd->x, vd->y, vd->zone_num,
+                vd->nb_stacks, vd->use_rows,
+                newvd->nb_stacks, newvd->use_rows);
+            change_desk_conf(newvd);
+            free(vd);
+            l->data = NULL;
+        }
+    }
+
+    for (Eina_List *l = cfdata->config.vdesks; l; l = l->next) {
+        vd = l->data;
+
+        if (!vd)
+            continue;
+        if (!get_vdesk(tiling_g.config->vdesks,
+                       vd->x, vd->y, vd->zone_num)) {
+            change_desk_conf(vd);
+            continue;
+        }
+    }
+
+    EINA_LIST_FREE(tiling_g.config->vdesks, vd) {
+        free(vd);
+    }
+
+    tiling_g.config->vdesks = cfdata->config.vdesks;
+    cfdata->config.vdesks = NULL; /* we don't want this list to be freed */
+
+    e_tiling_update_conf();
+
+    e_config_save_queue();
+
+    return EINA_TRUE;
+}
+
+E_Config_Dialog *
+e_int_config_tiling_module(E_Container *con,
+                           const char  *params)
+{
+    E_Config_Dialog *cfd;
+    E_Config_Dialog_View *v;
+    char buf[PATH_MAX];
+
+    if (e_config_dialog_find("E", "windows/tiling"))
+        return NULL;
+
+    v = E_NEW(E_Config_Dialog_View, 1);
+
+    v->create_cfdata = _create_data;
+    v->free_cfdata = _free_data;
+    v->basic.apply_cfdata = _basic_apply_data;
+    v->basic.create_widgets = _basic_create_widgets;
+
+    snprintf(buf, sizeof(buf), "%s/e-module-tiling.edj",
+             e_module_dir_get(tiling_g.module));
+    cfd = e_config_dialog_new(con,
+                              _("Tiling Configuration"),
+                              "E", "windows/tiling",
+                              buf, 0, v, NULL);
+    return cfd;
+}
diff --git a/src/modules/tiling/e_mod_tiling.c b/src/modules/tiling/e_mod_tiling.c
new file mode 100644 (file)
index 0000000..1408fba
--- /dev/null
@@ -0,0 +1,3917 @@
+#include "e_mod_tiling.h"
+
+/* types {{{ */
+
+#define TILING_OVERLAY_TIMEOUT 5.0
+#define TILING_RESIZE_STEP 5
+#define TILING_POPUP_LAYER 101
+#define TILING_WRAP_SPEED 0.1
+
+typedef enum {
+    TILING_RESIZE,
+    TILING_MOVE,
+} tiling_change_t;
+
+typedef enum {
+    INPUT_MODE_NONE,
+    INPUT_MODE_SWAPPING,
+    INPUT_MODE_MOVING,
+    INPUT_MODE_GOING,
+    INPUT_MODE_TRANSITION,
+} tiling_input_mode_t;
+
+typedef enum {
+    MOVE_UP,
+    MOVE_DOWN,
+    MOVE_LEFT,
+    MOVE_RIGHT,
+
+    MOVE_COUNT
+} tiling_move_t;
+
+typedef struct geom_t {
+    int x, y, w, h;
+} geom_t;
+
+typedef struct overlay_t {
+    E_Popup *popup;
+    Evas_Object *obj;
+} overlay_t;
+
+typedef struct transition_overlay_t {
+    overlay_t overlay;
+    int stack;
+    char key[4];
+    E_Border *bd;
+} transition_overlay_t;
+
+typedef struct Border_Extra {
+    E_Border *border;
+    geom_t expected;
+    struct {
+         geom_t geom;
+         unsigned int layer;
+         E_Stacking  stacking;
+         E_Maximize  maximized;
+         const char *bordername;
+    } orig;
+    overlay_t overlay;
+    char key[4];
+} Border_Extra;
+
+struct tiling_g tiling_g = {
+    .module = NULL,
+    .config = NULL,
+    .log_domain = -1,
+    .default_keyhints = "asdfg;lkjh",
+};
+
+static void
+_add_border(E_Border *bd);
+
+/* }}} */
+/* Globals {{{ */
+
+static struct tiling_mod_main_g
+{
+    char                  edj_path[PATH_MAX];
+    E_Config_DD          *config_edd,
+                         *vdesk_edd;
+    int                   currently_switching_desktop;
+    Ecore_X_Window        action_input_win;
+    Ecore_Event_Handler  *handler_key,
+                         *handler_border_resize,
+                         *handler_border_move,
+                         *handler_border_add,
+                         *handler_border_remove,
+                         *handler_border_iconify,
+                         *handler_border_uniconify,
+                         *handler_border_stick,
+                         *handler_border_unstick,
+                         *handler_desk_show,
+                         *handler_desk_before_show,
+                         *handler_desk_set,
+                         *handler_container_resize;
+    E_Border_Hook        *pre_border_assign_hook;
+
+    Tiling_Info          *tinfo;
+    Eina_Hash            *info_hash;
+    Eina_Hash            *border_extras;
+    Eina_Hash            *overlays;
+
+    E_Action             *act_togglefloat,
+                         *act_addstack,
+                         *act_removestack,
+                         *act_tg_stack,
+                         *act_swap,
+                         *act_move,
+                         *act_adjusttransitions,
+                         *act_go;
+
+    int                   warp_x,
+                          warp_y,
+                          old_warp_x,
+                          old_warp_y,
+                          warp_to_x,
+                          warp_to_y;
+    Ecore_Timer          *warp_timer;
+
+    overlay_t             move_overlays[MOVE_COUNT];
+    transition_overlay_t *transition_overlay;
+    Ecore_Timer          *action_timer;
+    E_Border             *focused_bd;
+    void (*action_cb)(E_Border *bd, Border_Extra *extra);
+
+    tiling_input_mode_t   input_mode;
+    char                  keys[4];
+} _G = {
+    .input_mode = INPUT_MODE_NONE,
+};
+
+/* }}} */
+/* Utils {{{ */
+
+/* I wonder why noone has implemented the following one yet? */
+static E_Desk *
+get_current_desk(void)
+{
+    E_Manager *m = e_manager_current_get();
+    E_Container *c = e_container_current_get(m);
+    E_Zone *z = e_zone_current_get(c);
+
+    return e_desk_current_get(z);
+}
+
+static Tiling_Info *
+_initialize_tinfo(const E_Desk *desk)
+{
+    Tiling_Info *tinfo;
+
+    tinfo = E_NEW(Tiling_Info, 1);
+    tinfo->desk = desk;
+    eina_hash_direct_add(_G.info_hash, &tinfo->desk, tinfo);
+
+    tinfo->conf = get_vdesk(tiling_g.config->vdesks, desk->x, desk->y,
+                            desk->zone->num);
+
+    return tinfo;
+}
+
+static void
+check_tinfo(const E_Desk *desk)
+{
+    if (!_G.tinfo || _G.tinfo->desk != desk) {
+        _G.tinfo = eina_hash_find(_G.info_hash, &desk);
+        if (!_G.tinfo) {
+            /* lazy init */
+            _G.tinfo = _initialize_tinfo(desk);
+        }
+        if (!_G.tinfo->conf) {
+            _G.tinfo->conf = get_vdesk(tiling_g.config->vdesks,
+                                       desk->x, desk->y,
+                                       desk->zone->num);
+        }
+    }
+}
+
+static int
+is_floating_window(const E_Border *bd)
+{
+    return EINA_LIST_IS_IN(_G.tinfo->floating_windows, bd);
+}
+
+static int
+is_tilable(const E_Border *bd)
+{
+    if (bd->client.icccm.min_h == bd->client.icccm.max_h
+    &&  bd->client.icccm.max_h > 0)
+        return false;
+
+    if (bd->client.icccm.gravity == ECORE_X_GRAVITY_STATIC)
+        return false;
+
+    if (!tiling_g.config->tile_dialogs
+            && ((bd->client.icccm.transient_for != 0)
+                || (bd->client.netwm.type == ECORE_X_WINDOW_TYPE_DIALOG)))
+        return false;
+
+    return true;
+}
+
+static void
+change_window_border(E_Border   *bd,
+                     const char *bordername)
+{
+    eina_stringshare_replace(&bd->bordername, bordername);
+    bd->client.border.changed = true;
+    bd->changes.border = true;
+    bd->changed = true;
+}
+
+static int
+get_stack(const E_Border *bd)
+{
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        if (EINA_LIST_IS_IN(_G.tinfo->stacks[i], bd))
+            return i;
+    }
+    return -1;
+}
+
+static int
+get_stack_count(void)
+{
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        if (!_G.tinfo->stacks[i])
+            return i;
+    }
+    return TILING_MAX_STACKS;
+}
+
+static int
+get_window_count(void)
+{
+    int res = 0;
+
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        if (!_G.tinfo->stacks[i])
+            break;
+        res += eina_list_count(_G.tinfo->stacks[i]);
+    }
+    return res;
+}
+
+static int
+get_transition_count(void)
+{
+    int res = 0;
+
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        if (!_G.tinfo->stacks[i])
+            break;
+        res += eina_list_count(_G.tinfo->stacks[i]);
+    }
+    if (_G.tinfo->stacks[0])
+        res--;
+    return res;
+}
+
+static void
+_theme_edje_object_set_aux(Evas_Object *obj, const char *group)
+{
+    if (!e_theme_edje_object_set(obj, "base/theme/modules/tiling",
+                                 group)) {
+        edje_object_file_set(obj, _G.edj_path, group);
+    }
+}
+#define _theme_edje_object_set(_obj, _group)                                 \
+    if (e_config->use_composite)                                             \
+        _theme_edje_object_set_aux(_obj, _group"/composite");                \
+    else                                                                     \
+        _theme_edje_object_set_aux(_obj, _group);
+
+static Eina_Bool
+_info_hash_update(const Eina_Hash *hash, const void *key,
+                  void *data, void *fdata)
+{
+    Tiling_Info *tinfo = data;
+
+    if (tinfo->desk) {
+        tinfo->conf = get_vdesk(tiling_g.config->vdesks,
+                                tinfo->desk->x, tinfo->desk->y,
+                                tinfo->desk->zone->num);
+    } else {
+        tinfo->conf = NULL;
+    }
+
+    return true;
+}
+
+void
+e_tiling_update_conf(void)
+{
+    eina_hash_foreach(_G.info_hash, _info_hash_update, NULL);
+}
+
+static void
+_e_border_move_resize(E_Border *bd,
+                      int       x,
+                      int       y,
+                      int       w,
+                      int       h)
+{
+    DBG("%p -> %dx%d+%d+%d", bd, w, h, x, y);
+    e_border_move_resize(bd, x, y, w, h);
+}
+
+static void
+_e_border_move(E_Border *bd,
+               int       x,
+               int       y)
+{
+    DBG("%p -> +%d+%d", bd, x, y);
+    e_border_move(bd, x, y);
+}
+
+static void
+_e_border_resize(E_Border *bd,
+                 int       w,
+                 int       h)
+{
+    DBG("%p -> %dx%d", bd, w, h);
+    e_border_resize(bd, w, h);
+}
+
+static void
+_e_border_maximize(E_Border *bd, E_Maximize max)
+{
+    DBG("%p -> %s", bd,
+        (max & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_NONE ? "NONE" :
+        (max & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_VERTICAL ? "VERTICAL" :
+        (max & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_HORIZONTAL ? "HORIZONTAL" :
+        "BOTH");
+    DBG("new_client:%s, bd->maximized=%x",
+        bd->new_client? "true": "false",
+        bd->maximized);
+    e_border_maximize(bd, max);
+}
+
+static void
+_e_border_unmaximize(E_Border *bd, E_Maximize max)
+{
+    DBG("%p -> %s", bd,
+        (max & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_NONE ? "NONE" :
+        (max & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_VERTICAL ? "VERTICAL" :
+        (max & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_HORIZONTAL ? "HORIZONTAL" :
+        "BOTH");
+    e_border_unmaximize(bd, max);
+}
+
+static void
+_restore_border(E_Border *bd)
+{
+    Border_Extra *extra;
+
+    extra = eina_hash_find(_G.border_extras, &bd);
+    if (!extra) {
+         ERR("No extra for %p", bd);
+         return;
+    }
+    _e_border_unmaximize(bd, E_MAXIMIZE_BOTH);
+    _e_border_move_resize(bd,
+                          extra->orig.geom.x,
+                          extra->orig.geom.y,
+                          extra->orig.geom.w,
+                          extra->orig.geom.h);
+    e_border_layer_set(bd, extra->orig.layer);
+    e_hints_window_stacking_set(bd, extra->orig.stacking);
+    if (extra->orig.maximized) {
+        e_border_maximize(bd, extra->orig.maximized);
+        bd->maximized = extra->orig.maximized;
+    }
+
+
+    change_window_border(bd, extra->orig.bordername);
+}
+
+static Border_Extra *
+_get_or_create_border_extra(E_Border *bd)
+{
+    Border_Extra *extra;
+
+    extra = eina_hash_find(_G.border_extras, &bd);
+    if (!extra) {
+        extra = E_NEW(Border_Extra, 1);
+        *extra = (Border_Extra) {
+            .border = bd,
+            .expected = {
+                .x = bd->x,
+                .y = bd->y,
+                .w = bd->w,
+                .h = bd->h,
+            },
+            .orig = {
+                .geom = {
+                    .x = bd->x,
+                    .y = bd->y,
+                    .w = bd->w,
+                    .h = bd->h,
+                },
+                .layer = bd->layer,
+                .stacking = bd->client.netwm.state.stacking,
+                .maximized = bd->maximized,
+                .bordername = eina_stringshare_add(bd->bordername),
+            },
+        };
+        eina_hash_direct_add(_G.border_extras, &extra->border, extra);
+    } else {
+        extra->expected = (geom_t) {
+            .x = bd->x,
+            .y = bd->y,
+            .w = bd->w,
+            .h = bd->h,
+        };
+    }
+
+    return extra;
+}
+
+/* }}} */
+/* Overlays {{{*/
+
+static void
+_overlays_free_cb(void *data)
+{
+    Border_Extra *extra = data;
+
+    if (extra->overlay.obj) {
+        evas_object_del(extra->overlay.obj);
+        extra->overlay.obj = NULL;
+    }
+    if (extra->overlay.popup) {
+        e_object_del(E_OBJECT(extra->overlay.popup));
+        extra->overlay.popup = NULL;
+    }
+
+    extra->key[0] = '\0';
+}
+
+static void
+end_special_input(void)
+{
+    if (_G.input_mode == INPUT_MODE_NONE)
+        return;
+
+    if (_G.overlays) {
+        eina_hash_free(_G.overlays);
+        _G.overlays = NULL;
+    }
+
+    if (_G.handler_key) {
+        ecore_event_handler_del(_G.handler_key);
+        _G.handler_key = NULL;
+    }
+    if (_G.action_input_win) {
+        e_grabinput_release(_G.action_input_win, _G.action_input_win);
+        ecore_x_window_free(_G.action_input_win);
+        _G.action_input_win = 0;
+    }
+    if (_G.action_timer) {
+        ecore_timer_del(_G.action_timer);
+        _G.action_timer = NULL;
+    }
+
+    _G.focused_bd = NULL;
+    _G.action_cb = NULL;
+
+    switch(_G.input_mode) {
+      case INPUT_MODE_MOVING:
+        for (int i = 0; i < MOVE_COUNT; i++) {
+            overlay_t *overlay = &_G.move_overlays[i];
+
+            if (overlay->obj) {
+                evas_object_del(overlay->obj);
+                overlay->obj = NULL;
+            }
+            if (overlay->popup) {
+                e_object_del(E_OBJECT(overlay->popup));
+                overlay->popup = NULL;
+            }
+        }
+        break;
+      case INPUT_MODE_TRANSITION:
+        if (_G.transition_overlay) {
+            if (_G.transition_overlay->overlay.obj) {
+                evas_object_del(_G.transition_overlay->overlay.obj);
+            }
+            if (_G.transition_overlay->overlay.popup) {
+                e_object_del(E_OBJECT(_G.transition_overlay->overlay.popup));
+            }
+            E_FREE(_G.transition_overlay);
+            _G.transition_overlay = NULL;
+        }
+        break;
+      default:
+        break;
+    }
+
+    _G.input_mode = INPUT_MODE_NONE;
+}
+
+static Eina_Bool
+overlay_key_down(void *data,
+          int type,
+          void *event)
+{
+    Ecore_Event_Key *ev = event;
+    Border_Extra *extra;
+
+    if (ev->event_window != _G.action_input_win)
+        return ECORE_CALLBACK_PASS_ON;
+
+    if (strcmp(ev->key, "Return") == 0)
+        goto stop;
+    if (strcmp(ev->key, "Escape") == 0)
+        goto stop;
+    if (strcmp(ev->key, "Backspace") == 0) {
+        char *key = _G.keys;
+
+        while (*key)
+            key++;
+        *key = '\0';
+        return ECORE_CALLBACK_RENEW;
+    }
+
+    if (ev->key[0] && !ev->key[1] && strchr(tiling_g.config->keyhints,
+                                            ev->key[1])) {
+        char *key = _G.keys;
+
+        while (*key)
+            key++;
+        *key++ = ev->key[0];
+        *key = '\0';
+
+        extra = eina_hash_find(_G.overlays, _G.keys);
+        if (extra) {
+            _G.action_cb(_G.focused_bd, extra);
+        } else {
+            return ECORE_CALLBACK_RENEW;
+        }
+    }
+
+stop:
+    end_special_input();
+    return ECORE_CALLBACK_DONE;
+}
+
+static Eina_Bool
+_timeout_cb(void *data)
+{
+    end_special_input();
+    return ECORE_CALLBACK_CANCEL;
+}
+
+static void
+_do_overlay(E_Border *focused_bd,
+            void (*action_cb)(E_Border *, Border_Extra *),
+            tiling_input_mode_t input_mode)
+{
+    Ecore_X_Window parent;
+    int nb_win;
+    int hints_len;
+    int key_len;
+    int n = 0;
+    int nmax;
+
+    end_special_input();
+
+    nb_win = get_window_count();
+    if (nb_win < 2) {
+        return;
+    }
+
+    _G.input_mode = input_mode;
+
+    _G.focused_bd = focused_bd;
+    _G.action_cb = action_cb;
+
+    _G.overlays = eina_hash_string_small_new(_overlays_free_cb);
+
+    hints_len = strlen(tiling_g.config->keyhints);
+    key_len = 1;
+    nmax = hints_len;
+    if (hints_len < nb_win) {
+        key_len = 2;
+        nmax *= hints_len;
+        if (hints_len * hints_len < nb_win) {
+            key_len = 3;
+            nmax *= hints_len;
+        }
+    }
+
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        Eina_List *l;
+        E_Border *bd;
+
+        if (!_G.tinfo->stacks[i])
+            break;
+        EINA_LIST_FOREACH(_G.tinfo->stacks[i], l, bd) {
+            if (bd != focused_bd && n < nmax) {
+                Border_Extra *extra;
+                Evas_Coord ew, eh;
+
+                extra = eina_hash_find(_G.border_extras, &bd);
+                if (!extra) {
+                    ERR("No extra for %p", bd);
+                    continue;
+                }
+
+                extra->overlay.popup = e_popup_new(bd->zone, 0, 0, 1, 1);
+                if (!extra->overlay.popup)
+                    continue;
+
+                e_popup_layer_set(extra->overlay.popup, TILING_POPUP_LAYER);
+                extra->overlay.obj =
+                    edje_object_add(extra->overlay.popup->evas);
+                e_theme_edje_object_set(extra->overlay.obj,
+                                        "base/theme/borders",
+                                        "e/widgets/border/default/resize");
+
+                switch (key_len) {
+                  case 1:
+                    extra->key[0] = tiling_g.config->keyhints[n];
+                    extra->key[1] = '\0';
+                    break;
+                  case 2:
+                    extra->key[0] = tiling_g.config->keyhints[n / hints_len];
+                    extra->key[1] = tiling_g.config->keyhints[n % hints_len];
+                    extra->key[2] = '\0';
+                    break;
+                  case 3:
+                    extra->key[0] = tiling_g.config->keyhints[n / hints_len / hints_len];
+                    extra->key[0] = tiling_g.config->keyhints[n / hints_len];
+                    extra->key[1] = tiling_g.config->keyhints[n % hints_len];
+                    extra->key[2] = '\0';
+                    break;
+                }
+                n++;
+
+                eina_hash_add(_G.overlays, extra->key, extra);
+                edje_object_part_text_set(extra->overlay.obj,
+                                          "e.text.label",
+                                          extra->key);
+                edje_object_size_min_calc(extra->overlay.obj, &ew, &eh);
+                evas_object_move(extra->overlay.obj, 0, 0);
+                evas_object_resize(extra->overlay.obj, ew, eh);
+                evas_object_show(extra->overlay.obj);
+                e_popup_edje_bg_object_set(extra->overlay.popup,
+                                           extra->overlay.obj);
+
+                evas_object_show(extra->overlay.obj);
+                e_popup_show(extra->overlay.popup);
+
+                e_popup_move_resize(extra->overlay.popup,
+                                    (bd->x - extra->overlay.popup->zone->x) +
+                                    ((bd->w - ew) / 2),
+                                    (bd->y - extra->overlay.popup->zone->y) +
+                                    ((bd->h - eh) / 2),
+                                    ew, eh);
+
+                e_popup_show(extra->overlay.popup);
+            }
+        }
+    }
+
+    /* Get input */
+    parent = _G.tinfo->desk->zone->container->win;
+    _G.action_input_win = ecore_x_window_input_new(parent, 0, 0, 1, 1);
+    if (!_G.action_input_win) {
+        end_special_input();
+        return;
+    }
+
+    ecore_x_window_show(_G.action_input_win);
+    if (!e_grabinput_get(_G.action_input_win, 0, _G.action_input_win)) {
+        end_special_input();
+        return;
+    }
+    _G.action_timer = ecore_timer_add(TILING_OVERLAY_TIMEOUT,
+                                      _timeout_cb, NULL);
+
+    _G.keys[0] = '\0';
+    _G.handler_key = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN,
+                                             overlay_key_down, NULL);
+}
+
+/* }}} */
+/* Reorganize Stacks {{{*/
+
+static void
+_reorganize_stack(int stack)
+{
+    if (stack < 0 || stack >= TILING_MAX_STACKS
+        || !_G.tinfo->stacks[stack])
+        return;
+
+    if (_G.tinfo->stacks[stack]->next) {
+        int zx, zy, zw, zh, i = 0, count;
+
+        e_zone_useful_geometry_get(_G.tinfo->desk->zone, &zx, &zy, &zw, &zh);
+
+        count = eina_list_count(_G.tinfo->stacks[stack]);
+
+        if (_G.tinfo->conf->use_rows) {
+            int y, w, h, cw;
+
+            y = _G.tinfo->pos[stack];
+            cw = 0;
+            w = zw / count;
+            h = _G.tinfo->size[stack];
+
+            for (Eina_List *l = _G.tinfo->stacks[stack]; l; l = l->next, i++) {
+                E_Border *bd = l->data;
+                Border_Extra *extra;
+                int d = (i * 2 * zw) % count
+                    - (2 * cw) % count;
+
+                extra = eina_hash_find(_G.border_extras, &bd);
+                if (!extra) {
+                    ERR("No extra for %p", bd);
+                    continue;
+                }
+
+                if ((bd->maximized & E_MAXIMIZE_HORIZONTAL) && count != 1) {
+                    _e_border_unmaximize(bd, E_MAXIMIZE_HORIZONTAL);
+                }
+                /* let's use a bresenham here */
+
+                extra->expected.x = cw + zx;
+                extra->expected.y = y;
+                extra->expected.w = w + d;
+                extra->expected.h = h;
+                cw += extra->expected.w;
+
+                _e_border_move_resize(bd,
+                                      extra->expected.x,
+                                      extra->expected.y,
+                                      extra->expected.w,
+                                      extra->expected.h);
+            }
+        } else {
+            int x, w, h, ch;
+
+            x = _G.tinfo->pos[stack];
+            ch = 0;
+            w = _G.tinfo->size[stack];
+            h = zh / count;
+
+            for (Eina_List *l = _G.tinfo->stacks[stack]; l; l = l->next, i++) {
+                E_Border *bd = l->data;
+                Border_Extra *extra;
+                int d = (i * 2 * zh) % count
+                    - (2 * ch) % count;
+
+                extra = eina_hash_find(_G.border_extras, &bd);
+                if (!extra) {
+                    ERR("No extra for %p", bd);
+                    continue;
+                }
+
+                if ((bd->maximized & E_MAXIMIZE_VERTICAL) && count != 1) {
+                    _e_border_unmaximize(bd, E_MAXIMIZE_VERTICAL);
+                }
+                /* let's use a bresenham here */
+
+                extra->expected.x = x;
+                extra->expected.y = ch + zy;
+                extra->expected.w = w;
+                extra->expected.h = h + d;
+                ch += extra->expected.h;
+
+                _e_border_move_resize(bd,
+                                      extra->expected.x,
+                                      extra->expected.y,
+                                      extra->expected.w,
+                                      extra->expected.h);
+            }
+        }
+    } else {
+        Border_Extra *extra;
+        E_Border *bd = _G.tinfo->stacks[stack]->data;
+
+        extra = eina_hash_find(_G.border_extras, &bd);
+        if (!extra) {
+            ERR("No extra for %p", bd);
+            return;
+        }
+
+        if (_G.tinfo->conf->use_rows) {
+            int x, w;
+
+            e_zone_useful_geometry_get(_G.tinfo->desk->zone,
+                                       &x, NULL, &w, NULL);
+
+            extra->expected.x = x;
+            extra->expected.y = _G.tinfo->pos[stack];
+            extra->expected.w = w;
+            extra->expected.h = _G.tinfo->size[stack];
+
+            _e_border_move_resize(bd,
+                                  extra->expected.x,
+                                  extra->expected.y,
+                                  extra->expected.w,
+                                  extra->expected.h);
+
+            _e_border_maximize(bd, E_MAXIMIZE_EXPAND | E_MAXIMIZE_HORIZONTAL);
+        } else {
+            int y, h;
+
+            e_zone_useful_geometry_get(_G.tinfo->desk->zone,
+                                       NULL, &y, NULL, &h);
+
+            extra->expected.x = _G.tinfo->pos[stack];
+            extra->expected.y = y;
+            extra->expected.w = _G.tinfo->size[stack];
+            extra->expected.h = h;
+
+            _e_border_move_resize(bd,
+                                  extra->expected.x,
+                                  extra->expected.y,
+                                  extra->expected.w,
+                                  extra->expected.h);
+
+            _e_border_maximize(bd, E_MAXIMIZE_EXPAND | E_MAXIMIZE_VERTICAL);
+        }
+    }
+}
+
+static void
+_move_resize_stack(int stack, int delta_pos, int delta_size)
+{
+    Eina_List *list = _G.tinfo->stacks[stack];
+
+    for (Eina_List *l = list; l; l = l->next) {
+        E_Border *bd = l->data;
+        Border_Extra *extra;
+
+        extra = eina_hash_find(_G.border_extras, &bd);
+        if (!extra) {
+            ERR("No extra for %p", bd);
+            continue;
+        }
+
+        if (_G.tinfo->conf->use_rows) {
+            extra->expected.y += delta_pos;
+            extra->expected.h += delta_size;
+        } else {
+            extra->expected.x += delta_pos;
+            extra->expected.w += delta_size;
+        }
+
+        _e_border_move_resize(bd,
+                              extra->expected.x,
+                              extra->expected.y,
+                              extra->expected.w,
+                              extra->expected.h);
+    }
+
+    _G.tinfo->pos[stack] += delta_pos;
+    _G.tinfo->size[stack] += delta_size;
+}
+
+static void
+_set_stack_geometry(int stack, int pos, int size)
+{
+    for (Eina_List *l = _G.tinfo->stacks[stack]; l; l = l->next) {
+        E_Border *bd = l->data;
+        Border_Extra *extra;
+
+        extra = eina_hash_find(_G.border_extras, &bd);
+        if (!extra) {
+            ERR("No extra for %p", bd);
+            continue;
+        }
+        DBG("expected: %dx%d+%d+%d (%p)",
+            extra->expected.w,
+            extra->expected.h,
+            extra->expected.x,
+            extra->expected.y,
+            bd);
+
+        if (_G.tinfo->conf->use_rows) {
+            extra->expected.y = pos;
+            extra->expected.h = size;
+
+            if (bd->maximized) {
+                if (l->next && (bd->maximized & E_MAXIMIZE_HORIZONTAL))
+                    _e_border_unmaximize(bd, E_MAXIMIZE_HORIZONTAL);
+                if (_G.tinfo->stacks[1] && (bd->maximized & E_MAXIMIZE_VERTICAL))
+                    _e_border_unmaximize(bd, E_MAXIMIZE_VERTICAL);
+            }
+        } else {
+            extra->expected.x = pos;
+            extra->expected.w = size;
+
+            if (bd->maximized) {
+                if (l->next && (bd->maximized & E_MAXIMIZE_VERTICAL))
+                    _e_border_unmaximize(bd, E_MAXIMIZE_VERTICAL);
+                if (_G.tinfo->stacks[1] && (bd->maximized & E_MAXIMIZE_HORIZONTAL))
+                    _e_border_unmaximize(bd, E_MAXIMIZE_HORIZONTAL);
+            }
+        }
+
+        _e_border_move_resize(bd,
+                              extra->expected.x,
+                              extra->expected.y,
+                              extra->expected.w,
+                              extra->expected.h);
+    }
+    _G.tinfo->pos[stack] = pos;
+    _G.tinfo->size[stack] = size;
+}
+
+static void
+_add_stack(void)
+{
+    int nb_borders;
+
+    if (_G.tinfo->conf->nb_stacks == TILING_MAX_STACKS)
+        return;
+
+    _G.tinfo->conf->nb_stacks++;
+
+    if (_G.tinfo->conf->nb_stacks == 1) {
+        for (Eina_List *l = e_border_focus_stack_get(); l; l = l->next) {
+            E_Border *bd;
+
+            bd = l->data;
+            if (bd->desk == _G.tinfo->desk)
+                _add_border(bd);
+        }
+    }
+    nb_borders = get_window_count();
+    if (_G.tinfo->stacks[_G.tinfo->conf->nb_stacks - 2]
+    &&  nb_borders >= _G.tinfo->conf->nb_stacks)
+    {
+        int nb_stacks = _G.tinfo->conf->nb_stacks - 1;
+        int pos, s;
+        /* Add stack */
+
+        if (_G.tinfo->conf->use_rows)
+            e_zone_useful_geometry_get(_G.tinfo->desk->zone,
+                                       NULL, &pos, NULL, &s);
+        else
+            e_zone_useful_geometry_get(_G.tinfo->desk->zone,
+                                       &pos, NULL, &s, NULL);
+
+        for (int i = 0; i <= nb_stacks; i++) {
+            int size = 0;
+
+            size = s / (nb_stacks + 1 - i);
+
+            _set_stack_geometry(i, pos, size);
+
+            s -= size;
+            pos += size;
+        }
+        for (int i = nb_stacks - 1; i >= 0; i--) {
+            if (eina_list_count(_G.tinfo->stacks[i]) == 1) {
+                _G.tinfo->stacks[i+1] = _G.tinfo->stacks[i];
+                _reorganize_stack(i+1);
+            } else {
+                E_Border *bd = eina_list_last(_G.tinfo->stacks[i])->data;
+
+                EINA_LIST_REMOVE(_G.tinfo->stacks[i], bd);
+                _reorganize_stack(i);
+
+                _G.tinfo->stacks[i+1] = NULL;
+                EINA_LIST_APPEND(_G.tinfo->stacks[i+1], bd);
+                _reorganize_stack(i+1);
+                return;
+            }
+        }
+    }
+}
+
+static void
+_remove_stack(void)
+{
+    if (!_G.tinfo->conf->nb_stacks)
+        return;
+
+    _G.tinfo->conf->nb_stacks--;
+
+    if (!_G.tinfo->conf->nb_stacks) {
+        for (int i = 0; i < TILING_MAX_STACKS; i++) {
+            for (Eina_List *l = _G.tinfo->stacks[i]; l; l = l->next) {
+                E_Border *bd = l->data;
+
+                _restore_border(bd);
+            }
+            eina_list_free(_G.tinfo->stacks[i]);
+            _G.tinfo->stacks[i] = NULL;
+        }
+        e_place_zone_region_smart_cleanup(_G.tinfo->desk->zone);
+    } else {
+        int nb_stacks = _G.tinfo->conf->nb_stacks;
+        int stack = _G.tinfo->conf->nb_stacks;
+        int pos, s;
+
+        if (_G.tinfo->stacks[stack]) {
+            _G.tinfo->stacks[stack-1] = eina_list_merge(
+                _G.tinfo->stacks[stack-1], _G.tinfo->stacks[stack]);
+            _reorganize_stack(stack-1);
+        }
+
+        if (_G.tinfo->conf->use_rows) {
+            e_zone_useful_geometry_get(_G.tinfo->desk->zone,
+                                       NULL, &pos, NULL, &s);
+        } else {
+            e_zone_useful_geometry_get(_G.tinfo->desk->zone,
+                                       &pos, NULL, &s, NULL);
+        }
+        for (int i = 0; i < nb_stacks; i++) {
+            int size = 0;
+
+            size = s / (nb_stacks - i);
+
+            _set_stack_geometry(i, pos, size);
+
+            s -= size;
+            pos += size;
+        }
+    }
+}
+
+static void
+_toggle_rows_cols(void)
+{
+#if 0
+    Eina_List *wins = NULL;
+    E_Border *bd;
+
+    _G.tinfo->conf->use_rows = !_G.tinfo->conf->use_rows;
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        EINA_LIST_FREE(_G.tinfo->stacks[i], bd) {
+            EINA_LIST_APPEND(wins, bd);
+            _restore_border(bd);
+        }
+        _G.tinfo->stacks[i] = NULL;
+        _G.tinfo->pos[i] = 0;
+        _G.tinfo->size[i] = 0;
+    }
+
+    DBG("reinsert (use_rows: %s)",
+        _G.tinfo->conf->use_rows ? "true":"false");
+
+    EINA_LIST_FREE(wins, bd) {
+        _add_border(bd);
+    }
+#else
+    int nb_stacks = _G.tinfo->conf->nb_stacks;
+    int pos, s;
+
+    _G.tinfo->conf->use_rows = !_G.tinfo->conf->use_rows;
+
+    if (_G.tinfo->conf->use_rows)
+        e_zone_useful_geometry_get(_G.tinfo->desk->zone,
+                                   NULL, &pos, NULL, &s);
+    else
+        e_zone_useful_geometry_get(_G.tinfo->desk->zone,
+                                   &pos, NULL, &s, NULL);
+
+    for (int i = 0; i < nb_stacks; i++) {
+        int size = 0;
+
+        size = s / (nb_stacks - i);
+
+        _set_stack_geometry(i, pos, size);
+
+        s -= size;
+        pos += size;
+    }
+    for (int i = 0; i < nb_stacks; i++) {
+        _reorganize_stack(i);
+    }
+#endif
+}
+
+void
+change_desk_conf(struct _Config_vdesk *newconf)
+{
+    E_Manager *m;
+    E_Container *c;
+    E_Zone *z;
+    E_Desk *d;
+    int old_nb_stacks = 0,
+        new_nb_stacks = newconf->nb_stacks;
+
+    m = e_manager_current_get();
+    if (!m) return;
+    c = e_container_current_get(m);
+    if (!c) return;
+    z = e_container_zone_number_get(c, newconf->zone_num);
+    if (!z) return;
+    d = e_desk_at_xy_get(z, newconf->x, newconf->y);
+    if (!d) return;
+
+    check_tinfo(d);
+    if (_G.tinfo->conf) {
+        old_nb_stacks = _G.tinfo->conf->nb_stacks;
+        if (_G.tinfo->conf->use_rows != newconf->use_rows) {
+            _G.tinfo->conf = newconf;
+            _G.tinfo->conf->use_rows = !_G.tinfo->conf->use_rows;
+            _toggle_rows_cols();
+            return;
+        }
+    } else {
+        newconf->nb_stacks = 0;
+    }
+    _G.tinfo->conf = newconf;
+    _G.tinfo->conf->nb_stacks = old_nb_stacks;
+
+    if (new_nb_stacks == old_nb_stacks)
+        return;
+
+    if (new_nb_stacks == 0) {
+        for (int i = 0; i < TILING_MAX_STACKS; i++) {
+            for (Eina_List *l = _G.tinfo->stacks[i]; l; l = l->next) {
+                E_Border *bd = l->data;
+
+                _restore_border(bd);
+            }
+            eina_list_free(_G.tinfo->stacks[i]);
+            _G.tinfo->stacks[i] = NULL;
+        }
+        e_place_zone_region_smart_cleanup(z);
+    } else if (new_nb_stacks > old_nb_stacks) {
+        for (int i = new_nb_stacks; i > old_nb_stacks; i--) {
+            _add_stack();
+        }
+    } else {
+        for (int i = new_nb_stacks; i < old_nb_stacks; i++) {
+            _remove_stack();
+        }
+    }
+    _G.tinfo->conf->nb_stacks = new_nb_stacks;
+}
+
+static void
+_e_mod_action_add_stack_cb(E_Object   *obj,
+                           const char *params)
+{
+    E_Desk *desk = get_current_desk();
+
+    end_special_input();
+
+    check_tinfo(desk);
+
+    _add_stack();
+
+    e_config_save_queue();
+}
+
+static void
+_e_mod_action_remove_stack_cb(E_Object   *obj,
+                              const char *params)
+{
+    E_Desk *desk = get_current_desk();
+
+    end_special_input();
+
+    check_tinfo(desk);
+
+    _remove_stack();
+
+    e_config_save_queue();
+}
+
+static void
+_e_mod_action_tg_stack_cb(E_Object   *obj,
+                          const char *params)
+{
+    E_Desk *desk = get_current_desk();
+
+    end_special_input();
+
+    check_tinfo(desk);
+
+    _toggle_rows_cols();
+
+    e_config_save_queue();
+}
+
+
+/* }}} */
+/* Reorganize windows {{{*/
+
+static void
+_add_border(E_Border *bd)
+{
+    Border_Extra *extra;
+    int stack;
+
+    if (!bd) {
+        return;
+    }
+    if (is_floating_window(bd)) {
+        return;
+    }
+    if (!is_tilable(bd)) {
+        return;
+    }
+    if (bd->fullscreen) {
+         return;
+    }
+
+    if (!_G.tinfo || !_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return;
+    }
+
+    extra = _get_or_create_border_extra(bd);
+
+    /* Stack tiled window below so that winlist doesn't mix up stacking */
+    e_border_layer_set(bd, 75);
+    e_hints_window_stacking_set(bd, E_STACKING_BELOW);
+
+    DBG("adding %p", bd);
+
+    if (_G.tinfo->stacks[0]) {
+        DBG("got stack 0");
+        if (_G.tinfo->stacks[_G.tinfo->conf->nb_stacks - 1]) {
+            DBG("using last stack");
+            stack = _G.tinfo->conf->nb_stacks - 1;
+
+            if (!_G.tinfo->stacks[stack]->next) {
+                _e_border_unmaximize(_G.tinfo->stacks[stack]->data,
+                                    E_MAXIMIZE_BOTH);
+            }
+            EINA_LIST_APPEND(_G.tinfo->stacks[stack], bd);
+            _reorganize_stack(stack);
+            if (bd->maximized)
+                _e_border_unmaximize(bd, E_MAXIMIZE_BOTH);
+        } else {
+            /* Add stack */
+            int nb_stacks = get_stack_count();
+            int x, y, w, h;
+            int pos, s, size = 0;
+
+            DBG("add stack");
+
+            e_zone_useful_geometry_get(bd->zone, &x, &y, &w, &h);
+
+            if (_G.tinfo->conf->use_rows) {
+                pos = y;
+                s = h;
+            } else {
+                pos = x;
+                s = w;
+            }
+
+            EINA_LIST_APPEND(_G.tinfo->stacks[nb_stacks], bd);
+
+            for (int i = 0; i < nb_stacks; i++) {
+
+                size = s / (nb_stacks + 1 - i);
+
+                _set_stack_geometry(i, pos, size);
+
+                s -= size;
+                pos += size;
+            }
+
+            _G.tinfo->pos[nb_stacks] = pos;
+            _G.tinfo->size[nb_stacks] = size;
+            if (_G.tinfo->conf->use_rows) {
+                extra->expected.x = x;
+                extra->expected.y = pos;
+                extra->expected.w = w;
+                extra->expected.h = size;
+                _e_border_maximize(bd, E_MAXIMIZE_EXPAND |
+                                      E_MAXIMIZE_HORIZONTAL);
+            } else {
+                extra->expected.x = pos;
+                extra->expected.y = y;
+                extra->expected.w = size;
+                extra->expected.h = h;
+                _e_border_maximize(bd, E_MAXIMIZE_EXPAND |
+                                      E_MAXIMIZE_VERTICAL);
+            }
+            _e_border_move_resize(bd,
+                                  extra->expected.x,
+                                  extra->expected.y,
+                                  extra->expected.w,
+                                  extra->expected.h);
+
+            stack = nb_stacks;
+        }
+    } else {
+        DBG("lonely window");
+        e_zone_useful_geometry_get(bd->zone,
+                                   &extra->expected.x,
+                                   &extra->expected.y,
+                                   &extra->expected.w,
+                                   &extra->expected.h);
+
+        if (bd->maximized & E_MAXIMIZE_BOTH)
+            _e_border_unmaximize(bd, E_MAXIMIZE_BOTH);
+        _e_border_move_resize(bd,
+                              extra->expected.x,
+                              extra->expected.y,
+                              extra->expected.w,
+                              extra->expected.h);
+        _e_border_maximize(bd, E_MAXIMIZE_EXPAND | E_MAXIMIZE_BOTH);
+        EINA_LIST_APPEND(_G.tinfo->stacks[0], bd);
+        if (_G.tinfo->conf->use_rows) {
+            e_zone_useful_geometry_get(bd->zone,
+                                       NULL, &_G.tinfo->pos[0],
+                                       NULL, &_G.tinfo->size[0]);
+        } else {
+            e_zone_useful_geometry_get(bd->zone,
+                                       &_G.tinfo->pos[0], NULL,
+                                       &_G.tinfo->size[0], NULL);
+        }
+        stack = 0;
+    }
+    DBG("expected: %dx%d+%d+%d (%p)",
+        extra->expected.w,
+        extra->expected.h,
+        extra->expected.x,
+        extra->expected.y,
+        bd);
+}
+
+static void
+_remove_border(E_Border *bd)
+{
+    int stack;
+    int nb_stacks;
+
+    nb_stacks = get_stack_count();
+
+    stack = get_stack(bd);
+    if (stack < 0)
+        return;
+
+    DBG("removing %p (%d%c)", bd, stack, _G.tinfo->conf->use_rows? 'r':'c');
+
+    EINA_LIST_REMOVE(_G.tinfo->stacks[stack], bd);
+    eina_hash_del(_G.border_extras, bd, NULL);
+
+    if (_G.tinfo->stacks[stack]) {
+        _reorganize_stack(stack);
+    } else {
+        int nb_borders = get_window_count();
+
+        if (nb_stacks > nb_borders) {
+            int pos, s;
+            /* Remove stack */
+
+            nb_stacks--;
+
+            for (int i = stack; i < nb_stacks; i++) {
+                _G.tinfo->stacks[i] = _G.tinfo->stacks[i+1];
+            }
+            _G.tinfo->stacks[nb_stacks] = NULL;
+            if (_G.tinfo->conf->use_rows) {
+                e_zone_useful_geometry_get(bd->zone,
+                                           NULL, &pos, NULL, &s);
+            } else {
+                e_zone_useful_geometry_get(bd->zone,
+                                           &pos, NULL, &s, NULL);
+            }
+            for (int i = 0; i < nb_stacks; i++) {
+                int size;
+
+                size = s / (nb_stacks - i);
+
+                _set_stack_geometry(i, pos, size);
+
+                s -= size;
+                pos += size;
+            }
+        } else {
+            for (int i = stack+1; i < nb_stacks; i++) {
+                if (eina_list_count(_G.tinfo->stacks[i]) > 1) {
+                    for (int j = stack; j < i - 1; j++) {
+                        _G.tinfo->stacks[j] = _G.tinfo->stacks[j+1];
+                        _reorganize_stack(j);
+                    }
+                    bd = _G.tinfo->stacks[i]->data;
+                    EINA_LIST_REMOVE(_G.tinfo->stacks[i], bd);
+                    _reorganize_stack(i);
+
+                    _G.tinfo->stacks[i-1] = NULL;
+                    EINA_LIST_APPEND(_G.tinfo->stacks[i-1], bd);
+                    _reorganize_stack(i-1);
+                    return;
+                }
+            }
+            for (int i = stack-1; i >= 0; i--) {
+                if (eina_list_count(_G.tinfo->stacks[i]) == 1) {
+                    _G.tinfo->stacks[i+1] = _G.tinfo->stacks[i];
+                    _reorganize_stack(i+1);
+                } else {
+                    bd = eina_list_last(_G.tinfo->stacks[i])->data;
+                    EINA_LIST_REMOVE(_G.tinfo->stacks[i], bd);
+                    _reorganize_stack(i);
+
+                    _G.tinfo->stacks[i+1] = NULL;
+                    EINA_LIST_APPEND(_G.tinfo->stacks[i+1], bd);
+                    _reorganize_stack(i+1);
+                    return;
+                }
+            }
+        }
+    }
+}
+
+static void
+_move_resize_border_stack(E_Border *bd, Border_Extra *extra,
+                          int stack, tiling_change_t change)
+{
+#define _MOVE_RESIZE_BORDER_STACK(_pos, _size)                               \
+    if (change == TILING_RESIZE) {                                           \
+        if (stack == TILING_MAX_STACKS || !_G.tinfo->stacks[stack + 1]) {    \
+            /* You're not allowed to resize */                               \
+            bd->_size = extra->expected._size;                               \
+        } else {                                                             \
+            int delta = bd->_size - extra->expected._size;                   \
+                                                                             \
+            if (delta + 1 > _G.tinfo->size[stack + 1])                       \
+                delta = _G.tinfo->size[stack + 1] - 1;                       \
+                                                                             \
+            _move_resize_stack(stack, 0, delta);                             \
+            _move_resize_stack(stack + 1, delta, -delta);                    \
+            extra->expected._size = bd->_size;                               \
+        }                                                                    \
+    } else {                                                                 \
+        if (stack == 0) {                                                    \
+            /* You're not allowed to move */                                 \
+            bd->_pos = extra->expected._pos;                                 \
+        } else {                                                             \
+            int delta = bd->_pos - extra->expected._pos;                     \
+                                                                             \
+            if (delta + 1 > _G.tinfo->size[stack - 1])                       \
+                delta = _G.tinfo->size[stack - 1] - 1;                       \
+                                                                             \
+            _move_resize_stack(stack, delta, -delta);                        \
+            _move_resize_stack(stack - 1, 0, delta);                         \
+            extra->expected._pos = bd->_pos;                                 \
+        }                                                                    \
+    }
+    if (_G.tinfo->conf->use_rows) {
+        _MOVE_RESIZE_BORDER_STACK(y, h)
+    } else {
+        _MOVE_RESIZE_BORDER_STACK(x, w)
+    }
+#undef _MOVE_RESIZE_BORDER_STACK
+}
+
+static void
+_move_resize_border_in_stack(E_Border *bd, Border_Extra *extra,
+                              int stack, tiling_change_t change)
+{
+    Eina_List *l;
+
+    l = eina_list_data_find_list(_G.tinfo->stacks[stack], bd);
+    if (!l) {
+        ERR("unable to bd %p in stack %d", bd, stack);
+        return;
+    }
+
+    switch (change) {
+      case TILING_RESIZE:
+        if (!l->next) {
+            if (l->prev) {
+                E_Border *prevbd = l->prev->data;
+                Border_Extra *prevextra;
+
+                prevextra = eina_hash_find(_G.border_extras, &prevbd);
+                if (!prevextra) {
+                    ERR("No extra for %p", prevbd);
+                    return;
+                }
+
+                if (_G.tinfo->conf->use_rows) {
+                    int delta;
+
+                    delta = bd->w - extra->expected.w;
+                    prevextra->expected.w -= delta;
+                    extra->expected.x -= delta;
+                    extra->expected.w = bd->w;
+                } else {
+                    int delta;
+
+                    delta = bd->h - extra->expected.h;
+                    prevextra->expected.h -= delta;
+                    extra->expected.y -= delta;
+                    extra->expected.h = bd->h;
+                }
+
+                _e_border_resize(prevbd,
+                                 prevextra->expected.w,
+                                 prevextra->expected.h);
+                _e_border_move(bd,
+                               extra->expected.x,
+                               extra->expected.y);
+            } else {
+                /* You're not allowed to resize */
+                _e_border_resize(bd,
+                                 extra->expected.w,
+                                 extra->expected.h);
+            }
+        } else {
+            E_Border *nextbd = l->next->data;
+            Border_Extra *nextextra;
+
+            nextextra = eina_hash_find(_G.border_extras, &nextbd);
+            if (!nextextra) {
+                ERR("No extra for %p", nextbd);
+                return;
+            }
+
+            if (_G.tinfo->conf->use_rows) {
+                int min_width = MAX(nextbd->client.icccm.base_w, 1);
+                int delta;
+
+                delta = bd->w - extra->expected.w;
+                if (nextextra->expected.w - delta < min_width)
+                    delta = nextextra->expected.w - min_width;
+
+                nextextra->expected.x += delta;
+                nextextra->expected.w -= delta;
+
+                extra->expected.w += delta;
+            } else {
+                int min_height = MAX(nextbd->client.icccm.base_h, 1);
+                int delta;
+
+                delta = bd->h - extra->expected.h;
+                if (nextextra->expected.h - delta < min_height)
+                    delta = nextextra->expected.h - min_height;
+
+                nextextra->expected.y += delta;
+                nextextra->expected.h -= delta;
+
+                extra->expected.h += delta;
+            }
+
+            _e_border_move_resize(nextbd,
+                                  nextextra->expected.x,
+                                  nextextra->expected.y,
+                                  nextextra->expected.w,
+                                  nextextra->expected.h);
+            _e_border_move_resize(bd,
+                                  extra->expected.x,
+                                  extra->expected.y,
+                                  extra->expected.w,
+                                  extra->expected.h);
+        }
+        break;
+      case TILING_MOVE:
+        if (!l->prev) {
+            /* You're not allowed to move */
+            if (_G.tinfo->conf->use_rows) {
+                bd->x = extra->expected.x;
+            } else {
+                bd->y = extra->expected.y;
+            }
+            _e_border_move(bd,
+                           extra->expected.x,
+                           extra->expected.y);
+            DBG("trying to move %p, but !l->prev", bd);
+        } else {
+            E_Border *prevbd = l->prev->data;
+            Border_Extra *prevextra;
+
+            prevextra = eina_hash_find(_G.border_extras, &prevbd);
+            if (!prevextra) {
+                ERR("No extra for %p", prevbd);
+                return;
+            }
+
+            if (_G.tinfo->conf->use_rows) {
+                int delta = bd->x - extra->expected.x;
+                int min_width = MAX(prevbd->client.icccm.base_w, 1);
+
+                if (prevextra->expected.w - delta < min_width)
+                    delta = prevextra->expected.w - min_width;
+
+                prevextra->expected.w += delta;
+
+                extra->expected.x += delta;
+                extra->expected.w -= delta;
+            } else {
+                int delta = bd->y - extra->expected.y;
+                int min_height = MAX(prevbd->client.icccm.base_h, 1);
+
+                if (prevextra->expected.h - delta < min_height)
+                    delta = prevextra->expected.h - min_height;
+
+                prevextra->expected.h += delta;
+
+                extra->expected.y += delta;
+                extra->expected.h -= delta;
+            }
+
+            _e_border_resize(prevbd,
+                             prevextra->expected.w,
+                             prevextra->expected.h);
+            _e_border_move_resize(bd,
+                                  extra->expected.x,
+                                  extra->expected.y,
+                                  extra->expected.w,
+                                  extra->expected.h);
+        }
+        break;
+      default:
+        ERR("invalid tiling change: %d", change);
+    }
+}
+
+/* }}} */
+/* Toggle Floating {{{ */
+
+static void
+toggle_floating(E_Border *bd)
+{
+    if (!bd)
+        return;
+    check_tinfo(bd->desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks)
+        return;
+
+    if (EINA_LIST_IS_IN(_G.tinfo->floating_windows, bd)) {
+        EINA_LIST_REMOVE(_G.tinfo->floating_windows, bd);
+
+        _add_border(bd);
+    } else {
+        _remove_border(bd);
+        _restore_border(bd);
+    }
+}
+
+static void
+_e_mod_action_toggle_floating_cb(E_Object   *obj,
+                                 const char *params)
+{
+    end_special_input();
+
+    toggle_floating(e_border_focused_get());
+}
+
+/* }}} */
+/* {{{ Swap */
+
+static void
+_action_swap(E_Border *bd_1,
+             Border_Extra *extra_2)
+{
+    Border_Extra *extra_1;
+    E_Border *bd_2 = extra_2->border;
+    Eina_List *l_1 = NULL,
+              *l_2 = NULL;
+    geom_t gt;
+    unsigned int bd_2_maximized;
+
+    extra_1 = eina_hash_find(_G.border_extras, &bd_1);
+    if (!extra_1) {
+        ERR("No extra for %p", bd_1);
+        return;
+    }
+
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        if ((l_1 = eina_list_data_find_list(_G.tinfo->stacks[i], bd_1))) {
+            break;
+        }
+    }
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        if ((l_2 = eina_list_data_find_list(_G.tinfo->stacks[i], bd_2))) {
+            break;
+        }
+    }
+
+    if (!l_1 || !l_2) {
+        return;
+    }
+
+    l_1->data = bd_2;
+    l_2->data = bd_1;
+
+    gt = extra_2->expected;
+    extra_2->expected = extra_1->expected;
+    extra_1->expected = gt;
+
+    bd_2_maximized = bd_2->maximized;
+    if (bd_2->maximized)
+        _e_border_unmaximize(bd_2, E_MAXIMIZE_BOTH);
+    if (bd_1->maximized) {
+        _e_border_unmaximize(bd_1, E_MAXIMIZE_BOTH);
+        _e_border_maximize(bd_2, bd_1->maximized);
+    }
+    if (bd_2_maximized) {
+        _e_border_maximize(bd_1, bd_2_maximized);
+    }
+    _e_border_move_resize(bd_1,
+                          extra_1->expected.x,
+                          extra_1->expected.y,
+                          extra_1->expected.w,
+                          extra_1->expected.h);
+    _e_border_move_resize(bd_2,
+                          extra_2->expected.x,
+                          extra_2->expected.y,
+                          extra_2->expected.w,
+                          extra_2->expected.h);
+}
+
+static void
+_e_mod_action_swap_cb(E_Object   *obj,
+                      const char *params)
+{
+    E_Desk *desk;
+    E_Border *focused_bd;
+
+    desk = get_current_desk();
+    if (!desk)
+        return;
+
+    focused_bd = e_border_focused_get();
+    if (!focused_bd || focused_bd->desk != desk)
+        return;
+
+    check_tinfo(desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return;
+    }
+
+    _do_overlay(focused_bd, _action_swap, INPUT_MODE_SWAPPING);
+}
+
+/* }}} */
+/* Move {{{*/
+
+static void
+_check_moving_anims(const E_Border *bd, const Border_Extra *extra, int stack)
+{
+    Eina_List *l = NULL;
+    overlay_t *overlay;
+    int nb_stacks = get_stack_count();
+
+    if (stack < 0) {
+        stack = get_stack(bd);
+        if (stack < 0)
+            return;
+    }
+    if (!extra) {
+        extra = eina_hash_find(_G.border_extras, &bd);
+        if (!extra) {
+            ERR("No extra for %p", bd);
+            return;
+        }
+    }
+    l = eina_list_data_find_list(_G.tinfo->stacks[stack], bd);
+    if (!l)
+        return;
+
+    /* move left */
+    overlay = &_G.move_overlays[MOVE_LEFT];
+    if ((!_G.tinfo->conf->use_rows && stack > 0)
+    ||  (_G.tinfo->conf->use_rows && l->prev)) {
+        if (overlay->popup) {
+            Evas_Coord ew, eh;
+
+            edje_object_size_min_calc(overlay->obj, &ew, &eh);
+            e_popup_move(_G.move_overlays[MOVE_LEFT].popup,
+                         extra->expected.x - ew/2,
+                         extra->expected.y + extra->expected.h/2 - eh/2);
+        } else {
+            Evas_Coord ew, eh;
+
+            overlay->popup = e_popup_new(bd->zone, 0, 0, 1, 1);
+            if (!overlay->popup)
+                return;
+
+            e_popup_layer_set(overlay->popup, TILING_POPUP_LAYER);
+            overlay->obj = edje_object_add(overlay->popup->evas);
+            _theme_edje_object_set(overlay->obj,
+                                   "modules/tiling/move/left");
+            edje_object_size_min_calc(overlay->obj, &ew, &eh);
+            e_popup_edje_bg_object_set(overlay->popup,
+                                       overlay->obj);
+            evas_object_show(overlay->obj);
+            e_popup_move_resize(overlay->popup,
+                                extra->expected.x - ew/2
+                                                  - overlay->popup->zone->x,
+                                extra->expected.y + extra->expected.h/2
+                                                  - eh/2
+                                                  - overlay->popup->zone->y,
+                                ew,
+                                eh);
+            evas_object_resize(overlay->obj, ew, eh);
+
+            e_popup_show(overlay->popup);
+        }
+    } else if (overlay->popup) {
+        if (overlay->obj) {
+            evas_object_del(overlay->obj);
+            overlay->obj = NULL;
+        }
+        if (overlay->popup) {
+            e_object_del(E_OBJECT(overlay->popup));
+            overlay->popup = NULL;
+        }
+    }
+
+    /* move right */
+    overlay = &_G.move_overlays[MOVE_RIGHT];
+    if ((_G.tinfo->conf->use_rows && l->next)
+    || (!_G.tinfo->conf->use_rows && (
+            stack != TILING_MAX_STACKS - 1
+            && ((stack == nb_stacks - 1 && _G.tinfo->stacks[stack]->next)
+                || (stack != nb_stacks - 1))))) {
+        if (overlay->popup) {
+            Evas_Coord ew, eh;
+
+            edje_object_size_min_calc(overlay->obj, &ew, &eh);
+            e_popup_move(_G.move_overlays[MOVE_RIGHT].popup,
+                         extra->expected.x + extra->expected.w - ew/2,
+                         extra->expected.y + extra->expected.h/2 - eh/2);
+        } else {
+            Evas_Coord ew, eh;
+
+            overlay->popup = e_popup_new(bd->zone, 0, 0, 1, 1);
+            if (!overlay->popup)
+                return;
+
+            e_popup_layer_set(overlay->popup, TILING_POPUP_LAYER);
+            overlay->obj = edje_object_add(overlay->popup->evas);
+            _theme_edje_object_set(overlay->obj,
+                                   "modules/tiling/move/right");
+            edje_object_size_min_calc(overlay->obj, &ew, &eh);
+            e_popup_edje_bg_object_set(overlay->popup,
+                                       overlay->obj);
+            evas_object_show(overlay->obj);
+            e_popup_move_resize(overlay->popup,
+                                extra->expected.x + extra->expected.w - ew/2
+                                                  - overlay->popup->zone->x,
+                                extra->expected.y + extra->expected.h/2
+                                                  - eh/2
+                                                  - overlay->popup->zone->y,
+                                ew,
+                                eh);
+            evas_object_resize(overlay->obj, ew, eh);
+
+            e_popup_show(overlay->popup);
+        }
+    } else if (overlay->popup) {
+        if (overlay->obj) {
+            evas_object_del(overlay->obj);
+            overlay->obj = NULL;
+        }
+        if (overlay->popup) {
+            e_object_del(E_OBJECT(overlay->popup));
+            overlay->popup = NULL;
+        }
+    }
+
+    /* move up */
+    overlay = &_G.move_overlays[MOVE_UP];
+    if ((!_G.tinfo->conf->use_rows && l->prev)
+    ||  (_G.tinfo->conf->use_rows && stack > 0)) {
+        if (overlay->popup) {
+            Evas_Coord ew, eh;
+
+            edje_object_size_min_calc(overlay->obj, &ew, &eh);
+            e_popup_move(_G.move_overlays[MOVE_UP].popup,
+                         extra->expected.x + extra->expected.w/2 - ew/2,
+                         extra->expected.y - eh/2);
+        } else {
+            Evas_Coord ew, eh;
+
+            overlay->popup = e_popup_new(bd->zone, 0, 0, 1, 1);
+            if (!overlay->popup)
+                return;
+
+            e_popup_layer_set(overlay->popup, TILING_POPUP_LAYER);
+            overlay->obj = edje_object_add(overlay->popup->evas);
+            _theme_edje_object_set(overlay->obj, "modules/tiling/move/up");
+            edje_object_size_min_calc(overlay->obj, &ew, &eh);
+            e_popup_edje_bg_object_set(overlay->popup,
+                                       overlay->obj);
+            evas_object_show(overlay->obj);
+            e_popup_move_resize(overlay->popup,
+                                extra->expected.x + extra->expected.w/2
+                                                  - ew/2
+                                                  - overlay->popup->zone->x,
+                                extra->expected.y - eh/2
+                                                  - overlay->popup->zone->y,
+                                ew,
+                                eh);
+            evas_object_resize(overlay->obj, ew, eh);
+
+            e_popup_show(overlay->popup);
+        }
+    } else if (overlay->popup) {
+        if (overlay->obj) {
+            evas_object_del(overlay->obj);
+            overlay->obj = NULL;
+        }
+        if (overlay->popup) {
+            e_object_del(E_OBJECT(overlay->popup));
+            overlay->popup = NULL;
+        }
+    }
+
+    /* move down */
+    overlay = &_G.move_overlays[MOVE_DOWN];
+    if ((!_G.tinfo->conf->use_rows && l->next)
+    || (_G.tinfo->conf->use_rows && (
+            stack != TILING_MAX_STACKS - 1
+            && ((stack == nb_stacks - 1 && _G.tinfo->stacks[stack]->next)
+                || (stack != nb_stacks - 1))))) {
+        if (overlay->popup) {
+            Evas_Coord ew, eh;
+
+            edje_object_size_min_calc(overlay->obj, &ew, &eh);
+            e_popup_move(_G.move_overlays[MOVE_DOWN].popup,
+                         extra->expected.x + extra->expected.w/2 - ew/2,
+                         extra->expected.y + extra->expected.h - eh/2);
+        } else {
+            Evas_Coord ew, eh;
+
+            overlay->popup = e_popup_new(bd->zone, 0, 0, 1, 1);
+            if (!overlay->popup)
+                return;
+
+            e_popup_layer_set(overlay->popup, TILING_POPUP_LAYER);
+            overlay->obj = edje_object_add(overlay->popup->evas);
+            _theme_edje_object_set(overlay->obj,
+                                   "modules/tiling/move/down");
+            edje_object_size_min_calc(overlay->obj, &ew, &eh);
+            e_popup_edje_bg_object_set(overlay->popup,
+                                       overlay->obj);
+            evas_object_show(overlay->obj);
+            e_popup_move_resize(overlay->popup,
+                                extra->expected.x + extra->expected.w/2
+                                                  - ew/2
+                                                  - overlay->popup->zone->x,
+                                extra->expected.y + extra->expected.h - eh/2
+                                                  - overlay->popup->zone->y,
+                                ew,
+                                eh);
+            evas_object_resize(overlay->obj, ew, eh);
+
+            e_popup_show(overlay->popup);
+        }
+    } else if (overlay->popup) {
+        if (overlay->obj) {
+            evas_object_del(overlay->obj);
+            overlay->obj = NULL;
+        }
+        if (overlay->popup) {
+            e_object_del(E_OBJECT(overlay->popup));
+            overlay->popup = NULL;
+        }
+    }
+}
+
+static void
+_move_up_cols(void)
+{
+    E_Border *bd_1 = _G.focused_bd,
+             *bd_2 = NULL;
+    Border_Extra *extra_1 = NULL,
+                 *extra_2 = NULL;
+    Eina_List *l_1 = NULL,
+              *l_2 = NULL;
+    int stack;
+
+    stack = get_stack(_G.focused_bd);
+    if (stack < 0)
+        return;
+
+    if (_G.tinfo->stacks[stack]->data == _G.focused_bd)
+        return;
+
+    l_1 = eina_list_data_find_list(_G.tinfo->stacks[stack], bd_1);
+    if (!l_1 || !l_1->prev)
+        return;
+    l_2 = l_1->prev;
+    bd_2 = l_2->data;
+
+    extra_1 = eina_hash_find(_G.border_extras, &bd_1);
+    if (!extra_1) {
+        ERR("No extra for %p", bd_1);
+        return;
+    }
+    extra_2 = eina_hash_find(_G.border_extras, &bd_2);
+    if (!extra_2) {
+        ERR("No extra for %p", bd_2);
+        return;
+    }
+
+    l_1->data = bd_2;
+    l_2->data = bd_1;
+
+    extra_1->expected.y = extra_2->expected.y;
+    extra_2->expected.y += extra_1->expected.h;
+
+    _e_border_move(bd_1,
+                   extra_1->expected.x,
+                   extra_1->expected.y);
+    _e_border_move(bd_2,
+                   extra_2->expected.x,
+                   extra_2->expected.y);
+
+    _check_moving_anims(bd_1, extra_1, stack);
+    ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                         extra_1->expected.x + extra_1->expected.w/2,
+                         extra_1->expected.y + extra_1->expected.h/2);
+}
+
+static void
+_move_down_cols(void)
+{
+    E_Border *bd_1 = _G.focused_bd,
+             *bd_2 = NULL;
+    Border_Extra *extra_1 = NULL,
+                 *extra_2 = NULL;
+    Eina_List *l_1 = NULL,
+              *l_2 = NULL;
+    int stack;
+
+    stack = get_stack(_G.focused_bd);
+    if (stack < 0)
+        return;
+
+    l_1 = eina_list_data_find_list(_G.tinfo->stacks[stack], bd_1);
+    if (!l_1 || !l_1->next)
+        return;
+    l_2 = l_1->next;
+    bd_2 = l_2->data;
+
+    extra_1 = eina_hash_find(_G.border_extras, &bd_1);
+    if (!extra_1) {
+        ERR("No extra for %p", bd_1);
+        return;
+    }
+    extra_2 = eina_hash_find(_G.border_extras, &bd_2);
+    if (!extra_2) {
+        ERR("No extra for %p", bd_2);
+        return;
+    }
+
+    l_1->data = bd_2;
+    l_2->data = bd_1;
+
+    extra_2->expected.y = extra_1->expected.y;
+    extra_1->expected.y += extra_2->expected.h;
+
+    _e_border_move(bd_1,
+                   extra_1->expected.x,
+                   extra_1->expected.y);
+    _e_border_move(bd_2,
+                   extra_2->expected.x,
+                   extra_2->expected.y);
+
+    _check_moving_anims(bd_1, extra_1, stack);
+    ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                         extra_1->expected.x + extra_1->expected.w/2,
+                         extra_1->expected.y + extra_1->expected.h/2);
+}
+
+static void
+_move_left_cols(void)
+{
+    E_Border *bd = _G.focused_bd;
+    Border_Extra *extra;
+    int stack;
+
+    stack = get_stack(_G.focused_bd);
+    if (stack <= 0)
+        return;
+
+
+    EINA_LIST_REMOVE(_G.tinfo->stacks[stack], bd);
+    EINA_LIST_APPEND(_G.tinfo->stacks[stack - 1], bd);
+
+    if (!_G.tinfo->stacks[stack]) {
+        int x, y, w, h;
+        int width = 0;
+        int nb_stacks;
+
+        /* Remove stack */
+        nb_stacks = get_stack_count();
+
+        e_zone_useful_geometry_get(bd->zone, &x, &y, &w, &h);
+
+        for (int i = stack; i < nb_stacks; i++) {
+            _G.tinfo->stacks[i] = _G.tinfo->stacks[i+1];
+        }
+        _G.tinfo->stacks[nb_stacks] = NULL;
+        for (int i = 0; i < nb_stacks; i++) {
+
+            width = w / (nb_stacks - i);
+
+            _set_stack_geometry(i, x, width);
+
+            w -= width;
+            x += width;
+        }
+        _reorganize_stack(stack - 1);
+    } else {
+        _reorganize_stack(stack);
+        _reorganize_stack(stack - 1);
+    }
+
+    extra = eina_hash_find(_G.border_extras, &bd);
+    if (!extra) {
+        ERR("No extra for %p", bd);
+        return;
+    }
+
+    _check_moving_anims(bd, extra, stack - 1);
+    ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                         extra->expected.x + extra->expected.w/2,
+                         extra->expected.y + extra->expected.h/2);
+}
+
+static void
+_move_right_cols(void)
+{
+    E_Border *bd = _G.focused_bd;
+    int stack;
+    int nb_stacks;
+    Border_Extra *extra;
+
+    stack = get_stack(bd);
+    if (stack == TILING_MAX_STACKS - 1)
+        return;
+
+    nb_stacks = get_stack_count();
+    if (stack == nb_stacks - 1 && !_G.tinfo->stacks[stack]->next)
+        return;
+
+    extra = eina_hash_find(_G.border_extras, &bd);
+    if (!extra) {
+        ERR("No extra for %p", bd);
+        return;
+    }
+
+    EINA_LIST_REMOVE(_G.tinfo->stacks[stack], bd);
+    EINA_LIST_APPEND(_G.tinfo->stacks[stack + 1], bd);
+
+    if (_G.tinfo->stacks[stack] && _G.tinfo->stacks[stack + 1]->next) {
+        _reorganize_stack(stack);
+        _reorganize_stack(stack + 1);
+        _check_moving_anims(bd, extra, stack + 1);
+    } else
+    if (_G.tinfo->stacks[stack]) {
+        /* Add stack */
+        int x, y, w, h;
+        int width = 0;
+
+        _reorganize_stack(stack);
+
+        e_zone_useful_geometry_get(bd->zone, &x, &y, &w, &h);
+
+        for (int i = 0; i < nb_stacks; i++) {
+
+            width = w / (nb_stacks + 1 - i);
+
+            _set_stack_geometry(i, x, width);
+
+            w -= width;
+            x += width;
+        }
+
+        _G.tinfo->pos[nb_stacks] = x;
+        _G.tinfo->size[nb_stacks] = width;
+        extra->expected.x = x;
+        extra->expected.y = y;
+        extra->expected.w = width;
+        extra->expected.h = h;
+        _e_border_move_resize(bd,
+                              extra->expected.x,
+                              extra->expected.y,
+                              extra->expected.w,
+                              extra->expected.h);
+        _e_border_maximize(bd, E_MAXIMIZE_EXPAND | E_MAXIMIZE_VERTICAL);
+
+        if (nb_stacks + 1 > _G.tinfo->conf->nb_stacks) {
+            _G.tinfo->conf->nb_stacks = nb_stacks + 1;
+            e_config_save_queue();
+        }
+        _check_moving_anims(bd, extra, stack + 1);
+    } else {
+        int x, y, w, h;
+        int width;
+
+        e_zone_useful_geometry_get(_G.tinfo->desk->zone, &x, &y, &w, &h);
+        for (int i = stack; i < nb_stacks; i++) {
+             _G.tinfo->stacks[i] = _G.tinfo->stacks[i + 1];
+        }
+        nb_stacks--;
+        for (int i = 0; i < nb_stacks; i++) {
+            width = w / (nb_stacks - i);
+
+            _set_stack_geometry(i, x, width);
+
+            w -= width;
+            x += width;
+        }
+        _G.tinfo->stacks[nb_stacks] = NULL;
+        _G.tinfo->pos[nb_stacks] = 0;
+        _G.tinfo->size[nb_stacks] = 0;
+        _reorganize_stack(stack);
+        _check_moving_anims(bd, extra, stack);
+    }
+
+    ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                         extra->expected.x + extra->expected.w/2,
+                         extra->expected.y + extra->expected.h/2);
+}
+
+static void
+_move_left_rows(void)
+{
+    E_Border *bd_1 = _G.focused_bd,
+             *bd_2 = NULL;
+    Border_Extra *extra_1 = NULL,
+                 *extra_2 = NULL;
+    Eina_List *l_1 = NULL,
+              *l_2 = NULL;
+    int stack;
+
+    stack = get_stack(_G.focused_bd);
+    if (stack < 0)
+        return;
+
+    if (_G.tinfo->stacks[stack]->data == _G.focused_bd)
+        return;
+
+    l_1 = eina_list_data_find_list(_G.tinfo->stacks[stack], bd_1);
+    if (!l_1 || !l_1->prev)
+        return;
+    l_2 = l_1->prev;
+    bd_2 = l_2->data;
+
+    extra_1 = eina_hash_find(_G.border_extras, &bd_1);
+    if (!extra_1) {
+        ERR("No extra for %p", bd_1);
+        return;
+    }
+    extra_2 = eina_hash_find(_G.border_extras, &bd_2);
+    if (!extra_2) {
+        ERR("No extra for %p", bd_2);
+        return;
+    }
+
+    l_1->data = bd_2;
+    l_2->data = bd_1;
+
+    extra_1->expected.x = extra_2->expected.x;
+    extra_2->expected.x += extra_1->expected.w;
+
+    _e_border_move(bd_1,
+                   extra_1->expected.x,
+                   extra_1->expected.y);
+    _e_border_move(bd_2,
+                   extra_2->expected.x,
+                   extra_2->expected.y);
+
+    _check_moving_anims(bd_1, extra_1, stack);
+    ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                         extra_1->expected.x + extra_1->expected.w/2,
+                         extra_1->expected.y + extra_1->expected.h/2);
+}
+
+static void
+_move_right_rows(void)
+{
+    E_Border *bd_1 = _G.focused_bd,
+             *bd_2 = NULL;
+    Border_Extra *extra_1 = NULL,
+                 *extra_2 = NULL;
+    Eina_List *l_1 = NULL,
+              *l_2 = NULL;
+    int stack;
+
+    stack = get_stack(_G.focused_bd);
+    if (stack < 0)
+        return;
+
+    l_1 = eina_list_data_find_list(_G.tinfo->stacks[stack], bd_1);
+    if (!l_1 || !l_1->next)
+        return;
+    l_2 = l_1->next;
+    bd_2 = l_2->data;
+
+    extra_1 = eina_hash_find(_G.border_extras, &bd_1);
+    if (!extra_1) {
+        ERR("No extra for %p", bd_1);
+        return;
+    }
+    extra_2 = eina_hash_find(_G.border_extras, &bd_2);
+    if (!extra_2) {
+        ERR("No extra for %p", bd_2);
+        return;
+    }
+
+    l_1->data = bd_2;
+    l_2->data = bd_1;
+
+    extra_2->expected.x = extra_1->expected.x;
+    extra_1->expected.x += extra_2->expected.w;
+
+    _e_border_move(bd_1,
+                   extra_1->expected.x,
+                   extra_1->expected.y);
+    _e_border_move(bd_2,
+                   extra_2->expected.x,
+                   extra_2->expected.y);
+
+    _check_moving_anims(bd_1, extra_1, stack);
+    ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                         extra_1->expected.x + extra_1->expected.w/2,
+                         extra_1->expected.y + extra_1->expected.h/2);
+}
+
+static void
+_move_up_rows(void)
+{
+    E_Border *bd = _G.focused_bd;
+    Border_Extra *extra;
+    int stack;
+
+    stack = get_stack(_G.focused_bd);
+    if (stack <= 0)
+        return;
+
+
+    EINA_LIST_REMOVE(_G.tinfo->stacks[stack], bd);
+    EINA_LIST_APPEND(_G.tinfo->stacks[stack - 1], bd);
+
+    if (!_G.tinfo->stacks[stack]) {
+        int x, y, w, h;
+        int nb_stacks;
+
+        /* Remove stack */
+        nb_stacks = get_stack_count();
+
+        e_zone_useful_geometry_get(bd->zone, &x, &y, &w, &h);
+
+        for (int i = stack; i < nb_stacks; i++) {
+            _G.tinfo->stacks[i] = _G.tinfo->stacks[i+1];
+        }
+        _G.tinfo->stacks[nb_stacks] = NULL;
+        for (int i = 0; i < nb_stacks; i++) {
+            int height = 0;
+
+            height = h / (nb_stacks - i);
+
+            _set_stack_geometry(i, y, height);
+
+            h -= height;
+            y += height;
+        }
+        _reorganize_stack(stack - 1);
+    } else {
+        _reorganize_stack(stack);
+        _reorganize_stack(stack - 1);
+    }
+
+    extra = eina_hash_find(_G.border_extras, &bd);
+    if (!extra) {
+        ERR("No extra for %p", bd);
+        return;
+    }
+
+    _check_moving_anims(bd, extra, stack - 1);
+    ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                         extra->expected.x + extra->expected.w/2,
+                         extra->expected.y + extra->expected.h/2);
+}
+
+static void
+_move_down_rows(void)
+{
+    E_Border *bd = _G.focused_bd;
+    int stack;
+    int nb_stacks;
+    Border_Extra *extra;
+
+    stack = get_stack(bd);
+    if (stack == TILING_MAX_STACKS - 1)
+        return;
+
+    nb_stacks = get_stack_count();
+    if (stack == nb_stacks - 1 && !_G.tinfo->stacks[stack]->next)
+        return;
+
+    extra = eina_hash_find(_G.border_extras, &bd);
+    if (!extra) {
+        ERR("No extra for %p", bd);
+        return;
+    }
+
+    EINA_LIST_REMOVE(_G.tinfo->stacks[stack], bd);
+    EINA_LIST_APPEND(_G.tinfo->stacks[stack + 1], bd);
+
+    if (_G.tinfo->stacks[stack] && _G.tinfo->stacks[stack + 1]->next) {
+        _reorganize_stack(stack);
+        _reorganize_stack(stack + 1);
+        _check_moving_anims(bd, extra, stack + 1);
+    } else
+    if (_G.tinfo->stacks[stack]) {
+        /* Add stack */
+        int x, y, w, h;
+        int height = 0;
+
+        _reorganize_stack(stack);
+
+        e_zone_useful_geometry_get(bd->zone, &x, &y, &w, &h);
+
+        for (int i = 0; i < nb_stacks; i++) {
+
+            height = h / (nb_stacks + 1 - i);
+
+            _set_stack_geometry(i, y, height);
+
+            h -= height;
+            y += height;
+        }
+
+        _G.tinfo->pos[nb_stacks] = y;
+        _G.tinfo->size[nb_stacks] = height;
+        extra->expected.x = x;
+        extra->expected.y = y;
+        extra->expected.w = w;
+        extra->expected.h = height;
+        _e_border_move_resize(bd,
+                              extra->expected.x,
+                              extra->expected.y,
+                              extra->expected.w,
+                              extra->expected.h);
+        _e_border_maximize(bd, E_MAXIMIZE_EXPAND | E_MAXIMIZE_HORIZONTAL);
+
+        if (nb_stacks + 1 > _G.tinfo->conf->nb_stacks) {
+            _G.tinfo->conf->nb_stacks = nb_stacks + 1;
+            e_config_save_queue();
+        }
+        _check_moving_anims(bd, extra, stack + 1);
+    } else {
+        int x, y, w, h;
+
+        e_zone_useful_geometry_get(_G.tinfo->desk->zone, &x, &y, &w, &h);
+        for (int i = stack; i < nb_stacks; i++) {
+             _G.tinfo->stacks[i] = _G.tinfo->stacks[i + 1];
+        }
+        nb_stacks--;
+        for (int i = 0; i < nb_stacks; i++) {
+            int height;
+
+            height = h / (nb_stacks - i);
+
+            _set_stack_geometry(i, y, height);
+
+            h -= height;
+            y += height;
+        }
+        _G.tinfo->stacks[nb_stacks] = NULL;
+        _G.tinfo->pos[nb_stacks] = 0;
+        _G.tinfo->size[nb_stacks] = 0;
+        _reorganize_stack(stack);
+        _check_moving_anims(bd, extra, stack);
+    }
+
+    ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                         extra->expected.x + extra->expected.w/2,
+                         extra->expected.y + extra->expected.h/2);
+}
+
+static Eina_Bool
+move_key_down(void *data,
+              int type,
+              void *event)
+{
+    Ecore_Event_Key *ev = event;
+
+    if (ev->event_window != _G.action_input_win)
+        return ECORE_CALLBACK_PASS_ON;
+
+    /* reset timer */
+    ecore_timer_delay(_G.action_timer, TILING_OVERLAY_TIMEOUT
+                      - ecore_timer_pending_get(_G.action_timer));
+
+    if ((strcmp(ev->key, "Up") == 0)
+    ||  (strcmp(ev->key, "k") == 0))
+    {
+        if (_G.tinfo->conf->use_rows)
+            _move_up_rows();
+        else
+            _move_up_cols();
+        return ECORE_CALLBACK_PASS_ON;
+    } else if ((strcmp(ev->key, "Down") == 0)
+           ||  (strcmp(ev->key, "j") == 0))
+    {
+        if (_G.tinfo->conf->use_rows)
+            _move_down_rows();
+        else
+            _move_down_cols();
+        return ECORE_CALLBACK_PASS_ON;
+    } else if ((strcmp(ev->key, "Left") == 0)
+           ||  (strcmp(ev->key, "h") == 0))
+    {
+        if (_G.tinfo->conf->use_rows)
+            _move_left_rows();
+        else
+            _move_left_cols();
+        return ECORE_CALLBACK_PASS_ON;
+    } else if ((strcmp(ev->key, "Right") == 0)
+           ||  (strcmp(ev->key, "l") == 0))
+    {
+        if (_G.tinfo->conf->use_rows)
+            _move_right_rows();
+        else
+            _move_right_cols();
+        return ECORE_CALLBACK_PASS_ON;
+    }
+
+    if (strcmp(ev->key, "Return") == 0)
+        goto stop;
+    if (strcmp(ev->key, "Escape") == 0)
+        goto stop; /* TODO: fallback */
+
+    return ECORE_CALLBACK_PASS_ON;
+stop:
+    end_special_input();
+    return ECORE_CALLBACK_DONE;
+}
+
+static void
+_e_mod_action_move_cb(E_Object   *obj,
+                      const char *params)
+{
+    E_Desk *desk;
+    E_Border *focused_bd;
+    Ecore_X_Window parent;
+
+    desk = get_current_desk();
+    if (!desk)
+        return;
+
+    focused_bd = e_border_focused_get();
+    if (!focused_bd || focused_bd->desk != desk)
+        return;
+
+    check_tinfo(desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return;
+    }
+
+    _G.focused_bd = focused_bd;
+
+    _G.input_mode = INPUT_MODE_MOVING;
+
+    /* Get input */
+    parent = focused_bd->zone->container->win;
+    _G.action_input_win = ecore_x_window_input_new(parent, 0, 0, 1, 1);
+    if (!_G.action_input_win) {
+        end_special_input();
+        return;
+    }
+
+    ecore_x_window_show(_G.action_input_win);
+    if (!e_grabinput_get(_G.action_input_win, 0, _G.action_input_win)) {
+        end_special_input();
+        return;
+    }
+    _G.action_timer = ecore_timer_add(TILING_OVERLAY_TIMEOUT,
+                                      _timeout_cb, NULL);
+
+    _G.handler_key = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN,
+                                             move_key_down, NULL);
+    _check_moving_anims(focused_bd, NULL, -1);
+}
+
+/* }}} */
+/* Adjust Transitions {{{ */
+
+static void
+_transition_overlays_free_cb(void *data)
+{
+    transition_overlay_t *trov = data;
+
+    if (trov->overlay.obj) {
+        evas_object_del(trov->overlay.obj);
+        trov->overlay.obj = NULL;
+    }
+    if (trov->overlay.popup) {
+        e_object_del(E_OBJECT(trov->overlay.popup));
+        trov->overlay.popup = NULL;
+    }
+    if (trov != _G.transition_overlay) {
+        E_FREE(trov);
+    }
+}
+
+static void
+_transition_move_cols(tiling_move_t direction)
+{
+    int delta = TILING_RESIZE_STEP;
+    int stack;
+    E_Popup *popup = NULL;
+
+    if (!_G.transition_overlay)
+        return;
+
+    stack = _G.transition_overlay->stack;
+
+    if (_G.transition_overlay->bd) {
+        Eina_List *l = NULL;
+        E_Border *bd = _G.transition_overlay->bd,
+                 *nextbd = NULL;
+        Border_Extra *extra = NULL,
+                     *nextextra = NULL;
+        int min_height = 0;
+
+        l = eina_list_data_find_list(_G.tinfo->stacks[stack], bd);
+        if (!l) {
+            ERR("unable to bd %p in stack %d", bd, stack);
+            return;
+        }
+
+        extra = eina_hash_find(_G.border_extras, &bd);
+        if (!extra) {
+            ERR("No extra for %p", bd);
+            return;
+        }
+        nextbd = l->next->data;
+        nextextra = eina_hash_find(_G.border_extras, &nextbd);
+        if (!nextextra) {
+            ERR("No extra for %p", nextbd);
+            return;
+        }
+
+        if (direction == MOVE_UP) {
+            delta *= -1;
+        }
+
+        nextbd = l->next->data;
+        min_height = MAX(nextbd->client.icccm.base_h, 1);
+
+        if (nextextra->expected.h - delta < min_height)
+            delta = nextextra->expected.h - min_height;
+
+        nextextra->expected.y += delta;
+        nextextra->expected.h -= delta;
+        _e_border_move_resize(nextbd,
+                              nextextra->expected.x,
+                              nextextra->expected.y,
+                              nextextra->expected.w,
+                              nextextra->expected.h);
+
+        extra->expected.h += delta;
+        _e_border_move_resize(bd,
+                              extra->expected.x,
+                              extra->expected.y,
+                              extra->expected.w,
+                              extra->expected.h);
+
+        popup = _G.transition_overlay->overlay.popup;
+        e_popup_move(popup, popup->x, popup->y + delta);
+    } else {
+
+        if (stack == TILING_MAX_STACKS || !_G.tinfo->stacks[stack + 1]) {
+            return;
+        }
+        if (direction == MOVE_LEFT) {
+            delta *= -1;
+        }
+
+        if (delta + 1 > _G.tinfo->size[stack + 1])
+            delta = _G.tinfo->size[stack + 1] - 1;
+
+        _move_resize_stack(stack, 0, delta);
+        _move_resize_stack(stack+1, delta, -delta);
+
+        popup = _G.transition_overlay->overlay.popup;
+        e_popup_move(popup, popup->x + delta, popup->y);
+    }
+}
+
+static void
+_transition_move_rows(tiling_move_t direction)
+{
+    int delta = TILING_RESIZE_STEP;
+    int stack;
+    E_Popup *popup = NULL;
+
+    if (!_G.transition_overlay)
+        return;
+
+    stack = _G.transition_overlay->stack;
+
+    if (_G.transition_overlay->bd) {
+        Eina_List *l = NULL;
+        E_Border *bd = _G.transition_overlay->bd,
+                 *nextbd = NULL;
+        Border_Extra *extra = NULL,
+                     *nextextra = NULL;
+        int min_width = 0;
+
+        l = eina_list_data_find_list(_G.tinfo->stacks[stack], bd);
+        if (!l) {
+            ERR("unable to bd %p in stack %d", bd, stack);
+            return;
+        }
+
+        extra = eina_hash_find(_G.border_extras, &bd);
+        if (!extra) {
+            ERR("No extra for %p", bd);
+            return;
+        }
+        nextbd = l->next->data;
+        nextextra = eina_hash_find(_G.border_extras, &nextbd);
+        if (!nextextra) {
+            ERR("No extra for %p", nextbd);
+            return;
+        }
+
+        if (direction == MOVE_LEFT) {
+            delta *= -1;
+        }
+
+        nextbd = l->next->data;
+        min_width = MAX(nextbd->client.icccm.base_w, 1);
+
+        if (nextextra->expected.w - delta < min_width)
+            delta = nextextra->expected.w - min_width;
+
+        nextextra->expected.x += delta;
+        nextextra->expected.w -= delta;
+        _e_border_move_resize(nextbd,
+                              nextextra->expected.x,
+                              nextextra->expected.y,
+                              nextextra->expected.w,
+                              nextextra->expected.h);
+
+        extra->expected.w += delta;
+        _e_border_move_resize(bd,
+                              extra->expected.x,
+                              extra->expected.y,
+                              extra->expected.w,
+                              extra->expected.h);
+
+        popup = _G.transition_overlay->overlay.popup;
+        e_popup_move(popup, popup->x + delta, popup->y);
+    } else {
+
+        if (stack == TILING_MAX_STACKS || !_G.tinfo->stacks[stack + 1]) {
+            return;
+        }
+        if (direction == MOVE_UP) {
+            delta *= -1;
+        }
+
+        if (delta + 1 > _G.tinfo->size[stack + 1])
+            delta = _G.tinfo->size[stack + 1] - 1;
+
+        _move_resize_stack(stack, 0, delta);
+        _move_resize_stack(stack+1, delta, -delta);
+
+        popup = _G.transition_overlay->overlay.popup;
+        e_popup_move(popup, popup->x, popup->y + delta);
+    }
+}
+
+static Eina_Bool
+_transition_overlay_key_down(void *data,
+                             int type,
+                             void *event)
+{
+    Ecore_Event_Key *ev = event;
+
+    if (ev->event_window != _G.action_input_win)
+        return ECORE_CALLBACK_PASS_ON;
+
+    if (strcmp(ev->key, "Return") == 0)
+        goto stop;
+    if (strcmp(ev->key, "Escape") == 0)
+        goto stop;
+
+    /* reset timer */
+    ecore_timer_delay(_G.action_timer, TILING_OVERLAY_TIMEOUT
+                      - ecore_timer_pending_get(_G.action_timer));
+
+    if (_G.transition_overlay) {
+        DBG("ev->key='%s'; %p %d", ev->key,
+            _G.transition_overlay->bd, _G.tinfo->conf->use_rows);
+        if ((strcmp(ev->key, "Up") == 0)
+            ||  (strcmp(ev->key, "k") == 0))
+        {
+            if (_G.transition_overlay->bd && !_G.tinfo->conf->use_rows) {
+                _transition_move_cols(MOVE_UP);
+                return ECORE_CALLBACK_PASS_ON;
+            } else
+            if (!_G.transition_overlay->bd && _G.tinfo->conf->use_rows) {
+                _transition_move_rows(MOVE_UP);
+                return ECORE_CALLBACK_PASS_ON;
+            }
+        } else
+        if ((strcmp(ev->key, "Down") == 0)
+        ||  (strcmp(ev->key, "j") == 0))
+        {
+            if (_G.transition_overlay->bd && !_G.tinfo->conf->use_rows) {
+                _transition_move_cols(MOVE_DOWN);
+                return ECORE_CALLBACK_PASS_ON;
+            } else
+            if (!_G.transition_overlay->bd && _G.tinfo->conf->use_rows) {
+                _transition_move_rows(MOVE_DOWN);
+                return ECORE_CALLBACK_PASS_ON;
+            }
+        } else
+        if ((strcmp(ev->key, "Left") == 0)
+        ||  (strcmp(ev->key, "h") == 0))
+        {
+            if (!_G.transition_overlay->bd && !_G.tinfo->conf->use_rows) {
+                _transition_move_cols(MOVE_LEFT);
+                return ECORE_CALLBACK_PASS_ON;
+            } else
+            if (_G.transition_overlay->bd && _G.tinfo->conf->use_rows) {
+                _transition_move_rows(MOVE_LEFT);
+                return ECORE_CALLBACK_PASS_ON;
+            }
+        } else
+        if ((strcmp(ev->key, "Right") == 0)
+        ||  (strcmp(ev->key, "l") == 0))
+        {
+            if (!_G.transition_overlay->bd && !_G.tinfo->conf->use_rows) {
+                _transition_move_cols(MOVE_RIGHT);
+                return ECORE_CALLBACK_PASS_ON;
+            } else
+            if (_G.transition_overlay->bd && _G.tinfo->conf->use_rows) {
+                _transition_move_rows(MOVE_RIGHT);
+                return ECORE_CALLBACK_PASS_ON;
+            }
+        }
+
+        return ECORE_CALLBACK_RENEW;
+    } else {
+        if (strcmp(ev->key, "Backspace") == 0) {
+            char *key = _G.keys;
+
+            while (*key)
+                key++;
+            *key = '\0';
+            return ECORE_CALLBACK_RENEW;
+        }
+        if (ev->key[0] && !ev->key[1] && strchr(tiling_g.config->keyhints,
+                                                ev->key[1]))
+        {
+            transition_overlay_t *trov = NULL;
+            E_Border *bd = NULL;
+            Border_Extra *extra = NULL;
+            Evas_Coord ew, eh;
+            char *key = _G.keys;
+
+            while (*key)
+                key++;
+            *key++ = ev->key[0];
+            *key = '\0';
+
+            trov = eina_hash_find(_G.overlays, _G.keys);
+            if (!trov) {
+                return ECORE_CALLBACK_RENEW;
+            }
+            bd = trov->bd;
+
+            _G.transition_overlay = trov;
+            eina_hash_free(_G.overlays);
+            _G.overlays = NULL;
+
+            if (bd) {
+                extra = eina_hash_find(_G.border_extras, &bd);
+                if (!extra) {
+                    ERR("No extra for %p", bd);
+                    goto stop;
+                }
+            }
+            if (!trov->overlay.popup) {
+                trov->overlay.popup = e_popup_new(_G.tinfo->desk->zone,
+                                                  0, 0, 1, 1);
+                e_popup_layer_set(trov->overlay.popup, TILING_POPUP_LAYER);
+            }
+            if (!trov->overlay.obj) {
+                trov->overlay.obj =
+                    edje_object_add(trov->overlay.popup->evas);
+            }
+            if ((bd && !_G.tinfo->conf->use_rows)
+            ||  (!bd && _G.tinfo->conf->use_rows)) {
+                _theme_edje_object_set(trov->overlay.obj,
+                                       "modules/tiling/transition/horizontal");
+            } else {
+                _theme_edje_object_set(trov->overlay.obj,
+                                       "modules/tiling/transition/vertical");
+            }
+
+            edje_object_size_min_calc(trov->overlay.obj, &ew, &eh);
+            e_popup_edje_bg_object_set(trov->overlay.popup,
+                                       trov->overlay.obj);
+            evas_object_show(trov->overlay.obj);
+            if (bd) {
+                if (_G.tinfo->conf->use_rows) {
+                    e_popup_move_resize(trov->overlay.popup,
+                        (extra->expected.x - trov->overlay.popup->zone->x +
+                            extra->expected.w - (ew / 2)),
+                        (extra->expected.y - trov->overlay.popup->zone->y +
+                            ((extra->expected.h - eh) / 2)),
+                        ew, eh);
+                } else {
+                    e_popup_move_resize(trov->overlay.popup,
+                        (extra->expected.x - trov->overlay.popup->zone->x +
+                            ((extra->expected.w - ew) / 2)),
+                        (extra->expected.y - trov->overlay.popup->zone->y +
+                            extra->expected.h - (eh / 2)),
+                        ew, eh);
+                }
+            } else {
+                if (_G.tinfo->conf->use_rows) {
+                    e_popup_move_resize(trov->overlay.popup,
+                                        (trov->overlay.popup->zone->w/2 - ew/2),
+                                        (_G.tinfo->pos[trov->stack]
+                                         + _G.tinfo->size[trov->stack]
+                                         - trov->overlay.popup->zone->y - eh/2),
+                                        ew, eh);
+                } else {
+                    e_popup_move_resize(trov->overlay.popup,
+                                        (_G.tinfo->pos[trov->stack]
+                                         + _G.tinfo->size[trov->stack]
+                                         - trov->overlay.popup->zone->x - ew/2),
+                                        (trov->overlay.popup->zone->h/2 - eh/2),
+                                        ew, eh);
+                }
+            }
+            evas_object_resize(trov->overlay.obj, ew, eh);
+            e_popup_show(trov->overlay.popup);
+
+            return ECORE_CALLBACK_RENEW;
+        }
+    }
+
+stop:
+    end_special_input();
+    return ECORE_CALLBACK_DONE;
+}
+
+static void
+_do_transition_overlay(void)
+{
+    int nb_transitions;
+    Ecore_X_Window parent;
+    int hints_len;
+    int key_len;
+    int n = 0;
+    int nmax;
+
+    end_special_input();
+
+    nb_transitions = get_transition_count();
+    if (nb_transitions < 1) {
+        return;
+    }
+
+    _G.input_mode = INPUT_MODE_TRANSITION;
+
+    _G.overlays = eina_hash_string_small_new(_transition_overlays_free_cb);
+    hints_len = strlen(tiling_g.config->keyhints);
+    key_len = 1;
+    nmax = hints_len;
+    if (hints_len < nb_transitions) {
+        key_len = 2;
+        nmax *= hints_len;
+        if (hints_len * hints_len < nb_transitions) {
+            key_len = 3;
+            nmax *= hints_len;
+        }
+    }
+
+
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        Eina_List *l;
+        E_Border *bd;
+
+        if (!_G.tinfo->stacks[i])
+            break;
+        EINA_LIST_FOREACH(_G.tinfo->stacks[i], l, bd) {
+            if (l->next && n < nmax) {
+                Border_Extra *extra;
+                Evas_Coord ew, eh;
+                transition_overlay_t *trov;
+
+                extra = eina_hash_find(_G.border_extras, &bd);
+                if (!extra) {
+                    ERR("No extra for %p", bd);
+                    continue;
+                }
+
+                trov = E_NEW(transition_overlay_t, 1);
+
+                trov->overlay.popup = e_popup_new(bd->zone, 0, 0, 1, 1);
+                if (!trov->overlay.popup)
+                    continue;
+
+                e_popup_layer_set(trov->overlay.popup, TILING_POPUP_LAYER);
+                trov->overlay.obj = edje_object_add(trov->overlay.popup->evas);
+                e_theme_edje_object_set(trov->overlay.obj,
+                                        "base/theme/borders",
+                                        "e/widgets/border/default/resize");
+
+                switch (key_len) {
+                  case 1:
+                    trov->key[0] = tiling_g.config->keyhints[n];
+                    trov->key[1] = '\0';
+                    break;
+                  case 2:
+                    trov->key[0] = tiling_g.config->keyhints[n / hints_len];
+                    trov->key[1] = tiling_g.config->keyhints[n % hints_len];
+                    trov->key[2] = '\0';
+                    break;
+                  case 3:
+                    trov->key[0] = tiling_g.config->keyhints[n / hints_len / hints_len];
+                    trov->key[0] = tiling_g.config->keyhints[n / hints_len];
+                    trov->key[1] = tiling_g.config->keyhints[n % hints_len];
+                    trov->key[2] = '\0';
+                    break;
+                }
+                n++;
+                trov->stack = i;
+                trov->bd = bd;
+
+                eina_hash_add(_G.overlays, trov->key, trov);
+                edje_object_part_text_set(trov->overlay.obj,
+                                          "e.text.label",
+                                          trov->key);
+                edje_object_size_min_calc(trov->overlay.obj, &ew, &eh);
+                evas_object_move(trov->overlay.obj, 0, 0);
+                evas_object_resize(trov->overlay.obj, ew, eh);
+                evas_object_show(trov->overlay.obj);
+                e_popup_edje_bg_object_set(trov->overlay.popup,
+                                           trov->overlay.obj);
+
+                evas_object_show(trov->overlay.obj);
+                e_popup_show(trov->overlay.popup);
+
+                if (_G.tinfo->conf->use_rows) {
+                    e_popup_move_resize(trov->overlay.popup,
+                        (extra->expected.x - trov->overlay.popup->zone->x +
+                            extra->expected.w - (ew / 2)),
+                        (extra->expected.y - trov->overlay.popup->zone->y +
+                            ((extra->expected.h - eh) / 2)),
+                        ew, eh);
+                } else {
+                    e_popup_move_resize(trov->overlay.popup,
+                        (extra->expected.x - trov->overlay.popup->zone->x +
+                            ((extra->expected.w - ew) / 2)),
+                        (extra->expected.y - trov->overlay.popup->zone->y +
+                            extra->expected.h - (eh / 2)),
+                        ew, eh);
+                }
+
+                e_popup_show(trov->overlay.popup);
+            }
+        }
+        if (i != TILING_MAX_STACKS && _G.tinfo->stacks[i+1] && n < nmax) {
+            Evas_Coord ew, eh;
+            transition_overlay_t *trov;
+
+            trov = E_NEW(transition_overlay_t, 1);
+
+            trov->overlay.popup = e_popup_new(_G.tinfo->desk->zone,
+                                              0, 0, 1, 1);
+            if (!trov->overlay.popup)
+                continue;
+
+            e_popup_layer_set(trov->overlay.popup, TILING_POPUP_LAYER);
+            trov->overlay.obj = edje_object_add(trov->overlay.popup->evas);
+            e_theme_edje_object_set(trov->overlay.obj,
+                                    "base/theme/borders",
+                                    "e/widgets/border/default/resize");
+
+            switch (key_len) {
+              case 1:
+                trov->key[0] = tiling_g.config->keyhints[n];
+                trov->key[1] = '\0';
+                break;
+              case 2:
+                trov->key[0] = tiling_g.config->keyhints[n / hints_len];
+                trov->key[1] = tiling_g.config->keyhints[n % hints_len];
+                trov->key[2] = '\0';
+                break;
+              case 3:
+                trov->key[0] = tiling_g.config->keyhints[n / hints_len / hints_len];
+                trov->key[0] = tiling_g.config->keyhints[n / hints_len];
+                trov->key[1] = tiling_g.config->keyhints[n % hints_len];
+                trov->key[2] = '\0';
+                break;
+            }
+            n++;
+            trov->stack = i;
+            trov->bd = NULL;
+
+            eina_hash_add(_G.overlays, trov->key, trov);
+            edje_object_part_text_set(trov->overlay.obj,
+                                      "e.text.label",
+                                      trov->key);
+            edje_object_size_min_calc(trov->overlay.obj, &ew, &eh);
+            evas_object_move(trov->overlay.obj, 0, 0);
+            evas_object_resize(trov->overlay.obj, ew, eh);
+            evas_object_show(trov->overlay.obj);
+            e_popup_edje_bg_object_set(trov->overlay.popup,
+                                       trov->overlay.obj);
+
+            evas_object_show(trov->overlay.obj);
+            e_popup_show(trov->overlay.popup);
+
+            if (_G.tinfo->conf->use_rows) {
+                e_popup_move_resize(trov->overlay.popup,
+                                    (trov->overlay.popup->zone->w/2 - ew/2),
+                                    (_G.tinfo->pos[trov->stack]
+                                     + _G.tinfo->size[trov->stack]
+                                     - trov->overlay.popup->zone->y - eh/2),
+                                    ew, eh);
+            } else {
+                e_popup_move_resize(trov->overlay.popup,
+                                    (_G.tinfo->pos[trov->stack]
+                                     + _G.tinfo->size[trov->stack]
+                                     - trov->overlay.popup->zone->x - ew/2),
+                                    (trov->overlay.popup->zone->h/2 - eh/2),
+                                    ew, eh);
+            }
+
+            e_popup_show(trov->overlay.popup);
+        }
+    }
+
+    /* Get input */
+    parent = _G.tinfo->desk->zone->container->win;
+    _G.action_input_win = ecore_x_window_input_new(parent, 0, 0, 1, 1);
+    if (!_G.action_input_win) {
+        end_special_input();
+        return;
+    }
+
+    ecore_x_window_show(_G.action_input_win);
+    if (!e_grabinput_get(_G.action_input_win, 0, _G.action_input_win)) {
+        end_special_input();
+        return;
+    }
+    _G.action_timer = ecore_timer_add(TILING_OVERLAY_TIMEOUT,
+                                      _timeout_cb, NULL);
+
+    _G.keys[0] = '\0';
+    _G.handler_key = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN,
+                                             _transition_overlay_key_down,
+                                             NULL);
+}
+
+static void
+_e_mod_action_adjust_transitions(E_Object   *obj,
+                                 const char *params)
+{
+    E_Desk *desk;
+
+    desk = get_current_desk();
+    if (!desk)
+        return;
+
+    check_tinfo(desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return;
+    }
+
+    _do_transition_overlay();
+}
+
+/* }}} */
+/* Go {{{ */
+
+static Eina_Bool
+_warp_timer(void *_)
+{
+    if (_G.warp_timer) {
+        double spd = TILING_WRAP_SPEED;
+
+        _G.old_warp_x = _G.warp_x;
+        _G.old_warp_y = _G.warp_y;
+        _G.warp_x = (_G.warp_x * (1.0 - spd)) + (_G.warp_to_x * spd);
+        _G.warp_y = (_G.warp_y * (1.0 - spd)) + (_G.warp_to_y * spd);
+
+        ecore_x_pointer_warp(_G.tinfo->desk->zone->container->win,
+                             _G.warp_x, _G.warp_y);
+
+        if (abs(_G.warp_x - _G.old_warp_x) <= 1
+        &&  abs(_G.warp_y - _G.old_warp_y) <= 1) {
+            _G.warp_timer = NULL;
+            return ECORE_CALLBACK_CANCEL;
+        }
+
+        return ECORE_CALLBACK_RENEW;
+    }
+    _G.warp_timer = NULL;
+    return ECORE_CALLBACK_CANCEL;
+}
+
+static void
+_action_go(E_Border *_,
+           Border_Extra *extra_2)
+{
+    E_Border *bd = extra_2->border;
+
+    _G.warp_to_x = bd->x + (bd->w / 2);
+    _G.warp_to_y = bd->y + (bd->h / 2);
+    ecore_x_pointer_xy_get(_G.tinfo->desk->zone->container->win,
+                           &_G.warp_x, &_G.warp_y);
+    e_border_focus_latest_set(bd);
+    _G.warp_timer = ecore_timer_add(0.01, _warp_timer, NULL);
+}
+
+static void
+_e_mod_action_go_cb(E_Object   *obj,
+                    const char *params)
+{
+    E_Desk *desk;
+
+    desk = get_current_desk();
+    if (!desk)
+        return;
+
+    check_tinfo(desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return;
+    }
+
+    _do_overlay(NULL, _action_go, INPUT_MODE_GOING);
+}
+
+/* }}} */
+/* Hooks {{{*/
+
+static void
+_pre_border_assign_hook(void *data,
+                        E_Border *bd)
+{
+    if (tiling_g.config->show_titles)
+        return;
+
+    if (!bd) {
+        return;
+    }
+
+    check_tinfo(bd->desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return;
+    }
+
+    if (is_floating_window(bd)) {
+        return;
+    }
+
+    if (!is_tilable(bd)) {
+        return;
+    }
+
+    if (bd->fullscreen) {
+         return;
+    }
+
+    if ((bd->bordername && strcmp(bd->bordername, "pixel"))
+    ||  !bd->bordername)
+    {
+        change_window_border(bd, "pixel");
+    }
+}
+
+static void _move_or_resize(E_Border *bd)
+{
+    Border_Extra *extra;
+    int stack = -1;
+
+    if (!bd) {
+        return;
+    }
+
+    check_tinfo(bd->desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return;
+    }
+
+    stack = get_stack(bd);
+    if (stack < 0) {
+        return;
+    }
+
+    DBG("Resize: %p / '%s' / '%s', (%d,%d), changes(size=%d, position=%d, border=%d)"
+        " g:%dx%d+%d+%d bdname:'%s' (stack:%d%c) maximized:%s fs:%s",
+        bd, bd->client.icccm.title, bd->client.netwm.name,
+        bd->desk->x, bd->desk->y,
+        bd->changes.size, bd->changes.pos, bd->changes.border,
+        bd->w, bd->h, bd->x, bd->y, bd->bordername,
+        stack, _G.tinfo->conf->use_rows? 'r':'c',
+        (bd->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_NONE ? "NONE" :
+        (bd->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_VERTICAL ? "VERTICAL" :
+        (bd->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_HORIZONTAL ? "HORIZONTAL" :
+        "BOTH", bd->fullscreen? "true": "false");
+
+    extra = eina_hash_find(_G.border_extras, &bd);
+    if (!extra) {
+        ERR("No extra for %p", bd);
+        return;
+    }
+
+    DBG("expected: %dx%d+%d+%d",
+        extra->expected.w,
+        extra->expected.h,
+        extra->expected.x,
+        extra->expected.y);
+    DBG("delta:%dx%d,%d,%d. step:%dx%d. base:%dx%d",
+        bd->w - extra->expected.w, bd->h - extra->expected.h,
+        bd->x - extra->expected.x, bd->y - extra->expected.y,
+        bd->client.icccm.step_w, bd->client.icccm.step_h,
+        bd->client.icccm.base_w, bd->client.icccm.base_h);
+
+    if (stack == 0 && !_G.tinfo->stacks[1] && !_G.tinfo->stacks[0]->next) {
+        if (bd->maximized) {
+            extra->expected.x = bd->x;
+            extra->expected.y = bd->y;
+            extra->expected.w = bd->w;
+            extra->expected.h = bd->h;
+        } else {
+            /* TODO: what if a window doesn't want to be maximized? */
+            _e_border_unmaximize(bd, E_MAXIMIZE_BOTH);
+            _e_border_maximize(bd, E_MAXIMIZE_EXPAND | E_MAXIMIZE_BOTH);
+        }
+    }
+    if (bd->x == extra->expected.x && bd->y == extra->expected.y
+    &&  bd->w == extra->expected.w && bd->h == extra->expected.h)
+    {
+        return;
+    }
+    if (bd->maximized) {
+        bool changed = false;
+
+        if (_G.tinfo->conf->use_rows) {
+            if (stack > 0 && bd->maximized & E_MAXIMIZE_VERTICAL) {
+                 _e_border_unmaximize(bd, E_MAXIMIZE_VERTICAL);
+                 _e_border_move_resize(bd,
+                                       extra->expected.x,
+                                       extra->expected.y,
+                                       extra->expected.w,
+                                       extra->expected.h);
+                 changed = true;
+            }
+            if (bd->maximized & E_MAXIMIZE_HORIZONTAL
+            && eina_list_count(_G.tinfo->stacks[stack]) > 1) {
+                 _e_border_unmaximize(bd, E_MAXIMIZE_HORIZONTAL);
+                 _e_border_move_resize(bd,
+                                       extra->expected.x,
+                                       extra->expected.y,
+                                       extra->expected.w,
+                                       extra->expected.h);
+                 changed = true;
+            }
+        } else {
+            if (stack > 0 && bd->maximized & E_MAXIMIZE_HORIZONTAL) {
+                 _e_border_unmaximize(bd, E_MAXIMIZE_HORIZONTAL);
+                 _e_border_move_resize(bd,
+                                       extra->expected.x,
+                                       extra->expected.y,
+                                       extra->expected.w,
+                                       extra->expected.h);
+                 changed = true;
+            }
+            if (bd->maximized & E_MAXIMIZE_VERTICAL
+            && eina_list_count(_G.tinfo->stacks[stack]) > 1) {
+                 _e_border_unmaximize(bd, E_MAXIMIZE_VERTICAL);
+                 _e_border_move_resize(bd,
+                                       extra->expected.x,
+                                       extra->expected.y,
+                                       extra->expected.w,
+                                       extra->expected.h);
+                 changed = true;
+            }
+        }
+        if (changed)
+            return;
+    }
+
+    if ((bd->changes.border && bd->changes.size)
+        || bd->x <= 0 || bd->y <= 0) {
+        _e_border_move_resize(bd,
+                              extra->expected.x,
+                              extra->expected.y,
+                              extra->expected.w,
+                              extra->expected.h);
+        return;
+    }
+
+    if (abs(extra->expected.w - bd->w) >= bd->client.icccm.step_w) {
+        if (_G.tinfo->conf->use_rows)
+            _move_resize_border_in_stack(bd, extra, stack, TILING_RESIZE);
+        else
+            _move_resize_border_stack(bd, extra, stack, TILING_RESIZE);
+    }
+    if (abs(extra->expected.h - bd->h) >= bd->client.icccm.step_h) {
+        if (_G.tinfo->conf->use_rows)
+            _move_resize_border_stack(bd, extra, stack, TILING_RESIZE);
+        else
+            _move_resize_border_in_stack(bd, extra, stack, TILING_RESIZE);
+    }
+    if (extra->expected.x != bd->x) {
+        if (_G.tinfo->conf->use_rows)
+            _move_resize_border_in_stack(bd, extra, stack, TILING_MOVE);
+        else
+            _move_resize_border_stack(bd, extra, stack, TILING_MOVE);
+    }
+    if (extra->expected.y != bd->y) {
+        if (_G.tinfo->conf->use_rows)
+            _move_resize_border_stack(bd, extra, stack, TILING_MOVE);
+        else
+            _move_resize_border_in_stack(bd, extra, stack, TILING_MOVE);
+    }
+
+    if (_G.input_mode == INPUT_MODE_MOVING
+    &&  bd == _G.focused_bd) {
+        _check_moving_anims(bd, extra, stack);
+    }
+}
+
+static Eina_Bool
+_resize_hook(void *data, int type, E_Event_Border_Resize *event)
+{
+    E_Border *bd = event->border;
+
+    _move_or_resize(bd);
+
+    return true;
+}
+
+static Eina_Bool
+_move_hook(void *data, int type, E_Event_Border_Move*event)
+{
+    E_Border *bd = event->border;
+
+    _move_or_resize(bd);
+
+    return true;
+}
+
+static Eina_Bool
+_add_hook(void *data, int type, E_Event_Border_Add *event)
+{
+    E_Border *bd = event->border;
+    int stack = -1;
+
+    if (_G.input_mode != INPUT_MODE_NONE
+    &&  _G.input_mode != INPUT_MODE_MOVING
+    &&  _G.input_mode != INPUT_MODE_TRANSITION)
+    {
+        end_special_input();
+    }
+
+    check_tinfo(bd->desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return true;
+    }
+
+    if (!is_tilable(bd)) {
+        return true;
+    }
+
+    stack = get_stack(bd);
+    if (stack >= 0) {
+        return true;
+    }
+
+    DBG("Add: %p / '%s' / '%s', (%d,%d), changes(size=%d, position=%d, border=%d)"
+        " g:%dx%d+%d+%d bdname:'%s' (stack:%d%c) maximized:%s fs:%s",
+        bd, bd->client.icccm.title, bd->client.netwm.name,
+        bd->desk->x, bd->desk->y,
+        bd->changes.size, bd->changes.pos, bd->changes.border,
+        bd->w, bd->h, bd->x, bd->y, bd->bordername,
+        stack, _G.tinfo->conf->use_rows? 'r':'c',
+        (bd->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_NONE ? "NONE" :
+        (bd->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_VERTICAL ? "VERTICAL" :
+        (bd->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_HORIZONTAL ? "HORIZONTAL" :
+        "BOTH", bd->fullscreen? "true": "false");
+
+    _add_border(bd);
+
+    return true;
+}
+
+static Eina_Bool
+_remove_hook(void *data, int type, E_Event_Border_Remove *event)
+{
+    E_Border *bd = event->border;
+
+    end_special_input();
+
+    if (_G.currently_switching_desktop)
+        return EINA_TRUE;
+
+    check_tinfo(bd->desk);
+    if (!_G.tinfo->conf)
+        return EINA_TRUE;
+
+    if (EINA_LIST_IS_IN(_G.tinfo->floating_windows, bd)) {
+        EINA_LIST_REMOVE(_G.tinfo->floating_windows, bd);
+        return EINA_TRUE;
+    }
+
+    _remove_border(bd);
+
+    return EINA_TRUE;
+}
+
+static bool
+_iconify_hook(void *_, int type, E_Event_Border_Iconify *event)
+{
+    E_Border *bd = event->border;
+
+    end_special_input();
+
+    check_tinfo(bd->desk);
+    if (!_G.tinfo->conf)
+        return true;
+
+    if (EINA_LIST_IS_IN(_G.tinfo->floating_windows, bd)) {
+        return true;
+    }
+
+    _remove_border(bd);
+
+    return true;
+}
+
+static bool
+_uniconify_hook(void *_, int type, E_Event_Border_Uniconify *event)
+{
+    E_Border *bd = event->border;
+    int stack = -1;
+
+    if (_G.input_mode != INPUT_MODE_NONE
+    &&  _G.input_mode != INPUT_MODE_MOVING
+    &&  _G.input_mode != INPUT_MODE_TRANSITION)
+    {
+        end_special_input();
+    }
+
+    check_tinfo(bd->desk);
+    if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+        return true;
+    }
+
+    if (!is_tilable(bd)) {
+        return true;
+    }
+
+    stack = get_stack(bd);
+    if (stack >= 0) {
+        return true;
+    }
+    _add_border(bd);
+
+    return true;
+}
+
+static Eina_Bool
+_stick_hook(void *data, int type, void *event)
+{
+    DBG("TODO");
+    return true;
+}
+
+static Eina_Bool
+_unstick_hook(void *data, int type, void *event)
+{
+    DBG("TODO");
+    return true;
+}
+
+static Eina_Bool
+_desk_show_hook(void *data, int type, void *event)
+{
+    _G.currently_switching_desktop = 0;
+
+    end_special_input();
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+_desk_before_show_hook(void *data, int type, void *event)
+{
+    end_special_input();
+
+    _G.currently_switching_desktop = 1;
+
+    return EINA_TRUE;
+}
+
+static bool
+_desk_set_hook(void *data, int type, E_Event_Border_Desk_Set *ev)
+{
+    DBG("%p: from (%d,%d) to (%d,%d)", ev->border,
+        ev->desk->x, ev->desk->y,
+        ev->border->desk->x, ev->border->desk->y);
+
+    end_special_input();
+
+    check_tinfo(ev->desk);
+    if (!_G.tinfo->conf) {
+        return true;
+    }
+
+    if (get_stack(ev->border) >= 0) {
+        _remove_border(ev->border);
+        _restore_border(ev->border);
+    }
+
+    check_tinfo(ev->border->desk);
+    if (!_G.tinfo->conf) {
+        return true;
+    }
+
+    if (get_stack(ev->border) < 0)
+        _add_border(ev->border);
+
+    return true;
+}
+
+static bool
+_container_resize_hook(void *_, int type, E_Event_Container_Resize *ev)
+{
+    Eina_List *l;
+    E_Zone *zone;
+
+    EINA_LIST_FOREACH(ev->container->zones, l, zone) {
+        for (int x = 0; x < zone->desk_x_count; x++)
+        {
+            for (int y = 0; y < zone->desk_y_count; y++) {
+                E_Desk *desk = zone->desks[x + (y * zone->desk_x_count)];
+                Eina_List *wins = NULL;
+                E_Border *bd;
+
+                check_tinfo(desk);
+                if (!_G.tinfo->conf || !_G.tinfo->conf->nb_stacks) {
+                    continue;
+                }
+
+                for (int i = 0; i < TILING_MAX_STACKS; i++) {
+                    EINA_LIST_FREE(_G.tinfo->stacks[i], bd) {
+                        EINA_LIST_APPEND(wins, bd);
+                        _restore_border(bd);
+                    }
+                    _G.tinfo->stacks[i] = NULL;
+                    _G.tinfo->pos[i] = 0;
+                    _G.tinfo->size[i] = 0;
+                }
+
+                EINA_LIST_FREE(wins, bd) {
+                    _add_border(bd);
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+/* }}} */
+/* Module setup {{{*/
+
+static void
+_clear_info_hash(void *data)
+{
+    Tiling_Info *ti = data;
+
+    eina_list_free(ti->floating_windows);
+    for (int i = 0; i < TILING_MAX_STACKS; i++) {
+        eina_list_free(ti->stacks[i]);
+        ti->stacks[i] = NULL;
+    }
+    E_FREE(ti);
+}
+
+static void
+_clear_border_extras(void *data)
+{
+    Border_Extra *extra = data;
+
+    eina_stringshare_del(extra->orig.bordername);
+
+    E_FREE(extra);
+}
+
+EAPI E_Module_Api e_modapi =
+{
+    E_MODULE_API_VERSION,
+    "Tiling"
+};
+
+EAPI void *
+e_modapi_init(E_Module *m)
+{
+    char buf[PATH_MAX];
+    E_Desk *desk;
+
+    tiling_g.module = m;
+
+    if (tiling_g.log_domain < 0) {
+        tiling_g.log_domain = eina_log_domain_register("tiling", NULL);
+        if (tiling_g.log_domain < 0) {
+            EINA_LOG_CRIT("could not register log domain 'tiling'");
+        }
+    }
+
+    _G.info_hash = eina_hash_pointer_new(_clear_info_hash);
+    _G.border_extras = eina_hash_pointer_new(_clear_border_extras);
+
+    _G.pre_border_assign_hook = e_border_hook_add(
+        E_BORDER_HOOK_EVAL_PRE_BORDER_ASSIGN,
+        (void (*)(void*,void*))_pre_border_assign_hook, NULL);
+
+#define HANDLER(_h, _e, _f)                                   \
+    _h = ecore_event_handler_add(E_EVENT_##_e,                \
+                                 (Ecore_Event_Handler_Cb) _f, \
+                                 NULL);
+
+    HANDLER(_G.handler_border_resize, BORDER_RESIZE, _resize_hook);
+    HANDLER(_G.handler_border_move, BORDER_MOVE, _move_hook);
+    HANDLER(_G.handler_border_add, BORDER_ADD, _add_hook);
+    HANDLER(_G.handler_border_remove, BORDER_REMOVE, _remove_hook);
+
+    HANDLER(_G.handler_border_iconify, BORDER_ICONIFY, _iconify_hook);
+    HANDLER(_G.handler_border_uniconify, BORDER_UNICONIFY, _uniconify_hook);
+    HANDLER(_G.handler_border_stick, BORDER_STICK, _stick_hook);
+    HANDLER(_G.handler_border_unstick, BORDER_UNSTICK, _unstick_hook);
+
+    HANDLER(_G.handler_desk_show, DESK_SHOW, _desk_show_hook);
+    HANDLER(_G.handler_desk_before_show, DESK_BEFORE_SHOW, _desk_before_show_hook);
+    HANDLER(_G.handler_desk_set, BORDER_DESK_SET, _desk_set_hook);
+    HANDLER(_G.handler_container_resize, CONTAINER_RESIZE, _container_resize_hook);
+#undef HANDLER
+
+#define ACTION_ADD(_act, _cb, _title, _value)                                \
+    {                                                                        \
+        E_Action *_action = _act;                                            \
+        const char *_name = _value;                                          \
+        if ((_action = e_action_add(_name))) {                               \
+            _action->func.go = _cb;                                          \
+            e_action_predef_name_set(_("Tiling"), _(_title), _name,      \
+                                     NULL, NULL, 0);                         \
+        }                                                                    \
+    }
+
+    /* Module's actions */
+    ACTION_ADD(_G.act_togglefloat, _e_mod_action_toggle_floating_cb,
+               "Toggle floating", "toggle_floating");
+    ACTION_ADD(_G.act_addstack, _e_mod_action_add_stack_cb,
+               "Add a stack", "add_stack");
+    ACTION_ADD(_G.act_removestack, _e_mod_action_remove_stack_cb,
+               "Remove a stack", "remove_stack");
+    ACTION_ADD(_G.act_tg_stack, _e_mod_action_tg_stack_cb,
+               "Toggle between rows and columns", "tg_cols_rows");
+    ACTION_ADD(_G.act_swap, _e_mod_action_swap_cb,
+               "Swap a window with an other", "swap");
+    ACTION_ADD(_G.act_move, _e_mod_action_move_cb,
+               "Move window", "move");
+    ACTION_ADD(_G.act_adjusttransitions, _e_mod_action_adjust_transitions,
+               "Adjust transitions", "adjust_transitions");
+    ACTION_ADD(_G.act_go, _e_mod_action_go_cb,
+               "Focus a particular window", "go");
+#undef ACTION_ADD
+
+    /* Configuration entries */
+    snprintf(_G.edj_path, sizeof(_G.edj_path),"%s/e-module-tiling.edj",
+             e_module_dir_get(m));
+    e_configure_registry_category_add("windows", 50, _("Windows"), NULL,
+                                      "preferences-system-windows");
+    e_configure_registry_item_add("windows/tiling", 150, _("Tiling"),
+                                  NULL, _G.edj_path,
+                                  e_int_config_tiling_module);
+
+    /* Configuration itself */
+    _G.config_edd = E_CONFIG_DD_NEW("Tiling_Config", Config);
+    _G.vdesk_edd = E_CONFIG_DD_NEW("Tiling_Config_VDesk",
+                                   struct _Config_vdesk);
+    E_CONFIG_VAL(_G.config_edd, Config, tile_dialogs, INT);
+    E_CONFIG_VAL(_G.config_edd, Config, show_titles, INT);
+    E_CONFIG_VAL(_G.config_edd, Config, keyhints, STR);
+
+    E_CONFIG_LIST(_G.config_edd, Config, vdesks, _G.vdesk_edd);
+    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, x, INT);
+    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, y, INT);
+    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, zone_num, INT);
+    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, nb_stacks, INT);
+    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, use_rows, INT);
+
+    tiling_g.config = e_config_domain_load("module.tiling", _G.config_edd);
+    if (!tiling_g.config) {
+        tiling_g.config = E_NEW(Config, 1);
+        tiling_g.config->tile_dialogs = 1;
+        tiling_g.config->show_titles = 1;
+    }
+    if (!tiling_g.config->keyhints)
+        tiling_g.config->keyhints = strdup(tiling_g.default_keyhints);
+    else
+        tiling_g.config->keyhints = strdup(tiling_g.config->keyhints);
+
+    E_CONFIG_LIMIT(tiling_g.config->tile_dialogs, 0, 1);
+    E_CONFIG_LIMIT(tiling_g.config->show_titles, 0, 1);
+
+    for (Eina_List *l = tiling_g.config->vdesks; l; l = l->next) {
+        struct _Config_vdesk *vd;
+
+        vd = l->data;
+
+        E_CONFIG_LIMIT(vd->nb_stacks, 0, TILING_MAX_STACKS);
+        E_CONFIG_LIMIT(vd->use_rows, 0, 1);
+    }
+
+    desk = get_current_desk();
+    _G.tinfo = _initialize_tinfo(desk);
+
+    _G.input_mode = INPUT_MODE_NONE;
+    _G.currently_switching_desktop = 0;
+    _G.action_cb = NULL;
+
+    return m;
+}
+
+EAPI int
+e_modapi_shutdown(E_Module *m)
+{
+
+    if (tiling_g.log_domain >= 0) {
+        eina_log_domain_unregister(tiling_g.log_domain);
+        tiling_g.log_domain = -1;
+    }
+
+    if (_G.pre_border_assign_hook) {
+        e_border_hook_del(_G.pre_border_assign_hook);
+        _G.pre_border_assign_hook = NULL;
+    }
+
+#define FREE_HANDLER(x)              \
+    if (x) {                         \
+        ecore_event_handler_del(x);  \
+        x = NULL;                    \
+    }
+    FREE_HANDLER(_G.handler_border_resize);
+    FREE_HANDLER(_G.handler_border_move);
+    FREE_HANDLER(_G.handler_border_add);
+    FREE_HANDLER(_G.handler_border_remove);
+
+    FREE_HANDLER(_G.handler_border_iconify);
+    FREE_HANDLER(_G.handler_border_uniconify);
+    FREE_HANDLER(_G.handler_border_stick);
+    FREE_HANDLER(_G.handler_border_unstick);
+
+    FREE_HANDLER(_G.handler_desk_show);
+    FREE_HANDLER(_G.handler_desk_before_show);
+    FREE_HANDLER(_G.handler_desk_set);
+#undef FREE_HANDLER
+
+
+#define ACTION_DEL(act, title, value)                        \
+    if (act) {                                               \
+        e_action_predef_name_del(_("Tiling"), _(title)); \
+        e_action_del(value);                                 \
+        act = NULL;                                          \
+    }
+    ACTION_DEL(_G.act_togglefloat, "Toggle floating", "toggle_floating");
+    ACTION_DEL(_G.act_addstack, "Add a stack", "add_stack");
+    ACTION_DEL(_G.act_removestack, "Remove a stack", "remove_stack");
+    ACTION_DEL(_G.act_tg_stack, "Toggle between rows and columns", "tg_cols_rows");
+    ACTION_DEL(_G.act_swap, "Swap a window with an other", "swap");
+    ACTION_DEL(_G.act_move, "Move window", "move");
+    ACTION_DEL(_G.act_adjusttransitions, "Adjust transitions",
+               "adjust_transitions");
+    ACTION_DEL(_G.act_go, "Focus a particular window", "go");
+#undef ACTION_DEL
+
+    e_configure_registry_item_del("windows/tiling");
+    e_configure_registry_category_del("windows");
+
+    end_special_input();
+
+    free(tiling_g.config->keyhints);
+    E_FREE(tiling_g.config);
+    E_CONFIG_DD_FREE(_G.config_edd);
+    E_CONFIG_DD_FREE(_G.vdesk_edd);
+
+    tiling_g.module = NULL;
+
+    eina_hash_free(_G.info_hash);
+    _G.info_hash = NULL;
+
+    eina_hash_free(_G.border_extras);
+    _G.border_extras = NULL;
+
+    _G.tinfo = NULL;
+
+    return 1;
+}
+
+EAPI int
+e_modapi_save(E_Module *m)
+{
+    e_config_domain_save("module.tiling", _G.config_edd, tiling_g.config);
+
+    return EINA_TRUE;
+}
+/* }}} */
diff --git a/src/modules/tiling/e_mod_tiling.h b/src/modules/tiling/e_mod_tiling.h
new file mode 100644 (file)
index 0000000..d989605
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef E_MOD_TILING_H
+#define E_MOD_TILING_H
+
+#include <e.h>
+#include <e_border.h>
+#include <e_shelf.h>
+
+#include <stdbool.h>
+
+#include "config.h"
+
+typedef struct _Config      Config;
+typedef struct _Tiling_Info Tiling_Info;
+
+struct tiling_g
+{
+   E_Module   *module;
+   Config     *config;
+   int         log_domain;
+   const char *default_keyhints;
+};
+extern struct tiling_g tiling_g;
+
+#define ERR(...) EINA_LOG_DOM_ERR(tiling_g.log_domain, __VA_ARGS__)
+#define DBG(...) EINA_LOG_DOM_DBG(tiling_g.log_domain, __VA_ARGS__)
+
+#define TILING_MAX_STACKS 8
+
+struct _Config_vdesk
+{
+   int           x, y;
+   unsigned int  zone_num;
+   int           nb_stacks;
+   int           use_rows;
+};
+
+struct _Config
+{
+    int            tile_dialogs;
+    int            show_titles;
+    char          *keyhints;
+    Eina_List     *vdesks;
+};
+
+struct _Tiling_Info
+{
+    /* The desk for which this _Tiling_Info is used. Needed because
+     * (for example) on e restart all desks are shown on all zones but no
+     * change events are triggered */
+    const E_Desk    *desk;
+
+    struct _Config_vdesk *conf;
+
+    /* List of windows which were toggled floating */
+    Eina_List *floating_windows;
+
+    Eina_List *stacks[TILING_MAX_STACKS];
+    int        pos[TILING_MAX_STACKS];
+    int        size[TILING_MAX_STACKS];
+};
+
+struct _E_Config_Dialog_Data
+{
+   struct _Config config;
+   Evas_Object *o_zonelist;
+   Evas_Object *o_desklist;
+   Evas_Object *osf;
+   Evas        *evas;
+};
+
+E_Config_Dialog *e_int_config_tiling_module(E_Container *con,
+                                            const char  *params);
+
+EAPI extern E_Module_Api e_modapi;
+
+EAPI void *e_modapi_init(E_Module *m);
+EAPI int   e_modapi_shutdown(E_Module *m);
+EAPI int   e_modapi_save(E_Module *m);
+
+void change_desk_conf(struct _Config_vdesk *newconf);
+
+void e_tiling_update_conf(void);
+
+struct _Config_vdesk *
+get_vdesk(Eina_List *vdesks,
+          int x,
+          int y,
+          unsigned int zone_num);
+
+#define EINA_LIST_IS_IN(_list, _el) \
+    (eina_list_data_find(_list, _el) == _el)
+#define EINA_LIST_APPEND(_list, _el) \
+    _list = eina_list_append(_list, _el)
+#define EINA_LIST_REMOVE(_list, _el) \
+    _list = eina_list_remove(_list, _el)
+
+#endif
diff --git a/src/modules/tiling/images/arrow_e.png b/src/modules/tiling/images/arrow_e.png
new file mode 100644 (file)
index 0000000..06b6f29
Binary files /dev/null and b/src/modules/tiling/images/arrow_e.png differ
diff --git a/src/modules/tiling/images/arrow_n.png b/src/modules/tiling/images/arrow_n.png
new file mode 100644 (file)
index 0000000..c5a5de5
Binary files /dev/null and b/src/modules/tiling/images/arrow_n.png differ
diff --git a/src/modules/tiling/images/arrow_ne.png b/src/modules/tiling/images/arrow_ne.png
new file mode 100644 (file)
index 0000000..8c5112d
Binary files /dev/null and b/src/modules/tiling/images/arrow_ne.png differ
diff --git a/src/modules/tiling/images/arrow_nw.png b/src/modules/tiling/images/arrow_nw.png
new file mode 100644 (file)
index 0000000..92e5656
Binary files /dev/null and b/src/modules/tiling/images/arrow_nw.png differ
diff --git a/src/modules/tiling/images/arrow_s.png b/src/modules/tiling/images/arrow_s.png
new file mode 100644 (file)
index 0000000..c5a9f06
Binary files /dev/null and b/src/modules/tiling/images/arrow_s.png differ
diff --git a/src/modules/tiling/images/arrow_se.png b/src/modules/tiling/images/arrow_se.png
new file mode 100644 (file)
index 0000000..22613e4
Binary files /dev/null and b/src/modules/tiling/images/arrow_se.png differ
diff --git a/src/modules/tiling/images/arrow_sw.png b/src/modules/tiling/images/arrow_sw.png
new file mode 100644 (file)
index 0000000..bfc2aea
Binary files /dev/null and b/src/modules/tiling/images/arrow_sw.png differ
diff --git a/src/modules/tiling/images/arrow_w.png b/src/modules/tiling/images/arrow_w.png
new file mode 100644 (file)
index 0000000..479bad9
Binary files /dev/null and b/src/modules/tiling/images/arrow_w.png differ
diff --git a/src/modules/tiling/images/module_icon.png b/src/modules/tiling/images/module_icon.png
new file mode 100644 (file)
index 0000000..328acc9
Binary files /dev/null and b/src/modules/tiling/images/module_icon.png differ
diff --git a/src/modules/tiling/module.desktop.in b/src/modules/tiling/module.desktop.in
new file mode 100644 (file)
index 0000000..b4220f2
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Link
+Name=Tiling
+Name[fr]=E-pavage
+Icon=e-module-tiling
+Comment=Positions/resizes your windows tilingly.
+Comment[fr]=Place/Redimensionne les fenêtres sans les superposer.
+Comment[it]=Posiziona/ridimensiona le finestre affiancandole.
+Comment[pt]=Posiciona/ajusta as janelas em mosaicos
+X-Enlightenment-ModuleType=core