/** @file * @brief Input devices dialog (new) - implementation */ /* Author: * Jon A. Cruz * * Copyright (C) 2008 Author * Released under GNU GPL. Read the file 'COPYING' for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui/widget/panel.h" #include "device-manager.h" #include "input.h" /* XPM */ static char const * core_xpm[] = { "16 16 4 1", " c None", ". c #808080", "+ c #000000", "@ c #FFFFFF", " ", " ", " ", " .++++++. ", " +@+@@+@+ ", " +@+@@+@+ ", " +.+..+.+ ", " +@@@@@@+ ", " +@@@@@@+ ", " +@@@@@@+ ", " +@@@@@@+ ", " +@@@@@@+ ", " .++++++. ", " ", " ", " "}; /* XPM */ static char const *eraser[] = { /* columns rows colors chars-per-pixel */ "16 16 5 1", " c black", ". c green", "X c #808080", "o c gray100", "O c None", /* pixels */ "OOOOOOOOOOOOOOOO", "OOOOOOOOOOOOO OO", "OOOOOOOOOOOO . O", "OOOOOOOOOOO . OO", "OOOOOOOOOO . OOO", "OOOOOOOOO . OOOO", "OOOOOOOO . OOOOO", "OOOOOOOXo OOOOOO", "OOOOOOXoXOOOOOOO", "OOOOOXoXOOOOOOOO", "OOOOXoXOOOOOOOOO", "OOOXoXOOOOOOOOOO", "OOXoXOOOOOOOOOOO", "OOXXOOOOOOOOOOOO", "OOOOOOOOOOOOOOOO", "OOOOOOOOOOOOOOOO" }; /* XPM */ static char const *mouse[] = { /* columns rows colors chars-per-pixel */ "16 16 3 1", " c black", ". c gray100", "X c None", /* pixels */ "XXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXX", "XXXXXXX XXXXXXX", "XXXXX . XXXXXXX", "XXXX .... XXXXXX", "XXXX .... XXXXXX", "XXXXX .... XXXXX", "XXXXX .... XXXXX", "XXXXXX .... XXXX", "XXXXXX .... XXXX", "XXXXXXX . XXXXX", "XXXXXXX XXXXXXX", "XXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXX" }; /* XPM */ static char const *pen[] = { /* columns rows colors chars-per-pixel */ "16 16 3 1", " c black", ". c gray100", "X c None", /* pixels */ "XXXXXXXXXXXXXXXX", "XXXXXXXXXXXXX XX", "XXXXXXXXXXXX . X", "XXXXXXXXXXX . XX", "XXXXXXXXXX . XXX", "XXXXXXXXX . XXXX", "XXXXXXXX . XXXXX", "XXXXXXX . XXXXXX", "XXXXXX . XXXXXXX", "XXXXX . XXXXXXXX", "XXXX . XXXXXXXXX", "XXX . XXXXXXXXXX", "XX . XXXXXXXXXXX", "XX XXXXXXXXXXXX", "XXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXX" }; /* XPM */ static char const *sidebuttons[] = { /* columns rows colors chars-per-pixel */ "16 16 4 1", " c black", ". c #808080", "o c green", "O c None", /* pixels */ "OOOOOOOOOOOOOOOO", "OOOOOOOOOOOOOOOO", "O..............O", "O.OOOOOOOOOOOO.O", "O OOOOOOOO O", "O o OOOOOOOO o O", "O o OOOOOOOO o O", "O OOOOOOOO O", "O.OOOOOOOOOOOO.O", "O.OOOOOOOOOOOO.O", "O.OOOOOOOOOOOO.O", "O.OOOOOOOOOOOO.O", "O.OOOOOOOOOOOO.O", "O..............O", "OOOOOOOOOOOOOOOO", "OOOOOOOOOOOOOOOO" }; /* XPM */ static char const *tablet[] = { /* columns rows colors chars-per-pixel */ "16 16 3 1", " c black", ". c gray100", "X c None", /* pixels */ "XXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXX", "X X", "X ............ X", "X ............ X", "X ............ X", "X ............ X", "X ............ X", "X ............ X", "X ............ X", "X ............ X", "X ............ X", "X ............ X", "X X", "XXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXX" }; /* XPM */ static char const *tip[] = { /* columns rows colors chars-per-pixel */ "16 16 5 1", " c black", ". c green", "X c #808080", "o c gray100", "O c None", /* pixels */ "OOOOOOOOOOOOOOOO", "OOOOOOOOOOOOOXOO", "OOOOOOOOOOOOXoXO", "OOOOOOOOOOOXoXOO", "OOOOOOOOOOXoXOOO", "OOOOOOOOOXoXOOOO", "OOOOOOOOXoXOOOOO", "OOOOOOO oXOOOOOO", "OOOOOO . OOOOOOO", "OOOOO . OOOOOOOO", "OOOO . OOOOOOOOO", "OOO . OOOOOOOOOO", "OO . OOOOOOOOOOO", "OO OOOOOOOOOOOO", "OOOOXXXXXOOOOOOO", "OOOOOOOOOXXXXXOO" }; /* XPM */ static char const *button_none[] = { /* columns rows colors chars-per-pixel */ "8 8 3 1", " c black", ". c #808080", "X c None", /* pixels */ "XXXXXXXX", "XX .. XX", "X .XX. X", "X.XX X.X", "X.X XX.X", "X .XX. X", "XX .. XX", "XXXXXXXX" }; /* XPM */ static char const *button_off[] = { /* columns rows colors chars-per-pixel */ "8 8 4 1", " c black", ". c #808080", "X c gray100", "o c None", /* pixels */ "oooooooo", "oo. .oo", "o. XX .o", "o XXXX o", "o XXXX o", "o. XX .o", "oo. .oo", "oooooooo" }; /* XPM */ static char const *button_on[] = { /* columns rows colors chars-per-pixel */ "8 8 3 1", " c black", ". c green", "X c None", /* pixels */ "XXXXXXXX", "XX XX", "X .. X", "X .... X", "X .... X", "X .. X", "XX XX", "XXXXXXXX" }; /* XPM */ static char const * axis_none_xpm[] = { "24 8 3 1", " c None", ". c #000000", "+ c #808080", " ", " .++++++++++++++++++. ", " .+ . .+. ", " + . . . + ", " + . . . + ", " .+. . +. ", " .++++++++++++++++++. ", " "}; /* XPM */ static char const * axis_off_xpm[] = { "24 8 4 1", " c None", ". c #808080", "+ c #000000", "@ c #FFFFFF", " ", " .++++++++++++++++++. ", " .+@@@@@@@@@@@@@@@@@@+. ", " +@@@@@@@@@@@@@@@@@@@@+ ", " +@@@@@@@@@@@@@@@@@@@@+ ", " .+@@@@@@@@@@@@@@@@@@+. ", " .++++++++++++++++++. ", " "}; /* XPM */ static char const * axis_on_xpm[] = { "24 8 3 1", " c None", ". c #000000", "+ c #00FF00", " ", " .................... ", " ..++++++++++++++++++.. ", " .++++++++++++++++++++. ", " .++++++++++++++++++++. ", " ..++++++++++++++++++.. ", " .................... ", " "}; using Inkscape::InputDevice; namespace Inkscape { namespace UI { namespace Dialog { class MyModelColumns : public Gtk::TreeModel::ColumnRecord { public: Gtk::TreeModelColumn filename; Gtk::TreeModelColumn description; Gtk::TreeModelColumn< Glib::RefPtr > thumbnail; Gtk::TreeModelColumn device; MyModelColumns() { add(filename); add(description); add(thumbnail); add(device); } }; class InputDialogImpl : public InputDialog { public: InputDialogImpl(); virtual ~InputDialogImpl() {} private: Glib::RefPtr corePix; Glib::RefPtr penPix; Glib::RefPtr mousePix; Glib::RefPtr tipPix; Glib::RefPtr tabletPix; Glib::RefPtr eraserPix; Glib::RefPtr sidebuttonsPix; Glib::RefPtr buttonsNonePix; Glib::RefPtr buttonsOnPix; Glib::RefPtr buttonsOffPix; Glib::RefPtr axisNonePix; Glib::RefPtr axisOnPix; Glib::RefPtr axisOffPix; std::map > buttonMap; std::map > > axesMap; GdkInputSource lastSourceSeen; Glib::ustring lastDevnameSeen; MyModelColumns cols; Glib::RefPtr store; Gtk::TreeIter tabletIter; Gtk::TreeView tree; Gtk::Frame frame2; Gtk::Frame testFrame; Gtk::ScrolledWindow treeScroller; Gtk::ScrolledWindow detailScroller; Gtk::HPaned splitter; Gtk::VPaned split2; Gtk::Label devName; Gtk::Label devKeyCount; Gtk::Label devAxesCount; Gtk::ComboBoxText axesCombo; Gtk::ProgressBar axesValues[6]; Gtk::ComboBoxText buttonCombo; Gtk::ComboBoxText linkCombo; sigc::connection linkConnection; Gtk::Label keyVal; Gtk::Entry keyEntry; Gtk::Table devDetails; Gtk::HPaned confSplitter; Gtk::Notebook topHolder; Gtk::Image testThumb; Gtk::Image testButtons[24]; Gtk::Image testAxes[8]; Gtk::Table imageTable; Gtk::EventBox testDetector; void setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo ); void updateTestButtons( Glib::ustring const& key, gint hotButton ); void updateTestAxes( Glib::ustring const& key, GdkDevice* dev ); void mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev); Glib::ustring getKeyFor( GdkDevice* device ); bool eventSnoop(GdkEvent* event); void linkComboChanged(); void resyncToSelection(); void handleDeviceChange(const Glib::RefPtr& device); void updateDeviceAxes(const Glib::RefPtr& device); void updateDeviceButtons(const Glib::RefPtr& device); void updateDeviceLinks(const Glib::RefPtr& device); bool findDevice(const Gtk::TreeModel::iterator& iter, Glib::ustring id, Gtk::TreeModel::iterator* result); bool findDeviceByLink(const Gtk::TreeModel::iterator& iter, Glib::ustring link, Gtk::TreeModel::iterator* result); }; // Now that we've defined the *Impl class, we can do the method to aquire one. InputDialog &InputDialog::getInstance() { InputDialog *dialog = new InputDialogImpl(); return *dialog; } InputDialogImpl::InputDialogImpl() : InputDialog(), corePix(Gdk::Pixbuf::create_from_xpm_data(core_xpm)), penPix(Gdk::Pixbuf::create_from_xpm_data(pen)), mousePix(Gdk::Pixbuf::create_from_xpm_data(mouse)), tipPix(Gdk::Pixbuf::create_from_xpm_data(tip)), tabletPix(Gdk::Pixbuf::create_from_xpm_data(tablet)), eraserPix(Gdk::Pixbuf::create_from_xpm_data(eraser)), sidebuttonsPix(Gdk::Pixbuf::create_from_xpm_data(sidebuttons)), buttonsNonePix(Gdk::Pixbuf::create_from_xpm_data(button_none)), buttonsOnPix(Gdk::Pixbuf::create_from_xpm_data(button_on)), buttonsOffPix(Gdk::Pixbuf::create_from_xpm_data(button_off)), axisNonePix(Gdk::Pixbuf::create_from_xpm_data(axis_none_xpm)), axisOnPix(Gdk::Pixbuf::create_from_xpm_data(axis_on_xpm)), axisOffPix(Gdk::Pixbuf::create_from_xpm_data(axis_off_xpm)), lastSourceSeen((GdkInputSource)-1), lastDevnameSeen(""), cols(), store(Gtk::TreeStore::create(cols)), tree(store), frame2(), testFrame("Test Area"), treeScroller(), detailScroller(), splitter(), split2(), linkCombo(), devDetails(12, 2), confSplitter(), topHolder(), imageTable(8, 7) { Gtk::Box *contents = _getContents(); treeScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); treeScroller.add(tree); split2.pack1(testFrame); split2.pack2(frame2); splitter.pack1(treeScroller); splitter.pack2(split2); testDetector.add(imageTable); testFrame.add(testDetector); testThumb.set(tabletPix); testThumb.set_padding(24, 24); imageTable.attach(testThumb, 0, 8, 0, 1, ::Gtk::EXPAND, ::Gtk::EXPAND); { guint col = 0; guint row = 1; for ( guint num = 0; num < G_N_ELEMENTS(testButtons); num++ ) { testButtons[num].set(buttonsNonePix); imageTable.attach(testButtons[num], col, col + 1, row, row + 1, ::Gtk::FILL, ::Gtk::FILL); col++; if (col > 7) { col = 0; row++; } } col = 0; for ( guint num = 0; num < G_N_ELEMENTS(testAxes); num++ ) { testAxes[num].set(axisNonePix); imageTable.attach(testAxes[num], col * 2, (col + 1) * 2, row, row + 1, ::Gtk::FILL, ::Gtk::FILL); col++; if (col > 3) { col = 0; row++; } } } topHolder.append_page(confSplitter, "Configuration"); topHolder.append_page(splitter, "Hardware"); // confSplitter.show_all(); // splitter.show_all(); topHolder.show_all(); topHolder.set_current_page(1); contents->pack_start(topHolder); int rowNum = 0; Gtk::Label* lbl = Gtk::manage(new Gtk::Label("Name:")); devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1, ::Gtk::FILL, ::Gtk::SHRINK); devDetails.attach(devName, 1, 2, rowNum, rowNum + 1, ::Gtk::SHRINK, ::Gtk::SHRINK); rowNum++; lbl = Gtk::manage(new Gtk::Label("Link:")); devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1, ::Gtk::FILL, ::Gtk::SHRINK); linkCombo.append_text("None"); linkCombo.set_active_text("None"); linkCombo.set_sensitive(false); linkConnection = linkCombo.signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::linkComboChanged)); devDetails.attach(linkCombo, 1, 2, rowNum, rowNum + 1, ::Gtk::FILL, ::Gtk::SHRINK); rowNum++; lbl = Gtk::manage(new Gtk::Label("Axes count:")); devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1, ::Gtk::FILL, ::Gtk::SHRINK); devDetails.attach(devAxesCount, 1, 2, rowNum, rowNum + 1, ::Gtk::SHRINK, ::Gtk::SHRINK); rowNum++; /* lbl = Gtk::manage(new Gtk::Label("Actual axes count:")); devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1, ::Gtk::FILL, ::Gtk::SHRINK); devDetails.attach(axesCombo, 1, 2, rowNum, rowNum + 1, ::Gtk::SHRINK, ::Gtk::SHRINK); rowNum++; */ for ( guint barNum = 0; barNum < static_cast(G_N_ELEMENTS(axesValues)); barNum++ ) { lbl = Gtk::manage(new Gtk::Label("axis:")); devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1, ::Gtk::FILL, ::Gtk::SHRINK); devDetails.attach(axesValues[barNum], 1, 2, rowNum, rowNum + 1, ::Gtk::EXPAND, ::Gtk::SHRINK); axesValues[barNum].set_sensitive(false); rowNum++; } lbl = Gtk::manage(new Gtk::Label("Button count:")); devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1, ::Gtk::FILL, ::Gtk::SHRINK); devDetails.attach(devKeyCount, 1, 2, rowNum, rowNum + 1, ::Gtk::SHRINK, ::Gtk::SHRINK); rowNum++; /* lbl = Gtk::manage(new Gtk::Label("Actual button count:")); devDetails.attach(*lbl, 0, 1, rowNum, rowNum+ 1, ::Gtk::FILL, ::Gtk::SHRINK); devDetails.attach(buttonCombo, 1, 2, rowNum, rowNum + 1, ::Gtk::SHRINK, ::Gtk::SHRINK); rowNum++; */ devDetails.attach(keyVal, 0, 2, rowNum, rowNum + 1, ::Gtk::FILL, ::Gtk::SHRINK); rowNum++; testDetector.signal_event().connect(sigc::mem_fun(*this, &InputDialogImpl::eventSnoop)); // void gdk_input_set_extension_events (GdkWindow *window, // gint mask, // GdkExtensionMode mode); gtk_widget_set_extension_events( GTK_WIDGET(testDetector.gobj()), GDK_EXTENSION_EVENTS_ALL ); 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); devDetails.attach(keyEntry, 0, 2, rowNum, rowNum + 1, ::Gtk::FILL, ::Gtk::SHRINK); rowNum++; devDetails.set_sensitive(false); detailScroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); detailScroller.add(devDetails); frame2.add(detailScroller); //- 16x16/devices // gnome-dev-mouse-optical // input-mouse // input-tablet // mouse Gtk::TreeModel::Row row; Gtk::TreeModel::Row childrow; Gtk::TreeModel::Row deviceRow; //Add the TreeView's view columns: tree.append_column("I", cols.thumbnail); tree.append_column("Bar", cols.description); tree.set_enable_tree_lines(); tree.set_headers_visible(false); tree.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &InputDialogImpl::resyncToSelection)); std::list devList = Inkscape::DeviceManager::getManager().getDevices(); if ( !devList.empty() ) { row = *(store->append()); row[cols.description] = "Hardware"; tabletIter = store->append(row.children()); childrow = *tabletIter; childrow[cols.description] = "Tablet"; childrow[cols.thumbnail] = tabletPix; for ( std::list::iterator it = devList.begin(); it != devList.end(); ++it ) { InputDevice const* dev = *it; if ( dev ) { // 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(), // dev->hasCursor() ? "Yes":"no", dev->getNumAxes(), dev->getNumKeys()); // if ( dev->getSource() != Gdk::SOURCE_MOUSE ) { if ( dev ) { deviceRow = *(store->append(childrow.children())); deviceRow[cols.description] = dev->getName(); deviceRow[cols.device] = dev; switch ( dev->getSource() ) { case GDK_SOURCE_MOUSE: deviceRow[cols.thumbnail] = corePix; break; case GDK_SOURCE_PEN: if (deviceRow[cols.description] == "pad") { deviceRow[cols.thumbnail] = sidebuttonsPix; } else { deviceRow[cols.thumbnail] = tipPix; } break; case GDK_SOURCE_CURSOR: deviceRow[cols.thumbnail] = mousePix; break; case GDK_SOURCE_ERASER: deviceRow[cols.thumbnail] = eraserPix; break; default: ; // nothing } } } else { g_warning("Null device in list"); } } } else { g_warning("No devices found"); } Inkscape::DeviceManager::getManager().signalDeviceChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::handleDeviceChange)); Inkscape::DeviceManager::getManager().signalAxesChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceAxes)); Inkscape::DeviceManager::getManager().signalButtonsChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceButtons)); Inkscape::DeviceManager::getManager().signalLinkChanged().connect(sigc::mem_fun(*this, &InputDialogImpl::updateDeviceLinks)); tree.expand_all(); show_all_children(); } void InputDialogImpl::handleDeviceChange(const Glib::RefPtr& /*device*/) { // g_message("OUCH!!!! for %p hits %s", &device, device->getId().c_str()); } void InputDialogImpl::updateDeviceAxes(const Glib::RefPtr& device) { gint live = device->getLiveAxes(); std::map > existing = axesMap[device->getId()]; gint mask = 0x1; for ( gint num = 0; num < 32; num++, mask <<= 1) { if ( (mask & live) != 0 ) { if ( (existing.find(num) == existing.end()) || (existing[num].first < 2) ) { axesMap[device->getId()][num].first = 2; axesMap[device->getId()][num].second = 0.0; } } } updateTestAxes( device->getId(), 0 ); } void InputDialogImpl::updateDeviceButtons(const Glib::RefPtr& device) { gint live = device->getLiveButtons(); std::set existing = buttonMap[device->getId()]; gint mask = 0x1; for ( gint num = 0; num < 32; num++, mask <<= 1) { if ( (mask & live) != 0 ) { if ( existing.find(num) == existing.end() ) { buttonMap[device->getId()].insert(num); } } } updateTestButtons(device->getId(), -1); } bool InputDialogImpl::findDevice(const Gtk::TreeModel::iterator& iter, Glib::ustring id, Gtk::TreeModel::iterator* result) { bool stop = false; const InputDevice* dev = (*iter)[cols.device]; if ( dev && (dev->getId() == id) ) { if ( result ) { *result = iter; } stop = true; } return stop; } bool InputDialogImpl::findDeviceByLink(const Gtk::TreeModel::iterator& iter, Glib::ustring link, Gtk::TreeModel::iterator* result) { bool stop = false; const InputDevice* dev = (*iter)[cols.device]; if ( dev && (dev->getLink() == link) ) { if ( result ) { *result = iter; } stop = true; } return stop; } void InputDialogImpl::updateDeviceLinks(const Glib::RefPtr& device) { // g_message("Links!!!! for %p hits [%s] with link of [%s]", &device, device->getId().c_str(), device->getLink().c_str()); Gtk::TreeModel::iterator deviceIter; store->foreach_iter( sigc::bind( sigc::mem_fun(*this, &InputDialogImpl::findDevice), device->getId(), &deviceIter) ); if ( deviceIter ) { // Found the device concerned. Can proceed. if ( device->getLink().empty() ) { // is now unlinked // g_message("Item %s is unlinked", device->getId().c_str()); if ( deviceIter->parent() != tabletIter ) { // Not the child of the tablet. move on up InputDevice const *dev = (*deviceIter)[cols.device]; Glib::ustring descr = (*deviceIter)[cols.description]; Glib::RefPtr thumb = (*deviceIter)[cols.thumbnail]; Gtk::TreeModel::Row deviceRow = *store->append(tabletIter->children()); deviceRow[cols.description] = descr; deviceRow[cols.thumbnail] = thumb; deviceRow[cols.device] = dev; Gtk::TreeModel::iterator oldParent = deviceIter->parent(); store->erase(deviceIter); if ( oldParent->children().empty() ) { store->erase(oldParent); } } } else { // is linking if ( deviceIter->parent() == tabletIter ) { // Simple case. Not already linked Gtk::TreeIter newGroup = store->append(tabletIter->children()); (*newGroup)[cols.description] = "Pen"; (*newGroup)[cols.thumbnail] = penPix; InputDevice const *dev = (*deviceIter)[cols.device]; Glib::ustring descr = (*deviceIter)[cols.description]; Glib::RefPtr thumb = (*deviceIter)[cols.thumbnail]; Gtk::TreeModel::Row deviceRow = *store->append(newGroup->children()); deviceRow[cols.description] = descr; deviceRow[cols.thumbnail] = thumb; deviceRow[cols.device] = dev; Gtk::TreeModel::iterator linkIter; store->foreach_iter( sigc::bind( sigc::mem_fun(*this, &InputDialogImpl::findDeviceByLink), device->getId(), &linkIter) ); if ( linkIter ) { dev = (*linkIter)[cols.device]; descr = (*linkIter)[cols.description]; thumb = (*linkIter)[cols.thumbnail]; deviceRow = *store->append(newGroup->children()); deviceRow[cols.description] = descr; deviceRow[cols.thumbnail] = thumb; deviceRow[cols.device] = dev; Gtk::TreeModel::iterator oldParent = linkIter->parent(); store->erase(linkIter); if ( oldParent->children().empty() ) { store->erase(oldParent); } } Gtk::TreeModel::iterator oldParent = deviceIter->parent(); store->erase(deviceIter); if ( oldParent->children().empty() ) { store->erase(oldParent); } tree.expand_row(Gtk::TreePath(newGroup), true); } } } } void InputDialogImpl::linkComboChanged() { Glib::RefPtr treeSel = tree.get_selection(); Gtk::TreeModel::iterator iter = treeSel->get_selected(); if (iter) { Gtk::TreeModel::Row row = *iter; Glib::ustring val = row[cols.description]; InputDevice const * dev = row[cols.device]; if ( dev ) { if ( linkCombo.get_active_row_number() == 0 ) { // It is the "None" entry DeviceManager::getManager().setLinkedTo(dev->getId(), ""); } else { Glib::ustring linkName = linkCombo.get_active_text(); std::list devList = Inkscape::DeviceManager::getManager().getDevices(); for ( std::list::const_iterator it = devList.begin(); it != devList.end(); ++it ) { if ( linkName == (*it)->getName() ) { DeviceManager::getManager().setLinkedTo(dev->getId(), (*it)->getId()); break; } } } } } } void InputDialogImpl::resyncToSelection() { bool clear = true; Glib::RefPtr treeSel = tree.get_selection(); Gtk::TreeModel::iterator iter = treeSel->get_selected(); if (iter) { Gtk::TreeModel::Row row = *iter; Glib::ustring val = row[cols.description]; InputDevice const * dev = row[cols.device]; if ( dev ) { devDetails.set_sensitive(true); linkConnection.block(); linkCombo.clear_items(); linkCombo.append_text("None"); linkCombo.set_active(0); if ( dev->getSource() != Gdk::SOURCE_MOUSE ) { Glib::ustring linked = dev->getLink(); std::list devList = Inkscape::DeviceManager::getManager().getDevices(); for ( std::list::const_iterator it = devList.begin(); it != devList.end(); ++it ) { if ( ((*it)->getSource() != Gdk::SOURCE_MOUSE) && ((*it) != dev) ) { linkCombo.append_text((*it)->getName().c_str()); if ( (linked.length() > 0) && (linked == (*it)->getId()) ) { linkCombo.set_active_text((*it)->getName().c_str()); } } } linkCombo.set_sensitive(true); } else { linkCombo.set_sensitive(false); } linkConnection.unblock(); clear = false; devName.set_label(row[cols.description]); setupValueAndCombo( dev->getNumAxes(), dev->getNumAxes(), devAxesCount, axesCombo); setupValueAndCombo( dev->getNumKeys(), dev->getNumKeys(), devKeyCount, buttonCombo); } } devDetails.set_sensitive(!clear); if (clear) { devName.set_label(""); devAxesCount.set_label(""); devKeyCount.set_label(""); } } void InputDialogImpl::setupValueAndCombo( gint reported, gint actual, Gtk::Label& label, Gtk::ComboBoxText& combo ) { gchar *tmp = g_strdup_printf("%d", reported); label.set_label(tmp); g_free(tmp); combo.clear_items(); for ( gint i = 1; i <= reported; ++i ) { tmp = g_strdup_printf("%d", i); combo.append_text(tmp); g_free(tmp); } if ( (1 <= actual) && (actual <= reported) ) { combo.set_active(actual - 1); } } void InputDialogImpl::updateTestButtons( Glib::ustring const& key, gint hotButton ) { for ( gint i = 0; i < static_cast(G_N_ELEMENTS(testButtons)); i++ ) { if ( buttonMap[key].find(i) != buttonMap[key].end() ) { if ( i == hotButton ) { testButtons[i].set(buttonsOnPix); } else { testButtons[i].set(buttonsOffPix); } } else { testButtons[i].set(buttonsNonePix); } } } void InputDialogImpl::updateTestAxes( Glib::ustring const& key, GdkDevice* dev ) { static gdouble epsilon = 0.0001; { Glib::RefPtr treeSel = tree.get_selection(); Gtk::TreeModel::iterator iter = treeSel->get_selected(); if (iter) { Gtk::TreeModel::Row row = *iter; Glib::ustring val = row[cols.description]; InputDevice const * idev = row[cols.device]; if ( !idev || (idev->getId() != key) ) { dev = 0; } } } for ( gint i = 0; i < static_cast(G_N_ELEMENTS(testAxes)); i++ ) { if ( axesMap[key].find(i) != axesMap[key].end() ) { switch ( axesMap[key][i].first ) { case 0: case 1: testAxes[i].set(axisNonePix); if ( dev && (i < static_cast(G_N_ELEMENTS(axesValues)) ) ) { axesValues[i].set_sensitive(false); } break; case 2: testAxes[i].set(axisOffPix); axesValues[i].set_sensitive(true); if ( dev && (i < static_cast(G_N_ELEMENTS(axesValues)) ) ) { if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) { axesValues[i].set_sensitive(true); axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) ); } gchar* str = g_strdup_printf("%f", axesMap[key][i].second); axesValues[i].set_text(str); g_free(str); } break; case 3: testAxes[i].set(axisOnPix); axesValues[i].set_sensitive(true); if ( dev && (i < static_cast(G_N_ELEMENTS(axesValues)) ) ) { if ( (dev->axes[i].max - dev->axes[i].min) > epsilon ) { axesValues[i].set_sensitive(true); axesValues[i].set_fraction( (axesMap[key][i].second- dev->axes[i].min) / (dev->axes[i].max - dev->axes[i].min) ); } gchar* str = g_strdup_printf("%f", axesMap[key][i].second); axesValues[i].set_text(str); g_free(str); } } } else { testAxes[i].set(axisNonePix); } } if ( !dev ) { for ( gint i = 0; i < static_cast(G_N_ELEMENTS(axesValues)); i++ ) { axesValues[i].set_fraction(0.0); axesValues[i].set_text(""); axesValues[i].set_sensitive(false); } } } void InputDialogImpl::mapAxesValues( Glib::ustring const& key, guint numAxes, gdouble const * axes, GdkDevice* dev ) { static gdouble epsilon = 0.0001; if ( (numAxes > 0) && axes) { for ( guint axisNum = 0; axisNum < numAxes; axisNum++ ) { // 0 == new, 1 == set value, 2 == changed value, 3 == active gdouble diff = axesMap[key][axisNum].second - axes[axisNum]; switch(axesMap[key][axisNum].first) { case 0: { axesMap[key][axisNum].first = 1; axesMap[key][axisNum].second = axes[axisNum]; } break; case 1: { if ( (diff > epsilon) || (diff < -epsilon) ) { // g_message("Axis %d changed on %s]", axisNum, key.c_str()); axesMap[key][axisNum].first = 3; axesMap[key][axisNum].second = axes[axisNum]; updateTestAxes(key, dev); DeviceManager::getManager().addAxis(key, axisNum); } } break; case 2: { if ( (diff > epsilon) || (diff < -epsilon) ) { axesMap[key][axisNum].first = 3; axesMap[key][axisNum].second = axes[axisNum]; updateTestAxes(key, dev); } } break; case 3: { if ( (diff > epsilon) || (diff < -epsilon) ) { axesMap[key][axisNum].second = axes[axisNum]; } else { axesMap[key][axisNum].first = 2; updateTestAxes(key, dev); } } } } } // std::map > > axesMap; } Glib::ustring InputDialogImpl::getKeyFor( GdkDevice* device ) { Glib::ustring key; switch ( device->source ) { case GDK_SOURCE_MOUSE: key = "M:"; break; case GDK_SOURCE_CURSOR: key = "C:"; break; case GDK_SOURCE_PEN: key = "P:"; break; case GDK_SOURCE_ERASER: key = "E:"; break; default: key = "?:"; } key += device->name; return key; } bool InputDialogImpl::eventSnoop(GdkEvent* event) { int modmod = 0; GdkInputSource source = lastSourceSeen; Glib::ustring devName = lastDevnameSeen; Glib::ustring key; gint hotButton = -1; switch ( event->type ) { case GDK_KEY_PRESS: case GDK_KEY_RELEASE: { GdkEventKey* keyEvt = reinterpret_cast(event); gchar* name = gtk_accelerator_name(keyEvt->keyval, static_cast(keyEvt->state)); keyVal.set_label(name); // g_message("%d KEY state:0x%08x 0x%04x [%s]", keyEvt->type, keyEvt->state, keyEvt->keyval, name); g_free(name); } break; case GDK_BUTTON_PRESS: modmod = 1; // fallthrough case GDK_BUTTON_RELEASE: { GdkEventButton* btnEvt = reinterpret_cast(event); if ( btnEvt->device ) { key = getKeyFor(btnEvt->device); source = btnEvt->device->source; devName = btnEvt->device->name; mapAxesValues(key, btnEvt->device->num_axes, btnEvt->axes, btnEvt->device); if ( buttonMap[key].find(btnEvt->button) == buttonMap[key].end() ) { // g_message("New button found for %s = %d", key.c_str(), btnEvt->button); buttonMap[key].insert(btnEvt->button); DeviceManager::getManager().addButton(key, btnEvt->button); } hotButton = modmod ? btnEvt->button : -1; updateTestButtons(key, hotButton); } gchar* name = gtk_accelerator_name(0, static_cast(btnEvt->state)); keyVal.set_label(name); // g_message("%d BTN state:0x%08x %c %4d [%s] dev:%p [%s] ", // btnEvt->type, btnEvt->state, // (modmod ? '+':'-'), // btnEvt->button, name, btnEvt->device, // (btnEvt->device ? btnEvt->device->name : "null") // ); g_free(name); } break; case GDK_MOTION_NOTIFY: { GdkEventMotion* btnMtn = reinterpret_cast(event); if ( btnMtn->device ) { key = getKeyFor(btnMtn->device); source = btnMtn->device->source; devName = btnMtn->device->name; mapAxesValues(key, btnMtn->device->num_axes, btnMtn->axes, btnMtn->device); } gchar* name = gtk_accelerator_name(0, static_cast(btnMtn->state)); keyVal.set_label(name); // 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, // name, btnMtn->device, // (btnMtn->device ? btnMtn->device->name : "null"), // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 0)) ? btnMtn->axes[0]:0), // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 1)) ? btnMtn->axes[1]:0), // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 2)) ? btnMtn->axes[2]:0), // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 3)) ? btnMtn->axes[3]:0), // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 4)) ? btnMtn->axes[4]:0), // ((btnMtn->device && btnMtn->axes && (btnMtn->device->num_axes > 5)) ? btnMtn->axes[5]:0) // ); g_free(name); } break; default: ;// nothing } if ( (lastSourceSeen != source) || (lastDevnameSeen != devName) ) { switch (source) { case GDK_SOURCE_MOUSE: { testThumb.set(corePix); } break; case GDK_SOURCE_CURSOR: { // g_message("flip to cursor"); testThumb.set(mousePix); } break; case GDK_SOURCE_PEN: { if (devName == "pad") { // g_message("flip to pad"); testThumb.set(sidebuttonsPix); } else { // g_message("flip to pen"); testThumb.set(tipPix); } } break; case GDK_SOURCE_ERASER: { // g_message("flip to eraser"); testThumb.set(eraserPix); } break; // default: // g_message("gurgle"); } updateTestButtons(key, hotButton); lastSourceSeen = source; lastDevnameSeen = devName; } return false; } } // end namespace Inkscape } // end namespace UI } // end namespace Dialog /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :