KiCad PCB EDA Suite
paged_dialog.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <confirm.h>
21 #include <wx/treebook.h>
22 #include <wx/treectrl.h>
23 #include <wx/grid.h>
24 #include <wx/statline.h>
25 
26 #include <widgets/paged_dialog.h>
27 
28 
29 // Maps from dialogTitle <-> pageTitle for keeping track of last-selected pages.
30 // This is not a simple page index because some dialogs have dynamic page sets.
31 std::map<wxString, wxString> g_lastPage;
32 std::map<wxString, wxString> g_lastParentPage;
33 
34 
35 PAGED_DIALOG::PAGED_DIALOG( wxWindow* aParent, const wxString& aTitle,
36  const wxString& aAuxiliaryAction ) :
37  DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxDefaultSize,
38  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
39  m_title( aTitle ),
40  m_errorCtrl( nullptr ),
41  m_errorRow( 0 ),
42  m_errorCol( 0 ),
43  m_auxiliaryButton( nullptr )
44 {
45  auto mainSizer = new wxBoxSizer( wxVERTICAL );
46  SetSizer( mainSizer );
47 
48  m_treebook = new wxTreebook( this, wxID_ANY );
49  mainSizer->Add( m_treebook, 1, wxEXPAND|wxLEFT|wxTOP, 10 );
50 
51  auto line = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
52  mainSizer->Add( line, 0, wxEXPAND|wxLEFT|wxTOP|wxRIGHT, 10 );
53 
54  auto buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
55 
56  if( !aAuxiliaryAction.IsEmpty() )
57  {
58  m_auxiliaryButton = new wxButton( this, wxID_ANY, aAuxiliaryAction );
59  buttonsSizer->Add( m_auxiliaryButton, 0, wxEXPAND|wxRIGHT|wxLEFT, 10 );
60  }
61 
62  auto sdbSizer = new wxStdDialogButtonSizer();
63  auto sdbSizerOK = new wxButton( this, wxID_OK );
64  sdbSizer->AddButton( sdbSizerOK );
65  auto sdbSizerCancel = new wxButton( this, wxID_CANCEL );
66  sdbSizer->AddButton( sdbSizerCancel );
67  sdbSizer->Realize();
68 
69  buttonsSizer->Add( sdbSizer, 1, wxEXPAND, 5 );
70  mainSizer->Add( buttonsSizer, 0, wxALL|wxEXPAND, 5 );
71 
72  sdbSizerOK->SetDefault();
73 
74  // We normally save the dialog size and position based on its class-name. This class
75  // substitutes the title so that each distinctly-titled dialog can have its own saved
76  // size and position.
77  m_hash_key = aTitle;
78 
79  if( m_auxiliaryButton )
80  m_auxiliaryButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PAGED_DIALOG::OnAuxiliaryAction ), nullptr, this );
81  Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( PAGED_DIALOG::OnUpdateUI ), nullptr, this );
82 }
83 
84 
85 // Finish initialization after the bookctrl pages have been added.
87 {
88  // For some reason adding page labels to the treeCtrl doesn't invalidate its bestSize
89  // cache so we have to do it by hand
90  m_treebook->GetTreeCtrl()->InvalidateBestSize();
91 
92  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
93  {
94  m_treebook->ExpandNode( i );
95  m_treebook->GetPage( i )->Layout();
96  }
97 
98  m_treebook->Fit();
99  m_treebook->Layout();
100 
102 }
103 
104 
105 void PAGED_DIALOG::SetInitialPage( const wxString& aPage, const wxString& aParentPage )
106 {
107  g_lastPage[ m_title ] = aPage;
108  g_lastParentPage[ m_title ] = aParentPage;
109 }
110 
111 
113 {
114  // Store the current parentPageTitle/pageTitle hierarchy so we can re-select it
115  // next time.
116  wxString lastPage = wxEmptyString;
117  wxString lastParentPage = wxEmptyString;
118 
119  int selected = m_treebook->GetSelection();
120 
121  if( selected != wxNOT_FOUND )
122  {
123  lastPage = m_treebook->GetPageText( (unsigned) selected );
124 
125  int parent = m_treebook->GetPageParent( (unsigned) selected );
126 
127  if( parent != wxNOT_FOUND )
128  lastParentPage = m_treebook->GetPageText( (unsigned) parent );
129  }
130 
131  g_lastPage[ m_title ] = lastPage;
132  g_lastParentPage[ m_title ] = lastParentPage;
133 
134  if( m_auxiliaryButton )
135  m_auxiliaryButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PAGED_DIALOG::OnAuxiliaryAction ), nullptr, this );
136  Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( PAGED_DIALOG::OnUpdateUI ), nullptr, this );
137 }
138 
139 
141 {
143 
144  // Call TransferDataToWindow() only once:
145  // this is enough on wxWidgets 3.1
146  if( !DIALOG_SHIM::TransferDataToWindow() )
147  return false;
148 
149  // On wxWidgets 3.0, TransferDataFromWindow() is not called recursively
150  // so we have to call it for each page
151 #if !wxCHECK_VERSION( 3, 1, 0 )
152  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
153  {
154  wxWindow* page = m_treebook->GetPage( i );
155 
156  if( !page->TransferDataToWindow() )
157  return false;
158  }
159 #endif
160 
161  // Search for a page matching the lastParentPageTitle/lastPageTitle hierarchy
162  wxString lastPage = g_lastPage[ m_title ];
163  wxString lastParentPage = g_lastParentPage[ m_title ];
164  int lastPageIndex = wxNOT_FOUND;
165 
166  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
167  {
168  if( m_treebook->GetPageText( i ) == lastPage )
169  {
170  if( lastParentPage.IsEmpty() )
171  {
172  lastPageIndex = i;
173  break;
174  }
175 
176  if( m_treebook->GetPageParent( i ) >= 0
177  && m_treebook->GetPageText( (unsigned) m_treebook->GetPageParent( i ) ) == lastParentPage )
178  {
179  lastPageIndex = i;
180  break;
181  }
182  }
183  }
184 
185  m_treebook->SetSelection( (unsigned) std::max( 0, lastPageIndex ) );
186 
187  return true;
188 }
189 
190 
192 {
193  // Call TransferDataFromWindow() only once:
194  // this is enough on wxWidgets 3.1
195  if( !DIALOG_SHIM::TransferDataFromWindow() )
196  return false;
197 
198  // On wxWidgets 3.0, TransferDataFromWindow() is not called recursively
199  // so we have to call it for each page
200 #if !wxCHECK_VERSION( 3, 1, 0 )
201  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
202  {
203  wxWindow* page = m_treebook->GetPage( i );
204 
205  if( !page->TransferDataFromWindow() )
206  return false;
207  }
208 #endif
209 
210  return true;
211 }
212 
213 
214 void PAGED_DIALOG::SetError( const wxString& aMessage, wxWindow* aPage, wxObject* aCtrl,
215  int aRow, int aCol )
216 {
217  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
218  {
219  if( m_treebook->GetPage( i ) == aPage )
220  {
221  m_treebook->SetSelection( i );
222  break;
223  }
224  }
225 
226  // Once the page has been changed we want to wait for it to update before displaying
227  // the error dialog. So store the rest of the error info and wait for OnUpdateUI.
228  m_errorMessage = aMessage;
229  m_errorCtrl = aCtrl;
230  m_errorRow = aRow;
231  m_errorCol = aCol;
232 }
233 
234 
235 void PAGED_DIALOG::OnUpdateUI( wxUpdateUIEvent& event )
236 {
237  // Handle an error. This is delayed to OnUpdateUI so that we can change the focus
238  // even when the original validation was triggered from a killFocus event, and so
239  // that the corresponding notebook page can be shown in the background when triggered
240  // from an OK.
241  if( m_errorCtrl )
242  {
243  // We will re-enter this routine when the error dialog is displayed, so make
244  // sure we don't keep putting up more dialogs.
245  wxObject* ctrl = m_errorCtrl;
246  m_errorCtrl = nullptr;
247 
249 
250  auto textCtrl = dynamic_cast<wxTextCtrl*>( ctrl );
251  if( textCtrl )
252  {
253  auto textEntry = dynamic_cast<wxTextEntry*>( textCtrl );
254  textEntry->SetSelection( -1, -1 );
255  textCtrl->SetFocus();
256  return;
257  }
258 
259  auto grid = dynamic_cast<wxGrid*>( ctrl );
260 
261  if( grid )
262  {
263  grid->SetFocus();
264  grid->MakeCellVisible( m_errorRow, m_errorCol );
265  grid->SetGridCursor( m_errorRow, m_errorCol );
266 
267  grid->EnableCellEditControl( true );
268  grid->ShowCellEditControl();
269  return;
270  }
271  }
272 }
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:249
std::map< wxString, wxString > g_lastPage
This file is part of the common library.
std::string m_hash_key
Definition: dialog_shim.h:186
void SetInitialPage(const wxString &aPage, const wxString &aParentPage=wxEmptyString)
void FinishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
~PAGED_DIALOG() override
std::map< wxString, wxString > g_lastParentPage
Class DIALOG_SHIM may sit in the inheritance tree between wxDialog and any class written by wxFormBui...
Definition: dialog_shim.h:83
bool TransferDataToWindow() override
wxButton * m_auxiliaryButton
Definition: paged_dialog.h:60
bool TransferDataFromWindow() override
PAGED_DIALOG(wxWindow *aParent, const wxString &aTitle, const wxString &aAuxiliaryAction=wxEmptyString)
void OnUpdateUI(wxUpdateUIEvent &event)
wxTreebook * m_treebook
Definition: paged_dialog.h:59
void SetError(const wxString &aMessage, wxWindow *aPage, wxObject *aCtrl, int aRow=-1, int aCol=-1)
wxString m_errorMessage
Definition: paged_dialog.h:33
virtual void OnAuxiliaryAction(wxCommandEvent &event)
Definition: paged_dialog.h:56
wxObject * m_errorCtrl
Definition: paged_dialog.h:34
#define max(a, b)
Definition: auxiliary.h:86
size_t i
Definition: json11.cpp:649
void finishInitialization()
wxString m_title
Definition: paged_dialog.h:31