text: // FbPager.cc for FbPager
  // Copyright (c) 2003-2004 Henrik Kinnunen (fluxgen at users.sourceforge.net)
  //
  // Permission is hereby granted, free of charge, to any person obtaining a
  // copy of this software and associated documentation files (the "Software"),
  // to deal in the Software without restriction, including without limitation
  // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  // and/or sell copies of the Software, and to permit persons to whom the
  // Software is furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  // DEALINGS IN THE SOFTWARE.
  
  // $Id$
  
  #include "FbPager.hh"
  
  // fbtk utils
  #include "FbTk/EventManager.hh"
  #include "FbTk/App.hh"
  #include "FbTk/Color.hh"
  #include "FbTk/StringUtil.hh"
  
  // client handlers
  #include "Ewmh.hh"
  
  #include "Workspace.hh"
  #include "WindowHint.hh"
  #include "ScaleWindowToWindow.hh"
  #include "Resources.hh"
  #include "RefBinder.hh"
  
  // x headers
  #include <X11/Xatom.h>
  #include <X11/Xutil.h>
  
  // std headers
  #include <iostream>
  #include <cstdio>
  #include <cassert>
  #include <functional>
  #include <algorithm>
  
  #ifdef HAVE_CONFIG_H
  #include "config.h"
  #endif // HAVE_CONFIG_H
  
  // copied from fluxbox code
  #ifdef HAVE_SSTREAM
  #include <sstream>
  #define FB_istringstream istringstream
  #define FB_ostringstream ostringstream
  #elif HAVE_STRSTREAM
  #include <strstream>
  #define FB_istringstream istrstream
  #define FB_ostringstream ostrstream
  #else
  #error "You dont have sstream or strstream headers!"
  #endif // HAVE_STRSTREAM
  
  using namespace std;
  
  namespace {
  
  int handleXErrors(Display *disp, XErrorEvent *xerror) {
      return 0;
  }
  
  template <typename Container>
  void destroyList(Container &c) {
      while (!c.empty()) {
          delete c.back();
          c.pop_back();
      }
  }
  
  // TODO: is this optimal? i dont think so, but it works so far
  bool compareButtonQueues(std::string q1, std::string q2) {
  
      FbTk::StringUtil::removeFirstWhitespace(q1);
      FbTk::StringUtil::removeFirstWhitespace(q2);
      FbTk::StringUtil::removeTrailingWhitespace(q1);
      FbTk::StringUtil::removeTrailingWhitespace(q2);
  
      return q1 == q2;
  }
  
  
  // little helper to keep  win  visible in  bigwin
  void respectConstraints(int& x, int& y,
                          const FbTk::FbWindow& win, const FbTk::FbWindow& bigwin) {
  
      const int left = - static_cast<int>((3 * win.width()) / 4);
      const int right = bigwin.width() - win.width() / 4;
      const int top =  - static_cast<int>((3 * win.height()) / 4);
      const int bottom = bigwin.height() - win.height() / 4;
  
      // make sure window isnt dragged out of visible area of workspace
      if ( x < left || x > right )
          x = win.x();
      if ( y < top || y > bottom )
          y = win.y();
  }
  
  } // end anonymous namespace
  
  
  namespace FbPager {
  
  
  FbPager::FbPager(int screen_num, bool withdraw,
                   bool show_resources,
                   int layer_flag,
                   const char *resource_filename):
      m_rootwin(screen_num),
      m_window(m_rootwin,
               0, 0, // pos
               64, 64,  // size
               // event mask
               ButtonPressMask | ButtonReleaseMask | ExposureMask |
               FocusChangeMask | StructureNotifyMask|
               SubstructureRedirectMask),
      m_curr_workspace(0),
      m_move_window(),
      m_resmanager(0, false),
      m_alpha(m_resmanager, 255,
              "fbpager.alpha",
              "FbPager.Alpha"),
      m_start_x(m_resmanager, 0,
                "fbpager.x",
                "FbPager.x"),
      m_start_y(m_resmanager, 0,
                "fbpager.y",
                "FbPager.y"),
      m_workspace_width(m_resmanager, 64,
                        "fbpager.workspace.width",
                        "FbPager.Workspace.Width"),
      m_workspace_height(m_resmanager, 64,
                         "fbpager.workspace.height",
                         "FbPager.Workspace.Height"),
      m_workspaces_per_row(m_resmanager, 6400, // big number...
                           "fbpager.workspacesPerRow",
                           "FbPager.WorkspacesPerRow"),
      m_layer(m_resmanager, "normal", // normal layer
              "fbpager.layer",
              "FbPager.Layer"),
      m_follow_drag(m_resmanager, false,
                    "fbpager.followDrag",
                    "FbPager.FollowDrag"),
      m_follow_move(m_resmanager, false,
                    "fbpager.followMove",
                    "FbPager.FollowMove"),
      m_follow_scale(m_resmanager, "height",
                     "fbpager.followScale",
                     "FbPager.followScale"),
      m_change_workspace_button(m_resmanager, "1",
                                "fbpager.changeWorkspaceButton",
                                "FbPager.ChangeWorkspaceButton"),
      m_raise_window_button(m_resmanager, "2",
                            "fbpager.raiseWindowButton",
                            "FbPager.RaiseWindowButton"),
      m_lower_window_button(m_resmanager, "3",
                            "fbpager.lowerWindowButton",
                            "FbPager.LowerWindowButton"),
      m_close_window_button(m_resmanager, "3 3 1",
                            "fbpager.closeWindowButton",
                            "FbPager.CloseWindowButton"),
      m_exit_button(m_resmanager, "1 3 3",
                    "fbpager.exitButton",
                    "FbPager.ExitButton"),
      m_next_workspace_button(m_resmanager, "4",
                              "fbpager.nextWorkspaceButton",
                              "FbPager.NextWorkspaceButton"),
      m_prev_workspace_button(m_resmanager, "5",
                              "fbpager.prevWorkspaceButton",
                              "FbPager.PrevWorkspaceButton"),
      m_move_in_workspace_button(m_resmanager, 2,
                                 "fbpager.moveInWorkspaceButton",
                                 "FbPager.MoveInWorkspaceButton"),
      m_drag_to_workspace_button(m_resmanager, 3,
                                 "fbpager.dragToWorkspaceButton",
                                 "FbPager.DragToWorkspaceButtton"),
      m_align(m_resmanager, LEFT_TO_RIGHT,
              "fbpager.align",
              "FbPager.Align"),
      m_color_str(m_resmanager, "white",
                  "fbpager.color",
                  "FbPager.Color"),
      m_window_color_str(m_resmanager, "white",
                         "fbpager.windowColor",
                         "FbPager.WindowColor"),
      m_focused_window_color_str(m_resmanager, "white",
                                 "fbpager.focusedWindowColor",
                                 "FbPager.FocusedWindowColor"),
      m_window_bordercolor_str(m_resmanager, "black",
                               "fbpager.windowBorderColor",
                               "FbPager.WindowBorderColor"),
      m_background_color_str(m_resmanager, "darkgray",
                             "fbpager.backgroundColor",
                             "FbPager.BackgroundColor"),
      m_current_background_color_str(m_resmanager, "lightgray",
                                     "fbpager.currentBackgroundColor",
                                     "FbPager.CurrentBackgroundColor"),
      m_multiclick_time(m_resmanager, 250,
                        "fbpager.multiClickTime",
                        "FbPager.MultiClickTime"),
      m_window_icons(m_resmanager, false,
                     "fbpager.icons",
                     "FbPager.Icons"),
      m_window_border_width(m_resmanager, 1,
                            "fbpager.windowBorderWidth",
                            "FbPager.WindowBorderWidth"),
      m_workspace_border_width(m_resmanager, 1,
                               "fbpager.workspace.border.active.width",
                               "FbPager.Workspace.Border.active.Width"),
      m_workspace_border_inactive_width(m_resmanager, 1,
                                        "fbpager.workspace.border.inactive.width",
                                        "FbPager.Workspace.Border.Inactive.Width"),
      m_workspace_border_active_color(m_resmanager, "darkgreen",
                                      "fbpager.border.active.color",
                                      "FbPager.Border.Active.Color"),
      m_workspace_border_inactive_color(m_resmanager, "white",
                                        "fbpager.border.inactive.color",
                                        "FbPager.Border.Inactive.Color"),
      m_last_workspace_num(-1) {
  
      m_clienthandlers.push_back(new Ewmh());
  
      XSetErrorHandler(handleXErrors);
  
      FbTk::EventManager::instance()->add(*this, m_window);
  
      // setup client window
      m_window.setBackgroundColor(FbTk::Color((*m_color_str).c_str(),
                                              m_window.screenNumber()));
      m_window.setAlpha(*m_alpha);
  
  
      XWMHints wmhints;
  
      if (!withdraw) {
  
          wmhints.initial_state = NormalState;
      } else // withdraw
          wmhints.initial_state = WithdrawnState;
  
  
      // setup no input
      wmhints.input = False;
      // set which hints we're using
      wmhints.flags = StateHint | InputHint;
  
  
      // set window name
      std::string name = "FbPager";
      char *name_prop = &name[0];
      XTextProperty windowname;
      XStringListToTextProperty(&name_prop, 1, &windowname);
  
      // setup class hints
      Display *disp = FbTk::App::instance()->display();
      XClassHint classhints;
      string res_name = "fbpager";
      string res_class = "FbPager";
      classhints.res_name = &res_name[0];
      classhints.res_class = &res_class[0];
      // set window properties
      XSetWMProperties(disp, m_window.window(), &windowname, 0, 0, 0,
                       0, &wmhints, &classhints);
  
      XFree( windowname.value );
  
      if (resource_filename != 0)
          load(resource_filename);
  
  
      int real_layer_flag = layer_flag;
  
      // no override from command line?
      if ( real_layer_flag == -1 ) {
          // use resource value
          if ( *m_layer == "bottom" ) {
              real_layer_flag = 0;
          } else if ( *m_layer == "top" ) {
              real_layer_flag = 2;
          }
      }
  
      int extra_flags = 0;
      // now setup extra flags
      if ( real_layer_flag != -1 ) {
          if (real_layer_flag == 2) {
              extra_flags = WindowHint::WHINT_LAYER_TOP;
          } else if (real_layer_flag == 0) {
              extra_flags = WindowHint::WHINT_LAYER_BOTTOM;
          }
      }
  
      // setup some extra wm hints
      if (!withdraw) {
          WindowHint hint;
          hint.add(WindowHint::WHINT_NO_DECOR | WindowHint::WHINT_SKIP_TASKBAR |
                   WindowHint::WHINT_STICKY | WindowHint::WHINT_SKIP_PAGER |
                   extra_flags );
          ClientHandlerList::iterator it = m_clienthandlers.begin();
          ClientHandlerList::iterator it_end = m_clienthandlers.end();
          for (; it != it_end; ++it)
              (*it)->setHints(m_window, hint);
      }
  
      // setup workspace count
      ClientHandlerList::iterator it = m_clienthandlers.begin();
      ClientHandlerList::iterator it_end = m_clienthandlers.end();
      int workspaces = 0;
      for (; it != it_end; ++it) {
          workspaces = max((*it)->numberOfWorkspaces(m_window.screenNumber()),
                           workspaces);
      }
  
      updateWorkspaceCount(workspaces);
      // finaly show window
      show();
      m_window.move(*m_start_x, *m_start_y);
  
      m_window.resize(m_window.width(), m_window.height());
  
      m_last_button_event.time= 0;
  
      if (show_resources) {
          // dump resources to stdout
          m_resmanager.dump(true);
      }
  
      Window root = DefaultRootWindow( FbTk::App::instance()->display() );
      XSelectInput( disp, root,
                    PropertyChangeMask | SubstructureNotifyMask );
  
      FbTk::EventManager::instance()->add(*this, root);
  
  }
  
  FbPager::~FbPager() {
      destroyList(m_clienthandlers);
      destroyList(m_workspaces);
  }
  
  
  void FbPager::show() {
      m_window.show();
  }
  
  void FbPager::hide() {
      m_window.hide();
  }
  
  void FbPager::handleEvent(XEvent &event) {
      // here we handle events that're not in FbTk::EventHandler interface
      if (event.type == ClientMessage)
          clientMessageEvent(event.xclient);
      else if (event.type == ConfigureNotify) {
          if (event.xconfigure.window != m_window.window()) {
              updateWindowHints(event.xconfigure.window);
          } else if (event.xconfigure.send_event == True){
              if (event.xconfigure.x != m_window.x() ||
                  event.xconfigure.y != m_window.y() ||
                  event.xconfigure.width != m_window.width() ||
                  event.xconfigure.height != m_window.height()) {
  
                  m_window.updateGeometry(event.xconfigure.x,
                                          event.xconfigure.y,
                                          event.xconfigure.width,
                                          event.xconfigure.height);
  
                  clearWindows(); // update transparency
              }
  
          }
      } else if (event.type == PropertyNotify) {
          propertyEvent(event.xproperty);
      } else {
  
      }
  }
  
  void FbPager::buttonPressEvent(XButtonEvent &event) {
  
      if ( event.time - m_last_button_event.time > 1000 )
          m_button_queue= "";
  
      FB_ostringstream os;
  
      // double click
      if (event.time - m_last_button_event.time < *m_multiclick_time &&
          event.button == m_last_button_event.button)
          os << event.button;
      else
          os << " " << event.button;
  
      // append button string to current queue
      m_button_queue += os.str();
  
      // save last event so we can compare it to next for double click
      m_last_button_event = event;
  
  
      if (event.button == *m_move_in_workspace_button ||
          event.button == *m_drag_to_workspace_button ) {
  
          m_move_window.curr_window = 0;
          m_move_window.client = ClientWindow(0);
  
          FbTk::FbWindow *win = 0;
          for (size_t w = 0; w < m_workspaces.size(); ++w) {
              win = m_workspaces[w]->find(event.subwindow);
              if (win && win->parent() != 0) {
                  m_move_window.client = m_workspaces[w]->findClient(*win);
                  m_move_window.curr_window = win;
                  m_move_window.curr_workspace= m_workspaces[w];
                  m_grab_x = event.x - win->x();
                  m_grab_y = event.y - win->y();
  
                  // drag -> move it above all workspaces
                  if (event.button == *m_drag_to_workspace_button) {
                      m_move_window.curr_window->reparent(m_window,
                                                          m_move_window.curr_window->x() +
                                                          m_move_window.curr_workspace->window().x(),
                                                          m_move_window.curr_window->y() +
                                                          m_move_window.curr_workspace->window().y());
                  }
                  break;
              }
          }
      }
  
  }
  
  void FbPager::buttonReleaseEvent(XButtonEvent &event) {
  
      ClientWindow client(0);
      if (m_move_window.curr_window != 0 &&
          *m_move_in_workspace_button == event.button) {
  
          FbTk::FbWindow &win = *m_move_window.curr_window;
          int client_x = win.x(), client_y = win.y();
          scaleFromWindowToWindow(*(win.parent()),
                                  m_rootwin,
                                  client_x, client_y);
  
          m_move_window.client.move(client_x, client_y);
          client = m_move_window.client;
          m_move_window.client = ClientWindow(0);
          m_move_window.curr_window = 0;
  
      }
  
      if (m_move_window.curr_window != 0 && *m_drag_to_workspace_button == event.button) {
  
          if (m_last_workspace_num >= 0 &&
              m_last_workspace_num < m_workspaces.size()) {
  
              Workspace* workspace = m_workspaces[m_last_workspace_num];
  
              // let someone else send it to the _new_ workspace
              if (workspace != m_move_window.curr_workspace) {
  
                  ClientHandlerList::iterator it = m_clienthandlers.begin();
                  ClientHandlerList::iterator it_end = m_clienthandlers.end();
                  for (; it != it_end; ++it) {
                      (*it)->sendToWorkspace(m_move_window.client, m_last_workspace_num);
                  }
              }
  
              // drop it to the last workspace, the mouse was over
              m_move_window.curr_window->reparent(workspace->window(),
                                                  m_move_window.curr_window->x() -
                                                  workspace->window().x(),
                                                  m_move_window.curr_window->y() -
                                                  workspace->window().y());
  
          }
          else {
  
              m_move_window.curr_window->reparent(m_move_window.curr_workspace->window(),
                                                  m_grab_x,
                                                  m_grab_y);
          }
  
          // move it to the right position
          FbTk::FbWindow &win = *m_move_window.curr_window;
          int client_x = win.x(), client_y = win.y();
          scaleFromWindowToWindow(*(win.parent()),
                                  m_rootwin,
                                  client_x, client_y);
  
          m_move_window.client.move(client_x, client_y);
  
          // clean up
          m_move_window.client = ClientWindow(0);
          m_move_window.curr_window = 0;
          m_move_window.curr_workspace = 0;
          m_button_queue.erase();
  
      } else if (compareButtonQueues(m_button_queue, *m_change_workspace_button)) {
          // change workspace
  
          // find out which workspace
          Workspace *workspace = 0;
          FbTk::FbWindow *win = 0;
          size_t workspace_it = 0;
          for (; workspace_it < m_workspaces.size(); ++workspace_it) {
              if (m_workspaces[workspace_it]->window() == event.window) {
                  workspace = m_workspaces[workspace_it];
                  break;
              } else {
                  win = m_workspaces[workspace_it]->find(event.window);
                  if (win != 0) {
                      workspace = m_workspaces[workspace_it];
                      break;
                  }
              }
          }
  
          if (workspace != 0)
              sendChangeToWorkspace(workspace_it);
  
      } else if (compareButtonQueues(m_button_queue, *m_raise_window_button) &&
                 client.window() != 0) {
          // raise, currently the same as focus
  
          for_each(m_clienthandlers.begin(),
                   m_clienthandlers.end(),
                   RefBind(&ClientHandler::setFocus, client));
  
          client.raise();
      } else if (compareButtonQueues(m_button_queue, *m_lower_window_button) &&
                 client.window() != 0) {
          client.lower();
      } else if (compareButtonQueues(m_button_queue, *m_close_window_button) &&
                 client.window() != 0) {
          for_each(m_clienthandlers.begin(),
                   m_clienthandlers.end(),
                   RefBind(&ClientHandler::closeWindow, client));
      } else if (compareButtonQueues(m_button_queue, *m_exit_button)) {
          // exit
          FbTk::App::instance()->end();
      } else if (compareButtonQueues(m_button_queue, *m_next_workspace_button)) {
          // next workspace
          sendChangeToWorkspace(m_curr_workspace < m_workspaces.size() - 1 ?
                                m_curr_workspace + 1 : 0);
      } else if (compareButtonQueues(m_button_queue, *m_prev_workspace_button)) {
          // prev workspace
          sendChangeToWorkspace(m_curr_workspace > 0 ? m_curr_workspace - 1 :
                                m_workspaces.size() - 1);
      } else {
          // ok, we didn't find any queue that match
          // so we return without clearing the current queue
          return;
      }
  
      m_button_queue.erase();
  }
  
  void FbPager::motionNotifyEvent(XMotionEvent &event) {
  
      if (m_move_window.curr_window != 0 &&
          m_move_window.curr_workspace != 0) {
  
          Workspace* workspace= 0;
          size_t w = 0;
          size_t old_workspace= m_last_workspace_num;
          int left, top, right, bottom, x, y;
  
          // find out, on which workspace the motion started and
          // on which workspace we are now
          for(; w < m_workspaces.size(); ++w) {
  
              if (m_workspaces[w]->window() == event.window)
                  workspace= m_workspaces[w];
  
              left = m_workspaces[w]->window().x();
              top = m_workspaces[w]->window().y();
              right = left + m_workspaces[w]->window().width();
              bottom = top + m_workspaces[w]->window().height();
  
              // m_window - coordinate - space
              x = m_move_window.curr_workspace->window().x() + event.x;
              y = m_move_window.curr_workspace->window().y() + event.y;
  
              // inside the new workspace?
              if ( left <= x && x <= right && top <= y && y <= bottom )
                  m_last_workspace_num= w;
          }
  
          int newx = event.x - m_grab_x;
          int newy = event.y - m_grab_y;
  
          // just move in current workspace
          if (m_last_button_event.button == *m_move_in_workspace_button) {
  
              respectConstraints(newx, newy, *m_move_window.curr_window, workspace->window());
              m_move_window.curr_window->move(newx, newy);
  
              if (*m_follow_move) {
  
                  FbTk::FbWindow &win = *m_move_window.curr_window;
                  int client_x = win.x(), client_y = win.y();
                  scaleFromWindowToWindow(*(win.parent()),
                                          m_rootwin,
                                          client_x, client_y);
                  m_move_window.client.move(client_x, client_y);
              }
  
          // drag between the workspaces
          } else if (m_last_button_event.button == *m_drag_to_workspace_button) {
  
              newx += m_move_window.curr_workspace->window().x();
              newy += m_move_window.curr_workspace->window().y();
  
              respectConstraints(newx, newy, *m_move_window.curr_window, m_window);
  
              m_move_window.curr_window->move(newx, newy);
  
              if (*m_follow_drag && m_last_workspace_num != old_workspace)
                  sendChangeToWorkspace(m_last_workspace_num);
          }
      }
  }
  
  void FbPager::exposeEvent(XExposeEvent &event) {
      if (m_window == event.window) {
          m_window.clearArea(event.x, event.y,
                             event.width, event.height);
      } else {
          for (size_t w = 0; w < m_workspaces.size(); ++w) {
              if (m_workspaces[w]->window() == event.window) {
                  FbTk::FbWindow &win = m_workspaces[w]->window();
                  win.clearArea(event.x, event.y,
                                event.width, event.height);
                  return;
              } else {
                  FbTk::FbWindow *win = m_workspaces[w]->find(event.window);
                  if (win != 0) {
                      win->clearArea(event.x, event.y,
                                     event.width, event.height);
                      return;
                  }
              }
          }
      }
  }
  
  void FbPager::clientMessageEvent(XClientMessageEvent &event) {
      char* atom = XGetAtomName( FbTk::App::instance()->display(),
                                 event.message_type );
      cout << "ClientMessage ATOM: " << atom << endl;
      XFree( atom );
  
      ClientHandlerList::iterator it = m_clienthandlers.begin();
      ClientHandlerList::iterator it_end = m_clienthandlers.end();
      for (; it != it_end; ++it) {
          (*it)->clientMessage(*this, event);
      }
  
  }
  
  void FbPager::propertyEvent(XPropertyEvent &event) {
      ClientHandlerList::iterator it = m_clienthandlers.begin();
      ClientHandlerList::iterator it_end = m_clienthandlers.end();
      for (; it != it_end; ++it) {
          (*it)->propertyNotify(*this, event);
      }
      updateWindowHints(event.window);
  }
  
  void FbPager::eraseAllWindows() {
      for_each(m_workspaces.begin(), m_workspaces.end(),
               mem_fun(&Workspace::removeAll));
      WindowList::iterator it = m_windows.begin();
      for (; it != m_windows.end(); ++it) {
          FbTk::EventManager::instance()->remove(*it);
      }
      m_windows.clear();
  }
  
  void FbPager::addWindows(const WindowsAndWorkspaces& wins) {
      // destroy all old windows
      eraseAllWindows();
  
      for (unsigned int w = 0; w < wins.size(); ++w) {
          addWindow(wins[w].first, wins[w].second);
      }
  }
  
  void FbPager::addWindow(Window win, unsigned int workspace_num) {
      if (workspace_num >= m_workspaces.size())
          return;
  
      m_windows.insert(win);
  
      m_workspaces[workspace_num]->add(win);
      // update window mode
      updateWindowHints(win);
  
  }
  
  void FbPager::moveToWorkspace(Window win, unsigned int w) {
      if (w >= 0x7FFFFFFF) {
          updateWindowHints(w);
          return;
      } else if (w >= m_workspaces.size())
          return;
  
      // remove from old workspace
      for (size_t workspace=0; workspace < m_workspaces.size(); ++workspace) {
          if (w != workspace && m_workspaces[workspace]->find(win) != 0)
              m_workspaces[workspace]->remove(win);
      }
  
      addWindow(win, w);
  }
  
  void FbPager::setFocusedWindow(Window win) {
      Workspace::setFocusedWindow(win);
      for_each(m_workspaces.begin(),
               m_workspaces.end(),
               mem_fun(&Workspace::updateFocusedWindow));
  }
  
  void FbPager::removeWindow(Window win) {
      for_each(m_workspaces.begin(),
               m_workspaces.end(),
               bind2nd(mem_fun(&Workspace::remove), win));
      FbTk::EventManager::instance()->remove(win);
      m_windows.erase(win);
  }
  
  void FbPager::updateWorkspaceCount(unsigned int num) {
      if (num == 0 || num == m_workspaces.size())
          return;
  
      if (m_workspaces.size() < num) {
          // setup proper workspace scale
          float screenScale = (float)m_rootwin.width() / m_rootwin.height();
          unsigned int width = *m_workspace_width;
          unsigned int height = *m_workspace_height;
          if ( *m_follow_scale == "height" ) {
              width *= screenScale;
          } else if ( *m_follow_scale == "width" ) {
              height *= screenScale;
          }
          // add some workspaces until we match number of workspaces
          FbTk::Color wincolor(m_window_color_str->c_str(), m_window.screenNumber());
          FbTk::Color focusedcolor(m_focused_window_color_str->c_str(), m_window.screenNumber());
          FbTk::Color bordercol(m_window_bordercolor_str->c_str(), m_window.screenNumber());
          FbTk::Color backgroundcol(m_background_color_str->c_str(), m_window.screenNumber());
  
          while (m_workspaces.size() != num) {
              Workspace *w = new Workspace(*this, m_window,
                                           width, height,
                                           focusedcolor,
                                           wincolor,
                                           bordercol,
                                           backgroundcol,
                                           "workspace",
                                           *m_window_icons,
                                           *m_window_border_width);
              w->setAlpha(*m_alpha);
              w->window().setBorderWidth(*m_workspace_border_width);
              m_workspaces.push_back(w);
          }
      } else { // remove some workspace until we match number or workspaces
  
          while (m_workspaces.size() != num) {
              delete m_workspaces.back();
              m_workspaces.pop_back();
              // extra precaution
              if (m_workspaces.empty())
                  break;
          }
      }
  
      // update size of our main window and align workspaces
      alignWorkspaces();
      m_window.showSubwindows();
  }
  
  void setupWindow( FbTk::FbWindow &window,
                    const std::string &bg_color,
                    const std::string &border_color,
                    unsigned int border_width ) {
  
      window.setBackgroundColor(FbTk::Color(bg_color.c_str(),
                                            window.screenNumber()));
  
      window.setBorderColor(FbTk::
                            Color(border_color.c_str(),
                                  window.screenNumber()));
  
      window.setBorderWidth(border_width);
      window.clear();
  
  }
  
  void FbPager::setCurrentWorkspace(unsigned int num) {
      if (num >= m_workspaces.size() || num == m_curr_workspace)
          return;
  
      // set current workspaces background to "normal" background,
      FbTk::FbWindow &oldWindow = m_workspaces[m_curr_workspace]->window();
      setupWindow(oldWindow,
                  *m_background_color_str,
                  *m_workspace_border_inactive_color,
                  *m_workspace_border_inactive_width);
  
      // set next workspace background
      // update workspace background
      FbTk::FbWindow& newWindow = m_workspaces[num]->window();
      setupWindow(newWindow,
                  *m_current_background_color_str,
                  *m_workspace_border_active_color,
                  *m_workspace_border_width);
  
      newWindow.raise();
  
      if (*m_workspace_border_inactive_width == 0) {
          oldWindow.move( oldWindow.x(),
                          oldWindow.y() );
      }
  
      m_curr_workspace = num;
  }
  
  /**
     Setup workspaces position and size in our main window
  */
  void FbPager::alignWorkspaces() {
  
      const int bevel = *m_workspace_border_width;
      int diff_x = 0;
      int diff_y = 0;
      // assumes at least one workspace
      const FbTk::FbWindow& workspace_win = m_workspaces[0]->window();
      const unsigned int workspace_width = workspace_win.width();
      const unsigned int workspace_height = workspace_win.height();
  
      // Fix alignment of workspaces
      if (*m_align == LEFT_TO_RIGHT)
          diff_x = workspace_width + bevel;
      else // align TOP_TO_BOTTOM
          diff_y = workspace_height + bevel;
  
      // resize and reposition all workspaces
      WorkspaceList::iterator it = m_workspaces.begin();
      WorkspaceList::iterator it_end = m_workspaces.end();
      int next_pos_x = 0;
      int next_pos_y = 0;
      int workspace = 0;
      int max_x = 0;
      int max_y = 0;
      for (; it != it_end;
           ++it, next_pos_y += diff_y, next_pos_x += diff_x, ++workspace) {
  
          if (workspace >= *m_workspaces_per_row) {
              workspace = 0;
              if (*m_align == LEFT_TO_RIGHT) {
                  next_pos_x = bevel;
                  next_pos_y += workspace_height + bevel;
              } else {
                  next_pos_y = bevel;
                  next_pos_x += workspace_width + bevel;
              }
          }
  
          if (next_pos_x + workspace_width > max_x)
              max_x = next_pos_x + workspace_width;
          if (next_pos_y + workspace_height > max_y)
              max_y = next_pos_y + workspace_height;
  
          (*it)->window().move(next_pos_x, next_pos_y);
          (*it)->resize(workspace_width, workspace_height);
      }
  
      m_window.resize(max_x + 2*bevel, max_y + 2*bevel);
  
      XSizeHints sizehints;
      sizehints.max_width = sizehints.min_width = m_window.width();
      sizehints.max_height = sizehints.min_height = m_window.height();
      sizehints.flags = PMinSize | PMaxSize;
      XSetWMProperties(FbTk::App::instance()->display(), m_window.window(), 0, 0, 0, 0,
                       &sizehints, 0, 0);
  
      for_each(m_clienthandlers.begin(),
               m_clienthandlers.end(),
               RefBind(&ClientHandler::moveResize, m_window));
  
  }
  
  
  void FbPager::sendChangeToWorkspace(unsigned int num) {
      assert(num < m_workspaces.size());
  
      ClientHandlerList::iterator it = m_clienthandlers.begin();
      ClientHandlerList::iterator it_end = m_clienthandlers.end();
      for (; it != it_end; ++it)
          (*it)->changeWorkspace(m_window.screenNumber(), num);
  
  }
  
  void FbPager::updateWindowHints(Window win) {
      WindowHint hint;
      ClientWindow client(win);
      ClientHandlerList::iterator it = m_clienthandlers.begin();
      ClientHandlerList::iterator it_end = m_clienthandlers.end();
      for (; it != it_end; ++it)
          (*it)->getHints(client, hint);
  
      for (int workspace = 0; workspace < m_workspaces.size(); workspace++) {
          if (m_workspaces[workspace]->find(win) == 0) {
              // if the window is sticky then add it to this workspace too
              // and if it's not skip_pager nor type_dock
              if ((hint.flags() & WindowHint::WHINT_STICKY) &&
                  !(hint.flags() & WindowHint::WHINT_SKIP_PAGER) &&
                  !(hint.flags() & WindowHint::WHINT_TYPE_DOCK)) {
                  m_workspaces[workspace]->add(win);
              } else
                  continue; // normal window without sticky
  
          } else if (!(hint.flags() & WindowHint::WHINT_STICKY) &&
                     hint.workspace() != workspace ||
                     (hint.flags() & WindowHint::WHINT_SKIP_PAGER) ||
                     (hint.flags() & WindowHint::WHINT_TYPE_DOCK)) {
              // if win not sticky and if it's not suppose to be on this
              // workspace then remove it and try next workspace
              m_workspaces[workspace]->remove(win);
              continue; // next workspace
          }
  
          m_workspaces[workspace]->updateGeometry(win);
  
          if (hint.flags() & WindowHint::WHINT_SHADED) {
              m_workspaces[workspace]->shadeWindow(win);
          } else
              m_workspaces[workspace]->unshadeWindow(win);
  
          if ((hint.flags() & WindowHint::WHINT_ICONIC) &&
              !(hint.flags() & WindowHint::WHINT_SHADED))
              m_workspaces[workspace]->iconifyWindow(win);
          else if (!(hint.flags() & WindowHint::WHINT_SHADED))
              m_workspaces[workspace]->deiconifyWindow(win);
  
      } // end for
  }
  
  void FbPager::raiseWindow(Window win) {
      // raise on all workspace
      for_each(m_workspaces.begin(),
               m_workspaces.end(),
               bind2nd(mem_fun(&Workspace::raiseWindow), win));
  }
  
  void FbPager::lowerWindow(Window win) {
      // lower on all workspace
      for_each(m_workspaces.begin(),
               m_workspaces.end(),
               bind2nd(mem_fun(&Workspace::lowerWindow), win));
  }
  
  void FbPager::load(const std::string &filename) {
      if (!m_resmanager.load(FbTk::StringUtil::expandFilename(filename).c_str()))
          cerr<<"Failed to load resource file: "<<filename<<endl;
  
      m_window.setAlpha(*m_alpha);
      m_window.setBackgroundColor(FbTk::Color((*m_color_str).c_str(), m_window.screenNumber()));
      FbTk::Color bgcolor(m_background_color_str->c_str(), m_window.screenNumber());
      for (size_t workspace = 0; workspace < m_workspaces.size(); ++workspace) {
          m_workspaces[workspace]->setAlpha(*m_alpha);
          m_workspaces[workspace]->setWindowColor(*m_focused_window_color_str,
                                                  *m_window_color_str,
                                                  *m_window_bordercolor_str);
          m_workspaces[workspace]->window().setBackgroundColor(bgcolor);
      }
  
  }
  
  void FbPager::clearWindows() {
      m_window.clear();
      for_each(m_workspaces.begin(),
               m_workspaces.end(),
               mem_fun(&Workspace::clearWindows));
  }
  
  } // end namespace FbPager
  
  