diff --git a/share/extensions b/share/extensions index 6c9b68507be427bffba23507bbaacf3f8a0f3752..d99349e15e2342dd9af99afb345a98df02ab1d1b 160000 --- a/share/extensions +++ b/share/extensions @@ -1 +1 @@ -Subproject commit 6c9b68507be427bffba23507bbaacf3f8a0f3752 +Subproject commit d99349e15e2342dd9af99afb345a98df02ab1d1b diff --git a/share/ui/style.css b/share/ui/style.css index d82029c8df7eacb5c1c8d67f9c9416d8c8ae403d..ba040920d4de7caa81cd9022ed4e2e3c6ff4a952 100644 --- a/share/ui/style.css +++ b/share/ui/style.css @@ -407,12 +407,12 @@ SPCanvas { } /* Keep palette scrollbar from generating warnings. */ -#PreviewHolderScroller .vertical slider { +.PreviewHolderScroller .vertical slider { min-height: 15px; } /* Override minimum size of container children for palette. */ -#ColorItemPreview { +.ColorItemPreview { min-height: 5px; min-width: 5px; } diff --git a/share/ui/win32.css b/share/ui/win32.css index 3b978bf87d199c9840318bd76f138140031a0353..51383a1cf02085ba94ada9db0ce3eb2eb9bf7f68 100644 --- a/share/ui/win32.css +++ b/share/ui/win32.css @@ -119,7 +119,7 @@ spinbutton entry:disabled { /* Fix hidden palette */ -#InkscapePanel #PreviewHolderGrid { +#InkscapePanel .PreviewHolderGrid { padding-bottom: 20px; } diff --git a/src/3rdparty/2geom b/src/3rdparty/2geom index 9d38946b7d7a0486a4a75669008112d306309d9e..651c48a76e2c0f361feba8a66d4d693a2a0605a9 160000 --- a/src/3rdparty/2geom +++ b/src/3rdparty/2geom @@ -1 +1 @@ -Subproject commit 9d38946b7d7a0486a4a75669008112d306309d9e +Subproject commit 651c48a76e2c0f361feba8a66d4d693a2a0605a9 diff --git a/src/ui/contextmenu.cpp b/src/ui/contextmenu.cpp index 87111d3e3358436e0209031c530f8cad96390aca..3c47b96ac0e5db2327b8100988485decfd440481 100644 --- a/src/ui/contextmenu.cpp +++ b/src/ui/contextmenu.cpp @@ -289,8 +289,8 @@ void ContextMenu::AppendItemFromVerb(Inkscape::Verb *verb) label->set_accel_widget(*item); // If there is an image associated with the action, then we can add it as an icon for the menu item - if (_show_icons && action->image) { - item->set_name("ImageMenuItem"); // custom name to identify our "ImageMenuItems" + if (_show_icon && action->image) { + item->get_style_context()->add_class("ImageMenuItem"); // custom name to identify our "ImageMenuItems" auto const icon = Gtk::manage(sp_get_icon_image(action->image, Gtk::ICON_SIZE_MENU)); // create a box to hold icon and label as GtkMenuItem derives from GtkBin and can only hold one child diff --git a/src/ui/dialog/color-item.cpp b/src/ui/dialog/color-item.cpp index 1daadd90ed39a8dc8c3c23fea4a570a817db5ee3..ff5de009e11916c8337ba6de5e4776a024852a7f 100644 --- a/src/ui/dialog/color-item.cpp +++ b/src/ui/dialog/color-item.cpp @@ -459,7 +459,7 @@ Gtk::Widget* ColorItem::_getPreview(UI::Widget::PreviewStyle style, widget = lbl; } else { auto preview = Gtk::manage(new UI::Widget::Preview()); - preview->set_name("ColorItemPreview"); + preview->get_style_context()->add_class("ColorItemPreview"); _regenPreview(preview); diff --git a/src/ui/dialog/dialog-multipaned.cpp b/src/ui/dialog/dialog-multipaned.cpp index 2e414f3b31d4bc9883c015021523a7a04155e96a..5625fa5f2073dbe243363f812b12c0f09e0ec063 100644 --- a/src/ui/dialog/dialog-multipaned.cpp +++ b/src/ui/dialog/dialog-multipaned.cpp @@ -60,7 +60,7 @@ MyDropZone::MyDropZone(Gtk::Orientation orientation) , Gtk::Orientable() , Gtk::EventBox() { - set_name("MultipanedDropZone"); + get_style_context()->add_class("MultipanedDropZone"); set_orientation(orientation); set_size(DROPZONE_SIZE); @@ -108,7 +108,7 @@ MyHandle::MyHandle(Gtk::Orientation orientation, int size = get_handle_size()) , _cross_size(0) , _child(nullptr) { - set_name("MultipanedHandle"); + get_style_context()->add_class("MultipanedHandle"); set_orientation(orientation); add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK); @@ -335,7 +335,7 @@ DialogMultipaned::DialogMultipaned(Gtk::Orientation orientation) , Gtk::Container() , _empty_widget(nullptr) { - set_name("DialogMultipaned"); + get_style_context()->add_class("DialogMultipaned"); set_orientation(orientation); set_has_window(false); set_redraw_on_allocate(false); diff --git a/src/ui/dialog/dialog-window.cpp b/src/ui/dialog/dialog-window.cpp index c361996537e042fb7332ce6edbaf74a2909dc9d9..7af277e334ad6e324c47c12cb8abd790e9899f7c 100644 --- a/src/ui/dialog/dialog-window.cpp +++ b/src/ui/dialog/dialog-window.cpp @@ -98,7 +98,7 @@ DialogWindow::DialogWindow(Gtk::Widget *page) // ================ Window ================== set_title(_title); - set_name(_title); + //set_name(_title); int window_width = INITIAL_WINDOW_WIDTH; int window_height = INITIAL_WINDOW_HEIGHT; diff --git a/src/ui/dialog/layers.cpp b/src/ui/dialog/layers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cdd95e2a632fe5b299a56c022ae883d82466422 --- /dev/null +++ b/src/ui/dialog/layers.cpp @@ -0,0 +1,982 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * A simple panel for layers + * + * Authors: + * Jon A. Cruz + * Abhishek Sharma + * + * Copyright (C) 2006,2010 Jon A. Cruz + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "layers.h" + +#include +#include +#include + +#include "desktop-style.h" +#include "desktop.h" +#include "document-undo.h" +#include "document.h" +#include "inkscape.h" +#include "layer-fns.h" +#include "layer-manager.h" +#include "selection-chemistry.h" +#include "verbs.h" + +#include "helper/action.h" +#include "ui/icon-loader.h" + +#include "include/gtkmm_version.h" + +#include "object/sp-root.h" + +#include "svg/css-ostringstream.h" + +#include "ui/icon-loader.h" +#include "ui/icon-names.h" +#include "ui/desktop/menu-icon-shift.h" +#include "ui/tools/tool-base.h" +#include "ui/widget/imagetoggler.h" + +//#define DUMP_LAYERS 1 + +namespace Inkscape { +namespace UI { +namespace Dialog { + +LayersPanel& LayersPanel::getInstance() +{ + return *new LayersPanel(); +} + +enum { + COL_VISIBLE = 1, + COL_LOCKED +}; + +enum { + BUTTON_NEW = 0, + BUTTON_RENAME, + BUTTON_TOP, + BUTTON_BOTTOM, + BUTTON_UP, + BUTTON_DOWN, + BUTTON_DUPLICATE, + BUTTON_DELETE, + BUTTON_SOLO, + BUTTON_SHOW_ALL, + BUTTON_HIDE_ALL, + BUTTON_LOCK_OTHERS, + BUTTON_LOCK_ALL, + BUTTON_UNLOCK_ALL, + DRAGNDROP +}; + +class LayersPanel::InternalUIBounce +{ +public: + int _actionCode; + SPObject* _target; +}; + +void LayersPanel::_styleButton( Gtk::Button& btn, SPDesktop *desktop, unsigned int code, char const* iconName, char const* fallback ) +{ + bool set = false; + + if ( iconName ) { + auto child = Glib::wrap(sp_get_icon_image(iconName, GTK_ICON_SIZE_SMALL_TOOLBAR)); + child->show(); + btn.add(*child); + btn.set_relief(Gtk::RELIEF_NONE); + set = true; + } + + if ( desktop ) { + Verb *verb = Verb::get( code ); + if ( verb ) { + SPAction *action = verb->get_action(Inkscape::ActionContext(desktop)); + if ( !set && action && action->image ) { + auto child = Glib::wrap(sp_get_icon_image(action->image, GTK_ICON_SIZE_SMALL_TOOLBAR)); + child->show(); + btn.add(*child); + set = true; + } + + if ( action && action->tip ) { + btn.set_tooltip_text (action->tip); + } + } + } + + if ( !set && fallback ) { + btn.set_label( fallback ); + } +} + + +Gtk::MenuItem& LayersPanel::_addPopupItem( SPDesktop *desktop, unsigned int code, int id ) +{ + Verb *verb = Verb::get( code ); + g_assert(verb); + SPAction *action = verb->get_action(Inkscape::ActionContext(desktop)); + + Gtk::MenuItem* item = Gtk::manage(new Gtk::MenuItem()); + + Gtk::Label *label = Gtk::manage(new Gtk::Label(action->name, true)); + label->set_xalign(0.0); + + if (_show_contextmenu_icons && action->image) { + item->get_style_context()->add_class("ImageMenuItem"); // custom name to identify our "ImageMenuItems" + Gtk::Image *icon = Gtk::manage(sp_get_icon_image(action->image, Gtk::ICON_SIZE_MENU)); + + // Create a box to hold icon and label as Gtk::MenuItem derives from GtkBin and can only hold one child + Gtk::Box *box = Gtk::manage(new Gtk::Box()); + box->pack_start(*icon, false, false, 0); + box->pack_start(*label, true, true, 0); + item->add(*box); + } else { + item->add(*label); + } + + item->signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &LayersPanel::_takeAction), id)); + _popupMenu.append(*item); + + return *item; +} + +void LayersPanel::_fireAction( unsigned int code ) +{ + if (auto desktop = getDesktop()) { + Verb *verb = Verb::get( code ); + if ( verb ) { + SPAction *action = verb->get_action(Inkscape::ActionContext(desktop)); + if ( action ) { + sp_action_perform( action, nullptr ); +// } else { +// g_message("no action"); + } +// } else { +// g_message("no verb for %u", code); + } +// } else { +// g_message("no active desktop"); + } +} + +// SP_VERB_LAYER_NEXT, +// SP_VERB_LAYER_PREV, +void LayersPanel::_takeAction( int val ) +{ + if ( !_pending ) { + _pending = new InternalUIBounce(); + _pending->_actionCode = val; + _pending->_target = _selectedLayer(); + Glib::signal_timeout().connect( sigc::mem_fun(*this, &LayersPanel::_executeAction), 0 ); + } +} + +bool LayersPanel::_executeAction() +{ + // Make sure selected layer hasn't changed since the action was triggered + auto desktop = getDesktop(); + if ( _pending + && ( + (_pending->_actionCode == BUTTON_NEW || _pending->_actionCode == DRAGNDROP) + || !( (desktop && desktop->currentLayer()) + && (desktop->currentLayer() != _pending->_target) + ) + ) + ) { + int val = _pending->_actionCode; +// SPObject* target = _pending->_target; + + switch ( val ) { + case BUTTON_NEW: + { + _fireAction( SP_VERB_LAYER_NEW ); + } + break; + case BUTTON_RENAME: + { + _fireAction( SP_VERB_LAYER_RENAME ); + } + break; + case BUTTON_TOP: + { + _fireAction( SP_VERB_LAYER_TO_TOP ); + } + break; + case BUTTON_BOTTOM: + { + _fireAction( SP_VERB_LAYER_TO_BOTTOM ); + } + break; + case BUTTON_UP: + { + _fireAction( SP_VERB_LAYER_RAISE ); + } + break; + case BUTTON_DOWN: + { + _fireAction( SP_VERB_LAYER_LOWER ); + } + break; + case BUTTON_DUPLICATE: + { + _fireAction( SP_VERB_LAYER_DUPLICATE ); + } + break; + case BUTTON_DELETE: + { + _fireAction( SP_VERB_LAYER_DELETE ); + } + break; + case BUTTON_SOLO: + { + _fireAction( SP_VERB_LAYER_SOLO ); + } + break; + case BUTTON_SHOW_ALL: + { + _fireAction( SP_VERB_LAYER_SHOW_ALL ); + } + break; + case BUTTON_HIDE_ALL: + { + _fireAction( SP_VERB_LAYER_HIDE_ALL ); + } + break; + case BUTTON_LOCK_OTHERS: + { + _fireAction( SP_VERB_LAYER_LOCK_OTHERS ); + } + break; + case BUTTON_LOCK_ALL: + { + _fireAction( SP_VERB_LAYER_LOCK_ALL ); + } + break; + case BUTTON_UNLOCK_ALL: + { + _fireAction( SP_VERB_LAYER_UNLOCK_ALL ); + } + break; + case DRAGNDROP: + { + _doTreeMove( ); + } + break; + } + + delete _pending; + _pending = nullptr; + } + + return false; +} + +class LayersPanel::ModelColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + + ModelColumns() + { + add(_colObject); + add(_colVisible); + add(_colLocked); + add(_colLabel); + } + ~ModelColumns() override = default; + + Gtk::TreeModelColumn _colObject; + Gtk::TreeModelColumn _colLabel; + Gtk::TreeModelColumn _colVisible; + Gtk::TreeModelColumn _colLocked; +}; + +void LayersPanel::_updateLayer( SPObject *layer ) { + _store->foreach( sigc::bind(sigc::mem_fun(*this, &LayersPanel::_checkForUpdated), layer) ); +} + +bool LayersPanel::_checkForUpdated(const Gtk::TreePath &/*path*/, const Gtk::TreeIter& iter, SPObject* layer) +{ + bool stopGoing = false; + Gtk::TreeModel::Row row = *iter; + if ( layer == row[_model->_colObject] ) + { + /* + * We get notified of layer update here (from layer->setLabel()) before layer->label() is set + * with the correct value (sp-object bug?). So use the inkscape:label attribute instead which + * has the correct value (bug #168351) + */ + //row[_model->_colLabel] = layer->label() ? layer->label() : layer->defaultLabel(); + gchar const *label = layer->getAttribute("inkscape:label"); + row[_model->_colLabel] = label ? label : layer->defaultLabel(); + row[_model->_colVisible] = SP_IS_ITEM(layer) ? !SP_ITEM(layer)->isHidden() : false; + row[_model->_colLocked] = SP_IS_ITEM(layer) ? SP_ITEM(layer)->isLocked() : false; + + stopGoing = true; + } + + return stopGoing; +} + +void LayersPanel::_selectLayer( SPObject *layer ) { + auto document = getDocument(); + if (!layer || (document && (layer == document->getRoot()))) { + if ( _tree.get_selection()->count_selected_rows() != 0 ) { + _tree.get_selection()->unselect_all(); + } + } else { + _store->foreach( sigc::bind(sigc::mem_fun(*this, &LayersPanel::_checkForSelected), layer) ); + } + + _checkTreeSelection(); +} + +bool LayersPanel::_checkForSelected(const Gtk::TreePath &path, const Gtk::TreeIter& iter, SPObject* layer) +{ + bool stopGoing = false; + + Gtk::TreeModel::Row row = *iter; + if ( layer == row[_model->_colObject] ) + { + _tree.expand_to_path( path ); + + Glib::RefPtr select = _tree.get_selection(); + + select->select(iter); + + stopGoing = true; + } + + return stopGoing; +} + +void LayersPanel::_layersChanged() +{ + if (auto document = getDocument()) { + if (auto root = document->getRoot()) { + _selectedConnection.block(); + auto desktop = getDesktop(); + if (desktop->layer_manager && desktop->layer_manager->includes( root ) ) { + SPObject* target = desktop->currentLayer(); + _store->clear(); + + #if DUMP_LAYERS + g_message("root:%p {%s} [%s]", root, root->id, root->label() ); + #endif // DUMP_LAYERS + _addLayer( document, root, nullptr, target, 0 ); + } + _selectedConnection.unblock(); + } + } +} + +void LayersPanel::_addLayer( SPDocument* doc, SPObject* layer, Gtk::TreeModel::Row* parentRow, SPObject* target, int level ) +{ + auto desktop = getDesktop(); + if (desktop && desktop->layer_manager && layer && (level < _maxNestDepth) ) { + unsigned int counter = desktop->layer_manager->childCount(layer); + for ( unsigned int i = 0; i < counter; i++ ) { + SPObject *child = desktop->layer_manager->nthChildOf(layer, i); + if ( child ) { +#if DUMP_LAYERS + g_message(" %3d layer:%p {%s} [%s]", level, child, child->getId(), child->label() ); +#endif // DUMP_LAYERS + + Gtk::TreeModel::iterator iter = parentRow ? _store->prepend(parentRow->children()) : _store->prepend(); + Gtk::TreeModel::Row row = *iter; + row[_model->_colObject] = child; + row[_model->_colLabel] = child->defaultLabel(); + row[_model->_colVisible] = SP_IS_ITEM(child) ? !SP_ITEM(child)->isHidden() : false; + row[_model->_colLocked] = SP_IS_ITEM(child) ? SP_ITEM(child)->isLocked() : false; + + if ( target && child == target ) { + _tree.expand_to_path( _store->get_path(iter) ); + + Glib::RefPtr select = _tree.get_selection(); + select->select(iter); + + _checkTreeSelection(); + } + + _addLayer( doc, child, &row, target, level + 1 ); + } + } + } +} + +SPObject* LayersPanel::_selectedLayer() +{ + SPObject* obj = nullptr; + + Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected(); + if ( iter ) { + Gtk::TreeModel::Row row = *iter; + obj = row[_model->_colObject]; + } + + return obj; +} + +void LayersPanel::_pushTreeSelectionToCurrent() +{ + auto desktop = getDesktop(); + if (desktop && desktop->layer_manager && desktop->currentRoot() ) { + SPObject* inTree = _selectedLayer(); + if ( inTree ) { + SPObject* curr = desktop->currentLayer(); + if (curr != inTree) { + desktop->layer_manager->setCurrentLayer(inTree); + } + } else { + desktop->layer_manager->setCurrentLayer(getDocument()->getRoot()); + } + } +} + +void LayersPanel::_checkTreeSelection() +{ + bool sensitive = false; + bool sensitiveNonTop = false; + bool sensitiveNonBottom = false; + if ( _tree.get_selection()->count_selected_rows() > 0 ) { + sensitive = true; + + SPObject* inTree = _selectedLayer(); + if ( inTree ) { + + sensitiveNonTop = (Inkscape::next_layer(inTree->parent, inTree) != nullptr); + sensitiveNonBottom = (Inkscape::previous_layer(inTree->parent, inTree) != nullptr); + + } + } + + + for (auto & it : _watching) { + it->set_sensitive( sensitive ); + } + for (auto & it : _watchingNonTop) { + it->set_sensitive( sensitiveNonTop ); + } + for (auto & it : _watchingNonBottom) { + it->set_sensitive( sensitiveNonBottom ); + } +} + +void LayersPanel::_preToggle( GdkEvent const *event ) +{ + + if ( _toggleEvent ) { + gdk_event_free(_toggleEvent); + _toggleEvent = nullptr; + } + + if ( event && (event->type == GDK_BUTTON_PRESS) ) { + // Make a copy so we can keep it around. + _toggleEvent = gdk_event_copy(const_cast(event)); + } +} + +void LayersPanel::_toggled( Glib::ustring const& str, int targetCol ) +{ + Gtk::TreeModel::Children::iterator iter = _tree.get_model()->get_iter(str); + Gtk::TreeModel::Row row = *iter; + + Glib::ustring tmp = row[_model->_colLabel]; + + SPObject* obj = row[_model->_colObject]; + SPItem* item = ( obj && SP_IS_ITEM(obj) ) ? SP_ITEM(obj) : nullptr; + + if (getDocument() && item) { + switch ( targetCol ) { + case COL_VISIBLE: + { + bool newValue = !row[_model->_colVisible]; + row[_model->_colVisible] = newValue; + item->setHidden( !newValue ); + item->updateRepr(); + DocumentUndo::done(getDocument(), SP_VERB_DIALOG_LAYERS, newValue? _("Unhide layer") : _("Hide layer")); + } + break; + + case COL_LOCKED: + { + bool newValue = !row[_model->_colLocked]; + row[_model->_colLocked] = newValue; + item->setLocked( newValue ); + item->updateRepr(); + DocumentUndo::done(getDocument(), SP_VERB_DIALOG_LAYERS, newValue? _("Lock layer") : _("Unlock layer")); + } + break; + } + } + Inkscape::SelectionHelper::fixSelection(getDesktop()); +} + +bool LayersPanel::_handleButtonEvent(GdkEventButton* event) +{ + static unsigned doubleclick = 0; + auto document = getDocument(); + auto desktop = getDesktop(); + + if ( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) ) { + // TODO - fix to a better is-popup function + Gtk::TreeModel::Path path; + int x = static_cast(event->x); + int y = static_cast(event->y); + if ( _tree.get_path_at_pos( x, y, path ) ) { + _checkTreeSelection(); + + _popupMenu.popup_at_pointer(reinterpret_cast(event)); + } + } + + if ( (event->type == GDK_BUTTON_PRESS) && (event->button == 1) + && (event->state & GDK_MOD1_MASK)) { + // Alt left click on the visible/lock columns - eat this event to keep row selection + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = nullptr; + int x = static_cast(event->x); + int y = static_cast(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) ) { + if (col == _tree.get_column(COL_VISIBLE-1) || + col == _tree.get_column(COL_LOCKED-1)) { + return true; + } + } + } + + // TODO - ImageToggler doesn't seem to handle Shift/Alt clicks - so we deal with them here. + if ( (event->type == GDK_BUTTON_RELEASE) && (event->button == 1) + && (event->state & (GDK_SHIFT_MASK | GDK_MOD1_MASK))) { + + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col = nullptr; + int x = static_cast(event->x); + int y = static_cast(event->y); + int x2 = 0; + int y2 = 0; + if ( _tree.get_path_at_pos( x, y, path, col, x2, y2 ) ) { + if (event->state & GDK_SHIFT_MASK) { + // Shift left click on the visible/lock columns toggles "solo" mode + if (col == _tree.get_column(COL_VISIBLE - 1)) { + _takeAction(BUTTON_SOLO); + } else if (col == _tree.get_column(COL_LOCKED - 1)) { + _takeAction(BUTTON_LOCK_OTHERS); + } + } else if (event->state & GDK_MOD1_MASK) { + // Alt+left click on the visible/lock columns toggles "solo" mode and preserves selection + Gtk::TreeModel::iterator iter = _store->get_iter(path); + if (_store->iter_is_valid(iter)) { + Gtk::TreeModel::Row row = *iter; + SPObject *obj = row[_model->_colObject]; + if (col == _tree.get_column(COL_VISIBLE - 1)) { + desktop->toggleLayerSolo( obj ); + DocumentUndo::maybeDone(document, "layer:solo", SP_VERB_LAYER_SOLO, _("Toggle layer solo")); + } else if (col == _tree.get_column(COL_LOCKED - 1)) { + desktop->toggleLockOtherLayers(obj); + DocumentUndo::maybeDone(document, "layer:lockothers", SP_VERB_LAYER_LOCK_OTHERS, _("Lock other layers")); + } + } + } + } + } + + if ( (event->type == GDK_2BUTTON_PRESS) && (event->button == 1) ) { + doubleclick = 1; + } + + return false; +} + +/* + * Drap and drop within the tree + * Save the drag source and drop target SPObjects and if its a drag between layers or into (sublayer) a layer + */ +bool LayersPanel::_handleDragDrop(const Glib::RefPtr& /*context*/, int x, int y, guint /*time*/) +{ + int cell_x = 0, cell_y = 0; + Gtk::TreeModel::Path target_path; + Gtk::TreeView::Column *target_column; + SPObject *selected = _selectedLayer(); + + _dnd_into = false; + _dnd_target = nullptr; + _dnd_source = ( selected && SP_IS_ITEM(selected) ) ? SP_ITEM(selected) : nullptr; + + if (_tree.get_path_at_pos (x, y, target_path, target_column, cell_x, cell_y)) { + // Are we before, inside or after the drop layer + Gdk::Rectangle rect; + _tree.get_background_area (target_path, *target_column, rect); + int cell_height = rect.get_height(); + _dnd_into = (cell_y > (int)(cell_height * 1/3) && cell_y <= (int)(cell_height * 2/3)); + if (cell_y > (int)(cell_height * 2/3)) { + Gtk::TreeModel::Path next_path = target_path; + next_path.next(); + if (_store->iter_is_valid(_store->get_iter(next_path))) { + target_path = next_path; + } else { + // Dragging to the "end" + Gtk::TreeModel::Path up_path = target_path; + up_path.up(); + if (_store->iter_is_valid(_store->get_iter(up_path))) { + // Drop into parent + target_path = up_path; + _dnd_into = true; + } else { + // Drop into the top level + _dnd_target = nullptr; + } + } + } + Gtk::TreeModel::iterator iter = _store->get_iter(target_path); + if (_store->iter_is_valid(iter)) { + Gtk::TreeModel::Row row = *iter; + SPObject *obj = row[_model->_colObject]; + _dnd_target = ( obj && SP_IS_ITEM(obj) ) ? SP_ITEM(obj) : nullptr; + } + } + + _takeAction(DRAGNDROP); + + return false; +} + +/* + * Move a layer in response to a drag & drop action + */ +void LayersPanel::_doTreeMove( ) +{ + if (_dnd_source && _dnd_source->getRepr() ) { + if (!_dnd_target) { + _dnd_source->doWriteTransform(_dnd_source->i2doc_affine() * _dnd_source->document->getRoot()->i2doc_affine().inverse()); + } else { + SPItem* parent = _dnd_into ? _dnd_target : dynamic_cast(_dnd_target->parent); + if(parent){ + Geom::Affine move = _dnd_source->i2doc_affine() * parent->i2doc_affine().inverse(); + _dnd_source->doWriteTransform(move); + } + } + _dnd_source->moveTo(_dnd_target, _dnd_into); + _selectLayer(_dnd_source); + _dnd_source = nullptr; + DocumentUndo::done(getDocument(), SP_VERB_NONE, _("Move layer")); + } +} + + +void LayersPanel::_handleEdited(const Glib::ustring& path, const Glib::ustring& new_text) +{ + Gtk::TreeModel::iterator iter = _tree.get_model()->get_iter(path); + Gtk::TreeModel::Row row = *iter; + + _renameLayer(row, new_text); +} + +void LayersPanel::_renameLayer(Gtk::TreeModel::Row row, const Glib::ustring& name) +{ + auto desktop = getDesktop(); + if (row && desktop && desktop->layer_manager) { + SPObject* obj = row[_model->_colObject]; + if (obj) { + gchar const* oldLabel = obj->label(); + if ( !name.empty() && (!oldLabel || name != oldLabel) ) { + desktop->layer_manager->renameLayer( obj, name.c_str(), FALSE ); + DocumentUndo::done(getDocument(), SP_VERB_NONE, _("Rename layer")); + } + + } + } +} + +bool LayersPanel::_rowSelectFunction( Glib::RefPtr const & /*model*/, Gtk::TreeModel::Path const & /*path*/, bool currentlySelected ) +{ + bool val = true; + if ( !currentlySelected && _toggleEvent ) + { + GdkEvent* event = gtk_get_current_event(); + if ( event ) { + // (keep these checks separate, so we know when to call gdk_event_free() + if ( event->type == GDK_BUTTON_PRESS ) { + GdkEventButton const* target = reinterpret_cast(_toggleEvent); + GdkEventButton const* evtb = reinterpret_cast(event); + + if ( (evtb->window == target->window) + && (evtb->send_event == target->send_event) + && (evtb->time == target->time) + && (evtb->state == target->state) + ) + { + // Ooooh! It's a magic one + val = false; + } + } + gdk_event_free(event); + } + } + return val; +} + +/** + * Constructor + */ +LayersPanel::LayersPanel() + : DialogBase("/dialogs/layers", "Layers") + , _maxNestDepth(20) + , _model(nullptr) + , _pending(nullptr) + , _toggleEvent(nullptr) + , _compositeSettings(SP_VERB_DIALOG_LAYERS, "layers", + UI::Widget::SimpleFilterModifier::ISOLATION | + UI::Widget::SimpleFilterModifier::BLEND | + UI::Widget::SimpleFilterModifier::OPACITY | + UI::Widget::SimpleFilterModifier::BLUR) + , _layersPage(Gtk::ORIENTATION_VERTICAL) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + _maxNestDepth = prefs->getIntLimited("/dialogs/layers/maxDepth", 20, 1, 1000); + + ModelColumns *zoop = new ModelColumns(); + _model = zoop; + + _store = Gtk::TreeStore::create( *zoop ); + + _tree.set_model( _store ); + _tree.set_headers_visible(false); + _tree.set_reorderable(true); + _tree.enable_model_drag_dest (Gdk::ACTION_MOVE); + + Inkscape::UI::Widget::ImageToggler *eyeRenderer = Gtk::manage( new Inkscape::UI::Widget::ImageToggler( + INKSCAPE_ICON("object-visible"), INKSCAPE_ICON("object-hidden")) ); + int visibleColNum = _tree.append_column("V", *eyeRenderer) - 1; + eyeRenderer->signal_pre_toggle().connect( sigc::mem_fun(*this, &LayersPanel::_preToggle) ); + eyeRenderer->signal_toggled().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_toggled), (int)COL_VISIBLE) ); + eyeRenderer->property_activatable() = true; + Gtk::TreeViewColumn* col = _tree.get_column(visibleColNum); + if ( col ) { + col->add_attribute( eyeRenderer->property_active(), _model->_colVisible ); + } + + + Inkscape::UI::Widget::ImageToggler * renderer = Gtk::manage( new Inkscape::UI::Widget::ImageToggler( + INKSCAPE_ICON("object-locked"), INKSCAPE_ICON("object-unlocked")) ); + int lockedColNum = _tree.append_column("L", *renderer) - 1; + renderer->signal_pre_toggle().connect( sigc::mem_fun(*this, &LayersPanel::_preToggle) ); + renderer->signal_toggled().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_toggled), (int)COL_LOCKED) ); + renderer->property_activatable() = true; + col = _tree.get_column(lockedColNum); + if ( col ) { + col->add_attribute( renderer->property_active(), _model->_colLocked ); + } + + _text_renderer = Gtk::manage(new Gtk::CellRendererText()); + int nameColNum = _tree.append_column("Name", *_text_renderer) - 1; + _name_column = _tree.get_column(nameColNum); + _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel); + + _tree.set_expander_column( *_tree.get_column(nameColNum) ); + _tree.set_search_column(nameColNum + 1); + _tree.set_enable_search(false); + + _compositeSettings.setSubject(&_subject); + + _selectedConnection = _tree.get_selection()->signal_changed().connect( sigc::mem_fun(*this, &LayersPanel::_pushTreeSelectionToCurrent) ); + _tree.get_selection()->set_select_function( sigc::mem_fun(*this, &LayersPanel::_rowSelectFunction) ); + + _tree.signal_drag_drop().connect( sigc::mem_fun(*this, &LayersPanel::_handleDragDrop), false); + + _text_renderer->property_editable() = true; + _text_renderer->signal_edited().connect( sigc::mem_fun(*this, &LayersPanel::_handleEdited) ); + + _tree.signal_button_press_event().connect( sigc::mem_fun(*this, &LayersPanel::_handleButtonEvent), false ); + _tree.signal_button_release_event().connect( sigc::mem_fun(*this, &LayersPanel::_handleButtonEvent), false ); + + _scroller.add( _tree ); + _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ); + _scroller.set_shadow_type(Gtk::SHADOW_IN); + Gtk::Requisition sreq; + Gtk::Requisition sreq_natural; + _scroller.get_preferred_size(sreq_natural, sreq); + int minHeight = 70; + if (sreq.height < minHeight) { + // Set a min height to see the layers when used with Ubuntu liboverlay-scrollbar + _scroller.set_size_request(sreq.width, minHeight); + } + + _watching.push_back( &_compositeSettings ); + + _layersPage.pack_start( _scroller, Gtk::PACK_EXPAND_WIDGET ); + _layersPage.pack_end(_compositeSettings, Gtk::PACK_SHRINK); + _layersPage.pack_end(_buttonsRow, Gtk::PACK_SHRINK); + + pack_start(_layersPage, Gtk::PACK_EXPAND_WIDGET); + + // targetDesktop is not actually used (eventually _fire_action is called which uses current desktop value). + SPDesktop *targetDesktop = dynamic_cast(getApp()->get_active_view()); + + Gtk::Button* btn = Gtk::manage( new Gtk::Button() ); + _styleButton( *btn, targetDesktop, SP_VERB_LAYER_NEW, INKSCAPE_ICON("list-add"), C_("Layers", "New") ); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_NEW) ); + _buttonsSecondary.pack_start(*btn, Gtk::PACK_SHRINK); + + btn = Gtk::manage( new Gtk::Button() ); + _styleButton( *btn, targetDesktop, SP_VERB_LAYER_TO_BOTTOM, INKSCAPE_ICON("go-bottom"), C_("Layers", "Bot") ); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_BOTTOM) ); + _watchingNonBottom.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + + btn = Gtk::manage( new Gtk::Button() ); + _styleButton( *btn, targetDesktop, SP_VERB_LAYER_LOWER, INKSCAPE_ICON("go-down"), C_("Layers", "Dn") ); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_DOWN) ); + _watchingNonBottom.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + + btn = Gtk::manage( new Gtk::Button() ); + _styleButton( *btn, targetDesktop, SP_VERB_LAYER_RAISE, INKSCAPE_ICON("go-up"), C_("Layers", "Up") ); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_UP) ); + _watchingNonTop.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + + btn = Gtk::manage( new Gtk::Button() ); + _styleButton( *btn, targetDesktop, SP_VERB_LAYER_TO_TOP, INKSCAPE_ICON("go-top"), C_("Layers", "Top") ); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_TOP) ); + _watchingNonTop.push_back( btn ); + _buttonsPrimary.pack_end(*btn, Gtk::PACK_SHRINK); + +// btn = Gtk::manage( new Gtk::Button("Dup") ); +// btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_DUPLICATE) ); +// _buttonsRow.add( *btn ); + + btn = Gtk::manage( new Gtk::Button() ); + _styleButton( *btn, targetDesktop, SP_VERB_LAYER_DELETE, INKSCAPE_ICON("list-remove"), _("X") ); + btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_DELETE) ); + _watching.push_back( btn ); + _buttonsSecondary.pack_start(*btn, Gtk::PACK_SHRINK); + + _buttonsRow.pack_start(_buttonsSecondary, Gtk::PACK_EXPAND_WIDGET); + _buttonsRow.pack_end(_buttonsPrimary, Gtk::PACK_EXPAND_WIDGET); + + + + // ------------------------------------------------------- + { + _show_contextmenu_icons = prefs->getBool("/theme/menuIcons_layers", true); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_NEW, (int)BUTTON_NEW ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RENAME, (int)BUTTON_RENAME ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_SOLO, (int)BUTTON_SOLO ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_SHOW_ALL, (int)BUTTON_SHOW_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_HIDE_ALL, (int)BUTTON_HIDE_ALL ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_OTHERS, (int)BUTTON_LOCK_OTHERS ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_ALL, (int)BUTTON_LOCK_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_UNLOCK_ALL, (int)BUTTON_UNLOCK_ALL ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watchingNonTop.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RAISE, (int)BUTTON_UP ) ); + _watchingNonBottom.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOWER, (int)BUTTON_DOWN ) ); + + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_DUPLICATE, (int)BUTTON_DUPLICATE ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_DELETE, (int)BUTTON_DELETE ) ); + + _popupMenu.show_all_children(); + + // Install CSS to shift icons into the space reserved for toggles (i.e. check and radio items). + _popupMenu.signal_map().connect(sigc::bind(sigc::ptr_fun(shift_icons), &_popupMenu)); + } + // ------------------------------------------------------- + + + + for (auto & it : _watching) { + it->set_sensitive( false ); + } + for (auto & it : _watchingNonTop) { + it->set_sensitive( false ); + } + for (auto & it : _watchingNonBottom) { + it->set_sensitive( false ); + } + + setDesktop( targetDesktop ); + + show_all_children(); + + // restorePanelPrefs(); +} + +LayersPanel::~LayersPanel() +{ + _compositeSettings.setSubject(nullptr); + _layerChangedConnection.disconnect(); + _layerUpdatedConnection.disconnect(); + _changedConnection.disconnect(); + _subject.setDesktop(nullptr); + + if ( _model ) + { + delete _model; + _model = nullptr; + } + + if (_pending) { + delete _pending; + _pending = nullptr; + } + + if ( _toggleEvent ) + { + gdk_event_free( _toggleEvent ); + _toggleEvent = nullptr; + } +} + +void LayersPanel::desktopReplaced() +{ + _layerChangedConnection.disconnect(); + _layerUpdatedConnection.disconnect(); + _changedConnection.disconnect(); + _subject.setDesktop(getDesktop()); + if (auto desktop = getDesktop()) { + //setLabel(document->name); + LayerManager *mgr = desktop->layer_manager; + if (mgr) { + _layerChangedConnection = mgr->connectCurrentLayerChanged( sigc::mem_fun(*this, &LayersPanel::_selectLayer) ); + _layerUpdatedConnection = mgr->connectLayerDetailsChanged( sigc::mem_fun(*this, &LayersPanel::_updateLayer) ); + _changedConnection = mgr->connectChanged( sigc::mem_fun(*this, &LayersPanel::_layersChanged) ); + } + _layersChanged(); + } +} + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/prototype.cpp b/src/ui/dialog/prototype.cpp index 97649c882fcf3c5c397caf58feadbf89f14c542e..9096bfcb61961b097fde0591ce319e48384f5f6a 100644 --- a/src/ui/dialog/prototype.cpp +++ b/src/ui/dialog/prototype.cpp @@ -31,7 +31,7 @@ Prototype::Prototype() _label = Gtk::manage(new Gtk::Label(_name)); _label->set_line_wrap(); - _debug_button.set_name("PrototypeDebugButton"); + _debug_buttonget_style_context()->add_class("PrototypeDebugButton"); _debug_button.set_hexpand(); _debug_button.signal_clicked().connect(sigc::mem_fun(*this, &Prototype::on_click)); diff --git a/src/ui/previewholder.cpp b/src/ui/previewholder.cpp index 12a71c8e89c0919595d648493b917f198355a559..cb786454221108f2b4ec2997ea21461b0c2ab588 100644 --- a/src/ui/previewholder.cpp +++ b/src/ui/previewholder.cpp @@ -43,13 +43,13 @@ PreviewHolder::PreviewHolder() : _wrap(false), _border(UI::Widget::BORDER_NONE) { - set_name( "PreviewHolder" ); + get_style_context()->add_class("PreviewHolder" ); _scroller = Gtk::manage(new Gtk::ScrolledWindow()); - _scroller->set_name( "PreviewHolderScroller" ); + _scroller->get_style_context()->add_class("PreviewHolderScroller" ); _scroller->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); _insides = Gtk::manage(new Gtk::Grid()); - _insides->set_name( "PreviewHolderGrid" ); + _insides->get_style_context()->add_class("PreviewHolderGrid" ); _insides->set_column_spacing(8); _scroller->set_hexpand(); diff --git a/src/ui/widget/canvas-grid.cpp b/src/ui/widget/canvas-grid.cpp index a5ebe82c2fa336835de6e8345b6a729b4fd4d330..748d91e6c73b187c000ab4fd51fa81b64a1b1ce5 100644 --- a/src/ui/widget/canvas-grid.cpp +++ b/src/ui/widget/canvas-grid.cpp @@ -58,7 +58,7 @@ CanvasGrid::CanvasGrid(SPDesktopWidget *dtw) _hadj = Gtk::Adjustment::create(0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0); _hadj->signal_value_changed().connect(sigc::mem_fun(_dtw, &SPDesktopWidget::on_adjustment_value_changed)); _hscrollbar = Gtk::manage(new Gtk::Scrollbar(_hadj, Gtk::ORIENTATION_HORIZONTAL)); - _hscrollbar->set_name("CanvasScrollbar"); + _hscrollbar->set_name("CanvasHScrollbar"); _hscrollbar->set_hexpand(true); _hscrollbar->set_no_show_all(); @@ -66,7 +66,7 @@ CanvasGrid::CanvasGrid(SPDesktopWidget *dtw) _vadj = Gtk::Adjustment::create(0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0); _vadj->signal_value_changed().connect(sigc::mem_fun(_dtw, &SPDesktopWidget::on_adjustment_value_changed)); _vscrollbar = Gtk::manage(new Gtk::Scrollbar(_vadj, Gtk::ORIENTATION_VERTICAL)); - _vscrollbar->set_name("CanvasScrollbar"); + _vscrollbar->set_name("CanvasVScrollbar"); _vscrollbar->set_vexpand(true); _vscrollbar->set_no_show_all(); diff --git a/src/ui/widget/color-notebook.cpp b/src/ui/widget/color-notebook.cpp index 52983f068fbca0151f4275b3b07cd6143618dabd..c1183c04567571a9195dd1061c1a82dffd346a6d 100644 --- a/src/ui/widget/color-notebook.cpp +++ b/src/ui/widget/color-notebook.cpp @@ -58,7 +58,7 @@ ColorNotebook::ColorNotebook(SelectedColor &color) : Gtk::Grid() , _selected_color(color) { - set_name("ColorNotebook"); + get_style_context()->add_class("ColorNotebook"); _available_pages.push_back(new Page(new ColorScalesFactory(SP_COLOR_SCALES_MODE_HSL), "color-selector-hsx")); _available_pages.push_back(new Page(new ColorScalesFactory(SP_COLOR_SCALES_MODE_HSV), "color-selector-hsx")); diff --git a/src/ui/widget/color-preview.cpp b/src/ui/widget/color-preview.cpp index ab81c60eeb5e061e09087754d48677f2bb415601..7c751f87c912df6cdca56b8bd829253f1e5c691f 100644 --- a/src/ui/widget/color-preview.cpp +++ b/src/ui/widget/color-preview.cpp @@ -25,7 +25,7 @@ ColorPreview::ColorPreview (guint32 rgba) { _rgba = rgba; set_has_window(false); - set_name("ColorPreview"); + get_style_context()->add_class("ColorPreview"); } void diff --git a/src/ui/widget/color-wheel-selector.cpp b/src/ui/widget/color-wheel-selector.cpp index d7acdb63709f54d211c4093c17b133a6b81fcb2b..7dcf61322d460290db6adf6ea0a89d1af06f2bfd 100644 --- a/src/ui/widget/color-wheel-selector.cpp +++ b/src/ui/widget/color-wheel-selector.cpp @@ -38,7 +38,7 @@ ColorWheelSelector::ColorWheelSelector(SelectedColor &color) , _wheel(nullptr) , _slider(nullptr) { - set_name("ColorWheelSelector"); + get_style_context()->add_class("ColorWheelSelector"); _initUI(); _color_changed_connection = color.signal_changed.connect(sigc::mem_fun(this, &ColorWheelSelector::_colorChanged)); diff --git a/src/ui/widget/font-selector-toolbar.cpp b/src/ui/widget/font-selector-toolbar.cpp index 68c5e790059bc023f546f71c5de169aa1ce8aeac..ded9f710722279db0e90cc3d644461c735ae1961 100644 --- a/src/ui/widget/font-selector-toolbar.cpp +++ b/src/ui/widget/font-selector-toolbar.cpp @@ -59,7 +59,7 @@ FontSelectorToolbar::FontSelectorToolbar () // Font family family_combo.set_model (font_lister->get_font_list()); family_combo.set_entry_text_column (0); - family_combo.set_name ("FontSelectorToolBar: Family"); + family_combo.set_name ("FontSelectorToolBar-Family"); family_combo.set_row_separator_func (&font_lister_separator_func); family_combo.clear(); // Clears all CellRenderer mappings. @@ -83,10 +83,10 @@ FontSelectorToolbar::FontSelectorToolbar () // Style style_combo.set_model (font_lister->get_style_list()); - style_combo.set_name ("FontSelectorToolbar: Style"); + style_combo.set_name ("FontSelectorToolbar-Style"); // Grid - set_name ("FontSelectorToolbar: Grid"); + set_name ("FontSelectorToolbar-Grid"); attach (family_combo, 0, 0, 1, 1); attach (style_combo, 1, 0, 1, 1); diff --git a/src/ui/widget/font-selector.cpp b/src/ui/widget/font-selector.cpp index eeab2d914c360c5373dcf4926b4c07bdb4c8c4c6..6dbe8a6e13a494d5273d48f760d987f79bf480b1 100644 --- a/src/ui/widget/font-selector.cpp +++ b/src/ui/widget/font-selector.cpp @@ -45,7 +45,7 @@ FontSelector::FontSelector (bool with_size, bool with_variations) family_treeview.set_row_separator_func (&font_lister_separator_func); family_treeview.set_model (font_lister->get_font_list()); - family_treeview.set_name ("FontSelector: Family"); + family_treeview.set_name ("FontSelector-Family"); family_treeview.set_headers_visible (false); family_treeview.append_column (family_treecolumn); diff --git a/src/ui/widget/ink-color-wheel.cpp b/src/ui/widget/ink-color-wheel.cpp index 447cf4253e4be3f16cea8a51babddc6120e0d51e..16641994dd6162e015a3094736c3f9857bd4f8a0 100644 --- a/src/ui/widget/ink-color-wheel.cpp +++ b/src/ui/widget/ink-color-wheel.cpp @@ -110,7 +110,7 @@ ColorWheel::ColorWheel() , _mode(DragMode::NONE) , _focus_on_ring(true) { - set_name("ColorWheel"); + get_style_context()->add_class("ColorWheel"); add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON_MOTION_MASK | diff --git a/src/ui/widget/ink-ruler.cpp b/src/ui/widget/ink-ruler.cpp index 899475ca3a06b673268352ca7150c4dd197bfbc4..563b363f2cd8d6792c939f768bcc82d9826895eb 100644 --- a/src/ui/widget/ink-ruler.cpp +++ b/src/ui/widget/ink-ruler.cpp @@ -53,7 +53,7 @@ Ruler::Ruler(Gtk::Orientation orientation) , _rect() , _position(0) { - set_name("InkRuler"); + get_style_context()->add_class("InkRuler"); set_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | // For guide creation diff --git a/src/ui/widget/ink-spinscale.cpp b/src/ui/widget/ink-spinscale.cpp index b977d219f7002d4be3aa2f449d462260e185de99..c20887bd4f004285ddf78067d5763b84044f15c3 100644 --- a/src/ui/widget/ink-spinscale.cpp +++ b/src/ui/widget/ink-spinscale.cpp @@ -36,7 +36,7 @@ InkScale::InkScale(Glib::RefPtr adjustment, Gtk::SpinButton* sp , _drag_start(0) , _drag_offset(0) { - set_name("InkScale"); + get_style_context()->add_class("InkScale"); // std::cout << "GType name: " << G_OBJECT_TYPE_NAME(gobj()) << std::endl; } @@ -234,7 +234,7 @@ InkSpinScale::InkSpinScale(double value, double lower, InkSpinScale::InkSpinScale(Glib::RefPtr adjustment) : _adjustment(std::move(adjustment)) { - set_name("InkSpinScale"); + get_style_context()->add_class("InkSpinScale"); g_assert (_adjustment->get_upper() - _adjustment->get_lower() > 0); diff --git a/src/ui/widget/layer-selector.cpp b/src/ui/widget/layer-selector.cpp index 0a7861dfdbcddae9f1361daa46f86de7bdfd9b27..384b02baa2a47da811f687124bd9753d08eae4b7 100644 --- a/src/ui/widget/layer-selector.cpp +++ b/src/ui/widget/layer-selector.cpp @@ -45,7 +45,7 @@ public: , _a(nullptr) , _b(nullptr) { - set_name("AlternateIcons"); + get_style_context()->add_class("AlternateIcons"); if (!a.empty()) { _a = Gtk::manage(sp_get_icon_image(a, size)); _a->set_no_show_all(true); diff --git a/src/ui/widget/notebook-page.cpp b/src/ui/widget/notebook-page.cpp index 876edb686c9b924c4da3753f30d6b1718dcd0a40..c28d8700c26bf87aeb4cd792cfb88f27b77c27b9 100644 --- a/src/ui/widget/notebook-page.cpp +++ b/src/ui/widget/notebook-page.cpp @@ -22,7 +22,7 @@ NotebookPage::NotebookPage(int n_rows, int n_columns, bool expand, bool fill, gu : Gtk::Box(Gtk::ORIENTATION_VERTICAL) , _table(Gtk::manage(new Gtk::Grid())) { - set_name("NotebookPage"); + get_style_context()->add_class("NotebookPage"); set_border_width(4); set_spacing(4); diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp index 1a4be9dfda48d6c6edb70f68bb7eb5bf0b57434e..6825238c6d11e1a07a3fff4eb5af672eaf8f49cb 100644 --- a/src/ui/widget/selected-style.cpp +++ b/src/ui/widget/selected-style.cpp @@ -139,7 +139,7 @@ SelectedStyle::SelectedStyle(bool /*layout*/) , _fill_empty_space("") , _opacity_blocked(false) { - set_name("SelectedStyle"); + get_style_context()->add_class("SelectedStyle"); _drop[0] = _drop[1] = nullptr; _dropEnabled[0] = _dropEnabled[1] = false; @@ -155,8 +155,8 @@ SelectedStyle::SelectedStyle(bool /*layout*/) _opacity_label.set_valign(Gtk::ALIGN_CENTER); _opacity_label.set_margin_top(0); _opacity_label.set_margin_bottom(0); - _stroke_width.set_name("monoStrokeWidth"); - _fill_empty_space.set_name("fillEmptySpace"); + _stroke_width.get_style_context()->add_class("monoStrokeWidth"); + _fill_empty_space.get_style_context()->add_class("fillEmptySpace"); _fill_label.set_margin_start(0); _fill_label.set_margin_end(0); diff --git a/src/ui/widget/spin-button-tool-item.cpp b/src/ui/widget/spin-button-tool-item.cpp index 0834bcfc00e5e93af4149eadf28d908e0072353a..4926c35bf340e4fa5d1e2740e9886f0bbf9e5419 100644 --- a/src/ui/widget/spin-button-tool-item.cpp +++ b/src/ui/widget/spin-button-tool-item.cpp @@ -371,7 +371,7 @@ SpinButtonToolItem::SpinButtonToolItem(const Glib::ustring name, { set_margin_start(3); set_margin_end(3); - set_name(_name); + get_style_context()->add_class(_name); // Handle popup menu _btn->signal_popup_menu().connect(sigc::mem_fun(*this, &SpinButtonToolItem::on_popup_menu), false); diff --git a/src/ui/widget/spin-scale.cpp b/src/ui/widget/spin-scale.cpp index 21aa525abd33f212dbe32364ffe5861617327ab4..591f71cef5a472e612600a2c2bba2b98ddd24ad4 100644 --- a/src/ui/widget/spin-scale.cpp +++ b/src/ui/widget/spin-scale.cpp @@ -29,7 +29,7 @@ SpinScale::SpinScale(const Glib::ustring label, double value, : AttrWidget(a, value) , _inkspinscale(value, lower, upper, step_increment, page_increment, 0) { - set_name("SpinScale"); + get_style_context()->add_class("SpinScale"); _inkspinscale.set_label (label); _inkspinscale.set_digits (digits); @@ -126,7 +126,7 @@ DualSpinScale::DualSpinScale(const Glib::ustring label1, const Glib::ustring lab //TRANSLATORS: "Link" means to _link_ two sliders together _link(C_("Sliders", "Link")) { - set_name("DualSpinScale"); + get_style_context()->add_class("DualSpinScale"); signal_value_changed().connect(signal_attr_changed().make_slot()); _s1.get_adjustment()->signal_value_changed().connect(_signal_value_changed.make_slot()); diff --git a/src/ui/widget/style-swatch.cpp b/src/ui/widget/style-swatch.cpp index a79f9d9dab24f88d61567159d1958cab94635524..ccdaa1fd443a303ba52a61d71dbe9a5d2cee4a2c 100644 --- a/src/ui/widget/style-swatch.cpp +++ b/src/ui/widget/style-swatch.cpp @@ -115,7 +115,8 @@ StyleSwatch::StyleSwatch(SPCSSAttr *css, gchar const *main_tip) _sw_unit(nullptr), _stroke(Gtk::ORIENTATION_HORIZONTAL) { - set_name("StyleSwatch"); + get_style_context()->add_class("StyleSwatch"); + _label[SS_FILL].set_markup(_("Fill:")); _label[SS_STROKE].set_markup(_("Stroke:")); diff --git a/src/ui/widget/unit-tracker.cpp b/src/ui/widget/unit-tracker.cpp index 7c52b3bd66df61c76018ee2423e863439448c4a6..39ff8b1bd7255ed31c7c8f4bdf95bf192ff8aa66 100644 --- a/src/ui/widget/unit-tracker.cpp +++ b/src/ui/widget/unit-tracker.cpp @@ -190,7 +190,7 @@ UnitTracker::create_tool_item(Glib::ustring const &label, auto combo = ComboToolItem::create(label, tooltip, "NotUsed", _store); combo->set_active(_active); combo->signal_changed().connect(sigc::mem_fun(*this, &UnitTracker::_unitChangedCB)); - combo->set_name("unit-tracker"); + combo->get_style_context()->add_class("unit-tracker"); combo->set_data(Glib::Quark("unit-tracker"), this); _combo_list.push_back(combo); return combo; diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 98744322d7d603a8f7d148f314ca51ca35b63a6a..2da96c03ebd19b48edee35e5fa7f5f7fec631fd8 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -1396,7 +1396,7 @@ void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags) if ( name == "TextToolbar" || name == "MeasureToolbar") continue; - auto tracker = dynamic_cast(sp_search_by_name_recursive(j, "unit-tracker")); + auto tracker = dynamic_cast(sp_search_by_class_recursive(j, "unit-tracker")); if (tracker) { // it's null when inkscape is first opened if (auto ptr = static_cast(tracker->get_data(Glib::Quark("unit-tracker")))) { diff --git a/src/widgets/spw-utilities.cpp b/src/widgets/spw-utilities.cpp index 13347d959f47f632e93da77550674f6d70f95645..493a9533dfe948c0f6353de651b301a49657c3bb 100644 --- a/src/widgets/spw-utilities.cpp +++ b/src/widgets/spw-utilities.cpp @@ -125,6 +125,42 @@ sp_search_by_name_recursive(Gtk::Widget *parent, const Glib::ustring& name) return nullptr; } +/** + * Returns a classed descendent of parent, which has the given class, or nullptr if there's none. + * + * \param[in] parent The widget to search + * \param[in] class A class on the desired child widget + * + * \return The specified child widget, or nullptr if it cannot be found + */ +Gtk::Widget * +sp_search_by_class_recursive(Gtk::Widget *parent, const Glib::ustring& classname) +{ + auto parent_bin = dynamic_cast(parent); + auto parent_container = dynamic_cast(parent); + + if (parent && parent->get_style_context()->has_class(classname)) { + return parent; + } + else if (parent_bin) { + auto child = parent_bin->get_child(); + return sp_search_by_class_recursive(child, classname); + } + else if (parent_container) { + auto children = parent_container->get_children(); + + for (auto child : children) { + auto tmp = sp_search_by_class_recursive(child, classname); + + if (tmp) { + return tmp; + } + } + } + + return nullptr; +} + /** * Returns the descendant of w which has the given key and value pair, or NULL if there's none. */ diff --git a/src/widgets/spw-utilities.h b/src/widgets/spw-utilities.h index fc37792b3ed586992b1584c5b922a0259a09a3c1..10a90397ed7dca2a52b63b76ed7ebe1cc937e02b 100644 --- a/src/widgets/spw-utilities.h +++ b/src/widgets/spw-utilities.h @@ -40,6 +40,9 @@ GtkWidget *sp_search_by_value_recursive(GtkWidget *w, gchar *key, gchar *value); Gtk::Widget * sp_search_by_name_recursive(Gtk::Widget *parent, const Glib::ustring& name); +Gtk::Widget * sp_search_by_class_recursive(Gtk::Widget *parent, + const Glib::ustring& classname); + Gtk::Widget* sp_traverse_widget_tree(Gtk::Widget* widget, const std::function& eval); Gtk::Widget* sp_find_focusable_widget(Gtk::Widget* widget);