Maximus: Tiled Maximization
authorCarlos R. Mafra <crmafra@gmail.com>
Tue, 1 Sep 2009 23:37:47 +0000 (2 01:37 +0200)
committerCarlos R. Mafra <crmafra@gmail.com>
Wed, 2 Sep 2009 02:42:05 +0000 (2 04:42 +0200)
This patch introduces the "tiled maximization" feature, a.k.a. Maximus.

By pressing the keyboard shortcut associated with Maximus, the focused
window will be maximized to the greatest area satisfying the constraint
of not overlapping existing windows.

src/actions.c
src/actions.h
src/defaults.c
src/event.c
src/keybind.h
src/placement.c
src/window.h

index e3e2249..f9b9257 100644 (file)
@@ -60,6 +60,10 @@ extern WPreferences wPreferences;
 extern Atom _XA_WM_TAKE_FOCUS;
 
 extern void ProcessPendingEvents();
+extern int calcIntersectionLength(int p1, int l1, int p2, int l2);
+
+static void find_Maximus_geometry(WWindow *wwin, WArea usableArea, int *new_x,
+                                 int *new_y, int *new_width, int *new_height);
 
 /******* Local Variables *******/
 static struct {
@@ -326,7 +330,7 @@ void wMaximizeWindow(WWindow * wwin, int directions)
                wUnshadeWindow(wwin);
        }
        /* Only save directions, not kbd or xinerama hints */
-       directions &= (MAX_HORIZONTAL|MAX_VERTICAL|MAX_LEFTHALF|MAX_RIGHTHALF);
+       directions &= (MAX_HORIZONTAL | MAX_VERTICAL | MAX_LEFTHALF | MAX_RIGHTHALF | MAX_MAXIMUS);
 
        changed_h = ((wwin->flags.maximized ^ directions) & MAX_HORIZONTAL);
        changed_v = ((wwin->flags.maximized ^ directions) & MAX_VERTICAL);
@@ -397,6 +401,9 @@ void wMaximizeWindow(WWindow * wwin, int directions)
                new_height -= wwin->frame->top_width + wwin->frame->bottom_width;
        }
 
+       if (directions & MAX_MAXIMUS)
+               find_Maximus_geometry(wwin, usableArea, &new_x, &new_y, &new_width, &new_height);
+
        wWindowConstrainSize(wwin, &new_width, &new_height);
 
        wWindowCropSize(wwin, usableArea.x2 - usableArea.x1,
@@ -409,6 +416,106 @@ void wMaximizeWindow(WWindow * wwin, int directions)
        wSoundPlay(WSOUND_MAXIMIZE);
 }
 
+/*
+ * Maximus: tiled maximization (maximize without overlapping other windows)
+ *
+ * The window to be maximized will be denoted by w_0 (sub-index zero)
+ * while the windows which will stop the maximization of w_0 are denoted by w_j.
+ */
+static void find_Maximus_geometry(WWindow *wwin, WArea usableArea, int *new_x, int *new_y,
+                                 int *new_width, int *new_height)
+{
+       WWindow *tmp;
+       int x_0            = wwin->frame_x;
+       int y_0            = wwin->frame_y;
+       int width_0        = wwin->frame->core->width;
+       int height_0       = wwin->frame->core->height;
+       int botton_0       = y_0 + height_0;
+       int right_border_0 = x_0 + width_0;
+       int new_x_0, new_y_0, new_botton_0, new_right_border_0, new_height_0;
+       int x_j,  y_j, width_j, height_j, botton_j, top_j, right_border_j;
+       int x_intsect, y_intsect;
+       /* Assume that the window w_0 has titlebar etc */
+       int has_titlebar = 1, has_resizebar = 1, has_border = 1;
+       int adjust_height, adjust_width;
+
+       /* Try to fully maximize first, then readjust later */
+       new_x_0            = usableArea.x1;
+       new_y_0            = usableArea.y1;
+       new_botton_0       = usableArea.y2;
+       new_right_border_0 = usableArea.x2;
+
+       if (!HAS_TITLEBAR(wwin))
+               has_titlebar = 0;
+       if (!HAS_RESIZEBAR(wwin))
+               has_resizebar = 0;
+       if (!HAS_BORDER(wwin))
+               has_border = 0;
+
+       /* the lengths to be subtracted if w_0 has titlebar, etc */
+       adjust_height = TITLEBAR_HEIGHT * has_titlebar
+               + 2 * FRAME_BORDER_WIDTH * has_border + RESIZEBAR_HEIGHT * has_resizebar;
+       adjust_width = 2 * FRAME_BORDER_WIDTH * has_border;
+
+       tmp = wwin;
+       /* TODO: Is the focused window always the last in the list? */
+       while (tmp->prev) {
+               /* ignore windows in other workspaces or minimized */
+               if (tmp->prev->frame->workspace != wwin->screen_ptr->current_workspace
+                   || tmp->prev->flags.miniaturized) {
+                       tmp = tmp->prev;
+                       continue;
+               }
+               tmp = tmp->prev;
+
+               /* set the w_j window coordinates */
+               x_j = tmp->frame_x;
+               y_j = tmp->frame_y;
+               width_j = tmp->frame->core->width;
+               height_j = tmp->frame->core->height;
+               botton_j = y_j + height_j;
+               top_j = y_j;
+               right_border_j = x_j + width_j;
+
+               /* Try to maximize in the y direction first */
+               x_intsect = calcIntersectionLength(x_0, width_0, x_j, width_j);
+               if (x_intsect != 0) {
+                       if (botton_j < y_0 && botton_j > new_y_0) {
+                               /* w_0 is below the botton of w_j */
+                               new_y_0 = botton_j;
+                       }
+                       if (botton_0 < top_j && top_j < new_botton_0) {
+                               /* The botton of w_0 is above the top of w_j */
+                               new_botton_0 = top_j;
+                       }
+               }
+
+               /*
+                * Use the updated y coordinates from the above step to account
+                * the possibility that the new value of y_0 will have different
+                * intersections with w_j
+                */
+               new_height_0 = new_botton_0 - new_y_0 - adjust_height;
+               y_intsect = calcIntersectionLength(new_y_0, new_height_0, y_j, height_j);
+               if (y_intsect != 0) {
+                       if (right_border_j < x_0 && right_border_j > new_x_0) {
+                               /* w_0 is completely to the right of w_j */
+                               new_x_0 = right_border_j;
+                       }
+                       if (right_border_0 < x_j && x_j < new_right_border_0) {
+                               /* w_0 is completely to the left of w_j */
+                               new_right_border_0 = x_j;
+                       }
+               }
+       }
+
+       new_height_0 = new_botton_0 - new_y_0 - adjust_height;
+       *new_x = new_x_0;
+       *new_y = new_y_0;
+       *new_height = new_height_0;
+       *new_width = new_right_border_0 - new_x_0 - adjust_width;
+}
+
 void wUnmaximizeWindow(WWindow * wwin)
 {
        int x, y, w, h;
index caff53f..7069ff5 100644 (file)
 
 #include "window.h"
 
-#define MAX_HORIZONTAL         1
-#define MAX_VERTICAL   2
-#define MAX_LEFTHALF   4
-#define MAX_RIGHTHALF  8
-#define MAX_IGNORE_XINERAMA 16
-#define MAX_KEYBOARD 32
+#define MAX_HORIZONTAL         (1 << 0)
+#define MAX_VERTICAL           (1 << 1)
+#define MAX_LEFTHALF           (1 << 2)
+#define MAX_RIGHTHALF          (1 << 3)
+#define MAX_MAXIMUS            (1 << 4)
+#define MAX_IGNORE_XINERAMA    (1 << 5)
+#define MAX_KEYBOARD           (1 << 6)
 
 void wSetFocusTo(WScreen *scr, WWindow *wwin);
 
index ef1fb71..d317600 100644 (file)
@@ -583,6 +583,8 @@ WDefaultEntry optionList[] = {
         NULL, getKeybind, setKeyGrab},
        {"RHMaximizeKey", "None", (void*)WKBD_RHMAXIMIZE,
         NULL, getKeybind, setKeyGrab},
+       {"MaximusKey", "None", (void*)WKBD_MAXIMUS,
+        NULL, getKeybind, setKeyGrab},
        {"RaiseKey", "\"Meta+Up\"", (void *)WKBD_RAISE,
         NULL, getKeybind, setKeyGrab},
        {"LowerKey", "\"Meta+Down\"", (void *)WKBD_LOWER,
index da83149..70b8a1e 100644 (file)
@@ -1497,6 +1497,19 @@ static void handleKeyPress(XEvent * event)
                        }
                }
                break;
+       case WKBD_MAXIMUS:
+               if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
+                       int newdir = MAX_MAXIMUS;
+
+                       CloseWindowMenu(scr);
+
+                       if (wwin->flags.maximized == newdir) {
+                               wUnmaximizeWindow(wwin);
+                       } else {
+                               wMaximizeWindow(wwin, newdir|MAX_KEYBOARD);
+                       }
+               }
+               break;
        case WKBD_RAISE:
                if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
                        CloseWindowMenu(scr);
index 446e27f..995e7a7 100644 (file)
@@ -35,6 +35,7 @@ enum {
        WKBD_HMAXIMIZE,
         WKBD_LHMAXIMIZE,
        WKBD_RHMAXIMIZE,
+       WKBD_MAXIMUS,
        WKBD_SELECT,
        WKBD_RAISE,
        WKBD_LOWER,
index 6a412ac..32bdf7e 100644 (file)
@@ -234,7 +234,7 @@ void PlaceIcon(WScreen *scr, int *x_ret, int *y_ret, int head)
 }
 
 /* Computes the intersecting length of two line sections */
-static int calcIntersectionLength(int p1, int l1, int p2, int l2)
+int calcIntersectionLength(int p1, int l1, int p2, int l2)
 {
        int isect;
        int tmp;
index 5e69eb2..e5ac7f4 100644 (file)
@@ -258,7 +258,7 @@ typedef struct WWindow {
         unsigned int miniaturized:1;
         unsigned int hidden:1;
         unsigned int shaded:1;
-               unsigned int maximized:4;
+       unsigned int maximized:5;
         unsigned int fullscreen:1;
         unsigned int omnipresent:1;