/** * staffops.cpp * functions dealing with whole staffs * * for Denemo, a gtk+ frontend to GNU Lilypond * (c) 1999-2005 Matthew Hiller */ #include "chordops.h" #include "contexts.h" #include #include "dialogs.h" #include "measureops.h" #include "moveviewport.h" #include "objops.h" #include "processstaffname.h" #include "staffops.h" #include #include #include #include "calculatepositions.h" #include "commandfuncs.h" /** * Return the first object node of the given measure * @param mnode a measurenode * @return the first object node of the measure */ objnode * firstobjnode (measurenode * mnode) { return (objnode *) mnode->data; } /** * Return the last object node of the given measure * @param mnode a measurenode * @return the last object node of the measure */ objnode * lastobjnode (measurenode * mnode) { return g_list_last ((objnode *) mnode->data); } /** * Return the first measure node of the given staffops * @param thestaff a staffnode * @return the first measure node of the staff */ measurenode * firstmeasurenode (staffnode * thestaff) { return ((DenemoStaff *) thestaff->data)->measures; } /** * Return the nth measure of the given staff * @param thestaff a staffnode * @param n the number of the measure to return * @return the nth measure of the staff */ measurenode * nth_measure_node_in_staff (staffnode * thestaff, gint n) { return g_list_nth (((DenemoStaff *) thestaff->data)->measures, n); } /** * Return the first object node in the given staff * @param thestaff a staffnode * @return the first object node of the staff */ objnode * firstobjnodeinstaff (staffnode * thestaff) { return firstobjnode (firstmeasurenode (thestaff)); } /** * Reset si->currentprimarystaff based on current value of * si->currentstaff * @param si a scoreinfo structure * @return none */ void setcurrentprimarystaff (DenemoScore * si) { for (si->currentprimarystaff = si->currentstaff; ((DenemoStaff *) si->currentprimarystaff->data)->voicenumber != 1; si->currentprimarystaff = si->currentprimarystaff->prev) ; } /** * Copies the staff data from a source staff to destination staff * @param src the source staff * @param dest the destination staff * @return none */ static void copy_staff_bits (DenemoStaff * src, DenemoStaff * dest) { dest->sclef = src->sclef; dest->skey = src->skey; dest->skey_isminor = src->skey_isminor; memcpy (dest->skeyaccs, src->skeyaccs, SEVENGINTS); dest->stime1 = src->stime1; dest->stime2 = src->stime2; dest->no_of_lines = 5; dest->transposition = 0; dest->pos_in_half_lines = 0; dest->space_above = 0; dest->space_below = 0; dest->context = DENEMO_NONE; } /** * Copies a staffs parameters from source to destination * @param src the source staff * @param dest the destination staff * @return none */ static void copy_staff_properties (DenemoStaff * src, DenemoStaff * dest) { set_lily_name (dest->denemo_name, dest->lily_name); /* !!!! Insert advisory function for detecting colliding staff names * here */ dest->midi_instrument = g_string_new (src->midi_instrument->str); dest->space_above = src->space_above; dest->space_below = src->space_below; dest->no_of_lines = src->no_of_lines; dest->transposition = src->transposition; dest->pos_in_half_lines = src->pos_in_half_lines; dest->volume = src->volume; dest->voicenumber = 2; beamsandstemdirswholestaff (dest); } /** * Insert a new staff into the score * @param si the scoreinfo structure * @param thestaffstruct the staff to insert * @param action where to insert the new staff * @param addat the position to insert at * @return none */ static void insert_staff (DenemoScore * si, DenemoStaff * thestaffstruct, enum newstaffcallbackaction action, gint addat) { si->thescore = g_list_insert (si->thescore, thestaffstruct, addat - 1); if (action == BEFORE) si->currentstaff = g_list_nth (si->thescore, addat - 1); else si->currentstaff = g_list_nth (si->thescore, si->currentstaffnum - 1); setcurrentprimarystaff (si); find_leftmost_staffcontext (thestaffstruct, si); g_print ("newstaff: Num staffs after insert %d\n", g_list_length (si->thescore)); } /** * Create and insert a new staff into the score * @param si the scoreinfo structure * @param action the staffs type / where to insert it * @param context the staffs contexts * @return none */ void newstaff (DenemoScore * si, enum newstaffcallbackaction action, DenemoContext context) { g_assert (si != NULL); /* What gets added */ gint ret = -1; DenemoStaff *thestaffstruct = (DenemoStaff *) g_malloc (sizeof (DenemoStaff)); struct newstaffinfotopass itp; measurenode *themeasures = NULL; /* Initial set of measures in staff */ gint numstaffs = g_list_length (si->thescore); gint i, n, addat = 1; if (si->lily_file) return; /* no code for this yet - just edit textually */ #ifdef DEBUG g_print ("newstaff: Num staffs %d\n", numstaffs); #endif if (numstaffs == 0) { action = INITIAL; n = 1; thestaffstruct->sclef = DENEMO_TREBLE_CLEF; thestaffstruct->skey = 0; thestaffstruct->skey_isminor = FALSE; memset (thestaffstruct->skeyaccs, 0, SEVENGINTS); thestaffstruct->stime1 = 4; thestaffstruct->stime2 = 4; thestaffstruct->no_of_lines = 5; thestaffstruct->transposition = 0; thestaffstruct->pos_in_half_lines = 0; thestaffstruct->space_above = 0; thestaffstruct->space_below = 0; thestaffstruct->volume = 128; thestaffstruct->nummeasures = 1; si->measurewidths = g_list_append (si->measurewidths, GINT_TO_POINTER (si->measurewidth)); } else { thestaffstruct->nummeasures = 1; copy_staff_bits ((DenemoStaff *) si->currentstaff->data, thestaffstruct); }; if (action == NEWVOICE) { thestaffstruct->voicenumber = 2; thestaffstruct->nummeasures = g_list_length (firstmeasurenode (si->currentstaff)); } else if (action == LYRICSTAFF) { thestaffstruct->voicenumber = 3; } else if (action == FIGURESTAFF) { thestaffstruct->voicenumber = 3; /* what does this mean? */ } else if (action == CHORDSTAFF) { thestaffstruct->voicenumber = 3; } else { thestaffstruct->voicenumber = 1; }; for (i = 0; i < thestaffstruct->nummeasures; i++) { themeasures = g_list_append (themeasures, NULL); }; if (action == INITIAL || action == ADDFROMLOAD) { si->currentmeasure = themeasures; }; /* Now fix the stuff that shouldn't be directly copied from * the current staff, if this staff was non-initial and that * was done to begin with */ thestaffstruct->measures = themeasures; thestaffstruct->denemo_name = g_string_new (NULL); thestaffstruct->lily_name = g_string_new (NULL); thestaffstruct->context = context; if (action == NEWVOICE) /* Fix me to something more reasonable and less prone to collision * with other staff names */ g_string_sprintf (thestaffstruct->denemo_name, _("poly voice")); else if (action == LYRICSTAFF) g_string_sprintf (thestaffstruct->denemo_name, _("lyrics")); else if (action == FIGURESTAFF) g_string_sprintf (thestaffstruct->denemo_name, _("figures")); else g_string_sprintf (thestaffstruct->denemo_name, _("voice %d"), numstaffs + 1); set_lily_name (thestaffstruct->denemo_name, thestaffstruct->lily_name); thestaffstruct->midi_instrument = g_string_new ("acoustic grand"); /* In what position should the scrollbar be added? */ switch (action) { case INITIAL: addat = 1; break; case LAST: case ADDFROMLOAD: addat = numstaffs + 1; break; case BEFORE: addat = si->currentstaffnum; break; case AFTER: case NEWVOICE: case LYRICSTAFF: case FIGURESTAFF: addat = si->currentstaffnum + 1; break; case CHORDSTAFF: addat = si->currentstaffnum + 1; break; case FIRST: addat = 1; break; } itp.staff = thestaffstruct; itp.addat = addat; if (action != INITIAL && action != ADDFROMLOAD) { if (action == NEWVOICE) { copy_staff_properties ((DenemoStaff *) si->currentstaff->data, thestaffstruct); insert_staff (si, thestaffstruct, action, addat); } else { ret = staff_properties_change (NULL, &itp); if (ret) { /* If staff_properties_change returns false, then the staff should probably be removed Fixed 09042005 Adam Tee */ insert_staff (si, thestaffstruct, action, addat); si->currentmeasurenum = 1; /* Reset leftmeasure num to 1 to be at the start of the next staff. */ si->leftmeasurenum = 1; } else { /* * Free the staff struct as it has not been inserted * into the score */ g_free (thestaffstruct); } } } else { insert_staff (si, thestaffstruct, action, addat); } si->haschanged = TRUE; } /** * Remove a number of staffs from the score * @param si the scoreinfo structure * @param pos the position of the staff to remove * @param numstaffs the number of staffs to remove * @return none */ void removestaff (DenemoScore * si, gint pos, gint numstaffs) { /* The second and third parameters are mostly ignored in this version * of the function */ DenemoStaff *curstaffstruct = (DenemoStaff *) (si->currentstaff ? si->currentstaff-> data : NULL); if (curstaffstruct) { gboolean isprimary = ((int) curstaffstruct->voicenumber == 1) ? TRUE : FALSE; if (si->lily_file != NULL) /* if a lily file, then the objects are freed with the lily tree */ g_list_foreach (curstaffstruct->measures, freeobjlist, NULL); g_list_free (curstaffstruct->measures); g_string_free (curstaffstruct->denemo_name, FALSE); g_string_free (curstaffstruct->lily_name, FALSE); g_string_free (curstaffstruct->midi_instrument, FALSE); if (si->lily_file) { curstaffstruct->measures = NULL; /* don't free the structure as a lily file references it */ } else g_free (curstaffstruct); si->thescore = g_list_remove_link (si->thescore, si->currentstaff); g_list_free_1 (si->currentstaff); if (pos == (gint) (g_list_length (si->thescore))) { si->currentstaffnum--; }; si->currentstaff = g_list_nth (si->thescore, si->currentstaffnum - 1); if (si->currentstaff) { /* O.K.; set si->currentprimarystaff correctly */ if (isprimary) { ((DenemoStaff *) si->currentstaff->data)->voicenumber = 1; si->currentprimarystaff = si->currentstaff; } else { setcurrentprimarystaff (si); }; } else { si->currentprimarystaff = NULL; } } } /** * Sets the beams and stem directions across the given staff * @param thestaff a staff structure * @return none */ void beamsandstemdirswholestaff (DenemoStaff * thestaff) { measurenode *curmeasure; gint nclef, time1, time2, stem_directive; nclef = thestaff->sclef; time1 = thestaff->stime1; time2 = thestaff->stime2; stem_directive = DENEMO_STEMBOTH; for (curmeasure = thestaff->measures; curmeasure; curmeasure = curmeasure->next) { calculatebeamsandstemdirs ((objnode *) curmeasure->data, &nclef, &time1, &time2, &stem_directive); } } /** * Sets which accidentals to show across a staff on a key sig change * @param thestaff a staff stucture * @return none */ void showwhichaccidentalswholestaff (DenemoStaff * thestaff) { gint feed[7]; gint feednum; measurenode *curmeasure; memcpy (feed, thestaff->skeyaccs, SEVENGINTS); feednum = thestaff->skey; for (curmeasure = thestaff->measures; curmeasure; curmeasure = curmeasure->next) feednum = showwhichaccidentals ((objnode *) curmeasure->data, feednum, feed); } /** * Function to set the note positions on the given staff when there is a clef change * @param thestaff a staff structure * @return none */ void fixnoteheights (DenemoStaff * thestaff) { gint nclef = thestaff->sclef; gint time1 = thestaff->stime1; gint time2 = thestaff->stime2; gint initialclef; measurenode *curmeasure; objnode *curobj; DenemoObject *theobj; for (curmeasure = thestaff->measures; curmeasure; curmeasure = curmeasure->next) { initialclef = nclef; for (curobj = (objnode *) curmeasure->data; curobj; curobj = curobj->next) { theobj = (DenemoObject *) curobj->data; switch (theobj->type) { case CHORD: newclefify (theobj, nclef); break; case TIMESIG: time1 = ((timesig *) theobj->object)->time1; time2 = ((timesig *) theobj->object)->time2; break; case CLEF: nclef = ((clef *) theobj->object)->type; break; default: break; } } /* End for */ } /* End for */ beamsandstemdirswholestaff (thestaff); } /** * Callback function to insert a staff in the initial position * @param action a Gtk Action * @param gui the DenemoGUI structure * @return none */ void newstaffinitial (GtkAction * action, DenemoGUI * gui) { newstaff (gui->si, INITIAL, DENEMO_NONE); set_bottom_staff (gui); displayhelper (gui); } /** Callback function to insert a staff before the current staff * @param action a Gtk Action * @param gui the DenemoGUI structure * @return none */ void newstaffbefore (GtkAction * action, DenemoGUI * gui) { // g_print ("New Staff before: score info %X\n", data); newstaff (gui->si, BEFORE, DENEMO_NONE); set_bottom_staff (gui); move_viewport_down (gui); staffup (gui); displayhelper (gui); } /** * Callback function to insert a staff after the current staff * @param action a Gtk Action * @param gui the DenemoGUI structure * @return none */ void dnm_newstaffafter (GtkAction * action, DenemoGUI * gui) { newstaff (gui->si, AFTER, DENEMO_NONE); set_bottom_staff (gui); update_vscrollbar (gui); staffdown (gui); displayhelper (gui); } /** * Callback function to insert a staff at the bottom of the score * @param action a Gtk Action * @param gui the DenemoGUI structure * @return none */ void newstafflast (GtkAction * action, DenemoGUI * gui) { newstaff (gui->si, LAST, DENEMO_NONE); set_bottom_staff (gui); update_vscrollbar (gui); staffdown (gui); displayhelper (gui); } /** * Callback function to add a new voice to the current staff * @param action a Gtk Action * @param gui the DenemoGUI structure * @return none */ void dnm_newstaffvoice (GtkAction * action, DenemoGUI * gui) { newstaff (gui->si, NEWVOICE, DENEMO_NONE); set_bottom_staff (gui); update_vscrollbar (gui); staffdown (gui); displayhelper (gui); } /** * Callback function to create a lyric staff below the current staff * @param action a Gtk Action * @param gui the DenemoGUI structure * @return none */ void newstafflyric (GtkAction * action, DenemoGUI * gui) { newstaff (gui->si, LYRICSTAFF, DENEMO_NONE); set_bottom_staff (gui); update_vscrollbar (gui); staffdown (gui); displayhelper (gui); } /** * Callback function to insert a figured bass staff after the current staff * @param action a Gtk Action * @param gui the DenemoGUI structure * @return none */ void dnm_newstafffigured (GtkAction * action, DenemoGUI * gui) { newstaff (gui->si, FIGURESTAFF, DENEMO_NONE); set_bottom_staff (gui); update_vscrollbar (gui); staffdown (gui); displayhelper (gui); } /* * Callback function to insert chords above the current staff * @param action a Gtk Action * @param gui the DenemoGUI structure * @return none */ void dnm_newstaffchords (GtkAction * action, DenemoGUI * gui) { printf("\n ok I am going to add chords to the staff now\n"); newstaff (gui->si, CHORDSTAFF, DENEMO_NONE); //set_bottom_staff (gui); update_vscrollbar (gui); //staffdown (gui); displayhelper (gui); }