2007-11-12 21:59:43 +00:00
|
|
|
// FocusableList.cc
|
|
|
|
// Copyright (c) 2007 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 "FocusableList.hh"
|
|
|
|
|
|
|
|
#include "Focusable.hh"
|
|
|
|
#include "FocusControl.hh"
|
|
|
|
#include "Screen.hh"
|
|
|
|
#include "WinClient.hh"
|
|
|
|
#include "Window.hh"
|
|
|
|
|
|
|
|
#include "FbTk/StringUtil.hh"
|
2008-09-21 11:44:48 +00:00
|
|
|
#include "FbTk/MemFun.hh"
|
2007-11-12 21:59:43 +00:00
|
|
|
|
|
|
|
#include <vector>
|
2011-02-23 21:46:13 +00:00
|
|
|
#include <algorithm>
|
2007-11-12 21:59:43 +00:00
|
|
|
|
2008-03-25 19:31:47 +00:00
|
|
|
#ifdef HAVE_CSTRING
|
|
|
|
#include <cstring>
|
|
|
|
#else
|
|
|
|
#include <string.h>
|
|
|
|
#endif
|
|
|
|
|
2007-11-12 21:59:43 +00:00
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
void FocusableList::parseArgs(const string &in, int &opts, string &pat) {
|
|
|
|
string options;
|
2007-12-20 20:23:56 +00:00
|
|
|
int err = FbTk::StringUtil::getStringBetween(options, in.c_str(), '{', '}',
|
|
|
|
" \t\n");
|
2007-11-12 21:59:43 +00:00
|
|
|
|
|
|
|
// the rest of the string is a ClientPattern
|
2007-12-20 20:23:56 +00:00
|
|
|
pat = in.c_str() + (err > 0 ? err : 0);
|
2007-11-12 21:59:43 +00:00
|
|
|
|
|
|
|
// now parse the options
|
|
|
|
vector<string> args;
|
|
|
|
FbTk::StringUtil::stringtok(args, options);
|
|
|
|
vector<string>::iterator it = args.begin(), it_end = args.end();
|
|
|
|
opts = 0;
|
|
|
|
for (; it != it_end; ++it) {
|
|
|
|
if (strcasecmp((*it).c_str(), "static") == 0)
|
|
|
|
opts |= STATIC_ORDER;
|
|
|
|
else if (strcasecmp((*it).c_str(), "groups") == 0)
|
|
|
|
opts |= LIST_GROUPS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const FocusableList *FocusableList::getListFromOptions(BScreen &scr, int opts) {
|
|
|
|
if (opts & LIST_GROUPS)
|
|
|
|
return (opts & STATIC_ORDER) ?
|
|
|
|
&scr.focusControl().creationOrderWinList() :
|
|
|
|
&scr.focusControl().focusedOrderWinList();
|
|
|
|
return (opts & STATIC_ORDER) ?
|
|
|
|
&scr.focusControl().creationOrderList() :
|
|
|
|
&scr.focusControl().focusedOrderList();
|
|
|
|
}
|
|
|
|
|
|
|
|
FocusableList::FocusableList(BScreen &scr, const string pat):
|
|
|
|
m_pat(0), m_parent(0), m_screen(scr) {
|
|
|
|
|
|
|
|
int options = 0;
|
|
|
|
string pattern;
|
|
|
|
parseArgs(pat, options, pattern);
|
|
|
|
m_parent = getListFromOptions(scr, options);
|
|
|
|
m_pat.reset(new ClientPattern(pattern.c_str()));
|
|
|
|
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
FocusableList::FocusableList(BScreen &scr, const FocusableList &parent,
|
|
|
|
const string pat):
|
|
|
|
m_pat(new ClientPattern(pat.c_str())), m_parent(&parent), m_screen(scr) {
|
|
|
|
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::init() {
|
|
|
|
addMatching();
|
|
|
|
m_parent->attachChild(*this);
|
|
|
|
|
|
|
|
// TODO: can't handle (head=[mouse]) yet
|
2008-09-21 11:44:48 +00:00
|
|
|
if (m_pat->dependsOnCurrentWorkspace()) {
|
|
|
|
join(m_screen.currentWorkspaceSig(),
|
|
|
|
FbTk::MemFun(*this, &FocusableList::workspaceChanged));
|
|
|
|
}
|
2008-09-28 08:46:49 +00:00
|
|
|
if (m_pat->dependsOnFocusedWindow()) {
|
|
|
|
join(m_screen.focusedWindowSig(),
|
|
|
|
FbTk::MemFun(*this, &FocusableList::focusedWindowChanged));
|
|
|
|
}
|
2007-11-12 21:59:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::update(FbTk::Subject *subj) {
|
|
|
|
if (subj == 0 || m_screen.isShuttingdown())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (typeid(*subj) == typeid(Focusable::FocusSubject)) {
|
|
|
|
Focusable::FocusSubject *fsubj =
|
|
|
|
static_cast<Focusable::FocusSubject *>(subj);
|
2010-03-26 17:15:45 +00:00
|
|
|
if (fsubj == &fsubj->win().dieSig()) {
|
2007-11-12 21:59:43 +00:00
|
|
|
remove(fsubj->win());
|
2010-03-26 17:15:45 +00:00
|
|
|
}
|
2007-11-12 21:59:43 +00:00
|
|
|
}
|
|
|
|
if (typeid(*subj) == typeid(FluxboxWindow::WinSubject)) {
|
|
|
|
FluxboxWindow::WinSubject *fsubj =
|
|
|
|
static_cast<FluxboxWindow::WinSubject *>(subj);
|
|
|
|
// we only bind these for matching patterns, so skip finding out signal
|
|
|
|
FluxboxWindow &fbwin = fsubj->win();
|
|
|
|
if (m_parent->contains(fbwin))
|
|
|
|
checkUpdate(fbwin);
|
|
|
|
std::list<WinClient *> list = fbwin.clientList();
|
|
|
|
std::list<WinClient *>::iterator it = list.begin(), it_end = list.end();
|
|
|
|
for (; it != it_end; ++it) {
|
|
|
|
if (m_parent->contains(**it))
|
|
|
|
checkUpdate(**it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (typeid(*subj) == typeid(FocusableListSubject)) {
|
|
|
|
FocusableListSubject *fsubj =
|
|
|
|
static_cast<FocusableListSubject *>(subj);
|
|
|
|
if (subj == &m_parent->addSig()) {
|
2007-11-20 19:01:45 +00:00
|
|
|
attachSignals(*fsubj->win());
|
2007-11-12 21:59:43 +00:00
|
|
|
if (m_pat->match(*fsubj->win())) {
|
|
|
|
insertFromParent(*fsubj->win());
|
|
|
|
m_addsig.notify(fsubj->win());
|
2007-11-20 19:01:45 +00:00
|
|
|
}
|
2007-11-12 21:59:43 +00:00
|
|
|
} else if (subj == &m_parent->removeSig())
|
|
|
|
remove(*fsubj->win());
|
|
|
|
else if (subj == &m_parent->resetSig())
|
|
|
|
reset();
|
|
|
|
else if (subj == &m_parent->orderSig()) {
|
|
|
|
Focusable *win = fsubj->win();
|
|
|
|
if (!win || !contains(*win))
|
|
|
|
return;
|
|
|
|
if (insertFromParent(*win))
|
|
|
|
m_ordersig.notify(win);
|
|
|
|
}
|
2008-09-28 08:46:49 +00:00
|
|
|
}
|
2007-11-12 21:59:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::checkUpdate(Focusable &win) {
|
|
|
|
if (contains(win)) {
|
|
|
|
if (!m_pat->match(win)) {
|
|
|
|
m_list.remove(&win);
|
2008-08-20 12:26:12 +00:00
|
|
|
m_pat->removeMatch();
|
2007-11-12 21:59:43 +00:00
|
|
|
m_removesig.notify(&win);
|
|
|
|
}
|
|
|
|
} else if (m_pat->match(win)) {
|
|
|
|
insertFromParent(win);
|
2008-08-20 12:26:12 +00:00
|
|
|
m_pat->addMatch();
|
2007-11-12 21:59:43 +00:00
|
|
|
m_addsig.notify(&win);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns whether or not the window was moved
|
|
|
|
bool FocusableList::insertFromParent(Focusable &win) {
|
|
|
|
const Focusables list = m_parent->clientList();
|
|
|
|
Focusables::const_iterator p_it = list.begin(), p_it_end = list.end();
|
|
|
|
Focusables::iterator our_it = m_list.begin(), our_it_end = m_list.end();
|
|
|
|
// walk through our list looking for corresponding entries in
|
|
|
|
// parent's list, until we find the window that moved
|
|
|
|
for (; our_it != our_it_end && p_it != p_it_end; p_it++) {
|
|
|
|
if (*p_it == &win) {
|
|
|
|
if (*our_it == &win) // win didn't move in our list
|
|
|
|
return false;
|
|
|
|
m_list.remove(&win);
|
|
|
|
m_list.insert(our_it, &win);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (*p_it == *our_it)
|
|
|
|
++our_it;
|
|
|
|
}
|
|
|
|
m_list.remove(&win);
|
|
|
|
m_list.push_back(&win);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::addMatching() {
|
|
|
|
if (!m_parent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const Focusables list = m_parent->clientList();
|
|
|
|
Focusables::const_iterator it = list.begin(), it_end = list.end();
|
|
|
|
for (; it != it_end; ++it) {
|
2008-08-20 12:26:12 +00:00
|
|
|
if (m_pat->match(**it)) {
|
2008-09-08 17:52:38 +00:00
|
|
|
m_list.push_back(*it);
|
2008-08-20 12:26:12 +00:00
|
|
|
m_pat->addMatch();
|
2008-09-08 17:52:38 +00:00
|
|
|
}
|
|
|
|
attachSignals(**it);
|
2007-11-12 21:59:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::pushFront(Focusable &win) {
|
|
|
|
m_list.push_front(&win);
|
|
|
|
attachSignals(win);
|
|
|
|
m_addsig.notify(&win);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::pushBack(Focusable &win) {
|
|
|
|
m_list.push_back(&win);
|
|
|
|
attachSignals(win);
|
|
|
|
m_addsig.notify(&win);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::moveToFront(Focusable &win) {
|
|
|
|
// if the window isn't already in this list, we could accidentally add it
|
|
|
|
if (!contains(win))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_list.remove(&win);
|
|
|
|
m_list.push_front(&win);
|
|
|
|
m_ordersig.notify(&win);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::moveToBack(Focusable &win) {
|
|
|
|
// if the window isn't already in this list, we could accidentally add it
|
|
|
|
if (!contains(win))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_list.remove(&win);
|
|
|
|
m_list.push_back(&win);
|
|
|
|
m_ordersig.notify(&win);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::remove(Focusable &win) {
|
|
|
|
// if the window isn't already in this list, we could send a bad signal
|
|
|
|
bool contained = contains(win);
|
|
|
|
|
|
|
|
detachSignals(win);
|
2010-03-26 17:15:45 +00:00
|
|
|
if (!contained) {
|
2007-11-12 21:59:43 +00:00
|
|
|
return;
|
2010-03-26 17:15:45 +00:00
|
|
|
}
|
2007-11-12 21:59:43 +00:00
|
|
|
m_list.remove(&win);
|
|
|
|
m_removesig.notify(&win);
|
|
|
|
}
|
|
|
|
|
2010-03-26 17:15:45 +00:00
|
|
|
void FocusableList::updateTitle(Focusable& win) {
|
|
|
|
checkUpdate(win);
|
|
|
|
}
|
|
|
|
|
2007-11-12 21:59:43 +00:00
|
|
|
void FocusableList::attachSignals(Focusable &win) {
|
|
|
|
win.dieSig().attach(this);
|
|
|
|
if (m_parent) {
|
|
|
|
// attach various signals for matching
|
2010-03-26 17:15:45 +00:00
|
|
|
if (m_signal_map.find(&win) == m_signal_map.end()) {
|
|
|
|
m_signal_map[&win] = join(win.titleSig(),
|
|
|
|
MemFunSelectArg1(*this,
|
|
|
|
&FocusableList::updateTitle));
|
|
|
|
}
|
|
|
|
|
2007-11-12 21:59:43 +00:00
|
|
|
FluxboxWindow *fbwin = win.fbwindow();
|
|
|
|
if (!fbwin)
|
|
|
|
return;
|
|
|
|
fbwin->workspaceSig().attach(this);
|
|
|
|
fbwin->stateSig().attach(this);
|
|
|
|
fbwin->layerSig().attach(this);
|
|
|
|
// TODO: can't watch (head=...) yet
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::detachSignals(Focusable &win) {
|
|
|
|
win.dieSig().detach(this);
|
|
|
|
if (m_parent) {
|
2010-03-26 17:15:45 +00:00
|
|
|
// disconnect client
|
|
|
|
SignalMap::iterator sigIt = m_signal_map.find(&win);
|
|
|
|
if (sigIt != m_signal_map.end()) {
|
|
|
|
leave(sigIt->second);
|
|
|
|
m_signal_map.erase(sigIt);
|
|
|
|
}
|
|
|
|
|
2007-11-12 21:59:43 +00:00
|
|
|
// detach various signals for matching
|
|
|
|
FluxboxWindow *fbwin = win.fbwindow();
|
|
|
|
if (!fbwin)
|
|
|
|
return;
|
|
|
|
fbwin->workspaceSig().detach(this);
|
|
|
|
fbwin->stateSig().detach(this);
|
|
|
|
fbwin->layerSig().detach(this);
|
|
|
|
// TODO: can't watch (head=...) yet
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FocusableList::reset() {
|
|
|
|
while (!m_list.empty()) {
|
|
|
|
detachSignals(*m_list.back());
|
|
|
|
m_list.pop_back();
|
|
|
|
}
|
2008-08-20 12:26:12 +00:00
|
|
|
m_pat->resetMatches();
|
2007-11-12 21:59:43 +00:00
|
|
|
if (m_parent)
|
|
|
|
addMatching();
|
|
|
|
m_resetsig.notify(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FocusableList::contains(const Focusable &win) const {
|
|
|
|
Focusables::const_iterator it = m_list.begin(), it_end = m_list.end();
|
2008-08-05 12:00:48 +00:00
|
|
|
it = std::find(it, it_end, &win);
|
2007-11-12 21:59:43 +00:00
|
|
|
return (it != it_end);
|
|
|
|
}
|
|
|
|
|
2008-08-05 12:00:48 +00:00
|
|
|
Focusable *FocusableList::find(const ClientPattern &pat) const {
|
|
|
|
Focusables::const_iterator it = m_list.begin(), it_end = m_list.end();
|
|
|
|
for (; it != it_end; ++it) {
|
|
|
|
if (pat.match(**it))
|
|
|
|
return *it;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-11-12 21:59:43 +00:00
|
|
|
void FocusableList::attachChild(FocusableList &child) const {
|
|
|
|
m_addsig.attach(&child);
|
|
|
|
m_removesig.attach(&child);
|
|
|
|
m_resetsig.attach(&child);
|
|
|
|
m_ordersig.attach(&child);
|
|
|
|
}
|
2008-09-21 11:44:48 +00:00
|
|
|
|
|
|
|
void FocusableList::workspaceChanged(BScreen &screen) {
|
|
|
|
reset();
|
|
|
|
}
|
2008-09-28 08:46:49 +00:00
|
|
|
|
|
|
|
void FocusableList::focusedWindowChanged(BScreen &screen,
|
|
|
|
FluxboxWindow *focused_win,
|
|
|
|
WinClient *client) {
|
|
|
|
reset();
|
|
|
|
}
|