8f19c90c4cb12543afc9dc0043598fbe2128e345
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 <list>
14 #include <glib/gprintf.h>
15 #include <glibmm/i18n.h>
16 #include <gtkmm/alignment.h>
17 #include <gtkmm/cellrenderercombo.h>
18 #include <gtkmm/checkbutton.h>
19 #include <gtkmm/comboboxtext.h>
20 #include <gtkmm/enums.h>
21 #include <gtkmm/eventbox.h>
22 #include <gtkmm/frame.h>
23 #include <gtkmm/image.h>
24 #include <gtkmm/liststore.h>
25 #include <gtkmm/menubar.h>
26 #include <gtkmm/notebook.h>
27 #include <gtkmm/paned.h>
28 #include <gtkmm/progressbar.h>
29 #include <gtkmm/scrolledwindow.h>
30 #include <gtkmm/table.h>
31 #include <gtkmm/treemodel.h>
32 #include <gtkmm/treemodelcolumn.h>
33 #include <gtkmm/treestore.h>
34 #include <gtkmm/treeview.h>
36 #include "device-manager.h"
37 #include "preferences.h"
38 #include "ui/widget/panel.h"
40 #include "input.h"
42 /* XPM */
43 static char const * core_xpm[] = {
44 "16 16 4 1",
45 " c None",
46 ". c #808080",
47 "+ c #000000",
48 "@ c #FFFFFF",
49 " ",
50 " ",
51 " ",
52 " .++++++. ",
53 " +@+@@+@+ ",
54 " +@+@@+@+ ",
55 " +.+..+.+ ",
56 " +@@@@@@+ ",
57 " +@@@@@@+ ",
58 " +@@@@@@+ ",
59 " +@@@@@@+ ",
60 " +@@@@@@+ ",
61 " .++++++. ",
62 " ",
63 " ",
64 " "};
66 /* XPM */
67 static char const *eraser[] = {
68 /* columns rows colors chars-per-pixel */
69 "16 16 5 1",
70 " c black",
71 ". c green",
72 "X c #808080",
73 "o c gray100",
74 "O c None",
75 /* pixels */
76 "OOOOOOOOOOOOOOOO",
77 "OOOOOOOOOOOOO OO",
78 "OOOOOOOOOOOO . O",
79 "OOOOOOOOOOO . OO",
80 "OOOOOOOOOO . OOO",
81 "OOOOOOOOO . OOOO",
82 "OOOOOOOO . OOOOO",
83 "OOOOOOOXo OOOOOO",
84 "OOOOOOXoXOOOOOOO",
85 "OOOOOXoXOOOOOOOO",
86 "OOOOXoXOOOOOOOOO",
87 "OOOXoXOOOOOOOOOO",
88 "OOXoXOOOOOOOOOOO",
89 "OOXXOOOOOOOOOOOO",
90 "OOOOOOOOOOOOOOOO",
91 "OOOOOOOOOOOOOOOO"
92 };
94 /* XPM */
95 static char const *mouse[] = {
96 /* columns rows colors chars-per-pixel */
97 "16 16 3 1",
98 " c black",
99 ". c gray100",
100 "X c None",
101 /* pixels */
102 "XXXXXXXXXXXXXXXX",
103 "XXXXXXXXXXXXXXXX",
104 "XXXXXXXXXXXXXXXX",
105 "XXXXXXXXXXXXXXXX",
106 "XXXXXXX XXXXXXX",
107 "XXXXX . XXXXXXX",
108 "XXXX .... XXXXXX",
109 "XXXX .... XXXXXX",
110 "XXXXX .... XXXXX",
111 "XXXXX .... XXXXX",
112 "XXXXXX .... XXXX",
113 "XXXXXX .... XXXX",
114 "XXXXXXX . XXXXX",
115 "XXXXXXX XXXXXXX",
116 "XXXXXXXXXXXXXXXX",
117 "XXXXXXXXXXXXXXXX"
118 };
120 /* XPM */
121 static char const *pen[] = {
122 /* columns rows colors chars-per-pixel */
123 "16 16 3 1",
124 " c black",
125 ". c gray100",
126 "X c None",
127 /* pixels */
128 "XXXXXXXXXXXXXXXX",
129 "XXXXXXXXXXXXX XX",
130 "XXXXXXXXXXXX . X",
131 "XXXXXXXXXXX . XX",
132 "XXXXXXXXXX . XXX",
133 "XXXXXXXXX . XXXX",
134 "XXXXXXXX . XXXXX",
135 "XXXXXXX . XXXXXX",
136 "XXXXXX . XXXXXXX",
137 "XXXXX . XXXXXXXX",
138 "XXXX . XXXXXXXXX",
139 "XXX . XXXXXXXXXX",
140 "XX . XXXXXXXXXXX",
141 "XX XXXXXXXXXXXX",
142 "XXXXXXXXXXXXXXXX",
143 "XXXXXXXXXXXXXXXX"
144 };
146 /* XPM */
147 static char const *sidebuttons[] = {
148 /* columns rows colors chars-per-pixel */
149 "16 16 4 1",
150 " c black",
151 ". c #808080",
152 "o c green",
153 "O c None",
154 /* pixels */
155 "OOOOOOOOOOOOOOOO",
156 "OOOOOOOOOOOOOOOO",
157 "O..............O",
158 "O.OOOOOOOOOOOO.O",
159 "O OOOOOOOO O",
160 "O o OOOOOOOO o O",
161 "O o OOOOOOOO o O",
162 "O OOOOOOOO O",
163 "O.OOOOOOOOOOOO.O",
164 "O.OOOOOOOOOOOO.O",
165 "O.OOOOOOOOOOOO.O",
166 "O.OOOOOOOOOOOO.O",
167 "O.OOOOOOOOOOOO.O",
168 "O..............O",
169 "OOOOOOOOOOOOOOOO",
170 "OOOOOOOOOOOOOOOO"
171 };
173 /* XPM */
174 static char const *tablet[] = {
175 /* columns rows colors chars-per-pixel */
176 "16 16 3 1",
177 " c black",
178 ". c gray100",
179 "X c None",
180 /* pixels */
181 "XXXXXXXXXXXXXXXX",
182 "XXXXXXXXXXXXXXXX",
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 "X X",
195 "XXXXXXXXXXXXXXXX",
196 "XXXXXXXXXXXXXXXX"
197 };
199 /* XPM */
200 static char const *tip[] = {
201 /* columns rows colors chars-per-pixel */
202 "16 16 5 1",
203 " c black",
204 ". c green",
205 "X c #808080",
206 "o c gray100",
207 "O c None",
208 /* pixels */
209 "OOOOOOOOOOOOOOOO",
210 "OOOOOOOOOOOOOXOO",
211 "OOOOOOOOOOOOXoXO",
212 "OOOOOOOOOOOXoXOO",
213 "OOOOOOOOOOXoXOOO",
214 "OOOOOOOOOXoXOOOO",
215 "OOOOOOOOXoXOOOOO",
216 "OOOOOOO oXOOOOOO",
217 "OOOOOO . OOOOOOO",
218 "OOOOO . OOOOOOOO",
219 "OOOO . OOOOOOOOO",
220 "OOO . OOOOOOOOOO",
221 "OO . OOOOOOOOOOO",
222 "OO OOOOOOOOOOOO",
223 "OOOOXXXXXOOOOOOO",
224 "OOOOOOOOOXXXXXOO"
225 };
227 /* XPM */
228 static char const *button_none[] = {
229 /* columns rows colors chars-per-pixel */
230 "8 8 3 1",
231 " c black",
232 ". c #808080",
233 "X c None",
234 /* pixels */
235 "XXXXXXXX",
236 "XX .. XX",
237 "X .XX. X",
238 "X.XX X.X",
239 "X.X XX.X",
240 "X .XX. X",
241 "XX .. XX",
242 "XXXXXXXX"
243 };
244 /* XPM */
245 static char const *button_off[] = {
246 /* columns rows colors chars-per-pixel */
247 "8 8 4 1",
248 " c black",
249 ". c #808080",
250 "X c gray100",
251 "o c None",
252 /* pixels */
253 "oooooooo",
254 "oo. .oo",
255 "o. XX .o",
256 "o XXXX o",
257 "o XXXX o",
258 "o. XX .o",
259 "oo. .oo",
260 "oooooooo"
261 };
262 /* XPM */
263 static char const *button_on[] = {
264 /* columns rows colors chars-per-pixel */
265 "8 8 3 1",
266 " c black",
267 ". c green",
268 "X c None",
269 /* pixels */
270 "XXXXXXXX",
271 "XX XX",
272 "X .. X",
273 "X .... X",
274 "X .... X",
275 "X .. X",
276 "XX XX",
277 "XXXXXXXX"
278 };
280 /* XPM */
281 static char const * axis_none_xpm[] = {
282 "24 8 3 1",
283 " c None",
284 ". c #000000",
285 "+ c #808080",
286 " ",
287 " .++++++++++++++++++. ",
288 " .+ . .+. ",
289 " + . . . + ",
290 " + . . . + ",
291 " .+. . +. ",
292 " .++++++++++++++++++. ",
293 " "};
294 /* XPM */
295 static char const * axis_off_xpm[] = {
296 "24 8 4 1",
297 " c None",
298 ". c #808080",
299 "+ c #000000",
300 "@ c #FFFFFF",
301 " ",
302 " .++++++++++++++++++. ",
303 " .+@@@@@@@@@@@@@@@@@@+. ",
304 " +@@@@@@@@@@@@@@@@@@@@+ ",
305 " +@@@@@@@@@@@@@@@@@@@@+ ",
306 " .+@@@@@@@@@@@@@@@@@@+. ",
307 " .++++++++++++++++++. ",
308 " "};
309 /* XPM */
310 static char const * axis_on_xpm[] = {
311 "24 8 3 1",
312 " c None",
313 ". c #000000",
314 "+ c #00FF00",
315 " ",
316 " .................... ",
317 " ..++++++++++++++++++.. ",
318 " .++++++++++++++++++++. ",
319 " .++++++++++++++++++++. ",
320 " ..++++++++++++++++++.. ",
321 " .................... ",
322 " "};
324 using Inkscape::InputDevice;
326 namespace Inkscape {
327 namespace UI {
328 namespace Dialog {
332 class DeviceModelColumns : public Gtk::TreeModel::ColumnRecord
333 {
334 public:
335 Gtk::TreeModelColumn<bool> toggler;
336 Gtk::TreeModelColumn<Glib::ustring> expander;
337 Gtk::TreeModelColumn<Glib::ustring> description;
338 Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > thumbnail;
339 Gtk::TreeModelColumn<Glib::RefPtr<InputDevice const> > device;
340 Gtk::TreeModelColumn<Gdk::InputMode> mode;
342 DeviceModelColumns() { add(toggler), add(expander), add(description); add(thumbnail); add(device); add(mode); }
343 };
345 static std::map<Gdk::InputMode, Glib::ustring> &getModeToString()
346 {
347 static std::map<Gdk::InputMode, Glib::ustring> mapping;
348 if (mapping.empty()) {
349 mapping[Gdk::MODE_DISABLED] = _("Disabled");
350 mapping[Gdk::MODE_SCREEN] = _("Screen");
351 mapping[Gdk::MODE_WINDOW] = _("Window");
352 }
354 return mapping;
355 }
357 static std::map<Glib::ustring, Gdk::InputMode> &getStringToMode()
358 {
359 static std::map<Glib::ustring, Gdk::InputMode> mapping;
360 if (mapping.empty()) {
361 mapping[_("Disabled")] = Gdk::MODE_DISABLED;
362 mapping[_("Screen")] = Gdk::MODE_SCREEN;
363 mapping[_("Window")] = Gdk::MODE_WINDOW;
364 }
366 return mapping;
367 }
371 class InputDialogImpl : public InputDialog {
372 public:
373 InputDialogImpl();
374 virtual ~InputDialogImpl() {}
376 private:
377 class ConfPanel : public Gtk::VBox
378 {
379 public:
380 ConfPanel();
381 ~ConfPanel();
383 class Blink : public Preferences::Observer
384 {
385 public:
386 Blink(ConfPanel &parent);
387 virtual ~Blink();
388 virtual void notify(Preferences::Entry const &new_val);
390 ConfPanel &parent;
391 };
393 static void commitCellModeChange(Glib::ustring const &path, Glib::ustring const &newText, Glib::RefPtr<Gtk::TreeStore> store);
394 static void setModeCellString(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter);
396 static void commitCellStateChange(Glib::ustring const &path, Glib::RefPtr<Gtk::TreeStore> store);
397 static void setCellStateToggle(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter);
399 void saveSettings();
400 void useExtToggled();
402 Glib::RefPtr<Gtk::TreeStore> store;
403 Gtk::TreeIter tabletIter;
404 Gtk::TreeView tree;
405 Gtk::ScrolledWindow treeScroller;
406 Blink watcher;
407 Gtk::CheckButton useExt;
408 Gtk::Button save;
409 };
411 static DeviceModelColumns &getCols();
413 enum PixId {PIX_CORE, PIX_PEN, PIX_MOUSE, PIX_TIP, PIX_TABLET, PIX_ERASER, PIX_SIDEBUTTONS,
414 PIX_BUTTONS_NONE, PIX_BUTTONS_ON, PIX_BUTTONS_OFF,
415 PIX_AXIS_NONE, PIX_AXIS_ON, PIX_AXIS_OFF};
417 static Glib::RefPtr<Gdk::Pixbuf> getPix(PixId id);
419 std::map<Glib::ustring, std::set<guint> > buttonMap;
420 std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
422 GdkInputSource lastSourceSeen;
423 Glib::ustring lastDevnameSeen;
425 Glib::RefPtr<Gtk::TreeStore> store;
426 Gtk::TreeIter tabletIter;
427 Gtk::TreeView tree;
428 Gtk::Frame frame2;
429 Gtk::Frame testFrame;
430 Gtk::ScrolledWindow treeScroller;
431 Gtk::ScrolledWindow detailScroller;
432 Gtk::HPaned splitter;
433 Gtk::VPaned split2;
434 Gtk::Label devName;
435 Gtk::Label devKeyCount;
436 Gtk::Label devAxesCount;
437 Gtk::ComboBoxText axesCombo;
438 Gtk::ProgressBar axesValues[6];
439 Gtk::ComboBoxText buttonCombo;
440 Gtk::ComboBoxText linkCombo;
441 sigc::connection linkConnection;
442 Gtk::Label keyVal;
443 Gtk::Entry keyEntry;
444 Gtk::Table devDetails;
445 Gtk::Notebook topHolder;
446 Gtk::Image testThumb;
447 Gtk::Image testButtons[24];
448 Gtk::Image testAxes[8];
449 Gtk::Table imageTable;
450 Gtk::EventBox testDetector;
452 ConfPanel cfgPanel;
454 static void setupTree( Glib::RefPtr<Gtk::TreeStore> store, Gtk::TreeIter &tablet );
455 void setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo );
456 void updateTestButtons( Glib::ustring const& key, gint hotButton );
457 void updateTestAxes( Glib::ustring const& key, GdkDevice* dev );
458 void mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev);
459 Glib::ustring getKeyFor( GdkDevice* device );
460 bool eventSnoop(GdkEvent* event);
461 void linkComboChanged();
462 void resyncToSelection();
463 void handleDeviceChange(Glib::RefPtr<InputDevice const> device);
464 void updateDeviceAxes(Glib::RefPtr<InputDevice const> device);
465 void updateDeviceButtons(Glib::RefPtr<InputDevice const> device);
466 static void updateDeviceLinks(Glib::RefPtr<InputDevice const> device, Gtk::TreeIter tabletIter, Glib::RefPtr<Gtk::TreeView> tree);
468 static bool findDevice(const Gtk::TreeModel::iterator& iter,
469 Glib::ustring id,
470 Gtk::TreeModel::iterator* result);
471 static bool findDeviceByLink(const Gtk::TreeModel::iterator& iter,
472 Glib::ustring link,
473 Gtk::TreeModel::iterator* result);
475 }; // class InputDialogImpl
478 DeviceModelColumns &InputDialogImpl::getCols()
479 {
480 static DeviceModelColumns cols;
481 return cols;
482 }
484 Glib::RefPtr<Gdk::Pixbuf> InputDialogImpl::getPix(PixId id)
485 {
486 static std::map<PixId, Glib::RefPtr<Gdk::Pixbuf> > mappings;
488 mappings[PIX_CORE] = Gdk::Pixbuf::create_from_xpm_data(core_xpm);
489 mappings[PIX_PEN] = Gdk::Pixbuf::create_from_xpm_data(pen);
490 mappings[PIX_MOUSE] = Gdk::Pixbuf::create_from_xpm_data(mouse);
491 mappings[PIX_TIP] = Gdk::Pixbuf::create_from_xpm_data(tip);
492 mappings[PIX_TABLET] = Gdk::Pixbuf::create_from_xpm_data(tablet);
493 mappings[PIX_ERASER] = Gdk::Pixbuf::create_from_xpm_data(eraser);
494 mappings[PIX_SIDEBUTTONS] = Gdk::Pixbuf::create_from_xpm_data(sidebuttons);
496 mappings[PIX_BUTTONS_NONE] = Gdk::Pixbuf::create_from_xpm_data(button_none);
497 mappings[PIX_BUTTONS_ON] = Gdk::Pixbuf::create_from_xpm_data(button_on);
498 mappings[PIX_BUTTONS_OFF] = Gdk::Pixbuf::create_from_xpm_data(button_off);
500 mappings[PIX_AXIS_NONE] = Gdk::Pixbuf::create_from_xpm_data(axis_none_xpm);
501 mappings[PIX_AXIS_ON] = Gdk::Pixbuf::create_from_xpm_data(axis_on_xpm);
502 mappings[PIX_AXIS_OFF] = Gdk::Pixbuf::create_from_xpm_data(axis_off_xpm);
504 Glib::RefPtr<Gdk::Pixbuf> pix;
505 if (mappings.find(id) != mappings.end()) {
506 pix = mappings[id];
507 }
509 return pix;
510 }
513 // Now that we've defined the *Impl class, we can do the method to aquire one.
514 InputDialog &InputDialog::getInstance()
515 {
516 InputDialog *dialog = new InputDialogImpl();
517 return *dialog;
518 }
521 InputDialogImpl::InputDialogImpl() :
522 InputDialog(),
524 lastSourceSeen((GdkInputSource)-1),
525 lastDevnameSeen(""),
526 store(Gtk::TreeStore::create(getCols())),
527 tabletIter(),
528 tree(store),
529 frame2(),
530 testFrame(_("Test Area")),
531 treeScroller(),
532 detailScroller(),
533 splitter(),
534 split2(),
535 linkCombo(),
536 devDetails(12, 2),
537 topHolder(),
538 imageTable(8, 7),
539 testDetector(),
540 cfgPanel()
541 {
542 Gtk::Box *contents = _getContents();
545 treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
546 treeScroller.add(tree);
547 split2.pack1(testFrame);
548 split2.pack2(frame2);
549 splitter.pack1(treeScroller);
550 splitter.pack2(split2);
552 testDetector.add(imageTable);
553 testFrame.add(testDetector);
554 testThumb.set(getPix(PIX_TABLET));
555 testThumb.set_padding(24, 24);
556 imageTable.attach(testThumb, 0, 8, 0, 1, ::Gtk::EXPAND, ::Gtk::EXPAND);
557 {
558 guint col = 0;
559 guint row = 1;
560 for ( guint num = 0; num < G_N_ELEMENTS(testButtons); num++ ) {
561 testButtons[num].set(getPix(PIX_BUTTONS_NONE));
562 imageTable.attach(testButtons[num], col, col + 1, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
563 col++;
564 if (col > 7) {
565 col = 0;
566 row++;
567 }
568 }
570 col = 0;
571 for ( guint num = 0; num < G_N_ELEMENTS(testAxes); num++ ) {
572 testAxes[num].set(getPix(PIX_AXIS_NONE));
573 imageTable.attach(testAxes[num], col * 2, (col + 1) * 2, row, row + 1, ::Gtk::FILL, ::Gtk::FILL);
574 col++;
575 if (col > 3) {
576 col = 0;
577 row++;
578 }
579 }
580 }
583 topHolder.append_page(cfgPanel, _("Configuration"));
584 topHolder.append_page(splitter, _("Hardware"));
585 topHolder.show_all();
586 topHolder.set_current_page(0);
588 contents->pack_start(topHolder);
590 int rowNum = 0;
592 Gtk::Label* lbl = Gtk::manage(new Gtk::Label(_("Name:")));
593 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
594 ::Gtk::FILL,
595 ::Gtk::SHRINK);
596 devDetails.attach(devName, 1, 2, rowNum, rowNum + 1,
597 ::Gtk::SHRINK,
598 ::Gtk::SHRINK);
600 rowNum++;
602 lbl = Gtk::manage(new Gtk::Label(_("Link:")));
603 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
604 ::Gtk::FILL,
605 ::Gtk::SHRINK);
607 linkCombo.append_text(_("None"));
608 linkCombo.set_active_text(_("None"));
609 linkCombo.set_sensitive(false);
610 linkConnection = linkCombo.signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::linkComboChanged));
612 devDetails.attach(linkCombo, 1, 2, rowNum, rowNum + 1,
613 ::Gtk::FILL,
614 ::Gtk::SHRINK);
615 rowNum++;
617 lbl = Gtk::manage(new Gtk::Label(_("Axes count:")));
618 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
619 ::Gtk::FILL,
620 ::Gtk::SHRINK);
621 devDetails.attach(devAxesCount, 1, 2, rowNum, rowNum + 1,
622 ::Gtk::SHRINK,
623 ::Gtk::SHRINK);
625 rowNum++;
627 /*
628 lbl = Gtk::manage(new Gtk::Label(_("Actual axes count:")));
629 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
630 ::Gtk::FILL,
631 ::Gtk::SHRINK);
632 devDetails.attach(axesCombo, 1, 2, rowNum, rowNum + 1,
633 ::Gtk::SHRINK,
634 ::Gtk::SHRINK);
636 rowNum++;
637 */
639 for ( guint barNum = 0; barNum < static_cast<guint>(G_N_ELEMENTS(axesValues)); barNum++ ) {
640 lbl = Gtk::manage(new Gtk::Label(_("axis:")));
641 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
642 ::Gtk::FILL,
643 ::Gtk::SHRINK);
644 devDetails.attach(axesValues[barNum], 1, 2, rowNum, rowNum + 1,
645 ::Gtk::EXPAND,
646 ::Gtk::SHRINK);
647 axesValues[barNum].set_sensitive(false);
649 rowNum++;
650 }
652 lbl = Gtk::manage(new Gtk::Label(_("Button count:")));
653 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
654 ::Gtk::FILL,
655 ::Gtk::SHRINK);
656 devDetails.attach(devKeyCount, 1, 2, rowNum, rowNum + 1,
657 ::Gtk::SHRINK,
658 ::Gtk::SHRINK);
660 rowNum++;
662 /*
663 lbl = Gtk::manage(new Gtk::Label(_("Actual button count:")));
664 devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1,
665 ::Gtk::FILL,
666 ::Gtk::SHRINK);
667 devDetails.attach(buttonCombo, 1, 2, rowNum, rowNum + 1,
668 ::Gtk::SHRINK,
669 ::Gtk::SHRINK);
671 rowNum++;
672 */
674 devDetails.attach(keyVal, 0, 2, rowNum, rowNum + 1,
675 ::Gtk::FILL,
676 ::Gtk::SHRINK);
677 rowNum++;
680 testDetector.signal_event().connect(sigc::mem_fun(*this, &InputDialogImpl::eventSnoop));
682 // void gdk_input_set_extension_events (GdkWindow *window,
683 // gint mask,
684 // GdkExtensionMode mode);
686 gtk_widget_set_extension_events( GTK_WIDGET(testDetector.gobj()), GDK_EXTENSION_EVENTS_ALL );
687 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);
689 devDetails.attach(keyEntry, 0, 2, rowNum, rowNum + 1,
690 ::Gtk::FILL,
691 ::Gtk::SHRINK);
692 rowNum++;
695 devDetails.set_sensitive(false);
696 detailScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
697 detailScroller.add(devDetails);
698 frame2.add(detailScroller);
700 //- 16x16/devices
701 // gnome-dev-mouse-optical
702 // input-mouse
703 // input-tablet
704 // mouse
708 //Add the TreeView's view columns:
709 tree.append_column("I", getCols().thumbnail);
710 tree.append_column("Bar", getCols().description);
712 tree.set_enable_tree_lines();
713 tree.set_headers_visible(false);
714 tree.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::resyncToSelection));
717 setupTree( store, tabletIter );
719 Inkscape::DeviceManager::getManager().signalDeviceChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::handleDeviceChange));
720 Inkscape::DeviceManager::getManager().signalAxesChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceAxes));
721 Inkscape::DeviceManager::getManager().signalButtonsChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceButtons));
722 Glib::RefPtr<Gtk::TreeView> treePtr(&tree);
723 Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, treePtr));
725 tree.expand_all();
726 show_all_children();
727 }
729 class TabletTmp {
730 public:
731 TabletTmp() {}
733 Glib::ustring name;
734 std::list<Glib::RefPtr<InputDevice const> > devices;
735 };
737 static Glib::ustring getCommon( std::list<Glib::ustring> const &names )
738 {
739 Glib::ustring result;
741 if ( !names.empty() ) {
742 size_t pos = 0;
743 bool match = true;
744 while ( match ) {
745 if ( names.begin()->length() > pos ) {
746 gunichar ch = (*names.begin())[pos];
747 for ( std::list<Glib::ustring>::const_iterator it = names.begin(); it != names.end(); ++it ) {
748 if ( (pos >= it->length())
749 || ((*it)[pos] != ch) ) {
750 match = false;
751 break;
752 }
753 }
754 if (match) {
755 result += ch;
756 pos++;
757 }
758 } else {
759 match = false;
760 }
761 }
762 }
764 return result;
765 }
767 void InputDialogImpl::setupTree( Glib::RefPtr<Gtk::TreeStore> store, Gtk::TreeIter &tablet )
768 {
769 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
770 if ( !devList.empty() ) {
771 Gtk::TreeModel::Row row = *(store->append());
772 row[getCols().description] = _("Hardware");
774 // Let's make some tablets!!!
775 std::list<TabletTmp> tablets;
776 std::set<Glib::ustring> consumed;
778 // Phase 1 - figure out which tablets are present
779 for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it = devList.begin(); it != devList.end(); ++it ) {
780 Glib::RefPtr<InputDevice const> dev = *it;
781 if ( dev ) {
782 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
783 consumed.insert( dev->getId() );
784 if ( tablets.empty() ) {
785 TabletTmp tmp;
786 tablets.push_back(tmp);
787 }
788 tablets.back().devices.push_back(dev);
789 }
790 } else {
791 g_warning("Null device in list");
792 }
793 }
795 // Phase 2 - build a UI for the present devices
796 for ( std::list<TabletTmp>::iterator it = tablets.begin(); it != tablets.end(); ++it ) {
797 tablet = store->append(row.children());
798 Gtk::TreeModel::Row childrow = *tablet;
799 if ( it->name.empty() ) {
800 // Check to see if we can derive one
801 std::list<Glib::ustring> names;
802 for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it2 = it->devices.begin(); it2 != it->devices.end(); ++it2 ) {
803 names.push_back( (*it2)->getName() );
804 }
805 Glib::ustring common = getCommon(names);
806 if ( !common.empty() ) {
807 it->name = common;
808 }
809 }
810 childrow[getCols().description] = it->name.empty() ? _("Tablet") : it->name ;
811 childrow[getCols().thumbnail] = getPix(PIX_TABLET);
813 // Check if there is an eraser we can link to a pen
814 for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it2 = it->devices.begin(); it2 != it->devices.end(); ++it2 ) {
815 Glib::RefPtr<InputDevice const> dev = *it2;
816 if ( dev->getSource() == Gdk::SOURCE_PEN ) {
817 for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it3 = it->devices.begin(); it3 != it->devices.end(); ++it3 ) {
818 Glib::RefPtr<InputDevice const> dev2 = *it3;
819 if ( dev2->getSource() == Gdk::SOURCE_ERASER ) {
820 DeviceManager::getManager().setLinkedTo(dev->getId(), dev2->getId());
821 break; // only check the first eraser... for now
822 }
823 break; // only check the first pen... for now
824 }
825 }
826 }
828 for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it2 = it->devices.begin(); it2 != it->devices.end(); ++it2 ) {
829 Glib::RefPtr<InputDevice const> dev = *it2;
830 Gtk::TreeModel::Row deviceRow = *(store->append(childrow.children()));
831 deviceRow[getCols().description] = dev->getName();
832 deviceRow[getCols().device] = dev;
833 deviceRow[getCols().mode] = dev->getMode();
834 switch ( dev->getSource() ) {
835 case GDK_SOURCE_MOUSE:
836 deviceRow[getCols().thumbnail] = getPix(PIX_CORE);
837 break;
838 case GDK_SOURCE_PEN:
839 if (deviceRow[getCols().description] == _("pad")) {
840 deviceRow[getCols().thumbnail] = getPix(PIX_SIDEBUTTONS);
841 } else {
842 deviceRow[getCols().thumbnail] = getPix(PIX_TIP);
843 }
844 break;
845 case GDK_SOURCE_CURSOR:
846 deviceRow[getCols().thumbnail] = getPix(PIX_MOUSE);
847 break;
848 case GDK_SOURCE_ERASER:
849 deviceRow[getCols().thumbnail] = getPix(PIX_ERASER);
850 break;
851 default:
852 ; // nothing
853 }
854 }
855 }
857 for ( std::list<Glib::RefPtr<InputDevice const> >::iterator it = devList.begin(); it != devList.end(); ++it ) {
858 Glib::RefPtr<InputDevice const> dev = *it;
859 if ( dev && (consumed.find( dev->getId() ) == consumed.end()) ) {
860 Gtk::TreeModel::Row deviceRow = *(store->append(row.children()));
861 deviceRow[getCols().description] = dev->getName();
862 deviceRow[getCols().device] = dev;
863 deviceRow[getCols().mode] = dev->getMode();
864 deviceRow[getCols().thumbnail] = getPix(PIX_CORE);
865 }
866 }
867 } else {
868 g_warning("No devices found");
869 }
870 }
873 InputDialogImpl::ConfPanel::ConfPanel() :
874 Gtk::VBox(),
875 store(Gtk::TreeStore::create(getCols())),
876 tabletIter(),
877 tree(store),
878 treeScroller(),
879 watcher(*this),
880 useExt(_("Use pressure-sensitive tablet (requires restart)")),
881 save(_("Save"))
882 {
883 pack_start(treeScroller);
885 treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
886 treeScroller.add(tree);
888 class Foo : public Gtk::TreeModel::ColumnRecord {
889 public :
890 Gtk::TreeModelColumn<Glib::ustring> one;
891 Foo() {add(one);}
892 };
893 static Foo foo;
894 Glib::RefPtr<Gtk::ListStore> poppers = Gtk::ListStore::create(foo);
895 poppers->reference();
897 Gtk::TreeModel::Row row = *(poppers->append());
898 row[foo.one] = getModeToString()[Gdk::MODE_DISABLED];
899 row = *(poppers->append());
900 row[foo.one] = getModeToString()[Gdk::MODE_SCREEN];
901 row = *(poppers->append());
902 row[foo.one] = getModeToString()[Gdk::MODE_WINDOW];
904 //Add the TreeView's view columns:
905 {
906 Gtk::CellRendererToggle *rendr = new Gtk::CellRendererToggle();
907 Gtk::TreeViewColumn *col = new Gtk::TreeViewColumn("xx", *rendr);
908 if (col) {
909 tree.append_column(*col);
910 col->set_cell_data_func(*rendr, sigc::ptr_fun(setCellStateToggle));
911 rendr->signal_toggled().connect(sigc::bind(sigc::ptr_fun(commitCellStateChange), store));
912 }
913 }
915 int expPos = tree.append_column("", getCols().expander);
917 tree.append_column("I", getCols().thumbnail);
918 tree.append_column("Bar", getCols().description);
920 {
921 Gtk::CellRendererCombo *rendr = new Gtk::CellRendererCombo();
922 rendr->property_model().set_value(poppers);
923 rendr->property_text_column().set_value(0);
924 rendr->property_has_entry() = false;
925 Gtk::TreeViewColumn *col = new Gtk::TreeViewColumn("X", *rendr);
926 if (col) {
927 tree.append_column(*col);
928 col->set_cell_data_func(*rendr, sigc::ptr_fun(setModeCellString));
929 rendr->signal_edited().connect(sigc::bind(sigc::ptr_fun(commitCellModeChange), store));
930 rendr->property_editable() = true;
931 }
932 }
934 tree.set_enable_tree_lines();
935 tree.set_headers_visible(false);
936 tree.set_expander_column( *tree.get_column(expPos - 1) );
938 setupTree( store, tabletIter );
940 Glib::RefPtr<Gtk::TreeView> treePtr(&tree);
941 Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::bind(sigc::ptr_fun(&InputDialogImpl::updateDeviceLinks), tabletIter, treePtr));
943 tree.expand_all();
945 useExt.set_active(Preferences::get()->getBool("/options/useextinput/value"));
946 useExt.signal_toggled().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::useExtToggled));
947 pack_start(useExt, Gtk::PACK_SHRINK);
949 save.signal_clicked().connect(sigc::mem_fun(*this, &InputDialogImpl::ConfPanel::saveSettings));
950 Gtk::Alignment *align = new Gtk::Alignment(Gtk::ALIGN_RIGHT, Gtk::ALIGN_TOP, 0, 0);
951 align->add(save);
952 pack_start(*Gtk::manage(align), Gtk::PACK_SHRINK);
953 }
955 InputDialogImpl::ConfPanel::~ConfPanel()
956 {
957 }
959 void InputDialogImpl::ConfPanel::setModeCellString(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter)
960 {
961 if (iter) {
962 Gtk::CellRendererCombo *combo = dynamic_cast<Gtk::CellRendererCombo *>(rndr);
963 if (combo) {
964 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
965 Gdk::InputMode mode = (*iter)[getCols().mode];
966 if (dev && (getModeToString().find(mode) != getModeToString().end())) {
967 combo->property_text() = getModeToString()[mode];
968 } else {
969 combo->property_text() = "";
970 }
971 }
972 }
973 }
975 void InputDialogImpl::ConfPanel::commitCellModeChange(Glib::ustring const &path, Glib::ustring const &newText, Glib::RefPtr<Gtk::TreeStore> store)
976 {
977 Gtk::TreeIter iter = store->get_iter(path);
978 if (iter) {
979 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
980 if (dev && (getStringToMode().find(newText) != getStringToMode().end())) {
981 Gdk::InputMode mode = getStringToMode()[newText];
982 Inkscape::DeviceManager::getManager().setMode( dev->getId(), mode );
983 }
984 }
985 }
987 void InputDialogImpl::ConfPanel::setCellStateToggle(Gtk::CellRenderer *rndr, Gtk::TreeIter const &iter)
988 {
989 if (iter) {
990 Gtk::CellRendererToggle *toggle = dynamic_cast<Gtk::CellRendererToggle *>(rndr);
991 if (toggle) {
992 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
993 if (dev) {
994 Gdk::InputMode mode = (*iter)[getCols().mode];
995 toggle->set_active(mode != Gdk::MODE_DISABLED);
996 } else {
997 toggle->set_active(false);
998 }
999 }
1000 }
1001 }
1003 void InputDialogImpl::ConfPanel::commitCellStateChange(Glib::ustring const &path, Glib::RefPtr<Gtk::TreeStore> store)
1004 {
1005 Gtk::TreeIter iter = store->get_iter(path);
1006 if (iter) {
1007 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
1008 if (dev) {
1009 Gdk::InputMode mode = (*iter)[getCols().mode];
1010 if (mode == Gdk::MODE_DISABLED) {
1011 Inkscape::DeviceManager::getManager().setMode( dev->getId(), Gdk::MODE_SCREEN );
1012 } else {
1013 Inkscape::DeviceManager::getManager().setMode( dev->getId(), Gdk::MODE_DISABLED );
1014 }
1015 }
1016 }
1017 }
1019 void InputDialogImpl::ConfPanel::saveSettings()
1020 {
1021 Inkscape::DeviceManager::getManager().saveConfig();
1022 }
1024 void InputDialogImpl::ConfPanel::useExtToggled()
1025 {
1026 bool active = useExt.get_active();
1027 if (active != Preferences::get()->getBool("/options/useextinput/value")) {
1028 Preferences::get()->setBool("/options/useextinput/value", active);
1029 if (active) {
1030 // As a work-around for a common problem, enable tablet toggles on the calligraphic tool.
1031 // Covered in Launchpad bug #196195.
1032 Preferences::get()->setBool("/tools/tweak/usepressure", true);
1033 Preferences::get()->setBool("/tools/calligraphic/usepressure", true);
1034 Preferences::get()->setBool("/tools/calligraphic/usetilt", true);
1035 }
1036 }
1037 }
1039 InputDialogImpl::ConfPanel::Blink::Blink(ConfPanel &parent) :
1040 Preferences::Observer("/options/useextinput/value"),
1041 parent(parent)
1042 {
1043 Preferences::get()->addObserver(*this);
1044 }
1046 InputDialogImpl::ConfPanel::Blink::~Blink()
1047 {
1048 Preferences::get()->removeObserver(*this);
1049 }
1051 void InputDialogImpl::ConfPanel::Blink::notify(Preferences::Entry const &new_val)
1052 {
1053 parent.useExt.set_active(new_val.getBool());
1054 }
1056 void InputDialogImpl::handleDeviceChange(Glib::RefPtr<InputDevice const> device)
1057 {
1058 // g_message("OUCH!!!! for %p hits %s", &device, device->getId().c_str());
1059 std::vector<Glib::RefPtr<Gtk::TreeStore> > stores;
1060 stores.push_back(store);
1061 stores.push_back(cfgPanel.store);
1063 for (std::vector<Glib::RefPtr<Gtk::TreeStore> >::iterator it = stores.begin(); it != stores.end(); ++it) {
1064 Gtk::TreeModel::iterator deviceIter;
1065 (*it)->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1066 sigc::ptr_fun(&InputDialogImpl::findDevice),
1067 device->getId(),
1068 &deviceIter) );
1069 if ( deviceIter ) {
1070 Gdk::InputMode mode = device->getMode();
1071 Gtk::TreeModel::Row row = *deviceIter;
1072 if (row[getCols().mode] != mode) {
1073 row[getCols().mode] = mode;
1074 }
1075 }
1076 }
1077 }
1079 void InputDialogImpl::updateDeviceAxes(Glib::RefPtr<InputDevice const> device)
1080 {
1081 gint live = device->getLiveAxes();
1083 std::map<guint, std::pair<guint, gdouble> > existing = axesMap[device->getId()];
1084 gint mask = 0x1;
1085 for ( gint num = 0; num < 32; num++, mask <<= 1) {
1086 if ( (mask & live) != 0 ) {
1087 if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) {
1088 axesMap[device->getId()][num].first = 2;
1089 axesMap[device->getId()][num].second = 0.0;
1090 }
1091 }
1092 }
1093 updateTestAxes( device->getId(), 0 );
1094 }
1096 void InputDialogImpl::updateDeviceButtons(Glib::RefPtr<InputDevice const> device)
1097 {
1098 gint live = device->getLiveButtons();
1099 std::set<guint> existing = buttonMap[device->getId()];
1100 gint mask = 0x1;
1101 for ( gint num = 0; num < 32; num++, mask <<= 1) {
1102 if ( (mask & live) != 0 ) {
1103 if ( existing.find(num) == existing.end() ) {
1104 buttonMap[device->getId()].insert(num);
1105 }
1106 }
1107 }
1108 updateTestButtons(device->getId(), -1);
1109 }
1112 bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter,
1113 Glib::ustring id,
1114 Gtk::TreeModel::iterator* result)
1115 {
1116 bool stop = false;
1117 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
1118 if ( dev && (dev->getId() == id) ) {
1119 if ( result ) {
1120 *result = iter;
1121 }
1122 stop = true;
1123 }
1124 return stop;
1125 }
1127 bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter,
1128 Glib::ustring link,
1129 Gtk::TreeModel::iterator* result)
1130 {
1131 bool stop = false;
1132 Glib::RefPtr<InputDevice const> dev = (*iter)[getCols().device];
1133 if ( dev && (dev->getLink() == link) ) {
1134 if ( result ) {
1135 *result = iter;
1136 }
1137 stop = true;
1138 }
1139 return stop;
1140 }
1142 void InputDialogImpl::updateDeviceLinks(Glib::RefPtr<InputDevice const> device, Gtk::TreeIter tabletIter, Glib::RefPtr<Gtk::TreeView> tree)
1143 {
1144 Glib::RefPtr<Gtk::TreeStore> store = Glib::RefPtr<Gtk::TreeStore>::cast_dynamic(tree->get_model());
1146 // g_message("Links!!!! for %p hits [%s] with link of [%s]", &device, device->getId().c_str(), device->getLink().c_str());
1147 Gtk::TreeModel::iterator deviceIter;
1148 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1149 sigc::ptr_fun(&InputDialogImpl::findDevice),
1150 device->getId(),
1151 &deviceIter) );
1153 if ( deviceIter ) {
1154 // Found the device concerned. Can proceed.
1156 if ( device->getLink().empty() ) {
1157 // is now unlinked
1158 // g_message("Item %s is unlinked", device->getId().c_str());
1159 if ( deviceIter->parent() != tabletIter ) {
1160 // Not the child of the tablet. move on up
1162 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1163 Glib::ustring descr = (*deviceIter)[getCols().description];
1164 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1166 Gtk::TreeModel::Row deviceRow = *store->append(tabletIter->children());
1167 deviceRow[getCols().description] = descr;
1168 deviceRow[getCols().thumbnail] = thumb;
1169 deviceRow[getCols().device] = dev;
1170 deviceRow[getCols().mode] = dev->getMode();
1172 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1173 store->erase(deviceIter);
1174 if ( oldParent->children().empty() ) {
1175 store->erase(oldParent);
1176 }
1177 }
1178 } else {
1179 // is linking
1180 if ( deviceIter->parent() == tabletIter ) {
1181 // Simple case. Not already linked
1183 Gtk::TreeIter newGroup = store->append(tabletIter->children());
1184 (*newGroup)[getCols().description] = _("Pen");
1185 (*newGroup)[getCols().thumbnail] = getPix(PIX_PEN);
1187 Glib::RefPtr<InputDevice const> dev = (*deviceIter)[getCols().device];
1188 Glib::ustring descr = (*deviceIter)[getCols().description];
1189 Glib::RefPtr<Gdk::Pixbuf> thumb = (*deviceIter)[getCols().thumbnail];
1191 Gtk::TreeModel::Row deviceRow = *store->append(newGroup->children());
1192 deviceRow[getCols().description] = descr;
1193 deviceRow[getCols().thumbnail] = thumb;
1194 deviceRow[getCols().device] = dev;
1195 deviceRow[getCols().mode] = dev->getMode();
1198 Gtk::TreeModel::iterator linkIter;
1199 store->foreach_iter( sigc::bind<Glib::ustring, Gtk::TreeModel::iterator*>(
1200 sigc::ptr_fun(&InputDialogImpl::findDeviceByLink),
1201 device->getId(),
1202 &linkIter) );
1203 if ( linkIter ) {
1204 dev = (*linkIter)[getCols().device];
1205 descr = (*linkIter)[getCols().description];
1206 thumb = (*linkIter)[getCols().thumbnail];
1208 deviceRow = *store->append(newGroup->children());
1209 deviceRow[getCols().description] = descr;
1210 deviceRow[getCols().thumbnail] = thumb;
1211 deviceRow[getCols().device] = dev;
1212 deviceRow[getCols().mode] = dev->getMode();
1213 Gtk::TreeModel::iterator oldParent = linkIter->parent();
1214 store->erase(linkIter);
1215 if ( oldParent->children().empty() ) {
1216 store->erase(oldParent);
1217 }
1218 }
1220 Gtk::TreeModel::iterator oldParent = deviceIter->parent();
1221 store->erase(deviceIter);
1222 if ( oldParent->children().empty() ) {
1223 store->erase(oldParent);
1224 }
1225 tree->expand_row(Gtk::TreePath(newGroup), true);
1226 }
1227 }
1228 }
1229 }
1231 void InputDialogImpl::linkComboChanged() {
1232 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1233 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1234 if (iter) {
1235 Gtk::TreeModel::Row row = *iter;
1236 Glib::ustring val = row[getCols().description];
1237 Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1238 if ( dev ) {
1239 if ( linkCombo.get_active_row_number() == 0 ) {
1240 // It is the "None" entry
1241 DeviceManager::getManager().setLinkedTo(dev->getId(), "");
1242 } else {
1243 Glib::ustring linkName = linkCombo.get_active_text();
1244 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1245 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1246 if ( linkName == (*it)->getName() ) {
1247 DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId());
1248 break;
1249 }
1250 }
1251 }
1252 }
1253 }
1254 }
1256 void InputDialogImpl::resyncToSelection() {
1257 bool clear = true;
1258 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1259 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1260 if (iter) {
1261 Gtk::TreeModel::Row row = *iter;
1262 Glib::ustring val = row[getCols().description];
1263 Glib::RefPtr<InputDevice const> dev = row[getCols().device];
1264 if ( dev ) {
1265 devDetails.set_sensitive(true);
1267 linkConnection.block();
1268 linkCombo.clear_items();
1269 linkCombo.append_text(_("None"));
1270 linkCombo.set_active(0);
1271 if ( dev->getSource() != Gdk::SOURCE_MOUSE ) {
1272 Glib::ustring linked = dev->getLink();
1273 std::list<Glib::RefPtr<InputDevice const> > devList = Inkscape::DeviceManager::getManager().getDevices();
1274 for ( std::list<Glib::RefPtr<InputDevice const> >::const_iterator it = devList.begin(); it != devList.end(); ++it ) {
1275 if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) {
1276 linkCombo.append_text((*it)->getName().c_str());
1277 if ( (linked.length() > 0) && (linked == (*it)->getId()) ) {
1278 linkCombo.set_active_text((*it)->getName().c_str());
1279 }
1280 }
1281 }
1282 linkCombo.set_sensitive(true);
1283 } else {
1284 linkCombo.set_sensitive(false);
1285 }
1286 linkConnection.unblock();
1288 clear = false;
1289 devName.set_label(row[getCols().description]);
1290 setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo);
1291 setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo);
1292 }
1293 }
1295 devDetails.set_sensitive(!clear);
1296 if (clear) {
1297 devName.set_label("");
1298 devAxesCount.set_label("");
1299 devKeyCount.set_label("");
1300 }
1301 }
1303 void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo )
1304 {
1305 gchar *tmp = g_strdup_printf("%d", reported);
1306 label.set_label(tmp);
1307 g_free(tmp);
1309 combo.clear_items();
1310 for ( gint i = 1; i <= reported; ++i ) {
1311 tmp = g_strdup_printf("%d", i);
1312 combo.append_text(tmp);
1313 g_free(tmp);
1314 }
1316 if ( (1 <= actual) && (actual <= reported) ) {
1317 combo.set_active(actual - 1);
1318 }
1319 }
1321 void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton )
1322 {
1323 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testButtons)); i++ ) {
1324 if ( buttonMap[key].find(i) != buttonMap[key].end() ) {
1325 if ( i == hotButton ) {
1326 testButtons[i].set(getPix(PIX_BUTTONS_ON));
1327 } else {
1328 testButtons[i].set(getPix(PIX_BUTTONS_OFF));
1329 }
1330 } else {
1331 testButtons[i].set(getPix(PIX_BUTTONS_NONE));
1332 }
1333 }
1334 }
1336 void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev )
1337 {
1338 static gdouble epsilon = 0.0001;
1339 {
1340 Glib::RefPtr<Gtk::TreeSelection> treeSel = tree.get_selection();
1341 Gtk::TreeModel::iterator iter = treeSel->get_selected();
1342 if (iter) {
1343 Gtk::TreeModel::Row row = *iter;
1344 Glib::ustring val = row[getCols().description];
1345 Glib::RefPtr<InputDevice const> idev = row[getCols().device];
1346 if ( !idev || (idev->getId() != key) ) {
1347 dev = 0;
1348 }
1349 }
1350 }
1353 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(testAxes)); i++ ) {
1354 if ( axesMap[key].find(i) != axesMap[key].end() ) {
1355 switch ( axesMap[key][i].first ) {
1356 case 0:
1357 case 1:
1358 testAxes[i].set(getPix(PIX_AXIS_NONE));
1359 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1360 axesValues[i].set_sensitive(false);
1361 }
1362 break;
1363 case 2:
1364 testAxes[i].set(getPix(PIX_AXIS_OFF));
1365 axesValues[i].set_sensitive(true);
1366 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1367 if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1368 axesValues[i].set_sensitive(true);
1369 axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1370 }
1371 gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1372 axesValues[i].set_text(str);
1373 g_free(str);
1374 }
1375 break;
1376 case 3:
1377 testAxes[i].set(getPix(PIX_AXIS_ON));
1378 axesValues[i].set_sensitive(true);
1379 if ( dev && (i < static_cast<gint>(G_N_ELEMENTS(axesValues)) ) ) {
1380 if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) {
1381 axesValues[i].set_sensitive(true);
1382 axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) );
1383 }
1384 gchar* str = g_strdup_printf("%f", axesMap[key][i].second);
1385 axesValues[i].set_text(str);
1386 g_free(str);
1387 }
1388 }
1390 } else {
1391 testAxes[i].set(getPix(PIX_AXIS_NONE));
1392 }
1393 }
1394 if ( !dev ) {
1395 for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(axesValues)); i++ ) {
1396 axesValues[i].set_fraction(0.0);
1397 axesValues[i].set_text("");
1398 axesValues[i].set_sensitive(false);
1399 }
1400 }
1401 }
1403 void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev )
1404 {
1405 static gdouble epsilon = 0.0001;
1406 if ( (numAxes > 0) && axes) {
1407 for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) {
1408 // 0 == new, 1 == set value, 2 == changed value, 3 == active
1409 gdouble diff = axesMap[key][axisNum].second - axes[axisNum];
1410 switch(axesMap[key][axisNum].first) {
1411 case 0:
1412 {
1413 axesMap[key][axisNum].first = 1;
1414 axesMap[key][axisNum].second = axes[axisNum];
1415 }
1416 break;
1417 case 1:
1418 {
1419 if ( (diff > epsilon) || (diff < -epsilon) ) {
1420 // g_message("Axis %d changed on %s]", axisNum, key.c_str());
1421 axesMap[key][axisNum].first = 3;
1422 axesMap[key][axisNum].second = axes[axisNum];
1423 updateTestAxes(key, dev);
1424 DeviceManager::getManager().addAxis(key, axisNum);
1425 }
1426 }
1427 break;
1428 case 2:
1429 {
1430 if ( (diff > epsilon) || (diff < -epsilon) ) {
1431 axesMap[key][axisNum].first = 3;
1432 axesMap[key][axisNum].second = axes[axisNum];
1433 updateTestAxes(key, dev);
1434 }
1435 }
1436 break;
1437 case 3:
1438 {
1439 if ( (diff > epsilon) || (diff < -epsilon) ) {
1440 axesMap[key][axisNum].second = axes[axisNum];
1441 } else {
1442 axesMap[key][axisNum].first = 2;
1443 updateTestAxes(key, dev);
1444 }
1445 }
1446 }
1447 }
1448 }
1449 // std::map<Glib::ustring, std::map<guint, std::pair<guint, gdouble> > > axesMap;
1450 }
1452 Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device )
1453 {
1454 Glib::ustring key;
1455 switch ( device->source ) {
1456 case GDK_SOURCE_MOUSE:
1457 key = "M:";
1458 break;
1459 case GDK_SOURCE_CURSOR:
1460 key = "C:";
1461 break;
1462 case GDK_SOURCE_PEN:
1463 key = "P:";
1464 break;
1465 case GDK_SOURCE_ERASER:
1466 key = "E:";
1467 break;
1468 default:
1469 key = "?:";
1470 }
1471 key += device->name;
1473 return key;
1474 }
1476 bool InputDialogImpl::eventSnoop(GdkEvent* event)
1477 {
1478 int modmod = 0;
1480 GdkInputSource source = lastSourceSeen;
1481 Glib::ustring devName = lastDevnameSeen;
1482 Glib::ustring key;
1483 gint hotButton = -1;
1485 switch ( event->type ) {
1486 case GDK_KEY_PRESS:
1487 case GDK_KEY_RELEASE:
1488 {
1489 GdkEventKey* keyEvt = reinterpret_cast<GdkEventKey*>(event);
1490 gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast<GdkModifierType>(keyEvt->state));
1491 keyVal.set_label(name);
1492 // g_message("%d KEY state:0x%08x 0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name);
1493 g_free(name);
1494 }
1495 break;
1496 case GDK_BUTTON_PRESS:
1497 modmod = 1;
1498 // fallthrough
1499 case GDK_BUTTON_RELEASE:
1500 {
1501 GdkEventButton* btnEvt = reinterpret_cast<GdkEventButton*>(event);
1502 if ( btnEvt->device ) {
1503 key = getKeyFor(btnEvt->device);
1504 source = btnEvt->device->source;
1505 devName = btnEvt->device->name;
1507 mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device);
1508 if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) {
1509 // g_message("New button found for %s = %d", key.c_str(), btnEvt->button);
1510 buttonMap[key].insert(btnEvt->button);
1511 DeviceManager::getManager().addButton(key, btnEvt->button);
1512 }
1513 hotButton = modmod ? btnEvt->button : -1;
1514 updateTestButtons(key, hotButton);
1515 }
1516 gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnEvt->state));
1517 keyVal.set_label(name);
1518 // g_message("%d BTN state:0x%08x %c %4d [%s] dev:%p [%s] ",
1519 // btnEvt->type, btnEvt->state,
1520 // (modmod ? '+':'-'),
1521 // btnEvt->button, name, btnEvt->device,
1522 // (btnEvt->device ? btnEvt->device->name : "null")
1524 // );
1525 g_free(name);
1526 }
1527 break;
1528 case GDK_MOTION_NOTIFY:
1529 {
1530 GdkEventMotion* btnMtn = reinterpret_cast<GdkEventMotion*>(event);
1531 if ( btnMtn->device ) {
1532 key = getKeyFor(btnMtn->device);
1533 source = btnMtn->device->source;
1534 devName = btnMtn->device->name;
1535 mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device);
1536 }
1537 gchar* name = gtk_accelerator_name(0, static_cast<GdkModifierType>(btnMtn->state));
1538 keyVal.set_label(name);
1539 // 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,
1540 // name, btnMtn->device,
1541 // (btnMtn->device ? btnMtn->device->name : "null"),
1542 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0),
1543 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0),
1544 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0),
1545 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0),
1546 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0),
1547 // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0)
1548 // );
1549 g_free(name);
1550 }
1551 break;
1552 default:
1553 ;// nothing
1554 }
1557 if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) {
1558 switch (source) {
1559 case GDK_SOURCE_MOUSE:
1560 {
1561 testThumb.set(getPix(PIX_CORE));
1562 }
1563 break;
1564 case GDK_SOURCE_CURSOR:
1565 {
1566 // g_message("flip to cursor");
1567 testThumb.set(getPix(PIX_MOUSE));
1568 }
1569 break;
1570 case GDK_SOURCE_PEN:
1571 {
1572 if (devName == _("pad")) {
1573 // g_message("flip to pad");
1574 testThumb.set(getPix(PIX_SIDEBUTTONS));
1575 } else {
1576 // g_message("flip to pen");
1577 testThumb.set(getPix(PIX_TIP));
1578 }
1579 }
1580 break;
1581 case GDK_SOURCE_ERASER:
1582 {
1583 // g_message("flip to eraser");
1584 testThumb.set(getPix(PIX_ERASER));
1585 }
1586 break;
1587 // default:
1588 // g_message("gurgle");
1589 }
1590 updateTestButtons(key, hotButton);
1591 lastSourceSeen = source;
1592 lastDevnameSeen = devName;
1593 }
1595 return false;
1596 }
1599 } // end namespace Inkscape
1600 } // end namespace UI
1601 } // end namespace Dialog
1604 /*
1605 Local Variables:
1606 mode:c++
1607 c-file-style:"stroustrup"
1608 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1609 indent-tabs-mode:nil
1610 fill-column:99
1611 End:
1612 */
1613 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :