570f009447d01e8f02e5eb49a581ab086c3f6a61
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/alignment.h>
16 #include <gtkmm/cellrenderercombo.h>
17 #include <gtkmm/checkbutton.h>
18 #include <gtkmm/comboboxtext.h>
19 #include <gtkmm/enums.h>
20 #include <gtkmm/eventbox.h>
21 #include <gtkmm/frame.h>
22 #include <gtkmm/image.h>
23 #include <gtkmm/liststore.h>
24 #include <gtkmm/menubar.h>
25 #include <gtkmm/notebook.h>
26 #include <gtkmm/paned.h>
27 #include <gtkmm/progressbar.h>
28 #include <gtkmm/scrolledwindow.h>
29 #include <gtkmm/table.h>
30 #include <gtkmm/treemodel.h>
31 #include <gtkmm/treemodelcolumn.h>
32 #include <gtkmm/treestore.h>
33 #include <gtkmm/treeview.h>
35 #include "device-manager.h"
36 #include "preferences.h"
37 #include "ui/widget/panel.h"
39 #include "input.h"
41 /* XPM */
42 static char const * core_xpm[] = {
43 "16 16 4 1",
44 " c None",
45 ". c #808080",
46 "+ c #000000",
47 "@ c #FFFFFF",
48 " ",
49 " ",
50 " ",
51 " .++++++. ",
52 " +@+@@+@+ ",
53 " +@+@@+@+ ",
54 " +.+..+.+ ",
55 " +@@@@@@+ ",
56 " +@@@@@@+ ",
57 " +@@@@@@+ ",
58 " +@@@@@@+ ",
59 " +@@@@@@+ ",
60 " .++++++. ",
61 " ",
62 " ",
63 " "};
65 /* XPM */
66 static char const *eraser[] = {
67 /* columns rows colors chars-per-pixel */
68 "16 16 5 1",
69 " c black",
70 ". c green",
71 "X c #808080",
72 "o c gray100",
73 "O c None",
74 /* pixels */
75 "OOOOOOOOOOOOOOOO",
76 "OOOOOOOOOOOOO OO",
77 "OOOOOOOOOOOO . O",
78 "OOOOOOOOOOO . OO",
79 "OOOOOOOOOO . OOO",
80 "OOOOOOOOO . OOOO",
81 "OOOOOOOO . OOOOO",
82 "OOOOOOOXo OOOOOO",
83 "OOOOOOXoXOOOOOOO",
84 "OOOOOXoXOOOOOOOO",
85 "OOOOXoXOOOOOOOOO",
86 "OOOXoXOOOOOOOOOO",
87 "OOXoXOOOOOOOOOOO",
88 "OOXXOOOOOOOOOOOO",
89 "OOOOOOOOOOOOOOOO",
90 "OOOOOOOOOOOOOOOO"
91 };
93 /* XPM */
94 static char const *mouse[] = {
95 /* columns rows colors chars-per-pixel */
96 "16 16 3 1",
97 " c black",
98 ". c gray100",
99 "X c None",
100 /* pixels */
101 "XXXXXXXXXXXXXXXX",
102 "XXXXXXXXXXXXXXXX",
103 "XXXXXXXXXXXXXXXX",
104 "XXXXXXXXXXXXXXXX",
105 "XXXXXXX XXXXXXX",
106 "XXXXX . XXXXXXX",
107 "XXXX .... XXXXXX",
108 "XXXX .... XXXXXX",
109 "XXXXX .... XXXXX",
110 "XXXXX .... XXXXX",
111 "XXXXXX .... XXXX",
112 "XXXXXX .... XXXX",
113 "XXXXXXX . XXXXX",
114 "XXXXXXX XXXXXXX",
115 "XXXXXXXXXXXXXXXX",
116 "XXXXXXXXXXXXXXXX"
117 };
119 /* XPM */
120 static char const *pen[] = {
121 /* columns rows colors chars-per-pixel */
122 "16 16 3 1",
123 " c black",
124 ". c gray100",
125 "X c None",
126 /* pixels */
127 "XXXXXXXXXXXXXXXX",
128 "XXXXXXXXXXXXX XX",
129 "XXXXXXXXXXXX . X",
130 "XXXXXXXXXXX . XX",
131 "XXXXXXXXXX . XXX",
132 "XXXXXXXXX . XXXX",
133 "XXXXXXXX . XXXXX",
134 "XXXXXXX . XXXXXX",
135 "XXXXXX . XXXXXXX",
136 "XXXXX . XXXXXXXX",
137 "XXXX . XXXXXXXXX",
138 "XXX . XXXXXXXXXX",
139 "XX . XXXXXXXXXXX",
140 "XX XXXXXXXXXXXX",
141 "XXXXXXXXXXXXXXXX",
142 "XXXXXXXXXXXXXXXX"
143 };
145 /* XPM */
146 static char const *sidebuttons[] = {
147 /* columns rows colors chars-per-pixel */
148 "16 16 4 1",
149 " c black",
150 ". c #808080",
151 "o c green",
152 "O c None",
153 /* pixels */
154 "OOOOOOOOOOOOOOOO",
155 "OOOOOOOOOOOOOOOO",
156 "O..............O",
157 "O.OOOOOOOOOOOO.O",
158 "O OOOOOOOO O",
159 "O o OOOOOOOO o O",
160 "O o OOOOOOOO o O",
161 "O OOOOOOOO O",
162 "O.OOOOOOOOOOOO.O",
163 "O.OOOOOOOOOOOO.O",
164 "O.OOOOOOOOOOOO.O",
165 "O.OOOOOOOOOOOO.O",
166 "O.OOOOOOOOOOOO.O",
167 "O..............O",
168 "OOOOOOOOOOOOOOOO",
169 "OOOOOOOOOOOOOOOO"
170 };
172 /* XPM */
173 static char const *tablet[] = {
174 /* columns rows colors chars-per-pixel */
175 "16 16 3 1",
176 " c black",
177 ". c gray100",
178 "X c None",
179 /* pixels */
180 "XXXXXXXXXXXXXXXX",
181 "XXXXXXXXXXXXXXXX",
182 "X X",
183 "X ............ X",
184 "X ............ X",
185 "X ............ X",
186 "X ............ X",
187 "X ............ X",
188 "X ............ X",
189 "X ............ X",
190 "X ............ X",
191 "X ............ X",
192 "X ............ X",
193 "X X",
194 "XXXXXXXXXXXXXXXX",
195 "XXXXXXXXXXXXXXXX"
196 };
198 /* XPM */
199 static char const *tip[] = {
200 /* columns rows colors chars-per-pixel */
201 "16 16 5 1",
202 " c black",
203 ". c green",
204 "X c #808080",
205 "o c gray100",
206 "O c None",
207 /* pixels */
208 "OOOOOOOOOOOOOOOO",
209 "OOOOOOOOOOOOOXOO",
210 "OOOOOOOOOOOOXoXO",
211 "OOOOOOOOOOOXoXOO",
212 "OOOOOOOOOOXoXOOO",
213 "OOOOOOOOOXoXOOOO",
214 "OOOOOOOOXoXOOOOO",
215 "OOOOOOO oXOOOOOO",
216 "OOOOOO . OOOOOOO",
217 "OOOOO . OOOOOOOO",
218 "OOOO . OOOOOOOOO",
219 "OOO . OOOOOOOOOO",
220 "OO . OOOOOOOOOOO",
221 "OO OOOOOOOOOOOO",
222 "OOOOXXXXXOOOOOOO",
223 "OOOOOOOOOXXXXXOO"
224 };
226 /* XPM */
227 static char const *button_none[] = {
228 /* columns rows colors chars-per-pixel */
229 "8 8 3 1",
230 " c black",
231 ". c #808080",
232 "X c None",
233 /* pixels */
234 "XXXXXXXX",
235 "XX .. XX",
236 "X .XX. X",
237 "X.XX X.X",
238 "X.X XX.X",
239 "X .XX. X",
240 "XX .. XX",
241 "XXXXXXXX"
242 };
243 /* XPM */
244 static char const *button_off[] = {
245 /* columns rows colors chars-per-pixel */
246 "8 8 4 1",
247 " c black",
248 ". c #808080",
249 "X c gray100",
250 "o c None",
251 /* pixels */
252 "oooooooo",
253 "oo. .oo",
254 "o. XX .o",
255 "o XXXX o",
256 "o XXXX o",
257 "o. XX .o",
258 "oo. .oo",
259 "oooooooo"
260 };
261 /* XPM */
262 static char const *button_on[] = {
263 /* columns rows colors chars-per-pixel */
264 "8 8 3 1",
265 " c black",
266 ". c green",
267 "X c None",
268 /* pixels */
269 "XXXXXXXX",
270 "XX XX",
271 "X .. X",
272 "X .... X",
273 "X .... X",
274 "X .. X",
275 "XX XX",
276 "XXXXXXXX"
277 };
279 /* XPM */
280 static char const * axis_none_xpm[] = {
281 "24 8 3 1",
282 " c None",
283 ". c #000000",
284 "+ c #808080",
285 " ",
286 " .++++++++++++++++++. ",
287 " .+ . .+. ",
288 " + . . . + ",
289 " + . . . + ",
290 " .+. . +. ",
291 " .++++++++++++++++++. ",
292 " "};
293 /* XPM */
294 static char const * axis_off_xpm[] = {
295 "24 8 4 1",
296 " c None",
297 ". c #808080",
298 "+ c #000000",
299 "@ c #FFFFFF",
300 " ",
301 " .++++++++++++++++++. ",
302 " .+@@@@@@@@@@@@@@@@@@+. ",
303 " +@@@@@@@@@@@@@@@@@@@@+ ",
304 " +@@@@@@@@@@@@@@@@@@@@+ ",
305 " .+@@@@@@@@@@@@@@@@@@+. ",
306 " .++++++++++++++++++. ",
307 " "};
308 /* XPM */
309 static char const * axis_on_xpm[] = {
310 "24 8 3 1",
311 " c None",
312 ". c #000000",
313 "+ c #00FF00",
314 " ",
315 " .................... ",
316 " ..++++++++++++++++++.. ",
317 " .++++++++++++++++++++. ",
318 " .++++++++++++++++++++. ",
319 " ..++++++++++++++++++.. ",
320 " .................... ",
321 " "};
323 using Inkscape::InputDevice;
325 namespace Inkscape {
326 namespace UI {
327 namespace Dialog {
331 class DeviceModelColumns : public Gtk::TreeModel::ColumnRecord
332 {
333 public:
334 Gtk::TreeModelColumn<Glib::ustring> description;
335 Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> > thumbnail;
336 Gtk::TreeModelColumn<Glib::RefPtr<InputDevice const> > device;
337 Gtk::TreeModelColumn<Gdk::InputMode> mode;
339 DeviceModelColumns() { add(description); add(thumbnail); add(device); add(mode); }
340 };
342 static std::map<Gdk::InputMode, Glib::ustring> &getModeToString()
343 {
344 static std::map<Gdk::InputMode, Glib::ustring> mapping;
345 if (mapping.empty()) {
346 mapping[Gdk::MODE_DISABLED] = _("Disabled");
347 mapping[Gdk::MODE_SCREEN] = _("Screen");
348 mapping[Gdk::MODE_WINDOW] = _("Window");
349 }
351 return mapping;
352 }
354 static std::map<Glib::ustring, Gdk::InputMode> &getStringToMode()
355 {
356 static std::map<Glib::ustring, Gdk::InputMode> mapping;
357 if (mapping.empty()) {
358 mapping[_("Disabled")] = Gdk::MODE_DISABLED;
359 mapping[_("Screen")] = Gdk::MODE_SCREEN;
360 mapping[_("Window")] = Gdk::MODE_WINDOW;
361 }
363 return mapping;
364 }
368 class InputDialogImpl : public InputDialog {
369 public:
370 InputDialogImpl();
371 virtual ~InputDialogImpl() {}
373 private:
374 class ConfPanel : public Gtk::VBox
375 {
376 public:
377 ConfPanel();
378 ~ConfPanel();
380 class Blink : public Preferences::Observer
381 {
382 public:
383 Blink(ConfPanel &parent);
384 virtual ~Blink();
385 virtual void notify(Preferences::Entry const &new_val);
387 ConfPanel &parent;
388 };
390 static void commitCellModeChange(Glib::ustring const &path, Glib::ustring const &newText, Glib::RefPtr<Gtk::TreeStore> store);
391 static void setModeCellString(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter);
393 void saveSettings();
394 void useExtToggled();
396 Glib::RefPtr<Gtk::TreeStore> store;
397 Gtk::TreeIter tabletIter;
398 Gtk::TreeView tree;
399 Gtk::ScrolledWindow treeScroller;
400 Blink watcher;
401 Gtk::CheckButton useExt;
402 Gtk::Button save;
403 };
405 static DeviceModelColumns &getCols();
407 enum PixId {PIX_CORE, PIX_PEN, PIX_MOUSE, PIX_TIP, PIX_TABLET, PIX_ERASER, PIX_SIDEBUTTONS,
408 PIX_BUTTONS_NONE, PIX_BUTTONS_ON, PIX_BUTTONS_OFF,
409 PIX_AXIS_NONE, PIX_AXIS_ON, PIX_AXIS_OFF};
411 static Glib::RefPtr<Gdk::Pixbuf> getPix(PixId id);
413 std::map<Glib::ustring, std::set<guint> > buttonMap;
414 std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
416 GdkInputSource lastSourceSeen;
417 Glib::ustring lastDevnameSeen;
419 Glib::RefPtr<Gtk::TreeStore> store;
420 Gtk::TreeIter tabletIter;
421 Gtk::TreeView tree;
422 Gtk::Frame frame2;
423 Gtk::Frame testFrame;
424 Gtk::ScrolledWindow treeScroller;
425 Gtk::ScrolledWindow detailScroller;
426 Gtk::HPaned splitter;
427 Gtk::VPaned split2;
428 Gtk::Label devName;
429 Gtk::Label devKeyCount;
430 Gtk::Label devAxesCount;
431 Gtk::ComboBoxText axesCombo;
432 Gtk::ProgressBar axesValues[6];
433 Gtk::ComboBoxText buttonCombo;
434 Gtk::ComboBoxText linkCombo;
435 sigc::connection linkConnection;
436 Gtk::Label keyVal;
437 Gtk::Entry keyEntry;
438 Gtk::Table devDetails;
439 Gtk::Notebook topHolder;
440 Gtk::Image testThumb;
441 Gtk::Image testButtons[24];
442 Gtk::Image testAxes[8];
443 Gtk::Table imageTable;
444 Gtk::EventBox testDetector;
446 ConfPanel cfgPanel;
448 static void setupTree( Glib::RefPtr<Gtk::TreeStore> store, Gtk::TreeIter &tablet );
449 void setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo );
450 void updateTestButtons( Glib::ustring const& key, gint hotButton );
451 void updateTestAxes( Glib::ustring const& key, GdkDevice* dev );
452 void mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev);
453 Glib::ustring getKeyFor( GdkDevice* device );
454 bool eventSnoop(GdkEvent* event);
455 void linkComboChanged();
456 void resyncToSelection();
457 void handleDeviceChange(const Glib::RefPtr<InputDevice>& device);
458 void updateDeviceAxes(const Glib::RefPtr<InputDevice>& device);
459 void updateDeviceButtons(const Glib::RefPtr<InputDevice>& device);
460 static void updateDeviceLinks(const Glib::RefPtr<InputDevice>& device, Gtk::TreeIter &tabletIter, Gtk::TreeView &tree);
462 static bool findDevice(const Gtk::TreeModel::iterator& iter,
463 Glib::ustring id,
464 Gtk::TreeModel::iterator* result);
465 static bool findDeviceByLink(const Gtk::TreeModel::iterator& iter,
466 Glib::ustring link,
467 Gtk::TreeModel::iterator* result);
469 }; // class InputDialogImpl
472 DeviceModelColumns &InputDialogImpl::getCols()
473 {
474 static DeviceModelColumns cols;
475 return cols;
476 }
478 Glib::RefPtr<Gdk::Pixbuf> InputDialogImpl::getPix(PixId id)
479 {
480 static std::map<PixId, Glib::RefPtr<Gdk::Pixbuf> > mappings;
482 mappings[PIX_CORE] = Gdk::Pixbuf::create_from_xpm_data(core_xpm);
483 mappings[PIX_PEN] = Gdk::Pixbuf::create_from_xpm_data(pen);
484 mappings[PIX_MOUSE] = Gdk::Pixbuf::create_from_xpm_data(mouse);
485 mappings[PIX_TIP] = Gdk::Pixbuf::create_from_xpm_data(tip);
486 mappings[PIX_TABLET] = Gdk::Pixbuf::create_from_xpm_data(tablet);
487 mappings[PIX_ERASER] = Gdk::Pixbuf::create_from_xpm_data(eraser);
488 mappings[PIX_SIDEBUTTONS] = Gdk::Pixbuf::create_from_xpm_data(sidebuttons);
490 mappings[PIX_BUTTONS_NONE] = Gdk::Pixbuf::create_from_xpm_data(button_none);
491 mappings[PIX_BUTTONS_ON] = Gdk::Pixbuf::create_from_xpm_data(button_on);
492 mappings[PIX_BUTTONS_OFF] = Gdk::Pixbuf::create_from_xpm_data(button_off);
494 mappings[PIX_AXIS_NONE] = Gdk::Pixbuf::create_from_xpm_data(axis_none_xpm);
495 mappings[PIX_AXIS_ON] = Gdk::Pixbuf::create_from_xpm_data(axis_on_xpm);
496 mappings[PIX_AXIS_OFF] = Gdk::Pixbuf::create_from_xpm_data(axis_off_xpm);
498 Glib::RefPtr<Gdk::Pixbuf> pix;
499 if (mappings.find(id) != mappings.end()) {
500 pix = mappings[id];
501 }
503 return pix;
504 }
507 // Now that we've defined the *Impl class, we can do the method to aquire one.
508 InputDialog &InputDialog::getInstance()
509 {
510 InputDialog *dialog = new InputDialogImpl();
511 return *dialog;
512 }
515 InputDialogImpl::InputDialogImpl() :
516 InputDialog(),
518 lastSourceSeen((GdkInputSource)-1),
519 lastDevnameSeen(""),
520 store(Gtk::TreeStore::create(getCols())),
521 tabletIter(),
522 tree(store),
523 frame2(),
524 testFrame(_("Test Area")),
525 treeScroller(),
526 detailScroller(),
527 splitter(),
528 split2(),
529 linkCombo(),
530 devDetails(12, 2),
531 topHolder(),
532 imageTable(8, 7),
533 testDetector(),
534 cfgPanel()
535 {
536 Gtk::Box *contents = _getContents();
539 treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
540 treeScroller.add(tree);
541 split2.pack1(testFrame);
542 split2.pack2(frame2);
543 splitter.pack1(treeScroller);
544 splitter.pack2(split2);
546 testDetector.add(imageTable);
547 testFrame.add(testDetector);
548 testThumb.set(getPix(PIX_TABLET));
549 testThumb.set_padding(24, 24);
550 imageTable.attach(testThumb, 0, 8, 0, 1, ::Gtk::EXPAND, ::Gtk::EXPAND);
551 {
552 guint col = 0;
553 guint row = 1;
554 for ( guint num = 0; num < G_N_ELEMENTS(testButtons); num++ ) {
555 testButtons[num].set(getPix(PIX_BUTTONS_NONE));
556 imageTable.attach(testButtons[num], col, col + 1, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
557 col++;
558 if (col > 7) {
559 col = 0;
560 row++;
561 }
562 }
564 col = 0;
565 for ( guint num = 0; num < G_N_ELEMENTS(testAxes); num++ ) {
566 testAxes[num].set(getPix(PIX_AXIS_NONE));
567 imageTable.attach(testAxes[num], col * 2, (col + 1) * 2, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
568 col++;
569 if (col > 3) {
570 col = 0;
571 row++;
572 }
573 }
574 }
577 topHolder.append_page(cfgPanel, _("Configuration"));
578 topHolder.append_page(splitter, _("Hardware"));
579 topHolder.show_all();
580 topHolder.set_current_page(0);
582 contents->pack_start(topHolder);
584 int rowNum = 0;
586 Gtk::Label* lbl = Gtk::manage(new Gtk::Label(_("Name:")));
587 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
588 ::Gtk::FILL,
589 ::Gtk::SHRINK);
590 devDetails.attach(devName, 1, 2, rowNum, rowNum + 1,
591 ::Gtk::SHRINK,
592 ::Gtk::SHRINK);
594 rowNum++;
596 lbl = Gtk::manage(new Gtk::Label(_("Link:")));
597 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
598 ::Gtk::FILL,
599 ::Gtk::SHRINK);
601 linkCombo.append_text(_("None"));
602 linkCombo.set_active_text(_("None"));
603 linkCombo.set_sensitive(false);
604 linkConnection = linkCombo.signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::linkComboChanged));
606 devDetails.attach(linkCombo, 1, 2, rowNum, rowNum + 1,
607 ::Gtk::FILL,
608 ::Gtk::SHRINK);
609 rowNum++;
611 lbl = Gtk::manage(new Gtk::Label(_("Axes count:")));
612 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
613 ::Gtk::FILL,
614 ::Gtk::SHRINK);
615 devDetails.attach(devAxesCount, 1, 2, rowNum, rowNum + 1,
616 ::Gtk::SHRINK,
617 ::Gtk::SHRINK);
619 rowNum++;
621 /*
622 lbl = Gtk::manage(new Gtk::Label(_("Actual axes count:")));
623 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
624 ::Gtk::FILL,
625 ::Gtk::SHRINK);
626 devDetails.attach(axesCombo, 1, 2, rowNum, rowNum + 1,
627 ::Gtk::SHRINK,
628 ::Gtk::SHRINK);
630 rowNum++;
631 */
633 for ( guint barNum = 0; barNum < static_cast<guint>(G_N_ELEMENTS(axesValues)); barNum++ ) {
634 lbl = Gtk::manage(new Gtk::Label(_("axis:")));
635 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
636 ::Gtk::FILL,
637 ::Gtk::SHRINK);
638 devDetails.attach(axesValues[barNum], 1, 2, rowNum, rowNum + 1,
639 ::Gtk::EXPAND,
640 ::Gtk::SHRINK);
641 axesValues[barNum].set_sensitive(false);
643 rowNum++;
644 }
646 lbl = Gtk::manage(new Gtk::Label(_("Button count:")));
647 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
648 ::Gtk::FILL,
649 ::Gtk::SHRINK);
650 devDetails.attach(devKeyCount, 1, 2, rowNum, rowNum + 1,
651 ::Gtk::SHRINK,
652 ::Gtk::SHRINK);
654 rowNum++;
656 /*
657 lbl = Gtk::manage(new Gtk::Label(_("Actual button count:")));
658 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
659 ::Gtk::FILL,
660 ::Gtk::SHRINK);
661 devDetails.attach(buttonCombo, 1, 2, rowNum, rowNum + 1,
662 ::Gtk::SHRINK,
663 ::Gtk::SHRINK);
665 rowNum++;
666 */
668 devDetails.attach(keyVal, 0, 2, rowNum, rowNum + 1,
669 ::Gtk::FILL,
670 ::Gtk::SHRINK);
671 rowNum++;
674 testDetector.signal_event().connect(sigc::mem_fun(*this, &InputDialogImpl::eventSnoop));
676 // void gdk_input_set_extension_events (GdkWindow *window,
677 // gint mask,
678 // GdkExtensionMode mode);
680 gtk_widget_set_extension_events( GTK_WIDGET(testDetector.gobj()), GDK_EXTENSION_EVENTS_ALL );
681 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);
683 devDetails.attach(keyEntry, 0, 2, rowNum, rowNum + 1,
684 ::Gtk::FILL,
685 ::Gtk::SHRINK);
686 rowNum++;
689 devDetails.set_sensitive(false);
690 detailScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
691 detailScroller.add(devDetails);
692 frame2.add(detailScroller);
694 //- 16x16/devices
695 // gnome-dev-mouse-optical
696 // input-mouse
697 // input-tablet
698 // mouse
702 //Add the TreeView's view columns:
703 tree.append_column("I", getCols().thumbnail);
704 tree.append_column("Bar", getCols().description);
706 tree.set_enable_tree_lines();
707 tree.set_headers_visible(false);
708 tree.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::resyncToSelection));
711 setupTree( store, tabletIter );
713 Inkscape::DeviceManager::getManager().signalDeviceChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::handleDeviceChange));
714 Inkscape::DeviceManager::getManager().signalAxesChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceAxes));
715 Inkscape::DeviceManager::getManager().signalButtonsChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceButtons));
716 Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind<Gtk::TreeIter &, Gtk::TreeView &>(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, tree));
718 tree.expand_all();
719 show_all_children();
720 }
722 void InputDialogImpl::setupTree( Glib::RefPtr<Gtk::TreeStore> store, Gtk::TreeIter &tablet )
723 {
724 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
725 if ( !devList.empty() ) {
726 Gtk::TreeModel::Row row = *(store->append());
727 row[getCols().description] = _("Hardware");
729 tablet = store->append(row.children());
730 Gtk::TreeModel::Row childrow = *tablet;
731 childrow[getCols().description] = _("Tablet");
732 childrow[getCols().thumbnail] = getPix(PIX_TABLET);
734 for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it = devList.begin(); it != devList.end(); ++it ) {
735 Glib::RefPtr<InputDevice const> dev = *it;
736 if ( dev ) {
737 // 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(),
738 // dev->hasCursor() ? "Yes":"no", dev->getNumAxes(), dev->getNumKeys());
740 // if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
741 if ( dev ) {
742 Gtk::TreeModel::Row deviceRow = *(store->append(childrow.children()));
743 deviceRow[getCols().description] = dev->getName();
744 deviceRow[getCols().device] = dev;
745 deviceRow[getCols().mode] = dev->getMode();
746 switch ( dev->getSource() ) {
747 case GDK_SOURCE_MOUSE:
748 deviceRow[getCols().thumbnail] = getPix(PIX_CORE);
749 break;
750 case GDK_SOURCE_PEN:
751 if (deviceRow[getCols().description] == _("pad")) {
752 deviceRow[getCols().thumbnail] = getPix(PIX_SIDEBUTTONS);
753 } else {
754 deviceRow[getCols().thumbnail] = getPix(PIX_TIP);
755 }
756 break;
757 case GDK_SOURCE_CURSOR:
758 deviceRow[getCols().thumbnail] = getPix(PIX_MOUSE);
759 break;
760 case GDK_SOURCE_ERASER:
761 deviceRow[getCols().thumbnail] = getPix(PIX_ERASER);
762 break;
763 default:
764 ; // nothing
765 }
766 }
767 } else {
768 g_warning("Null device in list");
769 }
770 }
771 } else {
772 g_warning("No devices found");
773 }
774 }
777 InputDialogImpl::ConfPanel::ConfPanel() :
778 Gtk::VBox(),
779 store(Gtk::TreeStore::create(getCols())),
780 tabletIter(),
781 tree(store),
782 treeScroller(),
783 watcher(*this),
784 useExt(_("Use pressure-sensitive tablet (requires restart)")),
785 save(_("Save"))
786 {
787 pack_start(treeScroller);
789 treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
790 treeScroller.add(tree);
792 class Foo : public Gtk::TreeModel::ColumnRecord {
793 public :
794 Gtk::TreeModelColumn<Glib::ustring> one;
795 Foo() {add(one);}
796 };
797 static Foo foo;
798 Glib::RefPtr<Gtk::ListStore> poppers = Gtk::ListStore::create(foo);
799 poppers->reference();
801 Gtk::TreeModel::Row row = *(poppers->append());
802 row[foo.one] = getModeToString()[Gdk::MODE_DISABLED];
803 row = *(poppers->append());
804 row[foo.one] = getModeToString()[Gdk::MODE_SCREEN];
805 row = *(poppers->append());
806 row[foo.one] = getModeToString()[Gdk::MODE_WINDOW];
808 Gtk::CellRendererCombo *rendr = new Gtk::CellRendererCombo();
809 rendr->property_model().set_value(poppers);
810 rendr->property_text_column().set_value(0);
811 rendr->property_has_entry() = false;
813 //Add the TreeView's view columns:
814 tree.append_column("I", getCols().thumbnail);
815 tree.append_column("Bar", getCols().description);
816 Gtk::TreeViewColumn *col = new Gtk::TreeViewColumn("X", *rendr);
817 if (col) {
818 tree.append_column(*col);
819 col->set_cell_data_func(*rendr, sigc::ptr_fun(setModeCellString));
820 rendr->signal_edited().connect(sigc::bind(sigc::ptr_fun(commitCellModeChange), store));
821 rendr->property_editable() = true;
822 }
824 tree.set_enable_tree_lines();
825 tree.set_headers_visible(false);
827 setupTree( store, tabletIter );
829 Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind<Gtk::TreeIter &, Gtk::TreeView &>(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, tree));
831 tree.expand_all();
833 useExt.set_active(Preferences::get()->getBool("/options/useextinput/value"));
834 useExt.signal_toggled().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::useExtToggled));
835 pack_start(useExt, Gtk::PACK_SHRINK);
837 save.signal_clicked().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::saveSettings));
838 Gtk::Alignment *align = new Gtk::Alignment(Gtk::ALIGN_RIGHT, Gtk::ALIGN_TOP, 0, 0);
839 align->add(save);
840 pack_start(*Gtk::manage(align), Gtk::PACK_SHRINK);
841 }
843 InputDialogImpl::ConfPanel::~ConfPanel()
844 {
845 }
847 void InputDialogImpl::ConfPanel::setModeCellString(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter)
848 {
849 if (iter) {
850 Gtk::CellRendererCombo *combo = dynamic_cast<Gtk::CellRendererCombo *>(rndr);
851 if (combo) {
852 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
853 Gdk::InputMode mode = (*iter)[getCols().mode];
854 if (dev && (getModeToString().find(mode) != getModeToString().end())) {
855 combo->property_text() = getModeToString()[mode];
856 } else {
857 combo->property_text() = "";
858 }
859 }
860 }
861 }
863 void InputDialogImpl::ConfPanel::commitCellModeChange(Glib::ustring const &path, Glib::ustring const &newText, Glib::RefPtr<Gtk::TreeStore> store)
864 {
865 Gtk::TreeIter iter = store->get_iter(path);
866 if (iter) {
867 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
868 if (dev && (getStringToMode().find(newText) != getStringToMode().end())) {
869 Gdk::InputMode mode = getStringToMode()[newText];
870 Inkscape::DeviceManager::getManager().setMode( dev->getId(), mode );
871 }
872 }
873 }
875 void InputDialogImpl::ConfPanel::saveSettings()
876 {
877 Inkscape::DeviceManager::getManager().saveConfig();
878 }
880 void InputDialogImpl::ConfPanel::useExtToggled()
881 {
882 bool active = useExt.get_active();
883 if (active != Preferences::get()->getBool("/options/useextinput/value")) {
884 Preferences::get()->setBool("/options/useextinput/value", active);
885 if (active) {
886 // As a work-around for a common problem, enable tablet toggles on the calligraphic tool.
887 // Covered in Launchpad bug #196195.
888 Preferences::get()->setBool("/tools/tweak/usepressure", true);
889 Preferences::get()->setBool("/tools/calligraphic/usepressure", true);
890 Preferences::get()->setBool("/tools/calligraphic/usetilt", true);
891 }
892 }
893 }
895 InputDialogImpl::ConfPanel::Blink::Blink(ConfPanel &parent) :
896 Preferences::Observer("/options/useextinput/value"),
897 parent(parent)
898 {
899 Preferences::get()->addObserver(*this);
900 }
902 InputDialogImpl::ConfPanel::Blink::~Blink()
903 {
904 Preferences::get()->removeObserver(*this);
905 }
907 void InputDialogImpl::ConfPanel::Blink::notify(Preferences::Entry const &new_val)
908 {
909 parent.useExt.set_active(new_val.getBool());
910 }
912 void InputDialogImpl::handleDeviceChange(const Glib::RefPtr<InputDevice>& device)
913 {
914 // g_message("OUCH!!!! for %p hits %s", &device, device->getId().c_str());
915 std::vector<Glib::RefPtr<Gtk::TreeStore> > stores;
916 stores.push_back(store);
917 stores.push_back(cfgPanel.store);
919 for (std::vector<Glib::RefPtr<Gtk::TreeStore> >::iterator it = stores.begin(); it != stores.end(); ++it) {
920 Gtk::TreeModel::iterator deviceIter;
921 (*it)->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
922 sigc::ptr_fun(&InputDialogImpl::findDevice),
923 device->getId(),
924 &deviceIter) );
925 if ( deviceIter ) {
926 Gdk::InputMode mode = device->getMode();
927 Gtk::TreeModel::Row row = *deviceIter;
928 if (row[getCols().mode] != mode) {
929 row[getCols().mode] = mode;
930 }
931 }
932 }
933 }
935 void InputDialogImpl::updateDeviceAxes(const Glib::RefPtr<InputDevice>& device)
936 {
937 gint live = device->getLiveAxes();
939 std::map<guint, std::pair<guint, gdouble> > existing = axesMap[device->getId()];
940 gint mask = 0x1;
941 for ( gint num = 0; num < 32; num++, mask <<= 1) {
942 if ( (mask & live) != 0 ) {
943 if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) {
944 axesMap[device->getId()][num].first = 2;
945 axesMap[device->getId()][num].second = 0.0;
946 }
947 }
948 }
949 updateTestAxes( device->getId(), 0 );
950 }
952 void InputDialogImpl::updateDeviceButtons(const Glib::RefPtr<InputDevice>& device)
953 {
954 gint live = device->getLiveButtons();
955 std::set<guint> existing = buttonMap[device->getId()];
956 gint mask = 0x1;
957 for ( gint num = 0; num < 32; num++, mask <<= 1) {
958 if ( (mask & live) != 0 ) {
959 if ( existing.find(num) == existing.end() ) {
960 buttonMap[device->getId()].insert(num);
961 }
962 }
963 }
964 updateTestButtons(device->getId(), -1);
965 }
968 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
969 Glib::ustring id,
970 Gtk::TreeModel::iterator* result)
971 {
972 bool stop = false;
973 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
974 if ( dev && (dev->getId() == id) ) {
975 if ( result ) {
976 *result = iter;
977 }
978 stop = true;
979 }
980 return stop;
981 }
983 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
984 Glib::ustring link,
985 Gtk::TreeModel::iterator* result)
986 {
987 bool stop = false;
988 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
989 if ( dev && (dev->getLink() == link) ) {
990 if ( result ) {
991 *result = iter;
992 }
993 stop = true;
994 }
995 return stop;
996 }
998 void InputDialogImpl::updateDeviceLinks(const Glib::RefPtr<InputDevice>& device, Gtk::TreeIter &tabletIter, Gtk::TreeView &tree)
999 {
1000 Glib::RefPtr<Gtk::TreeStore> store = Glib::RefPtr<Gtk::TreeStore>::cast_dynamic(tree.get_model());
1002 // g_message("Links!!!! for %p hits [%s] with link of [%s]", &device, device->getId().c_str(), device->getLink().c_str());
1003 Gtk::TreeModel::iterator deviceIter;
1004 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1005 sigc::ptr_fun(&InputDialogImpl::findDevice),
1006 device->getId(),
1007 &deviceIter) );
1009 if ( deviceIter ) {
1010 // Found the device concerned. Can proceed.
1012 if ( device->getLink().empty() ) {
1013 // is now unlinked
1014 // g_message("Item %s is unlinked", device->getId().c_str());
1015 if ( deviceIter->parent() != tabletIter ) {
1016 // Not the child of the tablet. move on up
1018 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1019 Glib::ustring descr = (*deviceIter)[getCols().description];
1020 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1022 Gtk::TreeModel::Row deviceRow = *store->append(tabletIter->children());
1023 deviceRow[getCols().description] = descr;
1024 deviceRow[getCols().thumbnail] = thumb;
1025 deviceRow[getCols().device] = dev;
1026 deviceRow[getCols().mode] = dev->getMode();
1028 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1029 store->erase(deviceIter);
1030 if ( oldParent->children().empty() ) {
1031 store->erase(oldParent);
1032 }
1033 }
1034 } else {
1035 // is linking
1036 if ( deviceIter->parent() == tabletIter ) {
1037 // Simple case. Not already linked
1039 Gtk::TreeIter newGroup = store->append(tabletIter->children());
1040 (*newGroup)[getCols().description] = _("Pen");
1041 (*newGroup)[getCols().thumbnail] = getPix(PIX_PEN);
1043 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1044 Glib::ustring descr = (*deviceIter)[getCols().description];
1045 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1047 Gtk::TreeModel::Row deviceRow = *store->append(newGroup->children());
1048 deviceRow[getCols().description] = descr;
1049 deviceRow[getCols().thumbnail] = thumb;
1050 deviceRow[getCols().device] = dev;
1051 deviceRow[getCols().mode] = dev->getMode();
1054 Gtk::TreeModel::iterator linkIter;
1055 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1056 sigc::ptr_fun(&InputDialogImpl::findDeviceByLink),
1057 device->getId(),
1058 &linkIter) );
1059 if ( linkIter ) {
1060 dev = (*linkIter)[getCols().device];
1061 descr = (*linkIter)[getCols().description];
1062 thumb = (*linkIter)[getCols().thumbnail];
1064 deviceRow = *store->append(newGroup->children());
1065 deviceRow[getCols().description] = descr;
1066 deviceRow[getCols().thumbnail] = thumb;
1067 deviceRow[getCols().device] = dev;
1068 deviceRow[getCols().mode] = dev->getMode();
1069 Gtk::TreeModel::iterator oldParent = linkIter->parent();
1070 store->erase(linkIter);
1071 if ( oldParent->children().empty() ) {
1072 store->erase(oldParent);
1073 }
1074 }
1076 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1077 store->erase(deviceIter);
1078 if ( oldParent->children().empty() ) {
1079 store->erase(oldParent);
1080 }
1081 tree.expand_row(Gtk::TreePath(newGroup), true);
1082 }
1083 }
1084 }
1085 }
1087 void InputDialogImpl::linkComboChanged() {
1088 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1089 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1090 if (iter) {
1091 Gtk::TreeModel::Row row = *iter;
1092 Glib::ustring val = row[getCols().description];
1093 Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1094 if ( dev ) {
1095 if ( linkCombo.get_active_row_number() == 0 ) {
1096 // It is the "None" entry
1097 DeviceManager::getManager().setLinkedTo(dev->getId(), "");
1098 } else {
1099 Glib::ustring linkName = linkCombo.get_active_text();
1100 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1101 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1102 if ( linkName == (*it)->getName() ) {
1103 DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId());
1104 break;
1105 }
1106 }
1107 }
1108 }
1109 }
1110 }
1112 void InputDialogImpl::resyncToSelection() {
1113 bool clear = true;
1114 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1115 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1116 if (iter) {
1117 Gtk::TreeModel::Row row = *iter;
1118 Glib::ustring val = row[getCols().description];
1119 Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1120 if ( dev ) {
1121 devDetails.set_sensitive(true);
1123 linkConnection.block();
1124 linkCombo.clear_items();
1125 linkCombo.append_text(_("None"));
1126 linkCombo.set_active(0);
1127 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
1128 Glib::ustring linked = dev->getLink();
1129 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1130 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1131 if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) {
1132 linkCombo.append_text((*it)->getName().c_str());
1133 if ( (linked.length() > 0) && (linked == (*it)->getId()) ) {
1134 linkCombo.set_active_text((*it)->getName().c_str());
1135 }
1136 }
1137 }
1138 linkCombo.set_sensitive(true);
1139 } else {
1140 linkCombo.set_sensitive(false);
1141 }
1142 linkConnection.unblock();
1144 clear = false;
1145 devName.set_label(row[getCols().description]);
1146 setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo);
1147 setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo);
1148 }
1149 }
1151 devDetails.set_sensitive(!clear);
1152 if (clear) {
1153 devName.set_label("");
1154 devAxesCount.set_label("");
1155 devKeyCount.set_label("");
1156 }
1157 }
1159 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
1160 {
1161 gchar *tmp = g_strdup_printf("%d", reported);
1162 label.set_label(tmp);
1163 g_free(tmp);
1165 combo.clear_items();
1166 for ( gint i = 1; i <= reported; ++i ) {
1167 tmp = g_strdup_printf("%d", i);
1168 combo.append_text(tmp);
1169 g_free(tmp);
1170 }
1172 if ( (1 <= actual) && (actual <= reported) ) {
1173 combo.set_active(actual - 1);
1174 }
1175 }
1177 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
1178 {
1179 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testButtons)); i++ ) {
1180 if ( buttonMap[key].find(i) != buttonMap[key].end() ) {
1181 if ( i == hotButton ) {
1182 testButtons[i].set(getPix(PIX_BUTTONS_ON));
1183 } else {
1184 testButtons[i].set(getPix(PIX_BUTTONS_OFF));
1185 }
1186 } else {
1187 testButtons[i].set(getPix(PIX_BUTTONS_NONE));
1188 }
1189 }
1190 }
1192 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
1193 {
1194 static gdouble epsilon = 0.0001;
1195 {
1196 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1197 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1198 if (iter) {
1199 Gtk::TreeModel::Row row = *iter;
1200 Glib::ustring val = row[getCols().description];
1201 Glib::RefPtr<InputDevice const> idev = row[getCols().device];
1202 if ( !idev || (idev->getId() != key) ) {
1203 dev = 0;
1204 }
1205 }
1206 }
1209 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testAxes)); i++ ) {
1210 if ( axesMap[key].find(i) != axesMap[key].end() ) {
1211 switch ( axesMap[key][i].first ) {
1212 case 0:
1213 case 1:
1214 testAxes[i].set(getPix(PIX_AXIS_NONE));
1215 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1216 axesValues[i].set_sensitive(false);
1217 }
1218 break;
1219 case 2:
1220 testAxes[i].set(getPix(PIX_AXIS_OFF));
1221 axesValues[i].set_sensitive(true);
1222 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1223 if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1224 axesValues[i].set_sensitive(true);
1225 axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1226 }
1227 gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1228 axesValues[i].set_text(str);
1229 g_free(str);
1230 }
1231 break;
1232 case 3:
1233 testAxes[i].set(getPix(PIX_AXIS_ON));
1234 axesValues[i].set_sensitive(true);
1235 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1236 if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1237 axesValues[i].set_sensitive(true);
1238 axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1239 }
1240 gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1241 axesValues[i].set_text(str);
1242 g_free(str);
1243 }
1244 }
1246 } else {
1247 testAxes[i].set(getPix(PIX_AXIS_NONE));
1248 }
1249 }
1250 if ( !dev ) {
1251 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(axesValues)); i++ ) {
1252 axesValues[i].set_fraction(0.0);
1253 axesValues[i].set_text("");
1254 axesValues[i].set_sensitive(false);
1255 }
1256 }
1257 }
1259 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1260 {
1261 static gdouble epsilon = 0.0001;
1262 if ( (numAxes > 0) && axes) {
1263 for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) {
1264 // 0 == new, 1 == set value, 2 == changed value, 3 == active
1265 gdouble diff = axesMap[key][axisNum].second - axes[axisNum];
1266 switch(axesMap[key][axisNum].first) {
1267 case 0:
1268 {
1269 axesMap[key][axisNum].first = 1;
1270 axesMap[key][axisNum].second = axes[axisNum];
1271 }
1272 break;
1273 case 1:
1274 {
1275 if ( (diff > epsilon) || (diff < -epsilon) ) {
1276 // g_message("Axis %d changed on %s]", axisNum, key.c_str());
1277 axesMap[key][axisNum].first = 3;
1278 axesMap[key][axisNum].second = axes[axisNum];
1279 updateTestAxes(key, dev);
1280 DeviceManager::getManager().addAxis(key, axisNum);
1281 }
1282 }
1283 break;
1284 case 2:
1285 {
1286 if ( (diff > epsilon) || (diff < -epsilon) ) {
1287 axesMap[key][axisNum].first = 3;
1288 axesMap[key][axisNum].second = axes[axisNum];
1289 updateTestAxes(key, dev);
1290 }
1291 }
1292 break;
1293 case 3:
1294 {
1295 if ( (diff > epsilon) || (diff < -epsilon) ) {
1296 axesMap[key][axisNum].second = axes[axisNum];
1297 } else {
1298 axesMap[key][axisNum].first = 2;
1299 updateTestAxes(key, dev);
1300 }
1301 }
1302 }
1303 }
1304 }
1305 // std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
1306 }
1308 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1309 {
1310 Glib::ustring key;
1311 switch ( device->source ) {
1312 case GDK_SOURCE_MOUSE:
1313 key = "M:";
1314 break;
1315 case GDK_SOURCE_CURSOR:
1316 key = "C:";
1317 break;
1318 case GDK_SOURCE_PEN:
1319 key = "P:";
1320 break;
1321 case GDK_SOURCE_ERASER:
1322 key = "E:";
1323 break;
1324 default:
1325 key = "?:";
1326 }
1327 key += device->name;
1329 return key;
1330 }
1332 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1333 {
1334 int modmod = 0;
1336 GdkInputSource source = lastSourceSeen;
1337 Glib::ustring devName = lastDevnameSeen;
1338 Glib::ustring key;
1339 gint hotButton = -1;
1341 switch ( event->type ) {
1342 case GDK_KEY_PRESS:
1343 case GDK_KEY_RELEASE:
1344 {
1345 GdkEventKey* keyEvt = reinterpret_cast<GdkEventKey*>(event);
1346 gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast<GdkModifierType>(keyEvt->state));
1347 keyVal.set_label(name);
1348 // g_message("%d KEY state:0x%08x 0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name);
1349 g_free(name);
1350 }
1351 break;
1352 case GDK_BUTTON_PRESS:
1353 modmod = 1;
1354 // fallthrough
1355 case GDK_BUTTON_RELEASE:
1356 {
1357 GdkEventButton* btnEvt = reinterpret_cast<GdkEventButton*>(event);
1358 if ( btnEvt->device ) {
1359 key = getKeyFor(btnEvt->device);
1360 source = btnEvt->device->source;
1361 devName = btnEvt->device->name;
1363 mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device);
1364 if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) {
1365 // g_message("New button found for %s = %d", key.c_str(), btnEvt->button);
1366 buttonMap[key].insert(btnEvt->button);
1367 DeviceManager::getManager().addButton(key, btnEvt->button);
1368 }
1369 hotButton = modmod ? btnEvt->button : -1;
1370 updateTestButtons(key, hotButton);
1371 }
1372 gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnEvt->state));
1373 keyVal.set_label(name);
1374 // g_message("%d BTN state:0x%08x %c %4d [%s] dev:%p [%s] ",
1375 // btnEvt->type, btnEvt->state,
1376 // (modmod ? '+':'-'),
1377 // btnEvt->button, name, btnEvt->device,
1378 // (btnEvt->device ? btnEvt->device->name : "null")
1380 // );
1381 g_free(name);
1382 }
1383 break;
1384 case GDK_MOTION_NOTIFY:
1385 {
1386 GdkEventMotion* btnMtn = reinterpret_cast<GdkEventMotion*>(event);
1387 if ( btnMtn->device ) {
1388 key = getKeyFor(btnMtn->device);
1389 source = btnMtn->device->source;
1390 devName = btnMtn->device->name;
1391 mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device);
1392 }
1393 gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnMtn->state));
1394 keyVal.set_label(name);
1395 // 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,
1396 // name, btnMtn->device,
1397 // (btnMtn->device ? btnMtn->device->name : "null"),
1398 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0),
1399 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0),
1400 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0),
1401 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0),
1402 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0),
1403 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0)
1404 // );
1405 g_free(name);
1406 }
1407 break;
1408 default:
1409 ;// nothing
1410 }
1413 if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) {
1414 switch (source) {
1415 case GDK_SOURCE_MOUSE:
1416 {
1417 testThumb.set(getPix(PIX_CORE));
1418 }
1419 break;
1420 case GDK_SOURCE_CURSOR:
1421 {
1422 // g_message("flip to cursor");
1423 testThumb.set(getPix(PIX_MOUSE));
1424 }
1425 break;
1426 case GDK_SOURCE_PEN:
1427 {
1428 if (devName == _("pad")) {
1429 // g_message("flip to pad");
1430 testThumb.set(getPix(PIX_SIDEBUTTONS));
1431 } else {
1432 // g_message("flip to pen");
1433 testThumb.set(getPix(PIX_TIP));
1434 }
1435 }
1436 break;
1437 case GDK_SOURCE_ERASER:
1438 {
1439 // g_message("flip to eraser");
1440 testThumb.set(getPix(PIX_ERASER));
1441 }
1442 break;
1443 // default:
1444 // g_message("gurgle");
1445 }
1446 updateTestButtons(key, hotButton);
1447 lastSourceSeen = source;
1448 lastDevnameSeen = devName;
1449 }
1451 return false;
1452 }
1455 } // end namespace Inkscape
1456 } // end namespace UI
1457 } // end namespace Dialog
1460 /*
1461 Local Variables:
1462 mode:c++
1463 c-file-style:"stroustrup"
1464 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1465 indent-tabs-mode:nil
1466 fill-column:99
1467 End:
1468 */
1469 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :