diff options
Diffstat (limited to 'src/livecode/context.cpp')
| -rw-r--r-- | src/livecode/context.cpp | 327 |
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 : |
