From 80b10f7772b91f1f4a61eace4e5235a79060e1f0 Mon Sep 17 00:00:00 2001
From: Henrik Kinnunen <fluxgen@fluxbox.org>
Date: Sun, 27 Apr 2008 21:22:18 +0200
Subject: [PATCH] Added SimpleObserver class.

This class works in the same way as the SimpleCommand class.
Use it with the makeObserver function.
It calls the receiver's member function when the subject sends
a signal.
---
 src/FbTk/Makefile.am       |  2 +-
 src/FbTk/SimpleObserver.hh | 70 ++++++++++++++++++++++++++++++++++++++
 src/SendToMenu.cc          | 31 ++++++++---------
 src/SendToMenu.hh          | 23 ++++++++++---
 src/SystemTray.cc          | 15 ++++----
 src/SystemTray.hh          | 11 +++---
 src/Toolbar.cc             | 37 +++++++++-----------
 src/Toolbar.hh             |  8 ++---
 8 files changed, 139 insertions(+), 58 deletions(-)
 create mode 100644 src/FbTk/SimpleObserver.hh

diff --git a/src/FbTk/Makefile.am b/src/FbTk/Makefile.am
index 6fa874a8..fcf03044 100644
--- a/src/FbTk/Makefile.am
+++ b/src/FbTk/Makefile.am
@@ -44,7 +44,7 @@ libFbTk_a_SOURCES = App.hh App.cc Color.cc Color.hh Command.hh \
 	StringUtil.hh StringUtil.cc Parser.hh Parser.cc \
 	RegExp.hh RegExp.cc \
 	FbString.hh FbString.cc \
-	Subject.hh Subject.cc Observer.hh Observer.cc \
+	Subject.hh Subject.cc Observer.hh Observer.cc SimpleObserver.hh \
 	Transparent.hh Transparent.cc \
 	FbPixmap.hh FbPixmap.cc \
 	FbDrawable.hh FbDrawable.cc \
diff --git a/src/FbTk/SimpleObserver.hh b/src/FbTk/SimpleObserver.hh
new file mode 100644
index 00000000..aafdf596
--- /dev/null
+++ b/src/FbTk/SimpleObserver.hh
@@ -0,0 +1,70 @@
+// SimpleObserver.hh
+// Copyright (c) 2008 Fluxbox Team (fluxgen at fluxbox dot org)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+#include "Observer.hh"
+#include "SimpleCommand.hh"
+
+namespace FbTk {
+
+/** Functor for observers, instead of using this directly use makeObserver.
+ * Usage: 
+ * @code
+ * class SomeClass {
+ * public:
+ *    void doAction();
+ * };
+ *
+ * SomeClass some;
+ *
+ * Observer* obs = makeProxyObserver(some, &SomeClass::doAction);
+ * SomeSubject subj;
+ * subj.attach(obs);
+ * @endcode
+ */
+template <typename Receiver>
+class SimpleObserver: public Observer {
+public:
+    typedef void (Receiver::* Action)();
+    SimpleObserver(Receiver &r, Action a):
+        m_receiver(r), m_action(a) {
+        
+    }
+    void update(Subject *changedSubj) {
+        (m_receiver.*m_action)();
+    }
+private:
+    Receiver &m_receiver;
+    Action m_action;
+};
+
+// Helpers
+/** Creates an observer that takes no arguments.
+ * @param receiver The receiving instance.
+ * @param action A function in the receiving class.
+ * @return allocated simple observer. @see SimpleObserver
+ */
+template <typename Receiver, typename Action>
+Observer *makeObserver(Receiver &receiver, Action action) {
+    return new SimpleObserver<Receiver>( receiver, action );
+}
+
+}
+
diff --git a/src/SendToMenu.cc b/src/SendToMenu.cc
index ae261b9d..8ec20940 100644
--- a/src/SendToMenu.cc
+++ b/src/SendToMenu.cc
@@ -1,5 +1,5 @@
 // SendToMenu.cc for Fluxbox
-// Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
+// Copyright (c) 2003 - 2008 Henrik Kinnunen (fluxgen at fluxbox dot org)
 //                and Simon Bowden    (rathnor at users.sourceforge.net)
 // 
 // Permission is hereby granted, free of charge, to any person obtaining a
@@ -31,6 +31,7 @@
 
 #include "FbTk/MultiButtonMenuItem.hh"
 #include "FbTk/Command.hh"
+#include "FbTk/SimpleObserver.hh"
 
 class SendToCmd: public FbTk::Command<void> {
 public:
@@ -54,24 +55,21 @@ SendToMenu::SendToMenu(BScreen &screen):
     // workspace count signal
     // workspace names signal
     // current workspace signal
-    screen.workspaceCountSig().attach(this);
-    screen.workspaceNamesSig().attach(this);
-    screen.currentWorkspaceSig().attach(this);
-
+    m_rebuildObs = makeObserver(*this, &SendToMenu::rebuildMenu);
+    screen.workspaceCountSig().attach(m_rebuildObs);
+    screen.workspaceNamesSig().attach(m_rebuildObs);
+    screen.currentWorkspaceSig().attach(m_rebuildObs);
+    // no title for this menu, it should be a submenu in the window menu.
     disableTitle();
-    // build menu
-    update(0);
+    // setup menu items
+    rebuildMenu();
 }
 
-void SendToMenu::update(FbTk::Subject *subj) {
-    if (subj != 0) {
-        if (subj == &(theme().reconfigSig())) {
-            // we got reconfig Theme signal, let base menu handle it 
-            FbTk::Menu::update(subj);
-            return;
-        }
-        
-    }
+SendToMenu::~SendToMenu() {
+    delete m_rebuildObs;
+}
+
+void SendToMenu::rebuildMenu() {
     // rebuild menu
 
     removeAll();
@@ -95,6 +93,7 @@ void SendToMenu::show() {
     if (WindowCmd<void>::window() != 0) {
         for (unsigned int i=0; i < numberOfItems(); ++i)
             setItemEnabled(i, true);
+        // update the workspace for the current window
         setItemEnabled(WindowCmd<void>::window()->workspaceNumber(), false);
         updateMenu();
     }
diff --git a/src/SendToMenu.hh b/src/SendToMenu.hh
index 77549879..0f668a9d 100644
--- a/src/SendToMenu.hh
+++ b/src/SendToMenu.hh
@@ -1,5 +1,5 @@
 // SendToMenu.hh for Fluxbox
-// Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
+// Copyright (c) 2003 - 2008 Henrik Kinnunen (fluxgen at fluxbox dot org)
 //                and Simon Bowden    (rathnor at users.sourceforge.net)
 // 
 // Permission is hereby granted, free of charge, to any person obtaining a
@@ -25,15 +25,28 @@
 
 #include "FbMenu.hh"
 
+namespace FbTk {
+class Observer;
+}
+
 class BScreen;
 
+/**
+ * Creates the "send to menu".
+ * Displays all the workspaces for which the current window can be sent to.
+ */
 class SendToMenu:public FbMenu {
 public:
-    explicit SendToMenu(BScreen &win);
-    virtual ~SendToMenu() { }
+    /// @param screen the screen on which this menu should be created on.
+    explicit SendToMenu(BScreen &screen);
+    virtual ~SendToMenu();
+    /// @see FbTk::Menu
     void show();
-protected:
-    void update(FbTk::Subject *subj);
+private:
+    /// Rebuild the menu from scratch.
+    void rebuildMenu();
+    /// listens to signals that makes this instance need to rebuild menu
+    FbTk::Observer *m_rebuildObs;
 };
 
 #endif // SENDTOMENU_HH
diff --git a/src/SystemTray.cc b/src/SystemTray.cc
index 4275099c..699ca473 100644
--- a/src/SystemTray.cc
+++ b/src/SystemTray.cc
@@ -30,6 +30,7 @@
 #include "WinClient.hh"
 #include "Screen.hh"
 #include "ButtonTheme.hh"
+#include "SimpleObserver.hh"
 
 #include <X11/Xutil.h>
 #include <X11/Xatom.h>
@@ -166,8 +167,10 @@ SystemTray::SystemTray(const FbTk::FbWindow& parent,
     
     FbTk::EventManager::instance()->add(*this, m_window);
     FbTk::EventManager::instance()->add(*this, m_selection_owner);
-    m_theme->reconfigSig().attach(this);
-    screen.bgChangeSig().attach(this);
+    // setup signals
+    m_observer.reset(makeObserver(*this, &SystemTray::update));
+    m_theme->reconfigSig().attach(m_observer.get());
+    screen.bgChangeSig().attach(m_observer.get());
 
     Fluxbox* fluxbox = Fluxbox::instance();
     Display *disp = fluxbox->display();
@@ -216,7 +219,7 @@ SystemTray::SystemTray(const FbTk::FbWindow& parent,
 
     XSendEvent(disp, root_window, false, StructureNotifyMask, &ce);
 
-    update(0);
+    update();
 }
 
 SystemTray::~SystemTray() {
@@ -276,7 +279,7 @@ void SystemTray::hide() {
 
 void SystemTray::show() {
 
-    update(0);
+    update();
     m_window.show();
 }
 
@@ -470,7 +473,7 @@ void SystemTray::rearrangeClients() {
     unsigned int trayw = m_num_visible_clients*h_rot0 + bw, trayh = h_rot0;
     FbTk::translateSize(orientation(), trayw, trayh);
     resize(trayw, trayh);
-    update(0);
+    update();
 
     // move and resize clients
     ClientList::iterator client_it = m_clients.begin();
@@ -529,7 +532,7 @@ void SystemTray::showClient(TrayWindow *traywin) {
     rearrangeClients();
 }
 
-void SystemTray::update(FbTk::Subject* subject) {
+void SystemTray::update() {
 
     if (!m_theme->texture().usePixmap()) {
         m_window.setBackgroundColor(m_theme->texture().color());
diff --git a/src/SystemTray.hh b/src/SystemTray.hh
index 3215e311..132069ac 100644
--- a/src/SystemTray.hh
+++ b/src/SystemTray.hh
@@ -25,12 +25,12 @@
 
 #include "FbTk/FbWindow.hh"
 #include "FbTk/EventHandler.hh"
-#include "FbTk/Observer.hh"
 
 #include "ToolTheme.hh"
 #include "ToolbarItem.hh"
 
 #include <list>
+#include <memory>
 
 class BScreen;
 class ButtonTheme;
@@ -39,9 +39,10 @@ class AtomHandler;
 
 namespace FbTk {
 template <class T> class ThemeProxy;
+class Observer;
 }
 
-class SystemTray: public ToolbarItem, public FbTk::EventHandler, public FbTk::Observer {
+class SystemTray: public ToolbarItem, public FbTk::EventHandler {
 public:
 
     explicit SystemTray(const FbTk::FbWindow &parent,
@@ -75,7 +76,7 @@ public:
         m_window.setBorderWidth(m_theme->border().width());
         m_window.setBorderColor(m_theme->border().color());
         m_window.setAlpha(alpha); 
-        update(0); 
+        update(); 
     }
     void updateSizing() { m_window.setBorderWidth(m_theme->border().width()); }
 
@@ -85,7 +86,7 @@ public:
 
 private:
 
-    void update(FbTk::Subject *subj);
+    void update();
 
     typedef std::list<TrayWindow *> ClientList;
     ClientList::iterator findClient(Window win);
@@ -108,7 +109,7 @@ private:
     // gaim/pidgin seems to barf if the selection is not an independent window.
     // I suspect it's an interacton with parent relationship and gdk window caching.
     FbTk::FbWindow m_selection_owner;
-
+    std::auto_ptr<FbTk::Observer> m_observer;
 };
 
 #endif // SYSTEMTRAY_HH
diff --git a/src/Toolbar.cc b/src/Toolbar.cc
index 26d87a8f..540ceb98 100644
--- a/src/Toolbar.cc
+++ b/src/Toolbar.cc
@@ -50,7 +50,7 @@
 #include "FbTk/BoolMenuItem.hh"
 #include "FbTk/IntMenuItem.hh"
 #include "FbTk/Shape.hh"
-
+#include "FbTk/SimpleObserver.hh"
 
 // use GNU extensions
 #ifndef	 _GNU_SOURCE
@@ -240,11 +240,15 @@ Toolbar::Toolbar(BScreen &scrn, FbTk::XLayer &layer, size_t width):
     m_shape(new FbTk::Shape(frame.window, 0)),
     m_resize_lock(false) {
     _FB_USES_NLS;
+    // NOTE: first subject is always the rearrangeItem !
+    m_observers.push_back(makeObserver(*this, &Toolbar::rearrangeItems));
     // we need to get notified when the theme is reloaded
-    m_theme.reconfigSig().attach(this);
+    m_observers.push_back(makeObserver(*this, &Toolbar::reconfigure));
+    m_theme.reconfigSig().attach(m_observers.back());
+    screen().reconfigureSig().attach(m_observers.back()); // get this on antialias change
     // listen to screen size changes
-    screen().resizeSig().attach(this);
-    screen().reconfigureSig().attach(this); // get this on antialias change
+    screen().resizeSig().attach(m_observers.back());
+
 
     moveToLayer((*m_rc_layernum).getNum());
 
@@ -370,6 +374,7 @@ void Toolbar::lower() {
 }
 
 void Toolbar::reconfigure() {
+
     updateVisibleState();
 
     if (!doAutoHide() && isHidden())
@@ -426,7 +431,8 @@ void Toolbar::reconfigure() {
                 if (item == 0)
                     continue;
                 m_item_list.push_back(item);
-                item->resizeSig().attach(this);
+                // attach to first observer ( which must be rearrangeItems )
+                item->resizeSig().attach(m_observers[0]);
 
             }
             // show all items
@@ -515,6 +521,11 @@ void Toolbar::reconfigure() {
     // area to be reserved on screen
     updateStrut();
 
+#ifdef XINERAMA
+    if (m_xineramaheadmenu)
+        m_xineramaheadmenu->reloadHeads();
+#endif // XINERAMA
+
 }
 
 
@@ -613,22 +624,6 @@ void Toolbar::handleEvent(XEvent &event) {
 */
 }
 
-void Toolbar::update(FbTk::Subject *subj) {
-
-    // either screen reconfigured, theme was reloaded
-    // or a tool resized itself
-
-    if (typeid(*subj) == typeid(ToolbarItem::ToolbarItemSubject))
-        rearrangeItems();
-    else
-        reconfigure();
-
-#ifdef XINERAMA
-    if (subj == &m_screen.resizeSig() && m_xineramaheadmenu)
-        m_xineramaheadmenu->reloadHeads();
-#endif // XINERAMA
-}
-
 void Toolbar::setPlacement(Toolbar::Placement where) {
     // disable vertical toolbar
 
diff --git a/src/Toolbar.hh b/src/Toolbar.hh
index c295e24d..c68210fb 100644
--- a/src/Toolbar.hh
+++ b/src/Toolbar.hh
@@ -37,7 +37,6 @@
 
 #include "FbTk/Timer.hh"
 #include "FbTk/Resource.hh"
-#include "FbTk/Observer.hh"
 #include "FbTk/XLayer.hh"
 #include "FbTk/XLayerItem.hh"
 #include "FbTk/EventHandler.hh"
@@ -57,7 +56,8 @@ class Shape;
 
 /// The toolbar.
 /// Handles iconbar, workspace name view and clock view
-class Toolbar: public FbTk::EventHandler, public FbTk::Observer, public LayerObject {
+class Toolbar: public FbTk::EventHandler,
+               public LayerObject {
 public:
 
     /// Toolbar placement on the screen
@@ -99,8 +99,6 @@ public:
     void reconfigure();
     void setPlacement(Placement where);
 
-    void update(FbTk::Subject *subj);
-
     int layerNumber() const { return const_cast<FbTk::XLayerItem &>(m_layeritem).getLayerNum(); }
 
     const FbTk::Menu &menu() const { return m_toolbarmenu; }
@@ -193,6 +191,8 @@ private:
     StringList m_tools;
 
     bool m_resize_lock; ///< to lock rearrangeItems or not
+    /// observers for various signals
+    std::vector<FbTk::Observer*> m_observers;
 };