Enhance the status bar with background progress and notifications
This commit is contained in:
parent
cfa49b5250
commit
5319316815
|
@ -186,6 +186,7 @@ set( COMMON_WIDGET_SRCS
|
|||
widgets/grid_text_helpers.cpp
|
||||
widgets/indicator_icon.cpp
|
||||
widgets/wx_infobar.cpp
|
||||
widgets/kistatusbar.cpp
|
||||
widgets/layer_box_selector.cpp
|
||||
widgets/lib_tree.cpp
|
||||
widgets/mathplot.cpp
|
||||
|
@ -300,6 +301,7 @@ set( COMMON_SRCS
|
|||
array_axis.cpp
|
||||
array_options.cpp
|
||||
asset_archive.cpp
|
||||
background_jobs_monitor.cpp
|
||||
base_screen.cpp
|
||||
base64.cpp
|
||||
bin_mod.cpp
|
||||
|
@ -363,6 +365,7 @@ set( COMMON_SRCS
|
|||
marker_base.cpp
|
||||
markup_parser.cpp
|
||||
netclass.cpp
|
||||
notifications_manager.cpp
|
||||
observable.cpp
|
||||
origin_transforms.cpp
|
||||
page_info.cpp
|
||||
|
@ -494,6 +497,7 @@ target_link_libraries( common
|
|||
gal
|
||||
scripting
|
||||
threadpool
|
||||
nlohmann_json
|
||||
pybind11::embed
|
||||
compoundfilereader
|
||||
pcm_settings
|
||||
|
|
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <wx/gauge.h>
|
||||
#include <wx/frame.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <background_jobs_monitor.h>
|
||||
#include <widgets/kistatusbar.h>
|
||||
|
||||
|
||||
class BACKGROUND_JOB_PANEL : public wxPanel
|
||||
{
|
||||
public:
|
||||
BACKGROUND_JOB_PANEL( wxWindow* aParent, BACKGROUND_JOB* aJob ) :
|
||||
wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxSize( -1, 75 ),
|
||||
wxBORDER_SIMPLE ),
|
||||
m_job( aJob )
|
||||
{
|
||||
SetSizeHints( wxDefaultSize, wxDefaultSize );
|
||||
|
||||
wxBoxSizer* mainSizer;
|
||||
mainSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) );
|
||||
|
||||
m_stName = new wxStaticText( this, wxID_ANY, aJob->m_name );
|
||||
m_stName->Wrap( -1 );
|
||||
m_stName->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT,
|
||||
wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
|
||||
mainSizer->Add( m_stName, 0, wxALL | wxEXPAND, 1 );
|
||||
|
||||
m_stStatus = new wxStaticText( this, wxID_ANY, aJob->m_status, wxDefaultPosition,
|
||||
wxDefaultSize, 0 );
|
||||
m_stStatus->Wrap( -1 );
|
||||
mainSizer->Add( m_stStatus, 0, wxALL | wxEXPAND, 1 );
|
||||
|
||||
m_progress = new wxGauge( this, wxID_ANY, aJob->m_maxProgress, wxDefaultPosition, wxDefaultSize,
|
||||
wxGA_HORIZONTAL );
|
||||
m_progress->SetValue( 0 );
|
||||
mainSizer->Add( m_progress, 0, wxALL | wxEXPAND, 1 );
|
||||
|
||||
SetSizer( mainSizer );
|
||||
Layout();
|
||||
|
||||
UpdateFromJob();
|
||||
}
|
||||
|
||||
|
||||
void UpdateFromJob()
|
||||
{
|
||||
m_stStatus->SetLabelText( m_job->m_status );
|
||||
m_progress->SetValue( m_job->m_currentProgress );
|
||||
m_progress->SetRange( m_job->m_maxProgress );
|
||||
}
|
||||
|
||||
private:
|
||||
wxGauge* m_progress;
|
||||
wxStaticText* m_stName;
|
||||
wxStaticText* m_stStatus;
|
||||
BACKGROUND_JOB* m_job;
|
||||
};
|
||||
|
||||
|
||||
class BACKGROUND_JOB_LIST : public wxFrame
|
||||
{
|
||||
public:
|
||||
BACKGROUND_JOB_LIST( wxWindow* parent, const wxPoint& pos ) :
|
||||
wxFrame( parent, wxID_ANY, _( "Background Jobs" ), pos, wxSize( 300, 150 ), wxFRAME_NO_TASKBAR )
|
||||
{
|
||||
SetSizeHints( wxDefaultSize, wxDefaultSize );
|
||||
|
||||
wxBoxSizer* bSizer1;
|
||||
bSizer1 = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition,
|
||||
wxSize( -1, -1 ), wxALWAYS_SHOW_SB | wxVSCROLL );
|
||||
m_scrolledWindow->SetScrollRate( 5, 5 );
|
||||
m_contentSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_scrolledWindow->SetSizer( m_contentSizer );
|
||||
m_scrolledWindow->Layout();
|
||||
m_contentSizer->Fit( m_scrolledWindow );
|
||||
bSizer1->Add( m_scrolledWindow, 1, wxEXPAND | wxALL, 0 );
|
||||
|
||||
Bind( wxEVT_KILL_FOCUS, &BACKGROUND_JOB_LIST::onFocusLoss, this );
|
||||
|
||||
SetSizer( bSizer1 );
|
||||
Layout();
|
||||
|
||||
SetFocus();
|
||||
}
|
||||
|
||||
void onFocusLoss( wxFocusEvent& aEvent )
|
||||
{
|
||||
Close( true );
|
||||
aEvent.Skip();
|
||||
}
|
||||
|
||||
|
||||
void Add( BACKGROUND_JOB* aJob )
|
||||
{
|
||||
BACKGROUND_JOB_PANEL* panel = new BACKGROUND_JOB_PANEL( m_scrolledWindow, aJob );
|
||||
m_contentSizer->Add( panel, 0, wxEXPAND | wxALL, 2 );
|
||||
m_scrolledWindow->Layout();
|
||||
m_contentSizer->Fit( m_scrolledWindow );
|
||||
|
||||
// call this at this window otherwise the child panels dont resize width properly
|
||||
Layout();
|
||||
|
||||
m_jobPanels[aJob] = panel;
|
||||
}
|
||||
|
||||
|
||||
void Remove( BACKGROUND_JOB* aJob )
|
||||
{
|
||||
auto it = m_jobPanels.find( aJob );
|
||||
if( it != m_jobPanels.end() )
|
||||
{
|
||||
BACKGROUND_JOB_PANEL* panel = m_jobPanels[aJob];
|
||||
m_contentSizer->Detach( panel );
|
||||
panel->Destroy();
|
||||
|
||||
m_jobPanels.erase( it );
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateJob( BACKGROUND_JOB* aJob )
|
||||
{
|
||||
auto it = m_jobPanels.find( aJob );
|
||||
if( it != m_jobPanels.end() )
|
||||
{
|
||||
BACKGROUND_JOB_PANEL* panel = m_jobPanels[aJob];
|
||||
panel->UpdateFromJob();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
wxScrolledWindow* m_scrolledWindow;
|
||||
wxBoxSizer* m_contentSizer;
|
||||
std::unordered_map<BACKGROUND_JOB*, BACKGROUND_JOB_PANEL*> m_jobPanels;
|
||||
};
|
||||
|
||||
|
||||
BACKGROUND_JOB_REPORTER::BACKGROUND_JOB_REPORTER( BACKGROUND_JOBS_MONITOR* aMonitor,
|
||||
BACKGROUND_JOB* aJob ) :
|
||||
PROGRESS_REPORTER_BASE( 1 ),
|
||||
m_monitor( aMonitor ), m_job( aJob )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool BACKGROUND_JOB_REPORTER::updateUI()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOB_REPORTER::Report( const wxString& aMessage )
|
||||
{
|
||||
m_job->m_status = aMessage;
|
||||
m_monitor->jobUpdated( m_job );
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOB_REPORTER::SetNumPhases( int aNumPhases )
|
||||
{
|
||||
PROGRESS_REPORTER_BASE::SetNumPhases( aNumPhases );
|
||||
m_job->m_maxProgress = m_numPhases;
|
||||
m_monitor->jobUpdated( m_job );
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOB_REPORTER::AdvancePhase()
|
||||
{
|
||||
PROGRESS_REPORTER_BASE::AdvancePhase();
|
||||
m_job->m_currentProgress = m_phase;
|
||||
m_monitor->jobUpdated( m_job );
|
||||
}
|
||||
|
||||
|
||||
BACKGROUND_JOBS_MONITOR::BACKGROUND_JOBS_MONITOR() : m_jobListDialog( nullptr )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
BACKGROUND_JOB* BACKGROUND_JOBS_MONITOR::Create( const wxString& aName )
|
||||
{
|
||||
BACKGROUND_JOB* job = new BACKGROUND_JOB();
|
||||
|
||||
job->m_name = aName;
|
||||
job->m_reporter = std::make_shared<BACKGROUND_JOB_REPORTER>( this, job );
|
||||
|
||||
m_jobs.push_back( job );
|
||||
|
||||
if( m_shownDialogs.size() > 0 )
|
||||
{
|
||||
// update dialogs
|
||||
for( BACKGROUND_JOB_LIST* list : m_shownDialogs )
|
||||
{
|
||||
list->Add( job );
|
||||
}
|
||||
}
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOBS_MONITOR::Remove( BACKGROUND_JOB* aJob )
|
||||
{
|
||||
if( m_shownDialogs.size() > 0 )
|
||||
{
|
||||
// update dialogs
|
||||
|
||||
for( BACKGROUND_JOB_LIST* list : m_shownDialogs )
|
||||
{
|
||||
list->Remove( aJob );
|
||||
}
|
||||
}
|
||||
|
||||
m_jobs.erase( std::remove_if( m_jobs.begin(), m_jobs.end(),
|
||||
[&]( BACKGROUND_JOB* job )
|
||||
{
|
||||
return job == aJob;
|
||||
} ) );
|
||||
|
||||
if( m_jobs.size() == 0 )
|
||||
{
|
||||
for( KISTATUSBAR* statusBar : m_statusBars )
|
||||
{
|
||||
statusBar->HideBackgroundProgressBar();
|
||||
statusBar->SetBackgroundStatusText( wxT( "" ) );
|
||||
}
|
||||
}
|
||||
|
||||
delete aJob;
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOBS_MONITOR::onListWindowClosed( wxCloseEvent& aEvent )
|
||||
{
|
||||
BACKGROUND_JOB_LIST* evtWindow = dynamic_cast<BACKGROUND_JOB_LIST*>( aEvent.GetEventObject() );
|
||||
|
||||
m_shownDialogs.erase( std::remove_if( m_shownDialogs.begin(), m_shownDialogs.end(),
|
||||
[&]( BACKGROUND_JOB_LIST* dialog )
|
||||
{
|
||||
return dialog == evtWindow;
|
||||
} ) );
|
||||
|
||||
aEvent.Skip();
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOBS_MONITOR::ShowList( wxWindow* aParent, wxPoint aPos )
|
||||
{
|
||||
BACKGROUND_JOB_LIST* list = new BACKGROUND_JOB_LIST( aParent, aPos );
|
||||
|
||||
for( BACKGROUND_JOB* job : m_jobs )
|
||||
{
|
||||
list->Add( job );
|
||||
}
|
||||
|
||||
m_shownDialogs.push_back( list );
|
||||
|
||||
list->Bind( wxEVT_CLOSE_WINDOW, &BACKGROUND_JOBS_MONITOR::onListWindowClosed, this );
|
||||
|
||||
// correct the position
|
||||
wxSize windowSize = list->GetSize();
|
||||
list->SetPosition( aPos - windowSize );
|
||||
|
||||
list->Show();
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOBS_MONITOR::jobUpdated( BACKGROUND_JOB* aJob )
|
||||
{
|
||||
//for now, we go and update the status bar if its the first job in the vector
|
||||
|
||||
if( m_jobs.size() > 0 )
|
||||
{
|
||||
if( m_jobs.front() == aJob )
|
||||
{
|
||||
// update all status bar entries
|
||||
for( KISTATUSBAR* statusBar : m_statusBars )
|
||||
{
|
||||
statusBar->ShowBackgroundProgressBar();
|
||||
statusBar->SetBackgroundProgress( aJob->m_currentProgress );
|
||||
statusBar->SetBackgroundProgressMax( aJob->m_maxProgress );
|
||||
statusBar->SetBackgroundStatusText( aJob->m_status );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( BACKGROUND_JOB_LIST* list : m_shownDialogs )
|
||||
{
|
||||
list->UpdateJob( aJob );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOBS_MONITOR::RegisterStatusBar( KISTATUSBAR* aStatusBar )
|
||||
{
|
||||
m_statusBars.push_back( aStatusBar );
|
||||
}
|
||||
|
||||
|
||||
void BACKGROUND_JOBS_MONITOR::UnregisterStatusBar( KISTATUSBAR* aStatusBar )
|
||||
{
|
||||
m_statusBars.erase( std::remove_if( m_statusBars.begin(), m_statusBars.end(),
|
||||
[&]( KISTATUSBAR* statusBar )
|
||||
{
|
||||
return statusBar == aStatusBar;
|
||||
} ) );
|
||||
}
|
|
@ -45,6 +45,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
|||
aBitmapInfoCache[BITMAPS::label_align_top].emplace_back( BITMAPS::label_align_top, wxT( "label_align_top_16.png" ), 16, wxT( "light" ) );
|
||||
aBitmapInfoCache[BITMAPS::label_align_bottom].emplace_back( BITMAPS::label_align_bottom, wxT( "label_align_bottom_16.png" ), 16, wxT( "light" ) );
|
||||
aBitmapInfoCache[BITMAPS::list_nets_16].emplace_back( BITMAPS::list_nets_16, wxT( "list_nets_16_16.png" ), 16, wxT( "light" ) );
|
||||
aBitmapInfoCache[BITMAPS::notifications].emplace_back( BITMAPS::notifications, wxT( "notifications_16.png" ), 16, wxT( "light" ) );
|
||||
aBitmapInfoCache[BITMAPS::options_generic_16].emplace_back( BITMAPS::options_generic_16, wxT( "options_generic_16_16.png" ), 16, wxT( "light" ) );
|
||||
aBitmapInfoCache[BITMAPS::pinorient_right].emplace_back( BITMAPS::pinorient_right, wxT( "pinorient_right_16.png" ), 16, wxT( "light" ) );
|
||||
aBitmapInfoCache[BITMAPS::pinorient_left].emplace_back( BITMAPS::pinorient_left, wxT( "pinorient_left_16.png" ), 16, wxT( "light" ) );
|
||||
|
@ -115,6 +116,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
|||
aBitmapInfoCache[BITMAPS::label_align_top].emplace_back( BITMAPS::label_align_top, wxT( "label_align_top_dark_16.png" ), 16, wxT( "dark" ) );
|
||||
aBitmapInfoCache[BITMAPS::label_align_bottom].emplace_back( BITMAPS::label_align_bottom, wxT( "label_align_bottom_dark_16.png" ), 16, wxT( "dark" ) );
|
||||
aBitmapInfoCache[BITMAPS::list_nets_16].emplace_back( BITMAPS::list_nets_16, wxT( "list_nets_16_dark_16.png" ), 16, wxT( "dark" ) );
|
||||
aBitmapInfoCache[BITMAPS::notifications].emplace_back( BITMAPS::notifications, wxT( "notifications_dark_16.png" ), 16, wxT( "dark" ) );
|
||||
aBitmapInfoCache[BITMAPS::options_generic_16].emplace_back( BITMAPS::options_generic_16, wxT( "options_generic_16_dark_16.png" ), 16, wxT( "dark" ) );
|
||||
aBitmapInfoCache[BITMAPS::pinorient_right].emplace_back( BITMAPS::pinorient_right, wxT( "pinorient_right_dark_16.png" ), 16, wxT( "dark" ) );
|
||||
aBitmapInfoCache[BITMAPS::pinorient_left].emplace_back( BITMAPS::pinorient_left, wxT( "pinorient_left_dark_16.png" ), 16, wxT( "dark" ) );
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <wx/filename.h>
|
||||
#include <wx/frame.h>
|
||||
#include <wx/hyperlink.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <paths.h>
|
||||
|
||||
#include <notifications_manager.h>
|
||||
#include <widgets/kistatusbar.h>
|
||||
|
||||
#include "core/wx_stl_compat.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
|
||||
|
||||
// Teaching json en/decoder to understand wxStrings
|
||||
namespace nlohmann
|
||||
{
|
||||
template <>
|
||||
struct adl_serializer<wxString>
|
||||
{
|
||||
static void to_json( json& j, const wxString& s ) { j = s.ToUTF8(); }
|
||||
|
||||
static void from_json( const json& j, wxString& s )
|
||||
{
|
||||
s = wxString::FromUTF8( j.get<std::string>().c_str() );
|
||||
}
|
||||
};
|
||||
} // namespace nlohmann
|
||||
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( NOTIFICATION, title, description, href, key, date )
|
||||
|
||||
class NOTIFICATION_PANEL : public wxPanel
|
||||
{
|
||||
public:
|
||||
NOTIFICATION_PANEL( wxWindow* aParent, NOTIFICATIONS_MANAGER* aManager, NOTIFICATION* aNoti ) :
|
||||
wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxSize( -1, 75 ),
|
||||
wxBORDER_SIMPLE ),
|
||||
m_hlDetails( nullptr ),
|
||||
m_notification( aNoti ),
|
||||
m_manager( aManager )
|
||||
{
|
||||
SetSizeHints( wxDefaultSize, wxDefaultSize );
|
||||
|
||||
wxBoxSizer* mainSizer;
|
||||
mainSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) );
|
||||
|
||||
m_stTitle = new wxStaticText( this, wxID_ANY, aNoti->title );
|
||||
m_stTitle->Wrap( -1 );
|
||||
m_stTitle->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT,
|
||||
wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
|
||||
mainSizer->Add( m_stTitle, 0, wxALL | wxEXPAND, 1 );
|
||||
|
||||
m_stDescription = new wxStaticText( this, wxID_ANY, aNoti->description, wxDefaultPosition,
|
||||
wxDefaultSize, 0 );
|
||||
m_stDescription->Wrap( -1 );
|
||||
mainSizer->Add( m_stDescription, 0, wxALL | wxEXPAND, 1 );
|
||||
|
||||
wxBoxSizer* tailSizer;
|
||||
tailSizer = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
if( !aNoti->href.IsEmpty() )
|
||||
{
|
||||
m_hlDetails =
|
||||
new wxHyperlinkCtrl( this, wxID_ANY, _( "View Details" ), aNoti->href,
|
||||
wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
|
||||
tailSizer->Add( m_hlDetails, 0, wxALL, 2 );
|
||||
}
|
||||
|
||||
m_hlDismiss = new wxHyperlinkCtrl( this, wxID_ANY, _( "Dismiss" ), aNoti->href,
|
||||
wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
|
||||
tailSizer->Add( m_hlDismiss, 0, wxALL, 2 );
|
||||
|
||||
mainSizer->Add( tailSizer, 1, wxEXPAND, 5 );
|
||||
|
||||
if( m_hlDetails != nullptr )
|
||||
{
|
||||
m_hlDetails->Bind( wxEVT_HYPERLINK, &NOTIFICATION_PANEL::onDetails, this );
|
||||
}
|
||||
|
||||
m_hlDismiss->Bind( wxEVT_HYPERLINK, &NOTIFICATION_PANEL::onDismiss, this );
|
||||
|
||||
Bind( wxEVT_SET_FOCUS, &NOTIFICATION_PANEL::onFocusSet, this );
|
||||
|
||||
SetSizer( mainSizer );
|
||||
Layout();
|
||||
}
|
||||
|
||||
private:
|
||||
void onFocusSet( wxFocusEvent& aEvent )
|
||||
{
|
||||
// hmmph
|
||||
|
||||
}
|
||||
|
||||
void onDetails( wxHyperlinkEvent& aEvent )
|
||||
{
|
||||
wxString url = aEvent.GetURL();
|
||||
|
||||
if( url.StartsWith( wxS( "kicad://" ) ) )
|
||||
{
|
||||
url.Replace( wxS( "kicad://" ), wxS( "" ) );
|
||||
|
||||
if( url == wxS( "pcm" ) )
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLaunchDefaultBrowser( aEvent.GetURL(), wxBROWSER_NEW_WINDOW );
|
||||
}
|
||||
}
|
||||
|
||||
void onDismiss( wxHyperlinkEvent& aEvent )
|
||||
{
|
||||
CallAfter(
|
||||
[=]()
|
||||
{
|
||||
// This will cause this panel to get deleted
|
||||
m_manager->Remove( m_notification->key );
|
||||
} );
|
||||
}
|
||||
|
||||
wxStaticText* m_stTitle;
|
||||
wxStaticText* m_stDescription;
|
||||
wxHyperlinkCtrl* m_hlDetails;
|
||||
wxHyperlinkCtrl* m_hlDismiss;
|
||||
NOTIFICATION* m_notification;
|
||||
NOTIFICATIONS_MANAGER* m_manager;
|
||||
};
|
||||
|
||||
|
||||
class NOTIFICATIONS_LIST : public wxFrame
|
||||
{
|
||||
public:
|
||||
NOTIFICATIONS_LIST( NOTIFICATIONS_MANAGER* aManager, wxWindow* parent, const wxPoint& pos ) :
|
||||
wxFrame( parent, wxID_ANY, _( "Notifications" ), pos, wxSize( 300, 150 ), wxFRAME_NO_TASKBAR ),
|
||||
m_manager( aManager )
|
||||
{
|
||||
SetSizeHints( wxDefaultSize, wxDefaultSize );
|
||||
|
||||
wxBoxSizer* bSizer1;
|
||||
bSizer1 = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition,
|
||||
wxSize( -1, -1 ), wxALWAYS_SHOW_SB | wxVSCROLL );
|
||||
m_scrolledWindow->SetScrollRate( 5, 5 );
|
||||
m_contentSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_scrolledWindow->SetSizer( m_contentSizer );
|
||||
m_scrolledWindow->Layout();
|
||||
m_contentSizer->Fit( m_scrolledWindow );
|
||||
bSizer1->Add( m_scrolledWindow, 1, wxEXPAND | wxALL, 0 );
|
||||
|
||||
Bind( wxEVT_KILL_FOCUS, &NOTIFICATIONS_LIST::onFocusLoss, this );
|
||||
|
||||
SetSizer( bSizer1 );
|
||||
Layout();
|
||||
|
||||
SetFocus();
|
||||
}
|
||||
|
||||
|
||||
void onFocusLoss( wxFocusEvent& aEvent )
|
||||
{
|
||||
// check if a child like say, the hyperlink texts got focus
|
||||
if( !IsDescendant( aEvent.GetWindow() ) )
|
||||
Close( true );
|
||||
|
||||
aEvent.Skip();
|
||||
}
|
||||
|
||||
|
||||
void Add( NOTIFICATION* aNoti )
|
||||
{
|
||||
NOTIFICATION_PANEL* panel = new NOTIFICATION_PANEL( m_scrolledWindow, m_manager, aNoti );
|
||||
m_contentSizer->Add( panel, 0, wxEXPAND | wxALL, 2 );
|
||||
m_scrolledWindow->Layout();
|
||||
m_contentSizer->Fit( m_scrolledWindow );
|
||||
|
||||
// call this at this window otherwise the child panels dont resize width properly
|
||||
Layout();
|
||||
|
||||
m_panelMap[aNoti] = panel;
|
||||
}
|
||||
|
||||
|
||||
void Remove( NOTIFICATION* aNoti )
|
||||
{
|
||||
auto it = m_panelMap.find( aNoti );
|
||||
if( it != m_panelMap.end() )
|
||||
{
|
||||
NOTIFICATION_PANEL* panel = m_panelMap[aNoti];
|
||||
m_contentSizer->Detach( panel );
|
||||
panel->Destroy();
|
||||
|
||||
m_panelMap.erase( it );
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
wxScrolledWindow* m_scrolledWindow;
|
||||
wxBoxSizer* m_contentSizer;
|
||||
std::unordered_map<NOTIFICATION*, NOTIFICATION_PANEL*> m_panelMap;
|
||||
NOTIFICATIONS_MANAGER* m_manager;
|
||||
};
|
||||
|
||||
|
||||
NOTIFICATIONS_MANAGER::NOTIFICATIONS_MANAGER()
|
||||
{
|
||||
m_destFileName = wxFileName( PATHS::GetUserCachePath(), wxT( "notifications.json" ) );
|
||||
}
|
||||
|
||||
|
||||
void NOTIFICATIONS_MANAGER::Load()
|
||||
{
|
||||
nlohmann::json saved_json;
|
||||
|
||||
std::ifstream saved_json_stream( m_destFileName.GetFullPath().ToUTF8() );
|
||||
|
||||
try
|
||||
{
|
||||
saved_json_stream >> saved_json;
|
||||
|
||||
m_notifications = saved_json.get<std::vector<NOTIFICATION>>();
|
||||
}
|
||||
catch( std::exception& )
|
||||
{
|
||||
// failed to load the json
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NOTIFICATIONS_MANAGER::Save()
|
||||
{
|
||||
std::ofstream jsonFileStream( m_destFileName.GetFullPath().ToUTF8() );
|
||||
|
||||
nlohmann::json saveJson = nlohmann::json( m_notifications );
|
||||
jsonFileStream << std::setw( 4 ) << saveJson << std::endl;
|
||||
jsonFileStream.flush();
|
||||
jsonFileStream.close();
|
||||
}
|
||||
|
||||
|
||||
void NOTIFICATIONS_MANAGER::Create( const wxString& aKey,
|
||||
const wxString& aTitle,
|
||||
const wxString& aDescription,
|
||||
const wxString& aHref )
|
||||
{
|
||||
auto it = std::find_if( m_notifications.begin(), m_notifications.end(),
|
||||
[&]( const NOTIFICATION& noti )
|
||||
{
|
||||
return noti.key == aKey;
|
||||
} );
|
||||
|
||||
if( it != m_notifications.end() )
|
||||
{
|
||||
NOTIFICATION& noti = *it;
|
||||
|
||||
noti.title = aTitle;
|
||||
noti.description = aDescription;
|
||||
noti.href = aHref;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_notifications.emplace_back( NOTIFICATION{ aTitle, aDescription, aHref, aKey } );
|
||||
}
|
||||
|
||||
if( m_shownDialogs.size() > 0 )
|
||||
{
|
||||
// update dialogs
|
||||
for( NOTIFICATIONS_LIST* list : m_shownDialogs )
|
||||
{
|
||||
list->Add( &m_notifications.back() );
|
||||
}
|
||||
}
|
||||
|
||||
for( KISTATUSBAR* statusBar : m_statusBars )
|
||||
{
|
||||
statusBar->SetNotificationCount( m_notifications.size() );
|
||||
}
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
|
||||
void NOTIFICATIONS_MANAGER::Remove( const wxString& aKey )
|
||||
{
|
||||
auto it = std::find_if( m_notifications.begin(), m_notifications.end(),
|
||||
[&]( const NOTIFICATION& noti )
|
||||
{
|
||||
return noti.key == aKey;
|
||||
} );
|
||||
|
||||
if( it == m_notifications.end() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_shownDialogs.size() > 0 )
|
||||
{
|
||||
// update dialogs
|
||||
|
||||
for( NOTIFICATIONS_LIST* list : m_shownDialogs )
|
||||
{
|
||||
list->Remove( &(*it) );
|
||||
}
|
||||
}
|
||||
|
||||
m_notifications.erase( it );
|
||||
|
||||
Save();
|
||||
|
||||
for( KISTATUSBAR* statusBar : m_statusBars )
|
||||
{
|
||||
statusBar->SetNotificationCount( m_notifications.size() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NOTIFICATIONS_MANAGER::onListWindowClosed( wxCloseEvent& aEvent )
|
||||
{
|
||||
NOTIFICATIONS_LIST* evtWindow = dynamic_cast<NOTIFICATIONS_LIST*>( aEvent.GetEventObject() );
|
||||
|
||||
m_shownDialogs.erase( std::remove_if( m_shownDialogs.begin(), m_shownDialogs.end(),
|
||||
[&]( NOTIFICATIONS_LIST* dialog )
|
||||
{
|
||||
return dialog == evtWindow;
|
||||
} ) );
|
||||
|
||||
aEvent.Skip();
|
||||
}
|
||||
|
||||
|
||||
void NOTIFICATIONS_MANAGER::ShowList( wxWindow* aParent, wxPoint aPos )
|
||||
{
|
||||
NOTIFICATIONS_LIST* list = new NOTIFICATIONS_LIST( this, aParent, aPos );
|
||||
|
||||
for( NOTIFICATION& job : m_notifications )
|
||||
{
|
||||
list->Add( &job );
|
||||
}
|
||||
|
||||
m_shownDialogs.push_back( list );
|
||||
|
||||
list->Bind( wxEVT_CLOSE_WINDOW, &NOTIFICATIONS_MANAGER::onListWindowClosed, this );
|
||||
|
||||
// correct the position
|
||||
wxSize windowSize = list->GetSize();
|
||||
list->SetPosition( aPos - windowSize );
|
||||
|
||||
list->Show();
|
||||
}
|
||||
|
||||
|
||||
void NOTIFICATIONS_MANAGER::RegisterStatusBar( KISTATUSBAR* aStatusBar )
|
||||
{
|
||||
m_statusBars.push_back( aStatusBar );
|
||||
|
||||
// notifications should already be loaded so set the initial notification count
|
||||
aStatusBar->SetNotificationCount( m_notifications.size() );
|
||||
}
|
||||
|
||||
|
||||
void NOTIFICATIONS_MANAGER::UnregisterStatusBar( KISTATUSBAR* aStatusBar )
|
||||
{
|
||||
m_statusBars.erase( std::remove_if( m_statusBars.begin(), m_statusBars.end(),
|
||||
[&]( KISTATUSBAR* statusBar )
|
||||
{
|
||||
return statusBar == aStatusBar;
|
||||
} ) );
|
||||
}
|
|
@ -43,6 +43,7 @@
|
|||
#include <wx/tooltip.h>
|
||||
|
||||
#include <advanced_config.h>
|
||||
#include <background_jobs_monitor.h>
|
||||
#include <bitmaps.h>
|
||||
#include <cli/cli_names.h> // Needed for the pre wx 3.2 cli workaround
|
||||
#include <common.h>
|
||||
|
@ -57,6 +58,7 @@
|
|||
#include <kiplatform/policy.h>
|
||||
#include <macros.h>
|
||||
#include <menus_helpers.h>
|
||||
#include <notifications_manager.h>
|
||||
#include <paths.h>
|
||||
#include <pgm_base.h>
|
||||
#include <policy_keys.h>
|
||||
|
@ -511,6 +513,8 @@ bool PGM_BASE::InitPgm( bool aHeadless, bool aSkipPyInit, bool aIsUnitTest )
|
|||
SetDefaultLanguage( tmp );
|
||||
|
||||
m_settings_manager = std::make_unique<SETTINGS_MANAGER>( aHeadless );
|
||||
m_background_jobs_monitor = std::make_unique<BACKGROUND_JOBS_MONITOR>();
|
||||
m_notifications_manager = std::make_unique<NOTIFICATIONS_MANAGER>();
|
||||
|
||||
// Our unit test mocks break if we continue
|
||||
// A bug caused InitPgm to terminate early in unit tests and the mocks are...simplistic
|
||||
|
@ -547,6 +551,8 @@ bool PGM_BASE::InitPgm( bool aHeadless, bool aSkipPyInit, bool aIsUnitTest )
|
|||
|
||||
ReadPdfBrowserInfos(); // needs GetCommonSettings()
|
||||
|
||||
GetNotificationsManager().Load();
|
||||
|
||||
// Create the python scripting stuff
|
||||
// Skip it fot applications that do not use it
|
||||
if( !aSkipPyInit )
|
||||
|
|
|
@ -41,7 +41,8 @@ BITMAP_BUTTON::BITMAP_BUTTON( wxWindow* aParent, wxWindowID aId, const wxPoint&
|
|||
m_badgeTextColor( wxColor( wxT( "white" ) ) ),
|
||||
m_buttonState( 0 ),
|
||||
m_padding( 0 ),
|
||||
m_acceptDraggedInClicks( false )
|
||||
m_acceptDraggedInClicks( false ),
|
||||
m_centerBitmap( false )
|
||||
{
|
||||
if( aSize == wxDefaultSize )
|
||||
SetMinSize( wxButton::GetDefaultSize() + wxSize( m_padding * 2, m_padding * 2) );
|
||||
|
@ -57,13 +58,18 @@ BITMAP_BUTTON::BITMAP_BUTTON( wxWindow* aParent, wxWindowID aId, const wxBitmap&
|
|||
wxPanel( aParent, aId, aPos, aSize, aStyles ),
|
||||
m_isRadioButton( false ),
|
||||
m_showBadge( false ),
|
||||
m_badgeColor( wxColor( 210, 0, 0 ) ), // dark red
|
||||
m_badgeTextColor( wxColor( wxT( "white" ) ) ),
|
||||
m_buttonState( 0 ),
|
||||
m_padding( 5 ),
|
||||
m_acceptDraggedInClicks( false )
|
||||
m_acceptDraggedInClicks( false ),
|
||||
m_centerBitmap( false )
|
||||
{
|
||||
if( aSize == wxDefaultSize )
|
||||
SetMinSize( wxButton::GetDefaultSize() + wxSize( m_padding * 2, m_padding * 2) );
|
||||
|
||||
m_badgeFont = GetFont().Smaller().MakeBold();
|
||||
|
||||
setupEvents();
|
||||
}
|
||||
|
||||
|
@ -144,9 +150,9 @@ void BITMAP_BUTTON::OnMouseEnter( wxEvent& aEvent )
|
|||
|
||||
void BITMAP_BUTTON::OnKillFocus( wxEvent& aEvent )
|
||||
{
|
||||
if( hasFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED ) )
|
||||
if( hasFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED | wxCONTROL_SELECTED ) )
|
||||
{
|
||||
clearFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED );
|
||||
clearFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED | wxCONTROL_SELECTED );
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
@ -279,18 +285,30 @@ void BITMAP_BUTTON::OnPaint( wxPaintEvent& aEvent )
|
|||
|
||||
const wxBitmapBundle& bmp = hasFlag( wxCONTROL_DISABLED ) ? m_disabledBitmap : m_normalBitmap;
|
||||
|
||||
wxPoint drawBmpPos( m_padding, m_padding );
|
||||
wxBitmap bmpImg = bmp.GetBitmapFor( this );
|
||||
if( m_centerBitmap )
|
||||
{
|
||||
drawBmpPos = wxPoint( (rect.width - bmpImg.GetWidth()) / 2, (rect.height - bmpImg.GetHeight()) / 2 );
|
||||
}
|
||||
|
||||
// Draw the bitmap with the upper-left corner offset by the padding
|
||||
if( bmp.IsOk() )
|
||||
dc.DrawBitmap( bmp.GetBitmapFor( this ), m_padding, m_padding, true );
|
||||
dc.DrawBitmap( bmpImg, drawBmpPos, true );
|
||||
|
||||
// Draw the badge
|
||||
if( m_showBadge )
|
||||
{
|
||||
dc.SetFont( m_badgeFont );
|
||||
|
||||
wxSize box_size = dc.GetTextExtent( m_badgeText ) + wxSize( 6, 2 );
|
||||
wxSize box_offset = box_size + wxSize( m_padding - 2, m_padding );
|
||||
wxSize text_offset = box_offset - wxSize( 3, 1 );
|
||||
wxSize box_size = dc.GetTextExtent( m_badgeText );
|
||||
wxSize box_offset = box_size;
|
||||
wxSize text_offset = box_offset;
|
||||
if( m_padding != 0 )
|
||||
{
|
||||
box_offset += wxSize( m_padding - 2, m_padding );
|
||||
text_offset -= wxSize( 3, 1 );
|
||||
}
|
||||
|
||||
dc.SetPen( wxPen( m_badgeColor ) );
|
||||
dc.SetBrush( wxBrush( m_badgeColor ) );
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <wx/button.h>
|
||||
#include <wx/statusbr.h>
|
||||
#include <wx/gauge.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <array>
|
||||
#include <widgets/kistatusbar.h>
|
||||
#include <widgets/bitmap_button.h>
|
||||
#include <pgm_base.h>
|
||||
#include <background_jobs_monitor.h>
|
||||
#include <notifications_manager.h>
|
||||
#include <bitmaps.h>
|
||||
|
||||
KISTATUSBAR::KISTATUSBAR( int aNumberFields, wxWindow* parent, wxWindowID id ) :
|
||||
wxStatusBar( parent, id ),
|
||||
m_normalFieldsCount( aNumberFields )
|
||||
{
|
||||
const int ExtraFields = 4;
|
||||
SetFieldsCount( aNumberFields + ExtraFields );
|
||||
|
||||
int* widths = new int[aNumberFields + ExtraFields];
|
||||
for( int i = 0; i < aNumberFields; i++ )
|
||||
widths[i] = -1;
|
||||
|
||||
widths[aNumberFields] = 200;
|
||||
widths[aNumberFields + 1] = 75;
|
||||
widths[aNumberFields + 2] = 20;
|
||||
widths[aNumberFields + 3] = 20;
|
||||
|
||||
SetStatusWidths( aNumberFields + ExtraFields, widths );
|
||||
delete[] widths;
|
||||
|
||||
|
||||
int* styles = new int[aNumberFields + ExtraFields];
|
||||
for( int i = 0; i < aNumberFields + ExtraFields; i++ )
|
||||
styles[i] = wxSB_FLAT;
|
||||
|
||||
SetStatusStyles( aNumberFields + ExtraFields, styles );
|
||||
delete[] styles;
|
||||
|
||||
m_backgroundTxt =
|
||||
new wxStaticText( this, wxID_ANY, wxT( "" ), wxDefaultPosition, wxDefaultSize );
|
||||
|
||||
|
||||
m_backgroundProgressBar = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize,
|
||||
wxGA_HORIZONTAL | wxGA_SMOOTH );
|
||||
|
||||
m_backgroundStopButton =
|
||||
new wxButton( this, wxID_ANY, "X", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT );
|
||||
|
||||
m_notificationsButton = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition,
|
||||
wxDefaultSize, wxBU_EXACTFIT );
|
||||
|
||||
m_notificationsButton->SetPadding( 0 );
|
||||
m_notificationsButton->SetBitmap( KiBitmap( BITMAPS::notifications ) );
|
||||
m_notificationsButton->SetShowBadge( true );
|
||||
m_notificationsButton->SetBitmapCentered( true );
|
||||
|
||||
m_notificationsButton->Bind( wxEVT_BUTTON, &KISTATUSBAR::onNotificationsIconClick, this );
|
||||
|
||||
Bind( wxEVT_SIZE, &KISTATUSBAR::onSize, this );
|
||||
m_backgroundProgressBar->Bind( wxEVT_LEFT_DOWN, &KISTATUSBAR::onBackgroundProgressClick, this );
|
||||
|
||||
HideBackgroundProgressBar();
|
||||
Layout();
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::onNotificationsIconClick( wxCommandEvent& aEvent )
|
||||
{
|
||||
wxPoint pos = m_notificationsButton->GetScreenPosition();
|
||||
|
||||
wxRect r;
|
||||
GetFieldRect( m_normalFieldsCount + 3, r );
|
||||
pos.x += r.GetWidth();
|
||||
|
||||
Pgm().GetNotificationsManager().ShowList( this, pos );
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::onBackgroundProgressClick( wxMouseEvent& aEvent )
|
||||
{
|
||||
wxPoint pos = m_backgroundProgressBar->GetScreenPosition();
|
||||
|
||||
wxRect r;
|
||||
GetFieldRect( m_normalFieldsCount + 1, r );
|
||||
pos.x += r.GetWidth();
|
||||
|
||||
Pgm().GetBackgroundJobMonitor().ShowList( this, pos );
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::onSize( wxSizeEvent& aEvent )
|
||||
{
|
||||
wxRect r;
|
||||
GetFieldRect( m_normalFieldsCount, r );
|
||||
int x = r.GetLeft();
|
||||
int y = r.GetTop();
|
||||
|
||||
m_backgroundTxt->SetPosition( { x, y } );
|
||||
|
||||
GetFieldRect( m_normalFieldsCount + 1, r );
|
||||
x = r.GetLeft();
|
||||
y = r.GetTop();
|
||||
int w = r.GetWidth();
|
||||
int h = r.GetHeight();
|
||||
constexpr int b = 5;
|
||||
|
||||
auto buttonSize = m_backgroundStopButton->GetEffectiveMinSize();
|
||||
m_backgroundStopButton->SetPosition( { x + w - buttonSize.GetWidth(), y } );
|
||||
m_backgroundStopButton->SetSize( buttonSize.GetWidth(), h );
|
||||
|
||||
m_backgroundProgressBar->SetPosition( { x, y } );
|
||||
m_backgroundProgressBar->SetSize( w - buttonSize.GetWidth() - b, h );
|
||||
|
||||
GetFieldRect( m_normalFieldsCount+3, r );
|
||||
x = r.GetLeft();
|
||||
y = r.GetTop();
|
||||
h = r.GetHeight();
|
||||
buttonSize = m_notificationsButton->GetEffectiveMinSize();
|
||||
m_notificationsButton->SetPosition( { x, y } );
|
||||
m_notificationsButton->SetSize( buttonSize.GetWidth() + 6, h );
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::ShowBackgroundProgressBar( bool aCancellable )
|
||||
{
|
||||
m_backgroundProgressBar->Show();
|
||||
|
||||
if( aCancellable )
|
||||
m_backgroundStopButton->Show();
|
||||
else
|
||||
m_backgroundStopButton->Hide();
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::HideBackgroundProgressBar()
|
||||
{
|
||||
m_backgroundProgressBar->Hide();
|
||||
m_backgroundStopButton->Hide();
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::SetBackgroundProgress( int aAmount )
|
||||
{
|
||||
m_backgroundProgressBar->SetValue( aAmount );
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::SetBackgroundProgressMax( int aAmount )
|
||||
{
|
||||
m_backgroundProgressBar->SetRange( aAmount );
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::SetBackgroundStatusText( const wxString& aTxt )
|
||||
{
|
||||
m_backgroundTxt->SetLabel( aTxt );
|
||||
}
|
||||
|
||||
|
||||
void KISTATUSBAR::SetNotificationCount(int aCount)
|
||||
{
|
||||
wxString cnt = "";
|
||||
if( aCount > 0 )
|
||||
{
|
||||
cnt = wxString::Format( "%d", aCount );
|
||||
}
|
||||
|
||||
m_notificationsButton->SetBadgeText( cnt );
|
||||
|
||||
// force a repaint or it wont until it gets activity
|
||||
Refresh();
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef BACKGROUND_JOBS_MONITOR_H
|
||||
#define BACKGROUND_JOBS_MONITOR_H
|
||||
|
||||
#include <widgets/progress_reporter_base.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class PROGRESS_REPORTER;
|
||||
class wxString;
|
||||
class KISTATUSBAR;
|
||||
struct BACKGROUND_JOB;
|
||||
class BACKGROUND_JOB_REPORTER;
|
||||
class BACKGROUND_JOB_LIST;
|
||||
class BACKGROUND_JOBS_MONITOR;
|
||||
class wxWindow;
|
||||
class wxCloseEvent;
|
||||
|
||||
class BACKGROUND_JOB_REPORTER : public PROGRESS_REPORTER_BASE
|
||||
{
|
||||
public:
|
||||
BACKGROUND_JOB_REPORTER( BACKGROUND_JOBS_MONITOR* aMonitor, BACKGROUND_JOB* aJob );
|
||||
|
||||
void SetTitle( const wxString& aTitle ) override
|
||||
{
|
||||
}
|
||||
|
||||
void Report( const wxString& aMessage ) override;
|
||||
|
||||
void Cancel() { m_cancelled.store( true ); }
|
||||
|
||||
void AdvancePhase() override;
|
||||
|
||||
void SetNumPhases( int aNumPhases ) override;
|
||||
|
||||
private:
|
||||
bool updateUI() override;
|
||||
|
||||
BACKGROUND_JOBS_MONITOR* m_monitor;
|
||||
BACKGROUND_JOB* m_job;
|
||||
wxString m_title;
|
||||
wxString m_report;
|
||||
};
|
||||
|
||||
|
||||
struct BACKGROUND_JOB
|
||||
{
|
||||
public:
|
||||
wxString m_name;
|
||||
wxString m_status;
|
||||
std::shared_ptr<BACKGROUND_JOB_REPORTER> m_reporter;
|
||||
|
||||
int m_maxProgress;
|
||||
int m_currentProgress;
|
||||
};
|
||||
|
||||
|
||||
class BACKGROUND_JOBS_MONITOR
|
||||
{
|
||||
friend class BACKGROUND_JOB_REPORTER;
|
||||
friend class BACKGROUND_JOB_LIST;
|
||||
|
||||
public:
|
||||
BACKGROUND_JOBS_MONITOR();
|
||||
|
||||
/**
|
||||
* Creates a background job with the given name
|
||||
*
|
||||
* @param aName is the displayed title for the event
|
||||
*/
|
||||
BACKGROUND_JOB* Create( const wxString& aName );
|
||||
|
||||
/**
|
||||
* Removes the given background job from any lists and frees it
|
||||
*/
|
||||
void Remove( BACKGROUND_JOB* job );
|
||||
|
||||
/**
|
||||
* Shows the background job list
|
||||
*/
|
||||
void ShowList( wxWindow* aParent, wxPoint aPos );
|
||||
|
||||
/**
|
||||
* Add a status bar for handling
|
||||
*/
|
||||
void RegisterStatusBar( KISTATUSBAR* aStatusBar );
|
||||
|
||||
/**
|
||||
* Removes status bar from handling
|
||||
*/
|
||||
void UnregisterStatusBar( KISTATUSBAR* aStatusBar );
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handles removing the shown list window from our list of shown windows
|
||||
*/
|
||||
void onListWindowClosed( wxCloseEvent& aEvent );
|
||||
|
||||
/**
|
||||
* Handles job status updates, intended to be called by BACKGROUND_JOB_REPORTER only
|
||||
*/
|
||||
void jobUpdated( BACKGROUND_JOB* aJob );
|
||||
|
||||
BACKGROUND_JOB_LIST* m_jobListDialog;
|
||||
|
||||
std::vector<BACKGROUND_JOB*> m_jobs;
|
||||
std::vector<BACKGROUND_JOB_LIST*> m_shownDialogs;
|
||||
|
||||
std::vector<KISTATUSBAR*> m_statusBars;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -370,6 +370,7 @@ enum class BITMAPS : unsigned int
|
|||
new_project_from_template,
|
||||
new_python,
|
||||
noconn,
|
||||
notifications,
|
||||
normal,
|
||||
open_project,
|
||||
open_project_demo,
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef NOTIFICATIONS_MANAGER_H
|
||||
#define NOTIFICATIONS_MANAGER_H
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
class wxString;
|
||||
class KISTATUSBAR;
|
||||
struct NOTIFICATION;
|
||||
class NOTIFICATIONS_LIST;
|
||||
class wxWindow;
|
||||
class wxCloseEvent;
|
||||
|
||||
|
||||
struct NOTIFICATION
|
||||
{
|
||||
public:
|
||||
wxString title; ///< Title of the notification
|
||||
wxString description; ///< Additional message displayed under title
|
||||
wxString href; ///< URL if any to link to for details
|
||||
wxString key; ///< Unique key to find a notification
|
||||
wxString date; ///< Date notification will display
|
||||
};
|
||||
|
||||
|
||||
class NOTIFICATIONS_MANAGER
|
||||
{
|
||||
friend class NOTIFICATION_LIST;
|
||||
|
||||
public:
|
||||
NOTIFICATIONS_MANAGER();
|
||||
|
||||
/**
|
||||
* Creates a notification with the given parameters
|
||||
* @param aKey is a unique key for the notification, this allows removing
|
||||
* @param aTitle is the displayed title for the event
|
||||
* @param aDescription is the text that displays underneath the title and has slightly more info
|
||||
* them later programtically in case a notificaiton is no logner required
|
||||
* @param aHref is link to external or internal content
|
||||
*/
|
||||
void Create( const wxString& aKey, const wxString& aTitle, const wxString& aDescription,
|
||||
const wxString& aHref = wxEmptyString );
|
||||
|
||||
/**
|
||||
* Remove a notification by key
|
||||
*
|
||||
* @param aKey is the unique key to locate
|
||||
*/
|
||||
void Remove( const wxString& aKey );
|
||||
|
||||
/**
|
||||
* Loads notifications stored from disk
|
||||
*/
|
||||
void Load();
|
||||
|
||||
/**
|
||||
* Saves notifications to disk
|
||||
*/
|
||||
void Save();
|
||||
|
||||
/**
|
||||
* Shows the notification list
|
||||
*/
|
||||
void ShowList( wxWindow* aParent, wxPoint aPos );
|
||||
|
||||
/**
|
||||
* Add a status bar for handling
|
||||
*/
|
||||
void RegisterStatusBar( KISTATUSBAR* aStatusBar );
|
||||
|
||||
/**
|
||||
* Removes status bar from handling
|
||||
*/
|
||||
void UnregisterStatusBar( KISTATUSBAR* aStatusBar );
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handles removing the shown list window from our list of shown windows
|
||||
*/
|
||||
void onListWindowClosed( wxCloseEvent& aEvent );
|
||||
|
||||
///< Current stack of notifications
|
||||
std::vector<NOTIFICATION> m_notifications;
|
||||
|
||||
///< Currently shown notification lists
|
||||
std::vector<NOTIFICATIONS_LIST*> m_shownDialogs;
|
||||
|
||||
///< Status bars registered for updates
|
||||
std::vector<KISTATUSBAR*> m_statusBars;
|
||||
|
||||
///< The cached file path to read/write notifications on disk
|
||||
wxFileName m_destFileName;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -48,6 +48,8 @@ class wxMenu;
|
|||
class wxWindow;
|
||||
class wxSplashScreen;
|
||||
|
||||
class BACKGROUND_JOBS_MONITOR;
|
||||
class NOTIFICATIONS_MANAGER;
|
||||
class COMMON_SETTINGS;
|
||||
class SETTINGS_MANAGER;
|
||||
class SCRIPTING;
|
||||
|
@ -136,7 +138,11 @@ public:
|
|||
|
||||
virtual SETTINGS_MANAGER& GetSettingsManager() const { return *m_settings_manager; }
|
||||
|
||||
virtual COMMON_SETTINGS* GetCommonSettings() const;
|
||||
virtual COMMON_SETTINGS* GetCommonSettings() const;
|
||||
|
||||
virtual BACKGROUND_JOBS_MONITOR& GetBackgroundJobMonitor() const { return *m_background_jobs_monitor; }
|
||||
|
||||
virtual NOTIFICATIONS_MANAGER& GetNotificationsManager() const { return *m_notifications_manager; }
|
||||
|
||||
virtual void SetTextEditor( const wxString& aFileName );
|
||||
|
||||
|
@ -396,6 +402,8 @@ protected:
|
|||
|
||||
protected:
|
||||
std::unique_ptr<SETTINGS_MANAGER> m_settings_manager;
|
||||
std::unique_ptr<BACKGROUND_JOBS_MONITOR> m_background_jobs_monitor;
|
||||
std::unique_ptr<NOTIFICATIONS_MANAGER> m_notifications_manager;
|
||||
|
||||
std::unique_ptr<SCRIPTING> m_python_scripting;
|
||||
|
||||
|
|
|
@ -115,6 +115,11 @@ public:
|
|||
m_badgeTextColor = aBadgeTextColor;
|
||||
}
|
||||
|
||||
void SetBitmapCentered( bool aCentered )
|
||||
{
|
||||
m_centerBitmap = aCentered;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setupEvents();
|
||||
|
||||
|
@ -157,6 +162,9 @@ private:
|
|||
|
||||
///< Accept mouse-up as click even if mouse-down happened outside of the control
|
||||
bool m_acceptDraggedInClicks;
|
||||
|
||||
///< Draws bitmap centered in the control
|
||||
bool m_centerBitmap;
|
||||
};
|
||||
|
||||
#endif /*BITMAP_BUTTON_H_*/
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef KISTATUSBAR_H
|
||||
#define KISTATUSBAR_H
|
||||
|
||||
class wxGauge;
|
||||
class wxButton;
|
||||
class wxStaticText;
|
||||
class BITMAP_BUTTON;
|
||||
|
||||
class KISTATUSBAR : public wxStatusBar
|
||||
{
|
||||
public:
|
||||
KISTATUSBAR( int aNumberFields, wxWindow* parent, wxWindowID id );
|
||||
|
||||
public:
|
||||
/**
|
||||
* Shows the background progress bar
|
||||
*/
|
||||
void ShowBackgroundProgressBar( bool aCancellable = false );
|
||||
|
||||
/**
|
||||
* Hides the background progress bar
|
||||
*/
|
||||
void HideBackgroundProgressBar();
|
||||
|
||||
/**
|
||||
* Sets the current progress of the progress bar
|
||||
*/
|
||||
void SetBackgroundProgress( int aAmount );
|
||||
|
||||
/**
|
||||
* Sets the maX progress of the progress bar
|
||||
*/
|
||||
void SetBackgroundProgressMax( int aAmount );
|
||||
|
||||
/**
|
||||
* Sets the status text that displays next to the progress bar
|
||||
*/
|
||||
void SetBackgroundStatusText( const wxString& aTxt );
|
||||
|
||||
/**
|
||||
* Sets the notification count on the notifications button
|
||||
* A value of 0 will hide the count
|
||||
*/
|
||||
void SetNotificationCount( int aCount );
|
||||
|
||||
private:
|
||||
void OnSize( wxSizeEvent& aEvent );
|
||||
void onBackgroundProgressClick( wxMouseEvent& aEvent );
|
||||
void onNotificationsIconClick( wxCommandEvent& aEvent );
|
||||
|
||||
private:
|
||||
wxGauge* m_backgroundProgressBar;
|
||||
wxButton* m_backgroundStopButton;
|
||||
wxStaticText* m_backgroundTxt;
|
||||
BITMAP_BUTTON* m_notificationsButton;
|
||||
int m_normalFieldsCount;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -30,6 +30,7 @@
|
|||
#include "widgets/bitmap_button.h"
|
||||
|
||||
#include <advanced_config.h>
|
||||
#include <background_jobs_monitor.h>
|
||||
#include <bitmaps.h>
|
||||
#include <build_version.h>
|
||||
#include <dialogs/panel_kicad_launcher.h>
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include <kiway.h>
|
||||
#include <kiway_express.h>
|
||||
#include <launch_ext.h>
|
||||
#include <notifications_manager.h>
|
||||
#include <reporter.h>
|
||||
#include <project/project_local_settings.h>
|
||||
#include <sch_file_versions.h>
|
||||
|
@ -57,6 +59,7 @@
|
|||
#include <tools/kicad_manager_control.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
#include <widgets/app_progress_dialog.h>
|
||||
#include <widgets/kistatusbar.h>
|
||||
#include <wx/ffile.h>
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/dcclient.h>
|
||||
|
@ -134,7 +137,9 @@ KICAD_MANAGER_FRAME::KICAD_MANAGER_FRAME( wxWindow* parent, const wxString& titl
|
|||
|
||||
// Create the status line (bottom of the frame). Left half is for project name; right half
|
||||
// is for Reporter (currently used by archiver/unarchiver and PCM).
|
||||
CreateStatusBar( 2 );
|
||||
CreateStatusBar( 3 );
|
||||
Pgm().GetBackgroundJobMonitor().RegisterStatusBar( (KISTATUSBAR*) GetStatusBar() );
|
||||
Pgm().GetNotificationsManager().RegisterStatusBar( (KISTATUSBAR*) GetStatusBar() );
|
||||
GetStatusBar()->SetFont( KIUI::GetStatusFont( this ) );
|
||||
|
||||
// Give an icon
|
||||
|
@ -246,6 +251,9 @@ KICAD_MANAGER_FRAME::KICAD_MANAGER_FRAME( wxWindow* parent, const wxString& titl
|
|||
|
||||
KICAD_MANAGER_FRAME::~KICAD_MANAGER_FRAME()
|
||||
{
|
||||
Pgm().GetBackgroundJobMonitor().UnregisterStatusBar( (KISTATUSBAR*) GetStatusBar() );
|
||||
Pgm().GetNotificationsManager().UnregisterStatusBar( (KISTATUSBAR*) GetStatusBar() );
|
||||
|
||||
// Shutdown all running tools
|
||||
if( m_toolManager )
|
||||
m_toolManager->ShutdownAllTools();
|
||||
|
@ -261,6 +269,13 @@ KICAD_MANAGER_FRAME::~KICAD_MANAGER_FRAME()
|
|||
}
|
||||
|
||||
|
||||
wxStatusBar* KICAD_MANAGER_FRAME::OnCreateStatusBar( int number, long style, wxWindowID id,
|
||||
const wxString& name )
|
||||
{
|
||||
return new KISTATUSBAR( number, this, id );
|
||||
}
|
||||
|
||||
|
||||
void KICAD_MANAGER_FRAME::CreatePCM()
|
||||
{
|
||||
// creates the PLUGIN_CONTENT_MANAGER, if not exists
|
||||
|
@ -271,20 +286,26 @@ void KICAD_MANAGER_FRAME::CreatePCM()
|
|||
[this]( int aUpdateCount )
|
||||
{
|
||||
m_pcmUpdateCount = aUpdateCount;
|
||||
|
||||
if( aUpdateCount > 0 )
|
||||
{
|
||||
Pgm().GetNotificationsManager().Create(
|
||||
wxS( "pcm" ),
|
||||
_( "PCM Updates Available" ),
|
||||
wxString::Format( _( "%d package update(s) avaliable" ), aUpdateCount ),
|
||||
wxT( "" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Pgm().GetNotificationsManager().Remove( wxS( "pcm" ) );
|
||||
}
|
||||
|
||||
CallAfter(
|
||||
[this]()
|
||||
{
|
||||
updatePcmButtonBadge();
|
||||
} );
|
||||
},
|
||||
[this]( const wxString aText )
|
||||
{
|
||||
CallAfter(
|
||||
[aText, this]()
|
||||
{
|
||||
SetStatusText( aText, 1 );
|
||||
} );
|
||||
} );
|
||||
});
|
||||
|
||||
m_pcm->SetRepositoryList( kicadSettings()->m_PcmRepositories );
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ public:
|
|||
void OnFileHistory( wxCommandEvent& event );
|
||||
void OnClearFileHistory( wxCommandEvent& aEvent );
|
||||
void OnExit( wxCommandEvent& event );
|
||||
wxStatusBar* OnCreateStatusBar( int number, long style, wxWindowID id,
|
||||
const wxString& name ) override;
|
||||
|
||||
void RecreateBaseHToolbar();
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <kicad_curl/kicad_curl.h>
|
||||
|
||||
#include "core/wx_stl_compat.h"
|
||||
#include <background_jobs_monitor.h>
|
||||
#include "build_version.h"
|
||||
#include "paths.h"
|
||||
#include "pcm.h"
|
||||
|
@ -59,47 +60,10 @@ class THROWING_ERROR_HANDLER : public nlohmann::json_schema::error_handler
|
|||
};
|
||||
|
||||
|
||||
class STATUS_TEXT_REPORTER : public PROGRESS_REPORTER_BASE
|
||||
{
|
||||
public:
|
||||
STATUS_TEXT_REPORTER( std::function<void( const wxString )> aStatusCallback ) :
|
||||
PROGRESS_REPORTER_BASE( 1 ), m_statusCallback( aStatusCallback )
|
||||
{
|
||||
}
|
||||
|
||||
void SetTitle( const wxString& aTitle ) override
|
||||
{
|
||||
m_title = aTitle;
|
||||
m_report = wxT( "" );
|
||||
}
|
||||
|
||||
void Report( const wxString& aMessage ) override
|
||||
{
|
||||
m_report = wxString::Format( wxT( ": %s" ), aMessage );
|
||||
}
|
||||
|
||||
void Cancel() { m_cancelled.store( true ); }
|
||||
|
||||
private:
|
||||
bool updateUI() override
|
||||
{
|
||||
m_statusCallback( wxString::Format( wxT( "%s%s" ), m_title, m_report ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::function<void( const wxString )> m_statusCallback;
|
||||
|
||||
wxString m_title;
|
||||
wxString m_report;
|
||||
};
|
||||
|
||||
|
||||
PLUGIN_CONTENT_MANAGER::PLUGIN_CONTENT_MANAGER(
|
||||
std::function<void( int )> aAvailableUpdateCallback,
|
||||
std::function<void( const wxString )> aStatusCallback ) :
|
||||
std::function<void( int )> aAvailableUpdateCallback ) :
|
||||
m_dialog( nullptr ),
|
||||
m_availableUpdateCallback( aAvailableUpdateCallback ),
|
||||
m_statusCallback( aStatusCallback )
|
||||
m_availableUpdateCallback( aAvailableUpdateCallback )
|
||||
{
|
||||
ReadEnvVar();
|
||||
|
||||
|
@ -437,7 +401,7 @@ bool PLUGIN_CONTENT_MANAGER::CacheRepository( const wxString& aRepositoryId )
|
|||
if( m_dialog )
|
||||
reporter = std::make_shared<WX_PROGRESS_REPORTER>( m_dialog, wxEmptyString, 1 );
|
||||
else
|
||||
reporter = m_statusReporter;
|
||||
reporter = m_updateBackgroundJob->m_reporter;
|
||||
|
||||
if( !FetchRepository( url, current_repo, reporter.get() ) )
|
||||
return false;
|
||||
|
@ -1136,7 +1100,8 @@ void PLUGIN_CONTENT_MANAGER::RunBackgroundUpdate()
|
|||
if( m_updateThread.joinable() )
|
||||
return;
|
||||
|
||||
m_statusReporter = std::make_shared<STATUS_TEXT_REPORTER>( m_statusCallback );
|
||||
|
||||
m_updateBackgroundJob = Pgm().GetBackgroundJobMonitor().Create( _( "PCM Update" ) );
|
||||
|
||||
m_updateThread = std::thread(
|
||||
[this]()
|
||||
|
@ -1144,6 +1109,10 @@ void PLUGIN_CONTENT_MANAGER::RunBackgroundUpdate()
|
|||
if( m_installed.size() == 0 )
|
||||
return;
|
||||
|
||||
int maxProgress = m_repository_list.size() + m_installed.size();
|
||||
m_updateBackgroundJob->m_reporter->SetNumPhases( maxProgress );
|
||||
m_updateBackgroundJob->m_reporter->Report( _( "Preparing to fetch repositories" ) );
|
||||
|
||||
// Only fetch repositories that have installed not pinned packages
|
||||
std::unordered_set<wxString> repo_ids;
|
||||
|
||||
|
@ -1155,25 +1124,31 @@ void PLUGIN_CONTENT_MANAGER::RunBackgroundUpdate()
|
|||
|
||||
for( const auto& [ repository_id, name, url ] : m_repository_list )
|
||||
{
|
||||
m_updateBackgroundJob->m_reporter->AdvancePhase();
|
||||
if( repo_ids.count( repository_id ) == 0 )
|
||||
continue;
|
||||
|
||||
m_updateBackgroundJob->m_reporter->Report(
|
||||
_( "Fetching repository..." ) );
|
||||
CacheRepository( repository_id );
|
||||
|
||||
if( m_statusReporter->IsCancelled() )
|
||||
if( m_updateBackgroundJob->m_reporter->IsCancelled() )
|
||||
break;
|
||||
}
|
||||
|
||||
if( m_statusReporter->IsCancelled() )
|
||||
if( m_updateBackgroundJob->m_reporter->IsCancelled() )
|
||||
return;
|
||||
|
||||
// Count packages with updates
|
||||
int availableUpdateCount = 0;
|
||||
|
||||
m_updateBackgroundJob->m_reporter->Report( _( "Reviewing packages..." ) );
|
||||
for( std::pair<const wxString, PCM_INSTALLATION_ENTRY>& pair : m_installed )
|
||||
{
|
||||
PCM_INSTALLATION_ENTRY& entry = pair.second;
|
||||
|
||||
m_updateBackgroundJob->m_reporter->AdvancePhase();
|
||||
|
||||
if( m_repository_cache.find( entry.repository_id ) != m_repository_cache.end() )
|
||||
{
|
||||
PCM_PACKAGE_STATE state = GetPackageState( entry.repository_id,
|
||||
|
@ -1183,15 +1158,15 @@ void PLUGIN_CONTENT_MANAGER::RunBackgroundUpdate()
|
|||
availableUpdateCount++;
|
||||
}
|
||||
|
||||
if( m_statusReporter->IsCancelled() )
|
||||
if( m_updateBackgroundJob->m_reporter->IsCancelled() )
|
||||
return;
|
||||
}
|
||||
|
||||
Pgm().GetBackgroundJobMonitor().Remove( m_updateBackgroundJob );
|
||||
m_updateBackgroundJob = nullptr;
|
||||
|
||||
// Update the badge on PCM button
|
||||
m_availableUpdateCallback( availableUpdateCount );
|
||||
|
||||
m_statusCallback( availableUpdateCount > 0 ? _( "Package updates are available" )
|
||||
: _( "No package updates available" ) );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -1200,7 +1175,8 @@ void PLUGIN_CONTENT_MANAGER::StopBackgroundUpdate()
|
|||
{
|
||||
if( m_updateThread.joinable() )
|
||||
{
|
||||
m_statusReporter->Cancel();
|
||||
if( m_updateBackgroundJob )
|
||||
m_updateBackgroundJob->m_reporter->Cancel();
|
||||
m_updateThread.join();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ typedef std::vector<std::pair<wxString, wxString>> STRING_PAIR_LIST;
|
|||
typedef std::vector<std::tuple<wxString, wxString, wxString>> STRING_TUPLE_LIST;
|
||||
|
||||
|
||||
class STATUS_TEXT_REPORTER;
|
||||
struct BACKGROUND_JOB;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -101,8 +101,7 @@ class STATUS_TEXT_REPORTER;
|
|||
class PLUGIN_CONTENT_MANAGER
|
||||
{
|
||||
public:
|
||||
PLUGIN_CONTENT_MANAGER( std::function<void( int )> aAvailableUpdateCallback,
|
||||
std::function<void( const wxString )> aStatusCallback );
|
||||
PLUGIN_CONTENT_MANAGER( std::function<void( int )> aAvailableUpdateCallbac );
|
||||
~PLUGIN_CONTENT_MANAGER();
|
||||
|
||||
/**
|
||||
|
@ -400,10 +399,9 @@ private:
|
|||
std::map<wxString, PCM_INSTALLATION_ENTRY> m_installed;
|
||||
const static std::tuple<int, int, int> m_kicad_version;
|
||||
std::function<void( int )> m_availableUpdateCallback;
|
||||
std::function<void( const wxString )> m_statusCallback;
|
||||
std::thread m_updateThread;
|
||||
|
||||
std::shared_ptr<STATUS_TEXT_REPORTER> m_statusReporter;
|
||||
BACKGROUND_JOB* m_updateBackgroundJob;
|
||||
};
|
||||
|
||||
#endif // PCM_H_
|
||||
|
|
|
@ -87,6 +87,7 @@ set( BMAPS_SMALL
|
|||
label_align_top
|
||||
label_align_bottom
|
||||
list_nets_16
|
||||
notifications
|
||||
options_generic_16
|
||||
pinorient_right
|
||||
pinorient_left
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 587 B |
Binary file not shown.
After Width: | Height: | Size: 639 B |
|
@ -0,0 +1,104 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Слой_1"
|
||||
data-name="Слой 1"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
sodipodi:docname="notifications.svg"
|
||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||
inkscape:export-filename="..\..\png\notifications_dark_16.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1027"
|
||||
id="namedview30"
|
||||
showgrid="true"
|
||||
inkscape:zoom="45.254834"
|
||||
inkscape:cx="7.7892231"
|
||||
inkscape:cy="7.8334173"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:current-layer="Слой_1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid_kicad"
|
||||
spacingx="0.5"
|
||||
spacingy="0.5"
|
||||
color="#9999ff"
|
||||
opacity="0.13"
|
||||
empspacing="2"
|
||||
originx="-2.0583608"
|
||||
originy="-0.53240496"
|
||||
units="px"
|
||||
visible="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata43">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>drc</dc:title>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs116686">
|
||||
<style
|
||||
id="style116684">.cls-1{fill:#b9b9b9;}.cls-2{fill:#fff;}.cls-2,.cls-3{stroke:#545454;}.cls-2,.cls-3,.cls-4{stroke-linecap:round;stroke-linejoin:round;}.cls-3,.cls-4{fill:none;}.cls-4{stroke:#1a81c4;}.cls-5{fill:#545454;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title116688">drc</title>
|
||||
<path
|
||||
style="fill:#545454;stroke:#ded3dd;stroke-width:1.47184;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
|
||||
d="m 2.5086208,11.299302 10.6831142,0.02238 -1.22801,-1.35504 V 5.8373277 c 0,0 0.266307,-4.2714869 -4.2889251,-4.2669535 -4.5880423,0.00455 -4.2889256,4.2669535 -4.2889256,4.2669535 V 9.966639 Z"
|
||||
id="path5"
|
||||
sodipodi:nodetypes="ccccsccc" />
|
||||
<path
|
||||
style="fill:#545454;stroke:#ded3dd;stroke-width:0.826064;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
|
||||
d="m 6.1082082,11.739465 3.7817331,0.03146 c 0,0 -0.0059,1.308909 -1.8831818,1.312612 -1.8676305,0.0037 -1.8985513,-1.34406 -1.8985513,-1.34406 z"
|
||||
id="path7"
|
||||
sodipodi:nodetypes="ccsc" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,104 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Слой_1"
|
||||
data-name="Слой 1"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
sodipodi:docname="notifications.svg"
|
||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||
inkscape:export-filename="..\..\png\notifications_16.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1027"
|
||||
id="namedview30"
|
||||
showgrid="false"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="5.734375"
|
||||
inkscape:cy="9.953125"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:current-layer="Слой_1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid_kicad"
|
||||
spacingx="0.5"
|
||||
spacingy="0.5"
|
||||
color="#9999ff"
|
||||
opacity="0.13"
|
||||
empspacing="2"
|
||||
originx="-2.0583608"
|
||||
originy="-0.53240496"
|
||||
units="px"
|
||||
visible="false" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata43">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>drc</dc:title>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs116686">
|
||||
<style
|
||||
id="style116684">.cls-1{fill:#b9b9b9;}.cls-2{fill:#fff;}.cls-2,.cls-3{stroke:#545454;}.cls-2,.cls-3,.cls-4{stroke-linecap:round;stroke-linejoin:round;}.cls-3,.cls-4{fill:none;}.cls-4{stroke:#1a81c4;}.cls-5{fill:#545454;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title116688">drc</title>
|
||||
<path
|
||||
style="fill:none;stroke:#545454;stroke-width:1.47184;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 2.5086208,11.299302 10.6831142,0.02238 -1.22801,-1.35504 V 5.8373277 c 0,0 0.266307,-4.2714869 -4.2889251,-4.2669535 -4.5880423,0.00455 -4.2889256,4.2669535 -4.2889256,4.2669535 V 9.966639 Z"
|
||||
id="path5"
|
||||
sodipodi:nodetypes="ccccsccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#545454;stroke-width:0.826064;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 6.1082082,11.739465 3.7817331,0.03146 c 0,0 -0.0059,1.308909 -1.8831818,1.312612 -1.8676305,0.0037 -1.8985513,-1.34406 -1.8985513,-1.34406 z"
|
||||
id="path7"
|
||||
sodipodi:nodetypes="ccsc" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
Loading…
Reference in New Issue