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, ¶m);
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