/* utils.c * Functions useful across the different modules of * drawing and non-drawing code. * * for Denemo, a gtk+ frontend to GNU Lilypond * (c) 1999-2005 Matthew Hiller */ #include #include /*for SIGTERM */ #include #include #include #include #include #include #include "display/accwidths.h" #include #include "display/notewidths.h" #include "core/utils.h" #include "smf.h" #include "export/print.h" #include "core/kbd-custom.h" #include "core/view.h" #include "command/object.h" #include "command/scorelayout.h" #include /*for SIGTERM */ #include "config.h" #ifdef G_OS_WIN32 #include "windows.h" #else #include "core/binreloc.h" #endif #include "audio/pitchentry.h" #include "command/measure.h" #ifdef _MACH_O_ #include #endif //generated by cairo_svg2path from piano_staff.svg static cairo_path_data_t piano_brace_data[] = { {.header.type = 0,.header.length = 2}, {.point.x = 13.694762,.point.y = 15.134294}, {.header.type = 0,.header.length = 2}, {.point.x = 13.694762,.point.y = 34.647228}, {.header.type = 2,.header.length = 4}, {.point.x = 13.694745,.point.y = 37.873117}, {.point.x = 13.456160,.point.y = 40.450299}, {.point.x = 12.979007,.point.y = 42.378764}, {.header.type = 2,.header.length = 4}, {.point.x = 12.533633,.point.y = 44.307289}, {.point.x = 11.849690,.point.y = 45.797498}, {.point.x = 10.927179,.point.y = 46.849380}, {.header.type = 2,.header.length = 4}, {.point.x = 10.036446,.point.y = 47.901323}, {.point.x = 8.748092,.point.y = 48.830499}, {.point.x = 7.062105,.point.y = 49.636952}, {.header.type = 2,.header.length = 4}, {.point.x = 9.734241,.point.y = 50.899267}, {.point.x = 11.499767,.point.y = 52.722581}, {.point.x = 12.358688,.point.y = 55.106874}, {.header.type = 2,.header.length = 4}, {.point.x = 13.249387,.point.y = 57.491228}, {.point.x = 13.694745,.point.y = 61.102772}, {.point.x = 13.694762,.point.y = 65.941541}, {.header.type = 1,.header.length = 2}, {.point.x = 13.694762,.point.y = 83.666226}, {.header.type = 2,.header.length = 4}, {.point.x = 13.694745,.point.y = 88.119297}, {.point.x = 14.251441,.point.y = 91.450330}, {.point.x = 15.364854,.point.y = 93.659376}, {.header.type = 2,.header.length = 4}, {.point.x = 15.937439,.point.y = 94.781380}, {.point.x = 16.557760,.point.y = 95.675508}, {.point.x = 17.225817,.point.y = 96.341727}, {.header.type = 2,.header.length = 4}, {.point.x = 17.925643,.point.y = 97.042993}, {.point.x = 18.434624,.point.y = 97.463756}, {.point.x = 18.752761,.point.y = 97.604034}, {.header.type = 2,.header.length = 4}, {.point.x = 19.102663,.point.y = 97.744242}, {.point.x = 19.532113,.point.y = 97.884511}, {.point.x = 20.041117,.point.y = 98.024788}, {.header.type = 1,.header.length = 2}, {.point.x = 20.041117,.point.y = 99.602655}, {.header.type = 2,.header.length = 4}, {.point.x = 14.442310,.point.y = 99.427312}, {.point.x = 10.815824,.point.y = 98.112447}, {.point.x = 9.161650,.point.y = 95.658006}, {.header.type = 2,.header.length = 4}, {.point.x = 7.412016,.point.y = 93.028214}, {.point.x = 6.537207,.point.y = 88.487468}, {.point.x = 6.537217,.point.y = 82.035749}, {.header.type = 1,.header.length = 2}, {.point.x = 6.537217,.point.y = 63.206580}, {.header.type = 2,.header.length = 4}, {.point.x = 6.537207,.point.y = 60.787187}, {.point.x = 6.362244,.point.y = 58.735972}, {.point.x = 6.012330,.point.y = 57.052911}, {.header.type = 2,.header.length = 4}, {.point.x = 5.694207,.point.y = 55.369866}, {.point.x = 5.026171,.point.y = 53.879675}, {.point.x = 4.008217,.point.y = 52.582286}, {.header.type = 2,.header.length = 4}, {.point.x = 3.022060,.point.y = 51.249883}, {.point.x = 1.685986,.point.y = 50.373300}, {.point.x = -0.000008,.point.y = 49.952520}, {.header.type = 1,.header.length = 2}, {.point.x = -0.000008,.point.y = 49.373968}, {.header.type = 2,.header.length = 4}, {.point.x = 4.358134,.point.y = 48.006509}, {.point.x = 6.537207,.point.y = 43.676162}, {.point.x = 6.537217,.point.y = 36.382874}, {.header.type = 1,.header.length = 2}, {.point.x = 6.537217,.point.y = 15.975830}, {.header.type = 2,.header.length = 4}, {.point.x = 6.537207,.point.y = 10.120270}, {.point.x = 7.443829,.point.y = 5.965213}, {.point.x = 9.257083,.point.y = 3.510685}, {.header.type = 2,.header.length = 4}, {.point.x = 11.102126,.point.y = 1.056314}, {.point.x = 14.696800,.point.y = -0.258578}, {.point.x = 20.041117,.point.y = -0.433981}, {.header.type = 1,.header.length = 2}, {.point.x = 20.041117,.point.y = 1.143903}, {.header.type = 2,.header.length = 4}, {.point.x = 18.068795,.point.y = 1.740052}, {.point.x = 16.462326,.point.y = 3.212732}, {.point.x = 15.221705,.point.y = 5.561917}, {.header.type = 2,.header.length = 4}, {.point.x = 14.203726,.point.y = 7.490478}, {.point.x = 13.694745,.point.y = 10.681285}, {.point.x = 13.694762,.point.y = 15.134294}, }; static cairo_path_t piano_brace_path = { 0, piano_brace_data, 92 }; /** * This checks to see if there's a .denemo/ directory in the user's * home directory, * if create tries to create one if there isn't, and returns the * path to it * else returns NULL * * .denemo/ is used for holding configuration files, templates, and so on. * * On windows the home directory is the one containing the My Documents folder. */ const gchar * get_user_data_dir (gboolean create) { static gchar *dotdenemo = NULL; gboolean err; if (!dotdenemo) { dotdenemo = g_build_filename (g_get_home_dir (), ".denemo-" PACKAGE_VERSION, NULL); } if ((!create) && !g_file_test (dotdenemo, G_FILE_TEST_IS_DIR)) return NULL; err = g_mkdir_with_parents (dotdenemo, 0770); if (err) { warningdialog (_("Could not create .denemo for you personal settings")); g_free (dotdenemo); dotdenemo = NULL; } return dotdenemo; } /* return a path to a temporary directory to be used for print intermediate files */ const gchar * locateprintdir (void) { static gchar *printdir = NULL; if (!printdir) printdir = make_temp_dir (); return printdir; } void add_font_directory (gchar * fontpath) { #ifdef G_OS_WIN32 if (0 == AddFontResource (fontpath)) g_warning("Failed to add font dir %s.", fontpath); SendMessage (HWND_BROADCAST, WM_FONTCHANGE, 0, 0); #endif if(FcConfigAppFontAddDir (NULL, (FcChar8*) fontpath) == FcFalse) g_warning("Failed to add font dir %s.", fontpath); } void add_font_file (gchar * fontname) { #ifdef G_OS_WIN32 if (0 == AddFontResource (fontname)) g_warning("Failed to add font file %s.", fontname); SendMessage (HWND_BROADCAST, WM_FONTCHANGE, 0, 0); #endif if(FcConfigAppFontAddFile (NULL, (FcChar8*) fontname) == FcFalse) g_warning("Failed to add font file %s.", fontname); } #ifdef G_OS_WIN32 gboolean CoInitializeExCalled = FALSE; #endif // Create a unique temporary directory starting gchar * make_temp_dir (void) { gchar *ret = NULL; #ifdef G_OS_WIN32 gchar buf[1024] = "C:\\TMP\\\0"; gint length = 1024; (void) GetTempPath (length, buf); gint integer = 0; //Windows does not delete the temp directory, use a constant one. g_rand_int(g_rand_new()); ret = g_strdup_printf ("%sDenemo%d", buf, integer); gint fail = g_mkdir_with_parents (ret, 0700); if (fail) g_warning ("Could not create temp dir %s", ret); else g_info ("Created temp dir %s\n", ret); #else ret = g_strdup ("/tmp/DenemoXXXXXX"); mkdtemp ((char *) ret); #endif return ret; } gboolean run_file_association (gchar * filename) { #ifdef G_OS_WIN32 gint value = 0; if (!CoInitializeExCalled) { value = CoInitializeExCalled = TRUE; CoInitializeEx (NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); g_debug ("coinit returned %d\n", value); } g_info ("Running ShellExecute %s \n", filename); return ShellExecute (NULL, NULL, filename, NULL, NULL, 0) > 32 /* value above 32 indicating success */ ; #else g_warning ("No file assoc code - set pref in externals tab of prefs dialog"); return 0; #endif } /** Popups up the menu named. */ void popup_menu (gchar * name) { GtkWidget *menu = gtk_ui_manager_get_widget (Denemo.ui_manager, name); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ()); } /** * outputs a warning and sounds bell * @return none */ void warningmessage (gchar * msg) { gdk_beep (); g_warning ("%s", msg); } /** * Pops up an info or warning dialog and blocks until it is dismissed * @param msg warning message to display * @param info TRUE if informational only * @return none */ void infowarningdialog (gchar * msg, gboolean info) { if(Denemo.non_interactive) g_warning("%s", msg); else { GtkWidget *dialog; 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); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window)); gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } } /** * Pops up a warning dialog and blocks until it is dismissed * @param msg warning message to display * @return none */ void warningdialog (gchar * msg) { infowarningdialog (msg, FALSE); } /** * Displays information message to screen, not blocking. * User can destroy window when no longer needed. * @param msg message to display * @return dialog */ GtkWidget * infodialog (gchar * msg) { if(Denemo.non_interactive){ g_info("%s", msg); return NULL; } GtkWidget *dialog; dialog = gtk_message_dialog_new (GTK_WINDOW (Denemo.window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", msg); #ifdef G_OS_WIN32 gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); //needed on windows because of a bug, not all text can be seen. #endif g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_hide), dialog); gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window)); gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE); gtk_widget_show_all (dialog); return dialog; } /* data stucture to contain Progressbar * data */ typedef struct _ProgressData { GtkWidget *window; GtkWidget *pbar; int timer; gboolean progressing; } ProgressData; static ProgressData progress_data; /* Update the value of the progress bar so that we get * some movement */ static gboolean progress_timeout (void) { if (progress_data.progressing) gtk_progress_bar_pulse (GTK_PROGRESS_BAR (progress_data.pbar)); else { gtk_widget_hide (progress_data.window); progress_data.timer = 0; return FALSE; } return TRUE; } /** * Displays progress bar * optionally pass a callback to be run on delete signal * @param msg message to display, callback (can be NULL) */ GtkWindow * progressbar (gchar * msg, gpointer callback) { GtkWidget *vbox; ProgressData *pdata = &progress_data; if (pdata->progressing) pdata->window; if (pdata->window == NULL) { if (callback && Denemo.prefs.progressbardecorations) pdata->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); else pdata->window = gtk_window_new (GTK_WINDOW_POPUP); 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... gtk_window_set_title (GTK_WINDOW (pdata->window), _("Progress")); 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.")); gtk_window_set_transient_for (GTK_WINDOW(pdata->window), GTK_WINDOW(Denemo.window)); gtk_window_set_keep_above (GTK_WINDOW (pdata->window), TRUE); vbox = gtk_vbox_new (FALSE, 5); gtk_container_add (GTK_CONTAINER (pdata->window), vbox); pdata->pbar = gtk_progress_bar_new (); gtk_container_add (GTK_CONTAINER (vbox), pdata->pbar); gtk_widget_show_all (vbox); } /* set text inside progress bar */ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pdata->pbar), msg); if (pdata->timer == 0) pdata->timer = g_timeout_add (100, (GSourceFunc) progress_timeout, pdata); pdata->progressing = TRUE; /* If this is false the progress bar will stop */ gtk_widget_show (pdata->window); /* If widget is destroyed stop the printing */ if(callback) g_signal_connect (G_OBJECT (pdata->window), "delete-event", G_CALLBACK (callback /*call_stop_lilypond*/), NULL); else g_signal_connect (G_OBJECT (pdata->window), "delete-event", G_CALLBACK (progressbar_stop), NULL); return GTK_WINDOW (pdata->window); } void progressbar_stop (void) { progress_data.progressing = FALSE; } void busy_cursor (GtkWidget *area) { static GdkCursor *busycursor = NULL; if(!busycursor) busycursor = gdk_cursor_new (GDK_WATCH); if (gtk_widget_get_window (Denemo.printarea)) gdk_window_set_cursor (gtk_widget_get_window (area), busycursor); } void normal_cursor (GtkWidget *area) { static GdkCursor *arrowcursor = NULL; if(!arrowcursor) arrowcursor = gdk_cursor_new (GDK_RIGHT_PTR); if (gtk_widget_get_window (area)) gdk_window_set_cursor (gtk_widget_get_window (area), arrowcursor); } /** * Draws the given bitmap mask on to the pixmap using the given * grahpics context. * * @param pixmap pixmap be drawn on. * @param gc graphics context to use * @param mask bitmap to be drawn * @param x x position on the pixmap * @param y y position on the pixmap * @param width width of the bitmap mask * @param height height of the bitmap mask * * @return none */ #ifdef G_OS_WIN32 //this code actually works on GNU/Linux too, it is not clear what to prefer static void windows_draw_text (cairo_t *cr, const char *font, const char *text, double x, double y, double size, gboolean invert) { y -= size; size *= 0.75; PangoLayout *layout; PangoFontDescription *desc; /* Create a PangoLayout, set the font and text */ layout = pango_cairo_create_layout (cr); pango_layout_set_text (layout, text, -1); desc = pango_font_description_from_string (font); pango_font_description_set_size (desc, size*PANGO_SCALE); pango_layout_set_font_description (layout, desc); pango_font_description_free (desc); pango_cairo_update_layout (cr, layout); cairo_move_to (cr, x, y); if (invert) cairo_scale (cr, 1, -1); pango_cairo_show_layout (cr, layout); /* free the layout object */ g_object_unref (layout); } #endif void drawbitmapinverse_cr (cairo_t * cr, DenemoGraphic * mask, gint x, gint y, gboolean invert) { cairo_save (cr); switch (mask->type) { case DENEMO_BITMAP: { #if GTK_MAJOR_VERSION==3 gdk_cairo_set_source_window (cr, mask->graphic, x, y); //??? bitmap???? asks torbenh #else cairo_rectangle (cr, x, y, mask->width, mask->height); #endif cairo_fill (cr); break; } case DENEMO_PATTERN: { cairo_pattern_t *pattern = (cairo_pattern_t *) mask->graphic; cairo_translate (cr, x, y); cairo_mask (cr, pattern); break; } case DENEMO_FONT: { DenemoGlyph *glyph = mask->graphic; #ifdef G_OS_WIN32 windows_draw_text (cr, glyph->fontname, glyph->utf, x, y, glyph->size, invert); #else cairo_select_font_face (cr, glyph->fontname, glyph->slant, glyph->weight); cairo_set_font_size (cr, glyph->size); cairo_move_to (cr, x, y); if (invert) cairo_scale (cr, 1, -1); cairo_show_text (cr, glyph->utf); #endif break; } } cairo_restore (cr); } void drawfetachar_cr (cairo_t * cr, gunichar uc, double x, double y) { int len; char utf_string[8]; len = g_unichar_to_utf8 (uc, utf_string); utf_string[len] = '\0'; // 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. cairo_select_font_face (cr, "feta26", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size (cr, 35.0); cairo_move_to (cr, x, y); cairo_show_text (cr, utf_string); } void drawtext_cr (cairo_t * cr, const char *text, double x, double y, double size) { if (*text) { #ifdef G_OS_WIN32 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 #else //use the FreeSerif font as it has music symbols - there is no font substitution done by cairo here cairo_select_font_face (cr, "Denemo", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size (cr, size); cairo_move_to (cr, x, y); cairo_show_text (cr, text); #endif } } void drawnormaltext_cr (cairo_t * cr, const char *text, double x, double y) { drawtext_cr (cr, text, x, y, 14.0); } void drawlargetext_cr (cairo_t * cr, const char *text, double x, double y) { drawtext_cr (cr, text, x, y, 24.0); } /* draw display text and or graphics for directives return the widest graphic width*/ gint draw_for_directives (cairo_t * cr, GList * directives, gint x, gint y, gboolean at_cursor) { gint count = 10; gint maxwidth = 0; for (; directives; directives = directives->next, count += 10) { DenemoDirective *directive = (DenemoDirective *) directives->data; guint layout = selected_layout_id (); gdouble only = directive->y ? ((directive->y == layout) ? 0.5 : 0.0) : 0.0; gdouble exclude = directive->x ? ((directive->x == layout) ? 0.9 : 0.0) : 0.0; if (directive->y && directive->y != layout) exclude = 0.9; if (exclude>0.0 || only >0.0) { cairo_save (cr); cairo_set_source_rgba (cr, 0.4 + exclude -only/2, 0.5 + only, 0.4 -only/2, at_cursor ? 1.0 : 0.7); } if (directive->graphic) { gint gwidth, gheight; gwidth = directive->graphic->width; gheight = directive->graphic->height; maxwidth = MAX (gwidth, maxwidth); //g_debug("drawing a graphic at %d %d\n", xx+directive->gx+count-gwidth/2, y+height+directive->gy-gheight/2); drawbitmapinverse_cr (cr, directive->graphic, x + directive->gx + count - gwidth / 2, y + directive->gy - gheight / 2, FALSE); } if (directive->display) { #define MAXLEN (8) gchar c = 0; //if it is a long string only show it all when cursor is on it also only display from first line gchar *p; for (p = directive->display->str; *p; p++) { if (*p == '\n' || (!at_cursor && (p - directive->display->str) > MAXLEN)) { c = *p; *p = 0; break; } } drawnormaltext_cr (cr, directive->display->str, x + directive->tx + count, y + directive->ty); if (c) { *p = c; } if (exclude>0.0 || only >0.0) cairo_restore (cr); } } return maxwidth; } /* draw a brace straight or curly */ void draw_staff_brace (cairo_t *cr, gboolean curly, gint x, gint y, gint height) { cairo_set_source_rgb (cr, 0, 0, 0); if (!curly) { drawfetachar_cr (cr, 0xD8, x, y); cairo_rectangle (cr, x, y+2.0, 3, height - 4.0); cairo_fill (cr); cairo_rectangle (cr, x + 5.0, y + 1.0, 1, height - 2.0); cairo_fill (cr); drawfetachar_cr (cr, 0xD9, x, y + height); } else { //cairo_translate (cr, 0, 10.0); cairo_translate (cr, x-5, y + 2); cairo_scale (cr, 1.0, height / 100.0); cairo_append_path (cr, &piano_brace_path); cairo_fill (cr); } return; } gchar *pretty_name (gchar *lilynote) //display 𝄪𝄫♯♭♮ with note name { static gchar *natural = NULL; static gchar *sharp; static gchar *flat; static gchar *double_sharp; static gchar *double_flat; gchar *answer; if (!natural) { natural = g_strdup ("C♮"); sharp = g_strdup ("C♯"); flat = g_strdup ("C♭"); double_sharp = g_strdup ("C𝄪"); double_flat = g_strdup ("C𝄫"); } answer = natural; if(*(lilynote+1)==0) answer = natural; else if (*(lilynote+1) == 'i') { if (*(lilynote+3) == 'i') answer = double_sharp; else answer = sharp; } else if (*(lilynote+1) == 'e') { if (*(lilynote+3) == 'e') answer = double_flat; else answer = flat; } *answer = toupper(*lilynote); return answer; } /** * Utility function to set the number of ticks used by the given object * if it is within a given tuplet * * @param theobj DenemoObject to set the number of ticks * @param numerator numerator of the current tuplet * @param denominator denominator of the current tuplet * @return none */ void set_tuplefied_numticks (DenemoObject * theobj, gint numerator, gint denominator) { theobj->durinticks = theobj->basic_durinticks * numerator / denominator; /* Though WHOLENUMTICKS is chosen strategically so that in common * cases, this division works out evenly, things should also work * out properly if it doesn't; i.e., durinticks_untupletized is * rounded down */ } /** * Utility function to set the number of ticks of a mudela object * in a grace note * @param theobj the DenemoObject to set the number of ticks * @param multiplier the grace notes multiplier * @return none */ void set_grace_numticks (DenemoObject * theobj, gint multiplier) { theobj->durinticks = theobj->basic_durinticks / multiplier; } /** * Sets the number of ticks taken by the given DenemoObject. * * @param theobj the mudela object to set the number of ticks on * @return none */ void set_basic_numticks (DenemoObject * theobj) { gint power; gint withoutdots; gint addperdot, i; switch (theobj->type) { case CHORD: if (((chord *) theobj->object)->baseduration < 0) { withoutdots = -((chord *) theobj->object)->baseduration; } else { power = 1 << ((chord *) theobj->object)->baseduration; withoutdots = WHOLE_NUMTICKS / power; } addperdot = withoutdots / 2; theobj->basic_durinticks = withoutdots; for (i = 0; i < ((chord *) theobj->object)->numdots; addperdot /= 2, i++) theobj->basic_durinticks += addperdot; break; default: theobj->basic_durinticks = 0; theobj->durinticks = 0; /* There's no reason not to set that as well */ break; } } /** * Returns the amount of space to be left after a note or rest, only * taking the width of the measure into consideration * * @param numticks the number of ticks taken so far * @param wholenotewidth the number of ticks taken be a whole note * @return the amount of space to be left after a note or rest */ gint space_after (gint numticks, gint wholenotewidth) { return MAX (numticks * wholenotewidth / WHOLE_NUMTICKS, 0); } #define EXTRAWIDTH 5 /** * Sets the minimum space that needs to be allocated for drawing a mudela * object based on the type * also sets space_before * @param theobj the DenemoObject to set the minimum space on * @return none */ void setpixelmin (DenemoObject * theobj) { gint i, baseduration, headtype; chord chordval; GList *tnode; note *thetone; /* And these static declaration are copied right out of drawnotes.c * and drawaccidentals.c */ switch (theobj->type) { case CHORD: chordval = *(chord *) theobj->object; baseduration = chordval.baseduration; baseduration = MAX (baseduration, 0); headtype = MIN (baseduration, 2); if (headtype < 0) headtype = 0; //-ve values of baseduration are for specials gint directive_pixels = 0; // the largest amount of extra space asked for by any directive GList *g = chordval.directives; for (; g; g = g->next) directive_pixels = MAX (directive_pixels, ((DenemoDirective *) g->data)->minpixels); if (chordval.notes) { theobj->minpixelsalloted = headwidths[headtype]; //search through notes and their attached directives, find max display space requested //use this below g = chordval.notes; for (; g; g = g->next) { GList *h = ((note *) g->data)->directives; for (; h; h = h->next) directive_pixels = MAX (directive_pixels, ((DenemoDirective *) h->data)->minpixels); } } else /* a rest */ theobj->minpixelsalloted = restwidths[baseduration]; // Allow extra space specified by attached LilyPond directives - example: theobj->minpixelsalloted += directive_pixels; /* 12 pixels for the first dot, 6 for each dot thereafter */ if (chordval.numdots) theobj->minpixelsalloted += 6; for (i = 0; i < chordval.numdots; i++) theobj->minpixelsalloted += 6; theobj->space_before = 0; if (chordval.hasanacc) for (tnode = chordval.notes; tnode; tnode = tnode->next) { thetone = (note *) tnode->data; if (thetone->showaccidental) theobj->space_before = MAX (theobj->space_before, thetone->position_of_accidental); } if (chordval.is_reversealigned){ if (chordval.is_stemup) theobj->minpixelsalloted += headwidths[headtype]; else if (!chordval.hasanacc) /* Accidental positioning already accounts for the extra leading space that we need for reverse-aligned noteheads, provided the chord has an accidental in it somewhere. We only have to remark upon noteheads to the left of the stem if there weren't any accidentals to position. */ theobj->space_before += headwidths[headtype]; } theobj->minpixelsalloted += EXTRAWIDTH; break; case TUPOPEN: case TUPCLOSE: /* The real way do this will be with a gdk_string_width. Until * then, though: */ theobj->minpixelsalloted = #if 0 40; #else 16; #endif theobj->space_before = theobj->minpixelsalloted / 2; break; case LILYDIRECTIVE: { DenemoDirective *directive = (DenemoDirective *) theobj->object; theobj->minpixelsalloted = directive->minpixels ? directive->minpixels : 16; theobj->space_before = theobj->minpixelsalloted / 2; } break; case CLEF: theobj->minpixelsalloted = 35; theobj->space_before = 0; break; case KEYSIG: theobj->minpixelsalloted = 20; //needed so find_xes_in_measures assigns space to it without waiting for drawing to do so. theobj->space_before = 0; break; case TIMESIG: theobj->minpixelsalloted = 40; theobj->space_before = 0; break; case STEMDIRECTIVE: /* The real way do this will be with a gdk_string_width. Until * then, though: */ theobj->minpixelsalloted = 40; theobj->space_before = 0; break; case DYNAMIC: theobj->minpixelsalloted = 40; theobj->space_before = 0; break; case GRACE_START: case GRACE_END: theobj->minpixelsalloted = 16; theobj->space_before = theobj->minpixelsalloted / 2; break; default: theobj->minpixelsalloted = 0; theobj->space_before = 0; break; } } /** * * @param mid_c_offset the mid_c_offset of the the tone * @param dclef the clef of the current tone * * @return the height of a tone based on its mid_c_offset and the clef that it's in */ gint calculateheight (gint mid_c_offset, gint dclef) { switch (dclef) { case DENEMO_TREBLE_CLEF: return 5 * LINE_SPACE - HALF_LINE_SPACE * mid_c_offset; break; /* Probably gratuitous */ case DENEMO_ALTO_CLEF: return 2 * LINE_SPACE - HALF_LINE_SPACE * mid_c_offset; break; case DENEMO_G_8_CLEF: return LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 1); break; case DENEMO_BASS_CLEF: return -LINE_SPACE - HALF_LINE_SPACE * mid_c_offset; break; case DENEMO_F_8_CLEF: return -5 * LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 1); break; case DENEMO_TENOR_CLEF: return LINE_SPACE - HALF_LINE_SPACE * mid_c_offset; break; case DENEMO_SOPRANO_CLEF: return LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset - 6); break; case DENEMO_FRENCH_CLEF: return 6 * LINE_SPACE - HALF_LINE_SPACE * (mid_c_offset); break; } return (0); } /** * Converts the given offset to a number * * @param n the offset to convert * @return the result of the offset conversion */ gint offsettonumber (gint n) { if (n >= 0) return n % 7; else return (7 - (-n % 7)) % 7; /* Not all C implementations conform to the more recent standard on how % should operate on negative operands. */ } /** * converts the int mid_c_offset to the lilypond name * returns a gchar * so it will have to be freed * 0 returns "c", 1 returns "cis" * The octave ",,, or '''" is also appended" */ gchar * mid_c_offsettolily (int mid_c_offset, int enshift) { gint octave, k; GString *lilynote = g_string_new (""); g_string_append_printf (lilynote, "%c", mid_c_offsettoname (mid_c_offset)); if (enshift < 0) for (k = enshift; k; k++) g_string_append_printf (lilynote, "es"); else for (k = enshift; k; k--) g_string_append_printf (lilynote, "is"); octave = mid_c_offsettooctave (mid_c_offset); if (octave < 0) for (; octave; octave++) g_string_append_printf (lilynote, ","); else for (; octave; octave--) g_string_append_printf (lilynote, "\'"); return g_string_free (lilynote, FALSE); } /** * converts the mid_c_offset to the correct letter name * @param mid_c_offset the mid_c_offset to convert * @return the character name of the mid_c_offset */ gchar mid_c_offsettoname (gint mid_c_offset) { gint otn = offsettonumber (mid_c_offset); return ((otn + 2) % 7) + 'a'; } void note2lilynotename (struct note *noteobject, GString * ret) { gint mid_c_offset = noteobject->mid_c_offset; g_string_append_printf (ret, "%c", mid_c_offsettoname (mid_c_offset)); } void note2lilyaccidental (struct note *noteobject, GString * ret) { gint enshift = noteobject->enshift; gint k; if (enshift < 0) for (k = enshift; k; k++) g_string_append_printf (ret, "es"); else for (k = enshift; k; k--) g_string_append_printf (ret, "is"); } void note2lilyoctave (struct note *noteobject, GString * ret) { gint mid_c_offset = noteobject->mid_c_offset; gint octave = mid_c_offsettooctave (mid_c_offset); if (octave < 0) for (; octave; octave++) g_string_append_printf (ret, ","); else for (; octave; octave--) g_string_append_printf (ret, "\'"); } void chord2lilyduration (struct chord *chordobject, GString * ret) { chord2lilybaseduration (chordobject, ret); chord2lilynumdots (chordobject, ret); } void chord2lilybaseduration (struct chord *chordobject, GString * ret) { int baseduration = chordobject->baseduration; g_string_append_printf (ret, "%d", baseduration); } void chord2lilynumdots (struct chord *chordobject, GString * ret) { int numdots = chordobject->numdots; g_string_append_printf (ret, "%d", numdots); } /** * Calculate a pitches octave from the mid_c_offset * @param mid_c_offset the mid_c_offset to use * @return the octave of the given mid_c_offset */ gint mid_c_offsettooctave (gint mid_c_offset) { if (mid_c_offset < 0) return -((-mid_c_offset + 6) / 7) + 1; else return (mid_c_offset / 7) + 1; } /** * g_list_foreach helper function to free the given data * @param data the list elements data * @param user_data any user supplied data (not used in this case) */ void freeit (gpointer data, gpointer user_data) { g_free (data); } /************* routines for calling from debug code ***************/ #include "command/staff.h" G_GNUC_UNUSED void printobj (objnode * obj) { DenemoObject *curObj; curObj = (DenemoObject *) (obj->data); switch (curObj->type) { case CHORD: fprintf (stderr, "\t\t%s type\n", "CHORD"); break; case TUPOPEN: fprintf (stderr, "\t\t%s type\n", "TUPOPEN"); break; case TUPCLOSE: fprintf (stderr, "\t\t%s type\n", "TUPCLOSE"); break; case CLEF: fprintf (stderr, "\t\t%s type\n", "CLEF"); break; case TIMESIG: fprintf (stderr, "\t\t%s type\n", "TIMESIG"); break; case KEYSIG: fprintf (stderr, "\t\t%s type\n", "KEYSIG"); break; case BARLINE: fprintf (stderr, "\t\t%s type\n", "BARLINE"); break; case STEMDIRECTIVE: fprintf (stderr, "\t\t%s type\n", "STEMDIRECTIVE"); break; case MEASUREBREAK: fprintf (stderr, "\t\t%s type\n", "MEASUREBREAK"); break; case DYNAMIC: fprintf (stderr, "\t\t%s type\n", "DYNAMIC"); break; case GRACE_START: fprintf (stderr, "\t\t%s type\n", "GRACE_START"); break; case GRACE_END: fprintf (stderr, "\t\t%s type\n", "GRACE_END"); break; case LYRIC: fprintf (stderr, "\t\t%s type\n", "LYRIC"); break; case FIGURE: fprintf (stderr, "\t\t%s type\n", "FIGURE"); break; default: /* needs to be up to date with enum in include/denemo/denemo.h */ fprintf (stderr, "!!!!!unknown object type %x - see enum in denemo.h\n", curObj->type); break; } } G_GNUC_UNUSED void printobjs (objnode * obj) { objnode *curobj; if (obj == NULL) { fprintf (stderr, "NULL object\n"); return; } printobj (obj); fprintf (stderr, "previous objects\n"); curobj = obj; while (curobj->prev) { printobj (curobj->prev); curobj = curobj->prev; } fprintf (stderr, "next objects\n"); curobj = obj; while (curobj->next) { printobj (curobj->next); curobj = curobj->next; } } G_GNUC_UNUSED void printmeasure (measurenode * mnode) { if (mnode == NULL) { fprintf (stderr, "Empty measure\n"); return; } printobjs ((objnode*)measure_first_obj_node (mnode)); } G_GNUC_UNUSED void printmeasures (staffnode * thestaff) { GList *measure = staff_first_measure_node (thestaff); gint measurenum = 1; for (measure = staff_first_measure_node (thestaff); measure; measure = measure->next) { fprintf (stderr, "*************Measure %d *************\n", measurenum++); printmeasure (measure); } } G_GNUC_UNUSED void printscoreinfo (DenemoMovement * si) { if (si->thescore == NULL) { fprintf (stderr, "Staff with NULL thescore field\n"); return; } printmeasures (si->thescore); } /** * Function that initializes the code needed for the directory relocation. * @return none */ void initdir () { #ifndef G_OS_WIN32 GError *error = NULL; if (!gbr_init (&error) && (error != (GError *) GBR_INIT_ERROR_DISABLED)) { g_debug ("BinReloc failed to initialize:\n"); g_debug ("Domain: %d (%s)\n", (int) error->domain, g_quark_to_string (error->domain)); g_debug ("Code: %d\n", error->code); g_debug ("Message: %s\n", error->message); g_error_free (error); g_debug ("----------------\n"); } #endif /* not G_OS_WIN32 */ } extern gchar *gbr_find_pkg_data_dir (const gchar * default_pkg_data_dir, const gchar * pkg_name); static gchar *DENEMO_datadir = NULL; const gchar * get_system_data_dir () { //DENEMO_datadir?g_debug("datadir is %s at %p", DENEMO_datadir, DENEMO_datadir):g_debug("datadir not yet set"); if (DENEMO_datadir == NULL) { #ifdef G_OS_WIN32 gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL); DENEMO_datadir = g_build_filename (rootdir, "share", "denemo", NULL); g_message ("rootdir=%s", rootdir); g_message ("datadir=%s", DENEMO_datadir); g_free (rootdir); #else /* not G_OS_WIN32 */ #ifdef _MACH_O_ { char path[1024]; guint size = sizeof (path); _NSGetExecutablePath (path, &size); gchar *bindir = (gchar *) g_malloc (size); if (_NSGetExecutablePath (bindir, &size) == 0) g_message ("Using bin path %s", bindir); else g_critical ("Cannot get bin dir"); DENEMO_datadir = g_build_filename (g_path_get_dirname (bindir), "..", "share", "denemo", NULL); g_message ("OSX set data dir to %s", DENEMO_datadir); } #else #ifndef ENABLE_BINRELOC DENEMO_datadir = g_strdup (PKGDATADIR); #else DENEMO_datadir = gbr_find_pkg_data_dir (PKGDATADIR, PKGNAME); #endif //ENABLE_BINRELOC #endif //_MACH_O_ #endif /* not G_OS_WIN32 */ } return DENEMO_datadir; } const gchar * get_prefix_dir (void) { gchar *prefix; #ifdef G_OS_WIN32 prefix = g_win32_get_package_installation_directory (NULL, NULL); #else /* not G_OS_WIN32 */ #ifdef _MACH_O_ { char path[1024]; guint size = sizeof (path); _NSGetExecutablePath (path, &size); gchar *bindir = (gchar *) g_malloc (size); if (_NSGetExecutablePath (bindir, &size) == 0) { prefix = g_build_filename (bindir, "..", "..", NULL); g_message ("OSX set data prefix to %s", prefix); } else g_critical ("Cannot get bin dir"); } #else #ifndef ENABLE_BINRELOC prefix = g_strdup (PREFIX); #else prefix = gbr_find_prefix (PREFIX); #endif //ENABLE_BINRELOC #endif //_MACH_O_ #endif //G_OS_WIN32 return prefix; } static gchar *DENEMO_bindir = NULL; const gchar * get_system_bin_dir (void) { if (DENEMO_bindir == NULL) { #ifdef G_OS_WIN32 gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL); DENEMO_bindir = g_build_filename (rootdir, "bin", NULL); g_message ("rootdir=%s", rootdir); g_message ("bindir=%s", DENEMO_bindir); g_free (rootdir); #else /* not G_OS_WIN32 */ #ifdef _MACH_O_ { char path[1024]; guint size = sizeof (path); _NSGetExecutablePath (path, &size); gchar *bin = (gchar *) g_malloc (size); if (_NSGetExecutablePath (bin, &size) == 0) { DENEMO_bindir = g_build_filename (bin, "..", NULL); g_message ("Using bin path %s", DENEMO_bindir); } else g_critical ("Cannot get bin dir"); g_message ("OSX set bin dir to %s", DENEMO_bindir); } #else #ifndef ENABLE_BINRELOC DENEMO_bindir = g_strdup (BINDIR); #else DENEMO_bindir = gbr_find_bin_dir (BINDIR); #endif //ENABLE_BINRELOC #endif //_MACH_O_ #endif /* not G_OS_WIN32 */ } return DENEMO_bindir; } /** UNUSED const gchar * get_system_conf_dir () { static gchar *confdir = NULL; if (confdir == NULL) { #ifdef G_OS_WIN32 gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL); confdir = g_build_filename (rootdir, "etc", "denemo", NULL); g_free (rootdir); #else // not G_OS_WIN32 #ifdef _MACH_O_ { char path[1024]; guint size = sizeof (path); _NSGetExecutablePath (path, &size); gchar *bindir = (gchar *) g_malloc (size); if (_NSGetExecutablePath (bindir, &size) == 0) g_debug ("using bin path %s\n", bindir); else g_critical ("Cannot get bin dir\n"); confdir = g_build_filename (g_path_get_dirname (bindir), "..", "etc", "denemo", NULL); g_debug ("OSX set conf dir to %s\n", confdir); } #else #ifndef ENABLE_BINRELOC confdir = g_build_filename (SYSCONFDIR, NULL); #else confdir = g_build_filename (gbr_find_etc_dir (SYSCONFDIR), "denemo", NULL); #endif //ENABLE_BINRELOC #endif //_MACH_O_ #endif // not G_OS_WIN32 } return confdir; } */ const gchar * get_system_locale_dir () { static gchar *localedir = NULL; if (localedir == NULL) { #ifdef G_OS_WIN32 gchar *rootdir = g_win32_get_package_installation_directory (NULL, NULL); localedir = g_build_filename (rootdir, "share", "locale", NULL); g_free (rootdir); #else /* not G_OS_WIN32 */ #ifdef _MACH_O_ { char path[1024]; guint size = sizeof (path); _NSGetExecutablePath (path, &size); gchar *bindir = (gchar *) g_malloc (size); if (_NSGetExecutablePath (bindir, &size) == 0) g_message ("Using bin path %s", bindir); else g_critical ("Cannot get bin dir"); localedir = g_build_filename (g_path_get_dirname (bindir), "..", "share", "locale", NULL); g_message ("OSX set locale dir to %s", localedir); } #else #ifndef ENABLE_BINRELOC /* it seems to be the standard way (no binreloc) * to set the path of translations this way: * messages are in $LOCALEDIR/$LANG/denemo */ localedir = g_strdup (LOCALEDIR); #else /* ENABLE_BINRELOC */ /* binreloc says it is disabled even with built thanks to * --enable-binreloc... So, searhing falls back to * $LOCALEDIR/denemo/$LANG which is not a valid path */ localedir = gbr_find_locale_dir (LOCALEDIR); #endif /* ENABLE_BINRELOC */ #endif #endif /* not G_OS_WIN32 */ } return localedir; } const gchar* get_system_font_dir(){ static gchar* fontdir = NULL; if(fontdir == NULL) { #ifdef G_OS_WIN32 gchar *prefix = g_win32_get_package_installation_directory (NULL, NULL); #else gchar *prefix = g_build_filename (get_prefix_dir (), NULL); #endif fontdir = g_build_filename (prefix, "share", "fonts", "truetype", "denemo", NULL); } return fontdir; } void kill_process (GPid pid) { #ifdef G_OS_WIN32 TerminateProcess (pid, 0); #else /* not G_OS_WIN32 */ kill (pid, SIGTERM); #endif /* not G_OS_WIN32 */ g_spawn_close_pid (pid); } void init_denemo_notenames (void) { define_scheme_literal_variable ("Denemo-Note0", NOTE0, NULL); define_scheme_literal_variable ("Denemo-Rest0", REST0, NULL); define_scheme_literal_variable ("Denemo-Note1", NOTE1, NULL); define_scheme_literal_variable ("Denemo-Rest1", REST1, NULL); define_scheme_literal_variable ("Denemo-Note2", NOTE2, NULL); define_scheme_literal_variable ("Denemo-Rest2", REST2, NULL); define_scheme_literal_variable ("Denemo-Note3", NOTE3, NULL); define_scheme_literal_variable ("Denemo-Rest3", REST3, NULL); define_scheme_literal_variable ("Denemo-Note4", NOTE4, NULL); define_scheme_literal_variable ("Denemo-Rest4", REST4, NULL); define_scheme_literal_variable ("Denemo-Note5", NOTE5, NULL); define_scheme_literal_variable ("Denemo-Rest5", REST5, NULL); define_scheme_literal_variable ("Denemo-Note6", NOTE6, NULL); define_scheme_literal_variable ("Denemo-Rest6", REST6, NULL); define_scheme_literal_variable ("Denemo-Note7", NOTE7, NULL); define_scheme_literal_variable ("Denemo-Rest7", REST7, NULL); define_scheme_literal_variable ("Denemo-Note8", NOTE8, NULL); define_scheme_literal_variable ("Denemo-Rest8", REST8, NULL); } #define HIGHLIGHT " " /* markup the passed string to be in the denemo music font * caller must free the returned string */ gchar * music_font (gchar * str) { GString *s = g_string_new (""); gint c = *str; for (c = *str; c; c = *++str) switch (c) { case '0': g_string_append (s, " " NOTE0 " "); break; case HIGHLIGHT_OFFSET + '0': g_string_append (s, HIGHLIGHT NOTE0 " "); break; case '1': g_string_append (s, " " NOTE1 " "); break; case HIGHLIGHT_OFFSET + '1': g_string_append (s, HIGHLIGHT NOTE1 " "); break; case '2': g_string_append (s, " " NOTE2 " "); break; case HIGHLIGHT_OFFSET + '2': g_string_append (s, HIGHLIGHT NOTE2 " "); break; case '3': g_string_append (s, " " NOTE3 " "); break; case HIGHLIGHT_OFFSET + '3': g_string_append (s, HIGHLIGHT NOTE3 " "); break; case '4': g_string_append (s, " " NOTE4 " "); break; case HIGHLIGHT_OFFSET + '4': g_string_append (s, HIGHLIGHT NOTE4 " "); break; case '5': g_string_append (s, " " NOTE5 " "); break; case HIGHLIGHT_OFFSET + '5': g_string_append (s, HIGHLIGHT NOTE5 " "); break; case '6': g_string_append (s, " " NOTE6 " "); break; case HIGHLIGHT_OFFSET + '6': g_string_append (s, HIGHLIGHT NOTE6 " "); break; case '7': g_string_append (s, " " NOTE7 " "); break; case HIGHLIGHT_OFFSET + '7': g_string_append (s, HIGHLIGHT NOTE7 " "); break; case '8': g_string_append (s, " " NOTE8 " "); break; case HIGHLIGHT_OFFSET + '8': g_string_append (s, HIGHLIGHT NOTE8 " "); break; case 'r': g_string_append (s, " " REST0 " "); break; case HIGHLIGHT_OFFSET + 'r': g_string_append (s, HIGHLIGHT REST0 " "); break; case 's': g_string_append (s, " " REST1 " "); break; case HIGHLIGHT_OFFSET + 's': g_string_append (s, HIGHLIGHT REST1 " "); break; case 't': g_string_append (s, " " REST2 " "); break; case HIGHLIGHT_OFFSET + 't': g_string_append (s, HIGHLIGHT REST2 " "); break; case 'u': g_string_append (s, " " REST3 " "); break; case HIGHLIGHT_OFFSET + 'u': g_string_append (s, HIGHLIGHT REST3 " "); break; case 'v': g_string_append (s, " " REST4 " "); break; case HIGHLIGHT_OFFSET + 'v': g_string_append (s, HIGHLIGHT REST4 " "); break; case 'w': g_string_append (s, " " REST5 " "); break; case HIGHLIGHT_OFFSET + 'w': g_string_append (s, HIGHLIGHT REST5 " "); break; case 'x': g_string_append (s, " " REST6 " "); break; case HIGHLIGHT_OFFSET + 'x': g_string_append (s, HIGHLIGHT REST6 " "); break; case 'y': g_string_append (s, " " REST7 " "); break; case HIGHLIGHT_OFFSET + 'y': g_string_append (s, HIGHLIGHT REST7 " "); break; case 'z': g_string_append (s, " " REST8 " "); break; case HIGHLIGHT_OFFSET + 'z': g_string_append (s, HIGHLIGHT REST8 " "); break; default: g_string_append_c (s, c); } return g_string_free (s, FALSE); } void set_title_bar (DenemoProject * gui) { if(Denemo.non_interactive) return; gchar *title; if (gui->tabname && gui->tabname->len) title = gui->tabname->str; else title = _("(Untitled)"); title = g_strdup_printf ("%s%c", title, gui->notsaved ? '*' : ' '); gtk_window_set_title (GTK_WINDOW (Denemo.window), title); gchar *base = g_path_get_basename (title); gint index = g_list_index (Denemo.projects, gui); if(index < 0) { g_critical ("project is %p is not in list of projects, first tab is %p\n", gui, Denemo.projects->data); return; } GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (Denemo.notebook), index); if (page==NULL) { g_critical ("Bad page, passed project is %p, first tab is %p\n", gui, Denemo.projects->data); return; } gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (Denemo.notebook), page, base); gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (Denemo.notebook), page, base); g_free (title); g_free (base); } static const gchar * enshift_string (gint enshift) { switch (enshift) { case -2: return "𝄫"; case -1: return "♭"; case 0: return " "; case 1: return "♯"; case 2: return "𝄪"; default: return _("Error"); } } gint64 thetime; static void start_editing_timer (void) { thetime = g_get_monotonic_time (); } static void stop_editing_timer (void) { gint64 thistime = g_get_monotonic_time (); if ((thetime>0) && (thetimetotal_edit_time += (thistime - thetime)/1000000; } void reset_editing_timer (void) { thetime = 0; } gchar *time_spent_editing() { gint seconds = Denemo.project->total_edit_time; gint days = seconds/(24*60*60); gint hours; gint minutes; seconds -= days*(24*60*60); hours = (seconds/(60*60)); seconds -= hours*(60*60); minutes = seconds/60; seconds -= minutes*60; return g_strdup_printf("%d days %d hours %d minutes %d seconds\n", days, hours, minutes, seconds); } /* set the status of the current musical score - its change count and title bar and status bars. DenemoProject *gui the musical score. gboolean change TRUE = a change has just been made FALSE = the score is to be assumed saved */ void score_status (DenemoProject * gui, gboolean change) { if (change) { gboolean just_changed = !gui->notsaved; gui->notsaved = TRUE; gui->changecount++; gui->movement->changecount++; if (just_changed) if(!Denemo.non_interactive) start_editing_timer(); } else { gui->notsaved = FALSE; if(!Denemo.non_interactive) stop_editing_timer(); } if(!Denemo.non_interactive) { set_title_bar (gui); write_status (gui); } } /** * If the curObj is a chord with a note(s) * return the first note at or below cursory, or the last note * else return NULL */ note * findnote (DenemoObject * curObj, gint cursory) { note *curnote = NULL; if (curObj && curObj->type == CHORD && ((chord *) curObj->object)->notes) { GList *notes = ((chord *) curObj->object)->notes; for (; notes; notes = notes->next) { curnote = (note *) notes->data; //g_debug("comparing %d and %d\n", cursory, curnote->y); if (cursory <= curnote->mid_c_offset) break; } } return curnote; } /** * If the curObj is a chord with a note(s) * return the note at cursory else return NULL */ note * findnote_strict (DenemoObject * curObj, gint cursory) { note *curnote = NULL; if (curObj && curObj->type == CHORD && ((chord *) curObj->object)->notes) { GList *notes = ((chord *) curObj->object)->notes; for (; notes; notes = notes->next) { curnote = (note *) notes->data; //g_debug("comparing %d and %d\n", cursory, curnote->y); if (cursory == curnote->mid_c_offset) return curnote; } } return NULL; } /* get a fret diagram for the chord at the cursor or before the cursor if not on the chord */ gchar *get_fretdiagram_as_markup (void) { DenemoProject *gui = Denemo.project; DenemoObject *curObj; if (!Denemo.project || !(Denemo.project->movement) || !(Denemo.project->movement->currentobject) || !(curObj = Denemo.project->movement->currentobject->data) || !(DENEMO_OBJECT_TYPE_NAME (curObj))) return NULL; if(curObj->type != CHORD && Denemo.project->movement->currentobject->next) curObj = Denemo.project->movement->currentobject->next->data; if (gui->lilysync != gui->changecount) refresh_lily_cb (NULL, Denemo.project); if (curObj->lilypond) { gchar *text = g_strdup_printf ("\\score{\n\\DenemoGlobalTranspose\n\\new FretBoards {%s}\n\\layout{indent=0.0}\n}", curObj->lilypond); return text; } return NULL; } /* get a chord symbol for the chord at the cursor or before the cursor if not on the chord */ gchar *get_fakechord_as_markup (gchar *size, gchar *font) { DenemoProject *gui = Denemo.project; DenemoObject *curObj; if (!Denemo.project || !(Denemo.project->movement) || !(Denemo.project->movement->currentobject) || !(curObj = Denemo.project->movement->currentobject->data) || !(DENEMO_OBJECT_TYPE_NAME (curObj))) return NULL; if((curObj->type != CHORD) && Denemo.project->movement->currentobject->next) curObj = Denemo.project->movement->currentobject->next->data; if (gui->lilysync != gui->changecount) refresh_lily_cb (NULL, Denemo.project); if (curObj->lilypond) { 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); return text; } return NULL; } /**************** * write the status bar ********************/ void write_status (DenemoProject * gui) { if(Denemo.non_interactive) return; gint minutes = 0; gdouble seconds = 0.0; gdouble early = 0.0, late = 0.0; gchar *selection; if (gui->movement == NULL) return; static GList *last_object; if (gui->movement->currentobject) { if (last_object != gui->movement->currentobject) { if(get_wysiwyg_info()->stage != TypesetForPlaybackView) get_wysiwyg_info()->stage = STAGE_NONE;//remove the mark in the printview window as the cursor has moved get_wysiwyg_info()->Mark.width = 0; gtk_widget_queue_draw (Denemo.printarea); } last_object = gui->movement->currentobject; } if (gui->movement->currentobject && gui->movement->currentobject->data) { DenemoObject *curObj = gui->movement->currentobject->data; if ((gui->movement->smfsync == gui->movement->changecount)) { early = curObj->earliest_time, late = curObj->latest_time; } switch (curObj->type) { case CHORD: { chord *thechord = ((chord *) curObj->object); selection = g_strdup_printf ("%s%s%s%s%s%s%s%s%s", 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") : ""); if (thechord->notes) { GList *g; for (g = thechord->notes; g; g = g->next) { note *thenote = (note *) g->data; GList *h; for (h = thenote->directives; h; h = h->next) { DenemoDirective *directive = (DenemoDirective *) h->data; if (directive->postfix || directive->prefix) { gchar *old = selection; selection = g_strdup_printf ("%.50s (%s) %.50s", directive->prefix ? directive->prefix->str : "", selection, directive->postfix ? directive->postfix->str : ""); g_free (old); } } } } } break; case TUPOPEN: selection = g_strdup_printf (_("Tuplet %d/%d"), ((tupopen *) curObj->object)->numerator, ((tupopen *) curObj->object)->denominator); break; case TUPCLOSE: selection = g_strdup_printf (_("End tuplet")); break; case CLEF: selection = g_strdup_printf (_("Clef change")); break; case TIMESIG: selection = g_strdup_printf (_("Time signature change")); break; case KEYSIG: selection = g_strdup_printf (_("Key signature change")); break; case STEMDIRECTIVE: 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")); break; case DYNAMIC: selection = g_strdup_printf (_("Dynamic: %s"), ((dynamic *) curObj->object)->type->str); break; case LILYDIRECTIVE: { DenemoDirective *directive = (DenemoDirective *) curObj->object; 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"); } break; default: selection = g_strdup_printf (_("Cursor on an unknown object")); } //DenemoMeasure *measure = gui->movement->currentmeasure->data; //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); } else selection = g_strdup_printf (_("Cursor not on any object")); GString *status = g_string_new (_("Movement")); gint index = g_list_index (gui->movements, gui->movement); const gchar *dur =""; switch (get_prevailing_duration()) { case 0: dur = NOTE0; break; case 1: dur = NOTE1; break; case 2: dur = NOTE2; break; case 3: dur = NOTE3; break; case 4: dur = NOTE4; break; case 5: dur = NOTE5; break; case 6: dur = NOTE6; break; case 7: dur = NOTE7; break; case 8: dur = NOTE8; break; } g_string_printf (status, "%s%s %d: %s: ", enshift_string (gui->movement->pending_enshift), dur, index + 1, selection); if (gui->movement->smf && (gui->movement->smfsync == gui->movement->changecount) && Denemo.prefs.playback_controls) g_string_append_printf (status, _("%d min %.2f sec %.2f %.2f"), minutes, seconds, early, late); else 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" */ ); if (Denemo.prefs.midi_in_controls) { gchar *thesharp = sharpest (); gchar *theflat = flattest (); g_string_append_printf (status, " |%s - %s|", theflat, thesharp); g_free (theflat); g_free (thesharp); } g_free (selection); gchar *end_valid; if (!g_utf8_validate (status->str, -1, (const gchar **)&end_valid)) *end_valid = '\0'; gtk_label_set_text (GTK_LABEL (Denemo.statuslabel), status->str); g_string_free (status, TRUE); update_object_info (); } void write_input_status (void) { if(Denemo.non_interactive) return; gtk_label_set_markup (GTK_LABEL (Denemo.input_label), Denemo.input_filters->str); } /** * Display a message box asking primary & secondary messages * @return TRUE if the OK button was clicked or Enter pressed */ gboolean confirm (gchar * primary, gchar * secondary) { GtkWidget *dialog; gboolean r = 0; 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); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary); gtk_widget_show_all (dialog); r = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES); gtk_widget_destroy (dialog); return r; } gboolean choose_option (gchar *title, gchar * primary, gchar * secondary) { GtkWidget *dialog; gboolean r; dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), primary, GTK_RESPONSE_ACCEPT, secondary, GTK_RESPONSE_REJECT, NULL); //g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); gtk_window_set_urgency_hint (GTK_WINDOW (dialog), TRUE); gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window)); gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE); r = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT); gtk_widget_destroy (dialog); return r; } /* free a GString and the string it holds, and set the pointer to it to NULL */ void nullify_gstring (GString ** s) { if (*s) g_string_free (*s, TRUE); *s = NULL; } /* dialog to get a filename from the user */ gchar *choose_file (gchar *title, gchar *startdir, GList *extensions) { GtkWidget *dialog; gchar *filename = NULL; dialog = gtk_file_chooser_dialog_new (title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Open"), GTK_RESPONSE_ACCEPT, NULL); GtkFileFilter *filter = gtk_file_filter_new(); GString *filter_description = g_string_new (""); for(extensions;extensions;extensions=extensions->next) { gtk_file_filter_add_pattern (filter,(gchar*)extensions->data); g_string_append_printf (filter_description, "%s ", (gchar*)extensions->data); } gtk_file_filter_set_name (filter, filter_description->str); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter); gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), startdir); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); } gtk_widget_destroy (dialog); return filename; } static void hide_windows (void) { if (Denemo.prefs.hide_windows) { if(Denemo.window) gtk_widget_hide (Denemo.window); if(Denemo.printarea) gtk_widget_hide (gtk_widget_get_toplevel(Denemo.printarea)); } } static void show_windows (void) { if (Denemo.prefs.hide_windows) { if(Denemo.window) gtk_widget_show (Denemo.window); if(Denemo.printarea) gtk_widget_show (gtk_widget_get_toplevel(Denemo.printarea)); } } /** * Pops up a dialog box that has a text entry box and ok/cancel buttons * title is a title for the box. * initial_value is for the text entry box, or NULL if none. * instruction is a prompt for the user. * Returns a new value on Ok and NULL if cancelled. * The returned value should be freed by the caller. * */ gchar * string_dialog_entry (DenemoProject * gui, gchar * title, gchar * instruction, gchar * initial_value) { return string_dialog_entry_with_widget (gui, title, instruction, initial_value, NULL); } /* as string_dialog_entry() but with extra widget */ gchar * string_dialog_entry_with_widget_opt (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gboolean modal) { GtkWidget *dialog; GtkWidget *entry; GtkWidget *label; gchar *entry_string = NULL; //GString *string; entry = gtk_entry_new (); 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); 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 label = gtk_label_new (direction); GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); gtk_container_add (GTK_CONTAINER (content_area), label); if (widget) gtk_container_add (GTK_CONTAINER (content_area), widget); if (PreValue != NULL) { gtk_entry_set_text (GTK_ENTRY (entry), (gchar *) PreValue); } gtk_container_add (GTK_CONTAINER (content_area), entry); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window)); gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE); gtk_widget_show_all (dialog); if (modal) { gtk_widget_grab_focus (entry); hide_windows(); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { gchar *string = NULL; if (GTK_DIALOG (dialog)) { entry_string = (gchar *) gtk_entry_get_text (GTK_ENTRY (entry)); string = g_strdup (entry_string); gtk_widget_destroy (dialog); } show_windows(); return string; } else { if (GTK_DIALOG (dialog)) gtk_widget_destroy (dialog); } show_windows(); return NULL; } else { g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_main_quit), entry); gtk_main (); if (GTK_IS_WIDGET (entry)) { entry_string = GTK_IS_WIDGET (entry) ? g_strdup ((gchar *) gtk_entry_get_text (GTK_ENTRY (entry))) : NULL; gtk_widget_destroy (dialog); return entry_string; } return NULL; } } gchar * string_dialog_entry_with_widget (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget) { return string_dialog_entry_with_widget_opt (gui, wlabel, direction, PreValue, widget, TRUE); } /* as string_dialog_entry_with_widget() but gives a text editor instead of a single line editor */ gchar * string_dialog_editor_with_widget_opt (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gboolean modal, gpointer keypress_callback) { GtkWidget *dialog; GtkWidget *textview; GtkWidget *label; textview = gtk_text_view_new (); GtkTextBuffer *textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); gtk_text_buffer_set_text (textbuffer, PreValue ? PreValue : "", -1); 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)); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 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); if(keypress_callback) g_signal_connect (G_OBJECT (textview), "key-press-event", G_CALLBACK (keypress_callback), textbuffer); label = gtk_label_new (direction); GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); gtk_box_pack_start (GTK_BOX (content_area), label, FALSE, TRUE, 0); if (widget) { gtk_box_pack_start (GTK_BOX (content_area), widget, FALSE, TRUE, 0); g_object_set_data (G_OBJECT (widget), "textbuffer", textbuffer); g_object_set_data (G_OBJECT (widget), "textview", textview); } gtk_container_add (GTK_CONTAINER (sw), textview); gtk_box_pack_start (GTK_BOX (content_area), sw, TRUE, TRUE, 0); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window)); gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE); gtk_widget_show_all (dialog); gtk_widget_grab_focus (textview); if (modal) { if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { GtkTextIter startiter, enditer; gtk_text_buffer_get_start_iter (textbuffer, &startiter); gtk_text_buffer_get_end_iter (textbuffer, &enditer); gchar *text = gtk_text_buffer_get_text (textbuffer, &startiter, &enditer, FALSE); gtk_widget_destroy (dialog); return text; } else { gtk_widget_destroy (dialog); return NULL; } return NULL; } else { g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_main_quit), NULL); gtk_main (); if (GTK_IS_TEXT_BUFFER (textbuffer)) { GtkTextIter startiter, enditer; gtk_text_buffer_get_start_iter (textbuffer, &startiter); gtk_text_buffer_get_end_iter (textbuffer, &enditer); gchar *text = gtk_text_buffer_get_text (textbuffer, &startiter, &enditer, FALSE); gtk_widget_destroy (dialog); return text; } else { return NULL; } } } gchar * string_dialog_editor_with_widget (DenemoProject * gui, gchar * wlabel, gchar * direction, gchar * PreValue, GtkWidget * widget, gpointer keypress_callback) { return string_dialog_editor_with_widget_opt (gui, wlabel, direction, PreValue, widget, TRUE, keypress_callback); } static gboolean option_choice (GtkWidget * widget, gchar ** response) { if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) *response = g_object_get_data (G_OBJECT (widget), "choice"); //do not respond to the toggling off of other radio buttons return TRUE; } static gint root_x; static gint root_y; static gboolean dialog_realize (GtkWidget *dialog) { if(root_x) gtk_window_move (GTK_WINDOW(dialog), root_x, root_y); return FALSE; } /* run a dialog for the user to select a string from the NULL separated strings, str return NULL if user cancels.*/ static gchar * get_option_recursive (gchar *title, gchar * str, gint length, gboolean more) { gchar *response = NULL; if (title==NULL) title = _("Select from List (or Cancel)"); GtkWidget *dialog = gtk_dialog_new_with_buttons ( title, GTK_WINDOW (Denemo.window), (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), _("_OK"), GTK_RESPONSE_ACCEPT, _("_Cancel"), GTK_RESPONSE_REJECT, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); g_signal_connect_after (G_OBJECT(dialog), "realize", G_CALLBACK(dialog_realize), NULL); GtkWidget *vbox = gtk_vbox_new (FALSE, 1); GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); gtk_container_add (GTK_CONTAINER (content_area), vbox); gchar *opt; GtkWidget *widget1, *widget; for (opt = str; (opt - str) < length; opt += strlen (opt) + 1) { if (opt == str) { widget = widget1 = gtk_radio_button_new_with_label (NULL, opt); 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. } else { widget = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (widget1), opt); } g_object_set_data (G_OBJECT (widget), "choice", (gpointer) opt); g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (option_choice), &response); gtk_container_add (GTK_CONTAINER (vbox), widget); } if(more) { opt = _("More..."); widget = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (widget1), opt); g_object_set_data (G_OBJECT (widget), "choice", (gpointer) opt); g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (option_choice), &response); gtk_container_add (GTK_CONTAINER (vbox), widget); } gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(Denemo.window)); gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE); gtk_widget_show_all (dialog); hide_windows (); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_REJECT) { response = NULL; } g_debug ("Returning contents of response is %s\n", response); gtk_window_get_position (GTK_WINDOW(dialog), &root_x, &root_y); gtk_widget_destroy (dialog); show_windows (); return response; } #define MAX_ITEMS (Denemo.prefs.max_menu_size) gchar *get_option (gchar *title, gchar * str, gint length) { gchar *opt; gint i; for (opt = str, i = 0; (opt - str) < length; i++, opt += strlen (opt) + 1) { if (i < MAX_ITEMS) continue; else { gchar *response = get_option_recursive (title, str, opt-str, TRUE); if(response && (!strcmp (response, _("More...")))) { length -= (opt - str); str = opt; i = 0; continue; } else return response; } } if ((opt - str) >= length) return get_option_recursive (title, str, length, FALSE); return NULL; } /* output text to the console (lilypond errors) window. If text==NULL clear window*/ void console_output (gchar * text) { GtkTextIter enditer; GtkTextBuffer *buffer = gtk_text_view_get_buffer ((GtkTextView *) (Denemo.console)); gtk_text_buffer_get_end_iter (buffer, &enditer); if (text) gtk_text_buffer_insert (buffer, &enditer, text, -1); else gtk_text_buffer_set_text (buffer, "", -1); } /* returns an override flag ORd from all those in the list of directives, excluding ones with DENEMO_OVERRIDE_HIDDEN set */ gint get_override (GList * g) { gint ret = 0; for (; g; g = g->next) { DenemoDirective *d = g->data; if (!(d->override & DENEMO_OVERRIDE_HIDDEN)) ret |= d->override; } return ret; } //modfies name to truncate at the extension (a dot at end before any directory separators), returning a pointer to the extension chopped off // returns NULL if no extension, with name unchanged gchar * remove_extension (gchar * name) { gchar *c; if (name) for (c = name + strlen (name); c != name; c--) { if (*c == '.') { *c = '\0'; return c + 1; } if (*c == G_DIR_SEPARATOR) break; } return NULL; } //modifies name, removing the extension and returns a newly allocated string //with the passed extension gchar * substitute_extension (gchar * name, gchar * extension) { (void) remove_extension (name); return g_strdup_printf ("%s.%s", name, extension); } enum clefs cleftypefromname (gchar * str) { enum clefs ret = DENEMO_TREBLE_CLEF; if (g_strcasecmp (str, "treble") == 0) ret = DENEMO_TREBLE_CLEF; else if (g_strcasecmp (str, "bass") == 0) ret = DENEMO_BASS_CLEF; else if (g_strcasecmp (str, "alto") == 0) ret = DENEMO_ALTO_CLEF; else if (g_strcasecmp (str, "\"g_8\"") == 0) ret = DENEMO_G_8_CLEF; else if (g_strcasecmp (str, "tenor") == 0) ret = DENEMO_TENOR_CLEF; else if (g_strcasecmp (str, "soprano") == 0) ret = DENEMO_SOPRANO_CLEF; g_free (str); return ret; } gint get_widget_height (GtkWidget * w) { GtkAllocation allocation; gtk_widget_get_allocation (w, &allocation); return allocation.height; } gint get_widget_width (GtkWidget * w) { GtkAllocation allocation; gtk_widget_get_allocation (w, &allocation); return allocation.width; } void switch_back_to_main_window (void) { if (Denemo.non_interactive) return; gtk_window_present (GTK_WINDOW (Denemo.window)); gtk_widget_grab_focus (Denemo.scorearea); } /* set all labels in the hierarchy below widget to use markup */ void use_markup (GtkWidget * widget) { if (!widget) return; if (GTK_IS_LABEL (widget)) { gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); } else if (GTK_IS_CONTAINER (widget)) { GList *g = gtk_container_get_children (GTK_CONTAINER (widget)); for (; g; g = g->next) use_markup (g->data); if (GTK_IS_MENU_ITEM (widget)) { use_markup (gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget))); } } } #ifdef FAKE_TOOLTIPS static GtkWidget *TooltipDialog = NULL; static gboolean not_wanted = TRUE; static gboolean not_wanted_in_this_session = FALSE; static gboolean no_longer_wanted (GtkWidget *w) { not_wanted = TRUE; if(Denemo.prefs.newbie) not_wanted_in_this_session = FALSE; TooltipDialog = NULL; return FALSE; } gboolean show_tooltip (GtkWidget *w, GdkEvent *ev, gchar *text) { static gchar *last_tooltip; if (Denemo.prefs.newbie) not_wanted_in_this_session = not_wanted = FALSE; if (not_wanted) return FALSE; if (not_wanted_in_this_session) return FALSE; if (text && (*text) && (last_tooltip != text)) { if (TooltipDialog) { gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (TooltipDialog), text); gtk_widget_show (TooltipDialog); } else { TooltipDialog = infodialog (text); g_signal_connect (G_OBJECT (TooltipDialog), "delete-event", G_CALLBACK (no_longer_wanted), NULL); } } last_tooltip = text; return FALSE;//allow normal response } void free_tooltip (GtkWidget *w, gchar *tooltip) { //g_print ("Freeing tooltip\n"); g_free (tooltip); } #endif // Help for beginners using keyboard shortcuts static GtkWidget *KeyStrokes; static GtkWidget *KeyStrokeLabel; static GtkWidget *KeyStrokeHelp; void KeyStrokeAwait (gchar * first_keypress) { KeyStrokeShow (first_keypress, DENEMO_NO_COMMAND, TwoKey); } void KeyStrokeDecline (gchar * first_keypress) { KeyStrokeShow (first_keypress, DENEMO_NO_COMMAND, SingleKey); } void MouseGestureShow (gchar *str, gchar *help, DenemoShortcutType type) { gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), help); KeyStrokeShow (str, DENEMO_NO_COMMAND, type); } void KeyPlusMouseGestureShow(gchar *str, gint command_idx) { const gchar *label = lookup_label_from_idx (Denemo.map, command_idx); const gchar *tooltip = lookup_tooltip_from_idx (Denemo.map, command_idx); gchar *text = g_strdup_printf (_("Mouse shortcut %s" " invokes command %s"), str, label); gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Mouse Shortcut")); gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text); gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), tooltip); g_free (text); gtk_widget_show (KeyStrokes); } void KeyStrokeShow (gchar * str, gint command_idx, DenemoShortcutType type) { if (str != NULL) { gchar *text; if (command_idx != DENEMO_NO_COMMAND) { const gchar *label = lookup_label_from_idx (Denemo.map, command_idx); const gchar *tooltip = lookup_tooltip_from_idx (Denemo.map, command_idx); if (type) { text = g_strdup_printf (_("Key Press %s" " invokes command %s"), str, label); } else { text = g_strdup_printf (_("Key Presses %s" " invoke command %s"), str, label); } gtk_window_set_title (GTK_WINDOW (KeyStrokes), type==SingleKey ? _("Single Key Press") : _("Two Key Presses")); gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text); gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), tooltip); g_free (text); } else // no command { switch (type) { case SingleKey: gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Key Press")); gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), ""); text = g_strdup_printf (_("Key Press %s" " Is not a shortcut.\n%s"), str, (Denemo.project->view != DENEMO_MENU_VIEW)? _("(The menus are now restored in case you are lost.)"):""); break; case TwoKey: gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("First Key Press")); gtk_label_set_text (GTK_LABEL (KeyStrokeHelp), ""); text = g_strdup_printf (_("Key Press %s" " Awaiting continuation"), str); break; case MouseGesture: gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Mouse")); text = g_strdup_printf (_("Mouse %s"), str); break; case KeyPlusMouse: gtk_window_set_title (GTK_WINDOW (KeyStrokes), _("Key + Mouse")); text = g_strdup_printf (_("Key Press %s"), str); break; default: g_warning("bad call"); return; } gtk_label_set_markup (GTK_LABEL (KeyStrokeLabel), text); g_free (text); } } gtk_widget_show (KeyStrokes); } static gboolean toggle_show_keystroke_preference (void) { Denemo.prefs.learning = FALSE; gtk_widget_hide (KeyStrokes); return TRUE; } void initialize_keystroke_help (void) { if (KeyStrokes == NULL) { KeyStrokes = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (G_OBJECT (KeyStrokes), "delete-event", G_CALLBACK (toggle_show_keystroke_preference), NULL); gtk_window_set_transient_for (GTK_WINDOW(KeyStrokes), GTK_WINDOW(Denemo.window)); gtk_window_set_keep_above (GTK_WINDOW (KeyStrokes), TRUE); gtk_window_set_accept_focus (GTK_WINDOW (KeyStrokes), FALSE); KeyStrokeLabel = gtk_label_new (""); //gtk_label_set_line_wrap (KeyStrokeLabel, TRUE); KeyStrokeHelp = gtk_label_new (""); gtk_label_set_line_wrap (GTK_LABEL(KeyStrokeHelp), TRUE); GtkWidget *vbox = gtk_vbox_new (FALSE, 8); gtk_container_add (GTK_CONTAINER (KeyStrokes), vbox); gtk_box_pack_start (GTK_BOX (vbox), KeyStrokeLabel, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), KeyStrokeHelp, FALSE, TRUE, 0); gtk_label_set_use_markup (GTK_LABEL (KeyStrokeLabel), TRUE); gtk_widget_show_all (KeyStrokes); } gtk_widget_hide (KeyStrokes); } /** * find_dir_for_file: * @filename: The file to search * @dirs: A dir paths array, ending by NULL, where to search. * * Finds the first dir in the list that contains 'filename', and free the array. * * Returns: The dir path if found, NULL either **/ gchar* find_dir_for_file(gchar* filename, GList* dirs) { gchar *dir = NULL; gchar *path = NULL; GList* curdir = NULL; for(curdir = dirs ; curdir ; curdir = g_list_next(curdir)) { if(!dir) { path = g_build_filename (curdir->data, filename, NULL); if(g_file_test (path, G_FILE_TEST_EXISTS)) dir = g_strdup(curdir->data); //even though found, the loop is continued to clear the list of dirs // else here causes a memory leak as path is not used hereafter g_free(path); } g_free(curdir->data); } return dir; } /** * find_dir_for_files: * @files: The files to search * @dirs: A dir paths list, where to search. * * Finds the first dir in the list that contains 'filename', and free the array. * * Returns: The dir path if found, NULL either **/ gchar* find_dir_for_files(GList* files, GList* dirs) { gchar *dir = NULL; gchar *path = NULL; GList* curdir = NULL; GList* curfile = NULL; for(curdir = dirs; curdir; curdir = g_list_next(curdir)) { for(curfile = files; curfile; curfile = g_list_next(curfile)) { if(!dir) { path = g_build_filename (curdir->data, curfile->data, NULL); if(g_file_test (path, G_FILE_TEST_EXISTS)) dir = g_strdup(curdir->data); g_free(path); } } g_free(curdir->data); } return dir; } /** * find_path_for_file: * @filename: The file to search * @dirs: A dir paths list, where to search. * * Finds the first dir in the list that contains 'filename', and free the array. * * Returns: The file path if found, NULL either **/ gchar* find_path_for_file(gchar* filename, GList* dirs) { gchar* dir = find_dir_for_file (filename, dirs); if(dir){ gchar* path = g_build_filename(dir, filename, NULL); g_free(dir); return path; } return NULL; } gchar * get_project_dir (void) { if(Denemo.project && Denemo.project->filename->len) { return g_path_get_dirname (Denemo.project->filename->str); } return g_strdup(g_get_home_dir ()); } const gchar* get_local_dir(DenemoDirectory dir) { switch(dir) { case DENEMO_DIR_COMMANDS: return COMMANDS_DIR; case DENEMO_DIR_UI: return UI_DIR; case DENEMO_DIR_SOUNDFONTS: return SOUNDFONTS_DIR; case DENEMO_DIR_PIXMAPS: return PIXMAPS_DIR; case DENEMO_DIR_FONTS: return FONTS_DIR; case DENEMO_DIR_LOCALE: return LOCALE_DIR; case DENEMO_DIR_LILYPOND_INCLUDE: return LILYPOND_INCLUDE_DIR; default: return NULL; } } gchar* get_system_dir(DenemoDirectory dir) { switch(dir) { case DENEMO_DIR_COMMANDS: case DENEMO_DIR_UI: case DENEMO_DIR_SOUNDFONTS: case DENEMO_DIR_FONTS: case DENEMO_DIR_LILYPOND_INCLUDE: return g_build_filename(get_system_data_dir (), get_local_dir(dir), NULL); case DENEMO_DIR_PIXMAPS: return g_build_filename(get_system_data_dir (), PIXMAPS_DIR, NULL); break; case DENEMO_DIR_LOCALE: return g_strdup(get_system_locale_dir ()); break; case DENEMO_DIR_BIN: return g_strdup(get_system_bin_dir ()); break; default: return NULL; } } const gchar* get_executable_dir () { static const gchar* dir = NULL; if(dir == NULL) { char path[1024]; gint n; #ifdef G_OS_WIN32 GetModuleFileNameW(NULL, path, MAX_PATH); #elif defined _MACH_O_ guint size = sizeof (path); _NSGetExecutablePath (path, &size); #else if ((n=readlink("/proc/self/exe", path, sizeof(path))) < 0) { perror("/proc/self/exe"); return NULL; } path[n] = 0; #endif dir = g_path_get_dirname(path); } return dir; } /** * find: * @dir: The denemo directory where to search * @filename: The file to search * * Finds a file by searching: * - in the local directory * - in the executable parent directory * - in the user directory * - in the system directory * - in the system fonts directory **/ gchar* find_denemo_file (DenemoDirectory dir, gchar* filename) { //g_debug("find_denemo_file called with %d and %s\n", dir, filename); GList* dirs = NULL; dirs = g_list_append(dirs, g_build_filename(PACKAGE_SOURCE_DIR, get_local_dir (dir), NULL)); dirs = g_list_append(dirs, g_build_filename(get_executable_dir (), "..", get_local_dir (dir), NULL)); dirs = g_list_append(dirs, g_build_filename(get_user_data_dir (TRUE), get_local_dir (dir), NULL)); dirs = g_list_append(dirs, g_strdup(get_system_dir(dir))); dirs = g_list_append(dirs, g_build_filename(get_executable_dir (), "..", "share","fonts","truetype","denemo", NULL)); return find_path_for_file (filename, dirs); } gchar *escape_scheme (gchar *input) { gchar *c = input -1; GString *out = g_string_new(""); for(c=input; c && *c; c++) { if(*c=='"') g_string_append (out, "\\\\\\\""); else if(*c=='\\') g_string_append(out, "\\\\"); else { g_string_append_printf (out, "%c", *c); } } return g_string_free(out, FALSE); } gboolean shift_held_down(void) { GdkModifierType mask; GdkWindow *win = gtk_widget_get_window (Denemo.window); #if GTK_MAJOR_VERSION == 2 gdk_window_get_pointer (win, NULL, NULL, &mask); #else gdk_window_get_device_position (win, gdk_device_manager_get_client_pointer (gdk_display_get_device_manager(gdk_display_get_default())) ,NULL, NULL, &mask); #endif return (mask & GDK_SHIFT_MASK); }