bool
FruityPerfInput::on_button_press_event (GdkEventButton * ev, perfroll & roll)
{
    bool result = false;
    perform & p = roll.perf();
    roll.grab_focus();
    int & dropseq = roll.m_drop_sequence;       /* reference needed         */
    sequence * seq = p.get_sequence(dropseq);
    if (p.is_active(dropseq))
    {
        seq->unselect_triggers();
        roll.draw_all();
    }
    else
    {
        return false;
    }
    roll.m_drop_x = int(ev->x);
    roll.m_drop_y = int(ev->y);
    m_current_x = int(ev->x);
    m_current_y = int(ev->y);
    roll.convert_xy                             /* side-effects             */
    (
        roll.m_drop_x, roll.m_drop_y, roll.m_drop_tick, dropseq
    );
    if (SEQ64_CLICK_LEFT(ev->button))
    {
        result = on_left_button_pressed(ev, roll);
    }
    else if (SEQ64_CLICK_RIGHT(ev->button))
    {
        result = on_right_button_pressed(ev, roll);
    }
    else if (SEQ64_CLICK_MIDDLE(ev->button))   /* left-ctrl???, middle    */
    {
        /*
         * Implements some Stazed fixes now.
         */

        if (p.is_active(dropseq))
        {
            midipulse droptick = roll.m_drop_tick;
            droptick -= droptick % roll.m_snap; /* stazed fix: grid snap    */
            bool state = seq->get_trigger_state(droptick);
            if (state)                          /* trigger click, split it  */
            {
                roll.split_trigger(dropseq, droptick);
                result = true;
            }
            else                                /* track click, paste trig  */
            {
                p.push_trigger_undo(dropseq);
                seq->paste_trigger(droptick);
            }
        }
    }
    update_mouse_pointer(roll);
    return result;
}
Exemple #2
0
bool
perfnames::on_button_press_event (GdkEventButton * ev)
{
    int y = int(ev->y);
    int seqnum = convert_y(y);
    current_seq(seqnum);
    if (SEQ64_CLICK_LEFT(ev->button))
    {
        if (perf().is_active(seqnum))
        {
            guint modifiers;        /* for filtering out caps/num lock etc. */
            modifiers = gtk_accelerator_get_default_mod_mask();
            if ((ev->state & modifiers) == SEQ64_SHIFT_MASK)
            {
                /*
                 * \new ca 2016-03-15
                 *      If the Shift key is pressed, mute all other sequences.
                 *      Inactive sequences are skipped.
                 */

                for (int s = 0; s < m_sequence_max; ++s)
                {
                    if (s != seqnum)
                    {
                        sequence * seq = perf().get_sequence(s);
                        if (not_nullptr(seq))
                        {
                            bool muted = seq->get_song_mute();
                            seq->set_song_mute(! muted);
                        }
                    }
                }
            }
            else
            {
                sequence * seq = perf().get_sequence(seqnum);
                bool muted = seq->get_song_mute();
                seq->set_song_mute(! muted);
            }
            enqueue_draw();
        }
    }
    return true;
}
bool
Seq24PerfInput::on_button_release_event (GdkEventButton * ev, perfroll & roll)
{
    bool result = false;
    if (SEQ64_CLICK_LEFT(ev->button))
    {
        if (is_adding())
            set_adding_pressed(false);
    }
    else if (SEQ64_CLICK_RIGHT(ev->button))
    {
        /*
         * Minor new feature.  If the Super (Mod4, Windows) key is
         * pressed when release, keep the adding-state in force.  One
         * can then use the unadorned left-click key to add material.  Right
         * click to reset the adding mode.  This feature is enabled only
         * if allowed by Options / Mouse  (but is true by default).
         * See the same code in seq24seqroll.cpp.
         */

        bool addmode_exit = ! rc().allow_mod4_mode();
        if (! addmode_exit)
            addmode_exit = ! is_super_key(ev);              /* Mod4 held? */

        if (addmode_exit)
        {
            set_adding_pressed(false);
            activate_adding(false, roll);
        }
    }

    perform & p = roll.perf();
    roll.m_moving = roll.m_growing = false;
    set_adding_pressed(false);
    m_effective_tick = 0;
    if (p.is_active(roll.m_drop_sequence))
    {
        roll.draw_all();
    }
    return result;
}
bool
FruitySeqEventInput::on_button_release_event
(
    GdkEventButton * ev,
    seqevent & seqev
)
{
    bool result = false;
    midipulse tick_s;
    midipulse tick_f;
    seqev.grab_focus();
    seqev.m_current_x = int(ev->x) + seqev.m_scroll_offset_x;;
    if (seqev.m_moving || m_is_drag_pasting)
        seqev.snap_x(seqev.m_current_x);

    int delta_x = seqev.m_current_x - seqev.m_drop_x;
    midipulse delta_tick;
    if (SEQ64_CLICK_LEFT(ev->button))
    {
        int current_x = seqev.m_current_x;
        midipulse t_s, t_f;
        seqev.snap_x(current_x);
        seqev.convert_x(current_x, t_s);
        t_f = t_s + (seqev.m_zoom);                 /* shift back a few ticks */
        if (t_s < 0)
            t_s = 0;

        /*
         * Use the ctrl-left click button up for select/drag copy/paste;
         * use the left click button up for ending a move of selected notes.
         */

        if (m_is_drag_pasting)
        {
            m_is_drag_pasting = false;
            m_is_drag_pasting_start = false;
            seqev.m_paste = false; /* convert deltas into screen coordinates */
            seqev.m_seq.paste_selected(t_s, 0);
            result = true;
        }

        /* ctrl-left click but without movement - select a note */

        if (m_is_drag_pasting_start)
        {
            m_is_drag_pasting_start = false;

            /*
             * If a ctrl-left click without movement and if the note under
             * cursor is selected, and ctrl is held and button-down,
             * just select one.
             */

            if                                  /* deselect the event? */
            (
                is_ctrl_key(ev) &&
                ! m_justselected_one &&
                seqev.m_seq.select_events
                (
                    t_s, t_f, seqev.m_status, seqev.m_cc, sequence::e_is_selected
                ) 
            )
            {
                (void) seqev.m_seq.select_events
                (
                    t_s, t_f, seqev.m_status, seqev.m_cc, sequence::e_deselect
                );
            }
        }
        m_justselected_one = false;         /* clear flag on left button up */
        if (seqev.m_moving)
        {
            delta_x -= seqev.m_move_snap_offset_x; /* adjust for snap         */
            seqev.convert_x(delta_x, delta_tick);  /* deltas to screen coords */
            seqev.m_seq.move_selected_notes(delta_tick, 0);
            result = true;
        }
    }

    /*
     * Yet another stazed fix.  :-)
     */

    bool right = SEQ64_CLICK_RIGHT(ev->button);
    if (! right)
        right = is_ctrl_key(ev) && SEQ64_CLICK_LEFT(ev->button);

    if (right)
    {
        if (seqev.m_selecting)
        {
            int x, w;
            seqev.x_to_w(seqev.m_drop_x, seqev.m_current_x, x, w);
            seqev.convert_x(x, tick_s);
            seqev.convert_x(x + w, tick_f);
            (void) seqev.m_seq.select_events
            (
                tick_s, tick_f,
                seqev.m_status, seqev.m_cc, sequence::e_toggle_selection
            );

#ifdef USE_STAZED_SELECTION_EXTENSIONS

            /*
             * Stazed fix
             */

            if (event::is_strict_note_msg(seqev.m_status))
                seqev.m_seq.select_linked(tick_s, tick_f, seqev.m_status);
#endif

            /*
             * To update the select or unselect of notes by this action.
             * Not sure this makes sense, though.  How does selection dirty
             * anything?
             */

            seqev.m_seq.set_dirty();
        }
    }
    seqev.m_selecting = false;          /* turn it all off */
    seqev.m_moving = false;
    seqev.m_growing = false;
    seqev.m_moving_init = false;
    seqev.m_painting = false;
    seqev.m_seq.unpaint_all();
    seqev.update_pixmap();              /* if they clicked, something changed */
    seqev.draw_pixmap_on_window();
    update_mouse_pointer(seqev);
    return result;                      // true;
}
bool
FruitySeqEventInput::on_button_press_event
(
    GdkEventButton * ev,
    seqevent & seqev
)
{
    bool result = false;
    midipulse tick_s, tick_w;
    seqev.grab_focus();                 // NEW: I think this would be helpful
    seqev.convert_x(c_eventevent_x, tick_w);
    seqev.m_drop_x = seqev.m_current_x = int(ev->x) + seqev.m_scroll_offset_x;
    seqev.m_old.x = seqev.m_old.y = seqev.m_old.width = seqev.m_old.height = 0;
    if (seqev.m_paste)
    {
        seqev.snap_x(seqev.m_current_x);
        seqev.convert_x(seqev.m_current_x, tick_s);
        seqev.m_paste = false;
        seqev.m_seq.paste_selected(tick_s, 0);          /* does undo/mod    */
        seqev.m_seq.set_dirty();                        /* a stazed fix     */
        result = true;
    }
    else
    {
        int x, w;
        midipulse tick_f;
        if (SEQ64_CLICK_LEFT(ev->button))               /* Note 1   */
        {
            seqev.convert_x(seqev.m_drop_x, tick_s); /* x,y into tick/note    */
            tick_f = tick_s + seqev.m_zoom;          /* shift back some ticks */
            tick_s -= tick_w;
            if (tick_s < 0)
                tick_s = 0;

            int eventcount = seqev.m_seq.select_events
            (
                tick_s, tick_f, seqev.m_status, seqev.m_cc,
                sequence::e_would_select
            );
            if (! is_ctrl_key(ev) && eventcount == 0)
            {
                seqev.m_painting = true;
                seqev.snap_x(seqev.m_drop_x);
                seqev.convert_x(seqev.m_drop_x, tick_s); /* x,y-->tick/note  */
                eventcount = seqev.m_seq.select_events
                (
                    tick_s, tick_f, seqev.m_status, seqev.m_cc,
                    sequence::e_would_select
                );
                if (eventcount == 0)
                {
                    seqev.m_seq.push_undo();            /* add to add_event? */
                    seqev.drop_event(tick_s);           /* m_seq.add_event() */
                    result = true;
                }
            }
            else                                        /* selecting         */
            {
                eventcount = seqev.m_seq.select_events
                (
                    tick_s, tick_f, seqev.m_status, seqev.m_cc,
                    sequence::e_is_selected
                );
                if (eventcount == 0)
                {
                    eventcount = seqev.m_seq.select_events
                    (
                        tick_s, tick_f, seqev.m_status, seqev.m_cc,
                        sequence::e_would_select
                    );
                    if (eventcount > 0)                 /* if clicking event */
                    {
                        if (! is_ctrl_key(ev))
                            seqev.m_seq.unselect();
                    }
                    else    /* clickempty space, unselect all if no Ctrl-Sh  */
                    {
                        if (! is_ctrl_shift_key(ev))
                            seqev.m_seq.unselect();
                    }

                    /* on direct click select only one event */

                    eventcount = seqev.m_seq.select_events
                    (
                        tick_s, tick_f, seqev.m_status,
                        seqev.m_cc, sequence::e_select_one
                    );

                    /*
                     * Stazed fix:
                     */

#ifdef USE_STAZED_SELECTION_EXTENSIONS
                    if (event::is_strict_note_msg(seqev.m_status))
                    {
                        seqev.m_seq.select_linked(tick_s, tick_f, seqev.m_status);
                        seqev.m_seq.set_dirty();
                    }
#endif

                    if (eventcount)
                        m_justselected_one = true;  /* stop deselect on release */

                    /* if nothing selected, start the selection box */

                    if (is_ctrl_key(ev) && eventcount == 0)
                        seqev.m_selecting = true;
                }
                eventcount = seqev.m_seq.select_events
                (
                    tick_s, tick_f, seqev.m_status, seqev.m_cc,
                    sequence::e_is_selected
                );
                if (eventcount > 0)         /* if event under cursor selected */
                {
                    if (! is_ctrl_key(ev))          /* grab/move note       */
                    {
                        seqev.m_moving_init = true;
                        int note;
                        seqev.m_seq.get_selected_box(tick_s, note, tick_f, note);
                        tick_f += tick_w;
                        seqev.convert_t(tick_s, x); /* convert to X,Y values */
                        seqev.convert_t(tick_f, w);
                        w -= x;                     /* w is coordinates now  */

                        /* set the m_selected rectangle for x,y,w,h */

                        seqev.m_selected.x = x;
                        seqev.m_selected.width = w;
                        seqev.m_selected.y = (c_eventarea_y-c_eventevent_y) / 2;
                        seqev.m_selected.height = c_eventevent_y;

                        /* save offset that we get from the snap above */

                        int adjusted_selected_x = seqev.m_selected.x;
                        seqev.snap_x(adjusted_selected_x);
                        seqev.m_move_snap_offset_x =
                            seqev.m_selected.x - adjusted_selected_x;

                        /* align selection for drawing */

                        seqev.snap_x(seqev.m_selected.x);
                        seqev.snap_x(seqev.m_current_x);
                        seqev.snap_x(seqev.m_drop_x);
                    }
                    else if   /* Ctrl-Left-click when stuff already selected */
                    (
                        is_ctrl_key(ev) &&
                        seqev.m_seq.select_events(tick_s, tick_f,
                           seqev. m_status, seqev.m_cc, sequence::e_is_selected)
                    )
                    {
                        m_is_drag_pasting_start = true;
                    }
                }
            }

        }
        if (SEQ64_CLICK_RIGHT(ev->button))
        {
            seqev.convert_x(seqev.m_drop_x, tick_s); /* x,y in to tick/note   */
            tick_f = tick_s + seqev.m_zoom;          /* shift back some ticks */
            tick_s -= (tick_w);
            if (tick_s < 0)
                tick_s = 0;

            /*
             * Stazed fix: don't allow individual deletion of Note On/Off
             * events.  Should we do the same for AfterTouch events?  No, they
             * are not linked to Note On or Note Off events.
             */

            if (event::is_strict_note_msg(seqev.m_status))
                return true;

            int eventcount = seqev.m_seq.select_events
            (
                tick_s, tick_f, seqev.m_status, seqev.m_cc,
                sequence::e_would_select
            );
            if (eventcount > 0)   /* erase event under cursor if there is one */
            {
                /* remove only note under cursor, leave selection intact */

                (void) seqev.m_seq.select_events
                (
                    tick_s, tick_f,
                    seqev.m_status, seqev.m_cc, sequence::e_remove_one
                );
                seqev.redraw();
                seqev.m_seq.set_dirty();                /* take note!       */
                result = true;
            }
            else                                        /* selecting        */
            {
                if (! is_ctrl_key(ev))
                    seqev.m_seq.unselect();             /* nothing selected   */

                seqev.m_selecting = true;               /* start select-box   */
            }
        }
    }
    seqev.update_pixmap();              /* if they clicked, something changed */
    seqev.draw_pixmap_on_window();
    update_mouse_pointer(seqev);
    return result;                      // true;
}
Exemple #6
0
bool
Seq24SeqEventInput::on_button_press_event
(
    GdkEventButton * ev,
    seqevent & seqev
)
{
    bool result = false;
    midipulse tick_s, tick_w;
    seqev.grab_focus();                 // NEW: I think this would be helpful
    seqev.convert_x(c_eventevent_x, tick_w);
    seqev.set_current_drop_x(int(ev->x + seqev.m_scroll_offset_x));
    seqev.m_old.x = seqev.m_old.y = seqev.m_old.width = seqev.m_old.height = 0;
    if (seqev.m_paste)
    {
        seqev.snap_x(seqev.m_current_x);
        seqev.convert_x(seqev.m_current_x, tick_s);
        seqev.m_paste = false;
        seqev.m_seq.paste_selected(tick_s, 0);      /* handles undo & modify    */
        result = true;
    }
    else
    {
        int x, w;
        midipulse tick_f;
        if (SEQ64_CLICK_LEFT(ev->button))
        {
            seqev.convert_x(seqev.m_drop_x, tick_s); /* x,y in to tick/note     */
            tick_f = tick_s + seqev.m_zoom;          /* shift back a few ticks  */
            tick_s -= tick_w;
            if (tick_s < 0)
                tick_s = 0;

            int eventcount = 0;
            if (m_adding)
            {
                seqev.m_painting = true;
                seqev.snap_x(seqev.m_drop_x);
                seqev.convert_x(seqev.m_drop_x, tick_s); /* x,y to tick/note    */
                eventcount = seqev.m_seq.select_events
                (
                    tick_s, tick_f, seqev.m_status, seqev.m_cc,
                    sequence::e_would_select
                );
                if (eventcount == 0)
                {
                    seqev.m_seq.push_undo();
                    seqev.drop_event(tick_s);           // CHECK!
                    result = true;
                }
            }
            else                                        /* selecting */
            {
                eventcount = seqev.m_seq.select_events
                (
                    tick_s, tick_f, seqev.m_status,
                    seqev.m_cc, sequence::e_is_selected
                );

#ifdef USE_STAZED_SELECTION_EXTENSIONS

                /*
                 * Stazed fix: if we didn't select anything (user clicked empty
                 * space), then unselect all notes, and start selecting.
                 */
                
                if (event::is_strict_note_msg(seqev.m_status))
                {
                    seqev.m_seq.select_linked(tick_s, tick_f, seqev.m_status);
                    seqev.m_seq.set_dirty();
                }
#endif

                if (eventcount == 0)
                {
                    if (! is_ctrl_key(ev))
                        seqev.m_seq.unselect();

                    eventcount = seqev.m_seq.select_events
                    (
                        tick_s, tick_f, seqev.m_status,
                        seqev.m_cc, sequence::e_select_one
                    );

                    /*
                     * If nothing selected (user clicked empty space),
                     * unselect all notes, and start selecting with a new
                     * selection box.
                     */

                    if (eventcount == 0)
                    {
                        seqev.m_selecting = true;
                    }
                    else
                    {
                        /**
                         * Needs update.
                         * seqev.m_seq.unselect();  ???????
                         */
                    }
                }
                eventcount = seqev.m_seq.select_events
                (
                    tick_s, tick_f, seqev.m_status,
                    seqev.m_cc, sequence::e_is_selected
                );
                if (eventcount > 0)             /* get box selections are in */
                {
                    seqev.m_moving_init = true;
                    int note;
                    seqev.m_seq.get_selected_box(tick_s, note, tick_f, note);
                    tick_f += tick_w;
                    seqev.convert_t(tick_s, x); /* convert box to X,Y values */
                    seqev.convert_t(tick_f, w);
                    w -= x;                     /* w is coordinate now       */

                    /* set the m_selected rectangle for x,y,w,h */

                    seqev.m_selected.x = x;
                    seqev.m_selected.width = w;
                    seqev.m_selected.y = (c_eventarea_y - c_eventevent_y) / 2;
                    seqev.m_selected.height = c_eventevent_y;

                    /* save offset that we get from the snap above */

                    int adjusted_selected_x = seqev.m_selected.x;
                    seqev.snap_x(adjusted_selected_x);
                    seqev.m_move_snap_offset_x =
                        seqev.m_selected.x - adjusted_selected_x;

                    /* align selection for drawing */

                    seqev.snap_x(seqev.m_selected.x);
                    seqev.snap_x(seqev.m_current_x);
                    seqev.snap_x(seqev.m_drop_x);
                }
            }
        }
        if (SEQ64_CLICK_RIGHT(ev->button))
            set_adding(true, seqev);
    }
    seqev.update_pixmap();              /* if they clicked, something changed */
    seqev.draw_pixmap_on_window();
    return result;                      // true;
}
Exemple #7
0
bool
Seq24SeqEventInput::on_button_release_event
(
    GdkEventButton * ev,
    seqevent & seqev
)
{
    bool result = false;
    midipulse tick_s;
    midipulse tick_f;
    seqev.grab_focus();
    seqev.m_current_x = int(ev->x) + seqev.m_scroll_offset_x;
    if (seqev.m_moving)
        seqev.snap_x(seqev.m_current_x);

    int delta_x = seqev.m_current_x - seqev.m_drop_x;
    midipulse delta_tick;
    if (SEQ64_CLICK_LEFT(ev->button))
    {
        if (seqev.m_selecting)
        {
            int x, w;
            seqev.x_to_w(seqev.m_drop_x, seqev.m_current_x, x, w);
            seqev.convert_x(x, tick_s);
            seqev.convert_x(x + w, tick_f);
            (void) seqev.m_seq.select_events
            (
                tick_s, tick_f, seqev.m_status, seqev.m_cc, sequence::e_select
            );

#ifdef USE_STAZED_SELECTION_EXTENSIONS

            /*
             * Stazed fix: if we did'nt select anything (user clicked empty
             * space), then unselect all notes, and start selecting.
             */
            
            if (event::is_strict_note_msg(seqev.m_status))
            {
                seqev.m_seq.select_linked(tick_s, tick_f, seqev.m_status);
            }
            seqev.m_seq.set_dirty();    // ??????????? Why not in brackets?
#endif

        }
        if (seqev.m_moving)
        {
            delta_x -= seqev.m_move_snap_offset_x;  /* adjust for snap       */
            seqev.convert_x(delta_x, delta_tick);   /* to screen coordinates */
            seqev.m_seq.move_selected_notes(delta_tick, 0);
            result = true;
        }
        set_adding(m_adding, seqev);
    }
    if (SEQ64_CLICK_RIGHT(ev->button))
    {
        set_adding(false, seqev);
    }
    seqev.m_selecting = false;                      /* turn off              */
    seqev.m_moving = false;
    seqev.m_growing = false;
    seqev.m_moving_init = false;
    seqev.m_painting = false;
    seqev.m_seq.unpaint_all();
    seqev.update_pixmap();                  /* if a click, something changed */
    seqev.draw_pixmap_on_window();
    return result;                          // true;
}
void
perftime::draw_background ()
{
    draw_rectangle(white_paint(), 0, 0, m_window_x, m_window_y);
    draw_line(black_paint(), 0, m_window_y - 1, m_window_x, m_window_y - 1);
    midipulse first_measure = m_tick_offset / m_measure_length;
    midipulse last_measure = first_measure +
        (m_window_x * m_perf_scale_x / m_measure_length) + 1;

#ifdef USE_STAZED_EXTRAS

    float bar_draw = m_measure_length / float(m_perf_scale_x);
    int bar_skip = 1;
    if (bar_draw < 24)
        bar_skip = 4;

    if (bar_draw < 12)
        bar_skip = 8;

    if (bar_draw < 6)
        bar_skip = 16;

    if (bar_draw < 3)
        bar_skip = 32;

    if (bar_draw < .75)
        bar_skip = 64;
#endif

    m_gc->set_foreground(grey());                   /* draw vertical lines  */

#ifdef USE_STAZED_EXTRAS
    for (midipulse i = first_measure; i < last_measure; i += bar_skip)
    {
        int x_pos = ((i * m_measure_length) - m_tick_offset) / m_perf_scale_x;
#else
    for (midipulse i = first_measure; i < last_measure; ++i)
    {
        int x_pos = tick_to_pixel(i * m_measure_length);
#endif

        char bar[8];
        snprintf(bar, sizeof(bar), "%ld", i + 1);       /* bar numbers      */
        draw_line(x_pos, 0, x_pos, m_window_y);         /* beat             */
        render_string(x_pos + 2, 0, bar, font::BLACK, true);
    }

    midipulse left = tick_to_pixel(perf().get_left_tick());
    midipulse right = tick_to_pixel(perf().get_right_tick());
    if (left >= 0 && left <= m_window_x)                /* draw L marker    */
    {
        draw_rectangle(black_paint(), left, m_window_y - 9, 7, 10);
        render_string(left + 1, 9, "L", font::WHITE, true);
    }
    if (right >= 0 && right <= m_window_x)              /* draw R marker    */
    {
        draw_rectangle(black_paint(), right - 6, m_window_y - 9, 7, 10);
        render_string(right - 6 + 1, 9, "R", font::WHITE, true);
    }
}

/**
 *  Implement the button-press event to set the L and R ticks.  Added
 *  functionality to try to set the start-tick if ctrl-left-click is pressed.
 *
 * \param p0
 *      The button event.
 *
 * \return
 *      Always returns true.
 */

bool
perftime::on_button_press_event (GdkEventButton * p0)
{
    midipulse tick = pixel_to_tick(long(p0->x));
    tick -= tick % m_snap;

    /**
     * Why is setting the start-tick disabled?  We re-enable it and see if it
     * works.  To our surprise, it works, but it sticks between stop/pause and
     * the next playback in the performance editor.  We added a feature where
     * stop sets the start-tick to the left tick (or the beginning tick).
     */

    if (SEQ64_CLICK_MIDDLE(p0->button))
    {
        perf().set_start_tick(tick);
    }
    else if (SEQ64_CLICK_LEFT(p0->button))
    {
        if (is_ctrl_key(p0))
            perf().set_start_tick(tick);
        else
            perf().set_left_tick(tick);
    }
    else if (SEQ64_CLICK_RIGHT(p0->button))
    {
        perf().set_right_tick(tick + m_snap);
    }
    enqueue_draw();
    return true;
}
bool
Seq24PerfInput::on_button_press_event (GdkEventButton * ev, perfroll & roll)
{
    bool result = false;
    perform & p = roll.perf();
    int & dropseq = roll.m_drop_sequence;
    sequence * seq = p.get_sequence(dropseq);
    bool dropseq_active = p.is_active(dropseq);
    roll.grab_focus();
    if (dropseq_active)
    {
        seq->unselect_triggers();
        roll.draw_all();
    }
    roll.m_drop_x = int(ev->x);
    roll.m_drop_y = int(ev->y);
    roll.convert_drop_xy();                             /* affects dropseq  */
    seq = p.get_sequence(dropseq);
    dropseq_active = p.is_active(dropseq);
    if (! dropseq_active)
        return false;

    /*
     * EXPERIMENTAL.
     *  Let's make better use of the Ctrl key here.  First, let Ctrl-Left be
     *  handled exactly like the Middle click, then bug out.
     *
     *  Note that this middle-click code ought to be folded into a function.
     */

    if (is_ctrl_key(ev))
    {
        if (SEQ64_CLICK_LEFT(ev->button))
        {
            bool state = seq->get_trigger_state(roll.m_drop_tick);
            if (state)
            {
                roll.split_trigger(dropseq, roll.m_drop_tick);
            }
            else
            {
                p.push_trigger_undo(dropseq);
                seq->paste_trigger(roll.m_drop_tick);
            }
        }
        return true;
    }

    if (SEQ64_CLICK_LEFT(ev->button))
    {
        midipulse droptick = roll.m_drop_tick;
        if (is_adding())                /* add new note if nothing selected */
        {
            set_adding_pressed(true);
            midipulse seqlength = seq->get_length();
            bool state = seq->get_trigger_state(droptick);
            if (state)
            {
                p.push_trigger_undo(dropseq);           /* stazed fix   */
                seq->del_trigger(droptick);
            }
            else
            {
                droptick -= (droptick % seqlength);     /* snap         */
                p.push_trigger_undo(dropseq);           /* stazed fix   */
                seq->add_trigger(droptick, seqlength);
                roll.draw_all();
            }
            result = true;
        }
        else
        {
            /*
             * Set this flag to tell on_motion_notify() to call
             * p.push_trigger_undo().
             */

            roll.m_have_button_press = seq->select_trigger(droptick);

            midipulse tick0 = seq->selected_trigger_start();
            midipulse tick1 = seq->selected_trigger_end();
            int wscalex = s_perfroll_size_box_click_w * c_perf_scale_x;
            int ydrop = roll.m_drop_y % c_names_y;
            if
            (
                droptick >= tick0 && droptick <= (tick0 + wscalex) &&
                ydrop <= s_perfroll_size_box_click_w + 1
            )
            {
                roll.m_growing = true;
                roll.m_grow_direction = true;
                roll.m_drop_tick_trigger_offset = droptick -
                                                  seq->selected_trigger_start();
            }
            else if
            (
                droptick >= (tick1 - wscalex) && droptick <= tick1 &&
                ydrop >= c_names_y - s_perfroll_size_box_click_w - 1
            )
            {
                roll.m_growing = true;
                roll.m_grow_direction = false;
                roll.m_drop_tick_trigger_offset = droptick -
                                                  seq->selected_trigger_end();
            }
            else
            {
                roll.m_moving = true;
                roll.m_drop_tick_trigger_offset = droptick -
                                                  seq->selected_trigger_start();
            }
            roll.draw_all();
        }
    }
    else if (SEQ64_CLICK_RIGHT(ev->button))
    {
        activate_adding(true, roll);
        // Should we add this?
        // result = true;
    }
    else if (SEQ64_CLICK_MIDDLE(ev->button))                   /* split    */
    {
        /*
         * The middle click in seq24 interaction mode is either for splitting
         * the triggers or for setting the paste location of copy/paste.
         */

        bool state = seq->get_trigger_state(roll.m_drop_tick);
        if (state)
        {
            roll.split_trigger(dropseq, roll.m_drop_tick);
            result = true;
        }
        else
        {
            p.push_trigger_undo(dropseq);
            seq->paste_trigger(roll.m_drop_tick);
            // Should we add this?
            // result = true;
        }
    }
    return result;
}