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 "HelpSystem.h"
43
44#if wxUSE_ACCESSIBILITY
45#include "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 node.enabled = (w != nullptr);
489 if (stack.empty()) {
490 // Parameters are: AddPage(page, name, IsSelected, imageId).
491 if (w)
492 mCategories->AddPage(w, w->GetName(), false, 0);
493 }
494 else {
495 IntPair &top = *stack.rbegin();
496 if (w)
497 mCategories->InsertSubPage(top.first,
498 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 node.enabled = (mUniquePage != nullptr);
522 if (mUniquePage) {
523 wxWindow * uniquePageWindow = S.Prop(1)
524 .Position(wxEXPAND)
525 .AddWindow(mUniquePage);
526 // We're not in the wxTreebook, so add the accelerator here
527 wxAcceleratorEntry entries[1];
528#if defined(__WXMAC__)
529 // Is there a standard shortcut on Mac?
530#else
531 entries[0].Set(wxACCEL_NORMAL, (int) WXK_F1, wxID_HELP);
532#endif
533 wxAcceleratorTable accel(1, entries);
534 uniquePageWindow->SetAcceleratorTable(accel);
535 }
536 }
537 }
538 S.EndVerticalLay();
539
540 S.AddStandardButtons(eOkButton | eCancelButton | ePreviewButton | eHelpButton);
541
543 wxWindow *const previewButton =
544 wxWindow::FindWindowById(wxID_PREVIEW, GetParent());
545 previewButton->Show(false);
546 }
547
548#if defined(__WXGTK__)
549 if (mCategories)
550 mCategories->GetTreeCtrl()->EnsureVisible(mCategories->GetTreeCtrl()->GetRootItem());
551#endif
552
553// mCategories->SetMaxSize({ 790, 600 }); // 790 = 800 - (border * 2)
554 Layout();
555 Fit();
556 wxSize sz = GetSize();
557
558 // Collapse nodes only after layout so the tree is wide enough
559 if (mCategories)
560 {
561 int iPage = 0;
562 for (auto it = factories.begin(), end = factories.end();
563 it != end; ++it) {
564 if (it->enabled)
565 mCategories->ExpandNode(iPage++, it->expanded);
566 }
567 }
568
569 // This ASSERT was originally used to limit us to 800 x 600.
570 // However, the range of screen sizes and dpi of modern (2018) displays
571 // makes pixel dimensions an inadequate measure of usability, so
572 // now we only ASSERT that preferences will fit in the client display
573 // rectangle of the developer / tester's monitor.
574 // Use scrollers when necessary to ensure that preference pages will
575 // be fully visible.
576 wxRect screenRect(wxGetClientDisplayRect());
577 wxASSERT_MSG(sz.x <= screenRect.width && sz.y <= screenRect.height, wxT("Preferences dialog exceeds max size"));
578
579 sz.DecTo(screenRect.GetSize());
580
581 if( !mUniquePage ){
582 int prefWidth, prefHeight;
583 gPrefs->Read(wxT("/Prefs/Width"), &prefWidth, sz.x);
584 gPrefs->Read(wxT("/Prefs/Height"), &prefHeight, wxMax(480,sz.y));
585
586 wxSize prefSize = wxSize(prefWidth, prefHeight);
587 prefSize.DecTo(screenRect.GetSize());
588 SetSize(prefSize);
589 InvalidateBestSize();
590 Layout();
591 }
592 SetMinSize(sz);
593
594 // Center after all that resizing, but make sure it doesn't end up
595 // off-screen
596 CentreOnParent();
597
598 mTransaction = std::make_unique< SettingTransaction >();
599}
600
602{
603}
604
606{
607 if (mCategories) {
608 /* long is signed, size_t is unsigned. On some platforms they are different
609 * lengths as well. So we must check that the stored category is both > 0
610 * and within the possible range of categories, making the first check on the
611 * _signed_ value to avoid issues when converting an unsigned one.
612 */
613 long selected = GetPreferredPage();
614 if (selected < 0 || size_t(selected) >= mCategories->GetPageCount())
615 selected = 0; // clamp to available range of tabs
616 mCategories->SetSelection(selected);
617 }
618 else if (mUniquePage) {
619 auto Temp = mTitlePrefix;
620 Temp.Join( Verbatim( mUniquePage->GetLabel() ), wxT(" ") );
621 SetTitle(Temp);
622 SetName(Temp);
623 }
624
625 return wxDialogWrapper::ShowModal();
626}
627
628void PrefsDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
629{
631
632 if (mCategories) {
633 for (size_t i = 0; i < mCategories->GetPageCount(); i++) {
634 ((PrefsPanel *)mCategories->GetPage(i))->Cancel();
635 }
636 }
637 else if (mUniquePage)
639
640 // Remember modified dialog size, even if cancelling.
641 if( !mUniquePage ){
642 wxSize sz = GetSize();
643 gPrefs->Write(wxT("/Prefs/Width"), sz.x);
644 gPrefs->Write(wxT("/Prefs/Height"), sz.y);
645 }
646 gPrefs->Flush();
647
648 EndModal(false);
649}
650
652{
653 if( mCategories)
654 return static_cast<PrefsPanel*>(mCategories->GetCurrentPage());
655 else
656 return mUniquePage;
657}
658
659void PrefsDialog::OnPreview(wxCommandEvent & WXUNUSED(event))
660{
661 if (const auto pPanel = GetCurrentPanel())
662 pPanel->Preview();
663}
664
665void PrefsDialog::OnHelp(wxCommandEvent & WXUNUSED(event))
666{
667 if (const auto pPanel = GetCurrentPanel()) {
668 const auto &page = pPanel->HelpPageName();
669 HelpSystem::ShowHelp(this, page, true);
670 }
671}
672
674{
675 // Validate all pages first
676 if (mCategories) {
677 for (size_t i = 0; i < mCategories->GetPageCount(); i++) {
678 S.ResetId();
679 PrefsPanel *panel = (PrefsPanel *)mCategories->GetPage(i);
680 panel->PopulateOrExchange( S );
681 }
682 }
683 else
684 {
685 S.ResetId();
686 if (mUniquePage)
688 }
689}
690
691void PrefsDialog::OnTreeKeyDown(wxTreeEvent & event)
692{
693 if(event.GetKeyCode() == WXK_RETURN)
694 OnOK(event);
695 else
696 event.Skip(); // Ensure standard behavior when enter is not pressed
697}
698
699void PrefsDialog::OnOK(wxCommandEvent & WXUNUSED(event))
700{
702
703 // Validate all pages first
704 if (mCategories) {
705 for (size_t i = 0; i < mCategories->GetPageCount(); i++) {
706 PrefsPanel *panel = (PrefsPanel *)mCategories->GetPage(i);
707
708 // The dialog doesn't end until all the input is valid
709 if (!panel->Validate()) {
710 mCategories->SetSelection(i);
711 return;
712 }
713 }
714 }
715 else if (mUniquePage) {
716 if (!mUniquePage->Validate())
717 return;
718 }
719
720 // flush now so toolbars will know their position.
721 gPrefs->Flush();
722 if (mCategories) {
723 // Now apply the changes
724 // Reverse order - so Track Name is updated before language change
725 // A workaround for Bug 1661
726 for (int i = (int)mCategories->GetPageCount()-1; i>= 0; i--) {
727 PrefsPanel *panel = (PrefsPanel *)mCategories->GetPage(i);
728
729 panel->Preview();
730 panel->Commit();
731 }
732 }
733 else if (mUniquePage) {
736 }
737
738 if( !mUniquePage ){
739 wxSize sz = GetSize();
740 gPrefs->Write(wxT("/Prefs/Width"), sz.x);
741 gPrefs->Write(wxT("/Prefs/Height"), sz.y);
742 }
743 gPrefs->Flush();
744
746
747#if USE_PORTMIXER
748 auto gAudioIO = AudioIOBase::Get();
749 if (gAudioIO) {
750 // We cannot have opened this dialog if gAudioIO->IsAudioTokenActive(),
751 // per the setting of AudioIONotBusyFlag and AudioIOBusyFlag in
752 // AudacityProject::GetUpdateFlags().
753 // However, we can have an invalid audio token (so IsAudioTokenActive()
754 // is false), but be monitoring.
755 // If monitoring, have to stop the stream, so HandleDeviceChange() can work.
756 // We could disable the Preferences command while monitoring, i.e.,
757 // set AudioIONotBusyFlag/AudioIOBusyFlag according to monitoring, as well.
758 // Instead allow it because unlike recording, for example, monitoring
759 // is not clearly something that should prohibit opening prefs.
760 // TODO: We *could* be smarter in this method and call HandleDeviceChange()
761 // only when the device choices actually changed. True of lots of prefs!
762 // As is, we always stop monitoring before handling the device change.
763 if (gAudioIO->IsMonitoring())
764 {
765 gAudioIO->StopStream();
766 while (gAudioIO->IsBusy()) {
767 using namespace std::chrono;
768 std::this_thread::sleep_for(100ms);
769 }
770 }
771 gAudioIO->HandleDeviceChange();
772 }
773#endif
774
775 // PRL: Is the following concern still valid, now that prefs update is
776 // handled instead by delayed event processing?
777
778 // LL: wxMac can't handle recreating the menus when this dialog is still active,
779 // so AudacityProject::UpdatePrefs() or any of the routines it calls must
780 // not cause MenuCreator::RebuildMenuBar() to be executed.
781
783
784 mTransaction->Commit();
785
786 if( IsModal() )
787 EndModal(true);
788 else
789 Destroy();
790}
791
792void PrefsDialog::SelectPageByName(const wxString &pageName)
793{
794 if (mCategories) {
795 size_t n = mCategories->GetPageCount();
796
797 for (size_t i = 0; i < n; i++) {
798 if (mCategories->GetPageText(i) == pageName) {
799 mCategories->SetSelection(i);
800 // This covers the case, when ShowModal is called
801 // after selecting the page.
802 // ShowModal will select the page previously used by
803 // user
805 return;
806 }
807 }
808 }
809}
810
812{
813 if (mCategories)
814 return mCategories->GetSelection();
815 else
816 return 0;
817}
818
820 wxWindow * parent, AudacityProject *pProject,
821 PrefsPanel::Factories &factories)
822 : PrefsDialog(parent, pProject, XO("Preferences:"), factories)
823{
824}
825
827{
828}
829
831{
832 long prefscat = gPrefs->Read(wxT("/Prefs/PrefsCategory"), 0L);
833 return prefscat;
834}
835
837{
838 gPrefs->Write(wxT("/Prefs/PrefsCategory"), (long)GetSelectedPage());
839 gPrefs->Flush();
840}
841
843{
844 // Remember expansion state of the tree control
845 if (mCategories)
846 {
847 int iPage = 0;
848 for (auto it = mFactories.begin(), end = mFactories.end();
849 it != end; ++it) {
850 if (it->enabled)
851 it->expanded = mCategories->IsNodeExpanded(iPage++);
852 }
853 }
854 else
855 mFactories[0].expanded = true;
856}
857
858#include <wx/frame.h>
859#include "../Menus.h"
860#include "Project.h"
861
863{
865
866 {
867 GlobalPrefsDialog dialog(
868 &GetProjectFrame( project ) /* parent */, &project );
869 wxCommandEvent Evt;
870 //dialog.Show();
871 dialog.OnOK(Evt);
872 }
873
874 // LL: Moved from PrefsDialog since wxWidgets on OSX can't deal with
875 // rebuilding the menus while the PrefsDialog is still in the modal
876 // state.
877 for (auto p : AllProjects{}) {
879// TODO: The comment below suggests this workaround is obsolete.
880#if defined(__WXGTK__)
881 // Workaround for:
882 //
883 // http://bugzilla.audacityteam.org/show_bug.cgi?id=458
884 //
885 // This workaround should be removed when Audacity updates to wxWidgets
886 // 3.x which has a fix.
887 auto &window = GetProjectFrame( *p );
888 wxRect r = window.GetRect();
889 window.SetSize(wxSize(1,1));
890 window.SetSize(r.GetSize());
891#endif
892 }
893}
wxT("CloseDown"))
END_EVENT_TABLE()
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
#define _(s)
Definition: Internat.h:73
#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:37
@ eOkButton
Definition: ShuttleGui.h:594
@ eCancelButton
Definition: ShuttleGui.h:595
@ eHelpButton
Definition: ShuttleGui.h:598
@ ePreviewButton
Definition: ShuttleGui.h:599
#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:93
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:233
bool empty() const
Definition: Identifier.h:61
void RebuildMenuBar(AudacityProject &project)
Definition: Menus.cpp:497
static MenuManager & Get(AudacityProject &project)
Definition: Menus.cpp:69
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: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:92
virtual bool ShowsPreviewButton()
Definition: PrefsPanel.cpp:87
virtual bool Commit()=0
virtual void Preview()
Definition: PrefsPanel.h:105
virtual void Cancel()
Definition: PrefsPanel.cpp:83
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:625
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, GroupItemBase *pGroup, const GroupItemBase *pToMerge, const OrderingHint &hint, bool &doFlush)
Definition: Registry.cpp:643
static RegisteredToolbarFactory factory
static void ReinitializeAll()
Definition: Prefs.cpp:497