diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 85255eca1a..892c5a4333 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -211,6 +211,7 @@ set( COMMON_WIDGET_SRCS widgets/wx_busy_indicator.cpp widgets/wx_grid.cpp widgets/wx_angle_text.cpp + widgets/wx_infobar.cpp ) set( COMMON_PAGE_LAYOUT_SRCS diff --git a/common/eda_draw_frame.cpp b/common/eda_draw_frame.cpp index 94d5c2bdb9..ba2f1a7fb1 100644 --- a/common/eda_draw_frame.cpp +++ b/common/eda_draw_frame.cpp @@ -80,6 +80,7 @@ EDA_DRAW_FRAME::EDA_DRAW_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrame m_canvas = NULL; m_toolDispatcher = NULL; m_messagePanel = NULL; + m_infoBar = nullptr; m_currentScreen = NULL; m_showBorderAndTitleBlock = false; // true to display reference sheet. m_LastGridSizeId = 0; diff --git a/common/widgets/wx_infobar.cpp b/common/widgets/wx_infobar.cpp new file mode 100644 index 0000000000..e5629e0c7c --- /dev/null +++ b/common/widgets/wx_infobar.cpp @@ -0,0 +1,136 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 Ian McInerney + * Copyright (C) 2020 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 3 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, see . + */ + +#include +#include +#include +#include + + +BEGIN_EVENT_TABLE( WX_INFOBAR, wxInfoBarGeneric ) + EVT_BUTTON( ID_CLOSE_INFOBAR, WX_INFOBAR::OnCloseButton ) +END_EVENT_TABLE() + + +WX_INFOBAR::WX_INFOBAR( wxWindow* aParent, wxAuiManager *aMgr, wxWindowID aWinid ) + : wxInfoBarGeneric( aParent, aWinid ), + m_auiManager( aMgr ) +{ + // On GTK, the infobar seems to start too small, so increase its height +#ifdef __WXGTK__ + int sx, sy; + GetSize( &sx, &sy ); + SetSize( sx, 1.5 * sy ); +#endif +} + + +void WX_INFOBAR::ShowMessage( const wxString& aMessage, int aFlags ) +{ + wxInfoBarGeneric::ShowMessage( aMessage, aFlags ); + + UpdateAuiLayout( true ); +} + + +void WX_INFOBAR::Dismiss() +{ + wxInfoBarGeneric::Dismiss(); + UpdateAuiLayout( false ); +} + + +void WX_INFOBAR::UpdateAuiLayout( bool aShow ) +{ + // Update the AUI pane that contains the infobar + if( m_auiManager ) + { + if( aShow ) + m_auiManager->GetPane( this ).Show(); + else + m_auiManager->GetPane( this ).Hide(); + + m_auiManager->Update(); + } +} + + +void WX_INFOBAR::AddButton( wxWindowID aId, const wxString& aLabel ) +{ + wxButton* button = new wxButton( this, aId, aLabel ); + + AddButton( button ); +} + + +void WX_INFOBAR::AddButton( wxButton* aButton ) +{ + wxSizer* sizer = GetSizer(); + +#ifdef __WXMAC__ + // Based on the code in the original class: + // smaller buttons look better in the (narrow) info bar under OS X + aButton->SetWindowVariant( wxWINDOW_VARIANT_SMALL ); +#endif // __WXMAC__ + sizer->Add( aButton, wxSizerFlags().Centre() ); + + if( IsShown() ) + sizer->Layout(); +} + + +void WX_INFOBAR::AddCloseButton( const wxString& aTooltip ) +{ + wxBitmapButton* button = wxBitmapButton::NewCloseButton( this, ID_CLOSE_INFOBAR ); + + button->SetToolTip( aTooltip ); + + AddButton( button ); +} + + +void WX_INFOBAR::RemoveAllButtons() +{ + wxSizer* sizer = GetSizer(); + + if( sizer->GetItemCount() == 0 ) + return; + + // The last item is already the spacer + if( sizer->GetItem( sizer->GetItemCount() - 1 )->IsSpacer() ) + return; + + for( int i = sizer->GetItemCount() - 1; i >= 0; i-- ) + { + wxSizerItem* sItem = sizer->GetItem( i ); + + // The spacer is the end of the custom buttons + if( sItem->IsSpacer() ) + break; + + delete sItem->GetWindow(); + } +} + + +void WX_INFOBAR::OnCloseButton( wxCommandEvent& aEvt ) +{ + Dismiss(); +} diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index f1b82451db..9e35390743 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -55,6 +55,7 @@ #include #include #include +#include bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName, @@ -493,6 +494,15 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in UpdateTitle(); + wxFileName fn = Prj().AbsolutePath( GetScreen()->GetFileName() ); + + if( fn.FileExists() && !fn.IsFileWritable() ) + { + m_infoBar->RemoveAllButtons(); + m_infoBar->AddCloseButton(); + m_infoBar->ShowMessage( "Schematic file is read only.", wxICON_WARNING ); + } + // If requested, generate a netlist and exit immediately. // NOTE: This is intended as a developer-only feature for now, and can be removed in lieu of // Python scripting once that is possible. diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 0effab9165..2418798546 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -239,6 +240,8 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ): ReCreateVToolbar(); ReCreateOptToolbar(); + m_infoBar = new WX_INFOBAR( this, &m_auimgr ); + // Initialize common print setup dialog settings. m_pageSetupData.GetPrintData().SetPrintMode( wxPRINT_MODE_PRINTER ); m_pageSetupData.GetPrintData().SetQuality( wxPRINT_QUALITY_MEDIUM ); @@ -247,11 +250,21 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ): m_auimgr.SetManagedWindow( this ); - m_auimgr.AddPane( m_mainToolBar, EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) ); - m_auimgr.AddPane( m_optionsToolBar, EDA_PANE().VToolbar().Name( "OptToolbar" ).Left().Layer(3) ); - m_auimgr.AddPane( m_drawToolBar, EDA_PANE().VToolbar().Name( "ToolsToolbar" ).Right().Layer(1) ); - m_auimgr.AddPane( GetCanvas(), EDA_PANE().Canvas().Name( "DrawFrame" ).Center() ); - m_auimgr.AddPane( m_messagePanel, EDA_PANE().Messages().Name( "MsgPanel" ).Bottom().Layer(6) ); + m_auimgr.AddPane( m_mainToolBar, + EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) ); + m_auimgr.AddPane( m_optionsToolBar, + EDA_PANE().VToolbar().Name( "OptToolbar" ).Left().Layer(3) ); + m_auimgr.AddPane( m_drawToolBar, + EDA_PANE().VToolbar().Name( "ToolsToolbar" ).Right().Layer(2) ); + m_auimgr.AddPane( m_infoBar, + EDA_PANE().InfoBar().Name( "InfoBar" ).Top().Layer(1) ); + m_auimgr.AddPane( GetCanvas(), + EDA_PANE().Canvas().Name( "DrawFrame" ).Center() ); + m_auimgr.AddPane( m_messagePanel, + EDA_PANE().Messages().Name( "MsgPanel" ).Bottom().Layer(6) ); + + // We don't want the infobar displayed right away + m_auimgr.GetPane( "InfoBar" ).Hide(); m_auimgr.Update(); diff --git a/include/eda_base_frame.h b/include/eda_base_frame.h index eb5a37b64e..1c7bc1de8e 100644 --- a/include/eda_base_frame.h +++ b/include/eda_base_frame.h @@ -616,6 +616,17 @@ public: Resizable( true ); // expand to fit available space return *this; } + + /** + * Turn *this into a infobar for KiCad. + */ + EDA_PANE& InfoBar() + { + CaptionVisible( false ); + Movable( false ); + Resizable( true ); + return *this; + } }; #endif // EDA_BASE_FRAME_H_ diff --git a/include/eda_draw_frame.h b/include/eda_draw_frame.h index 2878e26585..1df10b119f 100644 --- a/include/eda_draw_frame.h +++ b/include/eda_draw_frame.h @@ -40,6 +40,7 @@ class ACTION_TOOLBAR; class COLOR_SETTINGS; class TOOL_MENU; class APP_SETTINGS_BASE; +class WX_INFOBAR; namespace KIGFX { @@ -113,6 +114,8 @@ protected: EDA_MSG_PANEL* m_messagePanel; int m_MsgFrameHeight; + WX_INFOBAR* m_infoBar; + COLOR_SETTINGS* m_colorSettings; /// The current canvas type diff --git a/include/id.h b/include/id.h index 019a1237cb..4f31b15ba1 100644 --- a/include/id.h +++ b/include/id.h @@ -113,6 +113,9 @@ enum main_id ID_OPT_TOOLBAR, ID_AUX_TOOLBAR, + // ID for the close button on the frame's infobar + ID_CLOSE_INFOBAR, + ID_EDIT_HOTKEY, ID_NO_TOOL_SELECTED, diff --git a/include/widgets/wx_infobar.h b/include/widgets/wx_infobar.h new file mode 100644 index 0000000000..642f9f2d38 --- /dev/null +++ b/include/widgets/wx_infobar.h @@ -0,0 +1,107 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 Ian McInerney + * Copyright (C) 2020 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 3 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, see . + */ + +#include +#include + +class wxAuiManager; + + +/** + * A modified version of the wxInfoBar class that allows us to: + * * Show the close button along with the other buttons + * * Remove all user-provided buttons at once + * + * This inherits from the generic infobar because the native infobar + * on GTK doesn't include the icon on the left and it looks worse. + */ +class WX_INFOBAR : public wxInfoBarGeneric +{ +public: + /** + * Construct an infobar that can exist inside an AUI managed frame. + * + * @param aParent is the parent + * @param aMgr is the AUI manager that this infobar is added to + * @param aWinId is the ID for this infobar object + */ + WX_INFOBAR( wxWindow* aParent, wxAuiManager* aMgr = nullptr, wxWindowID aWinid = wxID_ANY ); + + /** + * Add the default close button to the infobar on the right side. + * + * @param aTooltip is the tooltip to give the close button + */ + void AddCloseButton( const wxString& aTooltip = _( "Hide this message." ) ); + + /** + * Add an already created button to the infobar. + * New buttons are added in the right-most position. + * + * @param aButton is the button to add + */ + void AddButton( wxButton* aButton ); + + /** + * Add a button with the provided ID and text. + * The new button is created on the right-most positon. + * + * @param aId is the ID to assign to the button + * @param aLabel is the text for the button + */ + void AddButton( wxWindowID aId, const wxString& aLabel = wxEmptyString ) override; + + /** + * Remove all the buttons that have been added by the user. + */ + void RemoveAllButtons(); + + /** + * Show the info bar with the provided message and icon. + * + * @param aMessage is the message to display + * @param aFlags is the flag containing the icon to display on the left side of the infobar + */ + void ShowMessage( const wxString& aMessage, int aFlags = wxICON_INFORMATION ) override; + + /** + * Dismisses the infobar and updates the containing layout and AUI manager + * (if one is provided). + */ + void Dismiss() override; + +protected: + /** + * Event handler for the close button. + * This is bound to ID_CLOSE_INFOBAR on the infobar. + */ + void OnCloseButton( wxCommandEvent& aEvt ); + + /** + * Update the AUI pane to show or hide this infobar. + * + * @param aShow is true to show the pane + */ + void UpdateAuiLayout( bool aShow ); + + wxAuiManager* m_auiManager; + + DECLARE_EVENT_TABLE() +}; diff --git a/pagelayout_editor/files.cpp b/pagelayout_editor/files.cpp index 669a239677..b43688fc90 100644 --- a/pagelayout_editor/files.cpp +++ b/pagelayout_editor/files.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -228,6 +229,16 @@ bool PL_EDITOR_FRAME::LoadPageLayoutDescrFile( const wxString& aFullFileName ) SetCurrentFileName( aFullFileName ); UpdateFileHistory( aFullFileName ); GetScreen()->ClrModify(); + + wxFileName fn = aFullFileName; + + if( fn.FileExists() && !fn.IsFileWritable() ) + { + m_infoBar->RemoveAllButtons(); + m_infoBar->AddCloseButton(); + m_infoBar->ShowMessage( "Layout file is read only.", wxICON_WARNING ); + } + return true; } diff --git a/pagelayout_editor/pl_editor_frame.cpp b/pagelayout_editor/pl_editor_frame.cpp index 178af7e126..f777176951 100644 --- a/pagelayout_editor/pl_editor_frame.cpp +++ b/pagelayout_editor/pl_editor_frame.cpp @@ -56,6 +56,7 @@ #include #include #include +#include BEGIN_EVENT_TABLE( PL_EDITOR_FRAME, EDA_DRAW_FRAME ) EVT_CLOSE( PL_EDITOR_FRAME::OnCloseWindow ) @@ -124,6 +125,8 @@ PL_EDITOR_FRAME::PL_EDITOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) : ReCreateHToolbar(); ReCreateVToolbar(); + m_infoBar = new WX_INFOBAR( this, &m_auimgr ); + wxWindow* stsbar = GetStatusBar(); int dims[] = { @@ -159,13 +162,18 @@ PL_EDITOR_FRAME::PL_EDITOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_propertiesPagelayout = new PROPERTIES_FRAME( this ); // Horizontal items; layers 4 - 6 - m_auimgr.AddPane( m_mainToolBar, EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) ); - m_auimgr.AddPane( m_messagePanel, EDA_PANE().Messages().Name( "MsgPanel" ).Bottom().Layer(6) ); + m_auimgr.AddPane( m_mainToolBar, + EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) ); + m_auimgr.AddPane( m_messagePanel, + EDA_PANE().Messages().Name( "MsgPanel" ).Bottom().Layer(6) ); + m_auimgr.AddPane( m_infoBar, + EDA_PANE().InfoBar().Name( "InfoBar" ).Top().Layer(1) ); // Vertical items; layers 1 - 3 - m_auimgr.AddPane( m_drawToolBar, EDA_PANE().VToolbar().Name( "ToolsToolbar" ).Right().Layer(1) ); + m_auimgr.AddPane( m_drawToolBar, + EDA_PANE().VToolbar().Name( "ToolsToolbar" ).Right().Layer(2) ); - m_auimgr.AddPane( m_propertiesPagelayout, EDA_PANE().Palette().Name( "Props" ).Right().Layer(2) + m_auimgr.AddPane( m_propertiesPagelayout, EDA_PANE().Palette().Name( "Props" ).Right().Layer(3) .Caption( _( "Properties" ) ).MinSize( m_propertiesPagelayout->GetMinSize() ) .BestSize( m_propertiesFrameWidth, -1 ) ); @@ -174,6 +182,9 @@ PL_EDITOR_FRAME::PL_EDITOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) : GetCanvas()->GetView()->SetScale( GetZoomLevelCoeff() / GetScreen()->GetZoom() ); ActivateGalCanvas(); + // We don't want the infobar displayed right away + m_auimgr.GetPane( "InfoBar" ).Hide(); + m_auimgr.Update(); // Add the exit key handler diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 8cf646c960..47c45d6039 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -87,6 +87,7 @@ #include #include #include // for DIALOG_DRC_WINDOW_NAME definition +#include #if defined(KICAD_SCRIPTING) || defined(KICAD_SCRIPTING_WXPYTHON) #include @@ -222,19 +223,30 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : ReCreateOptToolbar(); ReCreateMicrowaveVToolbar(); + m_infoBar = new WX_INFOBAR( this, &m_auimgr ); + m_auimgr.SetManagedWindow( this ); // Horizontal items; layers 4 - 6 - m_auimgr.AddPane( m_mainToolBar, EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) ); - m_auimgr.AddPane( m_auxiliaryToolBar, EDA_PANE().HToolbar().Name( "AuxToolbar" ).Top().Layer(4) ); - m_auimgr.AddPane( m_messagePanel, EDA_PANE().Messages().Name( "MsgPanel" ).Bottom().Layer(6) ); + m_auimgr.AddPane( m_mainToolBar, + EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) ); + m_auimgr.AddPane( m_auxiliaryToolBar, + EDA_PANE().HToolbar().Name( "AuxToolbar" ).Top().Layer(5) ); + m_auimgr.AddPane( m_messagePanel, + EDA_PANE().Messages().Name( "MsgPanel" ).Bottom().Layer(6) ); + m_auimgr.AddPane( m_infoBar, + EDA_PANE().InfoBar().Name( "InfoBar" ).Top().Layer(1) ); // Vertical items; layers 1 - 3 - m_auimgr.AddPane( m_optionsToolBar, EDA_PANE().VToolbar().Name( "OptToolbar" ).Left().Layer(3) ); + m_auimgr.AddPane( m_optionsToolBar, + EDA_PANE().VToolbar().Name( "OptToolbar" ).Left().Layer(3) ); - m_auimgr.AddPane( m_microWaveToolBar, EDA_PANE().VToolbar().Name( "MicrowaveToolbar" ).Right().Layer(1) ); - m_auimgr.AddPane( m_drawToolBar, EDA_PANE().VToolbar().Name( "ToolsToolbar" ).Right().Layer(2) ); - m_auimgr.AddPane( m_Layers, EDA_PANE().Palette().Name( "LayersManager" ).Right().Layer(3) + m_auimgr.AddPane( m_microWaveToolBar, + EDA_PANE().VToolbar().Name( "MicrowaveToolbar" ).Right().Layer(2) ); + m_auimgr.AddPane( m_drawToolBar, + EDA_PANE().VToolbar().Name( "ToolsToolbar" ).Right().Layer(3) ); + m_auimgr.AddPane( m_Layers, + EDA_PANE().Palette().Name( "LayersManager" ).Right().Layer(4) .Caption( _( "Layers Manager" ) ).PaneBorder( false ) .MinSize( 80, -1 ).BestSize( m_Layers->GetBestSize() ) ); @@ -248,6 +260,9 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : // because contents establish size syncLayerWidgetLayer(); + // We don't want the infobar displayed right away + m_auimgr.GetPane( "InfoBar" ).Hide(); + m_auimgr.Update(); GetToolManager()->RunAction( ACTIONS::gridPreset, true, m_LastGridSizeId ); GetToolManager()->RunAction( ACTIONS::zoomFitScreen, false ); @@ -698,6 +713,16 @@ void PCB_EDIT_FRAME::onBoardLoaded() { UpdateTitle(); + wxFileName fn = GetBoard()->GetFileName(); + + // Display a warning that the file is read only + if( fn.FileExists() && !fn.IsFileWritable() ) + { + m_infoBar->RemoveAllButtons(); + m_infoBar->AddCloseButton(); + m_infoBar->ShowMessage( "Board file is read only.", wxICON_WARNING ); + } + // Re-create layers manager based on layer info in board ReFillLayerWidget(); ReCreateLayerBox();