]> Dogcows Code - chaz/openbox/blobdiff - src/bbwindow.cc
moving window.cc/hh to bbwindow.cc/hh since we'll have our own window class coming...
[chaz/openbox] / src / bbwindow.cc
diff --git a/src/bbwindow.cc b/src/bbwindow.cc
new file mode 100644 (file)
index 0000000..17c5396
--- /dev/null
@@ -0,0 +1,4220 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+
+#ifdef    HAVE_CONFIG_H
+#  include "../config.h"
+#endif // HAVE_CONFIG_H
+
+extern "C" {
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+
+#ifdef HAVE_STRING_H
+#  include <string.h>
+#endif // HAVE_STRING_H
+
+#ifdef    DEBUG
+#  ifdef    HAVE_STDIO_H
+#    include <stdio.h>
+#  endif // HAVE_STDIO_H
+#endif // DEBUG
+
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif // HAVE_STDLIB_H
+}
+
+#include "blackbox.hh"
+#include "font.hh"
+#include "gccache.hh"
+#include "image.hh"
+#include "screen.hh"
+#include "util.hh"
+#include "window.hh"
+#include "workspace.hh"
+
+using std::string;
+using std::abs;
+
+namespace ob {
+
+/*
+ * Initializes the class with default values/the window's set initial values.
+ */
+BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
+  // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
+  // sizeof(BlackboxWindow));
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
+#endif // DEBUG
+
+  /*
+    set timer to zero... it is initialized properly later, so we check
+    if timer is zero in the destructor, and assume that the window is not
+    fully constructed if timer is zero...
+  */
+  timer = 0;
+  blackbox = b;
+  client.window = w;
+  screen = s;
+  xatom = blackbox->getXAtom();
+
+  if (! validateClient()) {
+    delete this;
+    return;
+  }
+
+  // fetch client size and placement
+  XWindowAttributes wattrib;
+  if (! XGetWindowAttributes(otk::OBDisplay::display,
+                             client.window, &wattrib) ||
+      ! wattrib.screen || wattrib.override_redirect) {
+#ifdef    DEBUG
+    fprintf(stderr,
+            "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
+#endif // DEBUG
+
+    delete this;
+    return;
+  }
+
+  // set the eventmask early in the game so that we make sure we get
+  // all the events we are interested in
+  XSetWindowAttributes attrib_set;
+  attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
+                          StructureNotifyMask;
+  attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
+                                     ButtonMotionMask;
+  XChangeWindowAttributes(otk::OBDisplay::display, client.window,
+                          CWEventMask|CWDontPropagate, &attrib_set);
+
+  flags.moving = flags.resizing = flags.shaded = flags.visible =
+    flags.iconic = flags.focused = flags.stuck = flags.modal =
+    flags.send_focus_message = flags.shaped = flags.skip_taskbar =
+    flags.skip_pager = flags.fullscreen = False;
+  flags.maximized = 0;
+
+  blackbox_attrib.workspace = window_number = BSENTINEL;
+
+  blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = 0l;
+  blackbox_attrib.decoration = DecorNormal;
+  blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
+  blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
+
+  frame.border_w = 1;
+  frame.window = frame.plate = frame.title = frame.handle = None;
+  frame.close_button = frame.iconify_button = frame.maximize_button =
+    frame.stick_button = None;
+  frame.right_grip = frame.left_grip = None;
+
+  frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
+  frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
+    frame.ubutton_pixel = frame.fbutton_pixel = frame.uborder_pixel =
+    frame.fborder_pixel = frame.ugrip_pixel = frame.fgrip_pixel = 0;
+  frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
+  frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
+  frame.ugrip = frame.fgrip = None;
+
+  functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
+  mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
+                    Decor_Iconify | Decor_Maximize;
+
+  client.normal_hint_flags = 0;
+  client.window_group = None;
+  client.transient_for = 0;
+
+  current_state = NormalState;
+
+  /*
+    set the initial size and location of client window (relative to the
+    _root window_). This position is the reference point used with the
+    window's gravity to find the window's initial position.
+  */
+  client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
+  client.old_bw = wattrib.border_width;
+
+  lastButtonPressTime = 0;
+
+  timer = new otk::OBTimer(Openbox::instance->timerManager(),
+                           (otk::OBTimeoutHandler)timeout,
+                           this);
+  timer->setTimeout(blackbox->getAutoRaiseDelay());
+
+  // get size, aspect, minimum/maximum size and other hints set by the
+  // client
+
+  if (! getBlackboxHints())
+    getNetWMHints();
+
+  getWMProtocols();
+  getWMHints();
+  getWMNormalHints();
+
+  frame.window = createToplevelWindow();
+
+  blackbox->saveWindowSearch(frame.window, this);
+  
+  frame.plate = createChildWindow(frame.window, ExposureMask);
+  blackbox->saveWindowSearch(frame.plate, this);
+
+  // determine if this is a transient window
+  getTransientInfo();
+
+  // determine the window's type, so we can decide its decorations and
+  // functionality, or if we should not manage it at all
+  if (getWindowType()) {
+    // adjust the window decorations/behavior based on the window type
+    switch (window_type) {
+    case Type_Desktop:
+    case Type_Dock:
+    case Type_Menu:
+      blackbox_attrib.workspace = 0;  // we do need to belong to a workspace
+      flags.stuck = True;             // we show up on all workspaces
+    case Type_Splash:
+      // none of these windows are manipulated by the window manager
+      functions = 0;
+      break;
+
+    case Type_Toolbar:
+    case Type_Utility:
+      // these windows get less functionality
+      functions &= ~(Func_Maximize | Func_Resize | Func_Iconify);
+      break;
+
+    case Type_Dialog:
+      // dialogs cannot be maximized
+      functions &= ~Func_Maximize;
+      break;
+
+    case Type_Normal:
+      // normal windows retain all of the possible decorations and
+      // functionality
+      break;
+    }
+  } else {
+    getMWMHints();
+  }
+  
+  // further adjeust the window's decorations/behavior based on window sizes
+  if ((client.normal_hint_flags & PMinSize) &&
+      (client.normal_hint_flags & PMaxSize) &&
+      client.max_width <= client.min_width &&
+      client.max_height <= client.min_height) {
+    functions &= ~(Func_Resize | Func_Maximize);
+  }
+  
+  setAllowedActions();
+
+  setupDecor();
+  
+  if (decorations & Decor_Titlebar)
+    createTitlebar();
+
+  if (decorations & Decor_Handle)
+    createHandle();
+
+  // apply the size and gravity hint to the frame
+
+  upsize();
+
+  bool place_window = True;
+  if (blackbox->state() == Openbox::State_Starting || isTransient() ||
+      client.normal_hint_flags & (PPosition|USPosition)) {
+    applyGravity(frame.rect);
+
+    if (blackbox->state() == Openbox::State_Starting ||
+        client.rect.intersects(screen->getRect()))
+      place_window = False;
+  }
+
+  // add the window's strut. note this is done *after* placing the window.
+  screen->addStrut(&client.strut);
+  updateStrut();
+  
+  /*
+    the server needs to be grabbed here to prevent client's from sending
+    events while we are in the process of configuring their window.
+    We hold the grab until after we are done moving the window around.
+  */
+
+  XGrabServer(otk::OBDisplay::display);
+
+  associateClientWindow();
+
+  blackbox->saveWindowSearch(client.window, this);
+
+  if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
+    screen->getCurrentWorkspace()->addWindow(this, place_window);
+  else
+    screen->getWorkspace(blackbox_attrib.workspace)->
+      addWindow(this, place_window);
+
+  if (! place_window) {
+    // don't need to call configure if we are letting the workspace
+    // place the window
+    configure(frame.rect.x(), frame.rect.y(),
+              frame.rect.width(), frame.rect.height());
+
+  }
+
+  positionWindows();
+
+  XUngrabServer(otk::OBDisplay::display);
+
+#ifdef    SHAPE
+  if (blackbox->hasShapeExtensions() && flags.shaped)
+    configureShape();
+#endif // SHAPE
+
+  // now that we know where to put the window and what it should look like
+  // we apply the decorations
+  decorate();
+
+  grabButtons();
+
+  XMapSubwindows(otk::OBDisplay::display, frame.window);
+
+  // this ensures the title, buttons, and other decor are properly displayed
+  redrawWindowFrame();
+
+  // preserve the window's initial state on first map, and its current state
+  // across a restart
+  unsigned long initial_state = current_state;
+  if (! getState())
+    current_state = initial_state;
+
+  // get sticky state from our parent window if we've got one
+  if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
+      client.transient_for->isStuck() != flags.stuck)
+    flags.stuck = True;
+
+  if (flags.shaded) {
+    flags.shaded = False;
+    initial_state = current_state;
+    shade();
+
+    /*
+      At this point in the life of a window, current_state should only be set
+      to IconicState if the window was an *icon*, not if it was shaded.
+    */
+    if (initial_state != IconicState)
+      current_state = NormalState;
+  }
+
+  if (flags.stuck) {
+    flags.stuck = False;
+    stick();
+  }
+
+  if (flags.maximized && (functions & Func_Maximize))
+    remaximize();
+}
+
+
+BlackboxWindow::~BlackboxWindow(void) {
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
+          client.window);
+#endif // DEBUG
+
+  if (! timer) // window not managed...
+    return;
+
+  if (flags.moving)
+    endMove();
+
+  screen->removeStrut(&client.strut);
+  screen->updateAvailableArea();
+
+  // We don't need to worry about resizing because resizing always grabs the X
+  // server. This should only ever happen if using opaque moving.
+  if (flags.moving)
+    endMove();
+
+  delete timer;
+
+  if (client.window_group) {
+    BWindowGroup *group = blackbox->searchGroup(client.window_group);
+    if (group) group->removeWindow(this);
+  }
+
+  // remove ourselves from our transient_for
+  if (isTransient()) {
+    if (client.transient_for != (BlackboxWindow *) ~0ul)
+      client.transient_for->client.transientList.remove(this);
+    client.transient_for = (BlackboxWindow*) 0;
+  }
+
+  if (client.transientList.size() > 0) {
+    // reset transient_for for all transients
+    BlackboxWindowList::iterator it, end = client.transientList.end();
+    for (it = client.transientList.begin(); it != end; ++it)
+      (*it)->client.transient_for = (BlackboxWindow*) 0;
+  }
+
+  if (frame.title)
+    destroyTitlebar();
+
+  if (frame.handle)
+    destroyHandle();
+
+  if (frame.plate) {
+    blackbox->removeWindowSearch(frame.plate);
+    XDestroyWindow(otk::OBDisplay::display, frame.plate);
+  }
+
+  if (frame.window) {
+    blackbox->removeWindowSearch(frame.window);
+    XDestroyWindow(otk::OBDisplay::display, frame.window);
+  }
+
+  blackbox->removeWindowSearch(client.window);
+}
+
+
+void BlackboxWindow::enableDecor(bool enable) {
+  blackbox_attrib.flags |= AttribDecoration;
+  blackbox_attrib.decoration = enable ? DecorNormal : DecorNone;
+  setupDecor();
+    
+  // we can not be shaded if we lack a titlebar
+  if (! (decorations & Decor_Titlebar) && flags.shaded)
+    shade();
+    
+  if (flags.visible && frame.window) {
+    XMapSubwindows(otk::OBDisplay::display, frame.window);
+    XMapWindow(otk::OBDisplay::display, frame.window);
+  }
+
+  reconfigure();
+  setState(current_state);
+}
+
+
+void BlackboxWindow::setupDecor() {
+  if (blackbox_attrib.decoration != DecorNone) {
+    // start with everything on
+    decorations = Decor_Close |
+      (mwm_decorations & Decor_Titlebar ? Decor_Titlebar : 0) |
+      (mwm_decorations & Decor_Border ? Decor_Border : 0) |
+      (mwm_decorations & Decor_Handle ? Decor_Handle : 0) |
+      (mwm_decorations & Decor_Iconify ? Decor_Iconify : 0) |
+      (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0);
+
+    if (! (functions & Func_Close)) decorations &= ~Decor_Close;
+    if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize;
+    if (! (functions & Func_Iconify)) decorations &= ~Decor_Iconify;
+    if (! (functions & Func_Resize)) decorations &= ~Decor_Handle;
+
+    switch (window_type) {
+    case Type_Desktop:
+    case Type_Dock:
+    case Type_Menu:
+    case Type_Splash:
+      // none of these windows are decorated by the window manager at all
+      decorations = 0;
+      break;
+
+    case Type_Toolbar:
+    case Type_Utility:
+      decorations &= ~(Decor_Border);
+      break;
+
+    case Type_Dialog:
+      decorations &= ~Decor_Handle;
+      break;
+
+    case Type_Normal:
+      break;
+    }
+  } else {
+    decorations = 0;
+  }
+}
+
+/*
+ * Creates a new top level window, with a given location, size, and border
+ * width.
+ * Returns: the newly created window
+ */
+Window BlackboxWindow::createToplevelWindow(void) {
+  XSetWindowAttributes attrib_create;
+  unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
+                              CWOverrideRedirect | CWEventMask;
+
+  attrib_create.background_pixmap = None;
+  attrib_create.colormap = screen->getColormap();
+  attrib_create.override_redirect = True;
+  attrib_create.event_mask = EnterWindowMask | LeaveWindowMask |
+                             ButtonPress;
+  /*
+    We catch button presses because other wise they get passed down to the
+    root window, which will then cause root menus to show when you click the
+    window's frame.
+  */
+
+  return XCreateWindow(otk::OBDisplay::display, screen->getRootWindow(),
+                       0, 0, 1, 1, frame.border_w, screen->getDepth(),
+                       InputOutput, screen->getVisual(), create_mask,
+                       &attrib_create);
+}
+
+
+/*
+ * Creates a child window, and optionally associates a given cursor with
+ * the new window.
+ */
+Window BlackboxWindow::createChildWindow(Window parent,
+                                         unsigned long event_mask,
+                                         Cursor cursor) {
+  XSetWindowAttributes attrib_create;
+  unsigned long create_mask = CWBackPixmap | CWBorderPixel |
+                              CWEventMask;
+
+  attrib_create.background_pixmap = None;
+  attrib_create.event_mask = event_mask;
+
+  if (cursor) {
+    create_mask |= CWCursor;
+    attrib_create.cursor = cursor;
+  }
+
+  return XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0,
+                       screen->getDepth(), InputOutput, screen->getVisual(),
+                       create_mask, &attrib_create);
+}
+
+
+void BlackboxWindow::associateClientWindow(void) {
+  XSetWindowBorderWidth(otk::OBDisplay::display, client.window, 0);
+  getWMName();
+  getWMIconName();
+
+  XChangeSaveSet(otk::OBDisplay::display, client.window, SetModeInsert);
+
+  XSelectInput(otk::OBDisplay::display, frame.plate, SubstructureRedirectMask);
+
+  /*
+    note we used to grab around this call to XReparentWindow however the
+    server is now grabbed before this method is called
+  */
+  unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
+                             StructureNotifyMask;
+  XSelectInput(otk::OBDisplay::display, client.window,
+               event_mask & ~StructureNotifyMask);
+  XReparentWindow(otk::OBDisplay::display, client.window, frame.plate, 0, 0);
+  XSelectInput(otk::OBDisplay::display, client.window, event_mask);
+
+  XRaiseWindow(otk::OBDisplay::display, frame.plate);
+  XMapSubwindows(otk::OBDisplay::display, frame.plate);
+
+#ifdef    SHAPE
+  if (blackbox->hasShapeExtensions()) {
+    XShapeSelectInput(otk::OBDisplay::display, client.window,
+                      ShapeNotifyMask);
+
+    Bool shaped = False;
+    int foo;
+    unsigned int ufoo;
+
+    XShapeQueryExtents(otk::OBDisplay::display, client.window, &shaped,
+                       &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
+                       &ufoo, &ufoo);
+    flags.shaped = shaped;
+  }
+#endif // SHAPE
+}
+
+
+void BlackboxWindow::decorate(void) {
+  otk::BTexture* texture;
+
+  texture = &(screen->getWindowStyle()->b_focus);
+  frame.fbutton = texture->render(frame.button_w, frame.button_w,
+                                  frame.fbutton);
+  if (! frame.fbutton)
+    frame.fbutton_pixel = texture->color().pixel();
+
+  texture = &(screen->getWindowStyle()->b_unfocus);
+  frame.ubutton = texture->render(frame.button_w, frame.button_w,
+                                  frame.ubutton);
+  if (! frame.ubutton)
+    frame.ubutton_pixel = texture->color().pixel();
+
+  unsigned char needsPressed = 0;
+
+  texture = &(screen->getWindowStyle()->b_pressed_focus);
+  
+  if (texture->texture() != otk::BTexture::NoTexture) {
+    frame.pfbutton = texture->render(frame.button_w, frame.button_w,
+                                     frame.pfbutton);
+    if (! frame.pfbutton)
+      frame.pfbutton_pixel = texture->color().pixel();
+  } else {
+    needsPressed = 0x1;
+  }
+
+  texture = &(screen->getWindowStyle()->b_pressed_unfocus);
+  
+  if (texture->texture() != otk::BTexture::NoTexture) {
+    frame.pubutton = texture->render(frame.button_w, frame.button_w,
+                                     frame.pubutton);
+    if (! frame.pubutton)
+      frame.pubutton = texture->color().pixel();
+  } else {
+    needsPressed |= 0x2;
+  }
+
+  // if we either pressed unfocused, or pressed focused were undefined,
+  // make them inherit from the old resource. It's a hack for sure, but
+  // it allows for some backwards and forwards compatibility.
+  if (needsPressed) {
+    texture = &(screen->getWindowStyle()->b_pressed);
+    
+    if (needsPressed & 0x1) {
+      frame.pfbutton = texture->render(frame.button_w, frame.button_w,
+                                       frame.pfbutton);
+      if (! frame.pfbutton)
+        frame.pfbutton_pixel = texture->color().pixel();
+    }
+    if (needsPressed & 0x2) {
+      frame.pubutton = texture->render(frame.button_w, frame.button_w,
+                                       frame.pubutton);
+      if (! frame.pubutton)
+        frame.pubutton = texture->color().pixel();
+    }
+    
+  }
+
+  if (decorations & Decor_Titlebar) {
+    texture = &(screen->getWindowStyle()->t_focus);
+    frame.ftitle = texture->render(frame.inside_w, frame.title_h,
+                                   frame.ftitle);
+    if (! frame.ftitle)
+      frame.ftitle_pixel = texture->color().pixel();
+
+    texture = &(screen->getWindowStyle()->t_unfocus);
+    frame.utitle = texture->render(frame.inside_w, frame.title_h,
+                                   frame.utitle);
+    if (! frame.utitle)
+      frame.utitle_pixel = texture->color().pixel();
+
+    XSetWindowBorder(otk::OBDisplay::display, frame.title,
+                     screen->getBorderColor()->pixel());
+
+    decorateLabel();
+  }
+
+  if (decorations & Decor_Border) {
+    frame.fborder_pixel = screen->getWindowStyle()->f_focus.color().pixel();
+    frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.color().pixel();
+  }
+
+  if (decorations & Decor_Handle) {
+    texture = &(screen->getWindowStyle()->h_focus);
+    frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
+                                    frame.fhandle);
+    if (! frame.fhandle)
+      frame.fhandle_pixel = texture->color().pixel();
+
+    texture = &(screen->getWindowStyle()->h_unfocus);
+    frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
+                                    frame.uhandle);
+    if (! frame.uhandle)
+      frame.uhandle_pixel = texture->color().pixel();
+
+    texture = &(screen->getWindowStyle()->g_focus);
+    frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
+    if (! frame.fgrip)
+      frame.fgrip_pixel = texture->color().pixel();
+
+    texture = &(screen->getWindowStyle()->g_unfocus);
+    frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
+    if (! frame.ugrip)
+      frame.ugrip_pixel = texture->color().pixel();
+
+    XSetWindowBorder(otk::OBDisplay::display, frame.handle,
+                     screen->getBorderColor()->pixel());
+    XSetWindowBorder(otk::OBDisplay::display, frame.left_grip,
+                     screen->getBorderColor()->pixel());
+    XSetWindowBorder(otk::OBDisplay::display, frame.right_grip,
+                     screen->getBorderColor()->pixel());
+  }
+
+  XSetWindowBorder(otk::OBDisplay::display, frame.window,
+                   screen->getBorderColor()->pixel());
+}
+
+
+void BlackboxWindow::decorateLabel(void) {
+  otk::BTexture *texture;
+
+  texture = &(screen->getWindowStyle()->l_focus);
+  frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
+  if (! frame.flabel)
+    frame.flabel_pixel = texture->color().pixel();
+
+  texture = &(screen->getWindowStyle()->l_unfocus);
+  frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
+  if (! frame.ulabel)
+    frame.ulabel_pixel = texture->color().pixel();
+}
+
+
+void BlackboxWindow::createHandle(void) {
+  frame.handle = createChildWindow(frame.window,
+                                   ButtonPressMask | ButtonReleaseMask |
+                                   ButtonMotionMask | ExposureMask);
+  blackbox->saveWindowSearch(frame.handle, this);
+
+  frame.left_grip =
+    createChildWindow(frame.handle,
+                      ButtonPressMask | ButtonReleaseMask |
+                      ButtonMotionMask | ExposureMask,
+                      blackbox->getLowerLeftAngleCursor());
+  blackbox->saveWindowSearch(frame.left_grip, this);
+
+  frame.right_grip =
+    createChildWindow(frame.handle,
+                      ButtonPressMask | ButtonReleaseMask |
+                      ButtonMotionMask | ExposureMask,
+                      blackbox->getLowerRightAngleCursor());
+  blackbox->saveWindowSearch(frame.right_grip, this);
+}
+
+
+void BlackboxWindow::destroyHandle(void) {
+  if (frame.fhandle)
+    screen->getImageControl()->removeImage(frame.fhandle);
+
+  if (frame.uhandle)
+    screen->getImageControl()->removeImage(frame.uhandle);
+
+  if (frame.fgrip)
+    screen->getImageControl()->removeImage(frame.fgrip);
+
+  if (frame.ugrip)
+    screen->getImageControl()->removeImage(frame.ugrip);
+
+  blackbox->removeWindowSearch(frame.left_grip);
+  blackbox->removeWindowSearch(frame.right_grip);
+
+  XDestroyWindow(otk::OBDisplay::display, frame.left_grip);
+  XDestroyWindow(otk::OBDisplay::display, frame.right_grip);
+  frame.left_grip = frame.right_grip = None;
+
+  blackbox->removeWindowSearch(frame.handle);
+  XDestroyWindow(otk::OBDisplay::display, frame.handle);
+  frame.handle = None;
+}
+
+
+void BlackboxWindow::createTitlebar(void) {
+  frame.title = createChildWindow(frame.window,
+                                  ButtonPressMask | ButtonReleaseMask |
+                                  ButtonMotionMask | ExposureMask);
+  frame.label = createChildWindow(frame.title,
+                                  ButtonPressMask | ButtonReleaseMask |
+                                  ButtonMotionMask | ExposureMask);
+  blackbox->saveWindowSearch(frame.title, this);
+  blackbox->saveWindowSearch(frame.label, this);
+
+  if (decorations & Decor_Iconify) createIconifyButton();
+  if (decorations & Decor_Maximize) createMaximizeButton();
+  if (decorations & Decor_Close) createCloseButton();
+}
+
+
+void BlackboxWindow::destroyTitlebar(void) {
+  if (frame.close_button)
+    destroyCloseButton();
+
+  if (frame.iconify_button)
+    destroyIconifyButton();
+
+  if (frame.maximize_button)
+    destroyMaximizeButton();
+
+  if (frame.stick_button)
+    destroyStickyButton();
+
+  if (frame.ftitle)
+    screen->getImageControl()->removeImage(frame.ftitle);
+
+  if (frame.utitle)
+    screen->getImageControl()->removeImage(frame.utitle);
+
+  if (frame.flabel)
+    screen->getImageControl()->removeImage(frame.flabel);
+
+  if( frame.ulabel)
+    screen->getImageControl()->removeImage(frame.ulabel);
+
+  if (frame.fbutton)
+    screen->getImageControl()->removeImage(frame.fbutton);
+
+  if (frame.ubutton)
+    screen->getImageControl()->removeImage(frame.ubutton);
+
+  blackbox->removeWindowSearch(frame.title);
+  blackbox->removeWindowSearch(frame.label);
+
+  XDestroyWindow(otk::OBDisplay::display, frame.label);
+  XDestroyWindow(otk::OBDisplay::display, frame.title);
+  frame.title = frame.label = None;
+}
+
+
+void BlackboxWindow::createCloseButton(void) {
+  if (frame.title != None) {
+    frame.close_button = createChildWindow(frame.title,
+                                           ButtonPressMask |
+                                           ButtonReleaseMask |
+                                           ButtonMotionMask | ExposureMask);
+    blackbox->saveWindowSearch(frame.close_button, this);
+  }
+}
+
+
+void BlackboxWindow::destroyCloseButton(void) {
+  blackbox->removeWindowSearch(frame.close_button);
+  XDestroyWindow(otk::OBDisplay::display, frame.close_button);
+  frame.close_button = None;
+}
+
+
+void BlackboxWindow::createIconifyButton(void) {
+  if (frame.title != None) {
+    frame.iconify_button = createChildWindow(frame.title,
+                                             ButtonPressMask |
+                                             ButtonReleaseMask |
+                                             ButtonMotionMask | ExposureMask);
+    blackbox->saveWindowSearch(frame.iconify_button, this);
+  }
+}
+
+
+void BlackboxWindow::destroyIconifyButton(void) {
+  blackbox->removeWindowSearch(frame.iconify_button);
+  XDestroyWindow(otk::OBDisplay::display, frame.iconify_button);
+  frame.iconify_button = None;
+}
+
+
+void BlackboxWindow::createMaximizeButton(void) {
+  if (frame.title != None) {
+    frame.maximize_button = createChildWindow(frame.title,
+                                              ButtonPressMask |
+                                              ButtonReleaseMask |
+                                              ButtonMotionMask | ExposureMask);
+    blackbox->saveWindowSearch(frame.maximize_button, this);
+  }
+}
+
+
+void BlackboxWindow::destroyMaximizeButton(void) {
+  blackbox->removeWindowSearch(frame.maximize_button);
+  XDestroyWindow(otk::OBDisplay::display, frame.maximize_button);
+  frame.maximize_button = None;
+}
+
+void BlackboxWindow::createStickyButton(void) {
+  if (frame.title != None) {
+    frame.stick_button = createChildWindow(frame.title,
+                                           ButtonPressMask |
+                                           ButtonReleaseMask |
+                                           ButtonMotionMask | ExposureMask);
+    blackbox->saveWindowSearch(frame.stick_button, this);
+  }
+}
+
+void BlackboxWindow::destroyStickyButton(void) {
+  blackbox->removeWindowSearch(frame.stick_button);
+  XDestroyWindow(otk::OBDisplay::display, frame.stick_button);
+  frame.stick_button = None;
+}
+
+void BlackboxWindow::positionButtons(bool redecorate_label) {
+  string layout = blackbox->getTitlebarLayout();
+  string parsed;
+
+  bool hasclose, hasiconify, hasmaximize, haslabel, hasstick;
+  hasclose = hasiconify = hasmaximize = haslabel = hasstick = false;
+
+  string::const_iterator it, end;
+  for (it = layout.begin(), end = layout.end(); it != end; ++it) {
+    switch(*it) {
+    case 'C':
+      if (! hasclose && (decorations & Decor_Close)) {
+        hasclose = true;
+        parsed += *it;
+      }
+      break;
+    case 'I':
+      if (! hasiconify && (decorations & Decor_Iconify)) {
+        hasiconify = true;
+        parsed += *it;
+      }
+      break;
+    case 'S':
+      if (!hasstick) {
+        hasstick = true;
+        parsed += *it;
+      }
+      break;
+    case 'M':
+      if (! hasmaximize && (decorations & Decor_Maximize)) {
+        hasmaximize = true;
+        parsed += *it;
+      }
+      break;
+    case 'L':
+      if (! haslabel) {
+        haslabel = true;
+        parsed += *it;
+      }
+      break;
+    }
+  }
+  
+  if (! hasclose && frame.close_button)
+    destroyCloseButton();
+  if (! hasiconify && frame.iconify_button)
+    destroyIconifyButton();
+  if (! hasmaximize && frame.maximize_button)
+    destroyMaximizeButton();
+  if (! hasstick && frame.stick_button)
+    destroyStickyButton();
+  if (! haslabel)
+    parsed += 'L';      // require that the label be in the layout
+
+  const unsigned int bsep = frame.bevel_w + 1;  // separation between elements
+  const unsigned int by = frame.bevel_w + 1;
+  const unsigned int ty = frame.bevel_w;
+
+  frame.label_w = frame.inside_w - bsep * 2 -
+    (frame.button_w + bsep) * (parsed.size() - 1);
+
+  unsigned int x = bsep;
+  for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
+    switch(*it) {
+    case 'C':
+      if (! frame.close_button) createCloseButton();
+      XMoveResizeWindow(otk::OBDisplay::display, frame.close_button, x, by,
+                        frame.button_w, frame.button_w);
+      x += frame.button_w + bsep;
+      break;
+    case 'I':
+      if (! frame.iconify_button) createIconifyButton();
+      XMoveResizeWindow(otk::OBDisplay::display, frame.iconify_button, x, by,
+                        frame.button_w, frame.button_w);
+      x += frame.button_w + bsep;
+      break;
+    case 'S':
+      if (! frame.stick_button) createStickyButton();
+      XMoveResizeWindow(otk::OBDisplay::display, frame.stick_button, x, by,
+                        frame.button_w, frame.button_w);
+      x += frame.button_w + bsep;
+      break;
+    case 'M':
+      if (! frame.maximize_button) createMaximizeButton();
+      XMoveResizeWindow(otk::OBDisplay::display, frame.maximize_button, x, by,
+                        frame.button_w, frame.button_w);
+      x += frame.button_w + bsep;
+      break;
+    case 'L':
+      XMoveResizeWindow(otk::OBDisplay::display, frame.label, x, ty,
+                        frame.label_w, frame.label_h);
+      x += frame.label_w + bsep;
+      break;
+    }
+  }
+
+  if (redecorate_label) decorateLabel();
+  redrawLabel();
+  redrawAllButtons();
+}
+
+
+void BlackboxWindow::reconfigure(void) {
+  restoreGravity(client.rect);
+  upsize();
+  applyGravity(frame.rect);
+  positionWindows();
+  decorate();
+  redrawWindowFrame();
+
+  ungrabButtons();
+  grabButtons();
+}
+
+
+void BlackboxWindow::grabButtons(void) {
+  mod_mask = blackbox->getMouseModMask();
+
+  if (! screen->isSloppyFocus() || screen->doClickRaise())
+    // grab button 1 for changing focus/raising
+    otk::OBDisplay::grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
+                               GrabModeSync, GrabModeSync, frame.plate, None,
+                               screen->allowScrollLock());
+  
+  if (functions & Func_Move)
+    otk::OBDisplay::grabButton(Button1, mod_mask, frame.window, True,
+                         ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
+                         GrabModeAsync, frame.window, None,
+                         screen->allowScrollLock());
+  if (functions & Func_Resize)
+    otk::OBDisplay::grabButton(Button3, mod_mask, frame.window, True,
+                         ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
+                         GrabModeAsync, frame.window, None,
+                         screen->allowScrollLock());
+  // alt+middle lowers the window
+  otk::OBDisplay::grabButton(Button2, mod_mask, frame.window, True,
+                       ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
+                       frame.window, None, screen->allowScrollLock());
+}
+
+
+void BlackboxWindow::ungrabButtons(void) {
+  otk::OBDisplay::ungrabButton(Button1, 0, frame.plate);
+  otk::OBDisplay::ungrabButton(Button1, mod_mask, frame.window);
+  otk::OBDisplay::ungrabButton(Button2, mod_mask, frame.window);
+  otk::OBDisplay::ungrabButton(Button3, mod_mask, frame.window);
+}
+
+
+void BlackboxWindow::positionWindows(void) {
+  XMoveResizeWindow(otk::OBDisplay::display, frame.window,
+                    frame.rect.x(), frame.rect.y(), frame.inside_w,
+                    (flags.shaded) ? frame.title_h : frame.inside_h);
+  XSetWindowBorderWidth(otk::OBDisplay::display, frame.window,
+                        frame.border_w);
+  XSetWindowBorderWidth(otk::OBDisplay::display, frame.plate,
+                        frame.mwm_border_w);
+  XMoveResizeWindow(otk::OBDisplay::display, frame.plate,
+                    frame.margin.left - frame.mwm_border_w - frame.border_w,
+                    frame.margin.top - frame.mwm_border_w - frame.border_w,
+                    client.rect.width(), client.rect.height());
+  XMoveResizeWindow(otk::OBDisplay::display, client.window,
+                    0, 0, client.rect.width(), client.rect.height());
+  // ensure client.rect contains the real location
+  client.rect.setPos(frame.rect.left() + frame.margin.left,
+                     frame.rect.top() + frame.margin.top);
+
+  if (decorations & Decor_Titlebar) {
+    if (frame.title == None) createTitlebar();
+
+    XSetWindowBorderWidth(otk::OBDisplay::display, frame.title,
+                          frame.border_w);
+    XMoveResizeWindow(otk::OBDisplay::display, frame.title, -frame.border_w,
+                      -frame.border_w, frame.inside_w, frame.title_h);
+
+    positionButtons();
+    XMapSubwindows(otk::OBDisplay::display, frame.title);
+    XMapWindow(otk::OBDisplay::display, frame.title);
+  } else if (frame.title) {
+    destroyTitlebar();
+  }
+  if (decorations & Decor_Handle) {
+    if (frame.handle == None) createHandle();
+    XSetWindowBorderWidth(otk::OBDisplay::display, frame.handle,
+                          frame.border_w);
+    XSetWindowBorderWidth(otk::OBDisplay::display, frame.left_grip,
+                          frame.border_w);
+    XSetWindowBorderWidth(otk::OBDisplay::display, frame.right_grip,
+                          frame.border_w);
+
+    // use client.rect here so the value is correct even if shaded
+    XMoveResizeWindow(otk::OBDisplay::display, frame.handle,
+                      -frame.border_w,
+                      client.rect.height() + frame.margin.top +
+                      frame.mwm_border_w - frame.border_w,
+                      frame.inside_w, frame.handle_h);
+    XMoveResizeWindow(otk::OBDisplay::display, frame.left_grip,
+                      -frame.border_w, -frame.border_w,
+                      frame.grip_w, frame.handle_h);
+    XMoveResizeWindow(otk::OBDisplay::display, frame.right_grip,
+                      frame.inside_w - frame.grip_w - frame.border_w,
+                      -frame.border_w, frame.grip_w, frame.handle_h);
+
+    XMapSubwindows(otk::OBDisplay::display, frame.handle);
+    XMapWindow(otk::OBDisplay::display, frame.handle);
+  } else if (frame.handle) {
+    destroyHandle();
+  }
+  XSync(otk::OBDisplay::display, False);
+}
+
+
+void BlackboxWindow::updateStrut(void) {
+  unsigned long num = 4;
+  unsigned long *data;
+  if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
+                        num, &data))
+    return;
+  if (num == 4) {
+    client.strut.left = data[0];
+    client.strut.right = data[1];
+    client.strut.top = data[2];
+    client.strut.bottom = data[3];
+
+    screen->updateAvailableArea();
+  }
+
+  delete [] data;
+}
+
+
+bool BlackboxWindow::getWindowType(void) {
+  window_type = (WindowType) -1;
+
+  unsigned long *val;
+  unsigned long num = (unsigned) -1;
+  if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
+                        num, &val)) {
+    for (unsigned long i = 0; i < num; ++i) {
+      if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_desktop))
+        window_type = Type_Desktop;
+      else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dock))
+        window_type = Type_Dock;
+      else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
+        window_type = Type_Toolbar;
+      else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_menu))
+        window_type = Type_Menu;
+      else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_utility))
+        window_type = Type_Utility;
+      else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_splash))
+        window_type = Type_Splash;
+      else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dialog))
+        window_type = Type_Dialog;
+      else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_normal))
+        window_type = Type_Normal;
+      else if (val[i] ==
+               xatom->getAtom(XAtom::kde_net_wm_window_type_override))
+        mwm_decorations = 0; // prevent this window from getting any decor
+    }
+    delete val;
+  }
+    
+  if (window_type == (WindowType) -1) {
+    /*
+     * the window type hint was not set, which means we either classify ourself
+     * as a normal window or a dialog, depending on if we are a transient.
+     */
+    if (isTransient())
+      window_type = Type_Dialog;
+    else
+      window_type = Type_Normal;
+
+    return False;
+  }
+
+  return True;
+}
+
+
+void BlackboxWindow::getWMName(void) {
+  if (xatom->getValue(client.window, XAtom::net_wm_name,
+                      XAtom::utf8, client.title) &&
+      !client.title.empty()) {
+    xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
+    return;
+  }
+  //fall through to using WM_NAME
+  if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
+      && !client.title.empty()) {
+    xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
+    return;
+  }
+  // fall back to an internal default
+  client.title = "Unnamed";
+  xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
+                  client.title);
+
+#ifdef DEBUG_WITH_ID
+  // the 16 is the 8 chars of the debug text plus the number
+  char *tmp = new char[client.title.length() + 16];
+  sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window);
+  client.title = tmp;
+  delete tmp;
+#endif
+}
+
+
+void BlackboxWindow::getWMIconName(void) {
+  if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
+                      XAtom::utf8, client.icon_title) && 
+      !client.icon_title.empty()) {
+    xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
+    return;
+  }
+  //fall through to using WM_ICON_NAME
+  if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
+                      client.icon_title) && 
+      !client.icon_title.empty()) {
+    xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
+    return;
+  }
+  // fall back to using the main name
+  client.icon_title = client.title;
+  xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
+                  client.icon_title);
+}
+
+
+/*
+ * Retrieve which WM Protocols are supported by the client window.
+ * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
+ * window's decorations and allow the close behavior.
+ * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
+ * this.
+ */
+void BlackboxWindow::getWMProtocols(void) {
+  Atom *proto;
+  int num_return = 0;
+
+  if (XGetWMProtocols(otk::OBDisplay::display, client.window,
+                      &proto, &num_return)) {
+    for (int i = 0; i < num_return; ++i) {
+      if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
+        decorations |= Decor_Close;
+        functions |= Func_Close;
+      } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
+        flags.send_focus_message = True;
+    }
+
+    XFree(proto);
+  }
+}
+
+
+/*
+ * Gets the value of the WM_HINTS property.
+ * If the property is not set, then use a set of default values.
+ */
+void BlackboxWindow::getWMHints(void) {
+  focus_mode = F_Passive;
+
+  // remove from current window group
+  if (client.window_group) {
+    BWindowGroup *group = blackbox->searchGroup(client.window_group);
+    if (group) group->removeWindow(this);
+  }
+  client.window_group = None;
+
+  XWMHints *wmhint = XGetWMHints(otk::OBDisplay::display, client.window);
+  if (! wmhint) {
+    return;
+  }
+
+  if (wmhint->flags & InputHint) {
+    if (wmhint->input == True) {
+      if (flags.send_focus_message)
+        focus_mode = F_LocallyActive;
+    } else {
+      if (flags.send_focus_message)
+        focus_mode = F_GloballyActive;
+      else
+        focus_mode = F_NoInput;
+    }
+  }
+
+  if (wmhint->flags & StateHint)
+    current_state = wmhint->initial_state;
+
+  if (wmhint->flags & WindowGroupHint) {
+    client.window_group = wmhint->window_group;
+
+    // add window to the appropriate group
+    BWindowGroup *group = blackbox->searchGroup(client.window_group);
+    if (! group) { // no group found, create it!
+      new BWindowGroup(blackbox, client.window_group);
+      group = blackbox->searchGroup(client.window_group);
+    }
+    if (group)
+      group->addWindow(this);
+  }
+
+  XFree(wmhint);
+}
+
+
+/*
+ * Gets the value of the WM_NORMAL_HINTS property.
+ * If the property is not set, then use a set of default values.
+ */
+void BlackboxWindow::getWMNormalHints(void) {
+  long icccm_mask;
+  XSizeHints sizehint;
+
+  client.min_width = client.min_height =
+    client.width_inc = client.height_inc = 1;
+  client.base_width = client.base_height = 0;
+  client.win_gravity = NorthWestGravity;
+#if 0
+  client.min_aspect_x = client.min_aspect_y =
+    client.max_aspect_x = client.max_aspect_y = 1;
+#endif
+
+  // don't limit the size of a window, the default max width is the biggest
+  // possible
+  client.max_width = (unsigned) -1;
+  client.max_height = (unsigned) -1;
+
+
+  if (! XGetWMNormalHints(otk::OBDisplay::display, client.window,
+                          &sizehint, &icccm_mask))
+    return;
+
+  client.normal_hint_flags = sizehint.flags;
+
+  if (sizehint.flags & PMinSize) {
+    if (sizehint.min_width >= 0)
+      client.min_width = sizehint.min_width;
+    if (sizehint.min_height >= 0)
+      client.min_height = sizehint.min_height;
+  }
+
+  if (sizehint.flags & PMaxSize) {
+    if (sizehint.max_width > static_cast<signed>(client.min_width))
+      client.max_width = sizehint.max_width;
+    else
+      client.max_width = client.min_width;
+
+    if (sizehint.max_height > static_cast<signed>(client.min_height))
+      client.max_height = sizehint.max_height;
+    else
+      client.max_height = client.min_height;
+  }
+
+  if (sizehint.flags & PResizeInc) {
+    client.width_inc = sizehint.width_inc;
+    client.height_inc = sizehint.height_inc;
+  }
+
+#if 0 // we do not support this at the moment
+  if (sizehint.flags & PAspect) {
+    client.min_aspect_x = sizehint.min_aspect.x;
+    client.min_aspect_y = sizehint.min_aspect.y;
+    client.max_aspect_x = sizehint.max_aspect.x;
+    client.max_aspect_y = sizehint.max_aspect.y;
+  }
+#endif
+
+  if (sizehint.flags & PBaseSize) {
+    client.base_width = sizehint.base_width;
+    client.base_height = sizehint.base_height;
+  }
+
+  if (sizehint.flags & PWinGravity)
+    client.win_gravity = sizehint.win_gravity;
+}
+
+
+/*
+ * Gets the NETWM hints for the class' contained window.
+ */
+void BlackboxWindow::getNetWMHints(void) {
+  unsigned long workspace;
+
+  if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
+                      workspace)) {
+    if (workspace == 0xffffffff)
+      flags.stuck = True;
+    else
+      blackbox_attrib.workspace = workspace;
+  }
+
+  unsigned long *state;
+  unsigned long num = (unsigned) -1;
+  if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
+                      num, &state)) {
+    bool vert = False,
+         horz = False;
+    for (unsigned long i = 0; i < num; ++i) {
+      if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
+        flags.modal = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
+        flags.shaded = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
+        flags.skip_taskbar = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
+        flags.skip_pager = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
+        flags.fullscreen = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
+        setState(IconicState);
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
+        vert = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
+        horz = True;
+    }
+    if (vert && horz)
+      flags.maximized = 1;
+    else if (vert)
+      flags.maximized = 2;
+    else if (horz)
+      flags.maximized = 3;
+
+    delete [] state;
+  }
+}
+
+
+/*
+ * Gets the MWM hints for the class' contained window.
+ * This is used while initializing the window to its first state, and not
+ * thereafter.
+ * Returns: true if the MWM hints are successfully retreived and applied;
+ * false if they are not.
+ */
+void BlackboxWindow::getMWMHints(void) {
+  unsigned long num;
+  MwmHints *mwm_hint;
+
+  num = PropMwmHintsElements;
+  if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
+                        XAtom::motif_wm_hints, num,
+                        (unsigned long **)&mwm_hint))
+    return;
+  if (num < PropMwmHintsElements) {
+    delete [] mwm_hint;
+    return;
+  }
+
+  if (mwm_hint->flags & MwmHintsDecorations) {
+    if (mwm_hint->decorations & MwmDecorAll) {
+      mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
+                        Decor_Iconify | Decor_Maximize;
+    } else {
+      mwm_decorations = 0;
+
+      if (mwm_hint->decorations & MwmDecorBorder)
+        mwm_decorations |= Decor_Border;
+      if (mwm_hint->decorations & MwmDecorHandle)
+        mwm_decorations |= Decor_Handle;
+      if (mwm_hint->decorations & MwmDecorTitle)
+        mwm_decorations |= Decor_Titlebar;
+      if (mwm_hint->decorations & MwmDecorIconify)
+        mwm_decorations |= Decor_Iconify;
+      if (mwm_hint->decorations & MwmDecorMaximize)
+        mwm_decorations |= Decor_Maximize;
+    }
+  }
+
+  if (mwm_hint->flags & MwmHintsFunctions) {
+    if (mwm_hint->functions & MwmFuncAll) {
+      functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
+                  Func_Close;
+    } else {
+      functions = 0;
+
+      if (mwm_hint->functions & MwmFuncResize)
+        functions |= Func_Resize;
+      if (mwm_hint->functions & MwmFuncMove)
+        functions |= Func_Move;
+      if (mwm_hint->functions & MwmFuncIconify)
+        functions |= Func_Iconify;
+      if (mwm_hint->functions & MwmFuncMaximize)
+        functions |= Func_Maximize;
+      if (mwm_hint->functions & MwmFuncClose)
+        functions |= Func_Close;
+    }
+  }
+  delete [] mwm_hint;
+}
+
+
+/*
+ * Gets the blackbox hints from the class' contained window.
+ * This is used while initializing the window to its first state, and not
+ * thereafter.
+ * Returns: true if the hints are successfully retreived and applied; false if
+ * they are not.
+ */
+bool BlackboxWindow::getBlackboxHints(void) {
+  unsigned long num;
+  BlackboxHints *blackbox_hint;
+
+  num = PropBlackboxHintsElements;
+  if (! xatom->getValue(client.window, XAtom::blackbox_hints,
+                        XAtom::blackbox_hints, num,
+                        (unsigned long **)&blackbox_hint))
+    return False;
+  if (num < PropBlackboxHintsElements) {
+    delete [] blackbox_hint;
+    return False;
+  }
+
+  if (blackbox_hint->flags & AttribShaded)
+    flags.shaded = (blackbox_hint->attrib & AttribShaded);
+
+  if ((blackbox_hint->flags & AttribMaxHoriz) &&
+      (blackbox_hint->flags & AttribMaxVert))
+    flags.maximized = (blackbox_hint->attrib &
+                       (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
+  else if (blackbox_hint->flags & AttribMaxVert)
+    flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
+  else if (blackbox_hint->flags & AttribMaxHoriz)
+    flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
+
+  if (blackbox_hint->flags & AttribOmnipresent)
+    flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
+
+  if (blackbox_hint->flags & AttribWorkspace)
+    blackbox_attrib.workspace = blackbox_hint->workspace;
+
+  // if (blackbox_hint->flags & AttribStack)
+  //   don't yet have always on top/bottom for blackbox yet... working
+  //   on that
+
+  if (blackbox_hint->flags & AttribDecoration) {
+    switch (blackbox_hint->decoration) {
+    case DecorNone:
+      blackbox_attrib.decoration = DecorNone;
+      break;
+
+    case DecorTiny:
+    case DecorTool:
+    case DecorNormal:
+    default:
+      // blackbox_attrib.decoration defaults to DecorNormal
+      break;
+    }
+  }
+  
+  delete [] blackbox_hint;
+
+  return True;
+}
+
+
+void BlackboxWindow::getTransientInfo(void) {
+  if (client.transient_for &&
+      client.transient_for != (BlackboxWindow *) ~0ul) {
+    // reset transient_for in preparation of looking for a new owner
+    client.transient_for->client.transientList.remove(this);
+  }
+
+  // we have no transient_for until we find a new one
+  client.transient_for = (BlackboxWindow *) 0;
+
+  Window trans_for;
+  if (! XGetTransientForHint(otk::OBDisplay::display, client.window,
+                             &trans_for)) {
+    // transient_for hint not set
+    return;
+  }
+
+  if (trans_for == client.window) {
+    // wierd client... treat this window as a normal window
+    return;
+  }
+
+  if (trans_for == None || trans_for == screen->getRootWindow()) {
+    // this is an undocumented interpretation of the ICCCM. a transient
+    // associated with None/Root/itself is assumed to be a modal root
+    // transient.  we don't support the concept of a global transient,
+    // so we just associate this transient with nothing, and perhaps
+    // we will add support later for global modality.
+    client.transient_for = (BlackboxWindow *) ~0ul;
+    flags.modal = True;
+    return;
+  }
+
+  client.transient_for = blackbox->searchWindow(trans_for);
+  if (! client.transient_for &&
+      client.window_group && trans_for == client.window_group) {
+    // no direct transient_for, perhaps this is a group transient?
+    BWindowGroup *group = blackbox->searchGroup(client.window_group);
+    if (group) client.transient_for = group->find(screen);
+  }
+
+  if (! client.transient_for || client.transient_for == this) {
+    // no transient_for found, or we have a wierd client that wants to be
+    // a transient for itself, so we treat this window as a normal window
+    client.transient_for = (BlackboxWindow*) 0;
+    return;
+  }
+
+  // Check for a circular transient state: this can lock up Blackbox
+  // when it tries to find the non-transient window for a transient.
+  BlackboxWindow *w = this;
+  while(w->client.transient_for &&
+        w->client.transient_for != (BlackboxWindow *) ~0ul) {
+    if(w->client.transient_for == this) {
+      client.transient_for = (BlackboxWindow*) 0;
+      break;
+    }
+    w = w->client.transient_for;
+  }
+
+  if (client.transient_for &&
+      client.transient_for != (BlackboxWindow *) ~0ul) {
+    // register ourselves with our new transient_for
+    client.transient_for->client.transientList.push_back(this);
+    flags.stuck = client.transient_for->flags.stuck;
+  }
+}
+
+
+BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
+  if (client.transient_for &&
+      client.transient_for != (BlackboxWindow*) ~0ul)
+    return client.transient_for;
+  return 0;
+}
+
+
+/*
+ * This function is responsible for updating both the client and the frame
+ * rectangles.
+ * According to the ICCCM a client message is not sent for a resize, only a
+ * move.
+ */
+void BlackboxWindow::configure(int dx, int dy,
+                               unsigned int dw, unsigned int dh) {
+  bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
+                     ! flags.moving);
+
+  if (dw != frame.rect.width() || dh != frame.rect.height()) {
+    frame.rect.setRect(dx, dy, dw, dh);
+    frame.inside_w = frame.rect.width() - (frame.border_w * 2);
+    frame.inside_h = frame.rect.height() - (frame.border_w * 2);
+
+    if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
+      frame.rect.setPos(0, 0);
+
+    client.rect.setCoords(frame.rect.left() + frame.margin.left,
+                          frame.rect.top() + frame.margin.top,
+                          frame.rect.right() - frame.margin.right,
+                          frame.rect.bottom() - frame.margin.bottom);
+
+#ifdef    SHAPE
+    if (blackbox->hasShapeExtensions() && flags.shaped) {
+      configureShape();
+    }
+#endif // SHAPE
+
+    positionWindows();
+    decorate();
+    redrawWindowFrame();
+  } else {
+    frame.rect.setPos(dx, dy);
+
+    XMoveWindow(otk::OBDisplay::display, frame.window,
+                frame.rect.x(), frame.rect.y());
+    /*
+      we may have been called just after an opaque window move, so even though
+      the old coords match the new ones no ConfigureNotify has been sent yet.
+      There are likely other times when this will be relevant as well.
+    */
+    if (! flags.moving) send_event = True;
+  }
+
+  if (send_event) {
+    // if moving, the update and event will occur when the move finishes
+    client.rect.setPos(frame.rect.left() + frame.margin.left,
+                       frame.rect.top() + frame.margin.top);
+
+    XEvent event;
+    event.type = ConfigureNotify;
+
+    event.xconfigure.display = otk::OBDisplay::display;
+    event.xconfigure.event = client.window;
+    event.xconfigure.window = client.window;
+    event.xconfigure.x = client.rect.x();
+    event.xconfigure.y = client.rect.y();
+    event.xconfigure.width = client.rect.width();
+    event.xconfigure.height = client.rect.height();
+    event.xconfigure.border_width = client.old_bw;
+    event.xconfigure.above = frame.window;
+    event.xconfigure.override_redirect = False;
+
+    XSendEvent(otk::OBDisplay::display, client.window, False,
+               StructureNotifyMask, &event);
+    XFlush(otk::OBDisplay::display);
+  }
+}
+
+
+#ifdef SHAPE
+void BlackboxWindow::configureShape(void) {
+  XShapeCombineShape(otk::OBDisplay::display, frame.window, ShapeBounding,
+                     frame.margin.left - frame.border_w,
+                     frame.margin.top - frame.border_w,
+                     client.window, ShapeBounding, ShapeSet);
+
+  int num = 0;
+  XRectangle xrect[2];
+
+  if (decorations & Decor_Titlebar) {
+    xrect[0].x = xrect[0].y = -frame.border_w;
+    xrect[0].width = frame.rect.width();
+    xrect[0].height = frame.title_h + (frame.border_w * 2);
+    ++num;
+  }
+
+  if (decorations & Decor_Handle) {
+    xrect[1].x = -frame.border_w;
+    xrect[1].y = frame.rect.height() - frame.margin.bottom +
+                 frame.mwm_border_w - frame.border_w;
+    xrect[1].width = frame.rect.width();
+    xrect[1].height = frame.handle_h + (frame.border_w * 2);
+    ++num;
+  }
+
+  XShapeCombineRectangles(otk::OBDisplay::display, frame.window,
+                          ShapeBounding, 0, 0, xrect, num,
+                          ShapeUnion, Unsorted);
+}
+
+
+void BlackboxWindow::clearShape(void) {
+  XShapeCombineMask(otk::OBDisplay::display, frame.window, ShapeBounding,
+                    frame.margin.left - frame.border_w,
+                    frame.margin.top - frame.border_w,
+                    None, ShapeSet);
+}
+#endif // SHAPE
+
+
+bool BlackboxWindow::setInputFocus(void) {
+  if (flags.focused) return True;
+
+  assert(flags.stuck ||  // window must be on the current workspace or sticky
+         blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
+
+  /*
+     We only do this check for normal windows and dialogs because other windows
+     do this on purpose, such as kde's kicker, and we don't want to go moving
+     it.
+  */
+  if (window_type == Type_Normal || window_type == Type_Dialog)
+    if (! frame.rect.intersects(screen->getRect())) {
+      // client is outside the screen, move it to the center
+      configure((screen->getWidth() - frame.rect.width()) / 2,
+                (screen->getHeight() - frame.rect.height()) / 2,
+                frame.rect.width(), frame.rect.height());
+    }
+
+  if (client.transientList.size() > 0) {
+    // transfer focus to any modal transients
+    BlackboxWindowList::iterator it, end = client.transientList.end();
+    for (it = client.transientList.begin(); it != end; ++it)
+      if ((*it)->flags.modal) return (*it)->setInputFocus();
+  }
+
+  bool ret = True;
+  if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
+    XSetInputFocus(otk::OBDisplay::display, client.window,
+                   RevertToPointerRoot, CurrentTime);
+  } else {
+    /* we could set the focus to none, since the window doesn't accept focus,
+     * but we shouldn't set focus to nothing since this would surely make
+     * someone angry
+     */
+    ret = False;
+  }
+
+  if (flags.send_focus_message) {
+    XEvent ce;
+    ce.xclient.type = ClientMessage;
+    ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
+    ce.xclient.display = otk::OBDisplay::display;
+    ce.xclient.window = client.window;
+    ce.xclient.format = 32;
+    ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
+    ce.xclient.data.l[1] = blackbox->getLastTime();
+    ce.xclient.data.l[2] = 0l;
+    ce.xclient.data.l[3] = 0l;
+    ce.xclient.data.l[4] = 0l;
+    XSendEvent(otk::OBDisplay::display, client.window, False,
+               NoEventMask, &ce);
+    XFlush(otk::OBDisplay::display);
+  }
+
+  return ret;
+}
+
+
+void BlackboxWindow::iconify(void) {
+  if (flags.iconic || ! (functions & Func_Iconify)) return;
+
+  // We don't need to worry about resizing because resizing always grabs the X
+  // server. This should only ever happen if using opaque moving.
+  if (flags.moving)
+    endMove();
+    
+  /*
+   * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
+   * we need to clear the event mask on client.window for a split second.
+   * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
+   * split second, leaving us with a ghost window... so, we need to do this
+   * while the X server is grabbed
+   */
+  unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
+                             StructureNotifyMask;
+  XGrabServer(otk::OBDisplay::display);
+  XSelectInput(otk::OBDisplay::display, client.window,
+               event_mask & ~StructureNotifyMask);
+  XUnmapWindow(otk::OBDisplay::display, client.window);
+  XSelectInput(otk::OBDisplay::display, client.window, event_mask);
+  XUngrabServer(otk::OBDisplay::display);
+
+  XUnmapWindow(otk::OBDisplay::display, frame.window);
+  flags.visible = False;
+  flags.iconic = True;
+
+  setState(IconicState);
+
+  screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
+  if (flags.stuck) {
+    for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
+      if (i != blackbox_attrib.workspace)
+        screen->getWorkspace(i)->removeWindow(this, True);
+  }
+
+  if (isTransient()) {
+    if (client.transient_for != (BlackboxWindow *) ~0ul &&
+        ! client.transient_for->flags.iconic) {
+      // iconify our transient_for
+      client.transient_for->iconify();
+    }
+  }
+
+  screen->addIcon(this);
+
+  if (client.transientList.size() > 0) {
+    // iconify all transients
+    BlackboxWindowList::iterator it, end = client.transientList.end();
+    for (it = client.transientList.begin(); it != end; ++it) {
+      if (! (*it)->flags.iconic) (*it)->iconify();
+    }
+  }
+  screen->updateStackingList();
+}
+
+
+void BlackboxWindow::show(void) {
+  flags.visible = True;
+  flags.iconic = False;
+
+  current_state = (flags.shaded) ? IconicState : NormalState;
+  setState(current_state);
+
+  XMapWindow(otk::OBDisplay::display, client.window);
+  XMapSubwindows(otk::OBDisplay::display, frame.window);
+  XMapWindow(otk::OBDisplay::display, frame.window);
+
+#if 0
+  int real_x, real_y;
+  Window child;
+  XTranslateCoordinates(otk::OBDisplay::display, client.window,
+                        screen->getRootWindow(),
+                        0, 0, &real_x, &real_y, &child);
+  fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
+          client.rect.left(), client.rect.top(), real_x, real_y);
+  assert(client.rect.left() == real_x && client.rect.top() == real_y);
+#endif
+}
+
+
+void BlackboxWindow::deiconify(bool reassoc, bool raise) {
+  if (flags.iconic || reassoc)
+    screen->reassociateWindow(this, BSENTINEL, False);
+  else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
+    return;
+
+  show();
+
+  // reassociate and deiconify all transients
+  if (reassoc && client.transientList.size() > 0) {
+    BlackboxWindowList::iterator it, end = client.transientList.end();
+    for (it = client.transientList.begin(); it != end; ++it)
+      (*it)->deiconify(True, False);
+  }
+
+  if (raise)
+    screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
+}
+
+
+void BlackboxWindow::close(void) {
+  if (! (functions & Func_Close)) return;
+
+  XEvent ce;
+  ce.xclient.type = ClientMessage;
+  ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
+  ce.xclient.display = otk::OBDisplay::display;
+  ce.xclient.window = client.window;
+  ce.xclient.format = 32;
+  ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
+  ce.xclient.data.l[1] = CurrentTime;
+  ce.xclient.data.l[2] = 0l;
+  ce.xclient.data.l[3] = 0l;
+  ce.xclient.data.l[4] = 0l;
+  XSendEvent(otk::OBDisplay::display, client.window, False, NoEventMask, &ce);
+  XFlush(otk::OBDisplay::display);
+}
+
+
+void BlackboxWindow::withdraw(void) {
+  // We don't need to worry about resizing because resizing always grabs the X
+  // server. This should only ever happen if using opaque moving.
+  if (flags.moving)
+    endMove();
+    
+  flags.visible = False;
+  flags.iconic = False;
+
+  setState(current_state);
+
+  XUnmapWindow(otk::OBDisplay::display, frame.window);
+
+  XGrabServer(otk::OBDisplay::display);
+
+  unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
+                             StructureNotifyMask;
+  XSelectInput(otk::OBDisplay::display, client.window,
+               event_mask & ~StructureNotifyMask);
+  XUnmapWindow(otk::OBDisplay::display, client.window);
+  XSelectInput(otk::OBDisplay::display, client.window, event_mask);
+
+  XUngrabServer(otk::OBDisplay::display);
+}
+
+
+void BlackboxWindow::maximize(unsigned int button) {
+  if (! (functions & Func_Maximize)) return;
+
+  // We don't need to worry about resizing because resizing always grabs the X
+  // server. This should only ever happen if using opaque moving.
+  if (flags.moving)
+    endMove();
+
+  if (flags.maximized) {
+    flags.maximized = 0;
+
+    blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
+    blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
+
+    /*
+      when a resize finishes, maximize(0) is called to clear any maximization
+      flags currently set.  Otherwise it still thinks it is maximized.
+      so we do not need to call configure() because resizing will handle it
+    */
+    if (! flags.resizing)
+      configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
+                blackbox_attrib.premax_w, blackbox_attrib.premax_h);
+
+    blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
+    blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
+
+    redrawAllButtons(); // in case it is not called in configure()
+    setState(current_state);
+    return;
+  }
+
+  blackbox_attrib.premax_x = frame.rect.x();
+  blackbox_attrib.premax_y = frame.rect.y();
+  blackbox_attrib.premax_w = frame.rect.width();
+  // use client.rect so that clients can be restored even if shaded
+  blackbox_attrib.premax_h =
+    client.rect.height() + frame.margin.top + frame.margin.bottom;
+
+#ifdef    XINERAMA
+  if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
+    // find the area to use
+    RectList availableAreas = screen->allAvailableAreas();
+    RectList::iterator it, end = availableAreas.end();
+
+    for (it = availableAreas.begin(); it != end; ++it)
+      if (it->intersects(frame.rect)) break;
+    if (it == end) // the window isn't inside an area
+      it = availableAreas.begin(); // so just default to the first one
+
+    frame.changing = *it;
+  } else
+#endif // XINERAMA
+  frame.changing = screen->availableArea();
+
+  switch(button) {
+  case 1:
+    blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
+    blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
+    break;
+
+  case 2:
+    blackbox_attrib.flags |= AttribMaxVert;
+    blackbox_attrib.attrib |= AttribMaxVert;
+
+    frame.changing.setX(frame.rect.x());
+    frame.changing.setWidth(frame.rect.width());
+    break;
+
+  case 3:
+    blackbox_attrib.flags |= AttribMaxHoriz;
+    blackbox_attrib.attrib |= AttribMaxHoriz;
+
+    frame.changing.setY(frame.rect.y());
+    frame.changing.setHeight(frame.rect.height());
+    break;
+  }
+
+  constrain(TopLeft);
+
+  if (flags.shaded) {
+    blackbox_attrib.flags ^= AttribShaded;
+    blackbox_attrib.attrib ^= AttribShaded;
+    flags.shaded = False;
+  }
+
+  flags.maximized = button;
+
+  configure(frame.changing.x(), frame.changing.y(),
+            frame.changing.width(), frame.changing.height());
+  if (flags.focused)
+    screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
+  redrawAllButtons(); // in case it is not called in configure()
+  setState(current_state);
+}
+
+
+// re-maximizes the window to take into account availableArea changes
+void BlackboxWindow::remaximize(void) {
+  if (flags.shaded) {
+    // we only update the window's attributes otherwise we lose the shade bit
+    switch(flags.maximized) {
+    case 1:
+      blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
+      blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
+      break;
+
+    case 2:
+      blackbox_attrib.flags |= AttribMaxVert;
+      blackbox_attrib.attrib |= AttribMaxVert;
+      break;
+
+    case 3:
+      blackbox_attrib.flags |= AttribMaxHoriz;
+      blackbox_attrib.attrib |= AttribMaxHoriz;
+      break;
+    }
+    return;
+  }
+
+  // save the original dimensions because maximize will wipe them out
+  int premax_x = blackbox_attrib.premax_x,
+    premax_y = blackbox_attrib.premax_y,
+    premax_w = blackbox_attrib.premax_w,
+    premax_h = blackbox_attrib.premax_h;
+
+  unsigned int button = flags.maximized;
+  flags.maximized = 0; // trick maximize() into working
+  maximize(button);
+
+  // restore saved values
+  blackbox_attrib.premax_x = premax_x;
+  blackbox_attrib.premax_y = premax_y;
+  blackbox_attrib.premax_w = premax_w;
+  blackbox_attrib.premax_h = premax_h;
+}
+
+
+void BlackboxWindow::setWorkspace(unsigned int n) {
+  blackbox_attrib.flags |= AttribWorkspace;
+  blackbox_attrib.workspace = n;
+  if (n == BSENTINEL) { // iconified window
+    /*
+       we set the workspace to 'all workspaces' so that taskbars will show the
+       window. otherwise, it made uniconifying a window imposible without the
+       blackbox workspace menu
+    */
+    n = 0xffffffff;
+  }
+  xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
+}
+
+
+void BlackboxWindow::shade(void) {
+  if (flags.shaded) {
+    XResizeWindow(otk::OBDisplay::display, frame.window,
+                  frame.inside_w, frame.inside_h);
+    flags.shaded = False;
+    blackbox_attrib.flags ^= AttribShaded;
+    blackbox_attrib.attrib ^= AttribShaded;
+
+    setState(NormalState);
+
+    // set the frame rect to the normal size
+    frame.rect.setHeight(client.rect.height() + frame.margin.top +
+                         frame.margin.bottom);
+  } else {
+    if (! (decorations & Decor_Titlebar))
+      return; // can't shade it without a titlebar!
+
+    XResizeWindow(otk::OBDisplay::display, frame.window,
+                  frame.inside_w, frame.title_h);
+    flags.shaded = True;
+    blackbox_attrib.flags |= AttribShaded;
+    blackbox_attrib.attrib |= AttribShaded;
+
+    setState(IconicState);
+
+    // set the frame rect to the shaded size
+    frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
+  }
+}
+
+
+/*
+ * (Un)Sticks a window and its relatives.
+ */
+void BlackboxWindow::stick(void) {
+  if (flags.stuck) {
+    blackbox_attrib.flags ^= AttribOmnipresent;
+    blackbox_attrib.attrib ^= AttribOmnipresent;
+
+    flags.stuck = False;
+    
+    for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
+      if (i != blackbox_attrib.workspace)
+        screen->getWorkspace(i)->removeWindow(this, True);
+
+    if (! flags.iconic)
+      screen->reassociateWindow(this, BSENTINEL, True);
+    // temporary fix since sticky windows suck. set the hint to what we
+    // actually hold in our data.
+    xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
+                    blackbox_attrib.workspace);
+
+    setState(current_state);
+  } else {
+    flags.stuck = True;
+
+    blackbox_attrib.flags |= AttribOmnipresent;
+    blackbox_attrib.attrib |= AttribOmnipresent;
+
+    // temporary fix since sticky windows suck. set the hint to a different
+    // value than that contained in the class' data.
+    xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
+                    0xffffffff);
+    
+    for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
+      if (i != blackbox_attrib.workspace)
+        screen->getWorkspace(i)->addWindow(this, False, True);
+
+    setState(current_state);
+  }
+
+  redrawAllButtons();
+  
+  // go up the chain
+  if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
+      client.transient_for->isStuck() != flags.stuck)
+    client.transient_for->stick();
+  // go down the chain
+  BlackboxWindowList::iterator it;
+  const BlackboxWindowList::iterator end = client.transientList.end();
+  for (it = client.transientList.begin(); it != end; ++it)
+    if ((*it)->isStuck() != flags.stuck)
+      (*it)->stick();
+}
+
+
+void BlackboxWindow::redrawWindowFrame(void) const {
+  if (decorations & Decor_Titlebar) {
+    if (flags.focused) {
+      if (frame.ftitle)
+        XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                   frame.title, frame.ftitle);
+      else
+        XSetWindowBackground(otk::OBDisplay::display,
+                             frame.title, frame.ftitle_pixel);
+    } else {
+      if (frame.utitle)
+        XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                   frame.title, frame.utitle);
+      else
+        XSetWindowBackground(otk::OBDisplay::display,
+                             frame.title, frame.utitle_pixel);
+    }
+    XClearWindow(otk::OBDisplay::display, frame.title);
+
+    redrawLabel();
+    redrawAllButtons();
+  }
+
+  if (decorations & Decor_Handle) {
+    if (flags.focused) {
+      if (frame.fhandle)
+        XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                   frame.handle, frame.fhandle);
+      else
+        XSetWindowBackground(otk::OBDisplay::display,
+                             frame.handle, frame.fhandle_pixel);
+
+      if (frame.fgrip) {
+        XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                   frame.left_grip, frame.fgrip);
+        XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                   frame.right_grip, frame.fgrip);
+      } else {
+        XSetWindowBackground(otk::OBDisplay::display,
+                             frame.left_grip, frame.fgrip_pixel);
+        XSetWindowBackground(otk::OBDisplay::display,
+                             frame.right_grip, frame.fgrip_pixel);
+      }
+    } else {
+      if (frame.uhandle)
+        XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                   frame.handle, frame.uhandle);
+      else
+        XSetWindowBackground(otk::OBDisplay::display,
+                             frame.handle, frame.uhandle_pixel);
+
+      if (frame.ugrip) {
+        XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                   frame.left_grip, frame.ugrip);
+        XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                   frame.right_grip, frame.ugrip);
+      } else {
+        XSetWindowBackground(otk::OBDisplay::display,
+                             frame.left_grip, frame.ugrip_pixel);
+        XSetWindowBackground(otk::OBDisplay::display,
+                             frame.right_grip, frame.ugrip_pixel);
+      }
+    }
+    XClearWindow(otk::OBDisplay::display, frame.handle);
+    XClearWindow(otk::OBDisplay::display, frame.left_grip);
+    XClearWindow(otk::OBDisplay::display, frame.right_grip);
+  }
+
+  if (decorations & Decor_Border) {
+    if (flags.focused)
+      XSetWindowBorder(otk::OBDisplay::display,
+                       frame.plate, frame.fborder_pixel);
+    else
+      XSetWindowBorder(otk::OBDisplay::display,
+                       frame.plate, frame.uborder_pixel);
+  }
+}
+
+
+void BlackboxWindow::setFocusFlag(bool focus) {
+  // only focus a window if it is visible
+  if (focus && ! flags.visible)
+    return;
+
+  flags.focused = focus;
+
+  redrawWindowFrame();
+
+  if (flags.focused)
+    blackbox->setFocusedWindow(this);
+}
+
+
+void BlackboxWindow::installColormap(bool install) {
+  int i = 0, ncmap = 0;
+  Colormap *cmaps = XListInstalledColormaps(otk::OBDisplay::display,
+                                            client.window, &ncmap);
+  if (cmaps) {
+    XWindowAttributes wattrib;
+    if (XGetWindowAttributes(otk::OBDisplay::display,
+                             client.window, &wattrib)) {
+      if (install) {
+        // install the window's colormap
+        for (i = 0; i < ncmap; i++) {
+          if (*(cmaps + i) == wattrib.colormap)
+            // this window is using an installed color map... do not install
+            install = False;
+        }
+        // otherwise, install the window's colormap
+        if (install)
+          XInstallColormap(otk::OBDisplay::display, wattrib.colormap);
+      } else {
+        // uninstall the window's colormap
+        for (i = 0; i < ncmap; i++) {
+          if (*(cmaps + i) == wattrib.colormap)
+            // we found the colormap to uninstall
+            XUninstallColormap(otk::OBDisplay::display, wattrib.colormap);
+        }
+      }
+    }
+
+    XFree(cmaps);
+  }
+}
+
+
+void BlackboxWindow::setAllowedActions(void) {
+  Atom actions[7];
+  int num = 0;
+  
+  actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
+  actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
+  actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
+
+  if (functions & Func_Move)
+    actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
+  if (functions & Func_Resize)
+    actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
+  if (functions & Func_Maximize) {
+    actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
+    actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
+  }
+
+  xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
+                  actions, num);
+}
+
+
+void BlackboxWindow::setState(unsigned long new_state) {
+  current_state = new_state;
+
+  unsigned long state[2];
+  state[0] = current_state;
+  state[1] = None;
+  xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
+  xatom->setValue(client.window, XAtom::blackbox_attributes,
+                  XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
+                  PropBlackboxAttributesElements);
+
+  Atom netstate[8];
+  int num = 0;
+  if (flags.modal)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
+  if (flags.shaded)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
+  if (flags.iconic)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
+  if (flags.skip_taskbar)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
+  if (flags.skip_pager)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
+  if (flags.fullscreen)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
+  if (flags.maximized == 1 || flags.maximized == 2)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
+  if (flags.maximized == 1 || flags.maximized == 3)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
+  xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
+                  netstate, num);
+}
+
+
+bool BlackboxWindow::getState(void) {
+  bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
+                             current_state);
+  if (! ret) current_state = 0;
+  return ret;
+}
+
+
+void BlackboxWindow::restoreAttributes(void) {
+  unsigned long num = PropBlackboxAttributesElements;
+  BlackboxAttributes *net;
+  if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
+                        XAtom::blackbox_attributes, num,
+                        (unsigned long **)&net))
+    return;
+  if (num < PropBlackboxAttributesElements) {
+    delete [] net;
+    return;
+  }
+
+  if (net->flags & AttribShaded && net->attrib & AttribShaded) {
+    flags.shaded = False;
+    unsigned long orig_state = current_state;
+    shade();
+
+    /*
+      At this point in the life of a window, current_state should only be set
+      to IconicState if the window was an *icon*, not if it was shaded.
+    */
+    if (orig_state != IconicState)
+      current_state = WithdrawnState;
+ }
+
+  if (net->workspace != screen->getCurrentWorkspaceID() &&
+      net->workspace < screen->getWorkspaceCount())
+    screen->reassociateWindow(this, net->workspace, True);
+
+  if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
+      (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
+    // set to WithdrawnState so it will be mapped on the new workspace
+    if (current_state == NormalState) current_state = WithdrawnState;
+  } else if (current_state == WithdrawnState) {
+    // the window is on this workspace and is Withdrawn, so it is waiting to
+    // be mapped
+    current_state = NormalState;
+  }
+
+  if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
+      ! flags.stuck) {
+    stick();
+
+    // if the window was on another workspace, it was going to be hidden. this
+    // specifies that the window should be mapped since it is sticky.
+    if (current_state == WithdrawnState) current_state = NormalState;
+  }
+
+  if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
+    int x = net->premax_x, y = net->premax_y;
+    unsigned int w = net->premax_w, h = net->premax_h;
+    flags.maximized = 0;
+
+    unsigned int m = 0;
+    if ((net->flags & AttribMaxHoriz) &&
+        (net->flags & AttribMaxVert))
+      m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
+    else if (net->flags & AttribMaxVert)
+      m = (net->attrib & AttribMaxVert) ? 2 : 0;
+    else if (net->flags & AttribMaxHoriz)
+      m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
+
+    if (m) maximize(m);
+
+    blackbox_attrib.premax_x = x;
+    blackbox_attrib.premax_y = y;
+    blackbox_attrib.premax_w = w;
+    blackbox_attrib.premax_h = h;
+  }
+
+  if (net->flags & AttribDecoration) {
+    switch (net->decoration) {
+    case DecorNone:
+      enableDecor(False);
+      break;
+
+    /* since tools only let you toggle this anyways, we'll just make that all
+       it supports for now.
+     */
+    default:
+    case DecorNormal:
+    case DecorTiny:
+    case DecorTool:
+      enableDecor(True);
+      break;
+    }
+  }
+
+  // with the state set it will then be the map event's job to read the
+  // window's state and behave accordingly
+
+  delete [] net;
+}
+
+
+/*
+ * Positions the Rect r according the the client window position and
+ * window gravity.
+ */
+void BlackboxWindow::applyGravity(otk::Rect &r) {
+  // apply horizontal window gravity
+  switch (client.win_gravity) {
+  default:
+  case NorthWestGravity:
+  case SouthWestGravity:
+  case WestGravity:
+    r.setX(client.rect.x());
+    break;
+
+  case NorthGravity:
+  case SouthGravity:
+  case CenterGravity:
+    r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
+    break;
+
+  case NorthEastGravity:
+  case SouthEastGravity:
+  case EastGravity:
+    r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setX(client.rect.x() - frame.margin.left);
+    break;
+  }
+
+  // apply vertical window gravity
+  switch (client.win_gravity) {
+  default:
+  case NorthWestGravity:
+  case NorthEastGravity:
+  case NorthGravity:
+    r.setY(client.rect.y());
+    break;
+
+  case CenterGravity:
+  case EastGravity:
+  case WestGravity:
+    r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
+    break;
+
+  case SouthWestGravity:
+  case SouthEastGravity:
+  case SouthGravity:
+    r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setY(client.rect.y() - frame.margin.top);
+    break;
+  }
+}
+
+
+/*
+ * The reverse of the applyGravity function.
+ *
+ * Positions the Rect r according to the frame window position and
+ * window gravity.
+ */
+void BlackboxWindow::restoreGravity(otk::Rect &r) {
+  // restore horizontal window gravity
+  switch (client.win_gravity) {
+  default:
+  case NorthWestGravity:
+  case SouthWestGravity:
+  case WestGravity:
+    r.setX(frame.rect.x());
+    break;
+
+  case NorthGravity:
+  case SouthGravity:
+  case CenterGravity:
+    r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
+    break;
+
+  case NorthEastGravity:
+  case SouthEastGravity:
+  case EastGravity:
+    r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setX(frame.rect.x() + frame.margin.left);
+    break;
+  }
+
+  // restore vertical window gravity
+  switch (client.win_gravity) {
+  default:
+  case NorthWestGravity:
+  case NorthEastGravity:
+  case NorthGravity:
+    r.setY(frame.rect.y());
+    break;
+
+  case CenterGravity:
+  case EastGravity:
+  case WestGravity:
+    r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
+    break;
+
+  case SouthWestGravity:
+  case SouthEastGravity:
+  case SouthGravity:
+    r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setY(frame.rect.y() + frame.margin.top);
+    break;
+  }
+}
+
+
+void BlackboxWindow::redrawLabel(void) const {
+  if (flags.focused) {
+    if (frame.flabel)
+      XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                 frame.label, frame.flabel);
+    else
+      XSetWindowBackground(otk::OBDisplay::display,
+                           frame.label, frame.flabel_pixel);
+  } else {
+    if (frame.ulabel)
+      XSetWindowBackgroundPixmap(otk::OBDisplay::display,
+                                 frame.label, frame.ulabel);
+    else
+      XSetWindowBackground(otk::OBDisplay::display,
+                           frame.label, frame.ulabel_pixel);
+  }
+  XClearWindow(otk::OBDisplay::display, frame.label);
+
+  WindowStyle *style = screen->getWindowStyle();
+
+  int pos = frame.bevel_w * 2;
+  style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
+  style->font->drawString(frame.label, pos, 1,
+                          (flags.focused ? style->l_text_focus :
+                           style->l_text_unfocus),
+                          client.title);
+}
+
+
+void BlackboxWindow::redrawAllButtons(void) const {
+  if (frame.iconify_button) redrawIconifyButton(False);
+  if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
+  if (frame.close_button) redrawCloseButton(False);
+  if (frame.stick_button) redrawStickyButton(flags.stuck);
+}
+
+
+void BlackboxWindow::redrawButton(bool pressed, Window win,
+                                  Pixmap fppix, unsigned long fppixel,
+                                  Pixmap uppix, unsigned long uppixel,
+                                  Pixmap fpix, unsigned long fpixel,
+                                  Pixmap upix, unsigned long upixel) const {
+  Pixmap p;
+  unsigned long pix;
+  
+  if (pressed) {
+    if (flags.focused) {
+      p = fppix;
+      pix = fppixel;
+    } else {
+      p = uppix;
+      pix = uppixel;
+    }
+  } else {
+    if (flags.focused) {
+      p = fpix;
+      pix = fpixel;
+    } else {
+      p = upix;
+      pix = upixel;
+    }
+  }
+  
+  if (p)
+    XSetWindowBackgroundPixmap(otk::OBDisplay::display, win, p);
+  else
+    XSetWindowBackground(otk::OBDisplay::display, win, pix);
+
+}
+
+void BlackboxWindow::redrawIconifyButton(bool pressed) const {
+  redrawButton(pressed, frame.iconify_button, 
+               frame.pfbutton, frame.pfbutton_pixel,
+               frame.pubutton, frame.pubutton_pixel,
+               frame.fbutton, frame.fbutton_pixel,
+               frame.ubutton, frame.ubutton_pixel);
+
+  XClearWindow(otk::OBDisplay::display, frame.iconify_button);
+  otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
+                screen->getWindowStyle()->b_pic_unfocus);
+
+  PixmapMask pm = screen->getWindowStyle()->icon_button;
+  
+  if (screen->getWindowStyle()->icon_button.mask != None) {
+    XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask);
+    XSetClipOrigin(otk::OBDisplay::display, pen.gc(),
+                   (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
+
+    XFillRectangle(otk::OBDisplay::display, frame.iconify_button, pen.gc(),
+                   (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
+                   (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
+
+    XSetClipMask(otk::OBDisplay::display, pen.gc(), None);
+    XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0);
+  } else {
+    XDrawRectangle(otk::OBDisplay::display, frame.iconify_button, pen.gc(),
+                   2, (frame.button_w - 5), (frame.button_w - 5), 2);
+  }
+}
+
+
+void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
+  redrawButton(pressed, frame.maximize_button, 
+               frame.pfbutton, frame.pfbutton_pixel,
+               frame.pubutton, frame.pubutton_pixel,
+               frame.fbutton, frame.fbutton_pixel,
+               frame.ubutton, frame.ubutton_pixel);
+
+  XClearWindow(otk::OBDisplay::display, frame.maximize_button);
+
+  otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
+                screen->getWindowStyle()->b_pic_unfocus);
+  
+  PixmapMask pm = screen->getWindowStyle()->max_button;
+    
+  if (pm.mask != None) {
+    XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask);
+    XSetClipOrigin(otk::OBDisplay::display, pen.gc(),
+                   (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
+
+    XFillRectangle(otk::OBDisplay::display, frame.maximize_button, pen.gc(),
+                   (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
+                   (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
+    
+    XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 );
+    XSetClipMask( otk::OBDisplay::display, pen.gc(), None );
+  } else {
+    XDrawRectangle(otk::OBDisplay::display, frame.maximize_button, pen.gc(),
+                   2, 2, (frame.button_w - 5), (frame.button_w - 5));
+    XDrawLine(otk::OBDisplay::display, frame.maximize_button, pen.gc(),
+              2, 3, (frame.button_w - 3), 3);
+  }
+}
+
+
+void BlackboxWindow::redrawCloseButton(bool pressed) const {
+  redrawButton(pressed, frame.close_button, 
+               frame.pfbutton, frame.pfbutton_pixel,
+               frame.pubutton, frame.pubutton_pixel,
+               frame.fbutton, frame.fbutton_pixel,
+               frame.ubutton, frame.ubutton_pixel);
+
+  XClearWindow(otk::OBDisplay::display, frame.close_button);
+
+  otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
+                screen->getWindowStyle()->b_pic_unfocus);
+  
+  PixmapMask pm = screen->getWindowStyle()->close_button;
+
+  if (pm.mask != None) {
+    XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask);
+    XSetClipOrigin(otk::OBDisplay::display, pen.gc(),
+                   (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
+    
+    XFillRectangle(otk::OBDisplay::display, frame.close_button, pen.gc(),
+                   (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
+                   (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
+
+  
+    XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 );
+    XSetClipMask( otk::OBDisplay::display, pen.gc(), None );
+  } else {
+    XDrawLine(otk::OBDisplay::display, frame.close_button, pen.gc(),
+              2, 2, (frame.button_w - 3), (frame.button_w - 3));
+    XDrawLine(otk::OBDisplay::display, frame.close_button, pen.gc(),
+              2, (frame.button_w - 3), (frame.button_w - 3), 2);
+  }
+}
+
+void BlackboxWindow::redrawStickyButton(bool pressed) const {
+  redrawButton(pressed, frame.stick_button, 
+               frame.pfbutton, frame.pfbutton_pixel,
+               frame.pubutton, frame.pubutton_pixel,
+               frame.fbutton, frame.fbutton_pixel,
+               frame.ubutton, frame.ubutton_pixel);
+
+  XClearWindow(otk::OBDisplay::display, frame.stick_button);
+
+  otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
+                screen->getWindowStyle()->b_pic_unfocus);
+  
+  PixmapMask pm = screen->getWindowStyle()->stick_button;
+
+  if (pm.mask != None) {
+    XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask);
+    XSetClipOrigin(otk::OBDisplay::display, pen.gc(),
+                   (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
+    
+    XFillRectangle(otk::OBDisplay::display, frame.stick_button, pen.gc(),
+                   (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
+                   (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
+
+  
+    XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 );
+    XSetClipMask( otk::OBDisplay::display, pen.gc(), None );
+  } else {
+    XFillRectangle(otk::OBDisplay::display, frame.stick_button, pen.gc(),
+                   frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 );
+  }
+}
+
+void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
+  if (re->window != client.window)
+    return;
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
+          client.window);
+#endif // DEBUG
+
+  /*
+     Even though the window wants to be shown, if it is not on the current
+     workspace, then it isn't going to be shown right now.
+  */
+  if (! flags.stuck &&
+      blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
+      blackbox_attrib.workspace < screen->getWorkspaceCount())
+    if (current_state == NormalState) current_state = WithdrawnState;
+
+  switch (current_state) {
+  case IconicState:
+    iconify();
+    break;
+
+  case WithdrawnState:
+    withdraw();
+    break;
+
+  case NormalState:
+  case InactiveState:
+  case ZoomState:
+  default:
+    show();
+    screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
+    if (isNormal()) {
+      if (blackbox->state() != Openbox::State_Starting) {
+        XSync(otk::OBDisplay::display, False); // make sure the frame is mapped
+        if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
+                                     getTransientFor()->isFocused())) {
+          setInputFocus();
+        }
+        if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
+          int x, y, rx, ry;
+          Window c, r;
+          unsigned int m;
+          XQueryPointer(otk::OBDisplay::display, screen->getRootWindow(),
+                        &r, &c, &rx, &ry, &x, &y, &m);
+          beginMove(rx, ry);
+        }
+      }
+    }
+    break;
+  }
+}
+
+
+void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
+  if (ue->window != client.window)
+    return;
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
+          client.window);
+#endif // DEBUG
+
+  screen->unmanageWindow(this, False);
+}
+
+
+void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
+  if (de->window != client.window)
+    return;
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
+          client.window);
+#endif // DEBUG
+
+  screen->unmanageWindow(this, False);
+}
+
+
+void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
+  if (re->window != client.window || re->parent == frame.plate)
+    return;
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
+          "0x%lx.\n", client.window, re->parent);
+#endif // DEBUG
+
+  XEvent ev;
+  ev.xreparent = *re;
+  XPutBackEvent(otk::OBDisplay::display, &ev);
+  screen->unmanageWindow(this, True);
+}
+
+
+void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
+  if (pe->state == PropertyDelete || ! validateClient())
+    return;
+
+#if 0
+  fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
+          client.window);
+#endif
+
+  switch(pe->atom) {
+  case XA_WM_CLASS:
+  case XA_WM_CLIENT_MACHINE:
+  case XA_WM_COMMAND:
+    break;
+
+  case XA_WM_TRANSIENT_FOR: {
+    bool s = flags.stuck;
+    
+    // determine if this is a transient window
+    getTransientInfo();
+
+    if (flags.stuck != s) stick();
+
+    // adjust the window decorations based on transience
+    if (isTransient()) {
+      functions &= ~Func_Maximize;
+      setAllowedActions();
+      setupDecor();
+    }
+
+    reconfigure();
+  }
+    break;
+
+  case XA_WM_HINTS:
+    getWMHints();
+    break;
+
+  case XA_WM_ICON_NAME:
+    getWMIconName();
+    if (flags.iconic) screen->propagateWindowName(this);
+    break;
+
+  case XAtom::net_wm_name:
+  case XA_WM_NAME:
+    getWMName();
+
+    if (decorations & Decor_Titlebar)
+      redrawLabel();
+
+    screen->propagateWindowName(this);
+    break;
+
+  case XA_WM_NORMAL_HINTS: {
+    getWMNormalHints();
+
+    if ((client.normal_hint_flags & PMinSize) &&
+        (client.normal_hint_flags & PMaxSize)) {
+      // the window now can/can't resize itself, so the buttons need to be
+      // regrabbed.
+      ungrabButtons();
+      if (client.max_width <= client.min_width &&
+          client.max_height <= client.min_height) {
+        functions &= ~(Func_Resize | Func_Maximize);
+      } else {
+        if (! isTransient())
+          functions |= Func_Maximize;
+        functions |= Func_Resize;
+      }
+      grabButtons();
+      setAllowedActions();
+      setupDecor();
+    }
+
+    otk::Rect old_rect = frame.rect;
+
+    upsize();
+
+    if (old_rect != frame.rect)
+      reconfigure();
+
+    break;
+  }
+
+  default:
+    if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
+      getWMProtocols();
+
+      if ((decorations & Decor_Close) && (! frame.close_button)) {
+        createCloseButton();
+        if (decorations & Decor_Titlebar) {
+          positionButtons(True);
+          XMapSubwindows(otk::OBDisplay::display, frame.title);
+        }
+      }
+    } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
+      updateStrut();
+    }
+
+    break;
+  }
+}
+
+
+void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
+#if 0
+  fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
+#endif
+
+  if (frame.label == ee->window && (decorations & Decor_Titlebar))
+    redrawLabel();
+  else if (frame.close_button == ee->window)
+    redrawCloseButton(False);
+  else if (frame.maximize_button == ee->window)
+    redrawMaximizeButton(flags.maximized);
+  else if (frame.iconify_button == ee->window)
+    redrawIconifyButton(False);
+  else if (frame.stick_button == ee->window)
+    redrawStickyButton(flags.stuck);
+}
+
+
+void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
+  if (cr->window != client.window || flags.iconic)
+    return;
+
+  if (cr->value_mask & CWBorderWidth)
+    client.old_bw = cr->border_width;
+
+  if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
+    frame.changing = frame.rect;
+
+    if (cr->value_mask & (CWX | CWY)) {
+      if (cr->value_mask & CWX)
+        client.rect.setX(cr->x);
+      if (cr->value_mask & CWY)
+        client.rect.setY(cr->y);
+
+      applyGravity(frame.changing);
+    }
+
+    if (cr->value_mask & (CWWidth | CWHeight)) {
+      if (cr->value_mask & CWWidth)
+        frame.changing.setWidth(cr->width +
+                                frame.margin.left + frame.margin.right);
+
+      if (cr->value_mask & CWHeight)
+        frame.changing.setHeight(cr->height +
+                                 frame.margin.top + frame.margin.bottom);
+
+      /*
+        if a position change has been specified, then that position will be
+        used instead of determining a position based on the window's gravity.
+      */
+      if (! (cr->value_mask & (CWX | CWY))) {
+        Corner corner;
+        switch (client.win_gravity) {
+        case NorthEastGravity:
+        case EastGravity:
+          corner = TopRight;
+          break;
+        case SouthWestGravity:
+        case SouthGravity:
+          corner = BottomLeft;
+          break;
+        case SouthEastGravity:
+          corner = BottomRight;
+          break;
+        default:     // NorthWest, Static, etc
+          corner = TopLeft;
+        }
+        constrain(corner);
+      }
+    }
+
+    configure(frame.changing.x(), frame.changing.y(),
+              frame.changing.width(), frame.changing.height());
+  }
+
+  if (cr->value_mask & CWStackMode && !isDesktop()) {
+    switch (cr->detail) {
+    case Below:
+    case BottomIf:
+      screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
+      break;
+
+    case Above:
+    case TopIf:
+    default:
+      screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
+      break;
+    }
+  }
+}
+
+
+void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
+#ifdef DEBUG
+  fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  if (frame.maximize_button == be->window && be->button <= 3) {
+    redrawMaximizeButton(True);
+  } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
+    if (! flags.focused)
+      setInputFocus();
+
+    if (frame.iconify_button == be->window) {
+      redrawIconifyButton(True);
+    } else if (frame.close_button == be->window) {
+      redrawCloseButton(True);
+    } else if (frame.stick_button == be->window) {
+      redrawStickyButton(True);
+    } else if (frame.plate == be->window) {
+      screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
+
+      XAllowEvents(otk::OBDisplay::display, ReplayPointer, be->time);
+    } else {
+      if (frame.title == be->window || frame.label == be->window) {
+        if (((be->time - lastButtonPressTime) <=
+             blackbox->getDoubleClickInterval()) ||
+            (be->state == ControlMask)) {
+          lastButtonPressTime = 0;
+          shade();
+        } else {
+          lastButtonPressTime = be->time;
+        }
+      }
+
+      screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
+    }
+  } else if (be->button == 2 && (be->window != frame.iconify_button) &&
+             (be->window != frame.close_button) &&
+             (be->window != frame.stick_button)) {
+    screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
+  // mouse wheel up
+  } else if (be->button == 4) {
+    if ((be->window == frame.label ||
+         be->window == frame.title ||
+         be->window == frame.maximize_button ||
+         be->window == frame.iconify_button ||
+         be->window == frame.close_button ||
+         be->window == frame.stick_button) &&
+        ! flags.shaded)
+      shade();
+  // mouse wheel down
+  } else if (be->button == 5) {
+    if ((be->window == frame.label ||
+         be->window == frame.title ||
+         be->window == frame.maximize_button ||
+         be->window == frame.iconify_button ||
+         be->window == frame.close_button ||
+         be->window == frame.stick_button) &&
+        flags.shaded)
+      shade();
+  }
+}
+
+
+void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
+#ifdef DEBUG
+  fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  if (re->window == frame.maximize_button &&
+      re->button >= 1 && re->button <= 3) {
+    if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
+        (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
+      maximize(re->button);
+    } else {
+      redrawMaximizeButton(flags.maximized);
+    }
+  } else if (re->window == frame.iconify_button && re->button == 1) {
+    if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
+        (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
+      iconify();
+    } else {
+      redrawIconifyButton(False);
+    }
+  } else if (re->window == frame.stick_button && re->button == 1) {
+    if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
+        (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
+      stick();
+    } else {
+      redrawStickyButton(False);
+    }
+  } else if (re->window == frame.close_button & re->button == 1) {
+    if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
+        (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
+      close();
+    redrawCloseButton(False);
+  } else if (flags.moving) {
+    endMove();
+  } else if (flags.resizing) {
+    endResize();
+  } else if (re->window == frame.window) {
+    if (re->button == 2 && re->state == mod_mask)
+      XUngrabPointer(otk::OBDisplay::display, CurrentTime);
+  }
+}
+
+
+
+void BlackboxWindow::beginMove(int x_root, int y_root) {
+  if (! (functions & Func_Move)) return;
+
+  assert(! (flags.resizing || flags.moving));
+
+  /*
+    Only one window can be moved/resized at a time. If another window is already
+    being moved or resized, then stop it before whating to work with this one.
+  */
+  BlackboxWindow *changing = blackbox->getChangingWindow();
+  if (changing && changing != this) {
+    if (changing->flags.moving)
+      changing->endMove();
+    else // if (changing->flags.resizing)
+      changing->endResize();
+  }
+  
+  XGrabPointer(otk::OBDisplay::display, frame.window, False,
+               PointerMotionMask | ButtonReleaseMask,
+               GrabModeAsync, GrabModeAsync,
+               None, blackbox->getMoveCursor(), CurrentTime);
+
+  flags.moving = True;
+  blackbox->setChangingWindow(this);
+
+  if (! screen->doOpaqueMove()) {
+    XGrabServer(otk::OBDisplay::display);
+
+    frame.changing = frame.rect;
+    screen->showPosition(frame.changing.x(), frame.changing.y());
+
+    XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
+                   screen->getOpGC(),
+                   frame.changing.x(),
+                   frame.changing.y(),
+                   frame.changing.width() - 1,
+                   frame.changing.height() - 1);
+  }
+
+  frame.grab_x = x_root - frame.rect.x() - frame.border_w;
+  frame.grab_y = y_root - frame.rect.y() - frame.border_w;
+}
+
+
+void BlackboxWindow::doMove(int x_root, int y_root) {
+  assert(flags.moving);
+  assert(blackbox->getChangingWindow() == this);
+
+  int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
+  dx -= frame.border_w;
+  dy -= frame.border_w;
+
+  doWindowSnapping(dx, dy);
+
+  if (screen->doOpaqueMove()) {
+    if (screen->doWorkspaceWarping())
+      doWorkspaceWarping(x_root, y_root, dx);
+
+    configure(dx, dy, frame.rect.width(), frame.rect.height());
+  } else {
+    XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
+                   screen->getOpGC(),
+                   frame.changing.x(),
+                   frame.changing.y(),
+                   frame.changing.width() - 1,
+                   frame.changing.height() - 1);
+
+    if (screen->doWorkspaceWarping())
+      doWorkspaceWarping(x_root, y_root, dx);
+
+    frame.changing.setPos(dx, dy);
+
+    XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
+                   screen->getOpGC(),
+                   frame.changing.x(),
+                   frame.changing.y(),
+                   frame.changing.width() - 1,
+                   frame.changing.height() - 1);
+  }
+
+  screen->showPosition(dx, dy);
+}
+
+
+void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) {
+  // workspace warping
+  bool warp = False;
+  unsigned int dest = screen->getCurrentWorkspaceID();
+  if (x_root <= 0) {
+    warp = True;
+
+    if (dest > 0) dest--;
+    else dest = screen->getNumberOfWorkspaces() - 1;
+
+  } else if (x_root >= screen->getRect().right()) {
+    warp = True;
+
+    if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
+    else dest = 0;
+  }
+  if (! warp)
+    return;
+
+  bool focus = flags.focused; // had focus while moving?
+
+  int dest_x = x_root;
+  if (x_root <= 0) {
+    dest_x += screen->getRect().width() - 1;
+    dx += screen->getRect().width() - 1;
+  } else {
+    dest_x -= screen->getRect().width() - 1;
+    dx -= screen->getRect().width() - 1;
+  }
+
+  if (! flags.stuck)
+    screen->reassociateWindow(this, dest, False);
+  screen->changeWorkspaceID(dest);
+
+  if (screen->doOpaqueMove())
+    XGrabServer(otk::OBDisplay::display);
+
+  XUngrabPointer(otk::OBDisplay::display, CurrentTime);
+  XWarpPointer(otk::OBDisplay::display, None, 
+               screen->getRootWindow(), 0, 0, 0, 0,
+               dest_x, y_root);
+  XGrabPointer(otk::OBDisplay::display, frame.window, False,
+               PointerMotionMask | ButtonReleaseMask,
+               GrabModeAsync, GrabModeAsync,
+               None, blackbox->getMoveCursor(), CurrentTime);
+
+  if (screen->doOpaqueMove())
+    XUngrabServer(otk::OBDisplay::display);
+
+  if (focus)
+    setInputFocus();
+
+}
+
+
+void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
+  // how much resistance to edges to provide
+  const int resistance_size = screen->getResistanceSize();
+
+  // how far away to snap
+  const int snap_distance = screen->getSnapThreshold();
+
+  // how to snap windows
+  const int snap_to_windows = screen->getWindowToWindowSnap();
+  const int snap_to_edges = screen->getWindowToEdgeSnap();
+  // the amount of space away from the edge to provide resistance/snap
+  const int snap_offset = screen->getSnapOffset();
+
+  // find the geomeetery where the moving window currently is
+  const otk::Rect &moving =
+    screen->doOpaqueMove() ? frame.rect : frame.changing;
+
+  // window corners
+  const int wleft = dx,
+           wright = dx + frame.rect.width() - 1,
+             wtop = dy,
+          wbottom = dy + frame.rect.height() - 1;
+
+  if (snap_to_windows) {
+    otk::RectList rectlist;
+
+    Workspace *w = screen->getWorkspace(getWorkspaceNumber());
+    assert(w);
+
+    // add windows on the workspace to the rect list
+    const BlackboxWindowList& stack_list = w->getStackingList();
+    BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
+    for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
+      if (*st_it != this) // don't snap to ourself
+        rectlist.push_back( (*st_it)->frameRect() );
+
+    otk::RectList::const_iterator it, end = rectlist.end();
+    for (it = rectlist.begin(); it != end; ++it) {
+      bool snapped = False;
+      const otk::Rect &winrect = *it;
+      otk::Rect offsetrect;
+      offsetrect.setCoords(winrect.left() - snap_offset,
+                           winrect.top() - snap_offset,
+                           winrect.right() + snap_offset,
+                           winrect.bottom() + snap_offset);
+
+      if (snap_to_windows == BScreen::WindowResistance)
+        // if the window is already over top of this snap target, then
+        // resistance is futile, so just ignore it
+        if (winrect.intersects(moving))
+          continue;
+
+      int dleft, dright, dtop, dbottom;
+
+      // if the windows are in the same plane vertically
+      if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
+          wtop < (signed)(winrect.y() + winrect.height() - 1)) {
+
+        if (snap_to_windows == BScreen::WindowResistance) {
+          dleft = wright - offsetrect.left();
+          dright = offsetrect.right() - wleft;
+
+          // snap left of other window?
+          if (dleft >= 0 && dleft < resistance_size &&
+              dleft < (wright - wleft)) {
+            dx = offsetrect.left() - frame.rect.width();
+            snapped = True;
+          }
+          // snap right of other window?
+          else if (dright >= 0 && dright < resistance_size &&
+                   dright < (wright - wleft)) {
+            dx = offsetrect.right() + 1;
+            snapped = True;
+          }
+        } else { // BScreen::WindowSnap
+          dleft = abs(wright - offsetrect.left());
+          dright = abs(wleft - offsetrect.right());
+
+          // snap left of other window?
+          if (dleft < snap_distance && dleft <= dright) {
+            dx = offsetrect.left() - frame.rect.width();
+            snapped = True;
+          }
+          // snap right of other window?
+          else if (dright < snap_distance) {
+            dx = offsetrect.right() + 1;
+            snapped = True;
+          }            
+        }
+
+        if (snapped) {
+          if (screen->getWindowCornerSnap()) {
+            // try corner-snap to its other sides
+            if (snap_to_windows == BScreen::WindowResistance) {
+              dtop = winrect.top() - wtop;
+              dbottom = wbottom - winrect.bottom();
+              if (dtop > 0 && dtop < resistance_size) {
+                // if we're already past the top edge, then don't provide
+                // resistance
+                if (moving.top() >= winrect.top())
+                  dy = winrect.top();
+              } else if (dbottom > 0 && dbottom < resistance_size) {
+                // if we're already past the bottom edge, then don't provide
+                // resistance
+                if (moving.bottom() <= winrect.bottom())
+                  dy = winrect.bottom() - frame.rect.height() + 1;
+              }
+            } else { // BScreen::WindowSnap
+              dtop = abs(wtop - winrect.top());
+              dbottom = abs(wbottom - winrect.bottom());
+              if (dtop < snap_distance && dtop <= dbottom)
+                dy = winrect.top();
+              else if (dbottom < snap_distance)
+                dy = winrect.bottom() - frame.rect.height() + 1;
+            }
+          }
+
+          continue;
+        }
+      }
+
+      // if the windows are on the same plane horizontally
+      if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
+          wleft < (signed)(winrect.x() + winrect.width() - 1)) {
+
+        if (snap_to_windows == BScreen::WindowResistance) {
+          dtop = wbottom - offsetrect.top();
+          dbottom = offsetrect.bottom() - wtop;
+
+          // snap top of other window?
+          if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
+            dy = offsetrect.top() - frame.rect.height();
+            snapped = True;
+          }
+          // snap bottom of other window?
+          else if (dbottom >= 0 && dbottom < resistance_size &&
+                   dbottom < (wbottom - wtop)) {
+            dy = offsetrect.bottom() + 1;
+            snapped = True;
+          }
+        } else { // BScreen::WindowSnap
+          dtop = abs(wbottom - offsetrect.top());
+          dbottom = abs(wtop - offsetrect.bottom());
+
+          // snap top of other window?
+          if (dtop < snap_distance && dtop <= dbottom) {
+            dy = offsetrect.top() - frame.rect.height();
+            snapped = True;
+          }
+          // snap bottom of other window?
+          else if (dbottom < snap_distance) {
+            dy = offsetrect.bottom() + 1;
+            snapped = True;
+          }
+
+        }
+
+        if (snapped) {
+          if (screen->getWindowCornerSnap()) {
+            // try corner-snap to its other sides
+            if (snap_to_windows == BScreen::WindowResistance) {
+              dleft = winrect.left() - wleft;
+              dright = wright - winrect.right();
+              if (dleft > 0 && dleft < resistance_size) {
+                // if we're already past the left edge, then don't provide
+                // resistance
+                if (moving.left() >= winrect.left())
+                  dx = winrect.left();
+              } else if (dright > 0 && dright < resistance_size) {
+                // if we're already past the right edge, then don't provide
+                // resistance
+                if (moving.right() <= winrect.right())
+                  dx = winrect.right() - frame.rect.width() + 1;
+              }
+            } else { // BScreen::WindowSnap
+              dleft = abs(wleft - winrect.left());
+              dright = abs(wright - winrect.right());
+              if (dleft < snap_distance && dleft <= dright)
+                dx = winrect.left();
+              else if (dright < snap_distance)
+                dx = winrect.right() - frame.rect.width() + 1;
+            }
+          }
+
+          continue;
+        }
+      }
+    }
+  }
+
+  if (snap_to_edges) {
+    otk::RectList rectlist;
+
+    // snap to the screen edges (and screen boundaries for xinerama)
+#ifdef    XINERAMA
+    if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
+      rectlist.insert(rectlist.begin(),
+                      screen->getXineramaAreas().begin(),
+                      screen->getXineramaAreas().end());
+    } else
+#endif // XINERAMA
+      rectlist.push_back(screen->getRect());
+
+    otk::RectList::const_iterator it, end = rectlist.end();
+    for (it = rectlist.begin(); it != end; ++it) {
+      const otk::Rect &srect = *it;
+      otk::Rect offsetrect;
+      offsetrect.setCoords(srect.left() + snap_offset,
+                           srect.top() + snap_offset,
+                           srect.right() - snap_offset,
+                           srect.bottom() - snap_offset);
+
+      if (snap_to_edges == BScreen::WindowResistance) {
+        // if we're not in the rectangle then don't snap to it.
+        if (! srect.contains(moving))
+          continue;
+      } else { // BScreen::WindowSnap
+        // if we're not in the rectangle then don't snap to it.
+        if (! srect.intersects(otk::Rect(wleft, wtop, frame.rect.width(),
+                                         frame.rect.height())))
+          continue;
+      }
+
+      if (snap_to_edges == BScreen::WindowResistance) {
+      int dleft = offsetrect.left() - wleft,
+         dright = wright - offsetrect.right(),
+           dtop = offsetrect.top() - wtop,
+        dbottom = wbottom - offsetrect.bottom();
+
+        // snap left?
+        if (dleft > 0 && dleft < resistance_size)
+          dx = offsetrect.left();
+        // snap right?
+        else if (dright > 0 && dright < resistance_size)
+          dx = offsetrect.right() - frame.rect.width() + 1;
+
+        // snap top?
+        if (dtop > 0 && dtop < resistance_size)
+          dy = offsetrect.top();
+        // snap bottom?
+        else if (dbottom > 0 && dbottom < resistance_size)
+          dy = offsetrect.bottom() - frame.rect.height() + 1;
+      } else { // BScreen::WindowSnap
+        int dleft = abs(wleft - offsetrect.left()),
+           dright = abs(wright - offsetrect.right()),
+             dtop = abs(wtop - offsetrect.top()),
+          dbottom = abs(wbottom - offsetrect.bottom());
+
+        // snap left?
+        if (dleft < snap_distance && dleft <= dright)
+          dx = offsetrect.left();
+        // snap right?
+        else if (dright < snap_distance)
+          dx = offsetrect.right() - frame.rect.width() + 1;
+
+        // snap top?
+        if (dtop < snap_distance && dtop <= dbottom)
+          dy = offsetrect.top();
+        // snap bottom?
+        else if (dbottom < snap_distance)
+          dy = offsetrect.bottom() - frame.rect.height() + 1;
+      }
+    }
+  }
+}
+
+
+void BlackboxWindow::endMove(void) {
+  assert(flags.moving);
+  assert(blackbox->getChangingWindow() == this);
+
+  flags.moving = False;
+  blackbox->setChangingWindow(0);
+
+  if (! screen->doOpaqueMove()) {
+    /* when drawing the rubber band, we need to make sure we only draw inside
+     * the frame... frame.changing_* contain the new coords for the window,
+     * so we need to subtract 1 from changing_w/changing_h every where we
+     * draw the rubber band (for both moving and resizing)
+     */
+    XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
+                   screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                   frame.changing.width() - 1, frame.changing.height() - 1);
+      XUngrabServer(otk::OBDisplay::display);
+  
+      configure(frame.changing.x(), frame.changing.y(),
+                frame.changing.width(), frame.changing.height());
+  } else {
+    configure(frame.rect.x(), frame.rect.y(),
+              frame.rect.width(), frame.rect.height());
+  }
+  screen->hideGeometry();
+
+  XUngrabPointer(otk::OBDisplay::display, CurrentTime);
+
+  // if there are any left over motions from the move, drop them now
+  XSync(otk::OBDisplay::display, false); // make sure we don't miss any
+  XEvent e;
+  while (XCheckTypedWindowEvent(otk::OBDisplay::display, frame.window,
+                                MotionNotify, &e));
+}
+
+
+void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
+  if (! (functions & Func_Resize)) return;
+
+  assert(! (flags.resizing || flags.moving));
+
+  /*
+    Only one window can be moved/resized at a time. If another window is
+    already being moved or resized, then stop it before whating to work with
+    this one.
+  */
+  BlackboxWindow *changing = blackbox->getChangingWindow();
+  if (changing && changing != this) {
+    if (changing->flags.moving)
+      changing->endMove();
+    else // if (changing->flags.resizing)
+      changing->endResize();
+  }
+
+  resize_dir = dir;
+
+  Cursor cursor;
+  Corner anchor;
+  
+  switch (resize_dir) {
+  case BottomLeft:
+    anchor = TopRight;
+    cursor = blackbox->getLowerLeftAngleCursor();
+    break;
+
+  case BottomRight:
+    anchor = TopLeft;
+    cursor = blackbox->getLowerRightAngleCursor();
+    break;
+
+  case TopLeft:
+    anchor = BottomRight;
+    cursor = blackbox->getUpperLeftAngleCursor();
+    break;
+
+  case TopRight:
+    anchor = BottomLeft;
+    cursor = blackbox->getUpperRightAngleCursor();
+    break;
+
+  default:
+    assert(false); // unhandled Corner
+    return;        // unreachable, for the compiler
+  }
+  
+  XGrabServer(otk::OBDisplay::display);
+  XGrabPointer(otk::OBDisplay::display, frame.window, False,
+               PointerMotionMask | ButtonReleaseMask,
+               GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
+
+  flags.resizing = True;
+  blackbox->setChangingWindow(this);
+
+  unsigned int gw, gh;
+  frame.changing = frame.rect;
+
+  constrain(anchor,  &gw, &gh);
+
+  XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
+                 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                 frame.changing.width() - 1, frame.changing.height() - 1);
+
+  screen->showGeometry(gw, gh);
+  
+  frame.grab_x = x_root;
+  frame.grab_y = y_root;
+}
+
+
+void BlackboxWindow::doResize(int x_root, int y_root) {
+  assert(flags.resizing);
+  assert(blackbox->getChangingWindow() == this);
+
+  XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
+                 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                 frame.changing.width() - 1, frame.changing.height() - 1);
+
+  unsigned int gw, gh;
+  Corner anchor;
+  int dx, dy; // the amount of change in the size of the window
+
+  switch (resize_dir) {
+  case BottomLeft:
+    anchor = TopRight;
+    dx = - (x_root - frame.grab_x);
+    dy = + (y_root - frame.grab_y);
+    break;
+  case BottomRight:
+    anchor = TopLeft;
+    dx = + (x_root - frame.grab_x);
+    dy = + (y_root - frame.grab_y);
+    break;
+  case TopLeft:
+    anchor = BottomRight;
+    dx = - (x_root - frame.grab_x);
+    dy = - (y_root - frame.grab_y);
+    break;
+  case TopRight:
+    anchor = BottomLeft;
+    dx = + (x_root - frame.grab_x);
+    dy = - (y_root - frame.grab_y);
+    break;
+
+  default:
+    assert(false); // unhandled Corner
+    return;        // unreachable, for the compiler
+  }
+
+  // make sure the user cant resize the window smaller than 0, which makes it
+  // wrap around and become huge
+  if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width();
+  if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height();
+
+  frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy);
+
+  constrain(anchor, &gw, &gh);
+
+  XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
+                 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                 frame.changing.width() - 1, frame.changing.height() - 1);
+
+  screen->showGeometry(gw, gh);
+}
+
+
+void BlackboxWindow::endResize(void) {
+  assert(flags.resizing);
+  assert(blackbox->getChangingWindow() == this);
+
+  XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
+                 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                 frame.changing.width() - 1, frame.changing.height() - 1);
+  XUngrabServer(otk::OBDisplay::display);
+
+  // unset maximized state after resized when fully maximized
+  if (flags.maximized == 1)
+    maximize(0);
+  
+  flags.resizing = False;
+  blackbox->setChangingWindow(0);
+
+  configure(frame.changing.x(), frame.changing.y(),
+            frame.changing.width(), frame.changing.height());
+  screen->hideGeometry();
+
+  XUngrabPointer(otk::OBDisplay::display, CurrentTime);
+  
+  // if there are any left over motions from the resize, drop them now
+  XSync(otk::OBDisplay::display, false); // make sure we don't miss any
+  XEvent e;
+  while (XCheckTypedWindowEvent(otk::OBDisplay::display, frame.window,
+                                MotionNotify, &e));
+}
+
+
+void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
+#if 0
+  fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  if (flags.moving) {
+    doMove(me->x_root, me->y_root);
+  } else if (flags.resizing) {
+    doResize(me->x_root, me->y_root);
+  } else {
+    if ((functions & Func_Move) &&
+       (me->state & Button1Mask) &&
+        (frame.title == me->window || frame.label == me->window ||
+         frame.handle == me->window || frame.window == me->window)) {
+      beginMove(me->x_root, me->y_root);
+    } else if ((functions & Func_Resize) &&
+               ((me->state & Button1Mask) &&
+                (me->window == frame.right_grip ||
+                 me->window == frame.left_grip)) ||
+               ((me->state & Button3Mask) && (me->state & mod_mask) &&
+                (frame.title == me->window || frame.label == me->window ||
+                 frame.handle == me->window || frame.window == me->window ||
+                 frame.right_grip == me->window ||
+                 frame.left_grip == me->window))) {
+      unsigned int zones = screen->getResizeZones();
+      Corner corner;
+      
+      if (me->window == frame.left_grip) {
+        corner = BottomLeft;
+      } else if (me->window == frame.right_grip || zones == 1) {
+        corner = BottomRight;
+      } else {
+        bool top;
+        bool left = (me->x_root - frame.rect.x() <=
+                     static_cast<signed>(frame.rect.width() / 2));
+        if (zones == 2)
+          top = False;
+        else // (zones == 4)
+          top = (me->y_root - frame.rect.y() <=
+                 static_cast<signed>(frame.rect.height() / 2));
+        corner = (top ? (left ? TopLeft : TopRight) :
+                        (left ? BottomLeft : BottomRight));
+      }
+
+      beginResize(me->x_root, me->y_root, corner);
+    }
+  }
+}
+
+
+void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
+  if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
+    return;
+
+  XEvent e;
+  bool leave = False, inferior = False;
+
+  while (XCheckTypedWindowEvent(otk::OBDisplay::display, ce->window,
+                                LeaveNotify, &e)) {
+    if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
+      leave = True;
+      inferior = (e.xcrossing.detail == NotifyInferior);
+    }
+  }
+
+  if (! leave || inferior) {
+    if (! isFocused()) {
+      bool success = setInputFocus();
+      if (success)    // if focus succeeded install the colormap
+        installColormap(True); // XXX: shouldnt we honour no install?
+
+      /*
+        We only auto-raise when the window wasn't focused because otherwise
+        we run into problems with gtk+ drop-down lists. The window ends up
+        raising over the list.
+      */
+      if (screen->doAutoRaise())
+        timer->start();
+    }
+  }
+}
+
+
+void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
+  if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
+    return;
+
+  installColormap(False);
+
+  timer->stop();
+}
+
+
+#ifdef    SHAPE
+void BlackboxWindow::shapeEvent(XShapeEvent *e) {
+  if (blackbox->hasShapeExtensions()) {
+    if (! e->shaped && flags.shaped) {
+      clearShape();
+      flags.shaped = False;
+    } else if (e->shaped) {
+      configureShape();
+      flags.shaped = True;
+    }
+  }
+}
+#endif // SHAPE
+
+
+bool BlackboxWindow::validateClient(void) const {
+  XSync(otk::OBDisplay::display, False);
+
+  XEvent e;
+  if (XCheckTypedWindowEvent(otk::OBDisplay::display, client.window,
+                             DestroyNotify, &e) ||
+      XCheckTypedWindowEvent(otk::OBDisplay::display, client.window,
+                             UnmapNotify, &e)) {
+    XPutBackEvent(otk::OBDisplay::display, &e);
+
+    return False;
+  }
+
+  return True;
+}
+
+
+void BlackboxWindow::restore(bool remap) {
+  XChangeSaveSet(otk::OBDisplay::display, client.window, SetModeDelete);
+  XSelectInput(otk::OBDisplay::display, client.window, NoEventMask);
+  XSelectInput(otk::OBDisplay::display, frame.plate, NoEventMask);
+
+  // do not leave a shaded window as an icon unless it was an icon
+  if (flags.shaded && ! flags.iconic)
+    setState(NormalState);
+
+  // erase the netwm stuff that we read when a window maps, so that it
+  // doesn't persist between mappings.
+  // (these are the ones read in getNetWMFlags().)
+  xatom->eraseValue(client.window, XAtom::net_wm_desktop);
+  xatom->eraseValue(client.window, XAtom::net_wm_state);
+
+  restoreGravity(client.rect);
+
+  XUnmapWindow(otk::OBDisplay::display, frame.window);
+  XUnmapWindow(otk::OBDisplay::display, client.window);
+
+  XSetWindowBorderWidth(otk::OBDisplay::display, client.window, client.old_bw);
+
+  XEvent ev;
+  if (XCheckTypedWindowEvent(otk::OBDisplay::display, client.window,
+                             ReparentNotify, &ev)) {
+    remap = True;
+  } else {
+    // according to the ICCCM - if the client doesn't reparent to
+    // root, then we have to do it for them
+    XReparentWindow(otk::OBDisplay::display, client.window,
+                    screen->getRootWindow(),
+                    client.rect.x(), client.rect.y());
+  }
+
+  if (remap) XMapWindow(otk::OBDisplay::display, client.window);
+}
+
+
+// timer for autoraise
+void BlackboxWindow::timeout(BlackboxWindow *t) {
+  t->screen->getWorkspace(t->blackbox_attrib.workspace)->raiseWindow(t);
+  printf("TIMED OUT YA YAY\n");
+}
+
+
+void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
+  if ((net->flags & AttribShaded) &&
+      ((blackbox_attrib.attrib & AttribShaded) !=
+       (net->attrib & AttribShaded)))
+    shade();
+
+  if (flags.visible && // watch out for requests when we can not be seen
+      (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
+      ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
+       (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
+    if (flags.maximized) {
+      maximize(0);
+    } else {
+      int button = 0;
+
+      if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
+        button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
+      else if (net->flags & AttribMaxVert)
+        button = ((net->attrib & AttribMaxVert) ? 2 : 0);
+      else if (net->flags & AttribMaxHoriz)
+        button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
+
+      maximize(button);
+    }
+  }
+
+  if ((net->flags & AttribOmnipresent) &&
+      ((blackbox_attrib.attrib & AttribOmnipresent) !=
+       (net->attrib & AttribOmnipresent)))
+    stick();
+
+  if ((net->flags & AttribWorkspace) &&
+      (blackbox_attrib.workspace != net->workspace)) {
+    screen->reassociateWindow(this, net->workspace, True);
+
+    if (screen->getCurrentWorkspaceID() != net->workspace) {
+      withdraw();
+    } else {
+      show();
+      screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
+    }
+  }
+
+  if (net->flags & AttribDecoration) {
+    switch (net->decoration) {
+    case DecorNone:
+      enableDecor(False);
+      break;
+
+    default:
+    case DecorNormal:
+    case DecorTiny:
+    case DecorTool:
+      enableDecor(True);
+      break;
+    }
+  }
+}
+
+
+/*
+ * Set the sizes of all components of the window frame
+ * (the window decorations).
+ * These values are based upon the current style settings and the client
+ * window's dimensions.
+ */
+void BlackboxWindow::upsize(void) {
+  frame.bevel_w = screen->getBevelWidth();
+
+  if (decorations & Decor_Border) {
+    frame.border_w = screen->getBorderWidth();
+    if (! isTransient())
+      frame.mwm_border_w = screen->getFrameWidth();
+    else
+      frame.mwm_border_w = 0;
+  } else {
+    frame.mwm_border_w = frame.border_w = 0;
+  }
+
+  if (decorations & Decor_Titlebar) {
+    // the height of the titlebar is based upon the height of the font being
+    // used to display the window's title
+    WindowStyle *style = screen->getWindowStyle();
+    frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
+
+    frame.label_h = frame.title_h - (frame.bevel_w * 2);
+    frame.button_w = (frame.label_h - 2);
+
+    // set the top frame margin
+    frame.margin.top = frame.border_w + frame.title_h +
+                       frame.border_w + frame.mwm_border_w;
+  } else {
+    frame.title_h = 0;
+    frame.label_h = 0;
+    frame.button_w = 0;
+
+    // set the top frame margin
+    frame.margin.top = frame.border_w + frame.mwm_border_w;
+  }
+
+  // set the left/right frame margin
+  frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
+
+  if (decorations & Decor_Handle) {
+    frame.grip_w = frame.button_w * 2;
+    frame.handle_h = screen->getHandleWidth();
+
+    // set the bottom frame margin
+    frame.margin.bottom = frame.border_w + frame.handle_h +
+                          frame.border_w + frame.mwm_border_w;
+  } else {
+    frame.handle_h = 0;
+    frame.grip_w = 0;
+
+    // set the bottom frame margin
+    frame.margin.bottom = frame.border_w + frame.mwm_border_w;
+  }
+
+  /*
+    We first get the normal dimensions and use this to define the inside_w/h
+    then we modify the height if shading is in effect.
+    If the shade state is not considered then frame.rect gets reset to the
+    normal window size on a reconfigure() call resulting in improper
+    dimensions appearing in move/resize and other events.
+  */
+  unsigned int
+    height = client.rect.height() + frame.margin.top + frame.margin.bottom,
+    width = client.rect.width() + frame.margin.left + frame.margin.right;
+
+  frame.inside_w = width - (frame.border_w * 2);
+  frame.inside_h = height - (frame.border_w * 2);
+
+  if (flags.shaded)
+    height = frame.title_h + (frame.border_w * 2);
+  frame.rect.setSize(width, height);
+}
+
+
+/*
+ * Calculate the size of the client window and constrain it to the
+ * size specified by the size hints of the client window.
+ *
+ * The logical width and height are placed into pw and ph, if they
+ * are non-zero.  Logical size refers to the users perception of
+ * the window size (for example an xterm resizes in cells, not in pixels).
+ * pw and ph are then used to display the geometry during window moves, resize,
+ * etc.
+ *
+ * The physical geometry is placed into frame.changing_{x,y,width,height}.
+ * Physical geometry refers to the geometry of the window in pixels.
+ */
+void BlackboxWindow::constrain(Corner anchor,
+                               unsigned int *pw, unsigned int *ph) {
+  // frame.changing represents the requested frame size, we need to
+  // strip the frame margin off and constrain the client size
+  frame.changing.setCoords(frame.changing.left() + frame.margin.left,
+                           frame.changing.top() + frame.margin.top,
+                           frame.changing.right() - frame.margin.right,
+                           frame.changing.bottom() - frame.margin.bottom);
+
+  unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
+    base_width = (client.base_width) ? client.base_width : client.min_width,
+    base_height = (client.base_height) ? client.base_height :
+                                         client.min_height;
+
+  // constrain, but only if the min/max are being used. if they aren't, then
+  // this resize is going to be from a ConfigureRequest because the window
+  // isn't allowed to be resized by the user. And in that case, we don't want
+  // to limit what the app can do
+  if (client.max_width > client.min_width ||
+      client.max_height > client.min_height) {
+    if (dw < client.min_width) dw = client.min_width;
+    if (dh < client.min_height) dh = client.min_height;
+    if (dw > client.max_width) dw = client.max_width;
+    if (dh > client.max_height) dh = client.max_height;
+  }
+
+  if (client.width_inc > 1) {
+    dw -= base_width;
+    dw /= client.width_inc;
+  }
+  if (client.height_inc > 1) {
+    dh -= base_height;
+    dh /= client.height_inc;
+  }
+
+  if (pw)
+    *pw = dw;
+
+  if (ph)
+    *ph = dh;
+
+  if (client.width_inc > 1) {
+    dw *= client.width_inc;
+    dw += base_width;
+  }
+  if (client.height_inc > 1) {
+    dh *= client.height_inc;
+    dh += base_height;
+  }
+
+  frame.changing.setSize(dw, dh);
+
+  // add the frame margin back onto frame.changing
+  frame.changing.setCoords(frame.changing.left() - frame.margin.left,
+                           frame.changing.top() - frame.margin.top,
+                           frame.changing.right() + frame.margin.right,
+                           frame.changing.bottom() + frame.margin.bottom);
+
+  // move frame.changing to the specified anchor
+  int dx = 0,
+      dy = 0;
+  switch (anchor) {
+  case TopLeft:
+    break;
+
+  case TopRight:
+    dx = frame.rect.right() - frame.changing.right();
+    break;
+
+  case BottomLeft:
+    dy = frame.rect.bottom() - frame.changing.bottom();
+    break;
+
+  case BottomRight:
+    dx = frame.rect.right() - frame.changing.right();
+    dy = frame.rect.bottom() - frame.changing.bottom();
+    break;
+
+  default:
+    assert(false);  // unhandled corner
+  }
+  frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
+}
+
+
+void WindowStyle::doJustify(const std::string &text, int &start_pos,
+                            unsigned int max_length,
+                            unsigned int modifier) const {
+  size_t text_len = text.size();
+  unsigned int length;
+
+  do {
+    length = font->measureString(string(text, 0, text_len)) + modifier;
+  } while (length > max_length && text_len-- > 0);
+
+  switch (justify) {
+  case RightJustify:
+    start_pos += max_length - length;
+    break;
+
+  case CenterJustify:
+    start_pos += (max_length - length) / 2;
+    break;
+
+  case LeftJustify:
+  default:
+    break;
+  }
+}
+
+
+BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
+  : blackbox(b), group(_group) {
+  XWindowAttributes wattrib;
+  if (! XGetWindowAttributes(otk::OBDisplay::display, group, &wattrib)) {
+    // group window doesn't seem to exist anymore
+    delete this;
+    return;
+  }
+
+  XSelectInput(otk::OBDisplay::display, group,
+               PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
+
+  blackbox->saveGroupSearch(group, this);
+}
+
+
+BWindowGroup::~BWindowGroup(void) {
+  blackbox->removeGroupSearch(group);
+}
+
+
+BlackboxWindow *
+BWindowGroup::find(BScreen *screen, bool allow_transients) const {
+  BlackboxWindow *ret = blackbox->getFocusedWindow();
+
+  // does the focus window match (or any transient_fors)?
+  for (; ret; ret = ret->getTransientFor()) {
+    if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
+        (! ret->isTransient() || allow_transients))
+      break;
+  }
+
+  if (ret) return ret;
+
+  // the focus window didn't match, look in the group's window list
+  BlackboxWindowList::const_iterator it, end = windowList.end();
+  for (it = windowList.begin(); it != end; ++it) {
+    ret = *it;
+    if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
+        (! ret->isTransient() || allow_transients))
+      break;
+  }
+
+  return ret;
+}
+
+}
This page took 0.082793 seconds and 4 git commands to generate.