18#include <gdk/gdkkeysyms.h>
19#include <glibmm/i18n.h>
20#include <glibmm/main.h>
21#include <glibmm/regex.h>
22#include <glibmm/timer.h>
23#include <glibmm/ustring.h>
24#include <glibmm/varianttype.h>
25#include <giomm/simpleactiongroup.h>
27#include <gtkmm/builder.h>
28#include <gtkmm/button.h>
29#include <gtkmm/entry.h>
30#include <gtkmm/enums.h>
31#include <gtkmm/eventcontrollerkey.h>
32#include <gtkmm/image.h>
33#include <gtkmm/label.h>
34#include <gtkmm/liststore.h>
35#include <gtkmm/menubutton.h>
36#include <gtkmm/object.h>
37#include <gtkmm/popover.h>
38#include <gtkmm/scrolledwindow.h>
39#include <gtkmm/textview.h>
40#include <gtkmm/treemodel.h>
41#include <gtkmm/treeview.h>
42#include <sigc++/adaptors/bind.h>
43#include <sigc++/functors/mem_fun.h>
48# include <gtksourceview/gtksource.h>
94 auto& textview = edit->getTextView();
95 textview.set_wrap_mode(Gtk::WrapMode::WORD);
104 textview.signal_map().connect([owner](){
124 , _scrolled_text_view(
get_widget<
Gtk::ScrolledWindow>(_builder,
"scroll-wnd"))
125 , _content_sw(
get_widget<
Gtk::ScrolledWindow>(_builder,
"content-sw"))
126 , _scrolled_window(
get_widget<
Gtk::ScrolledWindow>(_builder,
"scrolled-wnd"))
127 , _treeView(
get_widget<
Gtk::TreeView>(_builder,
"tree-view"))
133 using namespace Syntax;
143 set_size_request(20, 15);
147 tv->get_buffer()->signal_end_user_action().connect([=,
this]() {
158 auto const delete_renderer = Gtk::make_managed<UI::Widget::IconRenderer>();
159 delete_renderer->add_icon(
"edit-delete");
161 _treeView.append_column(
"", *delete_renderer);
163 if (
auto const col =
_treeView.get_column(0)) {
164 auto add_icon = Gtk::manage(
sp_get_icon_image(
"list-add", Gtk::IconSize::NORMAL));
165 col->set_clickable(
true);
166 col->set_widget(*add_icon);
167 add_icon->set_tooltip_text(_(
"Add a new attribute"));
168 add_icon->set_visible(
true);
172 auto const key = Gtk::EventControllerKey::create();
179 _nameRenderer->property_placeholder_text().set_value(_(
"Attribute Name"));
192 _status.set_markup(message ? message :
"");
197 _valueRenderer->property_placeholder_text().set_value(_(
"Attribute Value"));
198 _valueRenderer->property_ellipsize().set_value(Pango::EllipsizeMode::END);
210 auto& apply = get_widget<Gtk::Button>(
_builder,
"btn-ok");
213 auto& cancel = get_widget<Gtk::Button>(
_builder,
"btn-cancel");
214 cancel.signal_clicked().connect([
this]{
216 _activeTextView().get_buffer()->set_text(_value_editing);
224 auto const popover_key = Gtk::EventControllerKey::create();
225 popover_key->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
227 _popover->add_controller(popover_key);
234 auto group = Gio::SimpleActionGroup::create();
236 action->property_state().signal_changed().connect([=,
this]{
int n; action->get_state(n);
238 insert_action_group(
"attrdialog", std::move(group));
278 vscroll->set_value(vscroll->get_lower());
286 if (!
_popover->is_visible())
return false;
290 case GDK_KEY_KP_Enter:
314 constexpr int MAX_LENGTH = 500;
316 Glib::ustring renderval;
319 if (g_utf8_strlen(value, -1) > MAX_LENGTH) {
320 renderval = Glib::ustring(value, MAX_LENGTH) +
"…";
326 auto ind = renderval.find(
'\n');
327 if (ind != Glib::ustring::npos) {
328 renderval.replace(ind, Glib::ustring::npos,
" ⏎ …");
340 static Glib::ustring
const class_name =
"mono-font";
341 auto const has_class = widget->has_css_class(class_name);
343 if (mono && !has_class) {
344 widget->add_css_class(class_name);
345 }
else if (!mono && has_class) {
346 widget->remove_css_class(class_name);
357 Gtk::Entry *entry =
dynamic_cast<Gtk::Entry *
>(cell);
370 if (!
_repr || !cell) {
374 auto const iter =
_store->get_iter(path);
382 const int dlg_width = get_allocated_width() - 10;
392 bool enable_rouding =
false;
394 if (attribute ==
"style") {
396 }
else if (attribute ==
"d" || attribute ==
"inkscape:original-d") {
397 enable_rouding =
true;
399 }
else if (attribute ==
"points") {
400 enable_rouding =
true;
404 edit_in_popup =
false;
408 get_widget<Gtk::Box>(
_builder,
"rounding-box").set_visible(enable_rouding);
414 auto entry =
dynamic_cast<Gtk::Entry*
>(cell);
419 int const width = entry->get_width();
423 edit_in_popup || colwidth - 10 <
width)
429 if (
_popover->get_position() == Gtk::PositionType::BOTTOM) {
430 rect.set_y(rect.get_y() + 20);
432 if (rect.get_x() >= dlg_width) {
433 rect.set_x(dlg_width - 1);
441 cell->property_editing_canceled() =
true;
442 cell->remove_widget();
444 Glib::signal_timeout().connect_once([=](){
445 cell->editing_done();
446 cell->remove_widget();
493 auto type = repr->
name();
494 auto elem = repr->
parent();
495 if (type && strcmp(type,
"string") == 0 && elem && elem->name() && strcmp(elem->name(),
"svg:style") == 0) {
517 auto const iter =
_store->prepend();
518 auto const path =
static_cast<Gtk::TreeModel::Path
>(iter);
526 _store->erase(row.get_iter());
528 setUndo(_(
"Delete attribute"));
533 g_assert(!(entry ==
nullptr && embedNewline));
551 const gchar *
name = g_quark_to_string(attr);
554 _(
"Attribute <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."),
name);
569 auto const name = g_quark_to_string(name_);
571 Glib::ustring renderval;
576 for (
auto &row :
_store->children()) {
578 if (
name == col_name) {
584 _store->erase(row.get_iter());
591 Gtk::TreeModel::Row row = *
_store->prepend();
617 Gtk::TreeModel::Row row = *
_store->get_iter(path);
626 auto textview =
dynamic_cast<Gtk::TextView *
>(
_content_sw.get_child());
630 auto buffer = textview->get_buffer();
631 if (!buffer->get_modified()) {
632 auto str = new_content.
pointer();
633 buffer->set_text(str ? str :
"");
635 buffer->set_modified(
false);
650 case GDK_KEY_KP_Delete: {
664 case GDK_KEY_KP_Enter:
680 case GDK_KEY_KP_Enter:
692 auto const iter =
selection->get_selected();
693 Gtk::TreeModel::Path path;
694 Gtk::TreeViewColumn *focus_column;
695 _treeView.get_cursor(path, focus_column);
696 if (path == modelpath && focus_column ==
_treeView.get_column(1)) {
706 auto iter =
_store->get_iter(path);
707 auto modelpath =
static_cast<Gtk::TreeModel::Path
>(iter);
709 if(row && this->
_repr) {
711 if (old_name ==
name) {
712 Glib::signal_timeout().connect_once([=,
this]{
storeMoveToNext(modelpath); }, 50);
721 const auto children =
_store->children();
722 for (
const auto &
child : children) {
727 if(std::any_of(
name.begin(),
name.end(), isspace)) {
732 if (!old_name.empty()) {
745 Glib::signal_timeout().connect_once([=,
this]{
storeMoveToNext(modelpath); }, 50);
746 setUndo(_(
"Rename attribute"));
769 Gtk::TreeModel::Row row = *
_store->get_iter(path);
773 if (old_value == value ||
name.empty()) {
779 if (!value.empty()) {
784 setUndo(_(
"Change attribute value"));
791 auto &menu_button = get_widget<Gtk::MenuButton>(
_builder,
"btn-menu");
792 auto const menu = menu_button.get_menu_model();
793 auto const section = menu->get_item_link(0, Gio::MenuModel::Link::SECTION);
794 auto const type = Glib::VariantType{g_variant_type_new(
"s")};
795 auto const variant = section->get_item_attribute(n, Gio::MenuModel::Attribute::LABEL, type);
796 auto const label =
' ' +
static_cast<Glib::Variant<Glib::ustring>
const &
>(variant).get();
797 get_widget<Gtk::Label>(
_builder,
"precision").set_label(
label);
799 menu_button.set_active(
false);
static Glib::ustring get_syntax_theme()
static bool is_text_or_comment_node(Inkscape::XML::Node const &node)
Return true if node is a text or comment node.
A dialog for XML attributes based on Gtk TreeView.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
int getIntLimited(Glib::ustring const &pref_path, int def=0, int min=INT_MIN, int max=INT_MAX)
Retrieve a limited integer.
Gtk::TreeModelColumn< Glib::ustring > _attributeValue
Gtk::TreeModelColumn< Glib::ustring > _attributeValueRender
Gtk::TreeModelColumn< Glib::ustring > _attributeName
The AttrDialog class This dialog allows to add, delete and modify XML attributes created in the xml e...
void adjust_popup_edit_size()
static std::unique_ptr< Syntax::TextEditView > init_text_view(AttrDialog *owner, Syntax::SyntaxMode coloring, bool map)
void setUndo(Glib::ustring const &event_description)
void startNameEdit(Gtk::CellEditable *cell, const Glib::ustring &path)
bool onTreeViewKeyPressed(unsigned keyval, unsigned keycode, Gdk::ModifierType state)
AttrDialog::onTreeViewKeyPressed Delete or create elements based on key presses.
void attr_reset_context(gint attr)
Sets the XML status bar, depending on which attr is selected.
Gtk::ScrolledWindow & _scrolled_text_view
sigc::scoped_connection _message_changed_connection
Signal handlers.
Gtk::TreeViewColumn * _valueCol
Gtk::TreeView & _treeView
void set_mono_font(bool mono)
void startValueEdit(Gtk::CellEditable *cell, const Glib::ustring &path)
Gtk::Entry * _editingEntry
Gtk::CellRendererText * _valueRenderer
void set_current_textedit(Syntax::TextEditView *edit)
Gtk::ScrolledWindow & _scrolled_window
void deleteAttribute(Gtk::TreeRow &row)
std::unique_ptr< Syntax::TextEditView > _points_edit
Glib::ustring _value_editing
void nameEdited(const Glib::ustring &path, const Glib::ustring &name)
Called when the name is edited in the TreeView editable column.
void notifyContentChanged(XML::Node &node, Util::ptr_shared old_content, Util::ptr_shared new_content) final
Content change callback.
void storeMoveToNext(Gtk::TreeModel::Path modelpath)
std::unique_ptr< Syntax::TextEditView > _text_edit
std::unique_ptr< Inkscape::MessageContext > _message_context
Gtk::CellRendererText * _nameRenderer
Syntax::TextEditView * _current_text_edit
std::unique_ptr< Syntax::TextEditView > _style_edit
Glib::RefPtr< Gtk::Builder > _builder
std::unique_ptr< Syntax::TextEditView > _attr_edit
void truncateDigits() const
Round the selected floating point numbers in the attribute edit popover.
void valueEdited(const Glib::ustring &path, const Glib::ustring &value)
AttrDialog::valueEdited.
void onAttrDelete(Glib::ustring const &path)
AttrDialog::onAttrDelete.
sigc::scoped_connection _adjust_size
void setRepr(Inkscape::XML::Node *repr)
AttrDialog::setRepr Set the internal xml object that I'm working on right now.
void notifyAttributeChanged(XML::Node &repr, GQuark name, Util::ptr_shared old_value, Util::ptr_shared new_value) final
AttrDialog::notifyAttributeChanged This is called when the XML has an updated attribute.
Gtk::TreeViewColumn * _nameCol
std::unique_ptr< Inkscape::MessageStack > _message_stack
Glib::ustring _value_path
sigc::scoped_connection _close_popup
std::unique_ptr< Syntax::TextEditView > _css_edit
Inkscape::XML::Node * _repr
void onTreeViewKeyReleased(unsigned keyval, unsigned keycode, Gdk::ModifierType state)
void onCreateClicked()
AttrDialog::onCreateClicked This function is a slot to signal_clicked for '+' button panel.
Gtk::ScrolledWindow & _content_sw
Gtk::TextView & _activeTextView() const
std::unique_ptr< Syntax::TextEditView > _svgd_edit
void setEditingEntry(Gtk::Entry *entry, bool embedNewline)
void setPrecision(int const n)
bool onPopoverKeyPressed(unsigned keyval, unsigned keycode, Gdk::ModifierType state)
AttrDialog()
Constructor A treeview whose each row corresponds to an XML attribute of a selected node New attribut...
Glib::RefPtr< Gtk::ListStore > _store
DialogBase is the base class for the dialog system.
SPDocument * getDocument() const
SPDesktop * getDesktop() const
Base class for styled text editing widget.
static std::unique_ptr< TextEditView > create(SyntaxMode mode)
Create a styled text view using the desired syntax highlighting mode.
virtual Glib::ustring getText() const =0
virtual void setStyle(const Glib::ustring &theme)=0
virtual void setText(const Glib::ustring &text)=0
virtual Gtk::TextView & getTextView() const =0
char const * pointer() const
Interface for refcounted XML nodes.
virtual Node * parent()=0
Get the parent of this node.
virtual void synthesizeEvents(NodeObserver &observer)=0
Generate a sequence of events corresponding to the state of this node.
virtual char const * name() const =0
Get the name of the element node.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
virtual void setContent(char const *value)=0
Set the content of a text or comment node.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
virtual void addObserver(NodeObserver &observer)=0
Add an object that will be notified of the changes to this node.
virtual NodeType type() const =0
Get the type of the node.
virtual void removeObserver(NodeObserver &observer)=0
Remove an object from the list of observers.
Utilities to more easily use Gtk::EventController & subclasses like Gesture.
Utility functions to convert ascii representations to numbers.
std::unordered_map< std::string, std::unique_ptr< SPDocument > > map
TODO: insert short description here.
Gtk::Image * sp_get_icon_image(Glib::ustring const &icon_name, int size)
Macro for icon names used in Inkscape.
Inkscape::XML::Node * node
Interface for locally managing a current status message.
Raw stack of active status messages.
static R & anchor(R &r)
Increments the reference count of a anchored object.
static R & release(R &r)
Decrements the reference count of a anchored object.
bool has_flag(Gdk::ModifierType const state, Gdk::ModifierType const flags)
Helper to query if ModifierType state contains one or more of given flag(s).
constexpr int MAX_POPOVER_WIDTH
void set_mono_class(Gtk::Widget *widget, bool mono)
static Glib::ustring prepare_rendervalue(const char *value)
Prepare value string suitable for display in a Gtk::CellRendererText.
constexpr int TEXT_MARGIN
constexpr int MAX_POPOVER_HEIGHT
SyntaxMode
Syntax highlighting mode (language).
static void popup_at(Gtk::Popover &popover, Gtk::Widget &widget, double const x_offset, double const y_offset, int width, int height)
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
@ COMMENT_NODE
Comment node, e.g. <!– some comment –>.
@ TEXT_NODE
Text node, e.g. "Some text" in <group>Some text</group> is represented by a text node.
MessageType
A hint about the meaning of a message; is it an ordinary message, a message advising the user of some...
static cairo_user_data_key_t key
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Singleton class to access the preferences file in a convenient way.
void truncate_digits(const Glib::RefPtr< Gtk::TextBuffer > &buffer, int precision)