From e85569f1df2511f4c5f54b5ea5615b6a80014bcc Mon Sep 17 00:00:00 2001
From: markt <markt>
Date: Fri, 23 Feb 2007 00:36:32 +0000
Subject: added some support for XEMBED protocol in systemtray

---
 ChangeLog         |   3 ++
 src/SystemTray.cc | 126 ++++++++++++++++++++++++++++++++++++++++--------------
 src/SystemTray.hh |   6 ++-
 3 files changed, 103 insertions(+), 32 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index dfc029d..37f91ea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
  (Format: Year/Month/Day)
 Changes for 1.0rc3:
+*07/02/22:
+   * Make systemtray support XEMBED protocol (Mark)
+     Systemtray.cc/hh
 *07/02/21:
    * Buttons should only run a command if the mouse was clicked down on the
      same button, e.g. bug #1437864 (Mark)
diff --git a/src/SystemTray.cc b/src/SystemTray.cc
index ecbdd4f..98a726a 100644
--- a/src/SystemTray.cc
+++ b/src/SystemTray.cc
@@ -49,7 +49,24 @@ using std::dec;
 /// helper class for tray windows, so we dont call XDestroyWindow
 class TrayWindow: public FbTk::FbWindow {
 public:
-    TrayWindow(Window win):FbTk::FbWindow(win) { }
+    TrayWindow(Window win):FbTk::FbWindow(win), m_visible(false) {
+        setEventMask(PropertyChangeMask);
+    }
+    bool isVisible() { return m_visible; }
+    void show() {
+        if (!m_visible) {
+            m_visible = true;
+            FbTk::FbWindow::show();
+        }
+    }
+    void hide() {
+        if (m_visible) {
+            m_visible = false;
+            FbTk::FbWindow::hide();
+        }
+    }
+private:
+    bool m_visible;
 };
 
 /// handles clientmessage event and notifies systemtray
@@ -115,7 +132,7 @@ SystemTray::SystemTray(const FbTk::FbWindow& parent, ButtonTheme& theme, BScreen
              SubstructureNotifyMask | SubstructureRedirectMask),
     m_theme(theme),
     m_screen(screen),
-    m_pixmap(0) {
+    m_pixmap(0), m_num_visible_clients(0) {
 
     FbTk::EventManager::instance()->add(*this, m_window);
     m_theme.reconfigSig().attach(this);
@@ -189,10 +206,9 @@ void SystemTray::resize(unsigned int width, unsigned int height) {
     if (width != m_window.width() ||
         height != m_window.height()) {
         m_window.resize(width, height);
-        if (!m_clients.empty()) {
+        if (m_num_visible_clients)
             rearrangeClients();
-            resizeSig().notify();
-        }
+        resizeSig().notify();
     }
 }
 
@@ -201,10 +217,9 @@ void SystemTray::moveResize(int x, int y,
     if (width != m_window.width() ||
         height != m_window.height()) {
         m_window.moveResize(x, y, width, height);
-        if (!m_clients.empty()) {
+        if (m_num_visible_clients)
             rearrangeClients();
-            resizeSig().notify();
-        }
+        resizeSig().notify();
     } else {
         move(x, y);
     }
@@ -224,14 +239,14 @@ unsigned int SystemTray::width() const {
     if (orientation() == FbTk::ROT90 || orientation() == FbTk::ROT270)
         return m_window.width();
 
-    return m_clients.size()* (height() - 2 * m_theme.border().width());
+    return m_num_visible_clients * (height() - 2 * m_theme.border().width());
 }
 
 unsigned int SystemTray::height() const {
     if (orientation() == FbTk::ROT0 || orientation() == FbTk::ROT180)
         return m_window.height();
 
-    return m_clients.size()* (width() - 2 * m_theme.border().width());
+    return m_num_visible_clients * (width() - 2 * m_theme.border().width());
 }
 
 unsigned int SystemTray::borderWidth() const {
@@ -297,26 +312,18 @@ void SystemTray::addClient(Window win) {
         return;
     }
 
-    FbTk::FbWindow *traywin = new TrayWindow(win);
+    TrayWindow *traywin = new TrayWindow(win);
 
 #ifdef DEBUG
     cerr<<"SystemTray::addClient(Window): 0x"<<hex<<win<<dec<<endl;
 #endif // DEBUG
-    if (m_clients.empty())
-        show();
 
     m_clients.push_back(traywin);
     FbTk::EventManager::instance()->add(*this, win);
     FbTk::EventManager::instance()->addParent(*this, window());
     XChangeSaveSet(FbTk::App::instance()->display(), win, SetModeInsert);
     traywin->reparent(m_window, 0, 0);
-    traywin->show();
-
-
-#ifdef DEBUG
-    cerr<<"SystemTray: number of clients = "<<m_clients.size()<<endl;
-#endif // DEBUG
-    rearrangeClients();
+    showClient(traywin);
 }
 
 void SystemTray::removeClient(Window win) {
@@ -327,15 +334,10 @@ void SystemTray::removeClient(Window win) {
 #ifdef DEBUG
     cerr<<__FILE__<<"(SystemTray::removeClient(Window)): 0x"<<hex<<win<<dec<<endl;
 #endif // DEBUG
-    FbTk::FbWindow *traywin = *tray_it;
+    TrayWindow *traywin = *tray_it;
     m_clients.erase(tray_it);
+    hideClient(traywin);
     delete traywin;
-    resize(width(), height());
-    rearrangeClients();
-    if (m_clients.empty()) {
-        // so we send notify signal to parent
-        resizeSig().notify();
-    }
 }
 
 void SystemTray::exposeEvent(XExposeEvent &event) {
@@ -345,11 +347,15 @@ void SystemTray::exposeEvent(XExposeEvent &event) {
 void SystemTray::handleEvent(XEvent &event) {
     if (event.type == DestroyNotify) {
         removeClient(event.xdestroywindow.window);
+    } else if (event.type == ReparentNotify && event.xreparent.parent != m_window.window()) {
+        removeClient(event.xreparent.window);
     } else if (event.type == UnmapNotify && event.xany.send_event) {
         // we ignore server-generated events, which can occur
         // on restart. The ICCCM says that a client must send
         // a synthetic event for the withdrawn state
-        removeClient(event.xunmap.window);
+        ClientList::iterator it = findClient(event.xunmap.window);
+        if (it != m_clients.end())
+            hideClient(*it);
     } else if (event.type == ConfigureNotify) {
         // we got configurenotify from an client
         // check and see if we need to update it's size
@@ -373,6 +379,38 @@ void SystemTray::handleEvent(XEvent &event) {
             }
         }
 
+    } else if (event.type == PropertyNotify) {
+        ClientList::iterator it = findClient(event.xproperty.window);
+        if (it != m_clients.end()) {
+            Atom embed_info = XInternAtom(FbTk::App::instance()->display(),"_XEMBED_INFO",false);
+            if (event.xproperty.atom == embed_info) {
+
+/* Flags for _XEMBED_INFO */
+#define XEMBED_MAPPED                   (1 << 0)
+
+                TrayWindow *traywin = *it;
+                Atom actual_type;
+                int actual_format;
+                unsigned long nitems, bytes_after;
+                unsigned long *prop;
+                if (traywin->property(embed_info, 0l, 2l, false, embed_info,
+                    &actual_type, &actual_format, &nitems, &bytes_after,
+                    (unsigned char **) &prop)) {
+
+                    bool mapped = (bool)(static_cast<unsigned long>(prop[1]) & XEMBED_MAPPED);
+                    XFree(static_cast<void *>(prop));
+
+#ifdef DEBUG
+    cerr<<__FILE__<<"(SystemTray::handleEvent(XEvent)): XEMBED_MAPPED = "<<mapped<<endl;
+#endif // DEBUG
+
+                    if (mapped)
+                        showClient(traywin);
+                    else
+                        hideClient(traywin);
+                }
+            }
+        }
     }
 }
 
@@ -380,7 +418,7 @@ void SystemTray::rearrangeClients() {
     unsigned int w_rot0 = width(), h_rot0 = height();
     const unsigned int bw = m_theme.border().width();
     FbTk::translateSize(orientation(), w_rot0, h_rot0);
-    unsigned int trayw = m_clients.size()*h_rot0 + bw, trayh = h_rot0;
+    unsigned int trayw = m_num_visible_clients*h_rot0 + bw, trayh = h_rot0;
     FbTk::translateSize(orientation(), trayw, trayh);
     resize(trayw, trayh);
     update(0);
@@ -389,9 +427,11 @@ void SystemTray::rearrangeClients() {
     ClientList::iterator client_it = m_clients.begin();
     ClientList::iterator client_it_end = m_clients.end();
     int next_x = bw;
-    for (; client_it != client_it_end;
-         ++client_it, next_x += h_rot0+bw) {
+    for (; client_it != client_it_end; ++client_it) {
+        if (!(*client_it)->isVisible())
+            continue;
         int x = next_x, y = bw;
+        next_x += h_rot0+bw;
         translateCoords(orientation(), x, y, w_rot0, h_rot0);
         translatePosition(orientation(), x, y, h_rot0, h_rot0, 0);
 
@@ -409,6 +449,28 @@ void SystemTray::removeAllClients() {
         delete m_clients.back();
         m_clients.pop_back();
     }
+    m_num_visible_clients = 0;
+}
+
+void SystemTray::hideClient(TrayWindow *traywin) {
+    if (!traywin || !traywin->isVisible())
+        return;
+
+    traywin->hide();
+    m_num_visible_clients--;
+    rearrangeClients();
+}
+
+void SystemTray::showClient(TrayWindow *traywin) {
+    if (!traywin || traywin->isVisible())
+        return;
+
+    if (!m_num_visible_clients)
+        show();
+
+    traywin->show();
+    m_num_visible_clients++;
+    rearrangeClients();
 }
 
 void SystemTray::update(FbTk::Subject* subject) {
@@ -432,6 +494,8 @@ void SystemTray::update(FbTk::Subject* subject) {
 
             // maybe not the best solution (yet), force a refresh of the
             // background of the client
+            if (!(*client_it)->isVisible())
+                continue;
             (*client_it)->hide();
             (*client_it)->show();
         }
diff --git a/src/SystemTray.hh b/src/SystemTray.hh
index 8be1cd8..0d4afc8 100644
--- a/src/SystemTray.hh
+++ b/src/SystemTray.hh
@@ -37,6 +37,7 @@
 
 #include <list>
 
+class TrayWindow;
 class AtomHandler;
 
 class SystemTray: public ToolbarItem, public FbTk::EventHandler, public FbTk::Observer {
@@ -77,11 +78,13 @@ private:
 
     void update(FbTk::Subject *subj);
     
-    typedef std::list<FbTk::FbWindow *> ClientList;
+    typedef std::list<TrayWindow *> ClientList;
     ClientList::iterator findClient(Window win);
 
     void rearrangeClients();
     void removeAllClients();
+    void hideClient(TrayWindow *traywin);
+    void showClient(TrayWindow *traywin);
 
     FbTk::FbWindow m_window;
     ButtonTheme& m_theme;
@@ -91,6 +94,7 @@ private:
     std::auto_ptr<AtomHandler> m_handler;
 
     ClientList m_clients;
+    size_t m_num_visible_clients;
 };
 
 #endif // SYSTEMTRAY_HH
-- 
cgit v0.11.2