You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

506 lines
14 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
  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-2017 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. * @file confirm.cpp
  26. * @brief utilities to display some error, warning and info short messges
  27. */
  28. #include <wx/stockitem.h>
  29. #include <wx/richmsgdlg.h>
  30. #include <confirm.h>
  31. #include <bitmaps.h>
  32. #include <html_messagebox.h>
  33. #include <dialog_exit_base.h>
  34. #include <functional>
  35. #include <unordered_map>
  36. // Set of dialogs that have been chosen not to be shown again
  37. static std::unordered_map<unsigned long, int> doNotShowAgainDlgs;
  38. KIDIALOG::KIDIALOG( wxWindow* aParent, const wxString& aMessage )
  39. : wxDialog( aParent, wxID_ANY, wxEmptyString ),
  40. m_btnSizer( nullptr ), m_cbDoNotShow( nullptr ), m_icon( nullptr )
  41. {
  42. SetSizeHints( wxDefaultSize, wxDefaultSize );
  43. m_sizerMain = new wxBoxSizer( wxVERTICAL );
  44. m_sizerUpper = new wxBoxSizer( wxHORIZONTAL );
  45. wxStaticText* message = new wxStaticText( this, wxID_ANY, aMessage );
  46. message->Wrap( -1 );
  47. //message->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
  48. m_sizerUpper->Add( message, 1, wxALL | wxEXPAND | wxALIGN_CENTER_VERTICAL, 5 );
  49. m_sizerMain->Add( m_sizerUpper, 1, wxALL | wxEXPAND, 5 );
  50. Type( KD_NONE );
  51. Buttons( wxOK );
  52. SetSizer( m_sizerMain );
  53. Connect( wxEVT_BUTTON, wxCommandEventHandler( KIDIALOG::onButtonClick ), NULL, this );
  54. }
  55. KIDIALOG& KIDIALOG::Type( KD_TYPE aType )
  56. {
  57. m_type = aType;
  58. const std::unordered_map<int, wxString> stdTitle = {
  59. { KD_NONE, _( "Message" ) }, { KD_INFO, _( "Information" ) }, { KD_QUESTION, _( "Question" ) },
  60. { KD_WARNING, _( "Warning" ) }, { KD_ERROR, _( "Error" ) }
  61. };
  62. const std::unordered_map<int, wxArtID> icons = {
  63. { KD_INFO, wxART_INFORMATION }, { KD_QUESTION, wxART_QUESTION },
  64. { KD_WARNING, wxART_WARNING }, { KD_ERROR, wxART_ERROR }
  65. };
  66. if( m_icon )
  67. {
  68. m_sizerUpper->Remove( 0 );
  69. m_icon->Destroy();
  70. m_icon = nullptr;
  71. }
  72. if( aType != KD_NONE )
  73. {
  74. m_icon = new wxStaticBitmap( this, wxID_ANY,
  75. wxArtProvider::GetBitmap( icons.at( aType ), wxART_CMN_DIALOG ) );
  76. m_sizerUpper->Prepend( m_icon, 0, wxALL, 5 );
  77. }
  78. if( !m_customTitle )
  79. SetTitle( stdTitle.at( aType ) );
  80. return *this;
  81. }
  82. KIDIALOG& KIDIALOG::Title( const wxString& aTitle )
  83. {
  84. m_customTitle = aTitle;
  85. SetTitle( aTitle );
  86. return *this;
  87. }
  88. KIDIALOG& KIDIALOG::Buttons( long aButtons )
  89. {
  90. wxASSERT( aButtons );
  91. if( !aButtons )
  92. aButtons = wxOK;
  93. const std::map<long, long> btnTypes = { { wxOK, wxID_OK }, { wxCANCEL, wxID_CANCEL },
  94. { wxYES, wxID_YES }, { wxNO, wxID_NO }, { wxAPPLY, wxID_APPLY }, { wxCLOSE, wxID_CLOSE },
  95. { wxHELP, wxID_HELP } };
  96. if( m_btnSizer )
  97. {
  98. m_sizerMain->Remove( m_btnSizer ); // also deletes m_btnSizer
  99. for( auto btn : m_buttons )
  100. btn->Destroy();
  101. }
  102. m_btnSizer = new wxBoxSizer( wxHORIZONTAL );
  103. for( auto type : btnTypes )
  104. {
  105. if( !( aButtons & type.first ) )
  106. continue;
  107. wxButton* btn = new wxButton( this, type.second );
  108. m_btnSizer->Add( btn, 1, wxALL | wxEXPAND | wxALIGN_RIGHT );
  109. m_buttons.push_back( btn );
  110. }
  111. m_sizerMain->Add( m_btnSizer, 0, wxALL | wxALIGN_RIGHT, 5 );
  112. return *this;
  113. }
  114. KIDIALOG& KIDIALOG::DoNotShowCheckbox()
  115. {
  116. if( !m_cbDoNotShow )
  117. {
  118. m_cbDoNotShow = new wxCheckBox( this, wxID_ANY, _( "Do not show again" ) );
  119. m_sizerMain->Insert( 1, m_cbDoNotShow, 1, wxALL | wxEXPAND, 5 );
  120. }
  121. return *this;
  122. }
  123. bool KIDIALOG::DoNotShowAgain() const
  124. {
  125. return doNotShowAgainDlgs.count( hash() ) > 0;
  126. }
  127. void KIDIALOG::ForceShowAgain()
  128. {
  129. doNotShowAgainDlgs.erase( hash() );
  130. }
  131. int KIDIALOG::ShowModal()
  132. {
  133. // Check if this dialog should be shown to the user
  134. auto it = doNotShowAgainDlgs.find( hash() );
  135. if( it != doNotShowAgainDlgs.end() )
  136. return it->second;
  137. Layout();
  138. m_sizerMain->Fit( this );
  139. int ret = wxDialog::ShowModal();
  140. // Has the user asked not to show the dialog again
  141. if( m_cbDoNotShow && m_cbDoNotShow->IsChecked() )
  142. doNotShowAgainDlgs[hash()] = ret;
  143. return ret;
  144. }
  145. void KIDIALOG::onButtonClick( wxCommandEvent& aEvent )
  146. {
  147. EndModal( aEvent.GetId() );
  148. }
  149. unsigned long KIDIALOG::hash() const
  150. {
  151. std::size_t h1 = std::hash<wxString>{}( m_message );
  152. std::size_t h2 = std::hash<wxString>{}( m_customTitle );
  153. std::size_t h3 = std::hash<int>{}( m_type );
  154. return h1 ^ ( h2 << 1 ) ^ ( h3 << 2 );
  155. }
  156. class DIALOG_EXIT: public DIALOG_EXIT_BASE
  157. {
  158. public:
  159. DIALOG_EXIT( wxWindow *aParent, const wxString& aMessage ) :
  160. DIALOG_EXIT_BASE( aParent )
  161. {
  162. m_bitmap->SetBitmap( KiBitmap( dialog_warning_xpm ) );
  163. if( !aMessage.IsEmpty() )
  164. m_TextInfo->SetLabel( aMessage );
  165. GetSizer()->Fit( this );
  166. GetSizer()->SetSizeHints( this );
  167. };
  168. private:
  169. void OnSaveAndExit( wxCommandEvent& event ) override { EndModal( wxID_YES ); }
  170. void OnExitNoSave( wxCommandEvent& event ) override { EndModal( wxID_NO ); }
  171. };
  172. int DisplayExitDialog( wxWindow* parent, const wxString& aMessage )
  173. {
  174. DIALOG_EXIT dlg( parent, aMessage );
  175. int ret = dlg.ShowModal();
  176. // Returns wxID_YES, wxID_NO, or wxID_CANCEL
  177. return ret;
  178. }
  179. // DisplayError should be deprecated, use DisplayErrorMessage instead
  180. void DisplayError( wxWindow* parent, const wxString& text, int displaytime )
  181. {
  182. wxMessageDialog* dialog;
  183. int icon = displaytime > 0 ? wxICON_INFORMATION : wxICON_ERROR;
  184. dialog = new wxMessageDialog( parent, text, _( "Warning" ),
  185. wxOK | wxCENTRE | wxRESIZE_BORDER | icon );
  186. dialog->ShowModal();
  187. dialog->Destroy();
  188. }
  189. void DisplayErrorMessage( wxWindow* aParent, const wxString& aText, const wxString& aExtraInfo )
  190. {
  191. wxRichMessageDialog* dlg;
  192. dlg = new wxRichMessageDialog( aParent, aText, _( "Error" ),
  193. wxOK | wxCENTRE | wxRESIZE_BORDER | wxICON_ERROR );
  194. if( !aExtraInfo.IsEmpty() )
  195. {
  196. dlg->ShowDetailedText( aExtraInfo );
  197. }
  198. dlg->ShowModal();
  199. dlg->Destroy();
  200. }
  201. void DisplayInfoMessage( wxWindow* aParent, const wxString& aMessage, const wxString& aExtraInfo )
  202. {
  203. wxRichMessageDialog* dlg;
  204. dlg = new wxRichMessageDialog( aParent, aMessage, _( "Info" ),
  205. wxOK | wxCENTRE | wxRESIZE_BORDER | wxICON_INFORMATION );
  206. if( !aExtraInfo.IsEmpty() )
  207. {
  208. dlg->ShowDetailedText( aExtraInfo );
  209. }
  210. dlg->ShowModal();
  211. dlg->Destroy();
  212. }
  213. bool IsOK( wxWindow* aParent, const wxString& aMessage )
  214. {
  215. wxMessageDialog dlg( aParent, aMessage, _( "Confirmation" ),
  216. wxYES_NO | wxCENTRE | wxICON_QUESTION );
  217. return dlg.ShowModal() == wxID_YES;
  218. }
  219. class DIALOG_YES_NO_CANCEL : public DIALOG_EXIT
  220. {
  221. public:
  222. DIALOG_YES_NO_CANCEL( wxWindow *aParent,
  223. const wxString& aPrimaryMessage,
  224. const wxString& aSecondaryMessage = wxEmptyString,
  225. const wxString& aYesButtonText = wxEmptyString,
  226. const wxString& aNoButtonText = wxEmptyString,
  227. const wxString& aCancelButtonText = wxEmptyString ) :
  228. DIALOG_EXIT( aParent, aSecondaryMessage )
  229. {
  230. m_TextInfo->SetLabel( aPrimaryMessage );
  231. if( aSecondaryMessage.IsEmpty() )
  232. m_staticText2->Hide();
  233. m_buttonSaveAndExit->SetLabel( aYesButtonText.IsEmpty() ? wxGetStockLabel( wxID_YES ) :
  234. aYesButtonText );
  235. m_buttonExitNoSave->SetLabel( aNoButtonText.IsEmpty() ? wxGetStockLabel( wxID_NO ) :
  236. aNoButtonText );
  237. m_buttonCancel->SetLabel( aCancelButtonText.IsEmpty() ? wxGetStockLabel( wxID_CANCEL ) :
  238. aCancelButtonText );
  239. GetSizer()->Fit( this );
  240. GetSizer()->SetSizeHints( this );
  241. };
  242. };
  243. int YesNoCancelDialog( wxWindow* aParent,
  244. const wxString& aPrimaryMessage,
  245. const wxString& aSecondaryMessage,
  246. const wxString& aYesButtonText,
  247. const wxString& aNoButtonText,
  248. const wxString& aCancelButtonText )
  249. {
  250. DIALOG_YES_NO_CANCEL dlg( aParent, aPrimaryMessage, aSecondaryMessage,
  251. aYesButtonText, aNoButtonText, aCancelButtonText );
  252. return dlg.ShowModal();
  253. }
  254. int SelectSingleOption( wxWindow* aParent, const wxString& aTitle, const wxString& aMessage, const wxArrayString& aOptions )
  255. {
  256. int ret = -1;
  257. wxDialog* dlg = new wxDialog( aParent, wxID_ANY, aTitle );
  258. wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL );
  259. if( !aMessage.IsEmpty() )
  260. boxSizer->Add( new wxStaticText( dlg, wxID_ANY, aMessage ), 0, wxEXPAND | wxALL, 5 );
  261. std::vector<wxRadioButton*> radioButtons;
  262. radioButtons.reserve( aOptions.Count() );
  263. for( const wxString& option : aOptions )
  264. {
  265. radioButtons.emplace_back( new wxRadioButton( dlg, wxID_ANY, _( option ) ) );
  266. boxSizer->Add( radioButtons.back(), 0, wxEXPAND | wxALL, 5 );
  267. }
  268. wxStdDialogButtonSizer* m_sdboxSizer = new wxStdDialogButtonSizer();
  269. wxButton* btnOk = new wxButton( dlg, wxID_OK );
  270. m_sdboxSizer->AddButton( btnOk );
  271. m_sdboxSizer->AddButton( new wxButton( dlg, wxID_CANCEL ) );
  272. m_sdboxSizer->Realize();
  273. btnOk->SetDefault();
  274. boxSizer->Add( m_sdboxSizer, 1, wxEXPAND | wxALL, 5 );
  275. dlg->SetSizer( boxSizer );
  276. dlg->Layout();
  277. boxSizer->Fit( dlg );
  278. boxSizer->SetSizeHints( dlg );
  279. dlg->Centre( wxBOTH );
  280. if( dlg->ShowModal() == wxID_OK )
  281. {
  282. for( unsigned int i = 0; i < radioButtons.size(); ++i )
  283. {
  284. if( radioButtons[i]->GetValue() )
  285. {
  286. ret = i;
  287. break;
  288. }
  289. }
  290. }
  291. else
  292. {
  293. ret = -1;
  294. }
  295. dlg->Destroy();
  296. return ret;
  297. }
  298. class DIALOG_MULTI_OPTIONS : public wxDialog
  299. {
  300. public:
  301. DIALOG_MULTI_OPTIONS( wxWindow* aParent, const wxString& aTitle, const wxString& aMessage,
  302. const wxArrayString& aOptions )
  303. : wxDialog( aParent, wxID_ANY, aTitle )
  304. {
  305. SetSizeHints( wxDefaultSize, wxDefaultSize );
  306. wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL );
  307. if( !aMessage.IsEmpty() )
  308. boxSizer->Add( new wxStaticText( this, wxID_ANY, aMessage ), 0, wxEXPAND | wxALL, 5 );
  309. m_checklist = new wxCheckListBox( this, wxID_ANY );
  310. for( const wxString& option : aOptions )
  311. m_checklist->Append( option );
  312. boxSizer->Add( m_checklist, 1, wxEXPAND | wxALL, 5 );
  313. wxBoxSizer* btnSizer = new wxBoxSizer( wxHORIZONTAL );
  314. wxButton* selectAll = new wxButton( this, wxID_ANY, _( "Select All" ) );
  315. btnSizer->Add( selectAll, 1, wxEXPAND | wxALL, 5 );
  316. wxButton* unselectAll = new wxButton( this, wxID_ANY, _( "Unselect All" ) );
  317. btnSizer->Add( unselectAll, 1, wxEXPAND | wxALL, 5 );
  318. boxSizer->Add( btnSizer, 0, wxEXPAND | wxALL, 5 );
  319. wxStdDialogButtonSizer* m_sdboxSizer = new wxStdDialogButtonSizer();
  320. wxButton* btnOk = new wxButton( this, wxID_OK );
  321. m_sdboxSizer->AddButton( btnOk );
  322. m_sdboxSizer->AddButton( new wxButton( this, wxID_CANCEL ) );
  323. m_sdboxSizer->Realize();
  324. btnOk->SetDefault();
  325. boxSizer->Add( m_sdboxSizer, 0, wxEXPAND | wxALL, 5 );
  326. SetSizer( boxSizer );
  327. Layout();
  328. boxSizer->Fit( this );
  329. boxSizer->SetSizeHints( this );
  330. Centre( wxBOTH );
  331. selectAll->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_MULTI_OPTIONS::selectAll, this );
  332. unselectAll->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_MULTI_OPTIONS::unselectAll, this );
  333. }
  334. std::vector<int> GetSelection() const
  335. {
  336. std::vector<int> ret;
  337. for( unsigned int i = 0; i < m_checklist->GetCount(); ++i )
  338. {
  339. if( m_checklist->IsChecked( i ) )
  340. ret.push_back( i );
  341. }
  342. return ret;
  343. }
  344. void SetCheckboxes( bool aValue )
  345. {
  346. for( unsigned int i = 0; i < m_checklist->GetCount(); ++i )
  347. m_checklist->Check( i, aValue );
  348. }
  349. protected:
  350. wxCheckListBox* m_checklist;
  351. void selectAll( wxCommandEvent& aEvent )
  352. {
  353. SetCheckboxes( true );
  354. }
  355. void unselectAll( wxCommandEvent& aEvent )
  356. {
  357. SetCheckboxes( false );
  358. }
  359. };
  360. std::pair<bool, std::vector<int>> SelectMultipleOptions( wxWindow* aParent, const wxString& aTitle,
  361. const wxString& aMessage, const wxArrayString& aOptions, bool aDefaultState )
  362. {
  363. std::vector<int> ret;
  364. bool clickedOk;
  365. DIALOG_MULTI_OPTIONS dlg( aParent, aTitle, aMessage, aOptions );
  366. dlg.SetCheckboxes( aDefaultState );
  367. if( dlg.ShowModal() == wxID_OK )
  368. {
  369. ret = dlg.GetSelection();
  370. clickedOk = true;
  371. }
  372. else
  373. {
  374. clickedOk = false;
  375. }
  376. return std::make_pair( clickedOk, ret );
  377. }
  378. std::pair<bool, std::vector<int>> SelectMultipleOptions( wxWindow* aParent, const wxString& aTitle,
  379. const wxString& aMessage, const std::vector<std::string>& aOptions, bool aDefaultState )
  380. {
  381. wxArrayString array;
  382. for( const auto& option : aOptions )
  383. array.Add( option );
  384. return SelectMultipleOptions( aParent, aTitle, aMessage, array, aDefaultState );
  385. }