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.

504 lines
13 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
1 year ago
1 year ago
1 year ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 CERN
  5. * Copyright (C) 2015-2024 KiCad Developers, see AUTHORS.txt for contributors.
  6. * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 2 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <algorithm>
  22. #include "wx_html_report_panel.h"
  23. #include <wildcards_and_files_ext.h>
  24. #include <gal/color4d.h>
  25. #include <wx/clipbrd.h>
  26. #include <string_utils.h>
  27. #include <wx/ffile.h>
  28. #include <wx/log.h>
  29. #include <wx/filedlg.h>
  30. #include <wx/msgdlg.h>
  31. #include <wx/menu.h>
  32. #include <wx/textctrl.h>
  33. #include <kiplatform/ui.h>
  34. #include <kiway_holder.h>
  35. #include <project.h>
  36. WX_HTML_REPORT_PANEL::WX_HTML_REPORT_PANEL( wxWindow* parent, wxWindowID id, const wxPoint& pos,
  37. const wxSize& size, long style ) :
  38. WX_HTML_REPORT_PANEL_BASE( parent, id, pos, size, style ),
  39. m_reporter( this ),
  40. m_severities( -1 ),
  41. m_lazyUpdate( false )
  42. {
  43. syncCheckboxes();
  44. m_htmlView->SetFont( KIUI::GetInfoFont( m_htmlView ) );
  45. Flush();
  46. Connect( wxEVT_COMMAND_MENU_SELECTED,
  47. wxMenuEventHandler( WX_HTML_REPORT_PANEL::onMenuEvent ), nullptr, this );
  48. m_htmlView->Bind( wxEVT_SYS_COLOUR_CHANGED,
  49. wxSysColourChangedEventHandler( WX_HTML_REPORT_PANEL::onThemeChanged ),
  50. this );
  51. }
  52. WX_HTML_REPORT_PANEL::~WX_HTML_REPORT_PANEL()
  53. {
  54. }
  55. void WX_HTML_REPORT_PANEL::onThemeChanged( wxSysColourChangedEvent &aEvent )
  56. {
  57. Flush();
  58. aEvent.Skip();
  59. }
  60. void WX_HTML_REPORT_PANEL::MsgPanelSetMinSize( const wxSize& aMinSize )
  61. {
  62. m_fgSizer->SetMinSize( aMinSize );
  63. GetSizer()->SetSizeHints( this );
  64. }
  65. REPORTER& WX_HTML_REPORT_PANEL::Reporter()
  66. {
  67. return m_reporter;
  68. }
  69. void WX_HTML_REPORT_PANEL::Report( const wxString& aText, SEVERITY aSeverity,
  70. REPORTER::LOCATION aLocation )
  71. {
  72. REPORT_LINE line;
  73. line.message = aText;
  74. line.severity = aSeverity;
  75. if( aLocation == REPORTER::LOC_HEAD )
  76. m_reportHead.push_back( line );
  77. else if( aLocation == REPORTER::LOC_TAIL )
  78. m_reportTail.push_back( line );
  79. else
  80. m_report.push_back( line );
  81. if( !m_lazyUpdate )
  82. {
  83. m_htmlView->AppendToPage( generateHtml( line ) );
  84. scrollToBottom();
  85. }
  86. }
  87. void WX_HTML_REPORT_PANEL::SetLazyUpdate( bool aLazyUpdate )
  88. {
  89. m_lazyUpdate = aLazyUpdate;
  90. }
  91. void WX_HTML_REPORT_PANEL::Flush( bool aSort )
  92. {
  93. wxString html;
  94. if( aSort )
  95. {
  96. std::sort( m_report.begin(), m_report.end(),
  97. []( const REPORT_LINE& a, const REPORT_LINE& b)
  98. {
  99. return a.severity < b.severity;
  100. });
  101. }
  102. for( const auto& line : m_reportHead )
  103. html += generateHtml( line );
  104. for( const auto& line : m_report )
  105. html += generateHtml( line );
  106. for( const auto& line : m_reportTail )
  107. html += generateHtml( line );
  108. m_htmlView->SetPage( html );
  109. scrollToBottom();
  110. }
  111. void WX_HTML_REPORT_PANEL::scrollToBottom()
  112. {
  113. int x, y, xUnit, yUnit;
  114. m_htmlView->GetVirtualSize( &x, &y );
  115. m_htmlView->GetScrollPixelsPerUnit( &xUnit, &yUnit );
  116. m_htmlView->Scroll( 0, y / yUnit );
  117. updateBadges();
  118. }
  119. void WX_HTML_REPORT_PANEL::updateBadges()
  120. {
  121. int count = Count(RPT_SEVERITY_ERROR );
  122. m_errorsBadge->UpdateNumber( count, RPT_SEVERITY_ERROR );
  123. count = Count(RPT_SEVERITY_WARNING );
  124. m_warningsBadge->UpdateNumber( count, RPT_SEVERITY_WARNING );
  125. }
  126. int WX_HTML_REPORT_PANEL::Count( int severityMask )
  127. {
  128. int count = 0;
  129. for( const auto& reportLineArray : { m_report, m_reportHead, m_reportTail } )
  130. {
  131. for( const REPORT_LINE& reportLine : reportLineArray )
  132. {
  133. if( severityMask & reportLine.severity )
  134. count++;
  135. }
  136. }
  137. return count;
  138. }
  139. wxString WX_HTML_REPORT_PANEL::generateHtml( const REPORT_LINE& aLine )
  140. {
  141. wxString retv;
  142. if( !( m_severities & aLine.severity ) )
  143. return retv;
  144. if( KIPLATFORM::UI::IsDarkTheme() )
  145. {
  146. switch( aLine.severity )
  147. {
  148. case RPT_SEVERITY_ERROR:
  149. retv = wxS( "<font color=#F04040 size=3>" ) + _( "Error:" ) + wxS( " </font>" )
  150. wxS( "<font size=3>" ) + aLine.message + wxS( "</font><br>" );
  151. break;
  152. case RPT_SEVERITY_WARNING:
  153. retv = wxS( "<font size=3>" ) + _( "Warning:" ) + wxS( " " ) + aLine.message + wxS( "</font><br>" );
  154. break;
  155. case RPT_SEVERITY_INFO:
  156. retv = wxS( "<font color=#909090 size=3>" ) + aLine.message + wxS( "</font><br>" );
  157. break;
  158. case RPT_SEVERITY_ACTION:
  159. retv = wxS( "<font color=#60D060 size=3>" ) + aLine.message + wxS( "</font><br>" );
  160. break;
  161. default:
  162. retv = wxS( "<font size=3>" ) + aLine.message + wxS( "</font><br>" );
  163. }
  164. }
  165. else
  166. {
  167. switch( aLine.severity )
  168. {
  169. case RPT_SEVERITY_ERROR:
  170. retv = wxS( "<font color=#D00000 size=3>" ) + _( "Error:" ) + wxS( " </font>" )
  171. wxS( "<font size=3>" ) + aLine.message + wxS( "</font><br>" );
  172. break;
  173. case RPT_SEVERITY_WARNING:
  174. retv = wxS( "<font size=3>" ) + _( "Warning:" ) + wxS( " " ) + aLine.message + wxS( "</font><br>" );
  175. break;
  176. case RPT_SEVERITY_INFO:
  177. retv = wxS( "<font color=#808080 size=3>" ) + aLine.message + wxS( "</font><br>" );
  178. break;
  179. case RPT_SEVERITY_ACTION:
  180. retv = wxS( "<font color=#008000 size=3>" ) + aLine.message + wxS( "</font><br>" );
  181. break;
  182. default:
  183. retv = wxS( "<font size=3>" ) + aLine.message + wxS( "</font><br>" );
  184. }
  185. }
  186. // wxHtmlWindow fails to do correct baseline alignment between Japanese/Chinese cells and
  187. // Roman cells. This keeps the line in a single cell.
  188. retv.Replace( wxS( " " ), wxS( "&nbsp;" ) );
  189. return retv;
  190. }
  191. wxString WX_HTML_REPORT_PANEL::generatePlainText( const REPORT_LINE& aLine )
  192. {
  193. switch( aLine.severity )
  194. {
  195. case RPT_SEVERITY_ERROR: return _( "Error:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
  196. case RPT_SEVERITY_WARNING: return _( "Warning:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
  197. case RPT_SEVERITY_INFO: return _( "Info:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
  198. default: return aLine.message + wxT( "\n" );
  199. }
  200. }
  201. void WX_HTML_REPORT_PANEL::onRightClick( wxMouseEvent& event )
  202. {
  203. wxMenu popup;
  204. popup.Append( wxID_COPY, "Copy" );
  205. PopupMenu( &popup );
  206. }
  207. void WX_HTML_REPORT_PANEL::onMenuEvent( wxMenuEvent& event )
  208. {
  209. if( event.GetId() == wxID_COPY )
  210. {
  211. wxLogNull doNotLog; // disable logging of failed clipboard actions
  212. if( wxTheClipboard->Open() )
  213. {
  214. bool primarySelection = wxTheClipboard->IsUsingPrimarySelection();
  215. wxTheClipboard->UsePrimarySelection( false ); // required to use the main clipboard
  216. wxTheClipboard->SetData( new wxTextDataObject( m_htmlView->SelectionToText() ) );
  217. wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
  218. wxTheClipboard->Close();
  219. wxTheClipboard->UsePrimarySelection( primarySelection );
  220. }
  221. }
  222. }
  223. // Don't globally define this; different facilities use different definitions of "ALL"
  224. static int RPT_SEVERITY_ALL = RPT_SEVERITY_WARNING | RPT_SEVERITY_ERROR | RPT_SEVERITY_INFO |
  225. RPT_SEVERITY_ACTION;
  226. void WX_HTML_REPORT_PANEL::onCheckBoxShowAll( wxCommandEvent& event )
  227. {
  228. if( event.IsChecked() )
  229. m_severities = RPT_SEVERITY_ALL;
  230. else
  231. m_severities = RPT_SEVERITY_ERROR;
  232. syncCheckboxes();
  233. Flush( true );
  234. }
  235. void WX_HTML_REPORT_PANEL::syncCheckboxes()
  236. {
  237. m_checkBoxShowAll->SetValue( m_severities == RPT_SEVERITY_ALL );
  238. m_checkBoxShowWarnings->SetValue( m_severities & RPT_SEVERITY_WARNING );
  239. m_checkBoxShowErrors->SetValue( m_severities & RPT_SEVERITY_ERROR );
  240. m_checkBoxShowInfos->SetValue( m_severities & RPT_SEVERITY_INFO );
  241. m_checkBoxShowActions->SetValue( m_severities & RPT_SEVERITY_ACTION );
  242. }
  243. void WX_HTML_REPORT_PANEL::onCheckBoxShowWarnings( wxCommandEvent& event )
  244. {
  245. if( event.IsChecked() )
  246. m_severities |= RPT_SEVERITY_WARNING;
  247. else
  248. m_severities &= ~RPT_SEVERITY_WARNING;
  249. syncCheckboxes();
  250. Flush( true );
  251. }
  252. void WX_HTML_REPORT_PANEL::onCheckBoxShowErrors( wxCommandEvent& event )
  253. {
  254. if( event.IsChecked() )
  255. m_severities |= RPT_SEVERITY_ERROR;
  256. else
  257. m_severities &= ~RPT_SEVERITY_ERROR;
  258. syncCheckboxes();
  259. Flush( true );
  260. }
  261. void WX_HTML_REPORT_PANEL::onCheckBoxShowInfos( wxCommandEvent& event )
  262. {
  263. if( event.IsChecked() )
  264. m_severities |= RPT_SEVERITY_INFO;
  265. else
  266. m_severities &= ~RPT_SEVERITY_INFO;
  267. syncCheckboxes();
  268. Flush( true );
  269. }
  270. void WX_HTML_REPORT_PANEL::onCheckBoxShowActions( wxCommandEvent& event )
  271. {
  272. if( event.IsChecked() )
  273. m_severities |= RPT_SEVERITY_ACTION;
  274. else
  275. m_severities &= ~RPT_SEVERITY_ACTION;
  276. syncCheckboxes();
  277. Flush( true );
  278. }
  279. void WX_HTML_REPORT_PANEL::onBtnSaveToFile( wxCommandEvent& event )
  280. {
  281. wxFileName fn;
  282. if( m_reportFileName.empty() )
  283. {
  284. fn = wxT( "report.txt" );
  285. if( KIWAY_HOLDER* parent = dynamic_cast<KIWAY_HOLDER*>( m_parent ) )
  286. fn.SetPath( parent->Prj().GetProjectPath() );
  287. }
  288. else
  289. {
  290. fn = m_reportFileName;
  291. }
  292. wxWindow* topLevelParent = wxGetTopLevelParent( this );
  293. wxFileDialog dlg( topLevelParent, _( "Save Report File" ), fn.GetPath(), fn.GetFullName(),
  294. FILEEXT::TextFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  295. if( dlg.ShowModal() != wxID_OK )
  296. return;
  297. fn = dlg.GetPath();
  298. if( fn.GetExt().IsEmpty() )
  299. fn.SetExt( "txt" );
  300. wxFFile f( fn.GetFullPath(), "wb" );
  301. if( !f.IsOpened() )
  302. {
  303. wxString msg;
  304. msg.Printf( _( "Cannot write report to file '%s'." ),
  305. fn.GetFullPath().GetData() );
  306. wxMessageBox( msg, _( "File save error" ), wxOK | wxICON_ERROR,
  307. wxGetTopLevelParent( this ) );
  308. return;
  309. }
  310. for( REPORT_LINES section : { m_reportHead, m_report, m_reportTail } )
  311. {
  312. for( const REPORT_LINE& l : section )
  313. {
  314. wxString s = generatePlainText( l );
  315. ConvertSmartQuotesAndDashes( &s );
  316. f.Write( s );
  317. }
  318. }
  319. m_reportFileName = fn.GetFullPath();
  320. f.Close();
  321. }
  322. void WX_HTML_REPORT_PANEL::Clear()
  323. {
  324. m_report.clear();
  325. m_reportHead.clear();
  326. m_reportTail.clear();
  327. }
  328. void WX_HTML_REPORT_PANEL::SetLabel( const wxString& aLabel )
  329. {
  330. m_box->GetStaticBox()->SetLabel( aLabel );
  331. }
  332. void WX_HTML_REPORT_PANEL::SetVisibleSeverities( int aSeverities )
  333. {
  334. if( aSeverities < 0 )
  335. m_severities = RPT_SEVERITY_ALL;
  336. else
  337. m_severities = aSeverities;
  338. syncCheckboxes();
  339. }
  340. int WX_HTML_REPORT_PANEL::GetVisibleSeverities() const
  341. {
  342. return m_severities;
  343. }
  344. void WX_HTML_REPORT_PANEL::SetFileName( const wxString& aReportFileName )
  345. {
  346. m_reportFileName = aReportFileName;
  347. }
  348. wxString& WX_HTML_REPORT_PANEL::GetFileName( void )
  349. {
  350. return ( m_reportFileName );
  351. }
  352. void WX_HTML_REPORT_PANEL::SetShowSeverity( SEVERITY aSeverity, bool aValue )
  353. {
  354. switch( aSeverity )
  355. {
  356. case RPT_SEVERITY_INFO: m_checkBoxShowInfos->SetValue( aValue ); break;
  357. case RPT_SEVERITY_ACTION: m_checkBoxShowActions->SetValue( aValue ); break;
  358. case RPT_SEVERITY_WARNING: m_checkBoxShowWarnings->SetValue( aValue ); break;
  359. default: m_checkBoxShowErrors->SetValue( aValue ); break;
  360. }
  361. }
  362. REPORTER& WX_HTML_PANEL_REPORTER::Report( const wxString& aText, SEVERITY aSeverity )
  363. {
  364. wxCHECK_MSG( m_panel != nullptr, *this,
  365. wxT( "No WX_HTML_REPORT_PANEL object defined in WX_HTML_PANEL_REPORTER." ) );
  366. m_panel->Report( aText, aSeverity );
  367. return *this;
  368. }
  369. REPORTER& WX_HTML_PANEL_REPORTER::ReportTail( const wxString& aText, SEVERITY aSeverity )
  370. {
  371. wxCHECK_MSG( m_panel != nullptr, *this,
  372. wxT( "No WX_HTML_REPORT_PANEL object defined in WX_HTML_PANEL_REPORTER." ) );
  373. m_panel->Report( aText, aSeverity, LOC_TAIL );
  374. return *this;
  375. }
  376. REPORTER& WX_HTML_PANEL_REPORTER::ReportHead( const wxString& aText, SEVERITY aSeverity )
  377. {
  378. wxCHECK_MSG( m_panel != nullptr, *this,
  379. wxT( "No WX_HTML_REPORT_PANEL object defined in WX_HTML_PANEL_REPORTER." ) );
  380. m_panel->Report( aText, aSeverity, LOC_HEAD );
  381. return *this;
  382. }
  383. bool WX_HTML_PANEL_REPORTER::HasMessage() const
  384. {
  385. // Check just for errors and warnings for compatibility
  386. return HasMessageOfSeverity( RPT_SEVERITY_ERROR | RPT_SEVERITY_WARNING );
  387. }
  388. bool WX_HTML_PANEL_REPORTER::HasMessageOfSeverity( int aSeverityMask ) const
  389. {
  390. return m_panel->Count( aSeverityMask ) > 0;
  391. }