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, ¶m);
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, ¶m);
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, ¶m);
1188 if (!param.status)
1189 warningmessage ("This is the first measure");
1190 break;
1191 case GDK_SCROLL_RIGHT:
1192 movetomeasureright (NULL, ¶m);
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