2015-08-10 03:41:08 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
TrackPanelResizeHandle.cpp
|
|
|
|
|
|
|
|
Paul Licameli split from TrackPanel.cpp
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
2021-05-09 15:16:56 +00:00
|
|
|
|
2015-08-10 03:41:08 +00:00
|
|
|
#include "TrackPanelResizeHandle.h"
|
2018-11-11 02:40:37 +00:00
|
|
|
|
2015-08-10 03:41:08 +00:00
|
|
|
#include <wx/cursor.h>
|
|
|
|
#include <wx/translation.h>
|
|
|
|
|
|
|
|
#include "HitTestResult.h"
|
2019-06-06 13:55:34 +00:00
|
|
|
#include "ProjectHistory.h"
|
2015-08-10 03:41:08 +00:00
|
|
|
#include "RefreshCode.h"
|
2019-05-06 23:00:10 +00:00
|
|
|
#include "Track.h"
|
2015-08-10 03:41:08 +00:00
|
|
|
#include "TrackPanelMouseEvent.h"
|
2019-06-18 15:29:38 +00:00
|
|
|
#include "tracks/ui/TrackView.h"
|
2015-08-10 03:41:08 +00:00
|
|
|
|
|
|
|
HitTestPreview TrackPanelResizeHandle::HitPreview(bool bLinked)
|
|
|
|
{
|
2017-01-11 19:00:31 +00:00
|
|
|
// TODO: more-than-two-channels-message
|
|
|
|
|
2015-08-10 03:41:08 +00:00
|
|
|
static wxCursor resizeCursor{ wxCURSOR_SIZENS };
|
|
|
|
|
|
|
|
/// When in the resize area we can adjust size or relative size.
|
|
|
|
// Check to see whether it is the first channel of a stereo track
|
|
|
|
if (bLinked) {
|
|
|
|
// If we are in the label we got here 'by mistake' and we're
|
|
|
|
// not actually in the resize area at all. (The resize area
|
|
|
|
// is shorter when it is between stereo tracks).
|
|
|
|
|
|
|
|
return {
|
2019-12-31 21:00:39 +00:00
|
|
|
XO(
|
|
|
|
"Click and drag to adjust relative size of stereo tracks, double-click to make heights equal"),
|
2015-08-10 03:41:08 +00:00
|
|
|
&resizeCursor
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return {
|
2019-12-08 21:24:20 +00:00
|
|
|
XO("Click and drag to resize the track."),
|
2015-08-10 03:41:08 +00:00
|
|
|
&resizeCursor
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackPanelResizeHandle::~TrackPanelResizeHandle()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-12-31 21:00:39 +00:00
|
|
|
UIHandle::Result TrackPanelResizeHandle::Click(
|
|
|
|
const TrackPanelMouseEvent &evt, AudacityProject *pProject )
|
2015-08-10 03:41:08 +00:00
|
|
|
{
|
2019-12-31 21:00:39 +00:00
|
|
|
using namespace RefreshCode;
|
|
|
|
if ( evt.event.LeftDClick() && mMode == IsResizingBetweenLinkedTracks ) {
|
|
|
|
auto &tracks = TrackList::Get( *pProject );
|
|
|
|
auto pTrack = tracks.Lock(mpTrack);
|
|
|
|
if (pTrack &&
|
|
|
|
!TrackView::Get(*pTrack).GetMinimized()) {
|
|
|
|
auto range = TrackList::Channels( pTrack.get() );
|
|
|
|
auto size = range.size();
|
|
|
|
auto height = range.sum( [](const Track *pTrack){
|
|
|
|
return TrackView::Get(*pTrack).GetHeight(); } );
|
|
|
|
int ii = 1;
|
|
|
|
int coord = 0;
|
|
|
|
for ( const auto channel : range ) {
|
|
|
|
int newCoord = ((double)ii++ /size) * height;
|
|
|
|
TrackView::Get(*channel).SetHeight( newCoord - coord );
|
|
|
|
coord = newCoord;
|
|
|
|
}
|
|
|
|
ProjectHistory::Get( *pProject ).ModifyState(false);
|
|
|
|
// Do not start a drag
|
|
|
|
return Cancelled | RefreshAll;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return RefreshNone;
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
}
|
2015-08-10 03:41:08 +00:00
|
|
|
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
TrackPanelResizeHandle::TrackPanelResizeHandle
|
2017-01-11 19:00:31 +00:00
|
|
|
( const std::shared_ptr<Track> &track, int y )
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
: mpTrack{ track }
|
|
|
|
, mMouseClickY( y )
|
|
|
|
{
|
2017-01-11 19:00:31 +00:00
|
|
|
// TODO: more-than-two-channels
|
2018-01-12 17:08:09 +00:00
|
|
|
|
|
|
|
//STM: Determine whether we should rescale one or two tracks
|
2017-01-11 19:00:31 +00:00
|
|
|
auto channels = TrackList::Channels(track.get());
|
|
|
|
auto last = *channels.rbegin();
|
2019-06-18 15:32:26 +00:00
|
|
|
auto &lastView = TrackView::Get( *last );
|
|
|
|
mInitialTrackHeight = lastView.GetHeight();
|
|
|
|
mInitialActualHeight = lastView.GetActualHeight();
|
|
|
|
mInitialMinimized = lastView.GetMinimized();
|
2017-01-11 19:00:31 +00:00
|
|
|
|
|
|
|
if (channels.size() > 1) {
|
|
|
|
auto first = *channels.begin();
|
2019-06-18 15:32:26 +00:00
|
|
|
auto &firstView = TrackView::Get( *first );
|
2017-01-11 19:00:31 +00:00
|
|
|
|
2019-06-18 15:32:26 +00:00
|
|
|
mInitialUpperTrackHeight = firstView.GetHeight();
|
|
|
|
mInitialUpperActualHeight = firstView.GetActualHeight();
|
2017-01-11 19:00:31 +00:00
|
|
|
|
|
|
|
if (track.get() == *channels.rbegin())
|
|
|
|
// capturedTrack is the lowest track
|
|
|
|
mMode = IsResizingBelowLinkedTracks;
|
|
|
|
else
|
|
|
|
// capturedTrack is not the lowest track
|
|
|
|
mMode = IsResizingBetweenLinkedTracks;
|
2018-01-12 17:08:09 +00:00
|
|
|
}
|
2017-01-11 19:00:31 +00:00
|
|
|
else
|
2018-01-12 17:08:09 +00:00
|
|
|
mMode = IsResizing;
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UIHandle::Result TrackPanelResizeHandle::Drag
|
|
|
|
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
auto &tracks = TrackList::Get( *pProject );
|
|
|
|
auto pTrack = tracks.Lock(mpTrack);
|
2017-06-24 21:04:07 +00:00
|
|
|
if ( !pTrack )
|
2015-08-10 03:41:08 +00:00
|
|
|
return RefreshCode::Cancelled;
|
|
|
|
|
2019-06-18 15:32:26 +00:00
|
|
|
auto &view = TrackView::Get( *pTrack );
|
|
|
|
|
2015-08-10 03:41:08 +00:00
|
|
|
const wxMouseEvent &event = evt.event;
|
|
|
|
|
|
|
|
int delta = (event.m_y - mMouseClickY);
|
|
|
|
|
|
|
|
// On first drag, jump out of minimized mode. Initial height
|
|
|
|
// will be height of minimized track.
|
|
|
|
//
|
|
|
|
// This used to be in HandleResizeClick(), but simply clicking
|
|
|
|
// on a resize border would switch the minimized state.
|
2019-06-18 15:29:38 +00:00
|
|
|
auto &data = TrackView::Get( *pTrack );
|
|
|
|
if (data.GetMinimized()) {
|
2017-01-11 19:00:31 +00:00
|
|
|
auto channels = TrackList::Channels( pTrack.get() );
|
|
|
|
for (auto channel : channels) {
|
2019-06-18 15:32:26 +00:00
|
|
|
auto &channelView = TrackView::Get( *channel );
|
|
|
|
channelView.SetHeight(channelView.GetHeight());
|
|
|
|
channelView.SetMinimized( false );
|
2017-01-11 19:00:31 +00:00
|
|
|
}
|
2015-08-10 03:41:08 +00:00
|
|
|
|
2017-01-11 19:00:31 +00:00
|
|
|
if (channels.size() > 1) {
|
2015-08-10 03:41:08 +00:00
|
|
|
// Initial values must be reset since they weren't based on the
|
|
|
|
// minimized heights.
|
2019-06-18 15:32:26 +00:00
|
|
|
auto &channelView = TrackView::Get( **channels.begin() );
|
|
|
|
mInitialUpperTrackHeight = channelView.GetHeight();
|
|
|
|
mInitialTrackHeight = channelView.GetHeight();
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Common pieces of code for MONO_WAVE_PAN and otherwise.
|
2017-12-08 11:26:09 +00:00
|
|
|
auto doResizeBelow = [&] (Track *prev, bool WXUNUSED(vStereo)) {
|
2017-01-11 19:00:31 +00:00
|
|
|
// TODO: more-than-two-channels
|
|
|
|
|
2019-06-18 15:32:26 +00:00
|
|
|
auto &prevView = TrackView::Get( *prev );
|
|
|
|
|
2015-08-10 03:41:08 +00:00
|
|
|
double proportion = static_cast < double >(mInitialTrackHeight)
|
|
|
|
/ (mInitialTrackHeight + mInitialUpperTrackHeight);
|
|
|
|
|
|
|
|
int newTrackHeight = static_cast < int >
|
|
|
|
(mInitialTrackHeight + delta * proportion);
|
|
|
|
|
|
|
|
int newUpperTrackHeight = static_cast < int >
|
|
|
|
(mInitialUpperTrackHeight + delta * (1.0 - proportion));
|
|
|
|
|
|
|
|
//make sure neither track is smaller than its minimum height
|
2019-06-18 15:32:26 +00:00
|
|
|
if (newTrackHeight < view.GetMinimizedHeight())
|
|
|
|
newTrackHeight = view.GetMinimizedHeight();
|
|
|
|
if (newUpperTrackHeight < prevView.GetMinimizedHeight())
|
|
|
|
newUpperTrackHeight = prevView.GetMinimizedHeight();
|
2015-08-10 03:41:08 +00:00
|
|
|
|
2019-06-18 15:32:26 +00:00
|
|
|
view.SetHeight(newTrackHeight);
|
|
|
|
prevView.SetHeight(newUpperTrackHeight);
|
2015-08-10 03:41:08 +00:00
|
|
|
};
|
|
|
|
|
2017-12-08 11:26:09 +00:00
|
|
|
auto doResizeBetween = [&] (Track *next, bool WXUNUSED(vStereo)) {
|
2017-01-11 19:00:31 +00:00
|
|
|
// TODO: more-than-two-channels
|
|
|
|
|
2019-06-18 15:32:26 +00:00
|
|
|
auto &nextView = TrackView::Get( *next );
|
2015-08-10 03:41:08 +00:00
|
|
|
int newUpperTrackHeight = mInitialUpperTrackHeight + delta;
|
|
|
|
int newTrackHeight = mInitialTrackHeight - delta;
|
|
|
|
|
|
|
|
// make sure neither track is smaller than its minimum height
|
2019-06-18 15:32:26 +00:00
|
|
|
if (newTrackHeight < nextView.GetMinimizedHeight()) {
|
|
|
|
newTrackHeight = nextView.GetMinimizedHeight();
|
2015-08-10 03:41:08 +00:00
|
|
|
newUpperTrackHeight =
|
2019-06-18 15:32:26 +00:00
|
|
|
mInitialUpperTrackHeight + mInitialTrackHeight - nextView.GetMinimizedHeight();
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
2019-06-18 15:32:26 +00:00
|
|
|
if (newUpperTrackHeight < view.GetMinimizedHeight()) {
|
|
|
|
newUpperTrackHeight = view.GetMinimizedHeight();
|
2015-08-10 03:41:08 +00:00
|
|
|
newTrackHeight =
|
2019-06-18 15:32:26 +00:00
|
|
|
mInitialUpperTrackHeight + mInitialTrackHeight - view.GetMinimizedHeight();
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
|
|
|
|
2019-06-18 15:32:26 +00:00
|
|
|
view.SetHeight(newUpperTrackHeight);
|
|
|
|
nextView.SetHeight(newTrackHeight);
|
2015-08-10 03:41:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
auto doResize = [&] {
|
|
|
|
int newTrackHeight = mInitialTrackHeight + delta;
|
2019-06-18 15:32:26 +00:00
|
|
|
if (newTrackHeight < view.GetMinimizedHeight())
|
|
|
|
newTrackHeight = view.GetMinimizedHeight();
|
|
|
|
view.SetHeight(newTrackHeight);
|
2015-08-10 03:41:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//STM: We may be dragging one or two (stereo) tracks.
|
|
|
|
// If two, resize proportionally if we are dragging the lower track, and
|
|
|
|
// adjust compensatively if we are dragging the upper track.
|
|
|
|
|
2018-01-12 17:08:09 +00:00
|
|
|
switch( mMode )
|
|
|
|
{
|
|
|
|
case IsResizingBelowLinkedTracks:
|
2015-08-10 03:41:08 +00:00
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
auto prev = * -- tracks.Find(pTrack.get());
|
2018-01-12 17:08:09 +00:00
|
|
|
doResizeBelow(prev, false);
|
|
|
|
break;
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
2018-01-12 17:08:09 +00:00
|
|
|
case IsResizingBetweenLinkedTracks:
|
2015-08-10 03:41:08 +00:00
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
auto next = * ++ tracks.Find(pTrack.get());
|
2018-01-12 17:08:09 +00:00
|
|
|
doResizeBetween(next, false);
|
|
|
|
break;
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
2018-01-12 17:08:09 +00:00
|
|
|
case IsResizing:
|
|
|
|
{
|
|
|
|
doResize();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// don't refresh in this case.
|
|
|
|
return RefreshCode::RefreshNone;
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return RefreshCode::RefreshAll;
|
|
|
|
}
|
|
|
|
|
|
|
|
HitTestPreview TrackPanelResizeHandle::Preview
|
2020-01-04 14:40:33 +00:00
|
|
|
(const TrackPanelMouseState &, AudacityProject *)
|
2015-08-10 03:41:08 +00:00
|
|
|
{
|
|
|
|
return HitPreview(mMode == IsResizingBetweenLinkedTracks);
|
|
|
|
}
|
|
|
|
|
|
|
|
UIHandle::Result TrackPanelResizeHandle::Release
|
|
|
|
(const TrackPanelMouseEvent &, AudacityProject *pProject,
|
|
|
|
wxWindow *)
|
|
|
|
{
|
|
|
|
/// This happens when the button is released from a drag.
|
|
|
|
/// Since we actually took care of resizing the track when
|
|
|
|
/// we got drag events, all we have to do here is clean up.
|
|
|
|
/// We also modify the undo state (the action doesn't become
|
|
|
|
/// undo-able, but it gets merged with the previous undo-able
|
|
|
|
/// event).
|
2019-06-06 13:55:34 +00:00
|
|
|
ProjectHistory::Get( *pProject ).ModifyState(false);
|
2015-08-10 03:41:08 +00:00
|
|
|
return RefreshCode::FixScrollbars;
|
|
|
|
}
|
|
|
|
|
|
|
|
UIHandle::Result TrackPanelResizeHandle::Cancel(AudacityProject *pProject)
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
auto &tracks = TrackList::Get( *pProject );
|
|
|
|
auto pTrack = tracks.Lock(mpTrack);
|
2017-06-24 21:04:07 +00:00
|
|
|
if ( !pTrack )
|
2015-08-10 03:41:08 +00:00
|
|
|
return RefreshCode::Cancelled;
|
|
|
|
|
|
|
|
|
|
|
|
switch (mMode) {
|
|
|
|
case IsResizing:
|
|
|
|
{
|
2019-06-18 15:32:26 +00:00
|
|
|
auto &view = TrackView::Get( *pTrack );
|
|
|
|
view.SetHeight(mInitialActualHeight);
|
|
|
|
view.SetMinimized( mInitialMinimized );
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IsResizingBetweenLinkedTracks:
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
Track *const next = * ++ tracks.Find(pTrack.get());
|
2019-06-18 15:32:26 +00:00
|
|
|
auto
|
|
|
|
&view = TrackView::Get( *pTrack ), &nextView = TrackView::Get( *next );
|
|
|
|
view.SetHeight(mInitialUpperActualHeight);
|
|
|
|
view.SetMinimized( mInitialMinimized );
|
|
|
|
nextView.SetHeight(mInitialActualHeight);
|
|
|
|
nextView.SetMinimized( mInitialMinimized );
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IsResizingBelowLinkedTracks:
|
|
|
|
{
|
2019-05-06 23:00:10 +00:00
|
|
|
Track *const prev = * -- tracks.Find(pTrack.get());
|
2019-06-18 15:32:26 +00:00
|
|
|
auto
|
|
|
|
&view = TrackView::Get( *pTrack ), &prevView = TrackView::Get( *prev );
|
|
|
|
view.SetHeight(mInitialActualHeight);
|
|
|
|
view.SetMinimized( mInitialMinimized );
|
|
|
|
prevView.SetHeight(mInitialUpperActualHeight);
|
|
|
|
prevView.SetMinimized(mInitialMinimized);
|
2015-08-10 03:41:08 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return RefreshCode::RefreshAll;
|
|
|
|
}
|