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;
}
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;
}