Audacity 3.2.0
PrefsDialog.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 PrefsDialog.cpp
6
7 Joshua Haberman
8 James Crook
9
10*******************************************************************//*******************************************************************/
16
17
18#include "PrefsDialog.h"
19
20#include <thread>
21
22#include <wx/app.h>
23#include <wx/setup.h> // for wxUSE_* macros
24#include <wx/defs.h>
25#include <wx/button.h>
26#include <wx/dialog.h>
27#include <wx/event.h>
28#include <wx/font.h>
29#include <wx/gdicmn.h>
30#include <wx/intl.h>
31#include <wx/listbox.h>
32#include <wx/sizer.h>
33
34#include <wx/listbook.h>
35
36#include <wx/treebook.h>
37#include <wx/treectrl.h>
38
39#include "AudioIOBase.h"
40#include "Prefs.h"
41#include "ProjectWindows.h"
42#include "../ShuttleGui.h"
43#include "../commands/CommandManager.h"
44
45#include "PrefsPanel.h"
46
47#include "../widgets/HelpSystem.h"
48
49#if wxUSE_ACCESSIBILITY
50#include "../widgets/WindowAccessible.h"
51#endif
52
53
54#if wxUSE_ACCESSIBILITY
55
56#ifndef __WXMAC__
57
58// Just an alias
59using TreeCtrlAx = WindowAccessible;
60
61#else
62
63// utility functions
64namespace {
65 template< typename Result, typename Fn >
66 Result VisitItems( const wxTreeCtrl &ctrl, Fn fn )
67 {
68 // Do preorder visit of items in the tree until satisfying a test
69 std::vector< wxTreeItemId > stack;
70 stack.push_back( ctrl.GetRootItem() );
71 unsigned position = 0;
72 while ( !stack.empty() ) {
73 auto itemId = stack.back();
74 auto pair = fn( itemId, position );
75 if ( pair.first )
76 return pair.second;
77
78 wxTreeItemIdValue cookie;
79 auto childId = ctrl.GetFirstChild( itemId, cookie );
80 if ( childId )
81 stack.push_back( childId );
82 else do {
83 auto &id = stack.back();
84 if ( !!( id = ctrl.GetNextSibling( id ) ) )
85 break;
86 } while ( stack.pop_back(), !stack.empty() );
87
88 ++position;
89 }
90 return {};
91 }
92
93 unsigned FindItemPosition( const wxTreeCtrl &ctrl, wxTreeItemId id )
94 {
95 // Return the 1-based count of the item's position in the pre-order
96 // visit of the items in the tree (not counting the root item which we
97 // assume is a dummy that never matches id)
98 return VisitItems<unsigned>( ctrl,
99 [=]( wxTreeItemId itemId, unsigned position ){
100 return std::make_pair( itemId == id, position ); } );
101 }
102
103 wxTreeItemId FindItem( const wxTreeCtrl &ctrl, int nn )
104 {
105 // The inverse of the function above
106 return VisitItems<wxTreeItemId>( ctrl,
107 [=]( wxTreeItemId itemId, unsigned position ){
108 return std::make_pair( nn == position, itemId ); } );
109 }
110}
111
112// Define a custom class
113class TreeCtrlAx final
114 : public WindowAccessible
115{
116public:
117 TreeCtrlAx(wxTreeCtrl * ctrl);
118 virtual ~ TreeCtrlAx();
119
120 wxAccStatus GetChild(int childId, wxAccessible** child) override;
121
122 wxAccStatus GetChildCount(int* childCount) override;
123
124 wxAccStatus GetDefaultAction(int childId, wxString *actionName) override;
125
126 // Returns the description for this object or a child.
127 wxAccStatus GetDescription(int childId, wxString *description) override;
128
129 // Gets the window with the keyboard focus.
130 // If childId is 0 and child is NULL, no object in
131 // this subhierarchy has the focus.
132 // If this object has the focus, child should be 'this'.
133 wxAccStatus GetFocus(int *childId, wxAccessible **child) override;
134
135 // Returns help text for this object or a child, similar to tooltip text.
136 wxAccStatus GetHelpText(int childId, wxString *helpText) override;
137
138 // Returns the keyboard shortcut for this object or child.
139 // Return e.g. ALT+K
140 wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override;
141
142 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
143 // rect is in screen coordinates.
144 wxAccStatus GetLocation(wxRect& rect, int elementId) override;
145
146 // Gets the name of the specified object.
147 wxAccStatus GetName(int childId, wxString *name) override;
148
149 // Returns a role constant.
150 wxAccStatus GetRole(int childId, wxAccRole *role) override;
151
152 // Gets a variant representing the selected children
153 // of this object.
154 // Acceptable values:
155 // - a null variant (IsNull() returns TRUE)
156 // - a list variant (GetType() == wxT("list"))
157 // - an integer representing the selected child element,
158 // or 0 if this object is selected (GetType() == wxT("long"))
159 // - a "void*" pointer to a wxAccessible child object
160 //wxAccStatus GetSelections(wxVariant *selections) override;
161 // leave unimplemented
162
163 // Returns a state constant.
164 wxAccStatus GetState(int childId, long* state) override;
165
166 // Returns a localized string representing the value for the object
167 // or child.
168 wxAccStatus GetValue(int childId, wxString* strValue) override;
169
170 // Navigates from fromId to toId/toObject
171 // wxAccStatus Navigate(wxNavDir navDir, int fromId, int* toId, wxAccessible** toObject) override;
172
173 // Modify focus or selection
174 wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags) override;
175
176private:
177 wxTreeCtrl *GetCtrl() { return static_cast<wxTreeCtrl*>( GetWindow() ); }
178};
179
180TreeCtrlAx::TreeCtrlAx( wxTreeCtrl *ctrl )
181: WindowAccessible{ ctrl }
182{
183}
184
185TreeCtrlAx::~TreeCtrlAx() = default;
186
187wxAccStatus TreeCtrlAx::GetChild( int childId, wxAccessible** child )
188{
189 if( childId == wxACC_SELF )
190 {
191 *child = this;
192 }
193 else
194 {
195 *child = NULL;
196 }
197
198 return wxACC_OK;
199}
200
201wxAccStatus TreeCtrlAx::GetChildCount(int* childCount)
202{
203 auto ctrl = GetCtrl();
204 if (!ctrl)
205 return wxACC_FAIL;
206
207 *childCount = ctrl->GetCount();
208 return wxACC_OK;
209}
210
211wxAccStatus TreeCtrlAx::GetDefaultAction(int WXUNUSED(childId), wxString* actionName)
212{
213 actionName->clear();
214
215 return wxACC_OK;
216}
217
218// Returns the description for this object or a child.
219wxAccStatus TreeCtrlAx::GetDescription( int WXUNUSED(childId), wxString *description )
220{
221 description->clear();
222
223 return wxACC_OK;
224}
225
226// This isn't really used yet by wxWidgets as patched by Audacity for
227// Mac accessibility, as of Audacity 2.3.2, but here it is anyway, keeping the
228// analogy with TrackPanelAx
229wxAccStatus TreeCtrlAx::GetFocus( int *childId, wxAccessible **child )
230{
231 auto ctrl = GetCtrl();
232 if (!ctrl)
233 return wxACC_FAIL;
234
235 auto item = ctrl->GetFocusedItem();
236 auto id = FindItemPosition( *ctrl, item );
237 *childId = id;
238 *child = nullptr;
239 return wxACC_OK;
240}
241
242// Returns help text for this object or a child, similar to tooltip text.
243wxAccStatus TreeCtrlAx::GetHelpText( int WXUNUSED(childId), wxString *helpText )
244{
245 helpText->clear();
246
247 return wxACC_OK;
248}
249
250// Returns the keyboard shortcut for this object or child.
251// Return e.g. ALT+K
252wxAccStatus TreeCtrlAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut )
253{
254 shortcut->clear();
255
256 return wxACC_OK;
257}
258
259wxAccStatus TreeCtrlAx::GetLocation( wxRect& rect, int elementId )
260{
261 auto ctrl = GetCtrl();
262 if (!ctrl)
263 return wxACC_FAIL;
264
265 if (elementId == wxACC_SELF)
266 rect = ctrl->GetRect();
267 else {
268 auto item = FindItem( *ctrl, elementId );
269 if ( !( item && ctrl->GetBoundingRect( item, rect ) ) )
270 return wxACC_INVALID_ARG;
271 }
272 rect.SetPosition( ctrl->GetParent()->ClientToScreen( rect.GetPosition() ) );
273 return wxACC_OK;
274}
275
276wxAccStatus TreeCtrlAx::GetName(int childId, wxString* name)
277{
278 if ( childId == wxACC_SELF )
279 return WindowAccessible::GetName( childId, name );
280 else {
281 auto ctrl = GetCtrl();
282 if (!ctrl)
283 return wxACC_FAIL;
284
285 auto item = FindItem( *ctrl, childId );
286 if ( item ) {
287 *name = ctrl->GetItemText( item );
288 return wxACC_OK;
289 }
290 else
291 return wxACC_INVALID_ARG;
292 }
293}
294
295wxAccStatus TreeCtrlAx::GetRole( int childId, wxAccRole* role )
296{
297 // Not sure if this correct, but it is analogous with what we use in
298 // TrackPanel
299 *role =
300 childId == wxACC_SELF ? wxROLE_SYSTEM_PANE : wxROLE_SYSTEM_STATICTEXT;
301 return wxACC_OK;
302}
303
304// Returns a state constant.
305wxAccStatus TreeCtrlAx::GetState(int childId, long* state)
306{
307 auto ctrl = GetCtrl();
308 if (!ctrl)
309 return wxACC_FAIL;
310
311 *state = wxACC_STATE_SYSTEM_FOCUSABLE | wxACC_STATE_SYSTEM_SELECTABLE;
312
313 if ( childId != wxACC_SELF ) {
314 auto item = FindItem( *ctrl, childId );
315 if (item) {
316 if( item == ctrl->GetFocusedItem() )
317 *state |= wxACC_STATE_SYSTEM_FOCUSED;
318
319 if( item == ctrl->GetSelection() )
320 *state |= wxACC_STATE_SYSTEM_SELECTED;
321 }
322 }
323
324 return wxACC_OK;
325}
326
327// Returns a localized string representing the value for the object
328// or child.
329wxAccStatus TreeCtrlAx::GetValue(int childId, wxString* strValue)
330{
331 *strValue = wxString{};
332 return wxACC_OK;
333}
334
335//wxAccStatus TreeCtrlAx::Navigate(
336// wxNavDir navDir, int fromId, int* toId, wxAccessible** toObject)
337//{
338// to do
339//}
340
341// Modify focus or selection
342wxAccStatus TreeCtrlAx::Select(int childId, wxAccSelectionFlags selectFlags)
343{
344 auto ctrl = GetCtrl();
345 if (!ctrl)
346 return wxACC_FAIL;
347
348 if (childId != wxACC_SELF) {
349 int childCount;
350 GetChildCount( &childCount );
351 if (childId > childCount)
352 return wxACC_FAIL;
353
354 auto item = FindItem( *ctrl, childId );
355 if ( item ) {
356 if (selectFlags == wxACC_SEL_TAKEFOCUS)
357 ctrl->SetFocusedItem( item );
358 else if (selectFlags == wxACC_SEL_TAKESELECTION)
359 ctrl->SelectItem( item );
360 else
361 return wxACC_NOT_IMPLEMENTED;
362 return wxACC_OK;
363 }
364 }
365
366 return wxACC_NOT_IMPLEMENTED;
367}
368
369#endif
370
371#endif
372
373
374BEGIN_EVENT_TABLE(PrefsDialog, wxDialogWrapper)
379 EVT_TREE_KEY_DOWN(wxID_ANY, PrefsDialog::OnTreeKeyDown) // Handles key events when tree has focus
381
382
383class wxTreebookExt final : public wxTreebook
384{
385public:
386 wxTreebookExt( wxWindow *parent,
387 wxWindowID id, const TranslatableString &titlePrefix)
388 : wxTreebook( parent, id )
389 , mTitlePrefix(titlePrefix)
390 {;};
391 ~wxTreebookExt(){;};
392 int ChangeSelection(size_t n) override;
393 int SetSelection(size_t n) override;
394 const TranslatableString mTitlePrefix;
395};
396
397
398int wxTreebookExt::ChangeSelection(size_t n) {
399 int i = wxTreebook::ChangeSelection(n);
400 wxString Temp = GetPageText( n );
401 static_cast<wxDialog*>(GetParent())->SetTitle( Temp );
402 static_cast<wxDialog*>(GetParent())->SetName( Temp );
403 return i;
404};
405
406int wxTreebookExt::SetSelection(size_t n)
407{
408 int i = wxTreebook::SetSelection(n);
409 auto Temp = mTitlePrefix.Translation() + wxT(" ") + GetPageText( n );
410 static_cast<wxDialog*>(GetParent())->SetTitle( Temp );
411 static_cast<wxDialog*>(GetParent())->SetName( Temp );
412
413 PrefsPanel *const panel = static_cast<PrefsPanel *>(GetPage(n));
414 const bool showHelp = (!panel->HelpPageName().empty());
415 const bool showPreview = panel->ShowsPreviewButton();
416 wxWindow *const helpButton = wxWindow::FindWindowById(wxID_HELP, GetParent());
417 wxWindow *const previewButton = wxWindow::FindWindowById(wxID_PREVIEW, GetParent());
418
419 if (helpButton) {
420 if (showHelp) {
421 wxAcceleratorEntry entries[1];
422#if defined(__WXMAC__)
423 // Is there a standard shortcut on Mac?
424#else
425 entries[0].Set(wxACCEL_NORMAL, (int) WXK_F1, wxID_HELP);
426#endif
427 wxAcceleratorTable accel(1, entries);
428 this->SetAcceleratorTable(accel);
429 }
430 else {
431 this->SetAcceleratorTable(wxNullAcceleratorTable);
432 }
433
434 const bool changed = helpButton->Show(showHelp);
435 if (changed)
436 GetParent()->Layout();
437 }
438
439 if (previewButton) { // might still be NULL during population
440 const bool changed = previewButton->Show(showPreview);
441 if (changed)
442 GetParent()->Layout();
443 }
444
445 return i;
446}
447
449 wxWindow * parent, AudacityProject *pProject,
450 const TranslatableString &titlePrefix,
451 PrefsPanel::Factories &factories)
452: wxDialogWrapper(parent, wxID_ANY, XO("Audacity Preferences"),
453 wxDefaultPosition,
454 wxDefaultSize,
455 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
456, mFactories(factories)
457, mTitlePrefix(titlePrefix)
458{
459 wxASSERT(factories.size() > 0);
460 const bool uniquePage = (factories.size() == 1);
461 SetLayoutDirection(wxLayout_LeftToRight);
462
463 ShuttleGui S(this, eIsCreating);
464
465 S.StartVerticalLay(true);
466 {
467 wxASSERT(factories.size() > 0);
468 if (!uniquePage) {
469 mCategories = safenew wxTreebookExt(S.GetParent(), wxID_ANY, mTitlePrefix);
470#if wxUSE_ACCESSIBILITY
471 // so that name can be set on a standard control
472 mCategories->GetTreeCtrl()->SetAccessible(
473 safenew TreeCtrlAx(mCategories->GetTreeCtrl()));
474#endif
475 // RJH: Prevent NVDA from reading "treeCtrl"
476 mCategories->GetTreeCtrl()->SetName(_("Category"));
477 S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, true);
478 {
479 S.Prop(1)
480 .Position(wxEXPAND)
481 .AddWindow(mCategories);
482
483 {
484 typedef std::pair<int, int> IntPair;
485 std::vector<IntPair> stack;
486 int iPage = 0;
487 for (auto it = factories.begin(), end = factories.end();
488 it != end; ++it, ++iPage)
489 {
490 const auto &node = *it;
491 const auto &factory = node.factory;
492 wxWindow *const w = factory(mCategories, wxID_ANY, pProject);
493 if (stack.empty())
494 // Parameters are: AddPage(page, name, IsSelected, imageId).
495 mCategories->AddPage(w, w->GetName(), false, 0);
496 else {
497 IntPair &top = *stack.rbegin();
498 mCategories->InsertSubPage(top.first, w, w->GetName(), false, 0);
499 if (--top.second == 0) {
500 // Expand all nodes before the layout calculation
501 mCategories->ExpandNode(top.first, true);
502 stack.pop_back();
503 }
504 }
505 if (node.nChildren > 0)
506 stack.push_back(IntPair(iPage, node.nChildren));
507 }
508 }
509 }
510 S.EndHorizontalLay();
511 }
512 else {
513 // TODO: Look into getting rid of mUniquePage and instead
514 // adding into mCategories, so there is just one page in mCategories.
515 // And then hiding the treebook.
516
517 // Unique page, don't show the factory
518 const auto &node = factories[0];
519 const auto &factory = node.factory;
520 mUniquePage = factory(S.GetParent(), wxID_ANY, pProject);
521 wxWindow * uniquePageWindow = S.Prop(1)
522 .Position(wxEXPAND)
523 .AddWindow(mUniquePage);
524 // We're not in the wxTreebook, so add the accelerator here
525 wxAcceleratorEntry entries[1];
526#if defined(__WXMAC__)
527 // Is there a standard shortcut on Mac?
528#else
529 entries[0].Set(wxACCEL_NORMAL, (int) WXK_F1, wxID_HELP);
530#endif
531 wxAcceleratorTable accel(1, entries);
532 uniquePageWindow->SetAcceleratorTable(accel);
533 }
534 }
535 S.EndVerticalLay();
536
537 S.AddStandardButtons(eOkButton | eCancelButton | ePreviewButton | eHelpButton);
538
540 wxWindow *const previewButton =
541 wxWindow::FindWindowById(wxID_PREVIEW, GetParent());
542 previewButton->Show(false);
543 }
544
545#if defined(__WXGTK__)
546 if (mCategories)
547 mCategories->GetTreeCtrl()->EnsureVisible(mCategories->GetTreeCtrl()->GetRootItem());
548#endif
549
550// mCategories->SetMaxSize({ 790, 600 }); // 790 = 800 - (border * 2)
551 Layout();
552 Fit();
553 wxSize sz = GetSize();
554
555 // Collapse nodes only after layout so the tree is wide enough
556 if (mCategories)
557 {
558 int iPage = 0;
559 for (auto it = factories.begin(), end = factories.end();
560 it != end; ++it, ++iPage)
561 mCategories->ExpandNode(iPage, it->expanded);
562 }
563
564 // This ASSERT was originally used to limit us to 800 x 600.
565 // However, the range of screen sizes and dpi of modern (2018) displays
566 // makes pixel dimensions an inadequate measure of usability, so
567 // now we only ASSERT that preferences will fit in the client display
568 // rectangle of the developer / tester's monitor.
569 // Use scrollers when necessary to ensure that preference pages will
570 // be fully visible.
571 wxRect screenRect(wxGetClientDisplayRect());
572 wxASSERT_MSG(sz.x <= screenRect.width && sz.y <= screenRect.height, wxT("Preferences dialog exceeds max size"));
573
574 sz.DecTo(screenRect.GetSize());
575
576 if( !mUniquePage ){
577 int prefWidth, prefHeight;
578 gPrefs->Read(wxT("/Prefs/Width"), &prefWidth, sz.x);
579 gPrefs->Read(wxT("/Prefs/Height"), &prefHeight, wxMax(480,sz.y));
580
581 wxSize prefSize = wxSize(prefWidth, prefHeight);
582 prefSize.DecTo(screenRect.GetSize());
583 SetSize(prefSize);
584 InvalidateBestSize();
585 Layout();
586 }
587 SetMinSize(sz);
588
589 // Center after all that resizing, but make sure it doesn't end up
590 // off-screen
591 CentreOnParent();
592
593 mTransaction = std::make_unique< SettingTransaction >();
594}
595
597{
598}
599
601{
602 if (mCategories) {
603 /* long is signed, size_t is unsigned. On some platforms they are different
604 * lengths as well. So we must check that the stored category is both > 0
605 * and within the possible range of categories, making the first check on the
606 * _signed_ value to avoid issues when converting an unsigned one.
607 */
608 long selected = GetPreferredPage();
609 if (selected < 0 || size_t(selected) >= mCategories->GetPageCount())
610 selected = 0; // clamp to available range of tabs
611 mCategories->SetSelection(selected);
612 }
613 else {
614 auto Temp = mTitlePrefix;
615 Temp.Join( Verbatim( mUniquePage->GetLabel() ), wxT(" ") );
616 SetTitle(Temp);
617 SetName(Temp);
618 }
619
620 return wxDialogWrapper::ShowModal();
621}
622
623void PrefsDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
624{
626
627 if (mCategories) {
628 for (size_t i = 0; i < mCategories->GetPageCount(); i++) {
629 ((PrefsPanel *)mCategories->GetPage(i))->Cancel();
630 }
631 }
632 else
634
635 // Remember modified dialog size, even if cancelling.
636 if( !mUniquePage ){
637 wxSize sz = GetSize();
638 gPrefs->Write(wxT("/Prefs/Width"), sz.x);
639 gPrefs->Write(wxT("/Prefs/Height"), sz.y);
640 }
641 gPrefs->Flush();
642
643 EndModal(false);
644}
645
647{
648 if( mCategories)
649 return static_cast<PrefsPanel*>(mCategories->GetCurrentPage());
650 else
651 {
652 wxASSERT( mUniquePage );
653 return mUniquePage;
654 }
655}
656
657void PrefsDialog::OnPreview(wxCommandEvent & WXUNUSED(event))
658{
660}
661
662void PrefsDialog::OnHelp(wxCommandEvent & WXUNUSED(event))
663{
664 const auto &page = GetCurrentPanel()->HelpPageName();
665 HelpSystem::ShowHelp(this, page, true);
666}
667
669{
670 // Validate all pages first
671 if (mCategories) {
672 for (size_t i = 0; i < mCategories->GetPageCount(); i++) {
673 S.ResetId();
674 PrefsPanel *panel = (PrefsPanel *)mCategories->GetPage(i);
675 panel->PopulateOrExchange( S );
676 }
677 }
678 else
679 {
680 S.ResetId();
682 }
683}
684
685void PrefsDialog::OnTreeKeyDown(wxTreeEvent & event)
686{
687 if(event.GetKeyCode() == WXK_RETURN)
688 OnOK(event);
689 else
690 event.Skip(); // Ensure standard behavior when enter is not pressed
691}
692
693void PrefsDialog::OnOK(wxCommandEvent & WXUNUSED(event))
694{
696
697 // Validate all pages first
698 if (mCategories) {
699 for (size_t i = 0; i < mCategories->GetPageCount(); i++) {
700 PrefsPanel *panel = (PrefsPanel *)mCategories->GetPage(i);
701
702 // The dialog doesn't end until all the input is valid
703 if (!panel->Validate()) {
704 mCategories->SetSelection(i);
705 return;
706 }
707 }
708 }
709 else {
710 if (!mUniquePage->Validate())
711 return;
712 }
713
714 // flush now so toolbars will know their position.
715 gPrefs->Flush();
716 if (mCategories) {
717 // Now apply the changes
718 // Reverse order - so Track Name is updated before language change
719 // A workaround for Bug 1661
720 for (int i = (int)mCategories->GetPageCount()-1; i>= 0; i--) {
721 PrefsPanel *panel = (PrefsPanel *)mCategories->GetPage(i);
722
723 panel->Preview();
724 panel->Commit();
725 }
726 }
727 else {
730 }
731
732 if( !mUniquePage ){
733 wxSize sz = GetSize();
734 gPrefs->Write(wxT("/Prefs/Width"), sz.x);
735 gPrefs->Write(wxT("/Prefs/Height"), sz.y);
736 }
737 gPrefs->Flush();
738
740
741#if USE_PORTMIXER
742 auto gAudioIO = AudioIOBase::Get();
743 if (gAudioIO) {
744 // We cannot have opened this dialog if gAudioIO->IsAudioTokenActive(),
745 // per the setting of AudioIONotBusyFlag and AudioIOBusyFlag in
746 // AudacityProject::GetUpdateFlags().
747 // However, we can have an invalid audio token (so IsAudioTokenActive()
748 // is false), but be monitoring.
749 // If monitoring, have to stop the stream, so HandleDeviceChange() can work.
750 // We could disable the Preferences command while monitoring, i.e.,
751 // set AudioIONotBusyFlag/AudioIOBusyFlag according to monitoring, as well.
752 // Instead allow it because unlike recording, for example, monitoring
753 // is not clearly something that should prohibit opening prefs.
754 // TODO: We *could* be smarter in this method and call HandleDeviceChange()
755 // only when the device choices actually changed. True of lots of prefs!
756 // As is, we always stop monitoring before handling the device change.
757 if (gAudioIO->IsMonitoring())
758 {
759 gAudioIO->StopStream();
760 while (gAudioIO->IsBusy()) {
761 using namespace std::chrono;
762 std::this_thread::sleep_for(100ms);
763 }
764 }
765 gAudioIO->HandleDeviceChange();
766 }
767#endif
768
769 // PRL: Is the following concern still valid, now that prefs update is
770 // handled instead by delayed event processing?
771
772 // LL: wxMac can't handle recreating the menus when this dialog is still active,
773 // so AudacityProject::UpdatePrefs() or any of the routines it calls must
774 // not cause MenuCreator::RebuildMenuBar() to be executed.
775
777
778 mTransaction->Commit();
779
780 if( IsModal() )
781 EndModal(true);
782 else
783 Destroy();
784}
785
786void PrefsDialog::SelectPageByName(const wxString &pageName)
787{
788 if (mCategories) {
789 size_t n = mCategories->GetPageCount();
790
791 for (size_t i = 0; i < n; i++) {
792 if (mCategories->GetPageText(i) == pageName) {
793 mCategories->SetSelection(i);
794 // This covers the case, when ShowModal is called
795 // after selecting the page.
796 // ShowModal will select the page previously used by
797 // user
799 return;
800 }
801 }
802 }
803}
804
806{
807 if (mCategories)
808 return mCategories->GetSelection();
809 else
810 return 0;
811}
812
814 wxWindow * parent, AudacityProject *pProject,
815 PrefsPanel::Factories &factories)
816 : PrefsDialog(parent, pProject, XO("Preferences:"), factories)
817{
818}
819
821{
822}
823
825{
826 long prefscat = gPrefs->Read(wxT("/Prefs/PrefsCategory"), 0L);
827 return prefscat;
828}
829
831{
832 gPrefs->Write(wxT("/Prefs/PrefsCategory"), (long)GetSelectedPage());
833 gPrefs->Flush();
834}
835
837{
838 // Remember expansion state of the tree control
839 if (mCategories)
840 {
841 int iPage = 0;
842 for (auto it = mFactories.begin(), end = mFactories.end();
843 it != end; ++it, ++iPage)
844 it->expanded = mCategories->IsNodeExpanded(iPage);
845 }
846 else
847 mFactories[0].expanded = true;
848}
849
850#include <wx/frame.h>
851#include "../Menus.h"
852#include "Project.h"
853
855{
857
858 {
859 GlobalPrefsDialog dialog(
860 &GetProjectFrame( project ) /* parent */, &project );
861 wxCommandEvent Evt;
862 //dialog.Show();
863 dialog.OnOK(Evt);
864 }
865
866 // LL: Moved from PrefsDialog since wxWidgets on OSX can't deal with
867 // rebuilding the menus while the PrefsDialog is still in the modal
868 // state.
869 for (auto p : AllProjects{}) {
871// TODO: The comment below suggests this workaround is obsolete.
872#if defined(__WXGTK__)
873 // Workaround for:
874 //
875 // http://bugzilla.audacityteam.org/show_bug.cgi?id=458
876 //
877 // This workaround should be removed when Audacity updates to wxWidgets
878 // 3.x which has a fix.
879 auto &window = GetProjectFrame( *p );
880 wxRect r = window.GetRect();
881 window.SetSize(wxSize(1,1));
882 window.SetSize(r.GetSize());
883#endif
884 }
885}
static RegisteredToolbarFactory factory
END_EVENT_TABLE()
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:82
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:71
void DoReloadPreferences(AudacityProject &project)
static ProjectFileIORegistry::AttributeReaderEntries entries
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 ...
accessors for certain important windows associated with each project
@ eIsCreating
Definition: ShuttleGui.h:39
@ eOkButton
Definition: ShuttleGui.h:597
@ eCancelButton
Definition: ShuttleGui.h:598
@ eHelpButton
Definition: ShuttleGui.h:601
@ ePreviewButton
Definition: ShuttleGui.h:602
#define S(N)
Definition: ToChars.cpp:64
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
int id
static const auto fn
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 AudioIOBase * Get()
Definition: AudioIOBase.cpp:91
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
GlobalPrefsDialog(wxWindow *parent, AudacityProject *pProject, PrefsPanel::Factories &factories=PrefsPanel::DefaultFactories())
virtual ~GlobalPrefsDialog()
void SavePreferredPage() override
long GetPreferredPage() override
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:237
bool empty() const
Definition: Identifier.h:61
void RebuildMenuBar(AudacityProject &project)
Definition: Menus.cpp:486
static MenuManager & Get(AudacityProject &project)
Definition: Menus.cpp:71
Dialog that shows the current PrefsPanel in a tabbed divider.
Definition: PrefsDialog.h:35
void OnPreview(wxCommandEvent &e)
void ShuttleAll(ShuttleGui &S)
void RecordExpansionState()
void OnHelp(wxCommandEvent &e)
int GetSelectedPage() const
const TranslatableString mTitlePrefix
Definition: PrefsDialog.h:74
PrefsPanel * GetCurrentPanel()
void OnTreeKeyDown(wxTreeEvent &e)
void OnCancel(wxCommandEvent &e)
wxTreebook * mCategories
Definition: PrefsDialog.h:71
virtual void SavePreferredPage()=0
std::unique_ptr< SettingTransaction > mTransaction
Definition: PrefsDialog.h:76
PrefsDialog(wxWindow *parent, AudacityProject *pProject, const TranslatableString &titlePrefix=XO("Preferences:"), PrefsPanel::Factories &factories=PrefsPanel::DefaultFactories())
virtual long GetPreferredPage()=0
void OnOK(wxCommandEvent &e)
void SelectPageByName(const wxString &pageName)
PrefsPanel * mUniquePage
Definition: PrefsDialog.h:72
virtual ~PrefsDialog()
int ShowModal() override
PrefsPanel::Factories & mFactories
Definition: PrefsDialog.h:73
static void Broadcast(int id=0)
Call this static function to notify all PrefsListener objects.
Definition: Prefs.cpp:98
Base class for a panel in the PrefsDialog. Classes derived from this class include BatchPrefs,...
Definition: PrefsPanel.h:51
virtual ManualPageID HelpPageName()
If not empty string, the Help button is added below the panel.
Definition: PrefsPanel.cpp:98
virtual bool ShowsPreviewButton()
Definition: PrefsPanel.cpp:93
virtual bool Commit()=0
virtual void Preview()
Definition: PrefsPanel.h:101
virtual void Cancel()
Definition: PrefsPanel.cpp:89
virtual void PopulateOrExchange(ShuttleGui &WXUNUSED(S))
Definition: PrefsPanel.h:116
std::vector< PrefsPanel::PrefsNode > Factories
Definition: PrefsPanel.h:69
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
Holds a msgid for the translation catalog; may also bind format arguments.
TranslatableString & Join(TranslatableString arg, const wxString &separator={}) &
Append another translatable string.
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void SetTitle(const TranslatableString &title)
A wxTreebook is a class like wxNotebook, but not yet supported by wxWidgets 2.6.3.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
void VisitItems(Registry::Visitor &visitor, CollectedItems &collection, Path &path, GroupItem *pGroup, const GroupItem *pToMerge, const OrderingHint &hint, bool &doFlush)
Definition: Registry.cpp:620
static void ReinitializeAll()
Definition: Prefs.cpp:497