/* * CINELERRA * Copyright (C) 1997-2014 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "apatchgui.inc" #include "asset.h" #include "autoconf.h" #include "automation.h" #include "bcsignals.h" #include "bctimer.h" #include "clip.h" #include "bccolors.h" #include "cache.h" #include "canvas.h" #include "cplayback.h" #include "cursors.h" #include "cwindowgui.h" #include "cwindow.h" #include "edithandles.h" #include "editpopup.h" #include "edits.h" #include "edl.h" #include "edlsession.h" #include "floatauto.h" #include "floatautos.h" #include "gwindowgui.h" #include "indexstate.h" #include "intauto.h" #include "intautos.h" #include "keyframe.h" #include "keyframepopup.h" #include "keyframes.h" #include "keys.h" #include "labels.h" #include "localsession.h" #include "mainclock.h" #include "maincursor.h" #include "mainsession.h" #include "mainundo.h" #include "maskautos.h" #include "mbuttons.h" #include "mtimebar.h" #include "mwindowgui.h" #include "mwindow.h" #include "panautos.h" #include "patchbay.h" #include "playbackengine.h" #include "playtransport.h" #include "plugin.h" #include "pluginpopup.h" #include "pluginserver.h" #include "pluginset.h" #include "plugintoggles.h" #include "preferences.h" #include "renderengine.h" #include "resourcepixmap.h" #include "resourcethread.h" #include "swindow.h" #include "theme.h" #include "trackcanvas.h" #include "tracking.h" #include "trackpopup.h" #include "tracks.h" #include "transition.h" #include "transitionhandles.h" #include "transitionpopup.h" #include "transportque.h" #include "vframe.h" #include "vpatchgui.inc" #include "vrender.h" #include "zoombar.h" #include //#define PIXMAP_AGE -5 #define PIXMAP_AGE -32 TrackCanvas::TrackCanvas(MWindow *mwindow, TimelinePane *pane, int x, int y, int w, int h) : BC_SubWindow(x, y, w, h) { this->mwindow = mwindow; this->gui = mwindow->gui; this->pane = pane; selection_midpoint = 0; drag_scroll = 0; active = 0; temp_picon = 0; resource_timer = new Timer; render_timer = new Timer; hourglass_enabled = 0; timebar_position = -1; snapped = 0; } TrackCanvas::~TrackCanvas() { // delete transition_handles; delete edit_handles; delete keyframe_pixmap; delete camerakeyframe_pixmap; delete modekeyframe_pixmap; delete pankeyframe_pixmap; delete projectorkeyframe_pixmap; delete maskkeyframe_pixmap; delete background_pixmap; if(temp_picon) delete temp_picon; delete render_timer; delete resource_timer; } void TrackCanvas::create_objects() { background_pixmap = new BC_Pixmap(this, get_w(), get_h()); // transition_handles = new TransitionHandles(mwindow, this); edit_handles = new EditHandles(mwindow, this); keyframe_pixmap = new BC_Pixmap(this, mwindow->theme->keyframe_data, PIXMAP_ALPHA); camerakeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->camerakeyframe_data, PIXMAP_ALPHA); modekeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->modekeyframe_data, PIXMAP_ALPHA); pankeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->pankeyframe_data, PIXMAP_ALPHA); projectorkeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->projectorkeyframe_data, PIXMAP_ALPHA); maskkeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->maskkeyframe_data, PIXMAP_ALPHA); draw(NORMAL_DRAW, 1); update_cursor(0); flash(0); } void TrackCanvas::resize_event() { //printf("TrackCanvas::resize_event 1\n"); draw(NORMAL_DRAW, 0); flash(0); //printf("TrackCanvas::resize_event 2\n"); } int TrackCanvas::keypress_event() { return 0; } int TrackCanvas::cursor_leave_event() { // Because drag motion calls get_cursor_over_window we can be sure that // all highlights get deleted now. // This ended up blocking keyboard input from the drag operations. if( timebar_position >= 0 ) { timebar_position = -1; if( pane->timebar ) pane->timebar->update(1); } return 0; // return drag_motion(); } int TrackCanvas::drag_motion_event() { return gui->drag_motion(); } int TrackCanvas::drag_motion( Track **over_track, Edit **over_edit, PluginSet **over_pluginset, Plugin **over_plugin) { int cursor_x = get_relative_cursor_x(); int cursor_y = get_relative_cursor_y(); if( get_cursor_over_window() ) { drag_cursor_motion(cursor_x, cursor_y, over_track, over_edit, over_pluginset, over_plugin); } if( over_track && !*over_track ) *over_track = pane->over_patchbay(); return 0; } int TrackCanvas::drag_cursor_motion(int cursor_x, int cursor_y, Track **over_track, Edit **over_edit, PluginSet **over_pluginset, Plugin **over_plugin) { if( cursor_x >= 0 && cursor_y >= 0 && cursor_x < get_w() && cursor_y < get_h() ) { //printf("TrackCanvas::drag_motion %d %d\n", __LINE__, pane->number); // Find the edit and track the cursor is over for(Track *track = mwindow->edl->tracks->first; track; track = track->next) { int64_t track_x, track_y, track_w, track_h; track_dimensions(track, track_x, track_y, track_w, track_h); if(cursor_y >= track_y && cursor_y < track_y + track_h) { *over_track = track; for(Edit *edit = track->edits->first; edit; edit = edit->next) { int64_t edit_x, edit_y, edit_w, edit_h; edit_dimensions(edit, edit_x, edit_y, edit_w, 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++) { PluginSet *pluginset = track->plugin_set.values[i]; for(Plugin *plugin = (Plugin*)pluginset->first; plugin; plugin = (Plugin*)plugin->next) { int64_t plugin_x, plugin_y, plugin_w, plugin_h; plugin_dimensions(plugin, plugin_x, plugin_y, plugin_w, plugin_h); if(cursor_y >= plugin_y && cursor_y < plugin_y + plugin_h) { *over_pluginset = plugin->plugin_set; if(cursor_x >= plugin_x && cursor_x < plugin_x + plugin_w) { *over_plugin = plugin; break; } } } } break; } } } return 0; } int TrackCanvas::drag_stop_event() { int result = gui->drag_stop(); if( !result && mwindow->session->current_operation ) { mwindow->session->current_operation = NO_OPERATION; drag_scroll = 0; } return result; } int TrackCanvas::drag_stop(int *redraw) { int result = 0; int insertion = 0; int over_window = 0; int cursor_x = -1, cursor_y = -1; if( get_cursor_over_window() ) { if( (cursor_x = get_relative_cursor_x()) >= 0 && cursor_x < get_w() && (cursor_y = get_relative_cursor_y()) >= 0 && cursor_y < get_h() ) over_window = 1; else { Track *track = pane->over_patchbay(); if( track && mwindow->session->track_highlighted == track ) over_window = 1; } } if( over_window ) { switch(mwindow->session->current_operation) { case DRAG_VTRANSITION: case DRAG_ATRANSITION: if(mwindow->session->edit_highlighted) { if( (mwindow->session->current_operation == DRAG_ATRANSITION && mwindow->session->track_highlighted->data_type == TRACK_AUDIO) || (mwindow->session->current_operation == DRAG_VTRANSITION && mwindow->session->track_highlighted->data_type == TRACK_VIDEO) ) { mwindow->session->current_operation = NO_OPERATION; mwindow->paste_transition(); result = 1; } } *redraw = 1; break; // Behavior for dragged plugins is limited by the fact that a shared plugin // can only refer to a standalone plugin that exists in the same position in // time. Dragging a plugin from one point in time to another can't produce // a shared plugin to the original plugin. In this case we relocate the // plugin instead of sharing it. case DRAG_AEFFECT_COPY: case DRAG_VEFFECT_COPY: if( mwindow->session->track_highlighted && ((mwindow->session->current_operation == DRAG_AEFFECT_COPY && mwindow->session->track_highlighted->data_type == TRACK_AUDIO) || (mwindow->session->current_operation == DRAG_VEFFECT_COPY && mwindow->session->track_highlighted->data_type == TRACK_VIDEO)) ) { mwindow->session->current_operation = NO_OPERATION; int64_t drop_position = -1; Plugin *drag_plugin = mwindow->session->drag_plugin; PluginSet *hi_plugin_set = mwindow->session->pluginset_highlighted; Track *hi_track = mwindow->session->track_highlighted; // Insert shared plugin in source // Move source to different location if( hi_plugin_set && hi_plugin_set->track == drag_plugin->track ) { //printf("TrackCanvas::drag_stop 6\n"); drop_position = drop_plugin_position(hi_plugin_set, drag_plugin); if( drop_position < 0 ) { result = 1; break; // Do not do anything } Track *track = mwindow->session->track_highlighted; drop_position = track->frame_align(drop_position, 0); mwindow->move_effect(drag_plugin, hi_plugin_set, drop_position); result = 1; } else if( hi_track ) { // Put it in a new plugin set determined by an edit boundary, or at the start of the track Edit *hi_edit = mwindow->session->edit_highlighted; drop_position = hi_edit ? hi_edit->startproject : 0; if( drop_position < 0 ) { result = 1; break; // Do not do anything } Track *track = mwindow->session->track_highlighted; drop_position = track->frame_align(drop_position, 0); mwindow->move_effect(drag_plugin, hi_track, drop_position); result = 1; } } break; case DRAG_AEFFECT: case DRAG_VEFFECT: if( mwindow->session->track_highlighted && ((mwindow->session->current_operation == DRAG_AEFFECT && mwindow->session->track_highlighted->data_type == TRACK_AUDIO) || (mwindow->session->current_operation == DRAG_VEFFECT && mwindow->session->track_highlighted->data_type == TRACK_VIDEO)) ) { // Drop all the effects PluginSet *plugin_set = mwindow->session->pluginset_highlighted; Track *track = mwindow->session->track_highlighted; double start = 0; double length = track->get_length(); if(mwindow->session->plugin_highlighted) { start = track->from_units(mwindow->session->plugin_highlighted->startproject); length = track->from_units(mwindow->session->plugin_highlighted->length); if(length <= 0) length = track->get_length(); } else if(mwindow->session->pluginset_highlighted) { start = track->from_units(plugin_set->length()); length = track->get_length() - start; if(length <= 0) length = track->get_length(); } else if(mwindow->edl->local_session->get_selectionend() > mwindow->edl->local_session->get_selectionstart()) { start = mwindow->edl->local_session->get_selectionstart(); length = mwindow->edl->local_session->get_selectionend() - mwindow->edl->local_session->get_selectionstart(); } // Move to a point between two edits else if(mwindow->session->edit_highlighted) { start = mwindow->session->track_highlighted->from_units( mwindow->session->edit_highlighted->startproject); length = mwindow->session->track_highlighted->from_units( mwindow->session->edit_highlighted->length); } start = mwindow->edl->align_to_frame(start, 0); mwindow->insert_effects_canvas(start, length); *redraw = 1; } if( mwindow->session->track_highlighted ) result = 1; // we have to cleanup break; case DRAG_ASSET: if(mwindow->session->track_highlighted) { double asset_duration = 0; int64_t asset_length_units = 0; if(mwindow->session->current_operation == DRAG_ASSET && mwindow->session->drag_assets->total) { Indexable *indexable = mwindow->session->drag_assets->values[0]; // we use video if we are over video and audio if we are over audio if( indexable->have_video() && mwindow->session->track_highlighted->data_type == TRACK_VIDEO ) { // Images have length -1 int64_t video_length = indexable->get_video_frames(); 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 ; } asset_duration = video_length / indexable->get_frame_rate(); } else if( indexable->have_audio() && mwindow->session->track_highlighted->data_type == TRACK_AUDIO ) { int64_t audio_length = indexable->get_audio_samples(); asset_duration = (double)audio_length / indexable->get_sample_rate(); } else { result = 1; break; // Do not do anything } } else if( mwindow->session->current_operation == DRAG_ASSET && mwindow->session->drag_clips->total ) { EDL *clip = mwindow->session->drag_clips->values[0]; asset_duration = clip->tracks->total_length(); } 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); int64_t drop_position = drop_edit_position (&insertion, NULL, asset_length_units); if( drop_position < 0 ) { result = 1; break; // Do not do anything } Track *track = mwindow->session->track_highlighted; double track_position = track->from_units(drop_position); track_position = mwindow->edl->align_to_frame(track_position, 0); // if (!insertion) { // // 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(track_position, // track_position + asset_duration, 0); // } mwindow->paste_assets(track_position, track, !insertion); result = 1; // need to be one no matter what, since we have track highlited so we have to cleanup.... } break; case DRAG_EDIT: mwindow->session->current_operation = NO_OPERATION; if(mwindow->session->track_highlighted) { if( mwindow->session->track_highlighted->data_type == mwindow->session->drag_edit->track->data_type) { int64_t drop_position = drop_edit_position(&insertion, mwindow->session->drag_edit, mwindow->session->drag_edit->length); if( drop_position < 0 ) { result = 1; break; // Do not do anything } Track *track = mwindow->session->track_highlighted; double track_position = track->from_units(drop_position); track_position = mwindow->edl->align_to_frame(track_position, 0); mwindow->move_edits(mwindow->session->drag_edits, track, track_position, !insertion); } result = 1; } break; case DRAG_GROUP: mwindow->session->current_operation = NO_OPERATION; EDL *drag_group = mwindow->session->drag_group; if( drag_group ) { Track *drop_track = mwindow->session->track_highlighted; Track *drag_track = mwindow->session->drag_group_first_track; Edit *drag_edit = mwindow->session->drag_group_edit; Track *edit_track = drag_edit ? drag_edit->track : 0; while( drop_track && edit_track ) { if( edit_track == drag_track ) break; edit_track = edit_track->previous; drop_track = drop_track->previous; } if( drop_track ) { double cur_pos = mwindow->session->drag_group_position; double new_pos = mwindow->edl->get_cursor_position(cursor_x, pane->number); new_pos -= mwindow->session->drag_position - cur_pos; new_pos = mwindow->edl->align_to_frame(new_pos, 0); double drop_position = new_pos; int ret = test_track_group(drag_group, drop_track, new_pos); if( !ret && new_pos != drop_position ) { drop_position = new_pos; ret = test_track_group(drag_group, drop_track, new_pos); } if( ret >= 0 ) mwindow->move_group(drag_group, drop_track, drop_position, ret > 0 ? !shift_down() : shift_down()); drag_group->remove_user(); mwindow->session->drag_group = 0; } } result = 1; break; } } return result; } int TrackCanvas::drag_start_event() { int result = 0; int redraw = 0; int rerender = 0; int new_cursor, update_cursor; if( mwindow->session->current_operation == GROUP_TOGGLE ) mwindow->session->current_operation = NO_OPERATION; else if( mwindow->session->current_operation != NO_OPERATION ) return 0; if(is_event_win()) { if(do_plugins(get_drag_x(), get_drag_y(), 1, 0, redraw, rerender)) { result = 1; } else if(do_edits(get_drag_x(), get_drag_y(), 0, 1, redraw, rerender, new_cursor, update_cursor)) { result = 1; } } return result; } int64_t TrackCanvas::drop_edit_position(int *is_insertion, Edit *moved_edit, int64_t moved_edit_length) { // get the canvas/track position Track *track = mwindow->session->track_highlighted; int cursor_x = get_relative_cursor_x(); double zoom_scale = (double)mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample; double cur_pos = (cursor_x + mwindow->edl->local_session->view_start[pane->number]) / zoom_scale; double position = mwindow->edl->align_to_frame(cur_pos, 1); if( position <= 0 ) { *is_insertion = 1; return 0; } double cursor_position = position; int64_t drop_position = track->to_units(cursor_position, 1); if( moved_edit ) { // relative cursor position depends upon drop point double moved_edit_start = moved_edit->track->from_units(moved_edit->startproject); position -= mwindow->session->drag_position - moved_edit_start; } int64_t edit_position = track->to_units(position, 1); int64_t grab_position = edit_position; if( !moved_edit ) // for clips and assets acts as they were grabbed in the middle grab_position -= moved_edit_length / 2; Edit *last_edit = track->edits->last; if( !last_edit || edit_position >= (last_edit->startproject+last_edit->length) ) { *is_insertion = 0; return edit_position; } int64_t drop_start = 0, drop_end = 0; // drop zone boundries Edit *next_edit = track->edits->first; while( next_edit ) { Edit *edit = next_edit; next_edit = (Edit *)edit->next; int64_t edit_start = edit->startproject; int64_t edit_end = edit_start + edit->length; double edit_start_pos = edit->track->from_units(edit_start); if( (fabs(edit_start_pos-cursor_position)*zoom_scale) < HANDLE_W ) { *is_insertion = 1; // cursor is close to the beginning of an edit -> insertion return edit_start; } double edit_end_pos = edit->track->from_units(edit_end); if( (fabs(edit_end_pos-cursor_position)*zoom_scale) < HANDLE_W ) { *is_insertion = 1; // cursor is close to the end of an edit -> insertion return edit_end; } if( edit != moved_edit && !edit->silence() ) drop_start = edit_end; // reset drop zone if( next_edit ) { if( next_edit == moved_edit || next_edit->silence() ) continue; drop_end = edit_end; } else drop_end = INT64_MAX; if( edit_position >= drop_start && edit_position+moved_edit_length < drop_end ) { *is_insertion = 0; // fits in the zone return edit_position; } if( drop_position < drop_end ) { // drop in the zone if( (drop_end - drop_start) >= moved_edit_length ) { *is_insertion = 0; // fits in the zone, but over the edge int64_t dx0 = llabs(drop_position - drop_start); int64_t dx1 = llabs(drop_position - drop_end); return dx0 < dx1 ? drop_start : drop_end - moved_edit_length; } *is_insertion = 1; int64_t edit_center = (edit_start + edit_end) / 2; return position < edit_center ? drop_start : drop_end; } } *is_insertion = 0; return -1; } int64_t TrackCanvas::drop_plugin_position(PluginSet *plugin_set, Plugin *moved_plugin) { // get the canvas/track position Track *track = plugin_set->track; double moved_plugin_length = moved_plugin->track->from_units(moved_plugin->length); int64_t track_plugin_length = track->to_units(moved_plugin_length, 0); int cursor_x = get_relative_cursor_x(); double zoom_scale = (double)mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample; double cur_pos = (cursor_x + mwindow->edl->local_session->view_start[pane->number]) / zoom_scale; double position = mwindow->edl->align_to_frame(cur_pos, 1); if( position <= 0 ) return 0; int64_t drop_position = track->to_units(position, 1); Plugin *last_plugin = (Plugin *)plugin_set->last; if( !last_plugin ) return drop_position; double plugin_set_end = last_plugin->track->from_units(last_plugin->startproject+last_plugin->length); if( position >= plugin_set_end ) return drop_position; double moved_plugin_start = moved_plugin->track->from_units(moved_plugin->startproject); position -= mwindow->session->drag_position - moved_plugin_start; int64_t plugin_position = track->to_units(position, 1); int64_t drop_start = 0, drop_end = 0; // drop zone boundries Plugin *next_plugin = (Plugin *)plugin_set->first; while( next_plugin ) { Plugin *plugin = next_plugin; next_plugin = (Plugin *)plugin->next; int64_t plugin_start = plugin->startproject; int64_t plugin_end = plugin_start + plugin->length; double plugin_end_pos = plugin->track->from_units(plugin_end); int64_t track_plugin_end = track->to_units(plugin_end_pos, 0); if( plugin != moved_plugin && !plugin->silence() ) drop_start = track_plugin_end; if( next_plugin ) { if( next_plugin == moved_plugin || next_plugin->silence() ) continue; drop_end = track_plugin_end; } else drop_end = INT64_MAX; if( plugin_position >= drop_start && // fits in the zone plugin_position+track_plugin_length < drop_end ) { return plugin_position; } if( drop_position < drop_end ) { // drop in the zone if( (drop_end - drop_start) >= track_plugin_length ) { int64_t dx0 = llabs(drop_position - drop_start); int64_t dx1 = llabs(drop_position - drop_end); return dx0 < dx1 ? drop_start : drop_end - track_plugin_length; } } } return -1; } void TrackCanvas::draw(int mode, int hide_cursor) { const int debug = 0; // Swap pixmap layers if(get_w() != background_pixmap->get_w() || get_h() != background_pixmap->get_h()) { delete background_pixmap; background_pixmap = new BC_Pixmap(this, get_w(), get_h()); } // Cursor disappears after resize when this is called. // Cursor doesn't redraw after editing when this isn't called. if(pane->cursor && hide_cursor) pane->cursor->hide(); draw_top_background(get_parent(), 0, 0, get_w(), get_h(), background_pixmap); if(debug) PRINT_TRACE draw_resources(mode); if(debug) PRINT_TRACE draw_overlays(); if(debug) PRINT_TRACE } void TrackCanvas::update_cursor(int flush) { if( arrow_mode() ) set_cursor(ARROW_CURSOR, 0, flush); else if( ibeam_mode() ) set_cursor(IBEAM_CURSOR, 0, flush); } void TrackCanvas::test_timer() { if( resource_timer->get_difference() > 1000 && !hourglass_enabled ) { start_hourglass(); hourglass_enabled = 1; } } void TrackCanvas::draw_indexes(Indexable *indexable) { IndexState *index_state = indexable->index_state; // Don't redraw raw samples if(index_state->index_zoom > mwindow->edl->local_session->zoom_sample) return; draw_resources(NORMAL_DRAW, 1, indexable); draw_overlays(); flash(0); } void TrackCanvas::draw_resources(int mode, int indexes_only, Indexable *indexable) { const int debug = 0; if(debug) PRINT_TRACE // can't stop thread here, because this is called for every pane // if(mode != IGNORE_THREAD && !indexes_only) // gui->resource_thread->stop_draw(!indexes_only); if(mode != IGNORE_THREAD && !indexes_only && !gui->resource_thread->interrupted) { printf("TrackCanvas::draw_resources %d: called without stopping ResourceThread\n", __LINE__); BC_Signals::dump_stack(); } if(debug) PRINT_TRACE resource_timer->update(); // Age resource pixmaps for deletion if(!indexes_only) for(int i = 0; i < gui->resource_pixmaps.total; i++) gui->resource_pixmaps.values[i]->visible--; if(mode == FORCE_REDRAW) gui->resource_pixmaps.remove_all_objects(); if(debug) PRINT_TRACE // Search every edit for(Track *current = mwindow->edl->tracks->first; current; current = NEXT) { if(debug) PRINT_TRACE for(Edit *edit = current->edits->first; edit; edit = edit->next) { if(debug) PRINT_TRACE if( current->data_type != TRACK_SUBTITLE ) if(!edit->asset && !edit->nested_edl) continue; if(indexes_only) { if(edit->track->data_type != TRACK_AUDIO) continue; if(edit->nested_edl && strcmp(indexable->path, edit->nested_edl->path)) continue; if(edit->asset && strcmp(indexable->path, edit->asset->path)) continue; } if(debug) PRINT_TRACE int64_t edit_x, edit_y, edit_w, edit_h; edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h); // Edit is visible if(MWindowGUI::visible(edit_x, edit_x + edit_w, 0, get_w()) && MWindowGUI::visible(edit_y, edit_y + edit_h, 0, get_h())) { int64_t pixmap_x, pixmap_w, pixmap_h; if(debug) PRINT_TRACE // Search for existing pixmap containing edit for(int i = 0; i < gui->resource_pixmaps.total; i++) { ResourcePixmap* pixmap = gui->resource_pixmaps.values[i]; // Same pointer can be different edit if editing took place if(pixmap->edit_id == edit->id && pixmap->pane_number == pane->number) { pixmap->visible = 1; break; } } if(debug) PRINT_TRACE // Get new size, offset of pixmap needed get_pixmap_size(edit, edit_x, edit_w, pixmap_x, pixmap_w, pixmap_h); if(debug) PRINT_TRACE // Draw new data if(pixmap_w && pixmap_h) { // Create pixmap if it doesn't exist ResourcePixmap* pixmap = create_pixmap(edit, edit_x, pixmap_x, pixmap_w, pixmap_h); // Resize it if it's bigger if( pixmap_w > pixmap->pixmap_w || pixmap_h > pixmap->pixmap_h) pixmap->resize(pixmap_w, pixmap_h); pixmap->update_settings(edit, edit_x, edit_w, pixmap_x, pixmap_w, pixmap_h); // Draw data if( current->show_assets() ) pixmap->draw_data(this, edit, edit_x, edit_w, pixmap_x, pixmap_w, pixmap_h, mode, indexes_only); else { set_bg_color(BLACK); clear_box(0,0, pixmap_w,pixmap_h, pixmap); } // Draw title if( current->show_titles() ) pixmap->draw_title(this, edit, edit_x, edit_w, pixmap_x, pixmap_w); // Resize it if it's smaller if(pixmap_w < pixmap->pixmap_w || pixmap_h < pixmap->pixmap_h) pixmap->resize(pixmap_w, pixmap_h); // Copy pixmap to background canvas background_pixmap->draw_pixmap(pixmap, pixmap->pixmap_x, current->y_pixel - mwindow->edl->local_session->track_start[pane->number], pixmap->pixmap_w, edit_h); } if(debug) PRINT_TRACE } } } // Delete unused pixmaps if(debug) PRINT_TRACE if(!indexes_only) for(int i = gui->resource_pixmaps.total - 1; i >= 0; i--) if(gui->resource_pixmaps.values[i]->visible < PIXMAP_AGE) { //printf("TrackCanvas::draw_resources %d\n", __LINE__); delete gui->resource_pixmaps.values[i]; gui->resource_pixmaps.remove(gui->resource_pixmaps.values[i]); } if(debug) PRINT_TRACE if(hourglass_enabled) { stop_hourglass(); hourglass_enabled = 0; } if(debug) PRINT_TRACE // can't stop thread here, because this is called for every pane // if(mode != IGNORE_THREAD && !indexes_only) // gui->resource_thread->start_draw(); if(debug) PRINT_TRACE } ResourcePixmap* TrackCanvas::create_pixmap(Edit *edit, int64_t edit_x, int64_t pixmap_x, int64_t pixmap_w, int64_t pixmap_h) { ResourcePixmap *result = 0; for(int i = 0; i < gui->resource_pixmaps.total; i++) { //printf("TrackCanvas::create_pixmap 1 %d %d\n", edit->id, resource_pixmaps.values[i]->edit->id); if(gui->resource_pixmaps.values[i]->edit_id == edit->id && gui->resource_pixmaps.values[i]->pane_number == pane->number) { result = gui->resource_pixmaps.values[i]; break; } } if(!result) { //SET_TRACE result = new ResourcePixmap(mwindow, gui, edit, pane->number, pixmap_w, pixmap_h); //SET_TRACE gui->resource_pixmaps.append(result); } // result->resize(pixmap_w, pixmap_h); return result; } void TrackCanvas::get_pixmap_size(Edit *edit, int64_t edit_x, int64_t edit_w, int64_t &pixmap_x, int64_t &pixmap_w, int64_t &pixmap_h) { // Align x on frame boundaries // switch(edit->edits->track->data_type) // { // case TRACK_AUDIO: pixmap_x = edit_x; pixmap_w = edit_w; if(pixmap_x < 0) { pixmap_w -= -edit_x; pixmap_x = 0; } if(pixmap_x + pixmap_w > get_w()) { pixmap_w = get_w() - pixmap_x; } // break; // // case TRACK_VIDEO: // { // int64_t picon_w = (int64_t)(edit->picon_w() + 0.5); // int64_t frame_w = (int64_t)(edit->frame_w() + 0.5); // int64_t pixel_increment = MAX(picon_w, frame_w); // int64_t pixmap_x1 = edit_x; // int64_t pixmap_x2 = edit_x + edit_w; // // if(pixmap_x1 < 0) // { // pixmap_x1 = (int64_t)((double)-edit_x / pixel_increment) * // pixel_increment + // edit_x; // } // // if(pixmap_x2 > get_w()) // { // pixmap_x2 = (int64_t)((double)(get_w() - edit_x) / pixel_increment + 1) * // pixel_increment + // edit_x; // } // pixmap_x = pixmap_x1; // pixmap_w = pixmap_x2 - pixmap_x1; // break; // } // } pixmap_h = mwindow->edl->local_session->zoom_track; Track *track = edit->edits->track; if( track->show_titles() ) pixmap_h += mwindow->theme->get_image("title_bg_data")->get_h(); //printf("get_pixmap_size %d %d %d %d\n", edit_x, edit_w, pixmap_x, pixmap_w); } void TrackCanvas::edit_dimensions(Edit *edit, int64_t &x, int64_t &y, int64_t &w, int64_t &h) { x = Units::round(edit->track->from_units(edit->startproject) * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample - mwindow->edl->local_session->view_start[pane->number]); y = edit->edits->track->y_pixel - mwindow->edl->local_session->track_start[pane->number]; // Method for calculating w so when edits are together we never get off by one error due to rounding int64_t x_next = Units::round(edit->track->from_units(edit->startproject + edit->length) * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample - mwindow->edl->local_session->view_start[pane->number]); w = x_next - x; int edit_h = 0; if( edit->track->show_titles() ) edit_h += mwindow->theme->get_image("title_bg_data")->get_h(); if( edit->track->show_assets() ) edit_h += resource_h(); h = edit_h; } void TrackCanvas::track_dimensions(Track *track, int64_t &x, int64_t &y, int64_t &w, int64_t &h) { x = 0; w = get_w(); y = track->y_pixel - mwindow->edl->local_session->track_start[pane->number]; h = track->vertical_span(mwindow->theme); } void TrackCanvas::draw_paste_destination() { //int cursor_x = get_relative_cursor_x(); //int cursor_y = get_relative_cursor_y(); int current_atrack = 0; int current_vtrack = 0; int current_aedit = 0; int current_vedit = 0; int64_t w = 0; int64_t x; double position; int insertion = 0; //if(pane->number == BOTTOM_RIGHT_PANE) //printf("TrackCanvas::draw_paste_destination %d %p\n", __LINE__, mwindow->session->track_highlighted); if((mwindow->session->current_operation == DRAG_ASSET && (mwindow->session->drag_assets->total || mwindow->session->drag_clips->total)) || (mwindow->session->current_operation == DRAG_EDIT && mwindow->session->drag_edits->total)) { Indexable *indexable = 0; EDL *clip = 0; //int draw_box = 0; if(mwindow->session->current_operation == DRAG_ASSET && mwindow->session->drag_assets->size()) indexable = mwindow->session->drag_assets->get(0); if(mwindow->session->current_operation == DRAG_ASSET && mwindow->session->drag_clips->size()) clip = mwindow->session->drag_clips->get(0); int has_audio = 0, has_video = 0; double paste_audio_length = 0, paste_video_length = 0; double paste_audio_position = -1, paste_video_position = -1; if( indexable ) { has_audio = indexable->have_audio(); paste_audio_length = !has_audio ? 0 : (double)indexable->get_audio_samples() / indexable->get_sample_rate(); has_video = indexable->have_video(); Asset *asset = indexable->is_asset ? (Asset *)indexable : 0; // Images have length -1 if( asset && asset->video_data && asset->video_length < 0 ) { paste_video_length = mwindow->edl->session->si_useduration ? mwindow->edl->session->si_duration / asset->frame_rate : 1.0 / asset->frame_rate ; // 1 frame } else paste_video_length = !has_video ? 0 : (double)indexable->get_video_frames() / indexable->get_frame_rate(); } if( clip ) { has_audio = clip->tracks->total_audio_tracks() > 0 ? 1 : 0; paste_audio_length = !has_audio ? 0 : clip->tracks->total_audio_length(); has_video = clip->tracks->total_video_tracks() > 0 ? 1 : 0; paste_video_length = !has_video ? 0 : clip->tracks->total_video_length(); } // 'Align cursor on frame' lengths calculations if( mwindow->edl->session->cursor_on_frames ) { double fps = mwindow->edl->session->frame_rate; double aud = floor(paste_audio_length * fps) / fps; double vid = floor(paste_video_length * fps) / fps; double length = has_video && has_audio ? aud < vid ? aud : vid : has_video ? vid : has_audio ? aud : 0; paste_video_length = paste_audio_length = length; } if( has_audio ) { // we use video if we are over video and audio if we are over audio int64_t length = 0; switch( mwindow->session->track_highlighted->data_type ) { case TRACK_VIDEO: length = paste_video_length; break; case TRACK_AUDIO: length = paste_audio_length; break; } int64_t asset_length = mwindow->session->track_highlighted-> to_units(length, 1); paste_audio_position = mwindow->session->track_highlighted-> from_units(drop_edit_position(&insertion, NULL, asset_length)); } if( has_video ) { int64_t asset_length = mwindow->session->track_highlighted-> to_units(paste_video_length, 1); paste_video_position = mwindow->session->track_highlighted-> from_units(drop_edit_position(&insertion, NULL, asset_length)); } // Get destination track for(Track *dest = mwindow->session->track_highlighted; dest; dest = dest->next) { if(dest->record) { // Get source width in pixels w = -1; // Use start of highlighted edit if(mwindow->session->edit_highlighted) { position = mwindow->session->track_highlighted->from_units( mwindow->session->edit_highlighted->startproject); } else { // Use end of highlighted track, disregarding effects position = mwindow->session->track_highlighted->from_units( mwindow->session->track_highlighted->edits->length()); } // Get the x coordinate x = Units::to_int64(position * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample) - mwindow->edl->local_session->view_start[pane->number]; double paste_position = -1.; if(dest->data_type == TRACK_AUDIO) { if(indexable && current_atrack < indexable->get_audio_channels()) { //printf("TrackCanvas::draw_paste_destination %d %d\n", __LINE__, current_atrack); w = Units::to_int64((double)indexable->get_audio_samples() / indexable->get_sample_rate() * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample); paste_position = paste_audio_position; } else if(clip && current_atrack < clip->tracks->total_audio_tracks()) { w = Units::to_int64((double)clip->tracks->total_length() * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample); paste_position = paste_audio_position; //printf("draw_paste_destination %d\n", x); } else if(mwindow->session->current_operation == DRAG_EDIT && current_aedit < mwindow->session->drag_edits->total) { Edit *edit; while(current_aedit < mwindow->session->drag_edits->total && mwindow->session->drag_edits->values[current_aedit]->track->data_type != TRACK_AUDIO) current_aedit++; if(current_aedit < mwindow->session->drag_edits->total) { edit = mwindow->session->drag_edits->values[current_aedit]; w = Units::to_int64(edit->length / mwindow->edl->local_session->zoom_sample); paste_position = mwindow->session->track_highlighted-> from_units(drop_edit_position(&insertion, mwindow->session->drag_edit, mwindow->session->drag_edit->length)); current_aedit++; } } if( paste_position >= 0 ) { position = paste_position; current_atrack++; //draw_box = 1; } else w = -1; } else if(dest->data_type == TRACK_VIDEO) { //printf("draw_paste_destination 1\n"); if(indexable && current_vtrack < indexable->get_video_layers()) { w = Units::to_int64((double)indexable->get_video_frames() / indexable->get_frame_rate() * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample); paste_position = paste_video_position; } else if(clip && current_vtrack < clip->tracks->total_video_tracks()) { w = Units::to_int64(clip->tracks->total_length() * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample); paste_position = paste_video_position; } else if(mwindow->session->current_operation == DRAG_EDIT && current_vedit < mwindow->session->drag_edits->total) { Edit *edit; while(current_vedit < mwindow->session->drag_edits->total && mwindow->session->drag_edits->values[current_vedit]->track->data_type != TRACK_VIDEO) current_vedit++; if(current_vedit < mwindow->session->drag_edits->total) { edit = mwindow->session->drag_edits->values[current_vedit]; w = Units::to_int64(edit->track->from_units(edit->length) * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample); paste_position = mwindow->session->track_highlighted-> from_units(drop_edit_position(&insertion, mwindow->session->drag_edit, mwindow->session->drag_edit->length)); current_vedit++; } } if( paste_position >= 0 ) { position = paste_position; current_vtrack++; //draw_box = 1; } else w = -1; } if(w >= 0) { // Get the x coordinate x = Units::to_int64(position * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample) - mwindow->edl->local_session->view_start[pane->number]; int y = dest->y_pixel - mwindow->edl->local_session->track_start[pane->number]; int h = dest->vertical_span(mwindow->theme); //printf("TrackCanvas::draw_paste_destination 2 %d %d %d %d\n", x, y, w, h); if(x < -BC_INFINITY) { w -= -BC_INFINITY - x; x += -BC_INFINITY - x; } w = MIN(65535, w); // if(pane->number == TOP_RIGHT_PANE) // printf("TrackCanvas::draw_paste_destination %d %d %d %d %d\n", // __LINE__, x, y, w, h); if (insertion) draw_highlight_insertion(x, y, w, h); else draw_highlight_rectangle(x, y, w, h); } } } } } void TrackCanvas::plugin_dimensions(Plugin *plugin, int64_t &x, int64_t &y, int64_t &w, int64_t &h) { x = Units::round(plugin->track->from_units(plugin->startproject) * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample - mwindow->edl->local_session->view_start[pane->number]); w = Units::round(plugin->track->from_units(plugin->length) * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample); y = plugin->track->y_pixel - mwindow->edl->local_session->track_start[pane->number]; if( plugin->track->show_titles() ) y += mwindow->theme->get_image("title_bg_data")->get_h(); if( plugin->track->show_assets() ) y += resource_h(); y += plugin->plugin_set->get_number() * mwindow->theme->get_image("plugin_bg_data")->get_h(); h = mwindow->theme->get_image("plugin_bg_data")->get_h(); } int TrackCanvas::resource_h() { return mwindow->edl->local_session->zoom_track; } void TrackCanvas::draw_highlight_rectangle(int x, int y, int w, int h) { // if we have to draw a highlighted rectangle completely on the left or completely on the right of the viewport, // just draw arrows, so user has indication that something is there // FIXME: get better colors if (x + w <= 0) { draw_triangle_left(0, y + h /6, h * 2/3, h * 2/3, BLACK, GREEN, YELLOW, RED, BLUE); return; } else if (x >= get_w()) { draw_triangle_right(get_w() - h * 2/3, y + h /6, h * 2/3, h * 2/3, BLACK, GREEN, YELLOW, RED, BLUE); return; } // Fix bug in heroines & cvs version as of 22.8.2005: // If we grab when zoomed in and zoom out while dragging, when edit gets really narrow strange things start happening if (w >= 0 && w < 3) {x -= w /2; w = 3;}; if(x < -10) { w += x - -10; x = -10; } if(y < -10) { h += y - -10; y = -10; } w = MIN(w, get_w() + 20); h = MIN(h, get_h() + 20); if(w > 0 && h > 0) { set_color(mwindow->preferences->highlight_inverse); set_inverse(); //draw_rectangle(x, y, w, h); draw_rectangle(x + 1, y + 1, w - 2, h - 2); draw_rectangle(x + 2, y + 2, w - 4, h - 4); set_opaque(); draw_rectangle(x, y, w, h); } //if(pane->number == TOP_RIGHT_PANE) //printf("TrackCanvas::draw_highlight_rectangle %d %d %d %d %d\n", __LINE__, x, y, w, h); } void TrackCanvas::draw_highlight_insertion(int x, int y, int w, int h) { // if we have to draw a highlighted rectangle completely on the left or completely on the right of the viewport, // just draw arrows, so user has indication that something is there // FIXME: get better colors int h1 = h / 8; int h2 = h / 4; set_inverse(); /* these don't look so good draw_line(x, y, x, y+h); draw_line(x - h2 * 2, y + h1*2, x - h2, y+h1*2); draw_line(x - h2 * 2, y + h1*2+1, x - h2, y+h1*2+1); draw_line(x - h2 * 2, y + h1*6, x - h2, y+h1*6); draw_line(x - h2 * 2, y + h1*6+1, x - h2, y+h1*6+1); */ draw_triangle_right(x - h2, y + h1, h2, h2, BLACK, GREEN, YELLOW, RED, BLUE); draw_triangle_right(x - h2, y + h1*5, h2, h2, BLACK, GREEN, YELLOW, RED, BLUE); /* draw_line(x + h2 * 2, y + h1*2, x + h2, y+h1*2); draw_line(x + h2 * 2, y + h1*2+1, x + h2, y+h1*2+1); draw_line(x + h2 * 2, y + h1*6, x + h2, y+h1*6); draw_line(x - h2 * 2, y + h1*6+1, x + h2, y+h1*6+1); */ draw_triangle_left(x, y + h1, h2, h2, BLACK, GREEN, YELLOW, RED, BLUE); draw_triangle_left(x, y + h1*5, h2, h2, BLACK, GREEN, YELLOW, RED, BLUE); // draw the box centred around x x -= w / 2; // Fix bug in heroines & cvs version as of 22.8.2005: // If we grab when zoomed in and zoom out while dragging, when edit gets really narrow strange things start happening if (w >= 0 && w < 3) {x -= w /2; w = 3;}; if(x < -10) { w += x - -10; x = -10; } if(y < -10) { h += y - -10; y = -10; } w = MIN(w, get_w() + 20); h = MIN(h, get_h() + 20); set_color(mwindow->preferences->highlight_inverse); set_inverse(); draw_rectangle(x, y, w, h); draw_rectangle(x + 1, y + 1, w - 2, h - 2); set_opaque(); //printf("TrackCanvas::draw_highlight_insertion %d %d %d %d\n", x, y, w, h); } void TrackCanvas::draw_playback_cursor() { // Called before playback_cursor exists // if(mwindow->playback_cursor && mwindow->playback_cursor->visible) // { // mwindow->playback_cursor->visible = 0; // mwindow->playback_cursor->draw(); // } } void TrackCanvas::get_handle_coords(Edit *edit, int64_t &x, int64_t &y, int64_t &w, int64_t &h, int side) { int handle_w = mwindow->theme->edithandlein_data[0]->get_w(); int handle_h = mwindow->theme->edithandlein_data[0]->get_h(); edit_dimensions(edit, x, y, w, h); if( edit->track->show_titles() ) y += mwindow->theme->get_image("title_bg_data")->get_h(); if(side == EDIT_OUT) x += w - handle_w; h = handle_h; w = handle_w; } void TrackCanvas::get_transition_coords(Edit *edit, int64_t &x, int64_t &y, int64_t &w, int64_t &h) { int transition_w = 30, transition_h = 30; int has_titles = edit->track->show_titles(); int has_assets = edit->track->show_assets(); double title_bg_h = mwindow->theme->get_image("title_bg_data")->get_h(); double asset_h = resource_h(); double ys = has_assets ? asset_h : has_titles ? title_bg_h : 0; double dy = has_titles ? ( has_assets ? title_bg_h + asset_h/2 : title_bg_h/2 ) : ( has_assets ? asset_h/2 : 0) ; double title_h = mwindow->theme->title_h; if( dy < title_h / 2 ) { ys = title_h; dy = ys / 2; } y += dy; x -= transition_w / 2; y -= transition_h / 2; w = transition_w; h = transition_h; } void TrackCanvas::draw_highlighting() { int64_t x, y, w, h; int draw_box = 0; switch(mwindow->session->current_operation) { case DRAG_ATRANSITION: case DRAG_VTRANSITION: //printf("TrackCanvas::draw_highlighting 1 %p %p\n", // mwindow->session->track_highlighted, mwindow->session->edit_highlighted); if(mwindow->session->edit_highlighted) { if((mwindow->session->current_operation == DRAG_ATRANSITION && mwindow->session->track_highlighted->data_type == TRACK_AUDIO) || (mwindow->session->current_operation == DRAG_VTRANSITION && mwindow->session->track_highlighted->data_type == TRACK_VIDEO)) { edit_dimensions(mwindow->session->edit_highlighted, x, y, w, h); if(MWindowGUI::visible(x, x + w, 0, get_w()) && MWindowGUI::visible(y, y + h, 0, get_h())) { draw_box = 1; get_transition_coords(mwindow->session->edit_highlighted, x, y, w, h); } } } break; // Dragging a new effect from the Resource window case DRAG_AEFFECT: case DRAG_VEFFECT: if(mwindow->session->track_highlighted && ((mwindow->session->current_operation == DRAG_AEFFECT && mwindow->session->track_highlighted->data_type == TRACK_AUDIO) || (mwindow->session->current_operation == DRAG_VEFFECT && mwindow->session->track_highlighted->data_type == TRACK_VIDEO))) { // Put it before another plugin if(mwindow->session->plugin_highlighted) { plugin_dimensions(mwindow->session->plugin_highlighted, x, y, w, h); //printf("TrackCanvas::draw_highlighting 1 %d %d\n", x, w); } // Put it after a plugin set else if( mwindow->session->pluginset_highlighted && mwindow->session->pluginset_highlighted->last ) { plugin_dimensions((Plugin*)mwindow->session->pluginset_highlighted->last, x, y, w, h); //printf("TrackCanvas::draw_highlighting 1 %d %d\n", x, w); int64_t track_x, track_y, track_w, track_h; track_dimensions(mwindow->session->track_highlighted, track_x, track_y, track_w, track_h); x += w; w = Units::round( mwindow->session->track_highlighted->get_length() * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample - mwindow->edl->local_session->view_start[pane->number]) - x; //printf("TrackCanvas::draw_highlighting 2 %d\n", w); if(w <= 0) w = track_w; } else { track_dimensions(mwindow->session->track_highlighted, x, y, w, h); //printf("TrackCanvas::draw_highlighting 1 %d %d %d %d\n", x, y, w, h); // Put it in a new plugin set determined by the selected range if(mwindow->edl->local_session->get_selectionend() > mwindow->edl->local_session->get_selectionstart()) { x = Units::to_int64(mwindow->edl->local_session->get_selectionstart() * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample - mwindow->edl->local_session->view_start[pane->number]); w = Units::to_int64((mwindow->edl->local_session->get_selectionend() - mwindow->edl->local_session->get_selectionstart()) * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample); } // Put it in a new plugin set determined by an edit boundary else if(mwindow->session->edit_highlighted) { int64_t temp_y, temp_h; edit_dimensions(mwindow->session->edit_highlighted, x, temp_y, w, temp_h); } // Put it at the beginning of the track in a new plugin set } if( MWindowGUI::visible(x, x + w, 0, get_w()) && MWindowGUI::visible(y, y + h, 0, get_h()) ) { //printf("TrackCanvas::draw_highlighting 1\n"); draw_box = 1; } } break; case DRAG_ASSET: if(mwindow->session->track_highlighted) { // track_dimensions(mwindow->session->track_highlighted, x, y, w, h); // if(MWindowGUI::visible(y, y + h, 0, get_h())) // { draw_paste_destination(); // } } break; case DRAG_EDIT: if(mwindow->session->track_highlighted) { // track_dimensions(mwindow->session->track_highlighted, x, y, w, h); // // if(MWindowGUI::visible(y, y + h, 0, get_h())) { draw_paste_destination(); // } } break; case DRAG_GROUP: if( mwindow->session->track_highlighted && mwindow->session->drag_group && mwindow->session->drag_group_edit && mwindow->session->drag_group_first_track ) { Track *track = mwindow->session->track_highlighted; EDL *drag_group = mwindow->session->drag_group; Track *edit_track = mwindow->session->drag_group_edit->track; Track *drag_track = mwindow->session->drag_group_first_track; while( track && edit_track && edit_track != drag_track ) { edit_track = edit_track->previous; track = track->previous; } int cx = get_cursor_x(); double cur_pos = mwindow->session->drag_group_position; double new_pos = mwindow->edl->get_cursor_position(cx, pane->number); new_pos -= mwindow->session->drag_position - cur_pos; new_pos = mwindow->edl->align_to_frame(new_pos, 0); double drop_position = new_pos; int color = GREEN; int ret = test_track_group(drag_group, track, new_pos); if( ret < 0 ) color = ORANGE; else if( !ret ) { if( new_pos != drop_position ) { double pos = new_pos; pos += mwindow->session->drag_position - cur_pos; cx = mwindow->edl->get_position_cursorx(pos, pane->number); ret = test_track_group(drag_group, track, new_pos); } color = ret > 0 ? YELLOW : shift_down() ? RED : BLUE; } track_dimensions(mwindow->session->track_highlighted, x, y, w, h); int dx = cx - mwindow->session->drag_origin_x; int dy = y - mwindow->session->drag_origin_y; draw_selected_edits(mwindow->edl, dx, dy, color, -1); } break; // Dragging an effect from the timeline case DRAG_AEFFECT_COPY: case DRAG_VEFFECT_COPY: if( (mwindow->session->plugin_highlighted || mwindow->session->track_highlighted) && ((mwindow->session->current_operation == DRAG_AEFFECT_COPY && mwindow->session->track_highlighted->data_type == TRACK_AUDIO) || (mwindow->session->current_operation == DRAG_VEFFECT_COPY && mwindow->session->track_highlighted->data_type == TRACK_VIDEO))) { int64_t plugin_position = -1; Plugin *drag_plugin = mwindow->session->drag_plugin; PluginSet *hi_plugin_set = mwindow->session->pluginset_highlighted; Track *hi_track = mwindow->session->track_highlighted; Edit *hi_edit = mwindow->session->edit_highlighted; // Put it into a silence zone if( hi_plugin_set && hi_plugin_set->last && hi_plugin_set->track == drag_plugin->track ) { plugin_dimensions((Plugin *)hi_plugin_set->last, x, y, w, h); plugin_position = drop_plugin_position(hi_plugin_set, drag_plugin); hi_track = drag_plugin->track; } else if( hi_track ) { // Put it in a new plugin set determined by an edit boundary, or at the start of the track track_dimensions(hi_track, x, y, w, h); plugin_position = hi_edit ? hi_edit->startproject : 0; } if( plugin_position < 0 ) break; // Calculate length of plugin based on data type of track and units double zoom_scale = (double)mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample; x = Units::round(hi_track->from_units(plugin_position) * zoom_scale) - mwindow->edl->local_session->view_start[pane->number]; w = Units::round(hi_track->from_units(drag_plugin->length) * zoom_scale); if( MWindowGUI::visible(x, x + w, 0, get_w()) && MWindowGUI::visible(y, y + h, 0, get_h()) ) { draw_box = 1; } } break; case DRAG_PLUGINKEY: if(mwindow->session->plugin_highlighted && mwindow->session->current_operation == DRAG_PLUGINKEY) { // Just highlight the plugin plugin_dimensions(mwindow->session->plugin_highlighted, x, y, w, h); if(MWindowGUI::visible(x, x + w, 0, get_w()) && MWindowGUI::visible(y, y + h, 0, get_h())) { draw_box = 1; } } break; } if( draw_box ) draw_highlight_rectangle(x, y, w, h); draw_selected_edits(mwindow->edl, 0, 0, GREEN+BLUE, RED); } // x does not reliably draw a really big rectangle void TrackCanvas::draw_selected(int x, int y, int w, int h) { int x1 = bmax(x, 0), x2 = bmin(x+w, get_w()); if( x1 > x2 ) return; int y1 = bmax(y, 0), y2 = bmin(y+h, get_h()); if( y1 > y2 ) return; if( x >= 0 && x < get_w() ) draw_line(x,y1, x,y2); if( x+w >= 0 && x+w < get_w() ) draw_line(x+w,y1, x+w,y2); if( y >= 0 && y < get_h() ) draw_line(x1,y, x2,y); if( y+h >= 0 && y+h < get_h() ) draw_line(x1,y+h, x2,y+h); } void TrackCanvas::draw_selected_edits(EDL *edl, int dx, int dy, int color0, int color1) { for( Track *track=edl->tracks->first; track; track=track->next ) { for( Edit *edit=track->edits->first; edit; edit=edit->next ) { if( !edit->is_selected ) continue; int64_t x, y, w, h; edit_dimensions(edit, x, y, w, h); x += dx; y += dy; if( !MWindowGUI::visible(x, x + w, 0, get_w()) ) continue; if( !MWindowGUI::visible(y, y + h, 0, get_h()) ) continue; set_opaque(); int inner = color1 < 0 ? color0 : !edit->group_id ? color0 : mwindow->get_group_color(edit->group_id); set_color(inner); draw_selected(x, y, w, h); int outer = color1 < 0 ? color0 : !edit->group_id ? color1 : inner; set_color(outer); draw_selected(x-1, y-1, w+2, h+2); draw_selected(x-2, y-2, w+1, h+1); } } } void TrackCanvas::draw_plugins() { char string[BCTEXTLEN]; int current_on = 0; int current_show = 0; int current_preset = 0; for(int i = 0; i < plugin_on_toggles.total; i++) plugin_on_toggles.values[i]->in_use = 0; for(int i = 0; i < plugin_show_toggles.total; i++) plugin_show_toggles.values[i]->in_use = 0; for(int i = 0; i < preset_edit_buttons.total; i++) plugin_show_toggles.values[i]->in_use = 0; for(Track *track = mwindow->edl->tracks->first; track; track = track->next) { if(track->expand_view) { for(int i = 0; i < track->plugin_set.total; i++) { PluginSet *pluginset = track->plugin_set.values[i]; for(Plugin *plugin = (Plugin*)pluginset->first; plugin; plugin = (Plugin*)plugin->next) { int64_t total_x, y, total_w, h; plugin_dimensions(plugin, total_x, y, total_w, h); if(MWindowGUI::visible(total_x, total_x + total_w, 0, get_w()) && MWindowGUI::visible(y, y + h, 0, get_h()) && plugin->plugin_type != PLUGIN_NONE) { int x = total_x, w = total_w, left_margin = 5; int right_margin = 5; if(x < 0) { w -= -x; x = 0; } if(w + x > get_w()) w -= (w + x) - get_w(); draw_3segmenth(x, y, w, total_x, total_w, mwindow->theme->get_image("plugin_bg_data"), 0); set_color(mwindow->theme->title_color); set_font(mwindow->theme->title_font); plugin->calculate_title(string, 0); // Truncate string to int64_test visible in background int len = strlen(string), j; for(j = len; j >= 0; j--) { if(left_margin + get_text_width(mwindow->theme->title_font, string) > w) { string[j] = 0; } else break; } // Justify the text on the left boundary of the edit if it is visible. // Otherwise justify it on the left side of the screen. int64_t text_x = total_x + left_margin; int64_t text_w = get_text_width(mwindow->theme->title_font, string, strlen(string)); text_x = MAX(left_margin, text_x); draw_text(text_x, y + get_text_ascent(mwindow->theme->title_font) + 2, string, strlen(string), 0); int64_t min_x = total_x + text_w; // Update plugin toggles int toggle_x = total_x + total_w; int toggle_y = y; toggle_x = MIN(get_w() - right_margin, toggle_x); // On toggle toggle_x -= PluginOn::calculate_w(mwindow) + 10; if(toggle_x > min_x) { if(current_on >= plugin_on_toggles.total) { PluginOn *plugin_on = new PluginOn(mwindow, toggle_x, toggle_y, plugin); add_subwindow(plugin_on); plugin_on_toggles.append(plugin_on); } else { plugin_on_toggles.values[current_on]->update(toggle_x, toggle_y, plugin); } current_on++; } // Toggles for standalone plugins only if(plugin->plugin_type == PLUGIN_STANDALONE) { // Show toggle_x -= PluginShow::calculate_w(mwindow) + 10; if(toggle_x > min_x) { if(current_show >= plugin_show_toggles.total) { PluginShow *plugin_show = new PluginShow(mwindow, toggle_x, toggle_y, plugin); add_subwindow(plugin_show); plugin_show_toggles.append(plugin_show); } else { plugin_show_toggles.values[current_show]->update(toggle_x, toggle_y, plugin); } current_show++; } toggle_x -= PluginPresetEdit::calculate_w(mwindow) + 10; if(toggle_x > min_x) { if(current_preset >= preset_edit_buttons.total) { PluginPresetEdit *preset_edit = new PluginPresetEdit(mwindow, toggle_x, toggle_y, plugin); add_subwindow(preset_edit); preset_edit_buttons.append(preset_edit); } else { preset_edit_buttons.values[current_preset]->update(toggle_x, toggle_y, plugin); } current_preset++; } } } } } } } // Remove unused toggles while(current_preset < preset_edit_buttons.total) { preset_edit_buttons.remove_object_number(current_preset); } while(current_show < plugin_show_toggles.total) { plugin_show_toggles.remove_object_number(current_show); } while(current_on < plugin_on_toggles.total) { plugin_on_toggles.remove_object_number(current_on); } } void TrackCanvas::refresh_plugintoggles() { for(int i = 0; i < plugin_on_toggles.total; i++) { PluginOn *on = plugin_on_toggles.values[i]; on->reposition_window(on->get_x(), on->get_y()); } for(int i = 0; i < plugin_show_toggles.total; i++) { PluginShow *show = plugin_show_toggles.values[i]; show->reposition_window(show->get_x(), show->get_y()); } for(int i = 0; i < preset_edit_buttons.total; i++) { PluginPresetEdit *preset_edit = preset_edit_buttons.values[i]; preset_edit->reposition_window(preset_edit->get_x(), preset_edit->get_y()); } } void TrackCanvas::draw_hard_edges() { int64_t x, y, w, h; for(Track *track = mwindow->edl->tracks->first; track; track = track->next) { for(Edit *edit = track->edits->first; edit; edit = edit->next) { if( !edit->hard_left && !edit->hard_right ) continue; edit_dimensions(edit, x, y, w, h); set_color(GREEN); set_opaque(); int y1 = y; if( track->show_titles() ) y1 += mwindow->theme->get_image("title_bg_data")->get_h(); if( track->show_assets() ) y1 += resource_h(); if( y1 == y ) y1 += mwindow->theme->title_h; if( edit->hard_left ) { ArrayList xpt, ypt; xpt.append(x); ypt.append(y1); xpt.append(x+HANDLE_W); ypt.append(y1); xpt.append(x); ypt.append(y1-HANDLE_H); fill_polygon(&xpt, &ypt); } if( edit->hard_right ) { ArrayList xpt, ypt; int x1 = x+w-1; xpt.append(x1); ypt.append(y1); xpt.append(x1-HANDLE_W); ypt.append(y1); xpt.append(x1); ypt.append(y1-HANDLE_H); fill_polygon(&xpt, &ypt); } } } } void TrackCanvas::draw_inout_points() { } void TrackCanvas::draw_drag_handle() { if(mwindow->session->current_operation == DRAG_EDITHANDLE2 || mwindow->session->current_operation == DRAG_PLUGINHANDLE2) { //printf("TrackCanvas::draw_drag_handle 1 %ld %ld\n", mwindow->session->drag_sample, mwindow->edl->local_session->view_start); int64_t pixel1 = Units::round(mwindow->session->drag_position * mwindow->edl->session->sample_rate / 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(!snapped ? GREEN : (snapped=0, YELLOW)); set_inverse(); //printf("TrackCanvas::draw_drag_handle 3\n"); draw_line(pixel1, 0, pixel1, get_h()); set_opaque(); //printf("TrackCanvas::draw_drag_handle 4\n"); } } void TrackCanvas::draw_transitions() { int64_t x, y, w, h; for(Track *track = mwindow->edl->tracks->first; track; track = track->next) { if( !track->show_transitions() ) continue; for(Edit *edit = track->edits->first; edit; edit = edit->next) { if(!edit->transition) continue; edit_dimensions(edit, x, y, w, h); int strip_x = x, edit_y = y; get_transition_coords(edit, x, y, w, h); int strip_y = y - mwindow->theme->get_image("plugin_bg_data")->get_h(); if( track->show_assets() && track->show_titles() ) edit_y += mwindow->theme->get_image("title_bg_data")->get_h(); if( strip_y < edit_y ) strip_y = edit_y; int strip_w = Units::round(edit->track->from_units(edit->transition->length) * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample); if( MWindowGUI::visible(x, x + w, 0, get_w()) && MWindowGUI::visible(y, y + h, 0, get_h()) ) { PluginServer *server = mwindow->scan_plugindb(edit->transition->title, track->data_type); if( !server ) continue; VFrame *picon = server->get_picon(); if( !picon ) continue; int picon_w = picon->get_w(), picon_h = picon->get_h(); int track_h = edit->track->vertical_span(mwindow->theme); if( picon_h > track_h ) picon_h = track_h; draw_vframe(picon, x, y, w, h, 0, 0, picon_w, picon_h); } if(MWindowGUI::visible(strip_x, strip_x + strip_w, 0, get_w()) && MWindowGUI::visible(strip_y, strip_y + h, 0, get_h())) { int x = strip_x, w = strip_w; if( x < 0 ) { w -= -x; x = 0; } if( w + x > get_w() ) w -= (w + x) - get_w(); draw_3segmenth( x, strip_y, w, strip_x, strip_w, mwindow->theme->get_image("plugin_bg_data"), 0); } } } } void TrackCanvas::draw_loop_points() { //printf("TrackCanvas::draw_loop_points 1\n"); if(mwindow->edl->local_session->loop_playback) { //printf("TrackCanvas::draw_loop_points 2\n"); int64_t x = Units::round(mwindow->edl->local_session->loop_start * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample - mwindow->edl->local_session->view_start[pane->number]); //printf("TrackCanvas::draw_loop_points 3\n"); if(MWindowGUI::visible(x, x + 1, 0, get_w())) { set_color(GREEN); draw_line(x, 0, x, get_h()); } //printf("TrackCanvas::draw_loop_points 4\n"); x = Units::round(mwindow->edl->local_session->loop_end * mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample - mwindow->edl->local_session->view_start[pane->number]); //printf("TrackCanvas::draw_loop_points 5\n"); if(MWindowGUI::visible(x, x + 1, 0, get_w())) { set_color(GREEN); draw_line(x, 0, x, get_h()); } //printf("TrackCanvas::draw_loop_points 6\n"); } //printf("TrackCanvas::draw_loop_points 7\n"); } void TrackCanvas::draw_brender_range() { 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 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(x2, x2 + 1, 0, get_w())) { set_color(RED); draw_line(x2, 0, x2, get_h()); } } } // The operations which correspond to each automation type int TrackCanvas::auto_operations[AUTOMATION_TOTAL] = { DRAG_MUTE, DRAG_CAMERA_X, DRAG_CAMERA_Y, DRAG_CAMERA_Z, DRAG_PROJECTOR_X, DRAG_PROJECTOR_Y, DRAG_PROJECTOR_Z, DRAG_FADE, DRAG_PAN, DRAG_MODE, DRAG_MASK, DRAG_SPEED }; // The buttonpress operations, so nothing changes unless the mouse moves // a certain amount. This allows the keyframe to be used to position the // insertion point without moving itself. static int pre_auto_operations[AUTOMATION_TOTAL] = { DRAG_MUTE, DRAG_CAMERA_X, DRAG_CAMERA_Y, DRAG_CAMERA_Z, DRAG_PROJECTOR_X, DRAG_PROJECTOR_Y, DRAG_PROJECTOR_Z, DRAG_FADE, DRAG_PAN_PRE, DRAG_MODE_PRE, DRAG_MASK_PRE, DRAG_SPEED }; int TrackCanvas::do_keyframes(int cursor_x, int cursor_y, int draw, int buttonpress, int &new_cursor, int &update_cursor, int &rerender) { // Note: button 3 (right mouse button) is not eaten to allow // track context menu to appear int result = 0; EDLSession *session = mwindow->edl->session; static BC_Pixmap *auto_pixmaps[AUTOMATION_TOTAL] = { 0, 0, 0, 0, 0, 0, 0, 0, pankeyframe_pixmap, modekeyframe_pixmap, maskkeyframe_pixmap, }; for(Track *track = mwindow->edl->tracks->first; track && !result; track = track->next) { Auto *auto_keyframe = 0; Automation *automation = track->automation; // Handle keyframes in reverse drawing order if a button press int start = 0; int end = AUTOMATION_TOTAL; int step = 1; if(buttonpress) { start = AUTOMATION_TOTAL - 1; end = -1; step = -1; } for(int i = start; i != end && !result; i += step) { // Event not trapped and automation visible Autos *autos = automation->autos[i]; if(!result && session->auto_conf->autos[i] && autos) { switch(i) { case AUTOMATION_MODE: case AUTOMATION_PAN: case AUTOMATION_MASK: result = do_autos(track, automation->autos[i], cursor_x, cursor_y, draw, buttonpress, auto_pixmaps[i], auto_keyframe, rerender); break; default: { switch(autos->get_type()) { case Autos::AUTOMATION_TYPE_FLOAT: { Automation automation(0, track); int grouptype = automation.autogrouptype(i, track); if( buttonpress && i == AUTOMATION_SPEED ) { mwindow->speed_before(); } if(draw) // Do dropshadow result = do_float_autos(track, autos, 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, GWindowGUI::auto_colors[i], auto_keyframe, grouptype); if( !result && buttonpress && i == AUTOMATION_SPEED ) mwindow->speed_after(-1); int current_grouptype = mwindow->edl->local_session->zoombar_showautotype; if( result && buttonpress && grouptype != current_grouptype ) { mwindow->edl->local_session->zoombar_showautotype = grouptype; mwindow->gui->zoombar->update_autozoom(); } break; } case Autos::AUTOMATION_TYPE_INT: { if(draw) // Do dropshadow result = do_int_autos(track, autos, 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, GWindowGUI::auto_colors[i], auto_keyframe); break; } } break; } } if(result) { if(mwindow->session->current_operation == auto_operations[i]) rerender = 1; // printf("TrackCanvas::do_keyframes %d %d %d\n", // __LINE__, // mwindow->session->current_operation, // auto_operations[i]); if(buttonpress) { if (buttonpress == 2 && auto_keyframe ) { double position = track->from_units(auto_keyframe->position); mwindow->edl->local_session->set_selectionstart(position); mwindow->edl->local_session->set_selectionend(position); } if (buttonpress != 3) { if(i == AUTOMATION_FADE || i == AUTOMATION_SPEED) fill_ganged_autos(get_double_click(), 0, track, (FloatAuto*)mwindow->session->drag_auto); mwindow->session->current_operation = pre_auto_operations[i]; update_drag_caption(); rerender = 1; } else if( auto_keyframe ) { gui->keyframe_menu->update(automation, autos, auto_keyframe); 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 } } } } if(!result && session->auto_conf->plugins) { Plugin *plugin; KeyFrame *keyframe; result = do_plugin_autos(track, cursor_x, cursor_y, draw, buttonpress, plugin, keyframe); if(result && mwindow->session->current_operation == DRAG_PLUGINKEY) { rerender = 1; } if(result && (buttonpress == 1)) { mwindow->session->current_operation = DRAG_PLUGINKEY_PRE; update_drag_caption(); rerender = 1; } else if (result && (buttonpress == 3)) { gui->keyframe_menu->update(plugin, keyframe); gui->keyframe_menu->activate_menu(); rerender = 1; // the position changes } } } // Final pass to trap event for(int i = 0; i < AUTOMATION_TOTAL; i++) { if(mwindow->session->current_operation == pre_auto_operations[i] || mwindow->session->current_operation == auto_operations[i]) { result = 1; break; } } if(mwindow->session->current_operation == DRAG_PLUGINKEY || mwindow->session->current_operation == DRAG_PLUGINKEY_PRE) { result = 1; } update_cursor = 1; if(result) { new_cursor = UPRIGHT_ARROW_CURSOR; } 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 && isession->drag_auto ) dragging = 0; int autoidx = dragging && keyframe_hairline != HAIRLINE_ALWAYS ? mwindow->session->drag_auto->autos->autoidx : -1; if( get_buttonpress() == LEFT_BUTTON && dragging && keyframe_hairline == HAIRLINE_DRAGGING ) { draw_hairline(mwindow->session->drag_auto, RED, 1); return; } if( keyframe_hairline == HAIRLINE_ALWAYS || ( get_buttonpress() == MIDDLE_BUTTON && keyframe_hairline == HAIRLINE_DRAGGING && dragging ) ) { int show = dragging || keyframe_hairline == HAIRLINE_ALWAYS ? 1 : 0; for( Track *track = mwindow->edl->tracks->first; track; track=track->next ) { Automation *automation = track->automation; for( int i=0; iedl->session->auto_conf->autos[i] ) continue; // automation visible Autos *autos = automation->autos[i]; if( !autos ) continue; if( autoidx >= 0 && autos->autoidx != autoidx ) continue; for( Auto *auto_keyframe=autos->first; auto_keyframe; auto_keyframe = auto_keyframe->next ) { draw_hairline(auto_keyframe, BLUE, show); } } } if( dragging ) draw_hairline(mwindow->session->drag_auto, RED, 1); } } void TrackCanvas::draw_auto(Auto *current, int x, int y, int center_pixel, int zoom_track) { int x1, y1, x2, y2; x1 = x - HANDLE_W / 2; x2 = x + HANDLE_W / 2; y1 = center_pixel + y - HANDLE_W / 2; y2 = center_pixel + y + HANDLE_W / 2; if(y1 < center_pixel + -zoom_track / 2) y1 = center_pixel + -zoom_track / 2; if(y2 > center_pixel + zoom_track / 2) y2 = center_pixel + zoom_track / 2; draw_box(x1, y1, x2 - x1, y2 - y1); } // This draws lines for bezier in & out controls void TrackCanvas::draw_cropped_line(int x1, int y1, int x2, int y2, int min_y, int max_y) { // Don't care about x since it is clipped by the window. // Put y coords in ascending order if(y2 < y1) { y2 ^= y1; y1 ^= y2; y2 ^= y1; x2 ^= x1; x1 ^= x2; x2 ^= x1; } double slope = (double)(x2 - x1) / (y2 - y1); //printf("TrackCanvas::draw_cropped_line %d %d %d %d %d\n", __LINE__, x1, y1, x2, y2); if(y1 < min_y) { x1 = (int)(x1 + (min_y - y1) * slope); y1 = min_y; } else if(y1 >= max_y) { x1 = (int)(x1 + (max_y - 1 - y1) * slope); y1 = max_y - 1; } if(y2 >= max_y) { x2 = (int)(x2 + (max_y - 1 - y2) * slope); y2 = max_y - 1; } else if(y2 < min_y) { x2 = (int)(x2 + (min_y - y2) * slope); y1 = min_y; } //printf("TrackCanvas::draw_cropped_line %d %d %d %d %d\n", __LINE__, x1, y1, x2, y2); if( y1 >= min_y && y1 < max_y && y2 >= min_y && y2 < max_y ) draw_line(x1, y1, x2, y2); } 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) { int x1 = x - HANDLE_W / 2; // Center int x2 = x + HANDLE_W / 2; int y1 = center_pixel + y - HANDLE_H / 2; int y2 = center_pixel + y + HANDLE_H / 2; int ymin = center_pixel - zoom_track / 2; int ymax = center_pixel + zoom_track / 2; CLAMP(y1, ymin, ymax); CLAMP(y2, ymin, ymax); if(y2 - 1 > y1) { set_color(BLACK); draw_box(x1 + 1, y1 + 1, x2 - x1, y2 - y1); set_color(color); draw_box(x1, y1, x2 - x1, y2 - y1); } // 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) return; if(in_x != x) draw_floatauto_ctrlpoint(x, y, in_x, in_y, center_pixel, zoom_track, color); if(out_x != x) draw_floatauto_ctrlpoint(x, y, out_x, out_y, center_pixel, zoom_track, color); } inline int quantize(float f) { return (int)floor(f + 0.5); } inline void TrackCanvas::draw_floatauto_ctrlpoint( int x, int y, int cp_x, int cp_y, int center_pixel, int zoom_track, int color) // draw the tangent and a handle for given bézier ctrl point { bool handle_visible = (abs(cp_y) <= zoom_track / 2); float slope = (float)(cp_y - y)/(cp_x - x); CLAMP(cp_y, -zoom_track / 2, zoom_track / 2); if(slope != 0) cp_x = x + quantize((cp_y - y) / slope); 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 { 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; ex = sx - q * dash; } while(q*ex > 0); if(handle_visible) { int r = HANDLE_W / 2; int cp_x1 = cp_x - r; int cp_y1 = cp_y - r; set_color(BLACK); draw_disc(cp_x1, cp_y1, 2 * r, 2 * r); set_color(color); draw_circle(cp_x1, cp_y1, 2 * r, 2 * r); } } int TrackCanvas::test_auto(Auto *current, int x, int y, int center_pixel, int zoom_track, int cursor_x, int cursor_y, int buttonpress) { int x1, y1, x2, y2; int result = 0; x1 = x - HANDLE_W / 2; x2 = x + HANDLE_W / 2; y1 = center_pixel + y - HANDLE_H / 2; y2 = center_pixel + y + HANDLE_H / 2; int ymin = center_pixel - zoom_track / 2; int ymax = center_pixel + zoom_track / 2; CLAMP(y1, ymin, ymax); CLAMP(y2, ymin, ymax); if(cursor_x >= x1 && cursor_x < x2 && cursor_y >= y1 && cursor_y < y2) { if(buttonpress && buttonpress != 3) { mwindow->session->drag_auto = current; mwindow->session->drag_start_percentage = (float)((IntAuto*)current)->value; // Toggle Autos don't respond to vertical zoom, they always show up // with "on" == 100% == line on top mwindow->session->drag_start_position = current->position; mwindow->session->drag_origin_x = cursor_x; mwindow->session->drag_origin_y = cursor_y; } result = 1; } if(buttonpress && buttonpress != 3 && result) { //printf("TrackCanvas::test_auto %d\n", __LINE__); mwindow->undo->update_undo_before(); } return result; } // some Helpers for test_floatauto(..) // and for dragging the tangents/ctrl points inline float test_curve_line( int x0, int y0, int ctrl_x, int ctrl_y, float cursor_x, float cursor_y) { // Control point switched off? if( x0 == ctrl_x ) return 0.0; double x1 = ctrl_x-x0, y1 = ctrl_y-y0; double x = cursor_x-x0, y = cursor_y-y0; // wrong side of ctrl handle if( x*x1 < 0 ) return 0.0; // outside handle radius if( (x*x + y*y) > (x1*x1 + y1*y1) ) return 0; double xx1 = x1*x1, yy1 = y1*y1; double xx = x*x, yy = y*y; // distance squared from cursor to cursor projected to line from ctrl to handle // along a line perpendicular to line from ctrl to handle (closest approach) // (x**2*y1**2 - 2*x*x1*y*y1 + x1**2*y**2)/(x1**2 + y1**2) double dist2 = (xx*yy1 - 2*x*x1*y*y1 + xx1*yy)/(xx1 + yy1); return dist2 < (HANDLE_W*HANDLE_W)/4. ? x/x1 : 0.; } inline float levered_position(float position, float ref_pos) { if( 1e-6 > fabs(ref_pos) || isnan(ref_pos)) return 0.0; return ref_pos / position; } float TrackCanvas::value_to_percentage(float auto_value, int autogrouptype) // transforms automation value into current display coords, // dependant on current automation display range for the given kind of automation { if(!mwindow || !mwindow->edl) return 0; 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) ) return 0; 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 cursor_x, int cursor_y, int buttonpress, int autogrouptype) { int result = 0; int x1 = x - HANDLE_W / 2; int x2 = x + HANDLE_W / 2; int y1 = center_pixel + y - HANDLE_W / 2; int y2 = center_pixel + y + HANDLE_W / 2; int ymin = center_pixel - zoom_track / 2; int ymax = center_pixel + zoom_track / 2; CLAMP(y1, ymin, ymax); CLAMP(y2, ymin, ymax); int in_x1 = in_x - HANDLE_W / 2; int in_x2 = in_x + HANDLE_W / 2; int in_y1 = center_pixel + in_y - HANDLE_W / 2; int in_y2 = center_pixel + in_y + HANDLE_W / 2; CLAMP(in_y1, ymin, ymax); CLAMP(in_y2, ymin, ymax); int out_x1 = out_x - HANDLE_W / 2; int out_x2 = out_x + HANDLE_W / 2; int out_y1 = center_pixel + out_y - HANDLE_W / 2; int out_y2 = center_pixel + out_y + HANDLE_W / 2; CLAMP(out_y1, ymin, ymax); CLAMP(out_y2, ymin, ymax); //printf("TrackCanvas::test_floatauto %d %d %d %d %d %d\n", cursor_x, cursor_y, x1, x2, y1, y2); // buttonpress could be the start of a drag operation #define INIT_DRAG(POS,VAL) \ mwindow->session->drag_auto = current; \ mwindow->session->drag_origin_x = cursor_x; \ mwindow->session->drag_origin_y = cursor_y; \ mwindow->session->drag_start_position = (POS); \ mwindow->session->drag_start_percentage = (VAL); #define WITHIN(X1,X2,Y1,Y2) (cursor_x >=(X1) && cursor_x <(X2) && cursor_y >=(Y1) && cursor_y <(Y2) ) // without modifier we are manipulating the automation node // with ALT it's about dragging only the value of the node // with SHIFT the value snaps to the value of neighbouring nodes // CTRL indicates we are rather manipulating the tangent(s) of the node if(!ctrl_down()) { 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)) mwindow->session->drag_handle = 0; } } } else // ctrl_down() { if( WITHIN(x1,x2,y1,y2)) { result = 1; if(buttonpress && (buttonpress != 3)) { // 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 if( in_x != x && current->position > 0 && (FloatAuto::FREE == current->curve_mode || FloatAuto::TFREE == current->curve_mode)) // act on in control handle only if // tangent is significant and is editable (not automatically choosen) { lever = test_curve_line(x, y, in_x, in_y, cursor_x, cursor_y-center_pixel); // either cursor in ctrl-point handle or cursor on tangent line if( WITHIN(in_x1,in_x2,in_y1,in_y2) || lever > 0.0 ) { result = 1; if(buttonpress && (buttonpress != 3)) { if(lever == 0.0) lever=1.0; // we entered by dragging the handle... // lever = 1.0; mwindow->session->drag_handle = 1; float new_invalue = current->get_value() + lever * current->get_control_in_value(); INIT_DRAG(current->position + (int64_t)(lever * current->get_control_in_position()), value_to_percentage(new_invalue, autogrouptype)) } } } // Test out control if(out_x != x && (FloatAuto::FREE == current->curve_mode || FloatAuto::TFREE == current->curve_mode)) // act on out control only if tangent is significant and is editable { lever = test_curve_line(x, y, out_x, out_y, cursor_x, cursor_y-center_pixel); if(WITHIN(out_x1,out_x2,out_y1,out_y2) || lever > 0.0 ) { result = 1; if(buttonpress && (buttonpress != 3)) { if(lever == 0.0) lever=1.0; // lever = 1.0; mwindow->session->drag_handle = 2; float new_outvalue = current->get_value() + lever * current->get_control_out_value(); INIT_DRAG(current->position + (int64_t)(lever * current->get_control_out_position()), value_to_percentage(new_outvalue, autogrouptype)) } } } } // end ctrl_down() #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", // mwindow->session->drag_handle, // ctrl_down(), // cursor_x, // cursor_y, // x1, x2, y1, y2); if(buttonpress && (buttonpress != 3) && result) { mwindow->undo->update_undo_before(); } return result; } // Get the float value & y for position x on the canvas #define X_TO_FLOATLINE(x) \ int64_t position1 = (int64_t)(unit_start + x * zoom_units); \ int64_t position2 = (int64_t)(unit_start + x * zoom_units) + 1; \ /* Call by reference fails for some reason here */ \ float value1 = autos->get_value(position1, PLAY_FORWARD, previous1, next1); \ float value2 = autos->get_value(position2, PLAY_FORWARD, previous1, next1); \ double position = unit_start + x * zoom_units; \ double value = 0; \ if(position2 > position1) \ { \ value = value1 + \ (value2 - value1) * \ (position - position1) / \ (position2 - position1); \ } \ else \ { \ value = value1; \ } \ AUTOMATIONCLAMPS(value, autogrouptype); \ int y = center_pixel + \ (int)(((value - automation_min) / automation_range - 0.5) * -yscale); void TrackCanvas::draw_floatline(int center_pixel, FloatAuto *previous, FloatAuto *next, FloatAutos *autos, double unit_start, double zoom_units, double yscale, int x1, int y1, int x2, int y2, int color, int autogrouptype) { // Solve bezier equation for either every pixel or a certain large number of // points. // Not using slope intercept x1 = MAX(0, x1); int prev_y = y1 + center_pixel; // Call by reference fails for some reason here FloatAuto *previous1 = previous, *next1 = next; 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; for(int x = x1; x < x2; x++) { // Interpolate value between frames X_TO_FLOATLINE(x) if(/* x > x1 && */ y >= center_pixel - yscale / 2 && y < center_pixel + yscale / 2 - 1) { // printf("TrackCanvas::draw_floatline y=%d min=%d max=%d\n", // y, // (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; } } int TrackCanvas::test_floatline(int center_pixel, FloatAutos *autos, double unit_start, double zoom_units, double yscale, int x1, int x2, int cursor_x, int cursor_y, int buttonpress, int autogrouptype) { int result = 0; 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; FloatAuto *previous1 = 0, *next1 = 0; X_TO_FLOATLINE(cursor_x); if(cursor_x >= x1 && cursor_x < x2 && cursor_y >= y - HANDLE_W / 2 && cursor_y < y + HANDLE_W / 2 && !ctrl_down()) { result = 1; // Menu if(buttonpress == 3) { } else // Create keyframe if(buttonpress) { Auto *current; mwindow->undo->update_undo_before(); 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; mwindow->session->drag_origin_x = cursor_x; mwindow->session->drag_origin_y = cursor_y; mwindow->session->drag_handle = 0; } } return result; } void TrackCanvas::fill_ganged_autos(int all, float change, Track *skip, FloatAuto *fauto) { if( !skip->gang ) return; // Handles the special case of modifying a fadeauto // when there are ganged faders on several tracks double position = skip->from_units(fauto->position); int autoidx = fauto->autos->autoidx; for(Track *current = mwindow->edl->tracks->first; current; current = NEXT) { if( (all || current->data_type == skip->data_type) && current->gang && current->record && current != skip ) { 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]; int64_t current_position = current->to_units(position, 1); FloatAuto *keyframe = (FloatAuto*)fade_autos->get_auto_at_position(position); if( keyframe ) { // keyframe exists, just change it float value = keyframe->get_value(); float new_value = value + change; CLAMP(new_value, auto_min, auto_max); keyframe->adjust_to_new_coordinates(current_position, new_value); } else { // create keyframe on neighbouring track at the point in time given by fauto FloatAuto *previous = 0, *next = 0; float value = fade_autos->get_value(current_position, PLAY_FORWARD, previous, next); float new_value = value + change; CLAMP(new_value, auto_min, auto_max); keyframe = (FloatAuto*)fade_autos->insert_auto(current_position); keyframe->set_value(new_value); } mwindow->session->drag_auto_gang->append((Auto *)keyframe); } } } void TrackCanvas::update_ganged_autos(float change, Track *skip, FloatAuto *fauto) { 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(keyframe_position, new_value); } } void TrackCanvas::clear_ganged_autos() { // remove the gangs 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); } mwindow->session->drag_auto_gang->remove_all(); } void TrackCanvas::draw_toggleline(int center_pixel, int x1, int y1, int x2, int y2) { draw_line(x1, center_pixel + y1, x2, center_pixel + y1); if(y2 != y1) { draw_line(x2, center_pixel + y1, x2, center_pixel + y2); } } int TrackCanvas::test_toggleline(Autos *autos, int center_pixel, int x1, int y1, int x2, int y2, int cursor_x, int cursor_y, int buttonpress) { int result = 0; if(cursor_x >= x1 && cursor_x < x2) { int miny = center_pixel + y1 - HANDLE_W / 2; int maxy = center_pixel + y1 + HANDLE_W / 2; if(cursor_y >= miny && cursor_y < maxy) { result = 1; // Menu if(buttonpress == 3) { } else // Insert keyframe if(buttonpress) { Auto *current; double position = (double)(cursor_x + mwindow->edl->local_session->view_start[pane->number]) * mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate; int64_t unit_position = autos->track->to_units(position, 0); int new_value = (int)((IntAutos*)autos)->get_automation_constant(unit_position, unit_position); mwindow->undo->update_undo_before(); current = mwindow->session->drag_auto = autos->insert_auto(unit_position); ((IntAuto*)current)->value = new_value; // Toggle Autos don't respond to vertical zoom, they always show up // with "on" == 100% == line on top mwindow->session->drag_start_percentage = (float)new_value; mwindow->session->drag_start_position = current->position; mwindow->session->drag_origin_x = cursor_x; mwindow->session->drag_origin_y = cursor_y; } } }; return result; } void TrackCanvas::calculate_viewport(Track *track, double &view_start, // Seconds double &unit_start, double &view_end, // Seconds double &unit_end, double &yscale, int ¢er_pixel, double &zoom_sample, double &zoom_units) { view_start = (double)mwindow->edl->local_session->view_start[pane->number] * mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate; unit_start = track->to_doubleunits(view_start); view_end = (double)(mwindow->edl->local_session->view_start[pane->number] + get_w()) * mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate; unit_end = track->to_doubleunits(view_end); int y = track->y_pixel - mwindow->edl->local_session->track_start[pane->number]; int has_titles = track->show_titles(); int has_assets = track->show_assets(); double title_bg_h = mwindow->theme->get_image("title_bg_data")->get_h(); double asset_h = resource_h(); double title_h = mwindow->theme->title_h; double ys = has_assets ? asset_h : has_titles ? title_bg_h : 0; double dy = has_titles ? ( has_assets ? title_bg_h + asset_h/2 : title_bg_h/2) : ( has_assets ? asset_h/2 : 0) ; if( dy < title_h/2 ) { ys = title_h; dy = ys / 2; } yscale = ys; center_pixel = y + dy; zoom_sample = mwindow->edl->local_session->zoom_sample; zoom_units = track->to_doubleunits(zoom_sample / mwindow->edl->session->sample_rate); } float TrackCanvas::percentage_to_value(float percentage, int is_toggle, Auto *reference, int autogrouptype) { float result; if(is_toggle) { if(percentage > 0.5) result = 1; else result = 0; } else { 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; result = percentage * automation_range + automation_min; if(reference) { FloatAuto *ptr = (FloatAuto*)reference; result -= ptr->get_value(); } //printf("TrackCanvas::percentage_to_value %d %f\n", __LINE__, result); } return result; } void TrackCanvas::calculate_auto_position(double *x, double *y, double *in_x, double *in_y, double *out_x, double *out_y, Auto *current, double unit_start, double zoom_units, double yscale, 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; FloatAuto *ptr = (FloatAuto*)current; *x = (double)(ptr->position - unit_start) / zoom_units; *y = ((ptr->get_value() - automation_min) / automation_range - 0.5) * -yscale; if(in_x) { // *in_x = EQUIV(ptr->control_in_value, 0.0) ? *x : *x - mwindow->theme->control_pixels; *in_x = (double)(ptr->position + ptr->get_control_in_position() - unit_start) / zoom_units; } if(in_y) { *in_y = (((ptr->get_value() + ptr->get_control_in_value()) - automation_min) / automation_range - 0.5) * -yscale; } if(out_x) { // *out_x = EQUIV(ptr->control_out_value, 0.0) ? *x : *x + mwindow->theme->control_pixels; *out_x = (double)(ptr->position + ptr->get_control_out_position() - unit_start) / zoom_units; } if(out_y) { *out_y = (((ptr->get_value() + ptr->get_control_out_value()) - automation_min) / automation_range - 0.5) * -yscale; } } int TrackCanvas::do_float_autos(Track *track, Autos *autos, int cursor_x, int cursor_y, int draw, int buttonpress, int x_offset, int y_offset, int color, Auto* &auto_instance, int autogrouptype) { int result = 0; int center_pixel, draw_auto; double view_start, unit_start; double view_end, unit_end, yscale; double zoom_sample, zoom_units; double in_x2, in_y2, out_x2, out_y2; double slope; //int skip = 0; auto_instance = 0; if(draw) set_color(color); calculate_viewport(track, view_start, unit_start, view_end, unit_end, yscale, center_pixel, zoom_sample, zoom_units); // Get first auto before start Auto *current = 0, *previous = 0; for( current = autos->last; current && current->position >= unit_start; current = PREVIOUS ) ; Auto *first_auto = current ? current : autos->first ? autos->first : autos->default_auto; double ax = 0, ay = 0, ax2 = 0, ay2 = 0; 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 { current = autos->first; ax = 0; } do { //skip = 0; draw_auto = 1; if(current) { calculate_auto_position(&ax2, &ay2, &in_x2, &in_y2, &out_x2, &out_y2, current, unit_start, zoom_units, yscale, autogrouptype); } else { ax2 = get_w(); ay2 = ay; //skip = 1; } slope = ax2 > ax ? (ay2 - ay) / (ax2 - ax) : 0; if(ax2 > get_w()) { draw_auto = 0; ax2 = get_w(); ay2 = ay + slope * (get_w() - ax); } if(ax < 0) { ay = ay + slope * (0 - ax); ax = 0; } // Draw or test handle if( current && !result && current != autos->default_auto ) { if( !draw && track->record ) { result = test_floatauto((FloatAuto*)current, (int)ax2, (int)ay2, (int)in_x2, (int)in_y2, (int)out_x2, (int)out_y2, (int)center_pixel, (int)yscale, cursor_x, cursor_y, buttonpress, autogrouptype); if( result ) auto_instance = current; } if( draw && draw_auto ) { draw_floatauto((FloatAuto*)current, (int)ax2 + x_offset, (int)ay2, (int)in_x2 + x_offset, (int)in_y2, (int)out_x2 + x_offset, (int)out_y2, (int)center_pixel + y_offset, (int)yscale, color); } } // Draw or test joining line if( !draw && !result && track->record /* && buttonpress != 3 */ ) { result = test_floatline(center_pixel, (FloatAutos*)autos, unit_start, zoom_units, yscale, // Exclude auto coverage from the end of the line. The auto overlaps (int)ax, (int)ax2 - HANDLE_W / 2, cursor_x, cursor_y, buttonpress, autogrouptype); } if( draw ) draw_floatline(center_pixel, (FloatAuto*)previous, (FloatAuto*)current, (FloatAutos*)autos, unit_start, zoom_units, yscale, (int)ax, (int)ay, (int)ax2, (int)ay2, color, autogrouptype); if( current ) { previous = current; current = NEXT; } ax = ax2; ay = ay2; } while( current && current->position <= unit_end && !result ); if( ax < get_w() && !result ) { ax2 = get_w(); ay2 = ay; if(!draw && track->record /* && buttonpress != 3 */ ) { result = test_floatline(center_pixel, (FloatAutos*)autos, unit_start, zoom_units, yscale, (int)ax, (int)ax2, cursor_x, cursor_y, buttonpress, autogrouptype); } if( draw ) draw_floatline(center_pixel, (FloatAuto*)previous, (FloatAuto*)current, (FloatAutos*)autos, unit_start, zoom_units, yscale, (int)ax, (int)ay, (int)ax2, (int)ay2, color, autogrouptype); } return result; } int TrackCanvas::do_int_autos(Track *track, Autos *autos, int cursor_x, int cursor_y, int draw, int buttonpress, int x_offset, int y_offset, int color, Auto *&auto_instance) { int result = 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 ax, ay, ax2, ay2; auto_instance = 0; if(draw) set_color(color); calculate_viewport(track, view_start, unit_start, view_end, unit_end, yscale, center_pixel, zoom_sample, zoom_units); double high = -yscale * 0.8 / 2; double low = yscale * 0.8 / 2; // Get first auto before start Auto *current; for(current = autos->last; current && current->position >= unit_start; current = PREVIOUS) ; if(current) { ax = 0; ay = ((IntAuto*)current)->value > 0 ? high : low; current = NEXT; } else { current = autos->first ? autos->first : autos->default_auto; if(current) { ax = 0; ay = ((IntAuto*)current)->value > 0 ? high : low; } else { ax = 0; ay = yscale; } } do { if(current) { ax2 = (double)(current->position - unit_start) / zoom_units; ay2 = ((IntAuto*)current)->value > 0 ? high : low; } else { ax2 = get_w(); ay2 = ay; } if(ax2 > get_w()) ax2 = get_w(); if(current && !result) { if(current != autos->default_auto) { if(!draw) { if(track->record) { result = test_auto(current, (int)ax2, (int)ay2, (int)center_pixel, (int)yscale, cursor_x, cursor_y, buttonpress); if (result) auto_instance = current; } } else draw_auto(current, (int)ax2 + x_offset, (int)ay2 + y_offset, (int)center_pixel, (int)yscale); } current = NEXT; } if(!draw) { if(!result) { if(track->record /* && buttonpress != 3 */) { result = test_toggleline(autos, center_pixel, (int)ax, (int)ay, (int)ax2, (int)ay2, cursor_x, cursor_y, buttonpress); } } } else draw_toggleline(center_pixel + y_offset, (int)ax, (int)ay, (int)ax2, (int)ay2); ax = ax2; ay = ay2; }while(current && current->position <= unit_end && !result); if(ax < get_w() && !result) { ax2 = get_w(); ay2 = ay; if(!draw) { if(track->record /* && buttonpress != 3 */) { result = test_toggleline(autos, center_pixel, (int)ax, (int)ay, (int)ax2, (int)ay2, cursor_x, cursor_y, buttonpress); } } else draw_toggleline(center_pixel + y_offset, (int)ax, (int)ay, (int)ax2, (int)ay2); } return result; } int TrackCanvas::do_autos(Track *track, Autos *autos, int cursor_x, int cursor_y, int draw, int buttonpress, BC_Pixmap *pixmap, Auto * &auto_instance, int &rerender) { int result = 0; double view_start; double unit_start; double view_end; double unit_end; double yscale; int center_pixel; double zoom_sample; double zoom_units; calculate_viewport(track, view_start, unit_start, view_end, unit_end, yscale, center_pixel, zoom_sample, zoom_units); Auto *current; auto_instance = 0; for(current = autos->first; current && !result; current = NEXT) { if(current->position >= unit_start && current->position < unit_end) { int64_t x, y; x = (int64_t)((double)(current->position - unit_start) / zoom_units - (pixmap->get_w() / 2.0 + 0.5)); y = center_pixel - pixmap->get_h() / 2; if(!draw) { if(cursor_x >= x && cursor_y >= y && cursor_x < x + pixmap->get_w() && cursor_y < y + pixmap->get_h()) { result = 1; auto_instance = current; if(buttonpress && (buttonpress != 3)) { mwindow->session->drag_auto = current; mwindow->session->drag_start_position = current->position; mwindow->session->drag_origin_x = cursor_x; mwindow->session->drag_origin_y = cursor_y; double position = autos->track->from_units(current->position); double center = (mwindow->edl->local_session->get_selectionstart(1) + mwindow->edl->local_session->get_selectionend(1)) / 2; if(!shift_down()) { mwindow->edl->local_session->set_selectionstart(position); mwindow->edl->local_session->set_selectionend(position); } else if(position < center) { mwindow->edl->local_session->set_selectionstart(position); } else mwindow->edl->local_session->set_selectionend(position); rerender = 1; } } } else draw_pixmap(pixmap, x, y); } } return result; } // so this means it is always >0 when keyframe is found int TrackCanvas::do_plugin_autos(Track *track, int cursor_x, int cursor_y, int draw, int buttonpress, Plugin* &keyframe_plugin, KeyFrame* &keyframe_instance) { int result = 0; double view_start; double unit_start; double view_end; double unit_end; double yscale; int center_pixel; double zoom_sample; double zoom_units; if(!track->expand_view) return 0; calculate_viewport(track, view_start, unit_start, view_end, unit_end, yscale, center_pixel, zoom_sample, zoom_units); for(int i = 0; i < track->plugin_set.total && !result; i++) { PluginSet *plugin_set = track->plugin_set.values[i]; int center_pixel = track->y_pixel - mwindow->edl->local_session->track_start[pane->number]; if( track->show_titles() ) center_pixel += mwindow->theme->get_image("title_bg_data")->get_h(); if( track->show_assets() ) center_pixel += resource_h(); center_pixel += (i + 0.5) * mwindow->theme->get_image("plugin_bg_data")->get_h(); for(Plugin *plugin = (Plugin*)plugin_set->first; plugin && !result; plugin = (Plugin*)plugin->next) { for(KeyFrame *keyframe = (KeyFrame*)plugin->keyframes->first; keyframe && !result; keyframe = (KeyFrame*)keyframe->next) { //printf("TrackCanvas::draw_plugin_autos 3 %d\n", keyframe->position); if(keyframe->position >= unit_start && keyframe->position < unit_end) { int64_t x = (int64_t)((keyframe->position - unit_start) / zoom_units); int y = center_pixel - keyframe_pixmap->get_h() / 2; //printf("TrackCanvas::draw_plugin_autos 4 %d %d\n", x, center_pixel); if(!draw) { if(cursor_x >= x && cursor_y >= y && cursor_x < x + keyframe_pixmap->get_w() && cursor_y < y + keyframe_pixmap->get_h()) { result = 1; keyframe_plugin = plugin; keyframe_instance = keyframe; if(buttonpress) { mwindow->session->drag_auto = keyframe; mwindow->session->drag_start_position = keyframe->position; mwindow->session->drag_origin_x = cursor_x; mwindow->session->drag_origin_y = cursor_y; double position = track->from_units(keyframe->position); double center = (mwindow->edl->local_session->get_selectionstart(1) + mwindow->edl->local_session->get_selectionend(1)) / 2; if(!shift_down()) { mwindow->edl->local_session->set_selectionstart(position); mwindow->edl->local_session->set_selectionend(position); } else if(position < center) { mwindow->edl->local_session->set_selectionstart(position); } else mwindow->edl->local_session->set_selectionend(position); } } } else draw_pixmap(keyframe_pixmap, x, y); } } } } // if(buttonpress && buttonpress != 3 && result) // { // mwindow->undo->update_undo_before(); // } return result; } int TrackCanvas::draw_hairline(Auto *auto_keyframe, int color, int show) { Track *track = auto_keyframe->autos->track; int autogrouptype = auto_keyframe->autos->autogrouptype; 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); if( auto_keyframe->position < unit_start || auto_keyframe->position >= unit_end ) return 0; 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()); if( show ) { char text[BCSTRLEN]; if( auto_keyframe->is_floatauto() ) { FloatAuto *float_auto = (FloatAuto *)auto_keyframe; sprintf(text, "%0.2f", float_auto->get_value()); } else { IntAuto *int_auto = (IntAuto *)auto_keyframe; sprintf(text, "%d", int_auto->value); } int font = MEDIUMFONT; int tw = get_text_width(font, text) + TOOLTIP_MARGIN * 2; int th = get_text_height(font, text) + TOOLTIP_MARGIN * 2; set_color(get_resources()->tooltip_bg_color); ax += HANDLE_W/2; ay += center_pixel + HANDLE_W/2; draw_box(ax, ay, tw, th); set_color(BLACK); draw_rectangle(ax, ay, tw, th); set_font(font); ax += TOOLTIP_MARGIN; ay += TOOLTIP_MARGIN + get_text_ascent(font); draw_text(ax, ay, text); } return 0; } void TrackCanvas::draw_overlays() { int new_cursor, update_cursor, rerender; // Move background pixmap to foreground pixmap draw_pixmap(background_pixmap, 0, 0, get_w(), get_h(), 0, 0); // In/Out points draw_inout_points(); // Transitions draw_transitions(); // Plugins draw_plugins(); draw_hard_edges(); // Loop points draw_loop_points(); draw_brender_range(); // Highlighted areas draw_highlighting(); // Automation do_keyframes(0, 0, 1, 0, new_cursor, update_cursor, rerender); // Selection cursor if(pane->cursor) pane->cursor->restore(1); // Handle dragging draw_drag_handle(); // Playback cursor draw_playback_cursor(); draw_keyframe_reticle(); show_window(0); } int TrackCanvas::activate() { if(!active) { //printf("TrackCanvas::activate %d %d\n", __LINE__, pane->number); //BC_Signals::dump_stack(); get_top_level()->deactivate(); active = 1; set_active_subwindow(this); pane->cursor->activate(); gui->focused_pane = pane->number; } return 0; } int TrackCanvas::deactivate() { if(active) { active = 0; pane->cursor->deactivate(); } return 0; } void TrackCanvas::update_drag_handle() { double new_position; int cursor_x = get_cursor_x(); new_position = (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) { mwindow->session->drag_position = new_position; gui->mainclock->update(new_position); timebar_position = new_position; gui->update_timebar(0); EDL *edl = new EDL; edl->create_objects(); edl->copy_all(mwindow->edl); MainSession *session = mwindow->session; int edit_mode = mwindow->edl->session->edit_handle_mode[session->drag_button]; edl->modify_edithandles(session->drag_start, session->drag_position, session->drag_handle, edit_mode, edl->session->labels_follow_edits, edl->session->plugins_follow_edits, edl->session->autos_follow_edits, session->drag_edit->group_id); double position = edit_mode != MOVE_NO_EDITS && ( session->drag_handle || edit_mode == MOVE_ONE_EDIT ) ? session->drag_position : session->drag_start; Track *track = session->drag_handle_track(); int64_t pos = track->to_units(position, 0); render_handle_frame(edl, pos, shift_down() ? 0 : session->drag_handle ? 1 : 2); edl->remove_user(); } } int TrackCanvas::render_handle_frame(EDL *edl, int64_t pos, int mode) { int result = 0; int64_t left = pos-1; if( left < 0 ) left = 0; switch( mode ) { case 0: { VFrame vlt(edl->get_w(), edl->get_h(), edl->session->color_model); VFrame vrt(edl->get_w(), edl->get_h(), edl->session->color_model); TransportCommand command; command.command = CURRENT_FRAME; command.get_edl()->copy_all((EDL *)edl); command.change_type = CHANGE_ALL; command.realtime = 0; Preferences *preferences = mwindow->preferences; RenderEngine *render_engine = new RenderEngine(0, preferences, 0, 0); CICache *video_cache = new CICache(preferences); render_engine->set_vcache(video_cache); render_engine->arm_command(&command); int64_t left = pos-1; if( left < 0 ) left = 0; VRender *vrender = render_engine->vrender; result = vrender && !vrender->process_buffer(&vlt, left, 0) && !vrender->process_buffer(&vrt, pos , 0) ? 0 : 1; delete render_engine; delete video_cache; mwindow->cwindow->gui->lock_window("TrackCanvas::render_handle_frame 0"); Canvas *canvas = mwindow->cwindow->gui->canvas; canvas->lock_canvas("TrackCanvas::render_handle_frame 1"); int w = canvas->w, h = canvas->h, w2 = w/2, h2 = h/2; int lx = 0, ly = h2/2, rx = w2, ry = h2/2; BC_WindowBase *window = canvas->get_canvas(); window->set_color(BLACK); window->clear_box(0,0, window->get_w(),window->get_h()); window->draw_vframe(&vlt, lx,ly, w2,h2, 0,0,vlt.get_w(),vlt.get_h()); window->draw_vframe(&vrt, rx,ry, w2,h2, 0,0,vrt.get_w(),vrt.get_h()); window->flash(1); canvas->unlock_canvas(); mwindow->cwindow->gui->unlock_window(); break; } case 1: case 2: { Track *track = mwindow->session->drag_handle_track(); double position = track->from_units(mode == 1 ? left : pos); if( position < 0 ) position = 0; edl->local_session->set_selectionstart(position); edl->local_session->set_selectionend(position); PlaybackEngine *playback_engine = mwindow->cwindow->playback_engine; if( playback_engine->is_playing_back ) playback_engine->stop_playback(1); mwindow->cwindow->playback_engine->refresh_frame(CHANGE_EDL, edl, 0); break; } } return result; } int TrackCanvas::update_drag_edit() { int result = 0; return result; } int TrackCanvas::get_drag_values(float *percentage, int64_t *position, int do_clamp, int cursor_x, int cursor_y, Auto *current) { //int x = cursor_x - mwindow->session->drag_origin_x; //int y = cursor_y - mwindow->session->drag_origin_y; *percentage = 0; *position = 0; if(!current->autos->track->record) return 1; double view_start; double unit_start; double view_end; double unit_end; double yscale; int center_pixel; double zoom_sample; double zoom_units; calculate_viewport(current->autos->track, view_start, unit_start, view_end, unit_end, yscale, center_pixel, zoom_sample, zoom_units); *percentage = (float)(mwindow->session->drag_origin_y - cursor_y) / yscale + mwindow->session->drag_start_percentage; if(do_clamp) CLAMP(*percentage, 0, 1); *position = Units::to_int64(zoom_units * (cursor_x - mwindow->session->drag_origin_x) + mwindow->session->drag_start_position + 0.5); if((do_clamp) && *position < 0) *position = 0; return 0; } #define UPDATE_DRAG_HEAD(do_clamp) \ int result = 0, center_pixel; \ if(!current->autos->track->record) return 0; \ 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); \ \ float percentage = (float)(mwindow->session->drag_origin_y - cursor_y) / \ 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; 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, 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) { case 0: // Center // Snap to nearby values old_value = current->get_value(); if(shift_down()) { double value1, value2, distance1, 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); } 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 && !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() && !shift_down()) // ALT constrains movement: fixed position, only changing the value 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); update_ganged_autos(change, current->autos->track, current); show_message(current, 1,", %.2f", current->get_value()); } break; // In control case 1: { 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())); update_ganged_autos(0, current->autos->track, current); show_message(current, 1,", %.2f", current->get_control_in_value()); } 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())); update_ganged_autos(0, current->autos->track, current); show_message(current, 1,", %.2f", current->get_control_out_value()); } break; } } return result; } int TrackCanvas::update_drag_toggleauto(int cursor_x, int cursor_y) { IntAuto *current = (IntAuto*)mwindow->session->drag_auto; UPDATE_DRAG_HEAD(1); int value = (int)percentage_to_value(percentage, 1, 0, AUTOGROUPTYPE_INT255); if(value != current->value || position != current->position) { result = 1; current->value = value; current->position = position; show_message(current, 0,", %d", current->value); } return result; } // Autos which can't change value through dragging. int TrackCanvas::update_drag_auto(int cursor_x, int cursor_y) { Auto *current = (Auto*)mwindow->session->drag_auto; UPDATE_DRAG_HEAD(1) if(position != current->position) { result = 1; current->position = position; show_message(current, 0,""); 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)) / 2; if(!shift_down()) { mwindow->edl->local_session->set_selectionstart(position_f); mwindow->edl->local_session->set_selectionend(position_f); } else if(position_f < center_f) { mwindow->edl->local_session->set_selectionstart(position_f); } else mwindow->edl->local_session->set_selectionend(position_f); } return result; } int TrackCanvas::update_drag_pluginauto(int cursor_x, int cursor_y) { KeyFrame *current = (KeyFrame*)mwindow->session->drag_auto; UPDATE_DRAG_HEAD(1) if(position != current->position) { // printf("uida: autos: %p, track: %p ta: %p\n", current->autos, current->autos->track, current->autos->track->automation); Track *track = current->autos->track; //PluginAutos *pluginautos = (PluginAutos *)current->autos; PluginSet *pluginset; Plugin *plugin = 0; // figure out the correct pluginset & correct plugin int found = 0; for(int i = 0; i < track->plugin_set.total; i++) { pluginset = track->plugin_set.values[i]; for(plugin = (Plugin *)pluginset->first; plugin; plugin = (Plugin *)plugin->next) { KeyFrames *keyframes = plugin->keyframes; for(KeyFrame *currentkeyframe = (KeyFrame *)keyframes->first; currentkeyframe; currentkeyframe = (KeyFrame *) currentkeyframe->next) { if (currentkeyframe == current) { found = 1; break; } } if (found) break; } if (found) break; } mwindow->session->plugin_highlighted = plugin; mwindow->session->track_highlighted = track; result = 1; current->position = position; show_message(current, 0,""); 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)) / 2; if(!shift_down()) { mwindow->edl->local_session->set_selectionstart(position_f); mwindow->edl->local_session->set_selectionend(position_f); } else if(position_f < center_f) { mwindow->edl->local_session->set_selectionstart(position_f); } else mwindow->edl->local_session->set_selectionend(position_f); } return result; } void TrackCanvas::update_drag_caption() { switch(mwindow->session->current_operation) { case DRAG_FADE: break; } } int TrackCanvas::cursor_update(int in_motion) { int result = 0; int cursor_x = 0; int cursor_y = 0; int update_clock = 0; int update_zoom = 0; int update_scroll = 0; int update_overlay = 0; int update_cursor = 0; int rerender = 0; double position = 0.; //printf("TrackCanvas::cursor_update %d\n", __LINE__); // Default cursor int new_cursor = arrow_mode() ? ARROW_CURSOR : ibeam_mode() ? IBEAM_CURSOR : 0; switch(mwindow->session->current_operation) { case DRAG_EDITHANDLE1: // Outside threshold. Upgrade status if(active) { if(labs(get_cursor_x() - mwindow->session->drag_origin_x) > HANDLE_W) { mwindow->session->current_operation = DRAG_EDITHANDLE2; update_overlay = 1; } } break; case DRAG_EDITHANDLE2: if(active) { update_drag_handle(); update_overlay = 1; } break; case DRAG_PLUGINHANDLE1: if(active) { if(labs(get_cursor_x() - mwindow->session->drag_origin_x) > HANDLE_W) { mwindow->session->current_operation = DRAG_PLUGINHANDLE2; update_overlay = 1; } } break; case DRAG_PLUGINHANDLE2: if(active) { update_drag_handle(); update_overlay = 1; } break; // Rubber band curves case DRAG_FADE: case DRAG_SPEED: case DRAG_CZOOM: case DRAG_PZOOM: case DRAG_CAMERA_X: case DRAG_CAMERA_Y: case DRAG_CAMERA_Z: case DRAG_PROJECTOR_X: case DRAG_PROJECTOR_Y: case DRAG_PROJECTOR_Z: if(active) rerender = update_overlay = update_drag_floatauto(get_cursor_x(), get_cursor_y()); if( rerender && mwindow->session->current_operation == DRAG_SPEED ) mwindow->speed_after(!in_motion ? 1 : 0); break; case DRAG_PLAY: if(active) rerender = update_overlay = update_drag_toggleauto(get_cursor_x(), get_cursor_y()); break; case DRAG_MUTE: if(active) rerender = update_overlay = update_drag_toggleauto(get_cursor_x(), get_cursor_y()); break; // Keyframe icons are sticky case DRAG_PAN_PRE: case DRAG_MASK_PRE: case DRAG_MODE_PRE: case DRAG_PLUGINKEY_PRE: if(active) { if(labs(get_cursor_x() - mwindow->session->drag_origin_x) > HANDLE_W) { mwindow->session->current_operation++; update_overlay = 1; mwindow->undo->update_undo_before(); } } break; case DRAG_PAN: case DRAG_MASK: case DRAG_MODE: if(active) rerender = update_overlay = update_drag_auto(get_cursor_x(), get_cursor_y()); break; case DRAG_PLUGINKEY: if(active) rerender = update_overlay = update_drag_pluginauto(get_cursor_x(), get_cursor_y()); break; case SELECT_REGION: if(active) { cursor_x = get_cursor_x(); cursor_y = get_cursor_y(); position = (double)(cursor_x + mwindow->edl->local_session->view_start[pane->number]) * mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate; position = mwindow->edl->align_to_frame(position, 0); position = MAX(position, 0); double start = mwindow->edl->local_session->get_selectionstart(1); double end = mwindow->edl->local_session->get_selectionend(1); if(position < selection_midpoint) { mwindow->edl->local_session->set_selectionend(selection_midpoint); mwindow->edl->local_session->set_selectionstart(position); } else { mwindow->edl->local_session->set_selectionstart(selection_midpoint); mwindow->edl->local_session->set_selectionend(position); } // Que the CWindow gui->unlock_window(); int dir = start != mwindow->edl->local_session->get_selectionstart(1) ? 1 : end != mwindow->edl->local_session->get_selectionend(1) ? -1 : 0; mwindow->cwindow->update(dir, 0, 0, 0, 1); gui->lock_window("TrackCanvas::cursor_update 1"); // Update the faders mwindow->update_plugin_guis(); gui->update_patchbay(); timebar_position = mwindow->edl->local_session->get_selectionend(1); gui->hide_cursor(0); gui->draw_cursor(1); gui->update_timebar(0); gui->flash_canvas(1); result = 1; update_clock = 1; update_zoom = 1; update_scroll = 1; } break; case DROP_TARGETING: new_cursor = GRABBED_CURSOR; result = 1; break; default: if(is_event_win() && cursor_inside()) { // Update clocks cursor_x = get_cursor_x(); position = mwindow->edl->get_cursor_position(cursor_x, pane->number); position = mwindow->edl->align_to_frame(position, 0); update_clock = 1; // set all timebars for(int i = 0; i < TOTAL_PANES; i++) if(gui->pane[i]) gui->pane[i]->canvas->timebar_position = position; //printf("TrackCanvas::cursor_update %d %d %p %p\n", __LINE__, pane->number, pane, pane->timebar); gui->update_timebar(0); // Update cursor if(do_transitions(get_cursor_x(), get_cursor_y(), 0, new_cursor, update_cursor)) break; if(do_keyframes(get_cursor_x(), get_cursor_y(), 0, 0, new_cursor, update_cursor, rerender)) break; if(do_edit_handles(get_cursor_x(), get_cursor_y(), 0, rerender, update_overlay, new_cursor, update_cursor)) break; // Plugin boundaries if(do_plugin_handles(get_cursor_x(), get_cursor_y(), 0, rerender, update_overlay, new_cursor, update_cursor)) break; if(do_edits(get_cursor_x(), get_cursor_y(), 0, 0, update_overlay, rerender, new_cursor, update_cursor)) break; } break; } //printf("TrackCanvas::cursor_update 1\n"); if(update_cursor && new_cursor != get_cursor()) { set_cursor(new_cursor, 0, 1); } //printf("TrackCanvas::cursor_update 1 %d\n", rerender); if(rerender && render_timer->get_difference() > 0.25 ) { render_timer->update(); mwindow->restart_brender(); mwindow->sync_parameters(CHANGE_PARAMS); mwindow->update_plugin_guis(); gui->unlock_window(); mwindow->cwindow->update(1, 0, 0, 0, 1); gui->lock_window("TrackCanvas::cursor_update 2"); } if(rerender) { // Update faders gui->update_patchbay(); } if(update_clock) { if(!mwindow->cwindow->playback_engine->is_playing_back) gui->mainclock->update(position); } if(update_zoom) { gui->zoombar->update(); } if(update_scroll) { if(!drag_scroll && (cursor_x >= get_w() || cursor_x < 0 || cursor_y >= get_h() || cursor_y < 0)) start_dragscroll(); else if(drag_scroll && (cursor_x < get_w() && cursor_x >= 0 && cursor_y < get_h() && cursor_y >= 0)) stop_dragscroll(); } if(update_overlay) { gui->draw_overlays(1); } //printf("TrackCanvas::cursor_update %d\n", __LINE__); return result; } int TrackCanvas::cursor_motion_event() { return cursor_update(1); } void TrackCanvas::start_dragscroll() { if(!drag_scroll) { drag_scroll = 1; set_repeat(BC_WindowBase::get_resources()->scroll_repeat); //printf("TrackCanvas::start_dragscroll 1\n"); } } void TrackCanvas::stop_dragscroll() { if(drag_scroll) { drag_scroll = 0; unset_repeat(BC_WindowBase::get_resources()->scroll_repeat); //printf("TrackCanvas::stop_dragscroll 1\n"); } } int TrackCanvas::repeat_event(int64_t duration) { if(!drag_scroll) return 0; if(duration != BC_WindowBase::get_resources()->scroll_repeat) return 0; int sample_movement = 0; int track_movement = 0; int64_t x_distance = 0; int64_t y_distance = 0; double position = 0; int result = 0; switch(mwindow->session->current_operation) { case SELECT_REGION: //printf("TrackCanvas::repeat_event 1 %d\n", mwindow->edl->local_session->view_start); if(get_cursor_x() > get_w()) { x_distance = get_cursor_x() - get_w(); sample_movement = 1; } else if(get_cursor_x() < 0) { x_distance = get_cursor_x(); sample_movement = 1; } if(get_cursor_y() > get_h()) { y_distance = get_cursor_y() - get_h(); track_movement = 1; } else if(get_cursor_y() < 0) { y_distance = get_cursor_y(); track_movement = 1; } result = 1; break; } if(sample_movement) { position = (double)(get_cursor_x() + mwindow->edl->local_session->view_start[pane->number] + x_distance) * mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate; position = mwindow->edl->align_to_frame(position, 0); position = MAX(position, 0); //printf("TrackCanvas::repeat_event 1 %f\n", position); switch(mwindow->session->current_operation) { case SELECT_REGION: if(position < selection_midpoint) { mwindow->edl->local_session->set_selectionend(selection_midpoint); mwindow->edl->local_session->set_selectionstart(position); // Que the CWindow gui->unlock_window(); mwindow->cwindow->update(1, 0, 0); gui->lock_window("TrackCanvas::repeat_event"); // Update the faders mwindow->update_plugin_guis(); gui->update_patchbay(); } else { mwindow->edl->local_session->set_selectionstart(selection_midpoint); mwindow->edl->local_session->set_selectionend(position); // Don't que the CWindow } break; } mwindow->samplemovement( mwindow->edl->local_session->view_start[pane->number] + x_distance, pane->number); } if(track_movement) { mwindow->trackmovement(y_distance, pane->number); } return result; } int TrackCanvas::button_release_event() { int redraw = -1, update_overlay = 0; int result = 0, load_flags = 0; // printf("TrackCanvas::button_release_event %d\n", // mwindow->session->current_operation); if(active) { switch(mwindow->session->current_operation) { case DRAG_EDITHANDLE2: mwindow->session->current_operation = NO_OPERATION; drag_scroll = 0; result = 1; end_edithandle_selection(); break; case DRAG_EDITHANDLE1: mwindow->session->current_operation = NO_OPERATION; drag_scroll = 0; result = 1; break; case DRAG_PLUGINHANDLE2: mwindow->session->current_operation = NO_OPERATION; drag_scroll = 0; result = 1; end_pluginhandle_selection(); break; case DRAG_PLUGINHANDLE1: mwindow->session->current_operation = NO_OPERATION; drag_scroll = 0; result = 1; break; case DRAG_SPEED: redraw = FORCE_REDRAW; load_flags |= LOAD_EDITS; case DRAG_FADE: // delete the drag_auto_gang first and remove out of order keys clear_ganged_autos(); case DRAG_CZOOM: case DRAG_PZOOM: case DRAG_PLAY: case DRAG_MUTE: case DRAG_MASK: case DRAG_MODE: case DRAG_PAN: case DRAG_CAMERA_X: case DRAG_CAMERA_Y: case DRAG_CAMERA_Z: case DRAG_PROJECTOR_X: case DRAG_PROJECTOR_Y: case DRAG_PROJECTOR_Z: case DRAG_PLUGINKEY: load_flags |= LOAD_AUTOMATION; mwindow->session->current_operation = NO_OPERATION; mwindow->session->drag_handle = 0; // Remove any out-of-order keyframe if(mwindow->session->drag_auto) { mwindow->session->drag_auto->autos->remove_nonsequential( mwindow->session->drag_auto); // mwindow->session->drag_auto->autos->optimize(); update_overlay = 1; } mwindow->undo->update_undo_after(_("keyframe"), load_flags); result = 1; break; case DRAG_EDIT: case DRAG_GROUP: case DRAG_AEFFECT_COPY: case DRAG_VEFFECT_COPY: // Trap in drag stop break; case DROP_TARGETING: { int cursor_x = get_cursor_x(), cursor_y = get_cursor_y(); Track *track=0; Edit *edit=0; PluginSet *pluginset=0; Plugin *plugin=0; drag_cursor_motion(cursor_x, cursor_y, &track, &edit, &pluginset, &plugin); double position = mwindow->edl->get_cursor_position(cursor_x, pane->number); gui->edit_menu->activate_menu(track, edit, pluginset, plugin, position); mwindow->session->current_operation = NO_OPERATION; result = 1; break; } case GROUP_TOGGLE: { Edit *edit = mwindow->session->drag_edit; if( edit ) { if( shift_down() && edit->is_selected ) { if( edit->group_id > 0 ) { mwindow->edl->tracks->clear_selected_edits(); mwindow->edl->tracks->del_group(edit->group_id); } else { int id = mwindow->session->group_number++; mwindow->edl->tracks->new_group(id); } redraw = 0; } else { if( mwindow->preferences->ctrl_toggle && !ctrl_down() ) mwindow->edl->tracks->clear_selected_edits(); edit->set_selected(-1); } } mwindow->session->current_operation = NO_OPERATION; update_overlay = 1; result = 1; drag_scroll = 0; break; } default: if( mwindow->session->current_operation ) { // if(mwindow->session->current_operation == SELECT_REGION) { // mwindow->undo->update_undo_after(_("select"), LOAD_SESSION, 0, 0); // } mwindow->session->current_operation = NO_OPERATION; drag_scroll = 0; //result = 0; break; } break; } } if (result) cursor_update(0); if(update_overlay) { gui->draw_overlays(1); } if(redraw >= 0) { gui->draw_canvas(redraw, 0); gui->flash_canvas(1); } return result; } int TrackCanvas::do_edit_handles(int cursor_x, int cursor_y, int button_press, int &rerender, int &update_overlay, int &new_cursor, int &update_cursor) { Edit *edit_result = 0; int handle_result = -1; int result = 0; for( Track *track=mwindow->edl->tracks->first; track && !result; track=track->next) { for( Edit *edit=track->edits->first; edit && !result; edit=edit->next ) { 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_x <= edit_x + edit_w && cursor_y >= edit_y && cursor_y < edit_y + edit_h ) { if( cursor_x < edit_x + HANDLE_W ) { edit_result = edit; handle_result = 0; if( cursor_y >= edit_y+edit_h - HANDLE_W && track->show_assets() ) { new_cursor = DOWNLEFT_RESIZE; if( button_press == LEFT_BUTTON ) result = -1; } else result = 1; } else if( cursor_x >= edit_x + edit_w - HANDLE_W ) { edit_result = edit; handle_result = 1; if( cursor_y >= edit_y+edit_h - HANDLE_W && track->show_assets() ) { new_cursor = DOWNRIGHT_RESIZE; if( button_press == LEFT_BUTTON ) result = -1; } else result = 1; } } } } update_cursor = 1; if( result > 0 ) { double position = 0; if( handle_result == 0 ) { position = edit_result->track->from_units(edit_result->startproject); new_cursor = LEFT_CURSOR; } else if( handle_result == 1 ) { position = edit_result->track->from_units(edit_result->startproject + edit_result->length); new_cursor = RIGHT_CURSOR; } // Reposition cursor if( button_press ) { mwindow->session->drag_edit = edit_result; mwindow->session->drag_handle = handle_result; mwindow->session->drag_button = get_buttonpress() - 1; mwindow->session->drag_position = position; mwindow->session->current_operation = DRAG_EDITHANDLE1; mwindow->session->drag_origin_x = get_cursor_x(); mwindow->session->drag_origin_y = get_cursor_y(); mwindow->session->drag_start = position; rerender = start_selection(position); update_overlay = 1; } } else if( result < 0 ) { mwindow->undo->update_undo_before(); if( !shift_down() ) { if( handle_result == 0 ) edit_result->hard_left = !edit_result->hard_left; else if( handle_result == 1 ) edit_result->hard_right = !edit_result->hard_right; } else { int status = handle_result == 0 ? edit_result->hard_left : handle_result == 1 ? edit_result->hard_right : 0; int new_status = !status; int64_t edit_edge = edit_result->startproject; if( handle_result == 1 ) edit_edge += edit_result->length; double edge_position = edit_result->track->from_units(edit_edge); for( Track *track=mwindow->edl->tracks->first; track!=0; track=track->next ) { int64_t track_position = track->to_units(edge_position, 1); Edit *left_edit = track->edits->editof(track_position, PLAY_FORWARD, 0); if( left_edit ) { int64_t left_edge = left_edit->startproject; double left_position = track->from_units(left_edge); if( EQUIV(edge_position, left_position) ) { left_edit->hard_left = new_status; if( left_edit->previous ) left_edit->previous->hard_right = new_status; } } Edit *right_edit = track->edits->editof(track_position, PLAY_REVERSE, 0); if( right_edit ) { int64_t right_edge = right_edit->startproject + right_edit->length; double right_position = track->from_units(right_edge); if( EQUIV(edge_position, right_position) ) { right_edit->hard_right = new_status; if( right_edit->next ) right_edit->next->hard_left = new_status; } } } } rerender = update_overlay = 1; mwindow->undo->update_undo_after(_("hard_edge"), LOAD_EDITS); result = 1; } return result; } int TrackCanvas::do_plugin_handles(int cursor_x, int cursor_y, int button_press, int &rerender, int &update_overlay, int &new_cursor, int &update_cursor) { Plugin *plugin_result = 0; int handle_result = 0; int result = 0; for(Track *track = mwindow->edl->tracks->first; track && !result; track = track->next) { for(int i = 0; i < track->plugin_set.total && !result; i++) { PluginSet *plugin_set = track->plugin_set.values[i]; for(Plugin *plugin = (Plugin*)plugin_set->first; plugin && !result; plugin = (Plugin*)plugin->next) { int64_t plugin_x, plugin_y, plugin_w, plugin_h; plugin_dimensions(plugin, plugin_x, plugin_y, plugin_w, plugin_h); if(cursor_x >= plugin_x && cursor_x <= plugin_x + plugin_w && cursor_y >= plugin_y && cursor_y < plugin_y + plugin_h) { if(cursor_x < plugin_x + HANDLE_W) { plugin_result = plugin; handle_result = 0; result = 1; } else if(cursor_x >= plugin_x + plugin_w - HANDLE_W) { plugin_result = plugin; handle_result = 1; result = 1; } } } if(result && shift_down()) mwindow->session->trim_edits = plugin_set; } } update_cursor = 1; if(result) { double position = 0; if(handle_result == 0) { position = plugin_result->track->from_units(plugin_result->startproject); new_cursor = LEFT_CURSOR; } else if(handle_result == 1) { position = plugin_result->track->from_units(plugin_result->startproject + plugin_result->length); new_cursor = RIGHT_CURSOR; } if(button_press) { mwindow->session->drag_plugin = plugin_result; mwindow->session->drag_handle = handle_result; mwindow->session->drag_button = get_buttonpress() - 1; mwindow->session->drag_position = position; mwindow->session->current_operation = DRAG_PLUGINHANDLE1; mwindow->session->drag_origin_x = get_cursor_x(); mwindow->session->drag_origin_y = get_cursor_y(); mwindow->session->drag_start = position; rerender = start_selection(position); update_overlay = 1; } } return result; } int TrackCanvas::do_tracks(int cursor_x, int cursor_y, int button_press) { int result = 0; Track *track=0; Edit *edit=0; PluginSet *pluginset=0; Plugin *plugin=0; drag_cursor_motion(cursor_x, cursor_y, &track, &edit, &pluginset, &plugin); if( button_press && track ) { switch( get_buttonpress() ) { case RIGHT_BUTTON: { double position = mwindow->edl->get_cursor_position(cursor_x, pane->number); gui->track_menu->activate_menu(track, edit, pluginset, plugin, position); mwindow->session->current_operation = NO_OPERATION; result = 1; break; } case MIDDLE_BUTTON: set_cursor(GRABBED_CURSOR, 0, 1); mwindow->session->current_operation = DROP_TARGETING; result = 1; break; } } return result; } int TrackCanvas::arrow_mode() { return mwindow->edl->session->editing_mode == EDITING_ARROW ? 1 : 0; } int TrackCanvas::ibeam_mode() { return mwindow->edl->session->editing_mode == EDITING_IBEAM ? 1 : 0; } int TrackCanvas::do_edits(int cursor_x, int cursor_y, int button_press, int drag_start, int &redraw, int &rerender, int &new_cursor, int &update_cursor) { int result = 0; for(Track *track = mwindow->edl->tracks->first; track && !result; track = track->next) { for(Edit *edit = track->edits->first; edit && !result; edit = edit->next) { int64_t edit_x, edit_y, edit_w, edit_h; edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h); // Cursor inside a track // Cursor inside an edit if( cursor_x >= edit_x && cursor_x < edit_x + edit_w && cursor_y >= edit_y && cursor_y < edit_y + edit_h ) { if( button_press && get_buttonpress() == LEFT_BUTTON ) { if( get_double_click() ) { mwindow->edl->tracks->clear_selected_edits(); mwindow->edl->tracks->select_affected_edits( edit->track->from_units(edit->startproject), edit->track, 1); double start = edit->track->from_units(edit->startproject); start = mwindow->edl->align_to_frame(start, 0); mwindow->edl->local_session->set_selectionstart(start); double end = edit->track->from_units(edit->startproject+edit->length); end = mwindow->edl->align_to_frame(end, 0); mwindow->edl->local_session->set_selectionend(end); result = 1; // Select edit duration or select_region } else if( arrow_mode() || (ibeam_mode() && ctrl_down()) ) { mwindow->session->drag_edit = edit; mwindow->session->current_operation = GROUP_TOGGLE; result = 1; } if( result ) { rerender = 1; update_cursor = -1; } } else if( drag_start && track->record ) { mwindow->session->drag_edit = edit; mwindow->session->drag_origin_x = cursor_x; mwindow->session->drag_origin_y = cursor_y; // Where the drag started, so we know relative position inside the edit later mwindow->session->drag_position = mwindow->edl->get_cursor_position(cursor_x, pane->number); drag_start = 0; // if unselected "fast" drag if( !edit->silence() && !edit->is_selected ) { mwindow->edl->tracks->clear_selected_edits(); if( ibeam_mode() ) { double start = edit->track->from_units(edit->startproject); mwindow->edl->local_session->set_selectionstart(start); mwindow->edl->local_session->set_selectionend(start); edit->set_selected(1); } else mwindow->edl->tracks->select_affected_edits( edit->track->from_units(edit->startproject), edit->track, 1); drag_start = 1; } // Construct list of all affected edits if( drag_start || ibeam_mode() ) { mwindow->edl->tracks->get_selected_edits(mwindow->session->drag_edits); if( mwindow->session->drag_edits->size() > 0 ) { mwindow->session->current_operation = DRAG_EDIT; // Need to create drag window int cx, cy; get_abs_cursor(cx, cy); gui->drag_popup = new BC_DragWindow(gui, mwindow->theme->get_image("clip_icon"), cx, cy); result = 1; } else { rerender = start_selection(mwindow->session->drag_position); mwindow->session->current_operation = SELECT_REGION; update_cursor = 1; } } else if( edit->is_selected && arrow_mode() ) { if( mwindow->session->drag_group ) mwindow->session->drag_group->remove_user(); double start_position = 0; mwindow->session->drag_group = mwindow->selected_edits_to_clip(0, &start_position, &mwindow->session->drag_group_first_track, mwindow->edl->session->labels_follow_edits, mwindow->edl->session->autos_follow_edits, mwindow->edl->session->plugins_follow_edits); if( mwindow->session->drag_group ) { mwindow->session->current_operation = DRAG_GROUP; mwindow->session->drag_group_position = start_position; mwindow->session->drag_group_edit = edit; mwindow->session->drag_origin_y = edit_y; result = 1; } } } } } } return result; } int TrackCanvas::test_track_group(EDL *group, Track *first_track, double &pos) { Track *src = group->tracks->first; for( Track *track=first_track; track && src; track=track->next ) { if( !track->record ) continue; if( src->data_type != track->data_type ) return -1; for( Edit *src_edit=src->edits->first; src_edit; src_edit=src_edit->next ) { if( src_edit->silence() ) continue; if( edit_intersects(track, src_edit, pos) ) return 0; } src = src->next; } return !src ? 1 : 0; } int TrackCanvas::edit_intersects(Track *track, Edit *src_edit, double &pos) { if( pos < 0 ) { pos = 0; return 1; } int64_t src_start = src_edit->startproject; int64_t src_end = src_start + src_edit->length; double new_start = src_edit->track->from_units(src_start) + pos; double new_end = src_edit->track->from_units(src_end) + pos; int64_t trk_start = track->to_units(new_start, 1); int64_t trk_end = track->to_units(new_end, 1); for( Edit *edit=track->edits->first; edit; edit=edit->next ) { if( edit->is_selected || edit->silence() ) continue; int64_t edit_start = edit->startproject; if( edit_start >= trk_end ) continue; int64_t edit_end = edit_start + edit->length; if( trk_start >= edit_end ) continue; int64_t lt_dist = labs(trk_end - edit_start); int64_t rt_dist = labs(edit_end - trk_start); int64_t position; if( lt_dist < rt_dist ) { position = edit_start; lt_dist = abs(trk_start - edit_start); rt_dist = abs(trk_end - edit_start); if( lt_dist > rt_dist ) position -= src_end; } else { position = edit_end; lt_dist = abs(trk_start - edit_end); rt_dist = abs(trk_end - edit_end); if( lt_dist > rt_dist ) position -= src_end; } pos = edit->track->from_units(position); return 1; } return 0; } int TrackCanvas::test_resources(int cursor_x, int cursor_y) { return 0; } int TrackCanvas::do_plugins(int cursor_x, int cursor_y, int drag_start, int button_press, int &redraw, int &rerender) { Plugin *plugin = 0; int result = 0; int done = 0; int64_t x, y, w, h; Track *track = 0; for(track = mwindow->edl->tracks->first; track && !done; track = track->next) { if(!track->expand_view) continue; for(int i = 0; i < track->plugin_set.total && !done; i++) { // first check if plugins are visible at all if (!track->expand_view) continue; PluginSet *plugin_set = track->plugin_set.values[i]; for(plugin = (Plugin*)plugin_set->first; plugin && !done; plugin = (Plugin*)plugin->next) { plugin_dimensions(plugin, x, y, w, h); if(MWindowGUI::visible(x, x + w, 0, get_w()) && MWindowGUI::visible(y, y + h, 0, get_h())) { if(cursor_x >= x && cursor_x < x + w && cursor_y >= y && cursor_y < y + h) { done = 1; break; } } } } } if(plugin) { // Start plugin popup if(button_press) { if(get_buttonpress() == RIGHT_BUTTON ) { gui->plugin_menu->update(plugin); gui->plugin_menu->activate_menu(); result = 1; } else if (get_double_click() && !drag_start) { // Select range of plugin on doubleclick over plugin mwindow->edl->local_session->set_selectionstart(plugin->track->from_units(plugin->startproject)); mwindow->edl->local_session->set_selectionend(plugin->track->from_units(plugin->startproject) + plugin->track->from_units(plugin->length)); if(mwindow->edl->session->cursor_on_frames) { mwindow->edl->local_session->set_selectionstart( mwindow->edl->align_to_frame(mwindow->edl->local_session->get_selectionstart(1), 0)); mwindow->edl->local_session->set_selectionend( mwindow->edl->align_to_frame(mwindow->edl->local_session->get_selectionend(1), 1)); } rerender = 1; redraw = 1; result = 1; } } else // Move plugin if( drag_start && plugin->track->record && !plugin->silence() ) { if( mwindow->edl->session->editing_mode == EDITING_ARROW ) { if( plugin->track->data_type == TRACK_AUDIO ) mwindow->session->current_operation = DRAG_AEFFECT_COPY; else if( plugin->track->data_type == TRACK_VIDEO ) mwindow->session->current_operation = DRAG_VEFFECT_COPY; mwindow->session->drag_plugin = plugin; mwindow->session->drag_origin_x = cursor_x; mwindow->session->drag_origin_y = cursor_y; // Where the drag started, so we know relative position inside the edit later mwindow->session->drag_position = (double)(cursor_x + mwindow->edl->local_session->view_start[pane->number]) * mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate; // Create picon switch(plugin->plugin_type) { case PLUGIN_STANDALONE: { PluginServer *server = mwindow->scan_plugindb(plugin->title, plugin->track->data_type); if( !server ) break; VFrame *frame = server->picon; if(!frame) { if(plugin->track->data_type == TRACK_AUDIO) { frame = mwindow->theme->get_image("aeffect_icon"); } else { frame = mwindow->theme->get_image("veffect_icon"); } } int cx, cy; get_abs_cursor(cx, cy); gui->drag_popup = new BC_DragWindow(gui, frame, cx, cy); break; } case PLUGIN_SHAREDPLUGIN: case PLUGIN_SHAREDMODULE: { VFrame *frame = mwindow->theme->get_image("clip_icon"); int cx, cy; get_abs_cursor(cx, cy); gui->drag_popup = new BC_DragWindow(gui, frame, cx, cy); break; } } result = 1; } } } return result; } int TrackCanvas::do_transitions(int cursor_x, int cursor_y, int button_press, int &new_cursor, int &update_cursor) { Transition *transition = 0; int result = 0; int64_t x, y, w, h; for( Track *track = mwindow->edl->tracks->first; track && !result; track = track->next ) { if( !track->show_transitions() ) continue; for( Edit *edit = track->edits->first; edit; edit = edit->next ) { if( edit->transition ) { edit_dimensions(edit, x, y, w, h); get_transition_coords(edit, x, y, w, h); if( MWindowGUI::visible(x, x + w, 0, get_w()) && MWindowGUI::visible(y, y + h, 0, get_h()) ) { if( cursor_x >= x && cursor_x < x + w && cursor_y >= y && cursor_y < y + h ) { transition = edit->transition; result = 1; break; } } } } } update_cursor = 1; if(transition) { if(!button_press) { new_cursor = UPRIGHT_ARROW_CURSOR; } else if(get_buttonpress() == RIGHT_BUTTON ) { gui->transition_menu->update(transition); gui->transition_menu->activate_menu(); } } return result; } int TrackCanvas::button_press_event() { int result = 0; int cursor_x, cursor_y; int new_cursor; cursor_x = get_cursor_x(); cursor_y = get_cursor_y(); mwindow->session->trim_edits = 0; if(is_event_win() && cursor_inside()) { // double position = mwindow->edl->get_cursor_position(cursor_x, pane->number); result = 1; if(!active) { activate(); } if( get_buttonpress() == LEFT_BUTTON ) { gui->stop_transport("TrackCanvas::button_press_event"); } int update_overlay = 0, update_cursor = 0, rerender = 0; if(get_buttonpress() == WHEEL_UP) { if(shift_down()) mwindow->expand_sample(); else if(ctrl_down()) mwindow->move_left(get_w()/ 10); else mwindow->move_up(get_h() / 10); } else if(get_buttonpress() == WHEEL_DOWN) { if(shift_down()) mwindow->zoom_in_sample(); else if(ctrl_down()) mwindow->move_right(get_w() / 10); else mwindow->move_down(get_h() / 10); } else if(get_buttonpress() == 6) { if(ctrl_down()) mwindow->move_left(get_w()); else if(alt_down()) mwindow->move_left(get_w() / 20); else mwindow->move_left(get_w() / 5); } else if(get_buttonpress() == 7) { if(ctrl_down()) mwindow->move_right(get_w()); else if(alt_down()) mwindow->move_right(get_w() / 20); else mwindow->move_right(get_w() / 5); } else { if( arrow_mode() ) do { // Test handles and resource boundaries and highlight a track 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, update_cursor, rerender) ) break; // Test edit boundaries if( do_edit_handles(cursor_x, cursor_y, 1, rerender, update_overlay, new_cursor, update_cursor) ) break; // Test plugin boundaries if( do_plugin_handles(cursor_x, cursor_y, 1, rerender, update_overlay, new_cursor, update_cursor) ) break; if( do_edits(cursor_x, cursor_y, 1, 0, update_overlay, rerender, new_cursor, update_cursor) ) break; if( do_plugins(cursor_x, cursor_y, 0, 1, update_cursor, rerender) ) break; if( test_resources(cursor_x, cursor_y) ) break; if( do_tracks(cursor_x, cursor_y, 1) ) break; result = 0; } while(0); else if( ibeam_mode() ) do { // Test handles only and select a region double position = mwindow->edl->get_cursor_position(cursor_x, pane->number); //printf("TrackCanvas::button_press_event %d\n", position); 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, update_cursor, rerender)) { update_overlay = 1; break; } // Test edit boundaries if( do_edit_handles(cursor_x, cursor_y, 1, rerender, update_overlay, new_cursor, update_cursor) ) break; // Test plugin boundaries if( do_plugin_handles(cursor_x, cursor_y, 1, rerender, update_overlay, new_cursor, update_cursor) ) break; if( do_edits(cursor_x, cursor_y, 1, 0, update_cursor, rerender, new_cursor, update_cursor) ) break; if( do_plugins(cursor_x, cursor_y, 0, 1, update_cursor, rerender) ) break; 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; } while(0); } if( rerender ) { gui->unlock_window(); mwindow->cwindow->update(1, 0, 0, 0, 1); gui->lock_window("TrackCanvas::button_press_event 2"); // Update faders mwindow->update_plugin_guis(); gui->update_patchbay(); } if( update_overlay ) { gui->draw_overlays(1); } if( update_cursor < 0 ) { // double_click edit gui->swindow->update_selection(); gui->draw_canvas(0, 0); } if( update_cursor ) { gui->update_timebar(0); gui->hide_cursor(0); gui->show_cursor(1); gui->zoombar->update(); gui->flash_canvas(1); } } return result; } int TrackCanvas::start_selection(double position) { int rerender = 0; position = mwindow->edl->align_to_frame(position, 1); // Extend a border if(shift_down()) { double midpoint = (mwindow->edl->local_session->get_selectionstart(1) + mwindow->edl->local_session->get_selectionend(1)) / 2; if(position < midpoint) { mwindow->edl->local_session->set_selectionstart(position); selection_midpoint = mwindow->edl->local_session->get_selectionend(1); // Que the CWindow rerender = 1; } else { mwindow->edl->local_session->set_selectionend(position); selection_midpoint = mwindow->edl->local_session->get_selectionstart(1); // Don't que the CWindow for the end } } else // Start a new selection { //printf("TrackCanvas::start_selection %f\n", position); mwindow->edl->local_session->set_selectionstart(position); mwindow->edl->local_session->set_selectionend(position); selection_midpoint = position; // Que the CWindow rerender = 1; } return rerender; } void TrackCanvas::end_edithandle_selection() { mwindow->modify_edithandles(); } void TrackCanvas::end_pluginhandle_selection() { mwindow->modify_pluginhandles(); } double TrackCanvas::time_visible() { return (double)get_w() * mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate; } void TrackCanvas::show_message(Auto *current, int show_curve_type, const char *fmt, ...) { char string[BCTEXTLEN]; char *cp = string, *ep = cp + sizeof(string)-1; if( show_curve_type ) { cp += snprintf(string, ep-cp, "%-8s ", FloatAuto::curve_name(((FloatAuto*)current)->curve_mode)); } char string2[BCTEXTLEN]; Units::totext(string2, current->autos->track->from_units(current->position), mwindow->edl->session->time_format, mwindow->edl->session->sample_rate, mwindow->edl->session->frame_rate, mwindow->edl->session->frames_per_foot); cp += snprintf(cp, ep-cp, "%s", string2); va_list ap; va_start(ap, fmt); vsnprintf(cp, ep-cp, fmt, ap); va_end(ap); gui->show_message(string); } // Patchbay* TrackCanvas::get_patchbay() // { // if(pane->patchbay) return pane->patchbay; // if(gui->total_panes() == 2 && // gui->pane[TOP_LEFT_PANE] && // gui->pane[TOP_RIGHT_PANE]) // return gui->pane[TOP_LEFT_PANE]->patchbay; // if(gui->total_panes() == 4) // { // if(pane->number == TOP_RIGHT_PANE) // return gui->pane[TOP_LEFT_PANE]->patchbay; // else // return gui->pane[BOTTOM_LEFT_PANE]->patchbay; // } // // return 0; // }