1 /* Print.c
2 *
3 * printing support for GNU Denemo
4 * outputs to a pdf or png file
5 * for Denemo, a gtk+ frontend to GNU Lilypond
6 * (c) 2001-2005 Adam Tee, 2009, 2010, 2011 Richard Shann
7 */
8
9 #include <stdio.h>
10 #include <math.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <glib/gstdio.h>
16 #include <errno.h>
17 #include <denemo/denemo.h>
18
19 #ifdef HAVE_SYS_WAIT_H
20 #include <sys/wait.h>
21 #endif
22
23 #ifdef HAVE_WAIT_H
24 #include <wait.h>
25 #endif
26
27 #include "export/print.h"
28 #include "printview/printview.h"
29 #include "core/prefops.h"
30 #include "export/exportlilypond.h"
31 #include "core/utils.h"
32
33 gint LilyPond_stderr; //A file descriptor to pipe for LilyPond's stderr
34 GError *lily_err;
35 GPid previewerpid;
36
37 #if GTK_MAJOR_VERSION==3
38 typedef enum
39 {
40 GDK_RGB_DITHER_NONE,
41 GDK_RGB_DITHER_NORMAL,
42 GDK_RGB_DITHER_MAX
43 } GdkRgbDither;
44 #endif
45
46 typedef struct lilyversion
47 {
48 gint major;
49 gint minor;
50 } lilyversion;
51
52 gint LilyPond_stderr = -1; //A file descriptor to pipe for LilyPond's stderr
53 GError *lily_err = NULL;
54
55 GPid previewerpid = GPID_NONE;
56
57
58
59
60 WysiwygInfo*
get_wysiwyg_info()61 get_wysiwyg_info(){
62 static WysiwygInfo Ww; //Wysywyg information
63 return &Ww;
64 }
65
initialize_print_status(void)66 void initialize_print_status (void)
67 {
68
69 Denemo.printstatus = (DenemoPrintInfo*)g_malloc0(sizeof (DenemoPrintInfo));
70 Denemo.printstatus->printpid = GPID_NONE;
71 Denemo.printstatus->typeset_type = TYPESET_ALL_MOVEMENTS;
72 Denemo.printstatus->printbasename[0] = g_build_filename (locateprintdir (), "denemoprintA", NULL);
73 Denemo.printstatus->printbasename[1] = g_build_filename (locateprintdir (), "denemoprintB", NULL);
74 Denemo.printstatus->printname_pdf[0] = g_strconcat (Denemo.printstatus->printbasename[0], ".pdf", NULL);
75 Denemo.printstatus->printname_svg[0] = g_strconcat (Denemo.printstatus->printbasename[0], ".svg", NULL);
76 #ifdef G_OS_WIN32
77 Denemo.printstatus->printname_midi[0] = g_strconcat (Denemo.printstatus->printbasename[0], ".mid", NULL);//LilyPond outputs .mid files for midi
78 #else
79 Denemo.printstatus->printname_midi[0] = g_strconcat (Denemo.printstatus->printbasename[0], ".midi", NULL);
80 #endif
81 Denemo.printstatus->printname_ly[0] = g_strconcat (Denemo.printstatus->printbasename[0], ".ly", NULL);
82 Denemo.printstatus->printname_pdf[1] = g_strconcat (Denemo.printstatus->printbasename[1], ".pdf", NULL);
83 Denemo.printstatus->printname_svg[1] = g_strconcat (Denemo.printstatus->printbasename[1], ".svg", NULL);
84 #ifdef G_OS_WIN32
85 Denemo.printstatus->printname_midi[1] = g_strconcat (Denemo.printstatus->printbasename[1], ".mid", NULL);//LilyPond outputs .mid files for midi
86 #else
87 Denemo.printstatus->printname_midi[1] = g_strconcat (Denemo.printstatus->printbasename[1], ".midi", NULL);
88 #endif
89 Denemo.printstatus->printname_ly[1] = g_strconcat (Denemo.printstatus->printbasename[1], ".ly", NULL);
90 Denemo.printstatus->error_file = NULL;
91 }
92
93
94 static void
advance_printname()95 advance_printname ()
96 {
97
98
99 Denemo.printstatus->cycle = !Denemo.printstatus->cycle;
100 /*gint success =*/ g_unlink (Denemo.printstatus->printname_pdf[Denemo.printstatus->cycle]);
101 g_unlink (Denemo.printstatus->printname_svg[Denemo.printstatus->cycle]);
102
103 //g_debug("Removed old pdf file %s %d\n",Denemo.printstatus->printname_pdf[Denemo.printstatus->cycle], success);
104 }
105
106
107 /***
108 * make sure lilypond is in the path defined in the preferences
109 */
110 /* UNUSED
111 gboolean
112 check_lilypond_path (DenemoProject * gui)
113 {
114
115 gchar *lilypath = g_find_program_in_path (Denemo.prefs.lilypath->str);
116 if (lilypath == NULL)
117 {
118 // show a warning dialog
119 GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window),
120 GTK_DIALOG_DESTROY_WITH_PARENT,
121 GTK_MESSAGE_WARNING,
122 GTK_BUTTONS_OK,
123 _("Could not find %s"),
124 Denemo.prefs.lilypath->str);
125 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Please edit lilypond path " "in the preferences."));
126 gtk_dialog_run (GTK_DIALOG (dialog));
127
128 // free the memory and return
129 gtk_widget_destroy (dialog);
130 return 0;
131 }
132 else
133 return 1;
134 }
135 */
136
137 static int
version_check(lilyversion base,lilyversion installed)138 version_check (lilyversion base, lilyversion installed)
139 {
140 if (base.major > installed.major)
141 return LESSER;
142 if (base.major < installed.major)
143 return GREATER;
144 if (base.minor == installed.minor)
145 return SAME;
146 if (base.minor > installed.minor)
147 return LESSER;
148 if (base.minor < installed.minor)
149 return GREATER;
150
151 /* if none of the above something is wrong */
152 return -1;
153 }
154
155 static lilyversion
string_to_lilyversion(char * string)156 string_to_lilyversion (char *string)
157 {
158 lilyversion version = { 2, 0 };
159 char **token;
160 const char delimiters[] = ".";
161 if (string == NULL || *string == 0)
162 return version;
163 /* split string */
164 token = g_strsplit (string, delimiters, 2);
165
166 /* get major version number */
167 if (token[0])
168 version.major = atoi (token[0]);
169 /* get minor version number */
170 if (token[1])
171 version.minor = atoi (token[1]);
172 g_strfreev (token);
173 //intf("\nstring_to_lilyversion() major = %d minor = %d\n",version.major, version.minor);
174 return version;
175 }
176
177 /* UNUSED
178 static gchar *
179 regex_parse_version_number (const gchar * str)
180 {
181 GRegex *regex = NULL;
182 GMatchInfo *match_info;
183 GString *lilyversion = g_string_new ("");
184
185 regex = g_regex_new ("\\d.\\d\\d", 0, 0, NULL);
186 g_regex_match (regex, str, 0, &match_info);
187
188 if (g_match_info_matches (match_info))
189 {
190 g_string_append (lilyversion, g_match_info_fetch (match_info, 0));
191 }
192
193 g_match_info_free (match_info);
194 g_regex_unref (regex);
195 return g_string_free (lilyversion, FALSE);
196 }
197 */
198
199 gchar *
get_lily_version_string(void)200 get_lily_version_string (void)
201 {
202 GString *ver = Denemo.project->lilycontrol.lilyversion;
203 if (ver && (ver->len > 1))
204 return ver->str;
205 return INSTALLED_LILYPOND_VERSION;
206 }
207
208 int
check_lily_version(gchar * version)209 check_lily_version (gchar * version)
210 {
211 gchar *version_string = get_lily_version_string ();
212 lilyversion installed_version = string_to_lilyversion (version_string);
213 lilyversion check_version = string_to_lilyversion (version);
214 return version_check (check_version, installed_version);
215 }
216
217
218 /* returns the base name (/tmp/Denemo????/denemoprint usually) used as a base
219 filepath for printing.
220 The returned string should not be freed.
221 */
222
223 gchar *
get_printfile_pathbasename(void)224 get_printfile_pathbasename (void)
225 {
226 if (Denemo.printstatus->printbasename[0] == NULL)
227 advance_printname ();
228 return Denemo.printstatus->printbasename[Denemo.printstatus->cycle];
229 }
230
231 /* truncate epoint after 20 lines replacing the last three chars in that case with dots */
232 static void
truncate_lines(gchar * epoint)233 truncate_lines (gchar * epoint)
234 {
235 gint i;
236 for (i = 0; i < 20 && *epoint; i++)
237 {
238 while (*epoint && *epoint != '\n')
239 epoint++;
240 if (*epoint)
241 epoint++;
242 }
243 if (epoint)
244 *epoint-- = '\0';
245 /* replace last three chars with ... This is always possible if epoint is not NULL */
246 if (*epoint)
247 for (i = 3; i > 0; i--)
248 *epoint-- = '.';
249 }
250
251 /***
252 * Run the command line convert-ly to get the lilypond output
253 * current with the version running on the users computer
254 *
255 */
256 /* UNUSED
257 void
258 convert_ly (gchar * lilyfile)
259 {
260 GError *err = NULL;
261 #ifdef G_OS_WIN32
262 gchar *conv_argv[] = {
263 "python" "convert-ly.py",
264 "-e",
265 lilyfile,
266 NULL
267 };
268 #else
269 gchar *conv_argv[] = {
270 "convert-ly",
271 "-e",
272 lilyfile,
273 NULL
274 };
275 #endif
276 g_spawn_sync (locateprintdir (), // dir
277 conv_argv, NULL, // env
278 G_SPAWN_SEARCH_PATH, NULL, // child setup func
279 NULL, // user data
280 NULL, // stdout
281 NULL, // stderr
282 NULL, &err);
283
284 if (err != NULL)
285 {
286 g_warning ("%s", err->message);
287 if (err)
288 g_error_free (err);
289 err = NULL;
290 }
291 }
292 */
293
294 /* look in message for :line:col: where line and col are integers and return point where found or NULL if none*/
get_error_point(gchar * bytes,gint * line,gint * col)295 static gchar * get_error_point (gchar *bytes, gint *line, gint *col)
296 {
297 gchar *epoint;
298 gchar *message = bytes;
299 while (*message)
300 {
301 while (*message && (*message!=':')) message++;
302 if (*message==':')
303 {
304 epoint = message;
305 *line = atoi (message+1);
306 if (*line == 0)
307 {
308 message++;
309 continue;
310 }
311 message++;
312 while (*message && g_ascii_isdigit (*message)) message++;
313
314 if (*message==':')
315 {
316 *col = atoi (message+1);
317 if (*col == 0)
318 {
319 message++;
320 continue;
321 }
322
323 message++;
324 while (*message && g_ascii_isdigit (*message)) message++;
325
326
327 g_print ("%c", *message);
328 if (*message==':')
329 {
330 gchar *colon = epoint;
331 *colon = 0;
332 while ((epoint != bytes) && (*epoint != '\n')) epoint--;//FIXME is epoint now referring to the main file or some include file, line col will not work for an include file
333 if(strcmp (Denemo.printstatus->printname_ly[Denemo.printstatus->cycle], epoint)) // error is in an include file
334 Denemo.printstatus->error_file = g_strdup (epoint);
335 *colon = ':';
336 return epoint;
337 }
338 }
339 else {
340 message++;g_print ("%c", *message);
341 continue;
342 }
343 }
344 else {
345 message++;g_print ("%c", *message);
346 continue;
347 }
348 }
349 return NULL;
350 }
351 void
process_lilypond_errors(gchar * filename)352 process_lilypond_errors (gchar * filename)
353 {
354 Denemo.printstatus->invalid = 0;
355 gchar *logfile = g_strconcat (filename, ".log", NULL);
356 gchar *epoint = NULL;
357
358 gchar *bytes;
359 gint numbytes = g_file_get_contents (logfile, &bytes, NULL, NULL);
360 g_free (logfile);
361 if (bytes)
362 numbytes=strlen (bytes);
363 else
364 return;
365 //g_print("\nLilyPond error messages\n8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8>< %s \n8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><8><\n", bytes);
366 gint line, column;
367 epoint=get_error_point (bytes, &line, &column);
368 if (epoint)
369 {
370 truncate_lines (epoint); /* truncate epoint if it has too many lines */
371 line--; /* make this 0 based */
372 if (line >= gtk_text_buffer_get_line_count (Denemo.textbuffer))
373 warningdialog (_("Spurious line number")), line = 0;
374 /* gchar *errmsg = g_strdup_printf("Error at line %d column %d %d", line,column, cnv); */
375 /* warningdialog(errmsg); */
376 console_output (epoint);
377 if (Denemo.textbuffer)
378 {
379 set_lily_error (line + 1, column);
380 }
381 goto_lilypond_position (line + 1, column);
382 Denemo.printstatus->invalid = 2; //print_is_valid = FALSE;
383 if (Denemo.printarea)
384 gtk_widget_queue_draw (Denemo.printarea);
385 // FIXME this causes a lock-up warningdialog(_("Typesetter detected errors. Cursor is position on the error point.\nIf in doubt delete and re-enter the measure."));
386 }
387 else
388 {
389 // console_output (_("Done"));
390 set_lily_error (0, 0); /* line 0 meaning no line */
391 }
392 highlight_lily_error ();
393 if (lily_err != NULL)
394 {
395 if (*bytes)
396 console_output (bytes);
397 warningdialog (_("Could not execute lilypond - check Edit->preferences → externals → lilypond setting\nand lilypond installation"));
398 g_warning ("%s", lily_err->message);
399 if (lily_err)
400 g_error_free (lily_err);
401 lily_err = NULL;
402 }
403 g_free (bytes);
404 }
405
406 static void
open_viewer(gint status,gchar * filename)407 open_viewer (gint status, gchar * filename)
408 {
409 if (Denemo.printstatus->printpid == GPID_NONE)
410 return;
411 GError *err = NULL;
412 gchar *printfile;
413 gchar **arguments;
414 progressbar_stop ();
415 console_output (_("Done"));
416 g_spawn_close_pid (Denemo.printstatus->printpid);
417 Denemo.printstatus->printpid = GPID_NONE;
418 //normal_cursor();
419 process_lilypond_errors (filename);
420 #if GLIB_CHECK_VERSION(2,34,0)
421 {
422 GError* err = NULL;
423 status = g_spawn_check_exit_status (status, &err);
424 if(!status)
425 g_warning ("Lilypond did not end successfully: %s", err->message);
426 }
427 #endif
428
429 if (status)
430 {
431 g_warning /* a warning dialog causes deadlock in threaded version of program */ ("LilyPond engraver failed - See highlighting in LilyPond window (open the LilyPond window and right click to print)");
432 }
433 else
434 {
435 printfile = g_strconcat (filename, ".png", NULL);
436 if (!g_file_test (printfile, G_FILE_TEST_EXISTS))
437 {
438 //FIXME use filename in message
439 g_warning ("Failed to find %s, check permissions", (gchar *) printfile);
440 g_free (printfile);
441 return;
442 }
443 gchar *png[] = {
444 Denemo.prefs.imageviewer->str,
445 printfile,
446 NULL
447 };
448
449
450 arguments = png;
451
452 if (Denemo.prefs.imageviewer->len == 0)
453 {
454 gboolean ok = run_file_association (printfile);
455 if (!ok)
456 {
457 err = g_error_new (G_FILE_ERROR, -1, "Could not run file assoc for %s", ".png");
458 g_warning ("Could not run the file association for a %s file", ".png");
459 }
460 }
461 else
462 {
463 g_spawn_async_with_pipes (locateprintdir (), /* dir */
464 arguments, NULL, /* env */
465 G_SPAWN_SEARCH_PATH, /* search in path for executable */
466 NULL, /* child setup func */
467 NULL, /* user data */
468 &previewerpid, /* FIXME &pid see g_spawn_close_pid(&pid) */
469 NULL, NULL, NULL, &err);
470 }
471 if (err != NULL)
472 {
473 warningdialog (err->message);
474 g_warning ("%s", err->message);
475 g_error_free (err);
476 err = NULL;
477 }
478 g_free (printfile);
479 }
480 }
481
482 static void
open_pngviewer(G_GNUC_UNUSED GPid pid,gint status,gchar * filename)483 open_pngviewer (G_GNUC_UNUSED GPid pid, gint status, gchar * filename)
484 {
485 open_viewer (status, filename);
486 }
487 static gboolean
call_stop_lilypond(void)488 call_stop_lilypond (void)
489 {
490 progressbar_stop ();
491 stop_lilypond ();
492 return TRUE;
493 }
494
495 static gint
run_lilypond(gchar ** arguments)496 run_lilypond (gchar ** arguments)
497 {
498 gint error = 0;
499 if (Denemo.printstatus->background == STATE_NONE)
500 progressbar (_("Denemo Typesetting"), call_stop_lilypond);
501 if (lily_err)
502 {
503 g_warning ("Old error message from launching lilypond still present - message was %s\nDiscarding...", lily_err->message);
504 g_error_free (lily_err);
505 lily_err = NULL;
506 }
507 console_output (NULL);
508 console_output (_("Typesetting ..."));
509 gboolean lilypond_launch_success = g_spawn_async_with_pipes (locateprintdir (), /* dir */
510 arguments,
511 NULL, /* env */
512 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
513 NULL, /* child setup func */
514 NULL, /* user data */
515 &Denemo.printstatus->printpid,
516 NULL,
517 NULL, /* stdout */
518 NULL, /* stderr */
519 &lily_err);
520 if (lily_err)
521 {
522 g_critical ("Error launching lilypond! Message is %s", lily_err->message);
523 g_error_free (lily_err);
524 lily_err = NULL;
525 error = -1;
526 }
527 if (!lilypond_launch_success)
528 {
529 //g_critical ("Error executing lilypond. Perhaps Lilypond is not installed or its path is not correctly configured. %s", lily_err->message);
530 error = -1;
531 }
532 if (error)
533 progressbar_stop ();
534
535 return error;
536 }
537
538 gboolean
stop_lilypond()539 stop_lilypond ()
540 {
541 if (Denemo.printstatus->printpid != GPID_NONE)
542 {
543 kill_process (Denemo.printstatus->printpid);
544 Denemo.printstatus->printpid = GPID_NONE;
545 }
546 return FALSE; //do not call again
547 }
548
549 static void
generate_lilypond(gchar * lilyfile,gboolean part_only,gboolean all_movements)550 generate_lilypond (gchar * lilyfile, gboolean part_only, gboolean all_movements)
551 {
552 DenemoProject *gui = Denemo.project;
553 if (part_only)
554 export_lilypond_part (lilyfile, gui, all_movements);
555 else
556 exportlilypond (lilyfile, gui, all_movements);
557 }
558
559 static void
run_lilypond_for_pdf(gchar * filename,gchar * lilyfile)560 run_lilypond_for_pdf (gchar * filename, gchar * lilyfile)
561 {
562 static gchar *include = NULL;
563 static gchar *local_include = NULL;
564 if(!include) {
565 local_include = g_strdup_printf ("-I%s", g_build_filename(get_user_data_dir(TRUE), get_local_dir (DENEMO_DIR_LILYPOND_INCLUDE), NULL));
566 include = g_strdup_printf ("-I%s", get_system_dir (DENEMO_DIR_LILYPOND_INCLUDE));
567 }
568 /*arguments to pass to lilypond to create a pdf for printing */
569 gchar *arguments[] = {
570 Denemo.prefs.lilypath->str,
571 "-dgui",
572 "--loglevel=WARN",
573 "--pdf",
574 local_include,
575 include,
576 "-o",
577 filename,
578 lilyfile,
579 NULL
580 };
581 run_lilypond (arguments);
582 }
583 static void
run_lilypond_for_svg(gchar * filename,gchar * lilyfile)584 run_lilypond_for_svg (gchar * filename, gchar * lilyfile)
585 {
586 static gchar *include = NULL;
587 static gchar *local_include = NULL;
588 if(!include) {
589 local_include = g_strdup_printf ("-I%s", g_build_filename(get_user_data_dir(TRUE), get_local_dir (DENEMO_DIR_LILYPOND_INCLUDE), NULL));
590 include = g_strdup_printf ("-I%s", get_system_dir (DENEMO_DIR_LILYPOND_INCLUDE));
591 }
592 /*arguments to pass to lilypond to create a svg for printing */
593 gchar *arguments[] = {
594 Denemo.prefs.lilypath->str,
595 "-dgui",
596 "--loglevel=WARN",
597 "-dno-point-and-click", "-ddelete-intermediate-files", "-dbackend=svg",
598 local_include,
599 include,
600 "-o",
601 filename,
602 lilyfile,
603 NULL
604 };
605 run_lilypond (arguments);
606 }
607 /* create pdf of current score, optionally restricted to voices/staffs whose name match the current one.
608 * generate the lilypond text (on disk)
609 * Fork and run lilypond
610 */
611 void
create_pdf(gboolean part_only,gboolean all_movements)612 create_pdf (gboolean part_only, gboolean all_movements)
613 {
614 if (Denemo.printstatus->printpid != GPID_NONE)
615 {
616 if (confirm (_("Already Typesetting"), _("Abandon this typeset?")))
617 {
618 if (Denemo.printstatus->printpid != GPID_NONE) //It could have died while the user was making up their mind...
619 kill_process (Denemo.printstatus->printpid);
620 Denemo.printstatus->printpid = GPID_NONE;
621 }
622 else
623 {
624 warningdialog (_("Cancelled"));
625
626 return;
627 }
628 }
629 get_wysiwyg_info()->stage = STAGE_NONE;
630 advance_printname ();
631 gchar *filename = Denemo.printstatus->printbasename[Denemo.printstatus->cycle];
632 gchar *lilyfile = Denemo.printstatus->printname_ly[Denemo.printstatus->cycle];
633 g_remove (lilyfile);
634 Denemo.printstatus->invalid = 0;
635 g_free (Denemo.printstatus->error_file);Denemo.printstatus->error_file = NULL;
636 generate_lilypond (lilyfile, part_only, all_movements);
637 run_lilypond_for_pdf (filename, lilyfile);
638 }
639 /* create pdf of current score, optionally restricted to voices/staffs whose name match the current one.
640 * generate the lilypond text (on disk)
641 * Fork and run lilypond
642 */
643 void
create_svg(gboolean part_only,gboolean all_movements)644 create_svg (gboolean part_only, gboolean all_movements)
645 {
646 if (Denemo.printstatus->printpid != GPID_NONE)
647 {
648 if (confirm (_("Already Typesetting"), _("Abandon this typeset?")))
649 {
650 if (Denemo.printstatus->printpid != GPID_NONE) //It could have died while the user was making up their mind...
651 kill_process (Denemo.printstatus->printpid);
652 Denemo.printstatus->printpid = GPID_NONE;
653 }
654 else
655 {
656 warningdialog (_("Cancelled"));
657
658 return;
659 }
660 }
661 get_wysiwyg_info()->stage = TypesetForPlaybackView;
662 advance_printname ();
663 gchar *filename = Denemo.printstatus->printbasename[Denemo.printstatus->cycle];
664 gchar *lilyfile = Denemo.printstatus->printname_ly[Denemo.printstatus->cycle];
665 g_remove (lilyfile);
666 Denemo.printstatus->invalid = 0;
667 g_free (Denemo.printstatus->error_file);Denemo.printstatus->error_file = NULL;
668 generate_lilypond (lilyfile, part_only, all_movements);
669 run_lilypond_for_svg (filename, lilyfile);
670 }
671
create_pdf_for_lilypond(gchar * lilypond)672 void create_pdf_for_lilypond (gchar *lilypond)
673 {
674 #ifndef USE_EVINCE
675 g_debug("This feature requires denemo to be built with evince");
676 #else
677 if (Denemo.printstatus->printpid != GPID_NONE)
678 {
679 if (confirm (_("Already Typesetting"), _("Abandon this typeset?")))
680 {
681 if (Denemo.printstatus->printpid != GPID_NONE) //It could have died while the user was making up their mind...
682 kill_process (Denemo.printstatus->printpid);
683 Denemo.printstatus->printpid = GPID_NONE;
684 }
685 else
686 {
687 warningdialog (_("Cancelled"));
688
689 return;
690 }
691 }
692 get_wysiwyg_info()->stage = STAGE_NONE;
693 advance_printname ();
694 gchar *filename = Denemo.printstatus->printbasename[Denemo.printstatus->cycle];
695 gchar *lilyfile = Denemo.printstatus->printname_ly[Denemo.printstatus->cycle];
696 g_remove (lilyfile);
697 g_file_set_contents (lilyfile, lilypond, -1, NULL);
698 Denemo.printstatus->invalid = 0;
699 g_free (Denemo.printstatus->error_file);Denemo.printstatus->error_file = NULL;
700 run_lilypond_for_pdf (filename, lilyfile);
701 g_child_watch_add (Denemo.printstatus->printpid, (GChildWatchFunc) printview_finished, (gpointer) (FALSE));
702 #endif
703 }
704 /**
705 * Dialog function used to select measure range
706 *
707 */
708
709 void
printrangedialog(DenemoProject * gui)710 printrangedialog (DenemoProject * gui)
711 {
712 GtkWidget *dialog;
713 GtkWidget *label;
714 GtkWidget *hbox;
715 GtkWidget *from_measure;
716 GtkWidget *to_measure;
717
718 dialog = gtk_dialog_new_with_buttons (_("Print Excerpt Range"), GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, _("_Cancel"), GTK_RESPONSE_REJECT, NULL);
719
720 hbox = gtk_hbox_new (FALSE, 8);
721
722 GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
723 gtk_container_add (GTK_CONTAINER (content_area), hbox);
724
725 gint max_measure = g_list_length (((DenemoStaff *) (gui->movement->thescore->data))->themeasures);
726
727 label = gtk_label_new (_("Print from Measure"));
728 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
729
730 from_measure = gtk_spin_button_new_with_range (1.0, (gdouble) max_measure, 1.0);
731 gtk_box_pack_start (GTK_BOX (hbox), from_measure, TRUE, TRUE, 0);
732 gtk_spin_button_set_value (GTK_SPIN_BUTTON (from_measure), (gdouble) gui->movement->selection.firstmeasuremarked);
733
734 label = gtk_label_new (_("to"));
735 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
736
737 to_measure = gtk_spin_button_new_with_range (1.0, (gdouble) max_measure, 1.0);
738 gtk_box_pack_start (GTK_BOX (hbox), to_measure, TRUE, TRUE, 0);
739 // gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), to_measure, TRUE, TRUE, 0);
740 gtk_spin_button_set_value (GTK_SPIN_BUTTON (to_measure), (gdouble) gui->movement->selection.lastmeasuremarked);
741
742 gtk_widget_show (hbox);
743 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
744 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
745 gtk_widget_show_all (dialog);
746
747 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
748 {
749 gui->movement->selection.firstmeasuremarked = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (from_measure));
750 gui->movement->selection.lastmeasuremarked = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (to_measure));
751 //gtk_widget_destroy (dialog);
752 }
753 else
754 {
755 gui->movement->selection.firstmeasuremarked = gui->movement->selection.lastmeasuremarked = 0;
756 }
757 if (gui->movement->selection.firstmeasuremarked)
758 {
759 gui->movement->markstaffnum = gui->movement->selection.firststaffmarked = 1;
760 gui->movement->selection.laststaffmarked = g_list_length (gui->movement->thescore);
761 }
762
763 gtk_widget_destroy (dialog);
764 }
765
766 static void
rm_temp_files(gchar * file,gpointer free_only)767 rm_temp_files (gchar * file, gpointer free_only)
768 {
769 //g_debug("\n%s Deleting temp file %s\n",free_only?"Not":"", file);
770 if (!free_only)
771 g_remove (file);
772 g_free (file);
773 }
774
775 void
printpng_finished(G_GNUC_UNUSED GPid pid,G_GNUC_UNUSED gint status,GList * filelist)776 printpng_finished (G_GNUC_UNUSED GPid pid, G_GNUC_UNUSED gint status, GList * filelist)
777 {
778 g_debug ("printpng_finished\n");
779 g_list_foreach (filelist, (GFunc) rm_temp_files, FALSE);
780 g_list_free (filelist);
781 g_spawn_close_pid (Denemo.printstatus->printpid);
782 Denemo.printstatus->printpid = GPID_NONE;
783 progressbar_stop ();
784 infodialog (_("Your PNG file has now been created"));
785 }
786
787 static void
printpdf_finished(G_GNUC_UNUSED GPid pid,G_GNUC_UNUSED gint status,GList * filelist)788 printpdf_finished (G_GNUC_UNUSED GPid pid, G_GNUC_UNUSED gint status, GList * filelist)
789 {
790 if (filelist)
791 {
792 g_list_foreach (filelist, (GFunc) rm_temp_files, FALSE);
793 g_list_free (filelist);
794 }
795 g_spawn_close_pid (Denemo.printstatus->printpid);
796 Denemo.printstatus->printpid = GPID_NONE;
797 progressbar_stop ();
798 infodialog (_("Your PDF file has now been created"));
799 }
800
801 static void
prepare_preview(GPid pid,gint status,GList * filelist)802 prepare_preview (GPid pid, gint status, GList * filelist)
803 {
804 open_pngviewer (pid, status, (gchar *) get_printfile_pathbasename ());
805 printpng_finished (pid, status, (GList *) filelist);
806 }
807
808 /**
809 * Does all the export pdf work.
810 * calls exportmudela and then
811 * runs lilypond to a create a filename.pdf
812 *
813 * @param filename filename to save score to
814 * @param finish callback after creating png or if NULL, wait for finish before returning.
815 * @param gui pointer to the DenemoProject structure
816 */
817 void
export_png(gchar * filename,GChildWatchFunc finish,DenemoProject * gui)818 export_png (gchar * filename, GChildWatchFunc finish, DenemoProject * gui)
819 {
820 gchar *basename;
821 gchar *lilyfile;
822 gchar *epsfile;
823 gchar *epsfile2;
824 gchar *texfile;
825 gchar *texifile;
826 gchar *countfile;
827
828 GList *filelist = NULL;
829
830 /* get the intended resolution of the png */
831 gchar *resolution = g_strdup_printf ("-dresolution=%d", (int) Denemo.prefs.resolution);
832
833 /* create temp file names */
834 basename = get_printfile_pathbasename ();
835 lilyfile = g_strconcat (basename, ".ly", NULL);
836 epsfile = g_strconcat (filename, ".eps", NULL);
837 epsfile2 = g_strconcat (filename, "-1.eps", NULL);
838 texfile = g_strconcat (filename, "-systems.tex", NULL);
839 texifile = g_strconcat (filename, "-systems.texi", NULL);
840 countfile = g_strconcat (filename, "-systems.count", NULL);
841
842 /* create a list of files that need to be deleted */
843 filelist = g_list_append (filelist, lilyfile);
844 filelist = g_list_append (filelist, epsfile);
845 filelist = g_list_append (filelist, epsfile2);
846 filelist = g_list_append (filelist, texfile);
847 filelist = g_list_append (filelist, texifile);
848 filelist = g_list_append (filelist, countfile);
849
850 /* generate the lilypond file */
851 gui->lilysync = G_MAXUINT;
852 exportlilypond (lilyfile, gui, finish == (GChildWatchFunc) printpng_finished ? TRUE : FALSE);
853 /* create arguments needed to pass to lilypond to create a png */
854
855 gchar *arguments[] = {
856 Denemo.prefs.lilypath->str,
857 "-dgui",
858 "--loglevel=WARN",
859 "--png",
860 "-dbackend=eps",
861 resolution,
862 "-o",
863 filename,
864 lilyfile,
865 NULL
866 };
867
868 gchar* output = g_strconcat(filename, ".png", NULL);
869 g_debug("Generating %s from Lilypond", output);
870
871 /* generate the png file */
872 if (finish)
873 {
874 gint error = run_lilypond (arguments);
875 if (!error)
876 g_child_watch_add (Denemo.printstatus->printpid, (GChildWatchFunc) finish, (gchar *) filelist);
877
878 if(!g_file_test(output, G_FILE_TEST_EXISTS))
879 g_critical("Lilypond has not generated %s", output);
880 }
881 else
882 {
883 GError *err = NULL;
884 gint ret = 0;
885 gboolean success = g_spawn_sync (locateprintdir (), /* dir */
886 arguments,
887 NULL, /* env */
888 G_SPAWN_SEARCH_PATH,
889 NULL, /* child setup func */
890 NULL, /* user data */
891 NULL, /* stdout */
892 NULL, /* stderr */
893 &ret,
894 &err);
895 if(!success)
896 g_warning ("An error happened during lilypond launching: %s", err->message);
897
898 if(ret != 0)
899 g_warning ("Lilypond did not end successfully");
900
901 //These are in tmpdir and can be used for the .eps file, so don't delete them
902 //g_list_foreach(filelist, (GFunc)rm_temp_files, FALSE);
903 g_list_free (filelist);
904 }
905 g_free(output);
906 }
907
908 /**
909 * Does all the export pdf work.
910 * calls exportmudela and then
911 * runs lilypond to a create a filename.pdf
912 *
913 * @param filename filename to save score to
914 * @param gui pointer to the DenemoProject structure
915 */
916 void
export_pdf(gchar * filename,DenemoProject * gui)917 export_pdf (gchar * filename, DenemoProject * gui)
918 {
919 gchar *basename;
920 gchar *lilyfile;
921 gchar *psfile;
922 GList *filelist = NULL;
923
924 basename = get_printfile_pathbasename ();
925 lilyfile = g_strconcat (basename, ".ly", NULL);
926 psfile = g_strconcat (filename, ".ps", NULL);
927
928 /* create list of files that will need to be deleted */
929 filelist = g_list_append (filelist, lilyfile);
930 filelist = g_list_append (filelist, psfile);
931
932
933 /* generate the lilypond file */
934 exportlilypond (lilyfile, gui, TRUE);
935 /* create arguments to pass to lilypond to create a pdf */
936 gchar *arguments[] = {
937 Denemo.prefs.lilypath->str,
938 "-dgui",
939 "--loglevel=WARN",
940 "--pdf",
941 "-o",
942 filename,
943 lilyfile,
944 NULL
945 };
946 /* generate the pdf file */
947 return_on_windows_if_printing;
948
949 gint error = run_lilypond (arguments);
950 if (error)
951 {
952 g_spawn_close_pid (Denemo.printstatus->printpid);
953 Denemo.printstatus->printpid = GPID_NONE;
954 return;
955 }
956
957 g_child_watch_add (Denemo.printstatus->printpid, (GChildWatchFunc) printpdf_finished, filelist);
958 }
959
960 /* callback to print current part (staff) of score */
961 void
printpart_cb(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)962 printpart_cb (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
963 { return_on_windows_if_printing;
964 #ifndef USE_EVINCE
965 g_debug("This feature requires denemo to be built with evince");
966 #else
967 present_print_view_window();
968 DenemoProject *gui = Denemo.project;
969 if (gui->movement->markstaffnum)
970 if (confirm (_("A range of music is selected"), _("Print whole file?")))
971 {
972 gui->movement->markstaffnum = 0;
973 }
974 if ((gui->movements && g_list_length (gui->movements) > 1) && (confirm (_("This piece has several movements"), _("Print this part from all of them?"))))
975 create_pdf (TRUE, TRUE);
976 else
977 create_pdf (TRUE, FALSE);
978 g_child_watch_add (Denemo.printstatus->printpid, (GChildWatchFunc) printview_finished, (gpointer) (TRUE));
979 #endif
980 }
981
982 void
printselection_cb(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)983 printselection_cb (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
984 { return_on_windows_if_printing;
985 #ifndef USE_EVINCE
986 g_debug("This feature requires denemo to be built with evince");
987 #else
988 if (Denemo.project->movement->markstaffnum) {
989 present_print_view_window();
990 create_pdf (FALSE, FALSE);
991 g_child_watch_add (Denemo.printstatus->printpid, (GChildWatchFunc) printview_finished, (gpointer) (TRUE));
992 }
993 else
994 warningdialog (_("No selection to print"));
995 #endif
996 }
997
998 void
printexcerptpreview_cb(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)999 printexcerptpreview_cb (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
1000 { return_on_windows_if_printing;
1001 DenemoProject *gui = Denemo.project;
1002 if (!gui->movement->markstaffnum) //If no selection has been made
1003 printrangedialog (gui); //Launch a dialog to get selection
1004 if (gui->movement->selection.firstmeasuremarked)
1005 {
1006 gui->lilycontrol.excerpt = TRUE;
1007 export_png ((gchar *) get_printfile_pathbasename (), (GChildWatchFunc) prepare_preview, gui);
1008 gui->lilycontrol.excerpt = FALSE;
1009 }
1010 }
1011
1012 /* callback to print whole of score */
1013 void
printall_cb(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)1014 printall_cb (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
1015 { return_on_windows_if_printing;
1016 #ifndef USE_EVINCE
1017 g_debug("This feature requires denemo to be built with evince");
1018 #else
1019 print_from_print_view (TRUE);
1020 #endif
1021 }
1022
1023 /* callback to print movement of score */
1024 void
printmovement_cb(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)1025 printmovement_cb (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
1026 { return_on_windows_if_printing;
1027 #ifndef USE_EVINCE
1028 g_debug("This feature requires denemo to be built with evince");
1029 #else
1030 print_from_print_view (FALSE);
1031 #endif
1032 }
1033
1034 void
show_print_view(GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)1035 show_print_view (GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param){
1036 #ifndef USE_EVINCE
1037 g_debug("This feature requires denemo to be built with evince");
1038 #else
1039 implement_show_print_view(action!=NULL);
1040 #endif
1041 }
1042