1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ViewInfo.cpp
6
7 Paul Licameli
8
9 **********************************************************************/
10
11 #include "ViewInfo.h"
12
13
14
15 #include <algorithm>
16 #include "XMLAttributeValueView.h"
17
18 #include "Prefs.h"
19 #include "Project.h"
20 #include "XMLWriter.h"
21
22 wxDEFINE_EVENT( EVT_SELECTED_REGION_CHANGE, SelectedRegionEvent );
23
SelectedRegionEvent(wxEventType commandType,NotifyingSelectedRegion * pReg)24 SelectedRegionEvent::SelectedRegionEvent(
25 wxEventType commandType, NotifyingSelectedRegion *pReg )
26 : wxEvent{ 0, commandType }
27 , pRegion{ pReg }
28 {}
29
Clone() const30 wxEvent *SelectedRegionEvent::Clone() const
31 {
32 return safenew SelectedRegionEvent{ *this };
33 }
34
35 XMLMethodRegistryBase::Mutators<NotifyingSelectedRegion>
Mutators(const char * legacyT0Name,const char * legacyT1Name)36 NotifyingSelectedRegion::Mutators(
37 const char *legacyT0Name, const char *legacyT1Name)
38 {
39 XMLMethodRegistryBase::Mutators<NotifyingSelectedRegion> results;
40 // Get serialization methods of contained SelectedRegion, and wrap each
41 for (auto &delegate: SelectedRegion::Mutators(legacyT0Name, legacyT1Name)) {
42 results.emplace_back(
43 delegate.first,
44 [fn = std::move(delegate.second)](auto ®ion, auto value) {
45 fn( region.mRegion, value );
46 region.Notify( true );
47 }
48 );
49 }
50 return results;
51 }
52
operator =(const SelectedRegion & other)53 NotifyingSelectedRegion& NotifyingSelectedRegion::operator =
54 ( const SelectedRegion &other )
55 {
56 if ( mRegion != other ) {
57 mRegion = other;
58 Notify();
59 }
60 return *this;
61 }
62
setTimes(double t0,double t1)63 bool NotifyingSelectedRegion::setTimes(double t0, double t1)
64 {
65 bool result = false;
66 if ( mRegion.t0() != t0 || mRegion.t1() != t1 ) {
67 result = mRegion.setTimes( t0, t1 );
68 Notify();
69 }
70 return result;
71 }
72
setT0(double t,bool maySwap)73 bool NotifyingSelectedRegion::setT0(double t, bool maySwap)
74 {
75 bool result = false;
76 if ( mRegion.t0() != t ) {
77 result = mRegion.setT0( t, maySwap );
78 Notify();
79 }
80 return result;
81 }
82
setT1(double t,bool maySwap)83 bool NotifyingSelectedRegion::setT1(double t, bool maySwap)
84 {
85 bool result = false;
86 if ( mRegion.t1() != t ) {
87 result = mRegion.setT1( t, maySwap );
88 Notify();
89 }
90 return result;
91 }
92
collapseToT0()93 void NotifyingSelectedRegion::collapseToT0()
94 {
95 if ( mRegion.t0() != mRegion.t1() ) {
96 mRegion.collapseToT0();
97 Notify();
98 }
99 }
100
collapseToT1()101 void NotifyingSelectedRegion::collapseToT1()
102 {
103 if ( mRegion.t0() != mRegion.t1() ) {
104 mRegion.collapseToT1();
105 Notify();
106 }
107 }
108
move(double delta)109 void NotifyingSelectedRegion::move(double delta)
110 {
111 if (delta != 0) {
112 mRegion.move( delta );
113 Notify();
114 }
115 }
116
setFrequencies(double f0,double f1)117 bool NotifyingSelectedRegion::setFrequencies(double f0, double f1)
118 {
119 bool result = false;
120 if ( mRegion.f0() != f0 || mRegion.f1() != f1 ) {
121 result = mRegion.setFrequencies( f0, f1 );
122 Notify();
123 }
124 return result;
125 }
126
setF0(double f,bool maySwap)127 bool NotifyingSelectedRegion::setF0(double f, bool maySwap)
128 {
129 bool result = false;
130 if ( mRegion.f0() != f ) {
131 result = mRegion.setF0( f, maySwap );
132 Notify();
133 }
134 return result;
135 }
136
setF1(double f,bool maySwap)137 bool NotifyingSelectedRegion::setF1(double f, bool maySwap)
138 {
139 bool result = false;
140 if ( mRegion.f1() != f ) {
141 result = mRegion.setF1( f, maySwap );
142 Notify();
143 }
144 return result;
145 }
146
Notify(bool delayed)147 void NotifyingSelectedRegion::Notify( bool delayed )
148 {
149 SelectedRegionEvent evt{ EVT_SELECTED_REGION_CHANGE, this };
150 if ( delayed )
151 QueueEvent( evt.Clone() );
152 else
153 ProcessEvent( evt );
154 }
155
156 wxDEFINE_EVENT( EVT_PLAY_REGION_CHANGE, PlayRegionEvent );
157
PlayRegionEvent(wxEventType commandType,PlayRegion * pReg)158 PlayRegionEvent::PlayRegionEvent(
159 wxEventType commandType, PlayRegion *pReg )
160 : wxEvent{ 0, commandType }
161 {}
162
Clone() const163 wxEvent *PlayRegionEvent::Clone() const
164 {
165 return safenew PlayRegionEvent{ *this };
166 }
167
SetActive(bool active)168 void PlayRegion::SetActive( bool active )
169 {
170 if (mActive != active) {
171 mActive = active;
172 if (mActive) {
173 // Restore values
174 if (mStart != mLastActiveStart || mEnd != mLastActiveEnd) {
175 mStart = mLastActiveStart;
176 mEnd = mLastActiveEnd;
177 }
178 }
179 Notify();
180 }
181 }
182
SetStart(double start)183 void PlayRegion::SetStart( double start )
184 {
185 if (mStart != start) {
186 if (mActive)
187 mLastActiveStart = start;
188 mStart = start;
189 Notify();
190 }
191 }
192
SetEnd(double end)193 void PlayRegion::SetEnd( double end )
194 {
195 if (mEnd != end) {
196 if (mActive)
197 mLastActiveEnd = end;
198 mEnd = end;
199 Notify();
200 }
201 }
202
SetTimes(double start,double end)203 void PlayRegion::SetTimes( double start, double end )
204 {
205 if (mStart != start || mEnd != end) {
206 if (mActive)
207 mLastActiveStart = start, mLastActiveEnd = end;
208 mStart = start, mEnd = end;
209 Notify();
210 }
211 }
212
SetAllTimes(double start,double end)213 void PlayRegion::SetAllTimes( double start, double end )
214 {
215 SetTimes(start, end);
216 mLastActiveStart = start, mLastActiveEnd = end;
217 }
218
Clear()219 void PlayRegion::Clear()
220 {
221 SetAllTimes(invalidValue, invalidValue);
222 }
223
IsClear() const224 bool PlayRegion::IsClear() const
225 {
226 return GetStart() == invalidValue && GetEnd() == invalidValue;
227 }
228
IsLastActiveRegionClear() const229 bool PlayRegion::IsLastActiveRegionClear() const
230 {
231 return GetLastActiveStart() == invalidValue && GetLastActiveEnd() == invalidValue;
232 }
233
Order()234 void PlayRegion::Order()
235 {
236 if ( mStart >= 0 && mEnd >= 0 && mStart > mEnd) {
237 std::swap( mStart, mEnd );
238 if (mActive)
239 mLastActiveStart = mStart, mLastActiveEnd = mEnd;
240 Notify();
241 }
242 }
243
Notify()244 void PlayRegion::Notify()
245 {
246 PlayRegionEvent evt{ EVT_PLAY_REGION_CHANGE, this };
247 ProcessEvent( evt );
248 }
249
250 const TranslatableString LoopToggleText = XXO("&Loop On/Off");
251
252 static const AudacityProject::AttachedObjects::RegisteredFactory key{
__anoneaea0bc20202( ) 253 []( AudacityProject &project ) {
254 return std::make_unique<ViewInfo>(0.0, 1.0, ZoomInfo::GetDefaultZoom());
255 }
256 };
257
Get(AudacityProject & project)258 ViewInfo &ViewInfo::Get( AudacityProject &project )
259 {
260 return project.AttachedObjects::Get< ViewInfo >( key );
261 }
262
Get(const AudacityProject & project)263 const ViewInfo &ViewInfo::Get( const AudacityProject &project )
264 {
265 return Get( const_cast< AudacityProject & >( project ) );
266 }
267
ViewInfo(double start,double screenDuration,double pixelsPerSecond)268 ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond)
269 : ZoomInfo(start, pixelsPerSecond)
270 , selectedRegion()
271 , total(screenDuration)
272 , sbarH(0)
273 , sbarScreen(1)
274 , sbarTotal(1)
275 , sbarScale(1.0)
276 , scrollStep(16)
277 , bUpdateTrackIndicator(true)
278 , bScrollBeyondZero(false)
279 {
280 UpdatePrefs();
281 }
282
UpdateSelectedPrefs(int id)283 void ViewInfo::UpdateSelectedPrefs( int id )
284 {
285 if (id == UpdateScrollPrefsID())
286 gPrefs->Read(wxT("/GUI/AutoScroll"), &bUpdateTrackIndicator,
287 true);
288 ZoomInfo::UpdateSelectedPrefs( id );
289 }
290
UpdatePrefs()291 void ViewInfo::UpdatePrefs()
292 {
293 ZoomInfo::UpdatePrefs();
294 #ifdef EXPERIMENTAL_SCROLLING_LIMITS
295 bScrollBeyondZero = ScrollingPreference.Read();
296 #endif
297 gPrefs->Read(wxT("/GUI/AdjustSelectionEdges"), &bAdjustSelectionEdges,
298 true);
299
300 UpdateSelectedPrefs( UpdateScrollPrefsID() );
301 }
302
SetBeforeScreenWidth(wxInt64 beforeWidth,wxInt64 screenWidth,double lowerBoundTime)303 void ViewInfo::SetBeforeScreenWidth(wxInt64 beforeWidth, wxInt64 screenWidth, double lowerBoundTime)
304 {
305 h =
306 std::max(lowerBoundTime,
307 std::min(total - screenWidth / zoom,
308 beforeWidth / zoom));
309 }
310
WriteXMLAttributes(XMLWriter & xmlFile) const311 void ViewInfo::WriteXMLAttributes(XMLWriter &xmlFile) const
312 // may throw
313 {
314 selectedRegion.WriteXMLAttributes(xmlFile, "sel0", "sel1");
315 xmlFile.WriteAttr(wxT("vpos"), vpos);
316 xmlFile.WriteAttr(wxT("h"), h, 10);
317 xmlFile.WriteAttr(wxT("zoom"), zoom, 10);
318 }
319
320 //! Construct once at static intialization time to hook project file IO
321 static struct ViewInfo::ProjectFileIORegistration {
322
323 ProjectFileIORegistry::AttributeReaderEntries entries {
324 [](AudacityProject &project) -> NotifyingSelectedRegion &
__anoneaea0bc20302ViewInfo::ProjectFileIORegistration325 {
326 return ViewInfo::Get(project).selectedRegion;
327 },
328 NotifyingSelectedRegion::Mutators("sel0", "sel1")
329 };
330
331 ProjectFileIORegistry::AttributeReaderEntries entries2 {
332 // Just a pointer to function, but needing overload resolution as non-const:
333 (ViewInfo& (*)(AudacityProject &)) &ViewInfo::Get,
334 {
__anoneaea0bc20402ViewInfo::ProjectFileIORegistration335 { "vpos", [](auto &viewInfo, auto value){
336 viewInfo.vpos = value.Get(viewInfo.vpos);
337 // Note that (other than in import of old .aup files) there is no other
338 // reassignment of vpos, except in handling the vertical scroll.
339 } },
__anoneaea0bc20502ViewInfo::ProjectFileIORegistration340 { "h", [](auto &viewInfo, auto value){
341 viewInfo.h = value.Get(viewInfo.h);
342 } },
__anoneaea0bc20602ViewInfo::ProjectFileIORegistration343 { "zoom", [](auto &viewInfo, auto value){
344 viewInfo.zoom = value.Get(viewInfo.zoom);
345 } },
346 } };
347
348 } projectFileIORegistration;
349
UpdateScrollPrefsID()350 int ViewInfo::UpdateScrollPrefsID()
351 {
352 return 10000;
353 }
354
355 static ProjectFileIORegistry::AttributeWriterEntry entry {
__anoneaea0bc20702()356 [](const AudacityProject &project, XMLWriter &xmlFile){
357 ViewInfo::Get(project).WriteXMLAttributes(xmlFile);
358 }
359 };
360
361 BoolSetting ScrollingPreference{ L"/GUI/ScrollBeyondZero", false };
362