[dia] guides: Salvage patch from Martin van Zijl



commit 354cdb199553ec17440907095086cdfa9e096e28
Author: Zander Brown <zbrown gnome org>
Date:   Fri Oct 4 16:08:22 2019 +0100

    guides: Salvage patch from Martin van Zijl
    
    Minimal tweaks for compatability with current master
    
    Fix https://gitlab.gnome.org/GNOME/dia/issues/79

 app/commands.c                      |  68 +++++++++++
 app/commands.h                      |  24 ++--
 app/dia-props.c                     |   9 ++
 app/dia.gresource.xml               |   5 +
 app/diagram.c                       | 171 ++++++++++++++++++++++++++-
 app/diagram.h                       |  46 +++++++-
 app/display.c                       |  56 ++++++++-
 app/display.h                       |  12 +-
 app/grid.c                          | 115 ++++++++++++++++++
 app/grid.h                          |   1 +
 app/guide_tool.c                    | 221 +++++++++++++++++++++++++++++++++++
 app/guide_tool.h                    |  54 +++++++++
 app/icons/dia-guides-snap-off.png   | Bin 0 -> 361 bytes
 app/icons/dia-guides-snap-off.svg   |  90 ++++++++++++++
 app/icons/dia-guides-snap-on.png    | Bin 0 -> 373 bytes
 app/icons/dia-guides-snap-on.svg    |  90 ++++++++++++++
 app/interface.c                     | 178 ++++++++++++++++++++++++++++
 app/load_save.c                     | 105 ++++++++++++-----
 app/menus.c                         |  54 +++++++++
 app/menus.h                         |   1 +
 app/meson.build                     |   5 +
 app/modify_tool.c                   |  28 +++++
 app/new_guide_dialog.c              | 149 ++++++++++++++++++++++++
 app/new_guide_dialog.h              |  26 +++++
 app/preferences.c                   |  10 ++
 app/preferences.h                   |   5 +
 app/ruler.c                         |   2 +-
 app/tool.c                          |  29 +++--
 app/tool.h                          |   7 +-
 app/undo.c                          | 226 ++++++++++++++++++++++++++++++++++++
 app/undo.h                          |  25 ++++
 data/ui/display-ui.xml              |   7 ++
 data/ui/integrated-ui.xml           |   7 ++
 data/ui/popup-ui.xml                |   9 +-
 data/ui/properties-dialog.ui        |  29 ++++-
 lib/diagramdata.h                   |   2 +-
 lib/guide.c                         |   1 +
 lib/guide.h                         |  31 +++++
 lib/meson.build                     |   2 +
 plug-ins/python/pydia-diagramdata.c |  30 ++---
 40 files changed, 1834 insertions(+), 96 deletions(-)
---
diff --git a/app/commands.c b/app/commands.c
index ca0c237b..95d2c6a3 100644
--- a/app/commands.c
+++ b/app/commands.c
@@ -90,6 +90,8 @@ ShellExecuteA (long        hwnd,
 #include "dia-props.h"
 #include "authors.h"                /* master contributors data */
 #include "object.h"
+#include "new_guide_dialog.h"
+
 
 void
 file_quit_callback (GtkAction *action)
@@ -1426,6 +1428,72 @@ view_layers_callback (GtkAction *action)
   integrated_ui_layer_view_show (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
 }
 
+
+void
+view_new_guide_callback (GtkAction *action)
+{
+  DDisplay *ddisp;
+
+  ddisp = ddisplay_active ();
+  if (!ddisp) {
+    return;
+  }
+
+  dialog_new_guide_show ();
+}
+
+
+void
+view_visible_guides_callback (GtkToggleAction *action)
+{
+  DDisplay *ddisp;
+  gboolean old_val;
+
+  ddisp = ddisplay_active ();
+  if (!ddisp) {
+    return;
+  }
+
+  old_val = ddisp->guides_visible;
+  ddisp->guides_visible = gtk_toggle_action_get_active (action);
+
+  if (old_val != ddisp->guides_visible) {
+    ddisplay_add_update_all (ddisp);
+    ddisplay_flush (ddisp);
+  }
+}
+
+
+void
+view_snap_to_guides_callback (GtkToggleAction *action)
+{
+  DDisplay *ddisp;
+
+  ddisp = ddisplay_active ();
+  if (!ddisp) {
+    return;
+  }
+
+  ddisplay_set_snap_to_guides (ddisp, gtk_toggle_action_get_active (action));
+}
+
+
+void
+view_remove_all_guides_callback (GtkAction *action)
+{
+  Diagram *dia;
+
+  dia = ddisplay_active_diagram ();
+  if (!dia) {
+    return;
+  }
+
+  diagram_remove_all_guides (dia);
+  diagram_add_update_all (dia);
+  diagram_flush (dia);
+}
+
+
 void
 layers_add_layer_callback (GtkAction *action)
 {
diff --git a/app/commands.h b/app/commands.h
index 077f8344..4137b7cb 100644
--- a/app/commands.h
+++ b/app/commands.h
@@ -45,16 +45,20 @@ void edit_cut_text_callback   (GtkAction *action);
 
 void edit_paste_image_callback (GtkAction *action);
 
-void view_zoom_in_callback         (GtkAction *action);
-void view_zoom_out_callback        (GtkAction *action);
-void view_zoom_set_callback        (GtkAction *action);
-void view_unfullscreen             (void);
-void view_fullscreen_callback      (GtkToggleAction *action);
-void view_aa_callback              (GtkToggleAction *action);
-void view_visible_grid_callback    (GtkToggleAction *action);
-void view_snap_to_grid_callback    (GtkToggleAction *action);
-void view_snap_to_objects_callback (GtkToggleAction *action);
-void view_toggle_rulers_callback   (GtkToggleAction *action);
+void view_zoom_in_callback           (GtkAction *action);
+void view_zoom_out_callback          (GtkAction *action);
+void view_zoom_set_callback          (GtkAction *action);
+void view_unfullscreen               (void);
+void view_fullscreen_callback        (GtkToggleAction *action);
+void view_aa_callback                (GtkToggleAction *action);
+void view_visible_grid_callback      (GtkToggleAction *action);
+void view_snap_to_grid_callback      (GtkToggleAction *action);
+void view_new_guide_callback         (GtkAction *action);
+void view_visible_guides_callback    (GtkToggleAction *action);
+void view_snap_to_guides_callback    (GtkToggleAction *action);
+void view_remove_all_guides_callback (GtkAction *action);
+void view_snap_to_objects_callback   (GtkToggleAction *action);
+void view_toggle_rulers_callback     (GtkToggleAction *action);
 void view_toggle_scrollbars_callback (GtkToggleAction *action);
 
 void view_show_cx_pts_callback     (GtkToggleAction *action);
diff --git a/app/dia-props.c b/app/dia-props.c
index d363417e..a214cea6 100644
--- a/app/dia-props.c
+++ b/app/dia-props.c
@@ -54,6 +54,7 @@ struct _DiaDiagramPropertiesDialogPrivate {
   GtkWidget *background;
   GtkWidget *grid_lines;
   GtkWidget *page_lines;
+  GtkWidget *guide_lines;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (DiaDiagramPropertiesDialog, dia_diagram_properties_dialog, GTK_TYPE_DIALOG)
@@ -164,6 +165,9 @@ dia_diagram_properties_dialog_response (GtkDialog *dialog,
       dia_mem_swap_change_new (priv->diagram,
                                &priv->diagram->pagebreak_color,
                                sizeof(priv->diagram->pagebreak_color));
+      dia_mem_swap_change_new (priv->diagram,
+                               &priv->diagram->guide_color,
+                               sizeof(priv->diagram->guide_color));
       undo_set_transactionpoint (priv->diagram->undo);
 
       priv->diagram->grid.dynamic =
@@ -181,6 +185,8 @@ dia_diagram_properties_dialog_response (GtkDialog *dialog,
                                     &priv->diagram->grid.colour);
       dia_color_selector_get_color (priv->page_lines,
                                     &priv->diagram->pagebreak_color);
+      dia_color_selector_get_color (priv->guide_lines,
+                                    &priv->diagram->guide_color);
       diagram_add_update_all (priv->diagram);
       diagram_flush (priv->diagram);
       diagram_set_modified (priv->diagram, TRUE);
@@ -318,6 +324,7 @@ dia_diagram_properties_dialog_init (DiaDiagramPropertiesDialog *self)
   priv->background = GTK_WIDGET (gtk_builder_get_object (builder, "background"));
   priv->grid_lines = GTK_WIDGET (gtk_builder_get_object (builder, "grid_lines"));
   priv->page_lines = GTK_WIDGET (gtk_builder_get_object (builder, "page_lines"));
+  priv->guide_lines = GTK_WIDGET (gtk_builder_get_object (builder, "guide_lines"));
 
   g_clear_object (&builder);
 }
@@ -385,6 +392,8 @@ dia_diagram_properties_dialog_set_diagram (DiaDiagramPropertiesDialog *self,
                                 &diagram->grid.colour);
   dia_color_selector_set_color (priv->page_lines,
                                 &diagram->pagebreak_color);
+  dia_color_selector_set_color (priv->guide_lines,
+                                &diagram->guide_color);
 
   update_sensitivity (GTK_TOGGLE_BUTTON (priv->dynamic), self);
 
diff --git a/app/dia.gresource.xml b/app/dia.gresource.xml
index a09e1561..262a3952 100644
--- a/app/dia.gresource.xml
+++ b/app/dia.gresource.xml
@@ -23,6 +23,11 @@
     <file>icons/dia-layer-rename.png</file>
     <file>icons/dia-layer-move-above.png</file>
     <file>icons/dia-layer-move-below.png</file>
+    <!-- GTK3: Use SVG directly instead of PNG -->
+    <file>icons/dia-guides-snap-off.svg</file>
+    <file>icons/dia-guides-snap-on.svg</file>
+    <file>icons/dia-guides-snap-off.png</file>
+    <file>icons/dia-guides-snap-on.png</file>
     <file>dia-splash.png</file>
   </gresource>
 </gresources>
diff --git a/app/diagram.c b/app/diagram.c
index 44f3ccac..8e034980 100644
--- a/app/diagram.c
+++ b/app/diagram.c
@@ -234,6 +234,7 @@ dia_diagram_init (Diagram *self)
                                        /* Sure, but it's also silly ZB */
 
   self->pagebreak_color = prefs.new_diagram.pagebreak_color;
+  self->guide_color = prefs.new_diagram.guide_color;
 
   get_paper_info (&self->data->paper, -1, &prefs.new_diagram);
 
@@ -247,10 +248,7 @@ dia_diagram_init (Diagram *self)
   self->grid.dynamic = prefs.grid.dynamic;
   self->grid.major_lines = prefs.grid.major_lines;
 
-  self->guides.nhguides = 0;
-  self->guides.hguides = NULL;
-  self->guides.nvguides = 0;
-  self->guides.vguides = NULL;
+  self->guides = NULL;
 
   self->filename = NULL;
 
@@ -1610,3 +1608,168 @@ dia_diagram_get_file (Diagram *self)
 
   return priv->file;
 }
+
+
+Guide *
+diagram_add_guide (Diagram *dia, real position, GtkOrientation orientation, gboolean push_undo)
+{
+  Guide *guide = g_new0 (Guide, 1);
+  guide->position = position;
+  guide->orientation = orientation;
+  dia->guides = g_list_append (dia->guides, guide);
+
+  if(push_undo) {
+    dia_add_guide_change_new (dia, guide, TRUE);   /* Update undo stack. */
+    undo_set_transactionpoint (dia->undo);
+  }
+
+  diagram_add_update_all (dia);
+  diagram_modified (dia);
+  diagram_flush (dia);
+
+  return guide;
+}
+
+Guide *
+diagram_pick_guide (Diagram *dia,
+                    gdouble x,
+                    gdouble y,
+                    gdouble epsilon_x,
+                    gdouble epsilon_y)
+{
+  GList *list;
+  Guide *ret = NULL;
+  gdouble mindist = G_MAXDOUBLE;
+
+  g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL);
+
+  for (list = dia->guides;
+       list;
+       list = g_list_next (list)) {
+    Guide *guide = list->data;
+    real position = guide->position;
+    gdouble dist;
+
+    switch (guide->orientation) {
+      case GTK_ORIENTATION_HORIZONTAL:
+        dist = ABS (position - y);
+        if (dist < MIN (epsilon_y, mindist)) {
+          mindist = dist;
+          ret = guide;
+        }
+        break;
+
+      case GTK_ORIENTATION_VERTICAL:
+        dist = ABS (position - x);
+        if (dist < MIN (epsilon_x, mindist / epsilon_y * epsilon_x)) {
+          mindist = dist * epsilon_y / epsilon_x;
+          ret = guide;
+        }
+        break;
+
+      default:
+        continue;
+    }
+  }
+
+  return ret;
+}
+
+Guide *
+diagram_pick_guide_h (Diagram *dia,
+                      gdouble x,
+                      gdouble y,
+                      gdouble epsilon_x,
+                      gdouble epsilon_y)
+{
+  GList *list;
+  Guide *ret = NULL;
+  gdouble mindist = G_MAXDOUBLE;
+
+  g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL);
+
+  for (list = dia->guides;
+       list;
+       list = g_list_next (list)) {
+    Guide *guide = list->data;
+    real position = guide->position;
+    gdouble dist;
+
+    switch (guide->orientation) {
+      case GTK_ORIENTATION_HORIZONTAL:
+        dist = ABS (position - y);
+        if (dist < MIN (epsilon_y, mindist)) {
+          mindist = dist;
+          ret = guide;
+        }
+        break;
+
+      default:
+        continue;
+      }
+  }
+
+  return ret;
+}
+
+
+Guide *
+diagram_pick_guide_v (Diagram *dia,
+                      gdouble x,
+                      gdouble y,
+                      gdouble epsilon_x,
+                      gdouble epsilon_y)
+{
+  GList *list;
+  Guide *ret = NULL;
+  gdouble mindist = G_MAXDOUBLE;
+
+  g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL);
+
+  for (list = dia->guides;
+       list;
+       list = g_list_next (list)) {
+    Guide *guide = list->data;
+    real position = guide->position;
+    gdouble dist;
+
+    switch (guide->orientation) {
+      case GTK_ORIENTATION_VERTICAL:
+        dist = ABS (position - x);
+        if (dist < MIN (epsilon_x, mindist / epsilon_y * epsilon_x)) {
+          mindist = dist * epsilon_y / epsilon_x;
+          ret = guide;
+        }
+        break;
+
+      default:
+        continue;
+    }
+  }
+
+  return ret;
+}
+
+
+void
+diagram_remove_guide (Diagram *dia, Guide *guide, gboolean push_undo)
+{
+  if (push_undo) {
+    dia_delete_guide_change_new (dia, guide, TRUE);   /* Update undo stack. */
+  }
+
+  dia->guides = g_list_remove (dia->guides, guide);
+}
+
+
+void
+diagram_remove_all_guides (Diagram *dia)
+{
+  GList *list;
+
+  for(list = g_list_copy (dia->guides); list; list = g_list_next(list)) {
+    diagram_remove_guide (dia, list->data, TRUE);
+  }
+
+  undo_set_transactionpoint (dia->undo);
+}
diff --git a/app/diagram.h b/app/diagram.h
index e2546f3b..735205cb 100644
--- a/app/diagram.h
+++ b/app/diagram.h
@@ -26,6 +26,7 @@ typedef struct _Diagram Diagram;
 #include "diagramdata.h"
 #include "undo.h"
 #include "diagrid.h"
+#include "guide.h"
 
 G_BEGIN_DECLS
 
@@ -51,12 +52,8 @@ struct _Diagram {
   Color pagebreak_color; /*!< just to show page breaks */
   DiaGrid     grid;      /*!< the display grid */
 
-  /*! almost completely unused guides (load and save code is there) */
-  struct {
-    /* sorted arrays of the guides for the diagram */
-    real *hguides, *vguides;
-    guint nhguides, nvguides;
-  } guides;
+  GList *guides;         /*!< list of guides */
+  Color guide_color;     /*!< color for guides */
 
   DiagramData *data;     /*! just for compatibility, now that the Diagram _is_ and not _has_ DiagramData */
 
@@ -141,6 +138,43 @@ void     dia_diagram_set_file (Diagram *self,
                                GFile   *file);
 GFile   *dia_diagram_get_file (Diagram *self);
 
+/** Add a guide to the diagram at the given position and orientation.
+ *  Update the undo stack if "push_undo" is true. */
+Guide *diagram_add_guide (Diagram *dia, real position, GtkOrientation orientation, gboolean push_undo);
+
+/** Pick a guide within (epsilon_x, epsilon_y) distance of (x, y).
+ *  Return NULL if no such guide exists. */
+Guide *diagram_pick_guide (Diagram *dia,
+                           gdouble x,
+                           gdouble y,
+                           gdouble epsilon_x,
+                           gdouble epsilon_y);
+
+
+/** Pick a *horizontal* guide within (epsilon_x, epsilon_y) distance of (x, y).
+ *  Return NULL if no such guide exists. */
+Guide *diagram_pick_guide_h (Diagram *dia,
+                             gdouble x,
+                             gdouble y,
+                             gdouble epsilon_x,
+                             gdouble epsilon_y);
+
+/** Pick a *vertical* guide within (epsilon_x, epsilon_y) distance of (x, y).
+ *  Return NULL if no such guide exists. */
+Guide *diagram_pick_guide_v (Diagram *dia,
+                             gdouble x,
+                             gdouble y,
+                             gdouble epsilon_x,
+                             gdouble epsilon_y);
+
+/** Remove the given guide from the diagram.
+ *  Update the undo stack if "push_undo" is true. */
+void diagram_remove_guide (Diagram *dia, Guide *guide, gboolean push_undo);
+
+/** Remove all guides from the diagram. Updates undo stack. */
+void diagram_remove_all_guides (Diagram *dia);
+
+
 G_END_DECLS
 
 #endif /* DIAGRAM_H */
diff --git a/app/display.c b/app/display.c
index 081382c3..6fa5df61 100644
--- a/app/display.c
+++ b/app/display.c
@@ -228,6 +228,17 @@ new_display(Diagram *dia)
   if (preset != 0)
     ddisp->grid.snap = (preset > 0 ? TRUE : FALSE);
 
+  ddisp->guides_visible = prefs.guides_visible;
+  preset = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(dia), "show-guides"));
+  if (preset != 0) {
+    ddisp->guides_visible = (preset > 0 ? TRUE : FALSE);
+  }
+  ddisp->guides_snap = prefs.guides_snap;
+  preset = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(dia), "snap-to-guides"));
+  if (preset != 0) {
+    ddisp->guides_snap = (preset > 0 ? TRUE : FALSE);
+  }
+
   ddisp->show_cx_pts = prefs.show_cx_pts;
   preset = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(dia), "show-connection-points"));
   if (preset != 0)
@@ -267,6 +278,10 @@ new_display(Diagram *dia)
 
   ddisp->visible = visible;
 
+  ddisp->is_dragging_new_guideline = FALSE;
+  ddisp->dragged_new_guideline_position = 0;
+  ddisp->dragged_new_guideline_orientation = GTK_ORIENTATION_HORIZONTAL;
+
   initialize_display_widgets(ddisp);
   return ddisp;  /*  set the user data  */
 }
@@ -507,6 +522,7 @@ ddisplay_render_pixmap (DDisplay     *ddisp,
   /* Draw grid */
   grid_draw (ddisp, update);
   pagebreak_draw (ddisp, update);
+  guidelines_draw (ddisp, update);
 
 #ifdef TRACES
   timer = g_timer_new();
@@ -1153,15 +1169,19 @@ display_update_menu_state(DDisplay *ddisp)
   GtkToggleAction *rulers;
   GtkToggleAction *visible_grid;
   GtkToggleAction *snap_to_grid;
+  GtkToggleAction *visible_guides;
+  GtkToggleAction *snap_to_guides;
   GtkToggleAction *show_cx_pts;
   GtkToggleAction *antialiased;
   gboolean scrollbars_shown;
 
-  rulers       = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowrulers"));
-  visible_grid = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowgrid"));
-  snap_to_grid = GTK_TOGGLE_ACTION (menus_get_action ("ViewSnaptogrid"));
-  show_cx_pts  = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowconnectionpoints"));
-  antialiased  = GTK_TOGGLE_ACTION (menus_get_action ("ViewAntialiased"));
+  rulers         = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowrulers"));
+  visible_grid   = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowgrid"));
+  snap_to_grid   = GTK_TOGGLE_ACTION (menus_get_action ("ViewSnaptogrid"));
+  visible_guides = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowguides"));
+  snap_to_guides = GTK_TOGGLE_ACTION (menus_get_action ("ViewSnaptoguides"));
+  show_cx_pts    = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowconnectionpoints"));
+  antialiased    = GTK_TOGGLE_ACTION (menus_get_action ("ViewAntialiased"));
 
   gtk_action_set_sensitive (menus_get_action ("ViewAntialiased"),
                             g_type_from_name ("DiaCairoInteractiveRenderer") != 0);
@@ -1178,6 +1198,10 @@ display_update_menu_state(DDisplay *ddisp)
                                 ddisp->grid.visible);
   gtk_toggle_action_set_active (snap_to_grid,
                                 ddisp->grid.snap);
+  gtk_toggle_action_set_active (visible_guides,
+                                ddisp->guides_visible);
+  gtk_toggle_action_set_active (snap_to_guides,
+                                ddisp->guides_snap);
   gtk_toggle_action_set_active (show_cx_pts,
                                 ddisp->show_cx_pts);
 
@@ -1426,6 +1450,9 @@ display_set_active(DDisplay *ddisp)
         /* Object snapping */
         ddisplay_set_snap_to_objects (ddisp, ddisp->mainpoint_magnetism);
 
+        /* Snap to guides */
+        ddisplay_set_snap_to_guides (ddisp, ddisp->guides_snap);
+
         display_update_menu_state (ddisp);
 
         gtk_window_present (GTK_WINDOW(ddisp->shell));
@@ -1540,3 +1567,22 @@ ddisplay_show_all (DDisplay *ddisp)
   ddisplay_add_update_all(ddisp);
   ddisplay_flush(ddisp);
 }
+
+
+/** Set the display's snap-to-guides setting, updating menu and button
+ * in the process */
+void
+ddisplay_set_snap_to_guides (DDisplay *ddisp, gboolean snap)
+{
+  GtkToggleAction *snap_to_guides;
+  ddisp->guides_snap = snap;
+
+  snap_to_guides = GTK_TOGGLE_ACTION (menus_get_action ("ViewSnaptoguides"));
+
+  if (is_integrated_ui ())
+    integrated_ui_toolbar_guides_snap_synchronize_to_display (ddisp);
+
+  /* Currently, this can cause double emit, but that's a small problem. */
+  gtk_toggle_action_set_active (snap_to_guides, ddisp->guides_snap);
+  ddisplay_update_statusbar (ddisp);
+}
diff --git a/app/display.h b/app/display.h
index 5ca96415..07539aab 100644
--- a/app/display.h
+++ b/app/display.h
@@ -62,6 +62,7 @@ struct _DDisplay {
   GtkWidget *zoom_status;
   GtkWidget *grid_status;
   GtkWidget *mainpoint_status;
+  GtkWidget *guide_snap_status;
   GtkWidget *modified_status;
 
   GtkAccelGroup *accel_group;
@@ -75,7 +76,10 @@ struct _DDisplay {
 
   Grid grid;                      /* the grid in this display          */
 
-  gboolean show_cx_pts;                  /* Connection points toggle boolean  */
+  gboolean guides_visible;        /* Whether guides are visible. */
+  gboolean guides_snap;           /* Whether to snap to guides. */
+
+  gboolean show_cx_pts;           /* Connection points toggle boolean  */
   gboolean autoscroll;
   gboolean mainpoint_magnetism;   /* Mainpoints snapped from entire obj*/
 
@@ -108,6 +112,11 @@ struct _DDisplay {
 
   /* Rember the last clicked point per display, but in diagram coordinates */
   Point clicked_position;
+
+  /* For dragging a new guideline. */
+  gboolean is_dragging_new_guideline;
+  gdouble dragged_new_guideline_position;
+  GtkOrientation dragged_new_guideline_orientation;
 };
 
 extern GdkCursor *default_cursor;
@@ -143,6 +152,7 @@ void ddisplay_zoom_middle(DDisplay *ddisp, real magnify);
 
 void ddisplay_zoom_centered(DDisplay *ddisp, Point *point, real magnify);
 void ddisplay_set_snap_to_grid(DDisplay *ddisp, gboolean snap);
+void ddisplay_set_snap_to_guides(DDisplay *ddisp, gboolean snap);
 void ddisplay_set_snap_to_objects(DDisplay *ddisp, gboolean magnetic);
 void ddisplay_set_renderer(DDisplay *ddisp, int aa_renderer);
 void ddisplay_resize_canvas(DDisplay *ddisp,
diff --git a/app/grid.c b/app/grid.c
index 49648347..516667b7 100644
--- a/app/grid.c
+++ b/app/grid.c
@@ -377,9 +377,124 @@ pagebreak_draw (DDisplay *ddisp, DiaRectangle *update)
   }
 }
 
+
+void
+guidelines_draw (DDisplay *ddisp, DiaRectangle *update)
+{
+  Diagram *dia = ddisp->diagram;
+  DiaRenderer *renderer = ddisp->renderer;
+  GList *list;
+  real line_width;
+
+  int width = dia_interactive_renderer_get_width_pixels (DIA_INTERACTIVE_RENDERER (ddisp->renderer));
+  int height = dia_interactive_renderer_get_height_pixels (DIA_INTERACTIVE_RENDERER (ddisp->renderer));
+
+  Color guideline_color = dia->guide_color;
+
+  if (!dia) {
+    return;
+  }
+
+  /* Make the line width a little bigger than hairline. */
+  line_width = ddisplay_untransform_length (ddisp, 2);
+
+  dia_renderer_set_linewidth (renderer, line_width);
+  dia_renderer_set_linestyle (renderer, LINESTYLE_SOLID, 0.0);
+
+  if (ddisp->guides_visible) {
+    list = dia->guides;
+    while (list) {
+      int x;
+      int y;
+      Guide *guide = list->data;
+
+      switch (guide->orientation) {
+        case GTK_ORIENTATION_HORIZONTAL:
+          ddisplay_transform_coords (ddisp, 0, guide->position, &x, &y);
+          dia_interactive_renderer_draw_pixel_line (DIA_INTERACTIVE_RENDERER (renderer),
+                                                    0, y, width, y, &guideline_color);
+          break;
+
+        case GTK_ORIENTATION_VERTICAL:
+          ddisplay_transform_coords (ddisp, guide->position, 0, &x, &y);
+          dia_interactive_renderer_draw_pixel_line (DIA_INTERACTIVE_RENDERER (renderer),
+                                                    x, 0, x, height, &guideline_color);
+          break;
+
+        default:
+          g_print ("Should not have reached this.\n");
+          break;
+      }
+
+      list = g_list_next (list);
+    }
+  }
+
+  /* NOTE: We can still drag new guides even if guide visibility
+   * is set to off (like in GIMP). */
+  if (ddisp->is_dragging_new_guideline) {
+    int x;
+    int y;
+
+    switch (ddisp->dragged_new_guideline_orientation) {
+      case GTK_ORIENTATION_HORIZONTAL:
+        ddisplay_transform_coords (ddisp, 0, ddisp->dragged_new_guideline_position, &x, &y);
+        dia_interactive_renderer_draw_pixel_line (DIA_INTERACTIVE_RENDERER (renderer),
+                                                  0, y, width, y, &guideline_color);
+        break;
+
+      case GTK_ORIENTATION_VERTICAL:
+        ddisplay_transform_coords (ddisp, ddisp->dragged_new_guideline_position, 0, &x, &y);
+        dia_interactive_renderer_draw_pixel_line (DIA_INTERACTIVE_RENDERER (renderer),
+                                                  x, 0, x, height, &guideline_color);
+        break;
+
+      default:
+        g_print ("Should not have reached this.\n");
+        break;
+    }
+  }
+}
+
+
+/* For guides. */
+#define  FUNSCALEX(s,x)   ((x) / (s)->zoom_factor)
+#define  FUNSCALEY(s,y)   ((y) / (s)->zoom_factor)
+
+
 void
 snap_to_grid (DDisplay *ddisp, coord *x, coord *y)
 {
+  /* First snap to guides - only if they are visible and the setting is
+   * turned on. */
+  if (ddisp->guides_snap && ddisp->guides_visible) {
+    Guide *guide_h;
+    Guide *guide_v;
+    const gint snap_distance = prefs.snap_distance;
+
+    guide_h = diagram_pick_guide_h (ddisp->diagram, *x, *y,
+                                    FUNSCALEX (ddisp, snap_distance),
+                                    FUNSCALEY (ddisp, snap_distance));
+
+    guide_v = diagram_pick_guide_v (ddisp->diagram, *x, *y,
+                                    FUNSCALEX (ddisp, snap_distance),
+                                    FUNSCALEY (ddisp, snap_distance));
+
+    if (guide_h) {
+      *y = guide_h->position;
+    }
+
+    if (guide_v) {
+      *x = guide_v->position;
+    }
+
+    /* Assume this takes priority over grid. */
+    if (guide_h || guide_v) {
+      return;
+    }
+  }
+
+  /* Snap to grid. */
   if (ddisp->grid.snap) {
     if (ddisp->diagram->grid.hex) {
       real width_x = ddisp->diagram->grid.hex_size;
diff --git a/app/grid.h b/app/grid.h
index df835aae..910d88b7 100644
--- a/app/grid.h
+++ b/app/grid.h
@@ -32,6 +32,7 @@ struct _Grid {
 
 void grid_draw(DDisplay *ddisp, DiaRectangle *update);
 void pagebreak_draw(DDisplay *ddisp, DiaRectangle *update);
+void guidelines_draw(DDisplay *ddisp, DiaRectangle *update);
 void snap_to_grid(DDisplay *ddisp, coord *x, coord *y);
 
 gboolean grid_step (DDisplay *ddisp, GtkOrientation orientation,
diff --git a/app/guide_tool.c b/app/guide_tool.c
new file mode 100644
index 00000000..4abadce8
--- /dev/null
+++ b/app/guide_tool.c
@@ -0,0 +1,221 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <config.h>
+
+#include <gdk/gdk.h>
+
+#include "cursor.h"
+#include "guide_tool.h"
+#include "undo.h"
+#include "diainteractiverenderer.h"
+
+#define GUIDE_POSITION_UNDEFINED G_MINDOUBLE
+
+/* The original position of the current guide
+ * before we started dragging it. Need to keep track of it for
+ * undo moving/deleting guides. */
+static real guide_original_pos = 0;
+
+struct _GuideTool {
+  Tool tool;
+
+  Guide *guide;
+  real position;
+  GtkOrientation orientation;
+  int ruler_height;
+};
+
+
+
+static void
+guide_button_release(GuideTool *tool, GdkEventButton *event, DDisplay *ddisp);
+
+static void
+guide_motion(GuideTool *tool, GdkEventMotion *event, DDisplay *ddisp);
+
+
+
+void _guide_tool_start_new (DDisplay *display,
+                            GtkOrientation orientation)
+{
+  _guide_tool_start (display, orientation, NULL);
+}
+
+void _guide_tool_start (DDisplay *display,
+                        GtkOrientation orientation,
+                        Guide *guide)
+{
+  tool_select(GUIDE_TOOL, guide, GINT_TO_POINTER(orientation), NULL, 0);
+  display->dragged_new_guideline_orientation = orientation;
+}
+
+Tool *create_guide_tool(void)
+{
+  GuideTool *tool;
+
+  tool = g_new0(GuideTool, 1);
+  tool->tool.type = GUIDE_TOOL;
+
+  tool->tool.button_release_func = (ButtonReleaseFunc) &guide_button_release;
+  tool->tool.motion_func = (MotionFunc) &guide_motion;
+
+  tool->guide        = NULL;
+  tool->position     = GUIDE_POSITION_UNDEFINED;
+  tool->orientation  = GTK_ORIENTATION_HORIZONTAL;  /* Default. */
+
+  return (Tool *)tool;
+}
+
+void free_guide_tool(Tool *tool)
+{
+  GuideTool *gtool = (GuideTool *)tool;
+  g_free(gtool);
+}
+
+static void
+guide_button_release(GuideTool *tool, GdkEventButton *event,
+                     DDisplay *ddisp)
+{
+  /* Reset. */
+  ddisp->is_dragging_new_guideline = FALSE;
+
+  if (tool->position == GUIDE_POSITION_UNDEFINED)
+  {
+    /* Dragged out of bounds, so remove the guide. */
+    if (tool->guide) {
+      tool->guide->position = guide_original_pos; /* So that when we undo, it goes back to original 
position. */
+      diagram_remove_guide (ddisp->diagram, tool->guide, TRUE);
+      tool->guide = NULL;
+    }
+  }
+  else
+  {
+    if (!tool->guide) {
+      /* Add a new guide. */
+      diagram_add_guide (ddisp->diagram, tool->position, tool->orientation, TRUE);
+    }
+    else
+    {
+      /* Moved an existing guide, so update the undo stack. */
+      dia_move_guide_change_new (ddisp->diagram, tool->guide, guide_original_pos, tool->position);
+      undo_set_transactionpoint (ddisp->diagram->undo);
+    }
+  }
+
+  diagram_add_update_all(ddisp->diagram);
+  diagram_modified(ddisp->diagram);
+  diagram_flush(ddisp->diagram);
+
+  tool_reset();
+}
+
+static void
+guide_motion(GuideTool *tool, GdkEventMotion *event, DDisplay *ddisp)
+{
+  gint tx, ty;
+  Point to;
+  gint disp_width;
+  gint disp_height;
+
+  disp_width = dia_interactive_renderer_get_width_pixels (DIA_INTERACTIVE_RENDERER (ddisp->renderer));
+  disp_height = dia_interactive_renderer_get_height_pixels (DIA_INTERACTIVE_RENDERER (ddisp->renderer));
+
+  /* Event coordinates. */
+  tx = event->x;
+  ty = event->y;
+
+  /* Minus ruler height. */
+  if (tool->orientation == GTK_ORIENTATION_HORIZONTAL)
+    ty -= tool->ruler_height;
+  else
+    tx -= tool->ruler_height;
+
+  /* Diagram coordinates. */
+  ddisplay_untransform_coords(ddisp, tx, ty, &to.x, &to.y);
+
+  if (tx < 0 || tx >= disp_width ||
+      ty < 0 || ty >= disp_height)
+  {
+    /* Out of bounds. */
+    tool->position = GUIDE_POSITION_UNDEFINED;
+
+    /* Cancel dragging a new guide. */
+    ddisp->is_dragging_new_guideline = FALSE;
+    ddisp->dragged_new_guideline_position = 0;
+
+    if(tool->guide)
+    {
+      /* Deleting an existing guide. Hide it for now. */
+      tool->guide->position = G_MAXDOUBLE;
+    }
+  }
+  else
+  {
+    /* In bounds - add or move a guide. */
+    if (tool->orientation == GTK_ORIENTATION_HORIZONTAL)
+      tool->position = to.y;
+    else
+      tool->position = to.x;
+
+    if(tool->guide)
+    {
+      /* Move existing guide. */
+      tool->guide->position = tool->position;
+    }
+    else
+    {
+      /* Add new guide. */
+      ddisp->is_dragging_new_guideline = TRUE;
+      ddisp->dragged_new_guideline_position = tool->position;
+    }
+  }
+
+  diagram_add_update_all(ddisp->diagram);
+  diagram_modified(ddisp->diagram);
+  ddisplay_flush(ddisp);
+}
+
+void guide_tool_set_ruler_height(Tool *tool, int height)
+{
+  GuideTool *gtool = (GuideTool *)tool;
+  gtool->ruler_height = height;
+}
+
+void guide_tool_start_edit (DDisplay *display,
+                            Guide *guide)
+{
+  _guide_tool_start (display, guide->orientation, guide);
+
+  if(guide)
+  {
+    /* Store original position for undo stack. */
+    guide_original_pos = guide->position;
+  }
+}
+
+void guide_tool_set_guide(Tool *tool, Guide *guide)
+{
+  GuideTool *gtool = (GuideTool *)tool;
+  gtool->guide = guide;
+}
+
+void guide_tool_set_orientation(Tool *tool, GtkOrientation orientation)
+{
+  GuideTool *gtool = (GuideTool *)tool;
+  gtool->orientation = orientation;
+}
diff --git a/app/guide_tool.h b/app/guide_tool.h
new file mode 100644
index 00000000..1e003514
--- /dev/null
+++ b/app/guide_tool.h
@@ -0,0 +1,54 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef GUIDE_TOOL_H
+#define GUIDE_TOOL_H
+
+#include "guide.h"
+#include "tool.h"
+
+typedef struct _GuideTool GuideTool;
+
+Tool *create_guide_tool(void);
+void free_guide_tool(Tool *tool);
+
+/** Start dragging a new guide. */
+void _guide_tool_start_new (DDisplay *display,
+                            GtkOrientation orientation);
+
+/** Start editing (i.e. moving) an existing guide. */
+void guide_tool_start_edit (DDisplay *display,
+                            Guide *guide);
+
+/** Start using the guide tool.
+ *  If guide is not NULL, then start editing that guide.
+ *  If guide is NULL, then start adding a new guide. */
+void _guide_tool_start (DDisplay *display,
+                        GtkOrientation orientation,
+                        Guide *guide);
+
+/** Inform the tool of the ruler height. Required to calculate
+ *  the position of the guide on the page. */
+void guide_tool_set_ruler_height(Tool *tool, int height);
+
+/** Set the guide to edit. */
+void guide_tool_set_guide(Tool *tool, Guide *guide);
+
+/** Set the orientation of the tool. */
+void guide_tool_set_orientation(Tool *tool, GtkOrientation orientation);
+
+#endif /* GUIDE_TOOL_H */
diff --git a/app/icons/dia-guides-snap-off.png b/app/icons/dia-guides-snap-off.png
new file mode 100644
index 00000000..13ad1430
Binary files /dev/null and b/app/icons/dia-guides-snap-off.png differ
diff --git a/app/icons/dia-guides-snap-off.svg b/app/icons/dia-guides-snap-off.svg
new file mode 100644
index 00000000..ab1c3e2a
--- /dev/null
+++ b/app/icons/dia-guides-snap-off.svg
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="24"
+   height="24"
+   viewBox="0 0 6.3499999 6.3500002"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 5da689c313, 2019-01-14"
+   sodipodi:docname="dia-guides-snap-off.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.84"
+     inkscape:cx="13.354502"
+     inkscape:cy="10.620889"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     showguides="false"
+     inkscape:window-width="1366"
+     inkscape:window-height="704"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid817" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-290.64998)">
+    <rect
+       
style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#241f31;stroke-width:0.26061457;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers"
+       id="rect815"
+       width="5.5602164"
+       height="5.560216"
+       x="0.39489198"
+       y="291.04486" />
+    <rect
+       
style="opacity:1;fill:#26a269;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.3266578;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers"
+       id="rect846"
+       width="5.2916665"
+       height="1.0583333"
+       x="0.52916664"
+       y="294.35413" />
+    <rect
+       
style="opacity:1;fill:#26a269;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32665783;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers"
+       id="rect846-3"
+       width="1.0583333"
+       height="5.2916665"
+       x="3.7041667"
+       y="291.17914" />
+    <circle
+       
style="opacity:1;fill:#a51d2d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.38133451;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers"
+       id="path863"
+       cx="2.1166668"
+       cy="292.76663"
+       r="1.0583334" />
+  </g>
+</svg>
diff --git a/app/icons/dia-guides-snap-on.png b/app/icons/dia-guides-snap-on.png
new file mode 100644
index 00000000..0a1cb054
Binary files /dev/null and b/app/icons/dia-guides-snap-on.png differ
diff --git a/app/icons/dia-guides-snap-on.svg b/app/icons/dia-guides-snap-on.svg
new file mode 100644
index 00000000..3ea4a124
--- /dev/null
+++ b/app/icons/dia-guides-snap-on.svg
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="24"
+   height="24"
+   viewBox="0 0 6.3499999 6.3500002"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 5da689c313, 2019-01-14"
+   sodipodi:docname="dia-guides-snap-on.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="39.76"
+     inkscape:cx="13.354502"
+     inkscape:cy="7.1182622"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     showguides="false"
+     inkscape:window-width="1366"
+     inkscape:window-height="704"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid817" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-290.64998)">
+    <rect
+       
style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#241f31;stroke-width:0.26061457;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers"
+       id="rect815"
+       width="5.5602164"
+       height="5.560216"
+       x="0.39489198"
+       y="291.04486" />
+    <rect
+       
style="opacity:1;fill:#26a269;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.3266578;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers"
+       id="rect846"
+       width="5.2916665"
+       height="1.0583333"
+       x="0.52916664"
+       y="294.35413" />
+    <rect
+       
style="opacity:1;fill:#26a269;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32665783;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers"
+       id="rect846-3"
+       width="1.0583333"
+       height="5.2916665"
+       x="3.7041667"
+       y="291.17914" />
+    <circle
+       
style="opacity:1;fill:#a51d2d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.38133451;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers"
+       id="path863"
+       cx="4.2333336"
+       cy="294.8833"
+       r="1.0583334" />
+  </g>
+</svg>
diff --git a/app/interface.c b/app/interface.c
index 377efb68..a1918b06 100644
--- a/app/interface.c
+++ b/app/interface.c
@@ -50,6 +50,35 @@
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
+#include "guide_tool.h"
+
+#include <glib/gprintf.h>
+
+static gboolean _ddisplay_hruler_button_press (GtkWidget *widget,
+        GdkEventButton *bevent,
+        DDisplay *ddisp);
+
+static gboolean _ddisplay_vruler_button_press (GtkWidget *widget,
+        GdkEventButton *bevent,
+        DDisplay *ddisp);
+
+static gboolean _ddisplay_ruler_button_press (GtkWidget *widget,
+        GdkEventButton *event,
+        DDisplay *ddisp,
+        GtkOrientation orientation);
+
+static gboolean _ddisplay_ruler_button_release (GtkWidget *widget,
+        GdkEventButton *bevent,
+        DDisplay *ddisp);
+
+static gboolean _ddisplay_hruler_motion_notify (GtkWidget *widget,
+        GdkEventMotion *event,
+        DDisplay *ddisp);
+
+static gboolean _ddisplay_vruler_motion_notify (GtkWidget *widget,
+        GdkEventMotion *event,
+        DDisplay *ddisp);
+
 static void
 dia_dnd_file_drag_data_received (GtkWidget        *widget,
                                  GdkDragContext   *context,
@@ -154,6 +183,18 @@ interface_toggle_mainpoint_magnetism(GtkWidget *widget, gpointer data)
   ddisplay_flush(ddisp);
 }
 
+
+static void
+interface_toggle_snap_to_guides (GtkWidget *widget, gpointer data)
+{
+  DDisplay *ddisp = (DDisplay *) data;
+  ddisplay_set_snap_to_guides (ddisp,
+                               gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
+  ddisplay_add_update_all (ddisp);
+  ddisplay_flush (ddisp);
+}
+
+
 static gint
 origin_button_press(GtkWidget *widget, GdkEventButton *event, gpointer data)
 {
@@ -489,6 +530,32 @@ _ddisplay_setup_rulers (DDisplay *ddisp, GtkWidget *shell, GtkWidget *table)
   ddisp->hrule = dia_ruler_new (GTK_ORIENTATION_HORIZONTAL, shell, ddisp);
   ddisp->vrule = dia_ruler_new (GTK_ORIENTATION_VERTICAL, shell, ddisp);
 
+  /* Callbacks for adding guides - horizontal ruler. */
+  g_signal_connect (ddisp->hrule, "button-press-event",
+                    G_CALLBACK (_ddisplay_hruler_button_press),
+                    ddisp);
+
+  g_signal_connect (ddisp->hrule, "button_release_event",
+                    G_CALLBACK (_ddisplay_ruler_button_release),
+                    ddisp);
+
+  g_signal_connect (ddisp->hrule, "motion_notify_event",
+                    G_CALLBACK (_ddisplay_hruler_motion_notify),
+                    ddisp);
+
+  /* Callbacks for adding guides - vertical ruler. */
+  g_signal_connect (ddisp->vrule, "button-press-event",
+                    G_CALLBACK (_ddisplay_vruler_button_press),
+                    ddisp);
+
+  g_signal_connect (ddisp->vrule, "button_release_event",
+                    G_CALLBACK (_ddisplay_ruler_button_release),
+                    ddisp);
+
+  g_signal_connect (ddisp->vrule, "motion_notify_event",
+                    G_CALLBACK (_ddisplay_vruler_motion_notify),
+                    ddisp);
+
   /* harder to change position in the table, but we did not do it for years ;) */
   gtk_table_attach (GTK_TABLE (table), ddisp->hrule, 1, 2, 0, 1,
                     GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0);
@@ -681,6 +748,7 @@ use_integrated_ui_for_display_shell(DDisplay *ddisp, char *title)
 
   integrated_ui_toolbar_grid_snap_synchronize_to_display (ddisp);
   integrated_ui_toolbar_object_snap_synchronize_to_display (ddisp);
+  integrated_ui_toolbar_guides_snap_synchronize_to_display (ddisp);
 
   /*  set the focus to the canvas area  */
   gtk_widget_grab_focus (ddisp->canvas);
@@ -830,6 +898,16 @@ create_display_shell(DDisplay *ddisp,
   gtk_box_pack_start (GTK_BOX (status_hbox), ddisp->mainpoint_status,
                      FALSE, FALSE, 0);
 
+  ddisp->guide_snap_status = dia_toggle_button_new_with_icon_names ("dia-guides-snap-on",
+                                                                    "dia-guides-snap-off");
+
+  g_signal_connect(G_OBJECT(ddisp->guide_snap_status), "toggled",
+           G_CALLBACK (interface_toggle_snap_to_guides), ddisp);
+  gtk_widget_set_tooltip_text(ddisp->guide_snap_status,
+               _("Toggles snap-to-guides for this window."));
+  gtk_box_pack_start (GTK_BOX (status_hbox), ddisp->guide_snap_status,
+              FALSE, FALSE, 0);
+
 
   /* Statusbar */
   ddisp->modified_status = gtk_statusbar_new ();
@@ -846,6 +924,7 @@ create_display_shell(DDisplay *ddisp,
   gtk_widget_show (zoom_label);
   gtk_widget_show (ddisp->grid_status);
   gtk_widget_show (ddisp->mainpoint_status);
+  gtk_widget_show (ddisp->guide_snap_status);
   gtk_widget_show (ddisp->modified_status);
   gtk_widget_show (status_hbox);
   gtk_widget_show (table);
@@ -1236,3 +1315,102 @@ integrated_ui_statusbar_show (gboolean show)
       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), show);
   }
 }
+
+
+static gboolean
+_ddisplay_hruler_button_press (GtkWidget *widget,
+                               GdkEventButton *event,
+                               DDisplay *ddisp)
+{
+  return _ddisplay_ruler_button_press (widget, event, ddisp,
+                                       GTK_ORIENTATION_HORIZONTAL);
+}
+
+
+static gboolean
+_ddisplay_vruler_button_press (GtkWidget *widget,
+                               GdkEventButton *event,
+                               DDisplay *ddisp)
+{
+  return _ddisplay_ruler_button_press (widget, event, ddisp,
+                                       GTK_ORIENTATION_VERTICAL);
+}
+
+
+static gboolean
+_ddisplay_ruler_button_press (GtkWidget *widget,
+                              GdkEventButton *event,
+                              DDisplay *ddisp,
+                              GtkOrientation orientation)
+{
+  /* Start adding a new guide if the left button was pressed. */
+  if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+      _guide_tool_start_new (ddisp, orientation);
+  }
+
+  return FALSE;
+}
+
+
+static gboolean
+_ddisplay_ruler_button_release (GtkWidget *widget,
+                                 GdkEventButton *event,
+                                 DDisplay *ddisp)
+{
+  /* Hack to get this triggered. */
+  if(active_tool->type == GUIDE_TOOL)
+  {
+    if (active_tool->button_release_func)
+    {
+      (*active_tool->button_release_func) (active_tool, event, ddisp);
+    }
+  }
+
+  return FALSE;
+}
+
+
+static gboolean
+_ddisplay_hruler_motion_notify (GtkWidget *widget,
+    GdkEventMotion *event,
+    DDisplay *ddisp)
+{
+  /* Hack to get this triggered. */
+  if(active_tool->type == GUIDE_TOOL) {
+    if (active_tool->motion_func) {
+
+      /* Minus ruler height. */
+      GtkRequisition ruler_requisition;
+      gtk_widget_size_request (widget, &ruler_requisition);
+      guide_tool_set_ruler_height(active_tool, ruler_requisition.height);
+
+      /* Do the move. */
+      (*active_tool->motion_func) (active_tool, event, ddisp);
+    }
+  }
+
+  return FALSE;
+}
+
+
+static gboolean
+_ddisplay_vruler_motion_notify (GtkWidget *widget,
+    GdkEventMotion *event,
+    DDisplay *ddisp)
+{
+  /* Hack to get this triggered. */
+  if(active_tool->type == GUIDE_TOOL) {
+    if (active_tool->motion_func) {
+
+      /* Minus ruler width. */
+      GtkRequisition ruler_requisition;
+      gtk_widget_size_request (widget, &ruler_requisition);
+      guide_tool_set_ruler_height(active_tool, ruler_requisition.width);
+
+      /* Do the move. */
+      (*active_tool->motion_func) (active_tool, event, ddisp);
+    }
+  }
+
+  return FALSE;
+}
diff --git a/app/load_save.c b/app/load_save.c
index e5315b34..79fd40d2 100644
--- a/app/load_save.c
+++ b/app/load_save.c
@@ -377,7 +377,7 @@ diagram_data_load(const gchar *filename, DiagramData *data, DiaContext *ctx, voi
   xmlDocPtr doc;
   xmlNodePtr root;
   xmlNodePtr diagramdata;
-  xmlNodePtr paperinfo, gridinfo, guideinfo;
+  xmlNodePtr paperinfo, gridinfo;
   xmlNodePtr layer_node;
   AttributeNode attr;
   DiaLayer *layer;
@@ -564,39 +564,50 @@ diagram_data_load(const gchar *filename, DiagramData *data, DiaContext *ctx, voi
         data_color(attribute_first_data(attr), &diagram->grid.colour, ctx);
     }
   }
+
   if (diagram) {
-    attr = composite_find_attribute(diagramdata, "guides");
+    attr = composite_find_attribute (diagramdata, "guides");
     if (attr != NULL) {
-      guint i;
-      DataNode guide;
+      DataNode guides_data;
 
-      guideinfo = attribute_first_data(attr);
+      /* Clear old guides. */
+      g_list_free (diagram->guides);
+      diagram->guides = NULL;
 
-      attr = composite_find_attribute(guideinfo, "hguides");
-      if (attr != NULL) {
-        diagram->guides.nhguides = attribute_num_data(attr);
-        g_free(diagram->guides.hguides);
-        diagram->guides.hguides = g_new(real, diagram->guides.nhguides);
+      /* Load new guides. */
+      guides_data = attribute_first_data (attr);
+      while (guides_data) {
+        real position = 0;
+        GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
 
-        guide = attribute_first_data(attr);
-        for (i = 0; i < diagram->guides.nhguides; i++, guide = data_next(guide))
-         diagram->guides.hguides[i] = data_real(guide, ctx);
-      }
-      attr = composite_find_attribute(guideinfo, "vguides");
-      if (attr != NULL) {
-        diagram->guides.nvguides = attribute_num_data(attr);
-        g_free(diagram->guides.vguides);
-        diagram->guides.vguides = g_new(real, diagram->guides.nvguides);
+        attr = composite_find_attribute (guides_data, "position");
+        if(attr != NULL) {
+          position = data_real (attribute_first_data (attr), ctx);
+        }
+
+        attr = composite_find_attribute (guides_data, "orientation");
+        if (attr != NULL) {
+          orientation = data_int (attribute_first_data (attr), ctx);
+        }
 
-        guide = attribute_first_data(attr);
-        for (i = 0; i < diagram->guides.nvguides; i++, guide = data_next(guide))
-         diagram->guides.vguides[i] = data_real(guide, ctx);
+        diagram_add_guide (diagram, position, orientation, FALSE);
+
+        guides_data = data_next (guides_data);
       }
     }
+
+    /* Guide color. */
+    diagram->guide_color = prefs.new_diagram.guide_color;
+    attr = composite_find_attribute (diagramdata, "guide_color");
+    if (attr != NULL) {
+      data_color (attribute_first_data (attr), &diagram->guide_color, ctx);
+    }
   }
+
   /* parse some display settings */
   if (diagram) {
-    attr = composite_find_attribute(diagramdata, "display");
+    attr = composite_find_attribute (diagramdata, "display");
+
     if (attr != NULL) {
       DataNode dispinfo;
 
@@ -614,6 +625,13 @@ diagram_data_load(const gchar *filename, DiagramData *data, DiaContext *ctx, voi
        g_object_set_data(G_OBJECT(diagram),
          "snap-to-grid", GINT_TO_POINTER (data_boolean(attribute_first_data(attr), ctx) ? 1 : -1));
 
+      attr = composite_find_attribute(dispinfo, "snap-to-guides");
+      if (attr != NULL) {
+        g_object_set_data (G_OBJECT (diagram),
+                           "snap-to-guides",
+                           GINT_TO_POINTER (data_boolean (attribute_first_data (attr), ctx) ? 1 : -1));
+      }
+
       attr = composite_find_attribute(dispinfo, "snap-to-object");
       if (attr != NULL)
         g_object_set_data(G_OBJECT(diagram),
@@ -624,6 +642,14 @@ diagram_data_load(const gchar *filename, DiagramData *data, DiaContext *ctx, voi
         g_object_set_data(G_OBJECT(diagram),
          "show-grid", GINT_TO_POINTER (data_boolean(attribute_first_data(attr), ctx) ? 1 : -1));
 
+
+      attr = composite_find_attribute(dispinfo, "show-guides");
+      if (attr != NULL) {
+        g_object_set_data (G_OBJECT (diagram),
+                           "show-guides",
+                           GINT_TO_POINTER (data_boolean (attribute_first_data (attr), ctx) ? 1 : -1));
+      }
+
       attr = composite_find_attribute(dispinfo, "show-connection-points");
       if (attr != NULL)
         g_object_set_data(G_OBJECT(diagram),
@@ -929,6 +955,7 @@ diagram_data_write_doc(DiagramData *data, const char *filename, DiaContext *ctx)
   }
 
   if (diagram) {
+    GList *list;
     attr = new_attribute((ObjectNode)tree, "grid");
     gridinfo = data_add_composite(attr, "grid", ctx);
     data_add_boolean(composite_add_attribute(gridinfo, "dynamic"),
@@ -945,16 +972,26 @@ diagram_data_write_doc(DiagramData *data, const char *filename, DiaContext *ctx)
     data_add_composite(gridinfo, "color", ctx);
     data_add_color(attr, &diagram->grid.colour, ctx);
 
-    attr = new_attribute((ObjectNode)tree, "guides");
-    guideinfo = data_add_composite(attr, "guides", ctx);
-    attr = composite_add_attribute(guideinfo, "hguides");
-    for (i = 0; i < diagram->guides.nhguides; i++)
-      data_add_real(attr, diagram->guides.hguides[i], ctx);
-    attr = composite_add_attribute(guideinfo, "vguides");
-    for (i = 0; i < diagram->guides.nvguides; i++)
-      data_add_real(attr, diagram->guides.vguides[i], ctx);
+    /* Guides. */
+    attr = new_attribute ((ObjectNode) tree, "guides");
+    list = diagram->guides;
+    while (list) {
+      Guide *guide = list->data;
+
+      guideinfo = data_add_composite (attr, "guide", ctx);
+
+      data_add_real (composite_add_attribute (guideinfo, "position"), guide->position, ctx);
+      data_add_int (composite_add_attribute (guideinfo, "orientation"), guide->orientation, ctx);
+
+      list = g_list_next (list);
+    }
+
+    if (diagram) {
+      attr = new_attribute ((ObjectNode) tree, "guide_color");
+      data_add_color (attr, &diagram->guide_color, ctx);
+    }
 
-    if (g_slist_length(diagram->displays) == 1) {
+    if (g_slist_length (diagram->displays) == 1) {
       xmlNodePtr dispinfo;
       /* store some display attributes */
       DDisplay *ddisp = diagram->displays->data;
@@ -965,10 +1002,14 @@ diagram_data_write_doc(DiagramData *data, const char *filename, DiaContext *ctx)
                        ddisp->aa_renderer, ctx);
       data_add_boolean(composite_add_attribute(dispinfo, "snap-to-grid"),
                       ddisp->grid.snap, ctx);
+      data_add_boolean(composite_add_attribute(dispinfo, "snap-to-guides"),
+                      ddisp->guides_snap, ctx);
       data_add_boolean(composite_add_attribute(dispinfo, "snap-to-object"),
                       ddisp->mainpoint_magnetism, ctx);
       data_add_boolean(composite_add_attribute(dispinfo, "show-grid"),
                       ddisp->grid.visible, ctx);
+      data_add_boolean (composite_add_attribute (dispinfo, "show-guides"),
+                        ddisp->guides_visible, ctx);
       data_add_boolean(composite_add_attribute(dispinfo, "show-connection-points"),
                       ddisp->show_cx_pts, ctx);
     }
diff --git a/app/menus.c b/app/menus.c
index bfe82028..2107d4e3 100644
--- a/app/menus.c
+++ b/app/menus.c
@@ -57,6 +57,7 @@
 #define DIA_INTEGRATED_TOOLBAR_ZOOM_COMBO  "dia-integrated-toolbar-zoom-combo_entry"
 #define DIA_INTEGRATED_TOOLBAR_SNAP_GRID   "dia-integrated-toolbar-snap-grid"
 #define DIA_INTEGRATED_TOOLBAR_OBJECT_SNAP "dia-integrated-toolbar-object-snap"
+#define DIA_INTEGRATED_TOOLBAR_GUIDES_SNAP "dia-integrated-toolbar-guides-snap"
 
 #define ZOOM_FIT        _("Fit")
 
@@ -172,6 +173,9 @@ static const GtkActionEntry display_entries[] =
     { "ViewCloneview", NULL, N_("C_lone View"), NULL, NULL, G_CALLBACK (view_clone_view_callback) },
     { "ViewRedraw", GTK_STOCK_REFRESH, NULL, NULL, NULL, G_CALLBACK (view_redraw_callback) },
 
+    { "ViewGuides", NULL, N_("Guides"), NULL, NULL, NULL },
+      { "ViewNewguide", NULL, N_("New Guide..."), NULL, NULL, G_CALLBACK (view_new_guide_callback) },
+
   { "Objects", NULL, N_("_Objects"), NULL, NULL },
     { "ObjectsSendtoback", GTK_STOCK_GOTO_BOTTOM, N_("Send to _Back"), FIRST_MODIFIER "<shift>B", N_("Move 
selection to the bottom"), G_CALLBACK (objects_place_under_callback) },
     { "ObjectsBringtofront", GTK_STOCK_GOTO_TOP, N_("Bring to _Front"), FIRST_MODIFIER "<shift>F", N_("Move 
selection to the top"), G_CALLBACK (objects_place_over_callback) },
@@ -255,6 +259,9 @@ static const GtkToggleActionEntry display_toggle_entries[] =
     { "ViewAntialiased", NULL, N_("_Antialiased"), NULL, NULL, G_CALLBACK (view_aa_callback) },
     { "ViewShowgrid", NULL, N_("Show _Grid"), NULL, NULL, G_CALLBACK (view_visible_grid_callback) },
     { "ViewSnaptogrid", NULL, N_("_Snap to Grid"), NULL, NULL, G_CALLBACK (view_snap_to_grid_callback) },
+    { "ViewShowguides", NULL, N_("Show Guides"), NULL, NULL, G_CALLBACK (view_visible_guides_callback) },
+    { "ViewSnaptoguides", NULL, N_("Snap to Guides"), NULL, NULL, G_CALLBACK (view_snap_to_guides_callback) 
},
+    { "ViewRemoveallguides", NULL, N_("Remove all Guides"), NULL, NULL, G_CALLBACK 
(view_remove_all_guides_callback) },
     { "ViewSnaptoobjects", NULL, N_("Snap to _Objects"), NULL, NULL, G_CALLBACK 
(view_snap_to_objects_callback) },
     { "ViewShowrulers", NULL, N_("Show _Rulers"), NULL, NULL, G_CALLBACK (view_toggle_rulers_callback)  },
     { "ViewShowscrollbars", NULL, N_("Show Scrollbars"), NULL, N_("Show or hide the toolbar"), G_CALLBACK 
(view_toggle_scrollbars_callback) },
@@ -355,6 +362,24 @@ integrated_ui_toolbar_object_snap_synchronize_to_display (gpointer param)
   }
 }
 
+
+/**
+ * Synchronized the snap-to-guide property button with the display.
+ * @param param Display to synchronize to.
+ */
+void
+integrated_ui_toolbar_guides_snap_synchronize_to_display (gpointer param)
+{
+  DDisplay *ddisp = param;
+  if (ddisp && ddisp->common_toolbar) {
+    GtkToggleButton *b = g_object_get_data (G_OBJECT (ddisp->common_toolbar),
+                                            DIA_INTEGRATED_TOOLBAR_GUIDES_SNAP);
+    gboolean active = ddisp->guides_snap? TRUE : FALSE;
+    gtk_toggle_button_set_active (b, active);
+  }
+}
+
+
 /**
  * Sets the Object-snap property for the active display.
  * @param b Object snap toggle button.
@@ -370,6 +395,22 @@ integrated_ui_toolbar_object_snap_toggle (GtkToggleButton *b,
   }
 }
 
+
+/**
+ * Sets the Guide-snap property for the active display.
+ * @param b Guide snap toggle button.
+ * @param not_used
+ */
+static void
+integrated_ui_toolbar_guide_snap_toggle(GtkToggleButton *b, gpointer *not_used)
+{
+  DDisplay *ddisp = ddisplay_active ();
+  if (ddisp) {
+    ddisplay_set_snap_to_guides (ddisp, gtk_toggle_button_get_active (b));
+  }
+}
+
+
 /**
  * Synchronizes the Snap-to-grid property button with the display.
  * @param param Display to synchronize to.
@@ -607,6 +648,19 @@ create_integrated_ui_toolbar (void)
                      w);
   integrated_ui_toolbar_add_custom_item (toolbar, w);
 
+  /* Guide Snapping */
+  w = dia_toggle_button_new_with_icon_names ("dia-guides-snap-on",
+                                             "dia-guides-snap-off");
+  g_signal_connect (G_OBJECT (w),
+                    "toggled",
+                    G_CALLBACK (integrated_ui_toolbar_guide_snap_toggle),
+                    toolbar);
+  gtk_widget_set_tooltip_text (w, _("Toggles guide snapping."));
+  g_object_set_data (G_OBJECT (toolbar),
+                     DIA_INTEGRATED_TOOLBAR_GUIDES_SNAP,
+                     w);
+  integrated_ui_toolbar_add_custom_item (toolbar, w);
+
   sep = gtk_separator_tool_item_new ();
   gtk_toolbar_insert (toolbar, sep, -1);
   gtk_widget_show (GTK_WIDGET (sep));
diff --git a/app/menus.h b/app/menus.h
index b8856043..ccaea805 100644
--- a/app/menus.h
+++ b/app/menus.h
@@ -33,6 +33,7 @@ void            integrated_ui_toolbar_set_zoom_text                      (GtkToo
                                                                           const gchar *text);
 void            integrated_ui_toolbar_grid_snap_synchronize_to_display   (gpointer     ddisp);
 void            integrated_ui_toolbar_object_snap_synchronize_to_display (gpointer     ddisp);
+void            integrated_ui_toolbar_guides_snap_synchronize_to_display (gpointer     ddisp);
 
 /* TODO: rename: menus_get_integrated_ui_menubar() */
 void            menus_get_integrated_ui_menubar  (GtkWidget      **menubar,
diff --git a/app/meson.build b/app/meson.build
index 3dd80d71..308a440a 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -22,6 +22,11 @@ dia_sources = [
     'layer-editor/layer_dialog.c',
     'layer-editor/layer_dialog.h',
 
+    'guide_tool.c',
+    'guide_tool.h',
+    'new_guide_dialog.c',
+    'new_guide_dialog.h',
+
     'commands.c',
     'app_procs.c',
     'connectionpoint_ops.c',
diff --git a/app/modify_tool.c b/app/modify_tool.c
index 0862bc31..1f218250 100644
--- a/app/modify_tool.c
+++ b/app/modify_tool.c
@@ -42,6 +42,7 @@
 #include "prop_text.h"
 #include "object.h"
 
+#include "guide_tool.h"
 
 static DiaObject *click_select_object(DDisplay *ddisp, Point *clickedpoint,
                                   GdkEventButton *event);
@@ -79,6 +80,9 @@ struct _ModifyTool {
                              modify_motion was called */
   /* Undo info: */
   Point *orig_pos;
+
+  /* Guide info: */
+  Guide *guide;
 };
 
 
@@ -257,6 +261,11 @@ static int do_if_clicked_handle(DDisplay *ddisp, ModifyTool *tool,
   return FALSE;
 }
 
+
+#define  FUNSCALEX(s,x)   ((x) / (s)->zoom_factor)
+#define  FUNSCALEY(s,y)   ((y) / (s)->zoom_factor)
+
+
 static void
 modify_button_press(ModifyTool *tool, GdkEventButton *event,
                     DDisplay *ddisp)
@@ -264,11 +273,15 @@ modify_button_press(ModifyTool *tool, GdkEventButton *event,
   Point clickedpoint;
   DiaObject *clicked_obj;
   gboolean some_selected;
+  Guide *guide;
+  const gint pick_guide_snap_distance = 20;    /* Margin of error for selecting a guide. */
 
   ddisplay_untransform_coords(ddisp,
                              (int)event->x, (int)event->y,
                              &clickedpoint.x, &clickedpoint.y);
 
+  tool->guide = NULL;
+
   /* don't got to single handle movement if there is more than one object selected */
   some_selected = g_list_length (ddisp->diagram->data->selected) > 1;
   if (!some_selected && do_if_clicked_handle(ddisp, tool, &clickedpoint, event))
@@ -292,6 +305,21 @@ modify_button_press(ModifyTool *tool, GdkEventButton *event,
     tool->start_time = time_micro();
     ddisplay_set_all_cursor_name (NULL, "move");
   } else {
+    /* If there is a guide nearby, then drag it.
+     * Note: We can only drag guides if they are visible (like in GIMP). */
+    if (ddisp->guides_visible) {
+      guide = diagram_pick_guide (ddisp->diagram, clickedpoint.x, clickedpoint.y,
+      FUNSCALEX (ddisp, pick_guide_snap_distance ),
+      FUNSCALEY (ddisp, pick_guide_snap_distance ));
+
+      if (guide) {
+        tool->guide = guide;
+        guide_tool_start_edit (ddisp, guide);
+        return;
+      }
+    }
+
+    /* Box select. */
     tool->state = STATE_BOX_SELECT;
     tool->start_box = clickedpoint;
     tool->end_box = clickedpoint;
diff --git a/app/new_guide_dialog.c b/app/new_guide_dialog.c
new file mode 100644
index 00000000..d38e81cb
--- /dev/null
+++ b/app/new_guide_dialog.c
@@ -0,0 +1,149 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "intl.h"
+#include "new_guide_dialog.h"
+#include "object_ops.h"
+#include "object.h"
+#include "connectionpoint_ops.h"
+#include "undo.h"
+#include "message.h"
+#include "properties.h"
+#include "diaoptionmenu.h"
+
+static GtkWidget *dialog = NULL;
+static GtkWidget *position_entry, *orientation_menu;
+static Diagram *current_diagram = NULL;
+
+static void
+diagram_new_guide_respond(GtkWidget *widget,
+                         gint       response_id,
+                         gpointer   user_data)
+{
+  if (response_id == GTK_RESPONSE_OK) {
+    real position = gtk_spin_button_get_value( GTK_SPIN_BUTTON(position_entry) );
+    int orientation = dia_option_menu_get_active(orientation_menu);
+    diagram_add_guide(current_diagram, position, orientation, TRUE);
+  }
+
+  /* Hide the dialog if "OK" or "Cancel" were clicked. */
+  if (response_id != GTK_RESPONSE_APPLY)
+    gtk_widget_hide(dialog);
+}
+
+static void
+create_new_guide_dialog(Diagram *dia)
+{
+  GtkWidget *dialog_vbox;
+  GtkWidget *label;
+  GtkAdjustment *adj;
+  GtkWidget *table;
+  const gdouble UPPER_LIMIT = G_MAXDOUBLE;
+
+  current_diagram = dia;
+
+  dialog = gtk_dialog_new_with_buttons(
+             _("Add New Guide"),
+             GTK_WINDOW(ddisplay_active()->shell),
+             GTK_DIALOG_DESTROY_WITH_PARENT,
+             GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
+             GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
+             GTK_STOCK_OK, GTK_RESPONSE_OK,
+             NULL);
+
+  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+  dialog_vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+  gtk_window_set_role(GTK_WINDOW(dialog), "new_guide");
+
+  g_signal_connect(G_OBJECT(dialog), "response",
+                  G_CALLBACK(diagram_new_guide_respond), NULL);
+
+  g_signal_connect(G_OBJECT(dialog), "delete_event",
+                  G_CALLBACK(gtk_widget_hide), NULL);
+  g_signal_connect(G_OBJECT(dialog), "destroy",
+                  G_CALLBACK(gtk_widget_destroyed), &dialog);
+
+  table = gtk_table_new(3,3,FALSE);
+  gtk_container_set_border_width(GTK_CONTAINER(table), 2);
+  gtk_table_set_row_spacings(GTK_TABLE(table), 1);
+  gtk_table_set_col_spacings(GTK_TABLE(table), 2);
+
+  label = gtk_label_new(_("Orientation: "));
+  gtk_table_attach(GTK_TABLE(table), label, 0,1, 0,1,
+                  GTK_FILL, GTK_FILL, 0, 0);
+  gtk_widget_show(label);
+
+  orientation_menu = dia_option_menu_new();
+  gtk_table_attach(GTK_TABLE(table), orientation_menu, 1,2, 0,1,
+                  GTK_FILL, GTK_FILL, 0, 0);
+  dia_option_menu_add_item (orientation_menu, "Horizontal", GTK_ORIENTATION_HORIZONTAL);
+  dia_option_menu_add_item (orientation_menu, "Vertical", GTK_ORIENTATION_VERTICAL);
+  dia_option_menu_set_active (orientation_menu, GTK_ORIENTATION_HORIZONTAL);
+  gtk_widget_show(orientation_menu);
+
+  label = gtk_label_new(_("Position: "));
+  gtk_table_attach(GTK_TABLE(table), label, 0,1, 1,2,
+                  GTK_FILL, GTK_FILL, 0, 0);
+  gtk_widget_show(label);
+
+  adj = GTK_ADJUSTMENT(gtk_adjustment_new(1.0, 0.0, UPPER_LIMIT, 0.1, 10.0, 0));
+  position_entry = gtk_spin_button_new(adj, 1.0, 3);
+  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(position_entry), TRUE);
+  gtk_table_attach(GTK_TABLE(table), position_entry, 1,2, 1,2,
+                  GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);
+  gtk_widget_show(position_entry);
+
+  gtk_widget_show(table);
+
+  gtk_box_pack_start(GTK_BOX(dialog_vbox), table, TRUE, TRUE, 0);
+  gtk_widget_show(dialog_vbox);
+}
+
+void
+dialog_new_guide_show(void)
+{
+  Diagram *dia;
+  dia = ddisplay_active_diagram();
+
+  if(!dia)
+    return;
+
+  if (dialog) {
+    /* This makes the dialog a child of the newer diagram */
+    gtk_widget_destroy(dialog);
+    dialog = NULL;
+  }
+
+  create_new_guide_dialog(dia);
+
+  gtk_window_set_transient_for(GTK_WINDOW(dialog),
+                              GTK_WINDOW (ddisplay_active()->shell));
+  gtk_widget_show(dialog);
+}
diff --git a/app/new_guide_dialog.h b/app/new_guide_dialog.h
new file mode 100644
index 00000000..e8799b76
--- /dev/null
+++ b/app/new_guide_dialog.h
@@ -0,0 +1,26 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef NEW_GUIDE_DIALOG_H
+#define NEW_GUIDE_DIALOG_H
+
+#include "diatypes.h"
+#include "diagram.h"
+
+void dialog_new_guide_show (void);
+
+#endif /* NEW_GUIDE_DIALOG_H */
diff --git a/app/preferences.c b/app/preferences.c
index 34183447..191edf97 100644
--- a/app/preferences.c
+++ b/app/preferences.c
@@ -93,9 +93,11 @@ static int default_undo_depth = 15;
 static guint default_recent_documents = 5;
 static Color default_colour = DEFAULT_GRID_COLOR;
 static Color pbreak_colour = DEFAULT_PAGEBREAK_COLOR;
+static Color guide_colour = DEFAULT_GUIDE_COLOR;
 static const gchar *default_paper_name = NULL;
 static const gchar *default_length_unit = "Centimeter";
 static const gchar *default_fontsize_unit = "Point";
+static guint default_snap_distance = 10;
 
 static const char *default_favored_filter = N_("any");
 
@@ -184,6 +186,8 @@ DiaPrefData prefs_data[] =
   { "fontsize_unit", PREF_CHOICE, PREF_OFFSET(fontsize_unit),
     &default_fontsize_unit, UI_TAB, N_("Font size unit:"), NULL, FALSE,
     _get_units_name_list, update_internal_prefs },
+  { "snap_distance", PREF_UINT, PREF_OFFSET(snap_distance),
+    &default_snap_distance, 0, N_("Guide snapping distance:") },
 
   { NULL, PREF_NONE, 0, NULL, DIA_TAB, N_("New diagram:") },
   { "is_portrait", PREF_BOOLEAN, PREF_OFFSET(new_diagram.is_portrait), &default_true, DIA_TAB, 
N_("Portrait") },
@@ -216,6 +220,12 @@ DiaPrefData prefs_data[] =
   { "view_antialiased", PREF_BOOLEAN, PREF_OFFSET(view_antialiased), &default_false, VIEW_TAB, N_("view 
antialiased") },
   { NULL, PREF_END_GROUP, 0, NULL, VIEW_TAB, NULL },
 
+  { NULL, PREF_NONE, 0, NULL, VIEW_TAB, N_("Guides:") },
+  { "show_guides", PREF_BOOLEAN, PREF_OFFSET(guides_visible), &default_true, VIEW_TAB, N_("Visible") },
+  { "snap_to_guides", PREF_BOOLEAN, PREF_OFFSET(guides_snap), &default_true, VIEW_TAB, N_("Snap to guides") 
},
+  { "guide_colour", PREF_COLOUR, PREF_OFFSET(new_diagram.guide_color), &guide_colour, VIEW_TAB, N_("Color:") 
},
+  { NULL, PREF_END_GROUP, 0, NULL, VIEW_TAB, NULL },
+
   /* Favored Filter */
   { NULL, PREF_NONE, 0, NULL, FAVOR_TAB, N_("Export") },
   { "favored_png_export", PREF_CHOICE, PREF_OFFSET(favored_filter.png), &default_favored_filter,
diff --git a/app/preferences.h b/app/preferences.h
index 663a208c..b6f32b7c 100644
--- a/app/preferences.h
+++ b/app/preferences.h
@@ -23,6 +23,7 @@
 
 #define DEFAULT_GRID_COLOR { 0.85, .90, .90, 1.0 }
 #define DEFAULT_PAGEBREAK_COLOR { 0.0, 0.0, 0.6, 1.0 }
+#define DEFAULT_GUIDE_COLOR { 0.0, 1.0, 0.0, 1.0 }
 
 struct DiaPreferences {
   struct {
@@ -79,6 +80,10 @@ struct DiaPreferences {
     char *emf;
     char *print;
   } favored_filter;
+
+  int guides_visible;   /** Whether guides are visible. */
+  int guides_snap;      /** Whether to snap to guides. */
+  guint snap_distance;  /** The snapping distance for guides. */
 };
 
 extern struct DiaPreferences prefs;
diff --git a/app/ruler.c b/app/ruler.c
index 66aaf46c..1a1cb7b5 100644
--- a/app/ruler.c
+++ b/app/ruler.c
@@ -253,7 +253,7 @@ dia_ruler_new (GtkOrientation orientation, GtkWidget *shell, DDisplay *ddisp)
   DIA_RULER(rule)->orientation = orientation;
   DIA_RULER(rule)->ddisp = ddisp;
 
-  gtk_widget_set_events (rule, GDK_EXPOSURE_MASK);
+  gtk_widget_set_events (rule, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | 
GDK_POINTER_MOTION_MASK);
 
   g_signal_connect_swapped (G_OBJECT (shell), "motion_notify_event",
                             G_CALLBACK (dia_ruler_motion_notify),
diff --git a/app/tool.c b/app/tool.c
index 492af30c..8ec24d7f 100644
--- a/app/tool.c
+++ b/app/tool.c
@@ -26,14 +26,15 @@
 #include "interface.h"
 #include "defaults.h"
 #include "object.h"
+#include "guide_tool.h"
 
 Tool *active_tool = NULL;
 Tool *transient_tool = NULL;
 static GtkWidget *active_button = NULL;
 static GtkWidget *former_button = NULL;
 
-void 
-tool_select_former(void) 
+void
+tool_select_former(void)
 {
   if (former_button) {
     g_signal_emit_by_name(G_OBJECT(former_button), "clicked",
@@ -92,13 +93,16 @@ tool_free(Tool *tool)
   case TEXTEDIT_TOOL :
     free_textedit_tool(tool);
     break;
+  case GUIDE_TOOL:
+    free_guide_tool(tool);
+    break;
   default:
-    g_assert(0);    
+    g_assert(0);
   }
 }
 
-void 
-tool_select(ToolType type, gpointer extra_data, 
+void
+tool_select(ToolType type, gpointer extra_data,
             gpointer user_data, GtkWidget *button,
             int invert_persistence)
 {
@@ -124,21 +128,26 @@ tool_select(ToolType type, gpointer extra_data,
   case TEXTEDIT_TOOL :
     active_tool = create_textedit_tool();
     break;
+  case GUIDE_TOOL :
+    active_tool = create_guide_tool ();
+    guide_tool_set_guide (active_tool, extra_data);
+    guide_tool_set_orientation (active_tool, GPOINTER_TO_INT(user_data));
+    break;
   default:
-    g_assert(0);    
+    g_assert(0);
   }
   if (button)
     active_button = button;
 }
 
 void
-tool_options_dialog_show(ToolType type, gpointer extra_data, 
+tool_options_dialog_show(ToolType type, gpointer extra_data,
                         gpointer user_data, GtkWidget *button,
-                         int invert_persistence) 
+                         int invert_persistence)
 {
   DiaObjectType *objtype;
 
-  if (active_tool->type != type) 
+  if (active_tool->type != type)
     tool_select(type,extra_data,user_data,button,invert_persistence);
 
   switch(type) {
@@ -154,5 +163,7 @@ tool_options_dialog_show(ToolType type, gpointer extra_data,
     break;
   case TEXTEDIT_TOOL :
     break;
+  case GUIDE_TOOL :
+    break;
   }
 }
diff --git a/app/tool.h b/app/tool.h
index 174eda1a..e2a90104 100644
--- a/app/tool.h
+++ b/app/tool.h
@@ -38,12 +38,13 @@ enum _ToolType {
   MAGNIFY_TOOL,
   MODIFY_TOOL,
   SCROLL_TOOL,
-  TEXTEDIT_TOOL
+  TEXTEDIT_TOOL,
+  GUIDE_TOOL,
 };
 
 struct _Tool {
   ToolType type;
-  
+
   /*  Action functions  */
   ButtonPressFunc    button_press_func;
   ButtonHoldFunc     button_hold_func;
@@ -69,7 +70,7 @@ void tool_select(ToolType type, gpointer extra_data, gpointer user_date,
                  GtkWidget *button, int invert_persistence);
 void tool_select_former(void);
 void tool_reset(void);
-void tool_options_dialog_show(ToolType type, gpointer extra_data, 
+void tool_options_dialog_show(ToolType type, gpointer extra_data,
                              gpointer user_data,GtkWidget *button,
                               int invert_persistence);
 
diff --git a/app/undo.c b/app/undo.c
index 0e035025..c714541b 100644
--- a/app/undo.c
+++ b/app/undo.c
@@ -1642,3 +1642,229 @@ dia_mem_swap_change_new (Diagram *dia, gpointer dest, gsize size)
 
   return DIA_CHANGE (change);
 }
+
+
+
+struct _DiaMoveGuideChange {
+  DiaChange parent;
+
+  real orig_pos;
+  real dest_pos;
+  Guide *guide;
+};
+
+DIA_DEFINE_CHANGE (DiaMoveGuideChange, dia_move_guide_change)
+
+
+static void
+dia_move_guide_change_apply (DiaChange *self, Diagram *dia)
+{
+  DiaMoveGuideChange *change = DIA_MOVE_GUIDE_CHANGE (self);
+
+  change->guide->position = change->dest_pos;
+
+  /* Force redraw. */
+  diagram_add_update_all (dia);
+  diagram_modified (dia);
+  diagram_flush (dia);
+}
+
+
+static void
+dia_move_guide_change_revert (DiaChange *self, Diagram *dia)
+{
+  DiaMoveGuideChange *change = DIA_MOVE_GUIDE_CHANGE (self);
+
+  change->guide->position = change->orig_pos;
+
+  /* Force redraw. */
+  diagram_add_update_all (dia);
+  diagram_modified (dia);
+  diagram_flush (dia);
+}
+
+
+static void
+dia_move_guide_change_free (DiaChange *change)
+{
+  /* Nothing to free. */
+}
+
+
+DiaChange *
+dia_move_guide_change_new (Diagram *dia, Guide *guide, real orig_pos, real dest_pos)
+{
+  DiaMoveGuideChange *change = dia_change_new (DIA_TYPE_MOVE_GUIDE_CHANGE);
+
+  change->orig_pos = orig_pos;
+  change->dest_pos = dest_pos;
+  change->guide = guide;
+
+  undo_push_change (dia->undo, DIA_CHANGE (change));
+
+  return DIA_CHANGE (change);
+}
+
+
+
+struct _DiaAddGuideChange {
+  DiaChange parent;
+
+  Guide *guide;
+  int applied;
+};
+
+DIA_DEFINE_CHANGE (DiaAddGuideChange, dia_add_guide_change)
+
+
+static void
+dia_add_guide_change_apply (DiaChange *self, Diagram *dia)
+{
+  DiaAddGuideChange *change = DIA_ADD_GUIDE_CHANGE (self);
+  Guide *new_guide;
+
+  g_debug ("add_guide_apply()");
+
+  new_guide = diagram_add_guide (dia, change->guide->position, change->guide->orientation, FALSE);
+  g_free (change->guide);
+  change->guide = new_guide;
+
+  /* Force redraw. */
+  diagram_add_update_all (dia);
+  diagram_modified (dia);
+  diagram_flush (dia);
+
+  /* Set flag. */
+  change->applied = 1;
+}
+
+
+static void
+dia_add_guide_change_revert (DiaChange *self, Diagram *dia)
+{
+  DiaAddGuideChange *change = DIA_ADD_GUIDE_CHANGE (self);
+
+  g_debug ("add_guide_revert()");
+
+  diagram_remove_guide (dia, change->guide, FALSE);
+
+  /* Force redraw. */
+  diagram_add_update_all (dia);
+  diagram_modified (dia);
+  diagram_flush (dia);
+
+  /* Set flag. */
+  change->applied = 0;
+}
+
+
+static void
+dia_add_guide_change_free (DiaChange *self)
+{
+  DiaAddGuideChange *change = DIA_ADD_GUIDE_CHANGE (self);
+
+  g_debug ("add_guide_free()");
+
+  if (!change->applied) {
+    g_free (change->guide);
+  }
+}
+
+
+DiaChange *
+dia_add_guide_change_new (Diagram *dia, Guide *guide, int applied)
+{
+  DiaAddGuideChange *change = dia_change_new (DIA_TYPE_ADD_GUIDE_CHANGE);
+
+  change->guide = guide;
+  change->applied = applied;
+
+  undo_push_change (dia->undo, DIA_CHANGE (change));
+
+  return DIA_CHANGE (change);
+}
+
+
+
+struct _DiaDeleteGuideChange {
+  DiaChange parent;
+
+  Guide *guide;
+  int applied;
+};
+
+DIA_DEFINE_CHANGE (DiaDeleteGuideChange, dia_delete_guide_change)
+
+
+static void
+dia_delete_guide_change_apply (DiaChange *self, Diagram *dia)
+{
+  DiaDeleteGuideChange *change = DIA_DELETE_GUIDE_CHANGE (self);
+
+  g_debug ("delete_guide_apply()");
+
+  diagram_remove_guide (dia, change->guide, FALSE);
+
+  /* Force redraw. */
+  diagram_add_update_all (dia);
+  diagram_modified (dia);
+  diagram_flush (dia);
+
+  /* Set flag. */
+  change->applied = 1;
+}
+
+
+static void
+dia_delete_guide_change_revert (DiaChange *self, Diagram *dia)
+{
+  DiaDeleteGuideChange *change = DIA_DELETE_GUIDE_CHANGE (self);
+
+  /* Declare variable. */
+  Guide *new_guide;
+
+  /* Log message. */
+  g_debug ("delete_guide_revert()");
+
+  /* Add it again. */
+  new_guide = diagram_add_guide(dia, change->guide->position, change->guide->orientation, FALSE);
+
+  /* Reassign. */
+  g_free(change->guide);
+  change->guide = new_guide;
+
+  /* Force redraw. */
+  diagram_add_update_all(dia);
+  diagram_modified(dia);
+  diagram_flush(dia);
+
+  /* Set flag. */
+  change->applied = 0;
+}
+
+
+static void
+dia_delete_guide_change_free (DiaChange *self)
+{
+  DiaDeleteGuideChange *change = DIA_DELETE_GUIDE_CHANGE (self);
+
+  g_debug ("delete_guide_free()");
+
+  if (change->applied) {
+    g_free (change->guide);
+  }
+}
+
+
+DiaChange *
+dia_delete_guide_change_new (Diagram *dia, Guide *guide, int applied)
+{
+  DiaDeleteGuideChange *change = dia_change_new (DIA_TYPE_DELETE_GUIDE_CHANGE);
+
+  change->guide = guide;
+  change->applied = applied;
+
+  undo_push_change (dia->undo, DIA_CHANGE (change));
+
+  return DIA_CHANGE (change);
+}
diff --git a/app/undo.h b/app/undo.h
index cefcb00f..caad9ec1 100644
--- a/app/undo.h
+++ b/app/undo.h
@@ -22,6 +22,7 @@
 typedef struct _UndoStack UndoStack;
 
 #include "diagram.h"
+#include "guide.h"
 
 #include "dia-change.h"
 
@@ -174,5 +175,29 @@ DiaChange *dia_mem_swap_change_new                     (Diagram   *dia,
                                                         gsize      size);
 
 
+#define DIA_TYPE_MOVE_GUIDE_CHANGE dia_move_guide_change_get_type ()
+G_DECLARE_FINAL_TYPE (DiaMoveGuideChange, dia_move_guide_change, DIA, MOVE_GUIDE_CHANGE, DiaChange)
+
+DiaChange *dia_move_guide_change_new                   (Diagram   *dia,
+                                                        Guide     *guide,
+                                                        real       orig_pos,
+                                                        real       dest_pos);
+
+
+#define DIA_TYPE_ADD_GUIDE_CHANGE dia_add_guide_change_get_type ()
+G_DECLARE_FINAL_TYPE (DiaAddGuideChange, dia_add_guide_change, DIA, ADD_GUIDE_CHANGE, DiaChange)
+
+DiaChange *dia_add_guide_change_new                    (Diagram   *dia,
+                                                        Guide     *guide,
+                                                        int        applied);
+
+
+#define DIA_TYPE_DELETE_GUIDE_CHANGE dia_delete_guide_change_get_type ()
+G_DECLARE_FINAL_TYPE (DiaDeleteGuideChange, dia_delete_guide_change, DIA, DELETE_GUIDE_CHANGE, DiaChange)
+
+DiaChange *dia_delete_guide_change_new                 (Diagram   *dia,
+                                                        Guide     *guide,
+                                                        int        applied);
+
 #endif /* UNDO_H */
 
diff --git a/data/ui/display-ui.xml b/data/ui/display-ui.xml
index 049de3be..37e251d9 100644
--- a/data/ui/display-ui.xml
+++ b/data/ui/display-ui.xml
@@ -69,6 +69,13 @@
                        <menuitem name="ViewCloneview" action="ViewCloneview" />
                        <menuitem name="ViewRedraw" action="ViewRedraw" />
                        <separator name="ViewSep3" />
+                       <menu name="ViewGuides" action="ViewGuides">
+                               <menuitem name="ViewNewguide" action="ViewNewguide" />
+                               <menuitem name="ViewShowguides" action="ViewShowguides" />
+                               <menuitem name="ViewSnaptoguides" action="ViewSnaptoguides" />
+                               <menuitem name="ViewRemoveallguides" action="ViewRemoveallguides" />
+                       </menu>
+                       <separator name="ViewSep4" />
                        <separator name="ViewExtensionStart" />
                </menu>
                <menu name="Layers" action="Layers">
diff --git a/data/ui/integrated-ui.xml b/data/ui/integrated-ui.xml
index 25919ff5..0d0a1771 100644
--- a/data/ui/integrated-ui.xml
+++ b/data/ui/integrated-ui.xml
@@ -83,6 +83,13 @@
                        <menuitem name="ViewShowscrollbars" action="ViewShowscrollbars" />
                        <menuitem name="ViewShowconnectionpoints" action="ViewShowconnectionpoints" />
                        <separator name="ViewSep3" />
+                       <menu name="ViewGuides" action="ViewGuides">
+                               <menuitem name="ViewNewguide" action="ViewNewguide" />
+                               <menuitem name="ViewShowguides" action="ViewShowguides" />
+                               <menuitem name="ViewSnaptoguides" action="ViewSnaptoguides" />
+                               <menuitem name="ViewRemoveallguides" action="ViewRemoveallguides" />
+                       </menu>
+                       <separator name="ViewSep4" />
                        <menuitem name="ViewNewview" action="ViewNewview" />
                        <menuitem name="ViewCloneview" action="ViewCloneview" />
                        <menuitem name="ViewRedraw" action="ViewRedraw" />
diff --git a/data/ui/popup-ui.xml b/data/ui/popup-ui.xml
index 4a4b297e..71a15f31 100644
--- a/data/ui/popup-ui.xml
+++ b/data/ui/popup-ui.xml
@@ -64,11 +64,18 @@
                        <menuitem name="ViewShowrulers" action="ViewShowrulers" />
                        <menuitem name="ViewShowconnectionpoints" action="ViewShowconnectionpoints" />
                        <separator name="ViewSep2" />
+                       <menu name="ViewGuides" action="ViewGuides">
+                               <menuitem name="ViewNewguide" action="ViewNewguide" />
+                               <menuitem name="ViewShowguides" action="ViewShowguides" />
+                               <menuitem name="ViewSnaptoguides" action="ViewSnaptoguides" />
+                               <menuitem name="ViewRemoveallguides" action="ViewRemoveallguides" />
+                       </menu>
+                       <separator name="ViewSep3" />
                        <menuitem name="ViewNewview" action="ViewNewview" />
                        <menuitem name="ViewCloneview" action="ViewCloneview" />
                        <menuitem name="ViewShowall" action="ViewShowall" />
                        <menuitem name="ViewRedraw" action="ViewRedraw" />
-                       <separator name="ViewSep3" />
+                       <separator name="ViewSep4" />
                        <separator name="ViewExtensionStart" />
                </menu>
                <menu name="Layers" action="Layers">
diff --git a/data/ui/properties-dialog.ui b/data/ui/properties-dialog.ui
index 2b31becd..16ddb820 100644
--- a/data/ui/properties-dialog.ui
+++ b/data/ui/properties-dialog.ui
@@ -320,7 +320,7 @@
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="border_width">6</property>
-        <property name="n_rows">3</property>
+        <property name="n_rows">4</property>
         <property name="n_columns">2</property>
         <property name="column_spacing">6</property>
         <property name="row_spacing">6</property>
@@ -361,6 +361,19 @@
             <property name="y_options"/>
           </packing>
         </child>
+        <child>
+          <object class="GtkLabel" id="label12">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Guides</property>
+          </object>
+          <packing>
+            <property name="top_attach">3</property>
+            <property name="bottom_attach">4</property>
+            <property name="y_options"/>
+          </packing>
+        </child>
         <child>
           <object class="DiaColorSelector" id="background">
             <property name="visible">True</property>
@@ -401,6 +414,20 @@
             <property name="y_options"/>
           </packing>
         </child>
+        <child>
+          <object class="DiaColorSelector" id="guide_lines">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="top_attach">3</property>
+            <property name="bottom_attach">4</property>
+            <property name="y_options"/>
+          </packing>
+        </child>
       </object>
       <packing>
         <property name="position">1</property>
diff --git a/lib/diagramdata.h b/lib/diagramdata.h
index ab35f054..0bd3fbaf 100644
--- a/lib/diagramdata.h
+++ b/lib/diagramdata.h
@@ -40,7 +40,7 @@ struct _NewDiagramData {
   gfloat scaling;
   gboolean fitto;
   gint fitwidth, fitheight;
-  Color bg_color, pagebreak_color, grid_color;
+  Color bg_color, pagebreak_color, grid_color, guide_color;
   int compress_save;
   gchar *unit, *font_unit;
 };
diff --git a/lib/guide.c b/lib/guide.c
new file mode 100644
index 00000000..38609538
--- /dev/null
+++ b/lib/guide.c
@@ -0,0 +1 @@
+#include "guide.h"
diff --git a/lib/guide.h b/lib/guide.h
new file mode 100644
index 00000000..1be5669d
--- /dev/null
+++ b/lib/guide.h
@@ -0,0 +1,31 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef GUIDE_H
+#define GUIDE_H
+
+#include "diatypes.h"
+#include <gtk/gtk.h>
+
+typedef struct _Guide Guide;
+
+struct _Guide {
+  real position;
+  GtkOrientation orientation;
+};
+
+#endif /* GUIDE_H */
diff --git a/lib/meson.build b/lib/meson.build
index 3b1eac81..19519980 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -136,6 +136,8 @@ libdia_sources = stdprop_sources + [
     'dia_svg.c',
     'autoroute.c',
     'parent.c',
+    'guide.c',
+    'guide.h',
     'diaarrowchooser.c',
     'diaarrowselector.c',
     'diacolorselector.c',
diff --git a/plug-ins/python/pydia-diagramdata.c b/plug-ins/python/pydia-diagramdata.c
index cbb25748..90915c37 100644
--- a/plug-ins/python/pydia-diagramdata.c
+++ b/plug-ins/python/pydia-diagramdata.c
@@ -400,28 +400,14 @@ PyDiaDiagramData_GetAttr(PyDiaDiagramData *self, gchar *attr)
     } else {
       /* In the interactive case diagramdata is_a diagram */
       if (DIA_IS_DIAGRAM (self->data)) {
-       Diagram *diagram = DIA_DIAGRAM(self->data);
-       if (diagram) { /* paranoid and helping scan-build */
-         if (!strcmp(attr, "grid_width"))
-           return Py_BuildValue("(dd)", diagram->grid.width_x, diagram->grid.width_y);
-         else if (!strcmp(attr, "grid_visible"))
-           return Py_BuildValue("(ii)", diagram->grid.visible_x, diagram->grid.visible_y);
-         else if (!strcmp(attr, "hguides")) {
-           int len = diagram->guides.nhguides;
-           PyObject *ret = PyTuple_New(len);
-           int i;
-           for (i = 0; i < len; i++)
-             PyTuple_SetItem(ret, i, PyFloat_FromDouble(diagram->guides.hguides[i]));
-           return ret;
-         } else if (diagram && !strcmp(attr, "vguides")) {
-           int len = diagram->guides.nvguides;
-           PyObject *ret = PyTuple_New(len);
-           int i;
-           for (i = 0; i < len; i++)
-             PyTuple_SetItem(ret, i, PyFloat_FromDouble(diagram->guides.vguides[i]));
-           return ret;
-         }
-       }
+        Diagram *diagram = DIA_DIAGRAM (self->data);
+        if (diagram) { /* paranoid and helping scan-build */
+          if (!strcmp (attr, "grid_width")) {
+            return Py_BuildValue ("(dd)", diagram->grid.width_x, diagram->grid.width_y);
+          } else if (!strcmp (attr, "grid_visible")) {
+            return Py_BuildValue ("(ii)", diagram->grid.visible_x, diagram->grid.visible_y);
+          }
+        }
       }
     }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]