
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2017 Matthias Koefferlein

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/



#include "gsiDecl.h"
#include "gsiSignals.h"
#include "antObject.h"
#include "antService.h"
#include "layLayoutView.h"

namespace gsi
{

class AnnotationRef;

static int style_ruler ()       { return int (ant::Object::STY_ruler); }
static int style_arrow_end ()   { return int (ant::Object::STY_arrow_end); }
static int style_arrow_start () { return int (ant::Object::STY_arrow_start); }
static int style_arrow_both ()  { return int (ant::Object::STY_arrow_both); }
static int style_line ()        { return int (ant::Object::STY_line); }

static int outline_diag ()      { return int (ant::Object::OL_diag); }
static int outline_xy ()        { return int (ant::Object::OL_xy); }
static int outline_diag_xy ()   { return int (ant::Object::OL_diag_xy); }
static int outline_yx ()        { return int (ant::Object::OL_yx); }
static int outline_diag_yx ()   { return int (ant::Object::OL_diag_yx); }
static int outline_box ()       { return int (ant::Object::OL_box); }

static int angle_any ()         { return int (lay::AC_Any); }
static int angle_diagonal ()    { return int (lay::AC_Diagonal); }
static int angle_ortho ()       { return int (lay::AC_Ortho); }
static int angle_horizontal ()  { return int (lay::AC_Horizontal); }
static int angle_vertical ()    { return int (lay::AC_Vertical); }
static int angle_global ()      { return int (lay::AC_Global); }

static void clear_annotations (lay::LayoutView *view);
static void insert_annotation (lay::LayoutView *view, AnnotationRef *obj);
static void erase_annotation (lay::LayoutView *view, int id);
static void replace_annotation (lay::LayoutView *view, int id, const AnnotationRef &obj);

/**
 *  @brief An extension of the ant::Object that provides "live" updates of the view
 */
class AnnotationRef
  : public ant::Object
{
public:
  AnnotationRef ()
    : ant::Object ()
  {
    //  .. nothing yet ..
  }

  AnnotationRef (const ant::Object &other, lay::LayoutView *view)
    : ant::Object (other), mp_view (view)
  {
    //  .. nothing yet ..
  }

  AnnotationRef (const AnnotationRef &other)
    : ant::Object (other), mp_view (other.mp_view)
  {
    //  .. nothing yet ..
  }

  AnnotationRef &operator= (const AnnotationRef &other)
  {
    //  NOTE: assignment changes the properties, not the reference
    if (this != &other) {
      ant::Object::operator= (other);
    }
    return *this;
  }

  bool operator== (const AnnotationRef &other) const
  {
    return ant::Object::operator== (other);
  }

  bool operator!= (const AnnotationRef &other) const
  {
    return ant::Object::operator!= (other);
  }

  void detach ()
  {
    mp_view.reset (0);
  }

  bool is_valid () const
  {
    return (mp_view && id () >= 0);
  }

  void erase ()
  {
    if (mp_view && id () >= 0) {
      erase_annotation (mp_view.get (), id ());
      detach ();
    }
  }

  template <class T>
  AnnotationRef transformed (const T &t) const
  {
    return AnnotationRef (ant::Object::transformed<T> (t), const_cast<lay::LayoutView *> (mp_view.get ()));
  }

  void set_view (lay::LayoutView *view)
  {
    mp_view.reset (view);
  }

protected:
  void property_changed ()
  {
    if (mp_view && id () >= 0) {
      replace_annotation (mp_view.get (), id (), *this);
    }
  }

private:
  tl::weak_ptr<lay::LayoutView> mp_view;
};

static void clear_annotations (lay::LayoutView *view)
{
  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  if (ant_service) {
    ant_service->clear_rulers ();
  }
}

static void insert_annotation (lay::LayoutView *view, AnnotationRef *obj)
{
  if (obj->is_valid ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("The object is already inserted into a view - detach the object first or create a different object.")));
  }

  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  if (ant_service) {
    int id = ant_service->insert_ruler (*obj, false /*do not observe the ruler count limit*/);
    obj->id (id);
    obj->set_view (view);
  }
}

static void erase_annotation (lay::LayoutView *view, int id)
{
  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  if (ant_service) {
    for (ant::AnnotationIterator a = ant_service->begin_annotations (); ! a.at_end (); ++a) {
      if (a->id () == id) {
        ant_service->delete_ruler (a.current ());
        break;
      }
    }
  }
}

static void replace_annotation (lay::LayoutView *view, int id, const AnnotationRef &obj)
{
  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  if (ant_service) {
    for (ant::AnnotationIterator a = ant_service->begin_annotations (); ! a.at_end (); ++a) {
      if (a->id () == id) {
        ant_service->change_ruler (a.current (), obj);
        break;
      }
    }
  }
}

static int get_style (const AnnotationRef *obj)
{
  return int (obj->style ());
}

static void set_style (AnnotationRef *obj, int style)
{
  obj->style (AnnotationRef::style_type (style));
}

static int get_outline (const AnnotationRef *obj)
{
  return int (obj->outline ());
}

static void set_outline (AnnotationRef *obj, int outline)
{
  obj->outline (AnnotationRef::outline_type (outline));
}

static int get_angle_constraint (const AnnotationRef *obj)
{
  return int (obj->angle_constraint ());
}

static void set_angle_constraint (AnnotationRef *obj, int angle_constraint)
{
  obj->angle_constraint (lay::angle_constraint_type (angle_constraint));
}

/**
 *  @brief An alternative iterator that returns "live" AnnotationRef objects
 */
struct AnnotationRefIterator
  : public ant::AnnotationIterator
{
public:
  typedef AnnotationRef reference;

  AnnotationRefIterator ()
    : ant::AnnotationIterator ()
  {
    //  .. nothing yet ..
  }

  AnnotationRefIterator (const ant::AnnotationIterator &iter, lay::LayoutView *view)
    : ant::AnnotationIterator (iter), mp_view (view)
  {
    //  .. nothing yet ..
  }

  reference operator* () const
  {
    return reference (ant::AnnotationIterator::operator* (), const_cast<lay::LayoutView * >(mp_view.get ()));
  }

private:
  tl::weak_ptr<lay::LayoutView> mp_view;
};

static AnnotationRefIterator begin_annotations (lay::LayoutView *view)
{
  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  if (ant_service) {
    return AnnotationRefIterator (ant_service->begin_annotations (), view);
  } else {
    return AnnotationRefIterator ();
  }
}

static AnnotationRef get_annotation (lay::LayoutView *view, int id)
{
  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  if (ant_service) {
    for (AnnotationRefIterator iter (ant_service->begin_annotations (), view); !iter.at_end(); ++iter) {
      if ((*iter).id () == id) {
        return *iter;
      }
    }
  }
  return AnnotationRef ();
}

static tl::Event &get_annotations_changed_event (lay::LayoutView *view)
{
  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  tl_assert (ant_service != 0);
  return ant_service->annotations_changed_event;
}

static tl::Event &get_annotation_selection_changed_event (lay::LayoutView *view)
{
  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  tl_assert (ant_service != 0);
  return ant_service->annotation_selection_changed_event;
}

static tl::event<int> &get_annotation_changed_event (lay::LayoutView *view)
{
  ant::Service *ant_service = view->get_plugin <ant::Service> ();
  tl_assert (ant_service != 0);
  return ant_service->annotation_changed_event;
}

//  NOTE: ant::Object is available as "BasicAnnotation" to allow binding for other methods.
gsi::Class<ant::Object> decl_BasicAnnotation ("BasicAnnotation", gsi::Methods (), "@hide");

gsi::Class<AnnotationRef> decl_Annotation (decl_BasicAnnotation, "Annotation",
  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 "
    "some ticks at distances indicating a decade of units and a suitable "
    "subdivision into minor ticks at intervals of 1, 2 or 5 units."
  ) +
  gsi::method ("StyleArrowEnd|#style_arrow_end", &gsi::style_arrow_end,
    "@brief Gets the end arrow style code for use the \\style method\n"
    "When this style is specified, an arrow is drawn pointing from the start to the end point."
  ) +
  gsi::method ("StyleArrowStart|#style_arrow_start", &gsi::style_arrow_start,
    "@brief Gets the start arrow style code for use the \\style method\n"
    "When this style is specified, an arrow is drawn pointing from the end to the start point."
  ) +
  gsi::method ("StyleArrowBoth|#style_arrow_both", &gsi::style_arrow_both,
    "@brief Gets the both arrow ends style code for use the \\style method\n"
    "When this style is specified, a two-headed arrow is drawn."
  ) +
  gsi::method ("StyleLine|#style_line", &gsi::style_line,
    "@brief Gets the line style code for use with the \\style method\n"
    "When this style is specified, plain line is drawn."
  ) +
  gsi::method ("OutlineDiag|#outline_diag", &gsi::outline_diag,
    "@brief Gets the diagonal output code for use with the \\outline method\n"
    "When this outline style is specified, a line connecting start and "
    "end points in the given style (ruler, arrow or plain line) is drawn."
  ) +
  gsi::method ("OutlineXY|#outline_xy", &gsi::outline_xy,
    "@brief Gets the xy outline code for use with the \\outline method\n"
    "When this outline style is specified, two lines are drawn: one horizontal from left "
    "to right and attached to the end of that a line from the bottom to the top. The lines "
    "are drawn in the specified style (see \\style method)."
  ) +
  gsi::method ("OutlineDiagXY|#outline_diag_xy", &gsi::outline_diag_xy,
    "@brief Gets the xy plus diagonal outline code for use with the \\outline method\n"
    "@brief outline_xy code used by the \\outline method\n"
    "When this outline style is specified, three lines are drawn: one horizontal from left "
    "to right and attached to the end of that a line from the bottom to the top. Another line "
    "is drawn connecting the start and end points directly. The lines "
    "are drawn in the specified style (see \\style method)."
  ) +
  gsi::method ("OutlineYX|#outline_yx", &gsi::outline_yx ,
    "@brief Gets the yx outline code for use with the \\outline method\n"
    "When this outline style is specified, two lines are drawn: one vertical from bottom "
    "to top and attached to the end of that a line from the left to the right. The lines "
    "are drawn in the specified style (see \\style method)."
  ) +
  gsi::method ("OutlineDiagYX|#outline_diag_yx", &gsi::outline_diag_yx ,
    "@brief Gets the yx plus diagonal outline code for use with the \\outline method\n"
    "When this outline style is specified, three lines are drawn: one vertical from bottom "
    "to top and attached to the end of that a line from the left to the right. Another line "
    "is drawn connecting the start and end points directly. The lines "
    "are drawn in the specified style (see \\style method)."
  ) +
  gsi::method ("OutlineBox|#outline_box", &gsi::outline_box,
    "@brief Gets the box outline code for use with the \\outline method\n"
    "When this outline style is specified, a box is drawn with the corners specified by the "
    "start and end point. All box edges are drawn in the style specified with the \\style "
    "attribute."
  ) +
  gsi::method ("AngleAny|#angle_any", &gsi::angle_any,
    "@brief Gets the any angle code for use with the \\angle_constraint method\n"
    "If this value is specified for the angle constraint, all angles will be allowed."
  ) +
  gsi::method ("AngleDiagonal|#angle_diagonal", &gsi::angle_diagonal,
    "@brief Gets the diagonal angle code for use with the \\angle_constraint method\n"
    "If this value is specified for the angle constraint, only multiples of 45 degree are allowed."
  ) +
  gsi::method ("AngleOrtho|#angle_ortho", &gsi::angle_ortho,
    "@brief Gets the ortho angle code for use with the \\angle_constraint method\n"
    "If this value is specified for the angle constraint, only multiples of 90 degree are allowed."
  ) +
  gsi::method ("AngleHorizontal|#angle_horizontal", &gsi::angle_horizontal,
    "@brief Gets the horizontal angle code for use with the \\angle_constraint method\n"
    "If this value is specified for the angle constraint, only horizontal rulers are allowed."
  ) +
  gsi::method ("AngleVertical|#angle_vertical", &gsi::angle_vertical,
    "@brief Gets the vertical angle code for use with the \\angle_constraint method\n"
    "If this value is specified for the angle constraint, only vertical rulers are allowed."
  ) +
  gsi::method ("AngleGlobal|#angle_global", &gsi::angle_global,
    "@brief Gets the global angle code for use with the \\angle_constraint method.\n"
    "This code will tell the ruler or marker to use the angle constraint defined globally."
  ) +
  gsi::method ("detach", &AnnotationRef::detach,
    "@brief Detaches the annotation object from the view\n"
    "If the annotation object was inserted into the view, property changes will be "
    "reflected in the view. To disable this feature, 'detach' can be called after which "
    "the annotation object becomes inactive and changes will no longer be reflected in the view.\n"
    "\n"
    "This method has been introduced in version 0.25."
  ) +
  gsi::method ("delete", &AnnotationRef::erase,
    "@brief Deletes this annotation from the view\n"
    "If the annotation is an \"active\" one, this method will remove it from the view. "
    "This object will become detached and can still be manipulated, but without having an "
    "effect on the view."
    "\n"
    "This method has been introduced in version 0.25."
  ) +
  gsi::method ("p1", (const db::DPoint & (AnnotationRef::*) () const) &AnnotationRef::p1,
    "@brief Gets the first point of the ruler or marker\n"
    "The points of the ruler or marker are always given in micron units in floating-point "
    "coordinates.\n"
    "@return The first point\n"
  ) +
  gsi::method ("p2", (const db::DPoint & (AnnotationRef::*) () const) &AnnotationRef::p2,
    "@brief Gets the second point of the ruler or marker\n"
    "The points of the ruler or marker are always given in micron units in floating-point "
    "coordinates.\n"
    "@return The second point\n"
  ) +
  gsi::method ("p1=", (void (AnnotationRef::*) (const db::DPoint &)) &AnnotationRef::p1,
    "@brief Sets the first point of the ruler or marker\n"
    "The points of the ruler or marker are always given in micron units in floating-point "
    "coordinates.\n"
    "@args point\n"
  ) +
  gsi::method ("p2=", (void (AnnotationRef::*) (const db::DPoint &)) &AnnotationRef::p2,
    "@brief Sets the second point of the ruler or marker\n"
    "The points of the ruler or marker are always given in micron units in floating-point "
    "coordinates.\n"
    "@args point\n"
  ) +
  gsi::method ("box", &AnnotationRef::box,
    "@brief Gets the bounding box of the object (not including text)\n"
    "@return The bounding box\n"
  ) +
  gsi::method ("transformed", &AnnotationRef::transformed<db::DTrans>,
    "@brief Transforms the ruler or marker with the given simple transformation\n"
    "@args t\n"
    "@param t The transformation to apply\n"
    "@return The transformed object\n"
  ) +
  gsi::method ("transformed|#transformed_cplx", &AnnotationRef::transformed<db::DCplxTrans>,
    "@brief Transforms the ruler or marker with the given complex transformation\n"
    "@args t\n"
    "@param t The magnifying transformation to apply\n"
    "@return The transformed object\n"
    "\n"
    "Starting with version 0.25, all overloads all available as 'transform'."
  ) +
  gsi::method ("transformed|#transformed_cplx", &AnnotationRef::transformed<db::ICplxTrans>,
    "@brief Transforms the ruler or marker with the given complex transformation\n"
    "@args t\n"
    "@param t The magnifying transformation to apply\n"
    "@return The transformed object (in this case an integer coordinate object)\n"
    "\n"
    "This method has been introduced in version 0.18.\n"
    "\n"
    "Starting with version 0.25, all overloads all available as 'transform'."
  ) +
  gsi::method ("fmt=", (void (AnnotationRef::*) (const std::string &)) &AnnotationRef::fmt,
    "@brief Sets the format used for the label\n"
    "@args format\n"
    "@param format The format string\n"
    "Format strings can contain placeholders for values and formulas for computing derived "
    "values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
    "more details."
  ) +
  gsi::method ("fmt", (const std::string & (AnnotationRef::*) () const) &AnnotationRef::fmt,
    "@brief Returns the format used for the label\n"
    "@return The format string\n"
    "Format strings can contain placeholders for values and formulas for computing derived "
    "values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
    "more details."
  ) +
  gsi::method ("fmt_x=", (void (AnnotationRef::*) (const std::string &)) &AnnotationRef::fmt_x,
    "@brief Sets the format used for the x-axis label\n"
    "@args format\n"
    "X-axis labels are only used for styles that have a horizontal component. "
    "@param format The format string\n"
    "Format strings can contain placeholders for values and formulas for computing derived "
    "values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
    "more details."
  ) +
  gsi::method ("fmt_x", (const std::string & (AnnotationRef::*) () const) &AnnotationRef::fmt_x,
    "@brief Returns the format used for the x-axis label\n"
    "@return The format string\n"
    "Format strings can contain placeholders for values and formulas for computing derived "
    "values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
    "more details."
  ) +
  gsi::method ("fmt_y=", (void (AnnotationRef::*) (const std::string &)) &AnnotationRef::fmt_y,
    "@brief Sets the format used for the y-axis label\n"
    "@args format\n"
    "Y-axis labels are only used for styles that have a vertical component. "
    "@param format The format string\n"
    "Format strings can contain placeholders for values and formulas for computing derived "
    "values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
    "more details."
  ) +
  gsi::method ("fmt_y", (const std::string & (AnnotationRef::*) () const) &AnnotationRef::fmt_y,
    "@brief Returns the format used for the y-axis label\n"
    "@return The format string\n"
    "Format strings can contain placeholders for values and formulas for computing derived "
    "values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
    "more details."
  ) +
  gsi::method_ext ("style=", &gsi::set_style, 
    "@brief Sets the style used for drawing the annotation object\n"
    "@args style\n"
    "The Style... values can be used for defining the annotation object's style. The style determines "
    "if ticks or arrows are drawn."
  ) +
  gsi::method_ext ("style", &gsi::get_style,
    "@brief Returns the style of the annotation object\n"
  ) +
  gsi::method_ext ("outline=", &gsi::set_outline,
    "@brief Sets the outline style used for drawing the annotation object\n"
    "@args outline\n"
    "The Outline... values can be used for defining the annotation object's outline. The "
    "outline style determines what components are drawn. "
  ) +
  gsi::method_ext ("outline", &gsi::get_outline,
    "@brief Returns the outline style of the annotation object\n"
  ) +
  gsi::method ("snap=", (void (AnnotationRef::*) (bool)) &AnnotationRef::snap,
    "@brief Sets the 'snap to objects' attribute\n"
    "@args flag\n"
    "If this attribute is set to true, the ruler or marker snaps to other objects when moved. "
  ) +
  gsi::method ("snap?", (bool (AnnotationRef::*) () const) &AnnotationRef::snap,
    "@brief Returns the 'snap to objects' attribute\n"
  ) +
  gsi::method_ext ("angle_constraint=", &gsi::set_angle_constraint,
    "@brief Sets the angle constraint attribute\n"
    "@args flag\n"
    "This attribute controls if an angle constraint is applied when moving one of the ruler's "
    "points. The Angle... values can be used for this purpose." 
  ) +
  gsi::method_ext ("angle_constraint", &gsi::get_angle_constraint,
    "@brief Returns the angle constraint attribute\n"
    "See \\angle_constraint= for a more detailed description."
  ) +
  gsi::method ("text_x", (std::string (AnnotationRef::*)() const) &AnnotationRef::text_x,
    "@brief Returns the formatted text for the x-axis label"
  ) +
  gsi::method ("text_y", (std::string (AnnotationRef::*)() const) &AnnotationRef::text_y,
    "@brief Returns the formatted text for the y-axis label"
  ) +
  gsi::method ("text", (std::string (AnnotationRef::*)() const) &AnnotationRef::text,
    "@brief Returns the formatted text for the main label"
  ) +
  gsi::method ("id", (int (AnnotationRef::*)() const) &AnnotationRef::id,
    "@brief Returns the annotation's ID"
    "\n"
    "The annotation ID is an integer that uniquely identifies an annotation inside a view.\n"
    "The ID is used for replacing an annotation (see \\LayoutView#replace_annotation).\n"
    "\n"
    "This method was introduced in version 0.24."
  ) +
  gsi::method ("is_valid?", &AnnotationRef::is_valid,
    "@brief Returns a value indicating whether the object is a valid reference.\n"
    "If this value is true, the object represents an annotation on the screen. Otherwise, the "
    "object is a 'detached' annotation which does not have a representation on the screen.\n"
    "\n"
    "This method was introduced in version 0.25."
  ) +
  gsi::method ("to_s", &AnnotationRef::to_string,
    "@brief Returns the string representation of the ruler"
    "\n"
    "This method was introduced in version 0.19."
  ) +
  gsi::method ("==", &AnnotationRef::operator==,
    "@brief Equality operator\n"
    "@args other"
  ) +
  gsi::method ("!=", &AnnotationRef::operator!=,
    "@brief Inequality operator\n"
    "@args other"
  ),
  "@brief A layout annotation (i.e. ruler)\n"
  "\n"
  "Annotation objects provide a way to attach measurements or descriptive informations to a layout view. "
  "Annotation objects can appear as rulers for example. Annotation objects can be configured in "
  "different ways using the styles provided. By configuring an annotation object properly, it can appear "
  "as a rectangle or a plain line for example.\n"
  "See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
  "more details about the appearance options.\n"
  "\n"
  "Annotations are inserted into a layout view using \\LayoutView#insert_annotation. Here is some sample code "
  "in Ruby:\n"
  "\n"
  "@code\n"
  "app = RBA::Application.instance\n"
  "mw = app.main_window\n"
  "view = mw.current_view\n"
  "\n"
  "ant = RBA::Annotation::new\n"
  "ant.p1 = RBA::DPoint::new(0, 0)\n"
  "ant.p2 = RBA::DPoint::new(100, 0)\n"
  "ant.style = RBA::Annotation::StyleRuler\n"
  "view.insert_annotation(ant)\n"
  "@/code\n"
  "\n"
  "Annotations can be retrieved from a view with \\LayoutView#each_annotation and all "
  "annotations can be cleared with \\LayoutView#clear_annotations.\n"
  "\n"
  "Starting with version 0.25, annotations are 'live' objects once they are inserted into the view. "
  "Changing properties of annotations will automatically update the view (however, that is not true the "
  "other way round).\n"
  "\n"
  "Here is some sample code of changing the style of all rulers to two-sided arrows:\n"
  "\n"
  "@code\n"
  "view = RBA::LayoutView::current\n"
  "\n"
  "begin\n"
  "\n"
  "  view.transaction(\"Restyle annotations\")\n"
  "\n"
  "  view.each_annotation do |a|\n"
  "    a.style = RBA::Annotation::StyleArrowBoth\n"
  "  end\n"
  "  \n"
  "ensure\n"
  "  view.commit\n"
  "end\n"
  "@/code\n"
);

static
gsi::ClassExt<lay::LayoutView> layout_view_decl (
  gsi::method_ext ("clear_annotations", &gsi::clear_annotations, 
    "@brief Clears all annotations on this view"
  ) +
  gsi::method_ext ("insert_annotation", &gsi::insert_annotation, gsi::arg ("obj"),
    "@brief Inserts an annotation object into the given view\n"
    "Inserts a new annotation into the view. Existing annotation will remain. Use \\clear_annotations to "
    "delete them before inserting new ones. Use \\replace_annotation to replace an existing one with a new one. "
    "\n"
    "Starting with version 0.25 this method modifies self's ID to reflect the ID of the ruler created. "
    "After an annotation is inserted into the view, it can be modified and the changes of properties will become "
    "reflected immediately in the view."
  ) +
  gsi::method_ext ("erase_annotation", &gsi::erase_annotation, gsi::arg ("id"),
    "@brief Erases the annotation given by the id\n"
    "Deletes an existing annotation given by the id parameter. The id of an annotation "
    "can be obtained through \\Annotation#id.\n"
    "\n"
    "This method has been introduced in version 0.24.\n"
    "Starting with version 0.25, the annotation's \\Annotation#delete method can also be used to delete an annotation."
  ) +
  gsi::method_ext ("replace_annotation", &gsi::replace_annotation, gsi::arg ("id"), gsi::arg ("obj"),
    "@brief Replaces the annotation given by the id with the new one\n"
    "Replaces an existing annotation given by the id parameter with the new one. The id of an annotation "
    "can be obtained through \\Annotation#id.\n"
    "\n"
    "This method has been introduced in version 0.24.\n"
  ) +
  gsi::method_ext ("annotation", &gsi::get_annotation, gsi::arg ("id"),
    "@brief Gets the annotation given by an ID\n"
    "Returns a reference to the annotation given by the respective ID or an invalid annotation if the ID is not valid.\n"
    "Use \\Annotation#is_valid? to determine whether the returned annotation is valid or not.\n"
    "\n"
    "The returned annotation is a 'live' object and changing it will update the view.\n"
    "\n"
    "This method has been introduced in version 0.25.\n"
  ) +
  gsi::event_ext ("on_annotations_changed", &get_annotations_changed_event,
    "@brief A event indicating that annotations have been added or removed\n"
    "This event has been added in version 0.25.\n"
  ) +
  gsi::event_ext ("on_annotation_selection_changed", &get_annotation_selection_changed_event,
    "@brief A event indicating that the annotation selection has changed\n"
    "This event has been added in version 0.25.\n"
  ) +
  gsi::event_ext ("on_annotation_changed", &get_annotation_changed_event, gsi::arg ("id"),
    "@brief A event indicating that an annotation has been modified\n"
    "The argument of the event is the ID of the annotation that was changed.\n"
    "This event has been added in version 0.25.\n"
  ) +
  gsi::iterator_ext ("each_annotation", &gsi::begin_annotations,
    "@brief Iterates over all annotations attached to this view"
  ),
  ""
);

class AnnotationSelectionIterator 
{
public:
  typedef AnnotationRef value_type;
  typedef std::map<ant::Service::obj_iterator, unsigned int>::const_iterator iterator_type;
  typedef void pointer; 
  typedef const value_type &reference;
  typedef std::forward_iterator_tag iterator_category;
  typedef void difference_type;

  AnnotationSelectionIterator (const std::vector<ant::Service *> &services) 
    : m_services (services), m_service (0)
  {
    if (! m_services.empty ()) {
      m_iter = m_services [m_service]->selection ().begin ();
      next ();
    }
  }

  bool at_end () const
  {
    return (m_service >= m_services.size ());
  }

  AnnotationSelectionIterator &operator++ ()
  {
    ++m_iter;
    next ();
    return *this;
  }

  value_type operator* () const
  {
    return value_type (*(dynamic_cast<const ant::Object *> (m_iter->first->ptr ())), m_services[m_service]->view ());
  }

private:
  std::vector<ant::Service *> m_services;
  unsigned int m_service;
  iterator_type m_iter;

  void next ()
  {
    while (m_iter == m_services [m_service]->selection ().end ()) {
      ++m_service;
      if (m_service < m_services.size ()) {
        m_iter = m_services [m_service]->selection ().begin ();
      } else {
        break;
      }
    }
  }
};

//  extend the layout view by "edtService" specific methods 

static bool has_annotation_selection (const lay::LayoutView *view)
{
  std::vector<ant::Service *> ant_services = view->get_plugins <ant::Service> ();
  for (std::vector<ant::Service *>::const_iterator s = ant_services.begin (); s != ant_services.end (); ++s) {
    if ((*s)->selection_size () > 0) {
      return true;
    }
  }
  return false;
}

static AnnotationSelectionIterator begin_annotations_selected (const lay::LayoutView *view)
{
  return AnnotationSelectionIterator (view->get_plugins <ant::Service> ());
}


static
gsi::ClassExt<lay::LayoutView> layout_view_decl2 (
  gsi::method_ext ("has_annotation_selection?", &has_annotation_selection, 
    "@brief Returns true, if annotations (rulers) are selected in this view"
    "\n"
    "This method was introduced in version 0.19."
  ) +
  gsi::iterator_ext ("each_annotation_selected", &begin_annotations_selected,
    "@brief Iterate over each selected annotation objects, yielding a \\Annotation object for each of them"
    "\n"
    "This method was introduced in version 0.19."
  ),
  ""
);

}

