From 7e762f0a972b7714f76a94e537dd65d0067f67b2 Mon Sep 17 00:00:00 2001 From: Gregor Bollerhey <gbsoftware@arcor.de> Date: Sat, 14 Dec 2013 08:43:26 +0100 Subject: Make systray icon pinning available. The user options screenname.systray.pinLeft and screenname.systray.pinRight in .fluxbox/init are read as comma sperated list of window classnames. While preserving the order of the lists, systray icons are sorted so that pinLeft'ed classnames appear left and vice versa. --- src/SystemTray.cc | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++---- src/SystemTray.hh | 7 +++++ 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/SystemTray.cc b/src/SystemTray.cc index 4fa9da3..4b2cf56 100644 --- a/src/SystemTray.cc +++ b/src/SystemTray.cc @@ -37,6 +37,11 @@ #include <X11/Xatom.h> #include <string> +#include <sstream> +#include <vector> +#include <memory> +#include <algorithm> +#include <functional> using std::string; @@ -161,7 +166,6 @@ public: winclient.setEventMask(StructureNotifyMask | SubstructureNotifyMask | EnterWindowMask); m_tray.addClient(winclient.window(), false); - }; void updateWorkarea(BScreen &) { } @@ -192,7 +196,13 @@ SystemTray::SystemTray(const FbTk::FbWindow& parent, m_theme(theme), m_screen(screen), m_pixmap(0), m_num_visible_clients(0), - m_selection_owner(m_window, 0, 0, 1, 1, SubstructureNotifyMask, false, false, CopyFromParent, InputOnly) { + m_selection_owner(m_window, 0, 0, 1, 1, SubstructureNotifyMask, false, false, CopyFromParent, InputOnly), + m_rc_systray_pinleft(screen.resourceManager(), + "", screen.name() + ".systray.pinLeft", + screen.altName() + ".Systray.PinLeft"), + m_rc_systray_pinright(screen.resourceManager(), + "", screen.name() + ".systray.pinRight", + screen.altName() + ".Systray.PinRight") { FbTk::EventManager::instance()->add(*this, m_window); FbTk::EventManager::instance()->add(*this, m_selection_owner); @@ -456,6 +466,9 @@ void SystemTray::handleEvent(XEvent &event) { // check and see if we need to update it's size // and we must reposition and resize them to fit // our toolbar + + sortClients(); + ClientList::iterator it = findClient(event.xconfigure.window); if (it != m_clients.end()) { if (static_cast<unsigned int>(event.xconfigure.width) != (*it)->width() || @@ -473,7 +486,6 @@ void SystemTray::handleEvent(XEvent &event) { resizeSig().emit(); } } - } else if (event.type == PropertyNotify) { ClientList::iterator it = findClient(event.xproperty.window); if (it != m_clients.end()) { @@ -484,8 +496,7 @@ void SystemTray::handleEvent(XEvent &event) { hideClient(*it); } } - } - + } } void SystemTray::rearrangeClients() { @@ -556,6 +567,74 @@ void SystemTray::showClient(TrayWindow *traywin) { rearrangeClients(); } +static std::string trim(const std::string& str) +{ + const std::string whitespace(" \t"); + const auto strBegin = str.find_first_not_of(whitespace); + if (strBegin == std::string::npos) + return ""; // no content + + const auto strEnd = str.find_last_not_of(whitespace); + const auto strRange = strEnd - strBegin + 1; + + return str.substr(strBegin, strRange); +} + +static void parse_order(const std::string s, std::vector<std::string> &out) { + std::stringstream ss(s); + std::string item; + + while (std::getline(ss, item, ',')) + out.push_back(trim(item)); +} + +static int client_to_ordinal(const std::vector<std::string> left, + const std::vector<std::string> right, + TrayWindow *i) { + + std::unique_ptr<XClassHint> xclasshint (XAllocClassHint()); + if(XGetClassHint(Fluxbox::instance()->display(), + i->window(), xclasshint.get()) != BadWindow) + { + std::string classname(xclasshint.get()->res_class); + + auto ix = std::find(left.begin(), left.end(), classname); + if (ix != left.end()) + return -(left.end()-ix); // the more left, the negative (<0) + else { + ix = std::find(right.begin(), right.end(), classname); + if (ix != right.end()) + // the more right, the positive (>0) + return ix-right.begin()+1; + } + } + + // in neither list or invalid window (=0) + return 0; +} + +static bool client_comperator(const std::vector<std::string> left, + const std::vector<std::string> right, + TrayWindow *item1, TrayWindow *item2) { + const int a = client_to_ordinal(left, right, item1); + const int b = client_to_ordinal(left, right, item2); + return a<b; +} + + +void SystemTray::sortClients() { + std::vector<std::string> pinleft, pinright; + + parse_order(m_rc_systray_pinleft, pinleft); + parse_order(m_rc_systray_pinright, pinright); + + m_clients.sort(std::bind(client_comperator, + pinleft, pinright, + std::placeholders::_1, std::placeholders::_2)); + + rearrangeClients(); +} + void SystemTray::update() { if (!m_theme->texture().usePixmap()) { diff --git a/src/SystemTray.hh b/src/SystemTray.hh index 16e703e..b9fd75a 100644 --- a/src/SystemTray.hh +++ b/src/SystemTray.hh @@ -26,12 +26,14 @@ #include "FbTk/FbWindow.hh" #include "FbTk/EventHandler.hh" #include "FbTk/Signal.hh" +#include "FbTk/Resource.hh" #include "ToolTheme.hh" #include "ToolbarItem.hh" #include <list> #include <memory> +#include <string> class BScreen; class ButtonTheme; @@ -91,6 +93,7 @@ public: private: void update(); + void sortClients(); class TrayWindow; typedef std::list<TrayWindow*> ClientList; @@ -114,6 +117,10 @@ 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; + + // resources + FbTk::Resource<std::string> m_rc_systray_pinleft; + FbTk::Resource<std::string> m_rc_systray_pinright; }; #endif // SYSTEMTRAY_HH -- cgit v0.11.2