Code

Tracking of live axes
[inkscape.git] / src / ui / dialog / input.cpp
3 /* XPM */
4 static char * core_xpm[] = {
5 "16 16 4 1",
6 "       c None",
7 ".      c #808080",
8 "+      c #000000",
9 "@      c #FFFFFF",
10 "                ",
11 "                ",
12 "                ",
13 "    .++++++.    ",
14 "    +@+@@+@+    ",
15 "    +@+@@+@+    ",
16 "    +.+..+.+    ",
17 "    +@@@@@@+    ",
18 "    +@@@@@@+    ",
19 "    +@@@@@@+    ",
20 "    +@@@@@@+    ",
21 "    +@@@@@@+    ",
22 "    .++++++.    ",
23 "                ",
24 "                ",
25 "                "};
27 /* XPM */
28 static char *eraser[] = {
29 /* columns rows colors chars-per-pixel */
30 "16 16 5 1",
31 "  c black",
32 ". c green",
33 "X c #808080",
34 "o c gray100",
35 "O c None",
36 /* pixels */
37 "OOOOOOOOOOOOOOOO",
38 "OOOOOOOOOOOOO OO",
39 "OOOOOOOOOOOO . O",
40 "OOOOOOOOOOO . OO",
41 "OOOOOOOOOO . OOO",
42 "OOOOOOOOO . OOOO",
43 "OOOOOOOO . OOOOO",
44 "OOOOOOOXo OOOOOO",
45 "OOOOOOXoXOOOOOOO",
46 "OOOOOXoXOOOOOOOO",
47 "OOOOXoXOOOOOOOOO",
48 "OOOXoXOOOOOOOOOO",
49 "OOXoXOOOOOOOOOOO",
50 "OOXXOOOOOOOOOOOO",
51 "OOOOOOOOOOOOOOOO",
52 "OOOOOOOOOOOOOOOO"
53 };
55 /* XPM */
56 static char *mouse[] = {
57 /* columns rows colors chars-per-pixel */
58 "16 16 3 1",
59 "  c black",
60 ". c gray100",
61 "X c None",
62 /* pixels */
63 "XXXXXXXXXXXXXXXX",
64 "XXXXXXXXXXXXXXXX",
65 "XXXXXXXXXXXXXXXX",
66 "XXXXXXXXXXXXXXXX",
67 "XXXXXXX  XXXXXXX",
68 "XXXXX  . XXXXXXX",
69 "XXXX .... XXXXXX",
70 "XXXX .... XXXXXX",
71 "XXXXX .... XXXXX",
72 "XXXXX .... XXXXX",
73 "XXXXXX .... XXXX",
74 "XXXXXX .... XXXX",
75 "XXXXXXX .  XXXXX",
76 "XXXXXXX  XXXXXXX",
77 "XXXXXXXXXXXXXXXX",
78 "XXXXXXXXXXXXXXXX"
79 };
81 /* XPM */
82 static char *pen[] = {
83 /* columns rows colors chars-per-pixel */
84 "16 16 3 1",
85 "  c black",
86 ". c gray100",
87 "X c None",
88 /* pixels */
89 "XXXXXXXXXXXXXXXX",
90 "XXXXXXXXXXXXX XX",
91 "XXXXXXXXXXXX . X",
92 "XXXXXXXXXXX . XX",
93 "XXXXXXXXXX . XXX",
94 "XXXXXXXXX . XXXX",
95 "XXXXXXXX . XXXXX",
96 "XXXXXXX . XXXXXX",
97 "XXXXXX . XXXXXXX",
98 "XXXXX . XXXXXXXX",
99 "XXXX . XXXXXXXXX",
100 "XXX . XXXXXXXXXX",
101 "XX . XXXXXXXXXXX",
102 "XX  XXXXXXXXXXXX",
103 "XXXXXXXXXXXXXXXX",
104 "XXXXXXXXXXXXXXXX"
105 };
107 /* XPM */
108 static char *sidebuttons[] = {
109 /* columns rows colors chars-per-pixel */
110 "16 16 4 1",
111 "  c black",
112 ". c #808080",
113 "o c green",
114 "O c None",
115 /* pixels */
116 "OOOOOOOOOOOOOOOO",
117 "OOOOOOOOOOOOOOOO",
118 "O..............O",
119 "O.OOOOOOOOOOOO.O",
120 "O   OOOOOOOO   O",
121 "O o OOOOOOOO o O",
122 "O o OOOOOOOO o O",
123 "O   OOOOOOOO   O",
124 "O.OOOOOOOOOOOO.O",
125 "O.OOOOOOOOOOOO.O",
126 "O.OOOOOOOOOOOO.O",
127 "O.OOOOOOOOOOOO.O",
128 "O.OOOOOOOOOOOO.O",
129 "O..............O",
130 "OOOOOOOOOOOOOOOO",
131 "OOOOOOOOOOOOOOOO"
132 };
134 /* XPM */
135 static char *tablet[] = {
136 /* columns rows colors chars-per-pixel */
137 "16 16 3 1",
138 "  c black",
139 ". c gray100",
140 "X c None",
141 /* pixels */
142 "XXXXXXXXXXXXXXXX",
143 "XXXXXXXXXXXXXXXX",
144 "X              X",
145 "X ............ X",
146 "X ............ X",
147 "X ............ X",
148 "X ............ X",
149 "X ............ X",
150 "X ............ X",
151 "X ............ X",
152 "X ............ X",
153 "X ............ X",
154 "X ............ X",
155 "X              X",
156 "XXXXXXXXXXXXXXXX",
157 "XXXXXXXXXXXXXXXX"
158 };
160 /* XPM */
161 static char *tip[] = {
162 /* columns rows colors chars-per-pixel */
163 "16 16 5 1",
164 "  c black",
165 ". c green",
166 "X c #808080",
167 "o c gray100",
168 "O c None",
169 /* pixels */
170 "OOOOOOOOOOOOOOOO",
171 "OOOOOOOOOOOOOXOO",
172 "OOOOOOOOOOOOXoXO",
173 "OOOOOOOOOOOXoXOO",
174 "OOOOOOOOOOXoXOOO",
175 "OOOOOOOOOXoXOOOO",
176 "OOOOOOOOXoXOOOOO",
177 "OOOOOOO oXOOOOOO",
178 "OOOOOO . OOOOOOO",
179 "OOOOO . OOOOOOOO",
180 "OOOO . OOOOOOOOO",
181 "OOO . OOOOOOOOOO",
182 "OO . OOOOOOOOOOO",
183 "OO  OOOOOOOOOOOO",
184 "OOOOXXXXXOOOOOOO",
185 "OOOOOOOOOXXXXXOO"
186 };
188 /* XPM */
189 static char *button_none[] = {
190 /* columns rows colors chars-per-pixel */
191 "8 8 3 1",
192 "  c black",
193 ". c #808080",
194 "X c None",
195 /* pixels */
196 "XXXXXXXX",
197 "XX .. XX",
198 "X .XX. X",
199 "X.XX X.X",
200 "X.X XX.X",
201 "X .XX. X",
202 "XX .. XX",
203 "XXXXXXXX"
204 };
205 /* XPM */
206 static char *button_off[] = {
207 /* columns rows colors chars-per-pixel */
208 "8 8 4 1",
209 "  c black",
210 ". c #808080",
211 "X c gray100",
212 "o c None",
213 /* pixels */
214 "oooooooo",
215 "oo.  .oo",
216 "o. XX .o",
217 "o XXXX o",
218 "o XXXX o",
219 "o. XX .o",
220 "oo.  .oo",
221 "oooooooo"
222 };
223 /* XPM */
224 static char *button_on[] = {
225 /* columns rows colors chars-per-pixel */
226 "8 8 3 1",
227 "  c black",
228 ". c green",
229 "X c None",
230 /* pixels */
231 "XXXXXXXX",
232 "XX    XX",
233 "X  ..  X",
234 "X .... X",
235 "X .... X",
236 "X  ..  X",
237 "XX    XX",
238 "XXXXXXXX"
239 };
241 /* XPM */
242 static char * axis_none_xpm[] = {
243 "24 8 3 1",
244 "       c None",
245 ".      c #000000",
246 "+      c #808080",
247 "                        ",
248 "  .++++++++++++++++++.  ",
249 " .+               . .+. ",
250 " +          . . .     + ",
251 " +     . . .          + ",
252 " .+. .               +. ",
253 "  .++++++++++++++++++.  ",
254 "                        "};
255 /* XPM */
256 static char * axis_off_xpm[] = {
257 "24 8 4 1",
258 "       c None",
259 ".      c #808080",
260 "+      c #000000",
261 "@      c #FFFFFF",
262 "                        ",
263 "  .++++++++++++++++++.  ",
264 " .+@@@@@@@@@@@@@@@@@@+. ",
265 " +@@@@@@@@@@@@@@@@@@@@+ ",
266 " +@@@@@@@@@@@@@@@@@@@@+ ",
267 " .+@@@@@@@@@@@@@@@@@@+. ",
268 "  .++++++++++++++++++.  ",
269 "                        "};
270 /* XPM */
271 static char * axis_on_xpm[] = {
272 "24 8 3 1",
273 "       c None",
274 ".      c #000000",
275 "+      c #00FF00",
276 "                        ",
277 "  ....................  ",
278 " ..++++++++++++++++++.. ",
279 " .++++++++++++++++++++. ",
280 " .++++++++++++++++++++. ",
281 " ..++++++++++++++++++.. ",
282 "  ....................  ",
283 "                        "};
289 #include <map>
290 #include <set>
291 #include <glib/gprintf.h>
292 #include <glibmm/i18n.h>
293 #include <gtkmm/comboboxtext.h>
294 #include <gtkmm/enums.h>
295 #include <gtkmm/eventbox.h>
296 #include <gtkmm/frame.h>
297 #include <gtkmm/image.h>
298 #include <gtkmm/menubar.h>
299 #include <gtkmm/notebook.h>
300 #include <gtkmm/paned.h>
301 #include <gtkmm/progressbar.h>
302 #include <gtkmm/scrolledwindow.h>
303 #include <gtkmm/table.h>
304 #include <gtkmm/treemodel.h>
305 #include <gtkmm/treemodelcolumn.h>
306 #include <gtkmm/treestore.h>
307 #include <gtkmm/treeview.h>
309 #include "ui/widget/panel.h"
310 #include "device-manager.h"
312 #include "input.h"
314 using Inkscape::InputDevice;
316 namespace Inkscape {
317 namespace UI {
318 namespace Dialog {
322 class MyModelColumns : public Gtk::TreeModel::ColumnRecord
324 public:
325     Gtk::TreeModelColumn<Glib::ustring>                filename;
326     Gtk::TreeModelColumn<Glib::ustring>                description;
327     Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> >  thumbnail;
328     Gtk::TreeModelColumn<InputDevice const *>          device;
330     MyModelColumns() { add(filename); add(description); add(thumbnail); add(device); }
331 };
333 class InputDialogImpl : public InputDialog {
334 public:
335     InputDialogImpl();
336     virtual ~InputDialogImpl() {}
338 private:
339     Glib::RefPtr<Gdk::Pixbuf> corePix;
340     Glib::RefPtr<Gdk::Pixbuf> penPix;
341     Glib::RefPtr<Gdk::Pixbuf> mousePix;
342     Glib::RefPtr<Gdk::Pixbuf> tipPix;
343     Glib::RefPtr<Gdk::Pixbuf> tabletPix;
344     Glib::RefPtr<Gdk::Pixbuf> eraserPix;
345     Glib::RefPtr<Gdk::Pixbuf> sidebuttonsPix;
347     Glib::RefPtr<Gdk::Pixbuf> buttonsNonePix;
348     Glib::RefPtr<Gdk::Pixbuf> buttonsOnPix;
349     Glib::RefPtr<Gdk::Pixbuf> buttonsOffPix;
351     Glib::RefPtr<Gdk::Pixbuf> axisNonePix;
352     Glib::RefPtr<Gdk::Pixbuf> axisOnPix;
353     Glib::RefPtr<Gdk::Pixbuf> axisOffPix;
355     std::map<Glib::ustring, std::set<guint> > buttonMap;
356     std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
358     GdkInputSource lastSourceSeen;
359     Glib::ustring lastDevnameSeen;
361     MyModelColumns cols;
362     Glib::RefPtr<Gtk::TreeStore> store;
363     Gtk::TreeView tree;
364     Gtk::Frame frame2;
365     Gtk::Frame testFrame;
366     Gtk::ScrolledWindow treeScroller;
367     Gtk::ScrolledWindow detailScroller;
368     Gtk::HPaned splitter;
369     Gtk::VPaned split2;
370     Gtk::Label devName;
371     Gtk::Label devKeyCount;
372     Gtk::Label devAxesCount;
373     Gtk::ComboBoxText axesCombo;
374     Gtk::ProgressBar axesValues[6];
375     Gtk::ComboBoxText buttonCombo;
376     Gtk::ComboBoxText linkCombo;
377     sigc::connection linkConnection;
378     Gtk::Label keyVal;
379     Gtk::Entry keyEntry;
380     Gtk::Table devDetails;
381     Gtk::HPaned confSplitter;
382     Gtk::Notebook topHolder;
383     Gtk::Image testThumb;
384     Gtk::Image testButtons[24];
385     Gtk::Image testAxes[8];
386     Gtk::Table imageTable;
387     Gtk::EventBox testDetector;
389     void setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo );
390     void updateTestButtons( Glib::ustring const& key, gint hotButton );
391     void updateTestAxes( Glib::ustring const& key, GdkDevice* dev );
392     void mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev);
393     Glib::ustring getKeyFor( GdkDevice* device );
394     bool eventSnoop(GdkEvent* event);
395     void linkComboChanged();
396     void resyncToSelection();
397     void handleDeviceChange(const Glib::RefPtr<InputDevice>& device);
398     void updateDeviceAxes(const Glib::RefPtr<InputDevice>& device);
399     void updateDeviceButtons(const Glib::RefPtr<InputDevice>& device);
400     void updateDeviceLinks(const Glib::RefPtr<InputDevice>& device);
401 };
404 // Now that we've defined the *Impl class, we can do the method to aquire one.
405 InputDialog &InputDialog::getInstance()
407     InputDialog *dialog = new InputDialogImpl();
408     return *dialog;
412 InputDialogImpl::InputDialogImpl() :
413     InputDialog(),
415     corePix(Gdk::Pixbuf::create_from_xpm_data(core_xpm)),
416     penPix(Gdk::Pixbuf::create_from_xpm_data(pen)),
417     mousePix(Gdk::Pixbuf::create_from_xpm_data(mouse)),
418     tipPix(Gdk::Pixbuf::create_from_xpm_data(tip)),
419     tabletPix(Gdk::Pixbuf::create_from_xpm_data(tablet)),
420     eraserPix(Gdk::Pixbuf::create_from_xpm_data(eraser)),
421     sidebuttonsPix(Gdk::Pixbuf::create_from_xpm_data(sidebuttons)),
423     buttonsNonePix(Gdk::Pixbuf::create_from_xpm_data(button_none)),
424     buttonsOnPix(Gdk::Pixbuf::create_from_xpm_data(button_on)),
425     buttonsOffPix(Gdk::Pixbuf::create_from_xpm_data(button_off)),
427     axisNonePix(Gdk::Pixbuf::create_from_xpm_data(axis_none_xpm)),
428     axisOnPix(Gdk::Pixbuf::create_from_xpm_data(axis_on_xpm)),
429     axisOffPix(Gdk::Pixbuf::create_from_xpm_data(axis_off_xpm)),
431     lastSourceSeen((GdkInputSource)-1),
432     lastDevnameSeen(""),
433     cols(),
434     store(Gtk::TreeStore::create(cols)),
435     tree(store),
436     frame2(),
437     testFrame("Test Area"),
438     treeScroller(),
439     detailScroller(),
440     splitter(),
441     split2(),
442     linkCombo(),
443     devDetails(12, 2),
444     confSplitter(),
445     topHolder(),
446     imageTable(8, 7)
448     Gtk::Box *contents = _getContents();
451     treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
452     treeScroller.add(tree);
453     split2.pack1(testFrame);
454     split2.pack2(frame2);
455     splitter.pack1(treeScroller);
456     splitter.pack2(split2);
458     testDetector.add(imageTable);
459     testFrame.add(testDetector);
460     testThumb.set(tabletPix);
461     testThumb.set_padding(24, 24);
462     imageTable.attach(testThumb, 0, 8, 0, 1, ::Gtk::EXPAND, ::Gtk::EXPAND);
463     {
464         guint col = 0;
465         guint row = 1;
466         for ( guint num = 0; num < G_N_ELEMENTS(testButtons); num++ ) {
467             testButtons[num].set(buttonsNonePix);
468             imageTable.attach(testButtons[num], col, col + 1, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
469             col++;
470             if (col > 7) {
471                 col = 0;
472                 row++;
473             }
474         }
476         col = 0;
477         for ( guint num = 0; num < G_N_ELEMENTS(testAxes); num++ ) {
478             testAxes[num].set(axisNonePix);
479             imageTable.attach(testAxes[num], col * 2, (col + 1) * 2, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
480             col++;
481             if (col > 3) {
482                 col = 0;
483                 row++;
484             }
485         }
486     }
489     topHolder.append_page(confSplitter, "Configuration");
490     topHolder.append_page(splitter, "Hardware");
491 //     confSplitter.show_all();
492 //     splitter.show_all();
493     topHolder.show_all();
494     topHolder.set_current_page(1);
496     contents->pack_start(topHolder);
498     int rowNum = 0;
500     Gtk::Label* lbl = Gtk::manage(new Gtk::Label("Name:"));
501     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
502                       ::Gtk::FILL,
503                       ::Gtk::SHRINK);
504     devDetails.attach(devName, 1, 2, rowNum, rowNum + 1,
505                       ::Gtk::SHRINK,
506                       ::Gtk::SHRINK);
508     rowNum++;
510     lbl = Gtk::manage(new Gtk::Label("Link:"));
511     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
512                       ::Gtk::FILL,
513                       ::Gtk::SHRINK);
515     linkCombo.append_text("None");
516     linkCombo.set_active_text("None");
517     linkCombo.set_sensitive(false);
518     linkConnection = linkCombo.signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::linkComboChanged));
520     devDetails.attach(linkCombo, 1, 2, rowNum, rowNum + 1,
521                       ::Gtk::FILL,
522                       ::Gtk::SHRINK);
523     rowNum++;
525     lbl = Gtk::manage(new Gtk::Label("Reported axes count:"));
526     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
527                       ::Gtk::FILL,
528                       ::Gtk::SHRINK);
529     devDetails.attach(devAxesCount, 1, 2, rowNum, rowNum + 1,
530                       ::Gtk::SHRINK,
531                       ::Gtk::SHRINK);
533     rowNum++;
535     lbl = Gtk::manage(new Gtk::Label("Actual axes count:"));
536     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
537                       ::Gtk::FILL,
538                       ::Gtk::SHRINK);
539     devDetails.attach(axesCombo, 1, 2, rowNum, rowNum + 1,
540                       ::Gtk::SHRINK,
541                       ::Gtk::SHRINK);
543     rowNum++;
545     for ( guint barNum = 0; barNum < static_cast<guint>(G_N_ELEMENTS(axesValues)); barNum++ ) {
546         lbl = Gtk::manage(new Gtk::Label("axis:"));
547         devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
548                           ::Gtk::FILL,
549                           ::Gtk::SHRINK);
550         devDetails.attach(axesValues[barNum], 1, 2, rowNum, rowNum + 1,
551                           ::Gtk::EXPAND,
552                           ::Gtk::SHRINK);
553         axesValues[barNum].set_sensitive(false);
555         rowNum++;
556     }
558     lbl = Gtk::manage(new Gtk::Label("Reported button count:"));
559     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
560                       ::Gtk::FILL,
561                       ::Gtk::SHRINK);
562     devDetails.attach(devKeyCount, 1, 2, rowNum, rowNum + 1,
563                       ::Gtk::SHRINK,
564                       ::Gtk::SHRINK);
566     rowNum++;
568 /*
569     lbl = Gtk::manage(new Gtk::Label("Actual button count:"));
570     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
571                       ::Gtk::FILL,
572                       ::Gtk::SHRINK);
573     devDetails.attach(buttonCombo, 1, 2, rowNum, rowNum + 1,
574                       ::Gtk::SHRINK,
575                       ::Gtk::SHRINK);
577     rowNum++;
578 */
580     devDetails.attach(keyVal, 0, 2, rowNum, rowNum + 1,
581                       ::Gtk::FILL,
582                       ::Gtk::SHRINK);
583     rowNum++;
586     testDetector.signal_event().connect(sigc::mem_fun(*this, &InputDialogImpl::eventSnoop));
588 //     void gdk_input_set_extension_events (GdkWindow        *window,
589 //                                          gint              mask,
590 //                                          GdkExtensionMode  mode);
592     gtk_widget_set_extension_events( GTK_WIDGET(testDetector.gobj()), GDK_EXTENSION_EVENTS_ALL );
593     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);
595     devDetails.attach(keyEntry, 0, 2, rowNum, rowNum + 1,
596                       ::Gtk::FILL,
597                       ::Gtk::SHRINK);
598     rowNum++;
601     devDetails.set_sensitive(false);
602     detailScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
603     detailScroller.add(devDetails);
604     frame2.add(detailScroller);
606 //- 16x16/devices
607 // gnome-dev-mouse-optical
608 // input-mouse
609 // input-tablet
610 // mouse
614     Gtk::TreeModel::Row row;
615     Gtk::TreeModel::Row childrow;
616     Gtk::TreeModel::Row deviceRow;
619     //Add the TreeView's view columns:
620     tree.append_column("I", cols.thumbnail);
621     tree.append_column("Bar", cols.description);
623     tree.set_enable_tree_lines();
624     tree.set_headers_visible(false);
625     tree.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::resyncToSelection));
629     std::list<InputDevice const *> devList = Inkscape::DeviceManager::getManager().getDevices();
630     if ( !devList.empty() ) {
631         row = *(store->append());
632         row[cols.description] = "Hardware";
634         childrow = *(store->append(row.children()));
635         childrow[cols.description] = "Tablet";
636         childrow[cols.thumbnail] = tabletPix;
638         for ( std::list<InputDevice const *>::iterator it = devList.begin(); it != devList.end(); ++it ) {
639             InputDevice const* dev = *it;
640             if ( dev ) {
641 //                 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(),
642 //                           dev->hasCursor() ? "Yes":"no", dev->getNumAxes(), dev->getNumKeys());
644 //                 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
645                 if ( dev ) {
646                     deviceRow = *(store->append(childrow.children()));
647                     deviceRow[cols.description] = dev->getName();
648                     deviceRow[cols.device] = dev;
649                     switch ( dev->getSource() ) {
650                         case GDK_SOURCE_MOUSE:
651                             deviceRow[cols.thumbnail] = corePix;
652                             break;
653                         case GDK_SOURCE_PEN:
654                             if (deviceRow[cols.description] == "pad") {
655                                 deviceRow[cols.thumbnail] = sidebuttonsPix;
656                             } else {
657                                 deviceRow[cols.thumbnail] = tipPix;
658                             }
659                             break;
660                         case GDK_SOURCE_CURSOR:
661                             deviceRow[cols.thumbnail] = mousePix;
662                             break;
663                         case GDK_SOURCE_ERASER:
664                             deviceRow[cols.thumbnail] = eraserPix;
665                             break;
666                         default:
667                             ; // nothing
668                     }
669                 }
670             } else {
671                 g_warning("Null device in list");
672             }
673         }
674     } else {
675         g_warning("No devices found");
676     }
677     Inkscape::DeviceManager::getManager().signalDeviceChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::handleDeviceChange));
678     Inkscape::DeviceManager::getManager().signalAxesChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceAxes));
679     Inkscape::DeviceManager::getManager().signalButtonsChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceButtons));
680     Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceLinks));
682     tree.expand_all();
683     show_all_children();
686 void InputDialogImpl::handleDeviceChange(const Glib::RefPtr<InputDevice>& /*device*/)
688 //     g_message("OUCH!!!! for %p  hits %s", &device, device->getId().c_str());
691 void InputDialogImpl::updateDeviceAxes(const Glib::RefPtr<InputDevice>& device)
693     gint live = device->getLiveAxes();
695     std::map<guint, std::pair<guint, gdouble> > existing = axesMap[device->getId()];
696     gint mask = 0x1;
697     for ( gint num = 0; num < 32; num++, mask <<= 1) {
698         if ( (mask & live) != 0 ) {
699             if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) {
700                 axesMap[device->getId()][num].first = 2;
701                 axesMap[device->getId()][num].second = 0.0;
702             }
703         }
704     }
705     updateTestAxes( device->getId(), 0 );
708 void InputDialogImpl::updateDeviceButtons(const Glib::RefPtr<InputDevice>& device)
710     gint live = device->getLiveButtons();
711     std::set<guint> existing = buttonMap[device->getId()];
712     gint mask = 0x1;
713     for ( gint num = 0; num < 32; num++, mask <<= 1) {
714         if ( (mask & live) != 0 ) {
715             if ( existing.find(num) == existing.end() ) {
716                 buttonMap[device->getId()].insert(num);
717             }
718         }
719     }
720     updateTestButtons(device->getId(), -1);
723 void InputDialogImpl::updateDeviceLinks(const Glib::RefPtr<InputDevice>& /*device*/)
725 //     g_message("Links!!!! for %p  hits %s  with link of %s", &device, device->getId().c_str(), device->getLink().c_str());
728 void InputDialogImpl::linkComboChanged() {
729     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
730     Gtk::TreeModel::iterator iter = treeSel->get_selected();
731     if (iter) {
732         Gtk::TreeModel::Row row = *iter;
733         Glib::ustring val = row[cols.description];
734         InputDevice const * dev = row[cols.device];
735         if ( dev ) {
736             Glib::ustring linkName = linkCombo.get_active_text();
737             std::list<InputDevice const *> devList = Inkscape::DeviceManager::getManager().getDevices();
738             for ( std::list<InputDevice const *>::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
739                 if ( linkName == (*it)->getName() ) {
740                     DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId());
741                     break;
742                 }
743             }
744         }
745     }
748 void InputDialogImpl::resyncToSelection() {
749     bool clear = true;
750     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
751     Gtk::TreeModel::iterator iter = treeSel->get_selected();
752     if (iter) {
753         Gtk::TreeModel::Row row = *iter;
754         Glib::ustring val = row[cols.description];
755         InputDevice const * dev = row[cols.device];
756         if ( dev ) {
757             devDetails.set_sensitive(true);
759             linkConnection.block();
760             linkCombo.clear_items();
761             linkCombo.append_text("None");
762             linkCombo.set_active(0);
763             if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
764                 Glib::ustring linked = dev->getLink();
765                 std::list<InputDevice const *> devList = Inkscape::DeviceManager::getManager().getDevices();
766                 for ( std::list<InputDevice const *>::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
767                     if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) {
768                         linkCombo.append_text((*it)->getName().c_str());
769                         if ( (linked.length() > 0) && (linked == (*it)->getId()) ) {
770                             linkCombo.set_active_text((*it)->getName().c_str());
771                         }
772                     }
773                 }
774                 linkCombo.set_sensitive(true);
775             } else {
776                 linkCombo.set_sensitive(false);
777             }
778             linkConnection.unblock();
780             clear = false;
781             devName.set_label(row[cols.description]);
782             setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo);
783             setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo);
784         }
785     }
787     devDetails.set_sensitive(!clear);
788     if (clear) {
789         devName.set_label("");
790         devAxesCount.set_label("");
791         devKeyCount.set_label("");
792     }
795 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
797     gchar *tmp = g_strdup_printf("%d", reported);
798     label.set_label(tmp);
799     g_free(tmp);
801     combo.clear_items();
802     for ( gint i = 1; i <= reported; ++i ) {
803         tmp = g_strdup_printf("%d", i);
804         combo.append_text(tmp);
805         g_free(tmp);
806     }
808     if ( (1 <= actual) && (actual <= reported) ) {
809         combo.set_active(actual - 1);
810     }
813 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
815     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testButtons)); i++ ) {
816         if ( buttonMap[key].find(i) != buttonMap[key].end() ) {
817             if ( i == hotButton ) {
818                 testButtons[i].set(buttonsOnPix);
819             } else {
820                 testButtons[i].set(buttonsOffPix);
821             }
822         } else {
823             testButtons[i].set(buttonsNonePix);
824         }
825     }
828 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
830     static gdouble epsilon = 0.0001;
831     {
832         Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
833         Gtk::TreeModel::iterator iter = treeSel->get_selected();
834         if (iter) {
835             Gtk::TreeModel::Row row = *iter;
836             Glib::ustring val = row[cols.description];
837             InputDevice const * idev = row[cols.device];
838             if ( !idev || (idev->getId() != key) ) {
839                 dev = 0;
840             }
841         }
842     }
845     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testAxes)); i++ ) {
846         if ( axesMap[key].find(i) != axesMap[key].end() ) {
847             switch ( axesMap[key][i].first ) {
848                 case 0:
849                 case 1:
850                     testAxes[i].set(axisNonePix);
851                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
852                         axesValues[i].set_sensitive(false);
853                     }
854                     break;
855                 case 2:
856                     testAxes[i].set(axisOffPix);
857                     axesValues[i].set_sensitive(true);
858                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
859                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
860                             axesValues[i].set_sensitive(true);
861                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
862                         }
863                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
864                         axesValues[i].set_text(str);
865                         g_free(str);
866                     }
867                     break;
868                 case 3:
869                     testAxes[i].set(axisOnPix);
870                     axesValues[i].set_sensitive(true);
871                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
872                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
873                             axesValues[i].set_sensitive(true);
874                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
875                         }
876                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
877                         axesValues[i].set_text(str);
878                         g_free(str);
879                     }
880             }
882         } else {
883             testAxes[i].set(axisNonePix);
884         }
885     }
886     if ( !dev ) {
887         for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(axesValues)); i++ ) {
888             axesValues[i].set_fraction(0.0);
889             axesValues[i].set_text("");
890             axesValues[i].set_sensitive(false);
891         }
892     }
895 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
897     static gdouble epsilon = 0.0001;
898     if ( (numAxes > 0) && axes) {
899         for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) {
900             // 0 == new, 1 == set value, 2 == changed value, 3 == active
901             gdouble diff = axesMap[key][axisNum].second - axes[axisNum];
902             switch(axesMap[key][axisNum].first) {
903                 case 0:
904                 {
905                     axesMap[key][axisNum].first = 1;
906                     axesMap[key][axisNum].second = axes[axisNum];
907                 }
908                 break;
909                 case 1:
910                 {
911                     if ( (diff > epsilon) || (diff < -epsilon) ) {
912 //                         g_message("Axis %d changed on %s]", axisNum, key.c_str());
913                         axesMap[key][axisNum].first = 3;
914                         axesMap[key][axisNum].second = axes[axisNum];
915                         updateTestAxes(key, dev);
916                         DeviceManager::getManager().addAxis(key, axisNum);
917                     }
918                 }
919                 break;
920                 case 2:
921                 {
922                     if ( (diff > epsilon) || (diff < -epsilon) ) {
923                         axesMap[key][axisNum].first = 3;
924                         axesMap[key][axisNum].second = axes[axisNum];
925                         updateTestAxes(key, dev);
926                     }
927                 }
928                 break;
929                 case 3:
930                 {
931                     if ( (diff > epsilon) || (diff < -epsilon) ) {
932                         axesMap[key][axisNum].second = axes[axisNum];
933                     } else {
934                         axesMap[key][axisNum].first = 2;
935                         updateTestAxes(key, dev);
936                     }
937                 }
938             }
939         }
940     }
941     // std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
944 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
946     Glib::ustring key;
947     switch ( device->source ) {
948         case GDK_SOURCE_MOUSE:
949             key = "M:";
950             break;
951         case GDK_SOURCE_CURSOR:
952             key = "C:";
953             break;
954         case GDK_SOURCE_PEN:
955             key = "P:";
956             break;
957         case GDK_SOURCE_ERASER:
958             key = "E:";
959             break;
960         default:
961             key = "?:";
962     }
963     key += device->name;
965     return key;
968 bool InputDialogImpl::eventSnoop(GdkEvent* event)
970     int modmod = 0;
972     GdkInputSource source = lastSourceSeen;
973     Glib::ustring devName = lastDevnameSeen;
974     Glib::ustring key;
975     gint hotButton = -1;
977     switch ( event->type ) {
978         case GDK_KEY_PRESS:
979         case GDK_KEY_RELEASE:
980         {
981             GdkEventKey* keyEvt = reinterpret_cast<GdkEventKey*>(event);
982             gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast<GdkModifierType>(keyEvt->state));
983             keyVal.set_label(name);
984 //             g_message("%d KEY    state:0x%08x  0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name);
985             g_free(name);
986         }
987         break;
988         case GDK_BUTTON_PRESS:
989             modmod = 1;
990             // fallthrough
991         case GDK_BUTTON_RELEASE:
992         {
993             GdkEventButton* btnEvt = reinterpret_cast<GdkEventButton*>(event);
994             if ( btnEvt->device ) {
995                 key = getKeyFor(btnEvt->device);
996                 source = btnEvt->device->source;
997                 devName = btnEvt->device->name;
999                 mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device);
1000                 if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) {
1001 //                     g_message("New button found for %s = %d", key.c_str(), btnEvt->button);
1002                     buttonMap[key].insert(btnEvt->button);
1003                     DeviceManager::getManager().addButton(key, btnEvt->button);
1004                 }
1005                 hotButton = modmod ? btnEvt->button : -1;
1006                 updateTestButtons(key, hotButton);
1007             }
1008             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnEvt->state));
1009             keyVal.set_label(name);
1010 //             g_message("%d BTN    state:0x%08x %c  %4d [%s] dev:%p [%s]  ",
1011 //                       btnEvt->type, btnEvt->state,
1012 //                       (modmod ? '+':'-'),
1013 //                       btnEvt->button, name, btnEvt->device,
1014 //                       (btnEvt->device ? btnEvt->device->name : "null")
1016 //                 );
1017             g_free(name);
1018         }
1019         break;
1020         case GDK_MOTION_NOTIFY:
1021         {
1022             GdkEventMotion* btnMtn = reinterpret_cast<GdkEventMotion*>(event);
1023             if ( btnMtn->device ) {
1024                 key = getKeyFor(btnMtn->device);
1025                 source = btnMtn->device->source;
1026                 devName = btnMtn->device->name;
1027                 mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device);
1028             }
1029             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnMtn->state));
1030             keyVal.set_label(name);
1031 //             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,
1032 //                       name, btnMtn->device,
1033 //                       (btnMtn->device ? btnMtn->device->name : "null"),
1034 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0),
1035 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0),
1036 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0),
1037 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0),
1038 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0),
1039 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0)
1040 //                 );
1041             g_free(name);
1042         }
1043         break;
1044         default:
1045             ;// nothing
1046     }
1049     if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) {
1050         switch (source) {
1051             case GDK_SOURCE_MOUSE:
1052             {
1053                 testThumb.set(corePix);
1054             }
1055             break;
1056             case GDK_SOURCE_CURSOR:
1057             {
1058 //                 g_message("flip to cursor");
1059                 testThumb.set(mousePix);
1060             }
1061             break;
1062             case GDK_SOURCE_PEN:
1063             {
1064                 if (devName == "pad") {
1065 //                     g_message("flip to pad");
1066                     testThumb.set(sidebuttonsPix);
1067                 } else {
1068 //                     g_message("flip to pen");
1069                     testThumb.set(tipPix);
1070                 }
1071             }
1072             break;
1073             case GDK_SOURCE_ERASER:
1074             {
1075 //                 g_message("flip to eraser");
1076                 testThumb.set(eraserPix);
1077             }
1078             break;
1079 //             default:
1080 //                 g_message("gurgle");
1081         }
1082         updateTestButtons(key, hotButton);
1083         lastSourceSeen = source;
1084         lastDevnameSeen = devName;
1085     }
1087     return false;
1091 } // end namespace Inkscape
1092 } // end namespace UI
1093 } // end namespace Dialog
1096 /*
1097   Local Variables:
1098   mode:c++
1099   c-file-style:"stroustrup"
1100   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1101   indent-tabs-mode:nil
1102   fill-column:99
1103   End:
1104 */
1105 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :