diff --git a/share/icons/Tango/scalable/actions/horizontal.svg b/share/icons/Tango/scalable/actions/horizontal.svg new file mode 100644 index 0000000000000000000000000000000000000000..41f42514a9cbe6ab31282912db2a655b5820f1ad --- /dev/null +++ b/share/icons/Tango/scalable/actions/horizontal.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + 13 Nov 2018 + + + Ramón Miranda + + + + + see the license tab + + + + + icon ui + + + This is a little contribution with Inkscape Project +Ramon Miranda www.ramonmiranda.com (design and SVG file) +Jabiertxo (SVG stuff to integrate them in UI) + + + + + + + + + - + + + + Ramon Miranda +Jabier Arraiza + + + + + + + + + + + + + + + + diff --git a/share/icons/Tango/scalable/actions/vertical.svg b/share/icons/Tango/scalable/actions/vertical.svg new file mode 100644 index 0000000000000000000000000000000000000000..d625d286795d29c1bef33ac9f742bba8cd952276 --- /dev/null +++ b/share/icons/Tango/scalable/actions/vertical.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + 13 Nov 2018 + + + Ramón Miranda + + + + + see the license tab + + + + + icon ui + + + This is a little contribution with Inkscape Project +Ramon Miranda www.ramonmiranda.com (design and SVG file) +Jabiertxo (SVG stuff to integrate them in UI) + + + + + + + + + - + + + + Ramon Miranda +Jabier Arraiza + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/horizontal.svg b/share/icons/hicolor/scalable/actions/horizontal.svg new file mode 100644 index 0000000000000000000000000000000000000000..41f42514a9cbe6ab31282912db2a655b5820f1ad --- /dev/null +++ b/share/icons/hicolor/scalable/actions/horizontal.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + 13 Nov 2018 + + + Ramón Miranda + + + + + see the license tab + + + + + icon ui + + + This is a little contribution with Inkscape Project +Ramon Miranda www.ramonmiranda.com (design and SVG file) +Jabiertxo (SVG stuff to integrate them in UI) + + + + + + + + + - + + + + Ramon Miranda +Jabier Arraiza + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/vertical.svg b/share/icons/hicolor/scalable/actions/vertical.svg new file mode 100644 index 0000000000000000000000000000000000000000..d625d286795d29c1bef33ac9f742bba8cd952276 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/vertical.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + 13 Nov 2018 + + + Ramón Miranda + + + + + see the license tab + + + + + icon ui + + + This is a little contribution with Inkscape Project +Ramon Miranda www.ramonmiranda.com (design and SVG file) +Jabiertxo (SVG stuff to integrate them in UI) + + + + + + + + + - + + + + Ramon Miranda +Jabier Arraiza + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/horizontal-symbolic.svg b/share/icons/hicolor/symbolic/actions/horizontal-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..41f42514a9cbe6ab31282912db2a655b5820f1ad --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/horizontal-symbolic.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + 13 Nov 2018 + + + Ramón Miranda + + + + + see the license tab + + + + + icon ui + + + This is a little contribution with Inkscape Project +Ramon Miranda www.ramonmiranda.com (design and SVG file) +Jabiertxo (SVG stuff to integrate them in UI) + + + + + + + + + - + + + + Ramon Miranda +Jabier Arraiza + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/vertical-symbolic.svg b/share/icons/hicolor/symbolic/actions/vertical-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..d625d286795d29c1bef33ac9f742bba8cd952276 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/vertical-symbolic.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + 13 Nov 2018 + + + Ramón Miranda + + + + + see the license tab + + + + + icon ui + + + This is a little contribution with Inkscape Project +Ramon Miranda www.ramonmiranda.com (design and SVG file) +Jabiertxo (SVG stuff to integrate them in UI) + + + + + + + + + - + + + + Ramon Miranda +Jabier Arraiza + + + + + + + + + + + + + + + + diff --git a/share/ui/dialog-css.ui b/share/ui/dialog-css.ui index 6ffcabc71fef206d5d6259f8e6f2f4b88fcfbfbb..682df1b9315df186c408436d8a04d54def192a92 100644 --- a/share/ui/dialog-css.ui +++ b/share/ui/dialog-css.ui @@ -31,10 +31,16 @@ - + True False - Selector + + + True + False + Selector + + False @@ -42,6 +48,17 @@ 1 + + + True + True + + + False + True + 2 + + True @@ -51,7 +68,7 @@ False True - 2 + 4 @@ -68,7 +85,6 @@ True natural natural - False False True True diff --git a/share/ui/style.css b/share/ui/style.css index a7bed5d5963063568c0008921607703e72b8c098..6287a1622cecae3a2cf9bb31e7cb8f264c2f3538 100644 --- a/share/ui/style.css +++ b/share/ui/style.css @@ -66,7 +66,7 @@ * ::::::: Color based * ::::::: apply to colors * ::::::: scope widget and all his childs - * ".invert" invert colors + * ".inverted" invert colors * :::::::: Icon Based. * ".symbolic" Force icon symbolic * ".regular" Force colorful icons @@ -91,37 +91,22 @@ image { -gtk-icon-style: regular; } -.invert { - color: @theme_bg_color; - background-color: @theme_fg_color; - background-image: image(@theme_fg_color); -} - -.dark .forcebright { - color: @theme_bg_color; - background-color: @theme_fg_color; - background-image: image(@theme_fg_color); -} - +.inverted, +.dark .forcebright, .bright .forcedark { color: @theme_bg_color; background-color: @theme_fg_color; background-image: image(@theme_fg_color); } -.invert :not(overshoot):not(undershoot):not(.rawimage) { - color: inherit; - background-color: inherit; -} -.dark .forcebright :not(overshoot):not(undershoot):not(.rawimage) { - color: inherit; - background-color: inherit; -} -.bright .forcedark :not(overshoot):not(undershoot):not(.rawimage) { +.inverted :not(menuitem):not(.rawstyle):not(overshoot):not(undershoot):not(selection), +.bright .forcedark :not(menuitem):not(.rawstyle):not(overshoot):not(undershoot):not(selection), +.dark .forcebright :not(menuitem):not(.rawstyle):not(overshoot):not(undershoot):not(selection) { color: inherit; background-color: inherit; + background-image: inherit; } .combobright * { @@ -225,8 +210,8 @@ spinbutton undershoot { #LPESelectorFlowBox flowboxchild:selected image, #LPESelectorFlowBox flowboxchild:selected label { - background-color: @theme_selected_bg_color; color: @theme_selected_fg_color; + background-color: @theme_selected_bg_color; background-image: image(@theme_selected_bg_color); } @@ -311,6 +296,41 @@ spinbutton undershoot { padding-bottom: 0; } +#XMLAndAttributesDialog .radio.image-button, +#SelectorsAndStyleDialog .radio.image-button { + margin: 0; + padding: 0px; + border-radius: 2px 0 0 2px; +} + +#XMLAndAttributesDialog .radio.image-button:last-child, +#SelectorsAndStyleDialog .radio.image-button:last-child { + border-radius: 0 2px 2px 0; + border-left-width: 0; +} + +#SelectorsAndStyleDialog treeview button { + border-width: 0; + margin: 0 0 1px 0; +} + +#SelectorsAndStyleDialog treeview button:nth-child(3), +#SelectorsAndStyleDialog treeview.style_sheet button:nth-child(4) { + border-width: 0 0 0 2px; + border-color: @theme_fg_color; + border-style: solid; + opacity: 0.5; +} + +#SelectorsAndStyleDialog treeview.style_sheet button:nth-child(3) { + border-width: 0; +} + +#SelectorsAndStyleDialog treeview button { + min-height: 3px; + font-size: 1px; +} + #InkRuler { font-size: 7pt; } diff --git a/src/inkscape.cpp b/src/inkscape.cpp index 6614417293576dbed083823fb06d458ad6b0830f..f76ea3c22731b923aff1729bfec4f0a5427f3643 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -526,16 +526,23 @@ Glib::ustring Application::get_symbolic_colors() css_str += colornamederror; css_str += ";}"; css_str += "#InkRuler,"; - css_str += "image:not(.rawimage)"; + /* ":not(.rawstyle) > image" works only on images in first level of widget container + if in the future we use a complex widget with more levels and we dont want to tweak the color + here, retaining default we can add more lines like ":not(.rawstyle) > > image" */ + css_str += ":not(.rawstyle) > image"; css_str += "{color:"; css_str += colornamed; css_str += ";}"; - css_str += ".dark .forcebright image:not(.rawimage),"; - css_str += ".bright .forcedark image:not(.rawimage),"; - css_str += ".dark image.forcebright:not(.rawimage),"; - css_str += ".bright image.forcedark:not(.rawimage),"; - css_str += ".invert image:not(.rawimage),"; - css_str += "image.invert:not(.rawimage)"; + css_str += ".dark .forcebright :not(.rawstyle) > image,"; + css_str += ".dark .forcebright image:not(.rawstyle),"; + css_str += ".bright .forcedark :not(.rawstyle) > image,"; + css_str += ".bright .forcedark image:not(.rawstyle),"; + css_str += ".dark :not(.rawstyle) > image.forcebright,"; + css_str += ".dark image.forcebright:not(.rawstyle),"; + css_str += ".bright :not(.rawstyle) > image.forcedark,"; + css_str += ".bright image.forcedark:not(.rawstyle),"; + css_str += ".inverse :not(.rawstyle) > image,"; + css_str += ".inverse image:not(.rawstyle)"; css_str += "{color:"; css_str += colornamed_inverse; css_str += ";}"; diff --git a/src/ui/dialog/attrdialog.cpp b/src/ui/dialog/attrdialog.cpp index 3db739059d2cc02d8070bec8e8ee3e4646496985..a499602ce4dc9f016636f24b89759aaf74b48920 100644 --- a/src/ui/dialog/attrdialog.cpp +++ b/src/ui/dialog/attrdialog.cpp @@ -58,7 +58,6 @@ namespace Inkscape { namespace UI { namespace Dialog { - /** * Constructor * A treeview whose each row corresponds to an XML attribute of a selected node @@ -72,6 +71,9 @@ AttrDialog::AttrDialog() set_size_request(20, 15); _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET); _treeView.set_headers_visible(true); + _treeView.set_hover_selection(true); + _treeView.set_activate_on_single_click(true); + _treeView.set_can_focus(false); _scrolledWindow.add(_treeView); _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); @@ -103,16 +105,19 @@ AttrDialog::AttrDialog() _nameRenderer->property_editable() = true; _nameRenderer->property_placeholder_text().set_value(_("Attribute Name")); _nameRenderer->signal_edited().connect(sigc::mem_fun(*this, &AttrDialog::nameEdited)); + _nameRenderer->signal_editing_started().connect(sigc::mem_fun(*this, &AttrDialog::startNameEdit)); _treeView.append_column(_("Name"), *_nameRenderer); _nameCol = _treeView.get_column(1); if (_nameCol) { - _nameCol->add_attribute(_nameRenderer->property_text(), _attrColumns._attributeName); + _nameCol->set_resizable(true); + _nameCol->add_attribute(_nameRenderer->property_text(), _attrColumns._attributeName); } status.set_halign(Gtk::ALIGN_START); status.set_valign(Gtk::ALIGN_CENTER); status.set_size_request(1, -1); status.set_markup(""); status.set_line_wrap(true); + status.get_style_context()->add_class("inksmall"); status_box.pack_start(status, TRUE, TRUE, 0); _getContents()->pack_end(status_box, false, false, 2); @@ -126,19 +131,44 @@ AttrDialog::AttrDialog() _valueRenderer->property_placeholder_text().set_value(_("Attribute Value")); _valueRenderer->property_ellipsize().set_value(Pango::ELLIPSIZE_MIDDLE); _valueRenderer->signal_edited().connect(sigc::mem_fun(*this, &AttrDialog::valueEdited)); + _valueRenderer->signal_editing_started().connect(sigc::mem_fun(*this, &AttrDialog::startValueEdit)); _treeView.append_column(_("Value"), *_valueRenderer); _valueCol = _treeView.get_column(2); if (_valueCol) { _valueCol->add_attribute(_valueRenderer->property_text(), _attrColumns._attributeValue); } + _popover = Gtk::manage(new Gtk::Popover()); + Gtk::Box *vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + Gtk::Box *hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); + _textview = Gtk::manage(new Gtk::TextView()); + _textview->set_wrap_mode(Gtk::WrapMode::WRAP_CHAR); + _textview->set_editable(true); + _textview->set_monospace(true); + _textview->set_border_width(6); + Glib::RefPtr textbuffer = Gtk::TextBuffer::create(); + textbuffer->set_text(""); + _textview->set_buffer(textbuffer); + _scrolled_text_view.add(*_textview); + _scrolled_text_view.set_max_content_height(450); + _scrolled_text_view.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + _scrolled_text_view.set_propagate_natural_width(true); + _update = Gtk::manage(new Gtk::Button(_("Update"))); + _update->signal_clicked().connect(sigc::mem_fun(*this, &AttrDialog::valueEditedPop)); + hbox->pack_end(*_update, Gtk::PACK_EXPAND_WIDGET, 3); + vbox->pack_start(_scrolled_text_view, Gtk::PACK_EXPAND_WIDGET, 3); + vbox->pack_start(*hbox, Gtk::PACK_EXPAND_WIDGET, 3); + _popover->add(*vbox); + _popover->hide(); + _popover->set_relative_to(_treeView); + _popover->set_position(Gtk::PositionType::POS_BOTTOM); + _popover->signal_closed().connect(sigc::mem_fun(*this, &AttrDialog::popClosed)); + _popover->get_style_context()->add_class("inverted"); attr_reset_context(0); _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET); - setDesktop(getDesktop()); + _updating = false; } - - /** * @brief AttrDialog::~AttrDialog * Class destructor @@ -152,6 +182,71 @@ AttrDialog::~AttrDialog() _message_changed_connection.~connection(); } +void AttrDialog::startNameEdit(Gtk::CellEditable *cell, const Glib::ustring &path) +{ + Gtk::Entry *entry = dynamic_cast(cell); + entry->signal_key_press_event().connect(sigc::bind(sigc::mem_fun(*this, &AttrDialog::onNameKeyPressed), entry)); +} + +gboolean sp_show_attr_pop(gpointer data) +{ + AttrDialog *attrdialog = reinterpret_cast(data); + auto vscroll = attrdialog->_scrolled_text_view.get_vadjustment(); + int height = vscroll->get_upper() + 12; // padding 6+6 + if (height < 450) { + attrdialog->_scrolled_text_view.set_min_content_height(height); + } else { + attrdialog->_scrolled_text_view.set_min_content_height(450); + } + return FALSE; +} + +void AttrDialog::startValueEdit(Gtk::CellEditable *cell, const Glib::ustring &path) +{ + Gtk::Entry *entry = dynamic_cast(cell); + entry->signal_key_press_event().connect(sigc::bind(sigc::mem_fun(*this, &AttrDialog::onValueKeyPressed), entry)); + int width = 0; + int height = 0; + int colwidth = _valueCol->get_width(); + _textview->set_size_request(colwidth - 6, -1); + _popover->set_size_request(colwidth, -1); + valuepath = path; + entry->get_layout()->get_pixel_size(width, height); + Gtk::TreeIter iter = *_store->get_iter(path); + Gtk::TreeModel::Row row = *iter; + if (row && this->_repr) { + Glib::ustring name = row[_attrColumns._attributeName]; + if (colwidth < width || name == "content") { + Gtk::TreeIter iter = *_store->get_iter(path); + Gdk::Rectangle rect; + _treeView.get_cell_area((Gtk::TreeModel::Path)iter, *_valueCol, rect); + if (_popover->get_position() == Gtk::PositionType::POS_BOTTOM) { + rect.set_y(rect.get_y() + 20); + } + _popover->set_pointing_to(rect); + Glib::RefPtr textbuffer = Gtk::TextBuffer::create(); + textbuffer->set_text(entry->get_text()); + _textview->set_buffer(textbuffer); + cell->editing_done(); + cell->remove_widget(); + int scrolledcontentheight = 20; + if (name == "content") { + scrolledcontentheight = 450; + } + _scrolled_text_view.set_min_content_height(scrolledcontentheight); + _popover->show_all(); + _popover->check_resize(); + g_timeout_add(50, &sp_show_attr_pop, this); + } + } +} + +void AttrDialog::popClosed() +{ + Glib::RefPtr textbuffer = Gtk::TextBuffer::create(); + textbuffer->set_text(""); + _textview->set_buffer(textbuffer); +} /** * @brief AttrDialog::setDesktop @@ -224,6 +319,9 @@ void AttrDialog::attr_reset_context(gint attr) */ void AttrDialog::onAttrChanged(Inkscape::XML::Node *repr, const gchar * name, const gchar * new_value) { + if (_updating) { + return; + } for(auto iter: this->_store->children()) { Gtk::TreeModel::Row row = *iter; @@ -235,6 +333,7 @@ void AttrDialog::onAttrChanged(Inkscape::XML::Node *repr, const gchar * name, co } else { _store->erase(iter); } + break; } } if (new_value && strcmp(new_value, "") != 0) { @@ -243,7 +342,7 @@ void AttrDialog::onAttrChanged(Inkscape::XML::Node *repr, const gchar * name, co { return; } else { - Gtk::TreeModel::Row row = *(_store->append()); + Gtk::TreeModel::Row row = *(_store->prepend()); row[_attrColumns._attributeName] = name; row[_attrColumns._attributeValue] = new_value; } @@ -257,7 +356,7 @@ void AttrDialog::onAttrChanged(Inkscape::XML::Node *repr, const gchar * name, co bool AttrDialog::onAttrCreate(GdkEventButton *event) { if(event->type == GDK_BUTTON_RELEASE && event->button == 1 && this->_repr) { - Gtk::TreeIter iter = _store->append(); + Gtk::TreeIter iter = _store->prepend(); Gtk::TreeModel::Path path = (Gtk::TreeModel::Path)iter; _treeView.set_cursor(path, *_nameCol, true); grab_focus(); @@ -298,39 +397,80 @@ bool AttrDialog::onKeyPressed(GdkEventKey *event) if(this->_repr) { auto selection = this->_treeView.get_selection(); Gtk::TreeModel::Row row = *(selection->get_selected()); + Gtk::TreeIter iter = *(selection->get_selected()); + bool ret = false; switch (event->keyval) { case GDK_KEY_Delete: - case GDK_KEY_KP_Delete: - { + case GDK_KEY_KP_Delete: { // Create new attribute (repeat code, fold into above event!) Glib::ustring name = row[_attrColumns._attributeName]; - if(name == "content") { - return true; - } else { + if (name != "content") { this->_store->erase(row); this->_repr->setAttribute(name.c_str(), nullptr, false); this->setUndo(_("Delete attribute")); } - return true; - } + ret = true; + } break; case GDK_KEY_plus: case GDK_KEY_Insert: { // Create new attribute (repeat code, fold into above event!) - Gtk::TreeIter iter = this->_store->append(); + Gtk::TreeIter iter = this->_store->prepend(); Gtk::TreeModel::Path path = (Gtk::TreeModel::Path)iter; this->_treeView.set_cursor(path, *this->_nameCol, true); grab_focus(); - return true; - } + ret = true; + } break; } } return false; } +bool AttrDialog::onNameKeyPressed(GdkEventKey *event, Gtk::Entry *entry) +{ + g_debug("StyleDialog::_onNameKeyPressed"); + bool ret = false; + switch (event->keyval) { + case GDK_KEY_Tab: + case GDK_KEY_KP_Tab: + entry->editing_done(); + ret = true; + break; + } + return ret; +} + + +bool AttrDialog::onValueKeyPressed(GdkEventKey *event, Gtk::Entry *entry) +{ + g_debug("StyleDialog::_onValueKeyPressed"); + bool ret = false; + switch (event->keyval) { + case GDK_KEY_Tab: + case GDK_KEY_KP_Tab: + entry->editing_done(); + ret = true; + break; + } + return ret; +} + +gboolean sp_attrdialog_store_move_to_next(gpointer data) +{ + AttrDialog *attrdialog = reinterpret_cast(data); + auto selection = attrdialog->_treeView.get_selection(); + Gtk::TreeIter iter = *(selection->get_selected()); + Gtk::TreeModel::Path model = (Gtk::TreeModel::Path)iter; + if (model == attrdialog->_modelpath) { + attrdialog->_treeView.set_cursor(attrdialog->_modelpath, *attrdialog->_valueCol, true); + } + return FALSE; +} /** + * + * * @brief AttrDialog::nameEdited * @param event * @return @@ -339,36 +479,44 @@ bool AttrDialog::onKeyPressed(GdkEventKey *event) void AttrDialog::nameEdited (const Glib::ustring& path, const Glib::ustring& name) { Gtk::TreeIter iter = *_store->get_iter(path); - Gtk::TreeModel::Path modelpath = (Gtk::TreeModel::Path)iter; + _modelpath = (Gtk::TreeModel::Path)iter; Gtk::TreeModel::Row row = *iter; if(row && this->_repr) { Glib::ustring old_name = row[_attrColumns._attributeName]; if (old_name == name) { - _treeView.set_cursor(modelpath, *_valueCol, true); + g_timeout_add(50, &sp_attrdialog_store_move_to_next, this); grab_focus(); return; } - if (old_name == "content" || - old_name == name) - { + if (old_name == "content") { return; } Glib::ustring value = row[_attrColumns._attributeValue]; // Move to editing value, we set the name as a temporary store value if (!old_name.empty()) { // Remove old named value + _updating = true; _repr->setAttribute(old_name.c_str(), nullptr, false); + _updating = false; } if (!name.empty()) { row[_attrColumns._attributeName] = name; - _repr->setAttribute(name.c_str(), value, false); - _treeView.set_cursor(modelpath, *_valueCol, true); grab_focus(); + _updating = true; + _repr->setAttribute(name.c_str(), value, false); + _updating = false; + g_timeout_add(50, &sp_attrdialog_store_move_to_next, this); } this->setUndo(_("Rename attribute")); } } +void AttrDialog::valueEditedPop() +{ + Glib::ustring value = _textview->get_buffer()->get_text(); + valueEdited(valuepath, value); +} + /** * @brief AttrDialog::valueEdited * @param event @@ -380,6 +528,10 @@ void AttrDialog::valueEdited (const Glib::ustring& path, const Glib::ustring& va Gtk::TreeModel::Row row = *_store->get_iter(path); if(row && this->_repr) { Glib::ustring name = row[_attrColumns._attributeName]; + Glib::ustring old_value = row[_attrColumns._attributeValue]; + if (old_value == value) { + return; + } if(name.empty()) return; if (name == "content") { _repr->setContent(value.c_str()); @@ -389,7 +541,14 @@ void AttrDialog::valueEdited (const Glib::ustring& path, const Glib::ustring& va if(!value.empty()) { row[_attrColumns._attributeValue] = value; } + Inkscape::Selection *selection = _desktop->getSelection(); + SPObject *obj = nullptr; + if (selection->objects().size() == 1) { + obj = selection->objects().back(); + obj->style->readFromObject(obj); + obj->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } this->setUndo(_("Change attribute value")); } } diff --git a/src/ui/dialog/attrdialog.h b/src/ui/dialog/attrdialog.h index 68ea14cc9ff4a60f4f41e460219a4f24b460bae1..2c57447c4dd0cd5031f9ac752ac7289bd125175c 100644 --- a/src/ui/dialog/attrdialog.h +++ b/src/ui/dialog/attrdialog.h @@ -17,7 +17,9 @@ #include "message.h" #include #include +#include #include +#include #include #include @@ -61,6 +63,11 @@ public: Gtk::CellRendererText *_valueRenderer; Gtk::TreeViewColumn *_nameCol; Gtk::TreeViewColumn *_valueCol; + Gtk::TreeModel::Path _modelpath; + Gtk::Popover *_popover; + Gtk::TextView *_textview; + Gtk::Button *_update; + Glib::ustring valuepath; /** * Status bar @@ -71,14 +78,15 @@ public: // Widgets Gtk::VBox _mainBox; Gtk::ScrolledWindow _scrolledWindow; + Gtk::ScrolledWindow _scrolled_text_view; Gtk::HBox _buttonBox; Gtk::Button _buttonAddAttribute; - // Variables - Inkscape SPDesktop* _desktop; Inkscape::XML::Node* _repr; Gtk::HBox status_box; Gtk::Label status; + bool _updating; // Helper functions void setDesktop(SPDesktop* desktop) override; @@ -95,12 +103,17 @@ public: */ sigc::connection _message_changed_connection; void onAttrChanged(Inkscape::XML::Node *repr, const gchar * name, const gchar * new_value); + bool onNameKeyPressed(GdkEventKey *event, Gtk::Entry *entry); + bool onValueKeyPressed(GdkEventKey *event, Gtk::Entry *entry); void onAttrDelete(Glib::ustring path); bool onAttrCreate(GdkEventButton *event); bool onKeyPressed(GdkEventKey *event); + void popClosed(); + void startNameEdit(Gtk::CellEditable *cell, const Glib::ustring &path); + void startValueEdit(Gtk::CellEditable *cell, const Glib::ustring &path); void nameEdited(const Glib::ustring &path, const Glib::ustring &name); void valueEdited(const Glib::ustring &path, const Glib::ustring &value); - + void valueEditedPop(); }; diff --git a/src/ui/dialog/desktop-tracker.h b/src/ui/dialog/desktop-tracker.h index b681c9ca16ec0d54d8535506e0e2e7ca8ca4e742..7b94390068aa65ce7d46b02095726335c80940fc 100644 --- a/src/ui/dialog/desktop-tracker.h +++ b/src/ui/dialog/desktop-tracker.h @@ -46,7 +46,7 @@ private: SPDesktop *base; SPDesktop *desktop; GtkWidget *widget; - gulong hierID; + unsigned long hierID; sigc::connection inkID; bool trackActive; sigc::signal desktopChangedSig; diff --git a/src/ui/dialog/selectorsdialog.cpp b/src/ui/dialog/selectorsdialog.cpp index eda036c589e6f7606f464181ed0d863949128622..89d109ae9d4f75bdae63469a2411c7b95605ed4f 100644 --- a/src/ui/dialog/selectorsdialog.cpp +++ b/src/ui/dialog/selectorsdialog.cpp @@ -19,9 +19,10 @@ #include "inkscape.h" #include "selection.h" #include "style.h" -#include "verbs.h" #include "ui/icon-loader.h" +#include "ui/icon-names.h" #include "ui/widget/iconrenderer.h" +#include "verbs.h" #include "xml/attribute-record.h" #include "xml/node-observer.h" @@ -200,69 +201,6 @@ bool SelectorsDialog::TreeStore::row_draggable_vfunc(const Gtk::TreeModel::Path return Gtk::TreeStore::row_draggable_vfunc(path); } -void SelectorsDialog::fixCSSSelectors(Glib::ustring &selector) -{ - g_debug("SelectorsDialog::fixCSSSelectors"); - REMOVE_SPACES(selector); - Glib::ustring my_selector = selector + " {"; // Parsing fails sometimes without '{'. Fix me - CRSelector *cr_selector = cr_selector_parse_from_buf((guchar *)my_selector.c_str(), CR_UTF_8); - selector = Glib::ustring(""); - CRSelector const *cur = nullptr; - for (cur = cr_selector; cur; cur = cur->next) { - if (cur->simple_sel) { - gchar *selectorchar = reinterpret_cast(cr_simple_sel_to_string(cur->simple_sel)); - if (selectorchar) { - Glib::ustring toadd = Glib::ustring(selectorchar); - selector = selector.empty() ? toadd : selector + "," + toadd; - g_free(selectorchar); - } - } - } - std::vector tokens = Glib::Regex::split_simple("[,]+", selector); - std::vector selectorresult; - selector = Glib::ustring(""); - for (auto token : tokens) { - REMOVE_SPACES(token); - std::vector tokensplus = Glib::Regex::split_simple("[ ]+", token); - Glib::ustring selectorpart = Glib::ustring(""); - for (auto tokenplus : tokensplus) { - REMOVE_SPACES(tokenplus); - Glib::ustring toparse = Glib::ustring(tokenplus); - Glib::ustring tag = Glib::ustring(""); - if (toparse[0] != '.' && toparse[0] != '#') { - auto i = std::min(toparse.find("#"), toparse.find(".")); - tag = toparse.substr(0, i); - if (!SPAttributeRelSVG::isSVGElement(tag)) { - continue; - } - if (i != std::string::npos) { - toparse.erase(0, i); - } else { - toparse = tag; - selectorpart = selectorpart == Glib::ustring("") ? toparse : selectorpart + " " + toparse; - continue; - } - } - auto i = toparse.find("#"); - if (i != std::string::npos) { - toparse.erase(i, 1); - } - auto j = toparse.find("#"); - if (i != std::string::npos && j != std::string::npos) { - continue; - } else if (i != std::string::npos) { - toparse.insert(i, "#"); - } - toparse = tag + toparse; - selectorpart = selectorpart == Glib::ustring("") ? toparse : selectorpart + " " + toparse; - } - selectorresult.push_back(selectorpart); - } - for (auto selectorpart : selectorresult) { - selector = selector == Glib::ustring("") ? selectorpart : selector + "," + selectorpart; - } -} - /** * Allow dropping only in between other selectors. */ @@ -404,19 +342,30 @@ void SelectorsDialog::_showWidgets() _vadj = _scrolled_window_selectors.get_vadjustment(); _vadj->signal_value_changed().connect(sigc::mem_fun(*this, &SelectorsDialog::_vscrool)); _selectors_box.pack_start(_scrolled_window_selectors, Gtk::PACK_EXPAND_WIDGET); - Gtk::Label *dirtogglerlabel = Gtk::manage(new Gtk::Label(_("Paned vertical"))); + /* Gtk::Label *dirtogglerlabel = Gtk::manage(new Gtk::Label(_("Paned vertical"))); dirtogglerlabel->get_style_context()->add_class("inksmall"); _direction.property_active() = dir; _direction.property_active().signal_changed().connect(sigc::mem_fun(*this, &SelectorsDialog::_toggleDirection)); - _direction.get_style_context()->add_class("inkswitch"); + _direction.get_style_context()->add_class("inkswitch"); */ _styleButton(_create, "list-add", "Add a new CSS Selector"); _create.signal_clicked().connect(sigc::mem_fun(*this, &SelectorsDialog::_addSelector)); _styleButton(_del, "list-remove", "Remove a CSS Selector"); _button_box.pack_start(_create, Gtk::PACK_SHRINK); _button_box.pack_start(_del, Gtk::PACK_SHRINK); - _button_box.pack_start(_direction, false, false, 0); - _button_box.pack_start(*dirtogglerlabel, false, false, 0); - _selectors_box.pack_end(_button_box, false, false, 0); + Gtk::RadioButton::Group group; + Gtk::RadioButton *_horizontal = Gtk::manage(new Gtk::RadioButton()); + Gtk::RadioButton *_vertical = Gtk::manage(new Gtk::RadioButton()); + _horizontal->set_image_from_icon_name(INKSCAPE_ICON("horizontal")); + _vertical->set_image_from_icon_name(INKSCAPE_ICON("vertical")); + _horizontal->set_group(group); + _vertical->set_group(group); + _vertical->set_active(dir); + _vertical->signal_toggled().connect( + sigc::bind(sigc::mem_fun(*this, &SelectorsDialog::_toggleDirection), _vertical)); + _horizontal->property_draw_indicator() = false; + _vertical->property_draw_indicator() = false; + _button_box.pack_end(*_horizontal, false, false, 0); + _button_box.pack_end(*_vertical, false, false, 0); _del.signal_clicked().connect(sigc::mem_fun(*this, &SelectorsDialog::_delSelector)); _del.hide(); _style_dialog = new StyleDialog; @@ -424,36 +373,62 @@ void SelectorsDialog::_showWidgets() _paned.pack1(*_style_dialog, Gtk::SHRINK); _paned.pack2(_selectors_box, true, true); _getContents()->pack_start(_paned, Gtk::PACK_EXPAND_WIDGET); + _getContents()->pack_start(_button_box, false, false, 0); show_all(); int widthpos = _paned.property_max_position(); int panedpos = prefs->getInt("/dialogs/selectors/panedpos", 130); _paned.set_position(panedpos); _paned.property_wide_handle() = true; - _paned.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectorsDialog::_resized), false); + _paned.property_position().signal_changed().connect(sigc::mem_fun(*this, &SelectorsDialog::_childresized)); + _paned.signal_size_allocate().connect(sigc::mem_fun(*this, &SelectorsDialog::_panedresized)); set_size_request(320, 260); set_name("SelectorsAndStyleDialog"); } -bool SelectorsDialog::_resized(GdkEventButton *event) +void SelectorsDialog::_panedresized(Gtk::Allocation allocation) +{ + g_debug("SelectorsDialog::_panedresized"); + _resized(); +} + +void SelectorsDialog::_childresized() +{ + g_debug("SelectorsDialog::_childresized"); + _resized(); +} + +void SelectorsDialog::_resized() { g_debug("SelectorsDialog::_resized"); _scroollock = true; + if (_updating) { + return; + } + _updating = true; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool dir = !prefs->getBool("/dialogs/selectors/vertical", true); + int max = int(_paned.property_max_position() * 0.95); + int min = int(_paned.property_max_position() * 0.05); + if (_paned.get_position() > max) { + _paned.property_position() = max; + } + if (_paned.get_position() < min) { + _paned.property_position() = min; + } prefs->setInt("/dialogs/selectors/panedpos", _paned.get_position()); - return false; + _updating = false; } - -void SelectorsDialog::_toggleDirection() +void SelectorsDialog::_toggleDirection(Gtk::RadioButton *vertical) { g_debug("SelectorsDialog::_toggleDirection"); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool dir = !prefs->getBool("/dialogs/selectors/vertical", true); + bool dir = vertical->get_active(); prefs->setBool("/dialogs/selectors/vertical", dir); _paned.set_orientation(dir ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL); - int widthpos = _paned.property_max_position(); + _paned.check_resize(); + int widthpos = _paned.property_max_position() - _paned.property_min_position(); prefs->setInt("/dialogs/selectors/panedpos", widthpos / 2); _paned.set_position(widthpos / 2); } @@ -573,7 +548,7 @@ void SelectorsDialog::_readStyleElement() for (unsigned i = 0; i < tokens.size() - 1; i += 2) { Glib::ustring selector = tokens[i]; REMOVE_SPACES(selector); // Remove leading/trailing spaces - fixCSSSelectors(selector); + selector = _style_dialog->fixCSSSelectors(selector); for (auto &row : _store->children()) { Glib::ustring selectorold = row[_mColumns._colSelector]; if (selectorold == selector) { @@ -588,7 +563,7 @@ void SelectorsDialog::_readStyleElement() Glib::ustring selector = tokens[i]; REMOVE_SPACES(selector); // Remove leading/trailing spaces Glib::ustring selector_old = selector; - fixCSSSelectors(selector); + selector = _style_dialog->fixCSSSelectors(selector); if (selector_old != selector) { rewrite = true; } @@ -778,8 +753,12 @@ Glib::ustring sp_get_selector_classes(Glib::ustring selector) //, SelectorType s REMOVE_SPACES(selector); Glib::ustring toparse = Glib::ustring(selector); selector = Glib::ustring(""); + auto i = toparse.find("."); + if (i == std::string::npos) { + return ""; + } if (toparse[0] != '.' && toparse[0] != '#') { - auto i = std::min(toparse.find("#"), toparse.find(".")); + i = std::min(toparse.find("#"), toparse.find(".")); Glib::ustring tag = toparse.substr(0, i); if (!SPAttributeRelSVG::isSVGElement(tag)) { return selector; @@ -788,7 +767,7 @@ Glib::ustring sp_get_selector_classes(Glib::ustring selector) //, SelectorType s toparse.erase(0, i); } } - auto i = toparse.find("#"); + i = toparse.find("#"); if (i != std::string::npos) { toparse.erase(i, 1); } @@ -856,7 +835,7 @@ void SelectorsDialog::_addToSelector(Gtk::TreeModel::Row row) if (insertid) { multiselector = multiselector + ",#" + id; } - Gtk::TreeModel::Row childrow = *(_store->append(row->children())); + Gtk::TreeModel::Row childrow = *(_store->prepend(row->children())); childrow[_mColumns._colSelector] = "#" + Glib::ustring(id); childrow[_mColumns._colExpand] = false; childrow[_mColumns._colType] = OBJECT; @@ -1093,6 +1072,9 @@ void SelectorsDialog::_selectObjects(int eventX, int eventY) int y2 = 0; // To do: We should be able to do this via passing in row. if (_treeView.get_path_at_pos(eventX, eventY, path, col, x2, y2)) { + if (_lastpath.size() && _lastpath == path) { + return; + } if (col == _treeView.get_column(1) && x2 > 25) { getDesktop()->selection->clear(); Gtk::TreeModel::iterator iter = _store->get_iter(path); @@ -1108,6 +1090,7 @@ void SelectorsDialog::_selectObjects(int eventX, int eventY) getDesktop()->selection->add(obj); } } + _lastpath = path; } } } @@ -1179,9 +1162,8 @@ void SelectorsDialog::_addSelector() * for selector. If the entrybox is empty, the text (thus selectorName) is * set to ".Class1" */ - selectorValue = textEditPtr->get_text(); + selectorValue = _style_dialog->fixCSSSelectors(Glib::ustring(textEditPtr->get_text())); _del.show(); - fixCSSSelectors(selectorValue); if (selectorValue.empty()) { textLabelPtr->show(); } else { @@ -1190,7 +1172,6 @@ void SelectorsDialog::_addSelector() } delete textDialogPtr; // ==== Handle response ==== - // If class selector, add selector name to class attribute for each object REMOVE_SPACES(selectorValue); std::vector tokens = Glib::Regex::split_simple("[,]+", selectorValue); @@ -1214,7 +1195,7 @@ void SelectorsDialog::_addSelector() } } objVec = _getObjVec(selectorValue); - Gtk::TreeModel::Row row = *(_store->append()); + Gtk::TreeModel::Row row = *(_store->prepend()); row[_mColumns._colExpand] = true; row[_mColumns._colType] = SELECTOR; row[_mColumns._colSelector] = selectorValue; @@ -1223,7 +1204,7 @@ void SelectorsDialog::_addSelector() row[_mColumns._colVisible] = true; row[_mColumns._colSelected] = 400; for (auto &obj : objVec) { - Gtk::TreeModel::Row childrow = *(_store->append(row->children())); + Gtk::TreeModel::Row childrow = *(_store->prepend(row->children())); childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId()); childrow[_mColumns._colExpand] = false; childrow[_mColumns._colType] = OBJECT; @@ -1382,6 +1363,7 @@ void SelectorsDialog::_handleDesktopChanged(SPDesktop *desktop) void SelectorsDialog::_handleSelectionChanged() { g_debug("SelectorsDialog::_handleSelectionChanged()"); + _lastpath.clear(); _treeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); _selectRow(); } diff --git a/src/ui/dialog/selectorsdialog.h b/src/ui/dialog/selectorsdialog.h index 6830a0124e709285b5dcca56e360189d85e985cb..66d9bb859f86ca84e5ae745c7c65607d54712188 100644 --- a/src/ui/dialog/selectorsdialog.h +++ b/src/ui/dialog/selectorsdialog.h @@ -15,12 +15,13 @@ #ifndef SELECTORSDIALOG_H #define SELECTORSDIALOG_H -#include "ui/dialog/styledialog.h" #include "ui/dialog/desktop-tracker.h" #include "ui/dialog/dialog-manager.h" +#include "ui/dialog/styledialog.h" #include "ui/widget/panel.h" #include #include +#include #include #include #include @@ -64,7 +65,6 @@ class SelectorsDialog : public Widget::Panel { // Monitor all objects for addition/removal/attribute change class NodeWatcher; enum SelectorType { CLASS, ID, TAG }; - void fixCSSSelectors(Glib::ustring &selector); std::vector _nodeWatchers; void _nodeAdded( Inkscape::XML::Node &repr ); void _nodeRemoved( Inkscape::XML::Node &repr ); @@ -117,10 +117,10 @@ class SelectorsDialog : public Widget::Panel { Glib::RefPtr _modelfilter; Glib::RefPtr _store; Gtk::TreeView _treeView; + Gtk::TreeModel::Path _lastpath; // Widgets Gtk::Paned _paned; Glib::RefPtr _vadj; - Gtk::Switch _direction; Gtk::Box _button_box; Gtk::Box _selectors_box; Gtk::ScrolledWindow _scrolled_window_selectors; @@ -146,9 +146,11 @@ class SelectorsDialog : public Widget::Panel { void _insertClass(SPObject *obj, const Glib::ustring &className); void _removeClass(const std::vector &objVec, const Glib::ustring &className, bool all = false); void _removeClass(SPObject *obj, const Glib::ustring &className, bool all = false); - void _toggleDirection(); + void _toggleDirection(Gtk::RadioButton *vertical); void _showWidgets(); - bool _resized(GdkEventButton *event); + void _resized(); + void _childresized(); + void _panedresized(Gtk::Allocation allocation); void _selectObjects(int, int); // Variables diff --git a/src/ui/dialog/styledialog.cpp b/src/ui/dialog/styledialog.cpp index 75b44f8318658c4227af14ab1cb57a0ba35bd215..884da2df2906f2b211d5b965439caa3409a94964 100644 --- a/src/ui/dialog/styledialog.cpp +++ b/src/ui/dialog/styledialog.cpp @@ -201,18 +201,11 @@ StyleDialog::StyleDialog() Gtk::Box *alltoggler = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); Gtk::Label *infotoggler = Gtk::manage(new Gtk::Label(_("Edit Full Stylesheet"))); infotoggler->get_style_context()->add_class("inksmall"); - _all_css = Gtk::manage(new Gtk::Switch()); _vadj = _scrolledWindow.get_vadjustment(); _vadj->signal_value_changed().connect(sigc::mem_fun(*this, &StyleDialog::_vscrool)); - //_all_css->property_active().signal_changed().connect(sigc::mem_fun(*this, &StyleDialog::_reload)); - // alltoggler->pack_start(*_all_css, false, false, 0); - // alltoggler->pack_start(*infotoggler, false, false, 0); - //_all_css->set_active(false); - //_mainBox.pack_start(*alltoggler, false, false, 0); _mainBox.set_orientation(Gtk::ORIENTATION_VERTICAL); _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET); - //_all_css->get_style_context()->add_class("inkswitch"); // Document & Desktop _desktop_changed_connection = _desktopTracker.connectDesktopChanged(sigc::mem_fun(*this, &StyleDialog::_handleDesktopChanged)); @@ -223,6 +216,7 @@ StyleDialog::StyleDialog() _selection_changed_connection = getDesktop()->getSelection()->connectChanged( sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged))); + // Add watchers _updateWatchers(); @@ -240,6 +234,43 @@ void StyleDialog::_vscrool() } } +Glib::ustring StyleDialog::fixCSSSelectors(Glib::ustring selector) +{ + g_debug("SelectorsDialog::fixCSSSelectors"); + REMOVE_SPACES(selector); + Glib::ustring selector_out = ""; + std::vector tokens = Glib::Regex::split_simple("[,]+", selector); + for (auto token : tokens) { + if (!selector_out.empty()) { + selector_out += ","; + } + REMOVE_SPACES(token); + std::vector subtokens = Glib::Regex::split_simple("[ ]+", token); + for (auto subtoken : subtokens) { + REMOVE_SPACES(subtoken); + Glib::ustring my_selector = subtoken + " {"; // Parsing fails sometimes without '{'. Fix me + CRSelector *cr_selector = cr_selector_parse_from_buf((guchar *)my_selector.c_str(), CR_UTF_8); + gchar *selectorchar = reinterpret_cast(cr_selector_to_string(cr_selector)); + if (selectorchar) { + Glib::ustring toadd = Glib::ustring(selectorchar); + g_free(selectorchar); + if (toadd[0] != '.' && toadd[0] != '#' && toadd.size() > 1) { + auto i = std::min(toadd.find("#"), toadd.find(".")); + Glib::ustring tag = toadd; + if (i != std::string::npos) { + tag = tag.substr(0, i); + } + if (!SPAttributeRelSVG::isSVGElement(tag)) { + return ""; + } + } + selector_out = selector_out.empty() ? toadd : selector_out + " " + toadd; + } + } + } + return selector_out; +} + /** * Class destructor */ @@ -466,6 +497,7 @@ void StyleDialog::_readStyleElement() css_selector->set_text("element"); Gtk::TreeView *css_tree; _builder->get_widget("CSSTree", css_tree); + css_tree->get_style_context()->add_class("style_element"); Glib::RefPtr store = Gtk::TreeStore::create(_mColumns); css_tree->set_model(store); css_selector_event_add->signal_button_release_event().connect( @@ -473,7 +505,7 @@ void StyleDialog::_readStyleElement() sigc::mem_fun(*this, &StyleDialog::_addRow), store, css_tree, "style_properties", selectorpos)); Inkscape::UI::Widget::IconRenderer *addRenderer = manage(new Inkscape::UI::Widget::IconRenderer()); addRenderer->add_icon("edit-delete"); - int addCol = css_tree->append_column("Delete row", *addRenderer) - 1; + int addCol = css_tree->append_column("", *addRenderer) - 1; Gtk::TreeViewColumn *col = css_tree->get_column(addCol); if (col) { addRenderer->signal_activated().connect( @@ -485,9 +517,10 @@ void StyleDialog::_readStyleElement() label->signal_edited().connect(sigc::bind, Gtk::TreeView *>( sigc::mem_fun(*this, &StyleDialog::_nameEdited), store, css_tree)); label->signal_editing_started().connect(sigc::mem_fun(*this, &StyleDialog::_startNameEdit)); - addCol = css_tree->append_column("CSS Property", *label) - 1; + addCol = css_tree->append_column("", *label) - 1; col = css_tree->get_column(addCol); if (col) { + col->set_resizable(true); col->add_attribute(label->property_text(), _mColumns._colName); } Gtk::CellRendererText *value = Gtk::manage(new Gtk::CellRendererText()); @@ -497,7 +530,7 @@ void StyleDialog::_readStyleElement() sigc::bind>(sigc::mem_fun(*this, &StyleDialog::_valueEdited), store)); value->signal_editing_started().connect( sigc::bind>(sigc::mem_fun(*this, &StyleDialog::_startValueEdit), store)); - addCol = css_tree->append_column("CSS Value", *value) - 1; + addCol = css_tree->append_column("", *value) - 1; col = css_tree->get_column(addCol); if (col) { col->add_attribute(value->property_text(), _mColumns._colValue); @@ -506,7 +539,7 @@ void StyleDialog::_readStyleElement() Inkscape::UI::Widget::IconRenderer *urlRenderer = manage(new Inkscape::UI::Widget::IconRenderer()); urlRenderer->add_icon("empty"); urlRenderer->add_icon("edit-redo"); - int urlCol = css_tree->append_column("Go to", *urlRenderer) - 1; + int urlCol = css_tree->append_column("", *urlRenderer) - 1; Gtk::TreeViewColumn *urlcol = css_tree->get_column(urlCol); if (urlcol) { urlRenderer->signal_activated().connect( @@ -515,19 +548,19 @@ void StyleDialog::_readStyleElement() } std::map attr_prop; Gtk::TreeModel::Path path; - if (!_all_css->get_active() && obj && obj->getRepr()->attribute("style")) { + if (obj && obj->getRepr()->attribute("style")) { Glib::ustring style = obj->getRepr()->attribute("style"); attr_prop = parseStyle(style); for (auto iter : obj->style->properties()) { if (attr_prop.count(iter->name)) { - Gtk::TreeModel::Row row = *(store->append()); + Gtk::TreeModel::Row row = *(store->prepend()); row[_mColumns._colSelector] = "style_properties"; row[_mColumns._colSelectorPos] = 0; row[_mColumns._colActive] = true; row[_mColumns._colName] = iter->name; row[_mColumns._colValue] = iter->get_value(); row[_mColumns._colStrike] = false; - row[_mColumns._colOwner] = Glib::ustring("Value active"); + row[_mColumns._colOwner] = Glib::ustring("Current value"); row[_mColumns._colHref] = nullptr; row[_mColumns._colLinked] = false; if (is_url(iter->get_value().c_str())) { @@ -554,7 +587,7 @@ void StyleDialog::_readStyleElement() REMOVE_SPACES(selector); // Remove leading/trailing spaces // Get list of objects selector matches std::vector objVec = _getObjVec(selector); - if (obj && !_all_css->get_active()) { + if (obj) { bool stop = true; for (auto objel : objVec) { if (objel->getId() == obj->getId()) { @@ -592,14 +625,25 @@ void StyleDialog::_readStyleElement() _builder->get_widget("CSSSelectorContainer", css_selector_container); Gtk::Label *css_selector; _builder->get_widget("CSSSelector", css_selector); + Gtk::EventBox *css_selector_event_box; + _builder->get_widget("CSSSelectorEventBox", css_selector_event_box); + Gtk::Entry *css_edit_selector; + _builder->get_widget("CSSEditSelector", css_edit_selector); Gtk::EventBox *css_selector_event_add; _builder->get_widget("CSSSelectorEventAdd", css_selector_event_add); css_selector_event_add->add_events(Gdk::BUTTON_RELEASE_MASK); css_selector->set_text(selector); Gtk::TreeView *css_tree; _builder->get_widget("CSSTree", css_tree); + css_tree->get_style_context()->add_class("style_sheet"); Glib::RefPtr store = Gtk::TreeStore::create(_mColumns); css_tree->set_model(store); + css_selector_event_box->signal_button_release_event().connect( + sigc::bind(sigc::mem_fun(*this, &StyleDialog::_selectorStartEdit), css_selector, css_edit_selector)); + css_edit_selector->signal_key_press_event().connect(sigc::bind( + sigc::mem_fun(*this, &StyleDialog::_selectorEditKeyPress), store, css_selector, css_edit_selector)); + css_edit_selector->signal_activate().connect( + sigc::bind(sigc::mem_fun(*this, &StyleDialog::_selectorActivate), store, css_selector, css_edit_selector)); Inkscape::UI::Widget::IconRenderer *addRenderer = manage(new Inkscape::UI::Widget::IconRenderer()); addRenderer->add_icon("edit-delete"); int addCol = css_tree->append_column("Delete row", *addRenderer) - 1; @@ -625,6 +669,7 @@ void StyleDialog::_readStyleElement() addCol = css_tree->append_column("CSS Selector", *label) - 1; col = css_tree->get_column(addCol); if (col) { + col->set_resizable(true); col->add_attribute(label->property_text(), _mColumns._colName); } Gtk::CellRendererText *value = Gtk::manage(new Gtk::CellRendererText()); @@ -662,7 +707,7 @@ void StyleDialog::_readStyleElement() css_selector_event_add->signal_button_release_event().connect( sigc::bind, Gtk::TreeView *, Glib::ustring, gint>( sigc::mem_fun(*this, &StyleDialog::_addRow), store, css_tree, selector, selectorpos)); - if (obj && !_all_css->get_active()) { + if (obj) { for (auto iter : result_props) { Gtk::TreeIter iterstore = store->append(); Gtk::TreeModel::Path path = (Gtk::TreeModel::Path)iterstore; @@ -690,7 +735,7 @@ void StyleDialog::_readStyleElement() row[_mColumns._colOwner] = Glib::ustring(""); } else { row[_mColumns._colStrike] = false; - row[_mColumns._colOwner] = Glib::ustring("Value active"); + row[_mColumns._colOwner] = Glib::ustring("Current value"); _addOwnerStyle(iter.first, selector); } } else { @@ -701,7 +746,7 @@ void StyleDialog::_readStyleElement() } } else { for (auto iter : result_props) { - Gtk::TreeModel::Row row = *(store->append()); + Gtk::TreeModel::Row row = *(store->prepend()); row[_mColumns._colSelector] = selector; row[_mColumns._colSelectorPos] = selectorpos; row[_mColumns._colActive] = iter.second.second; @@ -727,12 +772,13 @@ void StyleDialog::_readStyleElement() css_selector_event_add->add_events(Gdk::BUTTON_RELEASE_MASK); store = Gtk::TreeStore::create(_mColumns); _builder->get_widget("CSSTree", css_tree); + css_tree->get_style_context()->add_class("style_attribute"); css_tree->set_model(store); css_selector_event_add->signal_button_release_event().connect( sigc::bind, Gtk::TreeView *, Glib::ustring, gint>( sigc::mem_fun(*this, &StyleDialog::_addRow), store, css_tree, "attributes", selectorpos)); bool hasattributes = false; - if (obj && !_all_css->get_active()) { + if (obj) { for (auto iter : obj->style->properties()) { if (iter->style_src != SP_STYLE_SRC_UNSET) { if (iter->name != "font" && iter->name != "d" && iter->name != "marker") { @@ -757,6 +803,7 @@ void StyleDialog::_readStyleElement() addCol = css_tree->append_column("CSS Property", *label) - 1; col = css_tree->get_column(addCol); if (col) { + col->set_resizable(true); col->add_attribute(label->property_text(), _mColumns._colName); } Gtk::CellRendererText *value = Gtk::manage(new Gtk::CellRendererText()); @@ -774,7 +821,7 @@ void StyleDialog::_readStyleElement() col->add_attribute(value->property_strikethrough(), _mColumns._colStrike); } } - Gtk::TreeIter iterstore = store->append(); + Gtk::TreeIter iterstore = store->prepend(); Gtk::TreeModel::Path path = (Gtk::TreeModel::Path)iterstore; Gtk::TreeModel::Row row = *(iterstore); row[_mColumns._colSelector] = "attributes"; @@ -788,7 +835,7 @@ void StyleDialog::_readStyleElement() row[_mColumns._colOwner] = tooltiptext; } else { row[_mColumns._colStrike] = false; - row[_mColumns._colOwner] = Glib::ustring("Value active"); + row[_mColumns._colOwner] = Glib::ustring("Current value"); _addOwnerStyle(iter->name, "inline attributes"); } hasattributes = true; @@ -826,6 +873,43 @@ void StyleDialog::_readStyleElement() _updating = false; } +bool StyleDialog::_selectorStartEdit(GdkEventButton *event, Gtk::Label *selector, Gtk::Entry *selector_edit) +{ + g_debug("StyleDialog::_selectorStartEdit"); + if (event->type == GDK_BUTTON_RELEASE && event->button == 1) { + selector->hide(); + selector_edit->set_text(selector->get_text()); + selector_edit->show(); + } + return false; +} + +void StyleDialog::_selectorActivate(Glib::RefPtr store, Gtk::Label *selector, Gtk::Entry *selector_edit) +{ + g_debug("StyleDialog::_selectorEditKeyPress"); + bool ret = false; + Glib::ustring newselector = fixCSSSelectors(selector_edit->get_text()); + if (newselector.empty()) { + selector_edit->get_style_context()->add_class("system_error_color"); + return; + } + _writeStyleElement(store, selector->get_text(), selector_edit->get_text()); +} + +bool StyleDialog::_selectorEditKeyPress(GdkEventKey *event, Glib::RefPtr store, Gtk::Label *selector, + Gtk::Entry *selector_edit) +{ + g_debug("StyleDialog::_selectorEditKeyPress"); + switch (event->keyval) { + case GDK_KEY_Escape: + selector->show(); + selector_edit->hide(); + selector_edit->get_style_context()->remove_class("system_error_color"); + break; + } + return false; +} + bool StyleDialog::_on_foreach_iter(const Gtk::TreeModel::iterator &iter) { g_debug("StyleDialog::_on_foreach_iter"); @@ -921,7 +1005,8 @@ std::map StyleDialog::parseStyle(Glib::ustring sty /** * Update the content of the style element as selectors (or objects) are added/removed. */ -void StyleDialog::_writeStyleElement(Glib::RefPtr store, Glib::ustring selector) +void StyleDialog::_writeStyleElement(Glib::RefPtr store, Glib::ustring selector, + Glib::ustring new_selector) { g_debug("StyleDialog::_writeStyleElemen"); if (_updating) { @@ -936,7 +1021,7 @@ void StyleDialog::_writeStyleElement(Glib::RefPtr store, Glib::u if (!obj) { obj = getDesktop()->getDocument()->getXMLDialogSelectedObject(); } - if (selection->objects().size() < 2 && !obj && !_all_css->get_active()) { + if (selection->objects().size() < 2 && !obj) { _readStyleElement(); return; } @@ -944,6 +1029,9 @@ void StyleDialog::_writeStyleElement(Glib::RefPtr store, Glib::u gint selectorpos = 0; std::string styleContent = ""; if (selector != "style_properties" && selector != "attributes") { + if (!new_selector.empty()) { + selector = new_selector; + } styleContent = "\n" + selector + " { \n"; } for (auto &row : store->children()) { @@ -965,13 +1053,17 @@ void StyleDialog::_writeStyleElement(Glib::RefPtr store, Glib::u styleContent = styleContent + "}"; } if (selector == "style_properties") { + _updating = true; obj->getRepr()->setAttribute("style", styleContent, false); + _updating = false; } else if (selector == "attributes") { for (auto iter : obj->style->properties()) { if (iter->name != "font" && iter->name != "d" && iter->name != "marker") { const gchar *attr = obj->getRepr()->attribute(iter->name.c_str()); if (attr) { + _updating = true; obj->getRepr()->setAttribute(iter->name.c_str(), nullptr); + _updating = false; } } } @@ -979,13 +1071,15 @@ void StyleDialog::_writeStyleElement(Glib::RefPtr store, Glib::u Glib::ustring name = row[_mColumns._colName]; Glib::ustring value = row[_mColumns._colValue]; if (!(name.empty() && value.empty())) { + _updating = true; obj->getRepr()->setAttribute(name.c_str(), value, false); + _updating = false; } } } else if (!selector.empty()) { // styleshet // We could test if styleContent is empty and then delete the style node here but there is no // harm in keeping it around ... - SPDocument *document = SP_ACTIVE_DOCUMENT; + std::string pos = std::to_string(selectorpos); std::string selectormatch = "("; for (selectorpos; selectorpos > 1; selectorpos--) { @@ -998,14 +1092,16 @@ void StyleDialog::_writeStyleElement(Glib::RefPtr store, Glib::u std::string result; std::regex_replace(std::back_inserter(result), content.begin(), content.end(), e, "$1" + styleContent + "$3"); textNode->setContent(result.c_str()); - INKSCAPE.readStyleSheets(); - for (auto iter : document->getObjectsBySelector(selector)) { - iter->style->readFromObject(iter); - iter->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); - } + INKSCAPE.readStyleSheets(true); } _updating = false; _readStyleElement(); + /* SPDocument *document = SP_ACTIVE_DOCUMENT; + for (auto iter : document->getObjectsBySelector(selector)) { + std::cout << std::endl; + iter->style->readFromObject(iter); + iter->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } */ DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_STYLE, _("Edited style element.")); g_debug("StyleDialog::_writeStyleElement(): | %s |", styleContent.c_str()); @@ -1017,7 +1113,7 @@ bool StyleDialog::_addRow(GdkEventButton *evt, Glib::RefPtr stor g_debug("StyleDialog::_addRow"); if (evt->type == GDK_BUTTON_RELEASE && evt->button == 1) { - Gtk::TreeIter iter = store->append(); + Gtk::TreeIter iter = store->prepend(); Gtk::TreeModel::Path path = (Gtk::TreeModel::Path)iter; Gtk::TreeModel::Row row = *(iter); row[_mColumns._colSelector] = selector; @@ -1050,7 +1146,7 @@ void StyleDialog::_setAutocompletion(Gtk::Entry *entry, SPStyleEnum const cssenu gint counter = 0; const char * key = cssenum[counter].key; while (key) { - Gtk::TreeModel::Row row = *(completionModel->append()); + Gtk::TreeModel::Row row = *(completionModel->prepend()); row[_mCSSData._colCSSData] = Glib::ustring(key); counter++; key = cssenum[counter].key; @@ -1144,7 +1240,9 @@ StyleDialog::_startValueEdit(Gtk::CellEditable* cell, const Glib::ustring& path, _setAutocompletion(entry, enum_color_interpolation); } entry->signal_key_release_event().connect( - sigc::bind(sigc::mem_fun(*this, &StyleDialog::_onValueKeyReleased), cell)); + sigc::bind(sigc::mem_fun(*this, &StyleDialog::_onValueKeyReleased), entry)); + entry->signal_key_press_event().connect( + sigc::bind(sigc::mem_fun(*this, &StyleDialog::_onValueKeyPressed), entry)); } } @@ -1165,7 +1263,22 @@ void StyleDialog::_startNameEdit(Gtk::CellEditable *cell, const Glib::ustring &p } Gtk::Entry *entry = dynamic_cast(cell); entry->set_completion(entry_completion); - entry->signal_key_release_event().connect(sigc::bind(sigc::mem_fun(*this, &StyleDialog::_onNameKeyReleased), cell)); + entry->signal_key_release_event().connect( + sigc::bind(sigc::mem_fun(*this, &StyleDialog::_onNameKeyReleased), entry)); + entry->signal_key_press_event().connect(sigc::bind(sigc::mem_fun(*this, &StyleDialog::_onNameKeyPressed), entry)); +} + + +gboolean sp_styledialog_store_move_to_next(gpointer data) +{ + StyleDialog *styledialog = reinterpret_cast(data); + auto selection = styledialog->_current_css_tree->get_selection(); + Gtk::TreeIter iter = *(selection->get_selected()); + Gtk::TreeModel::Path model = (Gtk::TreeModel::Path)iter; + if (model == styledialog->_current_path) { + styledialog->_current_css_tree->set_cursor(styledialog->_current_path, *styledialog->_current_value_col, true); + } + return FALSE; } /** @@ -1181,9 +1294,10 @@ void StyleDialog::_nameEdited(const Glib::ustring &path, const Glib::ustring &na _scroollock = true; Gtk::TreeModel::Row row = *store->get_iter(path); - Gtk::TreeModel::Path pathel = (Gtk::TreeModel::Path)*store->get_iter(path); + _current_path = (Gtk::TreeModel::Path)*store->get_iter(path); if (row) { + _current_css_tree = css_tree; Glib::ustring finalname = name; auto i = std::min(finalname.find(";"), finalname.find(":")); if (i != std::string::npos) { @@ -1197,7 +1311,7 @@ void StyleDialog::_nameEdited(const Glib::ustring &path, const Glib::ustring &na Glib::ustring selector = row[_mColumns._colSelector]; Glib::ustring value = row[_mColumns._colValue]; bool is_attr = selector == "attributes"; - + Glib::ustring old_name = row[_mColumns._colName]; row[_mColumns._colName] = finalname; if (finalname.empty() && value.empty()) { store->erase(row); @@ -1206,10 +1320,11 @@ void StyleDialog::_nameEdited(const Glib::ustring &path, const Glib::ustring &na if (pos < 1 || is_attr) { col = 2; } - if (write) { + _current_value_col = css_tree->get_column(col); + if (write && old_name != name) { _writeStyleElement(store, selector); } else { - css_tree->set_cursor(pathel, *(css_tree->get_column(col)), true); + g_timeout_add(50, &sp_styledialog_store_move_to_next, this); grab_focus(); } } @@ -1235,6 +1350,10 @@ void StyleDialog::_valueEdited(const Glib::ustring &path, const Glib::ustring &v if (i != std::string::npos) { finalvalue.erase(i, finalvalue.size() - i); } + Glib::ustring old_value = row[_mColumns._colValue]; + if (old_value == finalvalue) { + return; + } row[_mColumns._colValue] = finalvalue; Glib::ustring selector = row[_mColumns._colSelector]; Glib::ustring name = row[_mColumns._colName]; @@ -1258,56 +1377,81 @@ void StyleDialog::_activeToggled(const Glib::ustring &path, Glib::RefPtrkeyval) { case GDK_KEY_Tab: case GDK_KEY_KP_Tab: - case GDK_KEY_colon: { - cell->editing_done(); - return true; - } + entry->editing_done(); + ret = true; + break; + } + return ret; +} + +bool StyleDialog::_onNameKeyReleased(GdkEventKey *event, Gtk::Entry *entry) +{ + g_debug("StyleDialog::_onNameKeyReleased"); + bool ret = false; + switch (event->keyval) { + case GDK_KEY_equal: + case GDK_KEY_colon: + entry->editing_done(); + ret = true; + break; case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: case GDK_KEY_semicolon: { - Gtk::Entry *entry = dynamic_cast(cell); Glib::ustring text = entry->get_text(); auto i = std::min(text.find(";"), text.find(":")); if (i != std::string::npos) { - cell->editing_done(); - return true; + entry->editing_done(); + ret = true; } + break; } } - return false; + return ret; } -bool StyleDialog::_onValueKeyReleased(GdkEventKey *event, Gtk::CellEditable *cell) +bool StyleDialog::_onValueKeyPressed(GdkEventKey *event, Gtk::Entry *entry) { g_debug("StyleDialog::_onValueKeyReleased"); - + bool ret = false; switch (event->keyval) { case GDK_KEY_Tab: case GDK_KEY_KP_Tab: - case GDK_KEY_semicolon: { - cell->editing_done(); - return true; - } + entry->editing_done(); + ret = true; + break; + } + return ret; +} + +bool StyleDialog::_onValueKeyReleased(GdkEventKey *event, Gtk::Entry *entry) +{ + g_debug("StyleDialog::_onValueKeyReleased"); + bool ret = false; + switch (event->keyval) { + case GDK_KEY_semicolon: + entry->editing_done(); + ret = true; + break; case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: case GDK_KEY_colon: { - Gtk::Entry *entry = dynamic_cast(cell); Glib::ustring text = entry->get_text(); auto i = std::min(text.find(";"), text.find(":")); if (i != std::string::npos) { - cell->editing_done(); - return true; + entry->editing_done(); + ret = true; } + break; } } - return false; + return ret; } void StyleDialog::_addWatcherRecursive(Inkscape::XML::Node *node) diff --git a/src/ui/dialog/styledialog.h b/src/ui/dialog/styledialog.h index 90b9d298fc552732b432fc47febf9219570fb2a1..b7ccf661999f019886e6b67e1bcf130af62494ba 100644 --- a/src/ui/dialog/styledialog.h +++ b/src/ui/dialog/styledialog.h @@ -67,6 +67,10 @@ class StyleDialog : public Widget::Panel { static StyleDialog &getInstance() { return *new StyleDialog(); } void setCurrentSelector(Glib::ustring current_selector); + Gtk::TreeView *_current_css_tree; + Gtk::TreeViewColumn *_current_value_col; + Gtk::TreeModel::Path _current_path; + Glib::ustring fixCSSSelectors(Glib::ustring selector); private: // Monitor