Back to index

easystroke  0.5.5.1
actiondb.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008-2009, Thomas Jaeger <ThJaeger@gmail.com>
00003  *
00004  * Permission to use, copy, modify, and/or distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00011  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
00013  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
00014  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 #include "actiondb.h"
00017 #include "main.h"
00018 #include "win.h"
00019 #include <glibmm/i18n.h>
00020 
00021 #include <iostream>
00022 #include <fstream>
00023 #include <boost/archive/text_oarchive.hpp>
00024 #include <boost/archive/text_iarchive.hpp>
00025 #include <boost/serialization/map.hpp>
00026 #include <boost/serialization/set.hpp>
00027 #include <boost/serialization/list.hpp>
00028 #include <boost/serialization/vector.hpp>
00029 #include <boost/serialization/export.hpp>
00030 #include <boost/serialization/shared_ptr.hpp>
00031 
00032 BOOST_CLASS_EXPORT(StrokeSet)
00033 
00034 BOOST_CLASS_EXPORT(Action)
00035 BOOST_CLASS_EXPORT(Command)
00036 BOOST_CLASS_EXPORT(ModAction)
00037 BOOST_CLASS_EXPORT(SendKey)
00038 BOOST_CLASS_EXPORT(SendText)
00039 BOOST_CLASS_EXPORT(Scroll)
00040 BOOST_CLASS_EXPORT(Ignore)
00041 BOOST_CLASS_EXPORT(Button)
00042 BOOST_CLASS_EXPORT(Misc)
00043 
00044 template<class Archive> void Unique::serialize(Archive & ar, const unsigned int version) {}
00045 
00046 template<class Archive> void Action::serialize(Archive & ar, const unsigned int version) {}
00047 
00048 template<class Archive> void Command::serialize(Archive & ar, const unsigned int version) {
00049        ar & boost::serialization::base_object<Action>(*this);
00050        ar & cmd;
00051 }
00052 
00053 template<class Archive> void ModAction::serialize(Archive & ar, const unsigned int version) {
00054        ar & boost::serialization::base_object<Action>(*this);
00055        ar & mods;
00056 }
00057 
00058 template<class Archive> void SendKey::load(Archive & ar, const unsigned int version) {
00059        guint code;
00060        ar & boost::serialization::base_object<ModAction>(*this);
00061        ar & key;
00062        ar & code;
00063        if (version < 1) {
00064               bool xtest;
00065               ar & xtest;
00066        }
00067 }
00068 
00069 template<class Archive> void SendKey::save(Archive & ar, const unsigned int version) const {
00070        guint code = 0;
00071        ar & boost::serialization::base_object<ModAction>(*this);
00072        ar & key;
00073        ar & code;
00074 }
00075 
00076 template<class Archive> void SendText::load(Archive & ar, const unsigned int version) {
00077        ar & boost::serialization::base_object<Action>(*this);
00078        std::string str;
00079        ar & str;
00080        text = str;
00081 }
00082 
00083 template<class Archive> void SendText::save(Archive & ar, const unsigned int version) const {
00084        ar & boost::serialization::base_object<Action>(*this);
00085        std::string str(text);
00086        ar & str;
00087 }
00088 
00089 template<class Archive> void Scroll::serialize(Archive & ar, const unsigned int version) {
00090        ar & boost::serialization::base_object<ModAction>(*this);
00091 }
00092 
00093 template<class Archive> void Ignore::serialize(Archive & ar, const unsigned int version) {
00094        ar & boost::serialization::base_object<ModAction>(*this);
00095 }
00096 
00097 template<class Archive> void Button::serialize(Archive & ar, const unsigned int version) {
00098        ar & boost::serialization::base_object<ModAction>(*this);
00099        ar & button;
00100 }
00101 
00102 template<class Archive> void Misc::serialize(Archive & ar, const unsigned int version) {
00103        ar & boost::serialization::base_object<Action>(*this);
00104        ar & type;
00105 }
00106 
00107 template<class Archive> void StrokeSet::serialize(Archive & ar, const unsigned int version) {
00108        ar & boost::serialization::base_object<std::set<RStroke> >(*this);
00109 }
00110 
00111 template<class Archive> void StrokeInfo::serialize(Archive & ar, const unsigned int version) {
00112        ar & strokes;
00113        ar & action;
00114        if (version == 0) return;
00115        ar & name;
00116 }
00117 
00118 using namespace std;
00119 
00120 void Command::run() {
00121        pid_t pid = fork();
00122        switch (pid) {
00123               case 0:
00124                      execlp("/bin/sh", "sh", "-c", cmd.c_str(), NULL);
00125                      exit(1);
00126               case -1:
00127                      printf(_("Error: can't execute command \"%s\": fork() failed\n"), cmd.c_str());
00128        }
00129 }
00130 
00131 ButtonInfo Button::get_button_info() const {
00132        ButtonInfo bi;
00133        bi.button = button;
00134        bi.state = mods;
00135        return bi;
00136 }
00137 
00138 
00139 const Glib::ustring Button::get_label() const {
00140        return get_button_info().get_button_text();
00141 }
00142 
00143 const Glib::ustring Misc::get_label() const { return _(types[type]); }
00144 
00145 const char *Misc::types[5] = { N_("None"), N_("Unminimize"), N_("Show/Hide"), N_("Disable (Enable)"), NULL };
00146 
00147 template<class Archive> void ActionListDiff::serialize(Archive & ar, const unsigned int version) {
00148        ar & deleted;
00149        ar & added;
00150        ar & name;
00151        ar & children;
00152        ar & app;
00153        if (version == 0)
00154               return;
00155        ar & order;
00156 }
00157 
00158 ActionDB::ActionDB() {
00159        root.name = _("Default");
00160 }
00161 
00162 template<class Archive> void ActionDB::load(Archive & ar, const unsigned int version) {
00163        if (version >= 2) {
00164               ar & root;
00165        }
00166        if (version == 1) {
00167               std::map<int, StrokeInfo> strokes;
00168               ar & strokes;
00169               for (std::map<int, StrokeInfo>::iterator i = strokes.begin(); i != strokes.end(); ++i)
00170                      root.add(i->second);
00171        }
00172        if (version == 0) {
00173               std::map<std::string, StrokeInfo> strokes;
00174               ar & strokes;
00175               for (std::map<std::string, StrokeInfo>::iterator i = strokes.begin(); i != strokes.end(); ++i) {
00176                      i->second.name = i->first;
00177                      root.add(i->second);
00178               }
00179        }
00180 
00181        root.fix_tree(version == 2);
00182        root.add_apps(apps);
00183        root.name = _("Default");
00184 }
00185 
00186 template<class Archive> void ActionDB::save(Archive & ar, const unsigned int version) const {
00187        ar & root;
00188 }
00189 
00190 Source<bool> action_dummy;
00191 
00192 void update_actions() {
00193        action_dummy.set(false);
00194 }
00195 
00196 void ActionDBWatcher::init() {
00197        std::string filename = config_dir+"actions";
00198        for (const char **v = actions_versions; *v; v++)
00199               if (is_file(filename + *v)) {
00200                      filename += *v;
00201                      try {
00202                             ifstream ifs(filename.c_str(), ios::binary);
00203                             if (!ifs.fail()) {
00204                                    boost::archive::text_iarchive ia(ifs);
00205                                    ia >> actions;
00206                                    if (verbosity >= 2)
00207                                           printf("Loaded actions.\n");
00208                             }
00209                      } catch (exception &e) {
00210                             printf(_("Error: Couldn't read action database: %s.\n"), e.what());
00211                      }
00212                      break;
00213               }
00214        watch(action_dummy);
00215 }
00216 
00217 void ActionDBWatcher::timeout() {
00218        std::string filename = config_dir+"actions"+actions_versions[0];
00219        std::string tmp = filename + ".tmp";
00220        try {
00221               ofstream ofs(tmp.c_str());
00222               boost::archive::text_oarchive oa(ofs);
00223               oa << (const ActionDB &)actions;
00224               ofs.close();
00225               if (rename(tmp.c_str(), filename.c_str()))
00226                      throw std::runtime_error(_("rename() failed"));
00227               if (verbosity >= 2)
00228                      printf("Saved actions.\n");
00229        } catch (exception &e) {
00230               printf(_("Error: Couldn't save action database: %s.\n"), e.what());
00231               if (!good_state)
00232                      return;
00233               good_state = false;
00234               error_dialog(Glib::ustring::compose(_( "Couldn't save %1.  Your changes will be lost.  "
00235                             "Make sure that \"%2\" is a directory and that you have write access to it.  "
00236                             "You can change the configuration directory "
00237                             "using the -c or --config-dir command line options."), _("actions"), config_dir));
00238        }
00239 }
00240 
00241 
00242 RStrokeInfo ActionListDiff::get_info(Unique *id, bool *deleted, bool *stroke, bool *name, bool *action) const {
00243        if (deleted)
00244               *deleted = this->deleted.count(id);
00245        if (stroke)
00246               *stroke = false;
00247        if (name)
00248               *name = false;
00249        if (action)
00250               *action = false;
00251        RStrokeInfo si = parent ? parent->get_info(id) : RStrokeInfo(new StrokeInfo);
00252        std::map<Unique *, StrokeInfo>::const_iterator i = added.find(id);
00253        for (i = added.begin(); i != added.end(); i++) {
00254               if (i->first == id)
00255                      break;
00256        }
00257        if (i == added.end()) {
00258               return si;
00259        }
00260        if (i->second.name != "") {
00261               si->name = i->second.name;
00262               if (name)
00263                      *name = parent;
00264        }
00265        if (i->second.strokes.size()) {
00266               si->strokes = i->second.strokes;
00267               if (stroke)
00268                      *stroke = parent;
00269        }
00270        if (i->second.action) {
00271               si->action = i->second.action;
00272               if (action)
00273                      *action = parent;
00274        }
00275        return si;
00276 }
00277 
00278 boost::shared_ptr<std::map<Unique *, StrokeSet> > ActionListDiff::get_strokes() const {
00279        boost::shared_ptr<std::map<Unique *, StrokeSet> > strokes = parent ? parent->get_strokes() :
00280               boost::shared_ptr<std::map<Unique *, StrokeSet> >(new std::map<Unique *, StrokeSet>);
00281        for (std::set<Unique *>::const_iterator i = deleted.begin(); i != deleted.end(); i++)
00282               strokes->erase(*i);
00283        for (std::map<Unique *, StrokeInfo>::const_iterator i = added.begin(); i != added.end(); i++)
00284               if (i->second.strokes.size())
00285                      (*strokes)[i->first] = i->second.strokes;
00286        return strokes;
00287 }
00288 
00289 boost::shared_ptr<std::set<Unique *> > ActionListDiff::get_ids(bool include_deleted) const {
00290        boost::shared_ptr<std::set<Unique *> > ids = parent ? parent->get_ids(false) :
00291               boost::shared_ptr<std::set<Unique *> >(new std::set<Unique *>);
00292        if (!include_deleted)
00293               for (std::set<Unique *>::const_iterator i = deleted.begin(); i != deleted.end(); i++)
00294                      ids->erase(*i);
00295        for (std::map<Unique *, StrokeInfo>::const_iterator i = added.begin(); i != added.end(); i++)
00296               ids->insert(i->first);
00297        return ids;
00298 }
00299 
00300 void ActionListDiff::all_strokes(std::list<RStroke> &strokes) const {
00301        for (std::map<Unique *, StrokeInfo>::const_iterator i = added.begin(); i != added.end(); i++)
00302               for (std::set<RStroke>::const_iterator j = i->second.strokes.begin(); j != i->second.strokes.end(); j++)
00303                      strokes.push_back(*j);
00304        for (std::list<ActionListDiff>::const_iterator i = children.begin(); i != children.end(); i++)
00305               i->all_strokes(strokes);
00306 }
00307 
00308 RAction ActionListDiff::handle(RStroke s, RRanking &r) const {
00309        if (!s)
00310               return RAction();
00311        r.reset(new Ranking);
00312        r->stroke = s;
00313        r->score = 0.0;
00314        boost::shared_ptr<std::map<Unique *, StrokeSet> > strokes = get_strokes();
00315        for (std::map<Unique *, StrokeSet>::const_iterator i = strokes->begin(); i!=strokes->end(); i++) {
00316               for (StrokeSet::iterator j = i->second.begin(); j!=i->second.end(); j++) {
00317                      double score;
00318                      int match = Stroke::compare(s, *j, score);
00319                      if (match < 0)
00320                             continue;
00321                      RStrokeInfo si = get_info(i->first);
00322                      r->r.insert(pair<double, pair<std::string, RStroke> >
00323                                    (score, pair<std::string, RStroke>(si->name, *j)));
00324                      if (score > r->score) {
00325                             r->score = score;
00326                             if (match) {
00327                                    r->name = si->name;
00328                                    r->action = si->action;
00329                                    r->best_stroke = *j;
00330                             }
00331                      }
00332               }
00333        }
00334        if (!r->action && s->trivial())
00335               return RAction(new Click);
00336        if (r->action) {
00337               if (verbosity >= 1)
00338                      printf("Executing Action %s\n", r->name.c_str());
00339        } else {
00340               if (verbosity >= 1)
00341                      printf("Couldn't find matching stroke.\n");
00342        }
00343        return r->action;
00344 }
00345 
00346 void ActionListDiff::handle_advanced(RStroke s, std::map<guint, RAction> &as,
00347               std::map<guint, RRanking> &rs, int b1, int b2) const {
00348        if (!s)
00349               return;
00350        boost::shared_ptr<std::map<Unique *, StrokeSet> > strokes = get_strokes();
00351        for (std::map<Unique *, StrokeSet>::const_iterator i = strokes->begin(); i!=strokes->end(); i++) {
00352               for (StrokeSet::iterator j = i->second.begin(); j!=i->second.end(); j++) {
00353                      int b = (*j)->button;
00354                      if (!s->timeout && !b)
00355                             continue;
00356                      s->button = b;
00357                      double score;
00358                      int match = Stroke::compare(s, *j, score);
00359                      if (match < 0)
00360                             continue;
00361                      Ranking *r;
00362                      if (b == b1)
00363                             b = b2;
00364                      if (rs.count(b)) {
00365                             r = rs[b].get();
00366                      } else {
00367                             r = new Ranking;
00368                             rs[b].reset(r);
00369                             r->stroke = RStroke(new Stroke(*s));
00370                             r->score = -1;
00371                      }
00372                      RStrokeInfo si = get_info(i->first);
00373                      r->r.insert(pair<double, pair<std::string, RStroke> >
00374                                    (score, pair<std::string, RStroke>(si->name, *j)));
00375                      if (score > r->score) {
00376                             r->score = score;
00377                             if (match) {
00378                                    r->name = si->name;
00379                                    r->action = si->action;
00380                                    r->best_stroke = *j;
00381                                    as[b] = si->action;
00382                             }
00383                      }
00384               }
00385        }
00386 }
00387 
00388 ActionListDiff::~ActionListDiff() {
00389        if (app)
00390               actions.apps.erase(name);
00391 }
00392 
00393 ActionDB actions;