Two patches: Improved print dialog, standard tool shortcuts.
[dia.git] / app / paginate_psprint.c
blob1376deb9cefa770dd558f8a2c030768c49b318eb
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998, 1999 Alexander Larsson
4 * paginate_psprint.[ch] -- pagination code for the postscript backend
5 * Copyright (C) 1999 James Henstridge
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <stdio.h>
27 #include <string.h> /* strlen */
28 #include <signal.h>
29 #include <errno.h>
30 #include <sys/stat.h>
32 #include "intl.h"
33 #include "message.h"
34 #include "diagram.h"
35 #include "diagramdata.h"
36 #include "render_eps.h"
37 #include "diapsrenderer.h"
38 #include "paginate_psprint.h"
39 #include "diapagelayout.h"
40 #include "persistence.h"
42 #include <gtk/gtk.h>
44 #ifdef G_OS_WIN32
45 #include <io.h>
46 #include "win32print.h"
47 #define pclose(file) win32_printer_close (file)
48 #endif
50 /* keep track of print options between prints */
51 typedef struct _dia_print_options {
52 int printer;
53 } dia_print_options;
55 static dia_print_options last_print_options =
60 static void
61 count_objs(DiaObject *obj, DiaRenderer *renderer, int active_layer, guint *nobjs)
63 (*nobjs)++;
66 static guint
67 print_page(DiagramData *data, DiaRenderer *diarend, Rectangle *bounds)
69 DiaPsRenderer *rend = DIA_PS_RENDERER(diarend);
70 guint nobjs = 0;
71 gfloat tmargin = data->paper.tmargin, bmargin = data->paper.bmargin;
72 gfloat lmargin = data->paper.lmargin;
73 gfloat scale = data->paper.scaling;
74 gchar d1_buf[G_ASCII_DTOSTR_BUF_SIZE];
75 gchar d2_buf[G_ASCII_DTOSTR_BUF_SIZE];
77 rend->paper = data->paper.name;
78 rend->is_portrait = data->paper.is_portrait;
80 /* count the number of objects in this region */
81 data_render(data, diarend, bounds,
82 (ObjectRenderer) count_objs, &nobjs);
84 if (nobjs == 0)
85 return nobjs;
87 /* output a page number comment */
88 fprintf(rend->file, "%%%%Page: %d %d\n", rend->pagenum, rend->pagenum);
89 rend->pagenum++;
91 /* save print context */
92 fprintf(rend->file, "gs\n");
94 /* transform coordinate system */
95 if (data->paper.is_portrait) {
96 fprintf(rend->file, "%s %s scale\n",
97 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 28.346457*scale),
98 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -28.346457*scale) );
99 fprintf(rend->file, "%s %s translate\n",
100 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", lmargin/scale - bounds->left),
101 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -bmargin/scale - bounds->bottom) );
102 } else {
103 fprintf(rend->file, "90 rotate\n");
104 fprintf(rend->file, "%s %s scale\n",
105 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 28.346457*scale),
106 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -28.346457*scale) );
107 fprintf(rend->file, "%s %s translate\n",
108 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", lmargin/scale - bounds->left),
109 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", tmargin/scale - bounds->top) );
112 /* set up clip mask */
113 fprintf(rend->file, "n %s %s m ",
114 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
115 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
116 fprintf(rend->file, "%s %s l ",
117 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->right),
118 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
119 fprintf(rend->file, "%s %s l ",
120 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->right),
121 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->bottom) );
122 fprintf(rend->file, "%s %s l ",
123 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
124 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->bottom) );
125 fprintf(rend->file, "%s %s l ",
126 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
127 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
128 /* Tip from Dov Grobgeld: Clip does not destroy the path, so we should
129 do a newpath afterwards */
130 fprintf(rend->file, "clip n\n");
132 /* render the region */
133 data_render(data, diarend, bounds, NULL, NULL);
135 /* restore print context */
136 fprintf(rend->file, "gr\n");
138 /* print the page */
139 fprintf(rend->file, "showpage\n\n");
141 return nobjs;
144 void
145 paginate_psprint(Diagram *dia, FILE *file)
147 DiaRenderer *rend;
148 Rectangle *extents;
149 gfloat width, height;
150 gfloat x, y, initx, inity;
151 guint nobjs = 0;
153 rend = new_psprint_renderer(dia, file);
155 #ifdef DIA_PS_RENDERER_DUAL_PASS
156 /* Prepare the prolog (with fonts etc) */
157 data_render(dia->data, DIA_RENDERER(rend), NULL, NULL, NULL);
158 eps_renderer_prolog_done(rend);
159 #endif
161 /* the usable area of the page */
162 width = dia->data->paper.width;
163 height = dia->data->paper.height;
165 /* get extents, and make them multiples of width / height */
166 extents = &dia->data->extents;
167 initx = extents->left;
168 inity = extents->top;
169 /* make page boundaries align with origin */
170 if (!dia->data->paper.fitto) {
171 initx = floor(initx / width) * width;
172 inity = floor(inity / height) * height;
175 /* iterate through all the pages in the diagram */
176 for (y = inity; y < extents->bottom; y += height) {
177 /* ensure we are not producing pages for epsilon */
178 if ((extents->bottom - y) < 1e-6)
179 break;
180 for (x = initx; x < extents->right; x += width) {
181 Rectangle page_bounds;
183 if ((extents->right - x) < 1e-6)
184 break;
186 page_bounds.left = x;
187 page_bounds.right = x + width;
188 page_bounds.top = y;
189 page_bounds.bottom = y + height;
191 nobjs += print_page(dia->data,rend, &page_bounds);
195 g_object_unref(rend);
199 static void
200 change_entry_state(GtkToggleButton *radio, GtkWidget *entry)
202 gtk_widget_set_sensitive(entry, gtk_toggle_button_get_active(radio));
205 static void
206 ok_pressed(GtkButton *button, gboolean *flag)
208 *flag = TRUE;
209 gtk_main_quit();
212 static gboolean sigpipe_received = FALSE;
213 static void
214 pipe_handler(int signum)
216 sigpipe_received = TRUE;
219 static gboolean
220 diagram_print_destroy(GtkWidget *widget)
222 Diagram *dia;
224 if ((dia = gtk_object_get_user_data(GTK_OBJECT(widget))) != NULL) {
225 g_object_unref(dia);
226 gtk_object_set_user_data(GTK_OBJECT(widget), NULL);
229 return FALSE;
232 void
233 diagram_print_ps(Diagram *dia)
235 GtkWidget *dialog;
236 GtkWidget *vbox, *frame, *table, *box, *button;
237 GtkWidget *iscmd, *isofile;
238 GtkWidget *cmd, *ofile;
239 gboolean cont = FALSE;
240 gchar *printcmd = NULL;
241 gchar *orig_command, *orig_file;
243 FILE *file;
244 gboolean is_pipe;
245 #ifndef G_OS_WIN32
246 /* all the signal stuff below doesn't compile on win32, but it isn't
247 * needed anymore because the pipe handling - which never worked on win32
248 * anyway - is replace by "native" postscript printing now ...
250 void *old_action;
251 #endif
253 /* create the dialog */
254 dialog = gtk_dialog_new();
255 /* the dialog has it's own reference to the diagram */
256 g_object_ref(dia);
257 gtk_object_set_user_data(GTK_OBJECT(dialog), dia);
258 g_signal_connect(GTK_OBJECT(dialog), "destroy",
259 G_CALLBACK(diagram_print_destroy), NULL);
260 g_signal_connect(GTK_OBJECT(dialog), "delete_event",
261 G_CALLBACK(gtk_main_quit), NULL);
262 g_signal_connect(GTK_OBJECT(dialog), "delete_event",
263 G_CALLBACK(gtk_true), NULL);
264 vbox = GTK_DIALOG(dialog)->vbox;
266 frame = gtk_frame_new(_("Select Printer"));
267 gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
268 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
269 gtk_widget_show(frame);
271 table = gtk_table_new(2, 2, FALSE);
272 gtk_container_set_border_width(GTK_CONTAINER(table), 5);
273 gtk_table_set_row_spacings(GTK_TABLE(table), 5);
274 gtk_table_set_col_spacings(GTK_TABLE(table), 5);
275 gtk_container_add(GTK_CONTAINER(frame), table);
276 gtk_widget_show(table);
278 iscmd = gtk_radio_button_new_with_label(NULL, _("Printer"));
279 gtk_table_attach(GTK_TABLE(table), iscmd, 0,1, 0,1,
280 GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0);
281 gtk_widget_show(iscmd);
283 cmd = gtk_entry_new();
284 gtk_table_attach(GTK_TABLE(table), cmd, 1,2, 0,1,
285 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
286 gtk_widget_show(cmd);
288 g_signal_connect(GTK_OBJECT(iscmd), "toggled",
289 G_CALLBACK(change_entry_state), cmd);
291 isofile = gtk_radio_button_new_with_label(GTK_RADIO_BUTTON(iscmd)->group,
292 _("File"));
293 gtk_table_attach(GTK_TABLE(table), isofile, 0,1, 1,2,
294 GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0);
295 gtk_widget_show(isofile);
297 ofile = gtk_entry_new();
298 gtk_widget_set_sensitive(ofile, FALSE);
299 gtk_table_attach(GTK_TABLE(table), ofile, 1,2, 1,2,
300 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
301 gtk_widget_show(ofile);
302 g_signal_connect(GTK_OBJECT(isofile), "toggled",
303 G_CALLBACK(change_entry_state), ofile);
305 box = GTK_DIALOG(dialog)->action_area;
307 button = gtk_button_new_with_label(_("OK"));
308 g_signal_connect(GTK_OBJECT(button), "clicked",
309 G_CALLBACK(ok_pressed), &cont);
310 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
311 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
312 gtk_widget_grab_default(button);
313 gtk_widget_show(button);
315 button = gtk_button_new_with_label(_("Cancel"));
316 g_signal_connect(GTK_OBJECT(button), "clicked",
317 G_CALLBACK(gtk_main_quit), NULL);
318 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
319 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
320 gtk_widget_show(button);
322 /* Set default or old dialog values: */
323 #ifdef G_OS_WIN32
324 gtk_entry_set_text(GTK_ENTRY(cmd), win32_printer_default ());
325 #else
327 const gchar *printer = g_getenv("PRINTER");
329 if (printer) {
330 printcmd = g_strdup_printf("lpr -P%s", printer);
331 } else {
332 printcmd = g_strdup("lpr");
335 gtk_entry_set_text(GTK_ENTRY(cmd), printcmd);
336 g_free(printcmd);
337 printcmd = NULL;
339 #endif
340 persistence_register_string_entry("printer-command", cmd);
341 printcmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd)));
342 orig_command = printcmd;
344 /* Work out diagram filename and use this as default .ps file */
345 char *filename = g_path_get_basename(dia->filename);
346 char *printer_filename = g_malloc(strlen(filename) + 4);
347 printer_filename = strcpy(printer_filename, filename);
348 char *dot = strrchr(printer_filename, '.');
349 if ((dot != NULL) && (strcmp(dot, ".dia") == 0))
350 *dot = '\0';
351 printer_filename = strcat(printer_filename, ".ps");
352 gtk_entry_set_text(GTK_ENTRY(ofile), printer_filename);
353 g_free(printer_filename);
354 orig_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(ofile)));
356 /* Scaling is already set at creation. */
357 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(iscmd), last_print_options.printer);
358 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(isofile), !last_print_options.printer);
360 gtk_widget_show(dialog);
361 gboolean done = FALSE;
362 gboolean write_file = TRUE; /* Used in prompt to overwrite existing file */
363 do {
364 cont = FALSE;
365 write_file = TRUE;
366 gtk_main();
368 if(!dia) {
369 gtk_widget_destroy(dialog);
370 return;
373 if (!cont) {
374 persistence_change_string_entry("printer-command", orig_command, cmd);
375 gtk_widget_destroy(dialog);
376 g_free(orig_command);
377 g_free(orig_file);
378 return;
381 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd))) {
382 printcmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd)));
383 #ifdef G_OS_WIN32
384 file = win32_printer_open (printcmd);
385 #else
386 file = popen(printcmd, "w");
387 #endif
388 is_pipe = TRUE;
389 } else {
390 const gchar *filename = gtk_entry_get_text(GTK_ENTRY(ofile));
391 struct stat statbuf;
393 if (stat(filename, &statbuf) == 0) { /* Output file exists */
394 GtkWidget *confirm_overwrite_dialog = NULL;
395 char buffer[300];
396 char *utf8filename = NULL;
398 if (!g_utf8_validate(filename, -1, NULL)) {
399 utf8filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
401 if (utf8filename == NULL) {
402 message_warning(_("Some characters in the filename are neither "
403 "UTF-8\nnor your local encoding.\nSome things will break."));
407 if (utf8filename == NULL) utf8filename = g_strdup(filename);
408 g_snprintf(buffer, 300,
409 _("The file '%s' already exists.\n"
410 "Do you want to overwrite it?"), utf8filename);
411 g_free(utf8filename);
412 confirm_overwrite_dialog = gtk_message_dialog_new(GTK_WINDOW (dialog),
413 GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
414 GTK_BUTTONS_YES_NO,
415 buffer);
416 gtk_window_set_title(GTK_WINDOW (confirm_overwrite_dialog),
417 _("File already exists"));
418 gtk_dialog_set_default_response (GTK_DIALOG (confirm_overwrite_dialog),
419 GTK_RESPONSE_NO);
421 if (gtk_dialog_run(GTK_DIALOG(confirm_overwrite_dialog))
422 != GTK_RESPONSE_YES) {
423 write_file = FALSE;
424 file = 0;
427 gtk_widget_destroy(confirm_overwrite_dialog);
430 if (write_file) {
431 if (!g_path_is_absolute(filename)) {
432 char *dirname;
433 char *full_filename;
435 dirname = g_path_get_dirname(dia->filename);
436 full_filename = g_build_filename(dirname, filename, NULL);
437 file = fopen(full_filename, "w");
438 g_free(full_filename);
439 g_free(dirname);
440 } else {
441 file = fopen(filename, "w");
445 is_pipe = FALSE;
448 /* Store dialog values */
449 last_print_options.printer = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd));
451 if (write_file) {
452 if (!file) {
453 if (is_pipe) {
454 message_warning(_("Could not run command '%s': %s"), printcmd, strerror(errno));
455 g_free(printcmd);
456 } else
457 message_warning(_("Could not open '%s' for writing: %s"),
458 gtk_entry_get_text(GTK_ENTRY(ofile)), strerror(errno));
459 } else
460 done = TRUE;
462 } while(!done);
464 g_free(orig_command);
465 g_free(orig_file);
466 #ifndef G_OS_WIN32
467 /* set up a SIGPIPE handler to catch IO errors, rather than segfaulting */
468 sigpipe_received = FALSE;
469 old_action = signal(SIGPIPE, pipe_handler);
470 #endif
472 paginate_psprint(dia, file);
473 gtk_widget_destroy(dialog);
474 if (is_pipe) {
475 int exitval = pclose(file);
476 if (exitval != 0) {
477 message_error(_("Printing error: command '%s' returned %d\n"),
478 printcmd, exitval);
480 } else
481 fclose(file);
483 #ifndef G_OS_WIN32
484 /* restore original behaviour */
485 signal(SIGPIPE, old_action);
486 #endif
487 if (sigpipe_received)
488 message_error(_("Printing error: command '%s' caused sigpipe."),
489 printcmd);
491 if (is_pipe) g_free(printcmd);