/* mousing.cpp * callback functions for handling mouse clicks, drags, etc. * * for Denemo, a gtk+ frontend to GNU Lilypond * (c) 2000-2005 Matthew Hiller */ #include "commandfuncs.h" #include #include "staffops.h" #include "utils.h" /** * Get the mid_c_offset of an object or click from its height relative * to the top of the staff. */ gint offset_from_height (gdouble height, enum clefs clef) { /* Offset from the top of the staff, in half-tones. */ gint half_tone_offset = (gint) (height / HALF_LINE_SPACE + 0.5); #define R(x) return x - half_tone_offset switch (clef) { case DENEMO_TREBLE_CLEF: R (10); break; /* This break and the ones following are probably gratuitous. */ case DENEMO_BASS_CLEF: R (-2); break; case DENEMO_ALTO_CLEF: R (4); break; case DENEMO_G_8_CLEF: R (3); break; case DENEMO_TENOR_CLEF: R (2); break; case DENEMO_SOPRANO_CLEF: R (8); break; } #undef R return 0; } /** * Set the cursors y position from a mouse click * */ void set_cursor_y_from_click (DenemoGUI * gui, gdouble y) { /* Click height relative to the top of the staff. */ float click_height; gint staffs_from_top; gtk_widget_draw (gui->scorearea, NULL); staffs_from_top = gui->si->currentstaffnum - gui->si->top_staff; click_height = y - (gui->si->staffspace * staffs_from_top + gui->si->staffspace / 4); gui->si->cursor_y = offset_from_height (click_height, (enum clefs) gui->si->cursorclef); gui->si->staffletter_y = offsettonumber (gui->si->cursor_y); } struct placement_info { gint staff_number, measure_number, cursor_x; staffnode *the_staff; measurenode *the_measure; objnode *the_obj; }; /** * Gets the position from the clicked position * */ void get_placement_from_coordinates (struct placement_info *pi, gdouble x, gdouble y, DenemoScore * si) { GList *mwidthiterator = g_list_nth (si->measurewidths, si->leftmeasurenum - 1); objnode *obj_iterator; gint x_to_explain = (gint) (x); /* Calculate pi->staff_number, but don't go past si->bottom_staff. (It can happen if there are too few staffs to fill the drawing area from top to bottom.) */ pi->staff_number = MIN ((si->top_staff + ((gint) y) / si->staffspace), si->bottom_staff); pi->measure_number = si->leftmeasurenum; x_to_explain -= (KEY_MARGIN + si->maxkeywidth + SPACE_FOR_TIME); while (x_to_explain > GPOINTER_TO_INT (mwidthiterator->data) && pi->measure_number < si->rightmeasurenum) { x_to_explain -= (GPOINTER_TO_INT (mwidthiterator->data) + SPACE_FOR_BARLINE); mwidthiterator = mwidthiterator->next; pi->measure_number++; } pi->the_staff = g_list_nth (si->thescore, pi->staff_number - 1); pi->the_measure = nth_measure_node_in_staff (pi->the_staff, pi->measure_number - 1); if (pi->the_measure != NULL){ /*check to make sure user did not click on empty space*/ obj_iterator = (objnode *) pi->the_measure->data; pi->cursor_x = 0; pi->the_obj = NULL; if (obj_iterator) { DenemoObject *current, *next; for (; obj_iterator->next; obj_iterator = obj_iterator->next, pi->cursor_x++) { current = (DenemoObject *) obj_iterator->data; next = (DenemoObject *) obj_iterator->next->data; /* This comparison neatly takes care of two possibilities: 1) That the click was to the left of current, or 2) That the click was between current and next, but closer to current. Do the math - it really does work out. */ if (x_to_explain - (current->x + current->minpixelsalloted) < next->x - x_to_explain) { pi->the_obj = obj_iterator; break; } } if (!obj_iterator->next) /* That is, we exited the loop normally, not through a break. */ { DenemoObject *current = (DenemoObject *) obj_iterator->data; pi->the_obj = obj_iterator; /* The below makes clicking to get the object at the end of a measure (instead of appending after it) require precision. This may be bad; tweak me if necessary. */ if (x_to_explain > current->x + current->minpixelsalloted) pi->cursor_x++; } } } } /** * Mouse button release callback * */ gint scorearea_button_release (GtkWidget * widget, GdkEventButton * event, gpointer data) { struct placement_info pi; DenemoGUI *gui = (DenemoGUI *) data; if (event->y < 0) get_placement_from_coordinates (&pi, event->x, 0, gui->si); else get_placement_from_coordinates (&pi, event->x, event->y, gui->si); if (pi.the_measure != NULL){ /*don't place cursor in a place that is not there*/ gui->si->currentstaffnum = pi.staff_number; gui->si->currentstaff = pi.the_staff; gui->si->currentmeasurenum = pi.measure_number; gui->si->currentmeasure = pi.the_measure; gui->si->currentobject = pi.the_obj; gui->si->cursor_x = pi.cursor_x; gui->si->cursor_appending = (gui->si->cursor_x == (gint) (g_list_length ((objnode *) gui->si->currentmeasure->data))); /* Quickly redraw to reset si->cursorclef. */ gtk_widget_draw (gui->scorearea, NULL); set_cursor_y_from_click (gui, event->y); gtk_widget_draw (gui->scorearea, NULL); } return TRUE; }