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#include "ProjectWindow.h"
11
12#include "ActiveProject.h"
13#include "AllThemeResources.h"
14#include "AudioIO.h"
16#include "ProjectAudioIO.h"
17#include "ProjectFileIO.h"
18#include "ProjectWindows.h"
19#include "ProjectStatus.h"
20#include "ViewInfo.h"
21#include "WaveClip.h"
22#include "WaveTrack.h"
23#include "CommandManager.h"
24#include "prefs/ThemePrefs.h"
25#include "prefs/TracksPrefs.h"
28#include "wxPanelWrapper.h"
29#include "WindowAccessible.h"
30
31#include "ThemedWrappers.h"
32
33#include <wx/display.h>
34#include <wx/scrolbar.h>
35#include <wx/sizer.h>
36#include <wx/splitter.h>
37#include <wx/wupdlock.h>
38
39#include "TrackPanel.h"
40
41namespace
42{
43#ifdef HAS_AUDIOCOM_UPLOAD
44 constexpr int DEFAULT_WINDOW_WIDTH = 1180;
45#else
46 constexpr int DEFAULT_WINDOW_WIDTH = 1120;
47#endif
48 constexpr int DEFAULT_WINDOW_HEIGHT = 674;
49}
50
51BoolSetting ProjectWindowMaximized{ L"/Window/Maximized", false };
52BoolSetting ProjectWindowIconized{ L"/Window/Iconized", false };
53IntSetting ProjectWindowX{ L"/Window/X", 0 };
54IntSetting ProjectWindowY{ L"/Window/Y", 0 };
57IntSetting ProjectWindowNormalX{ L"/Window/Normal_X", 0 };
58IntSetting ProjectWindowNormalY{ L"/Window/Normal_Y", 0 };
61
62// Returns the screen containing a rectangle, or -1 if none does.
63int ScreenContaining( wxRect & r ){
64 unsigned int n = wxDisplay::GetCount();
65 for(unsigned int i = 0;i<n;i++){
66 wxDisplay d(i);
67 wxRect scr = d.GetClientArea();
68 if( scr.Contains( r ) )
69 return (int)i;
70 }
71 return -1;
72}
73
74// true IFF TL and BR corners are on a connected display.
75// Does not need to check all four. We just need to check that
76// the window probably is straddling screens in a sensible way.
77// If the user wants to use mixed landscape and portrait, they can.
78bool CornersOnScreen( wxRect & r ){
79 if( wxDisplay::GetFromPoint( r.GetTopLeft() ) == wxNOT_FOUND) return false;
80 if( wxDisplay::GetFromPoint( r.GetBottomRight() ) == wxNOT_FOUND) return false;
81 return true;
82}
83
84// true iff we have enough of the top bar to be able to reposition the window.
85bool IsWindowAccessible(wxRect *requestedRect)
86{
87 wxDisplay display;
88 wxRect targetTitleRect(requestedRect->GetLeftTop(), requestedRect->GetBottomRight());
89 // Hackery to approximate a window top bar size from a window size.
90 // and exclude the open/close and borders.
91 targetTitleRect.x += 15;
92 targetTitleRect.width -= 100;
93 if (targetTitleRect.width < 165) targetTitleRect.width = 165;
94 targetTitleRect.height = 15;
95 int targetBottom = targetTitleRect.GetBottom();
96 int targetRight = targetTitleRect.GetRight();
97 // This looks like overkill to check each and every pixel in the ranges.
98 // and decide that if any is visible on screen we are OK.
99 for (int i = targetTitleRect.GetLeft(); i < targetRight; i++) {
100 for (int j = targetTitleRect.GetTop(); j < targetBottom; j++) {
101 int monitor = display.GetFromPoint(wxPoint(i, j));
102 if (monitor != wxNOT_FOUND) {
103 return TRUE;
104 }
105 }
106 }
107 return FALSE;
108}
109
110// BG: The default size and position of the first window
111void GetDefaultWindowRect(wxRect *defRect)
112{
113 *defRect = wxGetClientDisplayRect();
114
115 int width = DEFAULT_WINDOW_WIDTH;
116 int height = DEFAULT_WINDOW_HEIGHT;
117
118 //These conditional values assist in improving placement and size
119 //of NEW windows on different platforms.
120#ifdef __WXGTK__
121 height += 20;
122#endif
123
124#ifdef __WXMSW__
125 height += 40;
126#endif
127
128#ifdef __WXMAC__
129 height += 55;
130#endif
131
132 // Use screen size where it is smaller than the values we would like.
133 // Otherwise use the values we would like, and centred.
134 if (width < defRect->width)
135 {
136 defRect->x = (defRect->width - width)/2;
137 defRect->width = width;
138 }
139
140 if (height < defRect->height)
141 {
142 defRect->y = (defRect->height - height)/2;
143 // Bug 1119 workaround
144 // Small adjustment for very small Mac screens.
145 // If there is only a tiny space at the top
146 // then instead of vertical centre, align to bottom.
147 const int pixelsFormenu = 60;
148 if( defRect->y < pixelsFormenu )
149 defRect->y *=2;
150 defRect->height = height;
151 }
152}
153
154// BG: Calculate where to place the next window (could be the first window)
155// BG: Does not store X and Y in prefs. This is intentional.
156//
157// LL: This should NOT need to be this complicated...FIXME
158void GetNextWindowPlacement(wxRect *nextRect, bool *pMaximized, bool *pIconized)
159{
160 int inc = 25;
161
162 wxRect defaultRect;
163 GetDefaultWindowRect(&defaultRect);
164
165 *pMaximized = ProjectWindowMaximized.Read();
166 *pIconized = ProjectWindowIconized.Read();
167
168 wxRect windowRect;
169 windowRect.x = ProjectWindowX.ReadWithDefault(defaultRect.x);
170 windowRect.y = ProjectWindowY.ReadWithDefault(defaultRect.y);
171 windowRect.width = ProjectWindowWidth.ReadWithDefault(defaultRect.width);
172 windowRect.height = ProjectWindowHeight.ReadWithDefault(defaultRect.height);
173
174 wxRect normalRect;
175 normalRect.x = ProjectWindowNormalX.ReadWithDefault(defaultRect.x);
176 normalRect.y = ProjectWindowNormalY.ReadWithDefault(defaultRect.y);
177 normalRect.width = ProjectWindowNormalWidth.ReadWithDefault(defaultRect.width);
178 normalRect.height = ProjectWindowNormalHeight.ReadWithDefault(defaultRect.height);
179
180 // Workaround 2.1.1 and earlier bug on OSX...affects only normalRect, but let's just
181 // validate for all rects and plats
182 if (normalRect.width == 0 || normalRect.height == 0) {
183 normalRect = defaultRect;
184 }
185 if (windowRect.width == 0 || windowRect.height == 0) {
186 windowRect = defaultRect;
187 }
188
189
190 wxRect screenRect( wxGetClientDisplayRect());
191#if defined(__WXMAC__)
192
193 // On OSX, the top of the window should never be less than the menu height,
194 // so something is amiss if it is
195 if (normalRect.y < screenRect.y) {
196 normalRect = defaultRect;
197 }
198 if (windowRect.y < screenRect.y) {
199 windowRect = defaultRect;
200 }
201#endif
202
203 // IF projects empty, THEN it's the first window.
204 // It lands where the config says it should, and can straddle screen.
205 if (AllProjects{}.empty()) {
206 if (*pMaximized || *pIconized) {
207 *nextRect = normalRect;
208 }
209 else {
210 *nextRect = windowRect;
211 }
212 // Resize, for example if one monitor that was on is now off.
213 if (!CornersOnScreen( wxRect(*nextRect).Deflate( 32, 32 ))) {
214 *nextRect = defaultRect;
215 }
216 if (!IsWindowAccessible(nextRect)) {
217 *nextRect = defaultRect;
218 }
219 // Do not trim the first project window down.
220 // All corners are on screen (or almost so), and
221 // the rect may straddle screens.
222 return;
223 }
224
225
226 // ELSE a subsequent NEW window. It will NOT straddle screens.
227
228 // We don't mind being 32 pixels off the screen in any direction.
229 // Make sure initial sizes (pretty much) fit within the display bounds
230 // We used to trim the sizes which could result in ridiculously small windows.
231 // contributing to bug 1243.
232 // Now instead if the window significantly doesn't fit the screen, we use the default
233 // window instead, which we know does.
234 if (ScreenContaining( wxRect(normalRect).Deflate( 32, 32 ))<0) {
235 normalRect = defaultRect;
236 }
237 if (ScreenContaining( wxRect(windowRect).Deflate( 32, 32 ) )<0) {
238 windowRect = defaultRect;
239 }
240
241 bool validWindowSize = false;
242 ProjectWindow * validProject = NULL;
243 for ( auto iter = AllProjects{}.rbegin(), end = AllProjects{}.rend();
244 iter != end; ++iter
245 ) {
246 auto pProject = *iter;
247 if (!GetProjectFrame( *pProject ).IsIconized()) {
248 validWindowSize = true;
249 validProject = &ProjectWindow::Get( *pProject );
250 break;
251 }
252 }
253 if (validWindowSize) {
254 *nextRect = validProject->GetRect();
255 *pMaximized = validProject->IsMaximized();
256 *pIconized = validProject->IsIconized();
257 // Do not straddle screens.
258 if (ScreenContaining( wxRect(*nextRect).Deflate( 32, 32 ) )<0) {
259 *nextRect = defaultRect;
260 }
261 }
262 else {
263 *nextRect = normalRect;
264 }
265
266 //Placement depends on the increments
267 nextRect->x += inc;
268 nextRect->y += inc;
269
270 // defaultrect is a rectangle on the first screen. It's the right fallback to
271 // use most of the time if things are not working out right with sizing.
272 // windowRect is a saved rectangle size.
273 // normalRect seems to be a substitute for windowRect when iconized or maximised.
274
275 // Windows can say that we are off screen when actually we are not.
276 // On Windows 10 I am seeing miscalculation by about 6 pixels.
277 // To fix this we allow some sloppiness on the edge being counted as off screen.
278 // This matters most when restoring very carefully sized windows that are maximised
279 // in one dimension (height or width) but not both.
280 const int edgeSlop = 10;
281
282 // Next four lines are getting the rectangle for the screen that contains the
283 // top left corner of nextRect (and defaulting to rect of screen 0 otherwise).
284 wxPoint p = nextRect->GetLeftTop();
285 int scr = std::max( 0, wxDisplay::GetFromPoint( p ));
286 wxDisplay d( scr );
287 screenRect = d.GetClientArea();
288
289 // Now we (possibly) start trimming our rectangle down.
290 // Have we hit the right side of the screen?
291 wxPoint bottomRight = nextRect->GetBottomRight();
292 if (bottomRight.x > (screenRect.GetRight()+edgeSlop)) {
293 int newWidth = screenRect.GetWidth() - nextRect->GetLeft();
294 if (newWidth < defaultRect.GetWidth()) {
295 nextRect->x = windowRect.x;
296 nextRect->y = windowRect.y;
297 nextRect->width = windowRect.width;
298 }
299 else {
300 nextRect->width = newWidth;
301 }
302 }
303
304 // Have we hit the bottom of the screen?
305 bottomRight = nextRect->GetBottomRight();
306 if (bottomRight.y > (screenRect.GetBottom()+edgeSlop)) {
307 nextRect->y -= inc;
308 bottomRight = nextRect->GetBottomRight();
309 if (bottomRight.y > (screenRect.GetBottom()+edgeSlop)) {
310 nextRect->SetBottom(screenRect.GetBottom());
311 }
312 }
313
314 // After all that we could have a window that does not have a visible
315 // top bar. [It is unlikely, but something might have gone wrong]
316 // If so, use the safe fallback size.
317 if (!IsWindowAccessible(nextRect)) {
318 *nextRect = defaultRect;
319 }
320}
321
322namespace {
323
324// This wrapper prevents the scrollbars from retaining focus after being
325// used. Otherwise, the only way back to the track panel is to click it
326// and that causes your original location to be lost.
327class ScrollBar final : public wxScrollBar
328{
329public:
330 ScrollBar(wxWindow* parent, wxWindowID id, long style)
331 : wxScrollBar(parent, id, wxDefaultPosition, wxDefaultSize, style)
332 {
333 }
334
335 void OnSetFocus(wxFocusEvent & e)
336 {
337 wxWindow *w = e.GetWindow();
338 if (w != NULL) {
339 w->SetFocus();
340 }
341 }
342
343 void SetScrollbar(int position, int thumbSize,
344 int range, int pageSize,
345 bool refresh = true) override;
346
347private:
348 DECLARE_EVENT_TABLE()
349};
350
351void ScrollBar::SetScrollbar(int position, int thumbSize,
352 int range, int pageSize,
353 bool refresh)
354{
355 // Mitigate flashing of scrollbars by refreshing only when something really changes.
356
357 // PRL: This may have been made unnecessary by other fixes for flashing, see
358 // commit ac05b190bee7dd0000bce56edb0e5e26185c972f
359
360 auto changed =
361 position != GetThumbPosition() ||
362 thumbSize != GetThumbSize() ||
363 range != GetRange() ||
364 pageSize != GetPageSize();
365 if (!changed)
366 return;
367
368 wxScrollBar::SetScrollbar(position, thumbSize, range, pageSize, refresh);
369}
370
371BEGIN_EVENT_TABLE(ScrollBar, wxScrollBar)
372 EVT_SET_FOCUS(ScrollBar::OnSetFocus)
374
376 []( AudacityProject &parent ) -> wxWeakRef< wxWindow > {
377 wxRect wndRect;
378 bool bMaximized = false;
379 bool bIconized = false;
380 GetNextWindowPlacement(&wndRect, &bMaximized, &bIconized);
381
382 auto pWindow = safenew ProjectWindow(
383 nullptr, -1,
384 wxDefaultPosition,
385 wxSize(wndRect.width, wndRect.height),
386 parent
387 );
388
389 auto &window = *pWindow;
390 // wxGTK3 seems to need to require creating the window using default position
391 // and then manually positioning it.
392 window.SetPosition(wndRect.GetPosition());
393
394 if(bMaximized) {
395 window.Maximize(true);
396 }
397 else if (bIconized) {
398 // if the user close down and iconized state we could start back up and iconized state
399 // window.Iconize(TRUE);
400 }
401
402 return pWindow;
403 }
404};
405
406}
407
409{
411}
412
414{
415 return Get( const_cast< AudacityProject & >( project ) );
416}
417
419{
420 return pProject
422 : nullptr;
423}
424
426{
427 return Find( const_cast< AudacityProject * >( pProject ) );
428}
429
431{
432 return mNextWindowID++;
433}
434
435enum {
436 FirstID = 1000,
437
438 // Window controls
439
442
444};
445
446namespace {
450 explicit Adapter(ProjectWindow &window) : mwWindow{ &window } {}
451 ~Adapter() override = default;
452
453 std::pair<int, int> ViewportSize() const override
454 { return mwWindow ? mwWindow->ViewportSize() : std::pair{ 1, 1 }; }
455
456 unsigned MinimumTrackHeight() override
457 { return mwWindow ? mwWindow->MinimumTrackHeight() : 0; }
458 bool IsTrackMinimized(const Track &track) override
459 { return mwWindow ? mwWindow->IsTrackMinimized(track) : false; }
460 void SetMinimized(Track &track, bool minimized) override
461 { if (mwWindow) mwWindow->SetMinimized(track, minimized); }
462 int GetTrackHeight(const Track &track) override
463 { return mwWindow ? mwWindow->GetTrackHeight(track) : 0; }
464 void SetChannelHeights(Track &track, unsigned height) override
465 { if (mwWindow) mwWindow->SetChannelHeights(track, height); }
466 int GetTotalHeight(const TrackList &trackList) override
467 { return mwWindow ? mwWindow->GetTotalHeight(trackList) : 0; }
468
469 int GetHorizontalThumbPosition() const override
470 { return mwWindow ? mwWindow->GetHorizontalThumbPosition() : 0; }
471 int GetHorizontalThumbSize() const override
472 { return mwWindow ? mwWindow->GetHorizontalThumbSize() : 0; }
473 int GetHorizontalRange() const override
474 { return mwWindow ? mwWindow->GetHorizontalRange() : 0; }
475 void SetHorizontalThumbPosition(int viewStart) override
476 {
477 if (mwWindow) mwWindow->SetHorizontalThumbPosition(viewStart);
478 }
479 void SetHorizontalScrollbar(int position, int thumbSize,
480 int range, int pageSize, bool refresh) override
481 {
482 if (mwWindow)
483 mwWindow->SetHorizontalScrollbar(
484 position, thumbSize, range, pageSize, refresh);
485 }
486 void ShowHorizontalScrollbar(bool shown) override
487 {
488 if (mwWindow)
489 mwWindow->ShowHorizontalScrollbar(shown);
490 }
491
492 int GetVerticalThumbPosition() const override
493 { return mwWindow ? mwWindow->GetVerticalThumbPosition() : 0; }
494 int GetVerticalThumbSize() const override
495 { return mwWindow ? mwWindow->GetVerticalThumbSize() : 0; }
496 int GetVerticalRange() const override
497 { return mwWindow ? mwWindow->GetVerticalRange() : 0; }
498 void SetVerticalThumbPosition(int viewStart) override
499 {
500 if (mwWindow) mwWindow->SetVerticalThumbPosition(viewStart);
501 }
502 void SetVerticalScrollbar(int position, int thumbSize,
503 int range, int pageSize, bool refresh) override
504 {
505 if (mwWindow)
506 mwWindow->SetVerticalScrollbar(
507 position, thumbSize, range, pageSize, refresh);
508 }
509 void ShowVerticalScrollbar(bool shown) override
510 {
511 if (mwWindow)
512 mwWindow->ShowVerticalScrollbar(shown);
513 }
514 void SetToDefaultSize() override
515 {
516 if (mwWindow)
517 mwWindow->SetToDefaultSize();
518 }
519
520 wxWeakRef<ProjectWindow> mwWindow;
521};
522}
523
524ProjectWindow::ProjectWindow(wxWindow * parent, wxWindowID id,
525 const wxPoint & pos,
526 const wxSize & size, AudacityProject &project
527) : ProjectWindowBase{ parent, id, pos, size, project }
528 , mViewportSubscription{
530 .Subscribe(*this, &ProjectWindow::OnViewportMessage) }
531{
532 Viewport::Get(project).SetCallbacks(std::make_unique<Adapter>(*this));
533
535
536 constexpr auto EffectsPanelMaxWidth { 500 };
537 constexpr auto TrackPanelMinWidth { 250 };
538
539 // Two sub-windows need to be made before Init(),
540 // so that this constructor can complete, and then TrackPanel and
541 // AdornedRulerPanel can retrieve those windows from this in their
542 // factory functions
543
544 // PRL: this panel groups the top tool dock and the ruler into one
545 // tab cycle.
546 // Must create it with non-default width equal to the main window width,
547 // or else the device toolbar doesn't make initial widths of the choice
548 // controls correct.
550 this, wxID_ANY, wxDefaultPosition,
551 wxSize{ this->GetSize().GetWidth(), -1 }
552 };
553 mTopPanel->SetLabel( "Top Panel" );// Not localised
554 mTopPanel->SetLayoutDirection(wxLayout_LeftToRight);
555 mTopPanel->SetAutoLayout(true);
556
557 auto container = safenew wxSplitterWindow(this, wxID_ANY,
558 wxDefaultPosition,
559 wxDefaultSize,
560 wxNO_BORDER | wxSP_LIVE_UPDATE | wxSP_THIN_SASH);
561 container->Bind(wxEVT_SPLITTER_DOUBLECLICKED, [](wxSplitterEvent& event){
562 //"The default behaviour is to unsplit the window"
563 event.Veto();//do noting instead
564 });
565 container->Bind(wxEVT_SPLITTER_SASH_POS_CHANGING, [=](wxSplitterEvent& event){
566 if(event.GetSashPosition() > EffectsPanelMaxWidth)
567 //Prevents left panel from expanding further
568 event.SetSashPosition(-1);
569 });
570 mContainerWindow = container;
571
573 wxDefaultPosition,
574 wxDefaultSize,
575 wxNO_BORDER);
576 mTrackListWindow->SetMinSize({TrackPanelMinWidth, -1});
577 mTrackListWindow->SetSizer( safenew wxBoxSizer(wxVERTICAL) );
578 mTrackListWindow->SetLabel("Main Panel");// Not localized.
579 mTrackListWindow->SetLayoutDirection(wxLayout_LeftToRight);
580
582
583 mPlaybackScroller = std::make_unique<PlaybackScroller>( &project );
584
585 // PRL: Old comments below. No longer observing the ordering that it
586 // recommends. ProjectWindow::OnActivate puts the focus directly into
587 // the TrackPanel, which avoids the problems.
588 // LLL: When Audacity starts or becomes active after returning from
589 // another application, the first window that can accept focus
590 // will be given the focus even if we try to SetFocus(). By
591 // creating the scrollbars after the TrackPanel, we resolve
592 // several focus problems.
593
594 mHsbar = safenew ScrollBar(mTrackListWindow, HSBarID, wxSB_HORIZONTAL);
595 mVsbar = safenew ScrollBar(mTrackListWindow, VSBarID, wxSB_VERTICAL);
596#if wxUSE_ACCESSIBILITY
597 // so that name can be set on a standard control
598 mHsbar->SetAccessible(safenew WindowAccessible(mHsbar));
599 mVsbar->SetAccessible(safenew WindowAccessible(mVsbar));
600#endif
601 mHsbar->SetLayoutDirection(wxLayout_LeftToRight);
602 mHsbar->SetName(_("Horizontal Scrollbar"));
603 mVsbar->SetName(_("Vertical Scrollbar"));
604
607
608 // Subscribe to title changes published by ProjectFileIO
611
612 // And also establish my initial consistency with it
614}
615
617{
618 // Tool manager gives us capture sometimes
619 if(HasCapture())
620 ReleaseMouse();
621}
622
623BEGIN_EVENT_TABLE(ProjectWindow, wxFrame)
625 EVT_MOUSE_EVENTS(ProjectWindow::OnMouseEvent)
627 EVT_SIZE(ProjectWindow::OnSize)
628 EVT_SHOW(ProjectWindow::OnShow)
629 EVT_ICONIZE(ProjectWindow::OnIconize)
630 EVT_MOVE(ProjectWindow::OnMove)
631 EVT_ACTIVATE(ProjectWindow::OnActivate)
632 EVT_COMMAND_SCROLL_LINEUP(HSBarID, ProjectWindow::OnScrollLeftButton)
633 EVT_COMMAND_SCROLL_LINEDOWN(HSBarID, ProjectWindow::OnScrollRightButton)
634 EVT_COMMAND_SCROLL(HSBarID, ProjectWindow::OnScroll)
635 EVT_COMMAND_SCROLL(VSBarID, ProjectWindow::OnScroll)
636 // Fires for menu with ID #1...first menu defined
637 EVT_UPDATE_UI(1, ProjectWindow::OnUpdateUI)
638 EVT_COMMAND(wxID_ANY, EVT_TOOLBAR_UPDATED, ProjectWindow::OnToolBarUpdate)
639 //mchinen:multithreaded calls - may not be threadsafe with CommandEvent: may have to change.
641
642void ProjectWindow::ApplyUpdatedTheme()
643{
644 SetBackgroundColour(theTheme.Colour( clrMedium ));
645 ClearBackground();// For wxGTK.
646}
647
649{
650 auto pProject = FindProject();
651 if (!pProject)
652 return;
653 auto &project = *pProject;
654
655 if (message.appearance)
656 return;
657 this->ApplyUpdatedTheme();
658 auto &toolManager = ToolManager::Get( project );
659 toolManager.ForEach([](auto pToolBar){
660 if( pToolBar )
661 pToolBar->ReCreateButtons();
662 });
663}
664
666{
667 // Update status bar widths in case of language change
669}
670
671#include "AllThemeResources.h"
672
674{
675 auto pProject = FindProject();
676 if (!pProject)
677 return;
679}
680
682{
683 auto pProject = FindProject();
684 if (!pProject)
685 return;
687}
688
689std::pair<int, int> ProjectWindow::ViewportSize() const
690{
691 auto pProject = FindProject();
692 if (!pProject)
693 return { 0, 0 };
694 auto &project = *pProject;
695 auto &trackPanel = TrackPanel::Get(project);
696 int width, height;
697 trackPanel.GetSize(&width, &height);
698 return { width, height };
699}
700
702{
704}
705
707{
708 return ChannelView::Get(*track.GetChannel(0)).GetMinimized();
709}
710
711void ProjectWindow::SetMinimized(Track &track, bool minimized)
712{
713 for (auto pChannel : track.Channels())
714 ChannelView::Get(*pChannel).SetMinimized(minimized);
715}
716
718{
720}
721
723{
724 return ChannelView::GetTotalHeight(trackList);
725}
726
727void ProjectWindow::SetChannelHeights(Track &track, unsigned height)
728{
729 for (auto pChannel : track.Channels())
730 ChannelView::Get(*pChannel).SetExpandedHeight(height);
731}
732
734{
735 return mHsbar->GetThumbPosition();
736}
737
739{
740 return mHsbar->GetThumbSize();
741}
742
744{
745 return mHsbar->GetRange();
746}
747
749{
750 mHsbar->SetThumbPosition(viewStart);
751}
752
753void ProjectWindow::SetHorizontalScrollbar(int position, int thumbSize,
754 int range, int pageSize, bool refresh)
755{
756 mHsbar->SetScrollbar(position, thumbSize, range, pageSize, refresh);
757}
758
760{
761#ifdef __WXGTK__
762 mHsbar->Show(shown);
763#else
764 mHsbar->Enable(shown);
765#endif
766}
767
769{
770 return mVsbar->GetThumbPosition();
771}
772
774{
775 return mVsbar->GetThumbSize();
776}
777
779{
780 return mVsbar->GetRange();
781}
782
784{
785 mVsbar->SetThumbPosition(viewStart);
786}
787
788void ProjectWindow::SetVerticalScrollbar(int position, int thumbSize,
789 int range, int pageSize, bool refresh)
790{
791 mVsbar->SetScrollbar(position, thumbSize, range, pageSize, refresh);
792}
793
795{
796#ifdef __WXGTK__
797 mVsbar->Show(shown);
798#else
799 mVsbar->Enable(shown);
800#endif
801}
802
804{
805 auto pProject = FindProject();
806 if (!pProject)
807 return;
808 auto &project = *pProject;
809 auto &trackPanel = GetProjectPanel( project );
810 auto &toolManager = ToolManager::Get( project );
811
812 // 1. Layout panel, to get widths of the docks.
813 Layout();
814 // 2. Layout toolbars to pack the toolbars correctly in docks which
815 // are now the correct width.
816 toolManager.LayoutToolBars();
817 // 3. Layout panel, to resize docks, in particular reducing the height
818 // of any empty docks, or increasing the height of docks that need it.
819 Layout();
820
821 // Bug 2455
822 // The commented out code below is to calculate a nice minimum size for
823 // the window. However on Ubuntu when the window is minimised it leads to
824 // an insanely tall window.
825 // Using a fixed min size fixes that.
826 // However there is still something strange when minimised, as once
827 // UpdateLayout is called once, when minimised, it gets called repeatedly.
828#if 0
829 // Retrieve size of this projects window
830 wxSize mainsz = GetSize();
831
832 // Retrieve position of the track panel to use as the size of the top
833 // third of the window
834 wxPoint tppos = ClientToScreen(trackPanel.GetParent()->GetPosition());
835
836 // Retrieve position of bottom dock to use as the size of the bottom
837 // third of the window
838 wxPoint sbpos = ClientToScreen(toolManager.GetBotDock()->GetPosition());
839
840 // The "+ 50" is the minimum height of the TrackPanel
841 SetMinSize( wxSize(250, (mainsz.y - sbpos.y) + tppos.y + 50));
842#endif
843 SetMinSize( wxSize(250, 250));
844 SetMaxSize( wxSize(20000, 20000));
845}
846
848{
849 return mIconized;
850}
851
853{
854 return mTrackListWindow;
855}
856
857wxSplitterWindow* ProjectWindow::GetContainerWindow() noexcept
858{
859 return mContainerWindow;
860}
861
862wxPanel* ProjectWindow::GetTopPanel() noexcept
863{
864 return mTopPanel;
865}
866
868{
869 wxRect defaultRect;
870 GetDefaultWindowRect(&defaultRect);
871
872 SetSize(defaultRect.width, defaultRect.height);
873}
874
876{
877 auto statusBar = GetStatusBar();
878
879 if (statusBar != nullptr)
880 statusBar->Destroy();
881
882 auto pProject = FindProject();
883
884 // Note that the first field of the status bar is a dummy, and its width is
885 // set to zero latter in the code. This field is needed for wxWidgets 2.8.12
886 // because if you move to the menu bar, the first field of the menu bar is
887 // cleared, which is undesirable behaviour. In addition, the help strings of
888 // menu items are by default sent to the first field. Currently there are no
889 // such help strings, but it they were introduced, then there would need to
890 // be an event handler to send them to the appropriate field.
891 statusBar = CreateStatusBar(
892 1 + ProjectStatusFieldsRegistry::Count(pProject.get()));
893
894 statusBar->Bind(
895 wxEVT_SIZE,
896 [this](auto& evt)
897 {
898 evt.Skip();
899 auto pProject = FindProject();
900 if (pProject != nullptr)
902 });
903
904 // We have a new status bar now, we need a full content update
905 if (pProject)
906 {
907 int index = 1;
909 [&](const StatusBarFieldItem& field, const auto&)
910 {
911 if (field.IsVisible(*pProject))
912 statusBar->SetStatusText(
913 field.GetText(*pProject).Translation(), index++);
914 });
915 }
916
917 return statusBar;
918}
919
921{
922 auto pProject = FindProject();
923 if (!pProject)
924 return;
925
926 const auto fieldsCount =
927 ProjectStatusFieldsRegistry::Count(pProject.get()) + 1;
928 auto statusBar = GetStatusBar();
929
930 bool statusBarRecreated = false;
931
932 if (!statusBar || fieldsCount != statusBar->GetFieldsCount())
933 {
934 statusBar = CreateProjectStatusBar();
935 statusBarRecreated = true;
936 }
937
938 const auto& functions = ProjectStatus::GetStatusWidthFunctions();
939
940 auto& project = *pProject;
941
942 std::vector<int> widths(fieldsCount, 0);
943
944 // The old behavior with zero-width first field is kept
945 int index = 1;
946
948 [&](const StatusBarFieldItem& field, const auto&)
949 {
950 if (!field.IsVisible(project))
951 return;
952
953 auto width = field.GetDefaultWidth(project);
954
955 // Negative width indicates that the field is expandable
956 if (width >= 0)
957 {
958 for (const auto& function : functions)
959 {
960 auto results = function(project, field.name);
961 for (const auto& string : results.first)
962 {
963 int w;
964 statusBar->GetTextExtent(string.Translation(), &w, nullptr);
965 width = std::max<int>(width, w + results.second);
966 }
967 }
968 }
969
970 widths[index++] = width;
971 });
972
973 statusBar->SetStatusWidths(fieldsCount, widths.data());
975}
976
978{
979 (void)show;//compiler food
980#ifdef __WXMAC__
981 // Save the focus so we can restore it to whatever had it before since
982 // showing a previously hidden toolbar will cause the focus to be set to
983 // its frame. If this is not done it will appear that activation events
984 // aren't being sent to the project window since they are actually being
985 // delivered to the last tool frame shown.
986 wxWindow *focused = FindFocus();
987
988 // Find all the floating toolbars, and show or hide them
989 const auto &children = GetChildren();
990 for(const auto &child : children) {
991 if (auto frame = dynamic_cast<ToolFrame*>(child)) {
992 if (!show) {
993 frame->Hide();
994 }
995 else if (frame->GetBar() &&
996 frame->GetBar()->IsVisible() ) {
997 frame->Show();
998 }
999 }
1000 }
1001
1002 // Restore the focus if needed
1003 if (focused) {
1004 focused->SetFocus();
1005 }
1006#endif
1007}
1008
1009void ProjectWindow::OnIconize(wxIconizeEvent &event)
1010{
1011 //JKC: On Iconizing we get called twice. Don't know
1012 // why but it does no harm.
1013 // Should we be returning true/false rather than
1014 // void return? I don't know.
1015 mIconized = event.IsIconized();
1016
1017#if defined(__WXMAC__)
1018 // Readdresses bug 1431 since a crash could occur when restoring iconized
1019 // floating toolbars due to recursion (bug 2411).
1021 if( !mIconized )
1022 {
1023 Raise();
1024 }
1025#endif
1026
1027 // VisibileProjectCount seems to be just a counter for debugging.
1028 // It's not used outside this function.
1029 auto VisibleProjectCount = std::count_if(
1031 []( const AllProjects::value_type &ptr ){
1032 return !GetProjectFrame( *ptr ).IsIconized();
1033 }
1034 );
1035 event.Skip();
1036
1037 // This step is to fix part of Bug 2040, where the BackingPanel
1038 // size was not restored after we leave Iconized state.
1039
1040 // Queue up a resize event using OnShow so that we
1041 // refresh the track panel. But skip this, if we're iconized.
1042 if( mIconized )
1043 return;
1044 wxShowEvent Evt;
1045 OnShow( Evt );
1046}
1047
1048void ProjectWindow::OnMove(wxMoveEvent & event)
1049{
1050 if (!this->IsMaximized() && !this->IsIconized())
1051 SetNormalizedWindowState(this->GetRect());
1052 event.Skip();
1053}
1054
1055void ProjectWindow::OnSize(wxSizeEvent & event)
1056{
1057 // (From Debian)
1058 //
1059 // (3.) GTK critical warning "IA__gdk_window_get_origin: assertion
1060 // 'GDK_IS_WINDOW (window)' failed": Received events of type wxSizeEvent
1061 // on the main project window cause calls to "ClientToScreen" - which is
1062 // not available until the window is first shown. So the class has to
1063 // keep track of wxShowEvent events and inhibit those actions until the
1064 // window is first shown.
1065 if (mShownOnce) {
1066 auto pProject = FindProject();
1067 if (pProject)
1068 Viewport::Get(*pProject).HandleResize();
1069 if (!this->IsMaximized() && !this->IsIconized())
1070 SetNormalizedWindowState(this->GetRect());
1071 }
1072 event.Skip();
1073}
1074
1075void ProjectWindow::OnShow(wxShowEvent & event)
1076{
1077 // Remember that the window has been shown at least once
1078 mShownOnce = true;
1079
1080 // (From Debian...see also TrackPanel::OnTimer and AudacityTimer::Notify)
1081 //
1082 // Description: Workaround for wxWidgets bug: Reentry in clipboard
1083 // The wxWidgets bug http://trac.wxwidgets.org/ticket/16636 prevents
1084 // us from doing clipboard operations in wxShowEvent and wxTimerEvent
1085 // processing because those event could possibly be processed during
1086 // the (not sufficiently protected) Yield() of a first clipboard
1087 // operation, causing reentry. Audacity had a workaround in place
1088 // for this problem (the class "CaptureEvents"), which however isn't
1089 // applicable with wxWidgets 3.0 because it's based on changing the
1090 // gdk event handler, a change that would be overridden by wxWidgets's
1091 // own gdk event handler change.
1092 // Instead, as a NEW workaround, specifically protect those processings
1093 // of wxShowEvent and wxTimerEvent that try to do clipboard operations
1094 // from being executed within Yield(). This is done by delaying their
1095 // execution by posting pure wxWidgets events - which are never executed
1096 // during Yield().
1097 // Author: Martin Stegh fer <[email protected]>
1098 // Bug-Debian: https://bugs.debian.org/765341
1099
1100 // the actual creation/showing of the window).
1101 // Post the event instead of calling OnSize(..) directly. This ensures that
1102 // this is a pure wxWidgets event (no GDK event behind it) and that it
1103 // therefore isn't processed within the YieldFor(..) of the clipboard
1104 // operations (workaround for Debian bug #765341).
1105 // QueueEvent() will take ownership of the event
1106 GetEventHandler()->QueueEvent(safenew wxSizeEvent(GetSize()));
1107
1108 // Further processing by default handlers
1109 event.Skip();
1110}
1111
1115void ProjectWindow::OnToolBarUpdate(wxCommandEvent & event)
1116{
1117 auto pProject = FindProject();
1118 if (pProject)
1119 Viewport::Get(*pProject).HandleResize();
1120 event.Skip(false); /* No need to propagate any further */
1121}
1122
1124{
1126 auto pProject = FindProject();
1127 if (!pProject)
1128 return;
1129 auto &project = *pProject;
1131 if (name != GetTitle()) {
1132 SetTitle(name);
1133 SetName(name); // to make the nvda screen reader read the correct title
1134 }
1135 }
1136}
1137
1138void ProjectWindow::OnScroll(wxScrollEvent &)
1139{
1140 auto pProject = FindProject();
1141 if (!pProject)
1142 return;
1143 Viewport::Get(*pProject).OnScroll();
1144}
1145
1146void ProjectWindow::OnMenu(wxCommandEvent & event)
1147{
1148#ifdef __WXMSW__
1149 // Bug 1642: We can arrive here with bogus menu IDs, which we
1150 // proceed to process. So if bogus, don't.
1151 // The bogus menu IDs are probably generated by controls on the TrackPanel,
1152 // such as the Project Rate.
1153 // 17000 is the magic number at which we start our menu.
1154 // This code would probably NOT be OK on Mac, since we assign
1155 // some specific ID numbers.
1156 if( event.GetId() < 17000){
1157 event.Skip();
1158 return;
1159 }
1160#endif
1161 auto pProject = FindProject();
1162 if (!pProject)
1163 return;
1164 auto &project = *pProject;
1165 auto &commandManager = CommandManager::Get( project );
1166 bool handled = commandManager.HandleMenuID(
1167 event.GetId(), CommandManager::Get( project ).GetUpdateFlags(),
1168 false);
1169
1170 if (handled)
1171 event.Skip(false);
1172 else{
1173 event.ResumePropagation( 999 );
1174 event.Skip(true);
1175 }
1176}
1177
1178void ProjectWindow::OnUpdateUI(wxUpdateUIEvent & WXUNUSED(event))
1179{
1180 auto pProject = FindProject();
1181 if (!pProject)
1182 return;
1183 auto &project = *pProject;
1185}
1186
1187void ProjectWindow::OnActivate(wxActivateEvent & event)
1188{
1189 // Activate events can fire during window teardown, so just
1190 // ignore them.
1191 if (IsBeingDeleted()) {
1192 return;
1193 }
1194
1195 auto pProject = FindProject();
1196 if (!pProject)
1197 return;
1198 auto &project = *pProject;
1199
1200 mActive = event.GetActive();
1201
1202 // Under Windows, focus can be "lost" when returning to
1203 // Audacity from a different application.
1204 //
1205 // This was observed by minimizing all windows using WINDOWS+M and
1206 // then ALT+TAB to return to Audacity. Focus will be given to the
1207 // project window frame which is not at all useful.
1208 //
1209 // So, we use ToolManager's observation of focus changes in a wxEventFilter.
1210 // Then, when we receive the
1211 // activate event, we restore that focus to the child or the track
1212 // panel if no child had the focus (which probably should never happen).
1213 if (mActive) {
1214 auto &toolManager = ToolManager::Get( project );
1216 if ( ! toolManager.RestoreFocus() )
1217 GetProjectPanel( project ).SetFocus();
1218 }
1219 event.Skip();
1220}
1221
1223{
1224 return mActive;
1225}
1226
1227void ProjectWindow::OnMouseEvent(wxMouseEvent & event)
1228{
1229 auto pProject = FindProject();
1230 if (!pProject)
1231 return;
1232 auto &project = *pProject;
1233 if (event.ButtonDown())
1235}
1236
1238: mProject(project)
1239{
1240}
1241
1243{
1244 auto gAudioIO = AudioIO::Get();
1245 mRecentStreamTime = gAudioIO->GetStreamTime();
1246
1247 auto cleanup = finally([&]{
1248 // Propagate the message to other listeners bound to this
1249 this->Publish({});
1250 });
1251
1252 if(!ProjectAudioIO::Get( *mProject ).IsAudioActive())
1253 return;
1254 else if (mMode == Mode::Refresh) {
1255 // PRL: see comments in Scrubbing.cpp for why this is sometimes needed.
1256 // These unnecessary refreshes cause wheel rotation events to be delivered more uniformly
1257 // to the application, so scrub speed control is smoother.
1258 // (So I see at least with OS 10.10 and wxWidgets 3.0.2.)
1259 // Is there another way to ensure that than by refreshing?
1260 auto &trackPanel = GetProjectPanel( *mProject );
1261 trackPanel.Refresh(false);
1262 }
1263 else if (mMode != Mode::Off) {
1264 // Pan the view, so that we put the play indicator at some fixed
1265 // fraction of the window width.
1266
1267 auto &viewInfo = ViewInfo::Get( *mProject );
1268 auto &trackPanel = GetProjectPanel( *mProject );
1269 const int posX = viewInfo.TimeToPosition(mRecentStreamTime);
1270 auto width = viewInfo.GetTracksUsableWidth();
1271 int deltaX;
1272 switch (mMode)
1273 {
1274 default:
1275 wxASSERT(false);
1276 /* fallthru */
1277 case Mode::Pinned:
1278 deltaX =
1280 break;
1281 case Mode::Right:
1282 deltaX = posX - width; break;
1283 }
1284 viewInfo.hpos =
1285 viewInfo.OffsetTimeByPixels(viewInfo.hpos, deltaX, true);
1286 // Can't scroll too far left
1287 viewInfo.hpos = std::max(0.0, viewInfo.hpos);
1288 trackPanel.Refresh(false);
1289 }
1290}
1291
1293{
1294 // Activate events can fire during window teardown, so just
1295 // ignore them.
1296 if (mIsDeleting)
1297 return;
1298 auto pProject = FindProject();
1299 if (!pProject)
1300 return;
1301 auto &project = *pProject;
1302 auto &viewInfo = ViewInfo::Get(project);
1303 auto &trackPanel = GetProjectPanel(project);
1304
1305 auto [rescroll, scrollbarVisibilityChanged, resize] = message;
1306
1307 if (rescroll)
1308 trackPanel.Refresh(false);
1309
1310 // why? Is there a menu item whose availability depends on scroll position
1311 // or zoom level?
1313
1314 if (scrollbarVisibilityChanged || resize)
1315 UpdateLayout();
1316}
1317
1319[]( wxWindow &window ){
1320 auto pProjectWindow = dynamic_cast< ProjectWindow* >( &window );
1321 return pProjectWindow ? pProjectWindow->GetTopPanel() : nullptr;
1322} };
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()
const TranslatableString name
Definition: Distortion.cpp:76
#define field(n, t)
Definition: ImportAUP.cpp:165
#define _(s)
Definition: Internat.h:73
EventMonitor monitor
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:89
#define safenew
Definition: MemoryX.h:10
ProjectFileIOMessage
Subscribe to ProjectFileIO to receive messages; always in idle time.
Definition: ProjectFileIO.h:50
@ ProjectTitleChange
A normal occurrence.
IntSetting ProjectWindowY
bool CornersOnScreen(wxRect &r)
IntSetting ProjectWindowX
void GetDefaultWindowRect(wxRect *defRect)
IntSetting ProjectWindowWidth
BoolSetting ProjectWindowIconized
bool IsWindowAccessible(wxRect *requestedRect)
BoolSetting ProjectWindowMaximized
IntSetting ProjectWindowNormalHeight
IntSetting ProjectWindowHeight
@ VSBarID
@ HSBarID
@ NextID
@ FirstID
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
const auto project
THEME_API Theme theTheme
Definition: Theme.cpp:82
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:57
const_iterator begin() const
Definition: Project.cpp:22
const_reverse_iterator rbegin() const
Definition: Project.cpp:32
bool empty() const
Definition: Project.h:47
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static AudioIO * Get()
Definition: AudioIO.cpp:126
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:346
IteratorRange< ChannelIterator< ChannelType > > Channels()
Get range of channels with mutative access.
Definition: Channel.h:381
std::shared_ptr< ChannelType > GetChannel(size_t iChannel)
Retrieve a channel, cast to the given type.
Definition: Channel.h:320
static int GetTotalHeight(const TrackList &list)
Definition: ChannelView.cpp:62
static ChannelView & Get(Channel &channel)
bool GetMinimized() const
Definition: ChannelView.h:69
void SetMinimized(bool minimized)
static int GetChannelGroupHeight(const Track *pTrack)
Definition: ChannelView.cpp:39
void SetExpandedHeight(int height)
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:342
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:318
static CommandManager & Get(AudacityProject &project)
void UpdateMenus(bool checkActive=true)
CommandFlag GetUpdateFlags(bool quick=false) const
typename GlobalVariable< TopPanelHook, const std::function< wxWindow * >, nullptr, Options... >::Scope Scope
Specialization of Setting for int.
Definition: Prefs.h:356
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
static ProjectAudioIO & Get(AudacityProject &project)
const wxString & GetProjectTitle() const
Definition: ProjectFileIO.h:96
static ProjectFileIO & Get(AudacityProject &project)
static const StatusWidthFunctions & GetStatusWidthFunctions()
PlaybackScroller(AudacityProject *project)
A top-level window associated with a project.
std::shared_ptr< AudacityProject > FindProject()
A top-level window associated with a project, and handling scrollbars and zooming.
Definition: ProjectWindow.h:36
void OnToolBarUpdate(wxCommandEvent &event)
wxScrollBar * mHsbar
wxSplitterWindow * mContainerWindow
int GetVerticalThumbSize() const
static ProjectWindow & Get(AudacityProject &project)
int GetTrackHeight(const Track &track)
wxPanel * mTopPanel
void SetMinimized(Track &track, bool minimized)
void ShowHorizontalScrollbar(bool shown)
unsigned MinimumTrackHeight()
void UpdatePrefs() override
void OnActivate(wxActivateEvent &event)
void ApplyUpdatedTheme()
void SetHorizontalThumbPosition(int viewStart)
void OnScrollLeftButton(wxScrollEvent &event)
void OnUpdateUI(wxUpdateUIEvent &event)
bool IsTrackMinimized(const Track &track)
bool IsActive() override
wxPanel * GetTopPanel() noexcept
Top panel contains project-related controls and tools.
int GetVerticalRange() const
void SetVerticalScrollbar(int position, int thumbSize, int range, int pageSize, bool refresh)
int GetVerticalThumbPosition() const
static ProjectWindow * Find(AudacityProject *pProject)
Observer::Subscription mThemeChangeSubscription
wxWindow * GetTrackListWindow() noexcept
Track list window is the parent container for TrackPanel.
void OnMove(wxMoveEvent &event)
wxSplitterWindow * GetContainerWindow() noexcept
Container is a parent window for both effects panel and track list windows.
void SetNormalizedWindowState(wxRect pSizeAndLocation)
void OnViewportMessage(const ViewportMessage &message)
void OnMouseEvent(wxMouseEvent &event)
void SetVerticalThumbPosition(int viewStart)
~ProjectWindow() override
void SetChannelHeights(Track &track, unsigned height)
bool IsBeingDeleted() const
Definition: ProjectWindow.h:56
void OnScrollRightButton(wxScrollEvent &event)
Observer::Subscription mTitleChangeSubscription
int GetTotalHeight(const TrackList &trackList)
void OnScroll(wxScrollEvent &event)
std::unique_ptr< PlaybackScroller > mPlaybackScroller
wxStatusBar * CreateProjectStatusBar()
void OnSize(wxSizeEvent &event)
void UpdateStatusWidths()
void OnProjectTitleChange(ProjectFileIOMessage)
void ShowVerticalScrollbar(bool shown)
void SetHorizontalScrollbar(int position, int thumbSize, int range, int pageSize, bool refresh)
void OnThemeChange(struct ThemeChangeMessage)
int GetHorizontalThumbSize() const
wxScrollBar * mVsbar
void OnMenu(wxCommandEvent &event)
int GetHorizontalRange() const
bool IsIconized() const override
int GetHorizontalThumbPosition() const
void SetToDefaultSize()
void OnShow(wxShowEvent &event)
std::pair< int, int > ViewportSize() const
wxWindow * mTrackListWindow
ProjectWindow(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, AudacityProject &project)
void OnIconize(wxIconizeEvent &event)
void MacShowUndockedToolbars(bool show)
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:213
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
Abstract base class for status bar fields.
Definition: ProjectStatus.h:38
wxColour & Colour(int iIndex)
class ToolFrame
Definition: ToolManager.h:189
static ToolManager & Get(AudacityProject &project)
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:850
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:234
static double GetPinnedHeadPositionPreference()
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
A callback facade hiding GUI toolkit details.
Definition: Viewport.h:22
void OnScrollLeftButton()
Definition: Viewport.cpp:144
void SetCallbacks(std::unique_ptr< ViewportCallbacks > pCallbacks)
Definition: Viewport.cpp:64
void OnScrollRightButton()
Definition: Viewport.cpp:161
void OnScroll()
Definition: Viewport.cpp:399
void HandleResize()
Definition: Viewport.cpp:389
static Viewport & Get(AudacityProject &project)
Definition: Viewport.cpp:33
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
ScrollBar(wxWindow *parent, wxWindowID id, long style)
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:201
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:375
AUDACITY_DLL_API unsigned MinimumTrackHeight()
void OnCloseWindow(wxCloseEvent &e)
AttachedWindows::RegisteredFactory sProjectWindowKey
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
static std::size_t Count(const AudacityProject *project)
Returns the number of fields in the registry. If project is no null, only visible fields are counted.
static void Visit(const StatusBarFieldRegistryVisitor &visitor)
Visits all fields in the registry in order.
static void OnSize(AudacityProject &project)
Handle OnSize event for all fields in the registry.
std::optional< PreferredSystemAppearance > appearance
Definition: Theme.h:111
bool IsTrackMinimized(const Track &track) override
void SetVerticalScrollbar(int position, int thumbSize, int range, int pageSize, bool refresh) override
void SetHorizontalScrollbar(int position, int thumbSize, int range, int pageSize, bool refresh) override
int GetTrackHeight(const Track &track) override
int GetTotalHeight(const TrackList &trackList) override
void SetVerticalThumbPosition(int viewStart) override
void SetMinimized(Track &track, bool minimized) override
void SetChannelHeights(Track &track, unsigned height) override
void SetHorizontalThumbPosition(int viewStart) override
std::pair< int, int > ViewportSize() const override
Width and height in pixels of proper viewport area (excluding scrollbars)