KiCad PCB EDA Suite
Loading...
Searching...
No Matches
kistatusbar.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) 2023 Mark Roszko <[email protected]>
5 * Copyright The 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#include <wx/button.h>
26#include <wx/statusbr.h>
27#include <wx/gauge.h>
28#include <wx/stattext.h>
29#include <wx/tokenzr.h>
30#include <fmt/format.h>
31#include <array>
32#include <widgets/kistatusbar.h>
35#include <widgets/ui_common.h>
36#include <pgm_base.h>
39#include <bitmaps.h>
40#include <reporter.h>
42#include <trace_helpers.h>
43#include <wx/dcclient.h>
44
45
46KISTATUSBAR::KISTATUSBAR( int aNumberFields, wxWindow* parent, wxWindowID id, STYLE_FLAGS aFlags ) :
47 wxStatusBar( parent, id ),
48 m_backgroundStopButton( nullptr ),
49 m_notificationsButton( nullptr ),
50 m_warningButton( nullptr ),
51 m_normalFieldsCount( aNumberFields ),
52 m_styleFlags( aFlags )
53{
54#ifdef __WXOSX__
55 // we need +1 extra field on OSX to offset from the rounded corner on the right
56 // OSX doesn't use resize grippers like the other platforms and the statusbar field
57 // includes the rounded part
58 int extraFields = 3;
59#else
60 int extraFields = 2;
61#endif
62
63 bool showNotification = ( m_styleFlags & NOTIFICATION_ICON );
64 bool showCancel = ( m_styleFlags & CANCEL_BUTTON );
65 bool showWarning = ( m_styleFlags & WARNING_ICON );
66
67 if( showCancel )
68 extraFields++;
69
70 if( showWarning )
71 extraFields++;
72
73 if( showNotification )
74 extraFields++;
75
76 SetFieldsCount( aNumberFields + extraFields );
77
78 int* widths = new int[aNumberFields + extraFields];
79
80 for( int i = 0; i < aNumberFields; i++ )
81 widths[i] = -1;
82
83 if( std::optional<int> idx = fieldIndex( FIELD::BGJOB_LABEL ) )
84 widths[aNumberFields + *idx] = -1; // background status text field (variable size)
85
86 if( std::optional<int> idx = fieldIndex( FIELD::BGJOB_GAUGE ) )
87 widths[aNumberFields + *idx] = 75; // background progress button
88
89 if( std::optional<int> idx = fieldIndex( FIELD::BGJOB_CANCEL ) )
90 widths[aNumberFields + *idx] = 20; // background stop button
91
92 if( std::optional<int> idx = fieldIndex( FIELD::WARNING ) )
93 widths[aNumberFields + *idx] = 20; // warning button
94
95 if( std::optional<int> idx = fieldIndex( FIELD::NOTIFICATION ) )
96 widths[aNumberFields + *idx] = 20; // notifications button
97
98#ifdef __WXOSX__
99 // offset from the right edge
100 widths[aNumberFields + extraFields - 1] = 10;
101#endif
102
103 SetStatusWidths( aNumberFields + extraFields, widths );
104 delete[] widths;
105
106 int* styles = new int[aNumberFields + extraFields];
107
108 for( int i = 0; i < aNumberFields + extraFields; i++ )
109 styles[i] = wxSB_FLAT;
110
111 SetStatusStyles( aNumberFields + extraFields, styles );
112 delete[] styles;
113
114 m_backgroundTxt = new wxStaticText( this, wxID_ANY, wxT( "" ) );
115
116 m_backgroundProgressBar = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize,
117 wxGA_HORIZONTAL | wxGA_SMOOTH );
118
119 if( showCancel )
120 {
121 m_backgroundStopButton = new wxButton( this, wxID_ANY, "X", wxDefaultPosition,
122 wxDefaultSize, wxBU_EXACTFIT );
123 }
124
125 if( showNotification )
126 {
127 m_notificationsButton = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition,
128 wxDefaultSize, wxBU_EXACTFIT );
129
130 m_notificationsButton->SetPadding( 0 );
132 m_notificationsButton->SetShowBadge( true );
133 m_notificationsButton->SetBitmapCentered( true );
134
136 }
137
138 if( showWarning )
139 {
140 m_warningButton = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition,
141 wxDefaultSize, wxBU_EXACTFIT );
142
143 m_warningButton->SetPadding( 0 );
145 m_warningButton->SetBitmapCentered( true );
146 m_warningButton->SetToolTip( _( "View load messages" ) );
147 m_warningButton->Hide();
148
149 m_warningButton->Bind( wxEVT_BUTTON, &KISTATUSBAR::onLoadWarningsIconClick, this );
150 }
151
152 Bind( wxEVT_SIZE, &KISTATUSBAR::onSize, this );
154
156 Layout();
157}
158
159
161{
163 m_notificationsButton->Unbind( wxEVT_BUTTON, &KISTATUSBAR::onNotificationsIconClick, this );
164
165 if( m_warningButton )
166 m_warningButton->Unbind( wxEVT_BUTTON, &KISTATUSBAR::onLoadWarningsIconClick, this );
167
168 Unbind( wxEVT_SIZE, &KISTATUSBAR::onSize, this );
170 this );
171}
172
173
174void KISTATUSBAR::onNotificationsIconClick( wxCommandEvent& aEvent )
175{
176 wxCHECK( m_notificationsButton, /* void */ );
177 wxPoint pos = m_notificationsButton->GetScreenPosition();
178
179 wxRect r;
180 if( std::optional<int> idx = fieldIndex( FIELD::NOTIFICATION ) )
181 {
182 GetFieldRect( m_normalFieldsCount + *idx, r );
183 pos.x += r.GetWidth();
184 }
185
186 Pgm().GetNotificationsManager().ShowList( this, pos );
187}
188
189
190void KISTATUSBAR::onBackgroundProgressClick( wxMouseEvent& aEvent )
191{
192 wxPoint pos = m_backgroundProgressBar->GetScreenPosition();
193
194 wxRect r;
195 if( std::optional<int> idx = fieldIndex( FIELD::BGJOB_GAUGE ) )
196 {
197 GetFieldRect( m_normalFieldsCount + *idx, r );
198 pos.x += r.GetWidth();
199 }
200
201 Pgm().GetBackgroundJobMonitor().ShowList( this, pos );
202}
203
204
205void KISTATUSBAR::onSize( wxSizeEvent& aEvent )
206{
207 constexpr int padding = 5;
208
209 wxRect r;
210 GetFieldRect( m_normalFieldsCount + *fieldIndex( FIELD::BGJOB_LABEL ), r );
211 int x = r.GetLeft();
212 int y = r.GetTop();
213 int textHeight = KIUI::GetTextSize( wxT( "bp" ), this ).y;
214
215 if( r.GetHeight() > textHeight )
216 y += ( r.GetHeight() - textHeight ) / 2;
217
218 m_backgroundTxt->SetPosition( { x, y } );
219
220 GetFieldRect( m_normalFieldsCount + *fieldIndex( FIELD::BGJOB_GAUGE ), r );
221 x = r.GetLeft();
222 y = r.GetTop();
223 int w = r.GetWidth();
224 int h = r.GetHeight();
225 wxSize buttonSize( 0, 0 );
226
228 {
229 buttonSize = m_backgroundStopButton->GetEffectiveMinSize();
230 m_backgroundStopButton->SetPosition( { x + w - buttonSize.GetWidth(), y } );
231 m_backgroundStopButton->SetSize( buttonSize.GetWidth(), h );
232 buttonSize.x += padding;
233 }
234
235 m_backgroundProgressBar->SetPosition( { x + padding, y } );
236 m_backgroundProgressBar->SetSize( w - buttonSize.GetWidth() - padding, h );
237
239 {
240 GetFieldRect( m_normalFieldsCount + *fieldIndex( FIELD::NOTIFICATION ), r );
241 x = r.GetLeft();
242 y = r.GetTop();
243 h = r.GetHeight();
244 buttonSize = m_notificationsButton->GetEffectiveMinSize();
245 m_notificationsButton->SetPosition( { x, y } );
246 m_notificationsButton->SetSize( buttonSize.GetWidth() + 6, h );
247 }
248
249 if( m_warningButton )
250 {
251 GetFieldRect( m_normalFieldsCount + *fieldIndex( FIELD::WARNING ), r );
252 x = r.GetLeft();
253 y = r.GetTop();
254 h = r.GetHeight();
255 buttonSize = m_warningButton->GetEffectiveMinSize();
256 m_warningButton->SetPosition( { x, y } );
257 m_warningButton->SetSize( buttonSize.GetWidth() + 6, h );
258 }
259}
260
261
263{
265
267 m_backgroundStopButton->Show( aCancellable );
268}
269
270
278
279
281{
282 int value = m_backgroundProgressBar->GetRange();
283
284 if( value <= aAmount )
285 value = aAmount;
286
287 m_backgroundProgressBar->SetValue( value );
288}
289
290
292{
293 m_backgroundProgressBar->SetRange( aAmount );
294}
295
296
297void KISTATUSBAR::SetBackgroundStatusText( const wxString& aTxt )
298{
299 m_backgroundTxt->SetLabel( aTxt );
300}
301
302
304{
305 wxCHECK( m_notificationsButton, /* void */ );
306 wxString cnt = "";
307
308 if( aCount > 0 )
309 cnt = fmt::format( "{}", aCount );
310
311 m_notificationsButton->SetBadgeText( cnt );
312
313 // force a repaint or it wont until it gets activity
314 Refresh();
315}
316
317
318void KISTATUSBAR::SetLoadWarningMessages( const wxString& aMessages )
319{
320 {
321 std::lock_guard<std::mutex> lock( m_loadWarningMutex );
322 m_loadWarningMessages.clear();
323
324 wxStringTokenizer tokenizer( aMessages, wxS( "\n" ), wxTOKEN_STRTOK );
325
326 while( tokenizer.HasMoreTokens() )
327 {
328 LOAD_MESSAGE msg;
329 msg.message = tokenizer.GetNextToken();
330 msg.severity = RPT_SEVERITY_WARNING; // Default to warning for font substitutions
331 m_loadWarningMessages.push_back( msg );
332 }
333 }
334
336}
337
338
339void KISTATUSBAR::AddLoadWarningMessages( const std::vector<LOAD_MESSAGE>& aMessages )
340{
341 wxLogTrace( traceLibraries, "KISTATUSBAR::AddLoadWarningMessages: this=%p, count=%zu",
342 this, aMessages.size() );
343
344 if( aMessages.empty() )
345 return;
346
347 {
348 std::lock_guard<std::mutex> lock( m_loadWarningMutex );
349 m_loadWarningMessages.insert( m_loadWarningMessages.end(), aMessages.begin(), aMessages.end() );
350 wxLogTrace( traceLibraries, " -> total messages now=%zu", m_loadWarningMessages.size() );
351 }
352
353 // Update UI on main thread
354 wxLogTrace( traceLibraries, " -> calling CallAfter for updateWarningUI" );
355 CallAfter( [this]() { updateWarningUI(); } );
356}
357
358
360{
361 std::lock_guard<std::mutex> lock( m_loadWarningMutex );
362 return m_loadWarningMessages.size();
363}
364
365
367{
368 wxLogTrace( traceLibraries, "KISTATUSBAR::updateWarningUI: this=%p, m_warningButton=%p",
369 this, m_warningButton );
370
371 if( !m_warningButton )
372 {
373 wxLogTrace( traceLibraries, " -> no warning button, returning early" );
374 return;
375 }
376
377 size_t messageCount;
378 {
379 std::lock_guard<std::mutex> lock( m_loadWarningMutex );
380 messageCount = m_loadWarningMessages.size();
381 }
382
383 wxLogTrace( traceLibraries, " -> message count=%zu, showing button=%s",
384 messageCount, messageCount > 0 ? "true" : "false" );
385
386 m_warningButton->Show( messageCount > 0 );
387
388 if( messageCount > 0 )
389 {
390 m_warningButton->SetToolTip( wxString::Format( _( "View %zu load message(s)" ), messageCount ) );
391
392 // Show count badge on the warning button
393 m_warningButton->SetShowBadge( true );
394 wxString badgeText = messageCount > 99
395 ? wxString( "99+" )
396 : wxString::Format( wxS( "%zu" ), messageCount );
397 m_warningButton->SetBadgeText( badgeText );
398
399 wxLogTrace( traceLibraries, " -> badge set to '%s'", badgeText );
400 }
401
402 Layout();
403 Refresh();
404 wxLogTrace( traceLibraries, " -> Layout and Refresh complete" );
405}
406
407
409{
410 {
411 std::lock_guard<std::mutex> lock( m_loadWarningMutex );
412 m_loadWarningMessages.clear();
413 }
414
415 if( m_warningButton )
416 {
417 m_warningButton->Hide();
418 m_warningButton->SetShowBadge( false );
419 m_warningButton->SetBadgeText( wxEmptyString );
420 Layout();
421 Refresh();
422 }
423}
424
425
426void KISTATUSBAR::onLoadWarningsIconClick( wxCommandEvent& aEvent )
427{
428 // Copy messages under lock to avoid holding lock during modal dialog
429 std::vector<LOAD_MESSAGE> messages;
430 {
431 std::lock_guard<std::mutex> lock( m_loadWarningMutex );
432 messages = m_loadWarningMessages;
433 }
434
435 if( messages.empty() )
436 return;
437
438 DIALOG_HTML_REPORTER dlg( GetParent(), wxID_ANY, _( "Load Messages" ) );
439
440 for( const LOAD_MESSAGE& msg : messages )
441 dlg.m_Reporter->Report( msg.message, msg.severity );
442
443 dlg.m_Reporter->Flush();
444 dlg.ShowModal();
445}
446
447void KISTATUSBAR::SetEllipsedTextField( const wxString& aText, int aFieldId )
448{
449 wxRect fieldRect;
450 int width = -1;
451 wxString etext = aText;
452
453 // Only GetFieldRect() returns the current size for variable size fields
454 // Other methods return -1 for the width of these fields.
455 if( GetFieldRect( aFieldId, fieldRect ) )
456 width = fieldRect.GetWidth();
457
458 if( width > 20 )
459 {
460 wxClientDC dc( this );
461
462 // Gives a margin to the text to be sure it is not clamped at its end
463 int margin = KIUI::GetTextSize( wxT( "XX" ), this ).x;
464 etext = wxControl::Ellipsize( etext, dc, wxELLIPSIZE_MIDDLE, width - margin );
465 }
466
467 SetStatusText( etext, aFieldId );
468}
469
470
471std::optional<int> KISTATUSBAR::fieldIndex( FIELD aField ) const
472{
473 switch( aField )
474 {
475 case FIELD::BGJOB_LABEL: return 0;
476 case FIELD::BGJOB_GAUGE: return 1;
478 {
480 return 2;
481
482 break;
483 }
484 case FIELD::WARNING:
485 {
487 {
488 int offset = 2;
489
491 offset++;
492
493 return offset;
494 }
495
496 break;
497 }
499 {
501 {
502 int offset = 2;
503
505 offset++;
506
508 offset++;
509
510 return offset;
511 }
512
513 break;
514 }
515 }
516
517 return std::nullopt;
518}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
void ShowList(wxWindow *aParent, wxPoint aPos)
Shows the background job list.
A bitmap button widget that behaves like an AUI toolbar item's button when it is drawn.
Class DIALOG_HTML_REPORTER.
WX_HTML_REPORT_BOX * m_Reporter
int ShowModal() override
STYLE_FLAGS m_styleFlags
BITMAP_BUTTON * m_notificationsButton
void onLoadWarningsIconClick(wxCommandEvent &aEvent)
std::optional< int > fieldIndex(FIELD aField) const
void SetBackgroundStatusText(const wxString &aTxt)
Set the status text that displays next to the progress bar.
BITMAP_BUTTON * m_warningButton
size_t GetLoadWarningCount() const
Get current message count (thread-safe).
void onBackgroundProgressClick(wxMouseEvent &aEvent)
KISTATUSBAR(int aNumberFields, wxWindow *parent, wxWindowID id, STYLE_FLAGS aFlags=DEFAULT_STYLE)
void onSize(wxSizeEvent &aEvent)
int m_normalFieldsCount
void SetBackgroundProgress(int aAmount)
Set the current progress of the progress bar.
wxGauge * m_backgroundProgressBar
void AddLoadWarningMessages(const std::vector< LOAD_MESSAGE > &aMessages)
Add warning/error messages thread-safely.
wxButton * m_backgroundStopButton
std::mutex m_loadWarningMutex
Protects m_loadWarningMessages.
void updateWarningUI()
Update warning button visibility and badge (main thread only)
std::vector< LOAD_MESSAGE > m_loadWarningMessages
void SetEllipsedTextField(const wxString &aText, int aFieldId)
Set the text in a field using wxELLIPSIZE_MIDDLE option to adjust the text size to the field size.
void HideBackgroundProgressBar()
Hide the background progress bar.
void onNotificationsIconClick(wxCommandEvent &aEvent)
void SetBackgroundProgressMax(int aAmount)
Set the max progress of the progress bar.
wxStaticText * m_backgroundTxt
void ShowBackgroundProgressBar(bool aCancellable=false)
Show the background progress bar.
void SetLoadWarningMessages(const wxString &aMessages)
void SetNotificationCount(int aCount)
Set the notification count on the notifications button.
void ClearLoadWarningMessages()
void ShowList(wxWindow *aParent, wxPoint aPos)
Show the notification list.
virtual BACKGROUND_JOBS_MONITOR & GetBackgroundJobMonitor() const
Definition pgm_base.h:138
virtual NOTIFICATIONS_MANAGER & GetNotificationsManager() const
Definition pgm_base.h:143
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
void Flush()
Build the HTML messages page.
#define _(s)
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
KICOMMON_API wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition ui_common.cpp:78
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
@ RPT_SEVERITY_WARNING
KISTATUSBAR is a wxStatusBar suitable for Kicad manager.
Definition kistatusbar.h:53
wxString message
Definition kistatusbar.h:54
SEVERITY severity
Definition kistatusbar.h:55
wxLogTrace helper definitions.
Functions to provide common constants and other functions to assist in making a consistent UI.