/* utils.cpp * 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 #include "accwidths.h" #include #include "notewidths.h" #include "utils.h" /** * Displays warning to screen * @param msg warning message to display * @return none */ void warningdialog (gchar * msg) { GtkWidget *dialog; dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, msg); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } /** * 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 */ void drawbitmapinverse (GdkPixmap * pixmap, GdkGC * gc, GdkBitmap * mask, gint x, gint y, gint width, gint height) { gdk_gc_set_clip_mask (gc, mask); gdk_gc_set_clip_origin (gc, x, y); gdk_draw_rectangle (pixmap, gc, TRUE, x, y, width, height); gdk_gc_set_clip_mask (gc, NULL); /* Removes clip mask */ } /** * 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: 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 * * @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; headtype = MIN (baseduration, 2); if (chordval.tones) { /* if (theobj->isstart_beamgroup && theobj->isend_beamgroup && chordval.is_stemup) theobj->minpixelsalloted = headwidths[headtype] + STEM_WIDTH; else theobj->minpixelsalloted = headwidths[headtype]; */ /* We can get away with that because the stems are narrower * than even the narrowest notes; upstemmed notes are * the unusual case here */ /* The above code will allow extra space for the stem of * stemup notes. It's commented out 'cause we no longer want * that behavior */ theobj->minpixelsalloted = headwidths[headtype]; } else /* a rest */ theobj->minpixelsalloted = restwidths[baseduration]; /* 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.tones; 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 = 40; theobj->space_before = 0; break; case CLEF: theobj->minpixelsalloted = 35; theobj->space_before = 0; break; case KEYSIG: 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 = 40; theobj->space_before = 0; break; default: theobj->minpixelsalloted = 0; theobj->space_before = 0; break; } } /** * Returns the height of a tone based on its mid_c_offset and the clef that it's in * * @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_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; } 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 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) { int otn = offsettonumber (mid_c_offset); return ((otn + 2) % 7) + 'a'; } /** * 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); } /** * Displays a progress dialog * * @param gui pointer to the DenemoGUI structure * */ GtkWidget * progressdialog (DenemoGUI * gui) { GtkWidget *dialog; dialog = gtk_dialog_new (); //vbox = gtk_vbox_new(FALSE,5); //gtk_container_add(GTK_CONTAINER(dialog), vbox); gui->progressbar = gtk_progress_bar_new (); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), gui->progressbar, FALSE, FALSE, 5); gtk_widget_show (gui->progressbar); gtk_widget_show (dialog); return (dialog); } /************* routines for calling from debug code ***************/ #include "staffops.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 (firstobjnode (mnode)); } G_GNUC_UNUSED void printmeasures (staffnode * thestaff) { GList *measure = firstmeasurenode (thestaff); gint measurenum = 1; for (measure = firstmeasurenode (thestaff); measure; measure = measure->next) { fprintf (stderr, "*************Measure %d *************\n", measurenum++); printmeasure (measure); } } G_GNUC_UNUSED void printscoreinfo (DenemoScore * si) { if (si->thescore == NULL) { fprintf (stderr, "Staff with NULL thescore field\n"); return; } printmeasures (si->thescore); }