1 /* utils.c
2 * Functions useful across the different modules of
3 * drawing and non-drawing code.
4 *
5 * for Denemo, a gtk+ frontend to GNU Lilypond
6 * (c) 1999-2005 Matthew Hiller
7 */
8
9 #include <stdio.h>
10 #include <string.h> /*for SIGTERM */
11 #include <ctype.h>
12 #include <math.h>
13 #include <fontconfig/fontconfig.h>
14 #include <gtk/gtk.h>
15 #include <gdk/gdk.h>
16 #include <stdlib.h>
17 #include "display/accwidths.h"
18 #include <denemo/denemo.h>
19 #include "display/notewidths.h"
20 #include "core/utils.h"
21 #include "smf.h"
22 #include "export/print.h"
23 #include "core/kbd-custom.h"
24 #include "core/view.h"
25 #include "command/object.h"
26 #include "command/scorelayout.h"
27 #include <signal.h> /*for SIGTERM */
28
29 #include "config.h"
30 #ifdef G_OS_WIN32
31 #include "windows.h"
32 #else
33 #include "core/binreloc.h"
34 #endif
35 #include "audio/pitchentry.h"
36 #include "command/measure.h"
37 #ifdef _MACH_O_
38 #include <mach-o/dyld.h>
39 #endif
40
41 //generated by cairo_svg2path from piano_staff.svg
42 static cairo_path_data_t piano_brace_data[] = {
43 {.header.type = 0,.header.length = 2},
44 {.point.x = 13.694762,.point.y = 15.134294},
45 {.header.type = 0,.header.length = 2},
46 {.point.x = 13.694762,.point.y = 34.647228},
47 {.header.type = 2,.header.length = 4},
48 {.point.x = 13.694745,.point.y = 37.873117},
49 {.point.x = 13.456160,.point.y = 40.450299},
50 {.point.x = 12.979007,.point.y = 42.378764},
51 {.header.type = 2,.header.length = 4},
52 {.point.x = 12.533633,.point.y = 44.307289},
53 {.point.x = 11.849690,.point.y = 45.797498},
54 {.point.x = 10.927179,.point.y = 46.849380},
55 {.header.type = 2,.header.length = 4},
56 {.point.x = 10.036446,.point.y = 47.901323},
57 {.point.x = 8.748092,.point.y = 48.830499},
58 {.point.x = 7.062105,.point.y = 49.636952},
59 {.header.type = 2,.header.length = 4},
60 {.point.x = 9.734241,.point.y = 50.899267},
61 {.point.x = 11.499767,.point.y = 52.722581},
62 {.point.x = 12.358688,.point.y = 55.106874},
63 {.header.type = 2,.header.length = 4},
64 {.point.x = 13.249387,.point.y = 57.491228},
65 {.point.x = 13.694745,.point.y = 61.102772},
66 {.point.x = 13.694762,.point.y = 65.941541},
67 {.header.type = 1,.header.length = 2},
68 {.point.x = 13.694762,.point.y = 83.666226},
69 {.header.type = 2,.header.length = 4},
70 {.point.x = 13.694745,.point.y = 88.119297},
71 {.point.x = 14.251441,.point.y = 91.450330},
72 {.point.x = 15.364854,.point.y = 93.659376},
73 {.header.type = 2,.header.length = 4},
74 {.point.x = 15.937439,.point.y = 94.781380},
75 {.point.x = 16.557760,.point.y = 95.675508},
76 {.point.x = 17.225817,.point.y = 96.341727},
77 {.header.type = 2,.header.length = 4},
78 {.point.x = 17.925643,.point.y = 97.042993},
79 {.point.x = 18.434624,.point.y = 97.463756},
80 {.point.x = 18.752761,.point.y = 97.604034},
81 {.header.type = 2,.header.length = 4},
82 {.point.x = 19.102663,.point.y = 97.744242},
83 {.point.x = 19.532113,.point.y = 97.884511},
84 {.point.x = 20.041117,.point.y = 98.024788},
85 {.header.type = 1,.header.length = 2},
86 {.point.x = 20.041117,.point.y = 99.602655},
87 {.header.type = 2,.header.length = 4},
88 {.point.x = 14.442310,.point.y = 99.427312},
89 {.point.x = 10.815824,.point.y = 98.112447},
90 {.point.x = 9.161650,.point.y = 95.658006},
91 {.header.type = 2,.header.length = 4},
92 {.point.x = 7.412016,.point.y = 93.028214},
93 {.point.x = 6.537207,.point.y = 88.487468},
94 {.point.x = 6.537217,.point.y = 82.035749},
95 {.header.type = 1,.header.length = 2},
96 {.point.x = 6.537217,.point.y = 63.206580},
97 {.header.type = 2,.header.length = 4},
98 {.point.x = 6.537207,.point.y = 60.787187},
99 {.point.x = 6.362244,.point.y = 58.735972},
100 {.point.x = 6.012330,.point.y = 57.052911},
101 {.header.type = 2,.header.length = 4},
102 {.point.x = 5.694207,.point.y = 55.369866},
103 {.point.x = 5.026171,.point.y = 53.879675},
104 {.point.x = 4.008217,.point.y = 52.582286},
105 {.header.type = 2,.header.length = 4},
106 {.point.x = 3.022060,.point.y = 51.249883},
107 {.point.x = 1.685986,.point.y = 50.373300},
108 {.point.x = -0.000008,.point.y = 49.952520},
109 {.header.type = 1,.header.length = 2},
110 {.point.x = -0.000008,.point.y = 49.373968},
111 {.header.type = 2,.header.length = 4},
112 {.point.x = 4.358134,.point.y = 48.006509},
113 {.point.x = 6.537207,.point.y = 43.676162},
114 {.point.x = 6.537217,.point.y = 36.382874},
115 {.header.type = 1,.header.length = 2},
116 {.point.x = 6.537217,.point.y = 15.975830},
117 {.header.type = 2,.header.length = 4},
118 {.point.x = 6.537207,.point.y = 10.120270},
119 {.point.x = 7.443829,.point.y = 5.965213},
120 {.point.x = 9.257083,.point.y = 3.510685},
121 {.header.type = 2,.header.length = 4},
122 {.point.x = 11.102126,.point.y = 1.056314},
123 {.point.x = 14.696800,.point.y = -0.258578},
124 {.point.x = 20.041117,.point.y = -0.433981},
125 {.header.type = 1,.header.length = 2},
126 {.point.x = 20.041117,.point.y = 1.143903},
127 {.header.type = 2,.header.length = 4},
128 {.point.x = 18.068795,.point.y = 1.740052},
129 {.point.x = 16.462326,.point.y = 3.212732},
130 {.point.x = 15.221705,.point.y = 5.561917},
131 {.header.type = 2,.header.length = 4},
132 {.point.x = 14.203726,.point.y = 7.490478},
133 {.point.x = 13.694745,.point.y = 10.681285},
134 {.point.x = 13.694762,.point.y = 15.134294},
135
136 };
137
138 static cairo_path_t piano_brace_path = { 0, piano_brace_data, 92 };
139 /**
140 * This checks to see if there's a .denemo/ directory in the user's
141 * home directory,
142 * if create tries to create one if there isn't, and returns the
143 * path to it
144 * else returns NULL
145 *
146 * .denemo/ is used for holding configuration files, templates, and so on.
147 *
148 * On windows the home directory is the one containing the My Documents folder.
149 */
150
151 const gchar *
get_user_data_dir(gboolean create)152 get_user_data_dir (gboolean create)
153 {
154 static gchar *dotdenemo = NULL;
155
156 gboolean err;
157 if (!dotdenemo)
158 {
159 dotdenemo = g_build_filename (g_get_home_dir (), ".denemo-" PACKAGE_VERSION, NULL);
160 }
161 if ((!create) && !g_file_test (dotdenemo, G_FILE_TEST_IS_DIR))
162 return NULL;
163 err = g_mkdir_with_parents (dotdenemo, 0770);
164 if (err)
165 {
166 warningdialog (_("Could not create .denemo for you personal settings"));
167 g_free (dotdenemo);
168 dotdenemo = NULL;
169 }
170 return dotdenemo;
171 }
172
173 /* return a path to a temporary directory to be used for print intermediate files */
174 const gchar *
locateprintdir(void)175 locateprintdir (void)
176 {
177 static gchar *printdir = NULL;
178 if (!printdir)
179 printdir = make_temp_dir ();
180 return printdir;
181 }
182
183
184 void
add_font_directory(gchar * fontpath)185 add_font_directory (gchar * fontpath)
186 {
187 #ifdef G_OS_WIN32
188 if (0 == AddFontResource (fontpath))
189 g_warning("Failed to add font dir %s.", fontpath);
190 SendMessage (HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
191 #endif
192 if(FcConfigAppFontAddDir (NULL, (FcChar8*) fontpath) == FcFalse)
193 g_warning("Failed to add font dir %s.", fontpath);
194 }
195
196 void
add_font_file(gchar * fontname)197 add_font_file (gchar * fontname)
198 {
199 #ifdef G_OS_WIN32
200 if (0 == AddFontResource (fontname))
201 g_warning("Failed to add font file %s.", fontname);
202 SendMessage (HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
203 #endif
204 if(FcConfigAppFontAddFile (NULL, (FcChar8*) fontname) == FcFalse)
205 g_warning("Failed to add font file %s.", fontname);
206 }
207
208 #ifdef G_OS_WIN32
209 gboolean CoInitializeExCalled = FALSE;
210 #endif
211
212 // Create a unique temporary directory starting
213 gchar *
make_temp_dir(void)214 make_temp_dir (void)
215 {
216 gchar *ret = NULL;
217 #ifdef G_OS_WIN32
218 gchar buf[1024] = "C:\\TMP\\\0";
219 gint length = 1024;
220 (void) GetTempPath (length, buf);
221 gint integer = 0; //Windows does not delete the temp directory, use a constant one. g_rand_int(g_rand_new());
222 ret = g_strdup_printf ("%sDenemo%d", buf, integer);
223
224 gint fail = g_mkdir_with_parents (ret, 0700);
225 if (fail)
226 g_warning ("Could not create temp dir %s", ret);
227 else
228 g_info ("Created temp dir %s\n", ret);
229 #else
230 ret = g_strdup ("/tmp/DenemoXXXXXX");
231 mkdtemp ((char *) ret);
232 #endif
233 return ret;
234 }
235
236 gboolean
run_file_association(gchar * filename)237 run_file_association (gchar * filename)
238 {
239 #ifdef G_OS_WIN32
240 gint value = 0;
241 if (!CoInitializeExCalled)
242 {
243 value = CoInitializeExCalled = TRUE;
244 CoInitializeEx (NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
245 g_debug ("coinit returned %d\n", value);
246 }
247 g_info ("Running ShellExecute %s \n", filename);
248 return ShellExecute (NULL, NULL, filename, NULL, NULL, 0) > 32 /* value above 32 indicating success */ ;
249 #else
250 g_warning ("No file assoc code - set pref in externals tab of prefs dialog");
251 return 0;
252 #endif
253
254 }
255
256 /**
257 Popups up the menu named.
258 */
259 void
popup_menu(gchar * name)260 popup_menu (gchar * name)
261 {
262 GtkWidget *menu = gtk_ui_manager_get_widget (Denemo.ui_manager, name);
263 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
264 }
265
266 /**
267 * outputs a warning and sounds bell
268 * @return none
269 */
270 void
warningmessage(gchar * msg)271 warningmessage (gchar * msg)
272 {
273 gdk_beep ();
274 g_warning ("%s", msg);
275 }
276
277 /**
278 * Pops up an info or warning dialog and blocks until it is dismissed
279 * @param msg warning message to display
280 * @param info TRUE if informational only
281 * @return none
282 */
283 void
infowarningdialog(gchar * msg,gboolean info)284 infowarningdialog (gchar * msg, gboolean info)
285 {
286 if(Denemo.non_interactive)
287 g_warning("%s", msg);
288 else
289 {
290 GtkWidget *dialog;
291 dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window), GTK_DIALOG_DESTROY_WITH_PARENT, info?GTK_MESSAGE_INFO :GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "%s", msg);
292 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
293 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window));
294 gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
295 gtk_dialog_run (GTK_DIALOG (dialog));
296 gtk_widget_destroy (dialog);
297 }
298 }
299 /**
300 * Pops up a warning dialog and blocks until it is dismissed
301 * @param msg warning message to display
302 * @return none
303 */
304 void
warningdialog(gchar * msg)305 warningdialog (gchar * msg)
306 {
307 infowarningdialog (msg, FALSE);
308 }
309
310 /**
311 * Displays information message to screen, not blocking.
312 * User can destroy window when no longer needed.
313 * @param msg message to display
314 * @return dialog
315 */
316 GtkWidget *
infodialog(gchar * msg)317 infodialog (gchar * msg)
318 {
319 if(Denemo.non_interactive){
320 g_info("%s", msg);
321 return NULL;
322 }
323
324 GtkWidget *dialog;
325 dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", msg);
326 #ifdef G_OS_WIN32
327 gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); //needed on windows because of a bug, not all text can be seen.
328 #endif
329 g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_hide), dialog);
330 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window));
331 gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
332 gtk_widget_show_all (dialog);
333 return dialog;
334 }
335
336 /* data stucture to contain Progressbar
337 * data
338 */
339 typedef struct _ProgressData
340 {
341 GtkWidget *window;
342 GtkWidget *pbar;
343 int timer;
344 gboolean progressing;
345 } ProgressData;
346
347 static ProgressData progress_data;
348
349
350 /* Update the value of the progress bar so that we get
351 * some movement */
352 static gboolean
progress_timeout(void)353 progress_timeout (void)
354 {
355 if (progress_data.progressing)
356 gtk_progress_bar_pulse (GTK_PROGRESS_BAR (progress_data.pbar));
357 else
358 {
359 gtk_widget_hide (progress_data.window);
360 progress_data.timer = 0;
361 return FALSE;
362 }
363 return TRUE;
364 }
365
366
367
368
369 /**
370 * Displays progress bar
371 * optionally pass a callback to be run on delete signal
372 * @param msg message to display, callback (can be NULL)
373 */
374 GtkWindow *
progressbar(gchar * msg,gpointer callback)375 progressbar (gchar * msg, gpointer callback)
376 {
377
378 GtkWidget *vbox;
379 ProgressData *pdata = &progress_data;
380 if (pdata->progressing) pdata->window;
381 if (pdata->window == NULL)
382 {
383 if (callback && Denemo.prefs.progressbardecorations)
384 pdata->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
385 else
386 pdata->window = gtk_window_new (GTK_WINDOW_POPUP);
387 gtk_window_set_accept_focus (GTK_WINDOW (pdata->window), FALSE); //FIXME this is only a hint; perhaps we should embed the progress bar in the status line...
388 gtk_window_set_title (GTK_WINDOW (pdata->window), _("Progress"));
389 gtk_widget_set_tooltip_text (pdata->window, _("This indicates the the LilyPond typesetter is still working on setting the Denemo score. This can take a long time, particularly for polyphony where voices must not collide. You can continue editing while the typesetting is taking place.\nKill this window if you want to re-start the typesetting e.g. after fixing a mistake you just spotted."));
390 gtk_window_set_transient_for (GTK_WINDOW(pdata->window), GTK_WINDOW(Denemo.window));
391 gtk_window_set_keep_above (GTK_WINDOW (pdata->window), TRUE);
392 vbox = gtk_vbox_new (FALSE, 5);
393 gtk_container_add (GTK_CONTAINER (pdata->window), vbox);
394 pdata->pbar = gtk_progress_bar_new ();
395 gtk_container_add (GTK_CONTAINER (vbox), pdata->pbar);
396 gtk_widget_show_all (vbox);
397 }
398 /* set text inside progress bar */
399 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pdata->pbar), msg);
400 if (pdata->timer == 0)
401 pdata->timer = g_timeout_add (100, (GSourceFunc) progress_timeout, pdata);
402 pdata->progressing = TRUE; /* If this is false the progress bar will stop */
403 gtk_widget_show (pdata->window);
404 /* If widget is destroyed stop the printing */
405 if(callback)
406 g_signal_connect (G_OBJECT (pdata->window), "delete-event", G_CALLBACK (callback /*call_stop_lilypond*/), NULL);
407 else
408 g_signal_connect (G_OBJECT (pdata->window), "delete-event", G_CALLBACK (progressbar_stop), NULL);
409 return GTK_WINDOW (pdata->window);
410 }
411
412 void
progressbar_stop(void)413 progressbar_stop (void)
414 {
415 progress_data.progressing = FALSE;
416 }
417
418
419 void
busy_cursor(GtkWidget * area)420 busy_cursor (GtkWidget *area)
421 {
422 static GdkCursor *busycursor = NULL;
423 if(!busycursor)
424 busycursor = gdk_cursor_new (GDK_WATCH);
425 if (gtk_widget_get_window (Denemo.printarea))
426 gdk_window_set_cursor (gtk_widget_get_window (area), busycursor);
427 }
428 void
normal_cursor(GtkWidget * area)429 normal_cursor (GtkWidget *area)
430 {
431 static GdkCursor *arrowcursor = NULL;
432 if(!arrowcursor)
433 arrowcursor = gdk_cursor_new (GDK_RIGHT_PTR);
434 if (gtk_widget_get_window (area))
435 gdk_window_set_cursor (gtk_widget_get_window (area), arrowcursor);
436 }
437
438
439 /**
440 * Draws the given bitmap mask on to the pixmap using the given
441 * grahpics context.
442 *
443 * @param pixmap pixmap be drawn on.
444 * @param gc graphics context to use
445 * @param mask bitmap to be drawn
446 * @param x x position on the pixmap
447 * @param y y position on the pixmap
448 * @param width width of the bitmap mask
449 * @param height height of the bitmap mask
450 *
451 * @return none
452 */
453
454
455 #ifdef G_OS_WIN32
456 //this code actually works on GNU/Linux too, it is not clear what to prefer
457 static void
windows_draw_text(cairo_t * cr,const char * font,const char * text,double x,double y,double size,gboolean invert)458 windows_draw_text (cairo_t *cr, const char *font, const char *text, double x, double y, double size, gboolean invert)
459 {
460 y -= size;
461 size *= 0.75;
462 PangoLayout *layout;
463 PangoFontDescription *desc;
464 /* Create a PangoLayout, set the font and text */
465 layout = pango_cairo_create_layout (cr);
466
467 pango_layout_set_text (layout, text, -1);
468 desc = pango_font_description_from_string (font);
469 pango_font_description_set_size (desc, size*PANGO_SCALE);
470 pango_layout_set_font_description (layout, desc);
471 pango_font_description_free (desc);
472 pango_cairo_update_layout (cr, layout);
473
474
475 cairo_move_to (cr, x, y);
476 if (invert)
477 cairo_scale (cr, 1, -1);
478 pango_cairo_show_layout (cr, layout);
479 /* free the layout object */
480 g_object_unref (layout);
481 }
482 #endif
483
484 void
drawbitmapinverse_cr(cairo_t * cr,DenemoGraphic * mask,gint x,gint y,gboolean invert)485 drawbitmapinverse_cr (cairo_t * cr, DenemoGraphic * mask, gint x, gint y, gboolean invert)
486 {
487 cairo_save (cr);
488 switch (mask->type)
489 {
490 case DENEMO_BITMAP:
491 {
492 #if GTK_MAJOR_VERSION==3
493 gdk_cairo_set_source_window (cr, mask->graphic, x, y); //??? bitmap???? asks torbenh
494 #else
495 cairo_rectangle (cr, x, y, mask->width, mask->height);
496 #endif
497 cairo_fill (cr);
498 break;
499 }
500 case DENEMO_PATTERN:
501 {
502 cairo_pattern_t *pattern = (cairo_pattern_t *) mask->graphic;
503 cairo_translate (cr, x, y);
504 cairo_mask (cr, pattern);
505 break;
506 }
507 case DENEMO_FONT:
508 {
509 DenemoGlyph *glyph = mask->graphic;
510 #ifdef G_OS_WIN32
511 windows_draw_text (cr, glyph->fontname, glyph->utf, x, y, glyph->size, invert);
512 #else
513 cairo_select_font_face (cr, glyph->fontname, glyph->slant, glyph->weight);
514 cairo_set_font_size (cr, glyph->size);
515 cairo_move_to (cr, x, y);
516
517 if (invert)
518 cairo_scale (cr, 1, -1);
519 cairo_show_text (cr, glyph->utf);
520 #endif
521 break;
522 }
523 }
524 cairo_restore (cr);
525 }
526
527 void
drawfetachar_cr(cairo_t * cr,gunichar uc,double x,double y)528 drawfetachar_cr (cairo_t * cr, gunichar uc, double x, double y)
529 {
530 int len;
531 char utf_string[8];
532 len = g_unichar_to_utf8 (uc, utf_string);
533 utf_string[len] = '\0';
534 // windows_draw_text (cr, "feta26", utf_string, x, y, 35.0, FALSE); this fails to position stuff correctly, but the code below is working on windows anyway.
535 cairo_select_font_face (cr, "feta26", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
536 cairo_set_font_size (cr, 35.0);
537 cairo_move_to (cr, x, y);
538 cairo_show_text (cr, utf_string);
539
540 }
541
542
543 void
drawtext_cr(cairo_t * cr,const char * text,double x,double y,double size)544 drawtext_cr (cairo_t * cr, const char *text, double x, double y, double size)
545 {
546 if (*text)
547 {
548 #ifdef G_OS_WIN32
549 return windows_draw_text (cr, "Denemo", text, x, y, size, FALSE); //these values arrived at by trial and error, to match the previously used code below
550 #else
551 //use the FreeSerif font as it has music symbols - there is no font substitution done by cairo here
552 cairo_select_font_face (cr, "Denemo", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
553 cairo_set_font_size (cr, size);
554 cairo_move_to (cr, x, y);
555 cairo_show_text (cr, text);
556 #endif
557 }
558 }
559
560 void
drawnormaltext_cr(cairo_t * cr,const char * text,double x,double y)561 drawnormaltext_cr (cairo_t * cr, const char *text, double x, double y)
562 {
563 drawtext_cr (cr, text, x, y, 14.0);
564 }
565
566 void
drawlargetext_cr(cairo_t * cr,const char * text,double x,double y)567 drawlargetext_cr (cairo_t * cr, const char *text, double x, double y)
568 {
569 drawtext_cr (cr, text, x, y, 24.0);
570 }
571
572 /* draw display text and or graphics for directives
573 return the widest graphic width*/
574 gint
draw_for_directives(cairo_t * cr,GList * directives,gint x,gint y,gboolean at_cursor)575 draw_for_directives (cairo_t * cr, GList * directives, gint x, gint y, gboolean at_cursor)
576 {
577 gint count = 10;
578 gint maxwidth = 0;
579 for (; directives; directives = directives->next, count += 10)
580 {
581 DenemoDirective *directive = (DenemoDirective *) directives->data;
582 guint layout = selected_layout_id ();
583 gdouble only = directive->y ? ((directive->y == layout) ? 0.5 : 0.0) : 0.0;
584 gdouble exclude = directive->x ? ((directive->x == layout) ? 0.9 : 0.0) : 0.0;
585 if (directive->y && directive->y != layout)
586 exclude = 0.9;
587 if (exclude>0.0 || only >0.0)
588 {
589 cairo_save (cr);
590 cairo_set_source_rgba (cr, 0.4 + exclude -only/2, 0.5 + only, 0.4 -only/2, at_cursor ? 1.0 : 0.7);
591 }
592 if (directive->graphic)
593 {
594 gint gwidth, gheight;
595 gwidth = directive->graphic->width;
596 gheight = directive->graphic->height;
597
598 maxwidth = MAX (gwidth, maxwidth);
599 //g_debug("drawing a graphic at %d %d\n", xx+directive->gx+count-gwidth/2, y+height+directive->gy-gheight/2);
600 drawbitmapinverse_cr (cr, directive->graphic, x + directive->gx + count - gwidth / 2, y + directive->gy - gheight / 2, FALSE);
601
602 }
603 if (directive->display)
604 {
605 #define MAXLEN (8)
606 gchar c = 0; //if it is a long string only show it all when cursor is on it also only display from first line
607 gchar *p;
608 for (p = directive->display->str; *p; p++)
609 {
610 if (*p == '\n' || (!at_cursor && (p - directive->display->str) > MAXLEN))
611 {
612 c = *p;
613 *p = 0;
614 break;
615 }
616 }
617 drawnormaltext_cr (cr, directive->display->str, x + directive->tx + count, y + directive->ty);
618 if (c)
619 {
620 *p = c;
621 }
622 if (exclude>0.0 || only >0.0)
623 cairo_restore (cr);
624 }
625 }
626
627 return maxwidth;
628 }
629
630 /* draw a brace straight or curly */
draw_staff_brace(cairo_t * cr,gboolean curly,gint x,gint y,gint height)631 void draw_staff_brace (cairo_t *cr, gboolean curly, gint x, gint y, gint height)
632 {
633 cairo_set_source_rgb (cr, 0, 0, 0);
634 if (!curly)
635 {
636 drawfetachar_cr (cr, 0xD8, x, y);
637 cairo_rectangle (cr, x, y+2.0, 3, height - 4.0);
638 cairo_fill (cr);
639 cairo_rectangle (cr, x + 5.0, y + 1.0, 1, height - 2.0);
640 cairo_fill (cr);
641 drawfetachar_cr (cr, 0xD9, x, y + height);
642 }
643 else
644 {
645 //cairo_translate (cr, 0, 10.0);
646 cairo_translate (cr, x-5, y + 2);
647 cairo_scale (cr, 1.0, height / 100.0);
648 cairo_append_path (cr, &piano_brace_path);
649 cairo_fill (cr);
650 }
651 return;
652 }
653
pretty_name(gchar * lilynote)654 gchar *pretty_name (gchar *lilynote) //display ♯♭♮ with note name
655 {
656 static gchar *natural = NULL;
657 static gchar *sharp;
658 static gchar *flat;
659 static gchar *double_sharp;
660 static gchar *double_flat;
661 gchar *answer;
662 if (!natural)
663 {
664 natural = g_strdup ("C♮");
665 sharp = g_strdup ("C♯");
666 flat = g_strdup ("C♭");
667 double_sharp = g_strdup ("C");
668 double_flat = g_strdup ("C");
669 }
670 answer = natural;
671 if(*(lilynote+1)==0)
672 answer = natural;
673 else if (*(lilynote+1) == 'i')
674 {
675 if (*(lilynote+3) == 'i')
676 answer = double_sharp;
677 else
678 answer = sharp;
679 }
680 else if (*(lilynote+1) == 'e')
681 {
682 if (*(lilynote+3) == 'e')
683 answer = double_flat;
684 else
685 answer = flat;
686 }
687 *answer = toupper(*lilynote);
688 return answer;
689 }
690
691
692 /**
693 * Utility function to set the number of ticks used by the given object
694 * if it is within a given tuplet
695 *
696 * @param theobj DenemoObject to set the number of ticks
697 * @param numerator numerator of the current tuplet
698 * @param denominator denominator of the current tuplet
699 * @return none
700 */
701 void
set_tuplefied_numticks(DenemoObject * theobj,gint numerator,gint denominator)702 set_tuplefied_numticks (DenemoObject * theobj, gint numerator, gint denominator)
703 {
704 theobj->durinticks = theobj->basic_durinticks * numerator / denominator;
705 /* Though WHOLENUMTICKS is chosen strategically so that in common
706 * cases, this division works out evenly, things should also work
707 * out properly if it doesn't; i.e., durinticks_untupletized is
708 * rounded down */
709 }
710
711 /**
712 * Utility function to set the number of ticks of a mudela object
713 * in a grace note
714 * @param theobj the DenemoObject to set the number of ticks
715 * @param multiplier the grace notes multiplier
716 * @return none
717 */
718 void
set_grace_numticks(DenemoObject * theobj,gint multiplier)719 set_grace_numticks (DenemoObject * theobj, gint multiplier)
720 {
721
722 theobj->durinticks = theobj->basic_durinticks / multiplier;
723
724
725 }
726
727
728 /**
729 * Sets the number of ticks taken by the given DenemoObject.
730 *
731 * @param theobj the mudela object to set the number of ticks on
732 * @return none
733 */
734 void
set_basic_numticks(DenemoObject * theobj)735 set_basic_numticks (DenemoObject * theobj)
736 {
737 gint power;
738 gint withoutdots;
739 gint addperdot, i;
740
741 switch (theobj->type)
742 {
743 case CHORD:
744 if (((chord *) theobj->object)->baseduration < 0)
745 {
746 withoutdots = -((chord *) theobj->object)->baseduration;
747 }
748 else
749 {
750 power = 1 << ((chord *) theobj->object)->baseduration;
751 withoutdots = WHOLE_NUMTICKS / power;
752 }
753 addperdot = withoutdots / 2;
754 theobj->basic_durinticks = withoutdots;
755 for (i = 0; i < ((chord *) theobj->object)->numdots; addperdot /= 2, i++)
756 theobj->basic_durinticks += addperdot;
757
758 break;
759 default:
760 theobj->basic_durinticks = 0;
761 theobj->durinticks = 0;
762 /* There's no reason not to set that as well */
763 break;
764 }
765 }
766
767 /**
768 * Returns the amount of space to be left after a note or rest, only
769 * taking the width of the measure into consideration
770 *
771 * @param numticks the number of ticks taken so far
772 * @param wholenotewidth the number of ticks taken be a whole note
773 * @return the amount of space to be left after a note or rest
774 */
775
776 gint
space_after(gint numticks,gint wholenotewidth)777 space_after (gint numticks, gint wholenotewidth)
778 {
779 return MAX (numticks * wholenotewidth / WHOLE_NUMTICKS, 0);
780 }
781
782 #define EXTRAWIDTH 5
783
784 /**
785 * Sets the minimum space that needs to be allocated for drawing a mudela
786 * object based on the type
787 * also sets space_before
788 * @param theobj the DenemoObject to set the minimum space on
789 * @return none
790 */
791
792 void
setpixelmin(DenemoObject * theobj)793 setpixelmin (DenemoObject * theobj)
794 {
795 gint i, baseduration, headtype;
796 chord chordval;
797 GList *tnode;
798 note *thetone;
799 /* And these static declaration are copied right out of drawnotes.c
800 * and drawaccidentals.c */
801
802 switch (theobj->type)
803 {
804 case CHORD:
805 chordval = *(chord *) theobj->object;
806 baseduration = chordval.baseduration;
807 baseduration = MAX (baseduration, 0);
808 headtype = MIN (baseduration, 2);
809 if (headtype < 0)
810 headtype = 0; //-ve values of baseduration are for specials
811 gint directive_pixels = 0; // the largest amount of extra space asked for by any directive
812 GList *g = chordval.directives;
813 for (; g; g = g->next)
814 directive_pixels = MAX (directive_pixels, ((DenemoDirective *) g->data)->minpixels);
815 if (chordval.notes)
816 {
817 theobj->minpixelsalloted = headwidths[headtype];
818 //search through notes and their attached directives, find max display space requested
819 //use this below
820
821 g = chordval.notes;
822 for (; g; g = g->next)
823 {
824 GList *h = ((note *) g->data)->directives;
825 for (; h; h = h->next)
826 directive_pixels = MAX (directive_pixels, ((DenemoDirective *) h->data)->minpixels);
827 }
828 }
829 else /* a rest */
830 theobj->minpixelsalloted = restwidths[baseduration];
831
832 // Allow extra space specified by attached LilyPond directives - example:
833 theobj->minpixelsalloted += directive_pixels;
834
835
836
837 /* 12 pixels for the first dot, 6 for each dot thereafter */
838 if (chordval.numdots)
839 theobj->minpixelsalloted += 6;
840 for (i = 0; i < chordval.numdots; i++)
841 theobj->minpixelsalloted += 6;
842
843 theobj->space_before = 0;
844 if (chordval.hasanacc)
845 for (tnode = chordval.notes; tnode; tnode = tnode->next)
846 {
847 thetone = (note *) tnode->data;
848 if (thetone->showaccidental)
849 theobj->space_before = MAX (theobj->space_before, thetone->position_of_accidental);
850 }
851 if (chordval.is_reversealigned){
852 if (chordval.is_stemup)
853 theobj->minpixelsalloted += headwidths[headtype];
854 else if (!chordval.hasanacc)
855 /* Accidental positioning already accounts for the extra leading
856 space that we need for reverse-aligned noteheads, provided
857 the chord has an accidental in it somewhere. We only have to
858 remark upon noteheads to the left of the stem if there weren't
859 any accidentals to position. */
860 theobj->space_before += headwidths[headtype];
861 }
862 theobj->minpixelsalloted += EXTRAWIDTH;
863 break;
864 case TUPOPEN:
865 case TUPCLOSE:
866 /* The real way do this will be with a gdk_string_width. Until
867 * then, though: */
868 theobj->minpixelsalloted =
869 #if 0
870 40;
871 #else
872 16;
873 #endif
874 theobj->space_before = theobj->minpixelsalloted / 2;
875 break;
876 case LILYDIRECTIVE:
877 {
878 DenemoDirective *directive = (DenemoDirective *) theobj->object;
879 theobj->minpixelsalloted = directive->minpixels ? directive->minpixels : 16;
880 theobj->space_before = theobj->minpixelsalloted / 2;
881 }
882 break;
883 case CLEF:
884 theobj->minpixelsalloted = 35;
885 theobj->space_before = 0;
886 break;
887 case KEYSIG:
888 theobj->minpixelsalloted = 20; //needed so find_xes_in_measures assigns space to it without waiting for drawing to do so.
889 theobj->space_before = 0;
890 break;
891 case TIMESIG:
892 theobj->minpixelsalloted = 40;
893 theobj->space_before = 0;
894 break;
895 case STEMDIRECTIVE:
896 /* The real way do this will be with a gdk_string_width. Until
897 * then, though: */
898 theobj->minpixelsalloted = 40;
899 theobj->space_before = 0;
900 break;
901 case DYNAMIC:
902 theobj->minpixelsalloted = 40;
903 theobj->space_before = 0;
904 break;
905 case GRACE_START:
906 case GRACE_END:
907 theobj->minpixelsalloted = 16;
908 theobj->space_before = theobj->minpixelsalloted / 2;
909 break;
910 default:
911 theobj->minpixelsalloted = 0;
912 theobj->space_before = 0;
913 break;
914 }
915 }
916
917 /**
918 *
919 * @param mid_c_offset the mid_c_offset of the the tone
920 * @param dclef the clef of the current tone
921 *
922 * @return the height of a tone based on its mid_c_offset and the clef that it's in
923 */
924 gint
calculateheight(gint mid_c_offset,gint dclef)925 calculateheight (gint mid_c_offset, gint dclef)
926 {
927 switch (dclef)
928 {
929 case DENEMO_TREBLE_CLEF:
930 return 5 * LINE_SPACE - HALF_LINE_SPACE * mid_c_offset;
931 break; /* Probably gratuitous */
932 case DENEMO_ALTO_CLEF:
933 return 2 * LINE_SPACE - HALF_LINE_SPACE * mid_c_offset;
934 break;
935 case DENEMO_G_8_CLEF:
936 return LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 1);
937 break;
938 case DENEMO_BASS_CLEF:
939 return -LINE_SPACE - HALF_LINE_SPACE * mid_c_offset;
940 break;
941 case DENEMO_F_8_CLEF:
942 return -5 * LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 1);
943 break;
944 case DENEMO_TENOR_CLEF:
945 return LINE_SPACE - HALF_LINE_SPACE * mid_c_offset;
946 break;
947 case DENEMO_SOPRANO_CLEF:
948 return LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 6);
949 break;
950 case DENEMO_FRENCH_CLEF:
951 return 6 * LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset);
952 break;
953 }
954
955 return (0);
956 }
957
958 /**
959 * Converts the given offset to a number
960 *
961 * @param n the offset to convert
962 * @return the result of the offset conversion
963 */
964 gint
offsettonumber(gint n)965 offsettonumber (gint n)
966 {
967 if (n >= 0)
968 return n % 7;
969 else
970 return (7 - (-n % 7)) % 7;
971 /* Not all C implementations conform to the more recent standard on how %
972 should operate on negative operands. */
973 }
974
975 /**
976 * converts the int mid_c_offset to the lilypond name
977 * returns a gchar * so it will have to be freed
978 * 0 returns "c", 1 returns "cis"
979 * The octave ",,, or '''" is also appended"
980 */
981
982 gchar *
mid_c_offsettolily(int mid_c_offset,int enshift)983 mid_c_offsettolily (int mid_c_offset, int enshift)
984 {
985 gint octave, k;
986 GString *lilynote = g_string_new ("");
987
988 g_string_append_printf (lilynote, "%c", mid_c_offsettoname (mid_c_offset));
989 if (enshift < 0)
990 for (k = enshift; k; k++)
991 g_string_append_printf (lilynote, "es");
992 else
993 for (k = enshift; k; k--)
994 g_string_append_printf (lilynote, "is");
995 octave = mid_c_offsettooctave (mid_c_offset);
996 if (octave < 0)
997 for (; octave; octave++)
998 g_string_append_printf (lilynote, ",");
999 else
1000 for (; octave; octave--)
1001 g_string_append_printf (lilynote, "\'");
1002
1003 return g_string_free (lilynote, FALSE);
1004 }
1005
1006 /**
1007 * converts the mid_c_offset to the correct letter name
1008 * @param mid_c_offset the mid_c_offset to convert
1009 * @return the character name of the mid_c_offset
1010 */
1011 gchar
mid_c_offsettoname(gint mid_c_offset)1012 mid_c_offsettoname (gint mid_c_offset)
1013 {
1014 gint otn = offsettonumber (mid_c_offset);
1015
1016 return ((otn + 2) % 7) + 'a';
1017 }
1018
1019 void
note2lilynotename(struct note * noteobject,GString * ret)1020 note2lilynotename (struct note *noteobject, GString * ret)
1021 {
1022 gint mid_c_offset = noteobject->mid_c_offset;
1023
1024 g_string_append_printf (ret, "%c", mid_c_offsettoname (mid_c_offset));
1025 }
1026
1027 void
note2lilyaccidental(struct note * noteobject,GString * ret)1028 note2lilyaccidental (struct note *noteobject, GString * ret)
1029 {
1030 gint enshift = noteobject->enshift;
1031 gint k;
1032 if (enshift < 0)
1033 for (k = enshift; k; k++)
1034 g_string_append_printf (ret, "es");
1035 else
1036 for (k = enshift; k; k--)
1037 g_string_append_printf (ret, "is");
1038 }
1039
1040 void
note2lilyoctave(struct note * noteobject,GString * ret)1041 note2lilyoctave (struct note *noteobject, GString * ret)
1042 {
1043 gint mid_c_offset = noteobject->mid_c_offset;
1044 gint octave = mid_c_offsettooctave (mid_c_offset);
1045 if (octave < 0)
1046 for (; octave; octave++)
1047 g_string_append_printf (ret, ",");
1048 else
1049 for (; octave; octave--)
1050 g_string_append_printf (ret, "\'");
1051 }
1052
1053 void
chord2lilyduration(struct chord * chordobject,GString * ret)1054 chord2lilyduration (struct chord *chordobject, GString * ret)
1055 {
1056 chord2lilybaseduration (chordobject, ret);
1057 chord2lilynumdots (chordobject, ret);
1058 }
1059
1060 void
chord2lilybaseduration(struct chord * chordobject,GString * ret)1061 chord2lilybaseduration (struct chord *chordobject, GString * ret)
1062 {
1063 int baseduration = chordobject->baseduration;
1064 g_string_append_printf (ret, "%d", baseduration);
1065 }
1066
1067 void
chord2lilynumdots(struct chord * chordobject,GString * ret)1068 chord2lilynumdots (struct chord *chordobject, GString * ret)
1069 {
1070 int numdots = chordobject->numdots;
1071 g_string_append_printf (ret, "%d", numdots);
1072 }
1073
1074 /**
1075 * Calculate a pitches octave from the mid_c_offset
1076 * @param mid_c_offset the mid_c_offset to use
1077 * @return the octave of the given mid_c_offset
1078 */
1079 gint
mid_c_offsettooctave(gint mid_c_offset)1080 mid_c_offsettooctave (gint mid_c_offset)
1081 {
1082 if (mid_c_offset < 0)
1083 return -((-mid_c_offset + 6) / 7) + 1;
1084 else
1085 return (mid_c_offset / 7) + 1;
1086 }
1087
1088 /**
1089 * g_list_foreach helper function to free the given data
1090 * @param data the list elements data
1091 * @param user_data any user supplied data (not used in this case)
1092 */
1093 void
freeit(gpointer data,gpointer user_data)1094 freeit (gpointer data, gpointer user_data)
1095 {
1096 g_free (data);
1097 }
1098
1099
1100
1101
1102
1103
1104
1105 /************* routines for calling from debug code ***************/
1106 #include "command/staff.h"
1107
1108 G_GNUC_UNUSED void
printobj(objnode * obj)1109 printobj (objnode * obj)
1110 {
1111 DenemoObject *curObj;
1112
1113 curObj = (DenemoObject *) (obj->data);
1114 switch (curObj->type)
1115 {
1116 case CHORD:
1117 fprintf (stderr, "\t\t%s type\n", "CHORD");
1118 break;
1119 case TUPOPEN:
1120 fprintf (stderr, "\t\t%s type\n", "TUPOPEN");
1121 break;
1122 case TUPCLOSE:
1123 fprintf (stderr, "\t\t%s type\n", "TUPCLOSE");
1124 break;
1125 case CLEF:
1126 fprintf (stderr, "\t\t%s type\n", "CLEF");
1127 break;
1128 case TIMESIG:
1129 fprintf (stderr, "\t\t%s type\n", "TIMESIG");
1130 break;
1131 case KEYSIG:
1132 fprintf (stderr, "\t\t%s type\n", "KEYSIG");
1133 break;
1134 case BARLINE:
1135 fprintf (stderr, "\t\t%s type\n", "BARLINE");
1136 break;
1137 case STEMDIRECTIVE:
1138 fprintf (stderr, "\t\t%s type\n", "STEMDIRECTIVE");
1139 break;
1140 case MEASUREBREAK:
1141 fprintf (stderr, "\t\t%s type\n", "MEASUREBREAK");
1142 break;
1143 case DYNAMIC:
1144 fprintf (stderr, "\t\t%s type\n", "DYNAMIC");
1145 break;
1146 case GRACE_START:
1147 fprintf (stderr, "\t\t%s type\n", "GRACE_START");
1148 break;
1149 case GRACE_END:
1150 fprintf (stderr, "\t\t%s type\n", "GRACE_END");
1151 break;
1152 case LYRIC:
1153 fprintf (stderr, "\t\t%s type\n", "LYRIC");
1154 break;
1155 case FIGURE:
1156 fprintf (stderr, "\t\t%s type\n", "FIGURE");
1157 break;
1158 default: /* needs to be up to date with enum in include/denemo/denemo.h */
1159 fprintf (stderr, "!!!!!unknown object type %x - see enum in denemo.h\n", curObj->type);
1160 break;
1161 }
1162 }
1163
1164 G_GNUC_UNUSED void
printobjs(objnode * obj)1165 printobjs (objnode * obj)
1166 {
1167 objnode *curobj;
1168 if (obj == NULL)
1169 {
1170 fprintf (stderr, "NULL object\n");
1171 return;
1172 }
1173 printobj (obj);
1174 fprintf (stderr, "previous objects\n");
1175 curobj = obj;
1176 while (curobj->prev)
1177 {
1178 printobj (curobj->prev);
1179 curobj = curobj->prev;
1180 }
1181 fprintf (stderr, "next objects\n");
1182 curobj = obj;
1183 while (curobj->next)
1184 {
1185 printobj (curobj->next);
1186 curobj = curobj->next;
1187 }
1188 }
1189
1190 G_GNUC_UNUSED void
printmeasure(measurenode * mnode)1191 printmeasure (measurenode * mnode)
1192 {
1193 if (mnode == NULL)
1194 {
1195 fprintf (stderr, "Empty measure\n");
1196 return;
1197 }
1198 printobjs ((objnode*)measure_first_obj_node (mnode));
1199 }
1200
1201 G_GNUC_UNUSED void
printmeasures(staffnode * thestaff)1202 printmeasures (staffnode * thestaff)
1203 {
1204 GList *measure = staff_first_measure_node (thestaff);
1205 gint measurenum = 1;
1206 for (measure = staff_first_measure_node (thestaff); measure; measure = measure->next)
1207 {
1208 fprintf (stderr, "*************Measure %d *************\n", measurenum++);
1209 printmeasure (measure);
1210 }
1211 }
1212
1213 G_GNUC_UNUSED void
printscoreinfo(DenemoMovement * si)1214 printscoreinfo (DenemoMovement * si)
1215 {
1216 if (si->thescore == NULL)
1217 {
1218 fprintf (stderr, "Staff with NULL thescore field\n");
1219 return;
1220 }
1221 printmeasures (si->thescore);
1222 }
1223
1224 /**
1225 * Function that initializes the code needed for the directory relocation.
1226 * @return none
1227 */
1228 void
initdir()1229 initdir ()
1230 {
1231 #ifndef G_OS_WIN32
1232 GError *error = NULL;
1233 if (!gbr_init (&error) && (error != (GError *) GBR_INIT_ERROR_DISABLED))
1234 {
1235 g_debug ("BinReloc failed to initialize:\n");
1236 g_debug ("Domain: %d (%s)\n", (int) error->domain, g_quark_to_string (error->domain));
1237 g_debug ("Code: %d\n", error->code);
1238 g_debug ("Message: %s\n", error->message);
1239 g_error_free (error);
1240 g_debug ("----------------\n");
1241 }
1242 #endif /* not G_OS_WIN32 */
1243 }
1244
1245 extern gchar *gbr_find_pkg_data_dir (const gchar * default_pkg_data_dir, const gchar * pkg_name);
1246
1247 static gchar *DENEMO_datadir = NULL;
1248 const gchar *
get_system_data_dir()1249 get_system_data_dir ()
1250 {
1251 //DENEMO_datadir?g_debug("datadir is %s at %p", DENEMO_datadir, DENEMO_datadir):g_debug("datadir not yet set");
1252 if (DENEMO_datadir == NULL)
1253 {
1254 #ifdef G_OS_WIN32
1255 gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL);
1256 DENEMO_datadir = g_build_filename (rootdir, "share", "denemo", NULL);
1257 g_message ("rootdir=%s", rootdir);
1258 g_message ("datadir=%s", DENEMO_datadir);
1259 g_free (rootdir);
1260 #else /* not G_OS_WIN32 */
1261
1262 #ifdef _MACH_O_
1263
1264 {
1265 char path[1024];
1266 guint size = sizeof (path);
1267 _NSGetExecutablePath (path, &size);
1268 gchar *bindir = (gchar *) g_malloc (size);
1269 if (_NSGetExecutablePath (bindir, &size) == 0)
1270 g_message ("Using bin path %s", bindir);
1271 else
1272 g_critical ("Cannot get bin dir");
1273 DENEMO_datadir = g_build_filename (g_path_get_dirname (bindir), "..", "share", "denemo", NULL);
1274 g_message ("OSX set data dir to %s", DENEMO_datadir);
1275 }
1276 #else
1277 #ifndef ENABLE_BINRELOC
1278 DENEMO_datadir = g_strdup (PKGDATADIR);
1279 #else
1280 DENEMO_datadir = gbr_find_pkg_data_dir (PKGDATADIR, PKGNAME);
1281 #endif //ENABLE_BINRELOC
1282
1283 #endif //_MACH_O_
1284 #endif /* not G_OS_WIN32 */
1285 }
1286 return DENEMO_datadir;
1287 }
1288
1289 const gchar *
get_prefix_dir(void)1290 get_prefix_dir (void)
1291 {
1292 gchar *prefix;
1293 #ifdef G_OS_WIN32
1294 prefix = g_win32_get_package_installation_directory (NULL, NULL);
1295 #else /* not G_OS_WIN32 */
1296 #ifdef _MACH_O_
1297 {
1298 char path[1024];
1299 guint size = sizeof (path);
1300 _NSGetExecutablePath (path, &size);
1301 gchar *bindir = (gchar *) g_malloc (size);
1302 if (_NSGetExecutablePath (bindir, &size) == 0)
1303 {
1304 prefix = g_build_filename (bindir, "..", "..", NULL);
1305 g_message ("OSX set data prefix to %s", prefix);
1306 }
1307 else
1308 g_critical ("Cannot get bin dir");
1309 }
1310 #else
1311
1312 #ifndef ENABLE_BINRELOC
1313 prefix = g_strdup (PREFIX);
1314 #else
1315 prefix = gbr_find_prefix (PREFIX);
1316 #endif //ENABLE_BINRELOC
1317
1318 #endif //_MACH_O_
1319 #endif //G_OS_WIN32
1320 return prefix;
1321 }
1322 static gchar *DENEMO_bindir = NULL;
1323 const gchar *
get_system_bin_dir(void)1324 get_system_bin_dir (void)
1325 {
1326
1327 if (DENEMO_bindir == NULL)
1328 {
1329 #ifdef G_OS_WIN32
1330 gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL);
1331 DENEMO_bindir = g_build_filename (rootdir, "bin", NULL);
1332 g_message ("rootdir=%s", rootdir);
1333 g_message ("bindir=%s", DENEMO_bindir);
1334 g_free (rootdir);
1335 #else /* not G_OS_WIN32 */
1336
1337 #ifdef _MACH_O_
1338
1339 {
1340 char path[1024];
1341 guint size = sizeof (path);
1342 _NSGetExecutablePath (path, &size);
1343 gchar *bin = (gchar *) g_malloc (size);
1344 if (_NSGetExecutablePath (bin, &size) == 0)
1345 {
1346 DENEMO_bindir = g_build_filename (bin, "..", NULL);
1347 g_message ("Using bin path %s", DENEMO_bindir);
1348 }
1349 else
1350 g_critical ("Cannot get bin dir");
1351
1352 g_message ("OSX set bin dir to %s", DENEMO_bindir);
1353 }
1354 #else
1355
1356 #ifndef ENABLE_BINRELOC
1357 DENEMO_bindir = g_strdup (BINDIR);
1358 #else
1359 DENEMO_bindir = gbr_find_bin_dir (BINDIR);
1360 #endif //ENABLE_BINRELOC
1361
1362 #endif //_MACH_O_
1363 #endif /* not G_OS_WIN32 */
1364 }
1365 return DENEMO_bindir;
1366 }
1367
1368 /** UNUSED
1369 const gchar *
1370 get_system_conf_dir ()
1371 {
1372 static gchar *confdir = NULL;
1373 if (confdir == NULL)
1374 {
1375 #ifdef G_OS_WIN32
1376 gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL);
1377 confdir = g_build_filename (rootdir, "etc", "denemo", NULL);
1378 g_free (rootdir);
1379 #else // not G_OS_WIN32
1380 #ifdef _MACH_O_
1381
1382 {
1383 char path[1024];
1384 guint size = sizeof (path);
1385 _NSGetExecutablePath (path, &size);
1386 gchar *bindir = (gchar *) g_malloc (size);
1387 if (_NSGetExecutablePath (bindir, &size) == 0)
1388 g_debug ("using bin path %s\n", bindir);
1389 else
1390 g_critical ("Cannot get bin dir\n");
1391 confdir = g_build_filename (g_path_get_dirname (bindir), "..", "etc", "denemo", NULL);
1392 g_debug ("OSX set conf dir to %s\n", confdir);
1393 }
1394 #else
1395
1396 #ifndef ENABLE_BINRELOC
1397 confdir = g_build_filename (SYSCONFDIR, NULL);
1398 #else
1399 confdir = g_build_filename (gbr_find_etc_dir (SYSCONFDIR), "denemo", NULL);
1400 #endif //ENABLE_BINRELOC
1401
1402 #endif //_MACH_O_
1403 #endif // not G_OS_WIN32
1404 }
1405 return confdir;
1406 }
1407 */
1408
1409 const gchar *
get_system_locale_dir()1410 get_system_locale_dir ()
1411 {
1412 static gchar *localedir = NULL;
1413 if (localedir == NULL)
1414 {
1415 #ifdef G_OS_WIN32
1416 gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL);
1417 localedir = g_build_filename (rootdir, "share", "locale", NULL);
1418 g_free (rootdir);
1419 #else /* not G_OS_WIN32 */
1420 #ifdef _MACH_O_
1421
1422 {
1423 char path[1024];
1424 guint size = sizeof (path);
1425 _NSGetExecutablePath (path, &size);
1426 gchar *bindir = (gchar *) g_malloc (size);
1427 if (_NSGetExecutablePath (bindir, &size) == 0)
1428 g_message ("Using bin path %s", bindir);
1429 else
1430 g_critical ("Cannot get bin dir");
1431 localedir = g_build_filename (g_path_get_dirname (bindir), "..", "share", "locale", NULL);
1432 g_message ("OSX set locale dir to %s", localedir);
1433 }
1434 #else
1435 #ifndef ENABLE_BINRELOC
1436 /* it seems to be the standard way (no binreloc)
1437 * to set the path of translations this way:
1438 * messages are in $LOCALEDIR/$LANG/denemo
1439 */
1440 localedir = g_strdup (LOCALEDIR);
1441 #else /* ENABLE_BINRELOC */
1442 /* binreloc says it is disabled even with built thanks to
1443 * --enable-binreloc... So, searhing falls back to
1444 * $LOCALEDIR/denemo/$LANG which is not a valid path
1445 */
1446 localedir = gbr_find_locale_dir (LOCALEDIR);
1447 #endif /* ENABLE_BINRELOC */
1448 #endif
1449 #endif /* not G_OS_WIN32 */
1450 }
1451 return localedir;
1452 }
1453
1454 const gchar*
get_system_font_dir()1455 get_system_font_dir(){
1456 static gchar* fontdir = NULL;
1457 if(fontdir == NULL)
1458 {
1459 #ifdef G_OS_WIN32
1460 gchar *prefix = g_win32_get_package_installation_directory (NULL, NULL);
1461 #else
1462 gchar *prefix = g_build_filename (get_prefix_dir (), NULL);
1463 #endif
1464 fontdir = g_build_filename (prefix, "share", "fonts", "truetype", "denemo", NULL);
1465 }
1466 return fontdir;
1467 }
1468
1469 void
kill_process(GPid pid)1470 kill_process (GPid pid)
1471 {
1472 #ifdef G_OS_WIN32
1473 TerminateProcess (pid, 0);
1474 #else /* not G_OS_WIN32 */
1475 kill (pid, SIGTERM);
1476 #endif /* not G_OS_WIN32 */
1477 g_spawn_close_pid (pid);
1478 }
1479
1480
1481 void
init_denemo_notenames(void)1482 init_denemo_notenames (void)
1483 {
1484
1485 define_scheme_literal_variable ("Denemo-Note0", NOTE0, NULL);
1486 define_scheme_literal_variable ("Denemo-Rest0", REST0, NULL);
1487 define_scheme_literal_variable ("Denemo-Note1", NOTE1, NULL);
1488 define_scheme_literal_variable ("Denemo-Rest1", REST1, NULL);
1489 define_scheme_literal_variable ("Denemo-Note2", NOTE2, NULL);
1490 define_scheme_literal_variable ("Denemo-Rest2", REST2, NULL);
1491 define_scheme_literal_variable ("Denemo-Note3", NOTE3, NULL);
1492 define_scheme_literal_variable ("Denemo-Rest3", REST3, NULL);
1493 define_scheme_literal_variable ("Denemo-Note4", NOTE4, NULL);
1494 define_scheme_literal_variable ("Denemo-Rest4", REST4, NULL);
1495 define_scheme_literal_variable ("Denemo-Note5", NOTE5, NULL);
1496 define_scheme_literal_variable ("Denemo-Rest5", REST5, NULL);
1497 define_scheme_literal_variable ("Denemo-Note6", NOTE6, NULL);
1498 define_scheme_literal_variable ("Denemo-Rest6", REST6, NULL);
1499 define_scheme_literal_variable ("Denemo-Note7", NOTE7, NULL);
1500 define_scheme_literal_variable ("Denemo-Rest7", REST7, NULL);
1501 define_scheme_literal_variable ("Denemo-Note8", NOTE8, NULL);
1502 define_scheme_literal_variable ("Denemo-Rest8", REST8, NULL);
1503
1504 }
1505
1506 #define HIGHLIGHT "<span background=\"lightblue\"> "
1507
1508 /* markup the passed string to be in the denemo music font
1509 * caller must free the returned string
1510 */
1511 gchar *
music_font(gchar * str)1512 music_font (gchar * str)
1513 {
1514 GString *s = g_string_new ("");
1515 gint c = *str;
1516 for (c = *str; c; c = *++str)
1517 switch (c)
1518 {
1519 case '0':
1520 g_string_append (s, " " NOTE0 " ");
1521 break;
1522 case HIGHLIGHT_OFFSET + '0':
1523 g_string_append (s, HIGHLIGHT NOTE0 " </span>");
1524 break;
1525
1526 case '1':
1527 g_string_append (s, " " NOTE1 " ");
1528 break;
1529 case HIGHLIGHT_OFFSET + '1':
1530 g_string_append (s, HIGHLIGHT NOTE1 " </span>");
1531 break;
1532 case '2':
1533 g_string_append (s, " " NOTE2 " ");
1534 break;
1535 case HIGHLIGHT_OFFSET + '2':
1536 g_string_append (s, HIGHLIGHT NOTE2 " </span>");
1537 break;
1538 case '3':
1539 g_string_append (s, " " NOTE3 " ");
1540 break;
1541 case HIGHLIGHT_OFFSET + '3':
1542 g_string_append (s, HIGHLIGHT NOTE3 " </span>");
1543 break;
1544 case '4':
1545 g_string_append (s, " " NOTE4 " ");
1546 break;
1547 case HIGHLIGHT_OFFSET + '4':
1548 g_string_append (s, HIGHLIGHT NOTE4 " </span>");
1549 break;
1550 case '5':
1551 g_string_append (s, " " NOTE5 " ");
1552 break;
1553 case HIGHLIGHT_OFFSET + '5':
1554 g_string_append (s, HIGHLIGHT NOTE5 " </span>");
1555 break;
1556 case '6':
1557 g_string_append (s, " " NOTE6 " ");
1558 break;
1559 case HIGHLIGHT_OFFSET + '6':
1560 g_string_append (s, HIGHLIGHT NOTE6 " </span>");
1561 break;
1562 case '7':
1563 g_string_append (s, " " NOTE7 " ");
1564 break;
1565 case HIGHLIGHT_OFFSET + '7':
1566 g_string_append (s, HIGHLIGHT NOTE7 " </span>");
1567 break;
1568 case '8':
1569 g_string_append (s, " " NOTE8 " ");
1570 break;
1571 case HIGHLIGHT_OFFSET + '8':
1572 g_string_append (s, HIGHLIGHT NOTE8 " </span>");
1573 break;
1574
1575 case 'r':
1576 g_string_append (s, " " REST0 " ");
1577 break;
1578 case HIGHLIGHT_OFFSET + 'r':
1579 g_string_append (s, HIGHLIGHT REST0 " </span>");
1580 break;
1581
1582 case 's':
1583 g_string_append (s, " " REST1 " ");
1584 break;
1585 case HIGHLIGHT_OFFSET + 's':
1586 g_string_append (s, HIGHLIGHT REST1 " </span>");
1587 break;
1588 case 't':
1589 g_string_append (s, " " REST2 " ");
1590 break;
1591 case HIGHLIGHT_OFFSET + 't':
1592 g_string_append (s, HIGHLIGHT REST2 " </span>");
1593 break;
1594 case 'u':
1595 g_string_append (s, " " REST3 " ");
1596 break;
1597 case HIGHLIGHT_OFFSET + 'u':
1598 g_string_append (s, HIGHLIGHT REST3 " </span>");
1599 break;
1600 case 'v':
1601 g_string_append (s, " " REST4 " ");
1602 break;
1603 case HIGHLIGHT_OFFSET + 'v':
1604 g_string_append (s, HIGHLIGHT REST4 " </span>");
1605 break;
1606 case 'w':
1607 g_string_append (s, " " REST5 " ");
1608 break;
1609 case HIGHLIGHT_OFFSET + 'w':
1610 g_string_append (s, HIGHLIGHT REST5 " </span>");
1611 break;
1612 case 'x':
1613 g_string_append (s, " " REST6 " ");
1614 break;
1615 case HIGHLIGHT_OFFSET + 'x':
1616 g_string_append (s, HIGHLIGHT REST6 " </span>");
1617 break;
1618 case 'y':
1619 g_string_append (s, " " REST7 " ");
1620 break;
1621 case HIGHLIGHT_OFFSET + 'y':
1622 g_string_append (s, HIGHLIGHT REST7 " </span>");
1623 break;
1624 case 'z':
1625 g_string_append (s, " " REST8 " ");
1626 break;
1627 case HIGHLIGHT_OFFSET + 'z':
1628 g_string_append (s, HIGHLIGHT REST8 " </span>");
1629 break;
1630 default:
1631 g_string_append_c (s, c);
1632 }
1633 return g_string_free (s, FALSE);
1634
1635 }
1636
1637 void
set_title_bar(DenemoProject * gui)1638 set_title_bar (DenemoProject * gui)
1639 {
1640 if(Denemo.non_interactive)
1641 return;
1642 gchar *title;
1643 if (gui->tabname && gui->tabname->len)
1644 title = gui->tabname->str;
1645 else
1646 title = _("(Untitled)");
1647 title = g_strdup_printf ("%s%c", title, gui->notsaved ? '*' : ' ');
1648 gtk_window_set_title (GTK_WINDOW (Denemo.window), title);
1649 gchar *base = g_path_get_basename (title);
1650 gint index = g_list_index (Denemo.projects, gui);
1651 if(index < 0)
1652 {
1653 g_critical ("project is %p is not in list of projects, first tab is %p\n", gui, Denemo.projects->data);
1654 return;
1655
1656 }
1657 GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (Denemo.notebook), index);
1658 if (page==NULL)
1659 {
1660 g_critical ("Bad page, passed project is %p, first tab is %p\n", gui, Denemo.projects->data);
1661 return;
1662
1663 }
1664 gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (Denemo.notebook), page, base);
1665
1666 gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (Denemo.notebook), page, base);
1667
1668
1669 g_free (title);
1670 g_free (base);
1671 }
1672
1673 static const gchar *
enshift_string(gint enshift)1674 enshift_string (gint enshift)
1675 {
1676 switch (enshift)
1677 {
1678 case -2:
1679 return "";
1680 case -1:
1681 return "♭";
1682 case 0:
1683 return " ";
1684 case 1:
1685 return "♯";
1686 case 2:
1687 return "";
1688 default:
1689 return _("Error");
1690 }
1691 }
1692
1693 gint64 thetime;
1694
start_editing_timer(void)1695 static void start_editing_timer (void)
1696 {
1697
1698 thetime = g_get_monotonic_time ();
1699
1700 }
stop_editing_timer(void)1701 static void stop_editing_timer (void)
1702 {
1703 gint64 thistime = g_get_monotonic_time ();
1704 if ((thetime>0) && (thetime<thistime))
1705 Denemo.project->total_edit_time += (thistime - thetime)/1000000;
1706 }
1707
reset_editing_timer(void)1708 void reset_editing_timer (void)
1709 {
1710 thetime = 0;
1711 }
1712
time_spent_editing()1713 gchar *time_spent_editing()
1714 {
1715 gint seconds = Denemo.project->total_edit_time;
1716 gint days = seconds/(24*60*60);
1717 gint hours;
1718 gint minutes;
1719
1720 seconds -= days*(24*60*60);
1721 hours = (seconds/(60*60));
1722 seconds -= hours*(60*60);
1723 minutes = seconds/60;
1724 seconds -= minutes*60;
1725 return g_strdup_printf("%d days %d hours %d minutes %d seconds\n", days, hours, minutes, seconds);
1726 }
1727
1728 /* set the status of the current musical score - its change count and
1729 title bar and status bars.
1730 DenemoProject *gui the musical score.
1731 gboolean change TRUE = a change has just been made
1732 FALSE = the score is to be assumed saved
1733 */
1734 void
score_status(DenemoProject * gui,gboolean change)1735 score_status (DenemoProject * gui, gboolean change)
1736 {
1737 if (change)
1738 {
1739 gboolean just_changed = !gui->notsaved;
1740 gui->notsaved = TRUE;
1741 gui->changecount++;
1742 gui->movement->changecount++;
1743 if (just_changed)
1744 if(!Denemo.non_interactive)
1745 start_editing_timer();
1746
1747 }
1748 else
1749 {
1750 gui->notsaved = FALSE;
1751 if(!Denemo.non_interactive)
1752 stop_editing_timer();
1753 }
1754 if(!Denemo.non_interactive)
1755 {
1756 set_title_bar (gui);
1757 write_status (gui);
1758 }
1759 }
1760
1761 /**
1762 * If the curObj is a chord with a note(s)
1763 * return the first note at or below cursory, or the last note
1764 * else return NULL
1765 */
1766 note *
findnote(DenemoObject * curObj,gint cursory)1767 findnote (DenemoObject * curObj, gint cursory)
1768 {
1769 note *curnote = NULL;
1770 if (curObj && curObj->type == CHORD && ((chord *) curObj->object)->notes)
1771 {
1772 GList *notes = ((chord *) curObj->object)->notes;
1773 for (; notes; notes = notes->next)
1774 {
1775 curnote = (note *) notes->data;
1776 //g_debug("comparing %d and %d\n", cursory, curnote->y);
1777 if (cursory <= curnote->mid_c_offset)
1778 break;
1779 }
1780
1781 }
1782 return curnote;
1783 }
1784 /**
1785 * If the curObj is a chord with a note(s)
1786 * return the note at cursory else return NULL
1787 */
1788 note *
findnote_strict(DenemoObject * curObj,gint cursory)1789 findnote_strict (DenemoObject * curObj, gint cursory)
1790 {
1791 note *curnote = NULL;
1792 if (curObj && curObj->type == CHORD && ((chord *) curObj->object)->notes)
1793 {
1794 GList *notes = ((chord *) curObj->object)->notes;
1795 for (; notes; notes = notes->next)
1796 {
1797 curnote = (note *) notes->data;
1798 //g_debug("comparing %d and %d\n", cursory, curnote->y);
1799 if (cursory == curnote->mid_c_offset)
1800 return curnote;
1801 }
1802
1803 }
1804 return NULL;
1805 }
1806
1807 /* get a fret diagram for the chord at the cursor or before the cursor if not on the chord */
get_fretdiagram_as_markup(void)1808 gchar *get_fretdiagram_as_markup (void)
1809 {
1810 DenemoProject *gui = Denemo.project;
1811 DenemoObject *curObj;
1812 if (!Denemo.project || !(Denemo.project->movement) || !(Denemo.project->movement->currentobject) || !(curObj = Denemo.project->movement->currentobject->data) || !(DENEMO_OBJECT_TYPE_NAME (curObj)))
1813 return NULL;
1814 if(curObj->type != CHORD && Denemo.project->movement->currentobject->next)
1815 curObj = Denemo.project->movement->currentobject->next->data;
1816 if (gui->lilysync != gui->changecount)
1817 refresh_lily_cb (NULL, Denemo.project);
1818 if (curObj->lilypond)
1819 {
1820 gchar *text = g_strdup_printf ("\\score{\n\\DenemoGlobalTranspose\n\\new FretBoards {%s}\n\\layout{indent=0.0}\n}", curObj->lilypond);
1821 return text;
1822 }
1823 return NULL;
1824 }
1825
1826 /* get a chord symbol for the chord at the cursor or before the cursor if not on the chord */
get_fakechord_as_markup(gchar * size,gchar * font)1827 gchar *get_fakechord_as_markup (gchar *size, gchar *font)
1828 {
1829 DenemoProject *gui = Denemo.project;
1830 DenemoObject *curObj;
1831 if (!Denemo.project || !(Denemo.project->movement) || !(Denemo.project->movement->currentobject) || !(curObj = Denemo.project->movement->currentobject->data) || !(DENEMO_OBJECT_TYPE_NAME (curObj)))
1832 return NULL;
1833 if((curObj->type != CHORD) && Denemo.project->movement->currentobject->next)
1834 curObj = Denemo.project->movement->currentobject->next->data;
1835 if (gui->lilysync != gui->changecount)
1836 refresh_lily_cb (NULL, Denemo.project);
1837 if (curObj->lilypond)
1838 {
1839 gchar *text = g_strdup_printf ("\\score{\n\\DenemoGlobalTranspose\n\\new ChordNames {\n\\override ChordName.font-name = #'\"%s\"\n\\override ChordName.font-size = #%s %s}\n\\layout{indent=0.0}\n}\n", font, size, curObj->lilypond);
1840 return text;
1841 }
1842 return NULL;
1843 }
1844
1845 /****************
1846 * write the status bar
1847
1848 ********************/
1849
1850 void
write_status(DenemoProject * gui)1851 write_status (DenemoProject * gui)
1852 {
1853 if(Denemo.non_interactive)
1854 return;
1855
1856 gint minutes = 0;
1857 gdouble seconds = 0.0;
1858 gdouble early = 0.0, late = 0.0;
1859 gchar *selection;
1860 if (gui->movement == NULL)
1861 return;
1862
1863 static GList *last_object;
1864
1865 if (gui->movement->currentobject)
1866 {
1867 if (last_object != gui->movement->currentobject)
1868 {
1869 if(get_wysiwyg_info()->stage != TypesetForPlaybackView)
1870 get_wysiwyg_info()->stage = STAGE_NONE;//remove the mark in the printview window as the cursor has moved
1871 get_wysiwyg_info()->Mark.width = 0;
1872 gtk_widget_queue_draw (Denemo.printarea);
1873 }
1874 last_object = gui->movement->currentobject;
1875 }
1876
1877 if (gui->movement->currentobject && gui->movement->currentobject->data)
1878 {
1879 DenemoObject *curObj = gui->movement->currentobject->data;
1880 if ((gui->movement->smfsync == gui->movement->changecount))
1881 {
1882 early = curObj->earliest_time, late = curObj->latest_time;
1883 }
1884
1885 switch (curObj->type)
1886 {
1887 case CHORD:
1888 {
1889 chord *thechord = ((chord *) curObj->object);
1890 selection = g_strdup_printf ("%s%s%s%s%s%s%s%s%s",
1891 thechord->notes ? (g_list_length (thechord->notes) > 1 ? _("Chord ") : _("Note ")) : _("Rest "), thechord->slur_begin_p ? _(", begin slur") : "", thechord->slur_end_p ? _(", end slur") : "", thechord->is_tied ? _(", tied") : "", thechord->crescendo_begin_p ? _(", begin cresc.") : "", thechord->crescendo_end_p ? _(", end cresc.") : "", thechord->diminuendo_begin_p ? _(", begin dim.") : "", thechord->diminuendo_end_p ? _(", end dim.") : "", thechord->is_grace ? _(", grace note") : "");
1892 if (thechord->notes)
1893 {
1894 GList *g;
1895 for (g = thechord->notes; g; g = g->next)
1896 {
1897 note *thenote = (note *) g->data;
1898 GList *h;
1899 for (h = thenote->directives; h; h = h->next)
1900 {
1901 DenemoDirective *directive = (DenemoDirective *) h->data;
1902 if (directive->postfix || directive->prefix)
1903 {
1904 gchar *old = selection;
1905 selection = g_strdup_printf ("%.50s (%s) %.50s", directive->prefix ? directive->prefix->str : "", selection, directive->postfix ? directive->postfix->str : "");
1906 g_free (old);
1907 }
1908 }
1909 }
1910 }
1911 }
1912 break;
1913
1914 case TUPOPEN:
1915 selection = g_strdup_printf (_("Tuplet %d/%d"), ((tupopen *) curObj->object)->numerator, ((tupopen *) curObj->object)->denominator);
1916 break;
1917 case TUPCLOSE:
1918 selection = g_strdup_printf (_("End tuplet"));
1919 break;
1920 case CLEF:
1921 selection = g_strdup_printf (_("Clef change"));
1922 break;
1923 case TIMESIG:
1924 selection = g_strdup_printf (_("Time signature change"));
1925 break;
1926 case KEYSIG:
1927 selection = g_strdup_printf (_("Key signature change"));
1928 break;
1929 case STEMDIRECTIVE:
1930 selection = g_strdup_printf (_("Stem directive: %s"), ((stemdirective *) curObj->object)->type == DENEMO_STEMDOWN ? _("stem down") : ((stemdirective *) curObj->object)->type == DENEMO_STEMUP ? _("stem up") : _("normal stemming"));
1931 break;
1932 case DYNAMIC:
1933 selection = g_strdup_printf (_("Dynamic: %s"), ((dynamic *) curObj->object)->type->str);
1934 break;
1935
1936 case LILYDIRECTIVE:
1937 {
1938 DenemoDirective *directive = (DenemoDirective *) curObj->object;
1939 selection = g_strdup_printf (_("Directive:(%.20s) %.20s%.50s"), directive->tag ? directive->tag->str : _("Unknown Tag"), directive->x ? _("Not all layouts") : directive->y ? _("Only for one Layout") : "", directive->postfix ? directive->postfix->str : directive->prefix ? directive->prefix->str : directive->graphic_name ? directive->graphic_name->str : directive->display ? directive->display->str : "empty");
1940 }
1941 break;
1942 default:
1943 selection = g_strdup_printf (_("Cursor on an unknown object"));
1944 }
1945
1946 //DenemoMeasure *measure = gui->movement->currentmeasure->data;
1947 //selection = g_strdup_printf ("%s %s %d/%d %d", selection, curObj->clef?get_clef_name (curObj->clef->type):"NULL", measure->timesig?measure->timesig->time1:0, measure->timesig?measure->timesig->time2:0, curObj->keysig?curObj->keysig->number:0xFFFF);
1948
1949
1950 }
1951 else
1952 selection = g_strdup_printf (_("Cursor not on any object"));
1953
1954 GString *status = g_string_new (_("Movement"));
1955 gint index = g_list_index (gui->movements, gui->movement);
1956 const gchar *dur ="";
1957 switch (get_prevailing_duration())
1958 {
1959 case 0: dur = NOTE0;
1960 break;
1961 case 1: dur = NOTE1;
1962 break;
1963 case 2: dur = NOTE2;
1964 break;
1965 case 3: dur = NOTE3;
1966 break;
1967 case 4: dur = NOTE4;
1968 break;
1969 case 5: dur = NOTE5;
1970 break;
1971 case 6: dur = NOTE6;
1972 break;
1973 case 7: dur = NOTE7;
1974 break;
1975 case 8: dur = NOTE8;
1976 break;
1977
1978
1979 }
1980 g_string_printf (status, "%s%s %d: %s: ", enshift_string (gui->movement->pending_enshift), dur, index + 1, selection);
1981
1982
1983
1984 if (gui->movement->smf && (gui->movement->smfsync == gui->movement->changecount) && Denemo.prefs.playback_controls)
1985 g_string_append_printf (status, _("%d min %.2f sec %.2f %.2f"), minutes, seconds, early, late);
1986 else
1987 g_string_append_printf (status, _(" Staff %d Measure %d Position %d %s"), gui->movement->currentstaffnum, gui->movement->currentmeasurenum, gui->movement->cursor_x + 1, gui->movement->cursor_appending ? _("Appending") : _("Not Appending") /*not understood this one... , gui->movement->cursoroffend?"Off End":"Not Off End" */ );
1988
1989 if (Denemo.prefs.midi_in_controls)
1990 {
1991 gchar *thesharp = sharpest ();
1992 gchar *theflat = flattest ();
1993 g_string_append_printf (status, " |%s - %s|", theflat, thesharp);
1994 g_free (theflat);
1995 g_free (thesharp);
1996 }
1997
1998 g_free (selection);
1999 gchar *end_valid;
2000 if (!g_utf8_validate (status->str, -1, (const gchar **)&end_valid))
2001 *end_valid = '\0';
2002 gtk_label_set_text (GTK_LABEL (Denemo.statuslabel), status->str);
2003
2004 g_string_free (status, TRUE);
2005 update_object_info ();
2006 }
2007
2008 void
write_input_status(void)2009 write_input_status (void)
2010 {
2011 if(Denemo.non_interactive)
2012 return;
2013 gtk_label_set_markup (GTK_LABEL (Denemo.input_label), Denemo.input_filters->str);
2014 }
2015
2016 /**
2017 * Display a message box asking primary & secondary messages
2018 * @return TRUE if the OK button was clicked or Enter pressed
2019 */
2020 gboolean
confirm(gchar * primary,gchar * secondary)2021 confirm (gchar * primary, gchar * secondary)
2022 {
2023 GtkWidget *dialog;
2024 gboolean r = 0;
2025
2026 dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", primary);
2027
2028 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary);
2029 gtk_widget_show_all (dialog);
2030 r = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES);
2031 gtk_widget_destroy (dialog);
2032 return r;
2033 }
2034
2035 gboolean
choose_option(gchar * title,gchar * primary,gchar * secondary)2036 choose_option (gchar *title, gchar * primary, gchar * secondary)
2037 {
2038 GtkWidget *dialog;
2039 gboolean r;
2040 dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
2041 primary, GTK_RESPONSE_ACCEPT, secondary, GTK_RESPONSE_REJECT, NULL);
2042 //g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
2043 gtk_window_set_urgency_hint (GTK_WINDOW (dialog), TRUE);
2044 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window));
2045 gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
2046 r = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT);
2047 gtk_widget_destroy (dialog);
2048 return r;
2049 }
2050
2051
2052 /* free a GString and the string it holds, and set the pointer to it to NULL */
2053 void
nullify_gstring(GString ** s)2054 nullify_gstring (GString ** s)
2055 {
2056 if (*s)
2057 g_string_free (*s, TRUE);
2058 *s = NULL;
2059 }
2060
2061 /* dialog to get a filename from the user
2062 */
choose_file(gchar * title,gchar * startdir,GList * extensions)2063 gchar *choose_file (gchar *title, gchar *startdir, GList *extensions)
2064 {
2065 GtkWidget *dialog;
2066 gchar *filename = NULL;
2067 dialog = gtk_file_chooser_dialog_new (title,
2068 NULL,
2069 GTK_FILE_CHOOSER_ACTION_OPEN,
2070 _("_Cancel"), GTK_RESPONSE_CANCEL,
2071 _("_Open"), GTK_RESPONSE_ACCEPT,
2072 NULL);
2073 GtkFileFilter *filter = gtk_file_filter_new();
2074 GString *filter_description = g_string_new ("");
2075 for(extensions;extensions;extensions=extensions->next)
2076 {
2077 gtk_file_filter_add_pattern (filter,(gchar*)extensions->data);
2078 g_string_append_printf (filter_description, "%s ", (gchar*)extensions->data);
2079 }
2080 gtk_file_filter_set_name (filter, filter_description->str);
2081 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
2082 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), startdir);
2083
2084 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2085 {
2086 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2087 }
2088
2089 gtk_widget_destroy (dialog);
2090 return filename;
2091 }
2092
2093
2094 static void
hide_windows(void)2095 hide_windows (void)
2096 {
2097 if (Denemo.prefs.hide_windows)
2098 {
2099 if(Denemo.window)
2100 gtk_widget_hide (Denemo.window);
2101 if(Denemo.printarea)
2102 gtk_widget_hide (gtk_widget_get_toplevel(Denemo.printarea));
2103 }
2104 }
2105 static void
show_windows(void)2106 show_windows (void)
2107 {
2108 if (Denemo.prefs.hide_windows)
2109 {
2110 if(Denemo.window)
2111 gtk_widget_show (Denemo.window);
2112 if(Denemo.printarea)
2113 gtk_widget_show (gtk_widget_get_toplevel(Denemo.printarea));
2114 }
2115 }
2116
2117 /**
2118 * Pops up a dialog box that has a text entry box and ok/cancel buttons
2119 * title is a title for the box.
2120 * initial_value is for the text entry box, or NULL if none.
2121 * instruction is a prompt for the user.
2122 * Returns a new value on Ok and NULL if cancelled.
2123 * The returned value should be freed by the caller.
2124 *
2125 */
2126
2127 gchar *
string_dialog_entry(DenemoProject * gui,gchar * title,gchar * instruction,gchar * initial_value)2128 string_dialog_entry (DenemoProject * gui, gchar * title, gchar * instruction, gchar * initial_value)
2129 {
2130 return string_dialog_entry_with_widget (gui, title, instruction, initial_value, NULL);
2131 }
2132
2133 /* as string_dialog_entry() but with extra widget */
2134 gchar *
string_dialog_entry_with_widget_opt(DenemoProject * gui,gchar * wlabel,gchar * direction,gchar * PreValue,GtkWidget * widget,gboolean modal)2135 string_dialog_entry_with_widget_opt (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gboolean modal)
2136 {
2137
2138 GtkWidget *dialog;
2139 GtkWidget *entry;
2140 GtkWidget *label;
2141 gchar *entry_string = NULL;
2142 //GString *string;
2143 entry = gtk_entry_new ();
2144
2145 dialog = modal ? gtk_dialog_new_with_buttons (wlabel, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, _("_Cancel"), GTK_RESPONSE_REJECT, NULL) : gtk_dialog_new_with_buttons (wlabel, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, NULL);
2146
2147 g_signal_connect (dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL); //if the user tries to dismiss the window, hide it, will that then hang??? It doesn't on GNOME using the right-click menu to close the window
2148
2149 label = gtk_label_new (direction);
2150 GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
2151 gtk_container_add (GTK_CONTAINER (content_area), label);
2152
2153 if (widget)
2154 gtk_container_add (GTK_CONTAINER (content_area), widget);
2155
2156 if (PreValue != NULL)
2157 {
2158 gtk_entry_set_text (GTK_ENTRY (entry), (gchar *) PreValue);
2159 }
2160 gtk_container_add (GTK_CONTAINER (content_area), entry);
2161
2162 gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
2163 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
2164 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
2165 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window));
2166 gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
2167 gtk_widget_show_all (dialog);
2168
2169 if (modal)
2170 {
2171 gtk_widget_grab_focus (entry);
2172 hide_windows();
2173
2174 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2175 {
2176 gchar *string = NULL;
2177 if (GTK_DIALOG (dialog))
2178 {
2179 entry_string = (gchar *) gtk_entry_get_text (GTK_ENTRY (entry));
2180 string = g_strdup (entry_string);
2181 gtk_widget_destroy (dialog);
2182 }
2183 show_windows();
2184 return string;
2185 }
2186 else
2187 {
2188 if (GTK_DIALOG (dialog))
2189 gtk_widget_destroy (dialog);
2190
2191 }
2192 show_windows();
2193 return NULL;
2194 }
2195 else
2196 {
2197 g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_main_quit), entry);
2198 gtk_main ();
2199 if (GTK_IS_WIDGET (entry))
2200 {
2201 entry_string = GTK_IS_WIDGET (entry) ? g_strdup ((gchar *) gtk_entry_get_text (GTK_ENTRY (entry))) : NULL;
2202 gtk_widget_destroy (dialog);
2203 return entry_string;
2204 }
2205 return NULL;
2206 }
2207 }
2208
2209 gchar *
string_dialog_entry_with_widget(DenemoProject * gui,gchar * wlabel,gchar * direction,gchar * PreValue,GtkWidget * widget)2210 string_dialog_entry_with_widget (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget)
2211 {
2212 return string_dialog_entry_with_widget_opt (gui, wlabel, direction, PreValue, widget, TRUE);
2213 }
2214
2215 /* as string_dialog_entry_with_widget() but gives a text editor instead of a single line editor */
2216 gchar *
string_dialog_editor_with_widget_opt(DenemoProject * gui,gchar * wlabel,gchar * direction,gchar * PreValue,GtkWidget * widget,gboolean modal,gpointer keypress_callback)2217 string_dialog_editor_with_widget_opt (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gboolean modal, gpointer keypress_callback)
2218 {
2219 GtkWidget *dialog;
2220 GtkWidget *textview;
2221 GtkWidget *label;
2222 textview = gtk_text_view_new ();
2223 GtkTextBuffer *textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
2224 gtk_text_buffer_set_text (textbuffer, PreValue ? PreValue : "", -1);
2225 GtkWidget *sw = gtk_scrolled_window_new (gtk_adjustment_new (1.0, 1.0, 2.0, 1.0, 4.0, 1.0), gtk_adjustment_new (1.0, 1.0, 2.0, 1.0, 4.0, 1.0));
2226
2227 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2228
2229 dialog = modal ? gtk_dialog_new_with_buttons (wlabel, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, _("_Cancel"), GTK_RESPONSE_REJECT, NULL) : gtk_dialog_new_with_buttons (wlabel, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, NULL);
2230 if(keypress_callback)
2231 g_signal_connect (G_OBJECT (textview), "key-press-event", G_CALLBACK (keypress_callback), textbuffer);
2232
2233 label = gtk_label_new (direction);
2234 GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
2235 gtk_box_pack_start (GTK_BOX (content_area), label, FALSE, TRUE, 0);
2236 if (widget)
2237 {
2238 gtk_box_pack_start (GTK_BOX (content_area), widget, FALSE, TRUE, 0);
2239 g_object_set_data (G_OBJECT (widget), "textbuffer", textbuffer);
2240 g_object_set_data (G_OBJECT (widget), "textview", textview);
2241 }
2242 gtk_container_add (GTK_CONTAINER (sw), textview);
2243 gtk_box_pack_start (GTK_BOX (content_area), sw, TRUE, TRUE, 0);
2244
2245 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
2246
2247
2248 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
2249 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window));
2250 gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
2251 gtk_widget_show_all (dialog);
2252 gtk_widget_grab_focus (textview);
2253 if (modal)
2254 {
2255 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2256 {
2257 GtkTextIter startiter, enditer;
2258 gtk_text_buffer_get_start_iter (textbuffer, &startiter);
2259 gtk_text_buffer_get_end_iter (textbuffer, &enditer);
2260 gchar *text = gtk_text_buffer_get_text (textbuffer, &startiter, &enditer, FALSE);
2261 gtk_widget_destroy (dialog);
2262 return text;
2263 }
2264 else
2265 {
2266 gtk_widget_destroy (dialog);
2267 return NULL;
2268 }
2269 return NULL;
2270 }
2271 else
2272 {
2273 g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_main_quit), NULL);
2274 gtk_main ();
2275 if (GTK_IS_TEXT_BUFFER (textbuffer))
2276 {
2277 GtkTextIter startiter, enditer;
2278 gtk_text_buffer_get_start_iter (textbuffer, &startiter);
2279 gtk_text_buffer_get_end_iter (textbuffer, &enditer);
2280 gchar *text = gtk_text_buffer_get_text (textbuffer, &startiter, &enditer, FALSE);
2281 gtk_widget_destroy (dialog);
2282 return text;
2283 }
2284 else
2285 {
2286 return NULL;
2287 }
2288 }
2289 }
2290
2291 gchar *
string_dialog_editor_with_widget(DenemoProject * gui,gchar * wlabel,gchar * direction,gchar * PreValue,GtkWidget * widget,gpointer keypress_callback)2292 string_dialog_editor_with_widget (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gpointer keypress_callback)
2293 {
2294 return string_dialog_editor_with_widget_opt (gui, wlabel, direction, PreValue, widget, TRUE, keypress_callback);
2295 }
2296
2297 static gboolean
option_choice(GtkWidget * widget,gchar ** response)2298 option_choice (GtkWidget * widget, gchar ** response)
2299 {
2300 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
2301 *response = g_object_get_data (G_OBJECT (widget), "choice");
2302 //do not respond to the toggling off of other radio buttons
2303 return TRUE;
2304 }
2305
2306 static gint root_x;
2307 static gint root_y;
dialog_realize(GtkWidget * dialog)2308 static gboolean dialog_realize (GtkWidget *dialog) {
2309 if(root_x)
2310 gtk_window_move (GTK_WINDOW(dialog), root_x, root_y);
2311 return FALSE;
2312 }
2313
2314 /* run a dialog for the user to select a string from the NULL separated strings, str
2315 return NULL if user cancels.*/
2316 static gchar *
get_option_recursive(gchar * title,gchar * str,gint length,gboolean more)2317 get_option_recursive (gchar *title, gchar * str, gint length, gboolean more)
2318 {
2319 gchar *response = NULL;
2320 if (title==NULL)
2321 title = _("Select from List (or Cancel)");
2322 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2323 GTK_WINDOW (Denemo.window),
2324 (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
2325 _("_OK"), GTK_RESPONSE_ACCEPT,
2326 _("_Cancel"), GTK_RESPONSE_REJECT,
2327 NULL);
2328 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
2329 g_signal_connect_after (G_OBJECT(dialog), "realize", G_CALLBACK(dialog_realize), NULL);
2330 GtkWidget *vbox = gtk_vbox_new (FALSE, 1);
2331 GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
2332 gtk_container_add (GTK_CONTAINER (content_area), vbox);
2333
2334 gchar *opt;
2335 GtkWidget *widget1, *widget;
2336 for (opt = str; (opt - str) < length; opt += strlen (opt) + 1)
2337 {
2338 if (opt == str)
2339 {
2340 widget = widget1 = gtk_radio_button_new_with_label (NULL, opt);
2341 response = str; //this is the first radio button, we set response to be the input string ie first option, in case the user makes no choice, in which case the callback option_choice is not called.
2342 }
2343 else
2344 {
2345 widget = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (widget1), opt);
2346 }
2347 g_object_set_data (G_OBJECT (widget), "choice", (gpointer) opt);
2348 g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (option_choice), &response);
2349 gtk_container_add (GTK_CONTAINER (vbox), widget);
2350 }
2351
2352 if(more)
2353 {
2354 opt = _("More...");
2355 widget = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (widget1), opt);
2356 g_object_set_data (G_OBJECT (widget), "choice", (gpointer) opt);
2357 g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (option_choice), &response);
2358 gtk_container_add (GTK_CONTAINER (vbox), widget);
2359 }
2360 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window));
2361 gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
2362 gtk_widget_show_all (dialog);
2363 hide_windows ();
2364 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_REJECT)
2365 {
2366 response = NULL;
2367 }
2368 g_debug ("Returning contents of response is %s\n", response);
2369 gtk_window_get_position (GTK_WINDOW(dialog), &root_x, &root_y);
2370 gtk_widget_destroy (dialog);
2371 show_windows ();
2372 return response;
2373 }
2374 #define MAX_ITEMS (Denemo.prefs.max_menu_size)
get_option(gchar * title,gchar * str,gint length)2375 gchar *get_option (gchar *title, gchar * str, gint length)
2376 {
2377 gchar *opt;
2378 gint i;
2379 for (opt = str, i = 0; (opt - str) < length; i++, opt += strlen (opt) + 1)
2380 {
2381 if (i < MAX_ITEMS)
2382 continue;
2383 else
2384 {
2385 gchar *response = get_option_recursive (title, str, opt-str, TRUE);
2386 if(response && (!strcmp (response, _("More..."))))
2387 {
2388 length -= (opt - str);
2389 str = opt;
2390 i = 0;
2391 continue;
2392 }
2393 else return response;
2394 }
2395 }
2396 if ((opt - str) >= length)
2397 return get_option_recursive (title, str, length, FALSE);
2398 return NULL;
2399 }
2400
2401
2402
2403
2404 /* output text to the console (lilypond errors) window. If text==NULL clear window*/
2405 void
console_output(gchar * text)2406 console_output (gchar * text)
2407 {
2408 GtkTextIter enditer;
2409 GtkTextBuffer *buffer = gtk_text_view_get_buffer ((GtkTextView *) (Denemo.console));
2410 gtk_text_buffer_get_end_iter (buffer, &enditer);
2411 if (text)
2412 gtk_text_buffer_insert (buffer, &enditer, text, -1);
2413 else
2414 gtk_text_buffer_set_text (buffer, "", -1);
2415 }
2416
2417 /* returns an override flag ORd from all those in the list of directives,
2418 excluding ones with DENEMO_OVERRIDE_HIDDEN set */
2419 gint
get_override(GList * g)2420 get_override (GList * g)
2421 {
2422 gint ret = 0;
2423 for (; g; g = g->next)
2424 {
2425 DenemoDirective *d = g->data;
2426 if (!(d->override & DENEMO_OVERRIDE_HIDDEN))
2427 ret |= d->override;
2428 }
2429 return ret;
2430 }
2431
2432 //modfies name to truncate at the extension (a dot at end before any directory separators), returning a pointer to the extension chopped off
2433 // returns NULL if no extension, with name unchanged
2434
2435 gchar *
remove_extension(gchar * name)2436 remove_extension (gchar * name)
2437 {
2438 gchar *c;
2439 if (name)
2440 for (c = name + strlen (name); c != name; c--)
2441 {
2442 if (*c == '.')
2443 {
2444 *c = '\0';
2445 return c + 1;
2446 }
2447 if (*c == G_DIR_SEPARATOR)
2448 break;
2449 }
2450 return NULL;
2451 }
2452
2453 //modifies name, removing the extension and returns a newly allocated string
2454 //with the passed extension
2455 gchar *
substitute_extension(gchar * name,gchar * extension)2456 substitute_extension (gchar * name, gchar * extension)
2457 {
2458 (void) remove_extension (name);
2459 return g_strdup_printf ("%s.%s", name, extension);
2460 }
2461
2462 enum clefs
cleftypefromname(gchar * str)2463 cleftypefromname (gchar * str)
2464 {
2465 enum clefs ret = DENEMO_TREBLE_CLEF;
2466
2467 if (g_strcasecmp (str, "treble") == 0)
2468 ret = DENEMO_TREBLE_CLEF;
2469 else if (g_strcasecmp (str, "bass") == 0)
2470 ret = DENEMO_BASS_CLEF;
2471 else if (g_strcasecmp (str, "alto") == 0)
2472 ret = DENEMO_ALTO_CLEF;
2473 else if (g_strcasecmp (str, "\"g_8\"") == 0)
2474 ret = DENEMO_G_8_CLEF;
2475 else if (g_strcasecmp (str, "tenor") == 0)
2476 ret = DENEMO_TENOR_CLEF;
2477 else if (g_strcasecmp (str, "soprano") == 0)
2478 ret = DENEMO_SOPRANO_CLEF;
2479 g_free (str);
2480 return ret;
2481 }
2482
2483 gint
get_widget_height(GtkWidget * w)2484 get_widget_height (GtkWidget * w)
2485 {
2486 GtkAllocation allocation;
2487 gtk_widget_get_allocation (w, &allocation);
2488 return allocation.height;
2489 }
2490
2491 gint
get_widget_width(GtkWidget * w)2492 get_widget_width (GtkWidget * w)
2493 {
2494 GtkAllocation allocation;
2495 gtk_widget_get_allocation (w, &allocation);
2496 return allocation.width;
2497 }
2498
2499 void
switch_back_to_main_window(void)2500 switch_back_to_main_window (void)
2501 {
2502 if (Denemo.non_interactive)
2503 return;
2504 gtk_window_present (GTK_WINDOW (Denemo.window));
2505 gtk_widget_grab_focus (Denemo.scorearea);
2506 }
2507
2508 /* set all labels in the hierarchy below widget to use markup */
2509 void
use_markup(GtkWidget * widget)2510 use_markup (GtkWidget * widget)
2511 {
2512 if (!widget)
2513 return;
2514 if (GTK_IS_LABEL (widget))
2515 {
2516 gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
2517 }
2518 else if (GTK_IS_CONTAINER (widget))
2519 {
2520 GList *g = gtk_container_get_children (GTK_CONTAINER (widget));
2521 for (; g; g = g->next)
2522 use_markup (g->data);
2523 if (GTK_IS_MENU_ITEM (widget))
2524 {
2525 use_markup (gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)));
2526 }
2527 }
2528 }
2529
2530 #ifdef FAKE_TOOLTIPS
2531 static GtkWidget *TooltipDialog = NULL;
2532 static gboolean not_wanted = TRUE;
2533 static gboolean not_wanted_in_this_session = FALSE;
2534
no_longer_wanted(GtkWidget * w)2535 static gboolean no_longer_wanted (GtkWidget *w)
2536 {
2537 not_wanted = TRUE;
2538 if(Denemo.prefs.newbie)
2539 not_wanted_in_this_session = FALSE;
2540 TooltipDialog = NULL;
2541 return FALSE;
2542 }
show_tooltip(GtkWidget * w,GdkEvent * ev,gchar * text)2543 gboolean show_tooltip (GtkWidget *w, GdkEvent *ev, gchar *text)
2544 {
2545 static gchar *last_tooltip;
2546 if (Denemo.prefs.newbie)
2547 not_wanted_in_this_session = not_wanted = FALSE;
2548 if (not_wanted)
2549 return FALSE;
2550 if (not_wanted_in_this_session)
2551 return FALSE;
2552
2553 if (text && (*text) && (last_tooltip != text))
2554 {
2555 if (TooltipDialog)
2556 {
2557 gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (TooltipDialog), text);
2558 gtk_widget_show (TooltipDialog);
2559 }
2560 else
2561 {
2562 TooltipDialog = infodialog (text);
2563 g_signal_connect (G_OBJECT (TooltipDialog), "delete-event", G_CALLBACK (no_longer_wanted), NULL);
2564 }
2565 }
2566 last_tooltip = text;
2567
2568 return FALSE;//allow normal response
2569 }
free_tooltip(GtkWidget * w,gchar * tooltip)2570 void free_tooltip (GtkWidget *w, gchar *tooltip)
2571 {
2572 //g_print ("Freeing tooltip\n");
2573 g_free (tooltip);
2574 }
2575 #endif
2576 // Help for beginners using keyboard shortcuts
2577 static GtkWidget *KeyStrokes;
2578 static GtkWidget *KeyStrokeLabel;
2579 static GtkWidget *KeyStrokeHelp;
2580
2581
2582 void
KeyStrokeAwait(gchar * first_keypress)2583 KeyStrokeAwait (gchar * first_keypress)
2584 {
2585 KeyStrokeShow (first_keypress, DENEMO_NO_COMMAND, TwoKey);
2586 }
2587
2588 void
KeyStrokeDecline(gchar * first_keypress)2589 KeyStrokeDecline (gchar * first_keypress)
2590 {
2591 KeyStrokeShow (first_keypress, DENEMO_NO_COMMAND, SingleKey);
2592 }
2593
MouseGestureShow(gchar * str,gchar * help,DenemoShortcutType type)2594 void MouseGestureShow (gchar *str, gchar *help, DenemoShortcutType type)
2595 {
2596 gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), help);
2597 KeyStrokeShow (str, DENEMO_NO_COMMAND, type);
2598 }
KeyPlusMouseGestureShow(gchar * str,gint command_idx)2599 void KeyPlusMouseGestureShow(gchar *str, gint command_idx)
2600 {
2601 const gchar *label = lookup_label_from_idx (Denemo.map, command_idx);
2602 const gchar *tooltip = lookup_tooltip_from_idx (Denemo.map, command_idx);
2603 gchar *text = g_strdup_printf (_("Mouse shortcut <span font_desc=\"24\" foreground=\"blue\">%s</span>" " invokes command <span font_desc=\"24\" foreground=\"dark red\">%s</span>"), str, label);
2604
2605 gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Mouse Shortcut"));
2606 gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text);
2607 gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), tooltip);
2608 g_free (text);
2609 gtk_widget_show (KeyStrokes);
2610 }
2611
2612 void
KeyStrokeShow(gchar * str,gint command_idx,DenemoShortcutType type)2613 KeyStrokeShow (gchar * str, gint command_idx, DenemoShortcutType type)
2614 {
2615 if (str != NULL)
2616 {
2617 gchar *text;
2618 if (command_idx != DENEMO_NO_COMMAND)
2619 {
2620 const gchar *label = lookup_label_from_idx (Denemo.map, command_idx);
2621 const gchar *tooltip = lookup_tooltip_from_idx (Denemo.map, command_idx);
2622 if (type)
2623 {
2624 text = g_strdup_printf (_("Key Press <span font_desc=\"24\" foreground=\"blue\">%s</span>" " invokes command <span font_desc=\"24\" foreground=\"dark red\">%s</span>"), str, label);
2625
2626 }
2627 else
2628 {
2629 text = g_strdup_printf (_("Key Presses <span font_desc=\"24\" foreground=\"blue\">%s</span>" " invoke command <span font_desc=\"24\" foreground=\"dark red\">%s</span>"), str, label);
2630 }
2631 gtk_window_set_title (GTK_WINDOW (KeyStrokes), type==SingleKey ? _("Single Key Press") : _("Two Key Presses"));
2632 gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text);
2633 gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), tooltip);
2634 g_free (text);
2635 }
2636 else // no command
2637 {
2638 switch (type) {
2639 case SingleKey:
2640 gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Key Press"));
2641 gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), "");
2642 text = g_strdup_printf (_("Key Press <span font_desc=\"24\" foreground=\"blue\">%s</span>" " Is not a shortcut.\n%s"), str,
2643 (Denemo.project->view != DENEMO_MENU_VIEW)?
2644 _("(The menus are now restored in case you are lost.)"):"");
2645 break;
2646 case TwoKey:
2647 gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("First Key Press"));
2648 gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), "");
2649 text = g_strdup_printf (_("Key Press <span font_desc=\"24\" foreground=\"blue\">%s</span>" " Awaiting continuation"), str);
2650 break;
2651 case MouseGesture:
2652 gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Mouse"));
2653 text = g_strdup_printf (_("Mouse <span font_desc=\"24\" foreground=\"blue\">%s</span>"), str);
2654 break;
2655 case KeyPlusMouse:
2656 gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Key + Mouse"));
2657 text = g_strdup_printf (_("Key Press <span font_desc=\"24\" foreground=\"blue\">%s</span>"), str);
2658 break;
2659 default:
2660 g_warning("bad call");
2661 return;
2662 }
2663 gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text);
2664 g_free (text);
2665 }
2666 }
2667 gtk_widget_show (KeyStrokes);
2668 }
2669
2670 static gboolean
toggle_show_keystroke_preference(void)2671 toggle_show_keystroke_preference (void)
2672 {
2673 Denemo.prefs.learning = FALSE;
2674 gtk_widget_hide (KeyStrokes);
2675 return TRUE;
2676 }
2677
2678 void
initialize_keystroke_help(void)2679 initialize_keystroke_help (void)
2680 {
2681 if (KeyStrokes == NULL)
2682 {
2683 KeyStrokes = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2684 g_signal_connect (G_OBJECT (KeyStrokes), "delete-event", G_CALLBACK (toggle_show_keystroke_preference), NULL);
2685 gtk_window_set_transient_for (GTK_WINDOW(KeyStrokes), GTK_WINDOW(Denemo.window));
2686 gtk_window_set_keep_above (GTK_WINDOW (KeyStrokes), TRUE);
2687 gtk_window_set_accept_focus (GTK_WINDOW (KeyStrokes), FALSE);
2688 KeyStrokeLabel = gtk_label_new ("");
2689 //gtk_label_set_line_wrap (KeyStrokeLabel, TRUE);
2690 KeyStrokeHelp = gtk_label_new ("");
2691 gtk_label_set_line_wrap (GTK_LABEL(KeyStrokeHelp), TRUE);
2692 GtkWidget *vbox = gtk_vbox_new (FALSE, 8);
2693 gtk_container_add (GTK_CONTAINER (KeyStrokes), vbox);
2694 gtk_box_pack_start (GTK_BOX (vbox), KeyStrokeLabel, FALSE, TRUE, 0);
2695 gtk_box_pack_start (GTK_BOX (vbox), KeyStrokeHelp, FALSE, TRUE, 0);
2696 gtk_label_set_use_markup (GTK_LABEL (KeyStrokeLabel), TRUE);
2697 gtk_widget_show_all (KeyStrokes);
2698 }
2699 gtk_widget_hide (KeyStrokes);
2700 }
2701
2702 /**
2703 * find_dir_for_file:
2704 * @filename: The file to search
2705 * @dirs: A dir paths array, ending by NULL, where to search.
2706 *
2707 * Finds the first dir in the list that contains 'filename', and free the array.
2708 *
2709 * Returns: The dir path if found, NULL either
2710 **/
2711 gchar*
find_dir_for_file(gchar * filename,GList * dirs)2712 find_dir_for_file(gchar* filename, GList* dirs)
2713 {
2714 gchar *dir = NULL;
2715 gchar *path = NULL;
2716 GList* curdir = NULL;
2717
2718 for(curdir = dirs ; curdir ; curdir = g_list_next(curdir))
2719 {
2720 if(!dir)
2721 {
2722 path = g_build_filename (curdir->data, filename, NULL);
2723 if(g_file_test (path, G_FILE_TEST_EXISTS))
2724 dir = g_strdup(curdir->data); //even though found, the loop is continued to clear the list of dirs
2725 // else here causes a memory leak as path is not used hereafter
2726 g_free(path);
2727 }
2728 g_free(curdir->data);
2729 }
2730 return dir;
2731 }
2732
2733 /**
2734 * find_dir_for_files:
2735 * @files: The files to search
2736 * @dirs: A dir paths list, where to search.
2737 *
2738 * Finds the first dir in the list that contains 'filename', and free the array.
2739 *
2740 * Returns: The dir path if found, NULL either
2741 **/
2742 gchar*
find_dir_for_files(GList * files,GList * dirs)2743 find_dir_for_files(GList* files, GList* dirs)
2744 {
2745 gchar *dir = NULL;
2746 gchar *path = NULL;
2747 GList* curdir = NULL;
2748 GList* curfile = NULL;
2749
2750 for(curdir = dirs; curdir; curdir = g_list_next(curdir))
2751 {
2752 for(curfile = files; curfile; curfile = g_list_next(curfile))
2753 {
2754 if(!dir)
2755 {
2756 path = g_build_filename (curdir->data, curfile->data, NULL);
2757 if(g_file_test (path, G_FILE_TEST_EXISTS))
2758 dir = g_strdup(curdir->data);
2759 g_free(path);
2760 }
2761 }
2762 g_free(curdir->data);
2763 }
2764 return dir;
2765 }
2766
2767 /**
2768 * find_path_for_file:
2769 * @filename: The file to search
2770 * @dirs: A dir paths list, where to search.
2771 *
2772 * Finds the first dir in the list that contains 'filename', and free the array.
2773 *
2774 * Returns: The file path if found, NULL either
2775 **/
2776 gchar*
find_path_for_file(gchar * filename,GList * dirs)2777 find_path_for_file(gchar* filename, GList* dirs)
2778 {
2779 gchar* dir = find_dir_for_file (filename, dirs);
2780 if(dir){
2781 gchar* path = g_build_filename(dir, filename, NULL);
2782 g_free(dir);
2783 return path;
2784 }
2785 return NULL;
2786 }
2787 gchar *
get_project_dir(void)2788 get_project_dir (void) {
2789 if(Denemo.project && Denemo.project->filename->len)
2790 {
2791 return g_path_get_dirname (Denemo.project->filename->str);
2792 }
2793 return g_strdup(g_get_home_dir ());
2794 }
2795 const gchar*
get_local_dir(DenemoDirectory dir)2796 get_local_dir(DenemoDirectory dir)
2797 {
2798 switch(dir)
2799 {
2800 case DENEMO_DIR_COMMANDS: return COMMANDS_DIR;
2801 case DENEMO_DIR_UI: return UI_DIR;
2802 case DENEMO_DIR_SOUNDFONTS: return SOUNDFONTS_DIR;
2803 case DENEMO_DIR_PIXMAPS: return PIXMAPS_DIR;
2804 case DENEMO_DIR_FONTS: return FONTS_DIR;
2805 case DENEMO_DIR_LOCALE: return LOCALE_DIR;
2806 case DENEMO_DIR_LILYPOND_INCLUDE: return LILYPOND_INCLUDE_DIR;
2807 default: return NULL;
2808 }
2809 }
2810
2811 gchar*
get_system_dir(DenemoDirectory dir)2812 get_system_dir(DenemoDirectory dir)
2813 {
2814 switch(dir)
2815 {
2816 case DENEMO_DIR_COMMANDS:
2817 case DENEMO_DIR_UI:
2818 case DENEMO_DIR_SOUNDFONTS:
2819 case DENEMO_DIR_FONTS:
2820 case DENEMO_DIR_LILYPOND_INCLUDE:
2821 return g_build_filename(get_system_data_dir (), get_local_dir(dir), NULL);
2822 case DENEMO_DIR_PIXMAPS:
2823 return g_build_filename(get_system_data_dir (), PIXMAPS_DIR, NULL);
2824 break;
2825 case DENEMO_DIR_LOCALE:
2826 return g_strdup(get_system_locale_dir ());
2827 break;
2828 case DENEMO_DIR_BIN:
2829 return g_strdup(get_system_bin_dir ());
2830 break;
2831 default:
2832 return NULL;
2833 }
2834 }
2835
2836 const gchar*
get_executable_dir()2837 get_executable_dir ()
2838 {
2839 static const gchar* dir = NULL;
2840 if(dir == NULL)
2841 {
2842 char path[1024];
2843 gint n;
2844 #ifdef G_OS_WIN32
2845 GetModuleFileNameW(NULL, path, MAX_PATH);
2846
2847 #elif defined _MACH_O_
2848 guint size = sizeof (path);
2849 _NSGetExecutablePath (path, &size);
2850
2851 #else
2852 if ((n=readlink("/proc/self/exe", path, sizeof(path))) < 0)
2853 {
2854 perror("/proc/self/exe");
2855 return NULL;
2856 }
2857 path[n] = 0;
2858 #endif
2859 dir = g_path_get_dirname(path);
2860 }
2861 return dir;
2862 }
2863
2864 /**
2865 * find:
2866 * @dir: The denemo directory where to search
2867 * @filename: The file to search
2868 *
2869 * Finds a file by searching:
2870 * - in the local directory
2871 * - in the executable parent directory
2872 * - in the user directory
2873 * - in the system directory
2874 * - in the system fonts directory
2875 **/
2876 gchar*
find_denemo_file(DenemoDirectory dir,gchar * filename)2877 find_denemo_file (DenemoDirectory dir, gchar* filename)
2878 {
2879 //g_debug("find_denemo_file called with %d and %s\n", dir, filename);
2880 GList* dirs = NULL;
2881 dirs = g_list_append(dirs, g_build_filename(PACKAGE_SOURCE_DIR, get_local_dir (dir), NULL));
2882 dirs = g_list_append(dirs, g_build_filename(get_executable_dir (), "..", get_local_dir (dir), NULL));
2883 dirs = g_list_append(dirs, g_build_filename(get_user_data_dir (TRUE), get_local_dir (dir), NULL));
2884 dirs = g_list_append(dirs, g_strdup(get_system_dir(dir)));
2885 dirs = g_list_append(dirs, g_build_filename(get_executable_dir (), "..", "share","fonts","truetype","denemo", NULL));
2886 return find_path_for_file (filename, dirs);
2887 }
2888
escape_scheme(gchar * input)2889 gchar *escape_scheme (gchar *input)
2890 {
2891 gchar *c = input -1;
2892 GString *out = g_string_new("");
2893 for(c=input; c && *c; c++)
2894 {
2895 if(*c=='"')
2896 g_string_append (out, "\\\\\\\"");
2897 else
2898 if(*c=='\\')
2899 g_string_append(out, "\\\\");
2900 else
2901 {
2902 g_string_append_printf (out, "%c", *c);
2903 }
2904 }
2905 return g_string_free(out, FALSE);
2906 }
2907
shift_held_down(void)2908 gboolean shift_held_down(void)
2909 {
2910 GdkModifierType mask;
2911 GdkWindow *win = gtk_widget_get_window (Denemo.window);
2912 #if GTK_MAJOR_VERSION == 2
2913 gdk_window_get_pointer (win, NULL, NULL, &mask);
2914 #else
2915 gdk_window_get_device_position (win, gdk_device_manager_get_client_pointer (gdk_display_get_device_manager(gdk_display_get_default())) ,NULL, NULL, &mask);
2916 #endif
2917 return (mask & GDK_SHIFT_MASK);
2918 }
2919