Icon order now is property of the SystemTray.

This way it is only parsed once in the constructor. Updating the
TrayWindow order is now a method which is called once when the icon is
created.

sortClients/rearrange is now only called on adding and removing icons.
This commit is contained in:
Gregor Bollerhey 2014-01-08 20:38:00 +01:00 committed by Jan Sucan
parent bb0790d794
commit b23aba180f
2 changed files with 64 additions and 68 deletions

View file

@ -41,7 +41,6 @@
#include <vector>
#include <memory>
#include <algorithm>
#include <functional>
using std::string;
@ -84,6 +83,31 @@ void getScreenCoordinates(Window win, int x, int y, int &screen_x, int &screen_y
static SystemTray *s_theoneandonly = 0;
static std::string trim(const std::string& str)
{
// removes trailing and leading whitespace from a string
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) {
// splits a comma seperated list and performs trimming
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, ','))
out.push_back(trim(item));
}
/// helper class for tray windows, so we dont call XDestroyWindow
class SystemTray::TrayWindow : public FbTk::FbWindow {
public:
@ -91,6 +115,8 @@ public:
setEventMask(PropertyChangeMask);
}
void pinByClassname(const std::vector<std::string> left, const std::vector<std::string> right);
bool isVisible() { return m_visible; }
bool isXEmbedded() { return m_xembedded; }
void show() {
@ -133,6 +159,34 @@ private:
bool m_xembedded; // using xembed protocol? (i.e. unmap when done)
};
void TrayWindow::pinByClassname(const std::vector<std::string> left,
const std::vector<std::string> right) {
// based on the parsed order list and a given window returns an
// ordinal used to sort the tray icons.
auto deleter = [](XClassHint *x){if(x) XFree(x);};
std::unique_ptr<XClassHint, decltype(deleter)>
xclasshint(XAllocClassHint(), deleter);
if(XGetClassHint(Fluxbox::instance()->display(),
this->window(), xclasshint.get()))
{
std::string classname(xclasshint.get()->res_class);
auto ix = std::find(left.begin(), left.end(), classname);
if (ix != left.end())
m_order = -(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)
m_order = ix-right.begin()+1;
}
}
// in neither list or invalid window (=0)
}
/// handles clientmessage event and notifies systemtray
class SystemTrayHandler: public AtomHandler {
public:
@ -205,6 +259,9 @@ SystemTray::SystemTray(const FbTk::FbWindow& parent,
"", screen.name() + ".systray.pinRight",
screen.altName() + ".Systray.PinRight") {
parse_order(m_rc_systray_pinleft, m_pinleft);
parse_order(m_rc_systray_pinright, m_pinright);
FbTk::EventManager::instance()->add(*this, m_window);
FbTk::EventManager::instance()->add(*this, m_selection_owner);
// setup signals
@ -427,6 +484,9 @@ void SystemTray::addClient(Window win, bool using_xembed) {
if (traywin->getMappedDefault())
showClient(traywin);
traywin->pinByClassname(m_pinleft, m_pinright);
sortClients();
}
void SystemTray::removeClient(Window win, bool destroyed) {
@ -444,6 +504,8 @@ void SystemTray::removeClient(Window win, bool destroyed) {
}
hideClient(traywin, destroyed);
delete traywin;
sortClients();
}
void SystemTray::exposeEvent(XExposeEvent &event) {
@ -468,8 +530,6 @@ void SystemTray::handleEvent(XEvent &event) {
// 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() ||
@ -568,73 +628,8 @@ void SystemTray::showClient(TrayWindow *traywin) {
rearrangeClients();
}
static std::string trim(const std::string& str)
{
// removes trailing and leading whitespace from a string
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) {
// splits a comma seperated list and performs trimming
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) {
// based on the parsed order list and a given window returns an
// ordinal used to sort the tray icons.
auto deleter = [](XClassHint *x){if(x) XFree(x);};
std::unique_ptr<XClassHint, decltype(deleter)>
xclasshint(XAllocClassHint(), deleter);
if(XGetClassHint(Fluxbox::instance()->display(),
i->window(), xclasshint.get()))
{
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;
}
void SystemTray::sortClients() {
std::vector<std::string> pinleft, pinright;
parse_order(m_rc_systray_pinleft, pinleft);
parse_order(m_rc_systray_pinright, pinright);
for(TrayWindow *i: m_clients)
i->m_order = client_to_ordinal(pinleft, pinright, i);
m_clients.sort([](TrayWindow *a, TrayWindow *b){return a->m_order < b->m_order;});
rearrangeClients();
}

View file

@ -121,6 +121,7 @@ private:
// resources
FbTk::Resource<std::string> m_rc_systray_pinleft;
FbTk::Resource<std::string> m_rc_systray_pinright;
std::vector<std::string> m_pinleft, m_pinright;
};
#endif // SYSTEMTRAY_HH