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(Glib::RefPtr<InputDevice const> device);
458 void updateDeviceAxes(Glib::RefPtr<InputDevice const> device);
459 void updateDeviceButtons(Glib::RefPtr<InputDevice const> device);
460 static void updateDeviceLinks(Glib::RefPtr<InputDevice const> device, Gtk::TreeIter tabletIter, Glib::RefPtr<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 Glib::RefPtr<Gtk::TreeView> treePtr(&tree);
717 Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, treePtr));
719 tree.expand_all();
720 show_all_children();
721 }
723 void InputDialogImpl::setupTree( Glib::RefPtr<Gtk::TreeStore> store, Gtk::TreeIter &tablet )
724 {
725 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
726 if ( !devList.empty() ) {
727 Gtk::TreeModel::Row row = *(store->append());
728 row[getCols().description] = _("Hardware");
730 tablet = store->append(row.children());
731 Gtk::TreeModel::Row childrow = *tablet;
732 childrow[getCols().description] = _("Tablet");
733 childrow[getCols().thumbnail] = getPix(PIX_TABLET);
735 for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it = devList.begin(); it != devList.end(); ++it ) {
736 Glib::RefPtr<InputDevice const> dev = *it;
737 if ( dev ) {
738 // 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(),
739 // dev->hasCursor() ? "Yes":"no", dev->getNumAxes(), dev->getNumKeys());
741 // if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
742 if ( dev ) {
743 Gtk::TreeModel::Row deviceRow = *(store->append(childrow.children()));
744 deviceRow[getCols().description] = dev->getName();
745 deviceRow[getCols().device] = dev;
746 deviceRow[getCols().mode] = dev->getMode();
747 switch ( dev->getSource() ) {
748 case GDK_SOURCE_MOUSE:
749 deviceRow[getCols().thumbnail] = getPix(PIX_CORE);
750 break;
751 case GDK_SOURCE_PEN:
752 if (deviceRow[getCols().description] == _("pad")) {
753 deviceRow[getCols().thumbnail] = getPix(PIX_SIDEBUTTONS);
754 } else {
755 deviceRow[getCols().thumbnail] = getPix(PIX_TIP);
756 }
757 break;
758 case GDK_SOURCE_CURSOR:
759 deviceRow[getCols().thumbnail] = getPix(PIX_MOUSE);
760 break;
761 case GDK_SOURCE_ERASER:
762 deviceRow[getCols().thumbnail] = getPix(PIX_ERASER);
763 break;
764 default:
765 ; // nothing
766 }
767 }
768 } else {
769 g_warning("Null device in list");
770 }
771 }
772 } else {
773 g_warning("No devices found");
774 }
775 }
778 InputDialogImpl::ConfPanel::ConfPanel() :
779 Gtk::VBox(),
780 store(Gtk::TreeStore::create(getCols())),
781 tabletIter(),
782 tree(store),
783 treeScroller(),
784 watcher(*this),
785 useExt(_("Use pressure-sensitive tablet (requires restart)")),
786 save(_("Save"))
787 {
788 pack_start(treeScroller);
790 treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
791 treeScroller.add(tree);
793 class Foo : public Gtk::TreeModel::ColumnRecord {
794 public :
795 Gtk::TreeModelColumn<Glib::ustring> one;
796 Foo() {add(one);}
797 };
798 static Foo foo;
799 Glib::RefPtr<Gtk::ListStore> poppers = Gtk::ListStore::create(foo);
800 poppers->reference();
802 Gtk::TreeModel::Row row = *(poppers->append());
803 row[foo.one] = getModeToString()[Gdk::MODE_DISABLED];
804 row = *(poppers->append());
805 row[foo.one] = getModeToString()[Gdk::MODE_SCREEN];
806 row = *(poppers->append());
807 row[foo.one] = getModeToString()[Gdk::MODE_WINDOW];
809 Gtk::CellRendererCombo *rendr = new Gtk::CellRendererCombo();
810 rendr->property_model().set_value(poppers);
811 rendr->property_text_column().set_value(0);
812 rendr->property_has_entry() = false;
814 //Add the TreeView's view columns:
815 tree.append_column("I", getCols().thumbnail);
816 tree.append_column("Bar", getCols().description);
817 Gtk::TreeViewColumn *col = new Gtk::TreeViewColumn("X", *rendr);
818 if (col) {
819 tree.append_column(*col);
820 col->set_cell_data_func(*rendr, sigc::ptr_fun(setModeCellString));
821 rendr->signal_edited().connect(sigc::bind(sigc::ptr_fun(commitCellModeChange), store));
822 rendr->property_editable() = true;
823 }
825 tree.set_enable_tree_lines();
826 tree.set_headers_visible(false);
828 setupTree( store, tabletIter );
830 Glib::RefPtr<Gtk::TreeView> treePtr(&tree);
831 Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, treePtr));
833 tree.expand_all();
835 useExt.set_active(Preferences::get()->getBool("/options/useextinput/value"));
836 useExt.signal_toggled().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::useExtToggled));
837 pack_start(useExt, Gtk::PACK_SHRINK);
839 save.signal_clicked().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::saveSettings));
840 Gtk::Alignment *align = new Gtk::Alignment(Gtk::ALIGN_RIGHT, Gtk::ALIGN_TOP, 0, 0);
841 align->add(save);
842 pack_start(*Gtk::manage(align), Gtk::PACK_SHRINK);
843 }
845 InputDialogImpl::ConfPanel::~ConfPanel()
846 {
847 }
849 void InputDialogImpl::ConfPanel::setModeCellString(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter)
850 {
851 if (iter) {
852 Gtk::CellRendererCombo *combo = dynamic_cast<Gtk::CellRendererCombo *>(rndr);
853 if (combo) {
854 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
855 Gdk::InputMode mode = (*iter)[getCols().mode];
856 if (dev && (getModeToString().find(mode) != getModeToString().end())) {
857 combo->property_text() = getModeToString()[mode];
858 } else {
859 combo->property_text() = "";
860 }
861 }
862 }
863 }
865 void InputDialogImpl::ConfPanel::commitCellModeChange(Glib::ustring const &path, Glib::ustring const &newText, Glib::RefPtr<Gtk::TreeStore> store)
866 {
867 Gtk::TreeIter iter = store->get_iter(path);
868 if (iter) {
869 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
870 if (dev && (getStringToMode().find(newText) != getStringToMode().end())) {
871 Gdk::InputMode mode = getStringToMode()[newText];
872 Inkscape::DeviceManager::getManager().setMode( dev->getId(), mode );
873 }
874 }
875 }
877 void InputDialogImpl::ConfPanel::saveSettings()
878 {
879 Inkscape::DeviceManager::getManager().saveConfig();
880 }
882 void InputDialogImpl::ConfPanel::useExtToggled()
883 {
884 bool active = useExt.get_active();
885 if (active != Preferences::get()->getBool("/options/useextinput/value")) {
886 Preferences::get()->setBool("/options/useextinput/value", active);
887 if (active) {
888 // As a work-around for a common problem, enable tablet toggles on the calligraphic tool.
889 // Covered in Launchpad bug #196195.
890 Preferences::get()->setBool("/tools/tweak/usepressure", true);
891 Preferences::get()->setBool("/tools/calligraphic/usepressure", true);
892 Preferences::get()->setBool("/tools/calligraphic/usetilt", true);
893 }
894 }
895 }
897 InputDialogImpl::ConfPanel::Blink::Blink(ConfPanel &parent) :
898 Preferences::Observer("/options/useextinput/value"),
899 parent(parent)
900 {
901 Preferences::get()->addObserver(*this);
902 }
904 InputDialogImpl::ConfPanel::Blink::~Blink()
905 {
906 Preferences::get()->removeObserver(*this);
907 }
909 void InputDialogImpl::ConfPanel::Blink::notify(Preferences::Entry const &new_val)
910 {
911 parent.useExt.set_active(new_val.getBool());
912 }
914 void InputDialogImpl::handleDeviceChange(Glib::RefPtr<InputDevice const> device)
915 {
916 // g_message("OUCH!!!! for %p hits %s", &device, device->getId().c_str());
917 std::vector<Glib::RefPtr<Gtk::TreeStore> > stores;
918 stores.push_back(store);
919 stores.push_back(cfgPanel.store);
921 for (std::vector<Glib::RefPtr<Gtk::TreeStore> >::iterator it = stores.begin(); it != stores.end(); ++it) {
922 Gtk::TreeModel::iterator deviceIter;
923 (*it)->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
924 sigc::ptr_fun(&InputDialogImpl::findDevice),
925 device->getId(),
926 &deviceIter) );
927 if ( deviceIter ) {
928 Gdk::InputMode mode = device->getMode();
929 Gtk::TreeModel::Row row = *deviceIter;
930 if (row[getCols().mode] != mode) {
931 row[getCols().mode] = mode;
932 }
933 }
934 }
935 }
937 void InputDialogImpl::updateDeviceAxes(Glib::RefPtr<InputDevice const> device)
938 {
939 gint live = device->getLiveAxes();
941 std::map<guint, std::pair<guint, gdouble> > existing = axesMap[device->getId()];
942 gint mask = 0x1;
943 for ( gint num = 0; num < 32; num++, mask <<= 1) {
944 if ( (mask & live) != 0 ) {
945 if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) {
946 axesMap[device->getId()][num].first = 2;
947 axesMap[device->getId()][num].second = 0.0;
948 }
949 }
950 }
951 updateTestAxes( device->getId(), 0 );
952 }
954 void InputDialogImpl::updateDeviceButtons(Glib::RefPtr<InputDevice const> device)
955 {
956 gint live = device->getLiveButtons();
957 std::set<guint> existing = buttonMap[device->getId()];
958 gint mask = 0x1;
959 for ( gint num = 0; num < 32; num++, mask <<= 1) {
960 if ( (mask & live) != 0 ) {
961 if ( existing.find(num) == existing.end() ) {
962 buttonMap[device->getId()].insert(num);
963 }
964 }
965 }
966 updateTestButtons(device->getId(), -1);
967 }
970 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
971 Glib::ustring id,
972 Gtk::TreeModel::iterator* result)
973 {
974 bool stop = false;
975 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
976 if ( dev && (dev->getId() == id) ) {
977 if ( result ) {
978 *result = iter;
979 }
980 stop = true;
981 }
982 return stop;
983 }
985 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
986 Glib::ustring link,
987 Gtk::TreeModel::iterator* result)
988 {
989 bool stop = false;
990 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
991 if ( dev && (dev->getLink() == link) ) {
992 if ( result ) {
993 *result = iter;
994 }
995 stop = true;
996 }
997 return stop;
998 }
1000 void InputDialogImpl::updateDeviceLinks(Glib::RefPtr<InputDevice const> device, Gtk::TreeIter tabletIter, Glib::RefPtr<Gtk::TreeView> tree)
1001 {
1002 Glib::RefPtr<Gtk::TreeStore> store = Glib::RefPtr<Gtk::TreeStore>::cast_dynamic(tree->get_model());
1004 // g_message("Links!!!! for %p hits [%s] with link of [%s]", &device, device->getId().c_str(), device->getLink().c_str());
1005 Gtk::TreeModel::iterator deviceIter;
1006 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1007 sigc::ptr_fun(&InputDialogImpl::findDevice),
1008 device->getId(),
1009 &deviceIter) );
1011 if ( deviceIter ) {
1012 // Found the device concerned. Can proceed.
1014 if ( device->getLink().empty() ) {
1015 // is now unlinked
1016 // g_message("Item %s is unlinked", device->getId().c_str());
1017 if ( deviceIter->parent() != tabletIter ) {
1018 // Not the child of the tablet. move on up
1020 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1021 Glib::ustring descr = (*deviceIter)[getCols().description];
1022 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1024 Gtk::TreeModel::Row deviceRow = *store->append(tabletIter->children());
1025 deviceRow[getCols().description] = descr;
1026 deviceRow[getCols().thumbnail] = thumb;
1027 deviceRow[getCols().device] = dev;
1028 deviceRow[getCols().mode] = dev->getMode();
1030 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1031 store->erase(deviceIter);
1032 if ( oldParent->children().empty() ) {
1033 store->erase(oldParent);
1034 }
1035 }
1036 } else {
1037 // is linking
1038 if ( deviceIter->parent() == tabletIter ) {
1039 // Simple case. Not already linked
1041 Gtk::TreeIter newGroup = store->append(tabletIter->children());
1042 (*newGroup)[getCols().description] = _("Pen");
1043 (*newGroup)[getCols().thumbnail] = getPix(PIX_PEN);
1045 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1046 Glib::ustring descr = (*deviceIter)[getCols().description];
1047 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1049 Gtk::TreeModel::Row deviceRow = *store->append(newGroup->children());
1050 deviceRow[getCols().description] = descr;
1051 deviceRow[getCols().thumbnail] = thumb;
1052 deviceRow[getCols().device] = dev;
1053 deviceRow[getCols().mode] = dev->getMode();
1056 Gtk::TreeModel::iterator linkIter;
1057 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1058 sigc::ptr_fun(&InputDialogImpl::findDeviceByLink),
1059 device->getId(),
1060 &linkIter) );
1061 if ( linkIter ) {
1062 dev = (*linkIter)[getCols().device];
1063 descr = (*linkIter)[getCols().description];
1064 thumb = (*linkIter)[getCols().thumbnail];
1066 deviceRow = *store->append(newGroup->children());
1067 deviceRow[getCols().description] = descr;
1068 deviceRow[getCols().thumbnail] = thumb;
1069 deviceRow[getCols().device] = dev;
1070 deviceRow[getCols().mode] = dev->getMode();
1071 Gtk::TreeModel::iterator oldParent = linkIter->parent();
1072 store->erase(linkIter);
1073 if ( oldParent->children().empty() ) {
1074 store->erase(oldParent);
1075 }
1076 }
1078 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1079 store->erase(deviceIter);
1080 if ( oldParent->children().empty() ) {
1081 store->erase(oldParent);
1082 }
1083 tree->expand_row(Gtk::TreePath(newGroup), true);
1084 }
1085 }
1086 }
1087 }
1089 void InputDialogImpl::linkComboChanged() {
1090 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1091 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1092 if (iter) {
1093 Gtk::TreeModel::Row row = *iter;
1094 Glib::ustring val = row[getCols().description];
1095 Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1096 if ( dev ) {
1097 if ( linkCombo.get_active_row_number() == 0 ) {
1098 // It is the "None" entry
1099 DeviceManager::getManager().setLinkedTo(dev->getId(), "");
1100 } else {
1101 Glib::ustring linkName = linkCombo.get_active_text();
1102 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1103 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1104 if ( linkName == (*it)->getName() ) {
1105 DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId());
1106 break;
1107 }
1108 }
1109 }
1110 }
1111 }
1112 }
1114 void InputDialogImpl::resyncToSelection() {
1115 bool clear = true;
1116 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1117 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1118 if (iter) {
1119 Gtk::TreeModel::Row row = *iter;
1120 Glib::ustring val = row[getCols().description];
1121 Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1122 if ( dev ) {
1123 devDetails.set_sensitive(true);
1125 linkConnection.block();
1126 linkCombo.clear_items();
1127 linkCombo.append_text(_("None"));
1128 linkCombo.set_active(0);
1129 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
1130 Glib::ustring linked = dev->getLink();
1131 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1132 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1133 if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) {
1134 linkCombo.append_text((*it)->getName().c_str());
1135 if ( (linked.length() > 0) && (linked == (*it)->getId()) ) {
1136 linkCombo.set_active_text((*it)->getName().c_str());
1137 }
1138 }
1139 }
1140 linkCombo.set_sensitive(true);
1141 } else {
1142 linkCombo.set_sensitive(false);
1143 }
1144 linkConnection.unblock();
1146 clear = false;
1147 devName.set_label(row[getCols().description]);
1148 setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo);
1149 setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo);
1150 }
1151 }
1153 devDetails.set_sensitive(!clear);
1154 if (clear) {
1155 devName.set_label("");
1156 devAxesCount.set_label("");
1157 devKeyCount.set_label("");
1158 }
1159 }
1161 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
1162 {
1163 gchar *tmp = g_strdup_printf("%d", reported);
1164 label.set_label(tmp);
1165 g_free(tmp);
1167 combo.clear_items();
1168 for ( gint i = 1; i <= reported; ++i ) {
1169 tmp = g_strdup_printf("%d", i);
1170 combo.append_text(tmp);
1171 g_free(tmp);
1172 }
1174 if ( (1 <= actual) && (actual <= reported) ) {
1175 combo.set_active(actual - 1);
1176 }
1177 }
1179 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
1180 {
1181 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testButtons)); i++ ) {
1182 if ( buttonMap[key].find(i) != buttonMap[key].end() ) {
1183 if ( i == hotButton ) {
1184 testButtons[i].set(getPix(PIX_BUTTONS_ON));
1185 } else {
1186 testButtons[i].set(getPix(PIX_BUTTONS_OFF));
1187 }
1188 } else {
1189 testButtons[i].set(getPix(PIX_BUTTONS_NONE));
1190 }
1191 }
1192 }
1194 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
1195 {
1196 static gdouble epsilon = 0.0001;
1197 {
1198 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1199 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1200 if (iter) {
1201 Gtk::TreeModel::Row row = *iter;
1202 Glib::ustring val = row[getCols().description];
1203 Glib::RefPtr<InputDevice const> idev = row[getCols().device];
1204 if ( !idev || (idev->getId() != key) ) {
1205 dev = 0;
1206 }
1207 }
1208 }
1211 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testAxes)); i++ ) {
1212 if ( axesMap[key].find(i) != axesMap[key].end() ) {
1213 switch ( axesMap[key][i].first ) {
1214 case 0:
1215 case 1:
1216 testAxes[i].set(getPix(PIX_AXIS_NONE));
1217 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1218 axesValues[i].set_sensitive(false);
1219 }
1220 break;
1221 case 2:
1222 testAxes[i].set(getPix(PIX_AXIS_OFF));
1223 axesValues[i].set_sensitive(true);
1224 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1225 if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1226 axesValues[i].set_sensitive(true);
1227 axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1228 }
1229 gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1230 axesValues[i].set_text(str);
1231 g_free(str);
1232 }
1233 break;
1234 case 3:
1235 testAxes[i].set(getPix(PIX_AXIS_ON));
1236 axesValues[i].set_sensitive(true);
1237 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1238 if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1239 axesValues[i].set_sensitive(true);
1240 axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1241 }
1242 gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1243 axesValues[i].set_text(str);
1244 g_free(str);
1245 }
1246 }
1248 } else {
1249 testAxes[i].set(getPix(PIX_AXIS_NONE));
1250 }
1251 }
1252 if ( !dev ) {
1253 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(axesValues)); i++ ) {
1254 axesValues[i].set_fraction(0.0);
1255 axesValues[i].set_text("");
1256 axesValues[i].set_sensitive(false);
1257 }
1258 }
1259 }
1261 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1262 {
1263 static gdouble epsilon = 0.0001;
1264 if ( (numAxes > 0) && axes) {
1265 for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) {
1266 // 0 == new, 1 == set value, 2 == changed value, 3 == active
1267 gdouble diff = axesMap[key][axisNum].second - axes[axisNum];
1268 switch(axesMap[key][axisNum].first) {
1269 case 0:
1270 {
1271 axesMap[key][axisNum].first = 1;
1272 axesMap[key][axisNum].second = axes[axisNum];
1273 }
1274 break;
1275 case 1:
1276 {
1277 if ( (diff > epsilon) || (diff < -epsilon) ) {
1278 // g_message("Axis %d changed on %s]", axisNum, key.c_str());
1279 axesMap[key][axisNum].first = 3;
1280 axesMap[key][axisNum].second = axes[axisNum];
1281 updateTestAxes(key, dev);
1282 DeviceManager::getManager().addAxis(key, axisNum);
1283 }
1284 }
1285 break;
1286 case 2:
1287 {
1288 if ( (diff > epsilon) || (diff < -epsilon) ) {
1289 axesMap[key][axisNum].first = 3;
1290 axesMap[key][axisNum].second = axes[axisNum];
1291 updateTestAxes(key, dev);
1292 }
1293 }
1294 break;
1295 case 3:
1296 {
1297 if ( (diff > epsilon) || (diff < -epsilon) ) {
1298 axesMap[key][axisNum].second = axes[axisNum];
1299 } else {
1300 axesMap[key][axisNum].first = 2;
1301 updateTestAxes(key, dev);
1302 }
1303 }
1304 }
1305 }
1306 }
1307 // std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
1308 }
1310 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1311 {
1312 Glib::ustring key;
1313 switch ( device->source ) {
1314 case GDK_SOURCE_MOUSE:
1315 key = "M:";
1316 break;
1317 case GDK_SOURCE_CURSOR:
1318 key = "C:";
1319 break;
1320 case GDK_SOURCE_PEN:
1321 key = "P:";
1322 break;
1323 case GDK_SOURCE_ERASER:
1324 key = "E:";
1325 break;
1326 default:
1327 key = "?:";
1328 }
1329 key += device->name;
1331 return key;
1332 }
1334 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1335 {
1336 int modmod = 0;
1338 GdkInputSource source = lastSourceSeen;
1339 Glib::ustring devName = lastDevnameSeen;
1340 Glib::ustring key;
1341 gint hotButton = -1;
1343 switch ( event->type ) {
1344 case GDK_KEY_PRESS:
1345 case GDK_KEY_RELEASE:
1346 {
1347 GdkEventKey* keyEvt = reinterpret_cast<GdkEventKey*>(event);
1348 gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast<GdkModifierType>(keyEvt->state));
1349 keyVal.set_label(name);
1350 // g_message("%d KEY state:0x%08x 0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name);
1351 g_free(name);
1352 }
1353 break;
1354 case GDK_BUTTON_PRESS:
1355 modmod = 1;
1356 // fallthrough
1357 case GDK_BUTTON_RELEASE:
1358 {
1359 GdkEventButton* btnEvt = reinterpret_cast<GdkEventButton*>(event);
1360 if ( btnEvt->device ) {
1361 key = getKeyFor(btnEvt->device);
1362 source = btnEvt->device->source;
1363 devName = btnEvt->device->name;
1365 mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device);
1366 if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) {
1367 // g_message("New button found for %s = %d", key.c_str(), btnEvt->button);
1368 buttonMap[key].insert(btnEvt->button);
1369 DeviceManager::getManager().addButton(key, btnEvt->button);
1370 }
1371 hotButton = modmod ? btnEvt->button : -1;
1372 updateTestButtons(key, hotButton);
1373 }
1374 gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnEvt->state));
1375 keyVal.set_label(name);
1376 // g_message("%d BTN state:0x%08x %c %4d [%s] dev:%p [%s] ",
1377 // btnEvt->type, btnEvt->state,
1378 // (modmod ? '+':'-'),
1379 // btnEvt->button, name, btnEvt->device,
1380 // (btnEvt->device ? btnEvt->device->name : "null")
1382 // );
1383 g_free(name);
1384 }
1385 break;
1386 case GDK_MOTION_NOTIFY:
1387 {
1388 GdkEventMotion* btnMtn = reinterpret_cast<GdkEventMotion*>(event);
1389 if ( btnMtn->device ) {
1390 key = getKeyFor(btnMtn->device);
1391 source = btnMtn->device->source;
1392 devName = btnMtn->device->name;
1393 mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device);
1394 }
1395 gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnMtn->state));
1396 keyVal.set_label(name);
1397 // 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,
1398 // name, btnMtn->device,
1399 // (btnMtn->device ? btnMtn->device->name : "null"),
1400 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0),
1401 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0),
1402 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0),
1403 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0),
1404 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0),
1405 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0)
1406 // );
1407 g_free(name);
1408 }
1409 break;
1410 default:
1411 ;// nothing
1412 }
1415 if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) {
1416 switch (source) {
1417 case GDK_SOURCE_MOUSE:
1418 {
1419 testThumb.set(getPix(PIX_CORE));
1420 }
1421 break;
1422 case GDK_SOURCE_CURSOR:
1423 {
1424 // g_message("flip to cursor");
1425 testThumb.set(getPix(PIX_MOUSE));
1426 }
1427 break;
1428 case GDK_SOURCE_PEN:
1429 {
1430 if (devName == _("pad")) {
1431 // g_message("flip to pad");
1432 testThumb.set(getPix(PIX_SIDEBUTTONS));
1433 } else {
1434 // g_message("flip to pen");
1435 testThumb.set(getPix(PIX_TIP));
1436 }
1437 }
1438 break;
1439 case GDK_SOURCE_ERASER:
1440 {
1441 // g_message("flip to eraser");
1442 testThumb.set(getPix(PIX_ERASER));
1443 }
1444 break;
1445 // default:
1446 // g_message("gurgle");
1447 }
1448 updateTestButtons(key, hotButton);
1449 lastSourceSeen = source;
1450 lastDevnameSeen = devName;
1451 }
1453 return false;
1454 }
1457 } // end namespace Inkscape
1458 } // end namespace UI
1459 } // end namespace Dialog
1462 /*
1463 Local Variables:
1464 mode:c++
1465 c-file-style:"stroustrup"
1466 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1467 indent-tabs-mode:nil
1468 fill-column:99
1469 End:
1470 */
1471 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :