3 /* XPM */
4 static char const * 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 const *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 const *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 const *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 const *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 const *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 const *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 const *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 const *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 const *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 const * 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 const * 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 const * 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
323 {
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()
414 {
415 InputDialog *dialog = new InputDialogImpl();
416 return *dialog;
417 }
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)
455 {
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();
695 }
697 void InputDialogImpl::handleDeviceChange(const Glib::RefPtr<InputDevice>& /*device*/)
698 {
699 // g_message("OUCH!!!! for %p hits %s", &device, device->getId().c_str());
700 }
702 void InputDialogImpl::updateDeviceAxes(const Glib::RefPtr<InputDevice>& device)
703 {
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 );
717 }
719 void InputDialogImpl::updateDeviceButtons(const Glib::RefPtr<InputDevice>& device)
720 {
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);
732 }
735 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
736 Glib::ustring id,
737 Gtk::TreeModel::iterator* result)
738 {
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;
748 }
750 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
751 Glib::ustring link,
752 Gtk::TreeModel::iterator* result)
753 {
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;
763 }
765 void InputDialogImpl::updateDeviceLinks(const Glib::RefPtr<InputDevice>& device)
766 {
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
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;
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 }
847 }
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 }
872 }
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 }
919 }
921 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
922 {
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 }
937 }
939 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
940 {
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 }
952 }
954 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
955 {
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 }
1019 }
1021 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1022 {
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;
1068 }
1070 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1071 {
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;
1092 }
1094 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1095 {
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;
1214 }
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 :