1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-14 17:14:07 +01:00

TrackPanel no longer implements label keystrokes, drags, text selection...

... also implemented ESC key for those drags

... temporarily loses the special CTRL click handling
This commit is contained in:
Paul Licameli
2015-08-17 17:49:00 -04:00
committed by Paul Licameli
parent bbfa574790
commit efdb9889b1
15 changed files with 923 additions and 326 deletions

View File

@@ -0,0 +1,181 @@
/**********************************************************************
Audacity: A Digital Audio Editor
LabelDefaultClickHandle.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "../../../Audacity.h"
#include "LabelDefaultClickHandle.h"
#include "../../../HitTestResult.h"
#include "../../../LabelTrack.h"
#include "../../../Project.h"
#include "../../../RefreshCode.h"
#include "../../../TrackPanelMouseEvent.h"
#include "../../../ViewInfo.h"
LabelDefaultClickHandle::LabelDefaultClickHandle()
{
}
LabelDefaultClickHandle &LabelDefaultClickHandle::Instance()
{
static LabelDefaultClickHandle instance;
return instance;
}
LabelDefaultClickHandle::~LabelDefaultClickHandle()
{
}
struct LabelDefaultClickHandle::LabelState {
std::vector< std::pair< LabelTrack*, LabelTrack::Flags > > mPairs;
};
void LabelDefaultClickHandle::SaveState( AudacityProject *pProject )
{
mLabelState = std::make_unique<LabelState>();
auto &pairs = mLabelState->mPairs;
TrackList *const tracks = pProject->GetTracks();
TrackListIterator iter(tracks);
Track *n = iter.First();
while (n) {
if (n->GetKind() == Track::Label) {
LabelTrack *const lt = static_cast<LabelTrack*>(n);
pairs.push_back( std::make_pair( lt, lt->SaveFlags() ) );
}
n = iter.Next();
}
}
void LabelDefaultClickHandle::UpdateState( AudacityProject *pProject )
{
if ( mLabelState ) {
auto trackList = pProject->GetTracks();
auto &pairs = mLabelState->mPairs;
auto it = pairs.begin();
while ( it != pairs.end() ) {
if ( trackList->Contains( it->first ) )
++it;
else
it = pairs.erase( it );
}
}
}
void LabelDefaultClickHandle::RestoreState( AudacityProject *pProject )
{
if ( mLabelState ) {
for ( const auto &pair : mLabelState->mPairs )
pair.first->RestoreFlags( pair.second );
mLabelState.reset();
}
}
void LabelDefaultClickHandle::DoClick
(const wxMouseEvent &event, AudacityProject *pProject, TrackPanelCell *pCell)
{
LabelTrack *pLT = static_cast<LabelTrack*>(pCell);
if (event.LeftDown())
{
SaveState( pProject );
TrackList *const tracks = pProject->GetTracks();
TrackListIterator iter(tracks);
Track *n = iter.First();
while (n) {
if (n->GetKind() == Track::Label && pCell != n) {
LabelTrack *const lt = static_cast<LabelTrack*>(n);
lt->ResetFlags();
lt->Unselect();
}
n = iter.Next();
}
}
}
UIHandle::Result LabelDefaultClickHandle::Click
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
// Redraw to show the change of text box selection status
UIHandle::Result result = RefreshAll;
DoClick(evt.event, pProject, evt.pCell);
if (mpForward)
result |= mpForward->Click(evt, pProject);
else
// No drag or release follows
result |= Cancelled;
return result;
}
UIHandle::Result LabelDefaultClickHandle::Drag
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
if (mpForward)
return mpForward->Drag(evt, pProject);
else
return RefreshCode::RefreshNone;
}
HitTestPreview LabelDefaultClickHandle::Preview
(const TrackPanelMouseEvent &evt, const AudacityProject *pProject)
{
if (mpForward)
return mpForward->Preview(evt, pProject);
else
return {};
}
UIHandle::Result LabelDefaultClickHandle::Release
(const TrackPanelMouseEvent &evt, AudacityProject *pProject,
wxWindow *pParent)
{
mLabelState.reset();
if (mpForward)
return mpForward->Release(evt, pProject, pParent);
else
return RefreshCode::RefreshNone;
}
UIHandle::Result LabelDefaultClickHandle::Cancel(AudacityProject *pProject)
{
UIHandle::Result result = RefreshCode::RefreshNone;
if (mpForward)
result |= mpForward->Cancel(pProject);
RestoreState( pProject );
return result;
}
void LabelDefaultClickHandle::DrawExtras
(DrawingPass pass,
wxDC * dc, const wxRegion &updateRegion, const wxRect &panelRect)
{
UIHandle::DrawExtras(pass, dc, updateRegion, panelRect);
if (mpForward)
mpForward->DrawExtras(pass, dc, updateRegion, panelRect);
}
bool LabelDefaultClickHandle::StopsOnKeystroke()
{
return
(mpForward && mpForward->StopsOnKeystroke()) ||
UIHandle::StopsOnKeystroke();
}
void LabelDefaultClickHandle::OnProjectChange(AudacityProject *pProject)
{
UpdateState( pProject );
if (mpForward)
return mpForward->OnProjectChange(pProject);
UIHandle::OnProjectChange(pProject);
}

View File

@@ -0,0 +1,70 @@
/**********************************************************************
Audacity: A Digital Audio Editor
LabelDefaultClickHandle.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_LABEL_DEFAULT_CLICK_HANDLE__
#define __AUDACITY_LABEL_DEFAULT_CLICK_HANDLE__
#include "../../../UIHandle.h"
#include "../../../MemoryX.h"
class wxMouseEvent;
struct HitTestResult;
class LabelTrack;
// Adds some behavior to click, then calls through to other mouse handling.
class LabelDefaultClickHandle final : public UIHandle
{
LabelDefaultClickHandle();
LabelDefaultClickHandle(const LabelDefaultClickHandle&) = delete;
LabelDefaultClickHandle &operator=(const LabelDefaultClickHandle&) = delete;
public:
static LabelDefaultClickHandle& Instance();
virtual ~LabelDefaultClickHandle();
void DoClick
(const wxMouseEvent &event, AudacityProject *pProject, TrackPanelCell *pCell);
Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
HitTestPreview Preview
(const TrackPanelMouseEvent &event, const AudacityProject *pProject)
override;
Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent) override;
Result Cancel(AudacityProject *pProject) override;
void DrawExtras
(DrawingPass pass,
wxDC * dc, const wxRegion &updateRegion, const wxRect &panelRect)
override;
bool StopsOnKeystroke() override;
void OnProjectChange(AudacityProject *pProject) override;
UIHandle *mpForward {};
private:
struct LabelState;
std::unique_ptr< LabelState > mLabelState;
void SaveState( AudacityProject *pProject );
void UpdateState( AudacityProject *pProject );
void RestoreState( AudacityProject *pProject );
};
#endif

View File

@@ -0,0 +1,164 @@
/**********************************************************************
Audacity: A Digital Audio Editor
LabelGlyphHandle.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "LabelGlyphHandle.h"
#include "../../../HitTestResult.h"
#include "../../../LabelTrack.h"
#include "../../../Project.h"
#include "../../../RefreshCode.h"
#include "../../../TrackPanelMouseEvent.h"
#include "../../../UndoManager.h"
#include "../../../ViewInfo.h"
#include "../../../MemoryX.h"
#include <wx/cursor.h>
#include <wx/translation.h>
LabelGlyphHandle::LabelGlyphHandle()
{
}
LabelGlyphHandle &LabelGlyphHandle::Instance()
{
static LabelGlyphHandle instance;
return instance;
}
HitTestPreview LabelGlyphHandle::HitPreview
(bool hitCenter, unsigned refreshResult)
{
static wxCursor arrowCursor{ wxCURSOR_ARROW };
return {
(hitCenter
? _("Drag one or more label boundaries.")
: _("Drag label boundary.")),
&arrowCursor,
// Unusually, can have a non-zero third member of HitTestPreview, so that
// mouse-over highlights it.
refreshResult
};
}
HitTestResult LabelGlyphHandle::HitTest
(const wxMouseEvent &event, LabelTrack *pLT)
{
using namespace RefreshCode;
unsigned refreshResult = RefreshNone;
// Note: this has side effects on pLT!
int edge = pLT->OverGlyph(event.m_x, event.m_y);
//KLUDGE: We refresh the whole Label track when the icon hovered over
//changes colouration. Inefficient.
edge += pLT->mbHitCenter ? 4 : 0;
if (edge != pLT->mOldEdge)
{
pLT->mOldEdge = edge;
refreshResult |= RefreshCell;
}
// IF edge!=0 THEN we've set the cursor and we're done.
// signal this by setting the tip.
if (edge != 0)
{
return {
HitPreview(pLT->mbHitCenter, refreshResult),
&Instance()
};
}
else {
// An empty result, except maybe, unusually, the refresh
return {
{ wxString{}, nullptr, refreshResult },
nullptr
};
}
}
LabelGlyphHandle::~LabelGlyphHandle()
{
}
UIHandle::Result LabelGlyphHandle::Click
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
TrackPanelCell *const pCell = evt.pCell;
const wxMouseEvent &event = evt.event;
const wxRect &rect = evt.rect;
mpLT = static_cast<LabelTrack*>(pCell);
mRect = rect;
ViewInfo &viewInfo = pProject->GetViewInfo();
mpLT->HandleGlyphClick(event, rect, viewInfo, &viewInfo.selectedRegion);
if (! mpLT->IsAdjustingLabel() )
{
// The positive hit test should have ensured otherwise
//wxASSERT(false);
return RefreshCode::Cancelled;
}
// redraw the track.
return RefreshCode::RefreshCell;
// handle shift+ctrl down
/*if (event.ShiftDown()) { // && event.ControlDown()) {
lTrack->SetHighlightedByKey(true);
Refresh(false);
return;
}*/
return RefreshCode::RefreshNone;
}
UIHandle::Result LabelGlyphHandle::Drag
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
const wxMouseEvent &event = evt.event;
ViewInfo &viewInfo = pProject->GetViewInfo();
mpLT->HandleGlyphDragRelease(event, mRect, viewInfo, &viewInfo.selectedRegion);
// Refresh all so that the change of selection is redrawn in all tracks
return RefreshCode::RefreshAll | RefreshCode::DrawOverlays;
}
HitTestPreview LabelGlyphHandle::Preview
(const TrackPanelMouseEvent &evt, const AudacityProject *pProject)
{
return HitPreview(mpLT->mbHitCenter, 0);
}
UIHandle::Result LabelGlyphHandle::Release
(const TrackPanelMouseEvent &evt, AudacityProject *pProject,
wxWindow *pParent)
{
mpLT->mOldEdge = 0;
const wxMouseEvent &event = evt.event;
ViewInfo &viewInfo = pProject->GetViewInfo();
if (mpLT->HandleGlyphDragRelease(event, mRect, viewInfo, &viewInfo.selectedRegion)) {
pProject->PushState(_("Modified Label"),
_("Label Edit"),
UndoPush::CONSOLIDATE);
}
// Refresh all so that the change of selection is redrawn in all tracks
return RefreshCode::RefreshAll | RefreshCode::DrawOverlays;
}
UIHandle::Result LabelGlyphHandle::Cancel(AudacityProject *pProject)
{
mpLT->mOldEdge = 0;
pProject->RollbackState();
return RefreshCode::RefreshAll;
}

View File

@@ -0,0 +1,58 @@
/**********************************************************************
Audacity: A Digital Audio Editor
LabelGlyphHandle.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_LABEL_GLYPH_HANDLE__
#define __AUDACITY_LABEL_GLYPH_HANDLE__
#include "../../../UIHandle.h"
#include <wx/gdicmn.h>
class wxMouseEvent;
struct HitTestResult;
class LabelTrack;
class LabelGlyphHandle final : public UIHandle
{
LabelGlyphHandle();
LabelGlyphHandle(const LabelGlyphHandle&) = delete;
LabelGlyphHandle &operator=(const LabelGlyphHandle&) = delete;
static LabelGlyphHandle& Instance();
static HitTestPreview HitPreview(bool hitCenter, unsigned refreshResult);
public:
static HitTestResult HitTest
(const wxMouseEvent &event, LabelTrack *pLT);
virtual ~LabelGlyphHandle();
Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
HitTestPreview Preview
(const TrackPanelMouseEvent &event, const AudacityProject *pProject)
override;
Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent) override;
Result Cancel(AudacityProject *pProject) override;
bool StopsOnKeystroke() override { return true; }
private:
LabelTrack *mpLT {};
wxRect mRect {};
};
#endif

View File

@@ -0,0 +1,184 @@
/**********************************************************************
Audacity: A Digital Audio Editor
LabelTextHandle.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "../../../Audacity.h"
#include "LabelTextHandle.h"
#include "../../../HitTestResult.h"
#include "../../../LabelTrack.h"
#include "../../../Project.h"
#include "../../../RefreshCode.h"
#include "../../../TrackPanelMouseEvent.h"
#include "../../../ViewInfo.h"
LabelTextHandle::LabelTextHandle()
{
}
LabelTextHandle &LabelTextHandle::Instance()
{
static LabelTextHandle instance;
return instance;
}
HitTestResult LabelTextHandle::HitTest(const wxMouseEvent &event, LabelTrack *pLT)
{
// If Control is down, let the select handle be hit instead
if (!event.ControlDown() &&
pLT->OverATextBox(event.m_x, event.m_y) >= 0)
// There was no cursor change or status message for mousing over a label text box
return { {}, &Instance() };
return {};
}
LabelTextHandle::~LabelTextHandle()
{
}
UIHandle::Result LabelTextHandle::Click
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
auto &selectionState = pProject->GetSelectionState();
TrackList *const tracks = pProject->GetTracks();
mChanger =
std::make_unique< SelectionStateChanger >( selectionState, *tracks );
TrackPanelCell *const pCell = evt.pCell;
const wxMouseEvent &event = evt.event;
ViewInfo &viewInfo = pProject->GetViewInfo();
mpLT = static_cast<LabelTrack*>(pCell);
mSelectedRegion = viewInfo.selectedRegion;
mpLT->HandleTextClick( event, evt.rect, viewInfo, &viewInfo.selectedRegion );
wxASSERT(mpLT->IsSelected());
{
// IF the user clicked a label, THEN select all other tracks by Label
TrackListIterator iter(tracks);
Track *t = iter.First();
//do nothing if at least one other track is selected
bool done = false;
while (!done && t) {
if (t->GetSelected() && t != mpLT)
done = true;
t = iter.Next();
}
if (!done) {
//otherwise, select all tracks
t = iter.First();
while (t)
{
selectionState.SelectTrack
( *pProject->GetTracks(), *t, true, true,
pProject->GetMixerBoard() );
t = iter.Next();
}
}
// Do this after, for its effect on TrackPanel's memory of last selected
// track (which affects shift-click actions)
selectionState.SelectTrack
( *pProject->GetTracks(), *mpLT, true, true,
pProject->GetMixerBoard() );
}
// PRL: bug1659 -- make selection change undo correctly
const bool unsafe = pProject->IsAudioActive();
if (!unsafe)
pProject->ModifyState(false);
return RefreshCode::RefreshCell | RefreshCode::UpdateSelection;
}
UIHandle::Result LabelTextHandle::Drag
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
Result result = RefreshNone;
const wxMouseEvent &event = evt.event;
if(mpLT)
mpLT->HandleTextDragRelease(event);
// locate the initial mouse position
if (event.LeftIsDown()) {
if (mLabelTrackStartXPos == -1) {
mLabelTrackStartXPos = event.m_x;
mLabelTrackStartYPos = event.m_y;
if (mpLT &&
(mpLT->getSelectedIndex() != -1) &&
mpLT->OverTextBox(
mpLT->GetLabel(mpLT->getSelectedIndex()),
mLabelTrackStartXPos,
mLabelTrackStartYPos))
mLabelTrackStartYPos = -1;
}
// if initial mouse position in the text box
// then only drag text
if (mLabelTrackStartYPos == -1)
result |= RefreshCell;
}
return result;
}
HitTestPreview LabelTextHandle::Preview
(const TrackPanelMouseEvent &evt, const AudacityProject *pProject)
{
return {};
}
UIHandle::Result LabelTextHandle::Release
(const TrackPanelMouseEvent &evt, AudacityProject *pProject,
wxWindow *pParent)
{
// Only selected a part of a text string and changed track selectedness.
// No undoable effects.
if (mChanger) {
mChanger->Commit();
mChanger.release();
}
const wxMouseEvent &event = evt.event;
if (mpLT)
mpLT->HandleTextDragRelease(event);
// handle mouse left button up
if (event.LeftUp())
mLabelTrackStartXPos = -1;
return RefreshCode::RefreshNone;
}
UIHandle::Result LabelTextHandle::Cancel( AudacityProject *pProject )
{
// Restore the selection states of tracks
// Note that we are also relying on LabelDefaultClickHandle::Cancel
// to restore the selection state of the labels in the tracks.
mChanger.release();
ViewInfo &viewInfo = pProject->GetViewInfo();
viewInfo.selectedRegion = mSelectedRegion;
return RefreshCode::RefreshAll;
}
void LabelTextHandle::OnProjectChange(AudacityProject *pProject)
{
if (! pProject->GetTracks()->Contains(mpLT)) {
mpLT = nullptr;
mRect = {};
}
UIHandle::OnProjectChange(pProject);
}

View File

@@ -0,0 +1,63 @@
/**********************************************************************
Audacity: A Digital Audio Editor
LabelTextHandle.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_LABEL_TEXT_HANDLE__
#define __AUDACITY_LABEL_TEXT_HANDLE__
#include "../../../UIHandle.h"
#include "../../../MemoryX.h"
#include "../../../SelectedRegion.h"
#include <wx/gdicmn.h>
class wxMouseEvent;
struct HitTestResult;
class LabelTrack;
class SelectionStateChanger;
class LabelTextHandle final : public UIHandle
{
LabelTextHandle();
LabelTextHandle(const LabelTextHandle&) = delete;
LabelTextHandle &operator=(const LabelTextHandle&) = delete;
static LabelTextHandle& Instance();
public:
static HitTestResult HitTest(const wxMouseEvent &event, LabelTrack *pLT);
virtual ~LabelTextHandle();
Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject) override;
HitTestPreview Preview
(const TrackPanelMouseEvent &event, const AudacityProject *pProject)
override;
Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent) override;
Result Cancel(AudacityProject *pProject) override;
void OnProjectChange(AudacityProject *pProject) override;
private:
LabelTrack *mpLT {};
wxRect mRect {};
int mLabelTrackStartXPos { -1 };
int mLabelTrackStartYPos { -1 };
SelectedRegion mSelectedRegion{};
std::unique_ptr<SelectionStateChanger> mChanger;
};
#endif

View File

@@ -10,15 +10,51 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../LabelTrack.h"
#include "LabelTrackControls.h"
#include "LabelDefaultClickHandle.h"
#include "LabelTrackVRulerControls.h"
#include "LabelGlyphHandle.h"
#include "LabelTextHandle.h"
#include "../../../HitTestResult.h"
#include "../../../TrackPanelMouseEvent.h"
HitTestResult LabelTrack::HitTest
(const TrackPanelMouseEvent &event,
(const TrackPanelMouseEvent &evt,
const AudacityProject *pProject)
{
return Track::HitTest(event, pProject);
// PRL: Maybe I did too much work to preserve old behavior, but anyway,
// this unusually combines parts of two or more hit test results.
HitTestResult result;
const wxMouseEvent &event = evt.event;
// Try label movement handles first
result = LabelGlyphHandle::HitTest(event, this);
// Hit test may request refresh even if a miss
auto refreshResult = result.preview.refreshCode;
if ( !result.handle ) {
// Missed glyph, try text box
// This hit test does not define its own messages or cursor
HitTestResult defaultResult = Track::HitTest(evt, pProject);
result = LabelTextHandle::HitTest(event, this);
if (result.handle)
// Use any cursor or status message change from catchall,
// But let the text ui handle pass
result.preview = defaultResult.preview;
else
result = defaultResult;
}
// Now attach some common extra work to the click action
LabelDefaultClickHandle::Instance().mpForward = result.handle;
result.handle = &LabelDefaultClickHandle::Instance();
// Don't lose the refresh result side effect of the glyph
// hit test
result.preview.refreshCode |= refreshResult;
return result;
}
TrackControls *LabelTrack::GetControls()