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) 2018 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  if( !DIALOG_SHIM::TransferDataToWindow() )
145  return false;
146 
147  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
148  {
149  wxWindow* page = m_treebook->GetPage( i );
150 
151  if( !page->TransferDataToWindow() )
152  return false;
153  }
154 
155  // Search for a page matching the lastParentPageTitle/lastPageTitle hierarchy
156  wxString lastPage = g_lastPage[ m_title ];
157  wxString lastParentPage = g_lastParentPage[ m_title ];
158  int lastPageIndex = wxNOT_FOUND;
159 
160  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
161  {
162  if( m_treebook->GetPageText( i ) == lastPage )
163  {
164  if( lastParentPage.IsEmpty() )
165  {
166  lastPageIndex = i;
167  break;
168  }
169 
170  if( m_treebook->GetPageParent( i ) >= 0
171  && m_treebook->GetPageText( (unsigned) m_treebook->GetPageParent( i ) ) == lastParentPage )
172  {
173  lastPageIndex = i;
174  break;
175  }
176  }
177  }
178 
179  m_treebook->SetSelection( (unsigned) std::max( 0, lastPageIndex ) );
180 
181  return true;
182 }
183 
184 
186 {
187  if( !DIALOG_SHIM::TransferDataFromWindow() )
188  return false;
189 
190  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
191  {
192  wxWindow* page = m_treebook->GetPage( i );
193 
194  if( !page->TransferDataFromWindow() )
195  return false;
196  }
197 
198  return true;
199 }
200 
201 
202 void PAGED_DIALOG::SetError( const wxString& aMessage, wxWindow* aPage, wxObject* aCtrl,
203  int aRow, int aCol )
204 {
205  for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
206  {
207  if( m_treebook->GetPage( i ) == aPage )
208  {
209  m_treebook->SetSelection( i );
210  break;
211  }
212  }
213 
214  // Once the page has been changed we want to wait for it to update before displaying
215  // the error dialog. So store the rest of the error info and wait for OnUpdateUI.
216  m_errorMessage = aMessage;
217  m_errorCtrl = aCtrl;
218  m_errorRow = aRow;
219  m_errorCol = aCol;
220 }
221 
222 
223 void PAGED_DIALOG::OnUpdateUI( wxUpdateUIEvent& event )
224 {
225  // Handle an error. This is delayed to OnUpdateUI so that we can change the focus
226  // even when the original validation was triggered from a killFocus event, and so
227  // that the corresponding notebook page can be shown in the background when triggered
228  // from an OK.
229  if( m_errorCtrl )
230  {
231  // We will re-enter this routine when the error dialog is displayed, so make
232  // sure we don't keep putting up more dialogs.
233  wxObject* ctrl = m_errorCtrl;
234  m_errorCtrl = nullptr;
235 
237 
238  auto textCtrl = dynamic_cast<wxTextCtrl*>( ctrl );
239  if( textCtrl )
240  {
241  auto textEntry = dynamic_cast<wxTextEntry*>( textCtrl );
242  textEntry->SetSelection( -1, -1 );
243  textCtrl->SetFocus();
244  return;
245  }
246 
247  auto grid = dynamic_cast<wxGrid*>( ctrl );
248 
249  if( grid )
250  {
251  grid->SetFocus();
252  grid->MakeCellVisible( m_errorRow, m_errorCol );
253  grid->SetGridCursor( m_errorRow, m_errorCol );
254 
255  grid->EnableCellEditControl( true );
256  grid->ShowCellEditControl();
257  return;
258  }
259  }
260 }
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:259
std::map< wxString, wxString > g_lastPage
This file is part of the common library.
std::string m_hash_key
Definition: dialog_shim.h:173
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:82
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:597
void finishInitialization()
wxString m_title
Definition: paged_dialog.h:31