1 /* mousing.c
2  * callback functions for handling mouse clicks, drags, etc.
3  *
4  *  for Denemo, a gtk+ frontend to GNU Lilypond
5  *  (c) 2000-2005 Matthew Hiller
6  */
7 
8 #include "command/commandfuncs.h"
9 #include "core/kbd-custom.h"
10 #include "command/staff.h"
11 #include "core/utils.h"
12 #include "command/object.h"
13 #include "command/select.h"
14 #include "command/lilydirectives.h"
15 #include "command/lyric.h"
16 #include "ui/moveviewport.h"
17 #include "ui/mousing.h"
18 #include "audio/fluid.h"
19 #include "audio/playback.h"
20 #include "display/draw.h"
21 #include "core/view.h"
22 #include "audio/audiointerface.h"
23 #include "export/exportmidi.h"
24 
25 
26 static gboolean lh_down;
27 static gdouble last_event_x;
28 static gdouble last_event_y;
29 static DenemoDirective *last_directive;
30 typedef enum DragDirection { DRAG_DIRECTION_NONE = 0, DRAG_DIRECTION_UP, DRAG_DIRECTION_DOWN, DRAG_DIRECTION_LEFT, DRAG_DIRECTION_RIGHT} DragDirection;
31 static enum DragDirection dragging_outside = DRAG_DIRECTION_NONE; //dragging to left or right outside window.
32 
33 /**
34  * Get the mid_c_offset of an object or click from its height relative
35  * to the top of the staff.
36  */
37 gint
offset_from_height(gdouble height,enum clefs clef)38 offset_from_height (gdouble height, enum clefs clef)
39 {
40   /* Offset from the top of the staff, in half-tones.  */
41   gint half_tone_offset = ((gint) (height / HALF_LINE_SPACE + ((height > 0) ? 0.5 : -0.5)));
42 
43 #define R(x) return x - half_tone_offset
44 
45   switch (clef)
46     {
47     case DENEMO_TREBLE_CLEF:
48       R (10);
49       break;
50     case DENEMO_BASS_CLEF:
51       R (-2);
52       break;
53     case DENEMO_ALTO_CLEF:
54       R (4);
55       break;
56     case DENEMO_G_8_CLEF:
57       R (3);
58       break;
59     case DENEMO_F_8_CLEF:
60       R (-9);
61       break;
62     case DENEMO_TENOR_CLEF:
63       R (2);
64       break;
65     case DENEMO_SOPRANO_CLEF:
66       R (8);
67       break;
68     case DENEMO_FRENCH_CLEF:
69       R (12);
70       break;
71       //when adding clefs get utils.c calculateheight() function correct first, then do this
72     default:
73       R (0);
74       break;
75     }
76 #undef R
77   return 0;
78 }
79 
80 
81 static gdouble
get_click_height(DenemoProject * gui,gdouble y)82 get_click_height (DenemoProject * gui, gdouble y)
83 {
84   gdouble click_height;
85   gint staffs_from_top;
86   staffs_from_top = 0;
87   GList *curstaff;
88   DenemoStaff *staff;
89   gint extra_space = 0;
90   gint space_below = 0;
91   gint i;
92   curstaff = g_list_nth (gui->movement->thescore, gui->movement->top_staff - 1);
93 
94   if (!(((DenemoStaff *) (gui->movement->currentstaff->data))->voicecontrol & DENEMO_PRIMARY))
95     staffs_from_top--;
96 
97   for (i=0, curstaff = g_list_nth (gui->movement->thescore, gui->movement->top_staff - 1); curstaff; i++, curstaff = curstaff->next)
98     {
99       staff = (DenemoStaff *) curstaff->data;
100      // g_print("%d from top, staff %d extra space %d (next is %d %d  %d) previous space_below = %d)\n", i, gui->movement->currentstaffnum, extra_space, staff->space_above, staff->space_shorten, staff->space_below, space_below);
101 
102       if (staff->hidden) continue;
103       if (staff->voicecontrol & DENEMO_PRIMARY)
104         extra_space += (staff->space_above - (i?staff->space_shorten:0)
105           + space_below);
106       if (curstaff == gui->movement->currentstaff)
107         break;
108       if (staff->voicecontrol & DENEMO_PRIMARY)
109         {
110 
111           space_below = 0;
112           staffs_from_top++;
113         }
114       space_below = MAX (space_below, ((staff->space_below) + (staff->verse_views ? LYRICS_HEIGHT : 0)));
115      // g_print("after extra space %d space_below %d\n", extra_space, space_below);
116     }
117 
118   click_height = y - (gui->movement->staffspace * staffs_from_top + gui->movement->staffspace / 4 + extra_space);
119   //g_print("top staff is %d total %d staffs from top is %d click %f\n", gui->movement->top_staff, extra_space, staffs_from_top, click_height);
120 
121   return click_height;
122 
123 
124 
125 }
126 
127 /**
128  * Set the cursor's y position from a mouse click
129  *
130  */
131 void
set_cursor_y_from_click(DenemoProject * gui,gdouble y)132 set_cursor_y_from_click (DenemoProject * gui, gdouble y)
133 {
134   DenemoStaff *staff = (DenemoStaff*)gui->movement->currentstaff->data;
135   gint cursorclef;
136   if (gui->movement->currentobject)
137     cursorclef = ((DenemoObject *)gui->movement->currentobject->data)->clef->type;
138   else
139     cursorclef = ((DenemoMeasure *)gui->movement->currentmeasure->data)->clef->type;
140   /* Click height relative to the top of the staff.  */
141   gdouble click_height = get_click_height (gui, y);
142   gint offset = offset_from_height (click_height, (enum clefs) cursorclef);
143   //g_print ("Does %d come within range %d %d?\n", offset, staff->range_hi, staff->range_lo);
144   if (staff->range)
145   {
146     if(offset < staff->range_lo)
147         offset = staff->range_lo;
148     else
149       if(offset > staff->range_hi)
150         offset = staff->range_hi;
151   }
152   gui->movement->cursor_y = offset;
153   gui->movement->staffletter_y = offsettonumber (gui->movement->cursor_y);
154 }
155 
156 struct placement_info
157 {
158   gint staff_number, measure_number, cursor_x;
159   staffnode *the_staff;
160   measurenode *the_measure;
161   objnode *the_obj;
162   gboolean nextmeasure;
163   gboolean offend;              //TRUE when the user has clicked beyond the last note, taking you into appending
164   gboolean at_edge; //TRUE when user clicked close (CURSOR_AT_EDGE) to right hand edge of scorearea.
165 };
166 #define CURSOR_AT_EDGE (60)
167 /* find the primary staff of the current staff, return its staffnum */
168 static gint
primary_staff(DenemoMovement * si)169 primary_staff (DenemoMovement * si)
170 {
171   GList *curstaff;
172   for (curstaff = si->currentstaff; curstaff && !(((DenemoStaff *) curstaff->data)->voicecontrol & DENEMO_PRIMARY); curstaff = curstaff->prev)
173     ;                           //do nothing
174   //g_debug("The position is %d\n", 1+g_list_position(si->thescore, curstaff));
175   return 1 + g_list_position (si->thescore, curstaff);
176 }
177 
178 
179 /* find which staff in si the height y lies in, return the staff number (not counting non-primary staffs ie voices) */
180 
181 static gint
staff_at(gint y,DenemoMovement * si)182 staff_at (gint y, DenemoMovement * si)
183 {
184   GList *curstaff;
185   gint space = 0;
186   gint count;
187   gint ret;
188   for (curstaff = g_list_nth (si->thescore, si->top_staff - 1), count = 0; curstaff && y > space; curstaff = curstaff->next)
189     {
190       DenemoStaff *staff = (DenemoStaff *) curstaff->data;
191 
192       count++;
193       if ((!staff->hidden) && (staff->voicecontrol & DENEMO_PRIMARY))
194         space += staff->space_above + staff->space_below - staff->space_shorten + si->staffspace + (staff->verse_views ? LYRICS_HEIGHT : 0);
195       //g_debug("y %d and space %d count = %d\n",y,space, count);
196     }
197 
198   if (y <= 1)
199     ret = 1;
200   ret = count + si->top_staff - 1;
201   if (ret == primary_staff (si))
202     ret = si->currentstaffnum;
203   return ret > 0 ? ret : 1;
204 }
205 
206 /**
207  * Gets the position from the clicked position
208  *
209  */
210 static void
get_placement_from_coordinates(struct placement_info * pi,gdouble x,gdouble y,gint leftmeasurenum,gint rightmeasurenum,gint scale)211 get_placement_from_coordinates (struct placement_info *pi, gdouble x, gdouble y, gint leftmeasurenum, gint rightmeasurenum, gint scale)
212 {
213   DenemoProject *gui = Denemo.project;
214   DenemoMovement *si = gui->movement;
215   GList *mwidthiterator = g_list_nth (si->measurewidths,
216                                       leftmeasurenum - 1);
217   objnode *obj_iterator;
218   gint x_to_explain = (gint) (x);
219   pi->offend = FALSE;
220   pi->the_obj = NULL;
221   if (mwidthiterator == NULL)
222     {
223       g_critical ("Array of measurewidths too small for leftmeasure %d\n", leftmeasurenum);
224       return;
225     }
226   pi->at_edge = (get_widget_width (Denemo.scorearea) -x) < CURSOR_AT_EDGE;
227   pi->staff_number = staff_at ((gint) y, si);
228   //g_debug("L/R %d %d got staff number %d\n", leftmeasurenum, rightmeasurenum, pi->staff_number);
229   pi->measure_number = leftmeasurenum;
230   if (scale)
231     x_to_explain = (x_to_explain * scale) / 100;
232   x_to_explain -= ((gui->leftmargin+35) + si->maxkeywidth + SPACE_FOR_TIME);
233 
234   //g_debug("Explaining %d\n", x_to_explain);
235   while (x_to_explain > GPOINTER_TO_INT (mwidthiterator->data) && pi->measure_number < rightmeasurenum)
236     {
237       x_to_explain -= (GPOINTER_TO_INT (mwidthiterator->data) + SPACE_FOR_BARLINE);
238       mwidthiterator = mwidthiterator->next;
239       pi->measure_number++;
240     }
241   //g_debug("got to measure %d\n", pi->measure_number);
242   pi->nextmeasure = ((si->system_height > 0.5 || x_to_explain > GPOINTER_TO_INT (mwidthiterator->data)) && pi->measure_number >= rightmeasurenum);
243 
244   pi->the_staff = g_list_nth (si->thescore, pi->staff_number - 1);
245   if (pi->the_staff != NULL)
246     pi->the_measure
247       = staff_nth_measure_node (pi->the_staff, pi->measure_number - 1);
248   else
249     pi->the_measure = NULL;
250   if (pi->the_measure != NULL)
251     {                           /*check to make sure user did not click on empty space */
252       obj_iterator = (objnode *) ((DenemoMeasure *)pi->the_measure->data)->objects;
253       pi->cursor_x = 0;
254       pi->the_obj = NULL;
255       if (obj_iterator)
256         {
257           DenemoObject *current, *next;
258 
259           for (; obj_iterator->next; obj_iterator = obj_iterator->next, pi->cursor_x++)
260             {
261               current = (DenemoObject *) obj_iterator->data;
262               next = (DenemoObject *) obj_iterator->next->data;
263               /* This comparison neatly takes care of two possibilities:
264 
265                  1) That the click was to the left of current, or
266 
267                  2) That the click was between current and next, but
268                  closer to current.
269 
270                  Do the math - it really does work out.  */
271 
272               //???modify current->x by gx where graphic_override is set????
273               if (x_to_explain - (current->x + current->minpixelsalloted) < next->x - x_to_explain)
274                 {
275                   pi->the_obj = obj_iterator;
276                   break;
277                 }
278             }
279           if (!obj_iterator->next)
280             /* That is, we exited the loop normally, not through a break.  */
281             {
282               DenemoObject *current = (DenemoObject *) obj_iterator->data;
283               pi->the_obj = obj_iterator;//g_print("x_to_explain %d, compare current->x=%d and minpix %d\n",x_to_explain,current->x,current->minpixelsalloted);
284               /* The below makes clicking to get the object at the end of
285                  a measure (instead of appending after it) require
286                  precision.  This may be bad; tweak me if necessary.  */
287               if ((x_to_explain > current->x + current->minpixelsalloted) || (x_to_explain > GPOINTER_TO_INT (mwidthiterator->data) - current->minpixelsalloted / 3))   //if closer to barline than object center
288                 pi->offend = TRUE, pi->cursor_x++;
289             }
290         }
291       //g_debug("got to cursor x %d\n", pi->cursor_x);
292     }
293 }
294 
295 
296 void
assign_cursor(guint state,guint cursor_num)297 assign_cursor (guint state, guint cursor_num)
298 {
299   guint *cursor_state = g_new (guint, 1);
300   *cursor_state = state;
301   //g_print("Storing cursor %d for state 0x%x in hash table %p\n", cursor_num, state, Denemo.map->cursors );
302   GdkCursor *cursor = gdk_cursor_new (cursor_num);
303   if (cursor)
304     g_hash_table_insert (Denemo.map->cursors, cursor_state, cursor);
305 }
306 
307 void
set_cursor_for(guint state)308 set_cursor_for (guint state)
309 {
310   gint the_state = state;
311   GdkCursor *cursor = g_hash_table_lookup (Denemo.map->cursors, &the_state);
312   //g_print("looked up %x in %p got cursor %p\n", state, Denemo.map->cursors,  cursor);
313   if (cursor)
314     gdk_window_set_cursor (gtk_widget_get_window (Denemo.window), cursor);
315   else
316     gdk_window_set_cursor (gtk_widget_get_window (Denemo.window), gdk_cursor_new (GDK_LEFT_PTR));       //FIXME? does this take time/hog memory
317 }
318 
319 
320 /* appends the name(s) for modifier mod to ret->str */
321 
322 void
append_modifier_name(GString * ret,gint mod)323 append_modifier_name (GString * ret, gint mod)
324 {
325   gint i;
326   static const gchar *names[] = {
327     "Shift",
328     "CapsLock",
329     "Control",
330     "Alt",
331     "NumLock",
332     "MOD3",
333     "Penguin",
334     "AltGr"
335   };
336   for (i = 0; i < DENEMO_NUMBER_MODIFIERS; i++)
337     if ((1 << i) & mod)
338       g_string_append_printf (ret, "%s%s", "-", names[i]);
339   g_string_append_printf (ret, "%s", mod ? "" : "");
340 }
341 
342 /* returns a newly allocated GString containing a shortcut name */
343 GString *
mouse_shortcut_name(gint mod,mouse_gesture gesture,gboolean left)344 mouse_shortcut_name (gint mod, mouse_gesture gesture, gboolean left)
345 {
346 
347   GString *ret = g_string_new ((gesture == GESTURE_PRESS) ? (left ? "PrsL" : "PrsR") : ((gesture == GESTURE_RELEASE) ? (left ? "RlsL" : "RlsR") : (left ? "MveL" : "MveR")));
348 
349   append_modifier_name (ret, mod);
350   //g_debug("Returning %s for mod %d\n", ret->str, mod);
351   return ret;
352 
353 }
354 
355 
356 /* perform an action for mouse-click stored with shortcuts */
357 static void
perform_command(gint modnum,mouse_gesture press,gboolean left)358 perform_command (gint modnum, mouse_gesture press, gboolean left)
359 {
360   GString *modname = mouse_shortcut_name (modnum, press, left);
361   gint command_idx = lookup_command_for_keybinding_name (Denemo.map, modname->str);
362   if (press != GESTURE_MOVE)
363     {
364       if (!Denemo.prefs.strictshortcuts)
365         {
366           if (command_idx < 0)
367             {
368               g_string_free (modname, TRUE);
369               modname = mouse_shortcut_name (modnum & (~GDK_LOCK_MASK /*CapsLock */ ), press, left);
370               command_idx = lookup_command_for_keybinding_name (Denemo.map, modname->str);
371             }
372           if (command_idx < 0)
373             {
374               g_string_free (modname, TRUE);
375               modname = mouse_shortcut_name (modnum & (~GDK_MOD2_MASK /*NumLock */ ), press, left);
376               command_idx = lookup_command_for_keybinding_name (Denemo.map, modname->str);
377             }
378           if (command_idx < 0)
379             {
380               g_string_free (modname, TRUE);
381               modname = mouse_shortcut_name (modnum & (~(GDK_LOCK_MASK | GDK_MOD2_MASK)), press, left);
382               command_idx = lookup_command_for_keybinding_name (Denemo.map, modname->str);
383             }
384         }
385     }
386 
387 
388   if (command_idx >= 0)
389     {
390 
391       if (Denemo.prefs.learning)
392         KeyPlusMouseGestureShow(modname->str, command_idx);
393 
394       execute_callback_from_idx (Denemo.map, command_idx);
395       displayhelper (Denemo.project);
396     }
397   g_string_free (modname, TRUE);
398 }
399 
400 static gboolean selecting = FALSE;
401 static gboolean dragging_separator = FALSE;
402 static gboolean dragging_audio = FALSE;
403 static gboolean dragging_tempo = FALSE;
404 
405 
406 static gboolean
change_staff(DenemoMovement * si,gint num,GList * staff)407 change_staff (DenemoMovement * si, gint num, GList * staff)
408 {
409   if (si->currentstaffnum == num)
410     return FALSE;
411   hide_lyrics ();
412   si->currentstaffnum = num;
413   si->currentstaff = staff;
414   show_lyrics ();
415   return TRUE;
416 }
417 
418 static void
transform_coords(double * x,double * y)419 transform_coords (double *x, double *y)
420 {
421   DenemoProject *gui = Denemo.project;
422 
423   gint application_height = get_widget_height (Denemo.scorearea);
424   gint line_height = application_height * gui->movement->system_height;
425   gint line_num = ((int) *y) / line_height;
426   *y -= line_num * line_height;
427   *x /= gui->movement->zoom;
428   *y /= gui->movement->zoom;
429   // *x += ((double)line_num * gui->movement->widthtoworkwith / ((int)(1/gui->movement->system_height))) - 1.0* (line_num?(double)gui->leftmargin:0.0);
430 }
431 
extend_selection(DragDirection direction)432 static void extend_selection (DragDirection direction)
433 {
434    switch (direction) {
435        case DRAG_DIRECTION_RIGHT:
436         cursorright (NULL, NULL);
437         break;
438       case DRAG_DIRECTION_LEFT:
439         cursorleft (NULL, NULL);
440         break;
441       case DRAG_DIRECTION_UP:
442         staffup (NULL, NULL);
443         move_viewport_up (Denemo.project);
444         break;
445       case DRAG_DIRECTION_DOWN:
446         staffdown (NULL, NULL);
447         move_viewport_down (Denemo.project);
448         break;
449     }
450 
451     gtk_widget_queue_draw(Denemo.scorearea);
452 }
453 gint
scorearea_leave_event(GtkWidget * widget,GdkEventCrossing * event)454 scorearea_leave_event (GtkWidget * widget, GdkEventCrossing * event)
455 {
456     gint allocated_height = get_widget_height (Denemo.scorearea);
457     gint allocated_width = get_widget_width (Denemo.scorearea);
458   if (event->state & GDK_BUTTON1_MASK)
459     {
460        dragging_outside = (event->x>=allocated_width)?DRAG_DIRECTION_RIGHT:(event->x < 0)?DRAG_DIRECTION_LEFT:(event->y < 0)? DRAG_DIRECTION_UP : DRAG_DIRECTION_DOWN;
461        last_event_x = event->x_root;
462        last_event_y = event->y_root;
463     }
464   gdk_window_set_cursor (gtk_widget_get_window (Denemo.window), gdk_cursor_new (GDK_LEFT_PTR)); //FIXME? does this take time/hog memory
465   return FALSE;                 //allow other handlers (specifically the pitch entry one)
466 }
467 
468 gint
scorearea_enter_event(GtkWidget * widget,GdkEventCrossing * event)469 scorearea_enter_event (GtkWidget * widget, GdkEventCrossing * event)
470 {
471   dragging_outside = DRAG_DIRECTION_NONE;
472   if(Denemo.keyboard_state_locked) return FALSE;
473 //g_debug("start the enter with ks = %x and state %x\n", Denemo.keyboard_state, event->state);
474   if (event->state & GDK_CONTROL_MASK)
475     Denemo.keyboard_state |= GDK_CONTROL_MASK;
476   else
477     Denemo.keyboard_state &= ~GDK_CONTROL_MASK;
478 
479   if (event->state & GDK_SHIFT_MASK)
480     Denemo.keyboard_state |= GDK_SHIFT_MASK;
481   else
482     Denemo.keyboard_state &= ~GDK_SHIFT_MASK;
483 #if 0
484 //perhaps it would be better to clear Denemo.keyboard_state on focus out event???
485   if (event->state & GDK_MOD1_MASK)
486     Denemo.keyboard_state |= GDK_MOD1_MASK;
487   else
488     Denemo.keyboard_state &= ~(CHORD_MASK | GDK_MOD1_MASK);
489 #endif
490 //      g_debug("end the enter with ks %x (values  %x %x)\n", event->state, ~GDK_CONTROL_MASK, Denemo.keyboard_state & (~GDK_CONTROL_MASK) );
491   set_midi_in_status ();
492   return FALSE;                 //allow other handlers
493 }
494 
495 /**
496  * Mouse motion callback
497  *
498  */
499 gint
scorearea_motion_notify(GtkWidget * widget,GdkEventButton * event)500 scorearea_motion_notify (GtkWidget * widget, GdkEventButton * event)
501 {
502   DenemoProject *gui = Denemo.project;
503   if (gui == NULL || gui->movement == NULL)
504     return FALSE;
505   if (Denemo.scorearea == NULL)
506     return FALSE;
507   gint allocated_height = get_widget_height (Denemo.scorearea);
508   gint line_height = allocated_height * gui->movement->system_height;
509   if(dragging_outside)
510     {
511           gint incrx, incry;
512           incrx=incry=0;
513           if(((gint)((last_event_x - event->x_root)/gui->movement->zoom)) != 0)
514             {
515                 incrx = -(last_event_x - event->x_root)/gui->movement->zoom;
516                 last_event_x = event->x_root;
517             }
518           if( ((gint)((last_event_y - event->y_root)/gui->movement->zoom)) != 0)
519             {
520                 incry = -(last_event_y - event->y_root)/gui->movement->zoom;
521                 last_event_y = event->y_root;
522             }
523         if((dragging_outside==DRAG_DIRECTION_RIGHT) && (incrx > 1)
524             || ((dragging_outside==DRAG_DIRECTION_LEFT) && (incrx < -1))
525             || ((dragging_outside==DRAG_DIRECTION_UP) && (incry < 0))
526             || ((dragging_outside==DRAG_DIRECTION_DOWN) && (incry > 0)))
527             extend_selection(dragging_outside);
528     return TRUE;
529     }
530   if (event->y < 0)
531     event->y = 0.0;
532   gint line_num = ((int) event->y) / line_height;
533 
534 
535    if (last_directive && (GDK_SHIFT_MASK & event->state) && (GDK_CONTROL_MASK & event->state))
536       {
537           gint incrx, incry;
538           incrx=incry=0;
539           if(((gint)((last_event_x - event->x_root)/gui->movement->zoom)) != 0)
540             {
541                 incrx = (last_event_x - event->x_root)/gui->movement->zoom;
542                 last_event_x = event->x_root;
543             }
544           if( ((gint)((last_event_y - event->y_root)/gui->movement->zoom)) != 0)
545             {
546                 incry = (last_event_y - event->y_root)/gui->movement->zoom;
547                 last_event_y = event->y_root;
548             }
549 
550         if(last_directive->graphic)
551             {
552                 last_directive->gx -= incrx;
553                 last_directive->gy -= incry;
554             }
555         else
556             {
557                 last_directive->tx -= incrx;
558                 last_directive->ty -= incry;
559             }
560         draw_score_area();
561 
562         return TRUE;
563       }
564 
565 
566 
567   if(gui->movement->recording && dragging_audio)
568     {
569         if(gui->movement->recording->type == DENEMO_RECORDING_MIDI)
570         {
571             #if 0
572             //This is moving only the NoteOn, so it could be moved later than the note off, and indeed later than a later note in the stream
573             //- quite a bit more work needed to drag MIDI to correct the timing.
574             smf_event_t *midievent;
575             GList *marked_onset = gui->movement->marked_onset;
576             if(marked_onset)
577                 {
578                 midievent = ((DenemoRecordedNote *)marked_onset->data)->event;
579                 gint shift =  2500*(event->x_root - last_event_x)/gui->movement->zoom;
580                 g_debug (" %f (%f %f)",shift/(double)gui->movement->recording->samplerate,
581                     midievent->time_seconds,
582                     ((DenemoRecordedNote *)marked_onset->data)->timing/(double)gui->movement->recording->samplerate) ;
583 
584                 ((DenemoRecordedNote *)marked_onset->data)->timing += shift;
585 
586                 midievent->time_seconds += shift/(double)gui->movement->recording->samplerate;
587                 }
588             #endif
589             g_warning("No drag for MIDI yet");
590             return TRUE;
591         }
592 
593         gui->movement->recording->leadin -= 500*(event->x_root - last_event_x)/gui->movement->zoom;//g_debug("%d %d => %d\n", (int)(10*last_event_x), (int)(10*event->x_root), (int)(10*last_event_x) - (int)(10*event->x_root));
594         last_event_x = event->x_root;
595         update_leadin_widget ( gui->movement->recording->leadin/(double)gui->movement->recording->samplerate);
596         gtk_widget_queue_draw(Denemo.scorearea);
597         return TRUE;
598     }
599   if(gui->movement->recording && dragging_tempo)
600     {
601         gdouble change = (event->x_root - last_event_x)/gui->movement->zoom;
602         last_event_x = event->x_root;
603         struct placement_info pi;
604         get_placement_from_coordinates (&pi, event->x, 0, gui->lefts[line_num], gui->rights[line_num], gui->scales[line_num]);
605         change /= pi.measure_number;
606         update_tempo_widget ( change);
607         set_tempo ();
608         score_status (Denemo.project, TRUE);
609         exportmidi (NULL, gui->movement);
610         gtk_widget_queue_draw(Denemo.scorearea);
611         return TRUE;
612     }
613 #define DENEMO_MINIMUM_SYSTEM_HEIGHT (0.01)
614 
615 
616   if (dragging_separator)
617     {
618       gui->movement->system_height = event->y / get_widget_height (Denemo.scorearea);
619       if (gui->movement->system_height < DENEMO_MINIMUM_SYSTEM_HEIGHT)
620         gui->movement->system_height = DENEMO_MINIMUM_SYSTEM_HEIGHT;
621       if (gui->movement->system_height > 1.0)
622         gui->movement->system_height = 1.0;
623       scorearea_configure_event (Denemo.scorearea, NULL);
624       draw_score_area();
625       return TRUE;
626     }
627 
628   if (line_height - ((int) event->y - 8) % line_height < 12)
629     gdk_window_set_cursor (gtk_widget_get_window (Denemo.window), gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW));
630   else
631     gdk_window_set_cursor (gtk_widget_get_window (Denemo.window), gdk_cursor_new (GDK_LEFT_PTR));       //FIXME? does this take time/hog memory
632 
633   transform_coords (&event->x, &event->y);
634   //g_debug("Marked %d\n", gui->movement->markstaffnum);
635 
636 
637   if (gui->lefts[line_num] == 0)
638     return TRUE;
639 
640 
641 
642 
643   if (lh_down || (selecting && gui->movement->markstaffnum))
644     {
645       struct placement_info pi;
646       pi.the_staff = NULL;
647       if (event->y < 0)
648         get_placement_from_coordinates (&pi, event->x, 0, gui->lefts[line_num], gui->rights[line_num], gui->scales[line_num]);
649       else
650         get_placement_from_coordinates (&pi, event->x, event->y, gui->lefts[line_num], gui->rights[line_num], gui->scales[line_num]);
651       if (pi.the_staff == NULL)
652         return TRUE;            //could not place the cursor
653       if (pi.the_measure != NULL)
654         {                       /*don't place cursor in a place that is not there */
655           change_staff (gui->movement, pi.staff_number, pi.the_staff);
656           gui->movement->currentmeasurenum = pi.measure_number;
657           gui->movement->currentmeasure = pi.the_measure;
658           gui->movement->currentobject = pi.the_obj;
659           gui->movement->cursor_x = pi.cursor_x;
660           gui->movement->cursor_appending = (gui->movement->cursor_x == (gint) (g_list_length ((objnode *) ((DenemoMeasure*)gui->movement->currentmeasure->data)->objects)));
661 
662           set_cursor_y_from_click (gui, event->y);
663           if (lh_down & !selecting)
664             {
665               if (gui->movement->markstaffnum)
666                 set_point (NULL, NULL);
667               else
668                 set_mark (NULL, NULL);
669               selecting = TRUE;
670             }
671           calcmarkboundaries (gui->movement);
672           if (event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK))
673             perform_command (event->state, GESTURE_MOVE, event->state & GDK_BUTTON1_MASK);
674 
675           /* redraw to show new cursor position  */
676           draw_score_area();
677         }
678     }
679 
680   if (Denemo.project->midi_destination & MIDICONDUCT)
681     {
682       advance_time (0.01);
683       return TRUE;
684     }
685   return TRUE;
686 }
687 
688 
689 /**
690  * Mouse button press callback
691  *
692  */
693 gint
scorearea_button_press(GtkWidget * widget,GdkEventButton * event)694 scorearea_button_press (GtkWidget * widget, GdkEventButton * event)
695 {
696   DenemoProject *gui = Denemo.project;
697   if (gui == NULL || gui->movement == NULL)
698     return FALSE;
699   gboolean left = (event->button != 3);
700   //if the cursor is at a system separator start dragging it
701   gint allocated_height = get_widget_height (Denemo.scorearea);
702   gint line_height = allocated_height * gui->movement->system_height;
703   gint line_num = ((int) event->y) / line_height;
704   last_event_x = event->x_root;
705   last_event_y = event->y_root;
706   //g_debug("diff %d\n", line_height - ((int)event->y)%line_height);
707 
708   if (dragging_separator == FALSE)
709     if (line_height - ((int) event->y - 8) % line_height < 12)
710       {
711         if (Denemo.prefs.learning)
712           MouseGestureShow(_("Dragging line separator."), _("This will allow the display to show more music, split into lines. The typeset score is not affected."),
713             MouseGesture);
714         dragging_separator = TRUE;
715         return TRUE;
716       }
717   dragging_separator = FALSE;
718 
719   if(gui->movement->recording)
720     {
721      //g_debug("audio %f %f\n", event->x, event->y);
722 
723 
724       if(event->y < 20*gui->movement->zoom /* see draw.c for this value, the note onsets are drawn in the top 20 pixels */)
725         {
726             if (event->type==GDK_2BUTTON_PRESS)
727                 {
728                     gui->movement->marked_onset_position = (gint)event->x/gui->movement->zoom;
729                     if(gui->movement->marked_onset_position < (gui->leftmargin+35) + SPACE_FOR_TIME + gui->movement->maxkeywidth) {
730                          if (Denemo.prefs.learning)
731                             MouseGestureShow(_("Double Click Note Onset"), _("This represents detected note onsets which occur\nbefore the start of the score.\nIf they are just noise,\nor if you are working on just a portion of the audio that is ok.\nOtherwise drag with left mouse button to synchronize\nwith the start of the score."),
732           MouseGesture);
733 
734                     }
735                     gtk_widget_queue_draw(Denemo.scorearea);
736                     return TRUE;
737                 } else
738                 {
739                     gdk_window_set_cursor (gtk_widget_get_window (Denemo.window), gdk_cursor_new (left?GDK_SB_H_DOUBLE_ARROW:GDK_X_CURSOR));
740                     left? (dragging_audio = TRUE) : (dragging_tempo = TRUE);
741                      if (Denemo.prefs.learning)
742                      left? MouseGestureShow(_("Left Drag Note Onset"), _("This moves the audio to synchronize the start with the score.\nYou can use the Leadin button for this too."),
743           MouseGesture) :
744                         MouseGestureShow(_("Right Drag Note Onset"), _("This changes the tempo of the score.\nUse this to synchronize the beat after setting the start"),
745           MouseGesture);
746                     gtk_widget_queue_draw(Denemo.scorearea);
747                     return TRUE;
748                 }
749         }
750 
751     }
752 
753 
754   //g_debug("before %f %f\n", event->x, event->y);
755   transform_coords (&event->x, &event->y);
756   //g_debug("after %f %f\n", event->x, event->y);
757 
758 
759   gtk_widget_grab_focus (widget);
760   gint key = gui->movement->maxkeywidth;
761   gint cmajor = key ? 0 : 5;    //allow some area for keysig in C-major
762 
763   if (gui->lefts[line_num] == 0)
764     return TRUE;                //On an empty system at the bottom where there is not enough room to draw another staff.
765 
766   struct placement_info pi;
767   pi.the_staff = NULL;
768   if (event->y < 0)
769     get_placement_from_coordinates (&pi, event->x, 0, gui->lefts[line_num], gui->rights[line_num], gui->scales[line_num]);
770   else
771     get_placement_from_coordinates (&pi, event->x, event->y, gui->lefts[line_num], gui->rights[line_num], gui->scales[line_num]);
772   if (pi.the_staff == NULL)
773     return TRUE;                //could not place the cursor
774   change_staff (gui->movement, pi.staff_number, pi.the_staff);
775 
776 
777   if (left && (gui->movement->leftmeasurenum > 1) && (event->x < (gui->leftmargin+35) + SPACE_FOR_TIME + key) && (event->x > gui->leftmargin))
778     {
779       if (Denemo.prefs.learning)
780         MouseGestureShow(_("Press Left."), _("This moved the cursor to the measure offscreen left. The display is shifted to place that measure on screen."),
781           MouseGesture);
782       set_currentmeasurenum (gui, gui->movement->leftmeasurenum - 1);
783       write_status (gui);
784       draw_score_area();
785       return TRUE;
786     }
787   else if (pi.nextmeasure)
788     {
789       if ((pi.at_edge) && ((pi.the_obj==NULL) || ((pi.the_obj->next == NULL) && (pi.offend))))//crashed here with the_obj 0x131 !!!
790         {
791           if ((gui->movement->currentmeasurenum != gui->movement->rightmeasurenum) &&
792                 (!set_currentmeasurenum (gui, gui->movement->rightmeasurenum + 1)))
793               set_currentmeasurenum (gui, gui->movement->rightmeasurenum);
794           else if ((gui->movement->cursor_appending) &&
795                 (!set_currentmeasurenum (gui, gui->movement->rightmeasurenum + 1)))
796               set_currentmeasurenum (gui, gui->movement->rightmeasurenum);
797 
798 
799 
800 
801           if (gui->movement->currentmeasurenum != gui->movement->rightmeasurenum) {
802             if (Denemo.prefs.learning)
803               MouseGestureShow(_("Press Left."), _("This moved the cursor to the measure off-screen right. The display is shifted to move the cursor to the middle."),
804                 MouseGesture);
805           write_status (gui);
806           return TRUE;
807         }
808         }
809     }
810 
811 
812   if (pi.the_measure != NULL)
813     {                           /*don't place cursor in a place that is not there */
814       //gui->movement->currentstaffnum = pi.staff_number;
815       //gui->movement->currentstaff = pi.the_staff;
816       gui->movement->currentmeasurenum = pi.measure_number;
817       gui->movement->currentmeasure = pi.the_measure;
818       gui->movement->currentobject = pi.the_obj;
819       gui->movement->cursor_x = pi.cursor_x;
820       gui->movement->cursor_appending = (gui->movement->cursor_x == (gint) (g_list_length ((objnode *) ((DenemoMeasure*)gui->movement->currentmeasure->data)->objects)));
821       set_cursor_y_from_click (gui, event->y);
822       if (event->type==GDK_2BUTTON_PRESS)
823                 {
824                     if(gui->movement->recording &&  !g_strcmp0 (((DenemoStaff *) gui->movement->currentstaff->data)->denemo_name->str, DENEMO_CLICK_TRACK_NAME))
825                         {
826                             gui->movement->marked_onset_position = (gint)event->x/gui->movement->zoom;
827                             if (Denemo.prefs.learning)
828                                 MouseGestureShow(_("Double Click on Click Track"), _("This will mark the MIDI note onset."), MouseGesture);
829                             return TRUE;
830 
831                         }
832 
833                     else
834                         {
835                           if (Denemo.prefs.learning)
836                             MouseGestureShow(_("Double Click."), _("This gives information about the object at the cursor. Click on a notehead for information about a note in a chord."),
837                               MouseGesture);
838                                     display_current_object();
839                                     return TRUE;
840                         }
841                 }
842             else
843                 {
844                   if (Denemo.prefs.learning)
845                     MouseGestureShow(_("Press Left."), _("This moved the cursor to the object position clicked. The cursor height becomes the clicked point."),
846                       MouseGesture);
847                             write_status (gui);
848                }
849             }
850 
851   gint offset = (gint) get_click_height (gui, event->y);
852 
853    if ((((DenemoStaff *) gui->movement->currentstaff->data)->voicecontrol != DENEMO_PRIMARY)
854         && (gui->movement->leftmeasurenum == 1) && (event->x > gui->leftmargin)
855         && ((event->x < (gui->leftmargin+35) + SPACE_FOR_TIME + key)))
856     {
857           infodialog(_("The clef shown here affects the display only (as this voice is displayed on the staff above)."
858           " You can change the display clef using the clef menu."
859           "\nWarning! you will get confused if you set the key signature or time signature of a voice different "
860           "to the staff it is typeset on. Run the Staff/Voice property editor to adjust any inconsistencies."));
861 
862     }
863   if ((((DenemoStaff *) gui->movement->currentstaff->data)->voicecontrol == DENEMO_PRIMARY) && (gui->movement->leftmeasurenum == 1) && (event->x > gui->leftmargin))
864     {
865       if (event->x < (gui->leftmargin+35) - cmajor)
866         {
867         if (offset<-10)
868             {
869                 if (Denemo.prefs.learning)
870                 MouseGestureShow(_("Left on Staff name."), _("This pops up the built-in staff properties. For other properties of the current staff see the staff menu or the tools icon before the clef."),
871                   MouseGesture);
872                 staff_properties_change_cb (NULL, NULL);
873             }
874         else
875             {
876               if (Denemo.prefs.learning)
877                 MouseGestureShow(_("Left on initial Clef."), _("This pops up the initial clef menu."),
878                   MouseGesture);
879               popup_menu ("/InitialClefEditPopup");
880             }
881           return TRUE;
882         }
883       else if (event->x < (gui->leftmargin+35) + key + cmajor)
884         {
885           if (left)
886             {
887               if (offset > 0 && (offset < STAFF_HEIGHT / 2))
888                 {
889                   if (Denemo.prefs.learning)
890                     MouseGestureShow(_("Left Click on blue."), _("This adds one sharp."),
891                       MouseGesture);
892                 if ((gui->movement->currentmeasure->next==NULL)  || confirm (_("Initial Key Signature Change"), _("Sharpen Keysignature?")))
893                   call_out_to_guile ("(d-SharpenInitialKeysigs)");
894                 }
895               else if (offset > 0 && (offset < STAFF_HEIGHT))
896                 {
897                   if (Denemo.prefs.learning)
898                     MouseGestureShow(_("Left Click on red."), _("This adds one flat."),
899                       MouseGesture);
900                   if ((gui->movement->currentmeasure->next==NULL) || confirm (_("Initial Key Signature Change"), _("Flatten Keysignature?")))
901                     call_out_to_guile ("(d-FlattenInitialKeysigs)");
902                 }
903             }
904           else
905             {
906               if (Denemo.prefs.learning)
907                 MouseGestureShow(_("Right Click on key."), _("This pops up the key signature menu."),
908                     MouseGesture);
909               popup_menu ("/InitialKeyEditPopup");
910             }
911           return TRUE;
912         }
913       else if (event->x < (gui->leftmargin+35) + SPACE_FOR_TIME + key)
914         {
915           if (Denemo.prefs.learning)
916             MouseGestureShow(_("Click on Time."), _("This pops up the time signature menu."),
917                     MouseGesture);
918           popup_menu ("/InitialTimeEditPopup");
919           return TRUE;
920         }
921     }
922 
923   if (event->x < gui->leftmargin)
924     {
925        if (gui->braces)
926         {
927                 gint width = BRACEWIDTH * g_list_length (gui->braces);
928                 //gint count = (gui->leftmargin - event->x)/BRACEWIDTH;
929                 if ((gui->leftmargin - event->x) < width)
930                 {
931                     gint count = 1 + (width - gui->leftmargin + event->x)/BRACEWIDTH;
932 
933 
934                     if ((count>0) && (count <= g_list_length (gui->braces)))
935                         {
936                             DenemoBrace *brace = (DenemoBrace*)g_list_nth_data (gui->braces, count-1);
937                             gint choice = choose_option (_("Editing Staff Groups (Braces)"), _("Edit Start Brace"), _("Edit End Brace"));
938                             gint staffnum = choice?brace->startstaff:brace->endstaff;
939                             //g_print ("Count is %d for start at %d\n", count, staffnum);
940                             GtkWidget *menuitem = gtk_ui_manager_get_widget (Denemo.ui_manager, "/ObjectMenu/StaffMenu/StaffGroupings");
941                             goto_movement_staff_obj (NULL, -1, staffnum, 1, 0, 0);
942                             if (menuitem)
943                                 if (choice)
944                                     gtk_menu_popup (GTK_MENU (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menuitem))), NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
945                                 else
946                                 {
947                                     if (staff_directive_get_tag ("BraceEnd"))
948                                         call_out_to_guile ("(d-BraceEnd)");
949                                     else
950                                         warningdialog (_( "This staff grouping has no End Brace so it finishes on the lowest staff. Use the Staffs/Voices->Staff Groupings menu to place an End Brace on the desired staff"));
951                                 }
952                             //note the popup returns as soon as the menu is popped up, so we can't go back to the original position.
953 
954                         }
955 
956                     return TRUE;
957                 }
958 
959         }
960 
961       if (pi.staff_number == gui->movement->currentstaffnum)
962         {
963           gint offset = (gint) get_click_height (gui, event->y);
964           if (offset < STAFF_HEIGHT / 2)
965             {
966               if (((DenemoStaff *) gui->movement->currentstaff->data)->staff_directives, 1)
967                 {
968                   if (Denemo.prefs.learning)
969                     MouseGestureShow(_("Click on Staff Directives."), _("This pops up the staff directives menu for editing"),
970                       MouseGesture);
971                   edit_staff_properties ();//gtk_menu_popup (((DenemoStaff *) gui->movement->currentstaff->data)->staffmenu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
972                 }
973               return TRUE;
974             }
975           else if (((DenemoStaff *) gui->movement->currentstaff->data)->voice_directives, 1)
976             {
977               if (Denemo.prefs.learning)
978                 MouseGestureShow(_("Click on Voice Directives."), _("This pops up the voice directives menu for editing"),
979                     MouseGesture);
980               edit_voice_properties ();//gtk_menu_popup (((DenemoStaff *) gui->movement->currentstaff->data)->voicemenu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
981               return TRUE;
982             }
983         }
984     }
985 
986   if (left)
987     {
988       if (!(GDK_SHIFT_MASK & event->state))
989         gui->movement->markstaffnum = 0;
990       lh_down = TRUE;
991     }
992   else
993     {
994       if (gui->movement->cursor_appending && (event->state==0))
995         {
996           if (Denemo.prefs.learning)
997            {
998 
999                     MouseGestureShow(_("Right Click Appending."), _("This pops up the append menu"),
1000                         MouseGesture);
1001 
1002            }
1003           popup_menu ("/NoteAppendPopup");
1004           return TRUE;
1005         }
1006 
1007         if ((GDK_CONTROL_MASK & event->state) == GDK_CONTROL_MASK) {
1008             if (Denemo.prefs.learning)
1009                     MouseGestureShow(_("Control-Right Click."), _("This pops up menu for inserting barlines and many other sorts of objects"),
1010                         MouseGesture);
1011             gtk_menu_popup (GTK_MENU (gtk_widget_get_parent(gtk_ui_manager_get_widget (Denemo.ui_manager, "/ObjectMenu/Directives/Markings"))),
1012                             NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
1013             return TRUE;
1014         }
1015       if ((GDK_SHIFT_MASK & event->state) == GDK_SHIFT_MASK) {
1016             if (Denemo.prefs.learning)
1017                     MouseGestureShow(_("Shift-Right Click."), _("This allows editing the directives/attributes of the object at the cursor"),
1018                         MouseGesture);
1019             call_out_to_guile ("(d-EditSimilar 'once)");
1020             return TRUE;
1021         }
1022     }
1023 
1024 
1025 
1026   if (left && (GDK_SHIFT_MASK & event->state) && (GDK_CONTROL_MASK & event->state))
1027   {
1028      // if current object is directive, start dragging its graphic, dragging_display=TRUE
1029 
1030       DenemoObject *obj;
1031       if (Denemo.prefs.learning)
1032                     MouseGestureShow(_("Control-Shift-Drag."), _("This allows dragging objects in the display.\nAll sorts of directives such as staccato dots, ornaments, repeat marks etc can be dragged if the display is too cluttered.\nThe typeset score is unaffected.\nClick on a notehead to drag things attached to the notehead,\nor off the noteheads for things attached to the whole chord."),
1033                         MouseGesture);
1034 
1035     last_directive = get_next_directive_at_cursor ();
1036     if(last_directive)
1037         {
1038             score_status (Denemo.project, TRUE);
1039             return TRUE;
1040         }
1041     infodialog (_("Control-Shift-Drag is used to tidy up the Denemo display. Useful if Denemo has created a clutter with your input music.\nIf you have several things attached to one object you can move them in turn by dragging them in turn.\nNotes, Slurs and Ties are fixed but most other things can be moved to make the input music clear. Does not affect the typeset score!\nNB! if you have dragged something to one side of a note you have to control-shift-click on the note itself to drag it back - it is where the cursor is that counts."));
1042     return TRUE;
1043  }
1044 
1045 
1046 
1047 
1048 
1049 
1050   set_cursor_for (event->state | (left ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK));
1051 
1052 
1053 
1054 
1055   //displayhelper(Denemo.project);
1056   draw_score(NULL);//this is needed to refresh cached values such as the prevailing time signature, before the command is invoked
1057 
1058   perform_command (event->state | (left ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK), GESTURE_PRESS, left);
1059 
1060   return TRUE;
1061 }
1062 
1063 
1064 /**
1065  * Mouse button release callback
1066  *
1067  */
1068 gint
scorearea_button_release(GtkWidget * widget,GdkEventButton * event)1069 scorearea_button_release (GtkWidget * widget, GdkEventButton * event)
1070 {
1071   DenemoProject *gui = Denemo.project;
1072   if (gui == NULL || gui->movement == NULL)
1073     return FALSE;
1074   last_directive = NULL;
1075   gboolean left = (event->button != 3);
1076   if(gui->movement->recording && (dragging_tempo || dragging_audio))
1077     {
1078             dragging_tempo = dragging_audio = FALSE;
1079             gdk_window_set_cursor (gtk_widget_get_window (Denemo.window), gdk_cursor_new (GDK_LEFT_PTR));       //FIXME? does this take time/hog memory
1080             return TRUE;
1081     }
1082   if (dragging_separator)
1083     {
1084       if (Denemo.prefs.learning)
1085         MouseGestureShow(_("Dragged line separator."), _("This allows the display to show more music, split into lines. The typeset score is not affected."),
1086           MouseGesture);
1087       dragging_separator = FALSE;
1088       return TRUE;
1089     }
1090 
1091   //g_signal_handlers_block_by_func(Denemo.scorearea, G_CALLBACK (scorearea_motion_notify), gui);
1092   if (left)
1093     lh_down = FALSE;
1094   selecting = FALSE;
1095   set_cursor_for (event->state & DENEMO_MODIFIER_MASK);
1096   transform_coords (&event->x, &event->y);
1097   set_cursor_y_from_click (gui, event->y);
1098   gtk_widget_queue_draw(Denemo.scorearea);
1099   perform_command (event->state, GESTURE_RELEASE, left);
1100 
1101   return TRUE;
1102 }
1103 
1104 gint
scorearea_scroll_event(GtkWidget * widget,GdkEventScroll * event)1105 scorearea_scroll_event (GtkWidget * widget, GdkEventScroll * event)
1106 {
1107   DenemoProject *gui = Denemo.project;
1108   if (gui == NULL || gui->movement == NULL)
1109     return FALSE;
1110   switch (event->direction)
1111     {
1112       DenemoScriptParam param;
1113     case GDK_SCROLL_UP:
1114       if (event->state & GDK_CONTROL_MASK)
1115         {
1116           if (Denemo.prefs.learning) {
1117             gint command_idx = lookup_command_from_name(Denemo.map, "ZoomIn");
1118             KeyStrokeShow (_("Ctrl + Mouse Wheel Up"), command_idx, TRUE);
1119           }
1120           Denemo.project->movement->zoom *= 1.1;
1121           scorearea_configure_event (Denemo.scorearea, NULL);
1122         }
1123       else if (event->state & GDK_SHIFT_MASK)
1124         {
1125           gint command_idx = lookup_command_from_name(Denemo.map, "MoveToMeasureLeft");
1126           if (Denemo.prefs.learning) {
1127             KeyStrokeShow (_("Shift + Mouse Wheel Up"), command_idx, TRUE);
1128           }
1129           execute_callback_from_idx(Denemo.map, command_idx);//scroll_left ();
1130 
1131         }
1132       else
1133         {
1134           if (Denemo.prefs.learning) {
1135             gint command_idx = lookup_command_from_name(Denemo.map, "MoveToStaffUp");
1136             KeyStrokeShow (_("Unshifted + Mouse Wheel Up"), command_idx, TRUE);
1137           }
1138           movetostaffup (NULL, &param);
1139           if (!param.status) {
1140             DenemoStaff *thestaff = (DenemoStaff*)(Denemo.project->movement->currentstaff->data);
1141             if(thestaff->space_above < MAXEXTRASPACE)
1142               {
1143                 thestaff->space_above++;
1144                 g_debug ("Increasing the height of the top staff");
1145               }
1146           }
1147         }
1148       break;
1149     case GDK_SCROLL_DOWN:
1150       if (event->state & GDK_CONTROL_MASK)
1151         {
1152           if (Denemo.prefs.learning) {
1153             gint command_idx = lookup_command_from_name(Denemo.map, "ZoomOut");
1154             KeyStrokeShow (_("Ctrl + Mouse Wheel Down"), command_idx, TRUE);
1155           }
1156           Denemo.project->movement->zoom /= 1.1;
1157           if (Denemo.project->movement->zoom < 0.01)
1158             Denemo.project->movement->zoom = 0.01;
1159           scorearea_configure_event (Denemo.scorearea, NULL);
1160           //displayhelper(gui);
1161         }
1162       else if (event->state & GDK_SHIFT_MASK)
1163         {
1164           gint command_idx = lookup_command_from_name(Denemo.map, "MoveToMeasureRight");
1165           if (Denemo.prefs.learning) {
1166             KeyStrokeShow (_("Shift + Mouse Wheel Down"), command_idx, TRUE);
1167           }
1168           execute_callback_from_idx(Denemo.map, command_idx);//scroll_right ();
1169         }
1170       else
1171         {
1172           if (Denemo.prefs.learning) {
1173             gint command_idx = lookup_command_from_name(Denemo.map, "MoveToStaffDown");
1174             KeyStrokeShow (_("Unshifted + Mouse Wheel Down"), command_idx, TRUE);
1175           }
1176           movetostaffdown (NULL, &param);
1177           if (!param.status) {
1178             warningmessage ("This is the bottom staff");
1179            // DenemoStaff *thestaff = (DenemoStaff*)(Denemo.project->movement->currentstaff->data);
1180            // thestaff->space_below++; //This doesn't help, because the viewport does not change.
1181            // warningmessage ("Increasing the space below the bottom staff");
1182            //move_viewport_down(Denemo.project);
1183           }
1184         }
1185       break;
1186     case GDK_SCROLL_LEFT:
1187       movetomeasureleft (NULL, &param);
1188       if (!param.status)
1189         warningmessage ("This is the first measure");
1190       break;
1191     case GDK_SCROLL_RIGHT:
1192       movetomeasureright (NULL, &param);
1193       if (!param.status)
1194         warningmessage ("This is the last measure");
1195       break;
1196 
1197     default:
1198       break;
1199     }
1200   displayhelper (Denemo.project);
1201   return FALSE;
1202 }
1203