Back to index

easystroke  0.5.5.1
prefs.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 "prefs.h"
00017 #include "win.h"
00018 #include "main.h"
00019 #include "grabber.h"
00020 #include <glibmm/i18n.h>
00021 
00022 #include <set>
00023 #include <iostream>
00024 
00025 class Check : private Base {
00026        IO<bool> &io;
00027        Gtk::CheckButton *check;
00028        virtual void notify() { check->set_active(io.get()); }
00029        void on_changed() {
00030               bool b = check->get_active();
00031               if (b == io.get()) return;
00032               io.set(b);
00033        }
00034 public:
00035        Check(IO<bool> &io_, const Glib::ustring &name) : io(io_) {
00036               io.connect(this);
00037               widgets->get_widget(name, check);
00038               notify();
00039               check->signal_toggled().connect(sigc::mem_fun(*this, &Check::on_changed));
00040        }
00041 };
00042 
00043 template <class T> class Adjustment : private Base {
00044        IO<T> &io;
00045        Glib::RefPtr<Gtk::Adjustment> adj;
00046        Gtk::Button *button;
00047        virtual void notify() { adj->set_value(io.get()); }
00048        void on_changed() {
00049               T i = (T)adj->get_value();
00050               if (i == io.get()) return;
00051               io.set(i);
00052        }
00053 public:
00054        Adjustment(IO<T> &io_, const Glib::ustring & name) : io(io_) {
00055               io.connect(this);
00056               adj = Glib::RefPtr<Gtk::Adjustment>::cast_dynamic(widgets->get_object(name));
00057               notify();
00058               adj->signal_value_changed().connect(sigc::mem_fun(*this, &Adjustment::on_changed));
00059        }
00060 };
00061 
00062 class Color : private Base {
00063        IO<RGBA> &io;
00064        Gtk::ColorButton *color;
00065        virtual void notify() {
00066               color->set_color(io.get().color);
00067               color->set_alpha(io.get().alpha);
00068        }
00069        void on_changed() {
00070               RGBA rgba;
00071               rgba.color = color->get_color();
00072               rgba.alpha = color->get_alpha();
00073               if (rgba == io.get()) return;
00074               io.set(rgba);
00075        }
00076 public:
00077        Color(IO<RGBA> &io_, const Glib::ustring &name) : io(io_) {
00078               io.connect(this);
00079               widgets->get_widget(name, color);
00080               color->set_use_alpha();
00081               notify();
00082               color->signal_color_set().connect(sigc::mem_fun(*this, &Color::on_changed));
00083        }
00084 };
00085 
00086 template <class T> class Combo : private Base {
00087 public:
00088        struct Info {
00089               T value;
00090               const char* name;
00091        };
00092 private:
00093        IO<T> &io;
00094        const Info *info;
00095        Gtk::ComboBoxText *combo;
00096        virtual void notify() {
00097               T value = io.get();
00098               int i = 0;
00099               while (info[i].name && info[i].value != value) i++;
00100               combo->set_active(i);
00101        }
00102        void on_changed() {
00103               int row = combo->get_active_row_number();
00104               if (row < 0)
00105                      return;
00106               T value = info[row].value;
00107               if (value == io.get())
00108                      return;
00109               io.set(value);
00110        }
00111 public:
00112        Combo(IO<T> &io_, const Glib::ustring &name, const Info *info_) : io(io_), info(info_) {
00113               io.connect(this);
00114               Gtk::Bin *parent;
00115               widgets->get_widget(name, parent);
00116               combo = Gtk::manage(new Gtk::ComboBoxText);
00117               parent->add(*combo);
00118               for (const Info *i = info; i->name; i++)
00119                      combo->append_text(_(i->name));
00120               notify();
00121               combo->signal_changed().connect(sigc::mem_fun(*this, &Combo::on_changed));
00122               combo->show();
00123        }
00124 };
00125 
00126 template <class T> class ButtonSet {
00127        IO<T> &io;
00128        T def;
00129        void on_click() { io.set(def); }
00130 public:
00131        ButtonSet(IO<T> &io_, const Glib::ustring &name, T def_) : io(io_), def(def_) {
00132               Gtk::Button *button;
00133               widgets->get_widget(name, button);
00134               button->signal_clicked().connect(sigc::mem_fun(*this, &ButtonSet::on_click));
00135        }
00136 };
00137 
00138 class Sensitive : private Base {
00139        Gtk::Widget *widget;
00140        Out<bool> &in;
00141 public:
00142        virtual void notify() { widget->set_sensitive(in.get()); }
00143        Sensitive(Out<bool> &in_, const Glib::ustring &name) : in(in_) {
00144               in.connect(this);
00145               widgets->get_widget(name, widget);
00146               notify();
00147        }
00148 };
00149 
00150 static bool is_custom(TimeoutType profile) { return profile == TimeoutCustom; }
00151 static bool draw_trace(TraceType t) { return t == TraceDefault || t == TraceShape; }
00152 
00153 const Combo<TraceType>::Info trace_info[] = {
00154        { TraceNone, N_("None") },
00155        { TraceDefault, N_("Default") },
00156        { TraceShape, N_("XShape") },
00157        { TraceAnnotate, N_("Annotate (compiz)") },
00158        { TraceFire, N_("Fire (compiz)") },
00159        { TraceWater, N_("Water (compiz)") },
00160        { TraceDefault, 0 }
00161 };
00162 
00163 const Combo<TimeoutType>::Info timeout_info[] = {
00164        { TimeoutOff, N_("Timeout Off") },
00165        { TimeoutConservative, N_("Conservative") },
00166        { TimeoutDefault, N_("Default") },
00167        { TimeoutMedium, N_("Medium") },
00168        { TimeoutAggressive, N_("Aggressive") },
00169        { TimeoutFlick, N_("Flick") },
00170        { TimeoutDefault, 0 }
00171 };
00172 
00173 const Combo<TimeoutType>::Info timeout_info_exp[] = {
00174        { TimeoutOff, N_("Timeout Off") },
00175        { TimeoutConservative, N_("Conservative") },
00176        { TimeoutDefault, N_("Default") },
00177        { TimeoutMedium, N_("Medium") },
00178        { TimeoutAggressive, N_("Aggressive") },
00179        { TimeoutFlick, N_("Flick") },
00180        { TimeoutCustom, N_("Custom") },
00181        { TimeoutDefault, 0 }
00182 };
00183 
00184 Source<bool> autostart_ok(true);
00185 
00186 #include <sys/stat.h>
00187 extern const char *desktop_file;
00188 
00189 class Autostart : public IO<bool>, private Base {
00190        bool a;
00191        std::string filename;
00192 public:
00193        Autostart() {
00194               std::string dir = getenv("HOME");
00195               dir += "/.config/autostart";
00196               filename = dir + "/easystroke.desktop";
00197 
00198               if (!is_dir(dir) && mkdir(dir.c_str(), 0777)) {
00199                      autostart_ok.set(false);
00200                      return;
00201               }
00202               a = is_file(filename);
00203        }
00204        virtual void set(const bool a_) {
00205               a = a_;
00206               notify();
00207        }
00208        virtual bool get() const { return a; }
00209        virtual void notify() {
00210               if (a) {
00211                      FILE *file = fopen(filename.c_str(), "w");
00212                      if (!file || fprintf(file, desktop_file, "easystroke") == -1)
00213                             autostart_ok.set(false);
00214                      if (file)
00215                             fclose(file);
00216               } else {
00217                      if (remove(filename.c_str()) == -1)
00218                             autostart_ok.set(false);
00219               }
00220               update();
00221        }
00222 };
00223 
00224 Autostart autostart;
00225 
00226 void remove_last_entry(const Glib::ustring & name) {
00227        Gtk::ComboBox *combo;
00228        widgets->get_widget(name, combo);
00229        Glib::RefPtr<Gtk::ListStore> combo_model = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(combo->get_model());
00230        Gtk::TreeIter i = combo_model->children().end();
00231        combo_model->erase(--i);
00232 }
00233 
00234 Prefs::Prefs() {
00235        new Check(prefs.advanced_ignore, "check_advanced_ignore");
00236 
00237        new Check(prefs.pressure_abort, "check_pressure_abort");
00238        new Adjustment<int>(prefs.pressure_threshold, "adjustment_pressure_threshold");
00239        new Sensitive(prefs.pressure_abort, "spin_pressure_threshold");
00240        new Sensitive(prefs.pressure_abort, "button_default_pressure_threshold");
00241 
00242        new Check(prefs.proximity, "check_proximity");
00243 
00244        new Check(prefs.feedback, "check_feedback");
00245        new Check(prefs.left_handed, "check_left_handed");
00246        new Sensitive(prefs.feedback, "check_left_handed");
00247        new Check(prefs.advanced_popups, "check_advanced_popups");
00248        new Sensitive(prefs.feedback, "check_advanced_popups");
00249 
00250        new Check(prefs.tray_icon, "check_tray_icon");
00251        new Sensitive(prefs.tray_icon, "check_tray_feedback");
00252        new Check(prefs.tray_feedback, "check_tray_feedback");
00253 
00254        new Check(autostart, "check_autostart");
00255        new Sensitive(autostart_ok, "check_autostart");
00256 
00257        new Adjustment<int>(prefs.init_timeout, "adjustment_init_timeout");
00258        new Adjustment<int>(prefs.final_timeout, "adjustment_final_timeout");
00259 
00260        new ButtonSet<int>(prefs.pressure_threshold, "button_default_pressure_threshold", default_pressure_threshold);
00261 
00262        new Combo<TraceType>(prefs.trace, "box_trace", trace_info);
00263        new Color(prefs.color, "button_color");
00264        new Adjustment<int>(prefs.trace_width, "adjustment_trace_width");
00265        new Combo<TimeoutType>(prefs.timeout_profile, "box_timeout", experimental ? timeout_info_exp : timeout_info);
00266 
00267        new Check(prefs.timeout_gestures, "check_timeout_gestures");
00268 
00269        new Check(prefs.scroll_invert, "check_scroll_invert");
00270        new Adjustment<double>(prefs.scroll_speed, "adjustment_scroll_speed");
00271 
00272        new Check(prefs.move_back, "check_move_back");
00273 
00274        new Check(prefs.show_osd, "check_osd");
00275 
00276        Gtk::Button *bbutton, *add_exception, *remove_exception, *add_extra, *edit_extra, *remove_extra;
00277        widgets->get_widget("button_add_exception", add_exception);
00278        widgets->get_widget("button_button", bbutton);
00279        widgets->get_widget("button_remove_exception", remove_exception);
00280        widgets->get_widget("label_button", blabel);
00281        widgets->get_widget("button_add_extra", add_extra);
00282        widgets->get_widget("button_edit_extra", edit_extra);
00283        widgets->get_widget("button_remove_extra", remove_extra);
00284        widgets->get_widget("treeview_exceptions", tv);
00285        widgets->get_widget("treeview_devices", dtv);
00286        widgets->get_widget("treeview_extra", etv);
00287 
00288        new Sensitive(*fun(&is_custom, prefs.timeout_profile), "hbox_timeout");
00289        /*
00290        new Sensitive(supports_pressure, "hbox_pressure");
00291        new Sensitive(supports_proximity, "check_proximity");
00292        */
00293        new Sensitive(*fun(&draw_trace, prefs.trace), "button_color");
00294        new Sensitive(*fun(&draw_trace, prefs.trace), "spin_trace_width");
00295 
00296        tm = Gtk::ListStore::create(cols);
00297        tv->set_model(tm);
00298        tv->append_column(_("Application (WM__CLASS)"), cols.user_app);
00299        tm->set_sort_column(cols.user_app, Gtk::SORT_ASCENDING);
00300 
00301        CellRendererTextish *button_renderer = Gtk::manage(new CellRendererTextish);
00302        button_renderer->mode = CellRendererTextish::POPUP;
00303        tv->append_column(_("Button"), *button_renderer);
00304        Gtk::TreeView::Column *col_button = tv->get_column(1);
00305        col_button->add_attribute(button_renderer->property_text(), cols.button);
00306        button_renderer->property_editable() = true;
00307        button_renderer->signal_editing_started().connect(sigc::mem_fun(*this, &Prefs::on_button_editing_started));
00308 
00309        bbutton->signal_clicked().connect(sigc::mem_fun(*this, &Prefs::on_select_button));
00310 
00311        add_exception->signal_clicked().connect(sigc::mem_fun(*this, &Prefs::on_add));
00312        remove_exception->signal_clicked().connect(sigc::mem_fun(*this, &Prefs::on_remove));
00313 
00314        dtm = Gtk::ListStore::create(dcs);
00315        dtv->set_model(dtm);
00316        dtv->append_column_editable(_("Enabled"), dcs.enabled);
00317        int n = dtv->append_column(_("Device"), dcs.name);
00318        Gtk::TreeView::Column *col_device = dtv->get_column(n-1);
00319        col_device->set_expand();
00320 
00321        Glib::RefPtr<Gtk::ListStore> timeout_model = Gtk::ListStore::create(timeout_columns);
00322        {
00323               Gtk::TreeModel::Row row = *(timeout_model->append());
00324               row[timeout_columns.name] = _("<unchanged>");
00325        }
00326        for (const Combo<TimeoutType>::Info *i = timeout_info; i->name; i++) {
00327               Gtk::TreeModel::Row row = *(timeout_model->append());
00328               row[timeout_columns.name] = i->name;
00329        }
00330 
00331        Gtk::CellRendererCombo *timeout_renderer = Gtk::manage(new Gtk::CellRendererCombo);
00332        timeout_renderer->property_model() = timeout_model;
00333        timeout_renderer->property_editable() = true;
00334        timeout_renderer->property_text_column() = 0;
00335        timeout_renderer->property_has_entry() = false;
00336         timeout_renderer->signal_edited().connect(sigc::mem_fun(*this, &Prefs::on_device_timeout_changed));
00337        n = dtv->append_column(_("Timeout profile"), *timeout_renderer);
00338        Gtk::TreeView::Column *col_timeout = dtv->get_column(n-1);
00339        col_timeout->add_attribute(timeout_renderer->property_text(), dcs.timeout);
00340 
00341        dtm->signal_row_changed().connect(sigc::mem_fun(*this, &Prefs::on_device_toggled));
00342        update_device_list();
00343 
00344        add_extra->signal_clicked().connect(sigc::mem_fun(*this, &Prefs::on_add_extra));
00345        edit_extra->signal_clicked().connect(sigc::mem_fun(*this, &Prefs::on_edit_extra));
00346        remove_extra->signal_clicked().connect(sigc::mem_fun(*this, &Prefs::on_remove_extra));
00347 
00348        etm = Gtk::ListStore::create(ecs);
00349        etv->set_model(etm);
00350        etv->append_column(_("Button"), ecs.str);
00351        update_extra_buttons();
00352 
00353        if (!experimental) {
00354               Gtk::HBox *hbox;
00355               widgets->get_widget("hbox_timeout", hbox);
00356               hbox->hide();
00357        }
00358        set_button_label();
00359 
00360        const std::map<std::string, RButtonInfo> &exceptions = prefs.exceptions.ref();
00361        for (std::map<std::string, RButtonInfo>::const_iterator i = exceptions.begin(); i!=exceptions.end(); i++) {
00362               Gtk::TreeModel::Row row = *(tm->append());
00363               row[cols.app] = i->first;
00364               row[cols.user_app] = app_name_hr(i->first);
00365               row[cols.button] = i->second ? i->second->get_button_text() : _("<App disabled>");
00366        }
00367 }
00368 
00369 void Prefs::update_device_list() {
00370        ignore_device_toggled = true;
00371        dtm->clear();
00372        std::set<std::string> names;
00373        for (Grabber::DeviceMap::iterator i = grabber->xi_devs.begin(); i != grabber->xi_devs.end(); ++i) {
00374               std::string name = i->second->name;
00375               if (names.count(name))
00376                      continue;
00377               names.insert(name);
00378               Gtk::TreeModel::Row row = *(dtm->append());
00379               row[dcs.enabled] = !prefs.excluded_devices.get().count(name);
00380               row[dcs.name] = name;
00381               row[dcs.timeout] = _("<unchanged>");
00382 
00383               const std::map<std::string, TimeoutType> &dt = prefs.device_timeout.ref();
00384               std::map<std::string, TimeoutType>::const_iterator j = dt.find(name);
00385               if (j != dt.end())
00386                      for (const Combo<TimeoutType>::Info *i = timeout_info; i->name; i++)
00387                             if (j->second == i->value)
00388                                    row[dcs.timeout] = i->name;
00389        }
00390        ignore_device_toggled = false;
00391 }
00392 
00393 void Prefs::update_extra_buttons() {
00394        etm->clear();
00395        std::vector<ButtonInfo> &extra = prefs.extra_buttons.unsafe_ref();
00396        for (std::vector<ButtonInfo>::iterator i = extra.begin(); i != extra.end(); i++) {
00397               Gtk::TreeModel::Row row = *(etm->append());
00398               row[ecs.str] = i->get_button_text();
00399               row[ecs.i] = i;
00400        }
00401 }
00402 
00403 void Prefs::on_add_extra() {
00404        ButtonInfo bi;
00405        SelectButton sb(bi, true, true);
00406        if (!sb.run())
00407               return;
00408        Atomic a;
00409        std::vector<ButtonInfo> &extra = prefs.extra_buttons.write_ref(a);
00410        for (std::vector<ButtonInfo>::iterator i = extra.begin(); i != extra.end();)
00411               if (i->overlap(sb.event))
00412                      i = extra.erase(i);
00413               else
00414                      i++;
00415        extra.push_back(sb.event);
00416        stable_sort(extra.begin(), extra.end());
00417        update_extra_buttons();
00418 }
00419 
00420 void Prefs::on_edit_extra() {
00421        Gtk::TreePath path;
00422        Gtk::TreeViewColumn *col;
00423        etv->get_cursor(path, col);
00424        if (!path.gobj())
00425               return;
00426        Gtk::TreeIter iter = *etm->get_iter(path);
00427        std::vector<ButtonInfo>::iterator i = (*iter)[ecs.i];
00428        SelectButton sb(*i, true, true);
00429        if (!sb.run())
00430               return;
00431        Atomic a;
00432        std::vector<ButtonInfo> &extra = prefs.extra_buttons.write_ref(a);
00433        for (std::vector<ButtonInfo>::iterator j = extra.begin(); j != extra.end();)
00434               if (j != i && j->overlap(sb.event))
00435                      j = extra.erase(j);
00436               else
00437                      j++;
00438        *i = sb.event;
00439        update_extra_buttons();
00440 }
00441 
00442 void Prefs::on_remove_extra() {
00443        Gtk::TreePath path;
00444        Gtk::TreeViewColumn *col;
00445        etv->get_cursor(path, col);
00446        if (!path.gobj())
00447               return;
00448        Gtk::TreeIter iter = *etm->get_iter(path);
00449        Atomic a;
00450        std::vector<ButtonInfo>::iterator i = (*iter)[ecs.i];
00451        prefs.extra_buttons.write_ref(a).erase(i);
00452        update_extra_buttons();
00453 }
00454 
00455 SelectButton::SelectButton(ButtonInfo bi, bool def, bool any) {
00456        widgets->get_widget("dialog_select", dialog);
00457        dialog->set_message(_("Select a Mouse or Pen Button"));
00458        dialog->set_secondary_text(_("Please place your mouse or pen in the box below and press the button that you want to select.  You can also hold down additional modifiers."));
00459        widgets->get_widget("eventbox", eventbox);
00460        widgets->get_widget("toggle_shift", toggle_shift);
00461        widgets->get_widget("toggle_alt", toggle_alt);
00462        widgets->get_widget("toggle_control", toggle_control);
00463        widgets->get_widget("toggle_super", toggle_super);
00464        widgets->get_widget("toggle_any", toggle_any);
00465        widgets->get_widget("radio_timeout_default", radio_timeout_default);
00466        widgets->get_widget("radio_instant", radio_instant);
00467        widgets->get_widget("radio_click_hold", radio_click_hold);
00468        Gtk::Bin *box_button;
00469        widgets->get_widget("box_button", box_button);
00470        Gtk::HBox *hbox_button_timeout;
00471        widgets->get_widget("hbox_button_timeout", hbox_button_timeout);
00472        select_button = dynamic_cast<Gtk::ComboBoxText *>(box_button->get_child());
00473        if (!select_button) {
00474               select_button = Gtk::manage(new Gtk::ComboBoxText);
00475               box_button->add(*select_button);
00476               for (int i = 1; i <= 12; i++)
00477                      select_button->append_text(Glib::ustring::compose(_("Button %1"), i));
00478               select_button->show();
00479        }
00480        select_button->set_active(bi.button-1);
00481        toggle_shift->set_active(bi.button && (bi.state & GDK_SHIFT_MASK));
00482        toggle_control->set_active(bi.button && (bi.state & GDK_CONTROL_MASK));
00483        toggle_alt->set_active(bi.button && (bi.state & GDK_MOD1_MASK));
00484        toggle_super->set_active(bi.button && (bi.state & GDK_SUPER_MASK));
00485        toggle_any->set_active(any && bi.button && bi.state == AnyModifier);
00486        if (any) {
00487               hbox_button_timeout->show();
00488               toggle_any->show();
00489        } else {
00490               hbox_button_timeout->hide();
00491               toggle_any->hide();
00492        }
00493        if (bi.instant)
00494               radio_instant->set_active();
00495        else if (bi.click_hold)
00496               radio_click_hold->set_active();
00497        else
00498               radio_timeout_default->set_active();
00499 
00500        Gtk::Button *select_default;
00501        widgets->get_widget("select_default", select_default);
00502        if (def)
00503               select_default->show();
00504        else
00505               select_default->hide();
00506 
00507        if (!eventbox->get_children().size()) {
00508               eventbox->set_events(Gdk::BUTTON_PRESS_MASK);
00509 
00510               Glib::RefPtr<Gdk::Pixbuf> pb = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB,true,8,400,200);
00511               pb->fill(0x808080ff);
00512               WIDGET(Gtk::Image, box, pb);
00513               eventbox->add(box);
00514               box.show();
00515        }
00516        handler[0] = eventbox->signal_button_press_event().connect(sigc::mem_fun(*this, &SelectButton::on_button_press));
00517        handler[1] = toggle_any->signal_toggled().connect(sigc::mem_fun(*this, &SelectButton::on_any_toggled));
00518        on_any_toggled();
00519 }
00520 
00521 SelectButton::~SelectButton() {
00522        handler[0].disconnect();
00523        handler[1].disconnect();
00524 }
00525 
00526 bool SelectButton::run() {
00527        grabber->queue_suspend();
00528        dialog->show();
00529        Gtk::Button *select_ok;
00530        widgets->get_widget("select_ok", select_ok);
00531        select_ok->grab_focus();
00532        int response;
00533        do {
00534               response = dialog->run();
00535        } while (!response);
00536        dialog->hide();
00537        grabber->queue_resume();
00538        switch (response) {
00539               case 1: // Okay
00540                      event.button = select_button->get_active_row_number() + 1;
00541                      if (!event.button)
00542                             return false;
00543                      event.state = 0;
00544                      if (toggle_any->get_active()) {
00545                             event.state = AnyModifier;
00546                             return true;
00547                      }
00548                      if (toggle_shift->get_active())
00549                             event.state |= GDK_SHIFT_MASK;
00550                      if (toggle_control->get_active())
00551                             event.state |= GDK_CONTROL_MASK;
00552                      if (toggle_alt->get_active())
00553                             event.state |= GDK_MOD1_MASK;
00554                      if (toggle_super->get_active())
00555                             event.state |= GDK_SUPER_MASK;
00556                      event.instant = radio_instant->get_active();
00557                      event.click_hold = radio_click_hold->get_active();
00558                      return true;
00559               case 2: // Default
00560                      event.button = 0;
00561                      event.state = 0;
00562                      event.instant = false;
00563                      return true;
00564               case 3: // Click - all the work has already been done
00565                      return true;
00566               case -1: // Cancel
00567               default: // Something went wrong
00568                      return false;
00569        }
00570 }
00571 
00572 bool SelectButton::on_button_press(GdkEventButton *ev) {
00573        event.button = ev->button;
00574        event.state  = ev->state;
00575        event.instant = radio_instant->get_active();
00576        event.click_hold = radio_click_hold->get_active();
00577        if (toggle_any->get_active()) {
00578               event.state = AnyModifier;
00579        } else {
00580               if (event.state & Mod4Mask)
00581                      event.state |= GDK_SUPER_MASK;
00582               event.state &= gtk_accelerator_get_default_mod_mask();
00583        }
00584        dialog->response(3);
00585        return true;
00586 }
00587 
00588 void SelectButton::on_any_toggled() {
00589        bool any = toggle_any->get_active(); 
00590        toggle_shift->set_sensitive(!any);
00591        toggle_control->set_sensitive(!any);
00592        toggle_alt->set_sensitive(!any);
00593        toggle_super->set_sensitive(!any);
00594 }
00595 
00596 void Prefs::on_select_button() {
00597        Atomic a;
00598        ButtonInfo &bi = prefs.button.write_ref(a);
00599        SelectButton sb(bi, true, true);
00600        if (!sb.run())
00601               return;
00602        bi = sb.event.button ? sb.event : default_button;
00603        set_button_label();
00604 }
00605 
00606 bool Prefs::select_row(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter, std::string name) {
00607        if ((std::string)(*iter)[cols.app] == name) {
00608               tv->set_cursor(path);
00609               return true;
00610        }
00611        return false;
00612 }
00613 
00614 void Prefs::on_add() {
00615        std::string str = select_window();
00616        bool is_new;
00617        {
00618               Atomic a;
00619               is_new = prefs.exceptions.write_ref(a).insert(
00620                             std::pair<std::string, RButtonInfo>(str, RButtonInfo())).second;
00621        }
00622        if (is_new) {
00623               Gtk::TreeModel::Row row = *(tm->append());
00624               row[cols.app] = str;
00625               row[cols.user_app] = app_name_hr(str);
00626               row[cols.button] = _("<App disabled>");
00627               Gtk::TreePath path = tm->get_path(row);
00628               tv->set_cursor(path);
00629        } else {
00630               tm->foreach(sigc::bind(sigc::mem_fun(*this, &Prefs::select_row), str));
00631        }
00632 }
00633 
00634 void Prefs::on_button_editing_started(Gtk::CellEditable* editable, const Glib::ustring& path) {
00635        Gtk::TreeRow row(*tm->get_iter(path));
00636        std::string app = row[cols.app];
00637        ButtonInfo bi;
00638        bi.button = 0;
00639        bi.state = 0;
00640        std::map<std::string, RButtonInfo>::const_iterator i = prefs.exceptions.ref().find(app);
00641        if (i != prefs.exceptions.ref().end() && i->second)
00642               bi = *i->second;
00643        SelectButton sb(bi, true, true);
00644        if (!sb.run())
00645               return;
00646        RButtonInfo bi2;
00647        if (sb.event.button)
00648               bi2.reset(new ButtonInfo(sb.event));
00649        row[cols.button] = bi2 ? bi2->get_button_text() : _("<App disabled>");
00650        Atomic a;
00651        prefs.exceptions.write_ref(a)[app] = bi2;
00652 }
00653 
00654 
00655 void Prefs::on_remove() {
00656        Gtk::TreePath path;
00657        Gtk::TreeViewColumn *col;
00658        tv->get_cursor(path, col);
00659        if (path.gobj() != 0) {
00660               Gtk::TreeIter iter = *tm->get_iter(path);
00661               Atomic a;
00662               prefs.exceptions.write_ref(a).erase((Glib::ustring)((*iter)[cols.app]));
00663               tm->erase(iter);
00664        }
00665 }
00666 
00667 void Prefs::on_device_toggled(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter) {
00668        if (ignore_device_toggled)
00669               return;
00670        Atomic a;
00671        std::set<std::string> &ex = prefs.excluded_devices.write_ref(a);
00672        Glib::ustring device = (*iter)[dcs.name];
00673        if ((*iter)[dcs.enabled])
00674               ex.erase(device);
00675        else
00676               ex.insert(device);
00677 }
00678 
00679 void Prefs::on_device_timeout_changed(const Glib::ustring& path, const Glib::ustring& new_text) {
00680        Gtk::TreeRow row(*dtm->get_iter(path));
00681        row[dcs.timeout] = new_text;
00682        Glib::ustring device = row[dcs.name];
00683        Atomic a;
00684 
00685        for (const Combo<TimeoutType>::Info *i = timeout_info; i->name; i++) {
00686               if (Glib::ustring(i->name) == new_text) {
00687                      std::map<std::string, TimeoutType> &dt = prefs.device_timeout.write_ref(a);
00688                      dt[device] = i->value;
00689                      return;
00690               }
00691        }
00692 
00693        std::map<std::string, TimeoutType> &dt = prefs.device_timeout.write_ref(a);
00694        dt.erase(device);
00695 }
00696 
00697 void Prefs::set_button_label() {
00698        blabel->set_text(prefs.button.ref().get_button_text());
00699 }