KiCad PCB EDA Suite
confirm.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) 2007 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <wx/app.h>
26#include <wx/stockitem.h>
27#include <wx/richmsgdlg.h>
28#include <wx/choicdlg.h>
29#include <confirm.h>
31#include <functional>
32#include <unordered_map>
33
34// Set of dialogs that have been chosen not to be shown again
35static std::unordered_map<unsigned long, int> doNotShowAgainDlgs;
36
37
38KIDIALOG::KIDIALOG( wxWindow* aParent, const wxString& aMessage, const wxString& aCaption,
39 long aStyle )
40 : wxRichMessageDialog( aParent, aMessage, aCaption, aStyle | wxCENTRE | wxSTAY_ON_TOP ),
41 m_hash( 0 ),
42 m_cancelMeansCancel( true )
43{
44}
45
46
47KIDIALOG::KIDIALOG( wxWindow* aParent, const wxString& aMessage, KD_TYPE aType,
48 const wxString& aCaption )
49 : wxRichMessageDialog( aParent, aMessage, getCaption( aType, aCaption ), getStyle( aType ) ),
50 m_hash( 0 ),
51 m_cancelMeansCancel( true )
52{
53}
54
55
56void KIDIALOG::DoNotShowCheckbox( wxString aUniqueId, int line )
57{
58 ShowCheckBox( _( "Do not show again" ), false );
59
60 m_hash = std::hash<wxString>{}( aUniqueId ) + line;
61}
62
63
65{
66 return doNotShowAgainDlgs.count( m_hash ) > 0;
67}
68
69
71{
73}
74
75
76bool KIDIALOG::Show( bool aShow )
77{
78 // We should check the do-not-show-again setting only when the dialog is displayed
79 if( aShow )
80 {
81 // Check if this dialog should be shown to the user
82 auto it = doNotShowAgainDlgs.find( m_hash );
83
84 if( it != doNotShowAgainDlgs.end() )
85 return it->second;
86 }
87
88 int ret = wxRichMessageDialog::Show( aShow );
89
90 // Has the user asked not to show the dialog again?
91 // Note that we don't save a Cancel value unless the Cancel button is being used for some
92 // other function (which is actually more common than it being used for Cancel).
93 if( IsCheckBoxChecked() && (!m_cancelMeansCancel || ret != wxID_CANCEL ) )
95
96 return ret;
97}
98
99
101{
102 // Check if this dialog should be shown to the user
103 auto it = doNotShowAgainDlgs.find( m_hash );
104
105 if( it != doNotShowAgainDlgs.end() )
106 return it->second;
107
108 int ret = wxRichMessageDialog::ShowModal();
109
110 // Has the user asked not to show the dialog again?
111 // Note that we don't save a Cancel value unless the Cancel button is being used for some
112 // other function (which is actually more common than it being used for Cancel).
113 if( IsCheckBoxChecked() && (!m_cancelMeansCancel || ret != wxID_CANCEL ) )
115
116 return ret;
117}
118
119
120wxString KIDIALOG::getCaption( KD_TYPE aType, const wxString& aCaption )
121{
122 if( !aCaption.IsEmpty() )
123 return aCaption;
124
125 switch( aType )
126 {
127 case KD_NONE: /* fall through */
128 case KD_INFO: return _( "Message" );
129 case KD_QUESTION: return _( "Question" );
130 case KD_WARNING: return _( "Warning" );
131 case KD_ERROR: return _( "Error" );
132 }
133
134 return wxEmptyString;
135}
136
137
139{
140 long style = wxOK | wxCENTRE | wxSTAY_ON_TOP;
141
142 switch( aType )
143 {
144 case KD_NONE: break;
145 case KD_INFO: style |= wxICON_INFORMATION; break;
146 case KD_QUESTION: style |= wxICON_QUESTION; break;
147 case KD_WARNING: style |= wxICON_WARNING; break;
148 case KD_ERROR: style |= wxICON_ERROR; break;
149 }
150
151 return style;
152}
153
154
155bool OverrideLock( wxWindow* aParent, const wxString& aMessage )
156{
157#ifdef __APPLE__
158 // wxMessageDialog gets the button spacing wrong on Mac so we have to use wxRichMessageDialog.
159 // Note that its warning icon is more like wxMessageDialog's error icon, so we use it instead
160 // of wxICON_ERROR.
161 wxRichMessageDialog dlg( aParent, aMessage, _( "File Open Warning" ),
162 wxYES_NO | wxICON_WARNING | wxCENTER );
163 dlg.SetExtendedMessage( _( "Interleaved saves may produce very unexpected results." )
164 + wxS( "\n" ) );
165 dlg.SetYesNoLabels( _( "Cancel" ), _( "Open Anyway" ) );
166#else
167 wxMessageDialog dlg( aParent, aMessage, _( "File Open Warning" ),
168 wxYES_NO | wxICON_ERROR | wxCENTER );
169 dlg.SetExtendedMessage( _( "Interleaved saves may produce very unexpected results." ) );
170 dlg.SetYesNoLabels( _( "Cancel" ), _( "Open Anyway" ) );
171#endif
172
173 return dlg.ShowModal() == wxID_NO;
174}
175
176
177int UnsavedChangesDialog( wxWindow* parent, const wxString& aMessage, bool* aApplyToAll )
178{
179 static bool s_apply_to_all = false;
180
181 wxRichMessageDialog dlg( parent, aMessage, _( "Save Changes?" ),
182 wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_WARNING | wxCENTER );
183 dlg.SetExtendedMessage( _( "If you don't save, all your changes will be permanently lost." )
184 + wxS( "\n" ) );
185 dlg.SetYesNoLabels( _( "Save" ), _( "Discard Changes" ) );
186
187 if( aApplyToAll )
188 dlg.ShowCheckBox( _( "Apply to all" ), s_apply_to_all );
189
190 int ret = dlg.ShowModal();
191
192 if( aApplyToAll )
193 {
194 *aApplyToAll = dlg.IsCheckBoxChecked();
195 s_apply_to_all = dlg.IsCheckBoxChecked();
196 }
197
198 // Returns wxID_YES, wxID_NO, or wxID_CANCEL
199 return ret;
200}
201
202
203int UnsavedChangesDialog( wxWindow* parent, const wxString& aMessage )
204{
205#ifdef __APPLE__
206 // wxMessageDialog gets the button order (and spacing) wrong on Mac so we have to use
207 // wxRichMessageDialog.
208 return UnsavedChangesDialog( parent, aMessage, nullptr );
209#else
210 #ifdef _WIN32
211 // wxMessageDialog on windows invokes TaskDialogIndirect which is a native function for a dialog
212 // As a result it skips wxWidgets for modal management...and we don't parent frames properly
213 // among other things for Windows to do the right thing by default
214 // Disable all the windows manually to avoid being able to hit this dialog from the tool frame and kicad frame at the same time
215 wxWindowDisabler disable( true );
216 #endif
217
218 wxMessageDialog dlg( parent, aMessage, _( "Save Changes?" ),
219 wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_WARNING | wxCENTER );
220 dlg.SetExtendedMessage( _( "If you don't save, all your changes will be permanently lost." ) );
221 dlg.SetYesNoLabels( _( "Save" ), _( "Discard Changes" ) );
222
223 // Returns wxID_YES, wxID_NO, or wxID_CANCEL
224 return dlg.ShowModal();
225#endif
226}
227
228
229bool ConfirmRevertDialog( wxWindow* parent, const wxString& aMessage )
230{
231 wxMessageDialog dlg( parent, aMessage, wxEmptyString,
232 wxOK | wxCANCEL | wxOK_DEFAULT | wxICON_WARNING | wxCENTER );
233 dlg.SetExtendedMessage( _( "Your current changes will be permanently lost." ) );
234 dlg.SetOKCancelLabels( _( "Revert" ), _( "Cancel" ) );
235
236 return dlg.ShowModal() == wxID_OK;
237}
238
239
240bool HandleUnsavedChanges( wxWindow* aParent, const wxString& aMessage,
241 const std::function<bool()>& aSaveFunction )
242{
243 switch( UnsavedChangesDialog( aParent, aMessage ) )
244 {
245 case wxID_YES: return aSaveFunction();
246 case wxID_NO: return true;
247 default:
248 case wxID_CANCEL: return false;
249 }
250}
251
252
253int OKOrCancelDialog( wxWindow* aParent, const wxString& aWarning, const wxString& aMessage,
254 const wxString& aDetailedMessage, const wxString& aOKLabel,
255 const wxString& aCancelLabel, bool* aApplyToAll )
256{
257 wxRichMessageDialog dlg( aParent, aMessage, aWarning,
258 wxOK | wxCANCEL | wxOK_DEFAULT | wxICON_WARNING | wxCENTER );
259
260 dlg.SetOKCancelLabels( ( aOKLabel.IsEmpty() ) ? _( "OK" ) : aOKLabel,
261 ( aCancelLabel.IsEmpty() ) ? _( "Cancel" ) : aCancelLabel );
262
263 if( !aDetailedMessage.IsEmpty() )
264 dlg.SetExtendedMessage( aDetailedMessage );
265
266 if( aApplyToAll )
267 dlg.ShowCheckBox( _( "Apply to all" ), true );
268
269 int ret = dlg.ShowModal();
270
271 if( aApplyToAll )
272 *aApplyToAll = dlg.IsCheckBoxChecked();
273
274 // Returns wxID_OK or wxID_CANCEL
275 return ret;
276}
277
278
279// DisplayError should be deprecated, use DisplayErrorMessage instead
280void DisplayError( wxWindow* aParent, const wxString& aText, int aDisplayTime )
281{
282 if( !wxTheApp || !wxTheApp->IsMainLoopRunning() )
283 {
284 wxLogError( "%s", aText );
285 return;
286 }
287
288 wxMessageDialog* dlg;
289 int icon = aDisplayTime > 0 ? wxICON_INFORMATION : wxICON_ERROR;
290
291 dlg = new wxMessageDialog( aParent, aText, _( "Warning" ),
292 wxOK | wxCENTRE | wxRESIZE_BORDER | icon | wxSTAY_ON_TOP );
293
294 dlg->ShowModal();
295 dlg->Destroy();
296}
297
298
299void DisplayErrorMessage( wxWindow* aParent, const wxString& aText, const wxString& aExtraInfo )
300{
301 if( !wxTheApp || !wxTheApp->IsMainLoopRunning() )
302 {
303 wxLogError( "%s %s", aText, aExtraInfo );
304 return;
305 }
306
307 wxMessageDialog* dlg;
308
309 dlg = new wxMessageDialog( aParent, aText, _( "Error" ),
310 wxOK | wxCENTRE | wxRESIZE_BORDER | wxICON_ERROR | wxSTAY_ON_TOP );
311
312 if( !aExtraInfo.IsEmpty() )
313 dlg->SetExtendedMessage( aExtraInfo );
314
315 dlg->ShowModal();
316 dlg->Destroy();
317}
318
319
320void DisplayInfoMessage( wxWindow* aParent, const wxString& aMessage, const wxString& aExtraInfo )
321{
322 if( !wxTheApp || !wxTheApp->GetTopWindow() )
323 {
324 wxLogDebug( "%s %s", aMessage, aExtraInfo );
325 return;
326 }
327
328 wxMessageDialog* dlg;
329 int icon = wxICON_INFORMATION;
330
331 dlg = new wxMessageDialog( aParent, aMessage, _( "Information" ),
332 wxOK | wxCENTRE | wxRESIZE_BORDER | icon | wxSTAY_ON_TOP );
333
334 if( !aExtraInfo.IsEmpty() )
335 dlg->SetExtendedMessage( aExtraInfo );
336
337 dlg->ShowModal();
338 dlg->Destroy();
339}
340
341
342bool IsOK( wxWindow* aParent, const wxString& aMessage )
343{
344 // wxMessageDialog no longer responds correctly to the <ESC> key (on at least OSX and MSW)
345 // so we're now using wxRichMessageDialog.
346 //
347 // Note also that we have to repurpose an OK/Cancel version of it because otherwise wxWidgets
348 // uses "destructive" spacing for the "No" button.
349
350#ifdef __APPLE__
351 // Why is wxICON_QUESTION a light-bulb on Mac? That has more of a hint or info connotation.
352 int icon = wxICON_WARNING;
353#else
354 int icon = wxICON_QUESTION;
355#endif
356
357 wxRichMessageDialog dlg( aParent, aMessage, _( "Confirmation" ),
358 wxOK | wxCANCEL | wxOK_DEFAULT | wxCENTRE | icon | wxSTAY_ON_TOP );
359
360 dlg.SetOKCancelLabels( _( "Yes" ), _( "No" ) );
361
362
363 return dlg.ShowModal() == wxID_OK;
364}
365
366
367int SelectSingleOption( wxWindow* aParent, const wxString& aTitle,
368 const wxString& aMessage, const wxArrayString& aOptions )
369{
370 wxSingleChoiceDialog dlg( aParent, aMessage, aTitle, aOptions );
371
372 if( dlg.ShowModal() != wxID_OK )
373 return -1;
374
375 return dlg.GetSelection();
376}
377
KD_TYPE
< Dialog type. Selects appropriate icon and default dialog title
Definition: confirm.h:49
@ KD_INFO
Definition: confirm.h:49
@ KD_QUESTION
Definition: confirm.h:49
@ KD_ERROR
Definition: confirm.h:49
@ KD_NONE
Definition: confirm.h:49
@ KD_WARNING
Definition: confirm.h:49
void ForceShowAgain()
Definition: confirm.cpp:70
bool DoNotShowAgain() const
Definition: confirm.cpp:64
bool m_cancelMeansCancel
Definition: confirm.h:79
static long getStyle(KD_TYPE aType)
Definition: confirm.cpp:138
unsigned long m_hash
Definition: confirm.h:78
KIDIALOG(wxWindow *aParent, const wxString &aMessage, const wxString &aCaption, long aStyle=wxOK)
Definition: confirm.cpp:38
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:56
int ShowModal() override
Definition: confirm.cpp:100
static wxString getCaption(KD_TYPE aType, const wxString &aCaption)
Definition: confirm.cpp:120
bool Show(bool aShow=true) override
Definition: confirm.cpp:76
int SelectSingleOption(wxWindow *aParent, const wxString &aTitle, const wxString &aMessage, const wxArrayString &aOptions)
Display a dialog with radioboxes asking the user to select an option.
Definition: confirm.cpp:367
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aDetailedMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Display a warning dialog with aMessage and returns the user response.
Definition: confirm.cpp:253
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:342
static std::unordered_map< unsigned long, int > doNotShowAgainDlgs
Definition: confirm.cpp:35
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
bool OverrideLock(wxWindow *aParent, const wxString &aMessage)
Display a dialog indicating the file is already open, with an option to reset the lock.
Definition: confirm.cpp:155
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:320
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition: confirm.cpp:240
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:299
int UnsavedChangesDialog(wxWindow *parent, const wxString &aMessage, bool *aApplyToAll)
A specialized version of HandleUnsavedChanges which handles an apply-to-all checkbox.
Definition: confirm.cpp:177
bool ConfirmRevertDialog(wxWindow *parent, const wxString &aMessage)
Display a confirmation dialog for a revert action.
Definition: confirm.cpp:229
This file is part of the common library.
#define _(s)