Code

This is the first c++ification commit from me. It handles sp-line, sp-polyline, sp...
[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(Glib::RefPtr<InputDevice const> device);
458     void updateDeviceAxes(Glib::RefPtr<InputDevice const> device);
459     void updateDeviceButtons(Glib::RefPtr<InputDevice const> device);
460     static void updateDeviceLinks(Glib::RefPtr<InputDevice const> device, Gtk::TreeIter tabletIter, Glib::RefPtr<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     Glib::RefPtr<Gtk::TreeView> treePtr(&tree);
717     Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, treePtr));
719     tree.expand_all();
720     show_all_children();
723 void InputDialogImpl::setupTree( Glib::RefPtr<Gtk::TreeStore> store, Gtk::TreeIter &tablet )
725     std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
726     if ( !devList.empty() ) {
727         Gtk::TreeModel::Row row = *(store->append());
728         row[getCols().description] = _("Hardware");
730         tablet = store->append(row.children());
731         Gtk::TreeModel::Row childrow = *tablet;
732         childrow[getCols().description] = _("Tablet");
733         childrow[getCols().thumbnail] = getPix(PIX_TABLET);
735         for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it = devList.begin(); it != devList.end(); ++it ) {
736             Glib::RefPtr<InputDevice const> dev = *it;
737             if ( dev ) {
738 //                 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(),
739 //                           dev->hasCursor() ? "Yes":"no", dev->getNumAxes(), dev->getNumKeys());
741 //                 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
742                 if ( dev ) {
743                     Gtk::TreeModel::Row deviceRow = *(store->append(childrow.children()));
744                     deviceRow[getCols().description] = dev->getName();
745                     deviceRow[getCols().device] = dev;
746                     deviceRow[getCols().mode] = dev->getMode();
747                     switch ( dev->getSource() ) {
748                         case GDK_SOURCE_MOUSE:
749                             deviceRow[getCols().thumbnail] = getPix(PIX_CORE);
750                             break;
751                         case GDK_SOURCE_PEN:
752                             if (deviceRow[getCols().description] == _("pad")) {
753                                 deviceRow[getCols().thumbnail] = getPix(PIX_SIDEBUTTONS);
754                             } else {
755                                 deviceRow[getCols().thumbnail] = getPix(PIX_TIP);
756                             }
757                             break;
758                         case GDK_SOURCE_CURSOR:
759                             deviceRow[getCols().thumbnail] = getPix(PIX_MOUSE);
760                             break;
761                         case GDK_SOURCE_ERASER:
762                             deviceRow[getCols().thumbnail] = getPix(PIX_ERASER);
763                             break;
764                         default:
765                             ; // nothing
766                     }
767                 }
768             } else {
769                 g_warning("Null device in list");
770             }
771         }
772     } else {
773         g_warning("No devices found");
774     }
778 InputDialogImpl::ConfPanel::ConfPanel() :
779     Gtk::VBox(),
780     store(Gtk::TreeStore::create(getCols())),
781     tabletIter(),
782     tree(store),
783     treeScroller(),
784     watcher(*this),
785     useExt(_("Use pressure-sensitive tablet (requires restart)")),
786     save(_("Save"))
788     pack_start(treeScroller);
790     treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
791     treeScroller.add(tree);
793     class Foo : public Gtk::TreeModel::ColumnRecord {
794     public :
795         Gtk::TreeModelColumn<Glib::ustring> one;
796         Foo() {add(one);}
797     };
798     static Foo foo;
799     Glib::RefPtr<Gtk::ListStore> poppers = Gtk::ListStore::create(foo);
800     poppers->reference();
802     Gtk::TreeModel::Row row = *(poppers->append());
803     row[foo.one] = getModeToString()[Gdk::MODE_DISABLED];
804     row = *(poppers->append());
805     row[foo.one] = getModeToString()[Gdk::MODE_SCREEN];
806     row = *(poppers->append());
807     row[foo.one] = getModeToString()[Gdk::MODE_WINDOW];
809     Gtk::CellRendererCombo *rendr = new Gtk::CellRendererCombo();
810     rendr->property_model().set_value(poppers);
811     rendr->property_text_column().set_value(0);
812     rendr->property_has_entry() = false;
814     //Add the TreeView's view columns:
815     tree.append_column("I", getCols().thumbnail);
816     tree.append_column("Bar", getCols().description);
817     Gtk::TreeViewColumn *col = new Gtk::TreeViewColumn("X", *rendr);
818     if (col) {
819         tree.append_column(*col);
820         col->set_cell_data_func(*rendr, sigc::ptr_fun(setModeCellString));
821         rendr->signal_edited().connect(sigc::bind(sigc::ptr_fun(commitCellModeChange), store));
822         rendr->property_editable() = true;
823     }
825     tree.set_enable_tree_lines();
826     tree.set_headers_visible(false);
828     setupTree( store, tabletIter );
830     Glib::RefPtr<Gtk::TreeView> treePtr(&tree);
831     Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, treePtr));
833     tree.expand_all();
835     useExt.set_active(Preferences::get()->getBool("/options/useextinput/value"));
836     useExt.signal_toggled().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::useExtToggled));
837     pack_start(useExt, Gtk::PACK_SHRINK);
839     save.signal_clicked().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::saveSettings));
840     Gtk::Alignment *align = new Gtk::Alignment(Gtk::ALIGN_RIGHT, Gtk::ALIGN_TOP, 0, 0);
841     align->add(save);
842     pack_start(*Gtk::manage(align), Gtk::PACK_SHRINK);
845 InputDialogImpl::ConfPanel::~ConfPanel()
849 void InputDialogImpl::ConfPanel::setModeCellString(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter)
851     if (iter) {
852         Gtk::CellRendererCombo *combo = dynamic_cast<Gtk::CellRendererCombo *>(rndr);
853         if (combo) {
854             Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
855             Gdk::InputMode mode = (*iter)[getCols().mode];
856             if (dev && (getModeToString().find(mode) != getModeToString().end())) {
857                 combo->property_text() = getModeToString()[mode];
858             } else {
859                 combo->property_text() = "";
860             }
861         }
862     }
865 void InputDialogImpl::ConfPanel::commitCellModeChange(Glib::ustring const &path, Glib::ustring const &newText, Glib::RefPtr<Gtk::TreeStore> store)
867     Gtk::TreeIter iter = store->get_iter(path);
868     if (iter) {
869         Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
870         if (dev && (getStringToMode().find(newText) != getStringToMode().end())) {
871             Gdk::InputMode mode = getStringToMode()[newText];
872             Inkscape::DeviceManager::getManager().setMode( dev->getId(), mode );
873         }
874     }
877 void InputDialogImpl::ConfPanel::saveSettings()
879     Inkscape::DeviceManager::getManager().saveConfig();
882 void InputDialogImpl::ConfPanel::useExtToggled()
884     bool active = useExt.get_active();
885     if (active != Preferences::get()->getBool("/options/useextinput/value")) {
886         Preferences::get()->setBool("/options/useextinput/value", active);
887         if (active) {
888             // As a work-around for a common problem, enable tablet toggles on the calligraphic tool.
889             // Covered in Launchpad bug #196195.
890             Preferences::get()->setBool("/tools/tweak/usepressure", true);
891             Preferences::get()->setBool("/tools/calligraphic/usepressure", true);
892             Preferences::get()->setBool("/tools/calligraphic/usetilt", true);
893         }
894     }
897 InputDialogImpl::ConfPanel::Blink::Blink(ConfPanel &parent) :
898     Preferences::Observer("/options/useextinput/value"),
899     parent(parent)
901     Preferences::get()->addObserver(*this);
904 InputDialogImpl::ConfPanel::Blink::~Blink()
906     Preferences::get()->removeObserver(*this);
909 void InputDialogImpl::ConfPanel::Blink::notify(Preferences::Entry const &new_val)
911     parent.useExt.set_active(new_val.getBool());
914 void InputDialogImpl::handleDeviceChange(Glib::RefPtr<InputDevice const> device)
916 //     g_message("OUCH!!!! for %p  hits %s", &device, device->getId().c_str());
917     std::vector<Glib::RefPtr<Gtk::TreeStore> > stores;
918     stores.push_back(store);
919     stores.push_back(cfgPanel.store);
921     for (std::vector<Glib::RefPtr<Gtk::TreeStore> >::iterator it = stores.begin(); it != stores.end(); ++it) {
922         Gtk::TreeModel::iterator deviceIter;
923         (*it)->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
924                                  sigc::ptr_fun(&InputDialogImpl::findDevice),
925                                  device->getId(),
926                                  &deviceIter) );
927         if ( deviceIter ) {
928             Gdk::InputMode mode = device->getMode();
929             Gtk::TreeModel::Row row = *deviceIter;
930             if (row[getCols().mode] != mode) {
931                 row[getCols().mode] = mode;
932             }
933         }
934     }
937 void InputDialogImpl::updateDeviceAxes(Glib::RefPtr<InputDevice const> device)
939     gint live = device->getLiveAxes();
941     std::map<guint, std::pair<guint, gdouble> > existing = axesMap[device->getId()];
942     gint mask = 0x1;
943     for ( gint num = 0; num < 32; num++, mask <<= 1) {
944         if ( (mask & live) != 0 ) {
945             if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) {
946                 axesMap[device->getId()][num].first = 2;
947                 axesMap[device->getId()][num].second = 0.0;
948             }
949         }
950     }
951     updateTestAxes( device->getId(), 0 );
954 void InputDialogImpl::updateDeviceButtons(Glib::RefPtr<InputDevice const> device)
956     gint live = device->getLiveButtons();
957     std::set<guint> existing = buttonMap[device->getId()];
958     gint mask = 0x1;
959     for ( gint num = 0; num < 32; num++, mask <<= 1) {
960         if ( (mask & live) != 0 ) {
961             if ( existing.find(num) == existing.end() ) {
962                 buttonMap[device->getId()].insert(num);
963             }
964         }
965     }
966     updateTestButtons(device->getId(), -1);
970 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
971                                  Glib::ustring id,
972                                  Gtk::TreeModel::iterator* result)
974     bool stop = false;
975     Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
976     if ( dev && (dev->getId() == id) ) {
977         if ( result ) {
978             *result = iter;
979         }
980         stop = true;
981     }
982     return stop;
985 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
986                                        Glib::ustring link,
987                                        Gtk::TreeModel::iterator* result)
989     bool stop = false;
990     Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
991     if ( dev && (dev->getLink() == link) ) {
992         if ( result ) {
993             *result = iter;
994         }
995         stop = true;
996     }
997     return stop;
1000 void InputDialogImpl::updateDeviceLinks(Glib::RefPtr<InputDevice const> device, Gtk::TreeIter tabletIter, Glib::RefPtr<Gtk::TreeView> tree)
1002     Glib::RefPtr<Gtk::TreeStore> store = Glib::RefPtr<Gtk::TreeStore>::cast_dynamic(tree->get_model());
1004 //     g_message("Links!!!! for %p  hits [%s]  with link of [%s]", &device, device->getId().c_str(), device->getLink().c_str());
1005     Gtk::TreeModel::iterator deviceIter;
1006     store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1007                              sigc::ptr_fun(&InputDialogImpl::findDevice),
1008                              device->getId(),
1009                              &deviceIter) );
1011     if ( deviceIter ) {
1012         // Found the device concerned. Can proceed.
1014         if ( device->getLink().empty() ) {
1015             // is now unlinked
1016 //             g_message("Item %s is unlinked", device->getId().c_str());
1017             if ( deviceIter->parent() != tabletIter ) {
1018                 // Not the child of the tablet. move on up
1020                 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1021                 Glib::ustring descr = (*deviceIter)[getCols().description];
1022                 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1024                 Gtk::TreeModel::Row deviceRow = *store->append(tabletIter->children());
1025                 deviceRow[getCols().description] = descr;
1026                 deviceRow[getCols().thumbnail] = thumb;
1027                 deviceRow[getCols().device] = dev;
1028                 deviceRow[getCols().mode] = dev->getMode();
1030                 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1031                 store->erase(deviceIter);
1032                 if ( oldParent->children().empty() ) {
1033                     store->erase(oldParent);
1034                 }
1035             }
1036         } else {
1037             // is linking
1038             if ( deviceIter->parent() == tabletIter ) {
1039                 // Simple case. Not already linked
1041                 Gtk::TreeIter newGroup = store->append(tabletIter->children());
1042                 (*newGroup)[getCols().description] = _("Pen");
1043                 (*newGroup)[getCols().thumbnail] = getPix(PIX_PEN);
1045                 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1046                 Glib::ustring descr = (*deviceIter)[getCols().description];
1047                 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1049                 Gtk::TreeModel::Row deviceRow = *store->append(newGroup->children());
1050                 deviceRow[getCols().description] = descr;
1051                 deviceRow[getCols().thumbnail] = thumb;
1052                 deviceRow[getCols().device] = dev;
1053                 deviceRow[getCols().mode] = dev->getMode();
1056                 Gtk::TreeModel::iterator linkIter;
1057                 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1058                                          sigc::ptr_fun(&InputDialogImpl::findDeviceByLink),
1059                                          device->getId(),
1060                                          &linkIter) );
1061                 if ( linkIter ) {
1062                     dev = (*linkIter)[getCols().device];
1063                     descr = (*linkIter)[getCols().description];
1064                     thumb = (*linkIter)[getCols().thumbnail];
1066                     deviceRow = *store->append(newGroup->children());
1067                     deviceRow[getCols().description] = descr;
1068                     deviceRow[getCols().thumbnail] = thumb;
1069                     deviceRow[getCols().device] = dev;
1070                     deviceRow[getCols().mode] = dev->getMode();
1071                     Gtk::TreeModel::iterator oldParent = linkIter->parent();
1072                     store->erase(linkIter);
1073                     if ( oldParent->children().empty() ) {
1074                         store->erase(oldParent);
1075                     }
1076                 }
1078                 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1079                 store->erase(deviceIter);
1080                 if ( oldParent->children().empty() ) {
1081                     store->erase(oldParent);
1082                 }
1083                 tree->expand_row(Gtk::TreePath(newGroup), true);
1084             }
1085         }
1086     }
1089 void InputDialogImpl::linkComboChanged() {
1090     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1091     Gtk::TreeModel::iterator iter = treeSel->get_selected();
1092     if (iter) {
1093         Gtk::TreeModel::Row row = *iter;
1094         Glib::ustring val = row[getCols().description];
1095         Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1096         if ( dev ) {
1097             if ( linkCombo.get_active_row_number() == 0 ) {
1098                 // It is the "None" entry
1099                 DeviceManager::getManager().setLinkedTo(dev->getId(), "");
1100             } else {
1101                 Glib::ustring linkName = linkCombo.get_active_text();
1102                 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1103                 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1104                     if ( linkName == (*it)->getName() ) {
1105                         DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId());
1106                         break;
1107                     }
1108                 }
1109             }
1110         }
1111     }
1114 void InputDialogImpl::resyncToSelection() {
1115     bool clear = true;
1116     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1117     Gtk::TreeModel::iterator iter = treeSel->get_selected();
1118     if (iter) {
1119         Gtk::TreeModel::Row row = *iter;
1120         Glib::ustring val = row[getCols().description];
1121         Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1122         if ( dev ) {
1123             devDetails.set_sensitive(true);
1125             linkConnection.block();
1126             linkCombo.clear_items();
1127             linkCombo.append_text(_("None"));
1128             linkCombo.set_active(0);
1129             if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
1130                 Glib::ustring linked = dev->getLink();
1131                 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1132                 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1133                     if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) {
1134                         linkCombo.append_text((*it)->getName().c_str());
1135                         if ( (linked.length() > 0) && (linked == (*it)->getId()) ) {
1136                             linkCombo.set_active_text((*it)->getName().c_str());
1137                         }
1138                     }
1139                 }
1140                 linkCombo.set_sensitive(true);
1141             } else {
1142                 linkCombo.set_sensitive(false);
1143             }
1144             linkConnection.unblock();
1146             clear = false;
1147             devName.set_label(row[getCols().description]);
1148             setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo);
1149             setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo);
1150         }
1151     }
1153     devDetails.set_sensitive(!clear);
1154     if (clear) {
1155         devName.set_label("");
1156         devAxesCount.set_label("");
1157         devKeyCount.set_label("");
1158     }
1161 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
1163     gchar *tmp = g_strdup_printf("%d", reported);
1164     label.set_label(tmp);
1165     g_free(tmp);
1167     combo.clear_items();
1168     for ( gint i = 1; i <= reported; ++i ) {
1169         tmp = g_strdup_printf("%d", i);
1170         combo.append_text(tmp);
1171         g_free(tmp);
1172     }
1174     if ( (1 <= actual) && (actual <= reported) ) {
1175         combo.set_active(actual - 1);
1176     }
1179 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
1181     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testButtons)); i++ ) {
1182         if ( buttonMap[key].find(i) != buttonMap[key].end() ) {
1183             if ( i == hotButton ) {
1184                 testButtons[i].set(getPix(PIX_BUTTONS_ON));
1185             } else {
1186                 testButtons[i].set(getPix(PIX_BUTTONS_OFF));
1187             }
1188         } else {
1189             testButtons[i].set(getPix(PIX_BUTTONS_NONE));
1190         }
1191     }
1194 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
1196     static gdouble epsilon = 0.0001;
1197     {
1198         Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1199         Gtk::TreeModel::iterator iter = treeSel->get_selected();
1200         if (iter) {
1201             Gtk::TreeModel::Row row = *iter;
1202             Glib::ustring val = row[getCols().description];
1203             Glib::RefPtr<InputDevice const> idev = row[getCols().device];
1204             if ( !idev || (idev->getId() != key) ) {
1205                 dev = 0;
1206             }
1207         }
1208     }
1211     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testAxes)); i++ ) {
1212         if ( axesMap[key].find(i) != axesMap[key].end() ) {
1213             switch ( axesMap[key][i].first ) {
1214                 case 0:
1215                 case 1:
1216                     testAxes[i].set(getPix(PIX_AXIS_NONE));
1217                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1218                         axesValues[i].set_sensitive(false);
1219                     }
1220                     break;
1221                 case 2:
1222                     testAxes[i].set(getPix(PIX_AXIS_OFF));
1223                     axesValues[i].set_sensitive(true);
1224                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1225                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1226                             axesValues[i].set_sensitive(true);
1227                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1228                         }
1229                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1230                         axesValues[i].set_text(str);
1231                         g_free(str);
1232                     }
1233                     break;
1234                 case 3:
1235                     testAxes[i].set(getPix(PIX_AXIS_ON));
1236                     axesValues[i].set_sensitive(true);
1237                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1238                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1239                             axesValues[i].set_sensitive(true);
1240                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1241                         }
1242                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1243                         axesValues[i].set_text(str);
1244                         g_free(str);
1245                     }
1246             }
1248         } else {
1249             testAxes[i].set(getPix(PIX_AXIS_NONE));
1250         }
1251     }
1252     if ( !dev ) {
1253         for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(axesValues)); i++ ) {
1254             axesValues[i].set_fraction(0.0);
1255             axesValues[i].set_text("");
1256             axesValues[i].set_sensitive(false);
1257         }
1258     }
1261 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1263     static gdouble epsilon = 0.0001;
1264     if ( (numAxes > 0) && axes) {
1265         for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) {
1266             // 0 == new, 1 == set value, 2 == changed value, 3 == active
1267             gdouble diff = axesMap[key][axisNum].second - axes[axisNum];
1268             switch(axesMap[key][axisNum].first) {
1269                 case 0:
1270                 {
1271                     axesMap[key][axisNum].first = 1;
1272                     axesMap[key][axisNum].second = axes[axisNum];
1273                 }
1274                 break;
1275                 case 1:
1276                 {
1277                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1278 //                         g_message("Axis %d changed on %s]", axisNum, key.c_str());
1279                         axesMap[key][axisNum].first = 3;
1280                         axesMap[key][axisNum].second = axes[axisNum];
1281                         updateTestAxes(key, dev);
1282                         DeviceManager::getManager().addAxis(key, axisNum);
1283                     }
1284                 }
1285                 break;
1286                 case 2:
1287                 {
1288                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1289                         axesMap[key][axisNum].first = 3;
1290                         axesMap[key][axisNum].second = axes[axisNum];
1291                         updateTestAxes(key, dev);
1292                     }
1293                 }
1294                 break;
1295                 case 3:
1296                 {
1297                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1298                         axesMap[key][axisNum].second = axes[axisNum];
1299                     } else {
1300                         axesMap[key][axisNum].first = 2;
1301                         updateTestAxes(key, dev);
1302                     }
1303                 }
1304             }
1305         }
1306     }
1307     // std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
1310 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1312     Glib::ustring key;
1313     switch ( device->source ) {
1314         case GDK_SOURCE_MOUSE:
1315             key = "M:";
1316             break;
1317         case GDK_SOURCE_CURSOR:
1318             key = "C:";
1319             break;
1320         case GDK_SOURCE_PEN:
1321             key = "P:";
1322             break;
1323         case GDK_SOURCE_ERASER:
1324             key = "E:";
1325             break;
1326         default:
1327             key = "?:";
1328     }
1329     key += device->name;
1331     return key;
1334 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1336     int modmod = 0;
1338     GdkInputSource source = lastSourceSeen;
1339     Glib::ustring devName = lastDevnameSeen;
1340     Glib::ustring key;
1341     gint hotButton = -1;
1343     switch ( event->type ) {
1344         case GDK_KEY_PRESS:
1345         case GDK_KEY_RELEASE:
1346         {
1347             GdkEventKey* keyEvt = reinterpret_cast<GdkEventKey*>(event);
1348             gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast<GdkModifierType>(keyEvt->state));
1349             keyVal.set_label(name);
1350 //             g_message("%d KEY    state:0x%08x  0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name);
1351             g_free(name);
1352         }
1353         break;
1354         case GDK_BUTTON_PRESS:
1355             modmod = 1;
1356             // fallthrough
1357         case GDK_BUTTON_RELEASE:
1358         {
1359             GdkEventButton* btnEvt = reinterpret_cast<GdkEventButton*>(event);
1360             if ( btnEvt->device ) {
1361                 key = getKeyFor(btnEvt->device);
1362                 source = btnEvt->device->source;
1363                 devName = btnEvt->device->name;
1365                 mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device);
1366                 if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) {
1367 //                     g_message("New button found for %s = %d", key.c_str(), btnEvt->button);
1368                     buttonMap[key].insert(btnEvt->button);
1369                     DeviceManager::getManager().addButton(key, btnEvt->button);
1370                 }
1371                 hotButton = modmod ? btnEvt->button : -1;
1372                 updateTestButtons(key, hotButton);
1373             }
1374             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnEvt->state));
1375             keyVal.set_label(name);
1376 //             g_message("%d BTN    state:0x%08x %c  %4d [%s] dev:%p [%s]  ",
1377 //                       btnEvt->type, btnEvt->state,
1378 //                       (modmod ? '+':'-'),
1379 //                       btnEvt->button, name, btnEvt->device,
1380 //                       (btnEvt->device ? btnEvt->device->name : "null")
1382 //                 );
1383             g_free(name);
1384         }
1385         break;
1386         case GDK_MOTION_NOTIFY:
1387         {
1388             GdkEventMotion* btnMtn = reinterpret_cast<GdkEventMotion*>(event);
1389             if ( btnMtn->device ) {
1390                 key = getKeyFor(btnMtn->device);
1391                 source = btnMtn->device->source;
1392                 devName = btnMtn->device->name;
1393                 mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device);
1394             }
1395             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnMtn->state));
1396             keyVal.set_label(name);
1397 //             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,
1398 //                       name, btnMtn->device,
1399 //                       (btnMtn->device ? btnMtn->device->name : "null"),
1400 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0),
1401 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0),
1402 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0),
1403 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0),
1404 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0),
1405 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0)
1406 //                 );
1407             g_free(name);
1408         }
1409         break;
1410         default:
1411             ;// nothing
1412     }
1415     if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) {
1416         switch (source) {
1417             case GDK_SOURCE_MOUSE:
1418             {
1419                 testThumb.set(getPix(PIX_CORE));
1420             }
1421             break;
1422             case GDK_SOURCE_CURSOR:
1423             {
1424 //                 g_message("flip to cursor");
1425                 testThumb.set(getPix(PIX_MOUSE));
1426             }
1427             break;
1428             case GDK_SOURCE_PEN:
1429             {
1430                 if (devName == _("pad")) {
1431 //                     g_message("flip to pad");
1432                     testThumb.set(getPix(PIX_SIDEBUTTONS));
1433                 } else {
1434 //                     g_message("flip to pen");
1435                     testThumb.set(getPix(PIX_TIP));
1436                 }
1437             }
1438             break;
1439             case GDK_SOURCE_ERASER:
1440             {
1441 //                 g_message("flip to eraser");
1442                 testThumb.set(getPix(PIX_ERASER));
1443             }
1444             break;
1445 //             default:
1446 //                 g_message("gurgle");
1447         }
1448         updateTestButtons(key, hotButton);
1449         lastSourceSeen = source;
1450         lastDevnameSeen = devName;
1451     }
1453     return false;
1457 } // end namespace Inkscape
1458 } // end namespace UI
1459 } // end namespace Dialog
1462 /*
1463   Local Variables:
1464   mode:c++
1465   c-file-style:"stroustrup"
1466   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1467   indent-tabs-mode:nil
1468   fill-column:99
1469   End:
1470 */
1471 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :