clean up bclistbox, listbox shift drag fix, plugin info
[goodguy/history.git] / cinelerra-5.1 / cinelerra / trackcanvas.C
index 28f149f351ff84f7830e8ed2035e975aa59f5800..5d925e6e1a3650180ba13526f339be1033192108 100644 (file)
@@ -26,7 +26,7 @@
 #include "bcsignals.h"
 #include "bctimer.h"
 #include "clip.h"
-#include "colors.h"
+#include "bccolors.h"
 #include "cplayback.h"
 #include "cursors.h"
 #include "cwindowgui.h"
@@ -38,6 +38,7 @@
 #include "edlsession.h"
 #include "floatauto.h"
 #include "floatautos.h"
+#include "gwindowgui.h"
 #include "indexstate.h"
 #include "intauto.h"
 #include "intautos.h"
@@ -45,6 +46,7 @@
 #include "keyframepopup.h"
 #include "keyframes.h"
 #include "keys.h"
+#include "labels.h"
 #include "localsession.h"
 #include "mainclock.h"
 #include "maincursor.h"
@@ -108,6 +110,7 @@ TrackCanvas::TrackCanvas(MWindow *mwindow,
        render_timer = new Timer;
        hourglass_enabled = 0;
        timebar_position = -1;
+       snapped = 0;
 }
 
 TrackCanvas::~TrackCanvas()
@@ -185,7 +188,7 @@ int TrackCanvas::drag_motion(Track **over_track,
        int cursor_y = get_relative_cursor_y();
 
        if( get_cursor_over_window() &&
-               cursor_x >= 0 && cursor_y >= 0 && 
+               cursor_x >= 0 && cursor_y >= 0 &&
                cursor_x < get_w() && cursor_y < get_h() )
        {
 //printf("TrackCanvas::drag_motion %d %d\n", __LINE__, pane->number);
@@ -203,15 +206,24 @@ int TrackCanvas::drag_motion(Track **over_track,
                                {
                                        int64_t edit_x, edit_y, edit_w, edit_h;
                                        edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h);
-
-                                       if(cursor_x >= edit_x &&
-                                               cursor_y >= edit_y &&
-                                               cursor_x < edit_x + edit_w &&
-                                               cursor_y < edit_y + edit_h)
-                                       {
+                                       if( cursor_y < edit_y || cursor_y >= edit_y + edit_h ) continue;
+                                       if( cursor_x >= edit_x && cursor_x < edit_x + edit_w ) {
                                                *over_edit = edit;
                                                break;
                                        }
+                                       if( edit != track->edits->last ) continue;
+                                       if( mwindow->session->current_operation != DRAG_ATRANSITION &&
+                                           mwindow->session->current_operation != DRAG_VTRANSITION ) continue;
+                                       if( !edit->silence() ) {
+                                               // add silence to allow drag transition past last edit
+                                               //  will be deleted by Edits::optimize if not used
+                                               double length = mwindow->edl->session->default_transition_length;
+                                               int64_t start = edit->startproject+edit->length;
+                                               int64_t units = track->to_units(length, 1);
+                                               track->edits->create_silence(start, start+units);
+                                               continue;
+                                       }
+                                       if( cursor_x >= edit_x ) { *over_edit = edit; break; }
                                }
 
                                for(int i = 0; i < track->plugin_set.total; i++)
@@ -244,7 +256,7 @@ int TrackCanvas::drag_motion(Track **over_track,
        }
 
         if( !*over_track )
-               *over_track = pane->is_over_patchbay();
+               *over_track = pane->over_patchbay();
 
        return 0;
 }
@@ -272,7 +284,7 @@ int TrackCanvas::drag_stop(int *redraw)
                    (cursor_y = get_relative_cursor_y()) >= 0 && cursor_y < get_h() )
                        over_window = 1;
                else {
-                       Track *track = pane->is_over_patchbay();
+                       Track *track = pane->over_patchbay();
                        if( track && mwindow->session->track_highlighted == track )
                                over_window = 1;
                }
@@ -383,7 +395,7 @@ int TrackCanvas::drag_stop(int *redraw)
                                double asset_duration = 0;
                                int64_t asset_length_units = 0;
                                int64_t position = 0;
-                                       
+
                                if(mwindow->session->current_operation == DRAG_ASSET &&
                                        mwindow->session->drag_assets->total) {
                                        Indexable *indexable = mwindow->session->drag_assets->values[0];
@@ -395,8 +407,8 @@ int TrackCanvas::drag_stop(int *redraw)
                                                if (video_length < 0) {
                                                        if(mwindow->edl->session->si_useduration)
                                                                video_length = mwindow->edl->session->si_duration;
-                                                       else    
-                                                               video_length = 1.0 / mwindow->edl->session->frame_rate ; 
+                                                       else
+                                                               video_length = 1.0 / mwindow->edl->session->frame_rate ;
                                                }
                                                asset_duration = video_length / indexable->get_frame_rate();
                                        }
@@ -418,14 +430,14 @@ int TrackCanvas::drag_stop(int *redraw)
                                else {
                                        printf("DRAG_ASSET error: Asset dropped, but both drag_clips and drag_assets total is zero\n");
                                }
-                       
+
                                asset_length_units = mwindow->session->track_highlighted->to_units(asset_duration, 1);
                                position = drop_edit_position (&insertion, NULL, asset_length_units);
                                if( position == -1 ) {
                                        result = 1;
                                        break;          // Do not do anything
                                }
-                               
+
                                double position_f = mwindow->session->track_highlighted->from_units(position);
                                Track *track = mwindow->session->track_highlighted;
 
@@ -433,7 +445,7 @@ int TrackCanvas::drag_stop(int *redraw)
 //                                     // FIXME, we should create an mwindow/EDL method that overwrites, without clearing the keyframes and autos
 //                                     // Unfortunately, this is _a lot_ of work to do right
 //                                     printf("Problematic insertion\n");
-//                                     mwindow->edl->tracks->clear(position_f, 
+//                                     mwindow->edl->tracks->clear(position_f,
 //                                             position_f + asset_duration, 0);
 //                             }
                                mwindow->paste_assets(position_f, track, !insertion);
@@ -454,7 +466,7 @@ int TrackCanvas::drag_stop(int *redraw)
                                                result = 1;
                                                break;          // Do not do anything
                                        }
-                                       
+
                                        double position_f = mwindow->session->track_highlighted->from_units(position);
                                        Track *track = mwindow->session->track_highlighted;
                                        mwindow->move_edits(mwindow->session->drag_edits,
@@ -1088,9 +1100,9 @@ void TrackCanvas::draw_paste_destination()
                                }
 
 // Get the x coordinate
-                               x = Units::to_int64(position * 
+                               x = Units::to_int64(position *
                                        mwindow->edl->session->sample_rate /
-                                       mwindow->edl->local_session->zoom_sample) - 
+                                       mwindow->edl->local_session->zoom_sample) -
                                        mwindow->edl->local_session->view_start[pane->number];
 
                                double paste_position = -1.;
@@ -1267,7 +1279,7 @@ void TrackCanvas::draw_highlight_rectangle(int x, int y, int w, int h)
        h = MIN(h, get_h() + 20);
        if(w > 0 && h > 0)
        {
-               set_color(WHITE);
+               set_color(mwindow->preferences->highlight_inverse);
                set_inverse();
                //draw_rectangle(x, y, w, h);
                draw_rectangle(x + 1, y + 1, w - 2, h - 2);
@@ -1329,7 +1341,7 @@ void TrackCanvas::draw_highlight_insertion(int x, int y, int w, int h)
        }
        w = MIN(w, get_w() + 20);
        h = MIN(h, get_h() + 20);
-       set_color(WHITE);
+       set_color(mwindow->preferences->highlight_inverse);
        set_inverse();
        draw_rectangle(x, y, w, h);
        draw_rectangle(x + 1, y + 1, w - 2, h - 2);
@@ -1753,7 +1765,7 @@ void TrackCanvas::draw_drag_handle()
                        mwindow->edl->local_session->zoom_sample -
                        mwindow->edl->local_session->view_start[pane->number]);
 //printf("TrackCanvas::draw_drag_handle 2 %d %jd\n", pane->number, pixel1);
-               set_color(GREEN);
+               set_color(!snapped ? GREEN : (snapped=0, YELLOW));
                set_inverse();
 //printf("TrackCanvas::draw_drag_handle 3\n");
                draw_line(pixel1, 0, pixel1, get_h());
@@ -1842,41 +1854,36 @@ void TrackCanvas::draw_loop_points()
 //printf("TrackCanvas::draw_loop_points 7\n");
 }
 
-void TrackCanvas::draw_brender_start()
+void TrackCanvas::draw_brender_range()
 {
-       if(mwindow->preferences->use_brender)
+       if( !mwindow->preferences->use_brender || !mwindow->brender_active ) return;
+       if( mwindow->edl->session->brender_start >= mwindow->edl->session->brender_end ) return;
+       if( mwindow->edl->session->brender_end > 0 )
        {
-               int64_t x = Units::round(mwindow->edl->session->brender_start *
+               int64_t x1 = Units::round(mwindow->edl->session->brender_start *
+                       mwindow->edl->session->sample_rate /
+                       mwindow->edl->local_session->zoom_sample -
+                       mwindow->edl->local_session->view_start[pane->number]);
+               if(MWindowGUI::visible(x1, x1 + 1, 0, get_w()))
+               {
+                       set_color(RED);
+                       draw_line(x1, 0, x1, get_h());
+               }
+               int64_t x2 = Units::round(mwindow->edl->session->brender_end *
                        mwindow->edl->session->sample_rate /
                        mwindow->edl->local_session->zoom_sample -
                        mwindow->edl->local_session->view_start[pane->number]);
 
-               if(MWindowGUI::visible(x, x + 1, 0, get_w()))
+               if(MWindowGUI::visible(x2, x2 + 1, 0, get_w()))
                {
                        set_color(RED);
-                       draw_line(x, 0, x, get_h());
+                       draw_line(x2, 0, x2, get_h());
                }
        }
 }
 
-static int auto_colors[AUTOMATION_TOTAL] =
-{
-       BLUE,
-       RED,
-       GREEN,
-       BLUE,
-       RED,
-       GREEN,
-       BLUE,
-       WHITE,
-       0,
-       0,
-       0,
-       WHITE
-};
-
 // The operations which correspond to each automation type
-static int auto_operations[AUTOMATION_TOTAL] =
+int TrackCanvas::auto_operations[AUTOMATION_TOTAL] =
 {
        DRAG_MUTE,
        DRAG_CAMERA_X,
@@ -1976,25 +1983,25 @@ int TrackCanvas::do_keyframes(int cursor_x,
                                                int grouptype = automation.autogrouptype(i, track);
                                                if(draw) // Do dropshadow
                                                        result = do_float_autos(track, autos,
-                                                               cursor_x, cursor_y, draw, 
+                                                               cursor_x, cursor_y, draw,
                                                                buttonpress, 1, 1, MDGREY,
                                                                auto_keyframe, grouptype);
 
                                                result = do_float_autos(track, autos,
-                                                       cursor_x, cursor_y, draw, 
-                                                       buttonpress, 0, 0, auto_colors[i],
+                                                       cursor_x, cursor_y, draw, buttonpress,
+                                                       0, 0, GWindowGUI::auto_colors[i],
                                                        auto_keyframe, grouptype);
                                                break; }
 
                                        case Autos::AUTOMATION_TYPE_INT: {
                                                if(draw) // Do dropshadow
                                                        result = do_int_autos(track, autos,
-                                                               cursor_x, cursor_y, draw, 
+                                                               cursor_x, cursor_y, draw,
                                                                buttonpress, 1, 1, MDGREY,
                                                                auto_keyframe);
                                                result = do_int_autos(track, autos,
-                                                       cursor_x, cursor_y, draw, 
-                                                       buttonpress, 0, 0, auto_colors[i],
+                                                       cursor_x, cursor_y, draw, buttonpress,
+                                                       0, 0, GWindowGUI::auto_colors[i],
                                                        auto_keyframe);
                                                break; }
                                        }
@@ -2014,7 +2021,7 @@ int TrackCanvas::do_keyframes(int cursor_x,
                                        {
                                                if (buttonpress != 3)
                                                {
-                                                       if(i == AUTOMATION_FADE)
+                                                       if(i == AUTOMATION_FADE || i == AUTOMATION_SPEED)
                                                                synchronize_autos(0,
                                                                        track,
                                                                        (FloatAuto*)mwindow->session->drag_auto,
@@ -2031,6 +2038,12 @@ int TrackCanvas::do_keyframes(int cursor_x,
                                                        gui->keyframe_menu->activate_menu();
                                                        rerender = 1; // the position changes
                                                }
+                                               else if( autos )
+                                               {
+                                                       gui->keyframe_hide->update(autos);
+                                                       gui->keyframe_hide->activate_menu();
+                                                       rerender = 1; // the position changes
+                                               }
                                                if(buttonpress == 1 && ctrl_down() &&
                                                   AUTOMATION_TYPE_FLOAT == autos->get_type())
                                                        rerender = 1; // special case: curve mode changed
@@ -2085,6 +2098,45 @@ int TrackCanvas::do_keyframes(int cursor_x,
        return result;
 }
 
+void TrackCanvas::draw_keyframe_reticle()
+{
+       int keyframe_hairline = mwindow->preferences->keyframe_reticle;
+       if( keyframe_hairline == HAIRLINE_NEVER ) return;
+
+       int current_op = mwindow->session->current_operation, dragging = 0;
+       for( int i=0; !dragging && i<AUTOMATION_TOTAL; ++i )
+               if( current_op == auto_operations[i] ) dragging = 1;
+
+       if( keyframe_hairline == HAIRLINE_DRAGGING && dragging ) {
+               if( mwindow->session->drag_auto && get_buttonpress() == 1 ) {
+                       draw_hairline(mwindow->session->drag_auto, RED);
+                       return;
+               }
+       }
+
+       if( keyframe_hairline == HAIRLINE_ALWAYS || ( get_buttonpress() == 2 &&
+           keyframe_hairline == HAIRLINE_DRAGGING && dragging ) ) {
+               for( Track *track = mwindow->edl->tracks->first; track;
+                    track=track->next ) {
+                       Automation *automation = track->automation;
+                       for( int i=0; i<AUTOMATION_TOTAL; ++i ) {
+                               if( !mwindow->edl->session->auto_conf->autos[i] ) continue;
+                               // automation visible
+                               Autos *autos = automation->autos[i];
+                               if( !autos ) continue;
+                               for( Auto *auto_keyframe=autos->first; auto_keyframe;
+                                    auto_keyframe = auto_keyframe->next ) {
+                                       draw_hairline(auto_keyframe, GREEN);
+                               }
+                       }
+
+                       if( dragging && mwindow->session->drag_auto ) {
+                               draw_hairline(mwindow->session->drag_auto, RED);
+                       }
+               }
+       }
+}
+
 void TrackCanvas::draw_auto(Auto *current,
        int x,
        int y,
@@ -2152,14 +2204,14 @@ void TrackCanvas::draw_cropped_line(int x1,
 }
 
 
-void TrackCanvas::draw_floatauto(FloatAuto *current, 
-       int x, 
-       int y, 
-       int in_x, 
-       int in_y, 
-       int out_x, 
-       int out_y, 
-       int center_pixel, 
+void TrackCanvas::draw_floatauto(FloatAuto *current,
+       int x,
+       int y,
+       int in_x,
+       int in_y,
+       int out_x,
+       int out_y,
+       int center_pixel,
        int zoom_track,
        int color)
 {
@@ -2180,7 +2232,7 @@ void TrackCanvas::draw_floatauto(FloatAuto *current,
                draw_box(x1, y1, x2 - x1, y2 - y1);
        }
 
-// show bezier control points (only) if this 
+// show bezier control points (only) if this
 // floatauto doesn't adjust it's tangents automatically
        if(current->curve_mode != FloatAuto::FREE &&
           current->curve_mode != FloatAuto::TFREE)
@@ -2208,22 +2260,22 @@ inline void TrackCanvas::draw_floatauto_ctrlpoint(
 
        y    += center_pixel;
        cp_y += center_pixel;
-       
+
        // drawing the tangent as a dashed line...
        int const dash = HANDLE_W;
        int const gap  = HANDLE_W / 2;
        float sx = 3 * (cp_x - x) / 4.;
        float ex = 0;
-       
+
        // q is the x displacement for a unit line of slope
        float q = (sx > 0 ? 1 : -1) / sqrt(1 + slope * slope);
-       
+
        float dist = 1/q * sx;
        if( dist > dash )
                ex = sx - q * dash;
-       
+
        set_color(color);
-       do {    
+       do {
                float sy = slope * sx, ey = slope * ex;
                draw_line(quantize(sx + x), quantize(sy + y), quantize(ex + x), quantize(ey + y));
                sx = ex - q * gap;
@@ -2306,13 +2358,12 @@ inline float test_curve_line( int x0, int y0, int ctrl_x, int ctrl_y,
 }
 
 
-inline 
+inline
 float levered_position(float position, float ref_pos)
 {
-       if( 1e-6 > fabs(ref_pos) || isnan(ref_pos)) 
+       if( 1e-6 > fabs(ref_pos) || isnan(ref_pos))
                return 0.0;
-       else 
-               return ref_pos / position;
+       return ref_pos / position;
 }
 
 
@@ -2324,16 +2375,15 @@ float TrackCanvas::value_to_percentage(float auto_value, int autogrouptype)
        float automation_min = mwindow->edl->local_session->automation_mins[autogrouptype];
        float automation_max = mwindow->edl->local_session->automation_maxs[autogrouptype];
        float automation_range = automation_max - automation_min;
-       if(0 == automation_range || isnan(auto_value) || isinf(auto_value))
+       if( 0 >= automation_range || isnan(auto_value) || isinf(auto_value) )
                return 0;
-       else
-               return (auto_value - automation_min) / automation_range;
+       return (auto_value - automation_min) / automation_range;
 }
 
 
 
 int TrackCanvas::test_floatauto(FloatAuto *current, int x, int y, int in_x,
-       int in_y, int out_x, int out_y, int center_pixel, int zoom_track, 
+       int in_y, int out_x, int out_y, int center_pixel, int zoom_track,
        int cursor_x, int cursor_y, int buttonpress, int autogrouptype)
 {
        int result = 0;
@@ -2384,7 +2434,7 @@ int TrackCanvas::test_floatauto(FloatAuto *current, int x, int y, int in_x,
                if( WITHIN(x1,x2,y1,y2))
                {       // cursor hits node
                        result = 1;
-                       
+
                        if(buttonpress && (buttonpress != 3))
                        {
                                INIT_DRAG(current->position, value_to_percentage(current->get_value(), autogrouptype))
@@ -2401,13 +2451,13 @@ int TrackCanvas::test_floatauto(FloatAuto *current, int x, int y, int in_x,
                                // could be ctrl-click or ctrl-drag
                                // click would cycle through tangent modes
                                ((FloatAuto*)current)->toggle_curve_mode();
-                               
+
                                // drag will start dragging the tangent, if applicable
                                INIT_DRAG(current->position, value_to_percentage(current->get_value(), autogrouptype))
                                mwindow->session->drag_handle = 0;
                        }
                }
-                       
+
                float lever = 0.0; // we use the tangent as a draggable lever. 1.0 is at the ctrl point
 
 // Test in control
@@ -2456,8 +2506,8 @@ int TrackCanvas::test_floatauto(FloatAuto *current, int x, int y, int in_x,
 #undef WITHIN
 #undef INIT_DRAG
 
-// if(buttonpress) 
-// printf("TrackCanvas::test_floatauto 2 drag_handle=%d ctrl_down=%d cursor_x=%d cursor_y=%d x1=%d x2=%d y1=%d y2=%d\n", 
+// if(buttonpress)
+// printf("TrackCanvas::test_floatauto 2 drag_handle=%d ctrl_down=%d cursor_x=%d cursor_y=%d x1=%d x2=%d y1=%d y2=%d\n",
 // mwindow->session->drag_handle,
 // ctrl_down(),
 // cursor_x,
@@ -2508,7 +2558,7 @@ void TrackCanvas::draw_floatline(int center_pixel,
 
 // Not using slope intercept
        x1 = MAX(0, x1);
-       int prev_y = y1;
+       int prev_y = y1 + center_pixel;
 
 
 // Call by reference fails for some reason here
@@ -2531,6 +2581,7 @@ void TrackCanvas::draw_floatline(int center_pixel,
 // (int)(center_pixel - yscale / 2),
 // (int)(center_pixel + yscale / 2 - 1));
 
+//printf("draw_line(%d,%d,  %d,%d)\n", x - 1, prev_y  , x, y);
                        draw_line(x - 1, prev_y  , x, y   );
                }
                prev_y = y;
@@ -2580,7 +2631,10 @@ int TrackCanvas::test_floatline(int center_pixel,
                {
                        Auto *current;
                        mwindow->undo->update_undo_before();
-                       current = mwindow->session->drag_auto = autos->insert_auto(position1);
+                       double position = autos->track->from_units(position1);
+                       position = mwindow->edl->align_to_frame(position, 0);
+                       int64_t new_position = autos->track->to_units(position,0);
+                       current = mwindow->session->drag_auto = autos->insert_auto(new_position);
                        ((FloatAuto*)current)->set_value(value);
                        mwindow->session->drag_start_percentage = value_to_percentage(value, autogrouptype);
                        mwindow->session->drag_start_position = current->position;
@@ -2596,71 +2650,62 @@ int TrackCanvas::test_floatline(int center_pixel,
 
 
 void TrackCanvas::synchronize_autos(float change,
-       Track *skip,
-       FloatAuto *fauto,
-       int fill_gangs)
+               Track *skip, FloatAuto *fauto, int fill_gangs)
 {
 // Handles the special case of modifying a fadeauto
 // when there are ganged faders on several tracks
 // (skip and fauto may be NULL if fill_gangs==-1)
-       if (fill_gangs == 1 && skip->gang)
-       {
-               for(Track *current = mwindow->edl->tracks->first;
-                       current;
-                       current = NEXT)
-               {
-                       if(current->data_type == skip->data_type &&
-                               current->gang &&
-                               current->record &&
-                               current != skip)
-                       {
-                               FloatAutos *fade_autos = (FloatAutos*)current->automation->autos[AUTOMATION_FADE];
-                               double position = skip->from_units(fauto->position);
-                               FloatAuto *previous = 0, *next = 0;
 
-                               float init_value = fade_autos->get_value(fauto->position, PLAY_FORWARD, previous, next);
-                               FloatAuto *keyframe;
-                               keyframe = (FloatAuto*)fade_autos->get_auto_at_position(position);
-
-                               if (!keyframe)
-                               {
+       if( fill_gangs > 0 && skip->gang ) {
+               double position = skip->from_units(fauto->position);
+               int autoidx = fauto->autos->autoidx;
+
+               for(Track *current = mwindow->edl->tracks->first; current; current = NEXT) {
+                       if( (current->data_type == skip->data_type || get_double_click()) &&
+                           current->gang && current->record && current != skip ) {
+                               int64_t current_position = current->to_units(position, 1);
+                               FloatAutos *fade_autos = (FloatAutos*)current->automation->autos[autoidx];
+                               float auto_min = mwindow->edl->local_session->automation_mins[fade_autos->autogrouptype];
+                               float auto_max = mwindow->edl->local_session->automation_maxs[fade_autos->autogrouptype];
+                               FloatAuto *previous = 0, *next = 0;
+                               FloatAuto *keyframe = (FloatAuto*)fade_autos->get_auto_at_position(current_position);
+                               if( !keyframe ) {
 // create keyframe on neighbouring track at the point in time given by fauto
-                                       keyframe = (FloatAuto*)fade_autos->insert_auto(fauto->position);
-                                       keyframe->set_value(init_value + change);
+                                       float init_value = fade_autos->get_value(current_position, PLAY_FORWARD, previous, next);
+                                       float new_value = init_value + change;
+                                       CLAMP(new_value, auto_min, auto_max);
+                                       keyframe = (FloatAuto*)fade_autos->insert_auto(current_position);
+                                       keyframe->set_value(new_value);
                                }
-                               else
-                               {
+                               else {
 // keyframe exists, just change it
-                                       keyframe->adjust_to_new_coordinates(fauto->position, keyframe->get_value() + change);
+                                       float new_value = keyframe->get_value() + change;
+                                       CLAMP(new_value, auto_min, auto_max);
+                                       keyframe->adjust_to_new_coordinates(current_position, new_value);
 // need to (re)set the position, as the existing node could be on a "equivalent" position (within half a frame)
                                }
 
                                mwindow->session->drag_auto_gang->append((Auto *)keyframe);
                        }
                }
-       } else
-// move the gangs
-       if (fill_gangs == 0)
-       {
-// Move the gang!
-               for (int i = 0; i < mwindow->session->drag_auto_gang->total; i++)
-               {
+       }
+       else if( !fill_gangs ) {
+               double position = skip->from_units(fauto->position);
+// Move the gangs
+               for (int i = 0; i < mwindow->session->drag_auto_gang->total; i++) {
                        FloatAuto *keyframe = (FloatAuto *)mwindow->session->drag_auto_gang->values[i];
-
+                       int64_t keyframe_position = keyframe->autos->track->to_units(position, 1);
                        float new_value = keyframe->get_value() + change;
                        CLAMP(new_value,
                              mwindow->edl->local_session->automation_mins[keyframe->autos->autogrouptype],
                              mwindow->edl->local_session->automation_maxs[keyframe->autos->autogrouptype]);
-                       keyframe->adjust_to_new_coordinates(fauto->position, new_value);
+                       keyframe->adjust_to_new_coordinates(keyframe_position, new_value);
                }
 
        }
-       else
+       else {
 // remove the gangs
-       if (fill_gangs == -1)
-       {
-               for (int i = 0; i < mwindow->session->drag_auto_gang->total; i++)
-               {
+               for (int i = 0; i < mwindow->session->drag_auto_gang->total; i++) {
                        FloatAuto *keyframe = (FloatAuto *)mwindow->session->drag_auto_gang->values[i];
                        keyframe->autos->remove_nonsequential(
                                        keyframe);
@@ -2864,10 +2909,10 @@ int TrackCanvas::do_float_autos(Track *track, Autos *autos, int cursor_x, int cu
                 autos->first ? autos->first : autos->default_auto;
 
        double ax = 0, ay = 0, ax2 = 0, ay2 = 0;
-       if( first_auto )
+       if( first_auto ) {
                calculate_auto_position(&ax, &ay, 0, 0, 0, 0,
                        first_auto, unit_start, zoom_units, yscale, autogrouptype);
-
+       }
        if( current )
                current = NEXT;
        else {
@@ -2950,7 +2995,7 @@ int TrackCanvas::do_float_autos(Track *track, Autos *autos, int cursor_x, int cu
                                (int)ax, (int)ax2, cursor_x, cursor_y,
                                buttonpress, autogrouptype);
                }
-               if( draw ) 
+               if( draw )
                        draw_floatline(center_pixel,
                                (FloatAuto*)previous, (FloatAuto*)current,
                                (FloatAutos*)autos, unit_start, zoom_units, yscale,
@@ -3332,6 +3377,28 @@ int TrackCanvas::do_plugin_autos(Track *track, int cursor_x, int cursor_y,
        return result;
 }
 
+int TrackCanvas::draw_hairline(Auto *auto_keyframe, int color)
+{
+       Track *track = auto_keyframe->autos->track;
+       int autogrouptype = auto_keyframe->autos->get_type();
+
+       int center_pixel;
+       double view_start, unit_start;
+       double view_end, unit_end, yscale;
+       double zoom_sample, zoom_units;
+
+       calculate_viewport(track, view_start, unit_start, view_end, unit_end,
+                       yscale, center_pixel, zoom_sample, zoom_units);
+
+       double ax = 0, ay = 0;
+       calculate_auto_position(&ax, &ay, 0, 0, 0, 0,
+               auto_keyframe, unit_start, zoom_units, yscale, autogrouptype);
+
+       set_color(color);
+       draw_line(ax, 0, ax, get_h());
+       return 0;
+}
+
 void TrackCanvas::draw_overlays()
 {
        int new_cursor, update_cursor, rerender;
@@ -3356,7 +3423,7 @@ void TrackCanvas::draw_overlays()
 
 // Loop points
        draw_loop_points();
-       draw_brender_start();
+       draw_brender_range();
 
 // Highlighted areas
        draw_highlighting();
@@ -3373,6 +3440,8 @@ void TrackCanvas::draw_overlays()
 // Playback cursor
        draw_playback_cursor();
 
+       draw_keyframe_reticle();
+
        show_window(0);
 }
 
@@ -3405,15 +3474,46 @@ int TrackCanvas::deactivate()
 void TrackCanvas::update_drag_handle()
 {
        double new_position;
+       int cursor_x = get_cursor_x();
 
        new_position =
-               (double)(get_cursor_x() +
+               (double)(cursor_x +
                mwindow->edl->local_session->view_start[pane->number]) *
                mwindow->edl->local_session->zoom_sample /
                mwindow->edl->session->sample_rate;
+
        new_position =
                mwindow->edl->align_to_frame(new_position, 0);
 
+       if( ctrl_down() && alt_down() ) {
+#define snapper(v) do { \
+       double pos = (v); \
+       if( pos < 0 ) break; \
+       double dist = fabs(new_position - pos); \
+       if( dist >= snap_min ) break; \
+       snap_position = pos;  snap_min = dist; \
+} while(0)
+               double snap_position = new_position;
+               double snap_min = DBL_MAX;
+               if( mwindow->edl->local_session->inpoint_valid() )
+                       snapper(mwindow->edl->local_session->get_inpoint());
+               if( mwindow->edl->local_session->outpoint_valid() )
+                       snapper(mwindow->edl->local_session->get_outpoint());
+               snapper(mwindow->edl->prev_edit(new_position));
+               snapper(mwindow->edl->next_edit(new_position));
+               Label *prev_label = mwindow->edl->labels->prev_label(new_position);
+               if( prev_label ) snapper(prev_label->position);
+               Label *next_label = mwindow->edl->labels->next_label(new_position);
+               if( next_label ) snapper(next_label->position);
+               int snap_x = snap_position * mwindow->edl->session->sample_rate /
+                       mwindow->edl->local_session->zoom_sample -
+                       mwindow->edl->local_session->view_start[pane->number];
+               if( abs(snap_x - cursor_x) < HANDLE_W ) {
+                       snapped = 1;
+                       new_position = snap_position;
+               }
+#undef snapper
+       }
 
        if(new_position != mwindow->session->drag_position)
        {
@@ -3485,36 +3585,22 @@ int TrackCanvas::get_drag_values(float *percentage,
 
 
 #define UPDATE_DRAG_HEAD(do_clamp) \
-       int result = 0; \
+       int result = 0, center_pixel; \
        if(!current->autos->track->record) return 0; \
-       double view_start; \
-       double unit_start; \
-       double view_end; \
-       double unit_end; \
-       double yscale; \
-       int center_pixel; \
-       double zoom_sample; \
-       double zoom_units; \
+       double view_start, unit_start, view_end, unit_end; \
+       double yscale, zoom_sample, zoom_units; \
  \
        calculate_viewport(current->autos->track,  \
-               view_start, \
-               unit_start, \
-               view_end, \
-               unit_end, \
-               yscale, \
-               center_pixel, \
-               zoom_sample, \
-               zoom_units); \
+               view_start, unit_start, view_end, unit_end, \
+               yscale, center_pixel, zoom_sample, zoom_units); \
  \
        float percentage = (float)(mwindow->session->drag_origin_y - cursor_y) / \
-               yscale +  \
-               mwindow->session->drag_start_percentage; \
+               yscale +  mwindow->session->drag_start_percentage; \
        if(do_clamp) CLAMP(percentage, 0, 1); \
  \
        int64_t position = Units::to_int64(zoom_units * \
                (cursor_x - mwindow->session->drag_origin_x) + \
                mwindow->session->drag_start_position); \
- \
        if((do_clamp) && position < 0) position = 0;
 
 
@@ -3523,119 +3609,99 @@ int TrackCanvas::update_drag_floatauto(int cursor_x, int cursor_y)
        FloatAuto *current = (FloatAuto*)mwindow->session->drag_auto;
 
        UPDATE_DRAG_HEAD(mwindow->session->drag_handle == 0);
-       int x = cursor_x - mwindow->session->drag_origin_x; \
-       int y = cursor_y - mwindow->session->drag_origin_y; \
-
-       float value;
-       float old_value;
+       int x = cursor_x - mwindow->session->drag_origin_x;
+       int y = cursor_y - mwindow->session->drag_origin_y;
+       float value, old_value;
+
+       if( mwindow->session->drag_handle == 0 && (ctrl_down() && !shift_down()) ) {
+// not really editing the node, rather start editing the curve
+// tangent is editable and drag movement is significant
+               if( (FloatAuto::FREE == current->curve_mode ||
+                    FloatAuto::TFREE==current->curve_mode) &&
+                   (fabs(x) > HANDLE_W / 2 || fabs(y) > HANDLE_W / 2))
+                       mwindow->session->drag_handle = x < 0 ? 1 : 2;
+       }
 
-       switch(mwindow->session->drag_handle)
-       {
-// Center
-               case 0:
-                       if(ctrl_down())
-                       // not really editing the node, rather start editing the curve
-                       {
-                               // tangent is editable and drag movement is significant
-                               if( (FloatAuto::FREE == current->curve_mode ||
-                                    FloatAuto::TFREE==current->curve_mode) &&                                          
-                                   (fabs(x) > HANDLE_W / 2 || fabs(y) > HANDLE_W / 2))
-                                       mwindow->session->drag_handle = x < 0 ? 1 : 2;
-                               break;
-                       }
+       switch(mwindow->session->drag_handle) {
+       case 0: // Center
 // Snap to nearby values
-                       old_value = current->get_value();
-                       if(shift_down())
-                       {
-                               double value1;
-                               double distance1;
-                               double value2;
-                               double distance2;
-
-                               if(current->previous)
-                               {
-                                       int autogrouptype = current->previous->autos->autogrouptype;
-                                       value = percentage_to_value(percentage, 0, 0, autogrouptype);
-                                       value1 = ((FloatAuto*)current->previous)->get_value();
-                                       distance1 = fabs(value - value1);
-                                       current->set_value(value1);
-                               }
+               old_value = current->get_value();
+               if(shift_down()) {
+                       double value1, value2, distance1, distance2;
 
-                               if(current->next)
-                               {
-                                       int autogrouptype = current->next->autos->autogrouptype;
-                                       value = percentage_to_value(percentage, 0, 0, autogrouptype);
-                                       value2 = ((FloatAuto*)current->next)->get_value();
-                                       distance2 = fabs(value - value2);
-                                       if(!current->previous || distance2 < distance1)
-                                       {
-                                               current->set_value(value2);
-                                       }
-                               }
+                       if(current->previous) {
+                               int autogrouptype = current->previous->autos->autogrouptype;
+                               value = percentage_to_value(percentage, 0, 0, autogrouptype);
+                               value1 = ((FloatAuto*)current->previous)->get_value();
+                               distance1 = fabs(value - value1);
+                               current->set_value(value1);
+                       }
 
-                               if(!current->previous && !current->next)
-                               {
-                                       current->set_value( ((FloatAutos*)current->autos)->default_);
+                       if(current->next) {
+                               int autogrouptype = current->next->autos->autogrouptype;
+                               value = percentage_to_value(percentage, 0, 0, autogrouptype);
+                               value2 = ((FloatAuto*)current->next)->get_value();
+                               distance2 = fabs(value - value2);
+                               if(!current->previous || distance2 < distance1) {
+                                       current->set_value(value2);
                                }
-                               value = current->get_value();
                        }
-                       else
-                       {
-                               int autogrouptype = current->autos->autogrouptype;
-                               value = percentage_to_value(percentage, 0, 0, autogrouptype);
+
+                       if(!current->previous && !current->next) {
+                               current->set_value( ((FloatAutos*)current->autos)->default_);
                        }
+                       value = current->get_value();
+               }
+               else {
+                       int autogrouptype = current->autos->autogrouptype;
+                       value = percentage_to_value(percentage, 0, 0, autogrouptype);
+               }
 
-                       if(alt_down())
+               if(alt_down() && !shift_down())
 // ALT constrains movement: fixed position, only changing the value
-                               position = mwindow->session->drag_start_position;
+                       position = mwindow->session->drag_start_position;
 
-                       if(value != old_value || position != current->position)
-                       {
-                               result = 1;
-                               float change = value - old_value;               
-                               current->adjust_to_new_coordinates(position, value);
-                               synchronize_autos(change, current->autos->track, current, 0);
-                               show_message(current, 1,", %.2f", current->get_value());
-                       }
-                       break;
+               if(value != old_value || position != current->position) {
+                       result = 1;
+                       float change = value - old_value;
+                       current->adjust_to_new_coordinates(position, value);
+                       synchronize_autos(change, current->autos->track, current, 0);
+                       show_message(current, 1,", %.2f", current->get_value());
+               }
+               break;
 
 // In control
-               case 1:
+       case 1: {
+               int autogrouptype = current->autos->autogrouptype;
+               value = percentage_to_value(percentage, 0, current, autogrouptype);
+               if(value != current->get_control_in_value())
                {
-                       int autogrouptype = current->autos->autogrouptype;
-                       value = percentage_to_value(percentage, 0, current, autogrouptype);
-                       if(value != current->get_control_in_value())
-                       {
-                               result = 1;
-                               // note: (position,value) need not be at the location of the ctrl point,
-                               // but could be somewhere in between on the curve (or even outward or
-                               // on the opposit side). We set the new control point such as
-                               // to point the curve through (position,value)
-                               current->set_control_in_value(
-                                       value * levered_position(position - current->position,
-                                                                current->get_control_in_position()));
-                               synchronize_autos(0, current->autos->track, current, 0);
-                               show_message(current, 1,", %.2f", current->get_control_in_value());
-                       }
+                       result = 1;
+                       // note: (position,value) need not be at the location of the ctrl point,
+                       // but could be somewhere in between on the curve (or even outward or
+                       // on the opposit side). We set the new control point such as
+                       // to point the curve through (position,value)
+                       current->set_control_in_value(
+                               value * levered_position(position - current->position,
+                                                        current->get_control_in_position()));
+                       synchronize_autos(0, current->autos->track, current, 0);
+                       show_message(current, 1,", %.2f", current->get_control_in_value());
                }
-                       break;
+               break; }
 
 // Out control
-               case 2:
-               {
-                       int autogrouptype = current->autos->autogrouptype;
-                       value = percentage_to_value(percentage, 0, current, autogrouptype);
-                       if(value != current->get_control_out_value())
-                       {
-                               result = 1;
-                               current->set_control_out_value(
-                                       value * levered_position(position - current->position,
-                                                                current->get_control_out_position()));
-                               synchronize_autos(0, current->autos->track, current, 0);
-                               show_message(current, 1,", %.2f", current->get_control_out_value());
-                       }
+       case 2: {
+               int autogrouptype = current->autos->autogrouptype;
+               value = percentage_to_value(percentage, 0, current, autogrouptype);
+               if(value != current->get_control_out_value()) {
+                       result = 1;
+                       current->set_control_out_value(
+                               value * levered_position(position - current->position,
+                                                        current->get_control_out_position()));
+                       synchronize_autos(0, current->autos->track, current, 0);
+                       show_message(current, 1,", %.2f", current->get_control_out_value());
                }
-                       break;
+               break; }
        }
 
        return result;
@@ -3674,7 +3740,7 @@ int TrackCanvas::update_drag_auto(int cursor_x, int cursor_y)
 
                double position_f = current->autos->track->from_units(current->position);
                double center_f = (mwindow->edl->local_session->get_selectionstart(1) +
-                       mwindow->edl->local_session->get_selectionend(1)) / 
+                       mwindow->edl->local_session->get_selectionend(1)) /
                        2;
                if(!shift_down())
                {
@@ -3706,7 +3772,7 @@ int TrackCanvas::update_drag_pluginauto(int cursor_x, int cursor_y)
                //PluginAutos *pluginautos = (PluginAutos *)current->autos;
                PluginSet *pluginset;
                Plugin *plugin = 0;
-// figure out the correct pluginset & correct plugin 
+// figure out the correct pluginset & correct plugin
                int found = 0;
                for(int i = 0; i < track->plugin_set.total; i++)
                {
@@ -3718,16 +3784,16 @@ int TrackCanvas::update_drag_pluginauto(int cursor_x, int cursor_y)
                                        currentkeyframe;
                                        currentkeyframe = (KeyFrame *) currentkeyframe->next)
                                {
-                                       if (currentkeyframe == current) 
+                                       if (currentkeyframe == current)
                                        {
                                                found = 1;
                                                break;
                                        }
+
                                }
-                               if (found) break;                       
+                               if (found) break;
                        }
-                       if (found) break;                       
+                       if (found) break;
                }
 
                mwindow->session->plugin_highlighted = plugin;
@@ -3738,7 +3804,7 @@ int TrackCanvas::update_drag_pluginauto(int cursor_x, int cursor_y)
 
                double position_f = current->autos->track->from_units(current->position);
                double center_f = (mwindow->edl->local_session->get_selectionstart(1) +
-                       mwindow->edl->local_session->get_selectionend(1)) / 
+                       mwindow->edl->local_session->get_selectionend(1)) /
                        2;
                if(!shift_down())
                {
@@ -3763,7 +3829,7 @@ void TrackCanvas::update_drag_caption()
        switch(mwindow->session->current_operation)
        {
                case DRAG_FADE:
-                       
+
                        break;
        }
 }
@@ -4365,7 +4431,7 @@ int TrackCanvas::do_tracks(int cursor_x, int cursor_y, int button_press)
                int64_t track_x, track_y, track_w, track_h;
                track_dimensions(track, track_x, track_y, track_w, track_h);
 
-               if(button_press && get_buttonpress() == 3 &&
+               if(button_press && get_buttonpress() == RIGHT_BUTTON &&
                        cursor_y >= track_y && cursor_y < track_y + track_h) {
                        gui->edit_menu->update(track, 0);
                        gui->edit_menu->activate_menu();
@@ -4441,10 +4507,12 @@ int TrackCanvas::do_edits(int cursor_x, int cursor_y, int button_press, int drag
                                                        mwindow->edl->local_session->zoom_sample /
                                                        mwindow->edl->session->sample_rate;
 
+                                               int cx, cy;
+                                               get_abs_cursor(cx, cy);
+                                               cx -= mwindow->theme->get_image("clip_icon")->get_w() / 2,
+                                               cy -= mwindow->theme->get_image("clip_icon")->get_h() / 2;
                                                gui->drag_popup = new BC_DragWindow(gui,
-                                                       mwindow->theme->get_image("clip_icon") /*,
-                                                       get_abs_cursor_x(0) - mwindow->theme->get_image("clip_icon")->get_w() / 2,
-                                                       get_abs_cursor_y(0) - mwindow->theme->get_image("clip_icon")->get_h() / 2 */);
+                                                       mwindow->theme->get_image("clip_icon"), cx, cy);
 
                                                result = 1;
                                        }
@@ -4553,19 +4621,22 @@ int TrackCanvas::do_plugins(int cursor_x, int cursor_y, int drag_start,
                                                        frame = mwindow->theme->get_image("veffect_icon");
                                                }
                                        }
-
-                                       gui->drag_popup = new BC_DragWindow(gui, frame /*,
-                                               get_abs_cursor_x(0) - frame->get_w() / 2,
-                                               get_abs_cursor_y(0) - frame->get_h() / 2 */);
+                                       int cx, cy;
+                                       get_abs_cursor(cx, cy);
+                                       cx -= frame->get_w() / 2;
+                                       cy -= frame->get_h() / 2;
+                                       gui->drag_popup = new BC_DragWindow(gui, frame, cx, cy);
                                                break; }
 
                                case PLUGIN_SHAREDPLUGIN:
-                               case PLUGIN_SHAREDMODULE:
-                                       gui->drag_popup = new BC_DragWindow(gui,
-                                               mwindow->theme->get_image("clip_icon") /*,
-                                               get_abs_cursor_x(0) - mwindow->theme->get_image("clip_icon")->get_w() / 2,
-                                               get_abs_cursor_y(0) - mwindow->theme->get_image("clip_icon")->get_h() / 2 */);
-                                       break;
+                               case PLUGIN_SHAREDMODULE: {
+                                       VFrame *frame = mwindow->theme->get_image("clip_icon");
+                                       int cx, cy;
+                                       get_abs_cursor(cx, cy);
+                                       cx -= frame->get_w() / 2;
+                                       cy -= frame->get_h() / 2;
+                                       gui->drag_popup = new BC_DragWindow(gui, frame, cx, cy);
+                                       break; }
                                }
 
                                result = 1;
@@ -4643,7 +4714,8 @@ int TrackCanvas::button_press_event()
                        activate();
                }
 
-               if( get_buttonpress() == LEFT_BUTTON ) {
+               if( get_buttonpress() == LEFT_BUTTON &&
+                   gui->mbuttons->transport->engine->command->command != STOP ) {
                        gui->unlock_window();
                        gui->mbuttons->transport->handle_transport(STOP, 1, 0, 0);
                        gui->lock_window("TrackCanvas::button_press_event");
@@ -4687,8 +4759,7 @@ int TrackCanvas::button_press_event()
                        switch(mwindow->edl->session->editing_mode) {
 // Test handles and resource boundaries and highlight a track
                        case EDITING_ARROW: {
-                               if( mwindow->edl->session->auto_conf->transitions &&
-                                       do_transitions(cursor_x, cursor_y,
+                               if( do_transitions(cursor_x, cursor_y,
                                                1, new_cursor, update_cursor) ) break;
 
                                if( do_keyframes(cursor_x, cursor_y,
@@ -4727,8 +4798,7 @@ int TrackCanvas::button_press_event()
                                        mwindow->edl->session->sample_rate;
 //printf("TrackCanvas::button_press_event %d\n", position);
 
-                               if(mwindow->edl->session->auto_conf->transitions &&
-                                       do_transitions(cursor_x, cursor_y,
+                               if( do_transitions(cursor_x, cursor_y,
                                                1, new_cursor, update_cursor)) break;
                                if(do_keyframes(cursor_x, cursor_y,
                                        0, get_buttonpress(), new_cursor,
@@ -4750,6 +4820,7 @@ int TrackCanvas::button_press_event()
 
                                if( do_tracks(cursor_x, cursor_y, 1) ) break;
 // Highlight selection
+                               if( get_buttonpress() != LEFT_BUTTON ) break;
                                rerender = start_selection(position);
                                mwindow->session->current_operation = SELECT_REGION;
                                update_cursor = 1;
@@ -4769,31 +4840,32 @@ int TrackCanvas::button_press_event()
                if( update_overlay ) {
                        gui->draw_overlays(1);
                }
-
-               if( update_cursor > 0 ) {
+               if( update_cursor < 0 ) {
+// double_click edit
+                       gui->swindow->update_selection();
+               }
+               if( update_cursor ) {
                        gui->update_timebar(0);
                        gui->hide_cursor(0);
                        gui->show_cursor(1);
                        gui->zoombar->update();
                        gui->flash_canvas(1);
                }
-               else if(update_cursor < 0) {
-                       gui->swindow->update_selection();
-               }
        }
+
        return result;
 }
 
 int TrackCanvas::start_selection(double position)
 {
        int rerender = 0;
-       position = mwindow->edl->align_to_frame(position, 0);
+       position = mwindow->edl->align_to_frame(position, 1);
 
 
 // Extend a border
        if(shift_down())
        {
-               double midpoint = (mwindow->edl->local_session->get_selectionstart(1) + 
+               double midpoint = (mwindow->edl->local_session->get_selectionstart(1) +
                        mwindow->edl->local_session->get_selectionend(1)) / 2;
 
                if(position < midpoint)
@@ -4820,7 +4892,7 @@ int TrackCanvas::start_selection(double position)
 // Que the CWindow
                rerender = 1;
        }
-       
+
        return rerender;
 }
 
@@ -4837,8 +4909,8 @@ void TrackCanvas::end_pluginhandle_selection()
 
 double TrackCanvas::time_visible()
 {
-       return (double)get_w() * 
-               mwindow->edl->local_session->zoom_sample / 
+       return (double)get_w() *
+               mwindow->edl->local_session->zoom_sample /
                mwindow->edl->session->sample_rate;
 }
 
@@ -4852,7 +4924,7 @@ void TrackCanvas::show_message(Auto *current, int show_curve_type, const char *f
                        FloatAuto::curve_name(((FloatAuto*)current)->curve_mode));
        }
        char string2[BCTEXTLEN];
-       Units::totext(string2, 
+       Units::totext(string2,
                current->autos->track->from_units(current->position),
                mwindow->edl->session->time_format,
                mwindow->edl->session->sample_rate,
@@ -4880,7 +4952,7 @@ void TrackCanvas::show_message(Auto *current, int show_curve_type, const char *f
 //             else
 //                     return gui->pane[BOTTOM_LEFT_PANE]->patchbay;
 //     }
-// 
+//
 //     return 0;
 // }