summaryrefslogtreecommitdiffstats
path: root/src/livecode/api.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/livecode/api.cpp')
-rw-r--r--src/livecode/api.cpp289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/livecode/api.cpp b/src/livecode/api.cpp
new file mode 100644
index 000000000..4617968c2
--- /dev/null
+++ b/src/livecode/api.cpp
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * API for the livecoding script language
+ *
+ * Authors:
+ * Sol Bekic <s+inkscape@s-ol.nu>
+ * Copyright (C) 2019 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "svg/stringstream.h"
+#include "svg/svg-color.h"
+#include "svg/svg.h"
+#include "document.h"
+#include "display/sp-canvas-item.h"
+
+#include "livecode/api.h"
+
+namespace Inkscape {
+namespace Livecode {
+
+API::API(SPDesktop *desktop)
+ : desktop(desktop)
+ , doc_root(nullptr)
+ , ui_root(nullptr)
+ , _mouse(*this)
+{
+}
+
+API::~API() {
+ if (doc_root) {
+ doc_root->deleteObject(true, true);
+ doc_root = nullptr;
+ }
+
+ if (ui_root) {
+ ui_root->deleteObject(true, true);
+ ui_root = nullptr;
+ }
+}
+
+Inkscape::XML::Node *API::make_rect(Geom::Rect const &rect, SPCSSAttr *css) {
+ SPDocument *doc = desktop->getDocument();
+ Inkscape::XML::Document *xml_doc = doc->getReprDoc();
+
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:rect");
+
+ sp_repr_set_svg_double(repr, "x", rect.left());
+ sp_repr_set_svg_double(repr, "y", rect.top());
+ sp_repr_set_svg_double(repr, "width", rect.width());
+ sp_repr_set_svg_double(repr, "height", rect.height());
+
+ if (css) {
+ Glib::ustring css_str;
+ sp_repr_css_write_string(css, css_str);
+ sp_repr_css_attr_unref(css);
+ repr->setAttribute("style", css_str.c_str());
+ }
+
+ return repr;
+}
+
+Inkscape::XML::Node *API::make_line(Geom::Point const &p1, Geom::Point const &p2, SPCSSAttr *css) {
+ SPDocument *doc = desktop->getDocument();
+ Inkscape::XML::Document *xml_doc = doc->getReprDoc();
+
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:line");
+
+ sp_repr_set_svg_double(repr, "x1", p1.x());
+ sp_repr_set_svg_double(repr, "y1", p1.y());
+ sp_repr_set_svg_double(repr, "x2", p2.x());
+ sp_repr_set_svg_double(repr, "y2", p2.y());
+
+ if (css) {
+ Glib::ustring css_str;
+ sp_repr_css_write_string(css, css_str);
+ sp_repr_css_attr_unref(css);
+ repr->setAttribute("style", css_str.c_str());
+ }
+
+ return repr;
+}
+
+Inkscape::XML::Node *API::make_path(Glib::ustring d, Geom::Point const &pos, SPCSSAttr *css) {
+ SPDocument *doc = desktop->getDocument();
+ Inkscape::XML::Document *xml_doc = doc->getReprDoc();
+
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
+
+ sp_repr_set_svg_double(repr, "x", pos.x());
+ sp_repr_set_svg_double(repr, "y", pos.y());
+ repr->setAttribute("d", d.c_str());
+
+ if (css) {
+ Glib::ustring css_str;
+ sp_repr_css_write_string(css, css_str);
+ sp_repr_css_attr_unref(css);
+ repr->setAttribute("style", css_str.c_str());
+ }
+
+ return repr;
+}
+
+Inkscape::XML::Node *API::make_arrow(Geom::Point const &from, Geom::Point const &to, SPCSSAttr *css) {
+ SPDocument *doc = desktop->getDocument();
+ Inkscape::XML::Document *xml_doc = doc->getReprDoc();
+
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
+
+ Geom::Point back = from - to;
+ back.normalize();
+ Geom::Point const cross_a = back.cw() + back * 2.0;
+ Geom::Point const cross_b = back.cw() + back * 2.0;
+
+ gchar* d = g_strdup_printf("M %f,%f %f,%f l %f,%f M %f,%f l %f,%f",
+ from.x(), from.y(),
+ to.x(), to.y(),
+ cross_a.x(), cross_a.y(),
+ to.x(), to.y(),
+ cross_b.x(), cross_b.y());
+ repr->setAttribute("d", d);
+ g_free(d);
+
+ Glib::ustring css_str;
+ if (css) {
+ sp_repr_css_write_string(css, css_str);
+ sp_repr_css_attr_unref(css);
+ } else {
+ css_str = "stroke: #000000;";
+ }
+ repr->setAttribute("style", css_str.c_str());
+
+ return repr;
+}
+
+namespace {
+SPCSSAttr *handle_style() {
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "fill", "#ffffff");
+ sp_repr_css_set_property(css, "stroke-width", "0.2");
+ sp_repr_css_set_property(css, "stroke", "#000000");
+
+ return css;
+}
+}
+
+bool API::input_point(Glib::ustring const &id, Geom::Point *point) {
+ auto const half_handle_size = Geom::Point(2, 2);
+ auto ui_point = *point * desktop->w2d().inverse();
+ Geom::Rect const rect(ui_point - half_handle_size, ui_point + half_handle_size);
+
+ SPCSSAttr *css = handle_style();
+ guint32 color = 0xffaaaaff;
+
+ if (rect.interiorContains(_mouse.ui_pos())) {
+ hot = id;
+ sp_repr_css_set_property(css, "fill", "#ff0000");
+ }
+
+ if (hot == id && _mouse.pressed()) {
+ active = id;
+ }
+
+ bool change = false;
+
+ if (active == id) {
+ sp_repr_css_set_property(css, "fill", "#0000ff");
+
+ auto const delta = _mouse.delta();
+ change = !delta.isZero();
+ *point += delta;
+ if (_mouse.released()) {
+ active = "";
+ }
+ }
+
+ Inkscape::XML::Node *repr = make_rect(rect, css);
+ draw_ui(id, repr);
+ return change;
+}
+
+bool API::input_line(Glib::ustring const &id, Geom::Point *p1, Geom::Point *p2) {
+ bool change = false;
+ if (input_point(id + "_p1", p1)) {
+ change = true;
+ }
+ if (input_point(id + "_p2", p2)) {
+ change = true;
+ }
+ draw_doc("", make_line(*p1, *p2));
+ return change;
+}
+
+bool API::input_arrow(Glib::ustring const &id, Geom::Point *from, Geom::Point *to) {
+ bool change = false;
+ if (input_point(id + "_from", from)) {
+ change = true;
+ }
+ if (input_point(id + "_to", to)) {
+ change = true;
+ }
+ draw_doc("", make_line(*from, *to));
+ return change;
+}
+
+bool API::input_rect(Glib::ustring const &id, Geom::Rect *rect) {
+ Geom::Point min = rect->min();
+ Geom::Point max = rect->max();
+
+ bool change = false;
+ if (input_point(id + "_min", &min)) {
+ Geom::Point const delta = min - rect->min();
+ *rect += delta;
+ change = true;
+ }
+ if (input_point(id + "_max", &max)) {
+ rect->setMax(max);
+ change = true;
+ }
+
+ draw_doc("", make_rect(*rect));
+ return change;
+}
+
+Mouse const &API::mouse() {
+ return _mouse;
+}
+
+void API::push_event(GdkEvent *event) {
+ _mouse.push_event(event);
+}
+
+void API::setup_frame() {
+ hot = "";
+
+ if (doc_root) {
+ doc_root->deleteObject(true, true);
+ doc_root = nullptr;
+ }
+
+ if (ui_root) {
+ ui_root->deleteObject(true, true);
+ ui_root = nullptr;
+ }
+
+ Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
+ Inkscape::XML::Node *rdoc = xml_doc->createElement("svg:g");
+ Inkscape::XML::Node *rui = xml_doc->createElement("svg:g");
+
+ doc_root = SP_ITEM(desktop->currentLayer()->appendChildRepr(rdoc));
+ doc_root->updateRepr();
+ Inkscape::GC::release(rdoc);
+
+ ui_root = SP_ITEM(desktop->currentLayer()->appendChildRepr(rui));
+ ui_root->doWriteTransform(ui2doc(), nullptr, true);
+ Inkscape::GC::release(rui);
+}
+
+void API::finish_frame() {
+ doc_root->updateRepr();
+ ui_root->updateRepr();
+ _mouse.finish_frame();
+}
+
+void API::draw_doc(Glib::ustring const &id, Inkscape::XML::Node *repr) {
+ repr->setAttribute("inkscape:livecode-id", id);
+ doc_root->appendChildRepr(repr);
+ Inkscape::GC::release(repr);
+}
+void API::draw_ui(Glib::ustring const &id, Inkscape::XML::Node *repr) {
+ repr->setAttribute("inkscape:livecode-id", id);
+ ui_root->appendChildRepr(repr);
+ Inkscape::GC::release(repr);
+}
+
+}
+}
+
+/*
+ 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 :