From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- src/ui/dialog/color-item.cpp | 667 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 667 insertions(+) create mode 100644 src/ui/dialog/color-item.cpp (limited to 'src/ui/dialog/color-item.cpp') diff --git a/src/ui/dialog/color-item.cpp b/src/ui/dialog/color-item.cpp new file mode 100644 index 0000000..1daadd9 --- /dev/null +++ b/src/ui/dialog/color-item.cpp @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Inkscape color swatch UI item. + */ +/* Authors: + * Jon A. Cruz + * Abhishek Sharma + * + * Copyright (C) 2010 Jon A. Cruz + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#include +#include + +#include "color-item.h" + +#include "desktop.h" + +#include "desktop-style.h" +#include "display/cairo-utils.h" +#include "document.h" +#include "document-undo.h" +#include "inkscape.h" // for SP_ACTIVE_DESKTOP +#include "message-context.h" + +#include "io/resource.h" +#include "io/sys.h" +#include "svg/svg-color.h" +#include "ui/icon-names.h" +#include "ui/widget/gradient-vector-selector.h" + + +namespace Inkscape { +namespace UI { +namespace Dialog { + +static std::vector mimeStrings; +static std::map mimeToInt; + + +void +ColorItem::handleClick() { + buttonClicked(false); +} + +void +ColorItem::handleSecondaryClick(gint /*arg1*/) { + buttonClicked(true); +} + +bool +ColorItem::handleEnterNotify(GdkEventCrossing* /*event*/) { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if ( desktop ) { + gchar* msg = g_strdup_printf(_("Color: %s; Click to set fill, Shift+click to set stroke"), + def.descr.c_str()); + desktop->tipsMessageContext()->set(Inkscape::INFORMATION_MESSAGE, msg); + g_free(msg); + } + + return false; +} + +bool +ColorItem::handleLeaveNotify(GdkEventCrossing* /*event*/) { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + if ( desktop ) { + desktop->tipsMessageContext()->clear(); + } + + return false; +} + +static bool getBlock( std::string& dst, guchar ch, std::string const & str ) +{ + bool good = false; + std::string::size_type pos = str.find(ch); + if ( pos != std::string::npos ) + { + std::string::size_type pos2 = str.find( '(', pos ); + if ( pos2 != std::string::npos ) { + std::string::size_type endPos = str.find( ')', pos2 ); + if ( endPos != std::string::npos ) { + dst = str.substr( pos2 + 1, (endPos - pos2 - 1) ); + good = true; + } + } + } + return good; +} + +static bool popVal( guint64& numVal, std::string& str ) +{ + bool good = false; + std::string::size_type endPos = str.find(','); + if ( endPos == std::string::npos ) { + endPos = str.length(); + } + + if ( endPos != std::string::npos && endPos > 0 ) { + std::string xxx = str.substr( 0, endPos ); + const gchar* ptr = xxx.c_str(); + gchar* endPtr = nullptr; + numVal = g_ascii_strtoull( ptr, &endPtr, 10 ); + if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) { + // overflow + } else if ( (numVal == 0) && (endPtr == ptr) ) { + // failed conversion + } else { + good = true; + str.erase( 0, endPos + 1 ); + } + } + + return good; +} + +// TODO resolve this more cleanly: +extern bool colorItemHandleButtonPress(GdkEventButton* event, UI::Widget::Preview *preview, gpointer user_data); + +void +ColorItem::drag_begin(const Glib::RefPtr &dc) +{ + using Inkscape::IO::Resource::get_path; + using Inkscape::IO::Resource::PIXMAPS; + using Inkscape::IO::Resource::SYSTEM; + int width = 32; + int height = 24; + + if (def.getType() != ege::PaintDef::RGB){ + GError *error; + gsize bytesRead = 0; + gsize bytesWritten = 0; + gchar *localFilename = g_filename_from_utf8(get_path(SYSTEM, PIXMAPS, "remove-color.png"), -1, &bytesRead, + &bytesWritten, &error); + auto pixbuf = Gdk::Pixbuf::create_from_file(localFilename, width, height, false); + g_free(localFilename); + dc->set_icon(pixbuf, 0, 0); + } else { + Glib::RefPtr pixbuf; + if (getGradient() ){ + cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_pattern_t *gradient = getGradient()->create_preview_pattern(width); + cairo_t *ct = cairo_create(s); + cairo_set_source(ct, gradient); + cairo_paint(ct); + cairo_destroy(ct); + cairo_pattern_destroy(gradient); + cairo_surface_flush(s); + + pixbuf = Glib::wrap(ink_pixbuf_create_from_cairo_surface(s)); + } else { + pixbuf = Gdk::Pixbuf::create( Gdk::COLORSPACE_RGB, false, 8, width, height ); + guint32 fillWith = (0xff000000 & (def.getR() << 24)) + | (0x00ff0000 & (def.getG() << 16)) + | (0x0000ff00 & (def.getB() << 8)); + pixbuf->fill( fillWith ); + } + dc->set_icon(pixbuf, 0, 0); + } +} + +//"drag-drop" +// gboolean dragDropColorData( GtkWidget *widget, +// GdkDragContext *drag_context, +// gint x, +// gint y, +// guint time, +// gpointer user_data) +// { +// // TODO finish + +// return TRUE; +// } + + +SwatchPage::SwatchPage() + : _prefWidth(0) +{ +} + +SwatchPage::~SwatchPage() += default; + + +ColorItem::ColorItem(ege::PaintDef::ColorType type) : + def(type), + _isFill(false), + _isStroke(false), + _isLive(false), + _linkIsTone(false), + _linkPercent(0), + _linkGray(0), + _linkSrc(nullptr), + _grad(nullptr), + _pattern(nullptr) +{ +} + +ColorItem::ColorItem( unsigned int r, unsigned int g, unsigned int b, Glib::ustring& name ) : + def( r, g, b, name.raw() ), + _isFill(false), + _isStroke(false), + _isLive(false), + _linkIsTone(false), + _linkPercent(0), + _linkGray(0), + _linkSrc(nullptr), + _grad(nullptr), + _pattern(nullptr) +{ +} + +ColorItem::~ColorItem() +{ + if (_pattern != nullptr) { + cairo_pattern_destroy(_pattern); + } +} + +ColorItem::ColorItem(ColorItem const &other) : + Inkscape::UI::Previewable() +{ + if ( this != &other ) { + *this = other; + } +} + +ColorItem &ColorItem::operator=(ColorItem const &other) +{ + if ( this != &other ) { + def = other.def; + + // TODO - correct linkage + _linkSrc = other._linkSrc; + g_message("Erk!"); + } + return *this; +} + +void ColorItem::setState( bool fill, bool stroke ) +{ + if ( (_isFill != fill) || (_isStroke != stroke) ) { + _isFill = fill; + _isStroke = stroke; + + for ( auto widget : _previews ) { + auto preview = dynamic_cast(widget); + + if (preview) { + int val = preview->get_linked(); + val &= ~(UI::Widget::PREVIEW_FILL | UI::Widget::PREVIEW_STROKE); + if ( _isFill ) { + val |= UI::Widget::PREVIEW_FILL; + } + if ( _isStroke ) { + val |= UI::Widget::PREVIEW_STROKE; + } + preview->set_linked(static_cast(val)); + } + } + } +} + +void ColorItem::setGradient(SPGradient *grad) +{ + if (_grad != grad) { + _grad = grad; + // TODO regen and push to listeners + } + + setName( gr_prepare_label(_grad) ); +} + +void ColorItem::setName(const Glib::ustring name) +{ + //def.descr = name; + + for (auto widget : _previews) { + auto preview = dynamic_cast(widget); + auto label = dynamic_cast(widget); + if (preview) { + preview->set_tooltip_text(name); + } + else if (label) { + label->set_text(name); + } + } +} + +void ColorItem::setPattern(cairo_pattern_t *pattern) +{ + if (pattern) { + cairo_pattern_reference(pattern); + } + if (_pattern) { + cairo_pattern_destroy(_pattern); + } + _pattern = pattern; + + _updatePreviews(); +} + +void +ColorItem::_dragGetColorData(const Glib::RefPtr& /*drag_context*/, + Gtk::SelectionData &data, + guint info, + guint /*time*/) +{ + std::string key; + if ( info < mimeStrings.size() ) { + key = mimeStrings[info]; + } else { + g_warning("ERROR: unknown value (%d)", info); + } + + if ( !key.empty() ) { + char* tmp = nullptr; + int len = 0; + int format = 0; + def.getMIMEData(key, tmp, len, format); + if ( tmp ) { + data.set(key, format, (guchar*)tmp, len ); + delete[] tmp; + } + } +} + +void ColorItem::_dropDataIn( GtkWidget */*widget*/, + GdkDragContext */*drag_context*/, + gint /*x*/, gint /*y*/, + GtkSelectionData */*data*/, + guint /*info*/, + guint /*event_time*/, + gpointer /*user_data*/) +{ +} + +void ColorItem::_colorDefChanged(void* data) +{ + ColorItem* item = reinterpret_cast(data); + if ( item ) { + item->_updatePreviews(); + } +} + +void ColorItem::_updatePreviews() +{ + for (auto widget : _previews) { + auto preview = dynamic_cast(widget); + if (preview) { + _regenPreview(preview); + preview->queue_draw(); + } + } + + for (auto & _listener : _listeners) { + guint r = def.getR(); + guint g = def.getG(); + guint b = def.getB(); + + if ( _listener->_linkIsTone ) { + r = ( (_listener->_linkPercent * _listener->_linkGray) + ((100 - _listener->_linkPercent) * r) ) / 100; + g = ( (_listener->_linkPercent * _listener->_linkGray) + ((100 - _listener->_linkPercent) * g) ) / 100; + b = ( (_listener->_linkPercent * _listener->_linkGray) + ((100 - _listener->_linkPercent) * b) ) / 100; + } else { + r = ( (_listener->_linkPercent * 255) + ((100 - _listener->_linkPercent) * r) ) / 100; + g = ( (_listener->_linkPercent * 255) + ((100 - _listener->_linkPercent) * g) ) / 100; + b = ( (_listener->_linkPercent * 255) + ((100 - _listener->_linkPercent) * b) ) / 100; + } + + _listener->def.setRGB( r, g, b ); + } +} + +void ColorItem::_regenPreview(UI::Widget::Preview * preview) +{ + if ( def.getType() != ege::PaintDef::RGB ) { + using Inkscape::IO::Resource::get_path; + using Inkscape::IO::Resource::PIXMAPS; + using Inkscape::IO::Resource::SYSTEM; + GError *error = nullptr; + gsize bytesRead = 0; + gsize bytesWritten = 0; + gchar *localFilename = + g_filename_from_utf8(get_path(SYSTEM, PIXMAPS, "remove-color.png"), -1, &bytesRead, &bytesWritten, &error); + auto pixbuf = Gdk::Pixbuf::create_from_file(localFilename); + if (!pixbuf) { + g_warning("Null pixbuf for %p [%s]", localFilename, localFilename ); + } + g_free(localFilename); + + preview->set_pixbuf(pixbuf); + } + else if ( !_pattern ){ + preview->set_color((def.getR() << 8) | def.getR(), + (def.getG() << 8) | def.getG(), + (def.getB() << 8) | def.getB() ); + } else { + // These correspond to PREVIEW_PIXBUF_WIDTH and VBLOCK from swatches.cpp + // TODO: the pattern to draw should be in the widget that draws the preview, + // so the preview can be scalable + int w = 128; + int h = 16; + + cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + cairo_t *ct = cairo_create(s); + cairo_set_source(ct, _pattern); + cairo_paint(ct); + cairo_destroy(ct); + cairo_surface_flush(s); + + auto pixbuf = Glib::wrap(ink_pixbuf_create_from_cairo_surface(s)); + preview->set_pixbuf(pixbuf); + } + + preview->set_linked(static_cast( (_linkSrc ? UI::Widget::PREVIEW_LINK_IN : 0) + | (_listeners.empty() ? 0 : UI::Widget::PREVIEW_LINK_OUT) + | (_isLive ? UI::Widget::PREVIEW_LINK_OTHER:0)) ); +} + +Gtk::Widget* ColorItem::createWidget() { + auto widget = dynamic_cast(_getPreview(Inkscape::UI::Widget::PREVIEW_STYLE_ICON, + Inkscape::UI::Widget::VIEW_TYPE_GRID, Inkscape::UI::Widget::PREVIEW_SIZE_TINY, 100, 0)); + + if (widget) widget->set_freesize(true); + + return widget; +} + +Gtk::Widget* +ColorItem::getPreview(UI::Widget::PreviewStyle style, + UI::Widget::ViewType view, + UI::Widget::PreviewSize size, + guint ratio, + guint border) +{ + auto widget = _getPreview(style, view, size, ratio, border); + _previews.push_back( widget ); + return widget; +} + + +Gtk::Widget* ColorItem::_getPreview(UI::Widget::PreviewStyle style, + UI::Widget::ViewType view, UI::Widget::PreviewSize size, + guint ratio, guint border) { + + Gtk::Widget* widget = nullptr; + if ( style == UI::Widget::PREVIEW_STYLE_BLURB) { + Gtk::Label *lbl = new Gtk::Label(def.descr); + lbl->set_halign(Gtk::ALIGN_START); + lbl->set_valign(Gtk::ALIGN_CENTER); + widget = lbl; + } else { + auto preview = Gtk::manage(new UI::Widget::Preview()); + preview->set_name("ColorItemPreview"); + + _regenPreview(preview); + + preview->set_details((UI::Widget::ViewType)view, + (UI::Widget::PreviewSize)size, + ratio, + border ); + + def.addCallback( _colorDefChanged, this ); + preview->set_focus_on_click(false); + preview->set_tooltip_text(def.descr); + + preview->signal_clicked().connect(sigc::mem_fun(*this, &ColorItem::handleClick)); + preview->signal_alt_clicked().connect(sigc::mem_fun(*this, &ColorItem::handleSecondaryClick)); + preview->signal_button_press_event().connect(sigc::bind(sigc::ptr_fun(&colorItemHandleButtonPress), preview, this)); + + { + auto listing = def.getMIMETypes(); + std::vector entries; + + for ( auto str : listing ) { + auto target = str.c_str(); + guint flags = 0; + if ( mimeToInt.find(str) == mimeToInt.end() ){ + // these next lines are order-dependent: + mimeToInt[str] = mimeStrings.size(); + mimeStrings.push_back(str); + } + auto info = mimeToInt[target]; + Gtk::TargetEntry entry(target, (Gtk::TargetFlags)flags, info); + entries.push_back(entry); + } + + preview->drag_source_set(entries, Gdk::BUTTON1_MASK, + Gdk::DragAction(Gdk::ACTION_MOVE | Gdk::ACTION_COPY) ); + } + + preview->signal_drag_data_get().connect(sigc::mem_fun(*this, &ColorItem::_dragGetColorData)); + preview->signal_drag_begin().connect(sigc::mem_fun(*this, &ColorItem::drag_begin)); + preview->signal_enter_notify_event().connect(sigc::mem_fun(*this, &ColorItem::handleEnterNotify)); + preview->signal_leave_notify_event().connect(sigc::mem_fun(*this, &ColorItem::handleLeaveNotify)); + + widget = preview; + } + + return widget; +} + +void ColorItem::buttonClicked(bool secondary) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop) { + char const * attrName = secondary ? "stroke" : "fill"; + + SPCSSAttr *css = sp_repr_css_attr_new(); + Glib::ustring descr; + switch (def.getType()) { + case ege::PaintDef::CLEAR: { + // TODO actually make this clear + sp_repr_css_set_property( css, attrName, "none" ); + descr = secondary? _("Remove stroke color") : _("Remove fill color"); + break; + } + case ege::PaintDef::NONE: { + sp_repr_css_set_property( css, attrName, "none" ); + descr = secondary? _("Set stroke color to none") : _("Set fill color to none"); + break; + } +//mark + case ege::PaintDef::RGB: { + Glib::ustring colorspec; + if ( _grad ){ + colorspec = "url(#"; + colorspec += _grad->getId(); + colorspec += ")"; + } else { + gchar c[64]; + guint32 rgba = (def.getR() << 24) | (def.getG() << 16) | (def.getB() << 8) | 0xff; + sp_svg_write_color(c, sizeof(c), rgba); + colorspec = c; + } +//end mark + sp_repr_css_set_property( css, attrName, colorspec.c_str() ); + descr = secondary? _("Set stroke color from swatch") : _("Set fill color from swatch"); + break; + } + } + sp_desktop_set_style(desktop, css); + sp_repr_css_attr_unref(css); + + DocumentUndo::done( desktop->getDocument(), descr.c_str(), INKSCAPE_ICON("swatches")); + } +} + +void ColorItem::_wireMagicColors( SwatchPage *colorSet ) +{ + if ( colorSet ) + { + for ( boost::ptr_vector::iterator it = colorSet->_colors.begin(); it != colorSet->_colors.end(); ++it ) + { + std::string::size_type pos = it->def.descr.find("*{"); + if ( pos != std::string::npos ) + { + std::string subby = it->def.descr.substr( pos + 2 ); + std::string::size_type endPos = subby.find("}*"); + if ( endPos != std::string::npos ) + { + subby.erase( endPos ); + //g_message("FOUND MAGIC at '%s'", (*it)->def.descr.c_str()); + //g_message(" '%s'", subby.c_str()); + + if ( subby.find('E') != std::string::npos ) + { + it->def.setEditable( true ); + } + + if ( subby.find('L') != std::string::npos ) + { + it->_isLive = true; + } + + std::string part; + // Tint. index + 1 more val. + if ( getBlock( part, 'T', subby ) ) { + guint64 colorIndex = 0; + if ( popVal( colorIndex, part ) ) { + guint64 percent = 0; + if ( popVal( percent, part ) ) { + it->_linkTint( colorSet->_colors[colorIndex], percent ); + } + } + } + + // Shade/tone. index + 1 or 2 more val. + if ( getBlock( part, 'S', subby ) ) { + guint64 colorIndex = 0; + if ( popVal( colorIndex, part ) ) { + guint64 percent = 0; + if ( popVal( percent, part ) ) { + guint64 grayLevel = 0; + if ( !popVal( grayLevel, part ) ) { + grayLevel = 0; + } + it->_linkTone( colorSet->_colors[colorIndex], percent, grayLevel ); + } + } + } + + } + } + } + } +} + + +void ColorItem::_linkTint( ColorItem& other, int percent ) +{ + if ( !_linkSrc ) + { + other._listeners.push_back(this); + _linkIsTone = false; + _linkPercent = percent; + if ( _linkPercent > 100 ) + _linkPercent = 100; + if ( _linkPercent < 0 ) + _linkPercent = 0; + _linkGray = 0; + _linkSrc = &other; + + ColorItem::_colorDefChanged(&other); + } +} + +void ColorItem::_linkTone( ColorItem& other, int percent, int grayLevel ) +{ + if ( !_linkSrc ) + { + other._listeners.push_back(this); + _linkIsTone = true; + _linkPercent = percent; + if ( _linkPercent > 100 ) + _linkPercent = 100; + if ( _linkPercent < 0 ) + _linkPercent = 0; + _linkGray = grayLevel; + _linkSrc = &other; + + ColorItem::_colorDefChanged(&other); + } +} + +} // namespace Dialog +} // 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 : -- cgit v1.2.3