2009-06-15 18:45:05 -04:00
/*
2009-10-14 12:10:01 -04:00
Copyright ( C ) 2009 Paul Davis
2009-06-15 18:45:05 -04:00
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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2009-06-10 21:05:01 -04:00
# include "ardour/session.h"
2012-07-23 22:58:28 -04:00
2013-04-04 00:32:52 -04:00
# include "canvas/debug.h"
2017-08-27 00:32:02 -04:00
# include <gtkmm/menu.h>
# include <gtkmm/menuitem.h>
2009-06-10 21:05:01 -04:00
# include "time_axis_view.h"
# include "streamview.h"
# include "editor_summary.h"
# include "gui_thread.h"
# include "editor.h"
# include "region_view.h"
2009-06-14 15:01:22 -04:00
# include "rgb_macros.h"
2009-06-14 21:21:30 -04:00
# include "keyboard.h"
2010-11-10 16:56:32 -05:00
# include "editor_routes.h"
2010-11-16 09:53:16 -05:00
# include "editor_cursors.h"
# include "mouse_cursors.h"
2011-01-07 21:35:35 -05:00
# include "route_time_axis.h"
2015-01-02 09:44:54 -05:00
# include "ui_config.h"
2009-06-10 21:05:01 -04:00
2017-08-27 00:32:02 -04:00
# include "pbd/i18n.h"
2009-06-10 21:05:01 -04:00
using namespace std ;
using namespace ARDOUR ;
2009-12-04 17:51:32 -05:00
using Gtkmm2ext : : Keyboard ;
2009-06-10 21:05:01 -04:00
2009-06-11 10:49:20 -04:00
/** Construct an EditorSummary.
* @ param e Editor to represent .
*/
2009-06-10 21:05:01 -04:00
EditorSummary : : EditorSummary ( Editor * e )
2009-07-03 14:37:15 -04:00
: EditorComponent ( e ) ,
2009-11-25 08:44:57 -05:00
_start ( 0 ) ,
_end ( 1 ) ,
2009-06-15 18:45:05 -04:00
_x_scale ( 1 ) ,
2010-06-15 13:37:44 -04:00
_track_height ( 16 ) ,
2009-06-16 20:29:56 -04:00
_last_playhead ( - 1 ) ,
2009-06-14 21:21:30 -04:00
_move_dragging ( false ) ,
2010-06-17 20:05:44 -04:00
_view_rectangle_x ( 0 , 0 ) ,
_view_rectangle_y ( 0 , 0 ) ,
2017-07-18 11:37:35 -04:00
_zoom_trim_dragging ( false ) ,
2014-06-17 07:44:36 -04:00
_old_follow_playhead ( false ) ,
2014-09-10 16:33:23 -04:00
_image ( 0 ) ,
2014-06-17 07:44:36 -04:00
_background_dirty ( true )
2009-06-10 21:05:01 -04:00
{
2017-03-20 11:32:05 -04:00
CairoWidget : : use_nsglview ( ) ;
2012-07-23 22:58:28 -04:00
add_events ( Gdk : : POINTER_MOTION_MASK | Gdk : : KEY_PRESS_MASK | Gdk : : KEY_RELEASE_MASK | Gdk : : ENTER_NOTIFY_MASK | Gdk : : LEAVE_NOTIFY_MASK ) ;
set_flags ( get_flags ( ) | Gtk : : CAN_FOCUS ) ;
2014-10-29 11:07:13 -04:00
2015-01-02 09:44:54 -05:00
UIConfiguration : : instance ( ) . ParameterChanged . connect ( sigc : : mem_fun ( * this , & EditorSummary : : parameter_changed ) ) ;
2009-06-10 21:05:01 -04:00
}
2014-10-11 07:36:52 -04:00
EditorSummary : : ~ EditorSummary ( )
{
cairo_surface_destroy ( _image ) ;
}
2014-10-29 11:07:13 -04:00
void
EditorSummary : : parameter_changed ( string p )
{
if ( p = = " color-regions-using-track-color " ) {
set_background_dirty ( ) ;
}
}
2014-06-17 07:44:36 -04:00
/** Handle a size allocation.
* @ param alloc GTK allocation .
*/
void
EditorSummary : : on_size_allocate ( Gtk : : Allocation & alloc )
{
2014-10-29 00:41:58 -04:00
CairoWidget : : on_size_allocate ( alloc ) ;
2014-10-29 11:07:13 -04:00
set_background_dirty ( ) ;
2014-06-17 07:44:36 -04:00
}
2009-07-03 14:37:15 -04:00
/** Connect to a session.
2009-06-11 10:49:20 -04:00
* @ param s Session .
*/
2009-06-10 21:05:01 -04:00
void
2009-12-17 13:24:23 -05:00
EditorSummary : : set_session ( Session * s )
2009-06-10 21:05:01 -04:00
{
2010-07-19 17:47:07 -04:00
SessionHandlePtr : : set_session ( s ) ;
2009-06-10 21:05:01 -04:00
2009-12-17 18:36:52 -05:00
set_dirty ( ) ;
2009-06-10 21:05:01 -04:00
2010-03-30 19:40:28 -04:00
/* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
* ( which attaches to StreamView : : RegionViewAdded ) , and cut regions by the RegionPropertyChanged
* emitted when a cut region is added to the ` cutlist ' playlist .
*/
2009-12-17 13:24:23 -05:00
if ( _session ) {
2014-06-17 07:44:36 -04:00
Region : : RegionPropertyChanged . connect ( region_property_connection , invalidator ( * this ) , boost : : bind ( & EditorSummary : : set_background_dirty , this ) , gui_context ( ) ) ;
2016-06-05 16:02:35 -04:00
PresentationInfo : : Change . connect ( route_ctrl_id_connection , invalidator ( * this ) , boost : : bind ( & EditorSummary : : set_background_dirty , this ) , gui_context ( ) ) ;
2014-06-17 07:44:36 -04:00
_editor - > playhead_cursor - > PositionChanged . connect ( position_connection , invalidator ( * this ) , boost : : bind ( & EditorSummary : : playhead_position_changed , this , _1 ) , gui_context ( ) ) ;
2014-06-18 11:52:21 -04:00
_session - > StartTimeChanged . connect ( _session_connections , invalidator ( * this ) , boost : : bind ( & EditorSummary : : set_background_dirty , this ) , gui_context ( ) ) ;
_session - > EndTimeChanged . connect ( _session_connections , invalidator ( * this ) , boost : : bind ( & EditorSummary : : set_background_dirty , this ) , gui_context ( ) ) ;
2014-06-17 07:44:36 -04:00
_editor - > selection - > RegionsChanged . connect ( sigc : : mem_fun ( * this , & EditorSummary : : set_background_dirty ) ) ;
2017-07-19 16:41:19 -04:00
}
2017-08-26 22:56:42 -04:00
_leftmost = max_framepos ;
_rightmost = 0 ;
2009-06-10 21:05:01 -04:00
}
void
2014-06-17 07:44:36 -04:00
EditorSummary : : render_background_image ( )
2009-06-10 21:05:01 -04:00
{
2014-10-11 07:36:52 -04:00
cairo_surface_destroy ( _image ) ; // passing NULL is safe
_image = cairo_image_surface_create ( CAIRO_FORMAT_RGB24 , get_width ( ) , get_height ( ) ) ;
2014-06-18 11:52:21 -04:00
2014-06-17 07:44:36 -04:00
cairo_t * cr = cairo_create ( _image ) ;
2017-07-01 12:42:24 -04:00
/* background (really just the dividing lines between tracks */
2009-10-14 12:10:01 -04:00
2009-06-10 21:05:01 -04:00
cairo_set_source_rgb ( cr , 0 , 0 , 0 ) ;
2011-11-18 10:35:58 -05:00
cairo_rectangle ( cr , 0 , 0 , get_width ( ) , get_height ( ) ) ;
2009-06-10 21:05:01 -04:00
cairo_fill ( cr ) ;
2009-11-25 08:44:57 -05:00
/* compute start and end points for the summary */
2011-06-01 13:00:29 -04:00
2017-08-26 22:56:42 -04:00
std : : pair < framepos_t , framepos_t > ext = _editor - > session_gui_extents ( ) ;
double theoretical_start = ext . first ;
double theoretical_end = ext . second ;
2017-07-19 14:58:56 -04:00
/* the summary should encompass the full extent of everywhere we've visited since the session was opened */
if ( _leftmost < theoretical_start )
theoretical_start = _leftmost ;
if ( _rightmost > theoretical_end )
theoretical_end = _rightmost ;
/* range-check */
2009-11-25 08:44:57 -05:00
_start = theoretical_start > 0 ? theoretical_start : 0 ;
2017-08-26 22:56:42 -04:00
_end = theoretical_end < max_framepos ? theoretical_end : max_framepos ;
2017-07-19 14:58:56 -04:00
/* calculate x scale */
if ( _end ! = _start ) {
_x_scale = static_cast < double > ( get_width ( ) ) / ( _end - _start ) ;
} else {
_x_scale = 1 ;
}
2009-11-25 08:44:57 -05:00
2010-06-15 13:37:44 -04:00
/* compute track height */
2010-06-15 19:43:51 -04:00
int N = 0 ;
for ( TrackViewList : : const_iterator i = _editor - > track_views . begin ( ) ; i ! = _editor - > track_views . end ( ) ; + + i ) {
if ( ! ( * i ) - > hidden ( ) ) {
+ + N ;
}
}
2011-06-01 13:00:29 -04:00
2010-06-15 13:37:44 -04:00
if ( N = = 0 ) {
_track_height = 16 ;
} else {
2011-11-18 10:35:58 -05:00
_track_height = ( double ) get_height ( ) / N ;
2010-06-15 13:37:44 -04:00
}
2009-06-10 21:05:01 -04:00
2010-06-14 19:59:21 -04:00
/* render tracks and regions */
2009-06-13 13:52:51 -04:00
double y = 0 ;
2009-12-13 14:09:52 -05:00
for ( TrackViewList : : const_iterator i = _editor - > track_views . begin ( ) ; i ! = _editor - > track_views . end ( ) ; + + i ) {
2010-06-14 19:59:21 -04:00
2010-06-15 19:43:51 -04:00
if ( ( * i ) - > hidden ( ) ) {
continue ;
}
2012-03-20 13:08:07 -04:00
/* paint a non-bg colored strip to represent the track itself */
2017-07-19 16:36:21 -04:00
if ( _track_height > 4 ) {
cairo_set_source_rgb ( cr , 0.2 , 0.2 , 0.2 ) ;
cairo_set_line_width ( cr , _track_height - 1 ) ;
cairo_move_to ( cr , 0 , y + _track_height / 2 ) ;
cairo_line_to ( cr , get_width ( ) , y + _track_height / 2 ) ;
cairo_stroke ( cr ) ;
}
2009-06-10 21:05:01 -04:00
StreamView * s = ( * i ) - > view ( ) ;
2009-06-13 13:52:51 -04:00
if ( s ) {
2015-01-07 01:42:49 -05:00
cairo_set_line_width ( cr , _track_height * 0.8 ) ;
2009-06-14 09:31:00 -04:00
2009-12-11 18:29:48 -05:00
s - > foreach_regionview ( sigc : : bind (
sigc : : mem_fun ( * this , & EditorSummary : : render_region ) ,
2009-06-10 21:05:01 -04:00
cr ,
2010-06-15 13:37:44 -04:00
y + _track_height / 2
2009-06-10 21:05:01 -04:00
) ) ;
}
2011-06-01 13:00:29 -04:00
2010-06-15 13:37:44 -04:00
y + = _track_height ;
2009-06-10 21:05:01 -04:00
}
2014-06-18 11:52:21 -04:00
/* start and end markers */
cairo_set_line_width ( cr , 1 ) ;
cairo_set_source_rgb ( cr , 1 , 1 , 0 ) ;
const double p = ( _session - > current_start_frame ( ) - _start ) * _x_scale ;
cairo_move_to ( cr , p , 0 ) ;
cairo_line_to ( cr , p , get_height ( ) ) ;
double const q = ( _session - > current_end_frame ( ) - _start ) * _x_scale ;
cairo_move_to ( cr , q , 0 ) ;
cairo_line_to ( cr , q , get_height ( ) ) ;
cairo_stroke ( cr ) ;
2014-06-17 07:44:36 -04:00
cairo_destroy ( cr ) ;
}
/** Render the required regions to a cairo context.
* @ param cr Context .
*/
void
2017-03-20 12:11:56 -04:00
EditorSummary : : render ( Cairo : : RefPtr < Cairo : : Context > const & ctx , cairo_rectangle_t * )
2014-06-17 07:44:36 -04:00
{
2017-03-20 12:11:56 -04:00
cairo_t * cr = ctx - > cobj ( ) ;
2014-06-17 07:44:36 -04:00
if ( _session = = 0 ) {
return ;
}
2017-07-19 14:58:56 -04:00
/* maintain the leftmost and rightmost locations that we've ever reached */
framecnt_t const leftmost = _editor - > leftmost_sample ( ) ;
if ( leftmost < _leftmost ) {
_leftmost = leftmost ;
_background_dirty = true ;
}
framecnt_t const rightmost = leftmost + _editor - > current_page_samples ( ) ;
if ( rightmost > _rightmost ) {
_rightmost = rightmost ;
_background_dirty = true ;
}
//draw the background (regions, markers, etc ) if they've changed
2014-06-17 07:44:36 -04:00
if ( ! _image | | _background_dirty ) {
render_background_image ( ) ;
_background_dirty = false ;
}
cairo_push_group ( cr ) ;
2015-10-05 10:17:49 -04:00
2014-06-18 11:52:21 -04:00
/* Fill with the background image */
2014-06-17 07:44:36 -04:00
cairo_rectangle ( cr , 0 , 0 , get_width ( ) , get_height ( ) ) ;
cairo_set_source_surface ( cr , _image , 0 , 0 ) ;
2014-06-18 11:52:21 -04:00
cairo_fill ( cr ) ;
2012-07-23 22:58:28 -04:00
/* Render the view rectangle. If there is an editor visual pending, don't update
2017-07-14 14:00:41 -04:00
* the view rectangle now - - - wait until the expose event that we ' ll get after
* the visual change . This prevents a flicker .
*/
2012-07-23 22:58:28 -04:00
if ( _editor - > pending_visual_change . idle_handler_id < 0 ) {
get_editor ( & _view_rectangle_x , & _view_rectangle_y ) ;
}
2014-06-17 07:44:36 -04:00
int32_t width = _view_rectangle_x . second - _view_rectangle_x . first ;
2017-07-18 11:37:35 -04:00
std : : min ( 8 , width ) ;
cairo_rectangle ( cr , _view_rectangle_x . first , 0 , width , get_height ( ) ) ;
2017-07-14 14:00:41 -04:00
cairo_set_source_rgba ( cr , 1 , 1 , 1 , 0.15 ) ;
cairo_fill ( cr ) ;
/* horiz zoom */
cairo_rectangle ( cr , _view_rectangle_x . first , 0 , width , get_height ( ) ) ;
2012-07-23 22:58:28 -04:00
cairo_set_line_width ( cr , 1 ) ;
2017-07-14 14:00:41 -04:00
cairo_set_source_rgba ( cr , 1 , 1 , 1 , 0.9 ) ;
2012-07-23 22:58:28 -04:00
cairo_stroke ( cr ) ;
/* Playhead */
cairo_set_line_width ( cr , 1 ) ;
/* XXX: colour should be set from configuration file */
cairo_set_source_rgba ( cr , 1 , 0 , 0 , 1 ) ;
2013-04-04 00:32:52 -04:00
const double ph = playhead_frame_to_position ( _editor - > playhead_cursor - > current_frame ( ) ) ;
2012-07-23 22:58:28 -04:00
cairo_move_to ( cr , ph , 0 ) ;
cairo_line_to ( cr , ph , get_height ( ) ) ;
cairo_stroke ( cr ) ;
2014-06-17 07:44:36 -04:00
cairo_pop_group_to_source ( cr ) ;
cairo_paint ( cr ) ;
2012-07-23 22:58:28 -04:00
_last_playhead = ph ;
2009-06-10 21:05:01 -04:00
}
2009-06-11 10:49:20 -04:00
/** Render a region for the summary.
* @ param r Region view .
* @ param cr Cairo context .
* @ param y y coordinate to render at .
*/
2009-06-10 21:05:01 -04:00
void
2009-11-25 08:44:57 -05:00
EditorSummary : : render_region ( RegionView * r , cairo_t * cr , double y ) const
2009-06-10 21:05:01 -04:00
{
2009-06-14 15:01:22 -04:00
uint32_t const c = r - > get_fill_color ( ) ;
cairo_set_source_rgb ( cr , UINT_RGBA_R ( c ) / 255.0 , UINT_RGBA_G ( c ) / 255.0 , UINT_RGBA_B ( c ) / 255.0 ) ;
2009-10-14 12:10:01 -04:00
2009-11-25 08:44:57 -05:00
if ( r - > region ( ) - > position ( ) > _start ) {
cairo_move_to ( cr , ( r - > region ( ) - > position ( ) - _start ) * _x_scale , y ) ;
} else {
cairo_move_to ( cr , 0 , y ) ;
}
if ( ( r - > region ( ) - > position ( ) + r - > region ( ) - > length ( ) ) > _start ) {
cairo_line_to ( cr , ( ( r - > region ( ) - > position ( ) - _start + r - > region ( ) - > length ( ) ) ) * _x_scale , y ) ;
} else {
cairo_line_to ( cr , 0 , y ) ;
}
2009-06-10 21:05:01 -04:00
cairo_stroke ( cr ) ;
}
2014-06-17 07:44:36 -04:00
void
EditorSummary : : set_background_dirty ( )
{
2016-07-09 11:50:38 -04:00
if ( ! _background_dirty ) {
_background_dirty = true ;
set_dirty ( ) ;
}
2014-06-17 07:44:36 -04:00
}
2009-06-16 20:29:56 -04:00
/** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
2009-06-10 21:05:01 -04:00
void
2009-06-16 20:29:56 -04:00
EditorSummary : : set_overlays_dirty ( )
2009-06-10 21:05:01 -04:00
{
2012-01-09 13:03:49 -05:00
ENSURE_GUI_THREAD ( * this , & EditorSummary : : set_overlays_dirty ) ;
2009-06-10 21:05:01 -04:00
queue_draw ( ) ;
}
2012-01-09 13:03:49 -05:00
/** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
void
2017-08-26 22:56:42 -04:00
EditorSummary : : set_overlays_dirty_rect ( int x , int y , int w , int h )
2012-01-09 13:03:49 -05:00
{
2017-08-26 22:56:42 -04:00
ENSURE_GUI_THREAD ( * this , & EditorSummary : : set_overlays_dirty_rect ) ;
2012-01-09 13:03:49 -05:00
queue_draw_area ( x , y , w , h ) ;
}
2009-06-11 10:49:20 -04:00
/** Handle a size request.
* @ param req GTK requisition
*/
2009-06-10 21:05:01 -04:00
void
EditorSummary : : on_size_request ( Gtk : : Requisition * req )
{
2017-07-18 11:37:35 -04:00
/* The left/right buttons will determine our height */
req - > width = - 1 ;
req - > height = - 1 ;
2009-06-10 21:05:01 -04:00
}
2009-06-14 09:30:21 -04:00
void
EditorSummary : : centre_on_click ( GdkEventButton * ev )
{
2009-06-15 21:03:24 -04:00
pair < double , double > xr ;
2017-07-14 14:00:41 -04:00
get_editor ( & xr ) ;
2009-06-15 21:03:24 -04:00
double const w = xr . second - xr . first ;
2011-04-11 14:48:44 -04:00
double ex = ev - > x - w / 2 ;
if ( ex < 0 ) {
ex = 0 ;
2011-11-18 10:35:58 -05:00
} else if ( ( ex + w ) > get_width ( ) ) {
ex = get_width ( ) - w ;
2009-06-14 09:30:21 -04:00
}
2009-06-15 21:03:24 -04:00
2017-07-14 14:00:41 -04:00
set_editor ( ex ) ;
2009-06-14 09:30:21 -04:00
}
2015-10-04 14:51:05 -04:00
bool
2012-07-23 22:58:28 -04:00
EditorSummary : : on_enter_notify_event ( GdkEventCrossing * )
{
grab_focus ( ) ;
Keyboard : : magic_widget_grab_focus ( ) ;
return false ;
}
2015-10-04 14:51:05 -04:00
bool
2012-07-23 22:58:28 -04:00
EditorSummary : : on_leave_notify_event ( GdkEventCrossing * )
{
2012-07-24 12:35:59 -04:00
/* there are no inferior/child windows, so any leave event means that
we ' re gone .
*/
2012-07-23 22:58:28 -04:00
Keyboard : : magic_widget_drop_focus ( ) ;
return false ;
}
bool
EditorSummary : : on_key_press_event ( GdkEventKey * key )
{
gint x , y ;
2012-08-02 06:30:48 -04:00
GtkAccelKey set_playhead_accel ;
if ( gtk_accel_map_lookup_entry ( " <Actions>/Editor/set-playhead " , & set_playhead_accel ) ) {
if ( key - > keyval = = set_playhead_accel . accel_key & & ( int ) key - > state = = set_playhead_accel . accel_mods ) {
if ( _session ) {
get_pointer ( x , y ) ;
2012-12-12 17:10:03 -05:00
_session - > request_locate ( _start + ( framepos_t ) x / _x_scale , _session - > transport_rolling ( ) ) ;
2012-08-02 06:30:48 -04:00
return true ;
}
2012-07-23 22:58:28 -04:00
}
}
return false ;
}
bool
EditorSummary : : on_key_release_event ( GdkEventKey * key )
{
2012-08-02 06:30:48 -04:00
GtkAccelKey set_playhead_accel ;
if ( gtk_accel_map_lookup_entry ( " <Actions>/Editor/set-playhead " , & set_playhead_accel ) ) {
if ( key - > keyval = = set_playhead_accel . accel_key & & ( int ) key - > state = = set_playhead_accel . accel_mods ) {
return true ;
}
2012-07-23 22:58:28 -04:00
}
return false ;
}
2017-08-27 00:32:02 -04:00
# include "gtkmm2ext/utils.h"
2009-06-11 10:49:20 -04:00
/** Handle a button press.
* @ param ev GTK event .
*/
2009-06-10 21:05:01 -04:00
bool
EditorSummary : : on_button_press_event ( GdkEventButton * ev )
{
2012-06-09 09:19:08 -04:00
_old_follow_playhead = _editor - > follow_playhead ( ) ;
2015-10-05 10:17:49 -04:00
2017-08-27 00:32:02 -04:00
if ( ev - > button = = 3 ) { //right-click: show the reset menu action
using namespace Gtk : : Menu_Helpers ;
Gtk : : Menu * m = manage ( new Gtk : : Menu ) ;
MenuList & items = m - > items ( ) ;
items . push_back ( MenuElem ( _ ( " Reset Summary to Extents " ) ,
sigc : : mem_fun ( * this , & EditorSummary : : reset_to_extents ) ) ) ;
m - > popup ( ev - > button , ev - > time ) ;
return true ;
}
2017-07-14 14:00:41 -04:00
if ( ev - > button ! = 1 ) {
return true ;
}
2009-08-20 15:12:24 -04:00
2017-07-14 14:00:41 -04:00
pair < double , double > xr ;
get_editor ( & xr ) ;
2009-10-14 12:10:01 -04:00
2017-07-14 14:00:41 -04:00
_start_editor_x = xr ;
_start_mouse_x = ev - > x ;
2017-07-18 11:37:35 -04:00
_start_mouse_y = ev - > y ;
2017-07-14 14:00:41 -04:00
_start_position = get_position ( ev - > x , ev - > y ) ;
2010-01-15 19:44:56 -05:00
2017-07-14 14:00:41 -04:00
if ( _start_position ! = INSIDE & & _start_position ! = TO_LEFT_OR_RIGHT ) {
2009-08-20 15:12:24 -04:00
2017-07-18 11:37:35 -04:00
/* start a zoom_trim drag */
2009-06-14 21:21:30 -04:00
2017-07-18 11:37:35 -04:00
_zoom_trim_position = get_position ( ev - > x , ev - > y ) ;
_zoom_trim_dragging = true ;
2017-07-14 14:00:41 -04:00
_editor - > _dragging_playhead = true ;
_editor - > set_follow_playhead ( false ) ;
2015-10-05 10:17:49 -04:00
2017-07-14 14:00:41 -04:00
if ( suspending_editor_updates ( ) ) {
get_editor ( & _pending_editor_x , & _pending_editor_y ) ;
_pending_editor_changed = false ;
}
2009-10-14 12:10:01 -04:00
2017-07-14 14:00:41 -04:00
} else if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : SecondaryModifier ) ) {
2009-08-24 15:02:26 -04:00
2017-07-14 14:00:41 -04:00
/* secondary-modifier-click: locate playhead */
if ( _session ) {
_session - > request_locate ( ev - > x / _x_scale + _start ) ;
}
2009-08-24 15:02:26 -04:00
2017-07-14 14:00:41 -04:00
} else if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : TertiaryModifier ) ) {
2009-10-14 12:10:01 -04:00
2017-07-14 14:00:41 -04:00
centre_on_click ( ev ) ;
2009-10-14 12:10:01 -04:00
2017-07-14 14:00:41 -04:00
} else {
2009-10-14 12:10:01 -04:00
2017-07-20 15:14:35 -04:00
/* start a move+zoom drag */
get_editor ( & _pending_editor_x , & _pending_editor_y ) ;
_pending_editor_changed = false ;
_editor - > _dragging_playhead = true ;
_editor - > set_follow_playhead ( false ) ;
_move_dragging = true ;
_last_mx = ev - > x ;
_last_my = ev - > y ;
_last_dx = 0 ;
_last_dy = 0 ;
_last_y_delta = 0 ;
get_window ( ) - > set_cursor ( * _editor - > _cursors - > expand_left_right ) ;
2009-06-10 21:05:01 -04:00
}
2009-10-14 12:10:01 -04:00
2009-06-10 21:05:01 -04:00
return true ;
}
2009-06-13 13:52:51 -04:00
2011-11-01 08:19:11 -04:00
/** @return true if we are currently suspending updates to the editor's viewport,
* which we do if configured to do so , and if in a drag of some kind .
*/
bool
EditorSummary : : suspending_editor_updates ( ) const
{
2017-07-20 15:14:35 -04:00
return ( ! UIConfiguration : : instance ( ) . get_update_editor_during_summary_drag ( ) & & ( _zoom_trim_dragging | | _move_dragging ) ) ;
2011-11-01 08:19:11 -04:00
}
2010-06-17 20:05:44 -04:00
/** Fill in x and y with the editor's current viewable area in summary coordinates */
2009-06-13 13:52:51 -04:00
void
2009-06-15 21:03:24 -04:00
EditorSummary : : get_editor ( pair < double , double > * x , pair < double , double > * y ) const
2009-06-13 13:52:51 -04:00
{
2010-06-17 20:05:44 -04:00
assert ( x ) ;
2011-11-01 08:19:11 -04:00
if ( suspending_editor_updates ( ) ) {
2009-06-13 13:52:51 -04:00
2011-11-01 08:19:11 -04:00
/* We are dragging, and configured not to update the editor window during drags,
2017-07-14 14:00:41 -04:00
* so just return where the editor will be when the drag finishes .
2011-11-01 08:19:11 -04:00
*/
2015-10-05 10:17:49 -04:00
2011-11-01 08:19:11 -04:00
* x = _pending_editor_x ;
2017-07-14 14:00:41 -04:00
if ( y ) {
* y = _pending_editor_y ;
}
return ;
}
2011-11-01 08:19:11 -04:00
2017-07-14 14:00:41 -04:00
/* Otherwise query the editor for its actual position */
2011-11-01 08:19:11 -04:00
2017-07-14 14:00:41 -04:00
x - > first = ( _editor - > leftmost_sample ( ) - _start ) * _x_scale ;
x - > second = x - > first + _editor - > current_page_samples ( ) * _x_scale ;
2015-10-05 10:17:49 -04:00
2017-07-14 14:00:41 -04:00
if ( y ) {
2011-11-01 08:19:11 -04:00
y - > first = editor_y_to_summary ( _editor - > vertical_adjustment . get_value ( ) ) ;
2014-10-29 00:43:38 -04:00
y - > second = editor_y_to_summary ( _editor - > vertical_adjustment . get_value ( ) + _editor - > visible_canvas_height ( ) - _editor - > get_trackview_group ( ) - > canvas_origin ( ) . y ) ;
2011-11-01 08:19:11 -04:00
}
2009-06-13 13:52:51 -04:00
}
2010-06-17 20:05:44 -04:00
/** Get an expression of the position of a point with respect to the view rectangle */
EditorSummary : : Position
EditorSummary : : get_position ( double x , double y ) const
{
/* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
in pixels */
2010-09-01 21:44:27 -04:00
int x_edge_size = ( _view_rectangle_x . second - _view_rectangle_x . first ) / 4 ;
x_edge_size = min ( x_edge_size , 8 ) ;
x_edge_size = max ( x_edge_size , 1 ) ;
bool const near_left = ( std : : abs ( x - _view_rectangle_x . first ) < x_edge_size ) ;
bool const near_right = ( std : : abs ( x - _view_rectangle_x . second ) < x_edge_size ) ;
2010-06-17 20:05:44 -04:00
bool const within_x = _view_rectangle_x . first < x & & x < _view_rectangle_x . second ;
2017-07-14 14:00:41 -04:00
if ( near_left ) {
2010-06-17 20:05:44 -04:00
return LEFT ;
2017-07-14 14:00:41 -04:00
} else if ( near_right ) {
2010-06-17 20:05:44 -04:00
return RIGHT ;
} else if ( within_x ) {
2017-07-14 14:00:41 -04:00
return INSIDE ;
2010-06-17 20:05:44 -04:00
} else {
2017-07-14 14:00:41 -04:00
return TO_LEFT_OR_RIGHT ;
2010-06-17 20:05:44 -04:00
}
}
2017-08-27 00:32:02 -04:00
void
EditorSummary : : reset_to_extents ( )
{
//reset as if the user never went anywhere outside the extents
_leftmost = max_framepos ;
_rightmost = 0 ;
_editor - > temporal_zoom_extents ( ) ;
set_background_dirty ( ) ;
}
2010-06-17 20:05:44 -04:00
void
EditorSummary : : set_cursor ( Position p )
{
switch ( p ) {
case LEFT :
2010-11-16 09:53:16 -05:00
get_window ( ) - > set_cursor ( * _editor - > _cursors - > resize_left ) ;
2010-06-17 20:05:44 -04:00
break ;
case RIGHT :
2010-11-16 09:53:16 -05:00
get_window ( ) - > set_cursor ( * _editor - > _cursors - > resize_right ) ;
2010-06-17 20:05:44 -04:00
break ;
case INSIDE :
2010-11-16 09:53:16 -05:00
get_window ( ) - > set_cursor ( * _editor - > _cursors - > move ) ;
2010-06-17 20:05:44 -04:00
break ;
2010-11-14 17:03:57 -05:00
case TO_LEFT_OR_RIGHT :
2017-07-19 09:20:25 -04:00
get_window ( ) - > set_cursor ( * _editor - > _cursors - > move ) ;
2010-11-14 17:03:57 -05:00
break ;
2010-06-17 20:05:44 -04:00
default :
2017-07-14 14:00:41 -04:00
assert ( 0 ) ;
2010-06-17 20:05:44 -04:00
get_window ( ) - > set_cursor ( ) ;
break ;
}
}
2017-07-19 09:12:25 -04:00
void
2017-07-19 15:13:18 -04:00
EditorSummary : : summary_zoom_step ( int steps /* positive steps to zoom "out" , negative steps to zoom "in" */ )
2017-07-19 09:12:25 -04:00
{
pair < double , double > xn ;
get_editor ( & xn ) ;
2017-07-19 15:13:18 -04:00
xn . first - = steps ;
xn . second + = steps ;
2017-07-19 09:12:25 -04:00
2017-07-19 16:36:21 -04:00
//for now, disallow really close zooming-in from the scroomer. ( currently it causes the start-offset to 'walk' because of integer limitations. to fix this, probably need to maintain float throught the get/set_editor() path )
if ( steps < 0 ) {
if ( ( xn . second - xn . first ) < 2 )
return ;
}
2017-07-19 09:12:25 -04:00
set_overlays_dirty ( ) ;
set_editor_x ( xn ) ;
}
2009-06-13 13:52:51 -04:00
bool
EditorSummary : : on_motion_notify_event ( GdkEventMotion * ev )
{
2009-06-14 21:21:30 -04:00
if ( _move_dragging ) {
2017-07-20 15:14:35 -04:00
//To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
//we use screen coordinates for this, not canvas-based grab_x
double mx = ev - > x ;
double dx = mx - _last_mx ;
double my = ev - > y ;
double dy = my - _last_my ;
//do zooming in windowed "steps" so it feels more reversible ?
const int stepsize = 2 ;
int y_delta = _start_mouse_y - my ;
y_delta = y_delta / stepsize ;
//do the zoom?
const float zscale = 3 ;
if ( ( dx = = 0 ) & & ( _last_dx = = 0 ) & & ( y_delta ! = _last_y_delta ) ) {
summary_zoom_step ( dy * zscale ) ;
//after the zoom we must re-calculate x-pos grabs
pair < double , double > xr ;
get_editor ( & xr ) ;
_start_editor_x = xr ;
_start_mouse_x = ev - > x ;
_last_y_delta = y_delta ;
2009-11-25 08:44:57 -05:00
}
2017-07-20 15:14:35 -04:00
//always track horizontal movement, if any
if ( dx ! = 0 ) {
2009-11-25 08:44:57 -05:00
2017-07-20 15:14:35 -04:00
double x = _start_editor_x . first ;
x + = ev - > x - _start_mouse_x ;
2017-08-26 22:56:42 -04:00
2017-07-20 15:14:35 -04:00
if ( x < 0 ) {
x = 0 ;
}
2017-08-26 22:56:42 -04:00
//zoom-behavior-tweaks
//protect the right edge from expanding beyond the end
pair < double , double > xr ;
get_editor ( & xr ) ;
double w = xr . second - xr . first ;
if ( x + w < get_width ( ) ) {
set_editor ( x ) ;
}
2017-07-20 15:14:35 -04:00
}
2009-06-14 21:21:30 -04:00
2017-07-20 15:14:35 -04:00
_last_my = my ;
_last_mx = mx ;
_last_dx = dx ;
_last_dy = dy ;
2017-07-18 11:37:35 -04:00
} else if ( _zoom_trim_dragging ) {
2017-07-20 15:14:35 -04:00
pair < double , double > xr = _start_editor_x ;
2009-06-15 21:03:24 -04:00
double const dx = ev - > x - _start_mouse_x ;
2009-06-14 21:21:30 -04:00
2017-07-18 11:37:35 -04:00
if ( _zoom_trim_position = = LEFT ) {
2010-06-17 22:07:13 -04:00
xr . first + = dx ;
2017-07-18 11:37:35 -04:00
} else if ( _zoom_trim_position = = RIGHT ) {
2017-08-26 22:56:42 -04:00
//zoom-behavior-tweaks
//protect the right edge from expanding beyond the edge
if ( ( xr . second + dx ) < get_width ( ) ) {
xr . second + = dx ;
}
2015-03-15 12:52:28 -04:00
} else {
2017-07-14 14:00:41 -04:00
assert ( 0 ) ;
2015-03-15 12:52:28 -04:00
xr . first = - 1 ; /* do not change */
2009-06-14 21:21:30 -04:00
}
2009-06-15 21:03:24 -04:00
2010-06-17 20:05:44 -04:00
set_overlays_dirty ( ) ;
2017-07-18 11:37:35 -04:00
set_cursor ( _zoom_trim_position ) ;
2017-07-14 14:00:41 -04:00
set_editor ( xr ) ;
2010-06-17 20:05:44 -04:00
} else {
2017-07-19 09:20:25 -04:00
set_cursor ( get_position ( ev - > x , ev - > y ) ) ;
2009-06-14 21:21:30 -04:00
}
2009-10-14 12:10:01 -04:00
2009-06-13 13:52:51 -04:00
return true ;
}
bool
2009-09-09 12:38:45 -04:00
EditorSummary : : on_button_release_event ( GdkEventButton * )
2009-06-13 13:52:51 -04:00
{
2011-11-01 08:19:11 -04:00
bool const was_suspended = suspending_editor_updates ( ) ;
2015-10-05 10:17:49 -04:00
2009-06-14 21:21:30 -04:00
_move_dragging = false ;
2017-07-18 11:37:35 -04:00
_zoom_trim_dragging = false ;
2009-06-13 15:39:21 -04:00
_editor - > _dragging_playhead = false ;
2011-02-08 06:46:11 -05:00
_editor - > set_follow_playhead ( _old_follow_playhead , false ) ;
2011-06-01 13:00:29 -04:00
2011-11-01 08:19:11 -04:00
if ( was_suspended & & _pending_editor_changed ) {
2017-07-14 14:00:41 -04:00
set_editor ( _pending_editor_x ) ;
2011-11-01 08:19:11 -04:00
}
2015-10-05 10:17:49 -04:00
2009-06-13 13:52:51 -04:00
return true ;
}
2009-06-15 18:45:05 -04:00
bool
EditorSummary : : on_scroll_event ( GdkEventScroll * ev )
{
/* mouse wheel */
pair < double , double > xr ;
2017-07-14 14:00:41 -04:00
get_editor ( & xr ) ;
2011-04-11 14:48:44 -04:00
double x = xr . first ;
2009-06-15 21:03:24 -04:00
2013-06-21 15:44:40 -04:00
switch ( ev - > direction ) {
2017-07-18 11:37:35 -04:00
case GDK_SCROLL_UP : {
2017-07-19 15:13:18 -04:00
summary_zoom_step ( - 4 ) ;
2017-07-18 11:37:35 -04:00
return true ;
} break ;
case GDK_SCROLL_DOWN : {
2017-07-19 09:12:25 -04:00
2017-07-19 15:13:18 -04:00
summary_zoom_step ( 4 ) ;
2017-07-18 11:37:35 -04:00
return true ;
} break ;
2013-06-21 15:44:40 -04:00
case GDK_SCROLL_LEFT :
2017-07-14 14:00:41 -04:00
if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : ScrollZoomHorizontalModifier ) ) {
_editor - > temporal_zoom_step ( false ) ;
} else if ( Keyboard : : modifier_state_contains ( ev - > state , Keyboard : : SecondaryModifier ) ) {
2013-06-21 15:44:40 -04:00
x - = 64 ;
} else if ( Keyboard : : modifier_state_contains ( ev - > state , Keyboard : : TertiaryModifier ) ) {
x - = 1 ;
} else {
2016-01-06 20:33:45 -05:00
_editor - > scroll_left_half_page ( ) ;
return true ;
2013-06-21 15:44:40 -04:00
}
break ;
case GDK_SCROLL_RIGHT :
2017-07-14 14:00:41 -04:00
if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : ScrollZoomHorizontalModifier ) ) {
_editor - > temporal_zoom_step ( true ) ;
} else if ( Keyboard : : modifier_state_contains ( ev - > state , Keyboard : : SecondaryModifier ) ) {
2013-06-21 15:44:40 -04:00
x + = 64 ;
} else if ( Keyboard : : modifier_state_contains ( ev - > state , Keyboard : : TertiaryModifier ) ) {
x + = 1 ;
} else {
2016-01-06 20:33:45 -05:00
_editor - > scroll_right_half_page ( ) ;
return true ;
2013-06-21 15:44:40 -04:00
}
break ;
default :
break ;
2009-06-15 18:45:05 -04:00
}
2009-10-14 12:10:01 -04:00
2017-07-14 14:00:41 -04:00
set_editor ( x ) ;
2009-06-15 18:45:05 -04:00
return true ;
}
2009-06-15 21:03:24 -04:00
2011-04-11 14:48:44 -04:00
/** Set the editor to display a x range with the left at a given position
* and a y range with the top at a given position .
2010-06-15 13:37:44 -04:00
* x and y parameters are specified in summary coordinates .
2011-04-11 14:48:44 -04:00
* Zoom is not changed in either direction .
2010-06-15 13:37:44 -04:00
*/
2009-06-15 21:03:24 -04:00
void
2017-07-14 14:00:41 -04:00
EditorSummary : : set_editor ( double const x )
2009-06-15 21:03:24 -04:00
{
2013-04-04 18:45:27 -04:00
if ( _editor - > pending_visual_change . idle_handler_id > = 0 & & _editor - > pending_visual_change . being_handled = = true ) {
2009-06-15 21:03:24 -04:00
/* As a side-effect, the Editor's visual change idle handler processes
pending GTK events . Hence this motion notify handler can be called
in the middle of a visual change idle handler , and if this happens ,
the queue_visual_change calls below modify the variables that the
2010-06-15 13:37:44 -04:00
idle handler is working with . This causes problems . Hence this
check . It ensures that we won ' t modify the pending visual change
2009-06-15 21:03:24 -04:00
while a visual change idle handler is in progress . It ' s not perfect ,
as it also means that we won ' t change these variables if an idle handler
is merely pending but not executing . But c ' est la vie .
*/
2015-10-05 10:17:49 -04:00
2010-06-15 13:37:44 -04:00
return ;
}
2011-06-01 13:00:29 -04:00
2010-06-17 20:05:44 -04:00
set_editor_x ( x ) ;
}
2011-04-11 14:48:44 -04:00
/** Set the editor to display a given x range and a y range with the top at a given position.
* The editor ' s x zoom is adjusted if necessary , but the y zoom is not changed .
* x and y parameters are specified in summary coordinates .
*/
void
2017-07-14 14:00:41 -04:00
EditorSummary : : set_editor ( pair < double , double > const x )
2010-06-17 20:05:44 -04:00
{
if ( _editor - > pending_visual_change . idle_handler_id > = 0 ) {
/* see comment in other set_editor () */
return ;
}
2010-12-20 10:35:15 -05:00
2015-03-15 12:52:28 -04:00
if ( x . first > = 0 ) {
set_editor_x ( x ) ;
}
2010-06-17 20:05:44 -04:00
}
2011-04-11 14:48:44 -04:00
/** Set the left of the x range visible in the editor.
* Caller should have checked that Editor : : pending_visual_change . idle_handler_id is < 0
* @ param x new x left position in summary coordinates .
*/
void
2011-09-14 09:57:20 -04:00
EditorSummary : : set_editor_x ( double x )
2011-04-11 14:48:44 -04:00
{
2011-09-14 09:57:20 -04:00
if ( x < 0 ) {
x = 0 ;
}
2011-11-01 08:19:11 -04:00
if ( suspending_editor_updates ( ) ) {
double const w = _pending_editor_x . second - _pending_editor_x . first ;
_pending_editor_x . first = x ;
_pending_editor_x . second = x + w ;
_pending_editor_changed = true ;
set_dirty ( ) ;
} else {
_editor - > reset_x_origin ( x / _x_scale + _start ) ;
}
2011-04-11 14:48:44 -04:00
}
2010-06-17 20:05:44 -04:00
/** Set the x range visible in the editor.
* Caller should have checked that Editor : : pending_visual_change . idle_handler_id is < 0
* @ param x new x range in summary coordinates .
*/
void
2011-09-14 09:57:20 -04:00
EditorSummary : : set_editor_x ( pair < double , double > x )
2010-06-17 20:05:44 -04:00
{
2011-09-14 09:57:20 -04:00
if ( x . first < 0 ) {
x . first = 0 ;
}
if ( x . second < 0 ) {
2011-09-14 10:00:03 -04:00
x . second = x . first + 1 ;
2011-09-14 09:57:20 -04:00
}
2011-06-01 13:00:29 -04:00
2011-11-01 08:19:11 -04:00
if ( suspending_editor_updates ( ) ) {
_pending_editor_x = x ;
_pending_editor_changed = true ;
set_dirty ( ) ;
} else {
_editor - > reset_x_origin ( x . first / _x_scale + _start ) ;
2015-10-05 10:17:49 -04:00
2011-11-01 08:19:11 -04:00
double const nx = (
( ( x . second - x . first ) / _x_scale ) /
2013-04-12 11:21:12 -04:00
_editor - > sample_to_pixel ( _editor - > current_page_samples ( ) )
2011-11-01 08:19:11 -04:00
) ;
2015-10-05 10:17:49 -04:00
2011-11-01 08:19:11 -04:00
if ( nx ! = _editor - > get_current_zoom ( ) ) {
_editor - > reset_zoom ( nx ) ;
}
2011-06-01 13:00:29 -04:00
}
2010-06-17 20:05:44 -04:00
}
2009-06-16 20:29:56 -04:00
void
2010-09-17 14:20:37 -04:00
EditorSummary : : playhead_position_changed ( framepos_t p )
2009-06-16 20:29:56 -04:00
{
2012-01-09 13:03:49 -05:00
int const o = int ( _last_playhead ) ;
int const n = int ( playhead_frame_to_position ( p ) ) ;
if ( _session & & o ! = n ) {
2012-11-14 00:01:25 -05:00
int a = max ( 2 , min ( o , n ) ) ;
2012-01-09 13:03:49 -05:00
int b = max ( o , n ) ;
2017-08-26 22:56:42 -04:00
set_overlays_dirty_rect ( a - 2 , 0 , b + 2 , get_height ( ) ) ;
2009-06-16 20:29:56 -04:00
}
}
2010-06-15 19:36:40 -04:00
double
EditorSummary : : editor_y_to_summary ( double y ) const
{
double sy = 0 ;
2010-06-17 20:05:44 -04:00
for ( TrackViewList : : const_iterator i = _editor - > track_views . begin ( ) ; i ! = _editor - > track_views . end ( ) ; + + i ) {
2011-06-01 13:00:29 -04:00
2010-06-15 19:43:51 -04:00
if ( ( * i ) - > hidden ( ) ) {
continue ;
}
2010-06-15 19:36:40 -04:00
double const h = ( * i ) - > effective_height ( ) ;
if ( y < h ) {
/* in this track */
return sy + y * _track_height / h ;
}
sy + = _track_height ;
y - = h ;
}
return sy ;
}
2011-01-07 21:35:35 -05:00
void
EditorSummary : : routes_added ( list < RouteTimeAxisView * > const & r )
{
for ( list < RouteTimeAxisView * > : : const_iterator i = r . begin ( ) ; i ! = r . end ( ) ; + + i ) {
2016-06-05 17:16:20 -04:00
/* Connect to the relevant signal for the route so that we know when its colour has changed */
( * i ) - > route ( ) - > presentation_info ( ) . PropertyChanged . connect ( * this , invalidator ( * this ) , boost : : bind ( & EditorSummary : : route_gui_changed , this , _1 ) , gui_context ( ) ) ;
2012-01-31 16:56:39 -05:00
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( ( * i ) - > route ( ) ) ;
if ( tr ) {
2014-06-17 07:44:36 -04:00
tr - > PlaylistChanged . connect ( * this , invalidator ( * this ) , boost : : bind ( & EditorSummary : : set_background_dirty , this ) , gui_context ( ) ) ;
2012-01-31 16:56:39 -05:00
}
2011-01-07 21:35:35 -05:00
}
2014-10-29 11:07:13 -04:00
set_background_dirty ( ) ;
2011-01-07 21:35:35 -05:00
}
void
2016-06-05 17:16:20 -04:00
EditorSummary : : route_gui_changed ( PBD : : PropertyChange const & what_changed )
2011-01-07 21:35:35 -05:00
{
2016-06-05 17:16:20 -04:00
if ( what_changed . contains ( Properties : : color ) ) {
2014-10-29 11:07:13 -04:00
set_background_dirty ( ) ;
2011-01-07 21:35:35 -05:00
}
}
2012-01-09 13:03:49 -05:00
double
EditorSummary : : playhead_frame_to_position ( framepos_t t ) const
{
return ( t - _start ) * _x_scale ;
}
2012-07-23 22:58:28 -04:00
framepos_t
EditorSummary : : position_to_playhead_frame_to_position ( double pos ) const
{
return _start + ( pos * _x_scale ) ;
}