1 /**
2  * staff.cpp
3  * functions dealing with whole staffs
4  *
5  * for Denemo, a gtk+ frontend to GNU Lilypond
6  * (c) 1999-2005 Matthew Hiller
7  */
8 
9 #include "command/chord.h"
10 #include "command/contexts.h"
11 #include <denemo/denemo.h>
12 #include "ui/dialogs.h"
13 #include "command/measure.h"
14 #include "ui/moveviewport.h"
15 #include "command/object.h"
16 #include "command/processstaffname.h"
17 #include "command/staff.h"
18 #include <stdlib.h>
19 #include <string.h>
20 #include "display/calculatepositions.h"
21 #include "command/commandfuncs.h"
22 #include "command/lilydirectives.h"
23 #include "display/displayanimation.h"
24 #include "command/select.h"
25 #include "core/cache.h"
26 #include "core/utils.h"
27 #include "command/lyric.h"
28 
29 gboolean
signal_structural_change(DenemoProject * project)30 signal_structural_change (DenemoProject * project)
31 {
32   project->layout_sync = project->changecount;
33   return TRUE;
34 }
35 
36 
37 /**
38  * Return the first measure node of the given staffops
39  * @param thestaff a staffnode
40  * @return the first measure node of the staff
41  */
42 measurenode *
staff_first_measure_node(staffnode * thestaff)43 staff_first_measure_node (staffnode * thestaff)
44 {
45   return ((DenemoStaff *) thestaff->data)->themeasures;
46 }
47 
48 /**
49  * Return the nth measure of the given staff
50  * @param thestaff a staffnode
51  * @param n the number of the measure to return
52  * @return the nth measure of the staff
53  */
54 measurenode *
staff_nth_measure_node(staffnode * thestaff,gint n)55 staff_nth_measure_node (staffnode * thestaff, gint n)
56 {
57   return g_list_nth (((DenemoStaff *) thestaff->data)->themeasures, n);
58 }
59 
60 /**
61  * Reset movement->currentprimarystaff based on current value of
62  * movement->currentstaff
63  * @param movement a scoreinfo structure
64  * @return none
65  */
66 void
staff_set_current_primary(DenemoMovement * movement)67 staff_set_current_primary (DenemoMovement * movement)
68 {
69   for (movement->currentprimarystaff = movement->currentstaff; movement->currentprimarystaff && (((DenemoStaff *) movement->currentprimarystaff->data)->voicecontrol & DENEMO_SECONDARY); movement->currentprimarystaff = movement->currentprimarystaff->prev)
70    ;
71 }
72 
73 /**
74  * Copies the staff data from a source staff to destination staff
75  * @param src the source staff
76  * @param dest the destination staff
77  * @return none
78  */
79 static void
staff_copy_bits(DenemoStaff * src,DenemoStaff * dest)80 staff_copy_bits (DenemoStaff * src, DenemoStaff * dest)
81 {
82   dest->clef.type = src->clef.type;     //other fields - take care if dynamic
83   dest->keysig.number = src->keysig.number;
84   dest->keysig.isminor = src->keysig.isminor;
85   memcpy (dest->keysig.accs, src->keysig.accs, SEVENGINTS);
86   dest->timesig.time1 = src->timesig.time1;
87   dest->timesig.time2 = src->timesig.time2;
88   dest->volume = 127;
89   dest->no_of_lines = 5;
90   dest->transposition = 0;
91   dest->hide_lyrics = src->hide_lyrics;
92   dest->space_above = 0;
93   dest->space_below = 0;
94   dest->context = DENEMO_NONE;
95 }
96 
97 /**
98  * Copies a staffs parameters from source to destination FIXME: only for a new voice - does not really copy
99  * @param src the source staff
100  * @param dest the destination staff
101  * @return none
102  */
103 static void
staff_copy_properties(DenemoStaff * src,DenemoStaff * dest)104 staff_copy_properties (DenemoStaff * src, DenemoStaff * dest)
105 {
106   dest->midi_instrument = g_string_new (src->midi_instrument->str);
107   dest->space_above = src->space_above;
108   dest->space_shorten = src->space_shorten;
109   dest->space_below = src->space_below;
110   dest->no_of_lines = src->no_of_lines;
111   dest->transposition = src->transposition;
112   dest->hide_lyrics = src->hide_lyrics;
113 
114   dest->volume = src->volume;
115   dest->voicecontrol = DENEMO_SECONDARY;
116   staff_beams_and_stems_dirs (dest);
117 }
118 
119 
120 
121 /* copies a staff without its music data to another staff */
122 void
staff_copy(DenemoStaff * src,DenemoStaff * dest)123 staff_copy (DenemoStaff * src, DenemoStaff * dest)
124 {
125   dest->denemo_name = g_string_new (src->denemo_name->str);
126   dest->lily_name = g_string_new (src->lily_name->str);
127   dest->midi_instrument = g_string_new (src->midi_instrument->str);
128   dest->midi_channel = src->midi_channel;
129   dest->midi_prognum = src->midi_prognum;
130   dest->hide_lyrics = src->hide_lyrics;
131 
132   dest->no_of_lines = src->no_of_lines;
133   dest->transposition = src->transposition;
134   dest->space_above = src->space_above; /**< space above the staff used in the denemo gui */
135   dest->space_shorten = src->space_shorten; /**< space by the staff is shorter in height because of few staff lines */
136   dest->space_below = src->space_below; /**< space below the staff used in the denemo gui */
137   dest->range = src->range;/**<TRUE if range_hi,lo should be observed. */
138   dest->range_hi = src->range_hi;/**< highest note playable by instrument, mid_c_offset */
139   dest->range_lo = src->range_lo;/**< lowest note playable by instrument, mid_c_offset */
140   dest->volume = src->volume;
141   dest->voicecontrol = src->voicecontrol;
142   dest->clef.type = src->clef.type;
143   dest->leftmost_clefcontext = &dest->clef;
144 
145   dest->staff_directives = clone_directives (src->staff_directives);
146   dest->voice_directives = clone_directives (src->voice_directives);
147 
148   dest->clef.directives = clone_directives (src->clef.directives);
149   dest->keysig.directives = clone_directives (src->keysig.directives);
150   dest->timesig.directives = clone_directives (src->timesig.directives);
151 
152 
153   dest->keysig.number = src->keysig.number;
154   dest->keysig.isminor = src->keysig.isminor;
155   memcpy (dest->keysig.accs, src->keysig.accs, SEVENGINTS);
156   dest->leftmost_keysig = &dest->keysig;
157 
158   dest->timesig.time1 = src->timesig.time1;
159   dest->timesig.time2 = src->timesig.time2;
160 
161   dest->transposition = src->transposition;
162 
163   dest->space_above = 0;
164   dest->space_below = 0;
165   dest->context = src->context;
166 }
167 
168 /**
169  * Insert a new staff into the score
170  * @param movement the scoreinfo structure
171  * @param staff the staff to insert
172  * @param action where to insert the new staff
173  * @param addat the position to insert at
174  * @return none
175  */
176 static void
staff_insert(DenemoMovement * movement,DenemoStaff * staff,enum newstaffcallbackaction action,gint addat)177 staff_insert (DenemoMovement * movement, DenemoStaff * staff, enum newstaffcallbackaction action, gint addat)
178 {
179   movement->thescore = g_list_insert (movement->thescore, staff, addat - 1);
180   if (action != BEFORE)
181     if (action != AFTER)
182       {
183         movement->currentstaff = g_list_nth (movement->thescore, addat - 1);
184         movement->currentstaffnum = addat;
185         staff_set_current_primary (movement);
186         find_leftmost_staffcontext (staff, movement);
187       }
188   set_staff_transition (20);
189 }
190 
191 /**
192  * Create and insert a new staff into the score
193  * @param movement the scoreinfo structure
194  * @param action the staffs type / where to insert it
195  * @param context the staffs contexts
196  * @return the newly created staff
197  */
198 DenemoStaff*
staff_new(DenemoProject * project,enum newstaffcallbackaction action,DenemoContext context)199 staff_new (DenemoProject * project, enum newstaffcallbackaction action, DenemoContext context)
200 {
201   DenemoMovement *movement = project->movement;
202   if (movement == NULL) return NULL;
203   take_snapshot ();
204   DenemoStaff *staff = (DenemoStaff *) g_malloc (sizeof (DenemoStaff));
205 
206   if(!Denemo.non_interactive){
207     staff->staffmenu = (GtkMenu *) gtk_menu_new ();
208     staff->voicemenu = (GtkMenu *) gtk_menu_new ();
209   }
210 
211   measurenode *themeasures = NULL;      /* Initial set of measures in staff */
212   gint numstaffs = g_list_length (movement->thescore);
213   gint i, addat = 1;
214   //g_debug ("newstaff: Num staffs %d", numstaffs);
215   if (numstaffs == 0)
216     {
217       action = INITIAL;
218 
219       staff->clef.type = DENEMO_TREBLE_CLEF;
220       staff->keysig.number = 0;
221       staff->keysig.isminor = FALSE;
222       memset (staff->keysig.accs, 0, SEVENGINTS);
223       staff->timesig.time1 = 4;
224       staff->timesig.time2 = 4;
225       staff->volume = 127;
226       staff->no_of_lines = 5;
227       staff->transposition = 0;
228 
229       staff->space_above = 20;
230       staff->space_below = 0;
231       staff->nummeasures = 1;
232       staff->midi_channel = 0;
233 #if 0
234       movement->measurewidths = g_list_append (movement->measurewidths, GINT_TO_POINTER (movement->measurewidth));
235 #else
236       movement->measurewidths = g_list_append (NULL, GINT_TO_POINTER (movement->measurewidth));     //FIXME free old measurewidths
237 #endif
238     }
239   else
240     {
241       /* how did this work before? a new staff must have the same number of measures as the present one(s) */
242       staff->nummeasures = g_list_length (staff_first_measure_node (movement->thescore));
243       staff_copy_bits ((DenemoStaff *) movement->currentstaff->data, staff);
244       staff->midi_channel = (numstaffs < 9 ? numstaffs : numstaffs + 1) & 0xF;
245     }
246 
247   if (action == NEWVOICE)
248     {
249       staff->voicecontrol = DENEMO_SECONDARY;
250       staff->nummeasures = g_list_length (staff_first_measure_node (movement->currentstaff));        //FIXME redundant
251     }
252   else
253     {
254       staff->voicecontrol = DENEMO_PRIMARY;
255     };
256 
257   for (i = 0; i < staff->nummeasures; i++)
258     {
259       themeasures = g_list_append (themeasures, (gpointer)g_malloc0(sizeof(DenemoMeasure)));
260     };
261 
262   if (action == INITIAL || action == ADDFROMLOAD)
263     {
264       movement->currentmeasure = themeasures;
265     }
266 
267   /* Now fix the stuff that shouldn't be directly copied from
268    * the current staff, if this staff was non-initial and that
269    * was done to begin with */
270 
271   staff->themeasures = themeasures;
272   staff->denemo_name = g_string_new ("");
273   staff->lily_name = g_string_new ("");
274 
275   staff->context = context;
276   if (action == INITIAL)
277     g_string_sprintf (staff->denemo_name, _("Part 1"));
278   else
279     g_string_sprintf (staff->denemo_name, _("Part %d"), numstaffs + 1);
280   set_lily_name (staff->denemo_name, staff->lily_name);
281   staff->midi_instrument = g_string_new ("");
282   staff->device_port = g_string_new ("NONE");
283   staff->leftmost_timesig = &staff->timesig;
284 
285   /* In what position should the scrollbar be added?  */
286   switch (action)
287     {
288     case INITIAL:
289       addat = 1;
290       break;
291     case LAST:
292     case ADDFROMLOAD:
293       addat = numstaffs + 1;
294       break;
295     case BEFORE:
296       addat = movement->currentstaffnum;
297       break;
298     case AFTER:
299     case NEWVOICE:
300     case LYRICSTAFF:
301     case FIGURESTAFF:
302       addat = movement->currentstaffnum + 1;
303       break;
304     case CHORDSTAFF:
305       addat = movement->currentstaffnum + 1;
306       break;
307     case FIRST:
308       addat = 1;
309       break;
310     default:
311       break;
312     }
313 
314   if (action != INITIAL && action != ADDFROMLOAD)
315     {
316       if (action == NEWVOICE)
317         {
318           staff_copy_properties ((DenemoStaff *) movement->currentstaff->data, staff);
319           set_lily_name (staff->denemo_name, staff->lily_name);       //this should be re-done if the denemo_name is reset.
320           staff_insert (movement, staff, action, addat);
321         }
322       else
323         {
324           //      ret = staff_properties_change (&itp);
325           //      if (ret)
326           {
327             /*
328                If staff_properties_change returns false,
329                then the staff should probably be removed
330                Fixed 09042005 Adam Tee
331              */
332             staff_insert (movement, staff, action, addat);
333             movement->currentmeasurenum = 1;
334             /*
335                Reset leftmeasure num to 1 to be at the start of
336                the next staff.
337              */
338             movement->leftmeasurenum = 1;
339           }
340           //    else
341           //      {
342           /*
343            *  Free the staff struct as it has not been inserted
344            *  into the score
345            */
346           //     g_free (staff);
347           //  }
348         }
349     }
350   else                          // is INITIAL or ADDFROMLOAD
351     {
352       staff_insert (movement, staff, action, addat);
353       movement->leftmeasurenum = 1;
354     }
355   if ( addat==1)
356        staff->space_above = 20;
357   cache_staff (g_list_find (Denemo.project->movement->thescore, staff));
358   return staff;
359 }
freemeasure(DenemoMeasure * meas)360 static void freemeasure (DenemoMeasure *meas)
361 {
362   //g_list_foreach (meas->objects, freeobjlist, NULL);
363   freeobjlist (meas->objects, NULL);
364 }
365 /**
366  * Remove the project->movement->currentstaff from the piece project and reset movement->currentstaff
367  * if only one staff, inserts a new empty one
368  * if interactive checks for custom_scoreblock
369  * if a staff is deleted, updates the changecount
370  * @param project the DenemoProject structure
371  * @return nothing
372  */
373 void
staff_delete(DenemoProject * project,gboolean interactive)374 staff_delete (DenemoProject * project, gboolean interactive)
375 {
376   DenemoMovement *movement = project->movement;
377   DenemoStaff *curstaffstruct = movement->currentstaff->data;
378   gboolean has_next = (movement->currentstaff->next != NULL);
379   (void) signal_structural_change (project);
380   if (movement->currentstaff == NULL)
381     return;
382 
383   gboolean give_info = FALSE;   //give info about removing matching context
384   if (interactive && (curstaffstruct->context != DENEMO_NONE) && (!confirm (_("A context is set on this staff"), _("You will need to alter/delete the matching staff; Proceed?"))))
385     return;
386   if (interactive && (curstaffstruct->context != DENEMO_NONE))
387     give_info = TRUE;
388   take_snapshot ();
389   gboolean isprimary = (gboolean) (curstaffstruct->voicecontrol & DENEMO_PRIMARY);
390   //FIXME free_staff()
391 
392   free_directives (curstaffstruct->staff_directives);
393   free_directives (curstaffstruct->timesig.directives);
394   free_directives (curstaffstruct->keysig.directives);
395 
396   if(!Denemo.non_interactive){
397     gtk_widget_destroy ((GtkWidget *) (curstaffstruct->staffmenu));
398     gtk_widget_destroy ((GtkWidget *) (curstaffstruct->voicemenu));
399   }
400 //FIXME DANGER
401   g_list_foreach (curstaffstruct->themeasures, (GFunc)freemeasure, NULL);
402   g_list_free_full (curstaffstruct->themeasures, g_free);
403   g_string_free (curstaffstruct->denemo_name, FALSE);   //FIXME these should all be TRUE??
404   g_string_free (curstaffstruct->lily_name, FALSE);
405   g_string_free (curstaffstruct->midi_instrument, FALSE);
406   // g_list_foreach (curstaffstruct->verse_views, (GFunc)destroy_parent, NULL);//FIXME it is enough to destroy the notebook, here we are only destroying the GtkTextViews
407   if (curstaffstruct->verse_views)
408     gtk_widget_destroy (gtk_widget_get_parent (gtk_widget_get_parent (curstaffstruct->verse_views->data)));
409 
410   g_free (curstaffstruct);
411 
412 
413   if (movement->currentstaff == g_list_last (movement->thescore))
414     movement->currentstaffnum--;      //deleting the last, so the currentstaffnum must decrease
415   else
416     set_staff_transition (20);
417   movement->thescore = g_list_delete_link (movement->thescore, movement->currentstaff);
418   if (movement->thescore == NULL)
419     {
420       staff_new (project, INITIAL, DENEMO_NONE);
421     }
422   movement->currentstaff = g_list_nth (movement->thescore, movement->currentstaffnum - 1);
423 
424 
425   if (isprimary && has_next)                // we deleted the primary, so the next one (which is present) must become the primary
426     {
427       ((DenemoStaff *) movement->currentstaff->data)->voicecontrol = DENEMO_PRIMARY;
428       movement->currentprimarystaff = movement->currentstaff;
429     }
430   else
431     {
432       staff_set_current_primary (movement);
433     }
434   setcurrents (movement);
435   if (movement->markstaffnum)
436     calcmarkboundaries (movement);
437   if (project->movement->currentstaffnum < project->movement->top_staff)
438     project->movement->top_staff = project->movement->currentstaffnum;
439   show_lyrics ();
440 
441   if(!Denemo.non_interactive){
442     update_vscrollbar (project);
443     displayhelper (project);
444     score_status (project, TRUE);
445   }
446 
447   if (give_info)
448     infodialog (_("The staff deleted had a start/end context; if you still have the staff with the matching end/start context\n then you should remove it (or its context) now.\nSee Staff->properties->context\nYou will not be able to print with miss-matched contexts."));
449   return;
450 }
451 
452 /**
453  * Sets the beams and stem directions across the given staff
454  * @param thestaff a staff structure
455  * @return none
456  */
457 void
staff_beams_and_stems_dirs(DenemoStaff * thestaff)458 staff_beams_and_stems_dirs (DenemoStaff * thestaff)
459 {
460   measurenode *curmeasure;
461 
462   for (curmeasure = thestaff->themeasures; curmeasure; curmeasure = curmeasure->next)
463     {
464       calculatebeamsandstemdirs ((DenemoMeasure*)curmeasure->data);
465     }
466 }
467 
468 /**
469  * Sets which accidentals to show across a staff on a key sig change
470  * @param thestaff a staff stucture
471  * @return none
472  */
473 void
staff_show_which_accidentals(DenemoStaff * thestaff)474 staff_show_which_accidentals (DenemoStaff * thestaff)
475 {
476   measurenode *curmeasure;
477   for (curmeasure = thestaff->themeasures; curmeasure; curmeasure = curmeasure->next)
478     showwhichaccidentals ((objnode *) ((DenemoMeasure*)curmeasure->data)->objects);
479 }
480 
481 /**
482  * Function to set the note positions on the given staff when there is a clef change
483  * @param thestaff a staff structure
484  * @return none
485  */
486 void
staff_fix_note_heights(DenemoStaff * thestaff)487 staff_fix_note_heights (DenemoStaff * thestaff)
488 {
489   //gint nclef = thestaff->clef.type;
490   //gint time1 = thestaff->stime1;//USELESS
491   //gint time2 = thestaff->stime2;//USELESS
492   //gint initialclef;//USELESS
493   measurenode *curmeasure;
494   objnode *curobj;
495   DenemoObject *theobj;
496 g_warning ("Staff fix note heights called uselessly?");
497   for (curmeasure = thestaff->themeasures; curmeasure; curmeasure = curmeasure->next)
498     {
499       //initialclef = nclef;
500       for (curobj = (objnode *) ((DenemoMeasure *)curmeasure->data)->objects; curobj; curobj = curobj->next)
501         {
502           theobj = (DenemoObject *) curobj->data;
503           switch (theobj->type)
504             {
505             case CHORD:
506               newclefify (theobj);
507               break;
508             default:
509               break;
510             }
511         }                       /* End for */
512     }                           /* End for */
513   staff_beams_and_stems_dirs (thestaff);
514 }
515 
516 /**
517  * Callback function to insert a staff in the initial position
518  * @param action a Gtk Action
519  * @param project the DenemoProject structure
520  * @return none
521  */
522 void
staff_new_initial(GtkAction * action,DenemoScriptParam * param)523 staff_new_initial (GtkAction * action, DenemoScriptParam * param)
524 {
525   DenemoProject *project = Denemo.project;
526   while (project->movement->currentstaff && project->movement->currentstaff->prev)
527     movetostaffup (NULL, NULL);
528   staff_new_before (action, NULL);
529 }
530 
531 /**
532  Callback function to insert a staff before the current staff
533  * @param action a Gtk Action
534  * @param project the DenemoProject structure
535  * @return none
536  */
537 void
staff_new_before(GtkAction * action,DenemoScriptParam * param)538 staff_new_before (GtkAction * action, DenemoScriptParam * param)
539 {
540   DenemoProject *project = Denemo.project;
541   (void) signal_structural_change (project);
542 
543   movetostart (NULL, NULL);
544   staff_new (project, BEFORE, DENEMO_NONE);
545   if (project->movement->currentstaffnum >= project->movement->top_staff)
546     project->movement->top_staff++;
547   project->movement->currentstaffnum++;
548   project->movement->bottom_staff++;
549   set_bottom_staff (project);
550   move_viewport_down (project);
551 
552   movetostaffup (NULL, NULL);
553   displayhelper (project);
554 
555 }
556 
557 /**
558  * Callback function to insert a staff after the current staff
559  * @param action a Gtk Action
560  * @param project the DenemoProject structure
561  * @return none
562  */
563 void
staff_new_after(GtkAction * action,DenemoScriptParam * param)564 staff_new_after (GtkAction * action, DenemoScriptParam * param)
565 {
566   DenemoProject *project = Denemo.project;
567   if (!signal_structural_change (project))
568     return;
569   movetostart (NULL, NULL);
570   staff_new (project, AFTER, DENEMO_NONE);
571   if(!Denemo.non_interactive){
572     set_bottom_staff (project);
573     update_vscrollbar (project);
574   }
575   movetostaffdown (NULL, NULL);
576   displayhelper (project);
577 }
578 
579 /**
580  * Callback function to insert a staff at the bottom of the score
581  * @param action a Gtk Action
582  * @param project the DenemoProject structure
583  * @return none
584  */
585 void
staff_new_last(GtkAction * action,DenemoScriptParam * param)586 staff_new_last (GtkAction * action, DenemoScriptParam * param)
587 {
588   DenemoProject *project = Denemo.project;
589   while (project->movement->currentstaff && project->movement->currentstaff->next)
590     movetostaffdown (NULL, NULL);
591   staff_new_after (action, param);
592 }
593 
594 /**
595  * Callback function to add a new voice to the current staff
596  * @param action a Gtk Action
597  * @param project the DenemoProject structure
598  * @return none
599  */
600 void
staff_new_voice(GtkAction * action,DenemoScriptParam * param)601 staff_new_voice (GtkAction * action, DenemoScriptParam * param)
602 {
603   DenemoProject *project = Denemo.project;
604   staff_new (project, NEWVOICE, DENEMO_NONE);
605   set_bottom_staff (project);
606   update_vscrollbar (project);
607   setcurrents (project->movement);
608   if (project->movement->markstaffnum)
609     calcmarkboundaries (project->movement);
610   displayhelper (project);
611 }
612