diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc index c81176af9..488809029 100644 --- a/src/ant/ant/antPlugin.cc +++ b/src/ant/ant/antPlugin.cc @@ -62,6 +62,25 @@ PluginDeclaration::instance () return sp_instance; } +static std::vector make_standard_templates () +{ + std::vector templates; + + templates.push_back (ant::Template (tl::to_string (tr ("Ruler")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_ruler")); + + templates.push_back (ant::Template (tl::to_string (tr ("Cross")), "", "", "$U,$V", ant::Object::STY_cross_both, ant::Object::OL_diag, true, lay::AC_Global, "_cross")); + templates.back ().set_mode (ant::Template::RulerSingleClick); + + templates.push_back (ant::Template (tl::to_string (tr ("Measure")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure")); + templates.back ().set_mode (ant::Template::RulerAutoMetric); + + templates.push_back (ant::Template (tl::to_string (tr ("Ellipse")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_ellipse, true, lay::AC_Global, std::string ())); + + templates.push_back (ant::Template (tl::to_string (tr ("Box")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_box, true, lay::AC_Global, std::string ())); + + return templates; +} + void PluginDeclaration::get_options (std::vector < std::pair > &options) const { @@ -72,7 +91,7 @@ PluginDeclaration::get_options (std::vector < std::pair (cfg_ruler_snap_mode, ACConverter ().to_string (lay::AC_Any))); options.push_back (std::pair (cfg_ruler_obj_snap, tl::to_string (true))); options.push_back (std::pair (cfg_ruler_grid_snap, tl::to_string (false))); - options.push_back (std::pair (cfg_ruler_templates, "")); + options.push_back (std::pair (cfg_ruler_templates, ant::TemplatesConverter ().to_string (make_standard_templates ()))); options.push_back (std::pair (cfg_current_ruler_template, "0")); // grid-micron is not configured here since some other entity is supposed to do this. } @@ -189,21 +208,7 @@ PluginDeclaration::initialized (lay::Dispatcher *root) // This is the migration path from <= 0.24 to 0.25: clear all templates unless we // have categorized ones there. Those can't be deleted, so we know we have a 0.25 // setup if there are some - m_templates.clear (); - - // Set up the templates we want to see (plus some non-categorized templates) - - m_templates.push_back (ant::Template (tl::to_string (tr ("Ruler")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_ruler")); - - m_templates.push_back (ant::Template (tl::to_string (tr ("Cross")), "", "", "$U,$V", ant::Object::STY_cross_both, ant::Object::OL_diag, true, lay::AC_Global, "_cross")); - m_templates.back ().set_mode (ant::Template::RulerSingleClick); - - m_templates.push_back (ant::Template (tl::to_string (tr ("Measure")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure")); - m_templates.back ().set_mode (ant::Template::RulerAutoMetric); - - m_templates.push_back (ant::Template (tl::to_string (tr ("Ellipse")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_ellipse, true, lay::AC_Global, std::string ())); - - m_templates.push_back (ant::Template (tl::to_string (tr ("Box")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_box, true, lay::AC_Global, std::string ())); + m_templates = make_standard_templates (); root->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (m_templates)); root->config_end (); @@ -292,7 +297,7 @@ PluginDeclaration::update_menu () } void -PluginDeclaration::register_annotation_template (const ant::Template &t) +PluginDeclaration::register_annotation_template (const ant::Template &t, lay::Plugin *plugin) { if (t.category ().empty ()) { return; @@ -305,8 +310,38 @@ PluginDeclaration::register_annotation_template (const ant::Template &t) } m_templates.push_back (t); - lay::Dispatcher::instance ()->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (m_templates)); - lay::Dispatcher::instance ()->config_end (); + + if (! plugin) { + plugin = lay::Dispatcher::instance (); + } + if (plugin) { + plugin->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (m_templates)); + plugin->config_end (); + } +} + +void +PluginDeclaration::unregister_annotation_template (const std::string &category, lay::Plugin *plugin) +{ + std::vector tpl; + + if (! category.empty ()) { + for (auto i = m_templates.begin (); i != m_templates.end (); ++i) { + if (i->category () != category) { + tpl.push_back (*i); + } + } + } + + m_templates.swap (tpl); + + if (! plugin) { + plugin = lay::Dispatcher::instance (); + } + if (plugin) { + plugin->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (m_templates)); + plugin->config_end (); + } } static tl::RegisteredClass config_decl (new ant::PluginDeclaration (), 3000, "ant::Plugin"); diff --git a/src/ant/ant/antPlugin.h b/src/ant/ant/antPlugin.h index 37e90870e..5cc70f3ae 100644 --- a/src/ant/ant/antPlugin.h +++ b/src/ant/ant/antPlugin.h @@ -54,7 +54,8 @@ public: virtual void uninitialize (lay::Dispatcher *); virtual bool menu_activated (const std::string &symbol) const; - void register_annotation_template (const ant::Template &t); + void register_annotation_template (const ant::Template &t, lay::Plugin *plugin = 0); + void unregister_annotation_template (const std::string &category, lay::Plugin *plugin = 0); static PluginDeclaration *instance (); diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index 14be7ffc9..6b4c5be9d 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -481,6 +481,14 @@ public: */ ant::Object create_measure_ruler(const db::DPoint &pt, lay::angle_constraint_type ac); + /** + * @brief Gets the annotation templates + */ + const std::vector &ruler_templates () const + { + return m_ruler_templates; + } + /** * @brief An event triggered when the annotations changed * When an annotation is added or removed, this event is triggered. diff --git a/src/ant/ant/antTemplate.cc b/src/ant/ant/antTemplate.cc index 390a4b8a9..84590a2e2 100644 --- a/src/ant/ant/antTemplate.cc +++ b/src/ant/ant/antTemplate.cc @@ -30,6 +30,32 @@ namespace ant { +ant::Template +Template::from_object (const ant::Object &a, const std::string &title, int mode) +{ + ant::Template t; + + t.angle_constraint (a.angle_constraint ()); + t.category (a.category ()); + t.fmt (a.fmt ()); + t.fmt_x (a.fmt_x ()); + t.fmt_y (a.fmt_y ()); + t.set_main_position (a.main_position ()); + t.set_main_xalign (a.main_xalign ()); + t.set_main_yalign (a.main_yalign ()); + t.set_xlabel_xalign (a.xlabel_xalign ()); + t.set_xlabel_yalign (a.xlabel_yalign ()); + t.set_ylabel_xalign (a.ylabel_xalign ()); + t.set_ylabel_yalign (a.ylabel_yalign ()); + t.outline (a.outline ()); + t.style (a.style ()); + t.title (title); + + t.set_mode (ant::Template::ruler_mode_type (mode)); + + return t; +} + Template::Template () : m_title (tl::to_string (tr ("Ruler"))), m_fmt_x ("$X"), m_fmt_y ("$Y"), m_fmt ("$D"), diff --git a/src/ant/ant/antTemplate.h b/src/ant/ant/antTemplate.h index fbc326680..0d8b921f5 100644 --- a/src/ant/ant/antTemplate.h +++ b/src/ant/ant/antTemplate.h @@ -65,6 +65,14 @@ public: RulerAutoMetric = 2 }; + /** + * @brief Creates a template from a ruler object + * + * This will ignore the positions of the ruler but use the properties to + * initialize the template. + */ + static ant::Template from_object (const ant::Object &object, const std::string &title, int mode); + /** * @brief Default constructor * diff --git a/src/ant/ant/gsiDeclAnt.cc b/src/ant/ant/gsiDeclAnt.cc index 8889af167..a1284d3ff 100644 --- a/src/ant/ant/gsiDeclAnt.cc +++ b/src/ant/ant/gsiDeclAnt.cc @@ -427,47 +427,68 @@ static int ruler_mode_auto_metric () static void register_annotation_template (const ant::Object &a, const std::string &title, int mode) { - ant::Template t; - - t.angle_constraint (a.angle_constraint ()); - t.category (a.category ()); - t.fmt (a.fmt ()); - t.fmt_x (a.fmt_x ()); - t.fmt_y (a.fmt_y ()); - t.set_main_position (a.main_position ()); - t.set_main_xalign (a.main_xalign ()); - t.set_main_yalign (a.main_yalign ()); - t.set_xlabel_xalign (a.xlabel_xalign ()); - t.set_xlabel_yalign (a.xlabel_yalign ()); - t.set_ylabel_xalign (a.ylabel_xalign ()); - t.set_ylabel_yalign (a.ylabel_yalign ()); - t.outline (a.outline ()); - t.style (a.style ()); - t.title (title); - - t.set_mode (ant::Template::ruler_mode_type (mode)); + ant::Template t = ant::Template::from_object (a, title, mode); if (ant::PluginDeclaration::instance ()) { ant::PluginDeclaration::instance ()->register_annotation_template (t); } } +static void register_annotation_template2 (lay::LayoutViewBase *view, const ant::Object &a, const std::string &title, int mode) +{ + ant::Template t = ant::Template::from_object (a, title, mode); + + if (ant::PluginDeclaration::instance ()) { + ant::PluginDeclaration::instance ()->register_annotation_template (t, view); + } +} + +static void unregister_annotation_template (const std::string &category) +{ + if (ant::PluginDeclaration::instance ()) { + ant::PluginDeclaration::instance ()->unregister_annotation_template (category); + } +} + +static void unregister_annotation_template2 (lay::LayoutViewBase *view, const std::string &category) +{ + if (ant::PluginDeclaration::instance ()) { + ant::PluginDeclaration::instance ()->unregister_annotation_template (category, view); + } +} + // NOTE: ant::Object is available as "BasicAnnotation" to allow binding for other methods. gsi::Class decl_BasicAnnotation ("lay", "BasicAnnotation", gsi::Methods (), "@hide\n@alias Annotation"); gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotation", gsi::method ("register_template", &gsi::register_annotation_template, gsi::arg ("annotation"), gsi::arg ("title"), gsi::arg ("mode", ruler_mode_normal (), "\\RulerModeNormal"), - "@brief Registers the given annotation as a template\n" + "@brief Registers the given annotation as a template globally\n" + "@annotation The annotation to use for the template (positions are ignored)\n" "@param title The title to use for the ruler template\n" "@param mode The mode the ruler will be created in (see Ruler... constants)\n" "\n" - "In order to register a system template, the category string of the annotation should be " + "In order to register a system template, the category string of the annotation has to be " "a unique and non-empty string. The annotation is added to the list of annotation templates " "and becomes available as a new template in the ruler drop-down menu.\n" "\n" + "The new annotation template is registered on all views.\n" + "\n" + "NOTE: this setting is persisted and the the application configuration is updated.\n" + "\n" "This method has been added in version 0.25." ) + + gsi::method ("unregister_templates", &gsi::unregister_annotation_template, + gsi::arg ("category"), + "@brief Unregisters the template or templates with the given category string globally\n" + "\n" + "This method will remove all templates with the given category string. If the category string is empty, " + "all templates are removed.\n" + "\n" + "NOTE: this setting is persisted and the the application configuration is updated.\n" + "\n" + "This method has been added in version 0.28." + ) + gsi::method ("RulerModeNormal", &gsi::ruler_mode_normal, "@brief Specifies normal ruler mode for the \\register_template method\n" "\n" @@ -988,6 +1009,25 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat "@/code\n" ); +static std::vector > get_annotation_templates (lay::LayoutViewBase *view) +{ + ant::Service *ant_service = view->get_plugin (); + tl_assert (ant_service != 0); + + std::vector > ant_objects; + const std::vector &ruler_templates = ant_service->ruler_templates (); + + ant_objects.reserve (ruler_templates.size ()); + for (auto i = ruler_templates.begin (); i != ruler_templates.end (); ++i) { + ant_objects.push_back (std::vector ()); + ant_objects.back ().push_back (tl::Variant (gsi::AnnotationRef (ant::Object (db::DPoint (), db::DPoint (), 0, *i), 0))); + ant_objects.back ().push_back (tl::Variant (i->title ())); + ant_objects.back ().push_back (tl::Variant (int (i->mode ()))); + } + + return ant_objects; +} + static gsi::ClassExt layout_view_decl ( gsi::method_ext ("clear_annotations", &gsi::clear_annotations, @@ -1063,6 +1103,42 @@ gsi::ClassExt layout_view_decl ( "@return The new ruler object\n" "\n" "This method was introduced in version 0.26." + ) + + gsi::method_ext ("register_annotation_template", &gsi::register_annotation_template2, + gsi::arg ("annotation"), gsi::arg ("title"), gsi::arg ("mode", ruler_mode_normal (), "\\RulerModeNormal"), + "@brief Registers the given annotation as a template for this particular view\n" + "@annotation The annotation to use for the template (positions are ignored)\n" + "@param title The title to use for the ruler template\n" + "@param mode The mode the ruler will be created in (see Ruler... constants)\n" + "\n" + "See \\Annotation#register_template for a method doing the same on application level. " + "This method is hardly useful normally, but can be used when customizing layout views as " + "individual widgets.\n" + "\n" + "This method has been added in version 0.28." + ) + + gsi::method_ext ("unregister_annotation_templates", &gsi::unregister_annotation_template2, + gsi::arg ("category"), + "@brief Unregisters the template or templates with the given category string on this particular view\n" + "\n" + "See \\Annotation#unregister_template for a method doing the same on application level." + "This method is hardly useful normally, but can be used when customizing layout views as " + "individual widgets.\n" + "\n" + "This method has been added in version 0.28." + ) + + gsi::method_ext ("annotation_templates", &get_annotation_templates, + "@brief Gets a list of \\Annotation objects representing the annotation templates.\n" + "\n" + "Annotation templates are the rulers available in the ruler drop-down (preset ruler types). " + "This method will fetch the templates available. This method returns triplets '(annotation, title, mode)'. " + "The first member of the triplet is the annotation object representing the template. The second " + "member is the title string displayed in the menu for this templates. The third member is the mode " + "value (one of the RulerMode... constants - e.g \\RulerModeNormal).\n" + "\n" + "The positions of the returned annotation objects are undefined.\n" + "\n" + "This method has been introduced in version 0.28." ), "" ); diff --git a/src/laybasic/laybasic/layPlugin.h b/src/laybasic/laybasic/layPlugin.h index f6bd14a15..5ea38a6e2 100644 --- a/src/laybasic/laybasic/layPlugin.h +++ b/src/laybasic/laybasic/layPlugin.h @@ -467,13 +467,27 @@ private: /** * @brief The plugin interface * - * Each configurable object must be derived from this interface. - * An configurable object can have a parent. This way, a hierarchy - * of configurable objects is created. The root object not having a - * parent acts as the main entry point: it will try to dispatch + * This is a basic interface providing several services in a + * hierarchically organized fashion. It also provides a configuration + * space (key/value pairs). + * + * Each object participating in the plugin scheme must be derived from this interface. + * An plugin can have a parent. This way, a hierarchy of plugin objects is created. + * The root object not having a parent acts as the main entry point: it will try to dispatch * configuration requests to the children. - * A node may have a local configuration - it will override any - * parent configurations. + * + * Each node has a local configuration space which overrides the configuration changes + * made to be parent. + * + * Each plugin also has a static or global configuration space inside the + * "plugin declaration". Configuration changes made to top level nodes are + * reflected in the static space too. + * + * A "standalone" node is one without a parent, but which does not communicate + * with the static configuration space. + * + * The "Dispatcher" adds the concept of a (singleton) root plugin to this hierarchical + * configuration tree. */ class LAYBASIC_PUBLIC Plugin diff --git a/testdata/ruby/antTest.rb b/testdata/ruby/antTest.rb index 183ef5afc..ff88d4ae4 100644 --- a/testdata/ruby/antTest.rb +++ b/testdata/ruby/antTest.rb @@ -564,6 +564,42 @@ class Ant_TestClass < TestBase end + # annotation template registration + def test_5 + + lv = RBA::LayoutView::new + tpl = lv.annotation_templates + assert_equal(tpl.size > 0, true) # at least one default template + + lv.unregister_annotation_templates("") + + tpl = lv.annotation_templates + assert_equal(tpl.size, 0) + + a = RBA::Annotation::new + a.fmt = "U" + a.category = "42" + lv.register_annotation_template(a, "X", RBA::Annotation::RulerModeSingleClick) + + tpl = lv.annotation_templates + assert_equal(tpl.size, 1) + assert_equal(tpl[0].size, 3) + (a, title, mode) = tpl[0] + assert_equal(a.fmt, "U") + assert_equal(a.category, "42") + assert_equal(title, "X") + assert_equal(mode, RBA::Annotation::RulerModeSingleClick) + + lv.unregister_annotation_templates("X") + tpl = lv.annotation_templates + assert_equal(tpl.size, 1) + + lv.unregister_annotation_templates("42") + tpl = lv.annotation_templates + assert_equal(tpl.size, 0) + + end + end load("test_epilogue.rb")