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
FruityPerfInput::on_button_release_event (GdkEventButton * ev, perfroll & roll)
{
    bool result = false;
    m_current_x = int(ev->x);
    m_current_y = int(ev->y);

    perform & p = roll.perf();
    roll.m_moving = false;
    roll.m_growing = false;
    set_adding_pressed(false);
    if (p.is_active(roll.m_drop_sequence))
        roll.draw_all();

    update_mouse_pointer(roll);
    return result;
}
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;
}
bool
FruityPerfInput::on_left_button_pressed (GdkEventButton * ev, perfroll & roll)
{
    bool result = false;
    perform & p = roll.perf();
    int dropseq = roll.m_drop_sequence;
    sequence * seq = p.get_sequence(dropseq);
    if (is_ctrl_key(ev))
    {
        if (p.is_active(dropseq))
        {
            midipulse droptick = roll.m_drop_tick;
            droptick -= droptick % roll.m_snap;     /* stazed: grid snap    */
            bool state = seq->get_trigger_state(droptick);
            if (state)
            {
                roll.split_trigger(dropseq, droptick);
                result = true;
            }
            else                                /* track click, paste trig  */
            {
                p.push_trigger_undo(dropseq);
                seq->paste_trigger(droptick);
            }
        }
    }
    else                    /* add a new note if we didn't select anything  */
    {
        midipulse droptick = roll.m_drop_tick;
        set_adding_pressed(true);
        if (p.is_active(dropseq))
        {
            midipulse seqlength = seq->get_length();
            bool state = seq->get_trigger_state(droptick);
            if (state)      /* resize or move event based on where clicked  */
            {
                set_adding_pressed(false);

                /*
                 * Set the flag that tells the motion-notify callback to call
                 * push_trigger_undo().
                 */

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

                midipulse starttick = seq->selected_trigger_start();
                midipulse endtick = 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 >= starttick && droptick <= (starttick + wscalex) &&
                    ydrop <= (s_perfroll_size_box_click_w + 1)
                )
                {           /* clicked left side: grow/shrink left side     */
                    roll.m_growing = true;
                    roll.m_grow_direction = true;
                    roll.m_drop_tick_trigger_offset = roll.m_drop_tick -
                        seq->selected_trigger_start();
                }
                else if
                (
                    droptick >= (endtick - wscalex) && droptick <= endtick &&
                    ydrop >= (c_names_y - s_perfroll_size_box_click_w - 1)
                )
                {           /* clicked right side: grow/shrink right side   */
                    roll.m_growing = true;
                    roll.m_grow_direction = false;
                    roll.m_drop_tick_trigger_offset = roll.m_drop_tick -
                        seq->selected_trigger_end();
                }
                else        /* clicked in the middle - move it              */
                {
                    roll.m_moving = true;
                    roll.m_drop_tick_trigger_offset = roll.m_drop_tick -
                         seq->selected_trigger_start();
                }
                roll.draw_all();
            }
            else                                    /* add a trigger        */
            {
                droptick -= (droptick % seqlength); /* snap to seqlength    */
                p.push_trigger_undo();
                seq->add_trigger(droptick, seqlength);
                result = true;
                roll.draw_all();
            }
        }
    }
    return result;
}