Heavyweight version of SelectedRegion stored in ViewInfo emits events

This commit is contained in:
Paul Licameli 2018-02-16 18:07:59 -05:00
parent 43af7b3265
commit 33f3d4b82d
16 changed files with 240 additions and 38 deletions

View File

@ -21,6 +21,7 @@ class wxTextFile;
class AudacityProject;
class DirManager;
class NotifyingSelectedRegion;
class TimeWarper;
struct LabelTrackHit;

View File

@ -58,7 +58,7 @@ bool ProjectSelectionManager::SnapSelection()
auto snapTo = settings.GetSnapTo();
if (snapTo != SNAP_OFF) {
auto &viewInfo = ViewInfo::Get( project );
SelectedRegion &selectedRegion = viewInfo.selectedRegion;
auto &selectedRegion = viewInfo.selectedRegion;
NumericConverter nc(NumericConverter::TIME,
settings.GetSelectionFormat(), 0, settings.GetRate());
const bool nearest = (snapTo == SNAP_NEAREST);

View File

@ -128,7 +128,7 @@ public:
bool findNotesQuantizeOld;
#endif
SelectedRegion *pSelectedRegion{};
const SelectedRegion *pSelectedRegion{};
ZoomInfo *pZoomInfo{};
bool drawEnvelope{ false };

View File

@ -851,7 +851,8 @@ void TrackPanel::DrawTracks(wxDC * dc)
const wxRect clip = GetRect();
mTrackArtist->pSelectedRegion = &mViewInfo->selectedRegion;
const SelectedRegion &sr = mViewInfo->selectedRegion;
mTrackArtist->pSelectedRegion = &sr;
mTrackArtist->pZoomInfo = mViewInfo;
TrackPanelDrawingContext context {
*dc, Target(), mLastMouseState, mTrackArtist.get()

View File

@ -21,6 +21,119 @@ Paul Licameli
#include "prefs/TracksBehaviorsPrefs.h"
#include "xml/XMLWriter.h"
wxDEFINE_EVENT( EVT_SELECTED_REGION_CHANGE, SelectedRegionEvent );
SelectedRegionEvent::SelectedRegionEvent(
wxEventType commandType, NotifyingSelectedRegion *pReg )
: wxEvent{ 0, commandType }
, pRegion{ pReg }
{}
wxEvent *SelectedRegionEvent::Clone() const
{
return safenew SelectedRegionEvent{ *this };
}
NotifyingSelectedRegion& NotifyingSelectedRegion::operator =
( const SelectedRegion &other )
{
if ( mRegion != other ) {
mRegion = other;
Notify();
}
return *this;
}
bool NotifyingSelectedRegion::setTimes(double t0, double t1)
{
bool result = false;
if ( mRegion.t0() != t0 || mRegion.t1() != t1 ) {
result = mRegion.setTimes( t0, t1 );
Notify();
}
return result;
}
bool NotifyingSelectedRegion::setT0(double t, bool maySwap)
{
bool result = false;
if ( mRegion.t0() != t ) {
result = mRegion.setT0( t, maySwap );
Notify();
}
return result;
}
bool NotifyingSelectedRegion::setT1(double t, bool maySwap)
{
bool result = false;
if ( mRegion.t1() != t ) {
result = mRegion.setT1( t, maySwap );
Notify();
}
return result;
}
void NotifyingSelectedRegion::collapseToT0()
{
if ( mRegion.t0() != mRegion.t1() ) {
mRegion.collapseToT0();
Notify();
}
}
void NotifyingSelectedRegion::collapseToT1()
{
if ( mRegion.t0() != mRegion.t1() ) {
mRegion.collapseToT1();
Notify();
}
}
void NotifyingSelectedRegion::move(double delta)
{
if (delta != 0) {
mRegion.move( delta );
Notify();
}
}
bool NotifyingSelectedRegion::setFrequencies(double f0, double f1)
{
bool result = false;
if ( mRegion.f0() != f0 || mRegion.f1() != f1 ) {
result = mRegion.setFrequencies( f0, f1 );
Notify();
}
return result;
}
bool NotifyingSelectedRegion::setF0(double f, bool maySwap)
{
bool result = false;
if ( mRegion.f0() != f ) {
result = mRegion.setF0( f, maySwap );
Notify();
}
return result;
}
bool NotifyingSelectedRegion::setF1(double f, bool maySwap)
{
bool result = false;
if ( mRegion.f1() != f ) {
result = mRegion.setF1( f, maySwap );
Notify();
}
return result;
}
void NotifyingSelectedRegion::Notify()
{
SelectedRegionEvent evt{ EVT_SELECTED_REGION_CHANGE, this };
ProcessEvent( evt );
}
static const AudacityProject::AttachedObjects::RegisteredFactory key{
[]( AudacityProject &project ) {
auto result =

View File

@ -14,11 +14,92 @@
#include <utility>
#include <vector>
#include <wx/event.h> // inherit wxEvtHandler
#include <wx/weakref.h> // member variable
#include "SelectedRegion.h"
#include "MemoryX.h"
#include "ZoomInfo.h" // to inherit
class NotifyingSelectedRegion;
struct SelectedRegionEvent : public wxEvent
{
SelectedRegionEvent( wxEventType commandType,
NotifyingSelectedRegion *pRegion );
wxEvent *Clone() const override;
wxWeakRef< NotifyingSelectedRegion > pRegion;
};
// To do: distinguish time changes from frequency changes perhaps?
wxDECLARE_EXPORTED_EVENT( AUDACITY_DLL_API,
EVT_SELECTED_REGION_CHANGE, SelectedRegionEvent );
// This heavyweight wrapper of the SelectedRegion structure emits events
// on mutating operations, that other classes can listen for.
class NotifyingSelectedRegion : public wxEvtHandler
{
public:
// Expose SelectedRegion's const accessors
double t0 () const { return mRegion.t0(); }
double t1 () const { return mRegion.t1(); }
double f0 () const { return mRegion.f0(); }
double f1 () const { return mRegion.f1(); }
double fc () const { return mRegion.fc(); }
bool isPoint() const { return mRegion.isPoint(); }
double duration() const { return mRegion.duration(); }
// Writing and reading of persistent fields -- the read is mutating but
// does not emit events
void WriteXMLAttributes
(XMLWriter &xmlFile,
const wxChar *legacyT0Name, const wxChar *legacyT1Name) const
{ mRegion.WriteXMLAttributes(xmlFile, legacyT0Name, legacyT1Name); }
bool HandleXMLAttribute
(const wxChar *attr, const wxChar *value,
const wxChar *legacyT0Name, const wxChar *legacyT1Name)
{ return mRegion.HandleXMLAttribute(
attr, value, legacyT0Name, legacyT1Name ); }
// const-only access allows assignment from this into a SelectedRegion
// or otherwise passing it into a function taking const SelectedRegion&
operator const SelectedRegion & () const { return mRegion; }
// These are the event-emitting operations
NotifyingSelectedRegion& operator = ( const SelectedRegion &other );
// Returns true iff the bounds got swapped
bool setTimes(double t0, double t1);
// Returns true iff the bounds got swapped
bool setT0(double t, bool maySwap = true);
// Returns true iff the bounds got swapped
bool setT1(double t, bool maySwap = true);
void collapseToT0();
void collapseToT1();
void move(double delta);
// Returns true iff the bounds got swapped
bool setFrequencies(double f0, double f1);
// Returns true iff the bounds got swapped
bool setF0(double f, bool maySwap = true);
// Returns true iff the bounds got swapped
bool setF1(double f, bool maySwap = true);
private:
void Notify();
SelectedRegion mRegion;
};
// See big pictorial comment in TrackPanel.cpp for explanation of these numbers
enum : int {
// constants related to y coordinates in the track panel
@ -117,7 +198,7 @@ public:
// Current selection
SelectedRegion selectedRegion;
NotifyingSelectedRegion selectedRegion;
PlayRegion playRegion;
// Scroll info

View File

@ -1171,14 +1171,14 @@ bool Effect::DoEffect(wxWindow *parent,
double projectRate,
TrackList *list,
TrackFactory *factory,
SelectedRegion *selectedRegion,
NotifyingSelectedRegion &selectedRegion,
bool shouldPrompt /* = true */)
{
wxASSERT(selectedRegion->duration() >= 0.0);
wxASSERT(selectedRegion.duration() >= 0.0);
mOutputTracks.reset();
mpSelectedRegion = selectedRegion;
mpSelectedRegion = &selectedRegion;
mFactory = factory;
mProjectRate = projectRate;
mTracks = list;
@ -1218,8 +1218,8 @@ bool Effect::DoEffect(wxWindow *parent,
newTrack->SetSelected(true);
}
mT0 = selectedRegion->t0();
mT1 = selectedRegion->t1();
mT0 = selectedRegion.t0();
mT1 = selectedRegion.t1();
if (mT1 > mT0)
{
// there is a selection: let's fit in there...
@ -1237,8 +1237,8 @@ bool Effect::DoEffect(wxWindow *parent,
: NumericConverter::DefaultSelectionFormat();
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
mF0 = selectedRegion->f0();
mF1 = selectedRegion->f1();
mF0 = selectedRegion.f0();
mF1 = selectedRegion.f1();
wxArrayString Names;
if( mF0 != SelectedRegion::UndefinedFrequency )
Names.push_back(wxT("control-f0"));
@ -1280,7 +1280,7 @@ bool Effect::DoEffect(wxWindow *parent,
if (returnVal && (mT1 >= mT0 ))
{
selectedRegion->setTimes(mT0, mT1);
selectedRegion.setTimes(mT0, mT1);
}
success = returnVal;
@ -1289,10 +1289,11 @@ bool Effect::DoEffect(wxWindow *parent,
bool Effect::Delegate( Effect &delegate, wxWindow *parent, bool shouldPrompt)
{
SelectedRegion region{ mT0, mT1 };
NotifyingSelectedRegion region;
region.setTimes( mT0, mT1 );
return delegate.DoEffect( parent, mProjectRate, mTracks, mFactory,
&region, shouldPrompt );
region, shouldPrompt );
}
// All legacy effects should have this overridden

View File

@ -43,6 +43,7 @@ class AudacityCommand;
class AudacityProject;
class LabelTrack;
class NotifyingSelectedRegion;
class ProgressDialog;
class SelectedRegion;
class EffectUIHost;
@ -257,7 +258,7 @@ class AUDACITY_DLL_API Effect /* not final */ : public wxEvtHandler,
// have the "selected" flag set to true, which is consistent with
// Audacity's standard UI.
/* not virtual */ bool DoEffect(wxWindow *parent, double projectRate, TrackList *list,
TrackFactory *factory, SelectedRegion *selectedRegion,
TrackFactory *factory, NotifyingSelectedRegion &selectedRegion,
bool shouldPrompt = true);
bool Delegate( Effect &delegate, wxWindow *parent, bool shouldPrompt);
@ -459,7 +460,7 @@ protected:
double mProjectRate; // Sample rate of the project - NEW tracks should
// be created with this rate...
double mSampleRate;
SelectedRegion *mpSelectedRegion{};
wxWeakRef<NotifyingSelectedRegion> mpSelectedRegion{};
TrackFactory *mFactory;
const TrackList *inputTracks() const { return mTracks; }
std::shared_ptr<TrackList> mOutputTracks; // used only if CopyInputTracks() is called.

View File

@ -166,7 +166,7 @@ void EffectManager::UnregisterEffect(const PluginID & ID)
EffectManager & em = EffectManager::Get();
success = em.DoEffect(ID, &window, rate,
&tracks, &trackFactory, &selectedRegion,
&tracks, &trackFactory, selectedRegion,
(flags & EffectManager::kConfigured) == 0);
if (!success)
@ -239,7 +239,7 @@ bool EffectManager::DoEffect(const PluginID & ID,
double projectRate,
TrackList *list,
TrackFactory *factory,
SelectedRegion *selectedRegion,
NotifyingSelectedRegion &selectedRegion,
bool shouldPrompt /* = true */)
{

View File

@ -41,6 +41,8 @@ class EffectRack;
class AudacityCommand;
class NotifyingSelectedRegion;
class AUDACITY_DLL_API EffectManager
{
public:
@ -88,7 +90,7 @@ public:
double projectRate,
TrackList *list,
TrackFactory *factory,
SelectedRegion *selectedRegion,
NotifyingSelectedRegion &selectedRegion,
bool shouldPrompt = true);
wxString GetEffectFamilyName(const PluginID & ID);

View File

@ -124,7 +124,7 @@ LabelGlyphHandle::~LabelGlyphHandle()
void LabelGlyphHandle::HandleGlyphClick
(LabelTrackHit &hit, const wxMouseEvent & evt,
const wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *WXUNUSED(newSel))
NotifyingSelectedRegion &WXUNUSED(newSel))
{
if (evt.ButtonDown())
{
@ -188,7 +188,7 @@ UIHandle::Result LabelGlyphHandle::Click
auto &viewInfo = ViewInfo::Get( *pProject );
HandleGlyphClick(
*mpHit, event, mRect, viewInfo, &viewInfo.selectedRegion);
*mpHit, event, mRect, viewInfo, viewInfo.selectedRegion);
if (! mpHit->mIsAdjustingLabel )
{
@ -278,7 +278,7 @@ bool LabelGlyphHandle::HandleGlyphDragRelease
(AudacityProject &project,
LabelTrackHit &hit, const wxMouseEvent & evt,
wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel)
NotifyingSelectedRegion &newSel)
{
const auto pTrack = mpLT;
const auto &mLabels = pTrack->GetLabels();
@ -340,7 +340,7 @@ bool LabelGlyphHandle::HandleGlyphDragRelease
auto selIndex = view.GetSelectedIndex( project );
//Set the selection region to be equal to
//the NEW size of the label.
*newSel = mLabels[ selIndex ].selectedRegion;
newSel = mLabels[ selIndex ].selectedRegion;
}
pTrack->SortLabels();
}
@ -356,7 +356,7 @@ UIHandle::Result LabelGlyphHandle::Drag
const wxMouseEvent &event = evt.event;
auto &viewInfo = ViewInfo::Get( *pProject );
HandleGlyphDragRelease(
*pProject, *mpHit, event, mRect, viewInfo, &viewInfo.selectedRegion);
*pProject, *mpHit, event, mRect, viewInfo, viewInfo.selectedRegion);
// Refresh all so that the change of selection is redrawn in all tracks
return result | RefreshCode::RefreshAll | RefreshCode::DrawOverlays;
@ -377,7 +377,7 @@ UIHandle::Result LabelGlyphHandle::Release
const wxMouseEvent &event = evt.event;
auto &viewInfo = ViewInfo::Get( *pProject );
if (HandleGlyphDragRelease(
*pProject, *mpHit, event, mRect, viewInfo, &viewInfo.selectedRegion)) {
*pProject, *mpHit, event, mRect, viewInfo, viewInfo.selectedRegion)) {
ProjectHistory::Get( *pProject ).PushState(_("Modified Label"),
_("Label Edit"),
UndoPush::CONSOLIDATE);

View File

@ -16,7 +16,7 @@ Paul Licameli split from TrackPanel.cpp
class wxMouseState;
class LabelTrack;
class LabelTrackEvent;
class SelectedRegion;
class NotifyingSelectedRegion;
class ZoomInfo;
/// mEdge:
@ -92,12 +92,12 @@ private:
void HandleGlyphClick
(LabelTrackHit &hit,
const wxMouseEvent & evt, const wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel);
NotifyingSelectedRegion &newSel);
bool HandleGlyphDragRelease
(AudacityProject &project,
LabelTrackHit &hit,
const wxMouseEvent & evt, wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel);
NotifyingSelectedRegion &newSel);
void MayAdjustLabel
( LabelTrackHit &hit,

View File

@ -78,7 +78,7 @@ void LabelTextHandle::HandleTextClick(AudacityProject &
,
const wxMouseEvent & evt,
const wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel)
NotifyingSelectedRegion &newSel)
{
auto pTrack = mpLT.lock();
if (!pTrack)
@ -94,7 +94,7 @@ void LabelTextHandle::HandleTextClick(AudacityProject &
if ( selIndex != -1 ) {
const auto &mLabels = pTrack->GetLabels();
const auto &labelStruct = mLabels[ selIndex ];
*newSel = labelStruct.selectedRegion;
newSel = labelStruct.selectedRegion;
if (evt.LeftDown()) {
// Find the NEW drag end
@ -135,7 +135,7 @@ void LabelTextHandle::HandleTextClick(AudacityProject &
if (!LabelTrackView::OverTextBox(&labelStruct, evt.m_x, evt.m_y))
view.SetSelectedIndex( -1 );
double t = zoomInfo.PositionToTime(evt.m_x, r.x);
*newSel = SelectedRegion(t, t);
newSel = SelectedRegion(t, t);
}
#endif
}
@ -143,7 +143,7 @@ void LabelTextHandle::HandleTextClick(AudacityProject &
if (evt.MiddleDown()) {
// Paste text, making a NEW label if none is selected.
wxTheClipboard->UsePrimarySelection(true);
view.PasteSelectedText(project, newSel->t0(), newSel->t1());
view.PasteSelectedText(project, newSel.t0(), newSel.t1());
wxTheClipboard->UsePrimarySelection(false);
}
#endif
@ -169,7 +169,7 @@ UIHandle::Result LabelTextHandle::Click
mSelectedRegion = viewInfo.selectedRegion;
HandleTextClick( *pProject,
event, evt.rect, viewInfo, &viewInfo.selectedRegion );
event, evt.rect, viewInfo, viewInfo.selectedRegion );
{
// IF the user clicked a label, THEN select all other tracks by Label

View File

@ -16,6 +16,7 @@ Paul Licameli split from TrackPanel.cpp
class wxMouseState;
class LabelTrack;
class NotifyingSelectedRegion;
class SelectionStateChanger;
class ZoomInfo;
@ -59,7 +60,7 @@ private:
void HandleTextClick
(AudacityProject &project,
const wxMouseEvent & evt, const wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel);
NotifyingSelectedRegion &newSel);
void HandleTextDragRelease(
AudacityProject &project, const wxMouseEvent & evt);

View File

@ -1336,7 +1336,7 @@ unsigned LabelTrackView::Char(
/// KeyEvent is called for every keypress when over the label track.
bool LabelTrackView::DoKeyDown(
AudacityProject &project, SelectedRegion &newSel, wxKeyEvent & event)
AudacityProject &project, NotifyingSelectedRegion &newSel, wxKeyEvent & event)
{
// Only track true changes to the label
bool updated = false;
@ -1564,7 +1564,7 @@ bool LabelTrackView::DoKeyDown(
/// OnChar is called for incoming characters -- that's any keypress not handled
/// by OnKeyDown.
bool LabelTrackView::DoChar(
AudacityProject &project, SelectedRegion &WXUNUSED(newSel),
AudacityProject &project, NotifyingSelectedRegion &WXUNUSED(newSel),
wxKeyEvent & event)
{
// Check for modifiers and only allow shift.

View File

@ -20,6 +20,7 @@ class LabelStruct;
class LabelTrack;
struct LabelTrackEvent;
struct LabelTrackHit;
class NotifyingSelectedRegion;
class SelectedRegion;
struct TrackPanelDrawingContext;
class ZoomInfo;
@ -54,9 +55,9 @@ public:
bool DoCaptureKey( AudacityProject &project, wxKeyEvent &event );
bool DoKeyDown(
AudacityProject &project, SelectedRegion &sel, wxKeyEvent & event);
AudacityProject &project, NotifyingSelectedRegion &sel, wxKeyEvent & event);
bool DoChar(
AudacityProject &project, SelectedRegion &sel, wxKeyEvent & event);
AudacityProject &project, NotifyingSelectedRegion &sel, wxKeyEvent & event);
//This returns the index of the label we just added.
int AddLabel(const SelectedRegion &region,