1 /* commandfuncs.c
2  * functions invoked by user keypresses in score area
3  *
4  * for Denemo, a gtk+ frontend to GNU Lilypond
5  * (c) 1999-2005 Matthew Hiller, Adam Tee
6  */
7 
8 #include <string.h>
9 #include <stdint.h>
10 #include "command/commandfuncs.h"
11 #include "display/calculatepositions.h"
12 #include "command/chord.h"
13 #include "command/contexts.h"
14 #include "display/draw.h"
15 #include "command/measure.h"
16 #include "audio/midi.h"
17 #include "command/object.h"
18 #include "ui/moveviewport.h"
19 #include "command/select.h"
20 #include "command/staff.h"
21 #include "core/utils.h"
22 #include "command/tuplet.h"
23 #include "command/grace.h"
24 #include "audio/instrumentname.h"
25 #include "export/file.h"
26 #include "export/exportlilypond.h"
27 #include "core/exportxml.h"
28 #include "core/prefops.h"
29 #include "command/keyresponses.h"
30 #include "core/view.h"
31 #include "audio/pitchentry.h"
32 #include "audio/audiointerface.h"
33 #include "display/displayanimation.h"
34 #include "core/cache.h"
35 /**
36  * Macro to get the current DenemoObject
37  */
38 #define declarecurmudelaobj \
39 DenemoObject *curmudelaobj = \
40        (DenemoObject *) ( (si->currentobject && si->currentobject->data) ? \
41        ((((DenemoObject *)si->currentobject->data)->type == TUPCLOSE) ? \
42        (si->currentobject->prev?si->currentobject->prev->data:si->currentobject->data) : si->currentobject->data) : NULL)
43 
44 
45 /**
46  * Move the current rhythm on to the next one
47  * FIXME re-arrange the list of rhythms each time as well so that it
48  * is easy to alternate rhythms.
49  */
50 void
nextrhythm(GtkAction * action,DenemoScriptParam * param)51 nextrhythm (GtkAction* action, DenemoScriptParam* param)
52 {
53   if (!Denemo.project->rhythms)
54     return;
55   if (Denemo.project->currhythm == NULL)
56     Denemo.project->currhythm = g_list_last (Denemo.project->rhythms);
57   unhighlight_rhythm ((RhythmPattern *) Denemo.project->currhythm->data);
58   if (Denemo.project->currhythm->next)
59     Denemo.project->currhythm = Denemo.project->currhythm->next;
60   else
61     Denemo.project->currhythm = Denemo.project->rhythms;
62 #define g  (Denemo.project->rstep)
63 
64   g = ((RhythmPattern *) Denemo.project->currhythm->data)->rsteps;
65 
66 
67   RhythmPattern *cursnip = ((RhythmPattern *)Denemo.project->currhythm->data);
68   Denemo.project->cstep = (cursnip->clipboard)->data;
69   if (((RhythmElement *) g->data)->highlightlabel)
70     {
71         set_rhythm_label (cursnip, ((RhythmElement *) g->data)->highlightlabel);
72     }
73 
74   highlight_rhythm ((RhythmPattern *) Denemo.project->currhythm->data);
75 #undef g
76   displayhelper (Denemo.project);
77   score_status(Denemo.project, TRUE);
78 }
79 
80 
81 
82 
83 /**
84  * Helper function for calculating the
85  * beam and stem direction
86  * I think it calculates the beam and stem directions for the si->currentmeasure
87  * on the assumption that si->curmeasureclef has been set to the appropriate value
88  * before the function is called. Likewise for
89  * si->cursortime1/2 and si->curmeasure_stem_directive.
90  * the calculated values are stored in the objects in the measure - the fields set are:
91  * isstart/end_beamgroup, is_stemup, stemy, is_reversealigned. reversealign, minpixelsalloted, space_before
92  * si->curmeasureclef is also set: it is set to the value prevailing at the end of the measure.
93  */
94 
95 void
beamandstemdirhelper(DenemoMovement * si)96 beamandstemdirhelper (DenemoMovement * si)
97 {
98   calculatebeamsandstemdirs ((DenemoMeasure*)si->currentmeasure->data);
99 }
100 
101 
102 /**
103  * Set si->current* variables from currentmeasurenum
104  * the current object is set to the first in the measure
105  * the selection is not updated
106  *
107  */
108 void
setcurrents(DenemoMovement * si)109 setcurrents (DenemoMovement * si)
110 {
111   if (((DenemoStaff *) si->currentstaff->data)->nummeasures >= si->currentmeasurenum)
112     {
113       si->currentmeasure = g_list_nth (staff_first_measure_node (si->currentstaff), si->currentmeasurenum - 1);
114     }
115   else
116     {
117       g_debug ("Setting measure to %d which is last in Staff\n", ((DenemoStaff *) si->currentstaff->data)->nummeasures);
118       si->currentmeasure = g_list_nth (staff_first_measure_node (si->currentstaff), ((DenemoStaff *) si->currentstaff->data)->nummeasures - 1);
119       si->currentmeasurenum = ((DenemoStaff *) si->currentstaff->data)->nummeasures;
120 
121     }
122 
123   si->cursor_x = 0;
124   si->currentobject = (objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects;
125   if (si->currentobject)
126     si->cursor_appending = FALSE;
127   else
128     si->cursor_appending = TRUE;
129   si->cursoroffend = FALSE;
130 }
131 
132 /**
133  * Push the score to the left off the
134  * displayed portion
135  */
136 void
nudgerightward(DenemoProject * gui)137 nudgerightward (DenemoProject * gui)
138 {
139   if (set_rightmeasurenum (gui->movement) || (gui->movement->currentmeasurenum > gui->movement->rightmeasurenum))
140     {
141       if (gui->movement->currentmeasurenum > gui->movement->rightmeasurenum)
142         {
143           while (gui->movement->currentmeasurenum > gui->movement->rightmeasurenum)
144             {
145               gui->movement->leftmeasurenum++;
146               set_rightmeasurenum (gui->movement);
147             }
148 
149         }
150       find_leftmost_allcontexts (gui->movement);
151       update_hscrollbar (gui);
152     }
153 }
154 
155 /**
156  * Push the score upwards off the displayed
157  * portion
158  */
159 void
nudge_downward(DenemoProject * gui)160 nudge_downward (DenemoProject * gui)
161 {
162   set_bottom_staff (gui);
163 
164   while (gui->movement->currentstaffnum > gui->movement->bottom_staff)
165     {
166       gui->movement->top_staff++;
167       set_bottom_staff (gui);
168     }
169   update_vscrollbar (gui);
170 }
171 
172 /**
173  * Set the width available for drawing the measures of the movements from the width of the working scorearea widget
174  */
175 void
set_width_to_work_with(DenemoProject * gui)176 set_width_to_work_with (DenemoProject * gui)
177 {
178   if(Denemo.non_interactive)
179     return;
180   GList *g;
181 
182   for (g = gui->movements; g; g = g->next)
183     {
184       DenemoMovement *si = ((DenemoMovement *) g->data);
185 
186 #if 0
187       si->widthtoworkwith = (double) (Denemo.scorearea->allocation.width * ((int) (1 / si->system_height)) / si->zoom - (RIGHT_MARGIN + (gui->leftmargin+35) + si->maxkeywidth + SPACE_FOR_TIME));
188 #else
189       //the total line length for drawing DenemoObjects onto the screen
190       // this length will be divided amongst the systems (line).
191       // This length is in "pixels", the Denemo unit of display, which corresponds to a screen pixel when zoom ==1.0
192       si->widthtoworkwith = (gint) ((get_widget_width (Denemo.scorearea) / si->zoom - (RIGHT_MARGIN + (gui->leftmargin+35) + si->maxkeywidth + SPACE_FOR_TIME)) * ((int) (1 / si->system_height)));
193 #endif
194 
195 
196       //g_debug("Width %d from num systems%d\n", si->widthtoworkwith, ((int)(1/si->system_height )));
197     }
198 }
199 
200 /**
201  * Change the basic width of each measure
202  */
203 void
adjustmeasurewidth(DenemoMovement * si,gint amount)204 adjustmeasurewidth (DenemoMovement * si, gint amount)
205 {
206   si->measurewidth += amount;
207   if (si->measurewidth < 10)
208     si->measurewidth = 10;
209   if (si->widthtoworkwith < si->measurewidth + SPACE_FOR_BARLINE)
210     si->measurewidth = si->widthtoworkwith - SPACE_FOR_BARLINE;
211   find_xes_in_all_measures (si);
212   displayhelper (Denemo.project);
213   score_status(Denemo.project, TRUE);
214   /*nudgerightward (si); */
215 }
216 
217 /**
218  * Determines whether the closer jump will be up or down
219  */
220 gint
jumpcursor(gint cursor_y,gint fromnote,gint tonote)221 jumpcursor (gint cursor_y, gint fromnote, gint tonote)
222 {
223   int distance;
224 
225   distance = (tonote - fromnote + 7) % 7;
226   if (distance <= 3)            /* an upward jump is good */
227     return cursor_y + distance;
228   else                          /* jump down */
229     return cursor_y - 7 + distance;
230 }
231 
232 /**
233  * Reset the cursor stats: sets currentobject to the si->cursor_x'th object in currentmeasure, if no such object sets cursor_appending. NOTE does not set cursor appending false if there is an object...
234  * @param si pointer to the scoreinfo structure
235  * @return none
236  */
237 static void
reset_cursor_stats(DenemoMovement * si)238 reset_cursor_stats (DenemoMovement * si)
239 {
240   si->currentobject = g_list_nth ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, si->cursor_x);
241   if (!si->currentobject)
242     {
243       si->currentobject = g_list_last ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects);
244       si->cursor_appending = TRUE;
245     }
246 }
247 
248 /**
249  *  General function for inserting a DenemoObject
250  *  into the score
251  * the object is inserted at position gui->movement->cursor_x in the list of objects (counting from 0)
252  * gui->movement->cursor_x is incremented
253  * gui->movement->currentobject is set to the object at the new cursor_x position, unless this is too large, or we have cursor_appending in which case it is set to the last object.
254  */
255 void
object_insert(DenemoProject * gui,DenemoObject * mudela_obj_new)256 object_insert (DenemoProject * gui, DenemoObject * mudela_obj_new)
257 {
258   DenemoMovement *si = gui->movement;
259   measurenode *curmeasure = si->currentmeasure;
260   if (mudela_obj_new->type == TIMESIG)
261     {
262     ((DenemoMeasure*)curmeasure->data)->timesig = mudela_obj_new->object;
263     }
264   /* update undo information */
265   DenemoUndoData *undo;
266   if (!si->undo_guard)
267     {
268       undo = (DenemoUndoData *) g_malloc (sizeof (DenemoUndoData));
269       // should not be needed, we are inserting the object undo->object = dnm_clone_object (mudela_obj_new);
270       //do position after inserting, so we can go back to it to delete
271     }
272     objnode *obj = g_list_nth ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, si->cursor_x);
273     if (obj && obj->prev) {
274         mudela_obj_new->clef = (mudela_obj_new->type == CLEF)?mudela_obj_new->object:((DenemoObject*)obj->prev->data)->clef;
275         mudela_obj_new->keysig = (mudela_obj_new->type == KEYSIG)?mudela_obj_new->object:((DenemoObject*)obj->prev->data)->keysig;
276         mudela_obj_new->stemdir = (mudela_obj_new->type == STEMDIRECTIVE)?mudela_obj_new->object:((DenemoObject*)obj->prev->data)->stemdir;
277 
278     } else
279     {
280 
281          mudela_obj_new->clef = (mudela_obj_new->type == CLEF)?mudela_obj_new->object:((DenemoMeasure*)curmeasure->data)->clef;
282          mudela_obj_new->keysig = (mudela_obj_new->type == KEYSIG)?mudela_obj_new->object:((DenemoMeasure*)curmeasure->data)->keysig;
283          mudela_obj_new->stemdir = (mudela_obj_new->type == STEMDIRECTIVE)?mudela_obj_new->object:((DenemoMeasure*)curmeasure->data)->stemdir;
284     }
285   ((DenemoMeasure*)si->currentmeasure->data)->objects = g_list_insert ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, mudela_obj_new, si->cursor_x);
286 
287   obj = g_list_nth ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, si->cursor_x);//g_assert (obj->data == mudela_obj_new);
288 
289   if (mudela_obj_new->type == CLEF)
290     update_clef_cache (si->currentmeasure, obj);
291   else if (mudela_obj_new->type == KEYSIG)
292      update_keysig_cache (si->currentmeasure, obj);
293   else if (mudela_obj_new->type == TIMESIG)
294      update_timesig_cache (si->currentmeasure);
295   else if (mudela_obj_new->type == STEMDIRECTIVE)
296      update_stemdir_cache (si->currentmeasure, obj);
297   if (mudela_obj_new->type == CLEF)
298     {
299       reset_cursor_stats (si);
300       staff_fix_note_heights ((DenemoStaff *) si->currentstaff->data);
301       staff_beams_and_stems_dirs ((DenemoStaff *) si->currentstaff->data);
302       find_xes_in_all_measures (si);
303     }
304 
305 
306   if (!si->undo_guard)
307     {
308       get_position (si, &undo->position);
309       undo->position.appending = 0;
310       undo->action = ACTION_INSERT;
311       update_undo_info (si, undo);
312     }
313 
314   si->cursor_x++;
315   if (si->cursor_appending)
316     si->currentobject = g_list_last ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects);
317   else
318     si->currentobject = g_list_nth ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, si->cursor_x);
319 
320   if (si->currentobject == NULL)
321     {
322       g_warning ("problematic parameters on insert %d out of %d objects", si->cursor_x + 1, g_list_length ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects));
323       si->cursor_x--;
324       si->currentobject = g_list_nth ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, si->cursor_x);
325     }
326 
327   //g_debug("object insert appending %d cursor_x %d length %d\n", si->cursor_appending, si->cursor_x, g_list_length(((DenemoMeasure*)si->currentmeasure->data)->objects));
328 
329   score_status (gui, TRUE);
330   si->markstaffnum = 0;
331   displayhelper (Denemo.project);
332   score_status(Denemo.project, TRUE);
333 }
334 
335 /**
336  *  Change the y position of each staff
337  */
338 void
adjuststaffheight(DenemoMovement * si,gint amount)339 adjuststaffheight (DenemoMovement * si, gint amount)
340 {
341   si->staffspace += amount;
342   if (si->staffspace < 2 * STAFF_HEIGHT)
343     si->staffspace = 2 * STAFF_HEIGHT;
344   displayhelper (Denemo.project);
345   score_status(Denemo.project, TRUE);
346   /*nudge_downward (si); */
347 }
348 
349 /**
350  * Move an entire measure to the left
351  *
352  */
353 static void
gomeasureleft(DenemoScriptParam * param,gboolean extend_selection)354 gomeasureleft (DenemoScriptParam * param, gboolean extend_selection)
355 {
356   DenemoProject *gui = Denemo.project;
357   DenemoMovement *si = gui->movement;
358   DenemoScriptParam dummy;
359   if (param == NULL)
360     param = &dummy;
361   param->status = FALSE;
362   if (extend_selection && !si->markstaffnum)
363     set_mark (NULL, NULL);
364   if (gui->movement->currentmeasure->prev)
365     {
366       gui->movement->currentmeasurenum--;
367       if (!gui->movement->playingnow) //during playback cursor moves should not affect viewport
368         isoffleftside (gui);
369       param->status = TRUE;
370       write_status (gui);
371     }
372   setcurrents (gui->movement);
373   if (extend_selection)
374     calcmarkboundaries (gui->movement);
375   if(!Denemo.non_interactive)
376     gtk_widget_queue_draw(Denemo.scorearea);
377 }
378 
379 /**
380  * Move an entire measure to the left
381  *
382  */
383 void
measureleft(GtkAction * action,DenemoScriptParam * param)384 measureleft (GtkAction* action, DenemoScriptParam * param)
385 {
386   gomeasureleft (param, TRUE);
387 }
388 
389 /**
390  * Move an entire measure to the right
391  *
392  */
393 static void
gomeasureright(DenemoScriptParam * param,gboolean extend_selection)394 gomeasureright (DenemoScriptParam * param, gboolean extend_selection)
395 {
396   DenemoProject *gui = Denemo.project;
397   DenemoMovement *si = gui->movement;
398   DenemoScriptParam dummy;
399   if (param == NULL)
400     param = &dummy;
401   param->status = FALSE;
402   if (extend_selection && !si->markstaffnum)
403     set_mark (NULL, NULL);
404   if (gui->movement->currentmeasure->next)
405     {
406       gui->movement->currentmeasurenum++;
407       if (!gui->movement->playingnow) //during playback cursor moves should not affect viewport
408         isoffrightside (gui);
409       setcurrents (gui->movement);
410       param->status = TRUE;
411       if (extend_selection)
412         calcmarkboundaries (gui->movement);
413             write_status (gui);
414     }
415   if(!Denemo.non_interactive)
416     gtk_widget_queue_draw(Denemo.scorearea);
417 }
418 
419 void
measureright(GtkAction * action,DenemoScriptParam * param)420 measureright (GtkAction* action, DenemoScriptParam * param)
421 {
422   gomeasureright (param, TRUE);
423 }
424 
425 void
movetomeasureright(GtkAction * action,DenemoScriptParam * param)426 movetomeasureright (GtkAction* action, DenemoScriptParam * param)
427 {
428   gomeasureright (param, FALSE);
429 }
430 
431 void
movetomeasureleft(GtkAction * action,DenemoScriptParam * param)432 movetomeasureleft (GtkAction* action, DenemoScriptParam * param)
433 {
434   gomeasureleft (param, FALSE);
435 }
436 
437 
438 /**
439  * swap the current movement for the previous one in the list of movements
440  * return TRUE if movements are swapped
441  */
442 gboolean
swapmovements(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)443 swapmovements (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
444 {
445   DenemoProject *gui = Denemo.project;
446   (void) signal_structural_change (gui);
447   GList *this = g_list_find (gui->movements, gui->movement);
448   if (this->prev)
449     {
450       GList *prev = this->prev;
451       GList *prevv = prev->prev;
452       GList *next = this->next;
453       if (next)
454         next->prev = prev;
455       if (prevv)
456         prevv->next = this;
457       else
458         gui->movements = this;
459       this->next = prev;
460       this->prev = prevv;
461       prev->next = next;
462       prev->prev = this;
463       set_movement_selector (gui);
464       gchar *str = g_strdup_printf (_("This movement is now number %d in the score"), 1 + g_list_index (gui->movements, gui->movement));
465       infodialog (str);
466       g_free (str);
467 
468       return TRUE;
469     }
470   else
471     warningdialog (_("There is no previous movement to swap with"));
472   return FALSE;
473 }
474 
475 /**
476  *
477  *
478  */
479 gboolean
swapstaffs(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)480 swapstaffs (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
481 {
482   DenemoProject *gui = Denemo.project;
483   (void) signal_structural_change (gui);
484   if (gui->movement->currentstaff && gui->movement->currentstaff->prev)
485     {
486       DenemoStaff *temp;
487       //if this is a staff with no voices extra voices on it then swap
488       if (((DenemoStaff *) gui->movement->currentstaff->data)->voicecontrol == DENEMO_PRIMARY && ((gui->movement->currentstaff->next == NULL) || !(((DenemoStaff *) gui->movement->currentstaff->next->data)->voicecontrol & DENEMO_SECONDARY)))
489         {
490           temp = gui->movement->currentstaff->data;
491           if (temp->context == DENEMO_NONE || confirm (_("A context is set on this staff"), _("You will need to alter the staff → properties → context of this and the previous staff; Proceed?")))
492             {
493               take_snapshot ();
494               gui->movement->currentstaff->data = gui->movement->currentstaff->prev->data;
495               gui->movement->currentstaff->prev->data = temp;
496               gui->movement->currentstaffnum--;
497               gui->movement->currentstaff = gui->movement->currentstaff->prev;
498               staff_set_current_primary (gui->movement);
499               setcurrents (gui->movement);
500               move_viewport_up (gui);
501               score_status (gui, TRUE);
502               displayhelper (gui);
503               return TRUE;
504             }
505         }
506       else
507         warningdialog (_("Split off voices from this staff first"));
508     }
509   else
510     warningdialog (_("There is no previous staff to swap with"));
511   return FALSE;
512 }
513 
514 /**
515  *
516  *
517  */
518 gboolean
splitstaffs(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)519 splitstaffs (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
520 {
521   DenemoProject *gui = Denemo.project;
522 
523   if (gui->movement->currentstaff && gui->movement->currentstaff->next)
524     {
525       take_snapshot ();
526       DenemoStaff *thestaff = (DenemoStaff *) gui->movement->currentstaff->data;
527       DenemoStaff *nextstaff = (DenemoStaff *) gui->movement->currentstaff->next->data;
528       if ((thestaff->voicecontrol & DENEMO_PRIMARY) && (nextstaff->voicecontrol == DENEMO_SECONDARY))
529         nextstaff->voicecontrol = DENEMO_SECONDARY | DENEMO_PRIMARY;
530       else
531         warningdialog (_("There is no voice below this one on this staff"));
532       staff_set_current_primary (gui->movement);
533       setcurrents (gui->movement);
534       move_viewport_up (gui);
535       score_status (gui, TRUE);
536       displayhelper (gui);
537       return TRUE;
538     }
539   else
540     warningdialog (_("There is no voice below this one to split from"));
541   return FALSE;
542 }
543 
544 
545 /**
546  *
547  *
548  */
549 gboolean
joinstaffs(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)550 joinstaffs (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
551 {
552   DenemoProject *gui = Denemo.project;
553 
554   if (gui->movement->currentstaff && gui->movement->currentstaff->prev)
555     {
556       take_snapshot ();
557       DenemoStaff *thestaff = (DenemoStaff *) gui->movement->currentstaff->data;
558       thestaff->voicecontrol = DENEMO_SECONDARY;
559       staff_set_current_primary (gui->movement);
560       setcurrents (gui->movement);
561       move_viewport_up (gui);
562       score_status (gui, TRUE);
563       displayhelper (gui);
564       return TRUE;
565     }
566   else
567     warningdialog (_("There is no staff above to move this staff into"));
568   return FALSE;
569 }
570 
571 
572 /**
573  * Move si->currentstaff up an one voice, return TRUE if successful
574  * param is NULL for interactive calls, otherwise status is returned in param->status;
575  * alter selection if extend_selection
576  */
577 static gboolean
govoiceup(DenemoScriptParam * param,gboolean extend_selection)578 govoiceup (DenemoScriptParam * param, gboolean extend_selection)
579 {
580   DenemoProject *gui = Denemo.project;
581   DenemoMovement *si = gui->movement;
582   DenemoScriptParam dummy;
583   if (param == NULL)
584     param = &dummy;
585   param->status = FALSE;
586   if (!gui->movement->currentstaff)
587     return param->status = FALSE;//should never happen
588   if (extend_selection && !si->markstaffnum)
589     set_mark (NULL, NULL);
590   if (gui->movement->currentstaff && (((DenemoStaff *) (gui->movement->currentstaff->data))->voicecontrol & DENEMO_SECONDARY))
591     {
592       hide_lyrics ();
593       gui->movement->currentstaffnum--;
594       gui->movement->currentstaff = gui->movement->currentstaff->prev;
595       staff_set_current_primary (gui->movement);
596       setcurrents (gui->movement);
597       show_lyrics ();
598       move_viewport_down (gui);
599       set_cursor_transition ();
600       param->status = TRUE;
601     }
602   else if (param == &dummy)     //is interactive
603     warningmessage (_("This is the first voice"));
604   write_status(gui);
605   if(!Denemo.non_interactive)
606     gtk_widget_queue_draw(Denemo.scorearea);
607   return param->status;
608 }
609 
610 /**
611  * Move si->currentstaff up an one staff, not skipping voices unless displayed on same staff, return TRUE if successful
612  * alter selection if extend_selection
613  */
614 static gboolean
gostaffup(DenemoScriptParam * param,gboolean extend_selection)615 gostaffup (DenemoScriptParam * param, gboolean extend_selection)
616 {
617   DenemoProject *gui = Denemo.project;
618   DenemoMovement *si = gui->movement;
619   DenemoScriptParam dummy;
620   if (param == NULL)
621     param = &dummy;
622   param->status = FALSE;
623   if (!gui->movement->currentstaff)
624     return param->status = FALSE;//should never happen
625   if (extend_selection && !si->markstaffnum)
626     set_mark (NULL, NULL);
627   while ((((DenemoStaff *) (gui->movement->currentstaff->data))->voicecontrol == DENEMO_SECONDARY) &&
628     govoiceup (param, extend_selection))
629       ;/* do nothing */
630   if (gui->movement->currentstaff->prev)
631     {
632       hide_lyrics ();
633       gui->movement->currentstaffnum--;
634       gui->movement->currentstaff = gui->movement->currentstaff->prev;
635       staff_set_current_primary (gui->movement);
636       setcurrents (gui->movement);
637       if (extend_selection)
638         calcmarkboundaries (gui->movement);
639       show_lyrics ();
640       find_leftmost_allcontexts (si);
641       update_drawing_cache ();;
642       move_viewport_up (gui);
643       set_cursor_transition ();
644       param->status = TRUE;
645     }
646   else if (param == &dummy)     //is interactive
647     warningmessage (_("This is the first staff"));
648   write_status(gui);
649   if(!Denemo.non_interactive)
650     gtk_widget_queue_draw(Denemo.scorearea);
651   return param->status;
652 }
653 
654 
655 /**
656  * Move si->currentstaff down one voice, skipping voices, return TRUE if successful
657  * alter selection if extend_selection
658  */
659 static gboolean
govoicedown(DenemoScriptParam * param,gboolean extend_selection)660 govoicedown (DenemoScriptParam * param, gboolean extend_selection)
661 {
662   DenemoProject *gui = Denemo.project;
663   DenemoMovement *si = gui->movement;
664   DenemoScriptParam dummy;
665   if (param == NULL)
666     param = &dummy;
667   param->status = FALSE;
668   if (!gui->movement->currentstaff)
669     return param->status = FALSE;
670   if (extend_selection && !si->markstaffnum)
671     set_mark (NULL, NULL);
672   if (gui->movement->currentstaff->next && ((DenemoStaff *) (gui->movement->currentstaff->next->data))->voicecontrol & DENEMO_SECONDARY)
673     {
674       hide_lyrics ();
675       gui->movement->currentstaffnum++;
676       gui->movement->currentstaff = gui->movement->currentstaff->next;
677       staff_set_current_primary (gui->movement);
678       setcurrents (gui->movement);
679       if (extend_selection)
680         calcmarkboundaries (gui->movement);
681       show_lyrics ();
682       move_viewport_down (gui);
683       set_cursor_transition ();
684       param->status = TRUE;
685     }
686   else if (param == &dummy)     //is interactive
687     warningmessage (_("This is the last voice"));
688   write_status(gui);
689   if(!Denemo.non_interactive)
690     gtk_widget_queue_draw(Denemo.scorearea);
691   return param->status;
692 }
693 
694 gboolean
movetovoicedown(GtkAction * action,DenemoScriptParam * param)695 movetovoicedown (GtkAction* action, DenemoScriptParam * param)
696 {
697 
698   return govoicedown (param, FALSE);
699 }
700 
701 gboolean
voicedown(GtkAction * action,DenemoScriptParam * param)702 voicedown (GtkAction* action, DenemoScriptParam * param)
703 {
704 
705   return govoicedown (param, TRUE);
706 }
707 
708 gboolean
movetovoiceup(GtkAction * action,DenemoScriptParam * param)709 movetovoiceup (GtkAction* action, DenemoScriptParam * param)
710 {
711 
712   return govoiceup (param, FALSE);
713 }
714 
715 gboolean
voiceup(GtkAction * action,DenemoScriptParam * param)716 voiceup (GtkAction* action, DenemoScriptParam * param)
717 {
718 
719   return govoiceup (param, TRUE);
720 }
721 
722 
723 
724 /**
725  * Move si->currentstaff down one staff/voice,  not skipping voices unless displayed on same staff, return TRUE if successful
726  * alter selection if extend_selection
727  */
728 static gboolean
gostaffdown(DenemoScriptParam * param,gboolean extend_selection)729 gostaffdown (DenemoScriptParam * param, gboolean extend_selection)
730 {
731   DenemoProject *gui = Denemo.project;
732   DenemoMovement *si = gui->movement;
733   DenemoScriptParam dummy;
734   if (param == NULL)
735     param = &dummy;
736   param->status = FALSE;
737 
738   if (!gui->movement->currentstaff)
739     return param->status = FALSE;
740   if (extend_selection && !si->markstaffnum)
741     set_mark (NULL, NULL);
742   while (gui->movement->currentstaff->next && (((DenemoStaff *) (gui->movement->currentstaff->next->data))->voicecontrol == DENEMO_SECONDARY)
743     && govoicedown (param, extend_selection))
744       ;     /* do nothing */
745   if (gui->movement->currentstaff->next)
746     {
747       hide_lyrics ();
748       gui->movement->currentstaffnum++;
749       gui->movement->currentstaff = gui->movement->currentstaff->next;
750       staff_set_current_primary (gui->movement);
751       setcurrents (gui->movement);
752       if (extend_selection)
753         calcmarkboundaries (gui->movement);
754       show_lyrics ();
755       find_leftmost_allcontexts (si);
756 
757       update_drawing_cache ();
758       move_viewport_down (gui);
759       set_cursor_transition ();
760 
761       param->status = TRUE;
762     }
763   else if (param == &dummy)     //is interactive
764     warningmessage (_("This is the last staff"));
765   write_status(gui);
766   if(!Denemo.non_interactive)
767     gtk_widget_queue_draw(Denemo.scorearea);
768   return param->status;
769 }
770 
771 gboolean
movetostaffdown(GtkAction * action,DenemoScriptParam * param)772 movetostaffdown (GtkAction* action, DenemoScriptParam * param)
773 {
774 
775   return gostaffdown (param, FALSE);
776 }
777 
778 gboolean
staffdown(GtkAction * action,DenemoScriptParam * param)779 staffdown (GtkAction* action, DenemoScriptParam * param)
780 {
781 
782   return gostaffdown (param, TRUE);
783 }
784 
785 gboolean
movetostaffup(GtkAction * action,DenemoScriptParam * param)786 movetostaffup (GtkAction* action, DenemoScriptParam * param)
787 {
788 
789   return gostaffup (param, FALSE);
790 }
791 
792 gboolean
staffup(GtkAction * action,DenemoScriptParam * param)793 staffup (GtkAction* action, DenemoScriptParam * param)
794 {
795 
796   return gostaffup (param, TRUE);
797 }
798 
799 
800 
801 /**
802  * move the cursor one position to the left
803  *
804  */
805 gboolean
move_left(DenemoScriptParam * param,gboolean extend_selection)806 move_left (DenemoScriptParam * param, gboolean extend_selection)
807 {
808   DenemoProject *gui = Denemo.project;
809   DenemoMovement *si = gui->movement;
810   DenemoScriptParam dummy;
811   if (param == NULL)
812     param = &dummy;
813   param->status = FALSE;
814   if (extend_selection && !si->markstaffnum)
815     set_mark (NULL, NULL);
816   g_debug ("cursorleft: cursorpos %d\n", si->cursor_x);
817   if (!si->cursor_x)
818     {
819       /* also the only situation where si->currentobject == NULL */
820       if (si->currentmeasure->prev)
821         {
822           g_debug ("Currentmeasure prev == TRUE");
823           /* Go to end of preceding measure */
824           si->cursor_appending = TRUE;
825           si->currentmeasure = si->currentmeasure->prev;
826           si->currentmeasurenum--;
827           if (!si->playingnow)  //during playback cursor moves should not affect viewport
828             isoffleftside (gui);
829           si->currentobject = g_list_last ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects);
830           /* The preceding statement will set currentobject to
831            * NULL if appropriate */
832           si->cursor_x = g_list_length ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects);
833           /* Despite appearances, there is not an off-by-one error in the
834            * preceding command */
835           param->status = TRUE;
836         }
837     }
838   else if (si->cursor_appending)
839     {
840       /* Can back off from appending */
841       si->cursor_appending = FALSE;
842       si->cursor_x--;
843       param->status = TRUE;
844     }
845   else
846     {
847       /* Can go back in the measure */
848       if (si->currentobject && si->currentobject->prev)
849         {
850           si->currentobject = si->currentobject->prev;
851           si->cursor_x--;
852           param->status = TRUE;
853         }
854     }
855   if (extend_selection)
856     calcmarkboundaries (si);
857   write_status (gui);
858   if(!Denemo.non_interactive)
859     gtk_widget_queue_draw(Denemo.scorearea);
860   return param->status;
861 }
862 
863 /**
864  * move the cursor one position to the right
865  * selection (if any) is extended if extend_selection is TRUE
866  * sets param->status TRUE if cursor could still move right, and returns that value.
867  */
868 gboolean
move_right(DenemoScriptParam * param,gboolean extend_selection)869 move_right (DenemoScriptParam * param, gboolean extend_selection)
870 {
871   DenemoProject *gui = Denemo.project;
872   DenemoMovement *si = gui->movement;
873   DenemoScriptParam dummy;
874   if (param == NULL)
875     param = &dummy;
876   param->status = FALSE;
877   if (extend_selection && !si->markstaffnum)
878     set_mark (NULL, NULL);
879   if (si->cursor_appending && si->currentmeasure->next)
880     {
881       /* Go to the next measure */
882       si->currentmeasure = si->currentmeasure->next;
883       si->currentmeasurenum++;
884       if (!si->playingnow)      //during playback cursor moves should not affect viewport
885         isoffrightside (gui);
886       si->currentobject = (objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects;
887       si->cursor_x = 0;
888       if (si->currentobject)
889         si->cursor_appending = FALSE;
890     }
891   else if (si->currentobject)
892     {
893       /* See if we should go to appending position. If not, go to the
894        * next note (if possible) */
895       if (!si->cursor_appending && !si->currentobject->next)
896         {
897           /* Go to appending position */
898           si->cursor_appending = TRUE;
899           si->cursor_x++;
900         }
901       else if (si->currentobject->next)
902         {
903           si->currentobject = si->currentobject->next;
904           si->cursor_x++;
905         }
906       else
907         return param->status = FALSE;
908     }
909   if (extend_selection)
910     calcmarkboundaries (si);
911   write_status (gui);
912   if(!Denemo.non_interactive)
913     gtk_widget_queue_draw(Denemo.scorearea);
914   return (param->status = (si->currentobject || (!si->cursor_appending) || si->currentmeasure->next));
915 }
916 
917 /**
918  * move the cursor one position to the right extending the selection if any
919  * sets param->status TRUE if cursor could still move right, and returns that value.
920  */
921 gboolean
cursorright(GtkAction * action,DenemoScriptParam * param)922 cursorright (GtkAction* action, DenemoScriptParam * param)
923 {
924   return move_right (param, TRUE);
925 }
926 
927 /**
928  * move the cursor one position to the left extending the selection if any
929  * sets param->status TRUE if cursor could still move left, and returns that value.
930  */
931 gboolean
cursorleft(GtkAction * action,DenemoScriptParam * param)932 cursorleft (GtkAction* action, DenemoScriptParam * param)
933 {
934   return move_left (param, TRUE);
935 }
936 
937 // cursor move without altering selection
938 gboolean
movecursorright(GtkAction * action,DenemoScriptParam * param)939 movecursorright (GtkAction* action, DenemoScriptParam * param)
940 {
941   return move_right (param, FALSE);
942 }
943 
944 gboolean
movecursorleft(GtkAction * action,DenemoScriptParam * param)945 movecursorleft (GtkAction* action, DenemoScriptParam * param)
946 {
947   return move_left (param, FALSE);
948 }
949 
950 
951 
952 // moves the cursor in the direction indicated, observing within_measure and if stopping stopping at empty measures
953 
954 static gboolean
to_object_direction(gboolean within_measure,gboolean right,gboolean stopping)955 to_object_direction (gboolean within_measure, gboolean right, gboolean stopping)
956 {
957   if (!Denemo.project || !(Denemo.project->movement))
958     return FALSE;
959   GList *start_obj = Denemo.project->movement->currentobject;
960   GList *start_measure = Denemo.project->movement->currentmeasure;
961   gboolean was_appending = Denemo.project->movement->cursor_appending;
962   if (start_obj && Denemo.project->movement->cursor_appending)
963     movecursorleft (NULL, NULL);
964   if (start_obj == NULL)
965     {
966       if (within_measure)
967         return FALSE;
968       // start object is NULL, not restricted to current measure
969       if (right)
970         {
971           if (start_measure->next)
972             {
973               movetomeasureright (NULL, NULL);
974               if (Denemo.project->movement->currentobject)
975                 return TRUE;
976               else if (stopping)
977                 return FALSE;
978               else
979                 return to_object_direction (within_measure, right, stopping);
980             }
981           else
982             return FALSE;
983         }
984       // going left, start object is NULL, not restricted to current measure, going previous
985       if (start_measure->prev)
986         {
987           movecursorleft (NULL, NULL);
988           if (Denemo.project->movement->currentobject == NULL){
989             if (stopping)
990               return FALSE;
991             else
992               return to_object_direction (within_measure, right, stopping);
993           }
994           movecursorleft (NULL, NULL);
995           return TRUE;
996         }
997       return FALSE;
998     }
999   //start object is not NULL
1000   if (within_measure)
1001     {
1002       if (right)
1003         {
1004           if (start_obj->next)
1005             {
1006               movecursorright (NULL, NULL);
1007               return TRUE;
1008             }
1009           if (was_appending)
1010             movecursorright (NULL, NULL);
1011           return FALSE;
1012         }
1013       //left
1014       if (start_obj->prev == NULL)
1015         return FALSE;
1016     }
1017   //not restricted to this measure
1018   if (right)
1019     {
1020       if (start_obj->next)
1021         {
1022           movecursorright (NULL, NULL);
1023           return TRUE;
1024         }
1025       if (start_measure->next)
1026         {
1027           movetomeasureright (NULL, NULL);
1028           if (Denemo.project->movement->currentobject == NULL){
1029             if (stopping)
1030               return FALSE;
1031             else
1032               return to_object_direction (within_measure, right, stopping);
1033           }
1034           return TRUE;
1035         }
1036       if (was_appending)
1037         movecursorright (NULL, NULL);
1038       return FALSE;
1039     }
1040   //left
1041   if (start_obj->prev)
1042     {
1043       movecursorleft (NULL, NULL);
1044       return TRUE;
1045     }
1046   if (start_measure->prev)
1047     {
1048       movecursorleft (NULL, NULL);
1049       if (Denemo.project->movement->currentobject == NULL){
1050         if (stopping)
1051           return FALSE;
1052         else
1053           return to_object_direction (within_measure, right, stopping);
1054       }
1055       movecursorleft (NULL, NULL);
1056       return TRUE;
1057     }
1058   return FALSE;
1059 }
1060 
1061 static gboolean
to_standalone_directive_direction(gboolean right)1062 to_standalone_directive_direction (gboolean right)
1063 {
1064   gboolean ret = to_object_direction (FALSE, right, FALSE);
1065   if (!ret)
1066     return ret;
1067     write_status(Denemo.project);
1068   if (Denemo.project->movement->currentobject && Denemo.project->movement->currentobject->data && ((DenemoObject *) Denemo.project->movement->currentobject->data)->type == LILYDIRECTIVE)
1069     return TRUE;
1070   else
1071     return to_standalone_directive_direction (right);
1072 }
1073 
1074 /* moves currentobject to object in the selection in the direction indicated by right.
1075    Steps over barlines (i.e. cursor_appending).
1076  returns TRUE if currentobject is different after than before the call
1077 */
1078 static gboolean
to_selected_object_direction(gboolean right)1079 to_selected_object_direction (gboolean right)
1080 {
1081   if (!Denemo.project || !(Denemo.project->movement))
1082     return FALSE;
1083   gboolean success = to_object_direction (FALSE, right, FALSE);
1084   if (!success)
1085     success = to_object_direction (FALSE, right, FALSE);
1086   write_status(Denemo.project);
1087   if ((success) && in_selection (Denemo.project->movement))
1088     return TRUE;
1089   if (success)
1090     to_object_direction (FALSE, !right, FALSE);
1091   return FALSE;
1092 }
1093 
1094 static gboolean
to_chord_direction(gboolean right,gboolean stopping)1095 to_chord_direction (gboolean right, gboolean stopping)
1096 {
1097   gboolean ret = to_object_direction (FALSE, right, stopping);
1098   if (!ret)
1099     return ret;
1100   write_status(Denemo.project);
1101   if (Denemo.project->movement->currentobject && Denemo.project->movement->currentobject->data && ((DenemoObject *) Denemo.project->movement->currentobject->data)->type == CHORD)
1102     return TRUE;
1103   else
1104     return to_chord_direction (right, stopping);
1105 }
1106 
1107 static gboolean
to_chord_direction_in_measure(gboolean right)1108 to_chord_direction_in_measure (gboolean right)
1109 {
1110   gboolean ret = to_object_direction (TRUE, right, TRUE);
1111   if (!ret)
1112     return ret;
1113   write_status(Denemo.project);
1114   if (Denemo.project->movement->currentobject && Denemo.project->movement->currentobject->data && ((DenemoObject *) Denemo.project->movement->currentobject->data)->type == CHORD)
1115     return TRUE;
1116   else
1117     return to_chord_direction_in_measure (right);
1118 }
1119 
1120 static gboolean
to_standalone_direction_in_measure(gboolean right)1121 to_standalone_direction_in_measure (gboolean right)
1122 {
1123   gboolean ret = to_object_direction (TRUE, right, TRUE);
1124   if (!ret)
1125     return ret;
1126   write_status(Denemo.project);
1127   if (Denemo.project->movement->currentobject && Denemo.project->movement->currentobject->data && ((DenemoObject *) Denemo.project->movement->currentobject->data)->type == LILYDIRECTIVE)
1128     return TRUE;
1129   else
1130     return to_standalone_direction_in_measure (right);
1131 }
1132 
1133  // there is a significant problem with the concept of next note in a chord of several notes. We have no way of iterating over the notes of a chord
1134   // since the notes may be altered during the iteration and Denemo does not define a "currentnote"
1135   //but we can define cursor_to_top_note() and cursor_next_note_down() the latter moving to the next note below the cursor position (if any).
1136 
cursor_to_nth_note_height(gint n)1137 gboolean cursor_to_nth_note_height(gint n) {
1138     DenemoProject *gui = Denemo.project;
1139   DenemoObject *curObj;
1140   chord *thechord;
1141   note *thenote;
1142   if (!Denemo.project ||
1143    !(Denemo.project->movement) ||
1144     !(Denemo.project->movement->currentobject) ||
1145      !(curObj = Denemo.project->movement->currentobject->data) ||
1146       (curObj->type != CHORD) ||
1147        !(thechord = (chord *) curObj->object) ||
1148         !(thechord->notes) ||
1149          !(thenote = (note *) thechord->notes->data))
1150     return FALSE;
1151     if(n >= g_list_length (thechord->notes))
1152         return FALSE;
1153     thenote = g_list_nth (thechord->notes, n)->data;
1154     gint mid_c_offset = thenote->mid_c_offset;
1155     //g_print("Mid c offset required %d\n", mid_c_offset);
1156     //g_print ("Currently gui->movement->cursor_y = %d\n", gui->movement->cursor_y);
1157     if (gui->movement->cursor_y < mid_c_offset)
1158         while (gui->movement->cursor_y < mid_c_offset)
1159             cursorup (NULL, NULL);
1160     else
1161          while (gui->movement->cursor_y > mid_c_offset)
1162             cursordown (NULL, NULL);
1163     return (gui->movement->cursor_y == mid_c_offset);
1164 }
1165 
cursor_to_next_note_height(void)1166 gboolean cursor_to_next_note_height(void) {
1167   GList *thenotes;
1168   DenemoProject *gui = Denemo.project;
1169   DenemoObject *curObj;
1170   chord *thechord;
1171   note *thenote;
1172   gint mid_c_offset;
1173   if (!Denemo.project ||
1174    !(Denemo.project->movement) ||
1175     !(Denemo.project->movement->currentobject) ||
1176      !(curObj = Denemo.project->movement->currentobject->data) ||
1177       (curObj->type != CHORD) ||
1178        !(thechord = (chord *) curObj->object) ||
1179         !(thenotes = thechord->notes) ||
1180          !(thenote = (note *) thenotes->data))
1181     return FALSE;
1182 
1183     cursorup (NULL, NULL); //seek *above* the current cursor position only
1184     for(;thenotes;thenotes=thenotes->next)
1185         {
1186             thenote = (note *) thenotes->data;
1187             mid_c_offset = thenote->mid_c_offset;// g_print ("Currently gui->movement->cursor_y = %d considering %d\n", gui->movement->cursor_y, mid_c_offset);
1188             while (gui->movement->cursor_y < mid_c_offset)
1189                 cursorup (NULL, NULL);
1190             if (gui->movement->cursor_y == mid_c_offset)
1191                 break;
1192         }
1193     return (gui->movement->cursor_y == mid_c_offset);
1194 }
1195 
1196 
1197 //This next note is next chord that is not a rest in the given direction.
1198 static gboolean
to_note_direction(gboolean right,gboolean stopping)1199 to_note_direction (gboolean right, gboolean stopping)
1200 {
1201   gboolean ret = to_chord_direction (right, stopping);
1202   if (!ret)
1203     return ret;
1204   write_status(Denemo.project);
1205   if (Denemo.project->movement->currentobject && Denemo.project->movement->currentobject->data && ((DenemoObject *) Denemo.project->movement->currentobject->data)->type == CHORD && ((((chord *) (((DenemoObject *) Denemo.project->movement->currentobject->data)->object))->notes)) && (!Denemo.project->movement->cursor_appending))
1206     return TRUE;
1207   else
1208     return to_note_direction (right, stopping);
1209 }
1210 
1211 /******** advances the cursor to the next note,  stopping
1212  at empty measures. The cursor is left after last note if no more notes */
1213 gboolean
next_editable_note(void)1214 next_editable_note (void)
1215 {
1216   gboolean ret = to_note_direction (TRUE, TRUE);
1217   if ((!ret) && Denemo.project->movement->currentobject == NULL)
1218     {
1219       to_note_direction (FALSE, TRUE);
1220     }
1221   if (!ret)
1222     movecursorright (NULL, NULL);
1223   else
1224     write_status(Denemo.project);
1225   return ret;
1226 }
1227 
1228 /******** advances the cursor to the next note,  stopping
1229  at empty measures and at appending position if any. The cursor is left after last note if no more notes */
1230 gboolean
next_insert_or_editable_note(void)1231 next_insert_or_editable_note (void)
1232 {
1233  gboolean ret;
1234   if (Denemo.project->movement->currentobject && (Denemo.project->movement->currentobject->next == NULL))
1235     {
1236     ret = FALSE;
1237     }
1238   else
1239     {
1240 
1241     ret = to_note_direction (TRUE, TRUE);
1242     if ((!ret) && Denemo.project->movement->currentobject == NULL)
1243         {
1244           to_note_direction (FALSE, TRUE);
1245         }
1246     }
1247   if (!ret)
1248     {
1249         movecursorright (NULL, NULL);
1250         if (Denemo.project->movement->currentobject == NULL)
1251             movecursorleft (NULL, NULL);
1252     }
1253   else
1254     write_status(Denemo.project);
1255   return ret;
1256 }
1257 gboolean
cursor_to_next_object(gboolean within_measure,gboolean stopping)1258 cursor_to_next_object (gboolean within_measure, gboolean stopping)
1259 {
1260 
1261   return to_object_direction (within_measure, TRUE, stopping);
1262 }
1263 
1264 gboolean
cursor_to_prev_object(gboolean within_measure,gboolean stopping)1265 cursor_to_prev_object (gboolean within_measure, gboolean stopping)
1266 {
1267 
1268   return to_object_direction (within_measure, FALSE, stopping);
1269 }
1270 
1271 
1272 gboolean
cursor_to_next_selected_object(void)1273 cursor_to_next_selected_object (void)
1274 {
1275   return to_selected_object_direction (TRUE);
1276 }
1277 
1278 gboolean
cursor_to_prev_selected_object(void)1279 cursor_to_prev_selected_object (void)
1280 {
1281   return to_selected_object_direction (FALSE);
1282 }
1283 
1284 gboolean
cursor_to_next_standalone_directive(void)1285 cursor_to_next_standalone_directive (void)
1286 {
1287   return to_standalone_directive_direction (TRUE);
1288 }
1289 
1290 gboolean
cursor_to_prev_standalone_directive(void)1291 cursor_to_prev_standalone_directive (void)
1292 {
1293   return to_standalone_directive_direction (FALSE);
1294 }
1295 
1296 gboolean
cursor_to_next_standalone_in_measure(void)1297 cursor_to_next_standalone_in_measure (void)
1298 {
1299   return to_standalone_direction_in_measure (TRUE);
1300 }
1301 
1302 gboolean
cursor_to_prev_standalone_in_measure(void)1303 cursor_to_prev_standalone_in_measure (void)
1304 {
1305   return to_standalone_direction_in_measure (FALSE);
1306 }
1307 
1308 gboolean
cursor_to_next_chord(void)1309 cursor_to_next_chord (void)
1310 {
1311   return to_chord_direction (TRUE, FALSE);
1312 }
1313 
1314 gboolean
cursor_to_prev_chord(void)1315 cursor_to_prev_chord (void)
1316 {
1317   return to_chord_direction (FALSE, FALSE);
1318 }
1319 
1320 gboolean
cursor_to_next_chord_in_measure(void)1321 cursor_to_next_chord_in_measure (void)
1322 {
1323   return to_chord_direction_in_measure (TRUE);
1324 }
1325 
1326 gboolean
cursor_to_prev_chord_in_measure(void)1327 cursor_to_prev_chord_in_measure (void)
1328 {
1329   return to_chord_direction_in_measure (FALSE);
1330 }
1331 //Badly named - it is a next chord that is not a rest
1332 gboolean
cursor_to_next_note(void)1333 cursor_to_next_note (void)
1334 {
1335   return to_note_direction (TRUE, FALSE);
1336 }
1337 //Badly named - it is a prev chord that is not a rest
1338 gboolean
cursor_to_prev_note(void)1339 cursor_to_prev_note (void)
1340 {
1341   return to_note_direction (FALSE, FALSE);
1342 }
1343 
1344 /**
1345  * Move the cursor up one diatonic step
1346  */
1347 void
cursorup(GtkAction * action,DenemoScriptParam * param)1348 cursorup (GtkAction* action, DenemoScriptParam * param)
1349 {
1350   DenemoProject *gui = Denemo.project;
1351   DenemoScriptParam dummy;
1352   if (param == NULL)
1353     param = &dummy;
1354   param->status = FALSE;
1355   gui->movement->cursor_y++;
1356   gui->movement->staffletter_y = (gui->movement->staffletter_y + 1) % 7;
1357   param->status = TRUE;         //FIXME introduce some range boundaries, settable by user for instrument ranges.
1358   //g_debug ("Cursor Y Position %d\n", gui->movement->cursor_y);
1359   if(!Denemo.non_interactive)
1360     gtk_widget_queue_draw(Denemo.scorearea);
1361   update_object_info ();
1362 }
1363 
1364 /**
1365  * Move the cursor down one diatonic step
1366  */
1367 void
cursordown(GtkAction * action,DenemoScriptParam * param)1368 cursordown (GtkAction* action, DenemoScriptParam * param)
1369 {
1370   DenemoProject *gui = Denemo.project;
1371   DenemoScriptParam dummy;
1372   if (param == NULL)
1373     param = &dummy;
1374   param->status = FALSE;
1375 
1376   gui->movement->cursor_y--;
1377   gui->movement->staffletter_y = (gui->movement->staffletter_y + 6) % 7;
1378   param->status = TRUE;         //FIXME introduce some range boundaries, settable by user for instrument ranges.
1379   //g_debug ("Cursor Y Position %d\n", gui->movement->cursor_y);
1380   if(!Denemo.non_interactive)
1381     gtk_widget_queue_draw(Denemo.scorearea);
1382   update_object_info ();
1383 }
1384 
1385 static gboolean
prev_object_is_rhythm(DenemoProject * gui)1386 prev_object_is_rhythm (DenemoProject * gui)
1387 {
1388   if (gui->movement->currentobject == NULL)
1389     return FALSE;
1390   return ((DenemoObject *) (gui->movement->currentobject->data))->isinvisible;
1391 }
1392 
1393 
1394 /* insert a note into the score at the current cursor position following the current rhythm step */
1395 
1396 void
insert_note_following_pattern(DenemoProject * gui)1397 insert_note_following_pattern (DenemoProject * gui)
1398 {
1399 
1400   if ((gui->mode & (INPUTEDIT | INPUTINSERT)) && gui->rstep)
1401     {
1402       GList *h;
1403       gint mode = gui->mode;
1404       gui->mode = mode & ~INPUTRHYTHM;
1405 
1406       // if(gui->currhythm && gui->currhythm->data && ((RhythmPattern*)gui->currhythm->data)->clipboard)
1407       if (gui->currhythm && gui->cstep)
1408         {
1409           //      g_debug("Have a clip\n");
1410           GList *objs;
1411           gboolean note_inserted = FALSE;
1412           for (objs = gui->cstep; objs; objs = objs->next)
1413             {
1414               if (((DenemoObject *) objs->data)->type == CHORD && (note_inserted))
1415                 break;
1416               DenemoObject *clipobj = dnm_clone_object (objs->data);
1417               if ((((DenemoObject *) objs->data)->type == CHORD) && ((chord *) clipobj->object)->notes)
1418                 {
1419                   chord *thechord = (chord *) clipobj->object;
1420                   note *thenote = (note *) (thechord->notes->data);
1421                   thenote->mid_c_offset = gui->movement->cursor_y;
1422                   gint dclef = gui->movement->currentobject?((DenemoObject*)gui->movement->currentobject->data)->clef->type:((DenemoMeasure*)gui->movement->currentmeasure->data)->clef->type;
1423                   thechord->lowesty = thechord->highesty = thenote->y = calculateheight (thenote->mid_c_offset, dclef);
1424                   thechord->lowestpitch = thechord->highestpitch = thechord->sum_mid_c_offset = thenote->mid_c_offset;
1425                   clipobj->isinvisible = FALSE;
1426                   note_inserted = TRUE;
1427 
1428 
1429                   gui->rstep = gui->rstep->next;
1430                }
1431               insertion_point_for_type (gui->movement, ((DenemoObject *) objs->data)->type);
1432 
1433               insert_object (clipobj);
1434             }
1435             //g_assert (g_list_first(gui->cstep) == (((RhythmPattern *) gui->currhythm->data)->clipboard)->data);
1436           gui->cstep = (objs ? objs : (((RhythmPattern *) gui->currhythm->data)->clipboard)->data);
1437         }
1438       else
1439         {
1440           insertion_point (gui->movement);
1441           gui->movement->cursoroffend = FALSE;
1442           h = ((RhythmElement *) gui->rstep->data)->functions;
1443 #if GTK_MAJOR_VERSION==3
1444           ((GSourceFunc) h->data) (gui);
1445 #else
1446           ((GtkFunction) h->data) (gui);
1447 #endif
1448           displayhelper (gui);
1449         }
1450 
1451       if (((RhythmElement *) gui->rstep->data)->highlightlabel)
1452         {                       /* singletons do not have highlightlabel */
1453           RhythmPattern *cursnip = gui->currhythm->data;
1454           set_rhythm_label (cursnip, ((RhythmElement *) gui->rstep->data)->highlightlabel);
1455         }
1456 
1457       gui->mode = mode;
1458       score_status (gui, TRUE);
1459     }
1460 
1461 }
1462 
1463 /* get duration of next element in current rhythm pattern  */
1464 
1465 gint
get_prevailing_duration(void)1466 get_prevailing_duration (void)
1467 {
1468   DenemoProject *gui = Denemo.project;
1469   gint duration = 0;
1470   if ((gui->mode & (INPUTEDIT | INPUTINSERT)) && gui->rstep)
1471     {
1472       if (gui->currhythm && gui->cstep)
1473         {
1474           GList *objs;
1475           for (objs = gui->cstep; objs; objs = objs->next)
1476             {
1477               if ((((DenemoObject *) objs->data)->type == CHORD))
1478                 {
1479                   duration = ((chord *) ((DenemoObject *) objs->data)->object)->baseduration;
1480                   break;
1481                 }
1482             }
1483         }
1484       else
1485         {
1486           for (duration = 0; duration < 7; duration++)
1487             if ((Denemo.project->prevailing_rhythm == Denemo.singleton_rhythms['0' + duration]) || (Denemo.project->prevailing_rhythm == Denemo.singleton_rhythms['r' + duration]))
1488               break;
1489         }
1490     }
1491   return duration;
1492 }
1493 
1494 
1495 /**
1496  * shiftcursor: FIXME change the name of this function!
1497  * Mode sensitive note actions:
1498  * In Classic mode: Move the cursor to a given note value nearest the current cursor
1499  * In Edit mode: Change the current note (or insert if none), if INPUTRHYTHM as well, move cursor to next note.
1500  * In Insert mode: Insert a note at the cursor
1501  */
1502 void
shiftcursor(DenemoProject * gui,gint note_value)1503 shiftcursor (DenemoProject * gui, gint note_value)
1504 {
1505   gint oldstaffletter_y = gui->movement->staffletter_y;
1506   gint oldcursor_y = gui->movement->cursor_y;
1507   gui->movement->staffletter_y = note_value;
1508   gui->movement->cursor_y = jumpcursor (gui->movement->cursor_y, oldstaffletter_y, gui->movement->staffletter_y);
1509   int mid_c_offset = gui->movement->cursor_y;
1510 
1511   /* in edit mode edit the current note name */
1512   if ((gui->mode & INPUTEDIT) && ((!gui->movement->cursor_appending) || prev_object_is_rhythm (gui)))
1513     {
1514       DenemoObject *theobj = (DenemoObject *) (gui->movement->currentobject->data);
1515       chord *thechord;
1516       if (theobj->type == CHORD && (thechord = (chord *) theobj->object)->notes)
1517         {
1518           store_for_undo_change (gui->movement, theobj);
1519           //turn off further storage of UNDO info while this takes place
1520           gui->movement->undo_guard++;
1521           theobj->isinvisible = FALSE;
1522           if (g_list_length (thechord->notes) > 1)
1523             {                   /* multi-note chord - remove and add a note */
1524               gui->movement->cursor_y = oldcursor_y;
1525               delete_chordnote (gui);
1526               gui->movement->cursor_y = mid_c_offset;
1527               insert_chordnote (gui);
1528             }
1529           else
1530             {                   /* single-note chord - change the note */
1531               gint dclef = theobj->clef->type;
1532               keysig *key = theobj->keysig;
1533               if(!thechord->is_tied)
1534                 modify_note (thechord, mid_c_offset, key->accs[note_value], dclef);
1535               else //if tied modify the tied note(s) too, FIXME but this breaks the UNDO mechanism, see store_for_undo_change (gui->movement, theobj) above - now recursive - does that fix it?
1536                 {
1537                     modify_note (thechord, mid_c_offset, key->accs[note_value], dclef);
1538                 #if 1
1539                    // modify_note already does tied notes
1540                     DenemoPosition pos;
1541                     get_position (Denemo.project->movement, &pos);
1542                     gboolean ret = cursor_to_next_chord ();
1543                     if (ret)
1544                         shiftcursor (gui, note_value);
1545                     goto_movement_staff_obj (NULL, -1, -1, pos.measure, pos.object, pos.leftmeasurenum);
1546                 #endif
1547                 }
1548             }
1549           gui->movement->undo_guard--;
1550           score_status (gui, TRUE);
1551         }
1552     }
1553   else
1554     /* in INSERT (or EDIT and appending) we insert a note using the next step of the rhythm pattern */
1555     insert_note_following_pattern (gui);
1556   if(!Denemo.non_interactive)
1557     gtk_widget_queue_draw(Denemo.scorearea);
1558 }
1559 
1560 /**
1561  * edit_pitch
1562  * edits the note at the cursor height to have given mid_c_offset and enshift
1563  */
1564 void
edit_pitch(gint note_value,gint enshift)1565 edit_pitch (gint note_value, gint enshift)
1566 {
1567   DenemoProject *gui = Denemo.project;
1568   gint oldstaffletter_y = gui->movement->staffletter_y;
1569   gint oldcursor_y = gui->movement->cursor_y;
1570   gui->movement->staffletter_y = note_value;
1571   gui->movement->cursor_y = jumpcursor (gui->movement->cursor_y, oldstaffletter_y, gui->movement->staffletter_y);
1572   int mid_c_offset = gui->movement->cursor_y;
1573 
1574   if ((gui->mode & INPUTEDIT) && ((!gui->movement->cursor_appending) || prev_object_is_rhythm (gui)))
1575     {
1576       DenemoObject *theobj = (DenemoObject *) (gui->movement->currentobject->data);
1577       chord *thechord;
1578       if (theobj->type == CHORD && (thechord = (chord *) theobj->object)->notes)
1579         {
1580           store_for_undo_change (gui->movement, theobj);
1581           //turn off further storage of UNDO info while this takes place
1582           gui->movement->undo_guard++;
1583           theobj->isinvisible = FALSE;
1584           if (g_list_length (thechord->notes) > 1)
1585             {                   /* multi-note chord - remove and add a note */
1586               gui->movement->cursor_y = oldcursor_y;
1587               delete_chordnote (gui);
1588               gui->movement->cursor_y = mid_c_offset;
1589               insert_chordnote (gui);
1590             }
1591           else
1592             {                   /* single-note chord - change the note */
1593               gint dclef = theobj->clef->type;
1594               keysig *key = theobj->keysig;
1595               if(!thechord->is_tied)
1596                 {
1597                         modify_note (thechord, mid_c_offset, key->accs[note_value], dclef);
1598                         setenshift (gui->movement, enshift);
1599                     }
1600               else //if tied modify the tied note(s) too, FIXME but this breaks the UNDO mechanism, see store_for_undo_change (gui->movement, theobj) above - now recursive - does that fix it?
1601                 {
1602                     modify_note (thechord, mid_c_offset, key->accs[note_value], dclef);
1603                     setenshift (gui->movement, enshift);
1604                    // modify_note already does tied notes
1605                     DenemoPosition pos;
1606                     get_position (Denemo.project->movement, &pos);
1607                     gboolean ret = cursor_to_next_chord ();
1608                     if (ret)
1609                         edit_pitch (note_value, enshift);
1610                     goto_movement_staff_obj (NULL, -1, -1, pos.measure, pos.object, pos.leftmeasurenum);
1611                 }
1612             }
1613           gui->movement->undo_guard--;
1614           score_status (gui, TRUE);
1615         }
1616     }
1617   else
1618    {
1619        shiftcursor (gui, note_value);
1620        setenshift (gui->movement, enshift);
1621    }
1622   if(!Denemo.non_interactive)
1623     gtk_widget_queue_draw(Denemo.scorearea);
1624 }
1625 
1626 void
insert_rhythm_pattern(GtkAction * action,DenemoScriptParam * param)1627 insert_rhythm_pattern (GtkAction* action, DenemoScriptParam* param)
1628 {
1629 
1630   if (Denemo.project->rhythms==NULL)
1631     {
1632         g_warning("No snippets");
1633         return;
1634     }
1635   if (Denemo.project->currhythm == NULL)
1636     call_out_to_guile("(d-InsertNthSnippet)");
1637   else
1638     insert_clipboard (((RhythmPattern *) Denemo.project->currhythm->data)->clipboard);
1639 }
1640 
1641 void
insertion_point_for_type(DenemoMovement * si,DenemoObjType type)1642 insertion_point_for_type (DenemoMovement * si, DenemoObjType type)
1643 {
1644   switch (type)
1645     {
1646     case TUPCLOSE:
1647       return;
1648     default:
1649       break;
1650     }
1651   insertion_point (si);
1652 }
set_cursor_offend(void)1653 static void set_cursor_offend (void)
1654 {
1655   DenemoMovement * si = Denemo.project->movement;
1656   DenemoObject *curObj = si->currentobject?(DenemoObject*)si->currentobject->data:NULL;
1657   if (curObj)
1658     {
1659         gint tickspermeasure =  WHOLE_NUMTICKS * ((DenemoMeasure*)si->currentmeasure->data)->timesig->time1 / ((DenemoMeasure*)si->currentmeasure->data)->timesig->time2;
1660         si->cursoroffend = (curObj->starttickofnextnote >= tickspermeasure);
1661     }
1662     else si->cursoroffend = FALSE;
1663 }
1664 
1665 /**
1666  * insertion_point()
1667  * chooses/creates a good insertion point.
1668  * if the cursor is at the end of a full measure:
1669  *      creates a new measure and makes it the current one.
1670  * if the cursor is at the end of a full measure before an empty measure:
1671  *      it makes that empty measure current.
1672  *
1673  */
1674 void
insertion_point(DenemoMovement * si)1675 insertion_point (DenemoMovement * si)
1676 {
1677   gboolean next_measure;
1678   /* First, check to see if the insertion'll cause the cursor to
1679    * jump to the next measure. (Denemo will implicitly create it
1680    * if it doesn't exist already.) */
1681   set_cursor_offend ();
1682   next_measure = FALSE;
1683   if(si->cursoroffend && si->cursor_appending) {
1684      if ( (!si->currentmeasure->next) || (!((DenemoMeasure*)si->currentmeasure->next->data)->objects))
1685       next_measure = TRUE;
1686      else
1687         {
1688             objnode *objnode = ((DenemoMeasure*)si->currentmeasure->next->data)->objects;
1689             DenemoObject *obj = objnode?objnode->data:NULL;
1690             while (obj && (obj->type != CHORD))
1691                 {
1692                     objnode=objnode->next;obj = objnode?objnode->data:NULL;
1693                 }
1694 
1695             if ((obj==NULL) || (obj->type != CHORD))
1696                 next_measure = TRUE;
1697         }
1698   }
1699 
1700   if (next_measure)
1701     {
1702       if (!si->currentmeasure->next)
1703         {
1704           gboolean all = TRUE;  //add to all measures
1705           //g_debug ("Appending a new measure\n");
1706 
1707           /* Add a measure and make it currentmeasure */
1708           if (!(all && si->currentstaff && g_list_length (((DenemoStaff *) si->currentstaff->data)->themeasures) == g_list_length (si->measurewidths)))
1709             all = FALSE;        // add only to current staff if it is shorter than some other staff
1710           si->currentmeasure = dnm_addmeasures (si, si->currentmeasurenum, 1, all);
1711         }
1712       else
1713         si->currentmeasure = si->currentmeasure->next;
1714 
1715 
1716 
1717 
1718       if (Denemo.project->mode & (INPUTRHYTHM))
1719         signal_measure_end ();
1720       /* Now the stuff that needs to be done for each case */
1721       si->currentmeasurenum++;
1722       si->currentobject = (objnode *) (si->currentmeasure->data?((DenemoMeasure*)si->currentmeasure->data)->objects : NULL);
1723       si->cursor_x = 0;
1724       while(si->currentobject && (((DenemoObject *)si->currentobject->data)->type != CHORD))
1725         {
1726             si->currentobject = si->currentobject->next;
1727             si->cursor_x++;
1728         }
1729      // memcpy (si->cursoraccs, si->nextmeasureaccs, SEVENGINTS);
1730      // memcpy (si->curmeasureaccs, si->nextmeasureaccs, SEVENGINTS);
1731      // si->curmeasureclef = si->cursorclef;
1732     }
1733 }
1734 
1735 //get the prevailing accidental for the current cursor height, that is the last accidental before the cursor at this height (from a note or keysig change), or the cached keysig accidental
get_cursoracc(void)1736 static gint get_cursoracc (void)
1737     {
1738       DenemoMovement *si = Denemo.project->movement;
1739       gint noteheight = si->staffletter_y;
1740       measurenode *meas = si->currentmeasure;
1741       objnode *obj = si->currentobject;
1742       if ((!si->cursor_appending) && obj)
1743         obj = obj->prev;//want the object before the cursor unless appending
1744 
1745       for (;obj;obj = obj->prev)
1746         {
1747           DenemoObject *curobj = (DenemoObject*)obj->data;
1748           if (curobj->type == CHORD)
1749             {
1750                 chord *thechord = (chord*) curobj->object;
1751                 GList *g;
1752                 for (g = thechord->notes?thechord->notes:NULL;g;g=g->next)
1753                     {
1754                             note *thenote = (note*)g->data;
1755                             if (thenote->mid_c_offset == noteheight)
1756                                 return thenote->enshift;
1757                     }
1758             }
1759          else if (curobj->type == KEYSIG)
1760           return curobj->keysig->accs [noteheight];
1761         }
1762         return ((DenemoMeasure *)meas->data)->keysig->accs [noteheight];// p *((DenemoMeasure *)(Denemo.project->movement->currentmeasure->data))->keysig
1763     }
1764 /**
1765  * Insert a chord into the score
1766  * @param si pointer to the scoreinfo structure
1767  * @param duration the duration of the chord to insert
1768  * @param mode the current input mode
1769  * @param rest specifies a note is a rest
1770  */
1771 void
dnm_insertchord(DenemoProject * gui,gint duration,input_mode mode,gboolean rest)1772 dnm_insertchord (DenemoProject * gui, gint duration, input_mode mode, gboolean rest)
1773 {
1774   DenemoMovement *si = gui->movement;
1775   DenemoObject *mudela_obj_new;
1776   gboolean inserting_midi = si->recording && (si->recording->type==DENEMO_RECORDING_MIDI) && si->marked_onset;
1777   if ((mode & INPUTEDIT) && !si->cursor_appending && !(mode & INPUTRHYTHM))
1778     {
1779       highlight_duration (gui, duration);
1780       changeduration (si, duration);
1781       return;
1782     }
1783   insertion_point (si);
1784 
1785 //At this point, if it is the user's preference, check if there is room for this duration in the current measure.
1786 //if not put in a shorter note and tie it, then call recursively to put in the remaining duration.
1787 //only do this if we are in the appending position
1788 //The difficulty here is that we do not have the prevailing time signature cached (?), so we do not know when a measure is full. draw.c is used to compute this.
1789 //well, they are cached as cursortime1 and 2 in the DenemoMovement structure.
1790 // is the curObj->starttickofnextnote > tickspermeasure where  tickspermeasure = WHOLE_NUMTICKS * time1 / time2
1791 
1792   if(Denemo.prefs.spillover && si->cursor_appending)
1793     {
1794      DenemoObject *curObj;
1795 
1796         if(duration>= 0 && si->currentobject && (curObj=si->currentobject->data))
1797         {
1798         if(curObj->type==CHORD)
1799             {//g_print("dur %d base %d\n", curObj->durinticks, curObj->basic_durinticks);
1800                 gint ticks  = (curObj->durinticks/ curObj->basic_durinticks) * WHOLE_NUMTICKS / (1 << duration); /* takes into account prevailing tuple */
1801                 gint tickspermeasure =  WHOLE_NUMTICKS * ((DenemoMeasure*)si->currentmeasure->data)->timesig->time1 / ((DenemoMeasure*)si->currentmeasure->data)->timesig->time2;
1802                 if ((curObj->starttickofnextnote < tickspermeasure) && ((ticks + curObj->starttickofnextnote) > tickspermeasure))
1803                     {
1804                     dnm_insertchord (gui, duration+1, mode, rest);
1805                     //set si->cursoroffend if measure is full
1806                     curObj = si->currentobject->data;
1807                     si->cursoroffend = (curObj->starttickofnextnote >= tickspermeasure);
1808                     toggle_tie (NULL, NULL);
1809                     dnm_insertchord (gui, duration+1, mode, rest);
1810                     return;
1811                     }
1812             }
1813         }
1814     }
1815   /* Now actually create the chord as an object (before insertion) */
1816   mudela_obj_new = newchord (duration, 0, 0);
1817   { //we have to give the obj a clef to add the note to it
1818     objnode *obj = g_list_nth ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, si->cursor_x);
1819     if (obj && obj->prev) {
1820         mudela_obj_new->clef = ((DenemoObject*)obj->prev->data)->clef;
1821         mudela_obj_new->keysig = ((DenemoObject*)obj->prev->data)->keysig;
1822 
1823     } else
1824     {
1825 
1826          mudela_obj_new->clef = ((DenemoMeasure*)si->currentmeasure->data)->clef;
1827          mudela_obj_new->keysig = ((DenemoMeasure*)si->currentmeasure->data)->keysig;
1828     }
1829   }
1830 
1831   if ((mode & INPUTNORMAL) && (rest != TRUE))
1832     {
1833         if(inserting_midi && si->recording && si->marked_onset && si->marked_onset->data)
1834         {
1835             DenemoRecordedNote *midinote = (DenemoRecordedNote*)si->marked_onset->data;
1836             addtone (mudela_obj_new,  midinote->mid_c_offset + 7 * midinote->octave,  midinote->enshift);
1837             si->marked_onset = si->marked_onset->next;
1838         } else
1839         addtone (mudela_obj_new, si->cursor_y, get_cursoracc ()); //mudela_obj_new->keysig->accs[si->staffletter_y]);
1840 
1841     }
1842   if ((mode & INPUTBLANK) || (gui->mode & INPUTBLANK) || (!rest && (Denemo.project->input_source == INPUTMIDI) && (gui->mode & (INPUTRHYTHM))))
1843     mudela_obj_new->isinvisible = TRUE;
1844 
1845   /* Insert the new note into the score.  Note that while we may have
1846      added a measure above, object_insert will invoke nudgerightward,
1847      which will in turn invoke update_hscrollbar, so we
1848      don't need to invoke that here.  */
1849   gboolean was_appending = si->cursor_appending;
1850   object_insert (gui, mudela_obj_new);
1851   if(inserting_midi)
1852       mudela_obj_new->isinvisible = FALSE;
1853 
1854   if (Denemo.project->input_source == INPUTMIDI && (gui->mode & (INPUTRHYTHM)))
1855     {
1856       if (Denemo.prefs.immediateplayback)
1857         {
1858             if(inserting_midi && !rest)
1859                 {
1860                 DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data;
1861                 //play_notes (DEFAULT_BACKEND, curstaffstruct->midi_port, curstaffstruct->midi_channel, (chord *) mudela_obj_new->object);//compute duration and use that???
1862                  note *n = ((chord *) mudela_obj_new->object)->notes->data;
1863 
1864                     /* Because mid_c_offset is a measure of notes and we need a measure of
1865                     * half-steps, this array will help */
1866                   const gint key_offset[] = { -10, -8, -7, -5, -3, -1, 0, 2, 4, 5, 7, 9, 11 };
1867 
1868                   gint offset = n->mid_c_offset;
1869 
1870                   /* 60 is middle-C in MIDI keys */
1871                   gchar key = 60 + 12 * (offset / 7) + key_offset[offset % 7 + 6];
1872                   key += n->enshift;
1873 #define MIDI_RESOLUTION (384)
1874                  gint duration_ms = 1000 * mudela_obj_new->durinticks * 60.0 / (si->tempo * MIDI_RESOLUTION);
1875                  g_debug("duration %d duration ms = %d\n", duration, duration_ms);
1876                  play_note (DEFAULT_BACKEND , curstaffstruct->midi_port , curstaffstruct->midi_channel, key, duration_ms, 127);
1877                 }
1878             else
1879                 rhythm_feedback (DEFAULT_BACKEND, duration, rest, FALSE);
1880         }
1881     //if (( (!rest) || ((!(mode & INPUTBLANK)) && (!(gui->mode & INPUTBLANK)))) && !was_appending)
1882     // if (!was_appending)
1883      //   movecursorleft (NULL, NULL);
1884     }
1885   else
1886     {
1887       if (Denemo.project->last_source == INPUTKEYBOARD)
1888         {
1889           DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data;
1890           if (Denemo.prefs.immediateplayback)
1891             {
1892               play_notes (DEFAULT_BACKEND, curstaffstruct->midi_port, curstaffstruct->midi_channel, (chord *) mudela_obj_new->object);
1893               if(si->currentobject->prev == NULL)
1894                 signal_measure_end();
1895             }
1896         }
1897    // if (((mode & INPUTBLANK) || (gui->mode & INPUTBLANK)) && !was_appending)
1898     //    movecursorleft (NULL, NULL);
1899     }
1900 }
1901 
1902 gboolean
insert_marked_midi_note(void)1903 insert_marked_midi_note (void)
1904 {
1905     DenemoMovement *si = Denemo.project->movement;
1906     gboolean inserting_midi = si->recording && (si->recording->type==DENEMO_RECORDING_MIDI) && si->marked_onset;
1907 
1908     if(inserting_midi && si->marked_onset && si->marked_onset->data)
1909         {
1910             gint dots;
1911             DenemoRecordedNote *midinote = (DenemoRecordedNote*)si->marked_onset->data;//FIXME look at marked_onset->prev to see if we should add to a chord
1912             dnm_insertchord (Denemo.project, midinote->duration, INPUTNORMAL, FALSE);
1913             changenumdots (si->currentobject->data, midinote->dots);
1914             return TRUE;
1915         }
1916 return FALSE;
1917 }
1918 /**
1919  * Insert tuplet into the score
1920  * @param si pointer to the scoreinfo structure
1921  * @param type the type of tuplet to insert
1922  */
1923 void
dnm_inserttuplet(DenemoProject * gui,tuplet_type type)1924 dnm_inserttuplet (DenemoProject * gui, tuplet_type type)
1925 {
1926   DenemoMovement *si = gui->movement;
1927   DenemoObject *mudela_obj_new;
1928 
1929   insertion_point (si);
1930   switch (type)
1931     {
1932     case DUPLET:
1933       mudela_obj_new = tuplet_open_new (3, 2);
1934       break;
1935     case TRIPLET:
1936       mudela_obj_new = tuplet_open_new (2, 3);
1937       break;
1938     case QUADTUPLET:
1939       mudela_obj_new = tuplet_open_new (3, 4);
1940       break;
1941     case QUINTUPLET:
1942       mudela_obj_new = tuplet_open_new (4, 5);
1943       break;
1944     case SEXTUPLET:
1945       mudela_obj_new = tuplet_open_new (4, 6);
1946       break;
1947     case SEPTUPLET:
1948       mudela_obj_new = tuplet_open_new (4, 7);
1949       break;
1950     default:
1951       mudela_obj_new = tuplet_open_new (2, 3);
1952       break;
1953     }
1954   //g_debug ("Cursor pos %d (Before tup open)\n", si->cursor_x);
1955   object_insert (gui, mudela_obj_new);
1956   //g_debug ("Cursor pos %d (After tup open, before tup close)\n",         si->cursor_x);
1957   /* Add the closing bracket */
1958   object_insert (gui, tuplet_close_new ());
1959   //g_debug ("Cursor pos %d (After tup close)\n", si->cursor_x);
1960   si->cursor_x--;
1961   //g_debug ("Cursor pos %d( After move back)\n", si->cursor_x);
1962 
1963   si->currentobject = si->currentobject->prev;
1964   si->cursor_appending = FALSE;
1965   displayhelper (Denemo.project);
1966   score_status(Denemo.project, TRUE);
1967 }
1968 
1969 
1970 /**
1971  * Change the duration of the current note/rest
1972  * @param si pointer to the scoreinfo structure
1973  * @param duration the duration to change the current CHORD
1974  * object to
1975  */
1976 void
changeduration(DenemoMovement * si,gint duration)1977 changeduration (DenemoMovement * si, gint duration)
1978 {
1979   declarecurmudelaobj;
1980 
1981   if (curmudelaobj && curmudelaobj->type == CHORD)
1982     {
1983       store_for_undo_change (si, curmudelaobj);
1984       changedur (curmudelaobj, duration, 0);
1985     }
1986 }
1987 
1988 /**
1989  *  notechange
1990  * If REMOVE delete the note closest to si->cursor_y in a ~si->currentobject
1991  * else add a note at si->cursor_y to the ~si->currentobject
1992  * FIXME ~si->currentobject in this comment means the thing gotten by the macro declaremudelaobj. This macro is a horrible hack induced by trying to be clever with tuplets - enforcing pairing of begin/end. notechange
1993  * @param si pointer to the scoreinfo structure
1994  * @param remove whether to remove note or not
1995  */
1996 static gboolean
notechange(DenemoMovement * si,gboolean remove)1997 notechange (DenemoMovement * si, gboolean remove)
1998 {
1999   declarecurmudelaobj;
2000   gboolean ret = FALSE;
2001   gboolean inserting_midi = si->recording && (si->recording->type==DENEMO_RECORDING_MIDI) && si->marked_onset;
2002 
2003   if (curmudelaobj && curmudelaobj->type == CHORD)
2004     {
2005       store_for_undo_change (si, curmudelaobj);
2006       if (remove == TRUE)
2007         ret = removetone (curmudelaobj, si->cursor_y /*mid_c_offset */  );
2008       else {
2009 
2010         if(inserting_midi)
2011             {
2012             DenemoRecordedNote *midinote = (DenemoRecordedNote*)si->marked_onset->data;
2013             ret = (gboolean) (intptr_t) addtone (curmudelaobj,  midinote->mid_c_offset + 7 * midinote->octave,  midinote->enshift);
2014             si->marked_onset = si->marked_onset->next;
2015             }
2016         else
2017             ret = (gboolean) (intptr_t) addtone (curmudelaobj, si->cursor_y /* mid_c_offset */ ,
2018                                              curmudelaobj->keysig->accs[si->staffletter_y] /* enshift */  );
2019         }
2020 
2021       if (Denemo.project->last_source == INPUTKEYBOARD)
2022         {
2023           DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data;
2024 
2025           if (Denemo.prefs.immediateplayback)
2026             {
2027               play_notes (DEFAULT_BACKEND, curstaffstruct->midi_port, curstaffstruct->midi_channel, (chord *) curmudelaobj->object);
2028             }
2029         }
2030       else
2031         {
2032           // Denemo.project->last_source = INPUTKEYBOARD;
2033         }
2034       displayhelper (Denemo.project);
2035       score_status(Denemo.project, TRUE);
2036     }
2037   return ret;
2038 }
2039 
2040 /**
2041  * Delete chord note closest to y cursor
2042  */
2043 gboolean
delete_chordnote(DenemoProject * gui)2044 delete_chordnote (DenemoProject * gui)
2045 {
2046   notechange (gui->movement, TRUE);
2047   return TRUE;
2048 }
2049 
2050 /**
2051  * Insert chord note at y cursor position
2052  */
2053 gboolean
insert_chordnote(DenemoProject * gui)2054 insert_chordnote (DenemoProject * gui)
2055 {
2056   DenemoObject *curObj;
2057   if (gui->movement->currentobject && (curObj = Denemo.project->movement->currentobject->data) && (curObj->type == CHORD))
2058     notechange (gui->movement, FALSE);
2059   else
2060     insert_note_following_pattern (gui);
2061   return TRUE;
2062 }
2063 /**
2064  * Insert chord note at y cursor position or delete one if already present
2065  * return TRUE if inserted else FALSE
2066  */
2067 gboolean
insert_or_delete_chordnote(gint enshift)2068 insert_or_delete_chordnote (gint enshift)
2069 {
2070   DenemoProject *gui = Denemo.project;
2071   DenemoObject *curObj;
2072   gboolean remove = FALSE;
2073   if (gui->movement->currentobject && (curObj = Denemo.project->movement->currentobject->data) && (curObj->type == CHORD))
2074      {
2075       GList *node;
2076       for (node = ((chord *) curObj->object)->notes; node; node=node->next)
2077         {
2078             note *thenote = (note*)node->data;
2079             if((thenote->mid_c_offset == gui->movement->cursor_y) && (thenote->enshift == enshift))
2080                remove = TRUE;
2081         }
2082      notechange (gui->movement, remove);
2083     }
2084   else
2085     insert_note_following_pattern (gui);
2086 
2087   return !remove;
2088 }
2089 /**
2090  * Helper function that contains calls to all the display
2091  * update functions
2092  *
2093  * @param gui pointer to the DenemoProject structure
2094  */
2095 void
displayhelper(DenemoProject * gui)2096 displayhelper (DenemoProject * gui)
2097 {
2098   if(Denemo.non_interactive)
2099     return;
2100 
2101   DenemoMovement *si = gui->movement;
2102   beamandstemdirhelper (si);
2103   showwhichaccidentals ((objnode *)((DenemoMeasure*)si->currentmeasure->data)->objects);
2104   find_xes_in_measure (si, si->currentmeasurenum);
2105   nudgerightward (gui);
2106   set_bottom_staff (gui);
2107   write_status (gui);
2108   gtk_widget_queue_draw (Denemo.scorearea);
2109 }
2110 
2111 
2112 
2113 /**
2114  * Increment the enharmonic shift of the tone closest to the cursor.
2115  * @param si pointer to the DenemoMovement structure
2116  * @param direction, +ve for sharp -ve for flat
2117  */
2118 
2119 void
incrementenshift(DenemoProject * gui,gint direction)2120 incrementenshift (DenemoProject * gui, gint direction)
2121 {
2122   DenemoMovement *si = gui->movement;
2123   declarecurmudelaobj;
2124 
2125   if (curmudelaobj && curmudelaobj->type == CHORD)
2126     {
2127       store_for_undo_change (si, curmudelaobj);
2128 
2129       shiftpitch (curmudelaobj, si->cursor_y, direction > 0);
2130       showwhichaccidentals ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects);
2131       find_xes_in_measure (si, si->currentmeasurenum);
2132 
2133 
2134       //if tied ...
2135             chord *next = curmudelaobj->object;
2136             if (next->is_tied)
2137                 {
2138                  objnode *nextobj = gui->movement->currentobject->next;
2139                   measurenode *current = gui->movement->currentmeasure;
2140                   if(nextobj==NULL)
2141                         {
2142                           current = current->next;
2143                           if(current && current->data)
2144                             {
2145                                nextobj = current->data;
2146                            }
2147                        }
2148                   while (nextobj)
2149                     {
2150                       DenemoObject *thenextobj= (DenemoObject *) nextobj->data;
2151 
2152                       if (thenextobj->type == CHORD)
2153                         {
2154                             chord *next = thenextobj->object;
2155                             shiftpitch (thenextobj, si->cursor_y, direction > 0);
2156                             showwhichaccidentals ((objnode *)((DenemoMeasure*) current->data)->objects);
2157                             if(next->is_tied)
2158                              {
2159                                 if(nextobj->next==NULL)
2160                                 {
2161                                   current = current->next;
2162                                   if(current && current->data)
2163                                     {
2164                                        nextobj = current->data;
2165                                        continue;
2166                                     }
2167                                 }
2168                                 nextobj = nextobj->next;
2169                                 continue;
2170                             }
2171                             else
2172                             break;
2173                         }
2174 
2175                         break;
2176                     }
2177 
2178         }
2179       if (Denemo.project->last_source == INPUTKEYBOARD)
2180         {
2181           DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data;
2182 
2183           if (Denemo.prefs.immediateplayback)
2184             {
2185               play_notes (DEFAULT_BACKEND, curstaffstruct->midi_port, curstaffstruct->midi_channel, (chord *) curmudelaobj->object);
2186             }
2187         }
2188       else
2189         {
2190           //      Denemo.project->last_source = INPUTKEYBOARD;
2191         }
2192 
2193       displayhelper (Denemo.project);
2194       score_status(Denemo.project, TRUE);
2195     }
2196 }
2197 
2198 /**
2199  * Set the enharmonic shift of the tone closest to the cursor.
2200  * @param si pointer to the DenemoMovement structure
2201  * @param enshift -2 .. +2 for double flat to double sharp FIXME make this a system wide enum
2202  */
2203 
2204 void
setenshift(DenemoMovement * si,gint enshift)2205 setenshift (DenemoMovement * si, gint enshift)
2206 {
2207   declarecurmudelaobj;
2208   if (curmudelaobj && curmudelaobj->type == CHORD)
2209     {
2210       store_for_undo_change (si, curmudelaobj);
2211 
2212       changeenshift (curmudelaobj, si->cursor_y, enshift);
2213 
2214 
2215       if (Denemo.project->input_source == INPUTKEYBOARD)
2216         {
2217           DenemoStaff *curstaffstruct = (DenemoStaff *) si->currentstaff->data;
2218           if (Denemo.prefs.immediateplayback)
2219             {
2220               play_notes (DEFAULT_BACKEND, curstaffstruct->midi_port, curstaffstruct->midi_channel, (chord *) curmudelaobj->object);
2221             }
2222         }
2223 
2224     }
2225 }
2226 
2227 /**
2228  * Change the stemdirection of the current chord object
2229  * by a given amount
2230  * @param si  pointer to the scoreinfo structure
2231  * @param amount the stem direction change to make
2232  */
2233 void
change_stem_directive(DenemoMovement * si,enum stemdirections amount)2234 change_stem_directive (DenemoMovement * si, enum stemdirections amount)
2235 {
2236   declarecurmudelaobj;
2237 
2238   if (curmudelaobj && curmudelaobj->type == STEMDIRECTIVE)
2239     {
2240       store_for_undo_change (si, curmudelaobj);
2241 
2242       switch (amount)
2243         {
2244         case DENEMO_STEMDOWN:
2245           ((stemdirective *) curmudelaobj->object)->type = DENEMO_STEMDOWN;
2246           break;
2247         case DENEMO_STEMUP:
2248           ((stemdirective *) curmudelaobj->object)->type = DENEMO_STEMUP;
2249           break;
2250         default:
2251           ((stemdirective *) curmudelaobj->object)->type = DENEMO_STEMBOTH;
2252           break;
2253         }
2254     displayhelper (Denemo.project);
2255     score_status(Denemo.project, TRUE);
2256     }
2257 }
2258 
2259 
2260 /**
2261  * Change the number of dots on the current chord
2262  *
2263  * @param si pointer to the scoreinfo structure
2264  * @param amount the number of dots to add/remove
2265  */
2266 void
changedots(DenemoMovement * si,gint amount)2267 changedots (DenemoMovement * si, gint amount)
2268 {
2269   declarecurmudelaobj;
2270 
2271   if (curmudelaobj && curmudelaobj->type == CHORD)
2272     {
2273       store_for_undo_change (si, curmudelaobj);
2274 
2275       if (Denemo.project->mode & (INPUTRHYTHM))
2276         {
2277           if (Denemo.prefs.immediateplayback)
2278             {
2279               chord *thechord = (chord *) curmudelaobj->object;
2280               gboolean rest = (thechord->notes == NULL);
2281               rhythm_feedback (DEFAULT_BACKEND, thechord->baseduration, rest, TRUE);
2282             }
2283         }
2284       changenumdots (curmudelaobj, amount);
2285     }
2286 }
2287 
2288 /**
2289  *  Insert measure into the score at the current position
2290  *
2291  * @param si pointer to the scoreinfo structure
2292  * @param number of measures to insert
2293  */
2294 void
dnm_insertmeasures(DenemoMovement * si,gint number)2295 dnm_insertmeasures (DenemoMovement * si, gint number)
2296 {
2297   si->currentmeasure = dnm_addmeasures (si, si->currentmeasurenum - 1, number, 1);
2298   si->cursor_x = 0;
2299   si->cursor_appending = TRUE;
2300   si->currentobject = NULL;
2301   set_rightmeasurenum (si);
2302   displayhelper (Denemo.project);
2303   score_status(Denemo.project, TRUE);
2304   //si->markstaffnum = 0;
2305   //calcmarkboundaries (si);
2306   /* update_hscrollbar (si); */
2307 }
2308 
2309 /**
2310  *  Insert measure into the staff after the current position
2311 
2312  */
2313 void
insertmeasureafter(GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)2314 insertmeasureafter (GtkAction* action, G_GNUC_UNUSED DenemoScriptParam* param)
2315 {
2316   DenemoMovement *si = Denemo.project->movement;
2317   take_snapshot ();
2318   si->currentmeasure = addmeasures (si, si->currentmeasurenum++, 1, 0);
2319   si->cursor_x = 0;
2320   si->cursor_appending = TRUE;
2321   si->currentobject = NULL;
2322   set_rightmeasurenum (si);
2323   //si->markstaffnum = 0;
2324   //calcmarkboundaries (si);
2325   /* update_hscrollbar (si); */
2326 }
2327 
2328 /**
2329  *  Insert measure into the staffs after the current position
2330 
2331  */
2332 void
addmeasureafter(GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)2333 addmeasureafter (GtkAction* action, G_GNUC_UNUSED DenemoScriptParam* param)
2334 {
2335   DenemoMovement *si = Denemo.project->movement;
2336   take_snapshot ();
2337   si->currentmeasure = addmeasures (si, si->currentmeasurenum++, 1, 1);
2338   si->cursor_x = 0;
2339   si->cursor_appending = TRUE;
2340   si->currentobject = NULL;
2341   set_rightmeasurenum (si);
2342   //si->markstaffnum = 0;
2343   draw_score_area();
2344   score_status(Denemo.project, TRUE);
2345   // calcmarkboundaries (si);
2346   /* update_hscrollbar (si); */
2347   displayhelper (Denemo.project);
2348 }
2349 
2350 /**
2351  *  Insert measure into the staff before the current position
2352 
2353  */
2354 void
insertmeasurebefore(GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)2355 insertmeasurebefore (GtkAction* action, G_GNUC_UNUSED DenemoScriptParam* param)
2356 {
2357   DenemoMovement *si = Denemo.project->movement;
2358   si->currentmeasure = addmeasures (si, si->currentmeasurenum - 1, 1, 0);
2359   si->cursor_x = 0;
2360   si->cursor_appending = TRUE;
2361   si->currentobject = NULL;
2362   set_rightmeasurenum (si);
2363   si->markstaffnum = 0;
2364 
2365   draw_score_area();// displayhelper (Denemo.project);
2366   score_status(Denemo.project, TRUE);
2367   /* update_hscrollbar (si); */
2368 }
2369 
2370 /**
2371  * Add measure to the end of the score
2372  *
2373  * @param si pointer to the scoreinfo structure
2374  * @param number the number of measures to append
2375  */
2376 void
appendmeasures(DenemoMovement * si,gint number)2377 appendmeasures (DenemoMovement * si, gint number)
2378 {
2379   dnm_addmeasures (si, g_list_length (staff_first_measure_node (si->currentstaff)), number, FALSE);
2380   /* Reset these two variables because si->currentmeasure and
2381    * si->currentobject may now be pointing to dead data */
2382   si->currentmeasure = g_list_nth (staff_first_measure_node (si->currentstaff), si->currentmeasurenum - 1);
2383   si->currentobject = g_list_nth ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, si->cursor_x - (si->cursor_appending == TRUE));
2384   set_rightmeasurenum (si);
2385   displayhelper (Denemo.project);
2386   score_status(Denemo.project, TRUE);
2387   /*update_hscrollbar (si); */
2388 }
2389 
2390 void
appendmeasurestoentirescore(DenemoMovement * si,gint number)2391 appendmeasurestoentirescore (DenemoMovement * si, gint number)
2392 {
2393   dnm_addmeasures (si, g_list_length (staff_first_measure_node (si->currentstaff)), number, TRUE);
2394   /* Reset these two variables because si->currentmeasure and
2395    * si->currentobject may now be pointing to dead data */
2396   si->currentmeasure = g_list_nth (staff_first_measure_node (si->currentstaff), si->currentmeasurenum - 1);
2397   si->currentobject = g_list_nth ((objnode *) ((DenemoMeasure*)si->currentmeasure->data)->objects, si->cursor_x - (si->cursor_appending == TRUE));
2398   set_rightmeasurenum (si);
2399   /* update_hscrollbar (si); */
2400 
2401 }
2402 
2403 
2404 
2405 
2406 
2407 /**
2408  * Delete staff wrapper to delete the preceding staff
2409  *
2410  * @param action pointer to the GtkAction event
2411  * @param gui pointer to the DenemoProject structure
2412  */
2413 void
delete_staff_before(GtkAction * action,DenemoScriptParam * param)2414 delete_staff_before (GtkAction * action, DenemoScriptParam * param)
2415 {
2416   DenemoProject *gui = Denemo.project;
2417   if (staffup (action, param))
2418     {
2419       staff_delete (gui, TRUE);
2420     }
2421 }
2422 
2423 /**
2424  * Delete staff wrapper to delete the next staff
2425  *
2426  * @param action pointer to the GtkAction event
2427  * @param gui pointer to the DenemoProject structure
2428  */
2429 void
delete_staff_after(GtkAction * action,DenemoScriptParam * param)2430 delete_staff_after (GtkAction * action, DenemoScriptParam * param)
2431 {
2432   DenemoProject *gui = Denemo.project;
2433   if (staffdown (action, param))
2434     {
2435       staff_delete (gui, TRUE);
2436     }
2437 }
2438 
2439 /**
2440  * Delete staff wrapper to delete current staff
2441  *
2442  * @param action pointer to the GtkAction event
2443  * @param gui pointer to the DenemoProject structure
2444  */
2445 void
delete_staff_current(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)2446 delete_staff_current (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
2447 {
2448   DenemoProject *gui = Denemo.project;
2449   staff_delete (gui, TRUE);
2450 }
2451 
2452 
2453 
2454 /**
2455  * Delete mesasure from score
2456  * @param gui pointer to the DenemoProject structure
2457  * @return none
2458  *
2459  * This is only a wrapper for the real dnm_deletemeasure
2460  * function.
2461  */
2462 void
deletemeasure(GtkAction * action,DenemoScriptParam * param)2463 deletemeasure (GtkAction* action, DenemoScriptParam* param)
2464 {
2465   dnm_deletemeasure (Denemo.project->movement);
2466   isoffleftside (Denemo.project);
2467   displayhelper (Denemo.project);
2468   score_status(Denemo.project, TRUE);
2469 }
2470 
2471 /**
2472  * Delete mesasure from all staffs of score
2473  * @param gui pointer to the DenemoProject structure
2474  * @return none
2475  *
2476 
2477  */
2478 void
deletemeasureallstaffs(GtkAction * action,DenemoScriptParam * param)2479 deletemeasureallstaffs (GtkAction* action, DenemoScriptParam* param)
2480 {
2481   DenemoMovement *si = Denemo.project->movement;
2482   //take_snapshot(); this does not prevent the multiple undo steps needed
2483   si->currentmeasure = removemeasures (si, si->currentmeasurenum - 1, 1, TRUE);
2484   setcurrents (si);
2485   if (si->markstaffnum)
2486     calcmarkboundaries (si);
2487   score_status (Denemo.project, TRUE);
2488   si->markstaffnum = 0;
2489   isoffleftside (Denemo.project);
2490   displayhelper (Denemo.project);
2491   score_status(Denemo.project, TRUE);
2492 }
2493 
2494 
2495 
2496 /**
2497  * Delete measure from the score
2498  *
2499  * TODO remove measure from current staff
2500  * rather than the entire score
2501  * @param gui pointer to the DenemoProject structure
2502  * @return none
2503  */
2504 void
dnm_deletemeasure(DenemoMovement * si)2505 dnm_deletemeasure (DenemoMovement * si)
2506 {
2507   si->currentmeasure = removemeasures (si, si->currentmeasurenum - 1, 1, FALSE);
2508 
2509   /* In case that was the last measure we just deleted, which'd cause
2510    * the current measure to be the left of what's displayed */
2511 
2512   setcurrents (si);
2513   if (si->markstaffnum)
2514     calcmarkboundaries (si);
2515   si->markstaffnum = 0;
2516   //g_debug("Removed current measure now %p number %d\n", si->currentmeasure, si->currentmeasurenum);
2517 
2518 }
2519 
2520 /**
2521  * Remove current object from the score
2522  *@param cur_measure pointer to the current measure
2523  * @param cur_objnode pointer to the current object
2524  * @return none
2525  */
2526 static void
remove_object(DenemoMeasure * cur_measure,objnode * cur_objnode)2527 remove_object (DenemoMeasure * cur_measure, objnode * cur_objnode)
2528 {
2529   if (cur_measure->objects)
2530     {
2531       cur_measure->objects = g_list_remove_link ((objnode *) cur_measure->objects, cur_objnode);
2532       freeobject ((DenemoObject *) cur_objnode->data);
2533       g_list_free_1 (cur_objnode);
2534     }
2535 }
2536 
2537 
2538 /**
2539  * Helper to remove the current object an reset cursor stats
2540  * @param si pointer to the scoreinfo structure
2541  * @return none
2542  */
2543 static void
delete_object_helper(DenemoMovement * si)2544 delete_object_helper (DenemoMovement * si)
2545 {
2546   remove_object ((DenemoMeasure*)si->currentmeasure->data, si->currentobject);
2547   reset_cursor_stats (si);
2548 }
2549 
2550 
2551 /**
2552  * Helper to remove a object from the score
2553  * @return none.
2554  *
2555  */
2556 void
deleteobject(GtkAction * action,DenemoScriptParam * param)2557 deleteobject (GtkAction* action, DenemoScriptParam* param)
2558 {
2559   dnm_deleteobject (Denemo.project->movement);
2560 }
2561 
2562 /**
2563  * Function to delete object from the score
2564  * @param gui - pointer to the DenemoProject structure
2565  * @return none
2566  */
2567 void
dnm_deleteobject(DenemoMovement * si)2568 dnm_deleteobject (DenemoMovement * si)
2569 {
2570   declarecurmudelaobj;
2571   //staffnode *curstaff;
2572   //measurenode *curmeasure;
2573   //g_debug ("dnm_deleteobject undo/redo mode %d\n", si->undo_redo_mode);
2574   if (curmudelaobj == NULL)
2575     return;
2576   /* when tone_store is active, act on that, not the staff itself */
2577 #ifdef _HAVE_PORTAUDIO_
2578   if (((DenemoStaff *) si->currentstaff->data)->tone_store)
2579     {
2580       if (si->currentobject && ((DenemoObject *) (si->currentobject->data))->type == CHORD)
2581         {
2582           if (delete_tone (si, ((DenemoObject *) (si->currentobject->data))->object))
2583             return;
2584         }
2585     }
2586 #endif
2587   if (curmudelaobj->type == LILYDIRECTIVE && ((lilydirective *) curmudelaobj->object)->locked)
2588     {
2589       DenemoDirective *directive = (lilydirective *) curmudelaobj->object;
2590       DenemoScriptParam param;
2591       param.string = g_string_new ("delete");
2592       GtkAction *action = directive->tag?lookup_action_from_name (directive->tag->str):NULL;
2593       if (action && (Denemo.keyboard_state != GDK_MOD2_MASK /*NumLock */ ))
2594         {
2595           activate_script (action, &param);
2596           g_string_free (param.string, TRUE);
2597           return;
2598         }
2599     }
2600   DenemoUndoData *undo;
2601   if (!si->undo_guard)
2602     {
2603       undo = (DenemoUndoData *) g_malloc (sizeof (DenemoUndoData));
2604       undo->object = dnm_clone_object (curmudelaobj);
2605       //get position after delete
2606     }
2607   if (!si->cursor_appending)
2608     {
2609       switch (curmudelaobj->type)
2610         {
2611         case CHORD:
2612           delete_object_helper (si);
2613 
2614           break;
2615         case TUPOPEN:
2616         case TUPCLOSE:
2617           /* TODO - add code that will automatically delete a tupbracket's
2618            * corresponding bracket */
2619 
2620           delete_object_helper (si);
2621 
2622           break;
2623         case CLEF:
2624 /* here we have to re-validate leftmost clef e.g. find_leftmost_allcontexts (gui->movement);
2625  which seems to be done... */
2626           delete_object_helper (si);
2627           cache_staff (si->currentstaff);
2628           staff_fix_note_heights ((DenemoStaff *) si->currentstaff->data);
2629           staff_beams_and_stems_dirs ((DenemoStaff *) si->currentstaff->data);
2630           find_xes_in_all_measures (si);
2631           break;
2632         case KEYSIG:
2633           delete_object_helper (si);
2634           cache_staff (si->currentstaff);
2635           staff_beams_and_stems_dirs ((DenemoStaff *) si->currentstaff->data);
2636           staff_show_which_accidentals ((DenemoStaff *) si->currentstaff->data);
2637           find_xes_in_all_measures (si);
2638           break;
2639         case TIMESIG:
2640           delete_object_helper (si);
2641           DenemoMeasure *measure = (DenemoMeasure *)si->currentmeasure->data;
2642           if (si->currentmeasure->prev)
2643             {
2644                 DenemoMeasure *prevmeas = (DenemoMeasure *)si->currentmeasure->prev->data;
2645                 measure->timesig = prevmeas->timesig;
2646             }
2647             else
2648             {
2649               DenemoStaff *thestaff = (DenemoStaff *) si->currentstaff->data;
2650               measure->timesig = &thestaff->timesig;
2651             }
2652           update_timesig_cache (si->currentmeasure);
2653           reset_cursor_stats (si);
2654           find_xes_in_all_measures (si);
2655           break;
2656         case STEMDIRECTIVE:
2657           delete_object_helper (si);
2658           cache_staff (si->currentstaff);
2659           staff_beams_and_stems_dirs ((DenemoStaff *) si->currentstaff->data);
2660           find_xes_in_all_measures (si);
2661           break;
2662         case DYNAMIC:
2663           delete_object_helper (si);
2664 
2665           break;
2666         case LILYDIRECTIVE:
2667           delete_object_helper (si);
2668           //displayhelper (gui);
2669           break;
2670         case GRACE_START:
2671         case GRACE_END:
2672           delete_object_helper (si);
2673 
2674           break;
2675         case LYRIC:
2676         case FIGURE:
2677           delete_object_helper (si);
2678 
2679           break;
2680         case BARLINE:
2681           //    case COMMENT:
2682         case MEASUREBREAK:
2683           break;
2684           // XXX: unhandled...
2685         case STAFFBREAK:
2686         case FAKECHORD:
2687         case PARTIAL:
2688           break;
2689         }
2690       si->markstaffnum = 0;
2691     }
2692 
2693   if (!si->undo_guard)
2694     {
2695       get_position (si, &undo->position);
2696       undo->action = ACTION_DELETE;
2697       update_undo_info (si, undo);
2698     }
2699   displayhelper (Denemo.project);
2700   score_status(Denemo.project, TRUE);
2701 }
2702 
2703 /**
2704  * Insert cloned chordobject into the
2705  * score
2706  * @param si - pointer to the scoreinfo structure
2707  * @return none
2708  */
2709 void
insertclone(DenemoProject * gui)2710 insertclone (DenemoProject * gui)
2711 {
2712   DenemoMovement *si = gui->movement;
2713   declarecurmudelaobj;
2714 
2715   if (curmudelaobj && curmudelaobj->type == CHORD)
2716     object_insert (gui, dnm_clone_object (curmudelaobj));
2717 }
2718 
2719 
2720 void
tolastobject(DenemoProject * gui)2721 tolastobject (DenemoProject * gui)
2722 {
2723   while (gui->movement->currentobject && (gui->movement->currentobject->next))
2724     {
2725       gui->movement->currentobject = gui->movement->currentobject->next;
2726       gui->movement->cursor_x++;
2727     }
2728 }
2729 
2730 
2731 /* Make note tied/untied */
2732 void
toggle_tie(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)2733 toggle_tie (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
2734 {
2735   DenemoProject *gui = Denemo.project;
2736   DenemoMovement *si = gui->movement;
2737   DenemoObject *curmudelaobj = (DenemoObject *) (gui->movement->currentobject ? gui->movement->currentobject->data : NULL);
2738   if (curmudelaobj && curmudelaobj->type == CHORD && ((((chord *) curmudelaobj->object)->notes) ||  ((chord *) curmudelaobj->object)->is_tied))
2739     {
2740       store_for_undo_change (si, curmudelaobj);
2741       ((chord *) curmudelaobj->object)->is_tied ^= 1;
2742       draw_score_area();
2743     }
2744   score_status (gui, TRUE);
2745 }
2746 
2747 
2748 
2749 
2750 /**
2751  * Move cursor to the end of the score  extending the selection if extend_selection is TRUE
2752  * @param param - pointer to a script parameter structure
2753  * @return none
2754  */
2755 static void
gotoend(gpointer param,gboolean extend_selection)2756 gotoend (gpointer param, gboolean extend_selection)
2757 {
2758   DenemoProject *gui = Denemo.project;
2759   if (extend_selection && !gui->movement->markstaffnum)
2760     set_mark (NULL, NULL);
2761   gui->movement->currentmeasurenum = g_list_length (((DenemoStaff *) gui->movement->currentstaff->data)->themeasures);
2762   setcurrents (gui->movement);
2763   if (extend_selection)
2764     calcmarkboundaries (gui->movement);
2765   tolastobject (gui);
2766   if (extend_selection)
2767     cursorright (NULL, param);
2768   else
2769     movecursorright (NULL, param);
2770   //refresh cached values, eg current timesig
2771   update_drawing_cache ();
2772   find_leftmost_allcontexts (gui->movement);  //FIXME is this done in displayhelper?
2773   displayhelper (gui);
2774 }
2775 
2776 /**
2777  * Move the cursor to the beginning of the score extending the selection if extend_selection is TRUE
2778  * @param param - pointer to a script parameter structure
2779  * @return none
2780 */
2781 static void
gotohome(gboolean extend_selection)2782 gotohome (gboolean extend_selection)
2783 {
2784   DenemoProject *gui = Denemo.project;
2785   if (extend_selection && !gui->movement->markstaffnum)
2786     set_mark (NULL, NULL);
2787   gui->movement->currentmeasurenum = gui->movement->leftmeasurenum = 1;
2788   displayhelper (gui);
2789   setcurrents (gui->movement);
2790   if (extend_selection)
2791     calcmarkboundaries (gui->movement);
2792   find_leftmost_allcontexts (gui->movement);
2793 
2794   if(!Denemo.non_interactive){
2795     //refresh cached values, eg current timesig
2796     update_drawing_cache ();
2797   }
2798 }
2799 
2800 
2801 /**
2802  * Move the cursor to the beginning of the score, extending the selection if any.
2803  * @param action - Gtk Action event
2804  * @param
2805  * @return none
2806 */
2807 void
tohome(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)2808 tohome (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
2809 {
2810   gotohome (TRUE);
2811 }
2812 
2813 
2814 /**
2815  * Move the cursor to the end of the score, extending the selection if any.
2816  * @param action - Gtk Action event
2817  * @param
2818  * @return none
2819 */
2820 void
toend(G_GNUC_UNUSED GtkAction * action,DenemoScriptParam * param)2821 toend (G_GNUC_UNUSED GtkAction * action, DenemoScriptParam * param)
2822 {
2823   gotoend (param, TRUE);
2824 }
2825 
2826 
2827 
2828 
2829 /**
2830  * Move the cursor to the beginning of the staff, without extending the selection if any.
2831  * @param action - Gtk Action event
2832  * @param
2833  * @return none
2834 */
2835 void
movetostart(G_GNUC_UNUSED GtkAction * action,DenemoScriptParam * param)2836 movetostart (G_GNUC_UNUSED GtkAction * action, DenemoScriptParam * param)
2837 {
2838   gotohome (FALSE);
2839   if (param)
2840     param->status = TRUE;
2841 }
2842 
2843 /**
2844  * Move the cursor to the end of the staff, without extending the selection if any.
2845  * @param action - Gtk Action event
2846  * @param
2847  * @return none
2848 */
2849 void
movetoend(G_GNUC_UNUSED GtkAction * action,DenemoScriptParam * param)2850 movetoend (G_GNUC_UNUSED GtkAction * action, DenemoScriptParam * param)
2851 {
2852   gotoend (param, FALSE);
2853   if (param)
2854     param->status = TRUE;
2855 }
2856 
2857 
2858 
2859 /**
2860  * Insert stem directive,  absolute stemdirection for the
2861  * entire staff or until a new stem directive in added
2862  *
2863  * @param action Gtk Action event
2864  * @param gui pointer to the DenemoProject structure
2865  * @return none
2866  */
2867 void
stem_directive_insert(G_GNUC_UNUSED GtkAction * action,G_GNUC_UNUSED DenemoScriptParam * param)2868 stem_directive_insert (G_GNUC_UNUSED GtkAction * action, G_GNUC_UNUSED DenemoScriptParam * param)
2869 {
2870   DenemoProject *gui = Denemo.project;
2871   object_insert (gui, dnm_stem_directive_new (DENEMO_STEMBOTH));
2872   /* This sets beams and stem directions in the measure, but that's
2873    * not sufficient */
2874 
2875   displayhelper (gui);
2876 
2877 
2878 }
2879 
2880 /**
2881  * Toggle start_slur flag for the current chord
2882  *
2883  * @return none
2884  */
2885 void
toggle_begin_slur(GtkAction * action,DenemoScriptParam * param)2886 toggle_begin_slur (GtkAction* action, DenemoScriptParam* param)
2887 {
2888   DenemoMovement *si = Denemo.project->movement;
2889   declarecurmudelaobj;
2890 
2891   if (curmudelaobj && curmudelaobj->type == CHORD)
2892     {
2893       store_for_undo_change (si, curmudelaobj);
2894 
2895 
2896       ((chord *) curmudelaobj->object)->slur_begin_p = !((chord *) curmudelaobj->object)->slur_begin_p;
2897 
2898 
2899     }
2900   displayhelper (Denemo.project);
2901   score_status(Denemo.project, TRUE);
2902 }
2903 
2904 /**
2905  *  Force a cautionary accidental
2906  * @param si pointer to the scoreinfo structure
2907  * @return none
2908  */
2909 void
caution(DenemoMovement * si)2910 caution (DenemoMovement * si)
2911 {
2912   declarecurmudelaobj;
2913 
2914   forceaccidentals (curmudelaobj);
2915   find_xes_in_measure (si, si->currentmeasurenum);
2916 }
2917 
2918 /**
2919  * Toggle end_slur flag for the current chord
2920  * @return none
2921  */
2922 void
toggle_end_slur(GtkAction * action,DenemoScriptParam * param)2923 toggle_end_slur (GtkAction* action, DenemoScriptParam * param)
2924 {
2925   DenemoMovement *si = Denemo.project->movement;
2926   declarecurmudelaobj;
2927 
2928   if (curmudelaobj && curmudelaobj->type == CHORD)
2929     {
2930       store_for_undo_change (si, curmudelaobj);
2931 
2932 
2933       ((chord *) curmudelaobj->object)->slur_end_p = !((chord *) curmudelaobj->object)->slur_end_p;
2934 
2935 
2936     }
2937   displayhelper (Denemo.project);
2938   score_status(Denemo.project, TRUE);
2939 }
2940 
2941 /**
2942  * Toggle start crescendo flag for current chord
2943  * @param si pointer to the scoreinfo structure
2944  * @return none
2945  */
2946 void
toggle_start_crescendo(GtkAction * action,DenemoScriptParam * param)2947 toggle_start_crescendo (GtkAction* action, DenemoScriptParam * param)
2948 {
2949   DenemoMovement *si = (DenemoMovement *) Denemo.project->movement;
2950   declarecurmudelaobj;
2951 
2952   if (curmudelaobj && curmudelaobj->type == CHORD)
2953     {
2954       ((chord *) curmudelaobj->object)->crescendo_begin_p = !((chord *) curmudelaobj->object)->crescendo_begin_p;
2955 
2956 
2957     }
2958   displayhelper (Denemo.project);
2959   score_status(Denemo.project, TRUE);
2960 }
2961 
2962 /**
2963  * Toggle end crescendo flag for current chord
2964  * @param si pointer to the scoreinfo structure
2965  * @return none
2966  */
2967 void
toggle_end_crescendo(GtkAction * action,DenemoScriptParam * param)2968 toggle_end_crescendo (GtkAction* action, DenemoScriptParam * param)
2969 {
2970   DenemoMovement *si = (DenemoMovement *) Denemo.project->movement;
2971   declarecurmudelaobj;
2972 
2973   if (curmudelaobj && curmudelaobj->type == CHORD)
2974     {
2975       ((chord *) curmudelaobj->object)->crescendo_end_p = !((chord *) curmudelaobj->object)->crescendo_end_p;
2976 
2977 
2978     }
2979   displayhelper (Denemo.project);
2980   score_status(Denemo.project, TRUE);
2981 }
2982 
2983 /**
2984  * Toggle start diminuendo flag for current chord
2985  * @param si pointer to the scoreinfo structure
2986  * @return none
2987  */
2988 void
toggle_start_diminuendo(GtkAction * action,DenemoScriptParam * param)2989 toggle_start_diminuendo (GtkAction* action, DenemoScriptParam * param)
2990 {
2991   DenemoMovement *si = (DenemoMovement *) Denemo.project->movement;
2992   declarecurmudelaobj;
2993 
2994   if (curmudelaobj && curmudelaobj->type == CHORD)
2995     {
2996       ((chord *) curmudelaobj->object)->diminuendo_begin_p = !((chord *) curmudelaobj->object)->diminuendo_begin_p;
2997 
2998 
2999     }
3000   displayhelper (Denemo.project);
3001   score_status(Denemo.project, TRUE);
3002 }
3003 
3004 /**
3005  * Toggle end diminuendo flag for current chord
3006  * @param si pointer to the scoreinfo structure
3007  * @return none
3008  */
3009 void
toggle_end_diminuendo(GtkAction * action,DenemoScriptParam * param)3010 toggle_end_diminuendo (GtkAction* action, DenemoScriptParam * param)
3011 {
3012   DenemoMovement *si = (DenemoMovement *) Denemo.project->movement;
3013   declarecurmudelaobj;
3014 
3015   if (curmudelaobj && curmudelaobj->type == CHORD)
3016     {
3017       ((chord *) curmudelaobj->object)->diminuendo_end_p = !((chord *) curmudelaobj->object)->diminuendo_end_p;
3018 
3019 
3020     }
3021   displayhelper (Denemo.project);
3022   score_status(Denemo.project, TRUE);
3023 }
3024 
3025 
3026 
3027 /**
3028  * Autosave timeout function - saves the current score after the current
3029  * timeout has expired
3030  * @param si pointer to the scoreinfo structure
3031  * @return TRUE for timer to continue FALSE if the DenemoProject has vanished.
3032  */
3033 gboolean
auto_save_document_timeout(DenemoProject * gui)3034 auto_save_document_timeout (DenemoProject * gui)
3035 { static gint old_changecount;
3036   /* first check that this timer has not been left running after destruction of the gui */
3037   if (g_list_find (Denemo.projects, gui) == NULL)
3038     {
3039       //do not do this, it causes denemo to hang. warningdialog (_("Timer left running"));
3040       return FALSE;             /* turns off the timer */
3041     }
3042   if ((!gui->notsaved) || (gui->changecount == old_changecount))
3043     return TRUE; // wait until project has been modified.
3044   DenemoMovement *si = gui->movement;
3045 
3046   g_message ("Autosaving");
3047   if (!gui->autosavename)
3048     {
3049       g_warning ("gui->autosavename not set");
3050         return FALSE;
3051     }
3052     old_changecount = gui->changecount;
3053   //g_debug ("Auto save file name %s\n", gui->autosavename->str);
3054   exportXML (gui->autosavename->str, gui);
3055   return TRUE;
3056 }
3057