summaryrefslogtreecommitdiffstats
path: root/src/livecode/context.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/livecode/context.cpp')
-rw-r--r--src/livecode/context.cpp327
1 files changed, 327 insertions, 0 deletions
diff --git a/src/livecode/context.cpp b/src/livecode/context.cpp
new file mode 100644
index 000000000..a4b59e9b0
--- /dev/null
+++ b/src/livecode/context.cpp
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Context 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 <giomm/file.h>
+
+#include "svg/stringstream.h"
+#include "svg/svg-color.h"
+#include "svg/svg.h"
+#include "document.h"
+#include "display/sp-canvas-item.h"
+
+#include "livecode/context.h"
+#include "livecode/api/api.h"
+
+namespace Inkscape {
+namespace Livecode {
+
+Context::Context(SPDesktop *desktop)
+ : desktop(desktop)
+ , doc_root(nullptr)
+ , ui_root(nullptr)
+ , _mouse(*this)
+ , script_function(nullptr)
+{
+ janet_init();
+
+ env = janet_core_env(NULL);
+ janet_lib_context(env, *this);
+ janet_lib_input(env);
+ janet_lib_geom_point(env);
+}
+
+Context::~Context() {
+ janet_deinit();
+
+ 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 *Context::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 *Context::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 *Context::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 *Context::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 Context::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 Context::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 Context::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 Context::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 &Context::mouse() {
+ return _mouse;
+}
+
+void Context::push_event(GdkEvent *event) {
+ _mouse.push_event(event);
+}
+
+void Context::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);
+
+ if (script_file) {
+ try {
+ char *data;
+ gsize length;
+ script_file->load_contents(data, length);
+
+ Janet result;
+ janet_dobytes(env, (uint8_t *)data, length, script_file->get_path().c_str(), &result);
+ if (janet_checktype(result, JANET_FUNCTION)) {
+ script_function = janet_unwrap_function(result);
+ }
+ } catch (...) {
+ g_message("error loading file");
+ }
+ }
+
+ if (script_function) {
+ Janet result;
+ janet_pcall(script_function, 0, NULL, &result, NULL);
+ }
+}
+
+void Context::finish_frame() {
+ doc_root->updateRepr();
+ ui_root->updateRepr();
+ _mouse.finish_frame();
+}
+
+void Context::load_script(Glib::ustring const &path) {
+ g_message("loading file %s", path.c_str());
+ script_file = Gio::File::create_for_path(path);
+}
+
+void Context::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 Context::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 :