Code

Visually indicate linked devices
[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::TreeIter tabletIter;
364     Gtk::TreeView tree;
365     Gtk::Frame frame2;
366     Gtk::Frame testFrame;
367     Gtk::ScrolledWindow treeScroller;
368     Gtk::ScrolledWindow detailScroller;
369     Gtk::HPaned splitter;
370     Gtk::VPaned split2;
371     Gtk::Label devName;
372     Gtk::Label devKeyCount;
373     Gtk::Label devAxesCount;
374     Gtk::ComboBoxText axesCombo;
375     Gtk::ProgressBar axesValues[6];
376     Gtk::ComboBoxText buttonCombo;
377     Gtk::ComboBoxText linkCombo;
378     sigc::connection linkConnection;
379     Gtk::Label keyVal;
380     Gtk::Entry keyEntry;
381     Gtk::Table devDetails;
382     Gtk::HPaned confSplitter;
383     Gtk::Notebook topHolder;
384     Gtk::Image testThumb;
385     Gtk::Image testButtons[24];
386     Gtk::Image testAxes[8];
387     Gtk::Table imageTable;
388     Gtk::EventBox testDetector;
390     void setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo );
391     void updateTestButtons( Glib::ustring const& key, gint hotButton );
392     void updateTestAxes( Glib::ustring const& key, GdkDevice* dev );
393     void mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev);
394     Glib::ustring getKeyFor( GdkDevice* device );
395     bool eventSnoop(GdkEvent* event);
396     void linkComboChanged();
397     void resyncToSelection();
398     void handleDeviceChange(const Glib::RefPtr<InputDevice>& device);
399     void updateDeviceAxes(const Glib::RefPtr<InputDevice>& device);
400     void updateDeviceButtons(const Glib::RefPtr<InputDevice>& device);
401     void updateDeviceLinks(const Glib::RefPtr<InputDevice>& device);
403     bool findDevice(const Gtk::TreeModel::iterator& iter,
404                     Glib::ustring id,
405                     Gtk::TreeModel::iterator* result);
406     bool findDeviceByLink(const Gtk::TreeModel::iterator& iter,
407                           Glib::ustring link,
408                           Gtk::TreeModel::iterator* result);
409 };
412 // Now that we've defined the *Impl class, we can do the method to aquire one.
413 InputDialog &InputDialog::getInstance()
415     InputDialog *dialog = new InputDialogImpl();
416     return *dialog;
420 InputDialogImpl::InputDialogImpl() :
421     InputDialog(),
423     corePix(Gdk::Pixbuf::create_from_xpm_data(core_xpm)),
424     penPix(Gdk::Pixbuf::create_from_xpm_data(pen)),
425     mousePix(Gdk::Pixbuf::create_from_xpm_data(mouse)),
426     tipPix(Gdk::Pixbuf::create_from_xpm_data(tip)),
427     tabletPix(Gdk::Pixbuf::create_from_xpm_data(tablet)),
428     eraserPix(Gdk::Pixbuf::create_from_xpm_data(eraser)),
429     sidebuttonsPix(Gdk::Pixbuf::create_from_xpm_data(sidebuttons)),
431     buttonsNonePix(Gdk::Pixbuf::create_from_xpm_data(button_none)),
432     buttonsOnPix(Gdk::Pixbuf::create_from_xpm_data(button_on)),
433     buttonsOffPix(Gdk::Pixbuf::create_from_xpm_data(button_off)),
435     axisNonePix(Gdk::Pixbuf::create_from_xpm_data(axis_none_xpm)),
436     axisOnPix(Gdk::Pixbuf::create_from_xpm_data(axis_on_xpm)),
437     axisOffPix(Gdk::Pixbuf::create_from_xpm_data(axis_off_xpm)),
439     lastSourceSeen((GdkInputSource)-1),
440     lastDevnameSeen(""),
441     cols(),
442     store(Gtk::TreeStore::create(cols)),
443     tree(store),
444     frame2(),
445     testFrame("Test Area"),
446     treeScroller(),
447     detailScroller(),
448     splitter(),
449     split2(),
450     linkCombo(),
451     devDetails(12, 2),
452     confSplitter(),
453     topHolder(),
454     imageTable(8, 7)
456     Gtk::Box *contents = _getContents();
459     treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
460     treeScroller.add(tree);
461     split2.pack1(testFrame);
462     split2.pack2(frame2);
463     splitter.pack1(treeScroller);
464     splitter.pack2(split2);
466     testDetector.add(imageTable);
467     testFrame.add(testDetector);
468     testThumb.set(tabletPix);
469     testThumb.set_padding(24, 24);
470     imageTable.attach(testThumb, 0, 8, 0, 1, ::Gtk::EXPAND, ::Gtk::EXPAND);
471     {
472         guint col = 0;
473         guint row = 1;
474         for ( guint num = 0; num < G_N_ELEMENTS(testButtons); num++ ) {
475             testButtons[num].set(buttonsNonePix);
476             imageTable.attach(testButtons[num], col, col + 1, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
477             col++;
478             if (col > 7) {
479                 col = 0;
480                 row++;
481             }
482         }
484         col = 0;
485         for ( guint num = 0; num < G_N_ELEMENTS(testAxes); num++ ) {
486             testAxes[num].set(axisNonePix);
487             imageTable.attach(testAxes[num], col * 2, (col + 1) * 2, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
488             col++;
489             if (col > 3) {
490                 col = 0;
491                 row++;
492             }
493         }
494     }
497     topHolder.append_page(confSplitter, "Configuration");
498     topHolder.append_page(splitter, "Hardware");
499 //     confSplitter.show_all();
500 //     splitter.show_all();
501     topHolder.show_all();
502     topHolder.set_current_page(1);
504     contents->pack_start(topHolder);
506     int rowNum = 0;
508     Gtk::Label* lbl = Gtk::manage(new Gtk::Label("Name:"));
509     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
510                       ::Gtk::FILL,
511                       ::Gtk::SHRINK);
512     devDetails.attach(devName, 1, 2, rowNum, rowNum + 1,
513                       ::Gtk::SHRINK,
514                       ::Gtk::SHRINK);
516     rowNum++;
518     lbl = Gtk::manage(new Gtk::Label("Link:"));
519     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
520                       ::Gtk::FILL,
521                       ::Gtk::SHRINK);
523     linkCombo.append_text("None");
524     linkCombo.set_active_text("None");
525     linkCombo.set_sensitive(false);
526     linkConnection = linkCombo.signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::linkComboChanged));
528     devDetails.attach(linkCombo, 1, 2, rowNum, rowNum + 1,
529                       ::Gtk::FILL,
530                       ::Gtk::SHRINK);
531     rowNum++;
533     lbl = Gtk::manage(new Gtk::Label("Axes count:"));
534     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
535                       ::Gtk::FILL,
536                       ::Gtk::SHRINK);
537     devDetails.attach(devAxesCount, 1, 2, rowNum, rowNum + 1,
538                       ::Gtk::SHRINK,
539                       ::Gtk::SHRINK);
541     rowNum++;
543 /*
544     lbl = Gtk::manage(new Gtk::Label("Actual axes count:"));
545     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
546                       ::Gtk::FILL,
547                       ::Gtk::SHRINK);
548     devDetails.attach(axesCombo, 1, 2, rowNum, rowNum + 1,
549                       ::Gtk::SHRINK,
550                       ::Gtk::SHRINK);
552     rowNum++;
553 */
555     for ( guint barNum = 0; barNum < static_cast<guint>(G_N_ELEMENTS(axesValues)); barNum++ ) {
556         lbl = Gtk::manage(new Gtk::Label("axis:"));
557         devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
558                           ::Gtk::FILL,
559                           ::Gtk::SHRINK);
560         devDetails.attach(axesValues[barNum], 1, 2, rowNum, rowNum + 1,
561                           ::Gtk::EXPAND,
562                           ::Gtk::SHRINK);
563         axesValues[barNum].set_sensitive(false);
565         rowNum++;
566     }
568     lbl = Gtk::manage(new Gtk::Label("Button count:"));
569     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
570                       ::Gtk::FILL,
571                       ::Gtk::SHRINK);
572     devDetails.attach(devKeyCount, 1, 2, rowNum, rowNum + 1,
573                       ::Gtk::SHRINK,
574                       ::Gtk::SHRINK);
576     rowNum++;
578 /*
579     lbl = Gtk::manage(new Gtk::Label("Actual button count:"));
580     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
581                       ::Gtk::FILL,
582                       ::Gtk::SHRINK);
583     devDetails.attach(buttonCombo, 1, 2, rowNum, rowNum + 1,
584                       ::Gtk::SHRINK,
585                       ::Gtk::SHRINK);
587     rowNum++;
588 */
590     devDetails.attach(keyVal, 0, 2, rowNum, rowNum + 1,
591                       ::Gtk::FILL,
592                       ::Gtk::SHRINK);
593     rowNum++;
596     testDetector.signal_event().connect(sigc::mem_fun(*this, &InputDialogImpl::eventSnoop));
598 //     void gdk_input_set_extension_events (GdkWindow        *window,
599 //                                          gint              mask,
600 //                                          GdkExtensionMode  mode);
602     gtk_widget_set_extension_events( GTK_WIDGET(testDetector.gobj()), GDK_EXTENSION_EVENTS_ALL );
603     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);
605     devDetails.attach(keyEntry, 0, 2, rowNum, rowNum + 1,
606                       ::Gtk::FILL,
607                       ::Gtk::SHRINK);
608     rowNum++;
611     devDetails.set_sensitive(false);
612     detailScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
613     detailScroller.add(devDetails);
614     frame2.add(detailScroller);
616 //- 16x16/devices
617 // gnome-dev-mouse-optical
618 // input-mouse
619 // input-tablet
620 // mouse
624     Gtk::TreeModel::Row row;
625     Gtk::TreeModel::Row childrow;
626     Gtk::TreeModel::Row deviceRow;
629     //Add the TreeView's view columns:
630     tree.append_column("I", cols.thumbnail);
631     tree.append_column("Bar", cols.description);
633     tree.set_enable_tree_lines();
634     tree.set_headers_visible(false);
635     tree.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::resyncToSelection));
639     std::list<InputDevice const *> devList = Inkscape::DeviceManager::getManager().getDevices();
640     if ( !devList.empty() ) {
641         row = *(store->append());
642         row[cols.description] = "Hardware";
644         tabletIter = store->append(row.children());
645         childrow = *tabletIter;
646         childrow[cols.description] = "Tablet";
647         childrow[cols.thumbnail] = tabletPix;
649         for ( std::list<InputDevice const *>::iterator it = devList.begin(); it != devList.end(); ++it ) {
650             InputDevice const* dev = *it;
651             if ( dev ) {
652 //                 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(),
653 //                           dev->hasCursor() ? "Yes":"no", dev->getNumAxes(), dev->getNumKeys());
655 //                 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
656                 if ( dev ) {
657                     deviceRow = *(store->append(childrow.children()));
658                     deviceRow[cols.description] = dev->getName();
659                     deviceRow[cols.device] = dev;
660                     switch ( dev->getSource() ) {
661                         case GDK_SOURCE_MOUSE:
662                             deviceRow[cols.thumbnail] = corePix;
663                             break;
664                         case GDK_SOURCE_PEN:
665                             if (deviceRow[cols.description] == "pad") {
666                                 deviceRow[cols.thumbnail] = sidebuttonsPix;
667                             } else {
668                                 deviceRow[cols.thumbnail] = tipPix;
669                             }
670                             break;
671                         case GDK_SOURCE_CURSOR:
672                             deviceRow[cols.thumbnail] = mousePix;
673                             break;
674                         case GDK_SOURCE_ERASER:
675                             deviceRow[cols.thumbnail] = eraserPix;
676                             break;
677                         default:
678                             ; // nothing
679                     }
680                 }
681             } else {
682                 g_warning("Null device in list");
683             }
684         }
685     } else {
686         g_warning("No devices found");
687     }
688     Inkscape::DeviceManager::getManager().signalDeviceChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::handleDeviceChange));
689     Inkscape::DeviceManager::getManager().signalAxesChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceAxes));
690     Inkscape::DeviceManager::getManager().signalButtonsChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceButtons));
691     Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceLinks));
693     tree.expand_all();
694     show_all_children();
697 void InputDialogImpl::handleDeviceChange(const Glib::RefPtr<InputDevice>& /*device*/)
699 //     g_message("OUCH!!!! for %p  hits %s", &device, device->getId().c_str());
702 void InputDialogImpl::updateDeviceAxes(const Glib::RefPtr<InputDevice>& device)
704     gint live = device->getLiveAxes();
706     std::map<guint, std::pair<guint, gdouble> > existing = axesMap[device->getId()];
707     gint mask = 0x1;
708     for ( gint num = 0; num < 32; num++, mask <<= 1) {
709         if ( (mask & live) != 0 ) {
710             if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) {
711                 axesMap[device->getId()][num].first = 2;
712                 axesMap[device->getId()][num].second = 0.0;
713             }
714         }
715     }
716     updateTestAxes( device->getId(), 0 );
719 void InputDialogImpl::updateDeviceButtons(const Glib::RefPtr<InputDevice>& device)
721     gint live = device->getLiveButtons();
722     std::set<guint> existing = buttonMap[device->getId()];
723     gint mask = 0x1;
724     for ( gint num = 0; num < 32; num++, mask <<= 1) {
725         if ( (mask & live) != 0 ) {
726             if ( existing.find(num) == existing.end() ) {
727                 buttonMap[device->getId()].insert(num);
728             }
729         }
730     }
731     updateTestButtons(device->getId(), -1);
735 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
736                                  Glib::ustring id,
737                                  Gtk::TreeModel::iterator* result)
739     bool stop = false;
740     const InputDevice* dev = (*iter)[cols.device];
741     if ( dev && (dev->getId() == id) ) {
742         if ( result ) {
743             *result = iter;
744         }
745         stop = true;
746     }
747     return stop;
750 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
751                                        Glib::ustring link,
752                                        Gtk::TreeModel::iterator* result)
754     bool stop = false;
755     const InputDevice* dev = (*iter)[cols.device];
756     if ( dev && (dev->getLink() == link) ) {
757         if ( result ) {
758             *result = iter;
759         }
760         stop = true;
761     }
762     return stop;
765 void InputDialogImpl::updateDeviceLinks(const Glib::RefPtr<InputDevice>& device)
767 //     g_message("Links!!!! for %p  hits [%s]  with link of [%s]", &device, device->getId().c_str(), device->getLink().c_str());
768     Gtk::TreeModel::iterator deviceIter;
769     store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
770                              sigc::mem_fun(*this, &InputDialogImpl::findDevice),
771                              device->getId(),
772                              &deviceIter) );
774     if ( deviceIter ) {
775         // Found the device concerned. Can proceed.
777         if ( device->getLink().empty() ) {
778             // is now unlinked
779 //             g_message("Item %s is unlinked", device->getId().c_str());
780             if ( deviceIter->parent() != tabletIter ) {
781                 // Not the child of the tablet. move on up
782                 
783                 InputDevice const *dev = (*deviceIter)[cols.device];
784                 Glib::ustring descr = (*deviceIter)[cols.description];
785                 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[cols.thumbnail];
787                 Gtk::TreeModel::Row deviceRow = *store->append(tabletIter->children());
788                 deviceRow[cols.description] = descr;
789                 deviceRow[cols.thumbnail] = thumb;
790                 deviceRow[cols.device] = dev;
792                 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
793                 store->erase(deviceIter);
794                 if ( oldParent->children().empty() ) {
795                     store->erase(oldParent);
796                 }
797             }
798         } else {
799             // is linking
800             if ( deviceIter->parent() == tabletIter ) {
801                 // Simple case. Not already linked
803                 Gtk::TreeIter newGroup = store->append(tabletIter->children());
804                 (*newGroup)[cols.description] = "Pen";
805                 (*newGroup)[cols.thumbnail] = penPix;
807                 InputDevice const *dev = (*deviceIter)[cols.device];
808                 Glib::ustring descr = (*deviceIter)[cols.description];
809                 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[cols.thumbnail];
811                 Gtk::TreeModel::Row deviceRow = *store->append(newGroup->children());
812                 deviceRow[cols.description] = descr;
813                 deviceRow[cols.thumbnail] = thumb;
814                 deviceRow[cols.device] = dev;
816                 
817                 Gtk::TreeModel::iterator linkIter;
818                 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
819                                          sigc::mem_fun(*this, &InputDialogImpl::findDeviceByLink),
820                                          device->getId(),
821                                          &linkIter) );
822                 if ( linkIter ) {
823                     dev = (*linkIter)[cols.device];
824                     descr = (*linkIter)[cols.description];
825                     thumb = (*linkIter)[cols.thumbnail];
827                     deviceRow = *store->append(newGroup->children());
828                     deviceRow[cols.description] = descr;
829                     deviceRow[cols.thumbnail] = thumb;
830                     deviceRow[cols.device] = dev;
831                     Gtk::TreeModel::iterator oldParent = linkIter->parent();
832                     store->erase(linkIter);
833                     if ( oldParent->children().empty() ) {
834                         store->erase(oldParent);
835                     }
836                 }
838                 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
839                 store->erase(deviceIter);
840                 if ( oldParent->children().empty() ) {
841                     store->erase(oldParent);
842                 }
843                 tree.expand_row(Gtk::TreePath(newGroup), true);
844             }
845         }
846     }
849 void InputDialogImpl::linkComboChanged() {
850     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
851     Gtk::TreeModel::iterator iter = treeSel->get_selected();
852     if (iter) {
853         Gtk::TreeModel::Row row = *iter;
854         Glib::ustring val = row[cols.description];
855         InputDevice const * dev = row[cols.device];
856         if ( dev ) {
857             if ( linkCombo.get_active_row_number() == 0 ) {
858                 // It is the "None" entry
859                 DeviceManager::getManager().setLinkedTo(dev->getId(), "");
860             } else {
861                 Glib::ustring linkName = linkCombo.get_active_text();
862                 std::list<InputDevice const *> devList = Inkscape::DeviceManager::getManager().getDevices();
863                 for ( std::list<InputDevice const *>::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
864                     if ( linkName == (*it)->getName() ) {
865                         DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId());
866                         break;
867                     }
868                 }
869             }
870         }
871     }
874 void InputDialogImpl::resyncToSelection() {
875     bool clear = true;
876     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
877     Gtk::TreeModel::iterator iter = treeSel->get_selected();
878     if (iter) {
879         Gtk::TreeModel::Row row = *iter;
880         Glib::ustring val = row[cols.description];
881         InputDevice const * dev = row[cols.device];
882         if ( dev ) {
883             devDetails.set_sensitive(true);
885             linkConnection.block();
886             linkCombo.clear_items();
887             linkCombo.append_text("None");
888             linkCombo.set_active(0);
889             if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
890                 Glib::ustring linked = dev->getLink();
891                 std::list<InputDevice const *> devList = Inkscape::DeviceManager::getManager().getDevices();
892                 for ( std::list<InputDevice const *>::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
893                     if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) {
894                         linkCombo.append_text((*it)->getName().c_str());
895                         if ( (linked.length() > 0) && (linked == (*it)->getId()) ) {
896                             linkCombo.set_active_text((*it)->getName().c_str());
897                         }
898                     }
899                 }
900                 linkCombo.set_sensitive(true);
901             } else {
902                 linkCombo.set_sensitive(false);
903             }
904             linkConnection.unblock();
906             clear = false;
907             devName.set_label(row[cols.description]);
908             setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo);
909             setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo);
910         }
911     }
913     devDetails.set_sensitive(!clear);
914     if (clear) {
915         devName.set_label("");
916         devAxesCount.set_label("");
917         devKeyCount.set_label("");
918     }
921 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
923     gchar *tmp = g_strdup_printf("%d", reported);
924     label.set_label(tmp);
925     g_free(tmp);
927     combo.clear_items();
928     for ( gint i = 1; i <= reported; ++i ) {
929         tmp = g_strdup_printf("%d", i);
930         combo.append_text(tmp);
931         g_free(tmp);
932     }
934     if ( (1 <= actual) && (actual <= reported) ) {
935         combo.set_active(actual - 1);
936     }
939 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
941     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testButtons)); i++ ) {
942         if ( buttonMap[key].find(i) != buttonMap[key].end() ) {
943             if ( i == hotButton ) {
944                 testButtons[i].set(buttonsOnPix);
945             } else {
946                 testButtons[i].set(buttonsOffPix);
947             }
948         } else {
949             testButtons[i].set(buttonsNonePix);
950         }
951     }
954 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
956     static gdouble epsilon = 0.0001;
957     {
958         Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
959         Gtk::TreeModel::iterator iter = treeSel->get_selected();
960         if (iter) {
961             Gtk::TreeModel::Row row = *iter;
962             Glib::ustring val = row[cols.description];
963             InputDevice const * idev = row[cols.device];
964             if ( !idev || (idev->getId() != key) ) {
965                 dev = 0;
966             }
967         }
968     }
971     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testAxes)); i++ ) {
972         if ( axesMap[key].find(i) != axesMap[key].end() ) {
973             switch ( axesMap[key][i].first ) {
974                 case 0:
975                 case 1:
976                     testAxes[i].set(axisNonePix);
977                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
978                         axesValues[i].set_sensitive(false);
979                     }
980                     break;
981                 case 2:
982                     testAxes[i].set(axisOffPix);
983                     axesValues[i].set_sensitive(true);
984                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
985                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
986                             axesValues[i].set_sensitive(true);
987                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
988                         }
989                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
990                         axesValues[i].set_text(str);
991                         g_free(str);
992                     }
993                     break;
994                 case 3:
995                     testAxes[i].set(axisOnPix);
996                     axesValues[i].set_sensitive(true);
997                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
998                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
999                             axesValues[i].set_sensitive(true);
1000                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1001                         }
1002                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1003                         axesValues[i].set_text(str);
1004                         g_free(str);
1005                     }
1006             }
1008         } else {
1009             testAxes[i].set(axisNonePix);
1010         }
1011     }
1012     if ( !dev ) {
1013         for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(axesValues)); i++ ) {
1014             axesValues[i].set_fraction(0.0);
1015             axesValues[i].set_text("");
1016             axesValues[i].set_sensitive(false);
1017         }
1018     }
1021 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1023     static gdouble epsilon = 0.0001;
1024     if ( (numAxes > 0) && axes) {
1025         for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) {
1026             // 0 == new, 1 == set value, 2 == changed value, 3 == active
1027             gdouble diff = axesMap[key][axisNum].second - axes[axisNum];
1028             switch(axesMap[key][axisNum].first) {
1029                 case 0:
1030                 {
1031                     axesMap[key][axisNum].first = 1;
1032                     axesMap[key][axisNum].second = axes[axisNum];
1033                 }
1034                 break;
1035                 case 1:
1036                 {
1037                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1038 //                         g_message("Axis %d changed on %s]", axisNum, key.c_str());
1039                         axesMap[key][axisNum].first = 3;
1040                         axesMap[key][axisNum].second = axes[axisNum];
1041                         updateTestAxes(key, dev);
1042                         DeviceManager::getManager().addAxis(key, axisNum);
1043                     }
1044                 }
1045                 break;
1046                 case 2:
1047                 {
1048                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1049                         axesMap[key][axisNum].first = 3;
1050                         axesMap[key][axisNum].second = axes[axisNum];
1051                         updateTestAxes(key, dev);
1052                     }
1053                 }
1054                 break;
1055                 case 3:
1056                 {
1057                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1058                         axesMap[key][axisNum].second = axes[axisNum];
1059                     } else {
1060                         axesMap[key][axisNum].first = 2;
1061                         updateTestAxes(key, dev);
1062                     }
1063                 }
1064             }
1065         }
1066     }
1067     // std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
1070 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1072     Glib::ustring key;
1073     switch ( device->source ) {
1074         case GDK_SOURCE_MOUSE:
1075             key = "M:";
1076             break;
1077         case GDK_SOURCE_CURSOR:
1078             key = "C:";
1079             break;
1080         case GDK_SOURCE_PEN:
1081             key = "P:";
1082             break;
1083         case GDK_SOURCE_ERASER:
1084             key = "E:";
1085             break;
1086         default:
1087             key = "?:";
1088     }
1089     key += device->name;
1091     return key;
1094 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1096     int modmod = 0;
1098     GdkInputSource source = lastSourceSeen;
1099     Glib::ustring devName = lastDevnameSeen;
1100     Glib::ustring key;
1101     gint hotButton = -1;
1103     switch ( event->type ) {
1104         case GDK_KEY_PRESS:
1105         case GDK_KEY_RELEASE:
1106         {
1107             GdkEventKey* keyEvt = reinterpret_cast<GdkEventKey*>(event);
1108             gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast<GdkModifierType>(keyEvt->state));
1109             keyVal.set_label(name);
1110 //             g_message("%d KEY    state:0x%08x  0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name);
1111             g_free(name);
1112         }
1113         break;
1114         case GDK_BUTTON_PRESS:
1115             modmod = 1;
1116             // fallthrough
1117         case GDK_BUTTON_RELEASE:
1118         {
1119             GdkEventButton* btnEvt = reinterpret_cast<GdkEventButton*>(event);
1120             if ( btnEvt->device ) {
1121                 key = getKeyFor(btnEvt->device);
1122                 source = btnEvt->device->source;
1123                 devName = btnEvt->device->name;
1125                 mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device);
1126                 if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) {
1127 //                     g_message("New button found for %s = %d", key.c_str(), btnEvt->button);
1128                     buttonMap[key].insert(btnEvt->button);
1129                     DeviceManager::getManager().addButton(key, btnEvt->button);
1130                 }
1131                 hotButton = modmod ? btnEvt->button : -1;
1132                 updateTestButtons(key, hotButton);
1133             }
1134             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnEvt->state));
1135             keyVal.set_label(name);
1136 //             g_message("%d BTN    state:0x%08x %c  %4d [%s] dev:%p [%s]  ",
1137 //                       btnEvt->type, btnEvt->state,
1138 //                       (modmod ? '+':'-'),
1139 //                       btnEvt->button, name, btnEvt->device,
1140 //                       (btnEvt->device ? btnEvt->device->name : "null")
1142 //                 );
1143             g_free(name);
1144         }
1145         break;
1146         case GDK_MOTION_NOTIFY:
1147         {
1148             GdkEventMotion* btnMtn = reinterpret_cast<GdkEventMotion*>(event);
1149             if ( btnMtn->device ) {
1150                 key = getKeyFor(btnMtn->device);
1151                 source = btnMtn->device->source;
1152                 devName = btnMtn->device->name;
1153                 mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device);
1154             }
1155             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnMtn->state));
1156             keyVal.set_label(name);
1157 //             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,
1158 //                       name, btnMtn->device,
1159 //                       (btnMtn->device ? btnMtn->device->name : "null"),
1160 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0),
1161 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0),
1162 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0),
1163 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0),
1164 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0),
1165 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0)
1166 //                 );
1167             g_free(name);
1168         }
1169         break;
1170         default:
1171             ;// nothing
1172     }
1175     if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) {
1176         switch (source) {
1177             case GDK_SOURCE_MOUSE:
1178             {
1179                 testThumb.set(corePix);
1180             }
1181             break;
1182             case GDK_SOURCE_CURSOR:
1183             {
1184 //                 g_message("flip to cursor");
1185                 testThumb.set(mousePix);
1186             }
1187             break;
1188             case GDK_SOURCE_PEN:
1189             {
1190                 if (devName == "pad") {
1191 //                     g_message("flip to pad");
1192                     testThumb.set(sidebuttonsPix);
1193                 } else {
1194 //                     g_message("flip to pen");
1195                     testThumb.set(tipPix);
1196                 }
1197             }
1198             break;
1199             case GDK_SOURCE_ERASER:
1200             {
1201 //                 g_message("flip to eraser");
1202                 testThumb.set(eraserPix);
1203             }
1204             break;
1205 //             default:
1206 //                 g_message("gurgle");
1207         }
1208         updateTestButtons(key, hotButton);
1209         lastSourceSeen = source;
1210         lastDevnameSeen = devName;
1211     }
1213     return false;
1217 } // end namespace Inkscape
1218 } // end namespace UI
1219 } // end namespace Dialog
1222 /*
1223   Local Variables:
1224   mode:c++
1225   c-file-style:"stroustrup"
1226   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1227   indent-tabs-mode:nil
1228   fill-column:99
1229   End:
1230 */
1231 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :