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