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