From 55fd49614bb1b9e37561147f18719b2ac277b64b Mon Sep 17 00:00:00 2001
From: Mark Tiefenbruck <mark@fluxbox.org>
Date: Wed, 27 Aug 2008 13:05:49 -0400
Subject: move FbWinFrame::State class to a new file

---
 src/CurrentWindowCmd.cc |   2 +-
 src/FbWinFrame.cc       | 303 +++---------------------------------------------
 src/FbWinFrame.hh       | 103 ++--------------
 src/Makefile.am         |   2 +-
 src/Remember.cc         |  40 +++----
 src/WinClient.hh        |   7 +-
 src/Window.cc           |  92 +++++++--------
 src/Window.hh           |  12 +-
 src/WindowState.cc      | 266 ++++++++++++++++++++++++++++++++++++++++++
 src/WindowState.hh      | 118 +++++++++++++++++++
 10 files changed, 486 insertions(+), 459 deletions(-)
 create mode 100644 src/WindowState.cc
 create mode 100644 src/WindowState.hh

diff --git a/src/CurrentWindowCmd.cc b/src/CurrentWindowCmd.cc
index 95f2933..59b0186 100644
--- a/src/CurrentWindowCmd.cc
+++ b/src/CurrentWindowCmd.cc
@@ -503,7 +503,7 @@ void SetTitleCmd::real_execute() {
 REGISTER_COMMAND_WITH_ARGS(setdecor, SetDecorCmd, void);
 
 SetDecorCmd::SetDecorCmd(const std::string &args):
-    m_mask(FbWinFrame::getDecoMaskFromString(args)) { }
+    m_mask(WindowState::getDecoMaskFromString(args)) { }
 
 void SetDecorCmd::real_execute() {
     fbwindow().setDecorationMask(m_mask);
diff --git a/src/FbWinFrame.cc b/src/FbWinFrame.cc
index 9e7f613..1a98ff5 100644
--- a/src/FbWinFrame.cc
+++ b/src/FbWinFrame.cc
@@ -28,7 +28,6 @@
 #include "FbTk/Compose.hh"
 #include "FbTk/Transparent.hh"
 #include "FbTk/CompareEqual.hh"
-#include "FbTk/StringUtil.hh"
 #include "FbTk/TextUtils.hh"
 
 #include "FbWinFrameTheme.hh"
@@ -194,7 +193,7 @@ void FbWinFrame::show() {
  Toggle shade state, and resize window
  */
 void FbWinFrame::shade() {
-    if (!(m_state.deco_mask & DECORM_TITLEBAR))
+    if (!(m_state.deco_mask & WindowState::DECORM_TITLEBAR))
         return;
 
     // toggle shade
@@ -254,7 +253,8 @@ void FbWinFrame::moveResize(int x, int y, unsigned int width, unsigned int heigh
         m_window.resize(width, height);
     }
 
-    saveGeometry();
+    m_state.saveGeometry(window().x(), window().y(),
+                         window().width(), window().height());
 
     if (move || (resize && m_screen.getTabPlacement() != TOPLEFT &&
                            m_screen.getTabPlacement() != LEFTTOP))
@@ -283,7 +283,8 @@ void FbWinFrame::moveResize(int x, int y, unsigned int width, unsigned int heigh
 void FbWinFrame::quietMoveResize(int x, int y,
                                  unsigned int width, unsigned int height) {
     m_window.moveResize(x, y, width, height);
-    saveGeometry();
+    m_state.saveGeometry(window().x(), window().y(),
+                         window().width(), window().height());
     if (m_tabmode == EXTERNAL) {
 
         switch(m_screen.getTabPlacement()) {
@@ -534,21 +535,6 @@ void FbWinFrame::setMaximized(int value) {
     applyState();
 }
 
-void FbWinFrame::saveGeometry() {
-    if (m_state.fullscreen || m_state.maximized == MAX_FULL)
-        return;
-
-    if (!(m_state.maximized & MAX_HORZ)) {
-        m_state.x = x();
-        m_state.width = width();
-    }
-    if (!(m_state.maximized & MAX_VERT)) {
-        m_state.y = y();
-        if (!m_state.shaded)
-            m_state.height = height();
-    }
-}
-
 void FbWinFrame::applyState() {
     applyDecorations();
 
@@ -556,7 +542,7 @@ void FbWinFrame::applyState() {
     int new_x = m_state.x, new_y = m_state.y;
     unsigned int new_w = m_state.width, new_h = m_state.height;
 
-    if (m_state.maximized & MAX_VERT) {
+    if (m_state.maximized & WindowState::MAX_VERT) {
         new_y = m_screen.maxTop(head);
         new_h = m_screen.maxBottom(head) - new_y - 2*window().borderWidth();
         if (!m_screen.getMaxOverTabs()) {
@@ -564,7 +550,7 @@ void FbWinFrame::applyState() {
             new_h -= heightOffset();
         }
     }
-    if (m_state.maximized & MAX_HORZ) {
+    if (m_state.maximized & WindowState::MAX_HORZ) {
         new_x = m_screen.maxLeft(head);
         new_w = m_screen.maxRight(head) - new_x - 2*window().borderWidth();
         if (!m_screen.getMaxOverTabs()) {
@@ -1482,50 +1468,11 @@ void FbWinFrame::applyTabContainer() {
     }
 }
 
-int FbWinFrame::getDecoMaskFromString(const string &str_label) {
-    string label = FbTk::StringUtil::toLower(str_label);
-    if (label == "none")
-        return DECOR_NONE;
-    if (label == "normal")
-        return DECOR_NORMAL;
-    if (label == "tiny")
-        return DECOR_TINY;
-    if (label == "tool")
-        return DECOR_TOOL;
-    if (label == "border")
-        return DECOR_BORDER;
-    if (label == "tab")
-        return DECOR_TAB;
-    int mask = -1;
-    if ((str_label.size() > 1 && str_label[0] == '0' && str_label[1] == 'x') ||
-        (str_label.size() > 0 && isdigit(str_label[0])))
-        mask = strtol(str_label.c_str(), NULL, 0);
-    return mask;
-}
-
-bool FbWinFrame::useBorder() const {
-    return !m_state.fullscreen && m_state.maximized != MAX_FULL &&
-           (m_state.deco_mask & DECORM_BORDER);
-}
-
-bool FbWinFrame::useTabs() const {
-    return !m_state.fullscreen && m_state.deco_mask & DECORM_TAB;
-}
-
-bool FbWinFrame::useTitlebar() const {
-    return !m_state.fullscreen && m_state.deco_mask & DECORM_TITLEBAR;
-}
-
-bool FbWinFrame::useHandle() const {
-    return !m_state.fullscreen && !m_state.shaded &&
-        m_state.deco_mask & DECORM_HANDLE;
-}
-
 int FbWinFrame::getShape() const {
     int shape = theme()->shapePlace();
-    if (!useTitlebar())
+    if (!m_state.useTitlebar())
         shape &= ~(FbTk::Shape::TOPRIGHT|FbTk::Shape::TOPLEFT);
-    if (!useHandle())
+    if (!m_state.useHandle())
         shape &= ~(FbTk::Shape::BOTTOMRIGHT|FbTk::Shape::BOTTOMLEFT);
     return shape;
 }
@@ -1541,13 +1488,13 @@ void FbWinFrame::applyDecorations() {
     // tab deocration only affects if we're external
     // must do before the setTabMode in case it goes
     // to external and is meant to be hidden
-    if (useTabs())
+    if (m_state.useTabs())
         client_move |= showTabs();
     else
         client_move |= hideTabs();
 
     // we rely on frame not doing anything if it is already shown/hidden
-    if (useTitlebar()) {
+    if (m_state.useTitlebar()) {
         client_move |= showTitlebar();
         if (m_screen.getDefaultInternalTabs())
             client_move |= setTabMode(INTERNAL);
@@ -1555,11 +1502,11 @@ void FbWinFrame::applyDecorations() {
             client_move |= setTabMode(EXTERNAL);
     } else {
         client_move |= hideTitlebar();
-        if (useTabs())
+        if (m_state.useTabs())
             client_move |= setTabMode(EXTERNAL);
     }
 
-    if (useHandle())
+    if (m_state.useHandle())
         client_move |= showHandle();
     else
         client_move |= hideHandle();
@@ -1581,7 +1528,7 @@ void FbWinFrame::applyDecorations() {
 
 bool FbWinFrame::setBorderWidth(bool do_move) {
     unsigned int border_width = theme()->border().width();
-    unsigned int win_bw = useBorder() ? border_width : 0;
+    unsigned int win_bw = m_state.useBorder() ? border_width : 0;
 
     if (border_width &&
         theme()->border().color().pixel() != window().borderColor()) {
@@ -1717,30 +1664,6 @@ void FbWinFrame::gravityTranslate(int &x, int &y,
     }
 }
 
-int FbWinFrame::normalX() const {
-    if ((m_state.maximized & MAX_HORZ) || m_state.fullscreen)
-        return m_state.x;
-    return x();
-}
-
-int FbWinFrame::normalY() const {
-    if ((m_state.maximized & MAX_VERT) || m_state.fullscreen)
-        return m_state.y;
-    return y();
-}
-
-unsigned int FbWinFrame::normalWidth() const {
-    if ((m_state.maximized & MAX_HORZ) || m_state.fullscreen)
-        return m_state.width;
-    return width();
-}
-
-unsigned int FbWinFrame::normalHeight() const {
-    if ((m_state.maximized & MAX_VERT) || m_state.fullscreen || m_state.shaded)
-        return m_state.height;
-    return height();
-}
-
 int FbWinFrame::widthOffset() const {
     if (m_tabmode != EXTERNAL || !m_use_tabs)
         return 0;
@@ -1825,201 +1748,3 @@ void FbWinFrame::displaySize(unsigned int width, unsigned int height) const {
                             width, height - titlebarHeight() - handleHeight());
     m_screen.showGeometry(i, j);
 }
-
-void FbWinFrame::SizeHints::reset(const XSizeHints &sizehint) {
-    if (sizehint.flags & PMinSize) {
-        min_width = sizehint.min_width;
-        min_height = sizehint.min_height;
-    } else
-        min_width = min_height = 1;
-
-    if (sizehint.flags & PBaseSize) {
-        base_width = sizehint.base_width;
-        base_height = sizehint.base_height;
-        if (!(sizehint.flags & PMinSize)) {
-            min_width = base_width;
-            min_height = base_height;
-        }
-    } else
-        base_width = base_height = 0;
-
-    if (sizehint.flags & PMaxSize) {
-        max_width = sizehint.max_width;
-        max_height = sizehint.max_height;
-    } else
-        max_width = max_height = 0; // unbounded
-
-    if (sizehint.flags & PResizeInc) {
-        width_inc = sizehint.width_inc;
-        height_inc = sizehint.height_inc;
-    } else
-        width_inc = height_inc = 1;
-
-    if (sizehint.flags & PAspect) {
-        min_aspect_x = sizehint.min_aspect.x;
-        min_aspect_y = sizehint.min_aspect.y;
-        max_aspect_x = sizehint.max_aspect.x;
-        max_aspect_y = sizehint.max_aspect.y;
-    } else {
-        min_aspect_x = max_aspect_y = 0;
-        min_aspect_y = max_aspect_x = 1;
-    }
-
-    if (sizehint.flags & PWinGravity)
-        win_gravity = sizehint.win_gravity;
-    else
-        win_gravity = NorthWestGravity;
-
-    // some sanity checks
-    if (width_inc == 0)
-        width_inc = 1;
-    if (height_inc == 0)
-        height_inc = 1;
-
-    if (base_width > min_width)
-        min_width = base_width;
-    if (base_height > min_height)
-        min_height = base_height;
-}
-
-/* For aspect ratios
-   Note that its slightly simplified in that only the
-   line gradient is given - this is because for aspect
-   ratios, we always have the line going through the origin
-
-   * Based on this formula:
-   http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
-
-   Note that a gradient from origin goes through ( grad , 1 )
- */
-
-void closestPointToAspect(unsigned int &ret_x, unsigned int &ret_y,
-                          unsigned int point_x, unsigned int point_y,
-                          unsigned int aspect_x, unsigned int aspect_y) {
-    double u = static_cast<double>(point_x * aspect_x + point_y * aspect_y) /
-               static_cast<double>(aspect_x * aspect_x + aspect_y * aspect_y);
-
-    ret_x = static_cast<unsigned int>(u * aspect_x);
-    ret_y = static_cast<unsigned int>(u * aspect_y);
-}
-
-unsigned int increaseToMultiple(unsigned int val, unsigned int inc) {
-    return val % inc ? val + inc - (val % inc) : val;
-}
-
-unsigned int decreaseToMultiple(unsigned int val, unsigned int inc) {
-    return val % inc ? val - (val % inc) : val;
-}
-    
-/**
- * Changes width and height to the nearest (lower) value
- * that conforms to it's size hints.
- *
- * display_* give the values that would be displayed
- * to the user when resizing.
- * We use pointers for display_* since they are optional.
- *
- * See ICCCM section 4.1.2.3
- */
-void FbWinFrame::SizeHints::apply(unsigned int &width, unsigned int &height,
-                                  bool make_fit) const {
-
-    /* aspect ratios are applied exclusive to the base size
-     *
-     * min_aspect_x      width      max_aspect_x
-     * ------------  <  -------  <  ------------
-     * min_aspect_y      height     max_aspect_y
-     *
-     * The trick is how to get back to the aspect ratio with minimal
-     * change - do we modify x, y or both?
-     * A: we minimise the distance between the current point, and
-     *    the target aspect ratio (consider them as x,y coordinates)
-     *  Consider that the aspect ratio is a line, and the current
-     *  w/h is a point, so we're just using the formula for
-     *  shortest distance from a point to a line!
-     */
-
-    // make respective to base_size
-    unsigned int w = width - base_width, h = height - base_height;
-
-    if (min_aspect_y > 0 && w * min_aspect_y < min_aspect_x * h) {
-        closestPointToAspect(w, h, w, h, min_aspect_x, min_aspect_y);
-        // new w must be > old w, new h must be < old h
-        w = increaseToMultiple(w, width_inc);
-        h = decreaseToMultiple(h, height_inc);
-    } else if (max_aspect_x > 0 && w * max_aspect_y > max_aspect_x * h) {
-        closestPointToAspect(w, h, w, h, max_aspect_x, max_aspect_y);
-        // new w must be < old w, new h must be > old h
-        w = decreaseToMultiple(w, width_inc);
-        h = increaseToMultiple(h, height_inc);
-    }
-
-    // Check minimum size
-    if (w + base_width < min_width) {
-        w = increaseToMultiple(min_width - base_width, width_inc);
-        // need to check maximum aspect again
-        if (max_aspect_x > 0 && w * max_aspect_y > max_aspect_x * h)
-            h = increaseToMultiple(w * max_aspect_y / max_aspect_x, height_inc);
-    }
-
-    if (h + base_height < min_height) {
-        h = increaseToMultiple(min_height - base_height, height_inc);
-        // need to check minimum aspect again
-        if (min_aspect_y > 0 && w * min_aspect_y < min_aspect_x * h)
-            w = increaseToMultiple(h * min_aspect_x / min_aspect_y, width_inc);
-    }
-
-    unsigned int max_w = make_fit && (width < max_width || max_width == 0) ?
-                         width : max_width;
-    unsigned int max_h = make_fit && (height < max_height || max_height == 0) ?
-                         height : max_height;
-
-    // Check maximum size
-    if (max_w > 0 && w + base_width > max_w)
-        w = max_w - base_width;
-
-    if (max_h > 0 && h + base_height > max_h)
-        h = max_h - base_height;
-
-    w = decreaseToMultiple(w, width_inc);
-    h = decreaseToMultiple(h, height_inc);
-
-    // need to check aspects one more time
-    if (min_aspect_y > 0 && w * min_aspect_y < min_aspect_x * h)
-        h = decreaseToMultiple(w * min_aspect_y / min_aspect_x, height_inc);
-
-    if (max_aspect_x > 0 && w * max_aspect_y > max_aspect_x * h)
-        w = decreaseToMultiple(h * max_aspect_x / max_aspect_y, width_inc);
-
-    width = w + base_width;
-    height = h + base_height;
-}
-
-// check if the given width and height satisfy the size hints
-bool FbWinFrame::SizeHints::valid(unsigned int w, unsigned int h) const {
-    if (w < min_width || h < min_height)
-        return false;
-
-    if (w > max_width || h > max_height)
-        return false;
-
-    if ((w - base_width) % width_inc != 0)
-        return false;
-
-    if ((h - base_height) % height_inc != 0)
-        return false;
-
-    if (min_aspect_x * (h - base_height) > (w - base_width) * min_aspect_y)
-        return false;
-
-    if (max_aspect_x * (h - base_height) < (w - base_width) * max_aspect_y)
-        return false;
-
-    return true;
-}
-
-void FbWinFrame::SizeHints::displaySize(unsigned int &i, unsigned int &j,
-        unsigned int width, unsigned int height) const {
-    i = (width - base_width) / width_inc;
-    j = (height - base_height) / height_inc;
-}
diff --git a/src/FbWinFrame.hh b/src/FbWinFrame.hh
index 55d47ed..4a33fec 100644
--- a/src/FbWinFrame.hh
+++ b/src/FbWinFrame.hh
@@ -33,6 +33,8 @@
 #include "FbTk/Container.hh"
 #include "FbTk/Shape.hh"
 
+#include "WindowState.hh"
+
 #include <X11/Xutil.h>
 
 #include <vector>
@@ -68,85 +70,6 @@ public:
         RIGHTBOTTOM, RIGHT, RIGHTTOP
     };
 
-    /**
-     * Types of maximization
-     */
-    enum MaximizeMode {
-        MAX_NONE = 0, ///< normal state
-        MAX_HORZ = 1, ///< maximize horizontal
-        MAX_VERT = 2, ///< maximize vertical
-        MAX_FULL = 3  ///< maximize full
-    };
-
-    /**
-       This enumeration represents individual decoration
-       attributes, they can be OR-d together to get a mask.
-       Useful for saving.
-    */
-    enum DecorationMask {
-        DECORM_TITLEBAR = (1<<0),
-        DECORM_HANDLE   = (1<<1),
-        DECORM_BORDER   = (1<<2),
-        DECORM_ICONIFY  = (1<<3),
-        DECORM_MAXIMIZE = (1<<4),
-        DECORM_CLOSE    = (1<<5),
-        DECORM_MENU     = (1<<6),
-        DECORM_STICKY   = (1<<7),
-        DECORM_SHADE    = (1<<8),
-        DECORM_TAB      = (1<<9),
-        DECORM_ENABLED  = (1<<10),
-        DECORM_LAST     = (1<<11) // useful for getting "All"
-    };
-
-    enum Decoration {
-        DECOR_NONE = 0,
-        DECOR_NORMAL = DECORM_LAST - 1,
-        DECOR_TINY = DECORM_TITLEBAR|DECORM_ICONIFY|DECORM_MENU|DECORM_TAB,
-        DECOR_TOOL = DECORM_TITLEBAR|DECORM_MENU,
-        DECOR_BORDER = DECORM_BORDER|DECORM_MENU,
-        DECOR_TAB = DECORM_BORDER|DECORM_MENU|DECORM_TAB
-    };
-
-    class SizeHints {
-    public:
-        SizeHints():
-            min_width(1), max_width(0), min_height(1), max_height(0),
-            width_inc(1), height_inc(1), base_width(0), base_height(0),
-            min_aspect_x(0), max_aspect_x(1),
-            min_aspect_y(1), max_aspect_y(0),
-            win_gravity(0) { }
-
-        void reset(const XSizeHints &sizehint);
-
-        void apply(unsigned int &w, unsigned int &h,
-                   bool maximizing = false) const;
-        bool valid(unsigned int width, unsigned int height) const;
-        void displaySize(unsigned int &i, unsigned int &j,
-                         unsigned int width, unsigned int height) const;
-
-        unsigned int min_width, max_width, min_height, max_height,
-                     width_inc, height_inc, base_width, base_height,
-                     min_aspect_x, max_aspect_x, min_aspect_y, max_aspect_y;
-        int win_gravity;
-    };
-
-    class State {
-    public:
-        State():
-            size_hints(),
-            deco_mask(DECOR_NORMAL),
-            focused(false),
-            shaded(false), fullscreen(false), maximized(0),
-            x(0), y(0), width(1), height(1) { }
-
-        SizeHints size_hints;
-        unsigned int deco_mask;
-        bool focused, shaded, fullscreen;
-        int maximized;
-        int x, y;
-        unsigned int width, height;
-    };
-
     /// create a top level window
     FbWinFrame(BScreen &screen, FocusableTheme<FbWinFrameTheme> &theme,
                FbTk::ImageControl &imgctrl,
@@ -247,18 +170,9 @@ public:
                         bool maximizing = false) const;
     void displaySize(unsigned int width, unsigned int height) const;
 
-    static int getDecoMaskFromString(const std::string &str);
     void setDecorationMask(unsigned int mask) { m_state.deco_mask = mask; }
     void applyDecorations();
     void applyState();
-    void saveGeometry();
-
-    /// determine if the given decoration should be shown in current state
-    bool useBorder() const;
-    bool useTabs() const;
-    bool useTitlebar() const;
-    bool useHandle() const;
-    int getShape() const;
 
     // this function translates its arguments according to win_gravity
     // if win_gravity is negative, it does an inverse translation
@@ -287,10 +201,10 @@ public:
     unsigned int width() const { return m_window.width(); }
     unsigned int height() const { return m_window.height(); }
 
-    int normalX() const;
-    int normalY() const;
-    unsigned int normalWidth() const;
-    unsigned int normalHeight() const;
+    int normalX() const { return m_state.x; }
+    int normalY() const { return m_state.y; }
+    unsigned int normalWidth() const { return m_state.width; }
+    unsigned int normalHeight() const { return m_state.height; }
 
     // extra bits for tabs
     int xOffset() const;
@@ -368,6 +282,9 @@ private:
     bool showHandle();
     bool setBorderWidth(bool do_move = true);
 
+    // check which corners should be rounded
+    int getShape() const;
+
     /**
        @name apply pixmaps depending on focus
     */
@@ -463,7 +380,7 @@ private:
     TabMode m_tabmode;
 
     unsigned int m_active_orig_client_bw;
-    State m_state;
+    WindowState m_state;
 
     bool m_need_render;
     int m_button_size; ///< size for all titlebar buttons
diff --git a/src/Makefile.am b/src/Makefile.am
index 448a781..923b2cc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -113,7 +113,7 @@ fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \
 	Slit.cc Slit.hh SlitTheme.hh SlitTheme.cc SlitClient.hh SlitClient.cc \
 	WinButton.hh WinButton.cc \
 	WinButtonTheme.hh WinButtonTheme.cc \
-	Window.cc Window.hh \
+	Window.cc Window.hh WindowState.cc WindowState.hh\
 	Workspace.cc Workspace.hh \
 	FbCommands.hh FbCommands.cc LayerMenu.hh LayerMenu.cc \
 	Layer.hh \
diff --git a/src/Remember.cc b/src/Remember.cc
index 1df5045..0ea8a6c 100644
--- a/src/Remember.cc
+++ b/src/Remember.cc
@@ -500,7 +500,7 @@ int parseApp(ifstream &file, Application &app, string *first_line = 0) {
                 app.rememberIconHiddenstate((strcasecmp(str_label.c_str(), "yes") == 0));
                 app.rememberFocusHiddenstate((strcasecmp(str_label.c_str(), "yes") == 0));
             } else if (str_key == "deco") {
-                int deco = FbWinFrame::getDecoMaskFromString(str_label);
+                int deco = WindowState::getDecoMaskFromString(str_label);
                 if (deco == -1)
                     had_error = 1;
                 else
@@ -535,13 +535,13 @@ int parseApp(ifstream &file, Application &app, string *first_line = 0) {
                 app.rememberMinimizedstate((strcasecmp(str_label.c_str(), "yes") == 0));
             } else if (str_key == "maximized") {
                 if (strcasecmp(str_label.c_str(), "yes") == 0)
-                    app.rememberMaximizedstate(FbWinFrame::MAX_FULL);
+                    app.rememberMaximizedstate(WindowState::MAX_FULL);
                 else if (strcasecmp(str_label.c_str(), "horz") == 0)
-                    app.rememberMaximizedstate(FbWinFrame::MAX_HORZ);
+                    app.rememberMaximizedstate(WindowState::MAX_HORZ);
                 else if (strcasecmp(str_label.c_str(), "vert") == 0)
-                    app.rememberMaximizedstate(FbWinFrame::MAX_VERT);
+                    app.rememberMaximizedstate(WindowState::MAX_VERT);
                 else
-                    app.rememberMaximizedstate(FbWinFrame::MAX_NONE);
+                    app.rememberMaximizedstate(WindowState::MAX_NONE);
             } else if (str_key == "fullscreen") {
                 app.rememberFullscreenstate((strcasecmp(str_label.c_str(), "yes") == 0));
             } else if (str_key == "jump") {
@@ -953,25 +953,25 @@ void Remember::save() {
                 apps_file << "  [Deco]\t{NONE}" << endl;
                 break;
             case (0xffffffff):
-            case (FbWinFrame::DECORM_LAST - 1):
+            case (WindowState::DECORM_LAST - 1):
                 apps_file << "  [Deco]\t{NORMAL}" << endl;
                 break;
-            case (FbWinFrame::DECORM_TITLEBAR
-                  | FbWinFrame::DECORM_ICONIFY
-                  | FbWinFrame::DECORM_MENU):
+            case (WindowState::DECORM_TITLEBAR
+                  | WindowState::DECORM_ICONIFY
+                  | WindowState::DECORM_MENU):
                 apps_file << "  [Deco]\t{TOOL}" << endl;
                 break;
-            case (FbWinFrame::DECORM_TITLEBAR
-                  | FbWinFrame::DECORM_MENU):
+            case (WindowState::DECORM_TITLEBAR
+                  | WindowState::DECORM_MENU):
                 apps_file << "  [Deco]\t{TINY}" << endl;
                 break;
-            case (FbWinFrame::DECORM_BORDER
-                  | FbWinFrame::DECORM_MENU):
+            case (WindowState::DECORM_BORDER
+                  | WindowState::DECORM_MENU):
                 apps_file << "  [Deco]\t{BORDER}" << endl;
                 break;
-            case (FbWinFrame::DECORM_BORDER
-                  | FbWinFrame::DECORM_MENU
-                  | FbWinFrame::DECORM_TAB):
+            case (WindowState::DECORM_BORDER
+                  | WindowState::DECORM_MENU
+                  | WindowState::DECORM_TAB):
                 apps_file << "  [Deco]\t{TAB}" << endl;
                 break;
             default:
@@ -999,16 +999,16 @@ void Remember::save() {
         if (a.maximizedstate_remember) {
             apps_file << "  [Maximized]\t{";
             switch (a.maximizedstate) {
-            case FbWinFrame::MAX_FULL:
+            case WindowState::MAX_FULL:
                 apps_file << "yes" << "}" << endl;
                 break;
-            case FbWinFrame::MAX_HORZ:
+            case WindowState::MAX_HORZ:
                 apps_file << "horz" << "}" << endl;
                 break;
-            case FbWinFrame::MAX_VERT:
+            case WindowState::MAX_VERT:
                 apps_file << "vert" << "}" << endl;
                 break;
-            case FbWinFrame::MAX_NONE:
+            case WindowState::MAX_NONE:
             default:
                 apps_file << "no" << "}" << endl;
                 break;
diff --git a/src/WinClient.hh b/src/WinClient.hh
index 18c9b0b..1903040 100644
--- a/src/WinClient.hh
+++ b/src/WinClient.hh
@@ -22,8 +22,9 @@
 #ifndef WINCLIENT_HH
 #define WINCLIENT_HH
 
-#include "FbWinFrame.hh"
 #include "Window.hh"
+#include "WindowState.hh"
+
 #include "FbTk/FbWindow.hh"
 #include "FbTk/FbString.hh"
 
@@ -114,7 +115,7 @@ public:
     Window getGroupLeftWindow() const;
 
     const MwmHints *getMwmHint() const { return m_mwm_hint; }
-    const FbWinFrame::SizeHints &sizeHints() const { return m_size_hints; }
+    const SizeHints &sizeHints() const { return m_size_hints; }
 
     unsigned int minWidth() const { return m_size_hints.min_width; }
     unsigned int minHeight() const { return m_size_hints.min_height; }
@@ -157,7 +158,7 @@ private:
 
     Focusable::WindowType m_window_type;
     MwmHints *m_mwm_hint;
-    FbWinFrame::SizeHints m_size_hints;
+    SizeHints m_size_hints;
 
     Strut *m_strut;
     // map transient_for X window to winclient transient 
diff --git a/src/Window.cc b/src/Window.cc
index 4b77b95..25a6baf 100644
--- a/src/Window.cc
+++ b/src/Window.cc
@@ -271,7 +271,7 @@ FluxboxWindow::FluxboxWindow(WinClient &client, FbTk::XLayer &layer):
     m_creation_time(0),
     moving(false), resizing(false), shaded(false), iconic(false),
     stuck(false), m_initialized(false), fullscreen(false),
-    maximized(FbWinFrame::MAX_NONE),
+    maximized(WindowState::MAX_NONE),
     m_attaching_tab(0),
     display(FbTk::App::instance()->display()),
     m_button_grab_x(0), m_button_grab_y(0),
@@ -423,7 +423,7 @@ void FluxboxWindow::init() {
     m_workspace_number = m_screen.currentWorkspaceID();
 
     // set default decorations but don't apply them
-    setDecorationMask(FbWinFrame::getDecoMaskFromString(screen().defaultDeco()),
+    setDecorationMask(WindowState::getDecoMaskFromString(screen().defaultDeco()),
                       false);
 
     functions.resize = functions.move = functions.iconify = functions.maximize
@@ -559,7 +559,7 @@ void FluxboxWindow::init() {
 
     if (maximized) {
         int tmp = maximized;
-        maximized = FbWinFrame::MAX_NONE;
+        maximized = WindowState::MAX_NONE;
         setMaximizedState(tmp);
     }
 
@@ -1045,7 +1045,7 @@ void FluxboxWindow::updateSizeHints() {
         if ((*it) == m_client)
             continue;
 
-        const FbWinFrame::SizeHints &hint = (*it)->sizeHints();
+        const SizeHints &hint = (*it)->sizeHints();
         if (m_size_hint.min_width < hint.min_width)
             m_size_hint.min_width = hint.min_width;
         if (m_size_hint.max_width > hint.max_width)
@@ -1144,7 +1144,7 @@ void FluxboxWindow::updateMWMHintsFromClient(WinClient &client) {
     }
 
     unsigned int mask = decorationMask();
-    mask &= FbWinFrame::getDecoMaskFromString(screen().defaultDeco());
+    mask &= WindowState::getDecoMaskFromString(screen().defaultDeco());
     setDecorationMask(mask, false);
 
     // functions.tabable is ours, not special one
@@ -1503,7 +1503,7 @@ void FluxboxWindow::setFullscreenLayer() {
 void FluxboxWindow::maximize(int type) {
 
     // nothing to do
-    if (type == FbWinFrame::MAX_NONE)
+    if (type == WindowState::MAX_NONE)
         return;
 
     int new_max = maximized;
@@ -1512,14 +1512,14 @@ void FluxboxWindow::maximize(int type) {
     // when _don't_ we want to toggle?
     // - type is horizontal maximise, or
     // - type is full and we are not maximised horz but already vertically
-    if (type != FbWinFrame::MAX_HORZ &&
-        (type != FbWinFrame::MAX_FULL || maximized != FbWinFrame::MAX_VERT))
-        new_max ^= FbWinFrame::MAX_VERT;
+    if (type != WindowState::MAX_HORZ &&
+        (type != WindowState::MAX_FULL || maximized != WindowState::MAX_VERT))
+        new_max ^= WindowState::MAX_VERT;
 
     // maximize horizontally?
-    if (type != FbWinFrame::MAX_VERT &&
-        (type != FbWinFrame::MAX_FULL || maximized != FbWinFrame::MAX_HORZ))
-        new_max ^= FbWinFrame::MAX_HORZ;
+    if (type != WindowState::MAX_VERT &&
+        (type != WindowState::MAX_FULL || maximized != WindowState::MAX_HORZ))
+        new_max ^= WindowState::MAX_HORZ;
 
     setMaximizedState(new_max);
 }
@@ -1552,21 +1552,21 @@ void FluxboxWindow::setMaximizedState(int type) {
  * Maximize window horizontal
  */
 void FluxboxWindow::maximizeHorizontal() {
-    maximize(FbWinFrame::MAX_HORZ);
+    maximize(WindowState::MAX_HORZ);
 }
 
 /**
  * Maximize window vertical
  */
 void FluxboxWindow::maximizeVertical() {
-    maximize(FbWinFrame::MAX_VERT);
+    maximize(WindowState::MAX_VERT);
 }
 
 /**
  * Maximize window fully
  */
 void FluxboxWindow::maximizeFull() {
-    maximize(FbWinFrame::MAX_FULL);
+    maximize(WindowState::MAX_FULL);
 }
 
 void FluxboxWindow::setWorkspace(int n) {
@@ -2807,9 +2807,9 @@ void FluxboxWindow::toggleDecoration() {
     if (m_toggled_decos) {
         m_old_decoration_mask = decorationMask();
         if (decorations.titlebar | decorations.tab)
-            setDecorationMask(FbWinFrame::DECOR_NONE);
+            setDecorationMask(WindowState::DECOR_NONE);
         else
-            setDecorationMask(FbWinFrame::DECOR_NORMAL);
+            setDecorationMask(WindowState::DECOR_NORMAL);
     } else //revert back to old decoration
         setDecorationMask(m_old_decoration_mask);
 
@@ -2818,42 +2818,42 @@ void FluxboxWindow::toggleDecoration() {
 unsigned int FluxboxWindow::decorationMask() const {
     unsigned int ret = 0;
     if (decorations.titlebar)
-        ret |= FbWinFrame::DECORM_TITLEBAR;
+        ret |= WindowState::DECORM_TITLEBAR;
     if (decorations.handle)
-        ret |= FbWinFrame::DECORM_HANDLE;
+        ret |= WindowState::DECORM_HANDLE;
     if (decorations.border)
-        ret |= FbWinFrame::DECORM_BORDER;
+        ret |= WindowState::DECORM_BORDER;
     if (decorations.iconify)
-        ret |= FbWinFrame::DECORM_ICONIFY;
+        ret |= WindowState::DECORM_ICONIFY;
     if (decorations.maximize)
-        ret |= FbWinFrame::DECORM_MAXIMIZE;
+        ret |= WindowState::DECORM_MAXIMIZE;
     if (decorations.close)
-        ret |= FbWinFrame::DECORM_CLOSE;
+        ret |= WindowState::DECORM_CLOSE;
     if (decorations.menu)
-        ret |= FbWinFrame::DECORM_MENU;
+        ret |= WindowState::DECORM_MENU;
     if (decorations.sticky)
-        ret |= FbWinFrame::DECORM_STICKY;
+        ret |= WindowState::DECORM_STICKY;
     if (decorations.shade)
-        ret |= FbWinFrame::DECORM_SHADE;
+        ret |= WindowState::DECORM_SHADE;
     if (decorations.tab)
-        ret |= FbWinFrame::DECORM_TAB;
+        ret |= WindowState::DECORM_TAB;
     if (decorations.enabled)
-        ret |= FbWinFrame::DECORM_ENABLED;
+        ret |= WindowState::DECORM_ENABLED;
     return ret;
 }
 
 void FluxboxWindow::setDecorationMask(unsigned int mask, bool apply) {
-    decorations.titlebar = mask & FbWinFrame::DECORM_TITLEBAR;
-    decorations.handle   = mask & FbWinFrame::DECORM_HANDLE;
-    decorations.border   = mask & FbWinFrame::DECORM_BORDER;
-    decorations.iconify  = mask & FbWinFrame::DECORM_ICONIFY;
-    decorations.maximize = mask & FbWinFrame::DECORM_MAXIMIZE;
-    decorations.close    = mask & FbWinFrame::DECORM_CLOSE;
-    decorations.menu     = mask & FbWinFrame::DECORM_MENU;
-    decorations.sticky   = mask & FbWinFrame::DECORM_STICKY;
-    decorations.shade    = mask & FbWinFrame::DECORM_SHADE;
-    decorations.tab      = mask & FbWinFrame::DECORM_TAB;
-    decorations.enabled  = mask & FbWinFrame::DECORM_ENABLED;
+    decorations.titlebar = mask & WindowState::DECORM_TITLEBAR;
+    decorations.handle   = mask & WindowState::DECORM_HANDLE;
+    decorations.border   = mask & WindowState::DECORM_BORDER;
+    decorations.iconify  = mask & WindowState::DECORM_ICONIFY;
+    decorations.maximize = mask & WindowState::DECORM_MAXIMIZE;
+    decorations.close    = mask & WindowState::DECORM_CLOSE;
+    decorations.menu     = mask & WindowState::DECORM_MENU;
+    decorations.sticky   = mask & WindowState::DECORM_STICKY;
+    decorations.shade    = mask & WindowState::DECORM_SHADE;
+    decorations.tab      = mask & WindowState::DECORM_TAB;
+    decorations.enabled  = mask & WindowState::DECORM_ENABLED;
     // we don't want to do this during initialization
     if (apply)
         applyDecorations();
@@ -3024,7 +3024,7 @@ void FluxboxWindow::doSnapping(int &orig_left, int &orig_top) {
     // we only care about the left/top etc that includes borders
     int borderW = 0;
 
-    if (decorationMask() & (FbWinFrame::DECORM_BORDER|FbWinFrame::DECORM_HANDLE))
+    if (decorationMask() & (WindowState::DECORM_BORDER|WindowState::DECORM_HANDLE))
         borderW = frame().window().borderWidth();
 
     int top = orig_top; // orig include the borders
@@ -3096,7 +3096,7 @@ void FluxboxWindow::doSnapping(int &orig_left, int &orig_top) {
         if ((*it) == this)
             continue; // skip myself
 
-        bw = (*it)->decorationMask() & (FbWinFrame::DECORM_BORDER|FbWinFrame::DECORM_HANDLE) ?
+        bw = (*it)->decorationMask() & (WindowState::DECORM_BORDER|WindowState::DECORM_HANDLE) ?
                 (*it)->frame().window().borderWidth() : 0;
 
         snapToWindow(dx, dy, left, right, top, bottom,
@@ -3175,7 +3175,7 @@ void FluxboxWindow::startResizing(int x, int y, ReferenceCorner dir) {
     m_resize_corner = dir;
 
     resizing = true;
-    maximized = FbWinFrame::MAX_NONE;
+    maximized = WindowState::MAX_NONE;
     frame().setMaximized(maximized);
 
     const Cursor& cursor = (m_resize_corner == LEFTTOP) ? frame().theme()->upperLeftAngleCursor() :
@@ -3862,7 +3862,7 @@ void FluxboxWindow::setWindowType(Focusable::WindowType type) {
         setFocusNew(false);
         setMouseFocus(false);
         setClickFocus(false);
-        setDecorationMask(FbWinFrame::DECOR_NONE);
+        setDecorationMask(WindowState::DECOR_NONE);
         moveToLayer(::Layer::DOCK);
         break;
     case Focusable::TYPE_DESKTOP:
@@ -3877,7 +3877,7 @@ void FluxboxWindow::setWindowType(Focusable::WindowType type) {
         setFocusNew(false);
         setMouseFocus(false);
         moveToLayer(::Layer::DESKTOP);
-        setDecorationMask(FbWinFrame::DECOR_NONE);
+        setDecorationMask(WindowState::DECOR_NONE);
         setTabable(false);
         setMovable(false);
         setResizable(false);
@@ -3889,7 +3889,7 @@ void FluxboxWindow::setWindowType(Focusable::WindowType type) {
          * window is a splash screen displayed as an application
          * is starting up.
          */
-        setDecorationMask(FbWinFrame::DECOR_NONE);
+        setDecorationMask(WindowState::DECOR_NONE);
         setFocusHidden(true);
         setIconHidden(true);
         setFocusNew(false);
@@ -3909,7 +3909,7 @@ void FluxboxWindow::setWindowType(Focusable::WindowType type) {
          * application). Windows of this type may set the
          * WM_TRANSIENT_FOR hint indicating the main application window.
          */
-        setDecorationMask(FbWinFrame::DECOR_TOOL);
+        setDecorationMask(WindowState::DECOR_TOOL);
         setIconHidden(true);
         moveToLayer(::Layer::ABOVE_DOCK);
         break;
diff --git a/src/Window.hh b/src/Window.hh
index 89cbc69..2dbb816 100644
--- a/src/Window.hh
+++ b/src/Window.hh
@@ -31,6 +31,7 @@
 #include "FbTk/Observer.hh"
 #include "FbTk/EventHandler.hh"
 #include "FbTk/XLayerItem.hh"
+
 #include "FbWinFrame.hh"
 #include "Focusable.hh"
 #include "FocusableTheme.hh"
@@ -45,7 +46,6 @@
 class WinClient;
 class FbWinFrameTheme;
 class BScreen;
-class FbWinFrame;
 class FocusControl;
 class FbMenu;
 
@@ -212,7 +212,7 @@ public:
     /// set fullscreen
     void setFullscreen(bool flag);
     /// toggle maximize
-    void maximize(int type = FbWinFrame::MAX_FULL);
+    void maximize(int type = WindowState::MAX_FULL);
     /// sets the maximized state
     void setMaximizedState(int type);
     /// maximizes the window horizontal
@@ -373,9 +373,9 @@ public:
     bool isIconic() const { return iconic; }
     bool isShaded() const { return shaded; }
     bool isFullscreen() const { return fullscreen; }
-    bool isMaximized() const { return maximized == FbWinFrame::MAX_FULL; }
-    bool isMaximizedVert() const { return (bool)(maximized & FbWinFrame::MAX_VERT); }
-    bool isMaximizedHorz() const { return (bool)(maximized & FbWinFrame::MAX_HORZ); }
+    bool isMaximized() const { return maximized == WindowState::MAX_FULL; }
+    bool isMaximizedVert() const { return (bool)(maximized & WindowState::MAX_VERT); }
+    bool isMaximizedHorz() const { return (bool)(maximized & WindowState::MAX_HORZ); }
     int maximizedState() const { return maximized; }
     bool isIconifiable() const { return functions.iconify; }
     bool isMaximizable() const { return functions.maximize; }
@@ -553,7 +553,7 @@ private:
     typedef std::map<WinClient *, IconButton *> Client2ButtonMap;
     Client2ButtonMap m_labelbuttons;
 
-    FbWinFrame::SizeHints m_size_hint;
+    SizeHints m_size_hint;
     struct _decorations {
         bool titlebar, handle, border, iconify,
             maximize, close, menu, sticky, shade, tab, enabled;
diff --git a/src/WindowState.cc b/src/WindowState.cc
new file mode 100644
index 0000000..2f8fd4e
--- /dev/null
+++ b/src/WindowState.cc
@@ -0,0 +1,266 @@
+// WindowState.cc
+// Copyright (c) 2008 Fluxbox Team (fluxgen at fluxbox dot org)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+#include "WindowState.hh"
+
+#include "FbTk/StringUtil.hh"
+
+#include <stdlib.h>
+
+bool WindowState::useBorder() const {
+    return !fullscreen && maximized != MAX_FULL && deco_mask & DECORM_BORDER;
+}
+
+bool WindowState::useHandle() const {
+    return !fullscreen && !shaded && deco_mask & DECORM_HANDLE;
+}
+
+bool WindowState::useTabs() const {
+    return !fullscreen && deco_mask & DECORM_TAB;
+}
+
+bool WindowState::useTitlebar() const {
+    return !fullscreen && deco_mask & DECORM_TITLEBAR;
+}
+
+void WindowState::saveGeometry(int new_x, int new_y,
+                               unsigned int new_w, unsigned int new_h) {
+    if (fullscreen || maximized == MAX_FULL)
+        return;
+
+    if (!(maximized & MAX_HORZ)) {
+        x = new_x;
+        width = new_w;
+    }
+    if (!(maximized & MAX_VERT)) {
+        y = new_y;
+        if (!shaded)
+            height = new_h;
+    }
+}
+
+int WindowState::getDecoMaskFromString(const std::string &str_label) {
+    std::string label = FbTk::StringUtil::toLower(str_label);
+    if (label == "none")
+        return DECOR_NONE;
+    if (label == "normal")
+        return DECOR_NORMAL;
+    if (label == "tiny")
+        return DECOR_TINY;
+    if (label == "tool")
+        return DECOR_TOOL;
+    if (label == "border")
+        return DECOR_BORDER;
+    if (label == "tab")
+        return DECOR_TAB;
+    int mask = -1;
+    if ((str_label.size() > 1 && str_label[0] == '0' && str_label[1] == 'x') ||
+        (str_label.size() > 0 && isdigit(str_label[0])))
+        mask = strtol(str_label.c_str(), NULL, 0);
+    return mask;
+}
+
+void SizeHints::reset(const XSizeHints &sizehint) {
+    if (sizehint.flags & PMinSize) {
+        min_width = sizehint.min_width;
+        min_height = sizehint.min_height;
+    } else
+        min_width = min_height = 1;
+
+    if (sizehint.flags & PBaseSize) {
+        base_width = sizehint.base_width;
+        base_height = sizehint.base_height;
+        if (!(sizehint.flags & PMinSize)) {
+            min_width = base_width;
+            min_height = base_height;
+        }
+    } else
+        base_width = base_height = 0;
+
+    if (sizehint.flags & PMaxSize) {
+        max_width = sizehint.max_width;
+        max_height = sizehint.max_height;
+    } else
+        max_width = max_height = 0; // unbounded
+
+    if (sizehint.flags & PResizeInc) {
+        width_inc = sizehint.width_inc;
+        height_inc = sizehint.height_inc;
+    } else
+        width_inc = height_inc = 1;
+
+    if (sizehint.flags & PAspect) {
+        min_aspect_x = sizehint.min_aspect.x;
+        min_aspect_y = sizehint.min_aspect.y;
+        max_aspect_x = sizehint.max_aspect.x;
+        max_aspect_y = sizehint.max_aspect.y;
+    } else {
+        min_aspect_x = max_aspect_y = 0;
+        min_aspect_y = max_aspect_x = 1;
+    }
+
+    if (sizehint.flags & PWinGravity)
+        win_gravity = sizehint.win_gravity;
+    else
+        win_gravity = NorthWestGravity;
+
+    // some sanity checks
+    if (width_inc == 0)
+        width_inc = 1;
+    if (height_inc == 0)
+        height_inc = 1;
+
+    if (base_width > min_width)
+        min_width = base_width;
+    if (base_height > min_height)
+        min_height = base_height;
+}
+
+void closestPointToAspect(unsigned int &ret_x, unsigned int &ret_y,
+                          unsigned int point_x, unsigned int point_y,
+                          unsigned int aspect_x, unsigned int aspect_y) {
+    double u = static_cast<double>(point_x * aspect_x + point_y * aspect_y) /
+               static_cast<double>(aspect_x * aspect_x + aspect_y * aspect_y);
+
+    ret_x = static_cast<unsigned int>(u * aspect_x);
+    ret_y = static_cast<unsigned int>(u * aspect_y);
+}
+
+unsigned int increaseToMultiple(unsigned int val, unsigned int inc) {
+    return val % inc ? val + inc - (val % inc) : val;
+}
+
+unsigned int decreaseToMultiple(unsigned int val, unsigned int inc) {
+    return val % inc ? val - (val % inc) : val;
+}
+    
+/**
+ * Changes width and height to the nearest (lower) value
+ * that conforms to it's size hints.
+ *
+ * display_* give the values that would be displayed
+ * to the user when resizing.
+ * We use pointers for display_* since they are optional.
+ *
+ * See ICCCM section 4.1.2.3
+ */
+void SizeHints::apply(unsigned int &width, unsigned int &height,
+                                  bool make_fit) const {
+
+    /* aspect ratios are applied exclusive to the base size
+     *
+     * min_aspect_x      width      max_aspect_x
+     * ------------  <  -------  <  ------------
+     * min_aspect_y      height     max_aspect_y
+     *
+     * The trick is how to get back to the aspect ratio with minimal
+     * change - do we modify x, y or both?
+     * A: we minimise the distance between the current point, and
+     *    the target aspect ratio (consider them as x,y coordinates)
+     *  Consider that the aspect ratio is a line, and the current
+     *  w/h is a point, so we're just using the formula for
+     *  shortest distance from a point to a line!
+     */
+
+    // make respective to base_size
+    unsigned int w = width - base_width, h = height - base_height;
+
+    if (min_aspect_y > 0 && w * min_aspect_y < min_aspect_x * h) {
+        closestPointToAspect(w, h, w, h, min_aspect_x, min_aspect_y);
+        // new w must be > old w, new h must be < old h
+        w = increaseToMultiple(w, width_inc);
+        h = decreaseToMultiple(h, height_inc);
+    } else if (max_aspect_x > 0 && w * max_aspect_y > max_aspect_x * h) {
+        closestPointToAspect(w, h, w, h, max_aspect_x, max_aspect_y);
+        // new w must be < old w, new h must be > old h
+        w = decreaseToMultiple(w, width_inc);
+        h = increaseToMultiple(h, height_inc);
+    }
+
+    // Check minimum size
+    if (w + base_width < min_width) {
+        w = increaseToMultiple(min_width - base_width, width_inc);
+        // need to check maximum aspect again
+        if (max_aspect_x > 0 && w * max_aspect_y > max_aspect_x * h)
+            h = increaseToMultiple(w * max_aspect_y / max_aspect_x, height_inc);
+    }
+
+    if (h + base_height < min_height) {
+        h = increaseToMultiple(min_height - base_height, height_inc);
+        // need to check minimum aspect again
+        if (min_aspect_y > 0 && w * min_aspect_y < min_aspect_x * h)
+            w = increaseToMultiple(h * min_aspect_x / min_aspect_y, width_inc);
+    }
+
+    unsigned int max_w = make_fit && (width < max_width || max_width == 0) ?
+                         width : max_width;
+    unsigned int max_h = make_fit && (height < max_height || max_height == 0) ?
+                         height : max_height;
+
+    // Check maximum size
+    if (max_w > 0 && w + base_width > max_w)
+        w = max_w - base_width;
+
+    if (max_h > 0 && h + base_height > max_h)
+        h = max_h - base_height;
+
+    w = decreaseToMultiple(w, width_inc);
+    h = decreaseToMultiple(h, height_inc);
+
+    // need to check aspects one more time
+    if (min_aspect_y > 0 && w * min_aspect_y < min_aspect_x * h)
+        h = decreaseToMultiple(w * min_aspect_y / min_aspect_x, height_inc);
+
+    if (max_aspect_x > 0 && w * max_aspect_y > max_aspect_x * h)
+        w = decreaseToMultiple(h * max_aspect_x / max_aspect_y, width_inc);
+
+    width = w + base_width;
+    height = h + base_height;
+}
+
+// check if the given width and height satisfy the size hints
+bool SizeHints::valid(unsigned int w, unsigned int h) const {
+    if (w < min_width || h < min_height)
+        return false;
+
+    if (w > max_width || h > max_height)
+        return false;
+
+    if ((w - base_width) % width_inc != 0)
+        return false;
+
+    if ((h - base_height) % height_inc != 0)
+        return false;
+
+    if (min_aspect_x * (h - base_height) > (w - base_width) * min_aspect_y)
+        return false;
+
+    if (max_aspect_x * (h - base_height) < (w - base_width) * max_aspect_y)
+        return false;
+
+    return true;
+}
+
+void SizeHints::displaySize(unsigned int &i, unsigned int &j,
+        unsigned int width, unsigned int height) const {
+    i = (width - base_width) / width_inc;
+    j = (height - base_height) / height_inc;
+}
diff --git a/src/WindowState.hh b/src/WindowState.hh
new file mode 100644
index 0000000..fa17eb6
--- /dev/null
+++ b/src/WindowState.hh
@@ -0,0 +1,118 @@
+// WindowState.hh
+// Copyright (c) 2008 Fluxbox Team (fluxgen at fluxbox dot org)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+#ifndef WINDOWSTATE_HH
+#define WINDOWSTATE_HH
+
+#include <X11/Xutil.h>
+
+#include <string>
+
+class SizeHints {
+public:
+    SizeHints():
+        min_width(1), max_width(0), min_height(1), max_height(0),
+        width_inc(1), height_inc(1), base_width(0), base_height(0),
+        min_aspect_x(0), max_aspect_x(1),
+        min_aspect_y(1), max_aspect_y(0),
+        win_gravity(0) { }
+
+    void reset(const XSizeHints &sizehint);
+
+    void apply(unsigned int &w, unsigned int &h,
+               bool maximizing = false) const;
+    bool valid(unsigned int width, unsigned int height) const;
+    void displaySize(unsigned int &i, unsigned int &j,
+                     unsigned int width, unsigned int height) const;
+
+    unsigned int min_width, max_width, min_height, max_height,
+                 width_inc, height_inc, base_width, base_height,
+                 min_aspect_x, max_aspect_x, min_aspect_y, max_aspect_y;
+    int win_gravity;
+};
+
+class WindowState {
+public:
+
+    /**
+     * Types of maximization
+     */
+    enum MaximizeMode {
+        MAX_NONE = 0, ///< normal state
+        MAX_HORZ = 1, ///< maximize horizontal
+        MAX_VERT = 2, ///< maximize vertical
+        MAX_FULL = 3  ///< maximize full
+    };
+
+    /**
+       This enumeration represents individual decoration
+       attributes, they can be OR-d together to get a mask.
+       Useful for saving.
+    */
+    enum DecorationMask {
+        DECORM_TITLEBAR = (1<<0),
+        DECORM_HANDLE   = (1<<1),
+        DECORM_BORDER   = (1<<2),
+        DECORM_ICONIFY  = (1<<3),
+        DECORM_MAXIMIZE = (1<<4),
+        DECORM_CLOSE    = (1<<5),
+        DECORM_MENU     = (1<<6),
+        DECORM_STICKY   = (1<<7),
+        DECORM_SHADE    = (1<<8),
+        DECORM_TAB      = (1<<9),
+        DECORM_ENABLED  = (1<<10),
+        DECORM_LAST     = (1<<11) // useful for getting "All"
+    };
+
+    enum Decoration {
+        DECOR_NONE = 0,
+        DECOR_NORMAL = DECORM_LAST - 1,
+        DECOR_TINY = DECORM_TITLEBAR|DECORM_ICONIFY|DECORM_MENU|DECORM_TAB,
+        DECOR_TOOL = DECORM_TITLEBAR|DECORM_MENU,
+        DECOR_BORDER = DECORM_BORDER|DECORM_MENU,
+        DECOR_TAB = DECORM_BORDER|DECORM_MENU|DECORM_TAB
+    };
+
+    WindowState():
+        size_hints(),
+        deco_mask(DECOR_NORMAL),
+        focused(false),
+        shaded(false), fullscreen(false), maximized(0),
+        x(0), y(0), width(1), height(1) { }
+
+    void saveGeometry(int x, int y, unsigned int width, unsigned int height);
+
+    bool useBorder() const;
+    bool useHandle() const;
+    bool useTabs() const;
+    bool useTitlebar() const;
+
+    static int getDecoMaskFromString(const std::string &str);
+
+    SizeHints size_hints;
+    unsigned int deco_mask;
+    bool focused, shaded, fullscreen;
+    int maximized;
+    int x, y;
+    unsigned int width, height;
+};
+
+#endif // WINDOWSTATE_HH
-- 
cgit v0.11.2