Code

Cleanup of device internal handling and API.
[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/comboboxtext.h>
16 #include <gtkmm/enums.h>
17 #include <gtkmm/eventbox.h>
18 #include <gtkmm/frame.h>
19 #include <gtkmm/image.h>
20 #include <gtkmm/menubar.h>
21 #include <gtkmm/notebook.h>
22 #include <gtkmm/paned.h>
23 #include <gtkmm/progressbar.h>
24 #include <gtkmm/scrolledwindow.h>
25 #include <gtkmm/table.h>
26 #include <gtkmm/treemodel.h>
27 #include <gtkmm/treemodelcolumn.h>
28 #include <gtkmm/treestore.h>
29 #include <gtkmm/treeview.h>
31 #include "ui/widget/panel.h"
32 #include "device-manager.h"
34 #include "input.h"
36 /* XPM */
37 static char const * core_xpm[] = {
38 "16 16 4 1",
39 "       c None",
40 ".      c #808080",
41 "+      c #000000",
42 "@      c #FFFFFF",
43 "                ",
44 "                ",
45 "                ",
46 "    .++++++.    ",
47 "    +@+@@+@+    ",
48 "    +@+@@+@+    ",
49 "    +.+..+.+    ",
50 "    +@@@@@@+    ",
51 "    +@@@@@@+    ",
52 "    +@@@@@@+    ",
53 "    +@@@@@@+    ",
54 "    +@@@@@@+    ",
55 "    .++++++.    ",
56 "                ",
57 "                ",
58 "                "};
60 /* XPM */
61 static char const *eraser[] = {
62 /* columns rows colors chars-per-pixel */
63 "16 16 5 1",
64 "  c black",
65 ". c green",
66 "X c #808080",
67 "o c gray100",
68 "O c None",
69 /* pixels */
70 "OOOOOOOOOOOOOOOO",
71 "OOOOOOOOOOOOO OO",
72 "OOOOOOOOOOOO . O",
73 "OOOOOOOOOOO . OO",
74 "OOOOOOOOOO . OOO",
75 "OOOOOOOOO . OOOO",
76 "OOOOOOOO . OOOOO",
77 "OOOOOOOXo OOOOOO",
78 "OOOOOOXoXOOOOOOO",
79 "OOOOOXoXOOOOOOOO",
80 "OOOOXoXOOOOOOOOO",
81 "OOOXoXOOOOOOOOOO",
82 "OOXoXOOOOOOOOOOO",
83 "OOXXOOOOOOOOOOOO",
84 "OOOOOOOOOOOOOOOO",
85 "OOOOOOOOOOOOOOOO"
86 };
88 /* XPM */
89 static char const *mouse[] = {
90 /* columns rows colors chars-per-pixel */
91 "16 16 3 1",
92 "  c black",
93 ". c gray100",
94 "X c None",
95 /* pixels */
96 "XXXXXXXXXXXXXXXX",
97 "XXXXXXXXXXXXXXXX",
98 "XXXXXXXXXXXXXXXX",
99 "XXXXXXXXXXXXXXXX",
100 "XXXXXXX  XXXXXXX",
101 "XXXXX  . XXXXXXX",
102 "XXXX .... XXXXXX",
103 "XXXX .... XXXXXX",
104 "XXXXX .... XXXXX",
105 "XXXXX .... XXXXX",
106 "XXXXXX .... XXXX",
107 "XXXXXX .... XXXX",
108 "XXXXXXX .  XXXXX",
109 "XXXXXXX  XXXXXXX",
110 "XXXXXXXXXXXXXXXX",
111 "XXXXXXXXXXXXXXXX"
112 };
114 /* XPM */
115 static char const *pen[] = {
116 /* columns rows colors chars-per-pixel */
117 "16 16 3 1",
118 "  c black",
119 ". c gray100",
120 "X c None",
121 /* pixels */
122 "XXXXXXXXXXXXXXXX",
123 "XXXXXXXXXXXXX XX",
124 "XXXXXXXXXXXX . X",
125 "XXXXXXXXXXX . XX",
126 "XXXXXXXXXX . XXX",
127 "XXXXXXXXX . XXXX",
128 "XXXXXXXX . XXXXX",
129 "XXXXXXX . XXXXXX",
130 "XXXXXX . XXXXXXX",
131 "XXXXX . XXXXXXXX",
132 "XXXX . XXXXXXXXX",
133 "XXX . XXXXXXXXXX",
134 "XX . XXXXXXXXXXX",
135 "XX  XXXXXXXXXXXX",
136 "XXXXXXXXXXXXXXXX",
137 "XXXXXXXXXXXXXXXX"
138 };
140 /* XPM */
141 static char const *sidebuttons[] = {
142 /* columns rows colors chars-per-pixel */
143 "16 16 4 1",
144 "  c black",
145 ". c #808080",
146 "o c green",
147 "O c None",
148 /* pixels */
149 "OOOOOOOOOOOOOOOO",
150 "OOOOOOOOOOOOOOOO",
151 "O..............O",
152 "O.OOOOOOOOOOOO.O",
153 "O   OOOOOOOO   O",
154 "O o OOOOOOOO o O",
155 "O o OOOOOOOO o O",
156 "O   OOOOOOOO   O",
157 "O.OOOOOOOOOOOO.O",
158 "O.OOOOOOOOOOOO.O",
159 "O.OOOOOOOOOOOO.O",
160 "O.OOOOOOOOOOOO.O",
161 "O.OOOOOOOOOOOO.O",
162 "O..............O",
163 "OOOOOOOOOOOOOOOO",
164 "OOOOOOOOOOOOOOOO"
165 };
167 /* XPM */
168 static char const *tablet[] = {
169 /* columns rows colors chars-per-pixel */
170 "16 16 3 1",
171 "  c black",
172 ". c gray100",
173 "X c None",
174 /* pixels */
175 "XXXXXXXXXXXXXXXX",
176 "XXXXXXXXXXXXXXXX",
177 "X              X",
178 "X ............ X",
179 "X ............ X",
180 "X ............ X",
181 "X ............ X",
182 "X ............ X",
183 "X ............ X",
184 "X ............ X",
185 "X ............ X",
186 "X ............ X",
187 "X ............ X",
188 "X              X",
189 "XXXXXXXXXXXXXXXX",
190 "XXXXXXXXXXXXXXXX"
191 };
193 /* XPM */
194 static char const *tip[] = {
195 /* columns rows colors chars-per-pixel */
196 "16 16 5 1",
197 "  c black",
198 ". c green",
199 "X c #808080",
200 "o c gray100",
201 "O c None",
202 /* pixels */
203 "OOOOOOOOOOOOOOOO",
204 "OOOOOOOOOOOOOXOO",
205 "OOOOOOOOOOOOXoXO",
206 "OOOOOOOOOOOXoXOO",
207 "OOOOOOOOOOXoXOOO",
208 "OOOOOOOOOXoXOOOO",
209 "OOOOOOOOXoXOOOOO",
210 "OOOOOOO oXOOOOOO",
211 "OOOOOO . OOOOOOO",
212 "OOOOO . OOOOOOOO",
213 "OOOO . OOOOOOOOO",
214 "OOO . OOOOOOOOOO",
215 "OO . OOOOOOOOOOO",
216 "OO  OOOOOOOOOOOO",
217 "OOOOXXXXXOOOOOOO",
218 "OOOOOOOOOXXXXXOO"
219 };
221 /* XPM */
222 static char const *button_none[] = {
223 /* columns rows colors chars-per-pixel */
224 "8 8 3 1",
225 "  c black",
226 ". c #808080",
227 "X c None",
228 /* pixels */
229 "XXXXXXXX",
230 "XX .. XX",
231 "X .XX. X",
232 "X.XX X.X",
233 "X.X XX.X",
234 "X .XX. X",
235 "XX .. XX",
236 "XXXXXXXX"
237 };
238 /* XPM */
239 static char const *button_off[] = {
240 /* columns rows colors chars-per-pixel */
241 "8 8 4 1",
242 "  c black",
243 ". c #808080",
244 "X c gray100",
245 "o c None",
246 /* pixels */
247 "oooooooo",
248 "oo.  .oo",
249 "o. XX .o",
250 "o XXXX o",
251 "o XXXX o",
252 "o. XX .o",
253 "oo.  .oo",
254 "oooooooo"
255 };
256 /* XPM */
257 static char const *button_on[] = {
258 /* columns rows colors chars-per-pixel */
259 "8 8 3 1",
260 "  c black",
261 ". c green",
262 "X c None",
263 /* pixels */
264 "XXXXXXXX",
265 "XX    XX",
266 "X  ..  X",
267 "X .... X",
268 "X .... X",
269 "X  ..  X",
270 "XX    XX",
271 "XXXXXXXX"
272 };
274 /* XPM */
275 static char const * axis_none_xpm[] = {
276 "24 8 3 1",
277 "       c None",
278 ".      c #000000",
279 "+      c #808080",
280 "                        ",
281 "  .++++++++++++++++++.  ",
282 " .+               . .+. ",
283 " +          . . .     + ",
284 " +     . . .          + ",
285 " .+. .               +. ",
286 "  .++++++++++++++++++.  ",
287 "                        "};
288 /* XPM */
289 static char const * axis_off_xpm[] = {
290 "24 8 4 1",
291 "       c None",
292 ".      c #808080",
293 "+      c #000000",
294 "@      c #FFFFFF",
295 "                        ",
296 "  .++++++++++++++++++.  ",
297 " .+@@@@@@@@@@@@@@@@@@+. ",
298 " +@@@@@@@@@@@@@@@@@@@@+ ",
299 " +@@@@@@@@@@@@@@@@@@@@+ ",
300 " .+@@@@@@@@@@@@@@@@@@+. ",
301 "  .++++++++++++++++++.  ",
302 "                        "};
303 /* XPM */
304 static char const * axis_on_xpm[] = {
305 "24 8 3 1",
306 "       c None",
307 ".      c #000000",
308 "+      c #00FF00",
309 "                        ",
310 "  ....................  ",
311 " ..++++++++++++++++++.. ",
312 " .++++++++++++++++++++. ",
313 " .++++++++++++++++++++. ",
314 " ..++++++++++++++++++.. ",
315 "  ....................  ",
316 "                        "};
318 using Inkscape::InputDevice;
320 namespace Inkscape {
321 namespace UI {
322 namespace Dialog {
326 class MyModelColumns : public Gtk::TreeModel::ColumnRecord
328 public:
329     Gtk::TreeModelColumn<Glib::ustring>                filename;
330     Gtk::TreeModelColumn<Glib::ustring>                description;
331     Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> >  thumbnail;
332     Gtk::TreeModelColumn<Glib::RefPtr<InputDevice const> > device;
334     MyModelColumns() { add(filename); add(description); add(thumbnail); add(device); }
335 };
337 class InputDialogImpl : public InputDialog {
338 public:
339     InputDialogImpl();
340     virtual ~InputDialogImpl() {}
342 private:
343     Glib::RefPtr<Gdk::Pixbuf> corePix;
344     Glib::RefPtr<Gdk::Pixbuf> penPix;
345     Glib::RefPtr<Gdk::Pixbuf> mousePix;
346     Glib::RefPtr<Gdk::Pixbuf> tipPix;
347     Glib::RefPtr<Gdk::Pixbuf> tabletPix;
348     Glib::RefPtr<Gdk::Pixbuf> eraserPix;
349     Glib::RefPtr<Gdk::Pixbuf> sidebuttonsPix;
351     Glib::RefPtr<Gdk::Pixbuf> buttonsNonePix;
352     Glib::RefPtr<Gdk::Pixbuf> buttonsOnPix;
353     Glib::RefPtr<Gdk::Pixbuf> buttonsOffPix;
355     Glib::RefPtr<Gdk::Pixbuf> axisNonePix;
356     Glib::RefPtr<Gdk::Pixbuf> axisOnPix;
357     Glib::RefPtr<Gdk::Pixbuf> axisOffPix;
359     std::map<Glib::ustring, std::set<guint> > buttonMap;
360     std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
362     GdkInputSource lastSourceSeen;
363     Glib::ustring lastDevnameSeen;
365     MyModelColumns cols;
366     Glib::RefPtr<Gtk::TreeStore> store;
367     Gtk::TreeIter tabletIter;
368     Gtk::TreeView tree;
369     Gtk::Frame frame2;
370     Gtk::Frame testFrame;
371     Gtk::ScrolledWindow treeScroller;
372     Gtk::ScrolledWindow detailScroller;
373     Gtk::HPaned splitter;
374     Gtk::VPaned split2;
375     Gtk::Label devName;
376     Gtk::Label devKeyCount;
377     Gtk::Label devAxesCount;
378     Gtk::ComboBoxText axesCombo;
379     Gtk::ProgressBar axesValues[6];
380     Gtk::ComboBoxText buttonCombo;
381     Gtk::ComboBoxText linkCombo;
382     sigc::connection linkConnection;
383     Gtk::Label keyVal;
384     Gtk::Entry keyEntry;
385     Gtk::Table devDetails;
386     Gtk::HPaned confSplitter;
387     Gtk::Notebook topHolder;
388     Gtk::Image testThumb;
389     Gtk::Image testButtons[24];
390     Gtk::Image testAxes[8];
391     Gtk::Table imageTable;
392     Gtk::EventBox testDetector;
394     void setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo );
395     void updateTestButtons( Glib::ustring const& key, gint hotButton );
396     void updateTestAxes( Glib::ustring const& key, GdkDevice* dev );
397     void mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev);
398     Glib::ustring getKeyFor( GdkDevice* device );
399     bool eventSnoop(GdkEvent* event);
400     void linkComboChanged();
401     void resyncToSelection();
402     void handleDeviceChange(const Glib::RefPtr<InputDevice>& device);
403     void updateDeviceAxes(const Glib::RefPtr<InputDevice>& device);
404     void updateDeviceButtons(const Glib::RefPtr<InputDevice>& device);
405     void updateDeviceLinks(const Glib::RefPtr<InputDevice>& device);
407     bool findDevice(const Gtk::TreeModel::iterator& iter,
408                     Glib::ustring id,
409                     Gtk::TreeModel::iterator* result);
410     bool findDeviceByLink(const Gtk::TreeModel::iterator& iter,
411                           Glib::ustring link,
412                           Gtk::TreeModel::iterator* result);
413 };
416 // Now that we've defined the *Impl class, we can do the method to aquire one.
417 InputDialog &InputDialog::getInstance()
419     InputDialog *dialog = new InputDialogImpl();
420     return *dialog;
424 InputDialogImpl::InputDialogImpl() :
425     InputDialog(),
427     corePix(Gdk::Pixbuf::create_from_xpm_data(core_xpm)),
428     penPix(Gdk::Pixbuf::create_from_xpm_data(pen)),
429     mousePix(Gdk::Pixbuf::create_from_xpm_data(mouse)),
430     tipPix(Gdk::Pixbuf::create_from_xpm_data(tip)),
431     tabletPix(Gdk::Pixbuf::create_from_xpm_data(tablet)),
432     eraserPix(Gdk::Pixbuf::create_from_xpm_data(eraser)),
433     sidebuttonsPix(Gdk::Pixbuf::create_from_xpm_data(sidebuttons)),
435     buttonsNonePix(Gdk::Pixbuf::create_from_xpm_data(button_none)),
436     buttonsOnPix(Gdk::Pixbuf::create_from_xpm_data(button_on)),
437     buttonsOffPix(Gdk::Pixbuf::create_from_xpm_data(button_off)),
439     axisNonePix(Gdk::Pixbuf::create_from_xpm_data(axis_none_xpm)),
440     axisOnPix(Gdk::Pixbuf::create_from_xpm_data(axis_on_xpm)),
441     axisOffPix(Gdk::Pixbuf::create_from_xpm_data(axis_off_xpm)),
443     lastSourceSeen((GdkInputSource)-1),
444     lastDevnameSeen(""),
445     cols(),
446     store(Gtk::TreeStore::create(cols)),
447     tree(store),
448     frame2(),
449     testFrame("Test Area"),
450     treeScroller(),
451     detailScroller(),
452     splitter(),
453     split2(),
454     linkCombo(),
455     devDetails(12, 2),
456     confSplitter(),
457     topHolder(),
458     imageTable(8, 7)
460     Gtk::Box *contents = _getContents();
463     treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
464     treeScroller.add(tree);
465     split2.pack1(testFrame);
466     split2.pack2(frame2);
467     splitter.pack1(treeScroller);
468     splitter.pack2(split2);
470     testDetector.add(imageTable);
471     testFrame.add(testDetector);
472     testThumb.set(tabletPix);
473     testThumb.set_padding(24, 24);
474     imageTable.attach(testThumb, 0, 8, 0, 1, ::Gtk::EXPAND, ::Gtk::EXPAND);
475     {
476         guint col = 0;
477         guint row = 1;
478         for ( guint num = 0; num < G_N_ELEMENTS(testButtons); num++ ) {
479             testButtons[num].set(buttonsNonePix);
480             imageTable.attach(testButtons[num], col, col + 1, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
481             col++;
482             if (col > 7) {
483                 col = 0;
484                 row++;
485             }
486         }
488         col = 0;
489         for ( guint num = 0; num < G_N_ELEMENTS(testAxes); num++ ) {
490             testAxes[num].set(axisNonePix);
491             imageTable.attach(testAxes[num], col * 2, (col + 1) * 2, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
492             col++;
493             if (col > 3) {
494                 col = 0;
495                 row++;
496             }
497         }
498     }
501     topHolder.append_page(confSplitter, "Configuration");
502     topHolder.append_page(splitter, "Hardware");
503 //     confSplitter.show_all();
504 //     splitter.show_all();
505     topHolder.show_all();
506     topHolder.set_current_page(1);
508     contents->pack_start(topHolder);
510     int rowNum = 0;
512     Gtk::Label* lbl = Gtk::manage(new Gtk::Label("Name:"));
513     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
514                       ::Gtk::FILL,
515                       ::Gtk::SHRINK);
516     devDetails.attach(devName, 1, 2, rowNum, rowNum + 1,
517                       ::Gtk::SHRINK,
518                       ::Gtk::SHRINK);
520     rowNum++;
522     lbl = Gtk::manage(new Gtk::Label("Link:"));
523     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
524                       ::Gtk::FILL,
525                       ::Gtk::SHRINK);
527     linkCombo.append_text("None");
528     linkCombo.set_active_text("None");
529     linkCombo.set_sensitive(false);
530     linkConnection = linkCombo.signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::linkComboChanged));
532     devDetails.attach(linkCombo, 1, 2, rowNum, rowNum + 1,
533                       ::Gtk::FILL,
534                       ::Gtk::SHRINK);
535     rowNum++;
537     lbl = Gtk::manage(new Gtk::Label("Axes count:"));
538     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
539                       ::Gtk::FILL,
540                       ::Gtk::SHRINK);
541     devDetails.attach(devAxesCount, 1, 2, rowNum, rowNum + 1,
542                       ::Gtk::SHRINK,
543                       ::Gtk::SHRINK);
545     rowNum++;
547 /*
548     lbl = Gtk::manage(new Gtk::Label("Actual axes count:"));
549     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
550                       ::Gtk::FILL,
551                       ::Gtk::SHRINK);
552     devDetails.attach(axesCombo, 1, 2, rowNum, rowNum + 1,
553                       ::Gtk::SHRINK,
554                       ::Gtk::SHRINK);
556     rowNum++;
557 */
559     for ( guint barNum = 0; barNum < static_cast<guint>(G_N_ELEMENTS(axesValues)); barNum++ ) {
560         lbl = Gtk::manage(new Gtk::Label("axis:"));
561         devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
562                           ::Gtk::FILL,
563                           ::Gtk::SHRINK);
564         devDetails.attach(axesValues[barNum], 1, 2, rowNum, rowNum + 1,
565                           ::Gtk::EXPAND,
566                           ::Gtk::SHRINK);
567         axesValues[barNum].set_sensitive(false);
569         rowNum++;
570     }
572     lbl = Gtk::manage(new Gtk::Label("Button count:"));
573     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
574                       ::Gtk::FILL,
575                       ::Gtk::SHRINK);
576     devDetails.attach(devKeyCount, 1, 2, rowNum, rowNum + 1,
577                       ::Gtk::SHRINK,
578                       ::Gtk::SHRINK);
580     rowNum++;
582 /*
583     lbl = Gtk::manage(new Gtk::Label("Actual button count:"));
584     devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
585                       ::Gtk::FILL,
586                       ::Gtk::SHRINK);
587     devDetails.attach(buttonCombo, 1, 2, rowNum, rowNum + 1,
588                       ::Gtk::SHRINK,
589                       ::Gtk::SHRINK);
591     rowNum++;
592 */
594     devDetails.attach(keyVal, 0, 2, rowNum, rowNum + 1,
595                       ::Gtk::FILL,
596                       ::Gtk::SHRINK);
597     rowNum++;
600     testDetector.signal_event().connect(sigc::mem_fun(*this, &InputDialogImpl::eventSnoop));
602 //     void gdk_input_set_extension_events (GdkWindow        *window,
603 //                                          gint              mask,
604 //                                          GdkExtensionMode  mode);
606     gtk_widget_set_extension_events( GTK_WIDGET(testDetector.gobj()), GDK_EXTENSION_EVENTS_ALL );
607     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);
609     devDetails.attach(keyEntry, 0, 2, rowNum, rowNum + 1,
610                       ::Gtk::FILL,
611                       ::Gtk::SHRINK);
612     rowNum++;
615     devDetails.set_sensitive(false);
616     detailScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
617     detailScroller.add(devDetails);
618     frame2.add(detailScroller);
620 //- 16x16/devices
621 // gnome-dev-mouse-optical
622 // input-mouse
623 // input-tablet
624 // mouse
628     Gtk::TreeModel::Row row;
629     Gtk::TreeModel::Row childrow;
630     Gtk::TreeModel::Row deviceRow;
633     //Add the TreeView's view columns:
634     tree.append_column("I", cols.thumbnail);
635     tree.append_column("Bar", cols.description);
637     tree.set_enable_tree_lines();
638     tree.set_headers_visible(false);
639     tree.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::resyncToSelection));
643     std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
644     if ( !devList.empty() ) {
645         row = *(store->append());
646         row[cols.description] = "Hardware";
648         tabletIter = store->append(row.children());
649         childrow = *tabletIter;
650         childrow[cols.description] = "Tablet";
651         childrow[cols.thumbnail] = tabletPix;
653         for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it = devList.begin(); it != devList.end(); ++it ) {
654             Glib::RefPtr<InputDevice const> dev = *it;
655             if ( dev ) {
656 //                 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(),
657 //                           dev->hasCursor() ? "Yes":"no", dev->getNumAxes(), dev->getNumKeys());
659 //                 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
660                 if ( dev ) {
661                     deviceRow = *(store->append(childrow.children()));
662                     deviceRow[cols.description] = dev->getName();
663                     deviceRow[cols.device] = dev;
664                     switch ( dev->getSource() ) {
665                         case GDK_SOURCE_MOUSE:
666                             deviceRow[cols.thumbnail] = corePix;
667                             break;
668                         case GDK_SOURCE_PEN:
669                             if (deviceRow[cols.description] == "pad") {
670                                 deviceRow[cols.thumbnail] = sidebuttonsPix;
671                             } else {
672                                 deviceRow[cols.thumbnail] = tipPix;
673                             }
674                             break;
675                         case GDK_SOURCE_CURSOR:
676                             deviceRow[cols.thumbnail] = mousePix;
677                             break;
678                         case GDK_SOURCE_ERASER:
679                             deviceRow[cols.thumbnail] = eraserPix;
680                             break;
681                         default:
682                             ; // nothing
683                     }
684                 }
685             } else {
686                 g_warning("Null device in list");
687             }
688         }
689     } else {
690         g_warning("No devices found");
691     }
692     Inkscape::DeviceManager::getManager().signalDeviceChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::handleDeviceChange));
693     Inkscape::DeviceManager::getManager().signalAxesChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceAxes));
694     Inkscape::DeviceManager::getManager().signalButtonsChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceButtons));
695     Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceLinks));
697     tree.expand_all();
698     show_all_children();
701 void InputDialogImpl::handleDeviceChange(const Glib::RefPtr<InputDevice>& /*device*/)
703 //     g_message("OUCH!!!! for %p  hits %s", &device, device->getId().c_str());
706 void InputDialogImpl::updateDeviceAxes(const Glib::RefPtr<InputDevice>& device)
708     gint live = device->getLiveAxes();
710     std::map<guint, std::pair<guint, gdouble> > existing = axesMap[device->getId()];
711     gint mask = 0x1;
712     for ( gint num = 0; num < 32; num++, mask <<= 1) {
713         if ( (mask & live) != 0 ) {
714             if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) {
715                 axesMap[device->getId()][num].first = 2;
716                 axesMap[device->getId()][num].second = 0.0;
717             }
718         }
719     }
720     updateTestAxes( device->getId(), 0 );
723 void InputDialogImpl::updateDeviceButtons(const Glib::RefPtr<InputDevice>& device)
725     gint live = device->getLiveButtons();
726     std::set<guint> existing = buttonMap[device->getId()];
727     gint mask = 0x1;
728     for ( gint num = 0; num < 32; num++, mask <<= 1) {
729         if ( (mask & live) != 0 ) {
730             if ( existing.find(num) == existing.end() ) {
731                 buttonMap[device->getId()].insert(num);
732             }
733         }
734     }
735     updateTestButtons(device->getId(), -1);
739 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
740                                  Glib::ustring id,
741                                  Gtk::TreeModel::iterator* result)
743     bool stop = false;
744     Glib::RefPtr<InputDevice const> dev = (*iter)[cols.device];
745     if ( dev && (dev->getId() == id) ) {
746         if ( result ) {
747             *result = iter;
748         }
749         stop = true;
750     }
751     return stop;
754 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
755                                        Glib::ustring link,
756                                        Gtk::TreeModel::iterator* result)
758     bool stop = false;
759     Glib::RefPtr<InputDevice const> dev = (*iter)[cols.device];
760     if ( dev && (dev->getLink() == link) ) {
761         if ( result ) {
762             *result = iter;
763         }
764         stop = true;
765     }
766     return stop;
769 void InputDialogImpl::updateDeviceLinks(const Glib::RefPtr<InputDevice>& device)
771 //     g_message("Links!!!! for %p  hits [%s]  with link of [%s]", &device, device->getId().c_str(), device->getLink().c_str());
772     Gtk::TreeModel::iterator deviceIter;
773     store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
774                              sigc::mem_fun(*this, &InputDialogImpl::findDevice),
775                              device->getId(),
776                              &deviceIter) );
778     if ( deviceIter ) {
779         // Found the device concerned. Can proceed.
781         if ( device->getLink().empty() ) {
782             // is now unlinked
783 //             g_message("Item %s is unlinked", device->getId().c_str());
784             if ( deviceIter->parent() != tabletIter ) {
785                 // Not the child of the tablet. move on up
787                 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[cols.device];
788                 Glib::ustring descr = (*deviceIter)[cols.description];
789                 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[cols.thumbnail];
791                 Gtk::TreeModel::Row deviceRow = *store->append(tabletIter->children());
792                 deviceRow[cols.description] = descr;
793                 deviceRow[cols.thumbnail] = thumb;
794                 deviceRow[cols.device] = dev;
796                 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
797                 store->erase(deviceIter);
798                 if ( oldParent->children().empty() ) {
799                     store->erase(oldParent);
800                 }
801             }
802         } else {
803             // is linking
804             if ( deviceIter->parent() == tabletIter ) {
805                 // Simple case. Not already linked
807                 Gtk::TreeIter newGroup = store->append(tabletIter->children());
808                 (*newGroup)[cols.description] = "Pen";
809                 (*newGroup)[cols.thumbnail] = penPix;
811                 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[cols.device];
812                 Glib::ustring descr = (*deviceIter)[cols.description];
813                 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[cols.thumbnail];
815                 Gtk::TreeModel::Row deviceRow = *store->append(newGroup->children());
816                 deviceRow[cols.description] = descr;
817                 deviceRow[cols.thumbnail] = thumb;
818                 deviceRow[cols.device] = dev;
821                 Gtk::TreeModel::iterator linkIter;
822                 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
823                                          sigc::mem_fun(*this, &InputDialogImpl::findDeviceByLink),
824                                          device->getId(),
825                                          &linkIter) );
826                 if ( linkIter ) {
827                     dev = (*linkIter)[cols.device];
828                     descr = (*linkIter)[cols.description];
829                     thumb = (*linkIter)[cols.thumbnail];
831                     deviceRow = *store->append(newGroup->children());
832                     deviceRow[cols.description] = descr;
833                     deviceRow[cols.thumbnail] = thumb;
834                     deviceRow[cols.device] = dev;
835                     Gtk::TreeModel::iterator oldParent = linkIter->parent();
836                     store->erase(linkIter);
837                     if ( oldParent->children().empty() ) {
838                         store->erase(oldParent);
839                     }
840                 }
842                 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
843                 store->erase(deviceIter);
844                 if ( oldParent->children().empty() ) {
845                     store->erase(oldParent);
846                 }
847                 tree.expand_row(Gtk::TreePath(newGroup), true);
848             }
849         }
850     }
853 void InputDialogImpl::linkComboChanged() {
854     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
855     Gtk::TreeModel::iterator iter = treeSel->get_selected();
856     if (iter) {
857         Gtk::TreeModel::Row row = *iter;
858         Glib::ustring val = row[cols.description];
859         Glib::RefPtr<InputDevice const> dev = row[cols.device];
860         if ( dev ) {
861             if ( linkCombo.get_active_row_number() == 0 ) {
862                 // It is the "None" entry
863                 DeviceManager::getManager().setLinkedTo(dev->getId(), "");
864             } else {
865                 Glib::ustring linkName = linkCombo.get_active_text();
866                 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
867                 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
868                     if ( linkName == (*it)->getName() ) {
869                         DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId());
870                         break;
871                     }
872                 }
873             }
874         }
875     }
878 void InputDialogImpl::resyncToSelection() {
879     bool clear = true;
880     Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
881     Gtk::TreeModel::iterator iter = treeSel->get_selected();
882     if (iter) {
883         Gtk::TreeModel::Row row = *iter;
884         Glib::ustring val = row[cols.description];
885         Glib::RefPtr<InputDevice const> dev = row[cols.device];
886         if ( dev ) {
887             devDetails.set_sensitive(true);
889             linkConnection.block();
890             linkCombo.clear_items();
891             linkCombo.append_text("None");
892             linkCombo.set_active(0);
893             if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
894                 Glib::ustring linked = dev->getLink();
895                 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
896                 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
897                     if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) {
898                         linkCombo.append_text((*it)->getName().c_str());
899                         if ( (linked.length() > 0) && (linked == (*it)->getId()) ) {
900                             linkCombo.set_active_text((*it)->getName().c_str());
901                         }
902                     }
903                 }
904                 linkCombo.set_sensitive(true);
905             } else {
906                 linkCombo.set_sensitive(false);
907             }
908             linkConnection.unblock();
910             clear = false;
911             devName.set_label(row[cols.description]);
912             setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo);
913             setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo);
914         }
915     }
917     devDetails.set_sensitive(!clear);
918     if (clear) {
919         devName.set_label("");
920         devAxesCount.set_label("");
921         devKeyCount.set_label("");
922     }
925 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
927     gchar *tmp = g_strdup_printf("%d", reported);
928     label.set_label(tmp);
929     g_free(tmp);
931     combo.clear_items();
932     for ( gint i = 1; i <= reported; ++i ) {
933         tmp = g_strdup_printf("%d", i);
934         combo.append_text(tmp);
935         g_free(tmp);
936     }
938     if ( (1 <= actual) && (actual <= reported) ) {
939         combo.set_active(actual - 1);
940     }
943 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
945     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testButtons)); i++ ) {
946         if ( buttonMap[key].find(i) != buttonMap[key].end() ) {
947             if ( i == hotButton ) {
948                 testButtons[i].set(buttonsOnPix);
949             } else {
950                 testButtons[i].set(buttonsOffPix);
951             }
952         } else {
953             testButtons[i].set(buttonsNonePix);
954         }
955     }
958 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
960     static gdouble epsilon = 0.0001;
961     {
962         Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
963         Gtk::TreeModel::iterator iter = treeSel->get_selected();
964         if (iter) {
965             Gtk::TreeModel::Row row = *iter;
966             Glib::ustring val = row[cols.description];
967             Glib::RefPtr<InputDevice const> idev = row[cols.device];
968             if ( !idev || (idev->getId() != key) ) {
969                 dev = 0;
970             }
971         }
972     }
975     for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testAxes)); i++ ) {
976         if ( axesMap[key].find(i) != axesMap[key].end() ) {
977             switch ( axesMap[key][i].first ) {
978                 case 0:
979                 case 1:
980                     testAxes[i].set(axisNonePix);
981                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
982                         axesValues[i].set_sensitive(false);
983                     }
984                     break;
985                 case 2:
986                     testAxes[i].set(axisOffPix);
987                     axesValues[i].set_sensitive(true);
988                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
989                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
990                             axesValues[i].set_sensitive(true);
991                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
992                         }
993                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
994                         axesValues[i].set_text(str);
995                         g_free(str);
996                     }
997                     break;
998                 case 3:
999                     testAxes[i].set(axisOnPix);
1000                     axesValues[i].set_sensitive(true);
1001                     if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1002                         if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1003                             axesValues[i].set_sensitive(true);
1004                             axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1005                         }
1006                         gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1007                         axesValues[i].set_text(str);
1008                         g_free(str);
1009                     }
1010             }
1012         } else {
1013             testAxes[i].set(axisNonePix);
1014         }
1015     }
1016     if ( !dev ) {
1017         for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(axesValues)); i++ ) {
1018             axesValues[i].set_fraction(0.0);
1019             axesValues[i].set_text("");
1020             axesValues[i].set_sensitive(false);
1021         }
1022     }
1025 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1027     static gdouble epsilon = 0.0001;
1028     if ( (numAxes > 0) && axes) {
1029         for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) {
1030             // 0 == new, 1 == set value, 2 == changed value, 3 == active
1031             gdouble diff = axesMap[key][axisNum].second - axes[axisNum];
1032             switch(axesMap[key][axisNum].first) {
1033                 case 0:
1034                 {
1035                     axesMap[key][axisNum].first = 1;
1036                     axesMap[key][axisNum].second = axes[axisNum];
1037                 }
1038                 break;
1039                 case 1:
1040                 {
1041                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1042 //                         g_message("Axis %d changed on %s]", axisNum, key.c_str());
1043                         axesMap[key][axisNum].first = 3;
1044                         axesMap[key][axisNum].second = axes[axisNum];
1045                         updateTestAxes(key, dev);
1046                         DeviceManager::getManager().addAxis(key, axisNum);
1047                     }
1048                 }
1049                 break;
1050                 case 2:
1051                 {
1052                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1053                         axesMap[key][axisNum].first = 3;
1054                         axesMap[key][axisNum].second = axes[axisNum];
1055                         updateTestAxes(key, dev);
1056                     }
1057                 }
1058                 break;
1059                 case 3:
1060                 {
1061                     if ( (diff > epsilon) || (diff < -epsilon) ) {
1062                         axesMap[key][axisNum].second = axes[axisNum];
1063                     } else {
1064                         axesMap[key][axisNum].first = 2;
1065                         updateTestAxes(key, dev);
1066                     }
1067                 }
1068             }
1069         }
1070     }
1071     // std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
1074 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1076     Glib::ustring key;
1077     switch ( device->source ) {
1078         case GDK_SOURCE_MOUSE:
1079             key = "M:";
1080             break;
1081         case GDK_SOURCE_CURSOR:
1082             key = "C:";
1083             break;
1084         case GDK_SOURCE_PEN:
1085             key = "P:";
1086             break;
1087         case GDK_SOURCE_ERASER:
1088             key = "E:";
1089             break;
1090         default:
1091             key = "?:";
1092     }
1093     key += device->name;
1095     return key;
1098 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1100     int modmod = 0;
1102     GdkInputSource source = lastSourceSeen;
1103     Glib::ustring devName = lastDevnameSeen;
1104     Glib::ustring key;
1105     gint hotButton = -1;
1107     switch ( event->type ) {
1108         case GDK_KEY_PRESS:
1109         case GDK_KEY_RELEASE:
1110         {
1111             GdkEventKey* keyEvt = reinterpret_cast<GdkEventKey*>(event);
1112             gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast<GdkModifierType>(keyEvt->state));
1113             keyVal.set_label(name);
1114 //             g_message("%d KEY    state:0x%08x  0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name);
1115             g_free(name);
1116         }
1117         break;
1118         case GDK_BUTTON_PRESS:
1119             modmod = 1;
1120             // fallthrough
1121         case GDK_BUTTON_RELEASE:
1122         {
1123             GdkEventButton* btnEvt = reinterpret_cast<GdkEventButton*>(event);
1124             if ( btnEvt->device ) {
1125                 key = getKeyFor(btnEvt->device);
1126                 source = btnEvt->device->source;
1127                 devName = btnEvt->device->name;
1129                 mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device);
1130                 if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) {
1131 //                     g_message("New button found for %s = %d", key.c_str(), btnEvt->button);
1132                     buttonMap[key].insert(btnEvt->button);
1133                     DeviceManager::getManager().addButton(key, btnEvt->button);
1134                 }
1135                 hotButton = modmod ? btnEvt->button : -1;
1136                 updateTestButtons(key, hotButton);
1137             }
1138             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnEvt->state));
1139             keyVal.set_label(name);
1140 //             g_message("%d BTN    state:0x%08x %c  %4d [%s] dev:%p [%s]  ",
1141 //                       btnEvt->type, btnEvt->state,
1142 //                       (modmod ? '+':'-'),
1143 //                       btnEvt->button, name, btnEvt->device,
1144 //                       (btnEvt->device ? btnEvt->device->name : "null")
1146 //                 );
1147             g_free(name);
1148         }
1149         break;
1150         case GDK_MOTION_NOTIFY:
1151         {
1152             GdkEventMotion* btnMtn = reinterpret_cast<GdkEventMotion*>(event);
1153             if ( btnMtn->device ) {
1154                 key = getKeyFor(btnMtn->device);
1155                 source = btnMtn->device->source;
1156                 devName = btnMtn->device->name;
1157                 mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device);
1158             }
1159             gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnMtn->state));
1160             keyVal.set_label(name);
1161 //             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,
1162 //                       name, btnMtn->device,
1163 //                       (btnMtn->device ? btnMtn->device->name : "null"),
1164 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0),
1165 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0),
1166 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0),
1167 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0),
1168 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0),
1169 //                       ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0)
1170 //                 );
1171             g_free(name);
1172         }
1173         break;
1174         default:
1175             ;// nothing
1176     }
1179     if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) {
1180         switch (source) {
1181             case GDK_SOURCE_MOUSE:
1182             {
1183                 testThumb.set(corePix);
1184             }
1185             break;
1186             case GDK_SOURCE_CURSOR:
1187             {
1188 //                 g_message("flip to cursor");
1189                 testThumb.set(mousePix);
1190             }
1191             break;
1192             case GDK_SOURCE_PEN:
1193             {
1194                 if (devName == "pad") {
1195 //                     g_message("flip to pad");
1196                     testThumb.set(sidebuttonsPix);
1197                 } else {
1198 //                     g_message("flip to pen");
1199                     testThumb.set(tipPix);
1200                 }
1201             }
1202             break;
1203             case GDK_SOURCE_ERASER:
1204             {
1205 //                 g_message("flip to eraser");
1206                 testThumb.set(eraserPix);
1207             }
1208             break;
1209 //             default:
1210 //                 g_message("gurgle");
1211         }
1212         updateTestButtons(key, hotButton);
1213         lastSourceSeen = source;
1214         lastDevnameSeen = devName;
1215     }
1217     return false;
1221 } // end namespace Inkscape
1222 } // end namespace UI
1223 } // end namespace Dialog
1226 /*
1227   Local Variables:
1228   mode:c++
1229   c-file-style:"stroustrup"
1230   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1231   indent-tabs-mode:nil
1232   fill-column:99
1233   End:
1234 */
1235 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :