Audacity 3.2.0
ProjectWindow.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5ProjectWindow.cpp
6
7Paul Licameli split from AudacityProject.cpp
8
9**********************************************************************/
10
11#include "ProjectWindow.h"
12
13
14
15#include "ActiveProject.h"
16#include "AllThemeResources.h"
17#include "AudioIO.h"
18#include "Menus.h"
19#include "Project.h"
20#include "ProjectAudioIO.h"
21#include "ProjectWindows.h"
22#include "ProjectStatus.h"
23#include "RefreshCode.h"
25#include "TrackPanelAx.h"
26#include "UndoManager.h"
27#include "ViewInfo.h"
28#include "WaveClip.h"
29#include "WaveTrack.h"
31#include "prefs/ThemePrefs.h"
32#include "prefs/TracksPrefs.h"
34#include "tracks/ui/Scrubbing.h"
35#include "tracks/ui/TrackView.h"
38
39#include "ThemedWrappers.h"
40#include "RealtimeEffectPanel.h"
41
42#include <wx/app.h>
43#include <wx/display.h>
44#include <wx/scrolbar.h>
45#include <wx/sizer.h>
46#include <wx/splitter.h>
47#include <wx/wupdlock.h>
48
49#include "TrackPanel.h"
50
51namespace
52{
53#ifdef HAS_AUDIOCOM_UPLOAD
54 constexpr int DEFAULT_WINDOW_WIDTH = 1120;
55#else
56 constexpr int DEFAULT_WINDOW_WIDTH = 1060;
57#endif
58 constexpr int DEFAULT_WINDOW_HEIGHT = 674;
59}
60
61BoolSetting ProjectWindowMaximized{ L"/Window/Maximized", false };
62BoolSetting ProjectWindowIconized{ L"/Window/Iconized", false };
63IntSetting ProjectWindowX{ L"/Window/X", 0 };
64IntSetting ProjectWindowY{ L"/Window/Y", 0 };
67IntSetting ProjectWindowNormalX{ L"/Window/Normal_X", 0 };
68IntSetting ProjectWindowNormalY{ L"/Window/Normal_Y", 0 };
71
72// Returns the screen containing a rectangle, or -1 if none does.
73int ScreenContaining( wxRect & r ){
74 unsigned int n = wxDisplay::GetCount();
75 for(unsigned int i = 0;i<n;i++){
76 wxDisplay d(i);
77 wxRect scr = d.GetClientArea();
78 if( scr.Contains( r ) )
79 return (int)i;
80 }
81 return -1;
82}
83
84// true IFF TL and BR corners are on a connected display.
85// Does not need to check all four. We just need to check that
86// the window probably is straddling screens in a sensible way.
87// If the user wants to use mixed landscape and portrait, they can.
88bool CornersOnScreen( wxRect & r ){
89 if( wxDisplay::GetFromPoint( r.GetTopLeft() ) == wxNOT_FOUND) return false;
90 if( wxDisplay::GetFromPoint( r.GetBottomRight() ) == wxNOT_FOUND) return false;
91 return true;
92}
93
94// true iff we have enough of the top bar to be able to reposition the window.
95bool IsWindowAccessible(wxRect *requestedRect)
96{
97 wxDisplay display;
98 wxRect targetTitleRect(requestedRect->GetLeftTop(), requestedRect->GetBottomRight());
99 // Hackery to approximate a window top bar size from a window size.
100 // and exclude the open/close and borders.
101 targetTitleRect.x += 15;
102 targetTitleRect.width -= 100;
103 if (targetTitleRect.width < 165) targetTitleRect.width = 165;
104 targetTitleRect.height = 15;
105 int targetBottom = targetTitleRect.GetBottom();
106 int targetRight = targetTitleRect.GetRight();
107 // This looks like overkill to check each and every pixel in the ranges.
108 // and decide that if any is visible on screen we are OK.
109 for (int i = targetTitleRect.GetLeft(); i < targetRight; i++) {
110 for (int j = targetTitleRect.GetTop(); j < targetBottom; j++) {
111 int monitor = display.GetFromPoint(wxPoint(i, j));
112 if (monitor != wxNOT_FOUND) {
113 return TRUE;
114 }
115 }
116 }
117 return FALSE;
118}
119
120// BG: The default size and position of the first window
121void GetDefaultWindowRect(wxRect *defRect)
122{
123 *defRect = wxGetClientDisplayRect();
124
125 int width = DEFAULT_WINDOW_WIDTH;
126 int height = DEFAULT_WINDOW_HEIGHT;
127
128 //These conditional values assist in improving placement and size
129 //of NEW windows on different platforms.
130#ifdef __WXGTK__
131 height += 20;
132#endif
133
134#ifdef __WXMSW__
135 height += 40;
136#endif
137
138#ifdef __WXMAC__
139 height += 55;
140#endif
141
142 // Use screen size where it is smaller than the values we would like.
143 // Otherwise use the values we would like, and centred.
144 if (width < defRect->width)
145 {
146 defRect->x = (defRect->width - width)/2;
147 defRect->width = width;
148 }
149
150 if (height < defRect->height)
151 {
152 defRect->y = (defRect->height - height)/2;
153 // Bug 1119 workaround
154 // Small adjustment for very small Mac screens.
155 // If there is only a tiny space at the top
156 // then instead of vertical centre, align to bottom.
157 const int pixelsFormenu = 60;
158 if( defRect->y < pixelsFormenu )
159 defRect->y *=2;
160 defRect->height = height;
161 }
162}
163
164// BG: Calculate where to place the next window (could be the first window)
165// BG: Does not store X and Y in prefs. This is intentional.
166//
167// LL: This should NOT need to be this complicated...FIXME
168void GetNextWindowPlacement(wxRect *nextRect, bool *pMaximized, bool *pIconized)
169{
170 int inc = 25;
171
172 wxRect defaultRect;
173 GetDefaultWindowRect(&defaultRect);
174
175 *pMaximized = ProjectWindowMaximized.Read();
176 *pIconized = ProjectWindowIconized.Read();
177
178 wxRect windowRect;
179 windowRect.x = ProjectWindowX.ReadWithDefault(defaultRect.x);
180 windowRect.y = ProjectWindowY.ReadWithDefault(defaultRect.y);
181 windowRect.width = ProjectWindowWidth.ReadWithDefault(defaultRect.width);
182 windowRect.height = ProjectWindowHeight.ReadWithDefault(defaultRect.height);
183
184 wxRect normalRect;
185 normalRect.x = ProjectWindowNormalX.ReadWithDefault(defaultRect.x);
186 normalRect.y = ProjectWindowNormalY.ReadWithDefault(defaultRect.y);
187 normalRect.width = ProjectWindowNormalWidth.ReadWithDefault(defaultRect.width);
188 normalRect.height = ProjectWindowNormalHeight.ReadWithDefault(defaultRect.height);
189
190 // Workaround 2.1.1 and earlier bug on OSX...affects only normalRect, but let's just
191 // validate for all rects and plats
192 if (normalRect.width == 0 || normalRect.height == 0) {
193 normalRect = defaultRect;
194 }
195 if (windowRect.width == 0 || windowRect.height == 0) {
196 windowRect = defaultRect;
197 }
198
199
200 wxRect screenRect( wxGetClientDisplayRect());
201#if defined(__WXMAC__)
202
203 // On OSX, the top of the window should never be less than the menu height,
204 // so something is amiss if it is
205 if (normalRect.y < screenRect.y) {
206 normalRect = defaultRect;
207 }
208 if (windowRect.y < screenRect.y) {
209 windowRect = defaultRect;
210 }
211#endif
212
213 // IF projects empty, THEN it's the first window.
214 // It lands where the config says it should, and can straddle screen.
215 if (AllProjects{}.empty()) {
216 if (*pMaximized || *pIconized) {
217 *nextRect = normalRect;
218 }
219 else {
220 *nextRect = windowRect;
221 }
222 // Resize, for example if one monitor that was on is now off.
223 if (!CornersOnScreen( wxRect(*nextRect).Deflate( 32, 32 ))) {
224 *nextRect = defaultRect;
225 }
226 if (!IsWindowAccessible(nextRect)) {
227 *nextRect = defaultRect;
228 }
229 // Do not trim the first project window down.
230 // All corners are on screen (or almost so), and
231 // the rect may straddle screens.
232 return;
233 }
234
235
236 // ELSE a subsequent NEW window. It will NOT straddle screens.
237
238 // We don't mind being 32 pixels off the screen in any direction.
239 // Make sure initial sizes (pretty much) fit within the display bounds
240 // We used to trim the sizes which could result in ridiculously small windows.
241 // contributing to bug 1243.
242 // Now instead if the window significantly doesn't fit the screen, we use the default
243 // window instead, which we know does.
244 if (ScreenContaining( wxRect(normalRect).Deflate( 32, 32 ))<0) {
245 normalRect = defaultRect;
246 }
247 if (ScreenContaining( wxRect(windowRect).Deflate( 32, 32 ) )<0) {
248 windowRect = defaultRect;
249 }
250
251 bool validWindowSize = false;
252 ProjectWindow * validProject = NULL;
253 for ( auto iter = AllProjects{}.rbegin(), end = AllProjects{}.rend();
254 iter != end; ++iter
255 ) {
256 auto pProject = *iter;
257 if (!GetProjectFrame( *pProject ).IsIconized()) {
258 validWindowSize = true;
259 validProject = &ProjectWindow::Get( *pProject );
260 break;
261 }
262 }
263 if (validWindowSize) {
264 *nextRect = validProject->GetRect();
265 *pMaximized = validProject->IsMaximized();
266 *pIconized = validProject->IsIconized();
267 // Do not straddle screens.
268 if (ScreenContaining( wxRect(*nextRect).Deflate( 32, 32 ) )<0) {
269 *nextRect = defaultRect;
270 }
271 }
272 else {
273 *nextRect = normalRect;
274 }
275
276 //Placement depends on the increments
277 nextRect->x += inc;
278 nextRect->y += inc;
279
280 // defaultrect is a rectangle on the first screen. It's the right fallback to
281 // use most of the time if things are not working out right with sizing.
282 // windowRect is a saved rectangle size.
283 // normalRect seems to be a substitute for windowRect when iconized or maximised.
284
285 // Windows can say that we are off screen when actually we are not.
286 // On Windows 10 I am seeing miscalculation by about 6 pixels.
287 // To fix this we allow some sloppiness on the edge being counted as off screen.
288 // This matters most when restoring very carefully sized windows that are maximised
289 // in one dimension (height or width) but not both.
290 const int edgeSlop = 10;
291
292 // Next four lines are getting the rectangle for the screen that contains the
293 // top left corner of nextRect (and defaulting to rect of screen 0 otherwise).
294 wxPoint p = nextRect->GetLeftTop();
295 int scr = std::max( 0, wxDisplay::GetFromPoint( p ));
296 wxDisplay d( scr );
297 screenRect = d.GetClientArea();
298
299 // Now we (possibly) start trimming our rectangle down.
300 // Have we hit the right side of the screen?
301 wxPoint bottomRight = nextRect->GetBottomRight();
302 if (bottomRight.x > (screenRect.GetRight()+edgeSlop)) {
303 int newWidth = screenRect.GetWidth() - nextRect->GetLeft();
304 if (newWidth < defaultRect.GetWidth()) {
305 nextRect->x = windowRect.x;
306 nextRect->y = windowRect.y;
307 nextRect->width = windowRect.width;
308 }
309 else {
310 nextRect->width = newWidth;
311 }
312 }
313
314 // Have we hit the bottom of the screen?
315 bottomRight = nextRect->GetBottomRight();
316 if (bottomRight.y > (screenRect.GetBottom()+edgeSlop)) {
317 nextRect->y -= inc;
318 bottomRight = nextRect->GetBottomRight();
319 if (bottomRight.y > (screenRect.GetBottom()+edgeSlop)) {
320 nextRect->SetBottom(screenRect.GetBottom());
321 }
322 }
323
324 // After all that we could have a window that does not have a visible
325 // top bar. [It is unlikely, but something might have gone wrong]
326 // If so, use the safe fallback size.
327 if (!IsWindowAccessible(nextRect)) {
328 *nextRect = defaultRect;
329 }
330}
331
332namespace {
333
334// This wrapper prevents the scrollbars from retaining focus after being
335// used. Otherwise, the only way back to the track panel is to click it
336// and that causes your original location to be lost.
337class ScrollBar final : public wxScrollBar
338{
339public:
340 ScrollBar(wxWindow* parent, wxWindowID id, long style)
341 : wxScrollBar(parent, id, wxDefaultPosition, wxDefaultSize, style)
342 {
343 }
344
345 void OnSetFocus(wxFocusEvent & e)
346 {
347 wxWindow *w = e.GetWindow();
348 if (w != NULL) {
349 w->SetFocus();
350 }
351 }
352
353 void SetScrollbar(int position, int thumbSize,
354 int range, int pageSize,
355 bool refresh = true) override;
356
357private:
358 DECLARE_EVENT_TABLE()
359};
360
361void ScrollBar::SetScrollbar(int position, int thumbSize,
362 int range, int pageSize,
363 bool refresh)
364{
365 // Mitigate flashing of scrollbars by refreshing only when something really changes.
366
367 // PRL: This may have been made unnecessary by other fixes for flashing, see
368 // commit ac05b190bee7dd0000bce56edb0e5e26185c972f
369
370 auto changed =
371 position != GetThumbPosition() ||
372 thumbSize != GetThumbSize() ||
373 range != GetRange() ||
374 pageSize != GetPageSize();
375 if (!changed)
376 return;
377
378 wxScrollBar::SetScrollbar(position, thumbSize, range, pageSize, refresh);
379}
380
381BEGIN_EVENT_TABLE(ScrollBar, wxScrollBar)
382 EVT_SET_FOCUS(ScrollBar::OnSetFocus)
384
385// Common mouse wheel handling in track panel cells, moved here to avoid
386// compilation dependencies on Track, TrackPanel, and Scrubbing at low levels
387// which made cycles
388static CommonTrackPanelCell::MouseWheelHook::Scope scope{
389// Need a bit of memory from one call to the next
390[mVertScrollRemainder = 0.0](
391 const TrackPanelMouseEvent &evt, AudacityProject *pProject )
392mutable -> unsigned {
393 using namespace RefreshCode;
394
395 if ( TrackList::Get( *pProject ).empty() )
396 // Scrolling and Zoom in and out commands are disabled when there are no tracks.
397 // This should be disabled too for consistency. Otherwise
398 // you do see changes in the time ruler.
399 return Cancelled;
400
401 unsigned result = RefreshAll;
402 const wxMouseEvent &event = evt.event;
403 auto &viewInfo = ViewInfo::Get( *pProject );
404 Scrubber &scrubber = Scrubber::Get( *pProject );
405 auto &window = ProjectWindow::Get( *pProject );
406 const auto steps = evt.steps;
407
408 if (event.ShiftDown()
409 // Don't pan during smooth scrolling. That would conflict with keeping
410 // the play indicator centered.
411 && !scrubber.IsScrollScrubbing()
412 )
413 {
414 // MM: Scroll left/right when used with Shift key down
415 window.TP_ScrollWindow(
416 viewInfo.OffsetTimeByPixels(
417 viewInfo.PositionToTime(0), 50.0 * -steps));
418 }
419 else if (event.CmdDown())
420 {
421#if 0
422 // JKC: Alternative scroll wheel zooming code
423 // using AudacityProject zooming, which is smarter,
424 // it keeps selections on screen and centred if it can,
425 // also this ensures mousewheel and zoom buttons give same result.
426 double ZoomFactor = pow(2.0, steps);
427 AudacityProject *p = GetProject();
428 if( steps > 0 )
429 // PRL: Track panel refresh may be needed if you reenable this
430 // code, but we don't want this file dependent on TrackPanel.cpp
431 p->ZoomInByFactor( ZoomFactor );
432 else
433 p->ZoomOutByFactor( ZoomFactor );
434#endif
435 // MM: Zoom in/out when used with Control key down
436 // We're converting pixel positions to times,
437 // counting pixels from the left edge of the track.
438 int trackLeftEdge = viewInfo.GetLeftOffset();
439
440 // Time corresponding to mouse position
441 wxCoord xx;
442 double center_h;
443 double mouse_h = viewInfo.PositionToTime(event.m_x, trackLeftEdge);
444
445 // Scrubbing? Expand or contract about the center, ignoring mouse position
446 if (scrubber.IsScrollScrubbing())
447 center_h = viewInfo.h +
448 (viewInfo.GetScreenEndTime() - viewInfo.h) / 2.0;
449 // Zooming out? Focus on mouse.
450 else if( steps <= 0 )
451 center_h = mouse_h;
452 // No Selection? Focus on mouse.
453 else if((viewInfo.selectedRegion.t1() - viewInfo.selectedRegion.t0() ) < 0.00001 )
454 center_h = mouse_h;
455 // Before Selection? Focus on left
456 else if( mouse_h < viewInfo.selectedRegion.t0() )
457 center_h = viewInfo.selectedRegion.t0();
458 // After Selection? Focus on right
459 else if( mouse_h > viewInfo.selectedRegion.t1() )
460 center_h = viewInfo.selectedRegion.t1();
461 // Inside Selection? Focus on mouse
462 else
463 center_h = mouse_h;
464
465 xx = viewInfo.TimeToPosition(center_h, trackLeftEdge);
466
467 // Time corresponding to last (most far right) audio.
468 double audioEndTime = TrackList::Get( *pProject ).GetEndTime();
469
470// Disabled this code to fix Bug 1923 (tricky to wheel-zoom right of waveform).
471#if 0
472 // When zooming in empty space, it's easy to 'lose' the waveform.
473 // This prevents it.
474 // IF zooming in
475 if (steps > 0)
476 {
477 // IF mouse is to right of audio
478 if (center_h > audioEndTime)
479 // Zooming brings far right of audio to mouse.
480 center_h = audioEndTime;
481 }
482#endif
483
484 wxCoord xTrackEnd = viewInfo.TimeToPosition( audioEndTime );
485 viewInfo.ZoomBy(pow(2.0, steps));
486
487 double new_center_h = viewInfo.PositionToTime(xx, trackLeftEdge);
488 viewInfo.h += (center_h - new_center_h);
489
490 // If wave has gone off screen, bring it back.
491 // This means that the end of the track stays where it was.
492 if( viewInfo.h > audioEndTime )
493 viewInfo.h += audioEndTime - viewInfo.PositionToTime( xTrackEnd );
494
495
496 result |= FixScrollbars;
497 }
498 else
499 {
500#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
501 if (scrubber.IsScrubbing()) {
502 scrubber.HandleScrollWheel(steps);
503 evt.event.Skip(false);
504 }
505 else
506#endif
507 {
508 // MM: Scroll up/down when used without modifier keys
509 double lines = steps * 4 + mVertScrollRemainder;
510 mVertScrollRemainder = lines - floor(lines);
511 lines = floor(lines);
512 auto didSomething = window.TP_ScrollUpDown((int)-lines);
513 if (!didSomething)
514 result |= Cancelled;
515 }
516 }
517
518 return result;
519} };
520
521AttachedWindows::RegisteredFactory sProjectWindowKey{
522 []( AudacityProject &parent ) -> wxWeakRef< wxWindow > {
523 wxRect wndRect;
524 bool bMaximized = false;
525 bool bIconized = false;
526 GetNextWindowPlacement(&wndRect, &bMaximized, &bIconized);
527
528 auto pWindow = safenew ProjectWindow(
529 nullptr, -1,
530 wxDefaultPosition,
531 wxSize(wndRect.width, wndRect.height),
532 parent
533 );
534
535 auto &window = *pWindow;
536 // wxGTK3 seems to need to require creating the window using default position
537 // and then manually positioning it.
538 window.SetPosition(wndRect.GetPosition());
539
540 if(bMaximized) {
541 window.Maximize(true);
542 }
543 else if (bIconized) {
544 // if the user close down and iconized state we could start back up and iconized state
545 // window.Iconize(TRUE);
546 }
547
548 return pWindow;
549 }
550};
551
552}
553
555{
557}
558
560{
561 return Get( const_cast< AudacityProject & >( project ) );
562}
563
565{
566 return pProject
568 : nullptr;
569}
570
572{
573 return Find( const_cast< AudacityProject * >( pProject ) );
574}
575
577{
578 auto& project = context.project;
579 auto& window = ProjectWindow::Get(project);
580
581 window.Reset();
582}
583
585{
586 return mNextWindowID++;
587}
588
589enum {
590 FirstID = 1000,
591
592 // Window controls
593
596
598};
599
600ProjectWindow::ProjectWindow(wxWindow * parent, wxWindowID id,
601 const wxPoint & pos,
602 const wxSize & size, AudacityProject &project)
603 : ProjectWindowBase{ parent, id, pos, size, project }
604{
606
607 // Two sub-windows need to be made before Init(),
608 // so that this constructor can complete, and then TrackPanel and
609 // AdornedRulerPanel can retrieve those windows from this in their
610 // factory functions
611
612 // PRL: this panel groups the top tool dock and the ruler into one
613 // tab cycle.
614 // Must create it with non-default width equal to the main window width,
615 // or else the device toolbar doesn't make initial widths of the choice
616 // controls correct.
618 this, wxID_ANY, wxDefaultPosition,
619 wxSize{ this->GetSize().GetWidth(), -1 }
620 };
621 mTopPanel->SetLabel( "Top Panel" );// Not localised
622 mTopPanel->SetLayoutDirection(wxLayout_LeftToRight);
623 mTopPanel->SetAutoLayout(true);
624#ifdef EXPERIMENTAL_DA2
625 mTopPanel->SetBackgroundColour(theTheme.Colour( clrMedium ));
626#endif
627
628 auto container = safenew wxSplitterWindow(this, wxID_ANY,
629 wxDefaultPosition,
630 wxDefaultSize,
631 wxNO_BORDER | wxSP_LIVE_UPDATE | wxSP_THIN_SASH);
632 container->Bind(wxEVT_SPLITTER_DOUBLECLICKED, [](wxSplitterEvent& event){
633 //"The default behaviour is to unsplit the window"
634 event.Veto();//do noting instead
635 });
636 mContainerWindow = container;
637
639 wxDefaultPosition,
640 wxDefaultSize,
641 wxNO_BORDER);
642 mTrackListWindow->SetSizer( safenew wxBoxSizer(wxVERTICAL) );
643 mTrackListWindow->SetLabel("Main Panel");// Not localized.
644 mTrackListWindow->SetLayoutDirection(wxLayout_LeftToRight);
645
647
649 effectsPanel->SetName(_("Realtime effects"));
650 effectsPanel->SetBackgroundColorIndex(clrMedium);
651 effectsPanel->Hide();//initially hidden
652 effectsPanel->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent&)
653 {
655 });
656 mEffectsWindow = effectsPanel;
657
658#ifdef EXPERIMENTAL_DA2
659 mTrackListWindow->SetBackgroundColour(theTheme.Colour( clrMedium ));
660#endif
661
662 mPlaybackScroller = std::make_unique<PlaybackScroller>( &project );
663
664 // PRL: Old comments below. No longer observing the ordering that it
665 // recommends. ProjectWindow::OnActivate puts the focus directly into
666 // the TrackPanel, which avoids the problems.
667 // LLL: When Audacity starts or becomes active after returning from
668 // another application, the first window that can accept focus
669 // will be given the focus even if we try to SetFocus(). By
670 // creating the scrollbars after the TrackPanel, we resolve
671 // several focus problems.
672
673 mHsbar = safenew ScrollBar(mTrackListWindow, HSBarID, wxSB_HORIZONTAL);
674 mVsbar = safenew ScrollBar(mTrackListWindow, VSBarID, wxSB_VERTICAL);
675#if wxUSE_ACCESSIBILITY
676 // so that name can be set on a standard control
677 mHsbar->SetAccessible(safenew WindowAccessible(mHsbar));
678 mVsbar->SetAccessible(safenew WindowAccessible(mVsbar));
679#endif
680 mHsbar->SetLayoutDirection(wxLayout_LeftToRight);
681 mHsbar->SetName(_("Horizontal Scrollbar"));
682 mVsbar->SetName(_("Vertical Scrollbar"));
683
685 .Subscribe([this](UndoRedoMessage message){
686 switch (message.type) {
687 case UndoRedoMessage::Pushed:
688 case UndoRedoMessage::Modified:
689 return OnUndoPushedModified();
690 case UndoRedoMessage::UndoOrRedo:
691 return OnUndoRedo();
692 case UndoRedoMessage::Reset:
693 return OnUndoReset();
694 default:
695 return;
696 }
697 });
698
701
703 .Subscribe([this](const TrackFocusChangeMessage& msg) {
704 if(mEffectsWindow->IsShown())
705 {
706 auto& project = GetProject();
707 auto& trackFocus = TrackFocus::Get(project);
708 ShowEffectsPanel(trackFocus.Get(), false);
709 }
710 });
711
712}
713
715{
716 // Tool manager gives us capture sometimes
717 if(HasCapture())
718 ReleaseMouse();
719}
720
721BEGIN_EVENT_TABLE(ProjectWindow, wxFrame)
723 EVT_MOUSE_EVENTS(ProjectWindow::OnMouseEvent)
725 EVT_SIZE(ProjectWindow::OnSize)
726 EVT_SHOW(ProjectWindow::OnShow)
727 EVT_ICONIZE(ProjectWindow::OnIconize)
728 EVT_MOVE(ProjectWindow::OnMove)
729 EVT_ACTIVATE(ProjectWindow::OnActivate)
730 EVT_COMMAND_SCROLL_LINEUP(HSBarID, ProjectWindow::OnScrollLeftButton)
731 EVT_COMMAND_SCROLL_LINEDOWN(HSBarID, ProjectWindow::OnScrollRightButton)
732 EVT_COMMAND_SCROLL(HSBarID, ProjectWindow::OnScroll)
733 EVT_COMMAND_SCROLL(VSBarID, ProjectWindow::OnScroll)
734 // Fires for menu with ID #1...first menu defined
735 EVT_UPDATE_UI(1, ProjectWindow::OnUpdateUI)
736 EVT_COMMAND(wxID_ANY, EVT_TOOLBAR_UPDATED, ProjectWindow::OnToolBarUpdate)
737 //mchinen:multithreaded calls - may not be threadsafe with CommandEvent: may have to change.
739
740void ProjectWindow::ApplyUpdatedTheme()
741{
742 auto &project = mProject;
743 SetBackgroundColour(theTheme.Colour( clrMedium ));
744 ClearBackground();// For wxGTK.
745}
746
747void ProjectWindow::RedrawProject(const bool bForceWaveTracks /*= false*/)
748{
749 auto pThis = wxWeakRef<ProjectWindow>(this);
750 CallAfter( [pThis, bForceWaveTracks]{
751
752 if (!pThis)
753 return;
754 if (pThis->IsBeingDeleted())
755 return;
756
757 auto &project = pThis->mProject ;
758 auto &tracks = TrackList::Get( project );
759 auto &trackPanel = GetProjectPanel( project );
760 pThis->FixScrollbars();
761 if (bForceWaveTracks)
762 {
763 for ( auto pWaveTrack : tracks.Any< WaveTrack >() )
764 for (const auto &clip: pWaveTrack->GetClips())
765 clip->MarkChanged();
766 }
767 trackPanel.Refresh(false);
768
769 });
770}
771
773{
774 if (message.appearance)
775 return;
776 auto &project = mProject;
777 this->ApplyUpdatedTheme();
778 auto &toolManager = ToolManager::Get( project );
779 for( int ii = 0; ii < ToolBarCount; ++ii )
780 {
781 ToolBar *pToolBar = toolManager.GetToolBar(ii);
782 if( pToolBar )
783 pToolBar->ReCreateButtons();
784 }
785}
786
788{
789 // Update status bar widths in case of language change
791}
792
794{
795 // Set a flag so we don't have to generate two update events
796 mAutoScrolling = true;
797
798 // Call our Scroll method which updates our ViewInfo variables
799 // to reflect the positions of the scrollbars
800 DoScroll();
801
802 mAutoScrolling = false;
803}
804
805#if defined(__WXMAC__)
806// const int sbarSpaceWidth = 15;
807// const int sbarControlWidth = 16;
808// const int sbarExtraLen = 1;
809const int sbarHjump = 30; //STM: This is how far the thumb jumps when the l/r buttons are pressed, or auto-scrolling occurs -- in pixels
810#elif defined(__WXMSW__)
811const int sbarSpaceWidth = 16;
812const int sbarControlWidth = 16;
813const int sbarExtraLen = 0;
814const int sbarHjump = 30; //STM: This is how far the thumb jumps when the l/r buttons are pressed, or auto-scrolling occurs -- in pixels
815#else // wxGTK, wxMOTIF, wxX11
816const int sbarSpaceWidth = 15;
817const int sbarControlWidth = 15;
818const int sbarExtraLen = 0;
819const int sbarHjump = 30; //STM: This is how far the thumb jumps when the l/r buttons are pressed, or auto-scrolling occurs -- in pixels
820#include "AllThemeResources.h"
821#endif
822
823// Make sure selection edge is in view
825{
826 auto &trackPanel = GetProjectPanel( mProject );
827 auto &viewInfo = ViewInfo::Get( mProject );
828 auto w = viewInfo.GetTracksUsableWidth();
829
830 int pixel = viewInfo.TimeToPosition(pos);
831 if (pixel < 0 || pixel >= w)
832 {
834 (viewInfo.OffsetTimeByPixels(pos, -(w / 2)));
835 trackPanel.Refresh(false);
836 }
837}
838
840{
841 auto &viewInfo = ViewInfo::Get( mProject );
842 ScrollIntoView(viewInfo.PositionToTime(x, viewInfo.GetLeftOffset()));
843}
844
850{
851 auto &project = mProject;
852 auto &viewInfo = ViewInfo::Get( project );
853 wxInt64 pos = mHsbar->GetThumbPosition();
854 // move at least one scroll increment
855 pos -= wxMax((wxInt64)(sbarHjump * viewInfo.sbarScale), 1);
856 pos = wxMax(pos, 0);
857 viewInfo.sbarH -= sbarHjump;
858 viewInfo.sbarH = std::max(viewInfo.sbarH,
859 -(wxInt64) PixelWidthBeforeTime(0.0));
860
861
862 if (pos != mHsbar->GetThumbPosition()) {
863 mHsbar->SetThumbPosition((int)pos);
865 }
866}
871
873{
874 auto &project = mProject;
875 auto &viewInfo = ViewInfo::Get( project );
876 wxInt64 pos = mHsbar->GetThumbPosition();
877 // move at least one scroll increment
878 // use wxInt64 for calculation to prevent temporary overflow
879 pos += wxMax((wxInt64)(sbarHjump * viewInfo.sbarScale), 1);
880 wxInt64 max = mHsbar->GetRange() - mHsbar->GetThumbSize();
881 pos = wxMin(pos, max);
882 viewInfo.sbarH += sbarHjump;
883 viewInfo.sbarH = std::min(viewInfo.sbarH,
884 viewInfo.sbarTotal
885 - (wxInt64) PixelWidthBeforeTime(0.0) - viewInfo.sbarScreen);
886
887 if (pos != mHsbar->GetThumbPosition()) {
888 mHsbar->SetThumbPosition((int)pos);
890 }
891}
892
893
897void ProjectWindow::OnScrollLeftButton(wxScrollEvent & /*event*/)
898{
899 auto &project = mProject;
900 auto &viewInfo = ViewInfo::Get( project );
901 wxInt64 pos = mHsbar->GetThumbPosition();
902 // move at least one scroll increment
903 pos -= wxMax((wxInt64)(sbarHjump * viewInfo.sbarScale), 1);
904 pos = wxMax(pos, 0);
905 viewInfo.sbarH -= sbarHjump;
906 viewInfo.sbarH = std::max(viewInfo.sbarH,
907 - (wxInt64) PixelWidthBeforeTime(0.0));
908
909 if (pos != mHsbar->GetThumbPosition()) {
910 mHsbar->SetThumbPosition((int)pos);
911 DoScroll();
912 }
913}
914
918void ProjectWindow::OnScrollRightButton(wxScrollEvent & /*event*/)
919{
920 auto &project = mProject;
921 auto &viewInfo = ViewInfo::Get( project );
922 wxInt64 pos = mHsbar->GetThumbPosition();
923 // move at least one scroll increment
924 // use wxInt64 for calculation to prevent temporary overflow
925 pos += wxMax((wxInt64)(sbarHjump * viewInfo.sbarScale), 1);
926 wxInt64 max = mHsbar->GetRange() - mHsbar->GetThumbSize();
927 pos = wxMin(pos, max);
928 viewInfo.sbarH += sbarHjump;
929 viewInfo.sbarH = std::min(viewInfo.sbarH,
930 viewInfo.sbarTotal
931 - (wxInt64) PixelWidthBeforeTime(0.0) - viewInfo.sbarScreen);
932
933 if (pos != mHsbar->GetThumbPosition()) {
934 mHsbar->SetThumbPosition((int)pos);
935 DoScroll();
936 }
937}
938
939
941{
942 auto &project = mProject;
943 auto &scrubber = Scrubber::Get( project );
944 auto &viewInfo = ViewInfo::Get( project );
945 if (viewInfo.bScrollBeyondZero)
946 return true;
947
948 if (scrubber.HasMark() ||
949 ProjectAudioIO::Get( project ).IsAudioActive()) {
950 if (mPlaybackScroller) {
951 auto mode = mPlaybackScroller->GetMode();
952 if (mode == PlaybackScroller::Mode::Pinned ||
954 return true;
955 }
956 }
957
958 return false;
959}
960
962{
963 auto &project = mProject;
964 auto &tracks = TrackList::Get( project );
965 auto &viewInfo = ViewInfo::Get( project );
966 if (!MayScrollBeyondZero())
967 return 0;
968 const double screen = viewInfo.GetScreenEndTime() - viewInfo.h;
969 return std::min(tracks.GetStartTime(), -screen);
970}
971
972// PRL: Bug1197: we seem to need to compute all in double, to avoid differing results on Mac
973// That's why ViewInfo::TimeRangeToPixelWidth was defined, with some regret.
974double ProjectWindow::PixelWidthBeforeTime(double scrollto) const
975{
976 auto &project = mProject;
977 auto &viewInfo = ViewInfo::Get( project );
978 const double lowerBound = ScrollingLowerBoundTime();
979 return
980 // Ignoring fisheye is correct here
981 viewInfo.TimeRangeToPixelWidth(scrollto - lowerBound);
982}
983
985{
986 auto &project = mProject;
987 auto &viewInfo = ViewInfo::Get( project );
988 const auto unscaled = PixelWidthBeforeTime(scrollto);
989 const int max = mHsbar->GetRange() - mHsbar->GetThumbSize();
990 const int pos =
991 std::min(max,
992 std::max(0,
993 (int)(floor(0.5 + unscaled * viewInfo.sbarScale))));
994 mHsbar->SetThumbPosition(pos);
995 viewInfo.sbarH = floor(0.5 + unscaled - PixelWidthBeforeTime(0.0));
996 viewInfo.sbarH = std::max(viewInfo.sbarH,
997 - (wxInt64) PixelWidthBeforeTime(0.0));
998 viewInfo.sbarH = std::min(viewInfo.sbarH,
999 viewInfo.sbarTotal
1000 - (wxInt64) PixelWidthBeforeTime(0.0) - viewInfo.sbarScreen);
1001}
1002
1003//
1004// This method, like the other methods prefaced with TP, handles TrackPanel
1005// 'callback'.
1006//
1008{
1009 SetHorizontalThumb(scrollto);
1010
1011 // Call our Scroll method which updates our ViewInfo variables
1012 // to reflect the positions of the scrollbars
1013 DoScroll();
1014}
1015
1016//
1017// Scroll vertically. This is called for example by the mouse wheel
1018// handler in Track Panel. A positive argument makes the window
1019// scroll down, while a negative argument scrolls up.
1020//
1022{
1023 int oldPos = mVsbar->GetThumbPosition();
1024 int pos = oldPos + delta;
1025 int max = mVsbar->GetRange() - mVsbar->GetThumbSize();
1026
1027 // Can be negative in case of only one track
1028 if (max < 0)
1029 max = 0;
1030
1031 if (pos > max)
1032 pos = max;
1033 else if (pos < 0)
1034 pos = 0;
1035
1036 if (pos != oldPos)
1037 {
1038 mVsbar->SetThumbPosition(pos);
1039
1040 DoScroll();
1041 return true;
1042 }
1043 else
1044 return false;
1045}
1046
1048{
1049 auto &project = mProject;
1050 auto &tracks = TrackList::Get( project );
1051 auto &trackPanel = GetProjectPanel( project );
1052 auto &viewInfo = ViewInfo::Get( project );
1053
1054 bool refresh = false;
1055 bool rescroll = false;
1056
1057 int totalHeight = TrackView::GetTotalHeight( tracks ) + 32;
1058
1059 auto panelWidth = viewInfo.GetTracksUsableWidth();
1060 auto panelHeight = viewInfo.GetHeight();
1061
1062 // (From Debian...at least I think this is the change corresponding
1063 // to this comment)
1064 //
1065 // (2.) GTK critical warning "IA__gtk_range_set_range: assertion
1066 // 'min < max' failed" because of negative numbers as result of window
1067 // size checking. Added a sanity check that straightens up the numbers
1068 // in edge cases.
1069 if (panelWidth < 0) {
1070 panelWidth = 0;
1071 }
1072 if (panelHeight < 0) {
1073 panelHeight = 0;
1074 }
1075
1076 auto LastTime = std::numeric_limits<double>::lowest();
1077 for (const Track *track : tracks) {
1078 // Iterate over pending changed tracks if present.
1079 track = track->SubstitutePendingChangedTrack().get();
1080 LastTime = std::max( LastTime, track->GetEndTime() );
1081 }
1082 LastTime =
1083 std::max(LastTime, viewInfo.selectedRegion.t1());
1084
1085 const double screen =
1086 viewInfo.GetScreenEndTime() - viewInfo.h;
1087 const double halfScreen = screen / 2.0;
1088
1089 // If we can scroll beyond zero,
1090 // Add 1/2 of a screen of blank space to the end
1091 // and another 1/2 screen before the beginning
1092 // so that any point within the union of the selection and the track duration
1093 // may be scrolled to the midline.
1094 // May add even more to the end, so that you can always scroll the starting time to zero.
1095 const double lowerBound = ScrollingLowerBoundTime();
1096 const double additional = MayScrollBeyondZero()
1097 ? -lowerBound + std::max(halfScreen, screen - LastTime)
1098 : screen / 4.0;
1099
1100 viewInfo.total = LastTime + additional;
1101
1102 // Don't remove time from total that's still on the screen
1103 viewInfo.total = std::max(viewInfo.total, viewInfo.h + screen);
1104
1105 if (viewInfo.h < lowerBound) {
1106 viewInfo.h = lowerBound;
1107 rescroll = true;
1108 }
1109
1110 viewInfo.sbarTotal = (wxInt64) (viewInfo.GetTotalWidth());
1111 viewInfo.sbarScreen = (wxInt64)(panelWidth);
1112 viewInfo.sbarH = (wxInt64) (viewInfo.GetBeforeScreenWidth());
1113
1114 // PRL: Can someone else find a more elegant solution to bug 812, than
1115 // introducing this boolean member variable?
1116 // Setting mVSbar earlier, int HandlXMLTag, didn't succeed in restoring
1117 // the vertical scrollbar to its saved position. So defer that till now.
1118 // mbInitializingScrollbar should be true only at the start of the life
1119 // of an AudacityProject reopened from disk.
1121 viewInfo.vpos = mVsbar->GetThumbPosition() * viewInfo.scrollStep;
1122 }
1124
1125 if (viewInfo.vpos >= totalHeight)
1126 viewInfo.vpos = totalHeight - 1;
1127 if (viewInfo.vpos < 0)
1128 viewInfo.vpos = 0;
1129
1130 bool oldhstate;
1131 bool oldvstate;
1132 bool newhstate =
1133 (viewInfo.GetScreenEndTime() - viewInfo.h) < viewInfo.total;
1134 bool newvstate = panelHeight < totalHeight;
1135
1136#ifdef __WXGTK__
1137 oldhstate = mHsbar->IsShown();
1138 oldvstate = mVsbar->IsShown();
1139 mHsbar->Show(newhstate);
1140 mVsbar->Show(panelHeight < totalHeight);
1141#else
1142 oldhstate = mHsbar->IsEnabled();
1143 oldvstate = mVsbar->IsEnabled();
1144 mHsbar->Enable(newhstate);
1145 mVsbar->Enable(panelHeight < totalHeight);
1146#endif
1147
1148 if (panelHeight >= totalHeight && viewInfo.vpos != 0) {
1149 viewInfo.vpos = 0;
1150
1151 refresh = true;
1152 rescroll = false;
1153 }
1154 if (!newhstate && viewInfo.sbarH != 0) {
1155 viewInfo.sbarH = 0;
1156
1157 refresh = true;
1158 rescroll = false;
1159 }
1160
1161 // wxScrollbar only supports int values but we need a greater range, so
1162 // we scale the scrollbar coordinates on demand. We only do this if we
1163 // would exceed the int range, so we can always use the maximum resolution
1164 // available.
1165
1166 // Don't use the full 2^31 max int range but a bit less, so rounding
1167 // errors in calculations do not overflow max int
1168 wxInt64 maxScrollbarRange = (wxInt64)(2147483647 * 0.999);
1169 if (viewInfo.sbarTotal > maxScrollbarRange)
1170 viewInfo.sbarScale = ((double)maxScrollbarRange) / viewInfo.sbarTotal;
1171 else
1172 viewInfo.sbarScale = 1.0; // use maximum resolution
1173
1174 {
1175 int scaledSbarH = (int)(viewInfo.sbarH * viewInfo.sbarScale);
1176 int scaledSbarScreen = (int)(viewInfo.sbarScreen * viewInfo.sbarScale);
1177 int scaledSbarTotal = (int)(viewInfo.sbarTotal * viewInfo.sbarScale);
1178 const int offset =
1179 (int)(floor(0.5 + viewInfo.sbarScale * PixelWidthBeforeTime(0.0)));
1180
1181 mHsbar->SetScrollbar(scaledSbarH + offset, scaledSbarScreen, scaledSbarTotal,
1182 scaledSbarScreen, TRUE);
1183 }
1184
1185 // Vertical scrollbar
1186 mVsbar->SetScrollbar(viewInfo.vpos / viewInfo.scrollStep,
1187 panelHeight / viewInfo.scrollStep,
1188 totalHeight / viewInfo.scrollStep,
1189 panelHeight / viewInfo.scrollStep, TRUE);
1190
1191 if (refresh || (rescroll &&
1192 (viewInfo.GetScreenEndTime() - viewInfo.h) < viewInfo.total)) {
1193 trackPanel.Refresh(false);
1194 }
1195
1196 MenuManager::Get( project ).UpdateMenus();
1197
1198 if (oldhstate != newhstate || oldvstate != newvstate) {
1199 UpdateLayout();
1200 }
1201}
1202
1204{
1205 auto &project = mProject;
1206 auto &trackPanel = GetProjectPanel( project );
1207 auto &toolManager = ToolManager::Get( project );
1208
1209 // 1. Layout panel, to get widths of the docks.
1210 Layout();
1211 // 2. Layout toolbars to pack the toolbars correctly in docks which
1212 // are now the correct width.
1213 toolManager.LayoutToolBars();
1214 // 3. Layout panel, to resize docks, in particular reducing the height
1215 // of any empty docks, or increasing the height of docks that need it.
1216 Layout();
1217
1218 // Bug 2455
1219 // The commented out code below is to calculate a nice minimum size for
1220 // the window. However on Ubuntu when the window is minimised it leads to
1221 // an insanely tall window.
1222 // Using a fixed min size fixes that.
1223 // However there is still something strange when minimised, as once
1224 // UpdateLayout is called once, when minimised, it gets called repeatedly.
1225#if 0
1226 // Retrieve size of this projects window
1227 wxSize mainsz = GetSize();
1228
1229 // Retrieve position of the track panel to use as the size of the top
1230 // third of the window
1231 wxPoint tppos = ClientToScreen(trackPanel.GetParent()->GetPosition());
1232
1233 // Retrieve position of bottom dock to use as the size of the bottom
1234 // third of the window
1235 wxPoint sbpos = ClientToScreen(toolManager.GetBotDock()->GetPosition());
1236
1237 // The "+ 50" is the minimum height of the TrackPanel
1238 SetMinSize( wxSize(250, (mainsz.y - sbpos.y) + tppos.y + 50));
1239#endif
1240 SetMinSize( wxSize(250, 250));
1241 SetMaxSize( wxSize(20000, 20000));
1242}
1243
1245{
1246 // Activate events can fire during window teardown, so just
1247 // ignore them.
1248 if (mIsDeleting) {
1249 return;
1250 }
1251
1252 CallAfter( [this]{
1253
1254 if (mIsDeleting)
1255 return;
1256
1257 FixScrollbars();
1258 UpdateLayout();
1259
1260 });
1261}
1262
1263
1265{
1266 return mIconized;
1267}
1268
1270{
1271 return mEffectsWindow;
1272}
1273
1275{
1276 return mTrackListWindow;
1277}
1278
1280{
1281 return mContainerWindow;
1282}
1283
1285{
1286 return mTopPanel;
1287}
1288
1290{
1291 wxRect defaultRect;
1292 GetDefaultWindowRect(&defaultRect);
1293
1294 SetSize(defaultRect.width, defaultRect.height);
1295}
1296
1298{
1299 enum { nWidths = nStatusBarFields + 1 };
1300 int widths[ nWidths ]{ 0 };
1301 widths[ rateStatusBarField ] = 150;
1302 const auto statusBar = GetStatusBar();
1303 const auto &functions = ProjectStatus::GetStatusWidthFunctions();
1304 // Start from 1 not 0
1305 // Specifying a first column always of width 0 was needed for reasons
1306 // I forget now
1307 for ( int ii = 1; ii <= nStatusBarFields; ++ii ) {
1308 int &width = widths[ ii ];
1309 for ( const auto &function : functions ) {
1310 auto results =
1311 function( mProject, static_cast< StatusBarField >( ii ) );
1312 for ( const auto &string : results.first ) {
1313 int w;
1314 statusBar->GetTextExtent(string.Translation(), &w, nullptr);
1315 width = std::max<int>( width, w + results.second );
1316 }
1317 }
1318 }
1319 // The main status field is not fixed width
1320 widths[ mainStatusBarField ] = -1;
1321 statusBar->SetStatusWidths( nWidths, widths );
1322}
1323
1325{
1326 (void)show;//compiler food
1327#ifdef __WXMAC__
1328 // Save the focus so we can restore it to whatever had it before since
1329 // showing a previously hidden toolbar will cause the focus to be set to
1330 // its frame. If this is not done it will appear that activation events
1331 // aren't being sent to the project window since they are actually being
1332 // delivered to the last tool frame shown.
1333 wxWindow *focused = FindFocus();
1334
1335 // Find all the floating toolbars, and show or hide them
1336 const auto &children = GetChildren();
1337 for(const auto &child : children) {
1338 if (auto frame = dynamic_cast<ToolFrame*>(child)) {
1339 if (!show) {
1340 frame->Hide();
1341 }
1342 else if (frame->GetBar() &&
1343 frame->GetBar()->IsVisible() ) {
1344 frame->Show();
1345 }
1346 }
1347 }
1348
1349 // Restore the focus if needed
1350 if (focused) {
1351 focused->SetFocus();
1352 }
1353#endif
1354}
1355
1356void ProjectWindow::OnIconize(wxIconizeEvent &event)
1357{
1358 //JKC: On Iconizing we get called twice. Don't know
1359 // why but it does no harm.
1360 // Should we be returning true/false rather than
1361 // void return? I don't know.
1362 mIconized = event.IsIconized();
1363
1364#if defined(__WXMAC__)
1365 // Readdresses bug 1431 since a crash could occur when restoring iconized
1366 // floating toolbars due to recursion (bug 2411).
1368 if( !mIconized )
1369 {
1370 Raise();
1371 }
1372#endif
1373
1374 // VisibileProjectCount seems to be just a counter for debugging.
1375 // It's not used outside this function.
1376 auto VisibleProjectCount = std::count_if(
1378 []( const AllProjects::value_type &ptr ){
1379 return !GetProjectFrame( *ptr ).IsIconized();
1380 }
1381 );
1382 event.Skip();
1383
1384 // This step is to fix part of Bug 2040, where the BackingPanel
1385 // size was not restored after we leave Iconized state.
1386
1387 // Queue up a resize event using OnShow so that we
1388 // refresh the track panel. But skip this, if we're iconized.
1389 if( mIconized )
1390 return;
1391 wxShowEvent Evt;
1392 OnShow( Evt );
1393}
1394
1395void ProjectWindow::OnMove(wxMoveEvent & event)
1396{
1397 if (!this->IsMaximized() && !this->IsIconized())
1398 SetNormalizedWindowState(this->GetRect());
1399 event.Skip();
1400}
1401
1402void ProjectWindow::OnSize(wxSizeEvent & event)
1403{
1404 // (From Debian)
1405 //
1406 // (3.) GTK critical warning "IA__gdk_window_get_origin: assertion
1407 // 'GDK_IS_WINDOW (window)' failed": Received events of type wxSizeEvent
1408 // on the main project window cause calls to "ClientToScreen" - which is
1409 // not available until the window is first shown. So the class has to
1410 // keep track of wxShowEvent events and inhibit those actions until the
1411 // window is first shown.
1412 if (mShownOnce) {
1413 HandleResize();
1414 if (!this->IsMaximized() && !this->IsIconized())
1415 SetNormalizedWindowState(this->GetRect());
1416 }
1417 event.Skip();
1418}
1419
1420void ProjectWindow::OnShow(wxShowEvent & event)
1421{
1422 // Remember that the window has been shown at least once
1423 mShownOnce = true;
1424
1425 // (From Debian...see also TrackPanel::OnTimer and AudacityTimer::Notify)
1426 //
1427 // Description: Workaround for wxWidgets bug: Reentry in clipboard
1428 // The wxWidgets bug http://trac.wxwidgets.org/ticket/16636 prevents
1429 // us from doing clipboard operations in wxShowEvent and wxTimerEvent
1430 // processing because those event could possibly be processed during
1431 // the (not sufficiently protected) Yield() of a first clipboard
1432 // operation, causing reentry. Audacity had a workaround in place
1433 // for this problem (the class "CaptureEvents"), which however isn't
1434 // applicable with wxWidgets 3.0 because it's based on changing the
1435 // gdk event handler, a change that would be overridden by wxWidgets's
1436 // own gdk event handler change.
1437 // Instead, as a NEW workaround, specifically protect those processings
1438 // of wxShowEvent and wxTimerEvent that try to do clipboard operations
1439 // from being executed within Yield(). This is done by delaying their
1440 // execution by posting pure wxWidgets events - which are never executed
1441 // during Yield().
1442 // Author: Martin Stegh fer <[email protected]>
1443 // Bug-Debian: https://bugs.debian.org/765341
1444
1445 // the actual creation/showing of the window).
1446 // Post the event instead of calling OnSize(..) directly. This ensures that
1447 // this is a pure wxWidgets event (no GDK event behind it) and that it
1448 // therefore isn't processed within the YieldFor(..) of the clipboard
1449 // operations (workaround for Debian bug #765341).
1450 // QueueEvent() will take ownership of the event
1451 GetEventHandler()->QueueEvent(safenew wxSizeEvent(GetSize()));
1452
1453 // Further processing by default handlers
1454 event.Skip();
1455}
1456
1460void ProjectWindow::OnToolBarUpdate(wxCommandEvent & event)
1461{
1462 HandleResize();
1463
1464 event.Skip(false); /* No need to propagate any further */
1465}
1466
1468{
1469 RedrawProject();
1470}
1471
1473{
1474 HandleResize();
1475 RedrawProject();
1476}
1477
1479{
1480 HandleResize();
1481 // RedrawProject(); // Should we do this here too?
1482}
1483
1484void ProjectWindow::OnScroll(wxScrollEvent & WXUNUSED(event))
1485{
1486 auto &project = mProject;
1487 auto &viewInfo = ViewInfo::Get( project );
1488 const wxInt64 offset = PixelWidthBeforeTime(0.0);
1489 viewInfo.sbarH =
1490 (wxInt64)(mHsbar->GetThumbPosition() / viewInfo.sbarScale) - offset;
1491 DoScroll();
1492
1493#ifndef __WXMAC__
1494 // Bug2179
1495 // This keeps the time ruler in sync with horizontal scrolling, without
1496 // making an undesirable compilation dependency of this source file on
1497 // the ruler
1498 wxTheApp->ProcessIdle();
1499#endif
1500}
1501
1503{
1504 auto &project = mProject;
1505 auto &trackPanel = GetProjectPanel( project );
1506 auto &viewInfo = ViewInfo::Get( project );
1507 const double lowerBound = ScrollingLowerBoundTime();
1508
1509 auto width = viewInfo.GetTracksUsableWidth();
1510 viewInfo.SetBeforeScreenWidth(viewInfo.sbarH, width, lowerBound);
1511
1512
1513 if (MayScrollBeyondZero()) {
1514 enum { SCROLL_PIXEL_TOLERANCE = 10 };
1515 if (std::abs(viewInfo.TimeToPosition(0.0, 0
1516 )) < SCROLL_PIXEL_TOLERANCE) {
1517 // Snap the scrollbar to 0
1518 viewInfo.h = 0;
1519 SetHorizontalThumb(0.0);
1520 }
1521 }
1522
1523 viewInfo.vpos = mVsbar->GetThumbPosition() * viewInfo.scrollStep;
1524
1525 //mchinen: do not always set this project to be the active one.
1526 //a project may autoscroll while playing in the background
1527 //I think this is okay since OnMouseEvent has one of these.
1528 //SetActiveProject(this);
1529
1530 if (!mAutoScrolling) {
1531 trackPanel.Refresh(false);
1532 }
1533}
1534
1535void ProjectWindow::OnMenu(wxCommandEvent & event)
1536{
1537#ifdef __WXMSW__
1538 // Bug 1642: We can arrive here with bogus menu IDs, which we
1539 // proceed to process. So if bogus, don't.
1540 // The bogus menu IDs are probably generated by controls on the TrackPanel,
1541 // such as the Project Rate.
1542 // 17000 is the magic number at which we start our menu.
1543 // This code would probably NOT be OK on Mac, since we assign
1544 // some specific ID numbers.
1545 if( event.GetId() < 17000){
1546 event.Skip();
1547 return;
1548 }
1549#endif
1550 auto &project = mProject;
1551 auto &commandManager = CommandManager::Get( project );
1552 bool handled = commandManager.HandleMenuID( GetProject(),
1553 event.GetId(), MenuManager::Get( project ).GetUpdateFlags(),
1554 false);
1555
1556 if (handled)
1557 event.Skip(false);
1558 else{
1559 event.ResumePropagation( 999 );
1560 event.Skip(true);
1561 }
1562}
1563
1564void ProjectWindow::OnUpdateUI(wxUpdateUIEvent & WXUNUSED(event))
1565{
1566 auto &project = mProject;
1567 MenuManager::Get( project ).UpdateMenus();
1568}
1569
1570void ProjectWindow::OnActivate(wxActivateEvent & event)
1571{
1572 // Activate events can fire during window teardown, so just
1573 // ignore them.
1574 if (IsBeingDeleted()) {
1575 return;
1576 }
1577
1578 auto &project = mProject;
1579
1580 mActive = event.GetActive();
1581
1582 // Under Windows, focus can be "lost" when returning to
1583 // Audacity from a different application.
1584 //
1585 // This was observed by minimizing all windows using WINDOWS+M and
1586 // then ALT+TAB to return to Audacity. Focus will be given to the
1587 // project window frame which is not at all useful.
1588 //
1589 // So, we use ToolManager's observation of focus changes in a wxEventFilter.
1590 // Then, when we receive the
1591 // activate event, we restore that focus to the child or the track
1592 // panel if no child had the focus (which probably should never happen).
1593 if (mActive) {
1594 auto &toolManager = ToolManager::Get( project );
1595 SetActiveProject( &project );
1596 if ( ! toolManager.RestoreFocus() )
1597 GetProjectPanel( project ).SetFocus();
1598 }
1599 event.Skip();
1600}
1601
1603{
1604 return mActive;
1605}
1606
1607void ProjectWindow::OnMouseEvent(wxMouseEvent & event)
1608{
1609 auto &project = mProject;
1610 if (event.ButtonDown())
1611 SetActiveProject( &project );
1612}
1613
1615{
1616 auto &project = mProject;
1617 auto &tracks = TrackList::Get( project );
1618 auto &trackPanel = GetProjectPanel( project );
1619
1620 DoZoomFit();
1621
1622 trackPanel.SetFocus();
1623 if (!pTrack)
1624 pTrack = *tracks.Selected().begin();
1625 if (!pTrack)
1626 pTrack = *tracks.Any().begin();
1627 if (pTrack) {
1628 TrackFocus::Get(project).Set(pTrack);
1629 pTrack->EnsureVisible();
1630 }
1631}
1632
1633// Utility function called by other zoom methods
1634void ProjectWindow::Zoom(double level)
1635{
1636 auto &project = mProject;
1637 auto &viewInfo = ViewInfo::Get( project );
1638 viewInfo.SetZoom(level);
1639 FixScrollbars();
1640 // See if we can center the selection on screen, and have it actually fit.
1641 // tOnLeft is the amount of time we would need before the selection left edge to center it.
1642 float t0 = viewInfo.selectedRegion.t0();
1643 float t1 = viewInfo.selectedRegion.t1();
1644 float tAvailable = viewInfo.GetScreenEndTime() - viewInfo.h;
1645 float tOnLeft = (tAvailable - t0 + t1)/2.0;
1646 // Bug 1292 (Enh) is effectively a request to do this scrolling of the selection into view.
1647 // If tOnLeft is positive, then we have room for the selection, so scroll to it.
1648 if( tOnLeft >=0 )
1649 TP_ScrollWindow( t0-tOnLeft);
1650}
1651
1652// Utility function called by other zoom methods
1653void ProjectWindow::ZoomBy(double multiplier)
1654{
1655 auto &project = mProject;
1656 auto &viewInfo = ViewInfo::Get( project );
1657 viewInfo.ZoomBy(multiplier);
1658 FixScrollbars();
1659}
1660
1662// This method 'rewinds' the track, by setting the cursor to 0 and
1663// scrolling the window to fit 0 on the left side of it
1664// (maintaining current zoom).
1665// If shift is held down, it will extend the left edge of the
1666// selection to 0 (holding right edge constant), otherwise it will
1667// move both left and right edge of selection to 0
1670{
1671 auto &project = mProject;
1672 auto &viewInfo = ViewInfo::Get( project );
1673 viewInfo.selectedRegion.setT0(0, false);
1674 if (!shift)
1675 viewInfo.selectedRegion.setT1(0);
1676
1677 TP_ScrollWindow(0);
1678}
1679
1680
1682// This method 'fast-forwards' the track, by setting the cursor to
1683// the end of the samples on the selected track and scrolling the
1684// window to fit the end on its right side (maintaining current zoom).
1685// If shift is held down, it will extend the right edge of the
1686// selection to the end (holding left edge constant), otherwise it will
1687// move both left and right edge of selection to the end
1690{
1691 auto &project = mProject;
1692 auto &tracks = TrackList::Get( project );
1693 auto &viewInfo = ViewInfo::Get( project );
1694 double len = tracks.GetEndTime();
1695
1696 viewInfo.selectedRegion.setT1(len, false);
1697 if (!shift)
1698 viewInfo.selectedRegion.setT0(len);
1699
1700 // Make sure the end of the track is visible
1701 ScrollIntoView(len);
1702}
1703
1704// TrackPanel callback method
1706{
1707 OnScrollLeft();
1708}
1709
1710// TrackPanel callback method
1712{
1713 OnScrollRight();
1714}
1715
1716// TrackPanel callback method
1718{
1719 FixScrollbars();
1720}
1721
1723{
1724 HandleResize();
1725}
1726
1728: mProject(project)
1729{
1730}
1731
1733{
1734 auto gAudioIO = AudioIO::Get();
1735 mRecentStreamTime = gAudioIO->GetStreamTime();
1736
1737 auto cleanup = finally([&]{
1738 // Propagate the message to other listeners bound to this
1739 this->Publish({});
1740 });
1741
1742 if(!ProjectAudioIO::Get( *mProject ).IsAudioActive())
1743 return;
1744 else if (mMode == Mode::Refresh) {
1745 // PRL: see comments in Scrubbing.cpp for why this is sometimes needed.
1746 // These unnecessary refreshes cause wheel rotation events to be delivered more uniformly
1747 // to the application, so scrub speed control is smoother.
1748 // (So I see at least with OS 10.10 and wxWidgets 3.0.2.)
1749 // Is there another way to ensure that than by refreshing?
1750 auto &trackPanel = GetProjectPanel( *mProject );
1751 trackPanel.Refresh(false);
1752 }
1753 else if (mMode != Mode::Off) {
1754 // Pan the view, so that we put the play indicator at some fixed
1755 // fraction of the window width.
1756
1757 auto &viewInfo = ViewInfo::Get( *mProject );
1758 auto &trackPanel = GetProjectPanel( *mProject );
1759 const int posX = viewInfo.TimeToPosition(mRecentStreamTime);
1760 auto width = viewInfo.GetTracksUsableWidth();
1761 int deltaX;
1762 switch (mMode)
1763 {
1764 default:
1765 wxASSERT(false);
1766 /* fallthru */
1767 case Mode::Pinned:
1768 deltaX =
1770 break;
1771 case Mode::Right:
1772 deltaX = posX - width; break;
1773 }
1774 viewInfo.h =
1775 viewInfo.OffsetTimeByPixels(viewInfo.h, deltaX, true);
1776 if (!ProjectWindow::Get( *mProject ).MayScrollBeyondZero())
1777 // Can't scroll too far left
1778 viewInfo.h = std::max(0.0, viewInfo.h);
1779 trackPanel.Refresh(false);
1780 }
1781}
1782
1783void ProjectWindow::ZoomInByFactor( double ZoomFactor )
1784{
1785 auto &project = mProject;
1786 auto &viewInfo = ViewInfo::Get( project );
1787
1788 auto gAudioIO = AudioIO::Get();
1789 // LLL: Handling positioning differently when audio is
1790 // actively playing. Don't do this if paused.
1791 if (gAudioIO->IsStreamActive(
1792 ProjectAudioIO::Get( project ).GetAudioIOToken()) &&
1793 !gAudioIO->IsPaused()){
1794 ZoomBy(ZoomFactor);
1795 ScrollIntoView(gAudioIO->GetStreamTime());
1796 return;
1797 }
1798
1799 // DMM: Here's my attempt to get logical zooming behavior
1800 // when there's a selection that's currently at least
1801 // partially on-screen
1802
1803 const double endTime = viewInfo.GetScreenEndTime();
1804 const double duration = endTime - viewInfo.h;
1805
1806 bool selectionIsOnscreen =
1807 (viewInfo.selectedRegion.t0() < endTime) &&
1808 (viewInfo.selectedRegion.t1() >= viewInfo.h);
1809
1810 bool selectionFillsScreen =
1811 (viewInfo.selectedRegion.t0() < viewInfo.h) &&
1812 (viewInfo.selectedRegion.t1() > endTime);
1813
1814 if (selectionIsOnscreen && !selectionFillsScreen) {
1815 // Start with the center of the selection
1816 double selCenter = (viewInfo.selectedRegion.t0() +
1817 viewInfo.selectedRegion.t1()) / 2;
1818
1819 // If the selection center is off-screen, pick the
1820 // center of the part that is on-screen.
1821 if (selCenter < viewInfo.h)
1822 selCenter = viewInfo.h +
1823 (viewInfo.selectedRegion.t1() - viewInfo.h) / 2;
1824 if (selCenter > endTime)
1825 selCenter = endTime -
1826 (endTime - viewInfo.selectedRegion.t0()) / 2;
1827
1828 // Zoom in
1829 ZoomBy(ZoomFactor);
1830 const double newDuration =
1831 viewInfo.GetScreenEndTime() - viewInfo.h;
1832
1833 // Recenter on selCenter
1834 TP_ScrollWindow(selCenter - newDuration / 2);
1835 return;
1836 }
1837
1838
1839 double origLeft = viewInfo.h;
1840 double origWidth = duration;
1841 ZoomBy(ZoomFactor);
1842
1843 const double newDuration =
1844 viewInfo.GetScreenEndTime() - viewInfo.h;
1845 double newh = origLeft + (origWidth - newDuration) / 2;
1846
1847 // MM: Commented this out because it was confusing users
1848 /*
1849 // make sure that the *right-hand* end of the selection is
1850 // no further *left* than 1/3 of the way across the screen
1851 if (viewInfo.selectedRegion.t1() < newh + viewInfo.screen / 3)
1852 newh = viewInfo.selectedRegion.t1() - viewInfo.screen / 3;
1853
1854 // make sure that the *left-hand* end of the selection is
1855 // no further *right* than 2/3 of the way across the screen
1856 if (viewInfo.selectedRegion.t0() > newh + viewInfo.screen * 2 / 3)
1857 newh = viewInfo.selectedRegion.t0() - viewInfo.screen * 2 / 3;
1858 */
1859
1860 TP_ScrollWindow(newh);
1861}
1862
1863void ProjectWindow::ZoomOutByFactor( double ZoomFactor )
1864{
1865 auto &project = mProject;
1866 auto &viewInfo = ViewInfo::Get( project );
1867
1868 //Zoom() may change these, so record original values:
1869 const double origLeft = viewInfo.h;
1870 const double origWidth = viewInfo.GetScreenEndTime() - origLeft;
1871
1872 ZoomBy(ZoomFactor);
1873 const double newWidth = viewInfo.GetScreenEndTime() - viewInfo.h;
1874
1875 const double newh = origLeft + (origWidth - newWidth) / 2;
1876 // newh = (newh > 0) ? newh : 0;
1877 TP_ScrollWindow(newh);
1878}
1879
1881{
1882 auto &project = mProject;
1883 auto &tracks = TrackList::Get( project );
1884 auto &viewInfo = ViewInfo::Get( project );
1885
1886 const double end = tracks.GetEndTime();
1887 const double start = viewInfo.bScrollBeyondZero
1888 ? std::min( tracks.GetStartTime(), 0.0)
1889 : 0;
1890 const double len = end - start;
1891
1892 if (len <= 0.0)
1893 return viewInfo.GetZoom();
1894
1895 auto w = viewInfo.GetTracksUsableWidth();
1896 w -= 10;
1897 return w/len;
1898}
1899
1901{
1902 auto &project = mProject;
1903 auto &viewInfo = ViewInfo::Get( project );
1904 auto &tracks = TrackList::Get( project );
1905 auto &window = *this;
1906
1907 const double start = viewInfo.bScrollBeyondZero
1908 ? std::min(tracks.GetStartTime(), 0.0)
1909 : 0;
1910
1911 window.Zoom( window.GetZoomOfToFit() );
1912 window.TP_ScrollWindow(start);
1913}
1914
1916{
1917 if(track == nullptr)
1918 {
1920 return;
1921 }
1922
1923 wxWindowUpdateLocker freeze(this);
1924
1925 mEffectsWindow->SetTrack(track->shared_from_this());
1926
1927 if(mContainerWindow->GetWindow1() != mEffectsWindow)
1928 {
1929 //Restore previous effects window size
1930 mContainerWindow->SplitVertically(
1933 mEffectsWindow->GetSize().GetWidth());
1934 }
1935 if(focus)
1937 Layout();
1938}
1939
1941{
1942 wxWindowUpdateLocker freeze(this);
1943
1944 if(mContainerWindow->GetWindow2() == nullptr)
1945 //only effects panel is present, restore split positions before removing effects panel
1946 //Workaround: ::Replace and ::Initialize do not work here...
1948
1950 mTrackListWindow->SetFocus();
1951 Layout();
1952}
1953
1955{
1956 return mEffectsWindow->IsShown();
1957}
1958
1959static ToolManager::TopPanelHook::Scope scope {
1960[]( wxWindow &window ){
1961 auto pProjectWindow = dynamic_cast< ProjectWindow* >( &window );
1962 return pProjectWindow ? pProjectWindow->GetTopPanel() : nullptr;
1963} };
void SetActiveProject(AudacityProject *project)
Handle changing of active project and keep global project pointer.
EVT_MENU(OnSetPlayRegionToSelectionID, AdornedRulerPanel::OnSetPlayRegionToSelection) EVT_COMMAND(OnTogglePinnedStateID
END_EVENT_TABLE()
int min(int a, int b)
#define _(s)
Definition: Internat.h:75
EventMonitor monitor
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:92
#define safenew
Definition: MemoryX.h:10
StatusBarField
Definition: ProjectStatus.h:24
@ mainStatusBarField
Definition: ProjectStatus.h:26
@ nStatusBarFields
Definition: ProjectStatus.h:29
@ rateStatusBarField
Definition: ProjectStatus.h:27
IntSetting ProjectWindowY
bool CornersOnScreen(wxRect &r)
IntSetting ProjectWindowX
void GetDefaultWindowRect(wxRect *defRect)
@ VSBarID
@ HSBarID
@ NextID
@ FirstID
IntSetting ProjectWindowWidth
BoolSetting ProjectWindowIconized
bool IsWindowAccessible(wxRect *requestedRect)
BoolSetting ProjectWindowMaximized
const int sbarHjump
IntSetting ProjectWindowNormalHeight
IntSetting ProjectWindowHeight
IntSetting ProjectWindowNormalX
void GetNextWindowPlacement(wxRect *nextRect, bool *pMaximized, bool *pIconized)
static ToolManager::TopPanelHook::Scope scope
int ScreenContaining(wxRect &r)
IntSetting ProjectWindowNormalWidth
IntSetting ProjectWindowNormalY
AUDACITY_DLL_API wxWindow & GetProjectPanel(AudacityProject &project)
Get the main sub-window of the project frame that displays track data.
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
AUDACITY_DLL_API AttachedWindows & GetAttachedWindows(AudacityProject &project)
accessors for certain important windows associated with each project
for(int ii=0, nn=names.size();ii< nn;++ii)
THEME_API Theme theTheme
Definition: Theme.cpp:82
@ ToolBarCount
Definition: ToolBar.h:90
int id
const_reverse_iterator rend() const
Definition: Project.cpp:37
const_iterator end() const
Definition: Project.cpp:27
Container::value_type value_type
Definition: Project.h:56
const_iterator begin() const
Definition: Project.cpp:22
const_reverse_iterator rbegin() const
Definition: Project.cpp:32
bool empty() const
Definition: Project.h:46
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
static AudioIO * Get()
Definition: AudioIO.cpp:133
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:339
Subclass * Find(const RegisteredFactory &key)
Get a (bare) pointer to an attachment, or null, down-cast it to Subclass *; will not create on demand...
Definition: ClientData.h:333
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:309
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
AudacityProject & project
static CommandManager & Get(AudacityProject &project)
Specialization of Setting for int.
Definition: Prefs.h:349
static MenuManager & Get(AudacityProject &project)
Definition: Menus.cpp:71
void UpdateMenus(bool checkActive=true)
Definition: Menus.cpp:643
CommandFlag GetUpdateFlags(bool checkActive=false) const
Definition: Menus.cpp:553
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
CallbackReturn Publish(const ProjectWindowDestroyedMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
bool IsAudioActive() const
int GetAudioIOToken() const
static ProjectAudioIO & Get(AudacityProject &project)
static const StatusWidthFunctions & GetStatusWidthFunctions()
PlaybackScroller(AudacityProject *project)
A top-level window associated with a project.
AudacityProject & mProject
AudacityProject & GetProject()
A top-level window associated with a project, and handling scrollbars and zooming.
Definition: ProjectWindow.h:40
void OnToolBarUpdate(wxCommandEvent &event)
void ZoomAfterImport(Track *pTrack)
wxScrollBar * mHsbar
void Rewind(bool shift)
void ZoomOutByFactor(double ZoomFactor)
wxSplitterWindow * mContainerWindow
RealtimeEffectPanel * mEffectsWindow
void ZoomBy(double multiplier)
static ProjectWindow & Get(AudacityProject &project)
Observer::Subscription mFocusChangeSubscription
wxPanel * mTopPanel
void SetHorizontalThumb(double scrollto)
void TP_ScrollLeft() override
void UpdatePrefs() override
void OnActivate(wxActivateEvent &event)
void ApplyUpdatedTheme()
double GetZoomOfToFit() const
void ShowEffectsPanel(Track *track=nullptr, bool focus=false)
void OnScrollLeftButton(wxScrollEvent &event)
void TP_HandleResize() override
void OnUpdateUI(wxUpdateUIEvent &event)
wxWindow * GetContainerWindow() noexcept
Container is a parent window for both effects panel and track list windows.
void Zoom(double level)
bool IsActive() override
wxPanel * GetTopPanel() noexcept
Top panel contains project-related controls and tools.
void RedrawProject(const bool bForceWaveTracks=false)
static ProjectWindow * Find(AudacityProject *pProject)
Observer::Subscription mThemeChangeSubscription
wxWindow * GetTrackListWindow() noexcept
Track list window is the parent container for TrackPanel.
double ScrollingLowerBoundTime() const
void OnMove(wxMoveEvent &event)
double PixelWidthBeforeTime(double scrollto) const
bool IsEffectsPanelShown()
void SetNormalizedWindowState(wxRect pSizeAndLocation)
bool MayScrollBeyondZero() const
void OnMouseEvent(wxMouseEvent &event)
wxWindow * GetEffectsWindow() noexcept
Effect window contains list off effects assigned to a selected track.
bool mbInitializingScrollbar
void ScrollIntoView(double pos)
~ProjectWindow() override
bool IsBeingDeleted() const
Definition: ProjectWindow.h:62
void OnScrollRightButton(wxScrollEvent &event)
void OnScroll(wxScrollEvent &event)
void OnUndoPushedModified()
std::unique_ptr< PlaybackScroller > mPlaybackScroller
void OnSize(wxSizeEvent &event)
void UpdateStatusWidths()
void FinishAutoScroll()
static void OnResetWindow(const CommandContext &context)
void OnThemeChange(struct ThemeChangeMessage)
void SkipEnd(bool shift)
bool TP_ScrollUpDown(int delta) override
Observer::Subscription mUndoSubscription
void ZoomInByFactor(double ZoomFactor)
void TP_ScrollRight() override
void TP_ScrollWindow(double scrollto) override
wxScrollBar * mVsbar
void OnMenu(wxCommandEvent &event)
void TP_RedrawScrollbars() override
bool IsIconized() const override
void OnShow(wxShowEvent &event)
wxWindow * mTrackListWindow
ProjectWindow(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, AudacityProject &project)
void OnIconize(wxIconizeEvent &event)
void MacShowUndockedToolbars(bool show)
void SetTrack(const std::shared_ptr< Track > &track)
Shows effects from the effect stack of the track.
void HandleScrollWheel(int steps)
bool IsScrollScrubbing() const
Definition: Scrubbing.h:93
bool IsScrubbing() const
static Scrubber & Get(AudacityProject &project)
Definition: Scrubbing.cpp:187
bool ReadWithDefault(T *pVar, const T &defaultValue) const
overload of ReadWithDefault returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:206
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
wxColour & Colour(int iIndex)
Works with ToolManager and ToolDock to provide a dockable window in which buttons can be placed.
Definition: ToolBar.h:101
virtual void ReCreateButtons()
Definition: ToolBar.cpp:516
class ToolFrame
Definition: ToolManager.h:168
static ToolManager & Get(AudacityProject &project)
Track * Get()
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:225
void EnsureVisible(bool modifyState=false)
Definition: Track.cpp:97
bool Any() const
Definition: Track.cpp:399
bool empty() const
Definition: Track.cpp:1009
double GetEndTime() const
Definition: Track.cpp:1052
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:486
static int GetTotalHeight(const TrackList &list)
Definition: TrackView.cpp:47
static double GetPinnedHeadPositionPreference()
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:67
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
A Track that contains audio waveform data.
Definition: WaveTrack.h:57
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
ScrollBar(wxWindow *parent, wxWindowID id, long style)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:206
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
Namespace containing an enum 'what to do on a refresh?'.
Definition: RefreshCode.h:16
void OnCloseWindow(wxCloseEvent &e)
AttachedWindows::RegisteredFactory sProjectWindowKey
std::optional< PreferredSystemAppearance > appearance
Definition: Theme.h:112
Type of message published by UndoManager.
Definition: UndoManager.h:61
enum UndoRedoMessage::Type type