diff --git a/src/ant/ant/RulerConfigPage4.ui b/src/ant/ant/RulerConfigPage4.ui
index ac380684e..649d18d44 100644
--- a/src/ant/ant/RulerConfigPage4.ui
+++ b/src/ant/ant/RulerConfigPage4.ui
@@ -89,7 +89,7 @@
...
-
+
:/up_16px.png:/up_16px.png
@@ -103,7 +103,7 @@
...
-
+
:/add_16px.png:/add_16px.png
@@ -117,7 +117,7 @@
...
-
+
:/del_16px.png:/del_16px.png
@@ -131,7 +131,7 @@
...
-
+
:/down_16px.png:/down_16px.png
@@ -833,6 +833,16 @@
Auto measure (points will be set automatically)
+ -
+
+ Angle measurement (three mouse clicks)
+
+
+ -
+
+ Multi-segment (finish with double click)
+
+
-
@@ -866,7 +876,7 @@
t_snap_cbx
-
+
diff --git a/src/ant/ant/antConfig.cc b/src/ant/ant/antConfig.cc
index 78356a41b..250945a98 100644
--- a/src/ant/ant/antConfig.cc
+++ b/src/ant/ant/antConfig.cc
@@ -239,6 +239,10 @@ RulerModeConverter::to_string (ant::Template::ruler_mode_type m)
return "single_click";
} else if (m == ant::Template::RulerAutoMetric) {
return "auto_metric";
+ } else if (m == ant::Template::RulerMultiSegment) {
+ return "multi_segment";
+ } else if (m == ant::Template::RulerAngle) {
+ return "angle";
} else {
return "normal";
}
@@ -254,6 +258,10 @@ RulerModeConverter::from_string (const std::string &s, ant::Template::ruler_mode
a = ant::Template::RulerSingleClick;
} else if (t == "auto_metric") {
a = ant::Template::RulerAutoMetric;
+ } else if (t == "multi_segment") {
+ a = ant::Template::RulerMultiSegment;
+ } else if (t == "angle") {
+ a = ant::Template::RulerAngle;
} else {
a = ant::Template::RulerNormal;
}
diff --git a/src/ant/ant/antObject.h b/src/ant/ant/antObject.h
index 7304c5bed..60752449d 100644
--- a/src/ant/ant/antObject.h
+++ b/src/ant/ant/antObject.h
@@ -292,6 +292,14 @@ public:
*/
void set_points (const point_list &points);
+ /**
+ * @brief Sets the ruler's definition points without cleaning
+ */
+ void set_points_exact (const point_list &points)
+ {
+ m_points = points;
+ }
+
/**
* @brief Gets the first point of the indicated segment
*/
diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc
index fda6d5b8a..74918b24c 100644
--- a/src/ant/ant/antPlugin.cc
+++ b/src/ant/ant/antPlugin.cc
@@ -68,12 +68,18 @@ static std::vector make_standard_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 ("Multi-ruler")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_multi_ruler"));
+ templates.back ().set_mode (ant::Template::RulerMultiSegment);
+
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 ("Angle")), "$X", "$Y", "$D", ant::Object::STY_line, ant::Object::OL_diag, true, lay::AC_Global, "_angle"));
+ templates.back ().set_mode (ant::Template::RulerAngle);
+
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 ()));
@@ -197,14 +203,50 @@ PluginDeclaration::initialized (lay::Dispatcher *root)
// Check if we already have templates (initial setup)
// NOTE: this is not done by using a default value for the configuration item but dynamically.
// This provides a migration path from earlier versions (not having templates) to recent ones.
- bool any_templates = false;
- for (std::vector::iterator i = m_templates.begin (); ! any_templates && i != m_templates.end (); ++i) {
- any_templates = ! i->category ().empty ();
+ std::map cat_names;
+ for (auto i = m_templates.begin (); i != m_templates.end (); ++i) {
+ if (! i->category ().empty ()) {
+ cat_names.insert (std::make_pair (i->category (), i.operator-> ()));
+ }
}
- if (! any_templates) {
+ bool any_missing = false;
+ auto std_templates = make_standard_templates ();
+ for (auto t = std_templates.begin (); ! any_missing && t != std_templates.end (); ++t) {
+ if (! t->category ().empty () && cat_names.find (t->category ()) == cat_names.end ()) {
+ any_missing = true;
+ }
+ }
+
+ if (cat_names.empty ()) {
+
+ // full initial configuration
root->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (make_standard_templates ()));
root->config_end ();
+
+ } else if (any_missing) {
+
+ // some standard templates are missing - add them now (migration path for later versions)
+ decltype (m_templates) new_templates;
+ for (auto t = std_templates.begin (); t != std_templates.end (); ++t) {
+ if (! t->category ().empty ()) {
+ auto tt = cat_names.find (t->category ());
+ if (tt != cat_names.end ()) {
+ new_templates.push_back (*tt->second);
+ } else {
+ new_templates.push_back (*t);
+ }
+ }
+ }
+ for (auto i = m_templates.begin (); i != m_templates.end (); ++i) {
+ if (i->category ().empty ()) {
+ new_templates.push_back (*i);
+ }
+ }
+
+ root->config_set (cfg_ruler_templates, ant::TemplatesConverter ().to_string (new_templates));
+ root->config_end ();
+
}
}
diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc
index ffd1e22cb..fb76bc076 100644
--- a/src/ant/ant/antService.cc
+++ b/src/ant/ant/antService.cc
@@ -1560,11 +1560,52 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio
return mouse_click_event (p, buttons, prio);
}
-bool
+void
+Service::finish_drawing ()
+{
+ // create the ruler object
+
+ // begin the transaction
+ if (manager ()) {
+ tl_assert (! manager ()->transacting ());
+ manager ()->transaction (tl::to_string (tr ("Create ruler")));
+ }
+
+ show_message ();
+
+ insert_ruler (ant::Object (m_current.points (), 0, current_template ()), true);
+
+ // stop dragging
+ drag_cancel ();
+ clear_transient_selection ();
+
+ // end the transaction
+ if (manager ()) {
+ manager ()->commit ();
+ }
+}
+
+bool
+Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio)
+{
+ if (m_drawing && prio && (buttons & lay::LeftButton) != 0) {
+
+ // ends the current ruler (specifically in multi-segment mode)
+ finish_drawing ();
+ return true;
+
+ }
+
+ return false;
+}
+
+bool
Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
if (prio && (buttons & lay::LeftButton) != 0) {
+ const ant::Template &tpl = current_template ();
+
if (! m_drawing) {
// cancel any edit operations so far
@@ -1577,8 +1618,6 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
// and clear surplus rulers
reduce_rulers (m_max_number_of_rulers - 1);
- const ant::Template &tpl = current_template ();
-
// create and start dragging the ruler
if (tpl.mode () == ant::Template::RulerSingleClick) {
@@ -1648,7 +1687,13 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
m_p1 = snap1 (p, m_obj_snap && tpl.snap ()).second;
- m_current = ant::Object (m_p1, m_p1, 0, tpl);
+ // NOTE: generating the ruler this way makes sure we have two points
+ ant::Object::point_list pts;
+ m_current = ant::Object (pts, 0, tpl);
+ pts.push_back (m_p1);
+ pts.push_back (m_p1);
+ m_current.set_points_exact (pts);
+
show_message ();
if (mp_active_ruler) {
@@ -1662,29 +1707,29 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
}
+ } else if (tpl.mode () == ant::Template::RulerMultiSegment || tpl.mode () == ant::Template::RulerAngle) {
+
+ ant::Object::point_list pts = m_current.points ();
+ tl_assert (! pts.empty ());
+
+ if (tpl.mode () == ant::Template::RulerAngle && pts.size () == 3) {
+
+ finish_drawing ();
+
+ } else {
+
+ // add a new point
+ m_p1 = pts.back ();
+
+ pts.push_back (m_p1);
+ m_current.set_points_exact (pts);
+
+ }
+
} else {
- // create the ruler object
+ finish_drawing ();
- // begin the transaction
- if (manager ()) {
- tl_assert (! manager ()->transacting ());
- manager ()->transaction (tl::to_string (tr ("Create ruler")));
- }
-
- show_message ();
-
- insert_ruler (ant::Object (m_current.p1 (), m_current.p2 (), 0, current_template ()), true);
-
- // stop dragging
- drag_cancel ();
- clear_transient_selection ();
-
- // end the transaction
- if (manager ()) {
- manager ()->commit ();
- }
-
}
return true;
@@ -1731,7 +1776,14 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
set_cursor (lay::Cursor::cross);
- m_current.p2 (snap2 (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)).second);
+ // NOTE: we use the direct access path so we do not encounter cleanup by the p1 and p2 setters
+ // otherwise we risk manipulating p1 too.
+ ant::Object::point_list pts = m_current.points ();
+ if (! pts.empty ()) {
+ pts.back () = snap2 (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)).second;
+ }
+ m_current.set_points_exact (pts);
+
mp_active_ruler->redraw ();
show_message ();
diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h
index 6b4c5be9d..5d55af179 100644
--- a/src/ant/ant/antService.h
+++ b/src/ant/ant/antService.h
@@ -566,6 +566,7 @@ private:
virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
+ virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual void deactivated ();
/**
@@ -585,6 +586,11 @@ private:
*/
void reduce_rulers (int num);
+ /**
+ * @brief Finishes drawing mode and creates the ruler
+ */
+ void finish_drawing ();
+
/**
* @brief Delete the selected rulers
*
diff --git a/src/ant/ant/antTemplate.h b/src/ant/ant/antTemplate.h
index 0d8b921f5..6b1bbbf8b 100644
--- a/src/ant/ant/antTemplate.h
+++ b/src/ant/ant/antTemplate.h
@@ -62,7 +62,17 @@ public:
/**
* @brief The ruler is auto-metric: a single click will place a ruler and the ruler will extend to the next adjacent structures
*/
- RulerAutoMetric = 2
+ RulerAutoMetric = 2,
+
+ /**
+ * @brief The ruler an angle type (two segments, three mouse clicks) for angle and circle radius measurements
+ */
+ RulerAngle = 3,
+
+ /**
+ * @brief The ruler is a multi-segment type
+ */
+ RulerMultiSegment = 4
};
/**
diff --git a/src/ant/ant/gsiDeclAnt.cc b/src/ant/ant/gsiDeclAnt.cc
index 949cb5c09..24b3d47ca 100644
--- a/src/ant/ant/gsiDeclAnt.cc
+++ b/src/ant/ant/gsiDeclAnt.cc
@@ -432,6 +432,16 @@ static int ruler_mode_auto_metric ()
return ant::Template::RulerAutoMetric;
}
+static int ruler_mode_angle ()
+{
+ return ant::Template::RulerAngle;
+}
+
+static int ruler_mode_multi_segment ()
+{
+ return ant::Template::RulerMultiSegment;
+}
+
static void register_annotation_template (const ant::Object &a, const std::string &title, int mode)
{
ant::Template t = ant::Template::from_object (a, title, mode);
@@ -503,16 +513,28 @@ gsi::Class decl_Annotation (decl_BasicAnnotation, "lay", "Annotat
) +
gsi::method ("RulerModeSingleClick", &gsi::ruler_mode_single_click,
"@brief Specifies single-click ruler mode for the \\register_template method\n"
- "In single click-mode, a ruler can be placed with a single click and p1 will be == p2."
+ "In single click-mode, a ruler can be placed with a single click and p1 will be == p2.\n"
"\n"
"This constant has been introduced in version 0.25"
) +
gsi::method ("RulerModeAutoMetric", &gsi::ruler_mode_auto_metric,
"@brief Specifies auto-metric ruler mode for the \\register_template method\n"
- "In auto-metric mode, a ruler can be placed with a single click and p1/p2 will be determined from the neighborhood."
+ "In auto-metric mode, a ruler can be placed with a single click and p1/p2 will be determined from the neighborhood.\n"
"\n"
"This constant has been introduced in version 0.25"
) +
+ gsi::method ("RulerAngle", &gsi::ruler_mode_angle,
+ "@brief Specifies angle ruler mode for the \\register_template method\n"
+ "In angle ruler mode, two segments are created for angle and circle radius measurements. Three mouse clicks are required.\n"
+ "\n"
+ "This constant has been introduced in version 0.28"
+ ) +
+ gsi::method ("RulerMultiSegment", &gsi::ruler_mode_multi_segment,
+ "@brief Specifies multi-segment mode\n"
+ "In multi-segment mode, multiple segments can be created. The ruler is finished with a double click.\n"
+ "\n"
+ "This constant has been introduced in version 0.28"
+ ) +
gsi::method ("StyleRuler|#style_ruler", &gsi::style_ruler,
"@brief Gets the ruler style code for use the \\style method\n"
"When this style is specified, the annotation will show a ruler with "