news download themes documentation links










ClientPattern.cc

00001 // ClientPattern.cc for Fluxbox Window Manager
00002 // Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net)
00003 //                and Simon Bowden    (rathnor at users.sourceforge.net)
00004 //
00005 // Permission is hereby granted, free of charge, to any person obtaining a
00006 // copy of this software and associated documentation files (the "Software"),
00007 // to deal in the Software without restriction, including without limitation
00008 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
00009 // and/or sell copies of the Software, and to permit persons to whom the
00010 // Software is furnished to do so, subject to the following conditions:
00011 //
00012 // The above copyright notice and this permission notice shall be included in
00013 // all copies or substantial portions of the Software.
00014 //
00015 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00018 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00019 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00020 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00021 // DEALINGS IN THE SOFTWARE.
00022 
00023 // $Id: ClientPattern.cc,v 1.6 2003/12/17 01:19:39 fluxgen Exp $
00024 
00025 #include "ClientPattern.hh"
00026 #include "RegExp.hh"
00027 #include "StringUtil.hh"
00028 #include "WinClient.hh"
00029 
00030 // use GNU extensions
00031 #ifndef _GNU_SOURCE
00032 #define _GNU_SOURCE
00033 #endif // _GNU_SOURCE
00034 
00035 
00036 #include <iostream>
00037 #include <fstream>
00038 #include <string>
00039 #include <memory>
00040 #include <cstdio>
00041 
00042 // needed as well for index on some systems (e.g. solaris)
00043 #include <strings.h> 
00044 
00045 #ifdef HAVE_CONFIG_H
00046 #include "config.h"
00047 #endif // HAVE_CONFIG_H
00048 
00049 #ifdef HAVE_SSTREAM
00050 #include <sstream>
00051 #define FB_istringstream istringstream
00052 #elif HAVE_STRSTREAM 
00053 #include <strstream>
00054 #define FB_istringstream istrstream
00055 #else
00056 #error "You dont have sstream or strstream headers!"
00057 #endif // HAVE_STRSTREAM
00058 
00059 
00060 using namespace std;
00061 
00062 ClientPattern::ClientPattern():
00063     m_matchlimit(0),
00064     m_nummatches(0) {}
00065 
00066 // parse the given pattern (to end of line)
00067 ClientPattern::ClientPattern(const char *str):
00068     m_matchlimit(0),
00069     m_nummatches(0)
00070 {
00071     /* A rough grammar of a pattern is:
00072        PATTERN ::= MATCH+ LIMIT?
00073        MATCH ::= '(' word ')' 
00074                  | '(' propertyname '=' word ')'
00075        LIMIT ::= '{' number '}'
00076                  
00077        i.e. one or more match definitions, followed by
00078             an optional limit on the number of apps to match to
00079        
00080        Match definitions are enclosed in parentheses, and if no
00081        property name is given, then CLASSNAME is assumed.
00082        If no limit is specified, no limit is applied (i.e. limit = infinity)
00083     */
00084 
00085     int had_error = 0;
00086 
00087     int pos = 0;
00088     string match;
00089     int err = 1; // for starting first loop
00090     while (had_error == 0 && err > 0) {
00091         err = FbTk::StringUtil::getStringBetween(match, 
00092                                                  str + pos,
00093                                                  '(', ')', " \t\n", true);
00094         if (err > 0) {
00095             size_t eq = match.find_first_of('=');
00096             if (eq == match.npos) {
00097                 if (!addTerm(match, NAME)) {
00098                     had_error = pos + match.find_first_of('(') + 1;
00099                     break;
00100                 }
00101             } else {
00102                 // need to determine the property used
00103                 string memstr, expr;
00104                 WinProperty prop;
00105                 memstr.assign(match, 0, eq); // memstr = our identifier
00106                 expr.assign(match, eq+1, match.length());
00107                 if (strcasecmp(memstr.c_str(), "name") == 0) {
00108                     prop = NAME;
00109                 } else if (strcasecmp(memstr.c_str(), "class") == 0) {
00110                     prop = CLASS;
00111                 } else if (strcasecmp(memstr.c_str(), "title") == 0) {
00112                     prop = TITLE;
00113                 } else {
00114                     had_error = pos + match.find_first_of('(') + 1;
00115                     break;
00116                 }
00117                 if (!addTerm(expr, prop)) {
00118                     had_error = pos + ((str+pos) - index(str+pos, '=')) + 1;
00119                     break;
00120                 }
00121             }
00122             pos += err;
00123         } 
00124     }
00125     if (pos == 0 && had_error == 0) {
00126         // no match terms given, this is not allowed
00127         had_error = 1;
00128     }
00129 
00130     if (had_error == 0) {
00131         // otherwise, we check for a number
00132         string number;
00133         err = FbTk::StringUtil::getStringBetween(number, 
00134                                              str+pos,
00135                                              '{', '}');
00136         if (err > 0) {
00137             FB_istringstream iss(number.c_str());
00138             iss >> m_matchlimit;
00139             pos+=err;
00140         }
00141         // we don't care if there isn't one
00142         
00143         // there shouldn't be anything else on the line
00144         match = str + pos;
00145         err = match.find_first_not_of(" \t\n", pos);
00146         if ((unsigned) err != match.npos) {
00147             // found something, not good
00148             had_error = err;
00149         }
00150     }
00151 
00152     if (had_error > 0) {
00153         m_matchlimit = had_error;
00154         // delete all the terms
00155         while (!m_terms.empty()) {
00156             Term * term = m_terms.back();
00157             delete term;
00158             m_terms.pop_back();
00159         }
00160     }
00161 } 
00162 
00163 ClientPattern::~ClientPattern() {
00164     // delete all the terms
00165     while (!m_terms.empty()) {
00166         delete m_terms.back();
00167         m_terms.pop_back();
00168     }
00169 }
00170 
00171 // return a string representation of this pattern
00172 std::string ClientPattern::toString() const {
00173     string pat;
00174     Terms::const_iterator it = m_terms.begin();
00175     Terms::const_iterator it_end = m_terms.end();
00176     for (; it != it_end; ++it) {
00177         pat.append(" (");
00178 
00179         switch ((*it)->prop) {
00180         case NAME:
00181             // do nothing -> this is the default
00182             break;
00183         case CLASS:
00184             pat.append("class=");
00185             break;
00186         case TITLE:
00187             pat.append("title=");
00188             break;
00189         }
00190 
00191         pat.append((*it)->orig);
00192         pat.append(")");
00193     }
00194 
00195     if (m_matchlimit > 0) {
00196         char num[20];
00197         sprintf(num, " {%d}", m_matchlimit);
00198         pat.append(num);
00199     }
00200     return pat;
00201 }
00202 
00203 // does this client match this pattern?
00204 bool ClientPattern::match(const WinClient &win) const {
00205     if (m_matchlimit != 0 && m_nummatches >= m_matchlimit || 
00206         m_terms.empty())
00207         return false; // already matched out
00208 
00209     // regmatch everything
00210     // currently, we use an "AND" policy for multiple terms
00211     // changing to OR would require minor modifications in this function only
00212     Terms::const_iterator it = m_terms.begin();
00213     Terms::const_iterator it_end = m_terms.end();
00214     for (; it != it_end; ++it) {
00215         if (!(*it)->regexp.match(getProperty((*it)->prop, win)))
00216             return false;
00217     }
00218     return true;
00219 }
00220 
00221 // add an expression to match against
00222 // The first argument is a regular expression, the second is the member
00223 // function that we wish to match against.
00224 bool ClientPattern::addTerm(const std::string &str, WinProperty prop) {
00225 
00226     Term *term = new Term(str, true);
00227     term->orig = str;
00228     term->prop = prop;
00229 
00230     if (term->regexp.error()) {
00231         delete term;
00232         return false;
00233     }
00234     m_terms.push_back(term);
00235     return true;
00236 }
00237 
00238 std::string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const {
00239     switch (prop) {
00240     case TITLE:
00241         return client.title();
00242         break;
00243     case CLASS:
00244         return client.getWMClassClass();
00245         break;
00246     case NAME:
00247         return client.getWMClassName();
00248         break;
00249     }
00250     return client.getWMClassName();
00251 }

Fluxbox CVS-Jan-2003




      



Got comments about the page? Send them to webmaster.
If you have general Fluxbox related questions ask them on our irc channel or mailing lists.

Show Source








Designed by aLEczapKA