aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--liblogdiag/ld-diagram-view.c213
-rw-r--r--liblogdiag/ld-diagram-view.h7
-rw-r--r--share/gui/window-main.ui4
-rw-r--r--share/logdiag.manifest11
-rw-r--r--share/logdiag.rc2
-rw-r--r--src/ld-window-main.c94
6 files changed, 277 insertions, 54 deletions
diff --git a/liblogdiag/ld-diagram-view.c b/liblogdiag/ld-diagram-view.c
index 23cbb4c..3ff7599 100644
--- a/liblogdiag/ld-diagram-view.c
+++ b/liblogdiag/ld-diagram-view.c
@@ -2,7 +2,7 @@
* ld-diagram-view.c
*
* This file is a part of logdiag.
- * Copyright 2010, 2011, 2012, 2015 Přemysl Eric Janouch
+ * Copyright 2010 - 2021 Přemysl Eric Janouch
*
* See the file LICENSE for licensing information.
*
@@ -335,7 +335,7 @@ static void oper_select_begin (LdDiagramView *self, const LdPoint *point);
static void oper_select_end (LdDiagramView *self);
static void oper_select_get_rectangle (LdDiagramView *self, LdRectangle *rect);
static void oper_select_queue_draw (LdDiagramView *self);
-static void oper_select_draw (GtkWidget *widget, DrawData *data);
+static void oper_select_draw (DrawData *data);
static void oper_select_motion (LdDiagramView *self, const LdPoint *point);
static void oper_move_selection_begin (LdDiagramView *self,
@@ -374,13 +374,19 @@ static void on_drag_leave (GtkWidget *widget, GdkDragContext *drag_ctx,
guint time, gpointer user_data);
static gboolean on_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data);
-static void draw_grid (GtkWidget *widget, DrawData *data);
-static void draw_diagram (GtkWidget *widget, DrawData *data);
-static void draw_terminal (GtkWidget *widget, DrawData *data);
+static void draw_grid (DrawData *data);
+static void draw_diagram (DrawData *data);
+static void draw_terminal (DrawData *data);
static void draw_object (LdDiagramObject *diagram_object, DrawData *data);
static void draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data);
static void draw_connection (LdDiagramConnection *connection, DrawData *data);
+/* Export. */
+
+static void get_diagram_bounds (LdDiagramView *self, LdRectangle *rect);
+static gboolean get_object_bounds (LdDiagramView *self, LdDiagramObject *object,
+ LdRectangle *rect);
+
G_DEFINE_TYPE_WITH_CODE (LdDiagramView, ld_diagram_view, GTK_TYPE_DRAWING_AREA,
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,
@@ -1045,6 +1051,27 @@ ld_diagram_view_diagram_to_widget_coords (LdDiagramView *self,
*wy = scale * (dy - self->priv->y) + 0.5 * allocation.height;
}
+static void
+ld_diagram_view_diagram_to_widget_coords_rect (LdDiagramView *self,
+ const LdRectangle *area, LdRectangle *rect)
+{
+ gdouble x1, x2, y1, y2;
+
+ ld_diagram_view_diagram_to_widget_coords (self,
+ area->x,
+ area->y,
+ &x1, &y1);
+ ld_diagram_view_diagram_to_widget_coords (self,
+ area->x + area->width,
+ area->y + area->height,
+ &x2, &y2);
+
+ rect->x = floor (x1);
+ rect->y = floor (y1);
+ rect->width = ceil (x2) - rect->x;
+ rect->height = ceil (y2) - rect->y;
+}
+
/**
* ld_diagram_view_get_x:
* @self: an #LdDiagramView object.
@@ -1612,14 +1639,12 @@ get_symbol_clip_area (LdDiagramView *self,
}
static gboolean
-get_symbol_area (LdDiagramView *self, LdDiagramSymbol *symbol,
+get_symbol_area_in_diagram_units (LdDiagramView *self, LdDiagramSymbol *symbol,
LdRectangle *rect)
{
gdouble object_x, object_y;
LdSymbol *library_symbol;
LdRectangle area;
- gdouble x1, x2;
- gdouble y1, y2;
gint rotation;
g_object_get (symbol, "x", &object_x, "y", &object_y,
@@ -1633,24 +1658,23 @@ get_symbol_area (LdDiagramView *self, LdDiagramSymbol *symbol,
rotate_symbol_area (&area, rotation);
- ld_diagram_view_diagram_to_widget_coords (self,
- object_x + area.x,
- object_y + area.y,
- &x1, &y1);
- ld_diagram_view_diagram_to_widget_coords (self,
- object_x + area.x + area.width,
- object_y + area.y + area.height,
- &x2, &y2);
+ rect->x = object_x + area.x;
+ rect->y = object_y + area.y;
+ rect->width = (rect->x + area.width) - rect->x;
+ rect->height = (rect->y + area.height) - rect->y;
+ return TRUE;
+}
- x1 = floor (x1);
- y1 = floor (y1);
- x2 = ceil (x2);
- y2 = ceil (y2);
+static gboolean
+get_symbol_area (LdDiagramView *self, LdDiagramSymbol *symbol,
+ LdRectangle *rect)
+{
+ LdRectangle intermediate;
- rect->x = x1;
- rect->y = y1;
- rect->width = x2 - x1;
- rect->height = y2 - y1;
+ if (!get_symbol_area_in_diagram_units (self, symbol, &intermediate))
+ return FALSE;
+
+ ld_diagram_view_diagram_to_widget_coords_rect (self, &intermediate, rect);
return TRUE;
}
@@ -1782,7 +1806,7 @@ get_connection_clip_area (LdDiagramView *self,
}
static gboolean
-get_connection_area (LdDiagramView *self,
+get_connection_area_in_diagram_units (LdDiagramView *self,
LdDiagramConnection *connection, LdRectangle *rect)
{
gdouble x_origin, y_origin;
@@ -1799,20 +1823,13 @@ get_connection_area (LdDiagramView *self,
g_object_get (connection, "x", &x_origin, "y", &y_origin, NULL);
- ld_diagram_view_diagram_to_widget_coords (self,
- x_origin + points->points[0].x,
- y_origin + points->points[0].y,
- &x, &y);
-
- x_max = x_min = x;
- y_max = y_min = y;
+ x_max = x_min = x_origin + points->points[0].x;
+ y_max = y_min = y_origin + points->points[0].y;
for (i = 1; i < points->length; i++)
{
- ld_diagram_view_diagram_to_widget_coords (self,
- x_origin + points->points[i].x,
- y_origin + points->points[i].y,
- &x, &y);
+ x = x_origin + points->points[i].x;
+ y = y_origin + points->points[i].y;
if (x < x_min)
x_min = x;
@@ -1834,6 +1851,19 @@ get_connection_area (LdDiagramView *self,
return TRUE;
}
+static gboolean
+get_connection_area (LdDiagramView *self,
+ LdDiagramConnection *connection, LdRectangle *rect)
+{
+ LdRectangle intermediate;
+
+ if (!get_connection_area_in_diagram_units (self, connection, &intermediate))
+ return FALSE;
+
+ ld_diagram_view_diagram_to_widget_coords_rect (self, &intermediate, rect);
+ return TRUE;
+}
+
/* ===== Operations ======================================================== */
@@ -2101,7 +2131,7 @@ oper_select_queue_draw (LdDiagramView *self)
}
static void
-oper_select_draw (GtkWidget *widget, DrawData *data)
+oper_select_draw (DrawData *data)
{
static const double dashes[] = {3, 5};
SelectData *select_data;
@@ -2664,19 +2694,19 @@ on_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data)
cairo_paint (data.cr);
if (data.self->priv->show_grid)
- draw_grid (widget, &data);
+ draw_grid (&data);
- draw_diagram (widget, &data);
- draw_terminal (widget, &data);
+ draw_diagram (&data);
+ draw_terminal (&data);
if (data.self->priv->operation == OPER_SELECT)
- oper_select_draw (widget, &data);
+ oper_select_draw (&data);
return FALSE;
}
static void
-draw_grid (GtkWidget *widget, DrawData *data)
+draw_grid (DrawData *data)
{
gdouble grid_step;
gint grid_factor;
@@ -2738,7 +2768,7 @@ draw_grid (GtkWidget *widget, DrawData *data)
}
static void
-draw_terminal (GtkWidget *widget, DrawData *data)
+draw_terminal (DrawData *data)
{
LdDiagramViewPrivate *priv;
LdPoint widget_coords;
@@ -2760,7 +2790,7 @@ draw_terminal (GtkWidget *widget, DrawData *data)
}
static void
-draw_diagram (GtkWidget *widget, DrawData *data)
+draw_diagram (DrawData *data)
{
GList *objects, *iter;
@@ -2905,5 +2935,98 @@ draw_connection (LdDiagramConnection *connection, DrawData *data)
draw_connection_end:
ld_point_array_free (points);
- return;
+}
+
+
+/* ===== Export ============================================================ */
+
+static void
+get_diagram_bounds (LdDiagramView *self, LdRectangle *rect)
+{
+ GList *objects;
+ gboolean initialized = FALSE;
+ LdRectangle partial;
+ gdouble x2, y2;
+
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+ g_return_if_fail (rect != NULL);
+
+ memset (rect, 0, sizeof *rect);
+ objects = (GList *) ld_diagram_get_objects (self->priv->diagram);
+ for (; objects != NULL; objects = objects->next)
+ {
+ if (!get_object_bounds (self, objects->data, &partial))
+ continue;
+
+ if (!initialized)
+ {
+ *rect = partial;
+ initialized = TRUE;
+ continue;
+ }
+
+ x2 = MAX (partial.x + partial.width, rect->x + rect->width);
+ y2 = MAX (partial.y + partial.height, rect->y + rect->height);
+ rect->x = MIN (rect->x, partial.x);
+ rect->y = MIN (rect->y, partial.y);
+ rect->width = x2 - rect->x;
+ rect->height = y2 - rect->y;
+ }
+}
+
+static gboolean
+get_object_bounds (LdDiagramView *self, LdDiagramObject *object,
+ LdRectangle *rect)
+{
+ if (LD_IS_DIAGRAM_SYMBOL (object))
+ return get_symbol_area_in_diagram_units (self,
+ LD_DIAGRAM_SYMBOL (object), rect);
+ if (LD_IS_DIAGRAM_CONNECTION (object))
+ return get_connection_area_in_diagram_units (self,
+ LD_DIAGRAM_CONNECTION (object), rect);
+ return FALSE;
+}
+
+/**
+ * ld_diagram_view_get_export_bounds:
+ * @self: an #LdDiagramView object.
+ * @rect: (out): diagram boundaries.
+ *
+ * Get the smallest rectangular area containing all objects in the diagram.
+ * The diagram object itself doesn't have any idea of how symbols are rendered.
+ *
+ * Return value: export units per diagram unit.
+ */
+gdouble
+ld_diagram_view_get_export_bounds (LdDiagramView *self, LdRectangle *rect)
+{
+ LdRectangle intermediate;
+
+ get_diagram_bounds (self, &intermediate);
+ ld_diagram_view_diagram_to_widget_coords_rect (self, &intermediate, rect);
+ return ld_diagram_view_get_scale_in_px (self);
+}
+
+/**
+ * ld_diagram_view_export:
+ * @self: an #LdDiagramView object.
+ * @cr: Cairo context to draw on.
+ * @clip: the clip area (the function itself does not clip).
+ *
+ * Get the smallest rectangular area containing all objects in the diagram.
+ * The diagram object itself doesn't have any idea of how symbols are rendered.
+ */
+void
+ld_diagram_view_export (LdDiagramView *self, cairo_t *cr,
+ const LdRectangle *clip)
+{
+ DrawData data;
+
+ data.cr = cr;
+ data.self = self;
+ /* FIXME: Various functions call this directly, this export is a hack. */
+ data.scale = ld_diagram_view_get_scale_in_px (data.self);
+ data.exposed_rect = *clip;
+
+ draw_diagram (&data);
}
diff --git a/liblogdiag/ld-diagram-view.h b/liblogdiag/ld-diagram-view.h
index d107989..f2b0bf9 100644
--- a/liblogdiag/ld-diagram-view.h
+++ b/liblogdiag/ld-diagram-view.h
@@ -2,7 +2,7 @@
* ld-diagram-view.h
*
* This file is a part of logdiag.
- * Copyright 2010, 2011 Přemysl Eric Janouch
+ * Copyright 2010 - 2021 Přemysl Eric Janouch
*
* See the file LICENSE for licensing information.
*
@@ -96,6 +96,11 @@ void ld_diagram_view_set_show_grid (LdDiagramView *self, gboolean show_grid);
void ld_diagram_view_add_object_begin (LdDiagramView *self,
LdDiagramObject *object);
+gdouble ld_diagram_view_get_export_bounds (LdDiagramView *self,
+ LdRectangle *rect);
+void ld_diagram_view_export (LdDiagramView *self,
+ cairo_t *cr, const LdRectangle *clip);
+
G_END_DECLS
diff --git a/share/gui/window-main.ui b/share/gui/window-main.ui
index 8209cd4..f826698 100644
--- a/share/gui/window-main.ui
+++ b/share/gui/window-main.ui
@@ -6,10 +6,8 @@
<menuitem action="Save" />
<menuitem action="SaveAs" />
<separator />
-<!--
- <menuitem action="Export" />
+ <menuitem action="Print" />
<separator />
--->
<menuitem action="Quit" />
</menu>
<menu action="EditMenu">
diff --git a/share/logdiag.manifest b/share/logdiag.manifest
new file mode 100644
index 0000000..ace298f
--- /dev/null
+++ b/share/logdiag.manifest
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity name="logdiag" version="1.0.0.0" type="win32" />
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0" type="win32" processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df" language="*" />
+ </dependentAssembly>
+ </dependency>
+</assembly>
diff --git a/share/logdiag.rc b/share/logdiag.rc
index 30684e2..cff1571 100644
--- a/share/logdiag.rc
+++ b/share/logdiag.rc
@@ -1 +1,3 @@
+#include <windows.h>
LD_ICON ICON "logdiag.ico"
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "logdiag.manifest"
diff --git a/src/ld-window-main.c b/src/ld-window-main.c
index c68ff3d..6616168 100644
--- a/src/ld-window-main.c
+++ b/src/ld-window-main.c
@@ -106,6 +106,9 @@ static void on_action_new (GtkAction *action, LdWindowMain *self);
static void on_action_open (GtkAction *action, LdWindowMain *self);
static void on_action_save (GtkAction *action, LdWindowMain *self);
static void on_action_save_as (GtkAction *action, LdWindowMain *self);
+static void on_action_print (GtkAction *action, LdWindowMain *self);
+static void on_action_print_draw_page (GtkPrintOperation *operation,
+ GtkPrintContext *context, int page_nr, LdWindowMain *self);
static void on_action_quit (GtkAction *action, LdWindowMain *self);
static void on_action_user_guide (GtkAction *action, LdWindowMain *self);
static void on_action_about (GtkAction *action, LdWindowMain *self);
@@ -146,11 +149,11 @@ static GtkActionEntry wm_action_entries[] =
{"SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), "<Shift><Ctrl>S",
N_("Save the current diagram with another name"),
G_CALLBACK (on_action_save_as)},
-/*
- * {"Export", NULL, N_("_Export"), NULL,
- * N_("Export the diagram"),
- * NULL},
- */
+
+ {"Print", GTK_STOCK_PRINT, N_("_Print"), "<Ctrl>P",
+ N_("Print the diagram"),
+ G_CALLBACK (on_action_print)},
+
{"Quit", GTK_STOCK_QUIT, N_("_Quit"), "<Ctrl>Q",
N_("Quit the application"),
G_CALLBACK (on_action_quit)},
@@ -995,6 +998,87 @@ on_action_save_as (GtkAction *action, LdWindowMain *self)
}
static void
+on_action_print (GtkAction *action, LdWindowMain *self)
+{
+ static GtkPrintSettings *settings = NULL;
+ GError *error = NULL;
+ GtkPrintOperation *print;
+ GtkPrintOperationResult res;
+ gchar *name;
+
+ print = gtk_print_operation_new ();
+ gtk_print_operation_set_n_pages (print, 1);
+ gtk_print_operation_set_embed_page_setup (print, TRUE);
+ gtk_print_operation_set_unit (print, GTK_UNIT_MM);
+
+ name = diagram_get_name (self);
+ gtk_print_operation_set_job_name (print, name);
+ g_free (name);
+
+ if (settings != NULL)
+ gtk_print_operation_set_print_settings (print, settings);
+ g_signal_connect (print, "draw-page",
+ G_CALLBACK (on_action_print_draw_page), self);
+
+ /* On Windows, it is not possible to get a print preview from the system
+ * print dialog. But in Windows XP previews do not work at all--unreadable
+ * EMFs come out. Windows 10 errors out with "A sharing violation occurred
+ * while accessing" the temporary EMF file on our first run of
+ * GtkPrintOperation, and following that it opens the previews up in
+ * fucking Paint, so there is no point in trying. It lacks a stage
+ * or controls for setting up page parameters anyway.
+ */
+ res = gtk_print_operation_run (print,
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ GTK_WINDOW (self), &error);
+ if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
+ {
+ if (settings != NULL)
+ g_object_unref (settings);
+ settings
+ = g_object_ref (gtk_print_operation_get_print_settings (print));
+ }
+ if (error)
+ display_and_free_error (self, _("Error"), error);
+
+ g_object_unref (print);
+}
+
+static void
+on_action_print_draw_page (GtkPrintOperation *operation,
+ GtkPrintContext *context, int page_nr, LdWindowMain *self)
+{
+ cairo_t *cr;
+ LdDiagramView *view;
+ gdouble area_width_mm, area_height_mm;
+ gdouble diagram_width_mm, diagram_height_mm;
+ gdouble diagram_to_export_units, scale;
+ LdRectangle bounds;
+
+ cr = gtk_print_context_get_cairo_context (context);
+ view = self->priv->view;
+
+ area_width_mm = gtk_print_context_get_width (context);
+ area_height_mm = gtk_print_context_get_height (context);
+ diagram_to_export_units = ld_diagram_view_get_export_bounds (view, &bounds);
+
+ /* Scale for the view's constant, measured in milimetres. */
+ scale = 1 / diagram_to_export_units * LD_DIAGRAM_VIEW_BASE_UNIT_LENGTH;
+ diagram_width_mm = bounds.width * scale;
+ diagram_height_mm = bounds.height * scale;
+
+ /* Scale to fit the paper. */
+ if (area_width_mm < diagram_width_mm)
+ scale *= area_width_mm / diagram_width_mm;
+ if (area_height_mm < diagram_height_mm)
+ scale *= area_height_mm / diagram_height_mm;
+
+ cairo_scale (cr, scale, scale);
+ cairo_translate (cr, -bounds.x, -bounds.y);
+ ld_diagram_view_export (view, cr, &bounds);
+}
+
+static void
on_action_quit (GtkAction *action, LdWindowMain *self)
{
if (may_quit (self))