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
327 {
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<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()
418 {
419 InputDialog *dialog = new InputDialogImpl();
420 return *dialog;
421 }
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)
459 {
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<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<InputDevice const *>::iterator it = devList.begin(); it != devList.end(); ++it ) {
654 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();
699 }
701 void InputDialogImpl::handleDeviceChange(const Glib::RefPtr<InputDevice>& /*device*/)
702 {
703 // g_message("OUCH!!!! for %p hits %s", &device, device->getId().c_str());
704 }
706 void InputDialogImpl::updateDeviceAxes(const Glib::RefPtr<InputDevice>& device)
707 {
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 );
721 }
723 void InputDialogImpl::updateDeviceButtons(const Glib::RefPtr<InputDevice>& device)
724 {
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);
736 }
739 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
740 Glib::ustring id,
741 Gtk::TreeModel::iterator* result)
742 {
743 bool stop = false;
744 const InputDevice* dev = (*iter)[cols.device];
745 if ( dev && (dev->getId() == id) ) {
746 if ( result ) {
747 *result = iter;
748 }
749 stop = true;
750 }
751 return stop;
752 }
754 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
755 Glib::ustring link,
756 Gtk::TreeModel::iterator* result)
757 {
758 bool stop = false;
759 const InputDevice* dev = (*iter)[cols.device];
760 if ( dev && (dev->getLink() == link) ) {
761 if ( result ) {
762 *result = iter;
763 }
764 stop = true;
765 }
766 return stop;
767 }
769 void InputDialogImpl::updateDeviceLinks(const Glib::RefPtr<InputDevice>& device)
770 {
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 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 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 }
851 }
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 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<InputDevice const *> devList = Inkscape::DeviceManager::getManager().getDevices();
867 for ( std::list<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 }
876 }
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 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<InputDevice const *> devList = Inkscape::DeviceManager::getManager().getDevices();
896 for ( std::list<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 }
923 }
925 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
926 {
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 }
941 }
943 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
944 {
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 }
956 }
958 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
959 {
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 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 }
1023 }
1025 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1026 {
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;
1072 }
1074 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1075 {
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;
1096 }
1098 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1099 {
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;
1218 }
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 :