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