From e139cbb0283f7480fc26c58dc3f8a48e69011eab Mon Sep 17 00:00:00 2001 From: rathnor <rathnor> Date: Thu, 12 Jun 2003 15:12:19 +0000 Subject: add regular expression support in remember capabilities see ChangeLog for details --- ChangeLog | 21 +++++ configure.in | 29 ++++++- src/ClientPattern.cc | 235 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ClientPattern.hh | 99 ++++++++++++++++++++++ src/Makefile.am | 6 +- src/RegExp.cc | 94 +++++++++++++++++++++ src/RegExp.hh | 66 +++++++++++++++ src/Remember.cc | 199 +++++++++++++++++++------------------------ src/Remember.hh | 32 ++++--- 9 files changed, 657 insertions(+), 124 deletions(-) create mode 100644 src/ClientPattern.cc create mode 100644 src/ClientPattern.hh create mode 100644 src/RegExp.cc create mode 100644 src/RegExp.hh diff --git a/ChangeLog b/ChangeLog index a5e7369..0532bef 100644 --- a/ChangeLog +++ b/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) diff --git a/configure.in b/configure.in index a301393..a521abd 100644 --- a/configure.in +++ b/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, diff --git a/src/ClientPattern.cc b/src/ClientPattern.cc new file mode 100644 index 0000000..7ad3a54 --- /dev/null +++ b/src/ClientPattern.cc @@ -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; + } +} diff --git a/src/ClientPattern.hh b/src/ClientPattern.hh new file mode 100644 index 0000000..00a3127 --- /dev/null +++ b/src/ClientPattern.hh @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 0b2892a..d9288f7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/RegExp.cc b/src/RegExp.cc new file mode 100644 index 0000000..2e991da --- /dev/null +++ b/src/RegExp.cc @@ -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 +} + diff --git a/src/RegExp.hh b/src/RegExp.hh new file mode 100644 index 0000000..9591bcb --- /dev/null +++ b/src/RegExp.hh @@ -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 diff --git a/src/Remember.cc b/src/Remember.cc index 5480d20..a31843b 100644 --- a/src/Remember.cc +++ b/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()); -} - -Application* Remember::add(WinClient &winclient) { - return add(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::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(); } diff --git a/src/Remember.hh b/src/Remember.hh index 8058c15..d3a30e0 100644 --- a/src/Remember.hh +++ b/src/Remember.hh @@ -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,12 +106,8 @@ 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 @@ -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; }; -- cgit v0.11.2