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