Skip to content

Commit

Permalink
Add new GValue wrapper for clipboard/DnD
Browse files Browse the repository at this point in the history
The GTK clipboard and DnD APIs make heavy use of GValue, and since the
Glibmm wrapper for this is somewhat clunky, add a less clunky one and
use it for clipboard and DnD.

Replace some leftover optional<Color> with Paint, fixing drag and drop
of colours on the status bar's fill/stroke indicators.
  • Loading branch information
pbs3141 committed Nov 11, 2024
1 parent efa669f commit acd3edd
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 117 deletions.
22 changes: 7 additions & 15 deletions src/ui/clipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
#include "ui/tools/node-tool.h"
#include "ui/tools/text-tool.h"
#include "util/scope_exit.h"
#include "util/value-utils.h"
#include "xml/repr.h"
#include "xml/sp-css-attr.h"

Expand All @@ -99,6 +100,8 @@
#include <windows.h>
#endif

using namespace Inkscape::Util;

namespace Inkscape::UI {
namespace {

Expand Down Expand Up @@ -155,10 +158,7 @@ auto const mime_uti = make_bimap<std::string, std::string>({
#endif

/// Type used to represent the Inkscape clipboard on the GTK clipboard.
struct ClipboardSvg
{
static auto type() { return Glib::Value<ClipboardSvg>::value_type(); }
};
struct ClipboardSvg {};

/*
* Fixme: Get rid of all event pumpers.
Expand Down Expand Up @@ -1608,12 +1608,7 @@ void ClipboardManagerImpl::_retrieveClipboard(Glib::ustring best_target)
_discardInternalClipboard();
}

auto value = Glib::ValueBase{};
value.init(ClipboardSvg::type());

try {
content->get_value(value);
} catch (Glib::Error const &) {
if (!GlibValue::from_content_provider<ClipboardSvg>(*content)) {
_discardInternalClipboard();
}

Expand Down Expand Up @@ -1960,7 +1955,7 @@ void ClipboardManagerImpl::_registerSerializers()
target_list.emplace_back("image/png");

for (auto const &tgt : target_list) {
gdk_content_register_serializer(ClipboardSvg::type(), tgt.c_str(), +[] (GdkContentSerializer *serializer) {
gdk_content_register_serializer(GlibValue::type<ClipboardSvg>(), tgt.c_str(), +[] (GdkContentSerializer *serializer) {
auto mime = gdk_content_serializer_get_mime_type(serializer);
auto out = Glib::wrap(gdk_content_serializer_get_output_stream(serializer), true);
auto self = reinterpret_cast<decltype(this)>(gdk_content_serializer_get_user_data(serializer));
Expand Down Expand Up @@ -2017,10 +2012,7 @@ void ClipboardManagerImpl::_setClipboardTargets()
}
#endif

auto value = Glib::Value<ClipboardSvg>{};
value.init(ClipboardSvg::type());
auto provider = Gdk::ContentProvider::create(value);
_clipboard->set_content(provider);
_clipboard->set_content(Gdk::ContentProvider::create(GlibValue::create<ClipboardSvg>()));
}

/**
Expand Down
13 changes: 6 additions & 7 deletions src/ui/dialog/color-item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "ui/dialog/dialog-container.h"
#include "ui/icon-names.h"
#include "ui/util.h"
#include "util/value-utils.h"
#include "util/variant-visitor.h"

namespace Inkscape::UI::Dialog {
Expand Down Expand Up @@ -508,14 +509,12 @@ Glib::RefPtr<Gdk::ContentProvider> ColorItem::on_drag_prepare()
{
if (!dialog) return {};

Glib::Value<Colors::Paint> value;
value.init(value.value_type());
if (is_paint_none()) {
value.set(Colors::NoColor{});
} else {
value.set(getColor());
Colors::Paint paint;
if (!is_paint_none()) {
paint = getColor();
}
return Gdk::ContentProvider::create(value);

return Gdk::ContentProvider::create(Util::GlibValue::create<Colors::Paint>(std::move(paint)));
}

void ColorItem::on_drag_begin(Gtk::DragSource &source)
Expand Down
18 changes: 7 additions & 11 deletions src/ui/dialog/symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ using namespace std::literals;
#include "ui/icon-loader.h"
#include "ui/pack.h"
#include "util/statics.h"
#include "util/value-utils.h"
#include "xml/href-attribute-helper.h"

#ifdef WITH_LIBVISIO
Expand Down Expand Up @@ -362,22 +363,17 @@ SymbolsDialog::SymbolsDialog(const char* prefsPath)
auto const dims = getSymbolDimensions(dragged);
sendToClipboard(*dragged, Geom::Rect(-0.5 * dims, 0.5 * dims), false);

Glib::Value<DnDSymbol> value;
value.init(value.value_type());
value.set(DnDSymbol{dragged->symbol_id, dragged->unique_key, dragged->symbol_document});
auto content = Gdk::ContentProvider::create(value);
source.set_content(content);
return content;
return Gdk::ContentProvider::create(Util::GlibValue::create<DnDSymbol>(
DnDSymbol{dragged->symbol_id, dragged->unique_key, dragged->symbol_document}
));
};
auto drag_begin = [this, &source = *source](Glib::RefPtr<Gdk::Drag> const &drag) {
auto c = source.get_content();
if (!c) return;

Glib::Value<DnDSymbol> value;
value.init(value.value_type());
c->get_value(value);
const auto& symbol = value.get();
auto tex = get_image(symbol.unique_key, symbol.document, symbol.id);
auto symbol = Util::GlibValue::from_content_provider<DnDSymbol>(*c);

auto tex = get_image(symbol->unique_key, symbol->document, symbol->id);
// TODO: scale for high dpi display (somehow)
int x = 0, y = 0;
if (tex) {
Expand Down
82 changes: 19 additions & 63 deletions src/ui/drag-and-drop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,61 +45,17 @@
#include "ui/tools/tool-base.h"
#include "ui/widget/canvas.h" // Target, canvas to world transform.
#include "ui/widget/desktop-widget.h"
#include "util/value-utils.h"

using Inkscape::DocumentUndo;
using namespace Inkscape::Util;

namespace {

/*
* Gtk API wrapping - Todo: Improve gtkmm.
*/

template <typename T>
T *get(GValue *value)
{
if (G_VALUE_HOLDS(value, Glib::Value<T>::value_type())) {
return reinterpret_cast<T *>(g_value_get_boxed(value));
} else {
return nullptr;
}
}

template <typename T>
T const *get(GValue const *value)
{
if (G_VALUE_HOLDS(value, Glib::Value<T>::value_type())) {
return reinterpret_cast<T const *>(g_value_get_boxed(value));
} else {
return nullptr;
}
}

template <typename T>
T *get(Glib::ValueBase &value)
{
return get<T>(value.gobj());
}

template <typename T>
T const *get(Glib::ValueBase const &value)
{
return get<T>(value.gobj());
}

bool holds(Glib::ValueBase const &value, GType type)
{
return G_VALUE_HOLDS(value.gobj(), type);
}

template <typename T>
GValue make_value(T &&t)
{
Glib::Value<T> v;
v.init(v.value_type());
*reinterpret_cast<T *>(g_value_get_boxed(v.gobj())) = std::forward<T>(t);
return std::exchange(*v.gobj(), GValue(G_VALUE_INIT));
}

template <typename T, typename F>
void foreach(GSList *list, F &&f)
{
Expand Down Expand Up @@ -128,7 +84,7 @@ Glib::RefPtr<Glib::Bytes> make_bytes(T &&t)
}

template <typename T>
GValue from_bytes(Glib::RefPtr<Glib::Bytes> &&bytes, char const *mime_type) = delete;
Glib::ValueBase from_bytes(Glib::RefPtr<Glib::Bytes> &&bytes, char const *mime_type) = delete;

template <typename T>
void deserialize_func(GdkContentDeserializer *deserializer)
Expand All @@ -139,7 +95,7 @@ void deserialize_func(GdkContentDeserializer *deserializer)
try {
out->splice_finish(result);
out->close();
*gdk_content_deserializer_get_value(deserializer) = from_bytes<T>(out->steal_as_bytes(), gdk_content_deserializer_get_mime_type(deserializer));
*gdk_content_deserializer_get_value(deserializer) = GlibValue::release(from_bytes<T>(out->steal_as_bytes(), gdk_content_deserializer_get_mime_type(deserializer)));
gdk_content_deserializer_return_success(deserializer);
} catch (Glib::Error const &error) {
gdk_content_deserializer_return_error(deserializer, g_error_copy(error.gobj()));
Expand All @@ -150,7 +106,7 @@ void deserialize_func(GdkContentDeserializer *deserializer)
template <typename T>
void register_deserializer(char const *mime_type)
{
gdk_content_register_deserializer(mime_type, Glib::Value<T>::value_type(), deserialize_func<T>, nullptr, nullptr);
gdk_content_register_deserializer(mime_type, GlibValue::type<T>(), deserialize_func<T>, nullptr, nullptr);
}

template <typename T>
Expand All @@ -160,7 +116,7 @@ template <typename T>
void serialize_func(GdkContentSerializer *serializer)
{
auto const out = Glib::wrap(gdk_content_serializer_get_output_stream(serializer), true);
auto const bytes = to_bytes(*get<T>(gdk_content_serializer_get_value(serializer)), gdk_content_serializer_get_mime_type(serializer));
auto const bytes = to_bytes(*GlibValue::get<T>(gdk_content_serializer_get_value(serializer)), gdk_content_serializer_get_mime_type(serializer));
auto const span = get_span(bytes);
out->write_all_async(span.data(), span.size_bytes(), [serializer, out, bytes] (Glib::RefPtr<Gio::AsyncResult> &result) {
try {
Expand All @@ -176,7 +132,7 @@ void serialize_func(GdkContentSerializer *serializer)
template <typename T>
void register_serializer(char const *mime_type)
{
gdk_content_register_serializer(Glib::Value<T>::value_type(), mime_type, serialize_func<T>, nullptr, nullptr);
gdk_content_register_serializer(GlibValue::type<T>(), mime_type, serialize_func<T>, nullptr, nullptr);
}

/*
Expand All @@ -189,16 +145,16 @@ struct DnDSvg
};

template <>
GValue from_bytes<DnDSvg>(Glib::RefPtr<Glib::Bytes> &&bytes, char const *)
Glib::ValueBase from_bytes<DnDSvg>(Glib::RefPtr<Glib::Bytes> &&bytes, char const *)
{
return make_value(DnDSvg{std::move(bytes)});
return GlibValue::create<DnDSvg>(DnDSvg{std::move(bytes)});
}

template <>
GValue from_bytes<Colors::Paint>(Glib::RefPtr<Glib::Bytes> &&bytes, char const *mime_type)
Glib::ValueBase from_bytes<Colors::Paint>(Glib::RefPtr<Glib::Bytes> &&bytes, char const *mime_type)
{
try {
return make_value(Colors::fromMIMEData(get_span(bytes), mime_type));
return GlibValue::create<Colors::Paint>(Colors::fromMIMEData(get_span(bytes), mime_type));
} catch (Colors::ColorError const &c) {
throw Glib::Error(G_FILE_ERROR, 0, c.what());
}
Expand Down Expand Up @@ -234,10 +190,10 @@ std::vector<GType> const &get_drop_types()
register_serializer<DnDSymbol>("text/plain;charset=utf-8");

return {
Glib::Value<Colors::Paint>::value_type(),
Glib::Value<DnDSvg>::value_type(),
GlibValue::type<Colors::Paint>(),
GlibValue::type<DnDSvg>(),
GDK_TYPE_FILE_LIST,
Glib::Value<DnDSymbol>::value_type(),
GlibValue::type<DnDSymbol>(),
GDK_TYPE_TEXTURE
};
}();
Expand All @@ -256,7 +212,7 @@ bool on_drop(Glib::ValueBase const &value, double x, double y, SPDesktopWidget *
auto const world_pos = canvas->canvas_to_world(canvas_pos);
auto const dt_pos = desktop->w2d(world_pos);

if (auto const ptr = get<Colors::Paint>(value)) {
if (auto const ptr = GlibValue::get<Colors::Paint>(value)) {
auto paint = *ptr;
auto const item = desktop->getItemAtPoint(world_pos, true);
if (!item) {
Expand Down Expand Up @@ -329,7 +285,7 @@ bool on_drop(Glib::ValueBase const &value, double x, double y, SPDesktopWidget *
item->updateRepr();
DocumentUndo::done(doc, _("Drop color"), "");
return true;
} else if (auto const dndsvg = get<DnDSvg>(value)) {
} else if (auto const dndsvg = GlibValue::get<DnDSvg>(value)) {
auto const data = get_span(dndsvg->bytes);
if (data.empty()) {
return false;
Expand Down Expand Up @@ -370,7 +326,7 @@ bool on_drop(Glib::ValueBase const &value, double x, double y, SPDesktopWidget *
Inkscape::GC::release(newgroup);
DocumentUndo::done(doc, _("Drop SVG"), "");
return true;
} else if (holds(value, GDK_TYPE_FILE_LIST)) {
} else if (G_VALUE_HOLDS(value.gobj(), GDK_TYPE_FILE_LIST)) {
auto list = reinterpret_cast<GSList *>(g_value_get_boxed(value.gobj()));
foreach<GFile>(list, [&] (GFile *f) {
auto const path = g_file_get_path(f);
Expand All @@ -380,12 +336,12 @@ bool on_drop(Glib::ValueBase const &value, double x, double y, SPDesktopWidget *
});

return true;
} else if (holds(value, Glib::Value<DnDSymbol>::value_type())) {
} else if (GlibValue::holds<DnDSymbol>(value)) {
auto cm = Inkscape::UI::ClipboardManager::get();
cm->insertSymbol(desktop, dt_pos, false);
DocumentUndo::done(doc, _("Drop Symbol"), "");
return true;
} else if (holds(value, GDK_TYPE_TEXTURE)) {
} else if (G_VALUE_HOLDS(value.gobj(), GDK_TYPE_TEXTURE)) {
auto const ext = Inkscape::Extension::find_by_mime("image/png");
bool const save = std::strcmp(ext->get_param_optiongroup("link"), "embed") == 0;
ext->set_param_optiongroup("link", "embed");
Expand Down
15 changes: 9 additions & 6 deletions src/ui/widget/selected-style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@
#include <gtkmm/checkbutton.h>

#include "selected-style.h"

#include "colors/dragndrop.h"
#include "colors/manager.h"
#include "colors/xml-color.h"
#include "desktop-style.h"
#include "document-undo.h"
#include "gradient-chemistry.h"
Expand Down Expand Up @@ -53,9 +52,10 @@
#include "ui/widget/gradient-image.h"
#include "ui/widget/popover-menu.h"
#include "ui/widget/popover-menu-item.h"
#include "util/safe-printf.h"
#include "util/units.cpp"
#include "util/value-utils.h"
#include "util-string/ustring-format.h"
#include "util/variant-visitor.h"

static constexpr int SELECTED_STYLE_SB_WIDTH = 48;
static constexpr int SELECTED_STYLE_PLACE_WIDTH = 50;
Expand Down Expand Up @@ -171,15 +171,18 @@ SelectedStyle::SelectedStyle()
drop[i] = std::make_unique<SelectedStyleDropTracker>();
drop[i]->parent = this;
drop[i]->item = i;
auto target = Gtk::DropTarget::create(Glib::Value<std::optional<Colors::Color>>::value_type(), Gdk::DragAction::COPY | Gdk::DragAction::MOVE);
auto target = Gtk::DropTarget::create(Util::GlibValue::type<Colors::Paint>(), Gdk::DragAction::COPY | Gdk::DragAction::MOVE);
target->signal_drop().connect([this, i] (Glib::ValueBase const &value, double, double) {
if (!dropEnabled[i]) {
return false;
}

auto const &tracker = *drop[i];
auto const &color = *reinterpret_cast<std::optional<Colors::Color>*>(g_value_get_boxed(value.gobj()));
std::string colorspec = color ? color->toString(false) : "none";
auto const paint = Util::GlibValue::get<Colors::Paint>(value);
auto const colorspec = std::visit(VariantVisitor{
[] (Colors::Color const &color) { return color.toString(false); },
[] (Colors::NoColor) -> std::string { return "none"; }
}, *paint);

auto const css = sp_repr_css_attr_new();
sp_repr_css_set_property_string(css, tracker.item == SS_FILL ? "fill" : "stroke", colorspec);
Expand Down
Loading

0 comments on commit acd3edd

Please sign in to comment.