add regular expression support in remember capabilities
see ChangeLog for details
This commit is contained in:
parent
94f1c16416
commit
e139cbb028
9 changed files with 657 additions and 124 deletions
21
ChangeLog
21
ChangeLog
|
@ -1,4 +1,25 @@
|
|||
(Format: Year/Month/Day)
|
||||
Changes for 0.9.4:
|
||||
*03/06/13:
|
||||
* Regular expression support for remember (Simon)
|
||||
Also ability to limit number of matches for a given rule
|
||||
Also ability to match several different window attributes
|
||||
Can disable in compile using --disable-regexp (will just do plain
|
||||
string equality then)
|
||||
- General format is:
|
||||
[app] (property=expr) ... {number}
|
||||
If "property=" is excluded, the name property is assumed.
|
||||
If {number} is excluded, 0 = no limit is assumed.
|
||||
- Current available properties are:
|
||||
* name -> the name of the window - the first field of WM_CLASS
|
||||
* class -> the class of the window - the second field of WM_CLASS
|
||||
* title -> the title of the window - the WM_NAME property
|
||||
- e.g. [app] (*[tT]erm) {2}
|
||||
will match anything ending with term, for up to 2 instances
|
||||
- e.g. [app] (title=.*gaim.*)
|
||||
will match anything with gaim in the title ("gaim", "the gaim
|
||||
window", etc.
|
||||
RegExp.hh/cc ClientPattern.hh/cc configure.in Makefile.am Remember.hh/cc WinClient.hh/cc StringUtil.hh/cc
|
||||
Changes for 0.9.3:
|
||||
*03/06/08:
|
||||
* Add Reconfigure and Restart Key actions, thanks Jann Fisher (Simon)
|
||||
|
|
29
configure.in
29
configure.in
|
@ -119,6 +119,32 @@ AC_ARG_ENABLE(
|
|||
)
|
||||
AM_CONDITIONAL(REMEMBER_SRC, test x$REMEMBER_SRC = xtrue)
|
||||
|
||||
AC_MSG_CHECKING([whether to have (POSIX) regular expression support])
|
||||
AC_ARG_ENABLE(
|
||||
regexp,
|
||||
[ --enable-regexp regular expression support [default=yes]],
|
||||
if test x$enableval = "xyes"; then
|
||||
AC_EGREP_HEADER([regex_t],regex.h,
|
||||
AC_DEFINE(USE_REGEXP, 1, "Regular Expression support")
|
||||
AC_MSG_RESULT([yes])
|
||||
REGEXP_SRC=true,
|
||||
AC_MSG_RESULT([no])
|
||||
REGEXP_SRC=false
|
||||
)
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
REGEXP_SRC=false
|
||||
fi,
|
||||
AC_EGREP_HEADER([regex_t],regex.h,
|
||||
AC_DEFINE(USE_REGEXP, 1, "Regular Expression support")
|
||||
AC_MSG_RESULT([yes])
|
||||
REGEXP_SRC=true,
|
||||
AC_MSG_RESULT([no])
|
||||
REGEXP_SRC=false
|
||||
)
|
||||
)
|
||||
AM_CONDITIONAL(REGEXP_SRC, test x$REGEXP_SRC = xtrue)
|
||||
|
||||
AC_MSG_CHECKING([whether to include the new WM Spec])
|
||||
AC_ARG_ENABLE(
|
||||
newwmspec,
|
||||
|
@ -307,9 +333,6 @@ AC_ARG_ENABLE(
|
|||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AC_MSG_CHECKING([whether to have Xmb (multibyte font, utf-8) support])
|
||||
AC_ARG_ENABLE(
|
||||
xmb,
|
||||
|
|
235
src/ClientPattern.cc
Normal file
235
src/ClientPattern.cc
Normal file
|
@ -0,0 +1,235 @@
|
|||
// ClientPattern.cc for Fluxbox Window Manager
|
||||
// Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net)
|
||||
// and Simon Bowden (rathnor at users.sourceforge.net)
|
||||
//
|
||||
// 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.
|
||||
|
||||
// $Id: ClientPattern.cc,v 1.1 2003/06/12 15:12:19 rathnor Exp $
|
||||
|
||||
#include "ClientPattern.hh"
|
||||
#include "RegExp.hh"
|
||||
#include "StringUtil.hh"
|
||||
#include "WinClient.hh"
|
||||
|
||||
//use GNU extensions
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif // _GNU_SOURCE
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/********************************************************
|
||||
* ClientPattern *
|
||||
***********/
|
||||
|
||||
ClientPattern::ClientPattern():
|
||||
m_matchlimit(0),
|
||||
m_nummatches(0) {}
|
||||
|
||||
// parse the given pattern (to end of line)
|
||||
ClientPattern::ClientPattern(const char *str):
|
||||
m_matchlimit(0),
|
||||
m_nummatches(0)
|
||||
{
|
||||
/* A rough grammar of a pattern is:
|
||||
PATTERN ::= MATCH+ LIMIT?
|
||||
MATCH ::= '(' word ')'
|
||||
| '(' propertyname '=' word ')'
|
||||
LIMIT ::= '{' number '}'
|
||||
|
||||
i.e. one or more match definitions, followed by
|
||||
an optional limit on the number of apps to match to
|
||||
|
||||
Match definitions are enclosed in parentheses, and if no
|
||||
property name is given, then CLASSNAME is assumed.
|
||||
If no limit is specified, no limit is applied (i.e. limit = infinity)
|
||||
*/
|
||||
|
||||
int had_error = 0;
|
||||
|
||||
int pos = 0;
|
||||
string match;
|
||||
int err = 1; // for starting first loop
|
||||
while (had_error == 0 && err > 0) {
|
||||
err = FbTk::StringUtil::getStringBetween(match,
|
||||
str + pos,
|
||||
'(', ')', " \t\n", true);
|
||||
if (err > 0) {
|
||||
size_t eq = match.find_first_of('=');
|
||||
if (eq == match.npos) {
|
||||
if (!addTerm(match, NAME)) {
|
||||
had_error = pos + match.find_first_of('(') + 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// need to determine the property used
|
||||
string memstr, expr;
|
||||
WinProperty prop;
|
||||
memstr.assign(match, 0, eq); // memstr = our identifier
|
||||
expr.assign(match, eq+1, match.length());
|
||||
if (strcasecmp(memstr.c_str(), "name") == 0) {
|
||||
prop = NAME;
|
||||
} else if (strcasecmp(memstr.c_str(), "class") == 0) {
|
||||
prop = CLASS;
|
||||
} else if (strcasecmp(memstr.c_str(), "title") == 0) {
|
||||
prop = TITLE;
|
||||
} else {
|
||||
had_error = pos + match.find_first_of('(') + 1;
|
||||
break;
|
||||
}
|
||||
if (!addTerm(expr, prop)) {
|
||||
had_error = pos + ((str+pos) - index(str+pos, '=')) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos += err;
|
||||
}
|
||||
}
|
||||
if (pos == 0 && had_error == 0) {
|
||||
// no match terms given, this is not allowed
|
||||
had_error = 1;
|
||||
}
|
||||
|
||||
if (had_error == 0) {
|
||||
// otherwise, we check for a number
|
||||
string number;
|
||||
err = FbTk::StringUtil::getStringBetween(number,
|
||||
str+pos,
|
||||
'{', '}');
|
||||
if (err > 0) {
|
||||
istringstream iss(number.c_str());
|
||||
iss >> m_matchlimit;
|
||||
pos+=err;
|
||||
}
|
||||
// we don't care if there isn't one
|
||||
|
||||
// there shouldn't be anything else on the line
|
||||
match = str + pos;
|
||||
err = match.find_first_not_of(" \t\n", pos);
|
||||
if ((unsigned) err != match.npos) {
|
||||
// found something, not good
|
||||
had_error = err;
|
||||
}
|
||||
}
|
||||
|
||||
if (had_error > 0) {
|
||||
m_matchlimit = had_error;
|
||||
// delete all the terms
|
||||
while (!m_terms.empty()) {
|
||||
Term * term = m_terms.back();
|
||||
delete term;
|
||||
m_terms.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClientPattern::~ClientPattern() {
|
||||
// delete all the terms
|
||||
while (!m_terms.empty()) {
|
||||
delete m_terms.back();
|
||||
m_terms.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// return a string representation of this pattern
|
||||
std::string ClientPattern::toString() const {
|
||||
string pat;
|
||||
Terms::const_iterator it = m_terms.begin();
|
||||
Terms::const_iterator it_end = m_terms.end();
|
||||
for (; it != it_end; ++it) {
|
||||
pat.append(" (");
|
||||
if ((*it)->prop == NAME) {
|
||||
// do nothing -> this is the default
|
||||
} else if ((*it)->prop == CLASS) {
|
||||
pat.append("class=");
|
||||
} else if ((*it)->prop == TITLE) {
|
||||
pat.append("title=");
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
cerr<<"WARNING: unknown window property, can't save properly"<<endl;
|
||||
#endif //DEBUG
|
||||
}
|
||||
pat.append((*it)->orig);
|
||||
pat.append(")");
|
||||
}
|
||||
|
||||
if (m_matchlimit > 0) {
|
||||
char num[20];
|
||||
sprintf(num, " {%d}", m_matchlimit);
|
||||
pat.append(num);
|
||||
}
|
||||
return pat;
|
||||
}
|
||||
|
||||
// does this client match this pattern?
|
||||
bool ClientPattern::match(const WinClient &win) const {
|
||||
if (m_matchlimit != 0 && m_nummatches >= m_matchlimit ||
|
||||
m_terms.empty())
|
||||
return false; // already matched out
|
||||
|
||||
// regmatch everything
|
||||
// currently, we use an "AND" policy for multiple terms
|
||||
// changing to OR would require minor modifications in this function only
|
||||
Terms::const_iterator it = m_terms.begin();
|
||||
Terms::const_iterator it_end = m_terms.end();
|
||||
for (; it != it_end; ++it) {
|
||||
if (!(*it)->regexp.match(getProperty((*it)->prop, win)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// add an expression to match against
|
||||
// The first argument is a regular expression, the second is the member
|
||||
// function that we wish to match against.
|
||||
bool ClientPattern::addTerm(const std::string &str, WinProperty prop) {
|
||||
|
||||
Term *term = new Term(str, true);
|
||||
term->orig = str;
|
||||
term->prop = prop;
|
||||
|
||||
if (term->regexp.error()) {
|
||||
delete term;
|
||||
return false;
|
||||
}
|
||||
m_terms.push_back(term);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const {
|
||||
switch (prop) {
|
||||
case TITLE:
|
||||
return client.getTitle();
|
||||
break;
|
||||
case CLASS:
|
||||
return client.getWMClassClass();
|
||||
break;
|
||||
case NAME:
|
||||
default:
|
||||
return client.getWMClassName();
|
||||
break;
|
||||
}
|
||||
}
|
99
src/ClientPattern.hh
Normal file
99
src/ClientPattern.hh
Normal file
|
@ -0,0 +1,99 @@
|
|||
// ClientPattern.hh for Fluxbox Window Manager
|
||||
// Copyright (c) 2002 Xavier Brouckaert
|
||||
// Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net)
|
||||
// and Simon Bowden (rathnor at users.sourceforge.net)
|
||||
//
|
||||
// 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.
|
||||
|
||||
// $Id: ClientPattern.hh,v 1.1 2003/06/12 15:12:19 rathnor Exp $
|
||||
|
||||
#ifndef CLIENTPATTERN_HH
|
||||
#define CLIENTPATTERN_HH
|
||||
|
||||
#include "RegExp.hh"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
class WinClient;
|
||||
|
||||
/**
|
||||
* This class represents a "pattern" that we can match against a
|
||||
* Window based on various properties.
|
||||
*/
|
||||
class ClientPattern {
|
||||
public:
|
||||
ClientPattern();
|
||||
// create the pattern from the given string as it would appear in the
|
||||
// apps file. the bool value returns the character at which
|
||||
// there was a parse problem, or -1.
|
||||
explicit ClientPattern(const char * str);
|
||||
|
||||
~ClientPattern();
|
||||
|
||||
// return a string representation of this pattern
|
||||
std::string toString() const;
|
||||
|
||||
enum WinProperty { TITLE, CLASS, NAME };
|
||||
|
||||
// does this client match this pattern?
|
||||
bool match(const WinClient &win) const;
|
||||
|
||||
// add an expression to match against
|
||||
// The first argument is a regular expression, the second is the member
|
||||
// function that we wish to match against.
|
||||
// returns false if the regexp wasn't valid
|
||||
bool addTerm(const std::string &str, WinProperty prop);
|
||||
|
||||
inline void addMatch() { ++m_nummatches; }
|
||||
inline void delMatch() { --m_nummatches; }
|
||||
|
||||
inline bool operator == (const WinClient &win) const {
|
||||
return match(win);
|
||||
}
|
||||
|
||||
// if there are no terms, then there is assumed to be an error
|
||||
// the column of the error is stored in m_matchlimit
|
||||
inline int error() { return (m_terms.empty())?m_matchlimit:0; }
|
||||
|
||||
std::string getProperty(WinProperty prop, const WinClient &winclient) const;
|
||||
|
||||
private:
|
||||
// This is the type of the actual pattern we want to match against
|
||||
// We have a "term" in the whole expression which is the full pattern
|
||||
// we also need to keep track of the uncompiled regular expression
|
||||
// for final output
|
||||
|
||||
struct Term {
|
||||
Term(const std::string ®str, bool full_match) :regexp(regstr, full_match){};
|
||||
std::string orig;
|
||||
RegExp regexp;
|
||||
WinProperty prop;
|
||||
};
|
||||
|
||||
// our pattern is made up of a sequence of terms
|
||||
// currently we "and" them all
|
||||
typedef std::list<Term *> Terms;
|
||||
|
||||
Terms m_terms;
|
||||
|
||||
int m_matchlimit, m_nummatches;
|
||||
};
|
||||
|
||||
#endif // CLIENTPATTERN_HH
|
|
@ -48,6 +48,10 @@ gnome_SOURCE= Gnome.hh Gnome.cc
|
|||
endif
|
||||
if REMEMBER_SRC
|
||||
REMEMBER_SOURCE= Remember.hh Remember.cc
|
||||
# For now we only want regexp if we have remember
|
||||
if REGEXP_SRC
|
||||
REGEXP_SOURCE = RegExp.hh RegExp.cc ClientPattern.hh ClientPattern.cc
|
||||
endif
|
||||
endif
|
||||
|
||||
fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \
|
||||
|
@ -76,7 +80,7 @@ fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \
|
|||
IntResMenuItem.hh IntResMenuItem.cc FbMenu.hh \
|
||||
WinClient.hh WinClient.cc \
|
||||
Xinerama.hh \
|
||||
${REMEMBER_SOURCE}
|
||||
${REMEMBER_SOURCE} ${REGEXP_SOURCE}
|
||||
|
||||
|
||||
LDADD=FbTk/libFbTk.a
|
||||
|
|
94
src/RegExp.cc
Normal file
94
src/RegExp.cc
Normal file
|
@ -0,0 +1,94 @@
|
|||
// RegExp.cc for Fluxbox Window Manager
|
||||
// Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net)
|
||||
// and Simon Bowden (rathnor at users.sourceforge.net)
|
||||
//
|
||||
// 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.
|
||||
|
||||
// $Id: RegExp.cc,v 1.1 2003/06/12 15:12:19 rathnor Exp $
|
||||
|
||||
#include "RegExp.hh"
|
||||
|
||||
//use GNU extensions
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif // _GNU_SOURCE
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/********************************************************
|
||||
* RegExp *
|
||||
***********/
|
||||
|
||||
// full_match is to say if we match on this regexp using the full string
|
||||
// or just a substring. Substrings aren't supported if not HAVE_REGEXP
|
||||
RegExp::RegExp(const std::string &str, bool full_match):
|
||||
#ifdef USE_REGEXP
|
||||
m_regex(0) {
|
||||
string match;
|
||||
if (full_match) {
|
||||
match = "^";
|
||||
match.append(str);
|
||||
match.append("$");
|
||||
} else {
|
||||
match = str;
|
||||
}
|
||||
|
||||
m_regex = new regex_t;
|
||||
int ret = regcomp(m_regex, match.c_str(), REG_NOSUB | REG_EXTENDED);
|
||||
if (ret != 0) {
|
||||
char *errstr = 0;
|
||||
// gives us the length of the string
|
||||
unsigned int size = regerror(ret, m_regex, errstr, 0);
|
||||
errstr = new char[size];
|
||||
|
||||
regerror(ret, m_regex, errstr, size);
|
||||
cerr<<"Error parsing regular expression: "<<errstr<<endl;
|
||||
delete [] errstr;
|
||||
delete m_regex; // I don't think I regfree a failed compile?
|
||||
m_regex = 0;
|
||||
}
|
||||
}
|
||||
#else // notdef USE_REGEXP
|
||||
m_str(str) {}
|
||||
#endif // USE_REGEXP
|
||||
|
||||
RegExp::~RegExp() {
|
||||
#ifdef USE_REGEXP
|
||||
if (m_regex != 0) {
|
||||
regfree(m_regex);
|
||||
delete m_regex;
|
||||
}
|
||||
#endif // USE_REGEXP
|
||||
}
|
||||
|
||||
bool RegExp::match(const std::string &str) {
|
||||
#ifdef USE_REGEXP
|
||||
if (m_regex)
|
||||
return (regexec(m_regex, str.c_str(), 0, 0, 0) == 0);
|
||||
else
|
||||
return false;
|
||||
#else // notdef USE_REGEXP
|
||||
return (m_str == str);
|
||||
#endif // USE_REGEXP
|
||||
}
|
||||
|
66
src/RegExp.hh
Normal file
66
src/RegExp.hh
Normal file
|
@ -0,0 +1,66 @@
|
|||
// RegExp.hh for Fluxbox Window Manager
|
||||
// Copyright (c) 2002 Xavier Brouckaert
|
||||
// Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net)
|
||||
// and Simon Bowden (rathnor at users.sourceforge.net)
|
||||
//
|
||||
// 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.
|
||||
|
||||
// $Id: RegExp.hh,v 1.1 2003/06/12 15:12:19 rathnor Exp $
|
||||
|
||||
#ifndef REGEXP_HH
|
||||
#define REGEXP_HH
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
* If USE_REGEXP isn't defined, then we match just using simple string equality
|
||||
*/
|
||||
|
||||
#ifdef USE_REGEXP
|
||||
#include <regex.h>
|
||||
#include <sys/types.h>
|
||||
#endif // USE_REGEXP
|
||||
|
||||
class WinClient;
|
||||
|
||||
class RegExp {
|
||||
public:
|
||||
RegExp(const std::string &str, bool full_match = true);
|
||||
~RegExp();
|
||||
|
||||
bool match(const std::string &str);
|
||||
|
||||
#ifdef USE_REGEXP
|
||||
inline bool error() { return m_regex == 0; }
|
||||
#else // notdef USE_REGEXP
|
||||
inline bool error() { return m_str == ""; }
|
||||
#endif // USE_REGEXP
|
||||
|
||||
private:
|
||||
#ifdef USE_REGEXP
|
||||
regex_t* m_regex;
|
||||
#else // notdef USE_REGEXP
|
||||
std::string m_str;
|
||||
#endif // USE_REGEXP
|
||||
|
||||
};
|
||||
|
||||
#endif // REGEXP_HH
|
199
src/Remember.cc
199
src/Remember.cc
|
@ -21,9 +21,10 @@
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// $Id: Remember.cc,v 1.23 2003/06/06 14:07:22 rathnor Exp $
|
||||
// $Id: Remember.cc,v 1.24 2003/06/12 15:12:19 rathnor Exp $
|
||||
|
||||
#include "Remember.hh"
|
||||
#include "ClientPattern.hh"
|
||||
#include "StringUtil.hh"
|
||||
#include "Screen.hh"
|
||||
#include "Window.hh"
|
||||
|
@ -125,32 +126,7 @@ FbTk::Menu *createRememberMenu(Remember &remember, FluxboxWindow &win) {
|
|||
return menu;
|
||||
};
|
||||
|
||||
std::string getWMClass(Window w) {
|
||||
XClassHint ch;
|
||||
|
||||
if (XGetClassHint(FbTk::App::instance()->display(), w, &ch) == 0) {
|
||||
cerr<<"Failed to read class hint!"<<endl;
|
||||
return "";
|
||||
} else {
|
||||
string instance_name;
|
||||
if (ch.res_name != 0) {
|
||||
instance_name = const_cast<char *>(ch.res_name);
|
||||
XFree(ch.res_name);
|
||||
} else
|
||||
instance_name = "";
|
||||
|
||||
if (ch.res_class != 0) {
|
||||
//m_class_name = const_cast<char *>(ch.res_class);
|
||||
XFree(ch.res_class);
|
||||
} else {
|
||||
//m_class_name = "";
|
||||
}
|
||||
|
||||
return instance_name;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}; // end anonymous namespace
|
||||
|
||||
Application::Application() {
|
||||
workspace_remember =
|
||||
|
@ -165,35 +141,56 @@ Application::Application() {
|
|||
save_on_close_remember = false;
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
* Remember *
|
||||
************/
|
||||
|
||||
Remember::Remember() {
|
||||
load();
|
||||
}
|
||||
|
||||
Application* Remember::add(const char* app_name) {
|
||||
if (!app_name)
|
||||
return 0;
|
||||
Application* a = new Application();
|
||||
apps[app_name] = a;
|
||||
return a;
|
||||
Remember::~Remember() {
|
||||
// free our resources
|
||||
|
||||
// the patterns free the "Application"s
|
||||
// the client mapping shouldn't need cleaning
|
||||
Patterns::iterator it;
|
||||
while (!m_pats.empty()) {
|
||||
it = m_pats.begin();
|
||||
delete it->first; // ClientPattern
|
||||
delete it->second; // Application
|
||||
m_pats.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
Application* Remember::find(WinClient &winclient) {
|
||||
return find(getWMClass(winclient.window()).c_str());
|
||||
// if it is already associated with a application, return that one
|
||||
// otherwise, check it against every pattern that we've got
|
||||
Clients::iterator wc_it = m_clients.find(&winclient);
|
||||
if (wc_it != m_clients.end())
|
||||
return wc_it->second;
|
||||
else {
|
||||
Patterns::iterator it = m_pats.begin();
|
||||
for (; it != m_pats.end(); it++)
|
||||
if (it->first->match(winclient)) {
|
||||
it->first->addMatch();
|
||||
m_clients[&winclient] = it->second;
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// oh well, no matches
|
||||
return 0;
|
||||
}
|
||||
|
||||
Application* Remember::add(WinClient &winclient) {
|
||||
return add(getWMClass(winclient.window()).c_str());
|
||||
}
|
||||
|
||||
|
||||
Application* Remember::find(const char* app_name) {
|
||||
if (!app_name)
|
||||
return 0;
|
||||
Apps::iterator i = apps.find(app_name);
|
||||
if (i != apps.end())
|
||||
return i->second;
|
||||
else
|
||||
return 0;
|
||||
Application * Remember::add(WinClient &winclient) {
|
||||
ClientPattern *p = new ClientPattern();
|
||||
Application *app = new Application();
|
||||
// by default, we match against the WMClass of a window.
|
||||
p->addTerm(p->getProperty(ClientPattern::NAME, winclient), ClientPattern::NAME);
|
||||
m_clients[&winclient] = app;
|
||||
p->addMatch();
|
||||
m_pats.push_back(make_pair(p, app));
|
||||
return app;
|
||||
}
|
||||
|
||||
int Remember::parseApp(ifstream &file, Application &app) {
|
||||
|
@ -315,31 +312,24 @@ void Remember::load() {
|
|||
if (line[0] == '#')
|
||||
continue;
|
||||
string key;
|
||||
int pos=0;
|
||||
int err = FbTk::StringUtil::getStringBetween(key,
|
||||
int err=0;
|
||||
int pos = FbTk::StringUtil::getStringBetween(key,
|
||||
line.c_str(),
|
||||
'[', ']');
|
||||
|
||||
if (err > 0 && key == "app") {
|
||||
pos += err;
|
||||
string label;
|
||||
err = FbTk::StringUtil::getStringBetween(label,
|
||||
line.c_str()+pos,
|
||||
'(', ')');
|
||||
if (err>0) {
|
||||
Application *app = 0;
|
||||
Apps::iterator i = apps.find(label);
|
||||
if (i == apps.end()) {
|
||||
app = new Application();
|
||||
apps[label] = app;
|
||||
} else
|
||||
app = i->second;
|
||||
if (pos > 0 && key == "app") {
|
||||
ClientPattern *pat = new ClientPattern(line.c_str() + pos);
|
||||
if ((err = pat->error()) == 0) {
|
||||
Application *app = new Application();
|
||||
m_pats.push_back(make_pair(pat, app));
|
||||
row += parseApp(apps_file, *app);
|
||||
} else
|
||||
cerr<<"Error1 in apps file. Line("<<row<<")"<<endl;
|
||||
} else {
|
||||
cerr<<"Error reading apps file at line "<<row<<", column "<<(err+pos)<<"."<<endl;
|
||||
delete pat; // since it didn't work
|
||||
}
|
||||
} else
|
||||
cerr<<"Error2 in apps file. Line("<<row<<")"<<endl;
|
||||
|
||||
cerr<<"Error in apps file on line "<<row<<"."<<endl;
|
||||
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
|
@ -358,28 +348,28 @@ void Remember::save() {
|
|||
string apps_string;
|
||||
Fluxbox::instance()->getDefaultDataFilename("apps", apps_string);
|
||||
ofstream apps_file(apps_string.c_str());
|
||||
Apps::iterator it = apps.begin();
|
||||
Apps::iterator it_end = apps.end();
|
||||
Patterns::iterator it = m_pats.begin();
|
||||
Patterns::iterator it_end = m_pats.end();
|
||||
for (; it != it_end; ++it) {
|
||||
apps_file << "[app] (" << it->first << ")" << endl;
|
||||
Application *a = it->second;
|
||||
if (a->workspace_remember) {
|
||||
apps_file << " [Workspace]\t{" << a->workspace << "}" << endl;
|
||||
apps_file << "[app]"<<it->first->toString()<<endl;
|
||||
Application &a = *it->second;
|
||||
if (a.workspace_remember) {
|
||||
apps_file << " [Workspace]\t{" << a.workspace << "}" << endl;
|
||||
}
|
||||
if (a->dimensions_remember) {
|
||||
apps_file << " [Dimensions]\t{" << a->w << " " << a->h << "}" << endl;
|
||||
if (a.dimensions_remember) {
|
||||
apps_file << " [Dimensions]\t{" << a.w << " " << a.h << "}" << endl;
|
||||
}
|
||||
if (a->position_remember) {
|
||||
apps_file << " [Position]\t{" << a->x << " " << a->y << "}" << endl;
|
||||
if (a.position_remember) {
|
||||
apps_file << " [Position]\t{" << a.x << " " << a.y << "}" << endl;
|
||||
}
|
||||
if (a->shadedstate_remember) {
|
||||
apps_file << " [Shaded]\t{" << ((a->shadedstate)?"yes":"no") << "}" << endl;
|
||||
if (a.shadedstate_remember) {
|
||||
apps_file << " [Shaded]\t{" << ((a.shadedstate)?"yes":"no") << "}" << endl;
|
||||
}
|
||||
if (a->tabstate_remember) {
|
||||
apps_file << " [Tab]\t\t{" << ((a->tabstate)?"yes":"no") << "}" << endl;
|
||||
if (a.tabstate_remember) {
|
||||
apps_file << " [Tab]\t\t{" << ((a.tabstate)?"yes":"no") << "}" << endl;
|
||||
}
|
||||
if (a->decostate_remember) {
|
||||
switch (a->decostate) {
|
||||
if (a.decostate_remember) {
|
||||
switch (a.decostate) {
|
||||
case (0) :
|
||||
apps_file << " [Deco]\t{NONE}" << endl;
|
||||
break;
|
||||
|
@ -401,21 +391,21 @@ void Remember::save() {
|
|||
apps_file << " [Deco]\t{BORDER}" << endl;
|
||||
break;
|
||||
default:
|
||||
apps_file << " [Deco]\t{0x"<<hex<<a->decostate<<dec<<"}"<<endl;
|
||||
apps_file << " [Deco]\t{0x"<<hex<<a.decostate<<dec<<"}"<<endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (a->stuckstate_remember) {
|
||||
apps_file << " [Sticky]\t{" << ((a->stuckstate)?"yes":"no") << "}" << endl;
|
||||
if (a.stuckstate_remember) {
|
||||
apps_file << " [Sticky]\t{" << ((a.stuckstate)?"yes":"no") << "}" << endl;
|
||||
}
|
||||
if (a->jumpworkspace_remember) {
|
||||
apps_file << " [Jump]\t{" << ((a->jumpworkspace)?"yes":"no") << "}" << endl;
|
||||
if (a.jumpworkspace_remember) {
|
||||
apps_file << " [Jump]\t{" << ((a.jumpworkspace)?"yes":"no") << "}" << endl;
|
||||
}
|
||||
if (a->layer_remember) {
|
||||
apps_file << " [Layer]\t{" << a->layer << "}" << endl;
|
||||
if (a.layer_remember) {
|
||||
apps_file << " [Layer]\t{" << a.layer << "}" << endl;
|
||||
}
|
||||
if (a->save_on_close_remember) {
|
||||
apps_file << " [Close]\t{" << ((a->save_on_close)?"yes":"no") << "}" << endl;
|
||||
if (a.save_on_close_remember) {
|
||||
apps_file << " [Close]\t{" << ((a.save_on_close)?"yes":"no") << "}" << endl;
|
||||
}
|
||||
apps_file << "[end]" << endl;
|
||||
}
|
||||
|
@ -563,6 +553,7 @@ void Remember::setupWindow(FluxboxWindow &win) {
|
|||
if (winclient.transientFor()) {
|
||||
// still put something in the menu so people don't get confused
|
||||
// so, we add a disabled item...
|
||||
// TODO: nls
|
||||
FbTk::MenuItem *item = new FbTk::MenuItem("Remember...");
|
||||
item->setEnabled(false);
|
||||
win.menu().insert(item, menupos);
|
||||
|
@ -623,10 +614,15 @@ void Remember::setupWindow(FluxboxWindow &win) {
|
|||
void Remember::updateWindowClose(FluxboxWindow &win) {
|
||||
// This doesn't work at present since fluxbox.cc is missing the windowclose stuff.
|
||||
// I don't trust it (particularly winClient()) while this is the case
|
||||
|
||||
return;
|
||||
|
||||
WinClient &winclient = win.winClient();
|
||||
Application *app = find(winclient);
|
||||
Clients::iterator wc_it = m_clients.find(&win.winClient());
|
||||
|
||||
if (wc_it != m_clients.end())
|
||||
m_clients.erase(wc_it);
|
||||
|
||||
if (!app || !(app->save_on_close_remember && app->save_on_close))
|
||||
return;
|
||||
|
@ -636,23 +632,6 @@ void Remember::updateWindowClose(FluxboxWindow &win) {
|
|||
rememberAttrib(winclient, (Attribute) attrib);
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (app->workspace_remember)
|
||||
app->rememberWorkspace(win.workspaceNumber());
|
||||
if (app->dimensions_remember)
|
||||
app->rememberDimensions(win.width(), win.height());
|
||||
if (app->position_remember)
|
||||
app->rememberPosition(win.x(), win.y());
|
||||
if (app->shadedstate_remember)
|
||||
app->rememberShadedstate(win.isShaded());
|
||||
// external tabs off atm
|
||||
//if (app->tabstate_remember) ...
|
||||
if (app->decostate_remember)
|
||||
app->rememberDecostate(win.decorationMask());
|
||||
if (app->stuckstate_remember)
|
||||
app->rememberStuckstate(win.isStuck());
|
||||
if (app->jumpworkspace_remember)
|
||||
app->rememberJumpworkspace(true);
|
||||
*/
|
||||
|
||||
save();
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// $Id: Remember.hh,v 1.6 2003/06/05 13:33:27 fluxgen Exp $
|
||||
// $Id: Remember.hh,v 1.7 2003/06/12 15:12:19 rathnor Exp $
|
||||
|
||||
/* Based on the original "Remember patch" by Xavier Brouckaert */
|
||||
|
||||
|
@ -32,7 +32,14 @@
|
|||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class FluxboxWindow;
|
||||
class BScreen;
|
||||
class WinClient;
|
||||
class ClientPattern;
|
||||
|
||||
class Application {
|
||||
public:
|
||||
|
@ -99,13 +106,9 @@ public:
|
|||
|
||||
bool save_on_close_remember;
|
||||
bool save_on_close;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class FluxboxWindow;
|
||||
class BScreen;
|
||||
class WinClient;
|
||||
|
||||
/**
|
||||
* Class Remember is an atomhandler to avoid interfering with
|
||||
* the main code as much as possible, since we hope that one day
|
||||
|
@ -132,13 +135,21 @@ public:
|
|||
REM_LASTATTRIB // not actually used
|
||||
};
|
||||
|
||||
typedef std::map<std::string, Application *> Apps;
|
||||
// a "pattern" to the relevant app
|
||||
// each app exists ONLY for that pattern.
|
||||
// And we need to keep a list of pairs as we want to keep the
|
||||
// applications in the same order as they will be in the apps file
|
||||
typedef std::list< std::pair<ClientPattern *, Application *> > Patterns;
|
||||
|
||||
// We keep track of which app is assigned to a winclient
|
||||
// particularly useful to update counters etc on windowclose
|
||||
typedef std::map<WinClient *, Application *> Clients;
|
||||
|
||||
Remember();
|
||||
~Remember();
|
||||
|
||||
Application* find(WinClient &winclient);
|
||||
Application* find(const char* app_name);
|
||||
Application* add(WinClient &winclient);
|
||||
Application* add(const char* app_name);
|
||||
|
||||
void load();
|
||||
void save();
|
||||
|
@ -176,7 +187,8 @@ private:
|
|||
|
||||
// returns number of lines read
|
||||
int parseApp(std::ifstream &file, Application &app);
|
||||
Apps apps;
|
||||
Patterns m_pats;
|
||||
Clients m_clients;
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue