Code

570f009447d01e8f02e5eb49a581ab086c3f6a61
[inkscape.git] / src / ui / dialog / input.cpp
1 /** @file
2  * @brief Input devices dialog (new) - implementation
3  */
4 /* Author:
5  *   Jon A. Cruz
6  *
7  * Copyright (C) 2008 Author
8  * Released under GNU GPL.  Read the file 'COPYING' for more information.
9  */
11 #include <map>
12 #include <set>
13 #include <glib/gprintf.h>
14 #include <glibmm/i18n.h>
15 #include <gtkmm/alignment.h>
16 #include <gtkmm/cellrenderercombo.h>
17 #include <gtkmm/checkbutton.h>
18 #include <gtkmm/comboboxtext.h>
19 #include <gtkmm/enums.h>
20 #include <gtkmm/eventbox.h>
21 #include <gtkmm/frame.h>
22 #include <gtkmm/image.h>
23 #include <gtkmm/liststore.h>
24 #include <gtkmm/menubar.h>
25 #include <gtkmm/notebook.h>
26 #include <gtkmm/paned.h>
27 #include <gtkmm/progressbar.h>
28 #include <gtkmm/scrolledwindow.h>
29 #include <gtkmm/table.h>
30 #include <gtkmm/treemodel.h>
31 #include <gtkmm/treemodelcolumn.h>
32 #include <gtkmm/treestore.h>
33 #include <gtkmm/treeview.h>
35 #include "device-manager.h"
36 #include "preferences.h"
37 #include "ui/widget/panel.h"
39 #include "input.h"
41 /* XPM */
42 static char const * core_xpm[] = {
43 "16 16 4 1",
44 "       c None",
45 ".      c #808080",
46 "+      c #000000",
47 "@      c #FFFFFF",
48 "                ",
49 "                ",
50 "                ",
51 "    .++++++.    ",
52 "    +@+@@+@+    ",
53 "    +@+@@+@+    ",
54 "    +.+..+.+    ",
55 "    +@@@@@@+    ",
56 "    +@@@@@@+    ",
57 "    +@@@@@@+    ",
58 "    +@@@@@@+    ",
59 "    +@@@@@@+    ",
60 "    .++++++.    ",
61 "                ",
62 "                ",
63 "                "};
65 /* XPM */
66 static char const *eraser[] = {
67 /* columns rows colors chars-per-pixel */
68 "16 16 5 1",
69 "  c black",
70 ". c green",
71 "X c #808080",
72 "o c gray100",
73 "O c None",
74 /* pixels */
75 "OOOOOOOOOOOOOOOO",
76 "OOOOOOOOOOOOO OO",
77 "OOOOOOOOOOOO . O",
78 "OOOOOOOOOOO . OO",
79 "OOOOOOOOOO . OOO",
80 "OOOOOOOOO . OOOO",
81 "OOOOOOOO . OOOOO",
82 "OOOOOOOXo OOOOOO",
83 "OOOOOOXoXOOOOOOO",
84 "OOOOOXoXOOOOOOOO",
85 "OOOOXoXOOOOOOOOO",
86 "OOOXoXOOOOOOOOOO",
87 "OOXoXOOOOOOOOOOO",
88 "OOXXOOOOOOOOOOOO",
89 "OOOOOOOOOOOOOOOO",
90 "OOOOOOOOOOOOOOOO"
91 };
93 /* XPM */
94 static char const *mouse[] = {
95 /* columns rows colors chars-per-pixel */
96 "16 16 3 1",
97 "  c black",
98 ". c gray100",
99 "X c None",
100 /* pixels */
101 "XXXXXXXXXXXXXXXX",
102 "XXXXXXXXXXXXXXXX",
103 "XXXXXXXXXXXXXXXX",
104 "XXXXXXXXXXXXXXXX",
105 "XXXXXXX  XXXXXXX",
106 "XXXXX  . XXXXXXX",
107 "XXXX .... XXXXXX",
108 "XXXX .... XXXXXX",
109 "XXXXX .... XXXXX",
110 "XXXXX .... XXXXX",
111 "XXXXXX .... XXXX",
112 "XXXXXX .... XXXX",
113 "XXXXXXX .  XXXXX",
114 "XXXXXXX  XXXXXXX",
115 "XXXXXXXXXXXXXXXX",
116 "XXXXXXXXXXXXXXXX"
117 };
119 /* XPM */
120 static char const *pen[] = {
121 /* columns rows colors chars-per-pixel */
122 "16 16 3 1",
123 "  c black",
124 ". c gray100",
125 "X c None",
126 /* pixels */
127 "XXXXXXXXXXXXXXXX",
128 "XXXXXXXXXXXXX XX",
129 "XXXXXXXXXXXX . X",
130 "XXXXXXXXXXX . XX",
131 "XXXXXXXXXX . XXX",
132 "XXXXXXXXX . XXXX",
133 "XXXXXXXX . XXXXX",
134 "XXXXXXX . XXXXXX",
135 "XXXXXX . XXXXXXX",
136 "XXXXX . XXXXXXXX",
137 "XXXX . XXXXXXXXX",
138 "XXX . XXXXXXXXXX",
139 "XX . XXXXXXXXXXX",
140 "XX  XXXXXXXXXXXX",
141 "XXXXXXXXXXXXXXXX",
142 "XXXXXXXXXXXXXXXX"
143 };
145 /* XPM */
146 static char const *sidebuttons[] = {
147 /* columns rows colors chars-per-pixel */
148 "16 16 4 1",
149 "  c black",
150 ". c #808080",
151 "o c green",
152 "O c None",
153 /* pixels */
154 "OOOOOOOOOOOOOOOO",
155 "OOOOOOOOOOOOOOOO",
156 "O..............O",
157 "O.OOOOOOOOOOOO.O",
158 "O   OOOOOOOO   O",
159 "O o OOOOOOOO o O",
160 "O o OOOOOOOO o O",
161 "O   OOOOOOOO   O",
162 "O.OOOOOOOOOOOO.O",
163 "O.OOOOOOOOOOOO.O",
164 "O.OOOOOOOOOOOO.O",
165 "O.OOOOOOOOOOOO.O",
166 "O.OOOOOOOOOOOO.O",
167 "O..............O",
168 "OOOOOOOOOOOOOOOO",
169 "OOOOOOOOOOOOOOOO"
170 };
172 /* XPM */
173 static char const *tablet[] = {
174 /* columns rows colors chars-per-pixel */
175 "16 16 3 1",
176 "  c black",
177 ". c gray100",
178 "X c None",
179 /* pixels */
180 "XXXXXXXXXXXXXXXX",
181 "XXXXXXXXXXXXXXXX",
182 "X              X",
183 "X ............ X",
184 "X ............ X",
185 "X ............ X",
186 "X ............ X",
187 "X ............ X",
188 "X ............ X",
189 "X ............ X",
190 "X ............ X",
191 "X ............ X",
192 "X ............ X",
193 "X              X",
194 "XXXXXXXXXXXXXXXX",
195 "XXXXXXXXXXXXXXXX"
196 };
198 /* XPM */
199 static char const *tip[] = {
200 /* columns rows colors chars-per-pixel */
201 "16 16 5 1",
202 "  c black",
203 ". c green",
204 "X c #808080",
205 "o c gray100",
206 "O c None",
207 /* pixels */
208 "OOOOOOOOOOOOOOOO",
209 "OOOOOOOOOOOOOXOO",
210 "OOOOOOOOOOOOXoXO",
211 "OOOOOOOOOOOXoXOO",
212 "OOOOOOOOOOXoXOOO",
213 "OOOOOOOOOXoXOOOO",
214 "OOOOOOOOXoXOOOOO",
215 "OOOOOOO oXOOOOOO",
216 "OOOOOO . OOOOOOO",
217 "OOOOO . OOOOOOOO",
218 "OOOO . OOOOOOOOO",
219 "OOO . OOOOOOOOOO",
220 "OO . OOOOOOOOOOO",
221 "OO  OOOOOOOOOOOO",
222 "OOOOXXXXXOOOOOOO",
223 "OOOOOOOOOXXXXXOO"
224 };
226 /* XPM */
227 static char const *button_none[] = {
228 /* columns rows colors chars-per-pixel */
229 "8 8 3 1",
230 "  c black",
231 ". c #808080",
232 "X c None",
233 /* pixels */
234 "XXXXXXXX",
235 "XX .. XX",
236 "X .XX. X",
237 "X.XX X.X",
238 "X.X XX.X",
239 "X .XX. X",
240 "XX .. XX",
241 "XXXXXXXX"
242 };
243 /* XPM */
244 static char const *button_off[] = {
245 /* columns rows colors chars-per-pixel */
246 "8 8 4 1",
247 "  c black",
248 ". c #808080",
249 "X c gray100",
250 "o c None",
251 /* pixels */
252 "oooooooo",
253 "oo.  .oo",
254 "o. XX .o",
255 "o XXXX o",
256 "o XXXX o",
257 "o. XX .o",
258 "oo.  .oo",
259 "oooooooo"
260 };
261 /* XPM */
262 static char const *button_on[] = {
263 /* columns rows colors chars-per-pixel */
264 "8 8 3 1",
265 "  c black",
266 ". c green",
267 "X c None",
268 /* pixels */
269 "XXXXXXXX",
270 "XX    XX",
271 "X  ..  X",
272 "X .... X",
273 "X .... X",
274 "X  ..  X",
275 "XX    XX",
276 "XXXXXXXX"
277 };
279 /* XPM */
280 static char const * axis_none_xpm[] = {
281 "24 8 3 1",
282 "       c None",
283 ".      c #000000",
284 "+      c #808080",
285 "                        ",
286 "  .++++++++++++++++++.  ",
287 " .+               . .+. ",
288 " +          . . .     + ",
289 " +     . . .          + ",
290 " .+. .               +. ",
291 "  .++++++++++++++++++.  ",
292 "                        "};
293 /* XPM */
294 static char const * axis_off_xpm[] = {
295 "24 8 4 1",
296 "       c None",
297 ".      c #808080",
298 "+      c #000000",
299 "@      c #FFFFFF",
300 "                        ",
301 "  .++++++++++++++++++.  ",
302 " .+@@@@@@@@@@@@@@@@@@+. ",
303 " +@@@@@@@@@@@@@@@@@@@@+ ",
304 " +@@@@@@@@@@@@@@@@@@@@+ ",
305 " .+@@@@@@@@@@@@@@@@@@+. ",
306 "  .++++++++++++++++++.  ",
307 "                        "};
308 /* XPM */
309 static char const * axis_on_xpm[] = {
310 "24 8 3 1",
311 "       c None",
312 ".      c #000000",
313 "+      c #00FF00",
314 "                        ",
315 "  ....................  ",
316 " ..++++++++++++++++++.. ",
317 " .++++++++++++++++++++. ",
318 " .++++++++++++++++++++. ",
319 " ..++++++++++++++++++.. ",
320 "  ....................  ",
321 "                        "};
323 using Inkscape::InputDevice;
325 namespace Inkscape {
326 namespace UI {
327 namespace Dialog {
331 class DeviceModelColumns : public Gtk::TreeModel::ColumnRecord
333 public:
334     Gtk::TreeModelColumn<Glib::ustring>                description;
335     Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> >  thumbnail;
336     Gtk::TreeModelColumn<Glib::RefPtr<InputDevice const> > device;
337     Gtk::TreeModelColumn<Gdk::InputMode>               mode;
339     DeviceModelColumns() { add(description); add(thumbnail); add(device); add(mode); }
340 };
342 static std::map<Gdk::InputMode, Glib::ustring> &getModeToString()
344     static std::map<Gdk::InputMode, Glib::ustring> mapping;
345     if (mapping.empty()) {
346         mapping[Gdk::MODE_DISABLED] = _("Disabled");
347         mapping[Gdk::MODE_SCREEN]   = _("Screen");
348         mapping[Gdk::MODE_WINDOW]   = _("Window");
349     }
351     return mapping;
354 static std::map<Glib::ustring, Gdk::InputMode> &getStringToMode()
356     static std::map<Glib::ustring, Gdk::InputMode> mapping;
357     if (mapping.empty()) {
358         mapping[_("Disabled")] = Gdk::MODE_DISABLED;
359         mapping[_("Screen")]   = Gdk::MODE_SCREEN;
360         mapping[_("Window")]   = Gdk::MODE_WINDOW;
361     }
363     return mapping;
368 class InputDialogImpl : public InputDialog {
369 public:
370     InputDialogImpl();
371     virtual ~InputDialogImpl() {}
373 private:
374     class ConfPanel : public Gtk::VBox
375     {
376     public:
377         ConfPanel();
378         ~ConfPanel();
380         class Blink : public Preferences::Observer
381         {
382         public:
383             Blink(ConfPanel &parent);
384             virtual ~Blink();
385             virtual void notify(Preferences::Entry const &new_val);
387             ConfPanel &parent;
388         };
390         static void commitCellModeChange(Glib::ustring const &path, Glib::ustring const &newText, Glib::RefPtr<Gtk::TreeStore> store);
391         static void setModeCellString(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter);
393         void saveSettings();
394         void useExtToggled();
396         Glib::RefPtr<Gtk::TreeStore> store;
397         Gtk::TreeIter tabletIter;
398         Gtk::TreeView tree;
399         Gtk::ScrolledWindow treeScroller;
400         Blink watcher;
401         Gtk::CheckButton useExt;
402         Gtk::Button save;
403     };
405     static DeviceModelColumns &getCols();
407     enum PixId {PIX_CORE, PIX_PEN, PIX_MOUSE, PIX_TIP, PIX_TABLET, PIX_ERASER, PIX_SIDEBUTTONS,
408                 PIX_BUTTONS_NONE, PIX_BUTTONS_ON, PIX_BUTTONS_OFF,
409                 PIX_AXIS_NONE, PIX_AXIS_ON, PIX_AXIS_OFF};
411     static Glib::RefPtr<Gdk::Pixbuf> getPix(PixId id);
413     std::map<Glib::ustring, std::set<guint> > buttonMap;
414     std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
416     GdkInputSource lastSourceSeen;
417     Glib::ustring lastDevnameSeen;
419     Glib::RefPtr<Gtk::TreeStore> store;
420     Gtk::TreeIter tabletIter;
421     Gtk::TreeView tree;
422     Gtk::Frame frame2;
423     Gtk::Frame testFrame;
424     Gtk::ScrolledWindow treeScroller;
425     Gtk::ScrolledWindow detailScroller;
426     Gtk::HPaned splitter;
427     Gtk::VPaned split2;
428     Gtk::Label devName;
429     Gtk::Label devKeyCount;
430     Gtk::Label devAxesCount;
431     Gtk::ComboBoxText axesCombo;
432     Gtk::ProgressBar axesValues[6];
433     Gtk::ComboBoxText buttonCombo;
434     Gtk::ComboBoxText linkCombo;
435     sigc::connection linkConnection;
436     Gtk::Label keyVal;
437     Gtk::Entry keyEntry;
438     Gtk::Table devDetails;
439     Gtk::Notebook topHolder;
440     Gtk::Image testThumb;
441     Gtk::Image testButtons[24];
442     Gtk::Image testAxes[8];
443     Gtk::Table imageTable;
444     Gtk::EventBox testDetector;
446     ConfPanel cfgPanel;
448     static void setupTree( Glib::RefPtr<Gtk::TreeStore> store, Gtk::TreeIter &tablet );
449     void setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo );
450     void updateTestButtons( Glib::ustring const& key, gint hotButton );
451     void updateTestAxes( Glib::ustring const& key, GdkDevice* dev );
452     void mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev);
453     Glib::ustring getKeyFor( GdkDevice* device );
454     bool eventSnoop(GdkEvent* event);
455     void linkComboChanged();
456     void resyncToSelection();
457     void handleDeviceChange(const Glib::RefPtr<InputDevice>& device);
458     void updateDeviceAxes(const Glib::RefPtr<InputDevice>& device);
459     void updateDeviceButtons(const Glib::RefPtr<InputDevice>& device);
460     static void updateDeviceLinks(const Glib::RefPtr<InputDevice>& device, Gtk::TreeIter &tabletIter, Gtk::TreeView &tree);
462     static bool findDevice(const Gtk::TreeModel::iterator& iter,
463                            Glib::ustring id,
464                            Gtk::TreeModel::iterator* result);
465     static bool findDeviceByLink(const Gtk::TreeModel::iterator& iter,
466                                  Glib::ustring link,
467                                  Gtk::TreeModel::iterator* result);
469 }; // class InputDialogImpl
472 DeviceModelColumns &InputDialogImpl::getCols()
474     static DeviceModelColumns cols;
475     return cols;
478 Glib::RefPtr<Gdk::Pixbuf> InputDialogImpl::getPix(PixId id)
480     static std::map<PixId, Glib::RefPtr<Gdk::Pixbuf> > mappings;
482     mappings[PIX_CORE]          = Gdk::Pixbuf::create_from_xpm_data(core_xpm);
483     mappings[PIX_PEN]           = Gdk::Pixbuf::create_from_xpm_data(pen);
484     mappings[PIX_MOUSE]         = Gdk::Pixbuf::create_from_xpm_data(mouse);
485     mappings[PIX_TIP]           = Gdk::Pixbuf::create_from_xpm_data(tip);
486     mappings[PIX_TABLET]        = Gdk::Pixbuf::create_from_xpm_data(tablet);
487     mappings[PIX_ERASER]        = Gdk::Pixbuf::create_from_xpm_data(eraser);
488     mappings[PIX_SIDEBUTTONS]   = Gdk::Pixbuf::create_from_xpm_data(sidebuttons);
490     mappings[PIX_BUTTONS_NONE]  = Gdk::Pixbuf::create_from_xpm_data(button_none);
491     mappings[PIX_BUTTONS_ON]    = Gdk::Pixbuf::create_from_xpm_data(button_on);
492     mappings[PIX_BUTTONS_OFF]   = Gdk::Pixbuf::create_from_xpm_data(button_off);
494     mappings[PIX_AXIS_NONE]     = Gdk::Pixbuf::create_from_xpm_data(axis_none_xpm);
495     mappings[PIX_AXIS_ON]       = Gdk::Pixbuf::create_from_xpm_data(axis_on_xpm);
496     mappings[PIX_AXIS_OFF]      = Gdk::Pixbuf::create_from_xpm_data(axis_off_xpm);
498     Glib::RefPtr<Gdk::Pixbuf> pix;
499     if (mappings.find(id) != mappings.end()) {
500         pix = mappings[id];
501     }
503     return pix;
507 // Now that we've defined the *Impl class, we can do the method to aquire one.
508 InputDialog &InputDialog::getInstance()
510     InputDialog *dialog = new InputDialogImpl();
511     return *dialog;
515 InputDialogImpl::InputDialogImpl() :
516     InputDialog(),
518     lastSourceSeen((GdkInputSource)-1),
519     lastDevnameSeen(""),
520     store(Gtk::TreeStore::create(getCols())),
521     tabletIter(),
522     tree(store),
523     frame2(),
524     testFrame(_("Test Area")),
525     treeScroller(),
526     detailScroller(),
527     splitter(),
528     split2(),
529     linkCombo(),
530     devDetails(12, 2),
531     topHolder(),
532     imageTable(8, 7),
533     testDetector(),
534     cfgPanel()
536     Gtk::Box *contents = _getContents();
539     treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
540     treeScroller.add(tree);
541     split2.pack1(testFrame);
542     split2.pack2(frame2);
543     splitter.pack1(treeScroller);
544     splitter.pack2(split2);
546     testDetector.add(imageTable);
547     testFrame.add(testDetector);
548     testThumb.set(getPix(PIX_TABLET));
549     testThumb.set_padding(24, 24);
550     imageTable.attach(testThumb, 0, 8, 0, 1, ::Gtk::EXPAND, ::Gtk::EXPAND);
551     {
552         guint col = 0;
553         guint row = 1;
554         for ( guint num = 0; num < G_N_ELEMENTS(testButtons); num++ ) {
555             testButtons[num].set(getPix(PIX_BUTTONS_NONE));
556             imageTable.attach(testButtons[num], col, col + 1, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
557             col++;
558             if (col > 7) {
559                 col = 0;
560                 row++;
561             }
562         }
564         col = 0;
565         for ( guint num = 0; num < G_N_ELEMENTS(testAxes); num++ ) {
566             testAxes[num].set(getPix(PIX_AXIS_NONE));
567             imageTable.attach(testAxes[num], col * 2, (col + 1) * 2, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
568             col++;
569             if (col > 3) {
570                 col = 0;
571                 row++;
572             }
573         }
574     }
577     topHolder.append_page(cfgPanel, _("Configuration"));
578     topHolder.append_page(splitter, _("Hardware"));
579     topHolder.show_all();
580     topHolder.set_current_page(0);
582     contents->pack_start(topHolder);
584     int rowNum = 0;
586     Gtk::Label* lbl = Gtk::manage(new Gtk::Label(_("Name:")));
587     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
588                       ::Gtk::FILL,
589                       ::Gtk::SHRINK);
590     devDetails.attach(devName, 1, 2, rowNum, rowNum + 1,
591                       ::Gtk::SHRINK,
592                       ::Gtk::SHRINK);
594     rowNum++;
596     lbl = Gtk::manage(new Gtk::Label(_("Link:")));
597     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
598                       ::Gtk::FILL,
599                       ::Gtk::SHRINK);
601     linkCombo.append_text(_("None"));
602     linkCombo.set_active_text(_("None"));
603     linkCombo.set_sensitive(false);
604     linkConnection = linkCombo.signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::linkComboChanged));
606     devDetails.attach(linkCombo, 1, 2, rowNum, rowNum + 1,
607                       ::Gtk::FILL,
608                       ::Gtk::SHRINK);
609     rowNum++;
611     lbl = Gtk::manage(new Gtk::Label(_("Axes count:")));
612     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
613                       ::Gtk::FILL,
614                       ::Gtk::SHRINK);
615     devDetails.attach(devAxesCount, 1, 2, rowNum, rowNum + 1,
616                       ::Gtk::SHRINK,
617                       ::Gtk::SHRINK);
619     rowNum++;
621 /*
622     lbl = Gtk::manage(new Gtk::Label(_("Actual axes count:")));
623     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
624                       ::Gtk::FILL,
625                       ::Gtk::SHRINK);
626     devDetails.attach(axesCombo, 1, 2, rowNum, rowNum + 1,
627                       ::Gtk::SHRINK,
628                       ::Gtk::SHRINK);
630     rowNum++;
631 */
633     for ( guint barNum = 0; barNum < static_cast<guint>(G_N_ELEMENTS(axesValues)); barNum++ ) {
634         lbl = Gtk::manage(new Gtk::Label(_("axis:")));
635         devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
636                           ::Gtk::FILL,
637                           ::Gtk::SHRINK);
638         devDetails.attach(axesValues[barNum], 1, 2, rowNum, rowNum + 1,
639                           ::Gtk::EXPAND,
640                           ::Gtk::SHRINK);
641         axesValues[barNum].set_sensitive(false);
643         rowNum++;
644     }
646     lbl = Gtk::manage(new Gtk::Label(_("Button count:")));
647     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
648                       ::Gtk::FILL,
649                       ::Gtk::SHRINK);
650     devDetails.attach(devKeyCount, 1, 2, rowNum, rowNum + 1,
651                       ::Gtk::SHRINK,
652                       ::Gtk::SHRINK);
654     rowNum++;
656 /*
657     lbl = Gtk::manage(new Gtk::Label(_("Actual button count:")));
658     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
659                       ::Gtk::FILL,
660                       ::Gtk::SHRINK);
661     devDetails.attach(buttonCombo, 1, 2, rowNum, rowNum + 1,
662                       ::Gtk::SHRINK,
663                       ::Gtk::SHRINK);
665     rowNum++;
666 */
668     devDetails.attach(keyVal, 0, 2, rowNum, rowNum + 1,
669                       ::Gtk::FILL,
670                       ::Gtk::SHRINK);
671     rowNum++;
674     testDetector.signal_event().connect(sigc::mem_fun(*this, &InputDialogImpl::eventSnoop));
676 //     void gdk_input_set_extension_events (GdkWindow        *window,
677 //                                          gint              mask,
678 //                                          GdkExtensionMode  mode);
680     gtk_widget_set_extension_events( GTK_WIDGET(testDetector.gobj()), GDK_EXTENSION_EVENTS_ALL );
681     testDetector.add_events(Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK |Gdk::PROXIMITY_IN_MASK|Gdk::PROXIMITY_OUT_MASK|Gdk::SCROLL_MASK);
683     devDetails.attach(keyEntry, 0, 2, rowNum, rowNum + 1,
684                       ::Gtk::FILL,
685                       ::Gtk::SHRINK);
686     rowNum++;
689     devDetails.set_sensitive(false);
690     detailScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
691     detailScroller.add(devDetails);
692     frame2.add(detailScroller);
694 //- 16x16/devices
695 // gnome-dev-mouse-optical
696 // input-mouse
697 // input-tablet
698 // mouse
702     //Add the TreeView's view columns:
703     tree.append_column("I", getCols().thumbnail);
704     tree.append_column("Bar", getCols().description);
706     tree.set_enable_tree_lines();
707     tree.set_headers_visible(false);
708     tree.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::resyncToSelection));
711     setupTree( store, tabletIter );
713     Inkscape::DeviceManager::getManager().signalDeviceChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::handleDeviceChange));
714     Inkscape::DeviceManager::getManager().signalAxesChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceAxes));
715     Inkscape::DeviceManager::getManager().signalButtonsChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceButtons));
716     Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind<Gtk::TreeIter &, Gtk::TreeView &>(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, tree));
718     tree.expand_all();
719     show_all_children();
722 void InputDialogImpl::setupTree( Glib::RefPtr<Gtk::TreeStore> store, Gtk::TreeIter &tablet )
724     std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
725     if ( !devList.empty() ) {
726         Gtk::TreeModel::Row row = *(store->append());
727         row[getCols().description] = _("Hardware");
729         tablet = store->append(row.children());
730         Gtk::TreeModel::Row childrow = *tablet;
731         childrow[getCols().description] = _("Tablet");
732         childrow[getCols().thumbnail] = getPix(PIX_TABLET);
734         for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it = devList.begin(); it != devList.end(); ++it ) {
735             Glib::RefPtr<InputDevice const> dev = *it;
736             if ( dev ) {
737 //                 g_message("device: name[%s] source[0x%x] mode[0x%x] cursor[%s] axis count[%d] key count[%d]", dev->getName().c_str(), dev->getSource(), dev->getMode(),
738 //                           dev->hasCursor() ? "Yes":"no", dev->getNumAxes(), dev->getNumKeys());
740 //                 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
741                 if ( dev ) {
742                     Gtk::TreeModel::Row deviceRow = *(store->append(childrow.children()));
743                     deviceRow[getCols().description] = dev->getName();
744                     deviceRow[getCols().device] = dev;
745                     deviceRow[getCols().mode] = dev->getMode();
746                     switch ( dev->getSource() ) {
747                         case GDK_SOURCE_MOUSE:
748                             deviceRow[getCols().thumbnail] = getPix(PIX_CORE);
749                             break;
750                         case GDK_SOURCE_PEN:
751                             if (deviceRow[getCols().description] == _("pad")) {
752                                 deviceRow[getCols().thumbnail] = getPix(PIX_SIDEBUTTONS);
753                             } else {
754                                 deviceRow[getCols().thumbnail] = getPix(PIX_TIP);
755                             }
756                             break;
757                         case GDK_SOURCE_CURSOR:
758                             deviceRow[getCols().thumbnail] = getPix(PIX_MOUSE);
759                             break;
760                         case GDK_SOURCE_ERASER:
761                             deviceRow[getCols().thumbnail] = getPix(PIX_ERASER);
762                             break;
763                         default:
764                             ; // nothing
765                     }
766                 }
767             } else {
768                 g_warning("Null device in list");
769             }
770         }
771     } else {
772         g_warning("No devices found");
773     }
777 InputDialogImpl::ConfPanel::ConfPanel() :
778     Gtk::VBox(),
779     store(Gtk::TreeStore::create(getCols())),
780     tabletIter(),
781     tree(store),
782     treeScroller(),
783     watcher(*this),
784     useExt(_("Use pressure-sensitive tablet (requires restart)")),
785     save(_("Save"))
787     pack_start(treeScroller);
789     treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
790     treeScroller.add(tree);
792     class Foo : public Gtk::TreeModel::ColumnRecord {
793     public :
794         Gtk::TreeModelColumn<Glib::ustring> one;
795         Foo() {add(one);}
796     };
797     static Foo foo;
798     Glib::RefPtr<Gtk::ListStore> poppers = Gtk::ListStore::create(foo);
799     poppers->reference();
801     Gtk::TreeModel::Row row = *(poppers->append());
802     row[foo.one] = getModeToString()[Gdk::MODE_DISABLED];
803     row = *(poppers->append());
804     row[foo.one] = getModeToString()[Gdk::MODE_SCREEN];
805     row = *(poppers->append());
806     row[foo.one] = getModeToString()[Gdk::MODE_WINDOW];
808     Gtk::CellRendererCombo *rendr = new Gtk::CellRendererCombo();
809     rendr->property_model().set_value(poppers);
810     rendr->property_text_column().set_value(0);
811     rendr->property_has_entry() = false;
813     //Add the TreeView's view columns:
814     tree.append_column("I", getCols().thumbnail);
815     tree.append_column("Bar", getCols().description);
816     Gtk::TreeViewColumn *col = new Gtk::TreeViewColumn("X", *rendr);
817     if (col) {
818         tree.append_column(*col);
819         col->set_cell_data_func(*rendr, sigc::ptr_fun(setModeCellString));
820         rendr->signal_edited().connect(sigc::bind(sigc::ptr_fun(commitCellModeChange), store));
821         rendr->property_editable() = true;
822     }
824     tree.set_enable_tree_lines();
825     tree.set_headers_visible(false);
827     setupTree( store, tabletIter );
829     Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind<Gtk::TreeIter &, Gtk::TreeView &>(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, tree));
831     tree.expand_all();
833     useExt.set_active(Preferences::get()->getBool("/options/useextinput/value"));
834     useExt.signal_toggled().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::useExtToggled));
835     pack_start(useExt, Gtk::PACK_SHRINK);
837     save.signal_clicked().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::saveSettings));
838     Gtk::Alignment *align = new Gtk::Alignment(Gtk::ALIGN_RIGHT, Gtk::ALIGN_TOP, 0, 0);
839     align->add(save);
840     pack_start(*Gtk::manage(align), Gtk::PACK_SHRINK);
843 InputDialogImpl::ConfPanel::~ConfPanel()
847 void InputDialogImpl::ConfPanel::setModeCellString(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter)
849     if (iter) {
850         Gtk::CellRendererCombo *combo = dynamic_cast<Gtk::CellRendererCombo *>(rndr);
851         if (combo) {
852             Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
853             Gdk::InputMode mode = (*iter)[getCols().mode];
854             if (dev && (getModeToString().find(mode) != getModeToString().end())) {
855                 combo->property_text() = getModeToString()[mode];
856             } else {
857                 combo->property_text() = "";
858             }
859         }
860     }
863 void InputDialogImpl::ConfPanel::commitCellModeChange(Glib::ustring const &path, Glib::ustring const &newText, Glib::RefPtr<Gtk::TreeStore> store)
865     Gtk::TreeIter iter = store->get_iter(path);
866     if (iter) {
867         Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
868         if (dev && (getStringToMode().find(newText) != getStringToMode().end())) {
869             Gdk::InputMode mode = getStringToMode()[newText];
870             Inkscape::DeviceManager::getManager().setMode( dev->getId(), mode );
871         }
872     }
875 void InputDialogImpl::ConfPanel::saveSettings()
877     Inkscape::DeviceManager::getManager().saveConfig();
880 void InputDialogImpl::ConfPanel::useExtToggled()
882     bool active = useExt.get_active();
883     if (active != Preferences::get()->getBool("/options/useextinput/value")) {
884         Preferences::get()->setBool("/options/useextinput/value", active);
885         if (active) {
886             // As a work-around for a common problem, enable tablet toggles on the calligraphic tool.
887             // Covered in Launchpad bug #196195.
888             Preferences::get()->setBool("/tools/tweak/usepressure", true);
889             Preferences::get()->setBool("/tools/calligraphic/usepressure", true);
890             Preferences::get()->setBool("/tools/calligraphic/usetilt", true);
891         }
892     }
895 InputDialogImpl::ConfPanel::Blink::Blink(ConfPanel &parent) :
896     Preferences::Observer("/options/useextinput/value"),
897     parent(parent)
899     Preferences::get()->addObserver(*this);
902 InputDialogImpl::ConfPanel::Blink::~Blink()
904     Preferences::get()->removeObserver(*this);
907 void InputDialogImpl::ConfPanel::Blink::notify(Preferences::Entry const &new_val)
909     parent.useExt.set_active(new_val.getBool());
912 void InputDialogImpl::handleDeviceChange(const Glib::RefPtr<InputDevice>& device)
914 //     g_message("OUCH!!!! for %p  hits %s", &device, device->getId().c_str());
915     std::vector<Glib::RefPtr<Gtk::TreeStore> > stores;
916     stores.push_back(store);
917     stores.push_back(cfgPanel.store);
919     for (std::vector<Glib::RefPtr<Gtk::TreeStore> >::iterator it = stores.begin(); it != stores.end(); ++it) {
920         Gtk::TreeModel::iterator deviceIter;
921         (*it)->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
922                                  sigc::ptr_fun(&InputDialogImpl::findDevice),
923                                  device->getId(),
924                                  &deviceIter) );
925         if ( deviceIter ) {
926             Gdk::InputMode mode = device->getMode();
927             Gtk::TreeModel::Row row = *deviceIter;
928             if (row[getCols().mode] != mode) {
929                 row[getCols().mode] = mode;
930             }
931         }
932     }
935 void InputDialogImpl::updateDeviceAxes(const Glib::RefPtr<InputDevice>& device)
937     gint live = device->getLiveAxes();
939     std::map<guint, std::pair<guint, gdouble> > existing = axesMap[device->getId()];
940     gint mask = 0x1;
941     for ( gint num = 0; num < 32; num++, mask <<= 1) {
942         if ( (mask & live) != 0 ) {
943             if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) {
944                 axesMap[device->getId()][num].first = 2;
945                 axesMap[device->getId()][num].second = 0.0;
946             }
947         }
948     }
949     updateTestAxes( device->getId(), 0 );
952 void InputDialogImpl::updateDeviceButtons(const Glib::RefPtr<InputDevice>& device)
954     gint live = device->getLiveButtons();
955     std::set<guint> existing = buttonMap[device->getId()];
956     gint mask = 0x1;
957     for ( gint num = 0; num < 32; num++, mask <<= 1) {
958         if ( (mask & live) != 0 ) {
959             if ( existing.find(num) == existing.end() ) {
960                 buttonMap[device->getId()].insert(num);
961             }
962         }
963     }
964     updateTestButtons(device->getId(), -1);
968 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
969                                  Glib::ustring id,
970                                  Gtk::TreeModel::iterator* result)
972     bool stop = false;
973     Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
974     if ( dev && (dev->getId() == id) ) {
975         if ( result ) {
976             *result = iter;
977         }
978         stop = true;
979     }
980     return stop;
983 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
984                                        Glib::ustring link,
985                                        Gtk::TreeModel::iterator* result)
987     bool stop = false;
988     Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
989     if ( dev && (dev->getLink() == link) ) {
990         if ( result ) {
991             *result = iter;
992         }
993         stop = true;
994     }
995     return stop;
998 void InputDialogImpl::updateDeviceLinks(const Glib::RefPtr<InputDevice>& device, Gtk::TreeIter &tabletIter, Gtk::TreeView &tree)
1000     Glib::RefPtr<Gtk::TreeStore> store = Glib::RefPtr<Gtk::TreeStore>::cast_dynamic(tree.get_model());
1002 //     g_message("Links!!!! for %p  hits [%s]  with link of [%s]", &device, device->getId().c_str(), device->getLink().c_str());
1003     Gtk::TreeModel::iterator deviceIter;
1004     store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1005                              sigc::ptr_fun(&InputDialogImpl::findDevice),
1006                              device->getId(),
1007                              &deviceIter) );
1009     if ( deviceIter ) {
1010         // Found the device concerned. Can proceed.
1012         if ( device->getLink().empty() ) {
1013             // is now unlinked
1014 //             g_message("Item %s is unlinked", device->getId().c_str());
1015             if ( deviceIter->parent() != tabletIter ) {
1016                 // Not the child of the tablet. move on up
1018                 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1019                 Glib::ustring descr = (*deviceIter)[getCols().description];
1020                 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1022                 Gtk::TreeModel::Row deviceRow = *store->append(tabletIter->children());
1023                 deviceRow[getCols().description] = descr;
1024                 deviceRow[getCols().thumbnail] = thumb;
1025                 deviceRow[getCols().device] = dev;
1026                 deviceRow[getCols().mode] = dev->getMode();
1028                 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1029                 store->erase(deviceIter);
1030                 if ( oldParent->children().empty() ) {
1031                     store->erase(oldParent);
1032                 }
1033             }
1034         } else {
1035             // is linking
1036             if ( deviceIter->parent() == tabletIter ) {
1037                 // Simple case. Not already linked
1039                 Gtk::TreeIter newGroup = store->append(tabletIter->children());
1040                 (*newGroup)[getCols().description] = _("Pen");
1041                 (*newGroup)[getCols().thumbnail] = getPix(PIX_PEN);
1043                 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1044                 Glib::ustring descr = (*deviceIter)[getCols().description];
1045                 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1047                 Gtk::TreeModel::Row deviceRow = *store->append(newGroup->children());
1048                 deviceRow[getCols().description] = descr;
1049                 deviceRow[getCols().thumbnail] = thumb;
1050                 deviceRow[getCols().device] = dev;
1051                 deviceRow[getCols().mode] = dev->getMode();
1054                 Gtk::TreeModel::iterator linkIter;
1055                 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1056                                          sigc::ptr_fun(&InputDialogImpl::findDeviceByLink),
1057                                          device->getId(),
1058                                          &linkIter) );
1059                 if ( linkIter ) {
1060                     dev = (*linkIter)[getCols().device];
1061                     descr = (*linkIter)[getCols().description];
1062                     thumb = (*linkIter)[getCols().thumbnail];
1064                     deviceRow = *store->append(newGroup->children());
1065                     deviceRow[getCols().description] = descr;
1066                     deviceRow[getCols().thumbnail] = thumb;
1067                     deviceRow[getCols().device] = dev;
1068                     deviceRow[getCols().mode] = dev->getMode();
1069                     Gtk::TreeModel::iterator oldParent = linkIter->parent();
1070                     store->erase(linkIter);
1071                     if ( oldParent->children().empty() ) {
1072                         store->erase(oldParent);
1073                     }
1074                 }
1076                 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1077                 store->erase(deviceIter);
1078                 if ( oldParent->children().empty() ) {
1079                     store->erase(oldParent);
1080                 }
1081                 tree.expand_row(Gtk::TreePath(newGroup), true);
1082             }
1083         }
1084     }
1087 void InputDialogImpl::linkComboChanged() {
1088     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1089     Gtk::TreeModel::iterator iter = treeSel->get_selected();
1090     if (iter) {
1091         Gtk::TreeModel::Row row = *iter;
1092         Glib::ustring val = row[getCols().description];
1093         Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1094         if ( dev ) {
1095             if ( linkCombo.get_active_row_number() == 0 ) {
1096                 // It is the "None" entry
1097                 DeviceManager::getManager().setLinkedTo(dev->getId(), "");
1098             } else {
1099                 Glib::ustring linkName = linkCombo.get_active_text();
1100                 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1101                 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1102                     if ( linkName == (*it)->getName() ) {
1103                         DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId());
1104                         break;
1105                     }
1106                 }
1107             }
1108         }
1109     }
1112 void InputDialogImpl::resyncToSelection() {
1113     bool clear = true;
1114     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1115     Gtk::TreeModel::iterator iter = treeSel->get_selected();
1116     if (iter) {
1117         Gtk::TreeModel::Row row = *iter;
1118         Glib::ustring val = row[getCols().description];
1119         Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1120         if ( dev ) {
1121             devDetails.set_sensitive(true);
1123             linkConnection.block();
1124             linkCombo.clear_items();
1125             linkCombo.append_text(_("None"));
1126             linkCombo.set_active(0);
1127             if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
1128                 Glib::ustring linked = dev->getLink();
1129                 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1130                 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1131                     if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) {
1132                         linkCombo.append_text((*it)->getName().c_str());
1133                         if ( (linked.length() > 0) && (linked == (*it)->getId()) ) {
1134                             linkCombo.set_active_text((*it)->getName().c_str());
1135                         }
1136                     }
1137                 }
1138                 linkCombo.set_sensitive(true);
1139             } else {
1140                 linkCombo.set_sensitive(false);
1141             }
1142             linkConnection.unblock();
1144             clear = false;
1145             devName.set_label(row[getCols().description]);
1146             setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo);
1147             setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo);
1148         }
1149     }
1151     devDetails.set_sensitive(!clear);
1152     if (clear) {
1153         devName.set_label("");
1154         devAxesCount.set_label("");
1155         devKeyCount.set_label("");
1156     }
1159 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
1161     gchar *tmp = g_strdup_printf("%d", reported);
1162     label.set_label(tmp);
1163     g_free(tmp);
1165     combo.clear_items();
1166     for ( gint i = 1; i <= reported; ++i ) {
1167         tmp = g_strdup_printf("%d", i);
1168         combo.append_text(tmp);
1169         g_free(tmp);
1170     }
1172     if ( (1 <= actual) && (actual <= reported) ) {
1173         combo.set_active(actual - 1);
1174     }
1177 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
1179     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testButtons)); i++ ) {
1180         if ( buttonMap[key].find(i) != buttonMap[key].end() ) {
1181             if ( i == hotButton ) {
1182                 testButtons[i].set(getPix(PIX_BUTTONS_ON));
1183             } else {
1184                 testButtons[i].set(getPix(PIX_BUTTONS_OFF));
1185             }
1186         } else {
1187             testButtons[i].set(getPix(PIX_BUTTONS_NONE));
1188         }
1189     }
1192 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
1194     static gdouble epsilon = 0.0001;
1195     {
1196         Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1197         Gtk::TreeModel::iterator iter = treeSel->get_selected();
1198         if (iter) {
1199             Gtk::TreeModel::Row row = *iter;
1200             Glib::ustring val = row[getCols().description];
1201             Glib::RefPtr<InputDevice const> idev = row[getCols().device];
1202             if ( !idev || (idev->getId() != key) ) {
1203                 dev = 0;
1204             }
1205         }
1206     }
1209     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testAxes)); i++ ) {
1210         if ( axesMap[key].find(i) != axesMap[key].end() ) {
1211             switch ( axesMap[key][i].first ) {
1212                 case 0:
1213                 case 1:
1214                     testAxes[i].set(getPix(PIX_AXIS_NONE));
1215                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1216                         axesValues[i].set_sensitive(false);
1217                     }
1218                     break;
1219                 case 2:
1220                     testAxes[i].set(getPix(PIX_AXIS_OFF));
1221                     axesValues[i].set_sensitive(true);
1222                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1223                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1224                             axesValues[i].set_sensitive(true);
1225                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1226                         }
1227                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1228                         axesValues[i].set_text(str);
1229                         g_free(str);
1230                     }
1231                     break;
1232                 case 3:
1233                     testAxes[i].set(getPix(PIX_AXIS_ON));
1234                     axesValues[i].set_sensitive(true);
1235                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1236                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1237                             axesValues[i].set_sensitive(true);
1238                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1239                         }
1240                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1241                         axesValues[i].set_text(str);
1242                         g_free(str);
1243                     }
1244             }
1246         } else {
1247             testAxes[i].set(getPix(PIX_AXIS_NONE));
1248         }
1249     }
1250     if ( !dev ) {
1251         for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(axesValues)); i++ ) {
1252             axesValues[i].set_fraction(0.0);
1253             axesValues[i].set_text("");
1254             axesValues[i].set_sensitive(false);
1255         }
1256     }
1259 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1261     static gdouble epsilon = 0.0001;
1262     if ( (numAxes > 0) && axes) {
1263         for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) {
1264             // 0 == new, 1 == set value, 2 == changed value, 3 == active
1265             gdouble diff = axesMap[key][axisNum].second - axes[axisNum];
1266             switch(axesMap[key][axisNum].first) {
1267                 case 0:
1268                 {
1269                     axesMap[key][axisNum].first = 1;
1270                     axesMap[key][axisNum].second = axes[axisNum];
1271                 }
1272                 break;
1273                 case 1:
1274                 {
1275                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1276 //                         g_message("Axis %d changed on %s]", axisNum, key.c_str());
1277                         axesMap[key][axisNum].first = 3;
1278                         axesMap[key][axisNum].second = axes[axisNum];
1279                         updateTestAxes(key, dev);
1280                         DeviceManager::getManager().addAxis(key, axisNum);
1281                     }
1282                 }
1283                 break;
1284                 case 2:
1285                 {
1286                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1287                         axesMap[key][axisNum].first = 3;
1288                         axesMap[key][axisNum].second = axes[axisNum];
1289                         updateTestAxes(key, dev);
1290                     }
1291                 }
1292                 break;
1293                 case 3:
1294                 {
1295                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1296                         axesMap[key][axisNum].second = axes[axisNum];
1297                     } else {
1298                         axesMap[key][axisNum].first = 2;
1299                         updateTestAxes(key, dev);
1300                     }
1301                 }
1302             }
1303         }
1304     }
1305     // std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
1308 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1310     Glib::ustring key;
1311     switch ( device->source ) {
1312         case GDK_SOURCE_MOUSE:
1313             key = "M:";
1314             break;
1315         case GDK_SOURCE_CURSOR:
1316             key = "C:";
1317             break;
1318         case GDK_SOURCE_PEN:
1319             key = "P:";
1320             break;
1321         case GDK_SOURCE_ERASER:
1322             key = "E:";
1323             break;
1324         default:
1325             key = "?:";
1326     }
1327     key += device->name;
1329     return key;
1332 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1334     int modmod = 0;
1336     GdkInputSource source = lastSourceSeen;
1337     Glib::ustring devName = lastDevnameSeen;
1338     Glib::ustring key;
1339     gint hotButton = -1;
1341     switch ( event->type ) {
1342         case GDK_KEY_PRESS:
1343         case GDK_KEY_RELEASE:
1344         {
1345             GdkEventKey* keyEvt = reinterpret_cast<GdkEventKey*>(event);
1346             gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast<GdkModifierType>(keyEvt->state));
1347             keyVal.set_label(name);
1348 //             g_message("%d KEY    state:0x%08x  0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name);
1349             g_free(name);
1350         }
1351         break;
1352         case GDK_BUTTON_PRESS:
1353             modmod = 1;
1354             // fallthrough
1355         case GDK_BUTTON_RELEASE:
1356         {
1357             GdkEventButton* btnEvt = reinterpret_cast<GdkEventButton*>(event);
1358             if ( btnEvt->device ) {
1359                 key = getKeyFor(btnEvt->device);
1360                 source = btnEvt->device->source;
1361                 devName = btnEvt->device->name;
1363                 mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device);
1364                 if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) {
1365 //                     g_message("New button found for %s = %d", key.c_str(), btnEvt->button);
1366                     buttonMap[key].insert(btnEvt->button);
1367                     DeviceManager::getManager().addButton(key, btnEvt->button);
1368                 }
1369                 hotButton = modmod ? btnEvt->button : -1;
1370                 updateTestButtons(key, hotButton);
1371             }
1372             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnEvt->state));
1373             keyVal.set_label(name);
1374 //             g_message("%d BTN    state:0x%08x %c  %4d [%s] dev:%p [%s]  ",
1375 //                       btnEvt->type, btnEvt->state,
1376 //                       (modmod ? '+':'-'),
1377 //                       btnEvt->button, name, btnEvt->device,
1378 //                       (btnEvt->device ? btnEvt->device->name : "null")
1380 //                 );
1381             g_free(name);
1382         }
1383         break;
1384         case GDK_MOTION_NOTIFY:
1385         {
1386             GdkEventMotion* btnMtn = reinterpret_cast<GdkEventMotion*>(event);
1387             if ( btnMtn->device ) {
1388                 key = getKeyFor(btnMtn->device);
1389                 source = btnMtn->device->source;
1390                 devName = btnMtn->device->name;
1391                 mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device);
1392             }
1393             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnMtn->state));
1394             keyVal.set_label(name);
1395 //             g_message("%d MOV    state:0x%08x         [%s] dev:%p [%s] %3.2f %3.2f %3.2f %3.2f %3.2f %3.2f", btnMtn->type, btnMtn->state,
1396 //                       name, btnMtn->device,
1397 //                       (btnMtn->device ? btnMtn->device->name : "null"),
1398 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0),
1399 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0),
1400 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0),
1401 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0),
1402 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0),
1403 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0)
1404 //                 );
1405             g_free(name);
1406         }
1407         break;
1408         default:
1409             ;// nothing
1410     }
1413     if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) {
1414         switch (source) {
1415             case GDK_SOURCE_MOUSE:
1416             {
1417                 testThumb.set(getPix(PIX_CORE));
1418             }
1419             break;
1420             case GDK_SOURCE_CURSOR:
1421             {
1422 //                 g_message("flip to cursor");
1423                 testThumb.set(getPix(PIX_MOUSE));
1424             }
1425             break;
1426             case GDK_SOURCE_PEN:
1427             {
1428                 if (devName == _("pad")) {
1429 //                     g_message("flip to pad");
1430                     testThumb.set(getPix(PIX_SIDEBUTTONS));
1431                 } else {
1432 //                     g_message("flip to pen");
1433                     testThumb.set(getPix(PIX_TIP));
1434                 }
1435             }
1436             break;
1437             case GDK_SOURCE_ERASER:
1438             {
1439 //                 g_message("flip to eraser");
1440                 testThumb.set(getPix(PIX_ERASER));
1441             }
1442             break;
1443 //             default:
1444 //                 g_message("gurgle");
1445         }
1446         updateTestButtons(key, hotButton);
1447         lastSourceSeen = source;
1448         lastDevnameSeen = devName;
1449     }
1451     return false;
1455 } // end namespace Inkscape
1456 } // end namespace UI
1457 } // end namespace Dialog
1460 /*
1461   Local Variables:
1462   mode:c++
1463   c-file-style:"stroustrup"
1464   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1465   indent-tabs-mode:nil
1466   fill-column:99
1467   End:
1468 */
1469 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :