KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wx_html_report_panel.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) 2015 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
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
25
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#include <kiway_holder.h>
38#include <project.h>
39
40WX_HTML_REPORT_PANEL::WX_HTML_REPORT_PANEL( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
41 long style ) :
42 WX_HTML_REPORT_PANEL_BASE( parent, id, pos, size, style ),
43 m_reporter( this ),
44 m_lazyUpdate( false )
45{
47 Flush();
48
49 Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( WX_HTML_REPORT_PANEL::onMenuEvent ), nullptr, this );
50
51 m_htmlView->Bind( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEventHandler( WX_HTML_REPORT_PANEL::onThemeChanged ),
52 this );
53}
54
55
59
60
61void WX_HTML_REPORT_PANEL::onThemeChanged( wxSysColourChangedEvent &aEvent )
62{
63 Flush();
64
65 aEvent.Skip();
66}
67
68
69void WX_HTML_REPORT_PANEL::MsgPanelSetMinSize( const wxSize& aMinSize )
70{
71 m_fgSizer->SetMinSize( aMinSize );
72 GetSizer()->SetSizeHints( this );
73}
74
75
80
81
82void WX_HTML_REPORT_PANEL::Report( const wxString& aText, SEVERITY aSeverity, REPORTER::LOCATION aLocation )
83{
84 REPORT_LINE line;
85 line.message = aText;
86 line.severity = aSeverity;
87
88 if( aLocation == REPORTER::LOC_HEAD )
89 m_reportHead.push_back( line );
90 else if( aLocation == REPORTER::LOC_TAIL )
91 m_reportTail.push_back( line );
92 else
93 m_report.push_back( line );
94
95 if( !m_lazyUpdate )
96 {
97 m_htmlView->AppendToPage( generateHtml( line ) );
99 }
100}
101
102
104{
105 wxString html;
106
107 if( aSort )
108 {
109 std::sort( m_report.begin(), m_report.end(),
110 []( const REPORT_LINE& a, const REPORT_LINE& b)
111 {
112 return a.severity < b.severity;
113 });
114 }
115
116 for( const auto& line : m_reportHead )
117 html += generateHtml( line );
118
119 for( const auto& line : m_report )
120 html += generateHtml( line );
121
122 for( const auto& line : m_reportTail )
123 html += generateHtml( line );
124
125 m_htmlView->SetPage( html );
127}
128
129
131{
132 int x, y, xUnit, yUnit;
133
134 m_htmlView->GetVirtualSize( &x, &y );
135 m_htmlView->GetScrollPixelsPerUnit( &xUnit, &yUnit );
136 m_htmlView->Scroll( 0, y / yUnit );
137
138 updateBadges();
139}
140
141
143{
144 int count = Count(RPT_SEVERITY_ERROR );
145 m_errorsBadge->UpdateNumber( count, RPT_SEVERITY_ERROR );
146
147 count = Count(RPT_SEVERITY_WARNING );
148 m_warningsBadge->UpdateNumber( count, RPT_SEVERITY_WARNING );
149}
150
151
152int WX_HTML_REPORT_PANEL::Count( int severityMask )
153{
154 int count = 0;
155
156 for( const auto& reportLineArray : { m_report, m_reportHead, m_reportTail } )
157 {
158 for( const REPORT_LINE& reportLine : reportLineArray )
159 {
160 if( severityMask & reportLine.severity )
161 count++;
162 }
163 }
164
165 return count;
166}
167
168
170{
171 wxString retv;
172
173 if( !( GetVisibleSeverities() & aLine.severity ) )
174 return retv;
175
177 {
178 switch( aLine.severity )
179 {
181 retv = wxS( "<font color=#F04040 size=3>" ) + _( "Error:" ) + wxS( " </font>" )
182 + wxS( "<font size=3>" ) + aLine.message + wxS( "</font><br>" );
183 break;
185 retv = wxS( "<font size=3>" ) + _( "Warning:" ) + wxS( " " ) + aLine.message + wxS( "</font><br>" );
186 break;
188 retv = wxS( "<font color=#909090 size=3>" ) + aLine.message + wxS( "</font><br>" );
189 break;
191 retv = wxS( "<font color=#60D060 size=3>" ) + aLine.message + wxS( "</font><br>" );
192 break;
193 default:
194 retv = wxS( "<font size=3>" ) + aLine.message + wxS( "</font><br>" );
195 }
196 }
197 else
198 {
199 switch( aLine.severity )
200 {
202 retv = wxS( "<font color=#D00000 size=3>" ) + _( "Error:" ) + wxS( " </font>" )
203 + wxS( "<font size=3>" ) + aLine.message + wxS( "</font><br>" );
204 break;
206 retv = wxS( "<font size=3>" ) + _( "Warning:" ) + wxS( " " ) + aLine.message + wxS( "</font><br>" );
207 break;
209 retv = wxS( "<font color=#808080 size=3>" ) + aLine.message + wxS( "</font><br>" );
210 break;
212 retv = wxS( "<font color=#008000 size=3>" ) + aLine.message + wxS( "</font><br>" );
213 break;
214 default:
215 retv = wxS( "<font size=3>" ) + aLine.message + wxS( "</font><br>" );
216 }
217 }
218
219 // wxHtmlWindow fails to do correct baseline alignment between Japanese/Chinese cells and
220 // Roman cells. This keeps the line in a single cell.
221 retv.Replace( wxS( " " ), wxS( "&nbsp;" ) );
222
223 return retv;
224}
225
226
228{
229 switch( aLine.severity )
230 {
231 case RPT_SEVERITY_ERROR: return _( "Error:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
232 case RPT_SEVERITY_WARNING: return _( "Warning:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
233 case RPT_SEVERITY_INFO: return _( "Info:" ) + wxS( " " ) + aLine.message + wxT( "\n" );
234 default: return aLine.message + wxT( "\n" );
235 }
236}
237
238
239void WX_HTML_REPORT_PANEL::onRightClick( wxMouseEvent& event )
240{
241 wxMenu popup;
242 popup.Append( wxID_COPY, _( "Copy" ) );
243 popup.Append( wxID_SELECTALL, _( "Select All" ) );
244 PopupMenu( &popup );
245}
246
247
248void WX_HTML_REPORT_PANEL::onMenuEvent( wxMenuEvent& event )
249{
250 if( event.GetId() == wxID_COPY )
251 {
252 wxLogNull doNotLog; // disable logging of failed clipboard actions
253
254 if( wxTheClipboard->Open() )
255 {
256 bool primarySelection = wxTheClipboard->IsUsingPrimarySelection();
257 wxTheClipboard->UsePrimarySelection( false ); // required to use the main clipboard
258 wxTheClipboard->SetData( new wxTextDataObject( m_htmlView->SelectionToText() ) );
259 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
260 wxTheClipboard->Close();
261 wxTheClipboard->UsePrimarySelection( primarySelection );
262 }
263 }
264 else if( event.GetId() == wxID_SELECTALL )
265 {
266 m_htmlView->SelectAll();
267 }
268}
269
270
271// Don't globally define this; different facilities use different definitions of "ALL"
274
275
276void WX_HTML_REPORT_PANEL::onCheckBox( wxCommandEvent& event )
277{
278 if( event.GetEventObject() == m_checkBoxShowAll )
279 {
280 m_checkBoxShowErrors->SetValue( true );
281 m_checkBoxShowWarnings->SetValue( event.IsChecked() );
282 m_checkBoxShowInfos->SetValue( event.IsChecked() );
283 m_checkBoxShowActions->SetValue( event.IsChecked() );
284 }
285
286 CallAfter(
287 [&]()
288 {
290 } );
291
292 Flush( true );
293 event.Skip();
294}
295
296
297void WX_HTML_REPORT_PANEL::onBtnSaveToFile( wxCommandEvent& event )
298{
299 wxFileName fn;
300
301 if( m_reportFileName.empty() )
302 {
303 fn = wxT( "report.txt" );
304
305 if( KIWAY_HOLDER* parent = dynamic_cast<KIWAY_HOLDER*>( m_parent ) )
306 fn.SetPath( parent->Prj().GetProjectPath() );
307 }
308 else
309 {
310 fn = m_reportFileName;
311 }
312
313 wxWindow* topLevelParent = wxGetTopLevelParent( this );
314
315 wxFileDialog dlg( topLevelParent, _( "Save Report File" ), fn.GetPath(), fn.GetFullName(),
316 FILEEXT::TextFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
317
318 if( dlg.ShowModal() != wxID_OK )
319 return;
320
321 fn = dlg.GetPath();
322
323 if( fn.GetExt().IsEmpty() )
324 fn.SetExt( "txt" );
325
326 wxFFile f( fn.GetFullPath(), "wb" );
327
328 if( !f.IsOpened() )
329 {
330 wxMessageBox( wxString::Format( _( "Cannot write report to file '%s'." ), fn.GetFullPath().GetData() ),
331 _( "File save error" ), wxOK | wxICON_ERROR, wxGetTopLevelParent( this ) );
332 return;
333 }
334
335 for( const REPORT_LINES& section : { m_reportHead, m_report, m_reportTail } )
336 {
337 for( const REPORT_LINE& l : section )
338 {
339 wxString s = generatePlainText( l );
340
342 f.Write( s );
343 }
344 }
345
346 m_reportFileName = fn.GetFullPath();
347 f.Close();
348}
349
350
352{
353 m_report.clear();
354 m_reportHead.clear();
355 m_reportTail.clear();
356}
357
358
359void WX_HTML_REPORT_PANEL::SetLabel( const wxString& aLabel )
360{
361 m_box->GetStaticBox()->SetLabel( aLabel );
362}
363
364
366{
367 int severities = 0;
368
369 if( m_checkBoxShowErrors->GetValue() )
370 severities |= RPT_SEVERITY_ERROR;
371
372 if( m_checkBoxShowWarnings->GetValue() )
373 severities |= RPT_SEVERITY_WARNING;
374
375 if( m_checkBoxShowActions->GetValue() )
376 severities |= RPT_SEVERITY_ACTION;
377
378 if( m_checkBoxShowInfos->GetValue() )
379 severities |= RPT_SEVERITY_INFO;
380
381 return severities;
382}
383
384
385REPORTER& WX_HTML_PANEL_REPORTER::Report( const wxString& aText, SEVERITY aSeverity )
386{
387 REPORTER::Report( aText, aSeverity );
388
389 wxCHECK_MSG( m_panel != nullptr, *this,
390 wxT( "No WX_HTML_REPORT_PANEL object defined in WX_HTML_PANEL_REPORTER." ) );
391
392 m_panel->Report( aText, aSeverity );
393 return *this;
394}
395
396
397REPORTER& WX_HTML_PANEL_REPORTER::ReportTail( const wxString& aText, SEVERITY aSeverity )
398{
399 REPORTER::Report( aText, aSeverity );
400
401 wxCHECK_MSG( m_panel != nullptr, *this,
402 wxT( "No WX_HTML_REPORT_PANEL object defined in WX_HTML_PANEL_REPORTER." ) );
403
404 m_panel->Report( aText, aSeverity, LOC_TAIL );
405 return *this;
406}
407
408
409REPORTER& WX_HTML_PANEL_REPORTER::ReportHead( const wxString& aText, SEVERITY aSeverity )
410{
411 REPORTER::Report( aText, aSeverity );
412
413 wxCHECK_MSG( m_panel != nullptr, *this,
414 wxT( "No WX_HTML_REPORT_PANEL object defined in WX_HTML_PANEL_REPORTER." ) );
415
416 m_panel->Report( aText, aSeverity, LOC_HEAD );
417 return *this;
418}
419
420
422{
423 // Check just for errors and warnings for compatibility
425}
426
427
428bool WX_HTML_PANEL_REPORTER::HasMessageOfSeverity( int aSeverityMask ) const
429{
430 return m_panel->Count( aSeverityMask ) > 0;
431}
A mix in class which holds the location of a wxWindow's KIWAY.
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
LOCATION
Location where the message is to be reported.
Definition reporter.h:88
@ LOC_TAIL
Definition reporter.h:91
@ LOC_HEAD
Definition reporter.h:89
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
REPORTER()
Definition reporter.h:75
bool HasMessageOfSeverity(int aSeverityMask) const override
Returns true if the reporter has one or more messages matching the specified severity mask.
bool HasMessage() const override
Returns true if any messages were reported.
REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Places the report at the end of the list, for objects that support report ordering.
REPORTER & ReportHead(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Places the report at the beginning of the list for objects that support ordering.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
WX_HTML_REPORT_PANEL * m_panel
WX_HTML_REPORT_PANEL_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
void Clear()
Clears the report panel.
wxString m_reportFileName
defaults to the not very useful /bin/report.txt
void onThemeChanged(wxSysColourChangedEvent &aEvent)
REPORT_LINES m_report
copy of the report, stored for filtering
void onRightClick(wxMouseEvent &event) override
int Count(int severityMask)
Return the number of messages matching the given severity mask.
WX_HTML_PANEL_REPORTER m_reporter
void SetLabel(const wxString &aLabel) override
Set the frame label.
std::vector< REPORT_LINE > REPORT_LINES
void MsgPanelSetMinSize(const wxSize &aMinSize)
Set the min size of the area which displays html messages.
wxString generateHtml(const REPORT_LINE &aLine)
void Report(const wxString &aText, SEVERITY aSeverity, REPORTER::LOCATION aLocation=REPORTER::LOC_BODY)
Report the string.
void Flush(bool aSort=false)
Force updating the HTML page, after the report is built in lazy mode If aSort = true,...
void onMenuEvent(wxMenuEvent &event)
wxString generatePlainText(const REPORT_LINE &aLine)
void onCheckBox(wxCommandEvent &event) override
void onBtnSaveToFile(wxCommandEvent &event) override
REPORT_LINES m_reportHead
... and at the beginning, regardless of sorting
REPORTER & Reporter()
Return the reporter object that reports to this panel.
REPORT_LINES m_reportTail
Lines to print at the end, regardless of sorting.
WX_HTML_REPORT_PANEL(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(500, 300), long style=wxTAB_TRAVERSAL)
#define _(s)
static wxString TextFileWildcard()
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition wxgtk/ui.cpp:48
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
bool ConvertSmartQuotesAndDashes(wxString *aString)
Convert curly quotes and em/en dashes to straight quotes and dashes.
Definition of file extensions used in Kicad.
static int RPT_SEVERITY_ALL