• Main Page
  • Classes
  • Files
  • File List

CConfig.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2012 Bolton Software Ltd.
00004  * Copyright (C) 2002 Chris Schoeneman
00005  * 
00006  * This package is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * found in the file COPYING that should have accompanied this file.
00009  * 
00010  * This package is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include "CConfig.h"
00020 #include "CServer.h"
00021 #include "CKeyMap.h"
00022 #include "KeyTypes.h"
00023 #include "XSocket.h"
00024 #include "stdistream.h"
00025 #include "stdostream.h"
00026 #include <cstdlib>
00027 
00028 //
00029 // CConfig
00030 //
00031 
00032 CConfig::CConfig() : m_hasLockToScreenAction(false)
00033 {
00034     // do nothing
00035 }
00036 
00037 CConfig::~CConfig()
00038 {
00039     // do nothing
00040 }
00041 
00042 bool
00043 CConfig::addScreen(const CString& name)
00044 {
00045     // alias name must not exist
00046     if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) {
00047         return false;
00048     }
00049 
00050     // add cell
00051     m_map.insert(std::make_pair(name, CCell()));
00052 
00053     // add name
00054     m_nameToCanonicalName.insert(std::make_pair(name, name));
00055 
00056     return true;
00057 }
00058 
00059 bool
00060 CConfig::renameScreen(const CString& oldName,
00061                             const CString& newName)
00062 {
00063     // get canonical name and find cell
00064     CString oldCanonical = getCanonicalName(oldName);
00065     CCellMap::iterator index = m_map.find(oldCanonical);
00066     if (index == m_map.end()) {
00067         return false;
00068     }
00069 
00070     // accept if names are equal but replace with new name to maintain
00071     // case.  otherwise, the new name must not exist.
00072     if (!CStringUtil::CaselessCmp::equal(oldName, newName) &&
00073         m_nameToCanonicalName.find(newName) != m_nameToCanonicalName.end()) {
00074         return false;
00075     }
00076 
00077     // update cell
00078     CCell tmpCell = index->second;
00079     m_map.erase(index);
00080     m_map.insert(std::make_pair(newName, tmpCell));
00081 
00082     // update name
00083     m_nameToCanonicalName.erase(oldCanonical);
00084     m_nameToCanonicalName.insert(std::make_pair(newName, newName));
00085 
00086     // update connections
00087     CName oldNameObj(this, oldName);
00088     for (index = m_map.begin(); index != m_map.end(); ++index) {
00089         index->second.rename(oldNameObj, newName);
00090     }
00091 
00092     // update alias targets
00093     if (CStringUtil::CaselessCmp::equal(oldName, oldCanonical)) {
00094         for (CNameMap::iterator iter = m_nameToCanonicalName.begin();
00095                             iter != m_nameToCanonicalName.end(); ++iter) {
00096             if (CStringUtil::CaselessCmp::equal(
00097                             iter->second, oldCanonical)) {
00098                 iter->second = newName;
00099             }
00100         }
00101     }
00102 
00103     return true;
00104 }
00105 
00106 void
00107 CConfig::removeScreen(const CString& name)
00108 {
00109     // get canonical name and find cell
00110     CString canonical = getCanonicalName(name);
00111     CCellMap::iterator index = m_map.find(canonical);
00112     if (index == m_map.end()) {
00113         return;
00114     }
00115 
00116     // remove from map
00117     m_map.erase(index);
00118 
00119     // disconnect
00120     CName nameObj(this, name);
00121     for (index = m_map.begin(); index != m_map.end(); ++index) {
00122         index->second.remove(nameObj);
00123     }
00124 
00125     // remove aliases (and canonical name)
00126     for (CNameMap::iterator iter = m_nameToCanonicalName.begin();
00127                                 iter != m_nameToCanonicalName.end(); ) {
00128         if (iter->second == canonical) {
00129             m_nameToCanonicalName.erase(iter++);
00130         }
00131         else {
00132             ++index;
00133         }
00134     }
00135 }
00136 
00137 void
00138 CConfig::removeAllScreens()
00139 {
00140     m_map.clear();
00141     m_nameToCanonicalName.clear();
00142 }
00143 
00144 bool
00145 CConfig::addAlias(const CString& canonical, const CString& alias)
00146 {
00147     // alias name must not exist
00148     if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) {
00149         return false;
00150     }
00151 
00152     // canonical name must be known
00153     if (m_map.find(canonical) == m_map.end()) {
00154         return false;
00155     }
00156 
00157     // insert alias
00158     m_nameToCanonicalName.insert(std::make_pair(alias, canonical));
00159 
00160     return true;
00161 }
00162 
00163 bool
00164 CConfig::removeAlias(const CString& alias)
00165 {
00166     // must not be a canonical name
00167     if (m_map.find(alias) != m_map.end()) {
00168         return false;
00169     }
00170 
00171     // find alias
00172     CNameMap::iterator index = m_nameToCanonicalName.find(alias);
00173     if (index == m_nameToCanonicalName.end()) {
00174         return false;
00175     }
00176 
00177     // remove alias
00178     m_nameToCanonicalName.erase(index);
00179 
00180     return true;
00181 }
00182 
00183 bool
00184 CConfig::removeAliases(const CString& canonical)
00185 {
00186     // must be a canonical name
00187     if (m_map.find(canonical) == m_map.end()) {
00188         return false;
00189     }
00190 
00191     // find and removing matching aliases
00192     for (CNameMap::iterator index = m_nameToCanonicalName.begin();
00193                             index != m_nameToCanonicalName.end(); ) {
00194         if (index->second == canonical && index->first != canonical) {
00195             m_nameToCanonicalName.erase(index++);
00196         }
00197         else {
00198             ++index;
00199         }
00200     }
00201 
00202     return true;
00203 }
00204 
00205 void
00206 CConfig::removeAllAliases()
00207 {
00208     // remove all names
00209     m_nameToCanonicalName.clear();
00210 
00211     // put the canonical names back in
00212     for (CCellMap::iterator index = m_map.begin();
00213                                 index != m_map.end(); ++index) {
00214         m_nameToCanonicalName.insert(
00215                                 std::make_pair(index->first, index->first));
00216     }
00217 }
00218 
00219 bool
00220 CConfig::connect(const CString& srcName,
00221                 EDirection srcSide,
00222                 float srcStart, float srcEnd,
00223                 const CString& dstName,
00224                 float dstStart, float dstEnd)
00225 {
00226     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00227 
00228     // find source cell
00229     CCellMap::iterator index = m_map.find(getCanonicalName(srcName));
00230     if (index == m_map.end()) {
00231         return false;
00232     }
00233 
00234     // add link
00235     CCellEdge srcEdge(srcSide, CInterval(srcStart, srcEnd));
00236     CCellEdge dstEdge(dstName, srcSide, CInterval(dstStart, dstEnd));
00237     return index->second.add(srcEdge, dstEdge);
00238 }
00239 
00240 bool
00241 CConfig::disconnect(const CString& srcName, EDirection srcSide)
00242 {
00243     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00244 
00245     // find source cell
00246     CCellMap::iterator index = m_map.find(srcName);
00247     if (index == m_map.end()) {
00248         return false;
00249     }
00250 
00251     // disconnect side
00252     index->second.remove(srcSide);
00253 
00254     return true;
00255 }
00256 
00257 bool
00258 CConfig::disconnect(const CString& srcName, EDirection srcSide, float position)
00259 {
00260     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00261 
00262     // find source cell
00263     CCellMap::iterator index = m_map.find(srcName);
00264     if (index == m_map.end()) {
00265         return false;
00266     }
00267 
00268     // disconnect side
00269     index->second.remove(srcSide, position);
00270 
00271     return true;
00272 }
00273 
00274 void
00275 CConfig::setSynergyAddress(const CNetworkAddress& addr)
00276 {
00277     m_synergyAddress = addr;
00278 }
00279 
00280 bool
00281 CConfig::addOption(const CString& name, OptionID option, OptionValue value)
00282 {
00283     // find options
00284     CScreenOptions* options = NULL;
00285     if (name.empty()) {
00286         options = &m_globalOptions;
00287     }
00288     else {
00289         CCellMap::iterator index = m_map.find(name);
00290         if (index != m_map.end()) {
00291             options = &index->second.m_options;
00292         }
00293     }
00294     if (options == NULL) {
00295         return false;
00296     }
00297 
00298     // add option
00299     options->insert(std::make_pair(option, value));
00300     return true;
00301 }
00302 
00303 bool
00304 CConfig::removeOption(const CString& name, OptionID option)
00305 {
00306     // find options
00307     CScreenOptions* options = NULL;
00308     if (name.empty()) {
00309         options = &m_globalOptions;
00310     }
00311     else {
00312         CCellMap::iterator index = m_map.find(name);
00313         if (index != m_map.end()) {
00314             options = &index->second.m_options;
00315         }
00316     }
00317     if (options == NULL) {
00318         return false;
00319     }
00320 
00321     // remove option
00322     options->erase(option);
00323     return true;
00324 }
00325 
00326 bool
00327 CConfig::removeOptions(const CString& name)
00328 {
00329     // find options
00330     CScreenOptions* options = NULL;
00331     if (name.empty()) {
00332         options = &m_globalOptions;
00333     }
00334     else {
00335         CCellMap::iterator index = m_map.find(name);
00336         if (index != m_map.end()) {
00337             options = &index->second.m_options;
00338         }
00339     }
00340     if (options == NULL) {
00341         return false;
00342     }
00343 
00344     // remove options
00345     options->clear();
00346     return true;
00347 }
00348 
00349 bool
00350 CConfig::isValidScreenName(const CString& name) const
00351 {
00352     // name is valid if matches validname
00353     //  name      ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9]
00354     //  domain    ::= . name
00355     //  validname ::= name domain*
00356     // we also accept names ending in . because many OS X users have
00357     // so misconfigured their systems.
00358 
00359     // empty name is invalid
00360     if (name.empty()) {
00361         return false;
00362     }
00363 
00364     // check each dot separated part
00365     CString::size_type b = 0;
00366     for (;;) {
00367         // accept trailing .
00368         if (b == name.size()) {
00369             break;
00370         }
00371 
00372         // find end of part
00373         CString::size_type e = name.find('.', b);
00374         if (e == CString::npos) {
00375             e = name.size();
00376         }
00377 
00378         // part may not be empty
00379         if (e - b < 1) {
00380             return false;
00381         }
00382 
00383         // check first and last characters
00384         if (!(isalnum(name[b]) || name[b] == '_') ||
00385             !(isalnum(name[e - 1]) || name[e - 1] == '_')) {
00386             return false;
00387         }
00388 
00389         // check interior characters
00390         for (CString::size_type i = b; i < e; ++i) {
00391             if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') {
00392                 return false;
00393             }
00394         }
00395 
00396         // next part
00397         if (e == name.size()) {
00398             // no more parts
00399             break;
00400         }
00401         b = e + 1;
00402     }
00403 
00404     return true;
00405 }
00406 
00407 CConfig::const_iterator
00408 CConfig::begin() const
00409 {
00410     return const_iterator(m_map.begin());
00411 }
00412 
00413 CConfig::const_iterator
00414 CConfig::end() const
00415 {
00416     return const_iterator(m_map.end());
00417 }
00418 
00419 CConfig::all_const_iterator
00420 CConfig::beginAll() const
00421 {
00422     return m_nameToCanonicalName.begin();
00423 }
00424 
00425 CConfig::all_const_iterator
00426 CConfig::endAll() const
00427 {
00428     return m_nameToCanonicalName.end();
00429 }
00430 
00431 bool
00432 CConfig::isScreen(const CString& name) const
00433 {
00434     return (m_nameToCanonicalName.count(name) > 0);
00435 }
00436 
00437 bool
00438 CConfig::isCanonicalName(const CString& name) const
00439 {
00440     return (!name.empty() &&
00441             CStringUtil::CaselessCmp::equal(getCanonicalName(name), name));
00442 }
00443 
00444 CString
00445 CConfig::getCanonicalName(const CString& name) const
00446 {
00447     CNameMap::const_iterator index = m_nameToCanonicalName.find(name);
00448     if (index == m_nameToCanonicalName.end()) {
00449         return CString();
00450     }
00451     else {
00452         return index->second;
00453     }
00454 }
00455 
00456 CString
00457 CConfig::getNeighbor(const CString& srcName, EDirection srcSide,
00458                 float position, float* positionOut) const
00459 {
00460     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00461 
00462     // find source cell
00463     CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
00464     if (index == m_map.end()) {
00465         return CString();
00466     }
00467 
00468     // find edge
00469     const CCellEdge* srcEdge, *dstEdge;
00470     if (!index->second.getLink(srcSide, position, srcEdge, dstEdge)) {
00471         // no neighbor
00472         return "";
00473     }
00474     else {
00475         // compute position on neighbor
00476         if (positionOut != NULL) {
00477             *positionOut =
00478                 dstEdge->inverseTransform(srcEdge->transform(position));
00479         }
00480 
00481         // return neighbor's name
00482         return getCanonicalName(dstEdge->getName());
00483     }
00484 }
00485 
00486 bool
00487 CConfig::hasNeighbor(const CString& srcName, EDirection srcSide) const
00488 {
00489     return hasNeighbor(srcName, srcSide, 0.0f, 1.0f);
00490 }
00491 
00492 bool
00493 CConfig::hasNeighbor(const CString& srcName, EDirection srcSide,
00494                             float start, float end) const
00495 {
00496     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00497 
00498     // find source cell
00499     CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
00500     if (index == m_map.end()) {
00501         return false;
00502     }
00503 
00504     return index->second.overlaps(CCellEdge(srcSide, CInterval(start, end)));
00505 }
00506 
00507 CConfig::link_const_iterator
00508 CConfig::beginNeighbor(const CString& srcName) const
00509 {
00510     CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
00511     assert(index != m_map.end());
00512     return index->second.begin();
00513 }
00514 
00515 CConfig::link_const_iterator
00516 CConfig::endNeighbor(const CString& srcName) const
00517 {
00518     CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
00519     assert(index != m_map.end());
00520     return index->second.end();
00521 }
00522 
00523 const CNetworkAddress&
00524 CConfig::getSynergyAddress() const
00525 {
00526     return m_synergyAddress;
00527 }
00528 
00529 const CConfig::CScreenOptions*
00530 CConfig::getOptions(const CString& name) const
00531 {
00532     // find options
00533     const CScreenOptions* options = NULL;
00534     if (name.empty()) {
00535         options = &m_globalOptions;
00536     }
00537     else {
00538         CCellMap::const_iterator index = m_map.find(name);
00539         if (index != m_map.end()) {
00540             options = &index->second.m_options;
00541         }
00542     }
00543 
00544     // return options
00545     return options;
00546 }
00547 
00548 bool
00549 CConfig::hasLockToScreenAction() const
00550 {
00551     return m_hasLockToScreenAction;
00552 }
00553 
00554 bool
00555 CConfig::operator==(const CConfig& x) const
00556 {
00557     if (m_synergyAddress != x.m_synergyAddress) {
00558         return false;
00559     }
00560     if (m_map.size() != x.m_map.size()) {
00561         return false;
00562     }
00563     if (m_nameToCanonicalName.size() != x.m_nameToCanonicalName.size()) {
00564         return false;
00565     }
00566 
00567     // compare global options
00568     if (m_globalOptions != x.m_globalOptions) {
00569         return false;
00570     }
00571 
00572     for (CCellMap::const_iterator index1 = m_map.begin(),
00573                                 index2 = x.m_map.begin();
00574                                 index1 != m_map.end(); ++index1, ++index2) {
00575         // compare names
00576         if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first)) {
00577             return false;
00578         }
00579 
00580         // compare cells
00581         if (index1->second != index2->second) {
00582             return false;
00583         }
00584     }
00585 
00586     for (CNameMap::const_iterator index1 = m_nameToCanonicalName.begin(),
00587                                 index2 = x.m_nameToCanonicalName.begin();
00588                                 index1 != m_nameToCanonicalName.end();
00589                                 ++index1, ++index2) {
00590         if (!CStringUtil::CaselessCmp::equal(index1->first,  index2->first) ||
00591             !CStringUtil::CaselessCmp::equal(index1->second, index2->second)) {
00592             return false;
00593         }
00594     }
00595 
00596     // compare input filters
00597     if (m_inputFilter != x.m_inputFilter) {
00598         return false;
00599     }
00600 
00601     return true;
00602 }
00603 
00604 bool
00605 CConfig::operator!=(const CConfig& x) const
00606 {
00607     return !operator==(x);
00608 }
00609 
00610 void
00611 CConfig::read(CConfigReadContext& context)
00612 {
00613     CConfig tmp;
00614     while (context) {
00615         tmp.readSection(context);
00616     }
00617     *this = tmp;
00618 }
00619 
00620 const char*
00621 CConfig::dirName(EDirection dir)
00622 {
00623     static const char* s_name[] = { "left", "right", "up", "down" };
00624 
00625     assert(dir >= kFirstDirection && dir <= kLastDirection);
00626 
00627     return s_name[dir - kFirstDirection];
00628 }
00629 
00630 CInputFilter*
00631 CConfig::getInputFilter()
00632 {
00633     return &m_inputFilter;
00634 }
00635 
00636 CString
00637 CConfig::formatInterval(const CInterval& x)
00638 {
00639     if (x.first == 0.0f && x.second == 1.0f) {
00640         return "";
00641     }
00642     return CStringUtil::print("(%d,%d)", (int)(x.first * 100.0f + 0.5f),
00643                                         (int)(x.second * 100.0f + 0.5f));
00644 }
00645 
00646 void
00647 CConfig::readSection(CConfigReadContext& s)
00648 {
00649     static const char s_section[] = "section:";
00650     static const char s_options[] = "options";
00651     static const char s_screens[] = "screens";
00652     static const char s_links[]   = "links";
00653     static const char s_aliases[] = "aliases";
00654 
00655     CString line;
00656     if (!s.readLine(line)) {
00657         // no more sections
00658         return;
00659     }
00660 
00661     // should be a section header
00662     if (line.find(s_section) != 0) {
00663         throw XConfigRead(s, "found data outside section");
00664     }
00665 
00666     // get section name
00667     CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1);
00668     if (i == CString::npos) {
00669         throw XConfigRead(s, "section name is missing");
00670     }
00671     CString name = line.substr(i);
00672     i = name.find_first_of(" \t");
00673     if (i != CString::npos) {
00674         throw XConfigRead(s, "unexpected data after section name");
00675     }
00676 
00677     // read section
00678     if (name == s_options) {
00679         readSectionOptions(s);
00680     }
00681     else if (name == s_screens) {
00682         readSectionScreens(s);
00683     }
00684     else if (name == s_links) {
00685         readSectionLinks(s);
00686     }
00687     else if (name == s_aliases) {
00688         readSectionAliases(s);
00689     }
00690     else {
00691         throw XConfigRead(s, "unknown section name \"%{1}\"", name);
00692     }
00693 }
00694 
00695 void
00696 CConfig::readSectionOptions(CConfigReadContext& s)
00697 {
00698     CString line;
00699     while (s.readLine(line)) {
00700         // check for end of section
00701         if (line == "end") {
00702             return;
00703         }
00704 
00705         // parse argument:  `nameAndArgs = [values][;[values]]'
00706         //   nameAndArgs  := <name>[(arg[,...])]
00707         //   values       := valueAndArgs[,valueAndArgs]...
00708         //   valueAndArgs := <value>[(arg[,...])]
00709         CString::size_type i = 0;
00710         CString name, value;
00711         CConfigReadContext::ArgList nameArgs, valueArgs;
00712         s.parseNameWithArgs("name", line, "=", i, name, nameArgs);
00713         ++i;
00714         s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
00715 
00716         bool handled = true;
00717         if (name == "address") {
00718             try {
00719                 m_synergyAddress = CNetworkAddress(value, kDefaultPort);
00720                 m_synergyAddress.resolve();
00721             }
00722             catch (XSocketAddress& e) {
00723                 throw XConfigRead(s,
00724                             CString("invalid address argument ") + e.what());
00725             }
00726         }
00727         else if (name == "heartbeat") {
00728             addOption("", kOptionHeartbeat, s.parseInt(value));
00729         }
00730         else if (name == "switchCorners") {
00731             addOption("", kOptionScreenSwitchCorners, s.parseCorners(value));
00732         }
00733         else if (name == "switchCornerSize") {
00734             addOption("", kOptionScreenSwitchCornerSize, s.parseInt(value));
00735         }
00736         else if (name == "switchDelay") {
00737             addOption("", kOptionScreenSwitchDelay, s.parseInt(value));
00738         }
00739         else if (name == "switchDoubleTap") {
00740             addOption("", kOptionScreenSwitchTwoTap, s.parseInt(value));
00741         }
00742         else if (name == "switchNeedsShift") {
00743             addOption("", kOptionScreenSwitchNeedsShift, s.parseBoolean(value));
00744         }
00745         else if (name == "switchNeedsControl") {
00746             addOption("", kOptionScreenSwitchNeedsControl, s.parseBoolean(value));
00747         }
00748         else if (name == "switchNeedsAlt") {
00749             addOption("", kOptionScreenSwitchNeedsAlt, s.parseBoolean(value));
00750         }
00751         else if (name == "screenSaverSync") {
00752             addOption("", kOptionScreenSaverSync, s.parseBoolean(value));
00753         }
00754         else if (name == "relativeMouseMoves") {
00755             addOption("", kOptionRelativeMouseMoves, s.parseBoolean(value));
00756         }
00757         else if (name == "win32KeepForeground") {
00758             addOption("", kOptionWin32KeepForeground, s.parseBoolean(value));
00759         }
00760         else {
00761             handled = false;
00762         }
00763 
00764         if (handled) {
00765             // make sure handled options aren't followed by more values
00766             if (i < line.size() && (line[i] == ',' || line[i] == ';')) {
00767                 throw XConfigRead(s, "to many arguments to %s", name.c_str());
00768             }
00769         }
00770         else {
00771             // make filter rule
00772             CInputFilter::CRule rule(parseCondition(s, name, nameArgs));
00773 
00774             // save first action (if any)
00775             if (!value.empty() || line[i] != ';') {
00776                 parseAction(s, value, valueArgs, rule, true);
00777             }
00778 
00779             // get remaining activate actions
00780             while (i < line.length() && line[i] != ';') {
00781                 ++i;
00782                 s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
00783                 parseAction(s, value, valueArgs, rule, true);
00784             }
00785 
00786             // get deactivate actions
00787             if (i < line.length() && line[i] == ';') {
00788                 // allow trailing ';'
00789                 i = line.find_first_not_of(" \t", i + 1);
00790                 if (i == CString::npos) {
00791                     i = line.length();
00792                 }
00793                 else {
00794                     --i;
00795                 }
00796 
00797                 // get actions
00798                 while (i < line.length()) {
00799                     ++i;
00800                     s.parseNameWithArgs("value", line, ",\n",
00801                                 i, value, valueArgs);
00802                     parseAction(s, value, valueArgs, rule, false);
00803                 }
00804             }
00805 
00806             // add rule
00807             m_inputFilter.addFilterRule(rule);
00808         }
00809     }
00810     throw XConfigRead(s, "unexpected end of options section");
00811 }
00812 
00813 void
00814 CConfig::readSectionScreens(CConfigReadContext& s)
00815 {
00816     CString line;
00817     CString screen;
00818     while (s.readLine(line)) {
00819         // check for end of section
00820         if (line == "end") {
00821             return;
00822         }
00823 
00824         // see if it's the next screen
00825         if (line[line.size() - 1] == ':') {
00826             // strip :
00827             screen = line.substr(0, line.size() - 1);
00828 
00829             // verify validity of screen name
00830             if (!isValidScreenName(screen)) {
00831                 throw XConfigRead(s, "invalid screen name \"%{1}\"", screen);
00832             }
00833 
00834             // add the screen to the configuration
00835             if (!addScreen(screen)) {
00836                 throw XConfigRead(s, "duplicate screen name \"%{1}\"", screen);
00837             }
00838         }
00839         else if (screen.empty()) {
00840             throw XConfigRead(s, "argument before first screen");
00841         }
00842         else {
00843             // parse argument:  `<name>=<value>'
00844             CString::size_type i = line.find_first_of(" \t=");
00845             if (i == 0) {
00846                 throw XConfigRead(s, "missing argument name");
00847             }
00848             if (i == CString::npos) {
00849                 throw XConfigRead(s, "missing =");
00850             }
00851             CString name = line.substr(0, i);
00852             i = line.find_first_not_of(" \t", i);
00853             if (i == CString::npos || line[i] != '=') {
00854                 throw XConfigRead(s, "missing =");
00855             }
00856             i = line.find_first_not_of(" \t", i + 1);
00857             CString value;
00858             if (i != CString::npos) {
00859                 value = line.substr(i);
00860             }
00861 
00862             // handle argument
00863             if (name == "halfDuplexCapsLock") {
00864                 addOption(screen, kOptionHalfDuplexCapsLock,
00865                     s.parseBoolean(value));
00866             }
00867             else if (name == "halfDuplexNumLock") {
00868                 addOption(screen, kOptionHalfDuplexNumLock,
00869                     s.parseBoolean(value));
00870             }
00871             else if (name == "halfDuplexScrollLock") {
00872                 addOption(screen, kOptionHalfDuplexScrollLock,
00873                     s.parseBoolean(value));
00874             }
00875             else if (name == "shift") {
00876                 addOption(screen, kOptionModifierMapForShift,
00877                     s.parseModifierKey(value));
00878             }
00879             else if (name == "ctrl") {
00880                 addOption(screen, kOptionModifierMapForControl,
00881                     s.parseModifierKey(value));
00882             }
00883             else if (name == "alt") {
00884                 addOption(screen, kOptionModifierMapForAlt,
00885                     s.parseModifierKey(value));
00886             }
00887             else if (name == "altgr") {
00888                 addOption(screen, kOptionModifierMapForAltGr,
00889                     s.parseModifierKey(value));
00890             }
00891             else if (name == "meta") {
00892                 addOption(screen, kOptionModifierMapForMeta,
00893                     s.parseModifierKey(value));
00894             }
00895             else if (name == "super") {
00896                 addOption(screen, kOptionModifierMapForSuper,
00897                     s.parseModifierKey(value));
00898             }
00899             else if (name == "xtestIsXineramaUnaware") {
00900                 addOption(screen, kOptionXTestXineramaUnaware,
00901                     s.parseBoolean(value));
00902             }
00903             else if (name == "switchCorners") {
00904                 addOption(screen, kOptionScreenSwitchCorners,
00905                     s.parseCorners(value));
00906             }
00907             else if (name == "switchCornerSize") {
00908                 addOption(screen, kOptionScreenSwitchCornerSize,
00909                     s.parseInt(value));
00910             }
00911             else if (name == "preserveFocus") {
00912                 addOption(screen, kOptionScreenPreserveFocus,
00913                     s.parseBoolean(value));
00914             }
00915             else {
00916                 // unknown argument
00917                 throw XConfigRead(s, "unknown argument \"%{1}\"", name);
00918             }
00919         }
00920     }
00921     throw XConfigRead(s, "unexpected end of screens section");
00922 }
00923 
00924 void
00925 CConfig::readSectionLinks(CConfigReadContext& s)
00926 {
00927     CString line;
00928     CString screen;
00929     while (s.readLine(line)) {
00930         // check for end of section
00931         if (line == "end") {
00932             return;
00933         }
00934 
00935         // see if it's the next screen
00936         if (line[line.size() - 1] == ':') {
00937             // strip :
00938             screen = line.substr(0, line.size() - 1);
00939 
00940             // verify we know about the screen
00941             if (!isScreen(screen)) {
00942                 throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
00943             }
00944             if (!isCanonicalName(screen)) {
00945                 throw XConfigRead(s, "cannot use screen name alias here");
00946             }
00947         }
00948         else if (screen.empty()) {
00949             throw XConfigRead(s, "argument before first screen");
00950         }
00951         else {
00952             // parse argument:  `<name>[(<s0>,<e0>)]=<value>[(<s1>,<e1>)]'
00953             // the stuff in brackets is optional.  interval values must be
00954             // in the range [0,100] and start < end.  if not given the
00955             // interval is taken to be (0,100).
00956             CString::size_type i = 0;
00957             CString side, dstScreen, srcArgString, dstArgString;
00958             CConfigReadContext::ArgList srcArgs, dstArgs;
00959             s.parseNameWithArgs("link", line, "=", i, side, srcArgs);
00960             ++i;
00961             s.parseNameWithArgs("screen", line, "", i, dstScreen, dstArgs);
00962             CInterval srcInterval(s.parseInterval(srcArgs));
00963             CInterval dstInterval(s.parseInterval(dstArgs));
00964 
00965             // handle argument
00966             EDirection dir;
00967             if (side == "left") {
00968                 dir = kLeft;
00969             }
00970             else if (side == "right") {
00971                 dir = kRight;
00972             }
00973             else if (side == "up") {
00974                 dir = kTop;
00975             }
00976             else if (side == "down") {
00977                 dir = kBottom;
00978             }
00979             else {
00980                 // unknown argument
00981                 throw XConfigRead(s, "unknown side \"%{1}\" in link", side);
00982             }
00983             if (!isScreen(dstScreen)) {
00984                 throw XConfigRead(s, "unknown screen name \"%{1}\"", dstScreen);
00985             }
00986             if (!connect(screen, dir,
00987                         srcInterval.first, srcInterval.second,
00988                         dstScreen,
00989                         dstInterval.first, dstInterval.second)) {
00990                 throw XConfigRead(s, "overlapping range");
00991             }
00992         }
00993     }
00994     throw XConfigRead(s, "unexpected end of links section");
00995 }
00996 
00997 void
00998 CConfig::readSectionAliases(CConfigReadContext& s)
00999 {
01000     CString line;
01001     CString screen;
01002     while (s.readLine(line)) {
01003         // check for end of section
01004         if (line == "end") {
01005             return;
01006         }
01007 
01008         // see if it's the next screen
01009         if (line[line.size() - 1] == ':') {
01010             // strip :
01011             screen = line.substr(0, line.size() - 1);
01012 
01013             // verify we know about the screen
01014             if (!isScreen(screen)) {
01015                 throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
01016             }
01017             if (!isCanonicalName(screen)) {
01018                 throw XConfigRead(s, "cannot use screen name alias here");
01019             }
01020         }
01021         else if (screen.empty()) {
01022             throw XConfigRead(s, "argument before first screen");
01023         }
01024         else {
01025             // verify validity of screen name
01026             if (!isValidScreenName(line)) {
01027                 throw XConfigRead(s, "invalid screen alias \"%{1}\"", line);
01028             }
01029 
01030             // add alias
01031             if (!addAlias(screen, line)) {
01032                 throw XConfigRead(s, "alias \"%{1}\" is already used", line);
01033             }
01034         }
01035     }
01036     throw XConfigRead(s, "unexpected end of aliases section");
01037 }
01038 
01039 
01040 CInputFilter::CCondition*
01041 CConfig::parseCondition(CConfigReadContext& s,
01042                 const CString& name, const std::vector<CString>& args)
01043 {
01044     if (name == "keystroke") {
01045         if (args.size() != 1) {
01046             throw XConfigRead(s, "syntax for condition: keystroke(modifiers+key)");
01047         }
01048 
01049         IPlatformScreen::CKeyInfo* keyInfo = s.parseKeystroke(args[0]);
01050 
01051         return new CInputFilter::CKeystrokeCondition(keyInfo);
01052     }
01053 
01054     if (name == "mousebutton") {
01055         if (args.size() != 1) {
01056             throw XConfigRead(s, "syntax for condition: mousebutton(modifiers+button)");
01057         }
01058 
01059         IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]);
01060 
01061         return new CInputFilter::CMouseButtonCondition(mouseInfo);
01062     }
01063 
01064     if (name == "connect") {
01065         if (args.size() != 1) {
01066             throw XConfigRead(s, "syntax for condition: connect([screen])");
01067         }
01068 
01069         CString screen = args[0];
01070         if (isScreen(screen)) {
01071             screen = getCanonicalName(screen);
01072         }
01073         else if (!screen.empty()) {
01074             throw XConfigRead(s, "unknown screen name \"%{1}\" in connect", screen);
01075         }
01076 
01077         return new CInputFilter::CScreenConnectedCondition(screen);
01078     }
01079 
01080     throw XConfigRead(s, "unknown argument \"%{1}\"", name);
01081 }
01082 
01083 void
01084 CConfig::parseAction(CConfigReadContext& s,
01085                 const CString& name, const std::vector<CString>& args,
01086                 CInputFilter::CRule& rule, bool activate)
01087 {
01088     CInputFilter::CAction* action;
01089 
01090     if (name == "keystroke" || name == "keyDown" || name == "keyUp") {
01091         if (args.size() < 1 || args.size() > 2) {
01092             throw XConfigRead(s, "syntax for action: keystroke(modifiers+key[,screens])");
01093         }
01094 
01095         IPlatformScreen::CKeyInfo* keyInfo;
01096         if (args.size() == 1) {
01097             keyInfo = s.parseKeystroke(args[0]);
01098         }
01099         else {
01100             std::set<CString> screens;
01101             parseScreens(s, args[1], screens);
01102             keyInfo = s.parseKeystroke(args[0], screens);
01103         }
01104 
01105         if (name == "keystroke") {
01106             IPlatformScreen::CKeyInfo* keyInfo2 =
01107                 IKeyState::CKeyInfo::alloc(*keyInfo);
01108             action = new CInputFilter::CKeystrokeAction(keyInfo2, true);
01109             rule.adoptAction(action, true);
01110             action   = new CInputFilter::CKeystrokeAction(keyInfo, false);
01111             activate = false;
01112         }
01113         else if (name == "keyDown") {
01114             action = new CInputFilter::CKeystrokeAction(keyInfo, true);
01115         }
01116         else {
01117             action = new CInputFilter::CKeystrokeAction(keyInfo, false);
01118         }
01119     }
01120 
01121     else if (name == "mousebutton" ||
01122                 name == "mouseDown" || name == "mouseUp") {
01123         if (args.size() != 1) {
01124             throw XConfigRead(s, "syntax for action: mousebutton(modifiers+button)");
01125         }
01126 
01127         IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]);
01128 
01129         if (name == "mousebutton") {
01130             IPlatformScreen::CButtonInfo* mouseInfo2 =
01131                 IPlatformScreen::CButtonInfo::alloc(*mouseInfo);
01132             action = new CInputFilter::CMouseButtonAction(mouseInfo2, true);
01133             rule.adoptAction(action, true);
01134             action   = new CInputFilter::CMouseButtonAction(mouseInfo, false);
01135             activate = false;
01136         }
01137         else if (name == "mouseDown") {
01138             action = new CInputFilter::CMouseButtonAction(mouseInfo, true);
01139         }
01140         else {
01141             action = new CInputFilter::CMouseButtonAction(mouseInfo, false);
01142         }
01143     }
01144 
01145 /* XXX -- not supported
01146     else if (name == "modifier") {
01147         if (args.size() != 1) {
01148             throw XConfigRead(s, "syntax for action: modifier(modifiers)");
01149         }
01150 
01151         KeyModifierMask mask = s.parseModifier(args[0]);
01152 
01153         action = new CInputFilter::CModifierAction(mask, ~mask);
01154     }
01155 */
01156 
01157     else if (name == "switchToScreen") {
01158         if (args.size() != 1) {
01159             throw XConfigRead(s, "syntax for action: switchToScreen(name)");
01160         }
01161 
01162         CString screen = args[0];
01163         if (isScreen(screen)) {
01164             screen = getCanonicalName(screen);
01165         }
01166         else if (!screen.empty()) {
01167             throw XConfigRead(s, "unknown screen name in switchToScreen");
01168         }
01169 
01170         action = new CInputFilter::CSwitchToScreenAction(screen);
01171     }
01172 
01173     else if (name == "switchInDirection") {
01174         if (args.size() != 1) {
01175             throw XConfigRead(s, "syntax for action: switchInDirection(<left|right|up|down>)");
01176         }
01177 
01178         EDirection direction;
01179         if (args[0] == "left") {
01180             direction = kLeft;
01181         }
01182         else if (args[0] == "right") {
01183             direction = kRight;
01184         }
01185         else if (args[0] == "up") {
01186             direction = kTop;
01187         }
01188         else if (args[0] == "down") {
01189             direction = kBottom;
01190         }
01191         else {
01192             throw XConfigRead(s, "unknown direction \"%{1}\" in switchToScreen", args[0]);
01193         }
01194 
01195         action = new CInputFilter::CSwitchInDirectionAction(direction);
01196     }
01197 
01198     else if (name == "lockCursorToScreen") {
01199         if (args.size() > 1) {
01200             throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
01201         }
01202 
01203         CInputFilter::CLockCursorToScreenAction::Mode mode =
01204             CInputFilter::CLockCursorToScreenAction::kToggle;
01205         if (args.size() == 1) {
01206             if (args[0] == "off") {
01207                 mode = CInputFilter::CLockCursorToScreenAction::kOff;
01208             }
01209             else if (args[0] == "on") {
01210                 mode = CInputFilter::CLockCursorToScreenAction::kOn;
01211             }
01212             else if (args[0] == "toggle") {
01213                 mode = CInputFilter::CLockCursorToScreenAction::kToggle;
01214             }
01215             else {
01216                 throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
01217             }
01218         }
01219 
01220         if (mode != CInputFilter::CLockCursorToScreenAction::kOff) {
01221             m_hasLockToScreenAction = true;
01222         }
01223 
01224         action = new CInputFilter::CLockCursorToScreenAction(mode);
01225     }
01226 
01227     else if (name == "keyboardBroadcast") {
01228         if (args.size() > 2) {
01229             throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])");
01230         }
01231 
01232         CInputFilter::CKeyboardBroadcastAction::Mode mode =
01233             CInputFilter::CKeyboardBroadcastAction::kToggle;
01234         if (args.size() >= 1) {
01235             if (args[0] == "off") {
01236                 mode = CInputFilter::CKeyboardBroadcastAction::kOff;
01237             }
01238             else if (args[0] == "on") {
01239                 mode = CInputFilter::CKeyboardBroadcastAction::kOn;
01240             }
01241             else if (args[0] == "toggle") {
01242                 mode = CInputFilter::CKeyboardBroadcastAction::kToggle;
01243             }
01244             else {
01245                 throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])");
01246             }
01247         }
01248 
01249         std::set<CString> screens;
01250         if (args.size() >= 2) {
01251             parseScreens(s, args[1], screens);
01252         }
01253 
01254         action = new CInputFilter::CKeyboardBroadcastAction(mode, screens);
01255     }
01256 
01257     else {
01258         throw XConfigRead(s, "unknown action argument \"%{1}\"", name);
01259     }
01260 
01261     rule.adoptAction(action, activate);
01262 }
01263 
01264 void
01265 CConfig::parseScreens(CConfigReadContext& c,
01266                 const CString& s, std::set<CString>& screens) const
01267 {
01268     screens.clear();
01269 
01270     CString::size_type i = 0;
01271     while (i < s.size()) {
01272         // find end of next screen name
01273         CString::size_type j = s.find(':', i);
01274         if (j == CString::npos) {
01275             j = s.size();
01276         }
01277 
01278         // extract name
01279         CString rawName;
01280         i = s.find_first_not_of(" \t", i);
01281         if (i < j) {
01282             rawName = s.substr(i, s.find_last_not_of(" \t", j - 1) - i + 1);
01283         }
01284 
01285         // add name
01286         if (rawName == "*") {
01287             screens.insert("*");
01288         }
01289         else if (!rawName.empty()) {
01290             CString name = getCanonicalName(rawName);
01291             if (name.empty()) {
01292                 throw XConfigRead(c, "unknown screen name \"%{1}\"", rawName);
01293             }
01294             screens.insert(name);
01295         }
01296 
01297         // next
01298         i = j + 1;
01299     }
01300 }
01301 
01302 const char*
01303 CConfig::getOptionName(OptionID id)
01304 {
01305     if (id == kOptionHalfDuplexCapsLock) {
01306         return "halfDuplexCapsLock";
01307     }
01308     if (id == kOptionHalfDuplexNumLock) {
01309         return "halfDuplexNumLock";
01310     }
01311     if (id == kOptionHalfDuplexScrollLock) {
01312         return "halfDuplexScrollLock";
01313     }
01314     if (id == kOptionModifierMapForShift) {
01315         return "shift";
01316     }
01317     if (id == kOptionModifierMapForControl) {
01318         return "ctrl";
01319     }
01320     if (id == kOptionModifierMapForAlt) {
01321         return "alt";
01322     }
01323     if (id == kOptionModifierMapForAltGr) {
01324         return "altgr";
01325     }
01326     if (id == kOptionModifierMapForMeta) {
01327         return "meta";
01328     }
01329     if (id == kOptionModifierMapForSuper) {
01330         return "super";
01331     }
01332     if (id == kOptionHeartbeat) {
01333         return "heartbeat";
01334     }
01335     if (id == kOptionScreenSwitchCorners) {
01336         return "switchCorners";
01337     }
01338     if (id == kOptionScreenSwitchCornerSize) {
01339         return "switchCornerSize";
01340     }
01341     if (id == kOptionScreenSwitchDelay) {
01342         return "switchDelay";
01343     }
01344     if (id == kOptionScreenSwitchTwoTap) {
01345         return "switchDoubleTap";
01346     }
01347     if (id == kOptionScreenSwitchNeedsShift) {
01348         return "switchNeedsShift";
01349     }
01350     if (id == kOptionScreenSwitchNeedsControl) {
01351         return "switchNeedsControl";
01352     }
01353     if (id == kOptionScreenSwitchNeedsAlt) {
01354         return "switchNeedsAlt";
01355     }
01356     if (id == kOptionScreenSaverSync) {
01357         return "screenSaverSync";
01358     }
01359     if (id == kOptionXTestXineramaUnaware) {
01360         return "xtestIsXineramaUnaware";
01361     }
01362     if (id == kOptionRelativeMouseMoves) {
01363         return "relativeMouseMoves";
01364     }
01365     if (id == kOptionWin32KeepForeground) {
01366         return "win32KeepForeground";
01367     }
01368     if (id == kOptionScreenPreserveFocus) {
01369         return "preserveFocus";
01370     }
01371     return NULL;
01372 }
01373 
01374 CString
01375 CConfig::getOptionValue(OptionID id, OptionValue value)
01376 {
01377     if (id == kOptionHalfDuplexCapsLock ||
01378         id == kOptionHalfDuplexNumLock ||
01379         id == kOptionHalfDuplexScrollLock ||
01380         id == kOptionScreenSwitchNeedsShift ||
01381         id == kOptionScreenSwitchNeedsControl ||
01382         id == kOptionScreenSwitchNeedsAlt ||
01383         id == kOptionScreenSaverSync ||
01384         id == kOptionXTestXineramaUnaware ||
01385         id == kOptionRelativeMouseMoves ||
01386         id == kOptionWin32KeepForeground ||
01387         id == kOptionScreenPreserveFocus) {
01388         return (value != 0) ? "true" : "false";
01389     }
01390     if (id == kOptionModifierMapForShift ||
01391         id == kOptionModifierMapForControl ||
01392         id == kOptionModifierMapForAlt ||
01393         id == kOptionModifierMapForAltGr ||
01394         id == kOptionModifierMapForMeta ||
01395         id == kOptionModifierMapForSuper) {
01396         switch (value) {
01397         case kKeyModifierIDShift:
01398             return "shift";
01399 
01400         case kKeyModifierIDControl:
01401             return "ctrl";
01402 
01403         case kKeyModifierIDAlt:
01404             return "alt";
01405 
01406         case kKeyModifierIDAltGr:
01407             return "altgr";
01408 
01409         case kKeyModifierIDMeta:
01410             return "meta";
01411 
01412         case kKeyModifierIDSuper:
01413             return "super";
01414 
01415         default:
01416             return "none";
01417         }
01418     }
01419     if (id == kOptionHeartbeat ||
01420         id == kOptionScreenSwitchCornerSize ||
01421         id == kOptionScreenSwitchDelay ||
01422         id == kOptionScreenSwitchTwoTap) {
01423         return CStringUtil::print("%d", value);
01424     }
01425     if (id == kOptionScreenSwitchCorners) {
01426         std::string result("none");
01427         if ((value & kTopLeftMask) != 0) {
01428             result += " +top-left";
01429         }
01430         if ((value & kTopRightMask) != 0) {
01431             result += " +top-right";
01432         }
01433         if ((value & kBottomLeftMask) != 0) {
01434             result += " +bottom-left";
01435         }
01436         if ((value & kBottomRightMask) != 0) {
01437             result += " +bottom-right";
01438         }
01439         return result;
01440     }
01441 
01442     return "";
01443 }
01444 
01445 
01446 //
01447 // CConfig::CName
01448 //
01449 
01450 CConfig::CName::CName(CConfig* config, const CString& name) :
01451     m_config(config),
01452     m_name(config->getCanonicalName(name))
01453 {
01454     // do nothing
01455 }
01456 
01457 bool
01458 CConfig::CName::operator==(const CString& name) const
01459 {
01460     CString canonical = m_config->getCanonicalName(name);
01461     return CStringUtil::CaselessCmp::equal(canonical, m_name);
01462 }
01463 
01464 
01465 //
01466 // CConfig::CCellEdge
01467 //
01468 
01469 CConfig::CCellEdge::CCellEdge(EDirection side, float position)
01470 {
01471     init("", side, CInterval(position, position));
01472 }
01473 
01474 CConfig::CCellEdge::CCellEdge(EDirection side, const CInterval& interval)
01475 {
01476     assert(interval.first >= 0.0f);
01477     assert(interval.second <= 1.0f);
01478     assert(interval.first < interval.second);
01479 
01480     init("", side, interval);
01481 }
01482 
01483 CConfig::CCellEdge::CCellEdge(const CString& name,
01484                 EDirection side, const CInterval& interval)
01485 {
01486     assert(interval.first >= 0.0f);
01487     assert(interval.second <= 1.0f);
01488     assert(interval.first < interval.second);
01489 
01490     init(name, side, interval);
01491 }
01492 
01493 CConfig::CCellEdge::~CCellEdge()
01494 {
01495     // do nothing
01496 }
01497 
01498 void
01499 CConfig::CCellEdge::init(const CString& name, EDirection side,
01500                 const CInterval& interval)
01501 {
01502     assert(side != kNoDirection);
01503 
01504     m_name     = name;
01505     m_side     = side;
01506     m_interval = interval;
01507 }
01508 
01509 CConfig::CInterval
01510 CConfig::CCellEdge::getInterval() const
01511 {
01512     return m_interval;
01513 }
01514 
01515 void
01516 CConfig::CCellEdge::setName(const CString& newName)
01517 {
01518     m_name = newName;
01519 }
01520 
01521 CString
01522 CConfig::CCellEdge::getName() const
01523 {
01524     return m_name;
01525 }
01526 
01527 EDirection
01528 CConfig::CCellEdge::getSide() const
01529 {
01530     return m_side;
01531 }
01532 
01533 bool
01534 CConfig::CCellEdge::overlaps(const CCellEdge& edge) const
01535 {
01536     const CInterval& x = m_interval;
01537     const CInterval& y = edge.m_interval;
01538     if (m_side != edge.m_side) {
01539         return false;
01540     }
01541     return  (x.first  >= y.first && x.first  <  y.second) ||
01542             (x.second >  y.first && x.second <= y.second) ||
01543             (y.first  >= x.first && y.first  <  x.second) ||
01544             (y.second >  x.first && y.second <= x.second);
01545 }
01546 
01547 bool
01548 CConfig::CCellEdge::isInside(float x) const
01549 {
01550     return (x >= m_interval.first && x < m_interval.second);
01551 }
01552 
01553 float
01554 CConfig::CCellEdge::transform(float x) const
01555 {
01556     return (x - m_interval.first) / (m_interval.second - m_interval.first);
01557 }
01558 
01559 
01560 float
01561 CConfig::CCellEdge::inverseTransform(float x) const
01562 {
01563     return x * (m_interval.second - m_interval.first) + m_interval.first;
01564 }
01565 
01566 bool
01567 CConfig::CCellEdge::operator<(const CCellEdge& o) const
01568 {
01569     if (static_cast<int>(m_side) < static_cast<int>(o.m_side)) {
01570         return true;
01571     }
01572     else if (static_cast<int>(m_side) > static_cast<int>(o.m_side)) {
01573         return false;
01574     }
01575 
01576     return (m_interval.first < o.m_interval.first);
01577 }
01578 
01579 bool
01580 CConfig::CCellEdge::operator==(const CCellEdge& x) const
01581 {
01582     return (m_side == x.m_side && m_interval == x.m_interval);
01583 }
01584 
01585 bool
01586 CConfig::CCellEdge::operator!=(const CCellEdge& x) const
01587 {
01588     return !operator==(x);
01589 }
01590 
01591 
01592 //
01593 // CConfig::CCell
01594 //
01595 
01596 bool
01597 CConfig::CCell::add(const CCellEdge& src, const CCellEdge& dst)
01598 {
01599     // cannot add an edge that overlaps other existing edges but we
01600     // can exactly replace an edge.
01601     if (!hasEdge(src) && overlaps(src)) {
01602         return false;
01603     }
01604 
01605     m_neighbors.erase(src);
01606     m_neighbors.insert(std::make_pair(src, dst));
01607     return true;
01608 }
01609 
01610 void
01611 CConfig::CCell::remove(EDirection side)
01612 {
01613     for (CEdgeLinks::iterator j = m_neighbors.begin();
01614                             j != m_neighbors.end(); ) {
01615         if (j->first.getSide() == side) {
01616             m_neighbors.erase(j++);
01617         }
01618         else {
01619             ++j;
01620         }
01621     }
01622 }
01623 
01624 void
01625 CConfig::CCell::remove(EDirection side, float position)
01626 {
01627     for (CEdgeLinks::iterator j = m_neighbors.begin();
01628                             j != m_neighbors.end(); ++j) {
01629         if (j->first.getSide() == side && j->first.isInside(position)) {
01630             m_neighbors.erase(j);
01631             break;
01632         }
01633     }
01634 }
01635 void
01636 CConfig::CCell::remove(const CName& name)
01637 {
01638     for (CEdgeLinks::iterator j = m_neighbors.begin();
01639                             j != m_neighbors.end(); ) {
01640         if (name == j->second.getName()) {
01641             m_neighbors.erase(j++);
01642         }
01643         else {
01644             ++j;
01645         }
01646     }
01647 }
01648 
01649 void
01650 CConfig::CCell::rename(const CName& oldName, const CString& newName)
01651 {
01652     for (CEdgeLinks::iterator j = m_neighbors.begin();
01653                             j != m_neighbors.end(); ++j) {
01654         if (oldName == j->second.getName()) {
01655             j->second.setName(newName);
01656         }
01657     }
01658 }
01659 
01660 bool
01661 CConfig::CCell::hasEdge(const CCellEdge& edge) const
01662 {
01663     CEdgeLinks::const_iterator i = m_neighbors.find(edge);
01664     return (i != m_neighbors.end() && i->first == edge);
01665 }
01666 
01667 bool
01668 CConfig::CCell::overlaps(const CCellEdge& edge) const
01669 {
01670     CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
01671     if (i != m_neighbors.end() && i->first.overlaps(edge)) {
01672         return true;
01673     }
01674     if (i != m_neighbors.begin() && (--i)->first.overlaps(edge)) {
01675         return true;
01676     }
01677     return false;
01678 }
01679 
01680 bool
01681 CConfig::CCell::getLink(EDirection side, float position,
01682                 const CCellEdge*& src, const CCellEdge*& dst) const
01683 {
01684     CCellEdge edge(side, position);
01685     CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
01686     if (i == m_neighbors.begin()) {
01687         return false;
01688     }
01689     --i;
01690     if (i->first.getSide() == side && i->first.isInside(position)) {
01691         src = &i->first;
01692         dst = &i->second;
01693         return true;
01694     }
01695     return false;
01696 }
01697 
01698 bool
01699 CConfig::CCell::operator==(const CCell& x) const
01700 {
01701     // compare options
01702     if (m_options != x.m_options) {
01703         return false;
01704     }
01705 
01706     // compare links
01707     if (m_neighbors.size() != x.m_neighbors.size()) {
01708         return false;
01709     }
01710     for (CEdgeLinks::const_iterator index1 = m_neighbors.begin(),
01711                                 index2 = x.m_neighbors.begin();
01712                                 index1 != m_neighbors.end();
01713                                 ++index1, ++index2) {
01714         if (index1->first != index2->first) {
01715             return false;
01716         }
01717         if (index1->second != index2->second) {
01718             return false;
01719         }
01720 
01721         // operator== doesn't compare names.  only compare destination
01722         // names.
01723         if (!CStringUtil::CaselessCmp::equal(index1->second.getName(),
01724                                 index2->second.getName())) {
01725             return false;
01726         }
01727     }
01728     return true;
01729 }
01730 
01731 bool
01732 CConfig::CCell::operator!=(const CCell& x) const
01733 {
01734     return !operator==(x);
01735 }
01736 
01737 CConfig::CCell::const_iterator
01738 CConfig::CCell::begin() const
01739 {
01740     return m_neighbors.begin();
01741 }
01742 
01743 CConfig::CCell::const_iterator
01744 CConfig::CCell::end() const
01745 {
01746     return m_neighbors.end();
01747 }
01748 
01749 
01750 //
01751 // CConfig I/O
01752 //
01753 
01754 std::istream&
01755 operator>>(std::istream& s, CConfig& config)
01756 {
01757     CConfigReadContext context(s);
01758     config.read(context);
01759     return s;
01760 }
01761 
01762 std::ostream&
01763 operator<<(std::ostream& s, const CConfig& config)
01764 {
01765     // screens section
01766     s << "section: screens" << std::endl;
01767     for (CConfig::const_iterator screen = config.begin();
01768                                 screen != config.end(); ++screen) {
01769         s << "\t" << screen->c_str() << ":" << std::endl;
01770         const CConfig::CScreenOptions* options = config.getOptions(*screen);
01771         if (options != NULL && options->size() > 0) {
01772             for (CConfig::CScreenOptions::const_iterator
01773                                 option  = options->begin();
01774                                 option != options->end(); ++option) {
01775                 const char* name = CConfig::getOptionName(option->first);
01776                 CString value    = CConfig::getOptionValue(option->first,
01777                                                             option->second);
01778                 if (name != NULL && !value.empty()) {
01779                     s << "\t\t" << name << " = " << value << std::endl;
01780                 }
01781             }
01782         }
01783     }
01784     s << "end" << std::endl;
01785 
01786     // links section
01787     CString neighbor;
01788     s << "section: links" << std::endl;
01789     for (CConfig::const_iterator screen = config.begin();
01790                                 screen != config.end(); ++screen) {
01791         s << "\t" << screen->c_str() << ":" << std::endl;
01792 
01793         for (CConfig::link_const_iterator
01794                 link = config.beginNeighbor(*screen),
01795                 nend = config.endNeighbor(*screen); link != nend; ++link) {         
01796             s << "\t\t" << CConfig::dirName(link->first.getSide()) <<
01797                 CConfig::formatInterval(link->first.getInterval()) <<
01798                 " = " << link->second.getName().c_str() <<
01799                 CConfig::formatInterval(link->second.getInterval()) <<
01800                 std::endl;
01801         }
01802     }
01803     s << "end" << std::endl;
01804 
01805     // aliases section (if there are any)
01806     if (config.m_map.size() != config.m_nameToCanonicalName.size()) {
01807         // map canonical to alias
01808         typedef std::multimap<CString, CString,
01809                                 CStringUtil::CaselessCmp> CMNameMap;
01810         CMNameMap aliases;
01811         for (CConfig::CNameMap::const_iterator
01812                                 index = config.m_nameToCanonicalName.begin();
01813                                 index != config.m_nameToCanonicalName.end();
01814                                 ++index) {
01815             if (index->first != index->second) {
01816                 aliases.insert(std::make_pair(index->second, index->first));
01817             }
01818         }
01819 
01820         // dump it
01821         CString screen;
01822         s << "section: aliases" << std::endl;
01823         for (CMNameMap::const_iterator index = aliases.begin();
01824                                 index != aliases.end(); ++index) {
01825             if (index->first != screen) {
01826                 screen = index->first;
01827                 s << "\t" << screen.c_str() << ":" << std::endl;
01828             }
01829             s << "\t\t" << index->second.c_str() << std::endl;
01830         }
01831         s << "end" << std::endl;
01832     }
01833 
01834     // options section
01835     s << "section: options" << std::endl;
01836     const CConfig::CScreenOptions* options = config.getOptions("");
01837     if (options != NULL && options->size() > 0) {
01838         for (CConfig::CScreenOptions::const_iterator
01839                             option  = options->begin();
01840                             option != options->end(); ++option) {
01841             const char* name = CConfig::getOptionName(option->first);
01842             CString value    = CConfig::getOptionValue(option->first,
01843                                                         option->second);
01844             if (name != NULL && !value.empty()) {
01845                 s << "\t" << name << " = " << value << std::endl;
01846             }
01847         }
01848     }
01849     if (config.m_synergyAddress.isValid()) {
01850         s << "\taddress = " <<
01851             config.m_synergyAddress.getHostname().c_str() << std::endl;
01852     }
01853     s << config.m_inputFilter.format("\t");
01854     s << "end" << std::endl;
01855 
01856     return s;
01857 }
01858 
01859 
01860 //
01861 // CConfigReadContext
01862 //
01863 
01864 CConfigReadContext::CConfigReadContext(std::istream& s, SInt32 firstLine) :
01865     m_stream(s),
01866     m_line(firstLine - 1)
01867 {
01868     // do nothing
01869 }
01870 
01871 CConfigReadContext::~CConfigReadContext()
01872 {
01873     // do nothing
01874 }
01875 
01876 bool
01877 CConfigReadContext::readLine(CString& line)
01878 {
01879     ++m_line;
01880     while (std::getline(m_stream, line)) {
01881         // strip leading whitespace
01882         CString::size_type i = line.find_first_not_of(" \t");
01883         if (i != CString::npos) {
01884             line.erase(0, i);
01885         }
01886 
01887         // strip comments and then trailing whitespace
01888         i = line.find('#');
01889         if (i != CString::npos) {
01890             line.erase(i);
01891         }
01892         i = line.find_last_not_of(" \r\t");
01893         if (i != CString::npos) {
01894             line.erase(i + 1);
01895         }
01896 
01897         // return non empty line
01898         if (!line.empty()) {
01899             // make sure there are no invalid characters
01900             for (i = 0; i < line.length(); ++i) {
01901                 if (!isgraph(line[i]) && line[i] != ' ' && line[i] != '\t') {
01902                     throw XConfigRead(*this,
01903                                 "invalid character %{1}",
01904                                 CStringUtil::print("%#2x", line[i]));
01905                 }
01906             }
01907 
01908             return true;
01909         }
01910 
01911         // next line
01912         ++m_line;
01913     }
01914     return false;
01915 }
01916 
01917 UInt32
01918 CConfigReadContext::getLineNumber() const
01919 {
01920     return m_line;
01921 }
01922 
01923 CConfigReadContext::operator void*() const
01924 {
01925     return m_stream;
01926 }
01927 
01928 bool
01929 CConfigReadContext::operator!() const
01930 {
01931     return !m_stream;
01932 }
01933 
01934 OptionValue
01935 CConfigReadContext::parseBoolean(const CString& arg) const
01936 {
01937     if (CStringUtil::CaselessCmp::equal(arg, "true")) {
01938         return static_cast<OptionValue>(true);
01939     }
01940     if (CStringUtil::CaselessCmp::equal(arg, "false")) {
01941         return static_cast<OptionValue>(false);
01942     }
01943     throw XConfigRead(*this, "invalid boolean argument \"%{1}\"", arg);
01944 }
01945 
01946 OptionValue
01947 CConfigReadContext::parseInt(const CString& arg) const
01948 {
01949     const char* s = arg.c_str();
01950     char* end;
01951     long tmp      = strtol(s, &end, 10);
01952     if (*end != '\0') {
01953         // invalid characters
01954         throw XConfigRead(*this, "invalid integer argument \"%{1}\"", arg);
01955     }
01956     OptionValue value = static_cast<OptionValue>(tmp);
01957     if (value != tmp) {
01958         // out of range
01959         throw XConfigRead(*this, "integer argument \"%{1}\" out of range", arg);
01960     }
01961     return value;
01962 }
01963 
01964 OptionValue
01965 CConfigReadContext::parseModifierKey(const CString& arg) const
01966 {
01967     if (CStringUtil::CaselessCmp::equal(arg, "shift")) {
01968         return static_cast<OptionValue>(kKeyModifierIDShift);
01969     }
01970     if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) {
01971         return static_cast<OptionValue>(kKeyModifierIDControl);
01972     }
01973     if (CStringUtil::CaselessCmp::equal(arg, "alt")) {
01974         return static_cast<OptionValue>(kKeyModifierIDAlt);
01975     }
01976     if (CStringUtil::CaselessCmp::equal(arg, "altgr")) {
01977         return static_cast<OptionValue>(kKeyModifierIDAltGr);
01978     }
01979     if (CStringUtil::CaselessCmp::equal(arg, "meta")) {
01980         return static_cast<OptionValue>(kKeyModifierIDMeta);
01981     }
01982     if (CStringUtil::CaselessCmp::equal(arg, "super")) {
01983         return static_cast<OptionValue>(kKeyModifierIDSuper);
01984     }
01985     if (CStringUtil::CaselessCmp::equal(arg, "none")) {
01986         return static_cast<OptionValue>(kKeyModifierIDNull);
01987     }
01988     throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
01989 }
01990 
01991 OptionValue
01992 CConfigReadContext::parseCorner(const CString& arg) const
01993 {
01994     if (CStringUtil::CaselessCmp::equal(arg, "left")) {
01995         return kTopLeftMask | kBottomLeftMask;
01996     }
01997     else if (CStringUtil::CaselessCmp::equal(arg, "right")) {
01998         return kTopRightMask | kBottomRightMask;
01999     }
02000     else if (CStringUtil::CaselessCmp::equal(arg, "top")) {
02001         return kTopLeftMask | kTopRightMask;
02002     }
02003     else if (CStringUtil::CaselessCmp::equal(arg, "bottom")) {
02004         return kBottomLeftMask | kBottomRightMask;
02005     }
02006     else if (CStringUtil::CaselessCmp::equal(arg, "top-left")) {
02007         return kTopLeftMask;
02008     }
02009     else if (CStringUtil::CaselessCmp::equal(arg, "top-right")) {
02010         return kTopRightMask;
02011     }
02012     else if (CStringUtil::CaselessCmp::equal(arg, "bottom-left")) {
02013         return kBottomLeftMask;
02014     }
02015     else if (CStringUtil::CaselessCmp::equal(arg, "bottom-right")) {
02016         return kBottomRightMask;
02017     }
02018     else if (CStringUtil::CaselessCmp::equal(arg, "none")) {
02019         return kNoCornerMask;
02020     }
02021     else if (CStringUtil::CaselessCmp::equal(arg, "all")) {
02022         return kAllCornersMask;
02023     }
02024     throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
02025 }
02026 
02027 OptionValue
02028 CConfigReadContext::parseCorners(const CString& args) const
02029 {
02030     // find first token
02031     CString::size_type i = args.find_first_not_of(" \t", 0);
02032     if (i == CString::npos) {
02033         throw XConfigRead(*this, "missing corner argument");
02034     }
02035     CString::size_type j = args.find_first_of(" \t", i);
02036 
02037     // parse first corner token
02038     OptionValue corners = parseCorner(args.substr(i, j - i));
02039 
02040     // get +/-
02041     i = args.find_first_not_of(" \t", j);
02042     while (i != CString::npos) {
02043         // parse +/-
02044         bool add;
02045         if (args[i] == '-') {
02046             add = false;
02047         }
02048         else if (args[i] == '+') {
02049             add = true;
02050         }
02051         else {
02052             throw XConfigRead(*this,
02053                             "invalid corner operator \"%{1}\"",
02054                             CString(args.c_str() + i, 1));
02055         }
02056 
02057         // get next corner token
02058         i = args.find_first_not_of(" \t", i + 1);
02059         j = args.find_first_of(" \t", i);
02060         if (i == CString::npos) {
02061             throw XConfigRead(*this, "missing corner argument");
02062         }
02063 
02064         // parse next corner token
02065         if (add) {
02066             corners |= parseCorner(args.substr(i, j - i));
02067         }
02068         else {
02069             corners &= ~parseCorner(args.substr(i, j - i));
02070         }
02071         i = args.find_first_not_of(" \t", j);
02072     }
02073 
02074     return corners;
02075 }
02076 
02077 CConfig::CInterval
02078 CConfigReadContext::parseInterval(const ArgList& args) const
02079 {
02080     if (args.size() == 0) {
02081         return CConfig::CInterval(0.0f, 1.0f);
02082     }
02083     if (args.size() != 2 || args[0].empty() || args[1].empty()) {
02084         throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
02085     }
02086 
02087     char* end;
02088     long startValue = strtol(args[0].c_str(), &end, 10);
02089     if (end[0] != '\0') {
02090         throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
02091     }
02092     long endValue = strtol(args[1].c_str(), &end, 10);
02093     if (end[0] != '\0') {
02094         throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
02095     }
02096 
02097     if (startValue < 0 || startValue > 100 ||
02098         endValue   < 0 || endValue   > 100 ||
02099         startValue >= endValue) {
02100         throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
02101     }
02102 
02103     return CConfig::CInterval(startValue / 100.0f, endValue / 100.0f);
02104 }
02105 
02106 void
02107 CConfigReadContext::parseNameWithArgs(
02108                 const CString& type, const CString& line,
02109                 const CString& delim, CString::size_type& index,
02110                 CString& name, ArgList& args) const
02111 {
02112     // skip leading whitespace
02113     CString::size_type i = line.find_first_not_of(" \t", index);
02114     if (i == CString::npos) {
02115         throw XConfigRead(*this, CString("missing ") + type);
02116     }
02117 
02118     // find end of name
02119     CString::size_type j = line.find_first_of(" \t(" + delim, i);
02120     if (j == CString::npos) {
02121         j = line.length();
02122     }
02123 
02124     // save name
02125     name = line.substr(i, j - i);
02126     args.clear();
02127 
02128     // is it okay to not find a delimiter?
02129     bool needDelim = (!delim.empty() && delim.find('\n') == CString::npos);
02130 
02131     // skip whitespace
02132     i = line.find_first_not_of(" \t", j);
02133     if (i == CString::npos && needDelim) {
02134         // expected delimiter but didn't find it
02135         throw XConfigRead(*this, CString("missing ") + delim[0]);
02136     }
02137     if (i == CString::npos) {
02138         // no arguments
02139         index = line.length();
02140         return;
02141     }
02142     if (line[i] != '(') {
02143         // no arguments
02144         index = i;
02145         return;
02146     }
02147 
02148     // eat '('
02149     ++i;
02150 
02151     // parse arguments
02152     j = line.find_first_of(",)", i);
02153     while (j != CString::npos) {
02154         // extract arg
02155         CString arg(line.substr(i, j - i));
02156         i = j;
02157 
02158         // trim whitespace
02159         j = arg.find_first_not_of(" \t");
02160         if (j != CString::npos) {
02161             arg.erase(0, j);
02162         }
02163         j = arg.find_last_not_of(" \t");
02164         if (j != CString::npos) {
02165             arg.erase(j + 1);
02166         }
02167 
02168         // save arg
02169         args.push_back(arg);
02170 
02171         // exit loop at end of arguments
02172         if (line[i] == ')') {
02173             break;
02174         }
02175 
02176         // eat ','
02177         ++i;
02178 
02179         // next
02180         j = line.find_first_of(",)", i);
02181     }
02182 
02183     // verify ')'
02184     if (j == CString::npos) {
02185         // expected )
02186         throw XConfigRead(*this, "missing )");
02187     }
02188 
02189     // eat ')'
02190     ++i;
02191 
02192     // skip whitespace
02193     j = line.find_first_not_of(" \t", i);
02194     if (j == CString::npos && needDelim) {
02195         // expected delimiter but didn't find it
02196         throw XConfigRead(*this, CString("missing ") + delim[0]);
02197     }
02198 
02199     // verify delimiter
02200     if (needDelim && delim.find(line[j]) == CString::npos) {
02201         throw XConfigRead(*this, CString("expected ") + delim[0]);
02202     }
02203 
02204     if (j == CString::npos) {
02205         j = line.length();
02206     }
02207 
02208     index = j;
02209     return;
02210 }
02211 
02212 IPlatformScreen::CKeyInfo*
02213 CConfigReadContext::parseKeystroke(const CString& keystroke) const
02214 {
02215     return parseKeystroke(keystroke, std::set<CString>());
02216 }
02217 
02218 IPlatformScreen::CKeyInfo*
02219 CConfigReadContext::parseKeystroke(const CString& keystroke,
02220                 const std::set<CString>& screens) const
02221 {
02222     CString s = keystroke;
02223 
02224     KeyModifierMask mask;
02225     if (!CKeyMap::parseModifiers(s, mask)) {
02226         throw XConfigRead(*this, "unable to parse key modifiers");
02227     }
02228 
02229     KeyID key;
02230     if (!CKeyMap::parseKey(s, key)) {
02231         throw XConfigRead(*this, "unable to parse key");
02232     }
02233 
02234     if (key == kKeyNone && mask == 0) {
02235         throw XConfigRead(*this, "missing key and/or modifiers in keystroke");
02236     }
02237 
02238     return IPlatformScreen::CKeyInfo::alloc(key, mask, 0, 0, screens);
02239 }
02240 
02241 IPlatformScreen::CButtonInfo*
02242 CConfigReadContext::parseMouse(const CString& mouse) const
02243 {
02244     CString s = mouse;
02245 
02246     KeyModifierMask mask;
02247     if (!CKeyMap::parseModifiers(s, mask)) {
02248         throw XConfigRead(*this, "unable to parse button modifiers");
02249     }
02250 
02251     char* end;
02252     ButtonID button = (ButtonID)strtol(s.c_str(), &end, 10);
02253     if (*end != '\0') {
02254         throw XConfigRead(*this, "unable to parse button");
02255     }
02256     if (s.empty() || button <= 0) {
02257         throw XConfigRead(*this, "invalid button");
02258     }
02259 
02260     return IPlatformScreen::CButtonInfo::alloc(button, mask);
02261 }
02262 
02263 KeyModifierMask
02264 CConfigReadContext::parseModifier(const CString& modifiers) const
02265 {
02266     CString s = modifiers;
02267 
02268     KeyModifierMask mask;
02269     if (!CKeyMap::parseModifiers(s, mask)) {
02270         throw XConfigRead(*this, "unable to parse modifiers");
02271     }
02272 
02273     if (mask == 0) {
02274         throw XConfigRead(*this, "no modifiers specified");
02275     }
02276 
02277     return mask;
02278 }
02279 
02280 CString
02281 CConfigReadContext::concatArgs(const ArgList& args)
02282 {
02283     CString s("(");
02284     for (size_t i = 0; i < args.size(); ++i) {
02285         if (i != 0) {
02286             s += ",";
02287         }
02288         s += args[i];
02289     }
02290     s += ")";
02291     return s;
02292 }
02293 
02294 
02295 //
02296 // CConfig I/O exceptions
02297 //
02298 
02299 XConfigRead::XConfigRead(const CConfigReadContext& context,
02300                 const CString& error) :
02301     m_error(CStringUtil::print("line %d: %s",
02302                             context.getLineNumber(), error.c_str()))
02303 {
02304     // do nothing
02305 }
02306 
02307 XConfigRead::XConfigRead(const CConfigReadContext& context,
02308                 const char* errorFmt, const CString& arg) :
02309     m_error(CStringUtil::print("line %d: ", context.getLineNumber()) +
02310                             CStringUtil::format(errorFmt, arg.c_str()))
02311 {
02312     // do nothing
02313 }
02314 
02315 XConfigRead::~XConfigRead()
02316 {
02317     // do nothing
02318 }
02319 
02320 CString
02321 XConfigRead::getWhat() const throw()
02322 {
02323     return format("XConfigRead", "read error: %{1}", m_error.c_str());
02324 }

Generated on Sat May 25 2013 00:00:03 for Synergy by  doxygen 1.7.1