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.

501 lines
19 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2010 Rafael Sokolowski <Rafael.Sokolowski@web.de>
  5. * Copyright (C) 2017-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. #include <config.h>
  25. #include <string>
  26. #include <build_version.h>
  27. #include <wx/clipbrd.h>
  28. #include <wx/msgdlg.h>
  29. #include <wx/hyperlink.h>
  30. /* All KiCad icons are linked into shared library 'libbitmaps.a'.
  31. * Icons:
  32. * preference_xpm; // Icon for 'Developers' tab
  33. * editor_xpm; // Icon for 'Doc Writers' tab
  34. * color_materials_xpm; // Icon for 'Artists' tab
  35. * language_xpm; // Icon for 'Translators' tab
  36. * right_xpm; // Right arrow icon for list items
  37. * info_xpm; // Bulb for description tab
  38. * tools_xpm; // Sheet of paper icon for license info tab
  39. */
  40. #include <bitmaps.h>
  41. #include <build_version.h>
  42. #include <dialogs/html_message_box.h>
  43. #include <tool/tool_manager.h>
  44. #include "dialog_about.h"
  45. DIALOG_ABOUT::DIALOG_ABOUT( EDA_BASE_FRAME *aParent, ABOUT_APP_INFO& aAppInfo )
  46. : DIALOG_ABOUT_BASE( aParent ), m_info( aAppInfo )
  47. {
  48. wxASSERT( aParent != nullptr );
  49. SetEvtHandlerEnabled( false );
  50. // TODO: Change these to 16x16 versions when available
  51. m_images = new wxImageList( 24, 24, false, 9 );
  52. m_images->Add( KiBitmap( BITMAPS::info ) ); // INFORMATION
  53. m_images->Add( KiBitmap( BITMAPS::recent ) ); // VERSION
  54. m_images->Add( KiBitmap( BITMAPS::preference ) ); // DEVELOPERS
  55. m_images->Add( KiBitmap( BITMAPS::editor ) ); // DOCWRITERS
  56. m_images->Add( KiBitmap( BITMAPS::library ) ); // LIBRARIANS
  57. m_images->Add( KiBitmap( BITMAPS::color_materials ) ); // ARTISTS
  58. m_images->Add( KiBitmap( BITMAPS::language ) ); // TRANSLATORS
  59. m_images->Add( KiBitmap( BITMAPS::zip ) ); // PACKAGERS
  60. m_images->Add( KiBitmap( BITMAPS::tools ) ); // LICENSE
  61. m_notebook->SetImageList( m_images );
  62. if( m_info.GetAppIcon().IsOk() )
  63. {
  64. SetIcon( m_info.GetAppIcon() );
  65. m_bitmapApp->SetBitmap( m_info.GetAppIcon() );
  66. }
  67. else
  68. {
  69. wxIcon icon;
  70. if( IsNightlyVersion() )
  71. icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly ) );
  72. else
  73. icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad ) );
  74. SetIcon( icon );
  75. m_bitmapApp->SetBitmap( icon );
  76. }
  77. m_titleName = aParent->GetAboutTitle();
  78. m_untranslatedTitleName = aParent->GetUntranslatedAboutTitle();
  79. m_staticTextAppTitle->SetLabel( m_titleName );
  80. m_staticTextCopyright->SetLabel( m_info.GetCopyright() );
  81. m_staticTextBuildVersion->SetLabel( "Version: " + m_info.GetBuildVersion() );
  82. m_staticTextLibVersion->SetLabel( m_info.GetLibVersion() );
  83. SetTitle( wxString::Format( _( "About %s" ), m_titleName ) );
  84. createNotebooks();
  85. SetEvtHandlerEnabled( true );
  86. GetSizer()->SetSizeHints( this );
  87. SetFocus();
  88. Centre();
  89. }
  90. DIALOG_ABOUT::~DIALOG_ABOUT()
  91. {
  92. delete m_images;
  93. }
  94. wxFlexGridSizer* DIALOG_ABOUT::createFlexGridSizer()
  95. {
  96. // three columns with vertical and horizontal extra space of two pixels
  97. wxFlexGridSizer* fgSizer = new wxFlexGridSizer( 3, 2, 2 );
  98. fgSizer->SetFlexibleDirection( wxHORIZONTAL );
  99. fgSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
  100. return fgSizer;
  101. }
  102. void DIALOG_ABOUT::createNotebooks()
  103. {
  104. createNotebookHtmlPage( m_notebook, _( "About" ), IMAGES::INFORMATION,
  105. m_info.GetDescription() );
  106. wxString version = GetVersionInfoData( m_untranslatedTitleName, true );
  107. createNotebookHtmlPage( m_notebook, _( "Version" ), IMAGES::VERSION, version, true );
  108. createNotebookPageByCategory( m_notebook, _( "Developers" ) , IMAGES::DEVELOPERS,
  109. m_info.GetDevelopers() );
  110. createNotebookPage( m_notebook, _( "Doc Writers" ), IMAGES::DOCWRITERS,
  111. m_info.GetDocWriters() );
  112. createNotebookPageByCategory( m_notebook, _( "Librarians" ), IMAGES::LIBRARIANS,
  113. m_info.GetLibrarians() );
  114. createNotebookPageByCategory( m_notebook, _( "Artists" ), IMAGES::ARTISTS,
  115. m_info.GetArtists() );
  116. createNotebookPageByCategory( m_notebook, _( "Translators" ), IMAGES::TRANSLATORS,
  117. m_info.GetTranslators() );
  118. createNotebookPageByCategory( m_notebook, _( "Packagers" ), IMAGES::PACKAGERS,
  119. m_info.GetPackagers() );
  120. createNotebookHtmlPage( m_notebook, _( "License" ), IMAGES::LICENSE, m_info.GetLicense() );
  121. }
  122. void DIALOG_ABOUT::createNotebookPage( wxNotebook* aParent, const wxString& aCaption,
  123. IMAGES aIconIndex, const CONTRIBUTORS& aContributors )
  124. {
  125. wxPanel* outerPanel = new wxPanel( aParent );
  126. wxBoxSizer* outerSizer = new wxBoxSizer( wxVERTICAL );
  127. wxBoxSizer* bSizer = new wxBoxSizer( wxHORIZONTAL );
  128. wxScrolledWindow* m_scrolledWindow1 = new wxScrolledWindow( outerPanel, wxID_ANY,
  129. wxDefaultPosition,
  130. wxDefaultSize,
  131. wxHSCROLL|wxVSCROLL );
  132. m_scrolledWindow1->SetScrollRate( 5, 5 );
  133. /* Panel for additional space at the left,
  134. * but can also be used to show an additional bitmap.
  135. */
  136. wxPanel* panel1 = new wxPanel( m_scrolledWindow1 );
  137. wxFlexGridSizer* fgSizer1 = createFlexGridSizer();
  138. for( size_t i=0; i<aContributors.GetCount(); ++i )
  139. {
  140. CONTRIBUTOR* contributor = &aContributors.Item( i );
  141. // Icon at first column
  142. wxStaticBitmap* m_bitmap1 = createStaticBitmap( m_scrolledWindow1, contributor->GetIcon() );
  143. fgSizer1->Add( m_bitmap1, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5 );
  144. // Name of contributor at second column
  145. if ( contributor->GetName() != wxEmptyString )
  146. {
  147. wxStaticText* m_staticText1 = new wxStaticText( m_scrolledWindow1, wxID_ANY,
  148. contributor->GetName(),
  149. wxDefaultPosition, wxDefaultSize, 0 );
  150. m_staticText1->Wrap( -1 );
  151. fgSizer1->Add( m_staticText1, 0, wxALIGN_LEFT|wxBOTTOM, 2 );
  152. }
  153. else
  154. {
  155. fgSizer1->AddSpacer( 5 );
  156. }
  157. // Email address of contributor at third column
  158. if ( contributor->GetExtra() != wxEmptyString )
  159. {
  160. wxStaticText* hyperlink = wxStaticTextRef( m_scrolledWindow1,
  161. contributor->GetExtra() );
  162. fgSizer1->Add( hyperlink, 0, wxALIGN_LEFT|wxBOTTOM, 2 );
  163. }
  164. else
  165. {
  166. fgSizer1->AddSpacer( 5 );
  167. }
  168. }
  169. bSizer->Add( panel1, 1, wxEXPAND|wxALL, 10 );
  170. bSizer->Add( fgSizer1, 7, wxEXPAND|wxALL, 10 ); // adjust width of panel with first int value
  171. m_scrolledWindow1->SetSizer( bSizer );
  172. m_scrolledWindow1->Layout();
  173. bSizer->Fit( m_scrolledWindow1 );
  174. outerSizer->Add( m_scrolledWindow1, 1, wxEXPAND, 0 );
  175. outerPanel->SetSizer( outerSizer );
  176. aParent->AddPage( outerPanel, aCaption, false, static_cast<int>( aIconIndex ) );
  177. }
  178. void DIALOG_ABOUT::createNotebookPageByCategory( wxNotebook* aParent, const wxString& aCaption,
  179. IMAGES aIconIndex,
  180. const CONTRIBUTORS& aContributors )
  181. {
  182. // The left justification between wxStaticText and wxHyperlinkCtrl is different so
  183. // we must pad to make the alignment look decent.
  184. //
  185. // @todo Just make all of the contributor lists HTML so the alignment is consistent.
  186. wxString padding;
  187. // Of course the padding is different depending on the platform so we adjust the
  188. // padding accordingly.
  189. #if defined( __WXGTK__ )
  190. padding += " ";
  191. #endif
  192. wxPanel* outerPanel = new wxPanel( aParent );
  193. wxBoxSizer* outerSizer = new wxBoxSizer( wxVERTICAL );
  194. wxBoxSizer* bSizer = new wxBoxSizer( wxHORIZONTAL );
  195. wxScrolledWindow* m_scrolledWindow1 = new wxScrolledWindow( outerPanel, wxID_ANY,
  196. wxDefaultPosition,
  197. wxDefaultSize,
  198. wxHSCROLL|wxVSCROLL );
  199. m_scrolledWindow1->SetScrollRate( 5, 5 );
  200. /* Panel for additional space at the left,
  201. * but can also be used to show an additional bitmap.
  202. */
  203. wxPanel* panel1 = new wxPanel( m_scrolledWindow1 );
  204. wxFlexGridSizer* fgSizer1 = createFlexGridSizer();
  205. for( size_t i=0; i < aContributors.GetCount(); ++i )
  206. {
  207. CONTRIBUTOR* contributor = &aContributors.Item( i );
  208. wxBitmap* icon = contributor->GetIcon();
  209. wxString category = contributor->GetCategory();
  210. /* to construct the next row we expect to have
  211. * a category and a contributor that was not considered up to now
  212. */
  213. if( ( category != wxEmptyString ) && !( contributor->IsChecked() ) )
  214. {
  215. // Icon at first column
  216. wxStaticBitmap* m_bitmap1 = createStaticBitmap( m_scrolledWindow1, icon );
  217. fgSizer1->Add( m_bitmap1, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5 );
  218. // Category name at second column
  219. wxStaticText* m_staticText1 = new wxStaticText( m_scrolledWindow1, wxID_ANY,
  220. contributor->GetCategory() + wxT( ":" ),
  221. wxDefaultPosition, wxDefaultSize, 0 );
  222. m_staticText1->SetFont( m_staticText1->GetFont().Bold() );
  223. m_staticText1->Wrap( -1 );
  224. fgSizer1->Add( m_staticText1, 0, wxALIGN_LEFT|wxBOTTOM|wxEXPAND, 2 );
  225. // Nothing at third column
  226. fgSizer1->AddSpacer( 5 );
  227. // Now, all contributors of the same category will follow
  228. for( size_t j=0; j < aContributors.GetCount(); ++j )
  229. {
  230. CONTRIBUTOR* sub_contributor = &aContributors.Item( j );
  231. if ( sub_contributor->GetCategory() == category )
  232. {
  233. // First column is empty
  234. fgSizer1->AddSpacer( 5 );
  235. wxControl* ctrl;
  236. // No URL supplied, display normal text control
  237. if( sub_contributor->GetUrl().IsEmpty() )
  238. {
  239. ctrl = new wxStaticText( m_scrolledWindow1, wxID_ANY,
  240. padding + wxT( "" ) + sub_contributor->GetName(),
  241. wxDefaultPosition,
  242. wxDefaultSize, 0 );
  243. }
  244. else
  245. {
  246. // Display a hyperlink control instead
  247. ctrl = new wxHyperlinkCtrl( m_scrolledWindow1, wxID_ANY,
  248. wxT( "" ) + sub_contributor->GetName(),
  249. sub_contributor->GetUrl(),
  250. wxDefaultPosition,
  251. wxDefaultSize,
  252. wxBORDER_NONE | wxHL_CONTEXTMENU | wxHL_ALIGN_LEFT );
  253. }
  254. m_staticText1->Wrap( -1 );
  255. fgSizer1->Add( ctrl, 0, wxALIGN_LEFT|wxBOTTOM, 2 );
  256. // Email address of contributor at third column
  257. if( sub_contributor->GetExtra() != wxEmptyString )
  258. {
  259. wxStaticText* mail = wxStaticTextRef( m_scrolledWindow1,
  260. sub_contributor->GetExtra() );
  261. fgSizer1->Add( mail, 0, wxALIGN_LEFT|wxBOTTOM, 2 );
  262. }
  263. else
  264. {
  265. fgSizer1->AddSpacer( 5 );
  266. }
  267. /* this contributor was added to the GUI,
  268. * thus can be ignored next time
  269. */
  270. sub_contributor->SetChecked( true );
  271. }
  272. }
  273. }
  274. else
  275. {
  276. continue;
  277. }
  278. }
  279. /* Now, lets list the remaining contributors that have not been considered
  280. * because they were not assigned to any category.
  281. */
  282. for ( size_t k=0; k < aContributors.GetCount(); ++k )
  283. {
  284. CONTRIBUTOR* contributor = &aContributors.Item( k );
  285. if ( contributor->IsChecked() )
  286. continue;
  287. // Icon at first column
  288. wxStaticBitmap* m_bitmap1 = createStaticBitmap( m_scrolledWindow1, contributor->GetIcon() );
  289. fgSizer1->Add( m_bitmap1, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5 );
  290. // Name of contributor at second column
  291. if( contributor->GetName() != wxEmptyString )
  292. {
  293. wxStaticText* m_staticText1 = new wxStaticText( m_scrolledWindow1, wxID_ANY,
  294. contributor->GetName(),
  295. wxDefaultPosition, wxDefaultSize, 0 );
  296. m_staticText1->Wrap( -1 );
  297. fgSizer1->Add( m_staticText1, 0, wxALIGN_LEFT|wxBOTTOM, 2 );
  298. }
  299. else
  300. {
  301. fgSizer1->AddSpacer( 5 );
  302. }
  303. // Email address of contributor at third column
  304. if ( contributor->GetExtra() != wxEmptyString )
  305. {
  306. wxStaticText* mail = wxStaticTextRef( m_scrolledWindow1, contributor->GetExtra() );
  307. fgSizer1->Add( mail, 0, wxALIGN_LEFT|wxBOTTOM, 2 );
  308. }
  309. else
  310. {
  311. fgSizer1->AddSpacer( 5 );
  312. }
  313. }
  314. bSizer->Add( panel1, 1, wxEXPAND|wxALL, 10 );
  315. bSizer->Add( fgSizer1, 7, wxEXPAND|wxALL, 10 ); // adjust width of panel with first int value
  316. m_scrolledWindow1->SetSizer( bSizer );
  317. m_scrolledWindow1->Layout();
  318. bSizer->Fit( m_scrolledWindow1 );
  319. outerSizer->Add( m_scrolledWindow1, 1, wxEXPAND, 0 );
  320. outerPanel->SetSizer( outerSizer );
  321. aParent->AddPage( outerPanel, aCaption, false, static_cast<int>( aIconIndex ) );
  322. }
  323. void DIALOG_ABOUT::createNotebookHtmlPage( wxNotebook* aParent, const wxString& aCaption,
  324. IMAGES aIconIndex, const wxString& html,
  325. bool aSelection )
  326. {
  327. wxPanel* panel = new wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
  328. wxTAB_TRAVERSAL );
  329. wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
  330. int flags = aSelection ? wxHW_SCROLLBAR_AUTO : ( wxHW_SCROLLBAR_AUTO | wxHW_NO_SELECTION );
  331. // the HTML page is going to be created with previously created HTML content
  332. HTML_WINDOW* htmlWindow = new HTML_WINDOW( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
  333. flags );
  334. // HTML font set to font properties as they are used for widgets to have an unique look
  335. // under different platforms with HTML
  336. wxFont font = GetFont();
  337. htmlWindow->SetStandardFonts( font.GetPointSize(), font.GetFaceName(), font.GetFaceName() );
  338. htmlWindow->SetPage( html );
  339. // the HTML window shall not be used to open external links, thus this task is delegated
  340. // to users default browser
  341. htmlWindow->Connect( wxEVT_COMMAND_HTML_LINK_CLICKED,
  342. wxHtmlLinkEventHandler( DIALOG_ABOUT::onHtmlLinkClicked ), NULL, this );
  343. // no additional space around the HTML window as it is also the case by the other notebook pages
  344. bSizer->Add( htmlWindow, 1, wxEXPAND, 0 );
  345. panel->SetSizer( bSizer );
  346. aParent->AddPage( panel, aCaption, false, static_cast<int>( aIconIndex ) );
  347. }
  348. wxStaticText* DIALOG_ABOUT::wxStaticTextRef( wxScrolledWindow* aParent, const wxString& aReference )
  349. {
  350. wxStaticText* text = new wxStaticText( aParent, wxID_ANY,
  351. wxT( "(" ) + aReference + wxT( ")" ) );
  352. return text;
  353. }
  354. wxStaticBitmap* DIALOG_ABOUT::createStaticBitmap( wxScrolledWindow* aParent, wxBitmap* aIcon )
  355. {
  356. wxStaticBitmap* bitmap = new wxStaticBitmap( aParent, wxID_ANY, wxNullBitmap,
  357. wxDefaultPosition, wxDefaultSize, 0 );
  358. if( aIcon )
  359. bitmap->SetBitmap( *aIcon );
  360. else
  361. bitmap->SetBitmap( KiBitmap( BITMAPS::right ) );
  362. return bitmap;
  363. }
  364. void DIALOG_ABOUT::onHtmlLinkClicked( wxHtmlLinkEvent& event )
  365. {
  366. ::wxLaunchDefaultBrowser( event.GetLinkInfo().GetHref() );
  367. }
  368. void DIALOG_ABOUT::onCopyVersionInfo( wxCommandEvent& event )
  369. {
  370. wxLogNull doNotLog; // disable logging of failed clipboard actions
  371. if( !wxTheClipboard->Open() )
  372. {
  373. wxMessageBox( _( "Could not open clipboard to write version information." ),
  374. _( "Clipboard Error" ), wxOK | wxICON_EXCLAMATION, this );
  375. return;
  376. }
  377. wxString msg_version = GetVersionInfoData( m_untranslatedTitleName );
  378. wxTheClipboard->SetData( new wxTextDataObject( msg_version ) );
  379. wxTheClipboard->Flush(); // Allow clipboard data to be available after KiCad closes
  380. wxTheClipboard->Close();
  381. m_btCopyVersionInfo->SetLabel( _( "Copied..." ) );
  382. }
  383. void DIALOG_ABOUT::onDonateClick( wxCommandEvent& event )
  384. {
  385. if( TOOL_MANAGER* mgr = static_cast<EDA_BASE_FRAME*>( GetParent() )->GetToolManager() )
  386. mgr->RunAction( "common.SuiteControl.donate", true );
  387. }
  388. void DIALOG_ABOUT::onReportBug( wxCommandEvent& event )
  389. {
  390. if( TOOL_MANAGER* mgr = static_cast<EDA_BASE_FRAME*>( GetParent() )->GetToolManager() )
  391. mgr->RunAction( "common.SuiteControl.reportBug", true );
  392. }
  393. void DIALOG_ABOUT::OnNotebookPageChanged( wxNotebookEvent& aEvent )
  394. {
  395. // Work around wxMac issue where the notebook pages are blank
  396. #ifdef __WXMAC__
  397. int page = aEvent.GetSelection();
  398. if( page >= 0 )
  399. m_notebook->ChangeSelection( static_cast<unsigned>( page ) );
  400. #endif
  401. }