Audacity  3.0.3
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 <wx/app.h>
21 #include <wx/setup.h> // for wxUSE_* macros
22 #include <wx/defs.h>
23 #include <wx/button.h>
24 #include <wx/dialog.h>
25 #include <wx/event.h>
26 #include <wx/font.h>
27 #include <wx/gdicmn.h>
28 #include <wx/intl.h>
29 #include <wx/listbox.h>
30 #include <wx/sizer.h>
31 
32 #include <wx/listbook.h>
33 
34 #include <wx/treebook.h>
35 #include <wx/treectrl.h>
36 
37 #include "../AudioIOBase.h"
38 #include "../Prefs.h"
39 #include "../ShuttleGui.h"
40 #include "../commands/CommandManager.h"
41 
42 #include "PrefsPanel.h"
43 
44 #include "../widgets/HelpSystem.h"
45 
46 #if wxUSE_ACCESSIBILITY
47 #include "../widgets/WindowAccessible.h"
48 #endif
49 
50 
51 #if wxUSE_ACCESSIBILITY
52 
53 #ifndef __WXMAC__
54 
55 // Just an alias
56 using TreeCtrlAx = WindowAccessible;
57 
58 #else
59 
60 // utility functions
61 namespace {
62  template< typename Result, typename Fn >
63  Result VisitItems( const wxTreeCtrl &ctrl, Fn fn )
64  {
65  // Do preorder visit of items in the tree until satisfying a test
66  std::vector< wxTreeItemId > stack;
67  stack.push_back( ctrl.GetRootItem() );
68  unsigned position = 0;
69  while ( !stack.empty() ) {
70  auto itemId = stack.back();
71  auto pair = fn( itemId, position );
72  if ( pair.first )
73  return pair.second;
74 
75  wxTreeItemIdValue cookie;
76  auto childId = ctrl.GetFirstChild( itemId, cookie );
77  if ( childId )
78  stack.push_back( childId );
79  else do {
80  auto &id = stack.back();
81  if ( !!( id = ctrl.GetNextSibling( id ) ) )
82  break;
83  } while ( stack.pop_back(), !stack.empty() );
84 
85  ++position;
86  }
87  return {};
88  }
89 
90  unsigned FindItemPosition( const wxTreeCtrl &ctrl, wxTreeItemId id )
91  {
92  // Return the 1-based count of the item's position in the pre-order
93  // visit of the items in the tree (not counting the root item which we
94  // assume is a dummy that never matches id)
95  return VisitItems<unsigned>( ctrl,
96  [=]( wxTreeItemId itemId, unsigned position ){
97  return std::make_pair( itemId == id, position ); } );
98  }
99 
100  wxTreeItemId FindItem( const wxTreeCtrl &ctrl, int nn )
101  {
102  // The inverse of the function above
103  return VisitItems<wxTreeItemId>( ctrl,
104  [=]( wxTreeItemId itemId, unsigned position ){
105  return std::make_pair( nn == position, itemId ); } );
106  }
107 }
108 
109 // Define a custom class
110 class TreeCtrlAx final
111  : public WindowAccessible
112 {
113 public:
114  TreeCtrlAx(wxTreeCtrl * ctrl);
115  virtual ~ TreeCtrlAx();
116 
117  wxAccStatus GetChild(int childId, wxAccessible** child) override;
118 
119  wxAccStatus GetChildCount(int* childCount) override;
120 
121  wxAccStatus GetDefaultAction(int childId, wxString *actionName) override;
122 
123  // Returns the description for this object or a child.
124  wxAccStatus GetDescription(int childId, wxString *description) override;
125 
126  // Gets the window with the keyboard focus.
127  // If childId is 0 and child is NULL, no object in
128  // this subhierarchy has the focus.
129  // If this object has the focus, child should be 'this'.
130  wxAccStatus GetFocus(int *childId, wxAccessible **child) override;
131 
132  // Returns help text for this object or a child, similar to tooltip text.
133  wxAccStatus GetHelpText(int childId, wxString *helpText) override;
134 
135  // Returns the keyboard shortcut for this object or child.
136  // Return e.g. ALT+K
137  wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override;
138 
139  // Returns the rectangle for this object (id = 0) or a child element (id > 0).
140  // rect is in screen coordinates.
141  wxAccStatus GetLocation(wxRect& rect, int elementId) override;
142 
143  // Gets the name of the specified object.
144  wxAccStatus GetName(int childId, wxString *name) override;
145 
146  // Returns a role constant.
147  wxAccStatus GetRole(int childId, wxAccRole *role) override;
148 
149  // Gets a variant representing the selected children
150  // of this object.
151  // Acceptable values:
152  // - a null variant (IsNull() returns TRUE)
153  // - a list variant (GetType() == wxT("list"))
154  // - an integer representing the selected child element,
155  // or 0 if this object is selected (GetType() == wxT("long"))
156  // - a "void*" pointer to a wxAccessible child object
157  //wxAccStatus GetSelections(wxVariant *selections) override;
158  // leave unimplemented
159 
160  // Returns a state constant.
161  wxAccStatus GetState(int childId, long* state) override;
162 
163  // Returns a localized string representing the value for the object
164  // or child.
165  wxAccStatus GetValue(int childId, wxString* strValue) override;
166 
167  // Navigates from fromId to toId/toObject
168  // wxAccStatus Navigate(wxNavDir navDir, int fromId, int* toId, wxAccessible** toObject) override;
169 
170  // Modify focus or selection
171  wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags) override;
172 
173 private:
174  wxTreeCtrl *GetCtrl() { return static_cast<wxTreeCtrl*>( GetWindow() ); }
175 };
176 
177 TreeCtrlAx::TreeCtrlAx( wxTreeCtrl *ctrl )
178 : WindowAccessible{ ctrl }
179 {
180 }
181 
182 TreeCtrlAx::~TreeCtrlAx() = default;
183 
184 wxAccStatus TreeCtrlAx::GetChild( int childId, wxAccessible** child )
185 {
186  if( childId == wxACC_SELF )
187  {
188  *child = this;
189  }
190  else
191  {
192  *child = NULL;
193  }
194 
195  return wxACC_OK;
196 }
197 
198 wxAccStatus TreeCtrlAx::GetChildCount(int* childCount)
199 {
200  auto ctrl = GetCtrl();
201  if (!ctrl)
202  return wxACC_FAIL;
203 
204  *childCount = ctrl->GetCount();
205  return wxACC_OK;
206 }
207 
208 wxAccStatus TreeCtrlAx::GetDefaultAction(int WXUNUSED(childId), wxString* actionName)
209 {
210  actionName->clear();
211 
212  return wxACC_OK;
213 }
214 
215 // Returns the description for this object or a child.
216 wxAccStatus TreeCtrlAx::GetDescription( int WXUNUSED(childId), wxString *description )
217 {
218  description->clear();
219 
220  return wxACC_OK;
221 }
222 
223 // This isn't really used yet by wxWidgets as patched by Audacity for
224 // Mac accessibility, as of Audacity 2.3.2, but here it is anyway, keeping the
225 // analogy with TrackPanelAx
226 wxAccStatus TreeCtrlAx::GetFocus( int *childId, wxAccessible **child )
227 {
228  auto ctrl = GetCtrl();
229  if (!ctrl)
230  return wxACC_FAIL;
231 
232  auto item = ctrl->GetFocusedItem();
233  auto id = FindItemPosition( *ctrl, item );
234  *childId = id;
235  *child = nullptr;
236  return wxACC_OK;
237 }
238 
239 // Returns help text for this object or a child, similar to tooltip text.
240 wxAccStatus TreeCtrlAx::GetHelpText( int WXUNUSED(childId), wxString *helpText )
241 {
242  helpText->clear();
243 
244  return wxACC_OK;
245 }
246 
247 // Returns the keyboard shortcut for this object or child.
248 // Return e.g. ALT+K
249 wxAccStatus TreeCtrlAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut )
250 {
251  shortcut->clear();
252 
253  return wxACC_OK;
254 }
255 
256 wxAccStatus TreeCtrlAx::GetLocation( wxRect& rect, int elementId )
257 {
258  auto ctrl = GetCtrl();
259  if (!ctrl)
260  return wxACC_FAIL;
261 
262  if (elementId == wxACC_SELF)
263  rect = ctrl->GetRect();
264  else {
265  auto item = FindItem( *ctrl, elementId );
266  if ( !( item && ctrl->GetBoundingRect( item, rect ) ) )
267  return wxACC_INVALID_ARG;
268  }
269  rect.SetPosition( ctrl->GetParent()->ClientToScreen( rect.GetPosition() ) );
270  return wxACC_OK;
271 }
272 
273 wxAccStatus TreeCtrlAx::GetName(int childId, wxString* name)
274 {
275  if ( childId == wxACC_SELF )
276  return WindowAccessible::GetName( childId, name );
277  else {
278  auto ctrl = GetCtrl();
279  if (!ctrl)
280  return wxACC_FAIL;
281 
282  auto item = FindItem( *ctrl, childId );
283  if ( item ) {
284  *name = ctrl->GetItemText( item );
285  return wxACC_OK;
286  }
287  else
288  return wxACC_INVALID_ARG;
289  }
290 }
291 
292 wxAccStatus TreeCtrlAx::GetRole( int childId, wxAccRole* role )
293 {
294  // Not sure if this correct, but it is analogous with what we use in
295  // TrackPanel
296  *role =
297  childId == wxACC_SELF ? wxROLE_SYSTEM_PANE : wxROLE_SYSTEM_STATICTEXT;
298  return wxACC_OK;
299 }
300 
301 // Returns a state constant.
302 wxAccStatus TreeCtrlAx::GetState(int childId, long* state)
303 {
304  auto ctrl = GetCtrl();
305  if (!ctrl)
306  return wxACC_FAIL;
307 
308  *state = wxACC_STATE_SYSTEM_FOCUSABLE | wxACC_STATE_SYSTEM_SELECTABLE;
309 
310  if ( childId != wxACC_SELF ) {
311  auto item = FindItem( *ctrl, childId );
312  if (item) {
313  if( item == ctrl->GetFocusedItem() )
314  *state |= wxACC_STATE_SYSTEM_FOCUSED;
315 
316  if( item == ctrl->GetSelection() )
317  *state |= wxACC_STATE_SYSTEM_SELECTED;
318  }
319  }
320 
321  return wxACC_OK;
322 }
323 
324 // Returns a localized string representing the value for the object
325 // or child.
326 wxAccStatus TreeCtrlAx::GetValue(int childId, wxString* strValue)
327 {
328  *strValue = wxString{};
329  return wxACC_OK;
330 }
331 
332 //wxAccStatus TreeCtrlAx::Navigate(
333 // wxNavDir navDir, int fromId, int* toId, wxAccessible** toObject)
334 //{
335 // to do
336 //}
337 
338 // Modify focus or selection
339 wxAccStatus TreeCtrlAx::Select(int childId, wxAccSelectionFlags selectFlags)
340 {
341  auto ctrl = GetCtrl();
342  if (!ctrl)
343  return wxACC_FAIL;
344 
345  if (childId != wxACC_SELF) {
346  int childCount;
347  GetChildCount( &childCount );
348  if (childId > childCount)
349  return wxACC_FAIL;
350 
351  auto item = FindItem( *ctrl, childId );
352  if ( item ) {
353  if (selectFlags == wxACC_SEL_TAKEFOCUS)
354  ctrl->SetFocusedItem( item );
355  else if (selectFlags == wxACC_SEL_TAKESELECTION)
356  ctrl->SelectItem( item );
357  else
358  return wxACC_NOT_IMPLEMENTED;
359  return wxACC_OK;
360  }
361  }
362 
363  return wxACC_NOT_IMPLEMENTED;
364 }
365 
366 #endif
367 
368 #endif
369 
370 
371 BEGIN_EVENT_TABLE(PrefsDialog, wxDialogWrapper)
372  EVT_BUTTON(wxID_OK, PrefsDialog::OnOK)
373  EVT_BUTTON(wxID_CANCEL, PrefsDialog::OnCancel)
374  EVT_BUTTON(wxID_PREVIEW, PrefsDialog::OnPreview)
375  EVT_BUTTON(wxID_HELP, PrefsDialog::OnHelp)
376  EVT_TREE_KEY_DOWN(wxID_ANY, PrefsDialog::OnTreeKeyDown) // Handles key events when tree has focus
378 
379 
380 class wxTreebookExt final : public wxTreebook
381 {
382 public:
383  wxTreebookExt( wxWindow *parent,
384  wxWindowID id, const TranslatableString &titlePrefix)
385  : wxTreebook( parent, id )
386  , mTitlePrefix(titlePrefix)
387  {;};
388  ~wxTreebookExt(){;};
389  int ChangeSelection(size_t n) override;
390  int SetSelection(size_t n) override;
391  const TranslatableString mTitlePrefix;
392 };
393 
394 
395 int wxTreebookExt::ChangeSelection(size_t n) {
396  int i = wxTreebook::ChangeSelection(n);
397  wxString Temp = GetPageText( n );
398  static_cast<wxDialog*>(GetParent())->SetTitle( Temp );
399  static_cast<wxDialog*>(GetParent())->SetName( Temp );
400  return i;
401 };
402 
403 int wxTreebookExt::SetSelection(size_t n)
404 {
405  int i = wxTreebook::SetSelection(n);
406  auto Temp = mTitlePrefix.Translation() + wxT(" ") + GetPageText( n );
407  static_cast<wxDialog*>(GetParent())->SetTitle( Temp );
408  static_cast<wxDialog*>(GetParent())->SetName( Temp );
409 
410  PrefsPanel *const panel = static_cast<PrefsPanel *>(GetPage(n));
411  const bool showHelp = (!panel->HelpPageName().empty());
412  const bool showPreview = panel->ShowsPreviewButton();
413  wxWindow *const helpButton = wxWindow::FindWindowById(wxID_HELP, GetParent());
414  wxWindow *const previewButton = wxWindow::FindWindowById(wxID_PREVIEW, GetParent());
415 
416  if (helpButton) {
417  if (showHelp) {
418  wxAcceleratorEntry entries[1];
419 #if defined(__WXMAC__)
420  // Is there a standard shortcut on Mac?
421 #else
422  entries[0].Set(wxACCEL_NORMAL, (int) WXK_F1, wxID_HELP);
423 #endif
424  wxAcceleratorTable accel(1, entries);
425  this->SetAcceleratorTable(accel);
426  }
427  else {
428  this->SetAcceleratorTable(wxNullAcceleratorTable);
429  }
430 
431  const bool changed = helpButton->Show(showHelp);
432  if (changed)
433  GetParent()->Layout();
434  }
435 
436  if (previewButton) { // might still be NULL during population
437  const bool changed = previewButton->Show(showPreview);
438  if (changed)
439  GetParent()->Layout();
440  }
441 
442  return i;
443 }
444 
446  wxWindow * parent, AudacityProject *pProject,
447  const TranslatableString &titlePrefix,
448  PrefsPanel::Factories &factories)
449 : wxDialogWrapper(parent, wxID_ANY, XO("Audacity Preferences"),
450  wxDefaultPosition,
451  wxDefaultSize,
452  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
453 , mFactories(factories)
454 , mTitlePrefix(titlePrefix)
455 {
456  wxASSERT(factories.size() > 0);
457  const bool uniquePage = (factories.size() == 1);
458  SetLayoutDirection(wxLayout_LeftToRight);
459 
460  ShuttleGui S(this, eIsCreating);
461 
462  S.StartVerticalLay(true);
463  {
464  wxASSERT(factories.size() > 0);
465  if (!uniquePage) {
466  mCategories = safenew wxTreebookExt(S.GetParent(), wxID_ANY, mTitlePrefix);
467 #if wxUSE_ACCESSIBILITY
468  // so that name can be set on a standard control
469  mCategories->GetTreeCtrl()->SetAccessible(
470  safenew TreeCtrlAx(mCategories->GetTreeCtrl()));
471 #endif
472  // RJH: Prevent NVDA from reading "treeCtrl"
473  mCategories->GetTreeCtrl()->SetName(_("Category"));
474  S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, true);
475  {
476  S.Prop(1)
477  .Position(wxEXPAND)
479 
480  {
481  typedef std::pair<int, int> IntPair;
482  std::vector<IntPair> stack;
483  int iPage = 0;
484  for (auto it = factories.begin(), end = factories.end();
485  it != end; ++it, ++iPage)
486  {
487  const auto &node = *it;
488  const auto &factory = node.factory;
489  wxWindow *const w = factory(mCategories, wxID_ANY, pProject);
490  if (stack.empty())
491  // Parameters are: AddPage(page, name, IsSelected, imageId).
492  mCategories->AddPage(w, w->GetName(), false, 0);
493  else {
494  IntPair &top = *stack.rbegin();
495  mCategories->InsertSubPage(top.first, w, w->GetName(), false, 0);
496  if (--top.second == 0) {
497  // Expand all nodes before the layout calculation
498  mCategories->ExpandNode(top.first, true);
499  stack.pop_back();
500  }
501  }
502  if (node.nChildren > 0)
503  stack.push_back(IntPair(iPage, node.nChildren));
504  }
505  }
506  }
507  S.EndHorizontalLay();
508  }
509  else {
510  // TODO: Look into getting rid of mUniquePage and instead
511  // adding into mCategories, so there is just one page in mCategories.
512  // And then hiding the treebook.
513 
514  // Unique page, don't show the factory
515  const auto &node = factories[0];
516  const auto &factory = node.factory;
517  mUniquePage = factory(S.GetParent(), wxID_ANY, pProject);
518  wxWindow * uniquePageWindow = S.Prop(1)
519  .Position(wxEXPAND)
521  // We're not in the wxTreebook, so add the accelerator here
522  wxAcceleratorEntry entries[1];
523 #if defined(__WXMAC__)
524  // Is there a standard shortcut on Mac?
525 #else
526  entries[0].Set(wxACCEL_NORMAL, (int) WXK_F1, wxID_HELP);
527 #endif
528  wxAcceleratorTable accel(1, entries);
529  uniquePageWindow->SetAcceleratorTable(accel);
530  }
531  }
532  S.EndVerticalLay();
533 
535 
537  wxWindow *const previewButton =
538  wxWindow::FindWindowById(wxID_PREVIEW, GetParent());
539  previewButton->Show(false);
540  }
541 
542 #if defined(__WXGTK__)
543  if (mCategories)
544  mCategories->GetTreeCtrl()->EnsureVisible(mCategories->GetTreeCtrl()->GetRootItem());
545 #endif
546 
547 // mCategories->SetMaxSize({ 790, 600 }); // 790 = 800 - (border * 2)
548  Layout();
549  Fit();
550  wxSize sz = GetSize();
551 
552  // Collapse nodes only after layout so the tree is wide enough
553  if (mCategories)
554  {
555  int iPage = 0;
556  for (auto it = factories.begin(), end = factories.end();
557  it != end; ++it, ++iPage)
558  mCategories->ExpandNode(iPage, it->expanded);
559  }
560 
561  // This ASSERT was originally used to limit us to 800 x 600.
562  // However, the range of screen sizes and dpi of modern (2018) displays
563  // makes pixel dimensions an inadequate measure of usability, so
564  // now we only ASSERT that preferences will fit in the client display
565  // rectangle of the developer / tester's monitor.
566  // Use scrollers when necessary to ensure that preference pages will
567  // be fully visible.
568  wxRect screenRect(wxGetClientDisplayRect());
569  wxASSERT_MSG(sz.x <= screenRect.width && sz.y <= screenRect.height, wxT("Preferences dialog exceeds max size"));
570 
571  sz.DecTo(screenRect.GetSize());
572 
573  if( !mUniquePage ){
574  int prefWidth, prefHeight;
575  gPrefs->Read(wxT("/Prefs/Width"), &prefWidth, sz.x);
576  gPrefs->Read(wxT("/Prefs/Height"), &prefHeight, wxMax(480,sz.y));
577 
578  wxSize prefSize = wxSize(prefWidth, prefHeight);
579  prefSize.DecTo(screenRect.GetSize());
580  SetSize(prefSize);
581  InvalidateBestSize();
582  Layout();
583  }
584  SetMinSize(sz);
585 
586  // Center after all that resizing, but make sure it doesn't end up
587  // off-screen
588  CentreOnParent();
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 
618 void 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
628  mUniquePage->Cancel();
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 
652 void PrefsDialog::OnPreview(wxCommandEvent & WXUNUSED(event))
653 {
655 }
656 
657 void PrefsDialog::OnHelp(wxCommandEvent & WXUNUSED(event))
658 {
659  wxString 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 
680 void 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 
688 void 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 {
723  mUniquePage->Preview();
724  mUniquePage->Commit();
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  wxMilliSleep(100);
757  }
758  gAudioIO->HandleDeviceChange();
759  }
760 #endif
761 
762  // PRL: Is the following concern still valid, now that prefs update is
763  // handled instead by delayed event processing?
764 
765  // LL: wxMac can't handle recreating the menus when this dialog is still active,
766  // so AudacityProject::UpdatePrefs() or any of the routines it calls must
767  // not cause MenuCreator::RebuildMenuBar() to be executed.
768 
769  wxTheApp->AddPendingEvent(wxCommandEvent{ EVT_PREFS_UPDATE });
770 
771  if( IsModal() )
772  EndModal(true);
773  else
774  Destroy();
775 }
776 
777 void PrefsDialog::SelectPageByName(const wxString &pageName)
778 {
779  if (mCategories) {
780  size_t n = mCategories->GetPageCount();
781 
782  for (size_t i = 0; i < n; i++) {
783  if (mCategories->GetPageText(i) == pageName) {
784  mCategories->SetSelection(i);
785  return;
786  }
787  }
788  }
789 }
790 
792 {
793  if (mCategories)
794  return mCategories->GetSelection();
795  else
796  return 0;
797 }
798 
800  wxWindow * parent, AudacityProject *pProject,
801  PrefsPanel::Factories &factories)
802  : PrefsDialog(parent, pProject, XO("Preferences:"), factories)
803 {
804 }
805 
807 {
808 }
809 
811 {
812  long prefscat = gPrefs->Read(wxT("/Prefs/PrefsCategory"), 0L);
813  return prefscat;
814 }
815 
817 {
818  gPrefs->Write(wxT("/Prefs/PrefsCategory"), (long)GetSelectedPage());
819  gPrefs->Flush();
820 }
821 
823 {
824  // Remember expansion state of the tree control
825  if (mCategories)
826  {
827  int iPage = 0;
828  for (auto it = mFactories.begin(), end = mFactories.end();
829  it != end; ++it, ++iPage)
830  it->expanded = mCategories->IsNodeExpanded(iPage);
831  }
832  else
833  mFactories[0].expanded = true;
834 }
835 
836 #include <wx/frame.h>
837 #include "../Menus.h"
838 #include "../Project.h"
839 
841 {
843 
844  {
845  GlobalPrefsDialog dialog(
846  &GetProjectFrame( project ) /* parent */, &project );
847  wxCommandEvent Evt;
848  //dialog.Show();
849  dialog.OnOK(Evt);
850  }
851 
852  // LL: Moved from PrefsDialog since wxWidgets on OSX can't deal with
853  // rebuilding the menus while the PrefsDialog is still in the modal
854  // state.
855  for (auto p : AllProjects{}) {
857 // TODO: The comment below suggests this workaround is obsolete.
858 #if defined(__WXGTK__)
859  // Workaround for:
860  //
861  // http://bugzilla.audacityteam.org/show_bug.cgi?id=458
862  //
863  // This workaround should be removed when Audacity updates to wxWidgets
864  // 3.x which has a fix.
865  auto &window = GetProjectFrame( *p );
866  wxRect r = window.GetRect();
867  window.SetSize(wxSize(1,1));
868  window.SetSize(r.GetSize());
869 #endif
870  }
871 }
PrefsDialog::ShowModal
int ShowModal() override
Definition: PrefsDialog.cpp:595
EVT_BUTTON
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
TranslatableString
Definition: Types.h:290
DoReloadPreferences
void DoReloadPreferences(AudacityProject &project)
Definition: PrefsDialog.cpp:840
ePreviewButton
@ ePreviewButton
Definition: ShuttleGui.h:612
PrefsDialog::OnCancel
void OnCancel(wxCommandEvent &e)
Definition: PrefsDialog.cpp:618
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:36
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1177
fn
static const auto fn
Definition: WaveformView.cpp:1102
PrefsDialog::GetPreferredPage
virtual long GetPreferredPage()=0
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:67
PrefsPanel.h
PrefsPanel::PopulateOrExchange
virtual void PopulateOrExchange(ShuttleGui &WXUNUSED(S))
Definition: PrefsPanel.h:116
PrefsDialog.h
GlobalPrefsDialog::~GlobalPrefsDialog
virtual ~GlobalPrefsDialog()
Definition: PrefsDialog.cpp:806
XO
#define XO(s)
Definition: Internat.h:32
eCancelButton
@ eCancelButton
Definition: ShuttleGui.h:608
PreferenceInitializer::ReinitializeAll
static void ReinitializeAll()
Definition: Prefs.cpp:396
PrefsDialog::GetSelectedPage
int GetSelectedPage() const
Definition: PrefsDialog.cpp:791
PrefsDialog::mCategories
wxTreebook * mCategories
Definition: PrefsDialog.h:70
PrefsPanel::Preview
virtual void Preview()
Definition: PrefsPanel.h:101
PrefsPanel::Commit
virtual bool Commit()=0
PrefsPanel::ShowsPreviewButton
virtual bool ShowsPreviewButton()
Definition: PrefsPanel.cpp:93
eOkButton
@ eOkButton
Definition: ShuttleGui.h:607
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1170
factory
static RegisteredToolbarFactory factory
Definition: ControlToolBar.cpp:804
PrefsDialog::OnOK
void OnOK(wxCommandEvent &e)
Definition: PrefsDialog.cpp:688
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1160
PrefsDialog::GetCurrentPanel
PrefsPanel * GetCurrentPanel()
Definition: PrefsDialog.cpp:641
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1196
GetProjectFrame
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 ...
Definition: Project.cpp:186
ShuttleGuiBase::ResetId
void ResetId()
Definition: ShuttleGui.cpp:186
eHelpButton
@ eHelpButton
Definition: ShuttleGui.h:611
PrefsDialog::SelectPageByName
void SelectPageByName(const wxString &pageName)
Definition: PrefsDialog.cpp:777
name
const TranslatableString name
Definition: Distortion.cpp:98
MenuCreator::RebuildMenuBar
void RebuildMenuBar(AudacityProject &project)
Definition: Menus.cpp:485
PrefsDialog::~PrefsDialog
virtual ~PrefsDialog()
Definition: PrefsDialog.cpp:591
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:503
AudioIOBase::Get
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:94
WindowAccessible
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
PrefsDialog::ShuttleAll
void ShuttleAll(ShuttleGui &S)
Definition: PrefsDialog.cpp:663
ShuttleGui::Prop
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:732
PrefsDialog::OnHelp
void OnHelp(wxCommandEvent &e)
Definition: PrefsDialog.cpp:657
id
int id
Definition: WaveTrackControls.cpp:589
GlobalPrefsDialog::GlobalPrefsDialog
GlobalPrefsDialog(wxWindow *parent, AudacityProject *pProject, PrefsPanel::Factories &factories=PrefsPanel::DefaultFactories())
Definition: PrefsDialog.cpp:799
anonymous_namespace{Registry.cpp}::VisitItems
void VisitItems(Registry::Visitor &visitor, CollectedItems &collection, Path &path, GroupItem *pGroup, const GroupItem *pToMerge, const OrderingHint &hint, bool &doFlush)
Definition: Registry.cpp:620
wxDialogWrapper
Definition: wxPanelWrapper.h:81
ShuttleGuiBase::AddWindow
wxWindow * AddWindow(wxWindow *pWindow)
Definition: ShuttleGui.cpp:292
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:151
HelpSystem::ShowHelp
static void ShowHelp(wxWindow *parent, const wxString &localFileName, const wxString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:238
ShuttleGui::Position
ShuttleGui & Position(int flags)
Definition: ShuttleGui.h:719
_
#define _(s)
Definition: Internat.h:76
PrefsPanel::Cancel
virtual void Cancel()
Definition: PrefsPanel.cpp:89
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:112
Verbatim
TranslatableString Verbatim(wxString str)
Definition: Types.h:581
PrefsDialog::mTitlePrefix
const TranslatableString mTitlePrefix
Definition: PrefsDialog.h:73
MenuManager::Get
static MenuManager & Get(AudacityProject &project)
Definition: Menus.cpp:69
AllProjects
an object of class AllProjects acts like a standard library container, but refers to a global array o...
Definition: Project.h:38
PrefsDialog
Dialog that shows the current PrefsPanel in a tabbed divider.
Definition: PrefsDialog.h:34
GlobalPrefsDialog
Definition: PrefsDialog.h:81
wxDialogWrapper::SetTitle
void SetTitle(const TranslatableString &title)
Definition: wxPanelWrapper.cpp:66
PrefsDialog::RecordExpansionState
void RecordExpansionState()
Definition: PrefsDialog.cpp:822
ShuttleGui::AddStandardButtons
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxWindow *extra=NULL)
Definition: ShuttleGui.cpp:2406
PrefsPanel
Base class for a panel in the PrefsDialog. Classes derived from this class include BatchPrefs,...
Definition: PrefsPanel.h:51
GlobalPrefsDialog::GetPreferredPage
long GetPreferredPage() override
Definition: PrefsDialog.cpp:810
PrefsDialog::mFactories
PrefsPanel::Factories & mFactories
Definition: PrefsDialog.h:72
WaveTrackVZoomHandle::Result
unsigned Result
Definition: WaveTrackVZoomHandle.h:24
wxDialogWrapper::SetName
void SetName()
Definition: wxPanelWrapper.cpp:81
PrefsPanel::HelpPageName
virtual wxString HelpPageName()
Definition: PrefsPanel.cpp:98
TranslatableString::Join
TranslatableString & Join(TranslatableString arg, const wxString &separator={}) &
Definition: Internat.cpp:393
safenew
#define safenew
Definition: MemoryX.h:8
PrefsDialog::OnTreeKeyDown
void OnTreeKeyDown(wxTreeEvent &e)
Definition: PrefsDialog.cpp:680
PrefsDialog::OnPreview
void OnPreview(wxCommandEvent &e)
Definition: PrefsDialog.cpp:652
wxTreebook
A wxTreebook is a class like wxNotebook, but not yet supported by wxWidgets 2.6.3.
END_EVENT_TABLE
END_EVENT_TABLE()
PrefsDialog::SavePreferredPage
virtual void SavePreferredPage()=0
PrefsPanel::Factories
std::vector< PrefsPanel::PrefsNode > Factories
Definition: PrefsPanel.h:69
GlobalPrefsDialog::SavePreferredPage
void SavePreferredPage() override
Definition: PrefsDialog.cpp:816
PrefsDialog::mUniquePage
PrefsPanel * mUniquePage
Definition: PrefsDialog.h:71
PrefsDialog::PrefsDialog
PrefsDialog(wxWindow *parent, AudacityProject *pProject, const TranslatableString &titlePrefix=XO("Preferences:"), PrefsPanel::Factories &factories=PrefsPanel::DefaultFactories())
Definition: PrefsDialog.cpp:445
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:638