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