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-2021 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
22 #include <algorithm>
23
24 #include "wx_html_report_panel.h"
25
26 #include <wildcards_and_files_ext.h>
27 #include <gal/color4d.h>
28 #include <wx/clipbrd.h>
29 #include <string_utils.h>
30 #include <wx/ffile.h>
31 #include <wx/log.h>
32 #include <wx/filedlg.h>
33 #include <wx/msgdlg.h>
34 #include <wx/menu.h>
35 #include <wx/textctrl.h>
36 #include <kiplatform/ui.h>
37
WX_HTML_REPORT_PANEL(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)38 WX_HTML_REPORT_PANEL::WX_HTML_REPORT_PANEL( wxWindow* parent, wxWindowID id, const wxPoint& pos,
39 const wxSize& size, long style ) :
40 WX_HTML_REPORT_PANEL_BASE( parent, id, pos, size, style ),
41 m_reporter( this ),
42 m_severities( -1 ),
43 m_lazyUpdate( false )
44 {
45 syncCheckboxes();
46 m_htmlView->SetFont( KIUI::GetInfoFont( m_htmlView ) );
47 Flush();
48
49 Connect( wxEVT_COMMAND_MENU_SELECTED,
50 wxMenuEventHandler( WX_HTML_REPORT_PANEL::onMenuEvent ), nullptr, this );
51
52 m_htmlView->Bind( wxEVT_SYS_COLOUR_CHANGED,
53 wxSysColourChangedEventHandler( WX_HTML_REPORT_PANEL::onThemeChanged ),
54 this );
55 }
56
57
~WX_HTML_REPORT_PANEL()58 WX_HTML_REPORT_PANEL::~WX_HTML_REPORT_PANEL()
59 {
60 }
61
62
onThemeChanged(wxSysColourChangedEvent & aEvent)63 void WX_HTML_REPORT_PANEL::onThemeChanged( wxSysColourChangedEvent &aEvent )
64 {
65 Flush();
66
67 aEvent.Skip();
68 }
69
70
MsgPanelSetMinSize(const wxSize & aMinSize)71 void WX_HTML_REPORT_PANEL::MsgPanelSetMinSize( const wxSize& aMinSize )
72 {
73 m_fgSizer->SetMinSize( aMinSize );
74 GetSizer()->SetSizeHints( this );
75 }
76
77
Reporter()78 REPORTER& WX_HTML_REPORT_PANEL::Reporter()
79 {
80 return m_reporter;
81 }
82
83
Report(const wxString & aText,SEVERITY aSeverity,REPORTER::LOCATION aLocation)84 void WX_HTML_REPORT_PANEL::Report( const wxString& aText, SEVERITY aSeverity,
85 REPORTER::LOCATION aLocation )
86 {
87 REPORT_LINE line;
88 line.message = aText;
89 line.severity = aSeverity;
90
91 if( aLocation == REPORTER::LOC_HEAD )
92 m_reportHead.push_back( line );
93 else if( aLocation == REPORTER::LOC_TAIL )
94 m_reportTail.push_back( line );
95 else
96 m_report.push_back( line );
97
98 if( !m_lazyUpdate )
99 {
100 m_htmlView->AppendToPage( generateHtml( line ) );
101 scrollToBottom();
102 }
103 }
104
105
SetLazyUpdate(bool aLazyUpdate)106 void WX_HTML_REPORT_PANEL::SetLazyUpdate( bool aLazyUpdate )
107 {
108 m_lazyUpdate = aLazyUpdate;
109 }
110
111
Flush(bool aSort)112 void WX_HTML_REPORT_PANEL::Flush( bool aSort )
113 {
114 wxString html;
115
116 if( aSort )
117 {
118 std::sort( m_report.begin(), m_report.end(),
119 []( const REPORT_LINE& a, const REPORT_LINE& b)
120 {
121 return a.severity < b.severity;
122 });
123 }
124
125 for( const auto& line : m_reportHead )
126 html += generateHtml( line );
127
128 for( const auto& line : m_report )
129 html += generateHtml( line );
130
131 for( const auto& line : m_reportTail )
132 html += generateHtml( line );
133
134 m_htmlView->SetPage( html );
135 scrollToBottom();
136 }
137
138
scrollToBottom()139 void WX_HTML_REPORT_PANEL::scrollToBottom()
140 {
141 int x, y, xUnit, yUnit;
142
143 m_htmlView->GetVirtualSize( &x, &y );
144 m_htmlView->GetScrollPixelsPerUnit( &xUnit, &yUnit );
145 m_htmlView->Scroll( 0, y / yUnit );
146
147 updateBadges();
148 }
149
150
updateBadges()151 void WX_HTML_REPORT_PANEL::updateBadges()
152 {
153 int count = Count(RPT_SEVERITY_ERROR );
154 m_errorsBadge->UpdateNumber( count, RPT_SEVERITY_ERROR );
155
156 count = Count(RPT_SEVERITY_WARNING );
157 m_warningsBadge->UpdateNumber( count, RPT_SEVERITY_WARNING );
158 }
159
160
Count(int severityMask)161 int WX_HTML_REPORT_PANEL::Count( int severityMask )
162 {
163 int count = 0;
164
165 for( const auto& reportLineArray : { m_report, m_reportHead, m_reportTail } )
166 {
167 for( const REPORT_LINE& reportLine : reportLineArray )
168 {
169 if( severityMask & reportLine.severity )
170 count++;
171 }
172 }
173
174 return count;
175 }
176
177
generateHtml(const REPORT_LINE & aLine)178 wxString WX_HTML_REPORT_PANEL::generateHtml( const REPORT_LINE& aLine )
179 {
180 wxString retv;
181
182 if( !( m_severities & aLine.severity ) )
183 return retv;
184
185 if( KIPLATFORM::UI::IsDarkTheme() )
186 {
187 switch( aLine.severity )
188 {
189 case RPT_SEVERITY_ERROR:
190 retv = "<font color=#F04040 size=3>" + _( "Error:" ) + " </font>"
191 "<font size=3>" + aLine.message + "</font><br>";
192 break;
193 case RPT_SEVERITY_WARNING:
194 retv = "<font size=3>" + _( "Warning:" ) + wxS( " " ) + aLine.message + "</font><br>";
195 break;
196 case RPT_SEVERITY_INFO:
197 retv = "<font color=#909090 size=3>" + aLine.message + "</font><br>";
198 break;
199 case RPT_SEVERITY_ACTION:
200 retv = "<font color=#60D060 size=3>" + aLine.message + "</font><br>";
201 break;
202 default:
203 retv = "<font size=3>" + aLine.message + "</font><br>";
204 }
205 }
206 else
207 {
208 switch( aLine.severity )
209 {
210 case RPT_SEVERITY_ERROR:
211 retv = "<font color=#D00000 size=3>" + _( "Error:" ) + " </font>"
212 "<font size=3>" + aLine.message + "</font><br>";
213 break;
214 case RPT_SEVERITY_WARNING:
215 retv = "<font size=3>" + _( "Warning:" ) + wxS( " " ) + aLine.message + "</font><br>";
216 break;
217 case RPT_SEVERITY_INFO:
218 retv = "<font color=#808080 size=3>" + aLine.message + "</font><br>";
219 break;
220 case RPT_SEVERITY_ACTION:
221 retv = "<font color=#008000 size=3>" + aLine.message + "</font><br>";
222 break;
223 default:
224 retv = "<font size=3>" + aLine.message + "</font><br>";
225 }
226 }
227
228 // wxHtmlWindow fails to do correct baseline alignment between Japanese/Chinese cells and
229 // Roman cells. This keeps the line in a single cell.
230 retv.Replace( " ", " " );
231
232 return retv;
233 }
234
235
generatePlainText(const REPORT_LINE & aLine)236 wxString WX_HTML_REPORT_PANEL::generatePlainText( const REPORT_LINE& aLine )
237 {
238 switch( aLine.severity )
239 {
240 case RPT_SEVERITY_ERROR: return _( "Error:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
241 case RPT_SEVERITY_WARNING: return _( "Warning:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
242 case RPT_SEVERITY_INFO: return _( "Info:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
243 default: return aLine.message + wxT( "\n" );
244 }
245 }
246
247
onRightClick(wxMouseEvent & event)248 void WX_HTML_REPORT_PANEL::onRightClick( wxMouseEvent& event )
249 {
250 wxMenu popup;
251 popup.Append( wxID_COPY, "Copy" );
252 PopupMenu( &popup );
253 }
254
255
onMenuEvent(wxMenuEvent & event)256 void WX_HTML_REPORT_PANEL::onMenuEvent( wxMenuEvent& event )
257 {
258 if( event.GetId() == wxID_COPY )
259 {
260 wxLogNull doNotLog; // disable logging of failed clipboard actions
261
262 if( wxTheClipboard->Open() )
263 {
264 bool primarySelection = wxTheClipboard->IsUsingPrimarySelection();
265 wxTheClipboard->UsePrimarySelection( false ); // required to use the main clipboard
266 wxTheClipboard->SetData( new wxTextDataObject( m_htmlView->SelectionToText() ) );
267 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
268 wxTheClipboard->Close();
269 wxTheClipboard->UsePrimarySelection( primarySelection );
270 }
271 }
272 }
273
274
275 // Don't globally define this; different facilities use different definitions of "ALL"
276 static int RPT_SEVERITY_ALL = RPT_SEVERITY_WARNING | RPT_SEVERITY_ERROR | RPT_SEVERITY_INFO |
277 RPT_SEVERITY_ACTION;
278
279
onCheckBoxShowAll(wxCommandEvent & event)280 void WX_HTML_REPORT_PANEL::onCheckBoxShowAll( wxCommandEvent& event )
281 {
282 if( event.IsChecked() )
283 m_severities = RPT_SEVERITY_ALL;
284 else
285 m_severities = RPT_SEVERITY_ERROR;
286
287 syncCheckboxes();
288 Flush( true );
289 }
290
291
syncCheckboxes()292 void WX_HTML_REPORT_PANEL::syncCheckboxes()
293 {
294 m_checkBoxShowAll->SetValue( m_severities == RPT_SEVERITY_ALL );
295 m_checkBoxShowWarnings->SetValue( m_severities & RPT_SEVERITY_WARNING );
296 m_checkBoxShowErrors->SetValue( m_severities & RPT_SEVERITY_ERROR );
297 m_checkBoxShowInfos->SetValue( m_severities & RPT_SEVERITY_INFO );
298 m_checkBoxShowActions->SetValue( m_severities & RPT_SEVERITY_ACTION );
299 }
300
301
onCheckBoxShowWarnings(wxCommandEvent & event)302 void WX_HTML_REPORT_PANEL::onCheckBoxShowWarnings( wxCommandEvent& event )
303 {
304 if( event.IsChecked() )
305 m_severities |= RPT_SEVERITY_WARNING;
306 else
307 m_severities &= ~RPT_SEVERITY_WARNING;
308
309 syncCheckboxes();
310 Flush( true );
311 }
312
313
onCheckBoxShowErrors(wxCommandEvent & event)314 void WX_HTML_REPORT_PANEL::onCheckBoxShowErrors( wxCommandEvent& event )
315 {
316 if( event.IsChecked() )
317 m_severities |= RPT_SEVERITY_ERROR;
318 else
319 m_severities &= ~RPT_SEVERITY_ERROR;
320
321 syncCheckboxes();
322 Flush( true );
323 }
324
325
onCheckBoxShowInfos(wxCommandEvent & event)326 void WX_HTML_REPORT_PANEL::onCheckBoxShowInfos( wxCommandEvent& event )
327 {
328 if( event.IsChecked() )
329 m_severities |= RPT_SEVERITY_INFO;
330 else
331 m_severities &= ~RPT_SEVERITY_INFO;
332
333 syncCheckboxes();
334 Flush( true );
335 }
336
337
onCheckBoxShowActions(wxCommandEvent & event)338 void WX_HTML_REPORT_PANEL::onCheckBoxShowActions( wxCommandEvent& event )
339 {
340 if( event.IsChecked() )
341 m_severities |= RPT_SEVERITY_ACTION;
342 else
343 m_severities &= ~RPT_SEVERITY_ACTION;
344
345 syncCheckboxes();
346 Flush( true );
347 }
348
349
onBtnSaveToFile(wxCommandEvent & event)350 void WX_HTML_REPORT_PANEL::onBtnSaveToFile( wxCommandEvent& event )
351 {
352 wxFileName fn;
353
354 if( m_reportFileName.empty() )
355 fn = wxT( "./report.txt" );
356 else
357 fn = m_reportFileName;
358
359 wxFileDialog dlg( this, _( "Save Report to File" ), fn.GetPath(), fn.GetFullName(),
360 TextFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
361
362 if( dlg.ShowModal() != wxID_OK )
363 return;
364
365 fn = dlg.GetPath();
366
367 if( fn.GetExt().IsEmpty() )
368 fn.SetExt( "txt" );
369
370 wxFFile f( fn.GetFullPath(), "wb" );
371
372 if( !f.IsOpened() )
373 {
374 wxString msg;
375
376 msg.Printf( _( "Cannot write report to file '%s'." ),
377 fn.GetFullPath().GetData() );
378 wxMessageBox( msg, _( "File save error" ), wxOK | wxICON_ERROR, this );
379 return;
380 }
381
382 for( REPORT_LINES section : { m_reportHead, m_report, m_reportTail } )
383 {
384 for( const REPORT_LINE& l : section )
385 {
386 wxString s = generatePlainText( l );
387
388 ConvertSmartQuotesAndDashes( &s );
389 f.Write( s );
390 }
391 }
392
393 m_reportFileName = fn.GetFullPath();
394 f.Close();
395 }
396
397
Clear()398 void WX_HTML_REPORT_PANEL::Clear()
399 {
400 m_report.clear();
401 m_reportHead.clear();
402 m_reportTail.clear();
403 }
404
405
SetLabel(const wxString & aLabel)406 void WX_HTML_REPORT_PANEL::SetLabel( const wxString& aLabel )
407 {
408 m_box->GetStaticBox()->SetLabel( aLabel );
409 }
410
411
SetVisibleSeverities(int aSeverities)412 void WX_HTML_REPORT_PANEL::SetVisibleSeverities( int aSeverities )
413 {
414 if( aSeverities < 0 )
415 m_severities = RPT_SEVERITY_ALL;
416 else
417 m_severities = aSeverities;
418
419 syncCheckboxes();
420 }
421
422
GetVisibleSeverities() const423 int WX_HTML_REPORT_PANEL::GetVisibleSeverities() const
424 {
425 return m_severities;
426 }
427
428
SetFileName(const wxString & aReportFileName)429 void WX_HTML_REPORT_PANEL::SetFileName( const wxString& aReportFileName )
430 {
431 m_reportFileName = aReportFileName;
432 }
433
434
GetFileName(void)435 wxString& WX_HTML_REPORT_PANEL::GetFileName( void )
436 {
437 return ( m_reportFileName );
438 }
439
440
SetShowSeverity(SEVERITY aSeverity,bool aValue)441 void WX_HTML_REPORT_PANEL::SetShowSeverity( SEVERITY aSeverity, bool aValue )
442 {
443 switch( aSeverity )
444 {
445 case RPT_SEVERITY_INFO: m_checkBoxShowInfos->SetValue( aValue ); break;
446 case RPT_SEVERITY_ACTION: m_checkBoxShowActions->SetValue( aValue ); break;
447 case RPT_SEVERITY_WARNING: m_checkBoxShowWarnings->SetValue( aValue ); break;
448 default: m_checkBoxShowErrors->SetValue( aValue ); break;
449 }
450 }
451