You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

456 lines
11 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 Ian McInerney <ian.s.mcinerney@ieee.org>
  5. * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <id.h>
  21. #include <kiplatform/ui.h>
  22. #include <widgets/wx_infobar.h>
  23. #include "wx/artprov.h"
  24. #include <wx/aui/framemanager.h>
  25. #include <wx/debug.h>
  26. #include <wx/infobar.h>
  27. #include <wx/sizer.h>
  28. #include <wx/timer.h>
  29. #include <wx/hyperlink.h>
  30. #include <wx/bmpbuttn.h>
  31. #include <eda_base_frame.h>
  32. #ifdef __WXMSW__
  33. #include <dpi_scaling_common.h>
  34. #endif
  35. wxDEFINE_EVENT( KIEVT_SHOW_INFOBAR, wxCommandEvent );
  36. wxDEFINE_EVENT( KIEVT_DISMISS_INFOBAR, wxCommandEvent );
  37. BEGIN_EVENT_TABLE( WX_INFOBAR, wxInfoBarGeneric )
  38. EVT_COMMAND( wxID_ANY, KIEVT_SHOW_INFOBAR, WX_INFOBAR::onShowInfoBar )
  39. EVT_COMMAND( wxID_ANY, KIEVT_DISMISS_INFOBAR, WX_INFOBAR::onDismissInfoBar )
  40. EVT_BUTTON( ID_CLOSE_INFOBAR, WX_INFOBAR::onCloseButton )
  41. EVT_TIMER( ID_CLOSE_INFOBAR, WX_INFOBAR::onTimer )
  42. END_EVENT_TABLE()
  43. WX_INFOBAR::WX_INFOBAR( wxWindow* aParent, wxAuiManager* aMgr, wxWindowID aWinid )
  44. : wxInfoBarGeneric( aParent, aWinid ),
  45. m_showTime( 0 ),
  46. m_updateLock( false ),
  47. m_showTimer( nullptr ),
  48. m_auiManager( aMgr ),
  49. m_type( MESSAGE_TYPE::GENERIC )
  50. {
  51. m_showTimer = new wxTimer( this, ID_CLOSE_INFOBAR );
  52. #ifdef __WXMAC__
  53. // wxWidgets hard-codes wxSYS_COLOUR_INFOBK to { 0xFF, 0xFF, 0xD3 } on Mac.
  54. if( KIPLATFORM::UI::IsDarkTheme() )
  55. SetBackgroundColour( wxColour( 28, 27, 20 ) );
  56. else
  57. SetBackgroundColour( wxColour( 255, 249, 189 ) );
  58. // Infobar is broken on Mac without the effects
  59. SetShowHideEffects( wxSHOW_EFFECT_ROLL_TO_BOTTOM, wxSHOW_EFFECT_ROLL_TO_TOP );
  60. SetEffectDuration( 200 );
  61. #else
  62. // Infobar freezes canvas on Windows with the effect, and GTK looks bad with it
  63. SetShowHideEffects( wxSHOW_EFFECT_NONE, wxSHOW_EFFECT_NONE );
  64. #endif
  65. // The infobar seems to start too small, so increase its height
  66. int sx, sy;
  67. GetSize( &sx, &sy );
  68. sy = 1.5 * sy;
  69. // The bitmap gets cutoff sometimes with the default size, so force it to be the same
  70. // height as the infobar.
  71. wxSizer* sizer = GetSizer();
  72. wxSize iconSize = wxArtProvider::GetSizeHint( wxART_BUTTON );
  73. #ifdef __WXMSW__
  74. DPI_SCALING_COMMON dpi( nullptr, aParent );
  75. iconSize.x *= dpi.GetContentScaleFactor();
  76. sx *= dpi.GetContentScaleFactor();
  77. sy *= dpi.GetContentScaleFactor();
  78. #endif
  79. SetSize( sx, sy );
  80. sizer->SetItemMinSize( (size_t) 0, iconSize.x, sy );
  81. // Forcefully remove all existing buttons added by the wx constructors.
  82. // The default close button doesn't work with the AUI manager update scheme, so this
  83. // ensures any close button displayed is ours.
  84. RemoveAllButtons();
  85. Layout();
  86. m_parent->Bind( wxEVT_SIZE, &WX_INFOBAR::onSize, this );
  87. }
  88. WX_INFOBAR::~WX_INFOBAR()
  89. {
  90. delete m_showTimer;
  91. }
  92. void WX_INFOBAR::SetShowTime( int aTime )
  93. {
  94. m_showTime = aTime;
  95. }
  96. void WX_INFOBAR::QueueShowMessage( const wxString& aMessage, int aFlags )
  97. {
  98. wxCommandEvent* evt = new wxCommandEvent( KIEVT_SHOW_INFOBAR );
  99. evt->SetString( aMessage.c_str() );
  100. evt->SetInt( aFlags );
  101. GetEventHandler()->QueueEvent( evt );
  102. }
  103. void WX_INFOBAR::QueueDismiss()
  104. {
  105. wxCommandEvent* evt = new wxCommandEvent( KIEVT_DISMISS_INFOBAR );
  106. GetEventHandler()->QueueEvent( evt );
  107. }
  108. void WX_INFOBAR::ShowMessageFor( const wxString& aMessage, int aTime, int aFlags,
  109. MESSAGE_TYPE aType )
  110. {
  111. // Don't do anything if we requested the UI update
  112. if( m_updateLock )
  113. return;
  114. m_showTime = aTime;
  115. ShowMessage( aMessage, aFlags );
  116. m_type = aType;
  117. }
  118. void WX_INFOBAR::ShowMessage( const wxString& aMessage, int aFlags )
  119. {
  120. // Don't do anything if we requested the UI update
  121. if( m_updateLock )
  122. return;
  123. m_updateLock = true;
  124. wxString msg = aMessage;
  125. msg.Trim();
  126. wxInfoBarGeneric::ShowMessage( msg, aFlags );
  127. if( m_auiManager )
  128. updateAuiLayout( true );
  129. if( m_showTime > 0 )
  130. m_showTimer->StartOnce( m_showTime );
  131. m_type = MESSAGE_TYPE::GENERIC;
  132. m_updateLock = false;
  133. }
  134. void WX_INFOBAR::ShowMessage( const wxString& aMessage, int aFlags, MESSAGE_TYPE aType )
  135. {
  136. // Don't do anything if we requested the UI update
  137. if( m_updateLock )
  138. return;
  139. ShowMessage( aMessage, aFlags );
  140. m_type = aType;
  141. }
  142. void WX_INFOBAR::Dismiss()
  143. {
  144. if( !IsShownOnScreen() )
  145. return;
  146. // Don't do anything if we requested the UI update
  147. if( m_updateLock )
  148. return;
  149. m_updateLock = true;
  150. wxInfoBarGeneric::Dismiss();
  151. if( m_auiManager )
  152. updateAuiLayout( false );
  153. if( m_callback )
  154. (*m_callback)();
  155. m_updateLock = false;
  156. }
  157. void WX_INFOBAR::onSize( wxSizeEvent& aEvent )
  158. {
  159. int barWidth = GetSize().GetWidth();
  160. // Calculate the horizontal size: because the infobar is shown on top of the draw canvas
  161. // it is adjusted to the canvas width.
  162. // On Mac, the canvas is the parent
  163. // On other OS the parent is EDA_BASE_FRAME that contains the canvas
  164. int parentWidth = m_parent->GetClientSize().GetWidth();
  165. EDA_BASE_FRAME* frame = dynamic_cast<EDA_BASE_FRAME*>( m_parent );
  166. if( frame && frame->GetToolCanvas() )
  167. parentWidth = frame->GetToolCanvas()->GetSize().GetWidth();
  168. if( barWidth != parentWidth )
  169. SetSize( parentWidth, GetSize().GetHeight() );
  170. aEvent.Skip();
  171. }
  172. void WX_INFOBAR::updateAuiLayout( bool aShow )
  173. {
  174. wxASSERT( m_auiManager );
  175. wxAuiPaneInfo& pane = m_auiManager->GetPane( this );
  176. // If the infobar is in a pane, then show/hide the pane
  177. if( pane.IsOk() )
  178. {
  179. if( aShow )
  180. pane.Show();
  181. else
  182. pane.Hide();
  183. }
  184. // Update the AUI manager regardless
  185. m_auiManager->Update();
  186. }
  187. void WX_INFOBAR::AddButton( wxWindowID aId, const wxString& aLabel )
  188. {
  189. wxButton* button = new wxButton( this, aId, aLabel );
  190. AddButton( button );
  191. }
  192. void WX_INFOBAR::AddButton( wxButton* aButton )
  193. {
  194. wxSizer* sizer = GetSizer();
  195. wxASSERT( aButton );
  196. #ifdef __WXMAC__
  197. // Based on the code in the original class:
  198. // smaller buttons look better in the (narrow) info bar under OS X
  199. aButton->SetWindowVariant( wxWINDOW_VARIANT_SMALL );
  200. #endif // __WXMAC__
  201. sizer->Add( aButton, wxSizerFlags().Centre().Border( wxRIGHT ) );
  202. if( IsShownOnScreen() )
  203. sizer->Layout();
  204. }
  205. void WX_INFOBAR::AddButton( wxHyperlinkCtrl* aHypertextButton )
  206. {
  207. wxSizer* sizer = GetSizer();
  208. wxASSERT( aHypertextButton );
  209. sizer->Add( aHypertextButton, wxSizerFlags().Centre().Border( wxRIGHT ) );
  210. if( IsShownOnScreen() )
  211. sizer->Layout();
  212. }
  213. void WX_INFOBAR::AddCloseButton( const wxString& aTooltip )
  214. {
  215. wxBitmapButton* button = wxBitmapButton::NewCloseButton( this, ID_CLOSE_INFOBAR );
  216. button->SetToolTip( aTooltip );
  217. AddButton( button );
  218. }
  219. void WX_INFOBAR::RemoveAllButtons()
  220. {
  221. wxSizer* sizer = GetSizer();
  222. if( sizer->GetItemCount() == 0 )
  223. return;
  224. // The last item is already the spacer
  225. if( sizer->GetItem( sizer->GetItemCount() - 1 )->IsSpacer() )
  226. return;
  227. for( int i = sizer->GetItemCount() - 1; i >= 0; i-- )
  228. {
  229. wxSizerItem* sItem = sizer->GetItem( i );
  230. // The spacer is the end of the custom buttons
  231. if( sItem->IsSpacer() )
  232. break;
  233. delete sItem->GetWindow();
  234. }
  235. }
  236. bool WX_INFOBAR::HasCloseButton() const
  237. {
  238. wxSizer* sizer = GetSizer();
  239. if( sizer->GetItemCount() == 0 )
  240. return false;
  241. if( sizer->GetItem( sizer->GetItemCount() - 1 )->IsSpacer() )
  242. return false;
  243. wxSizerItem* item = sizer->GetItem( sizer->GetItemCount() - 1 );
  244. return ( item->GetWindow()->GetId() == ID_CLOSE_INFOBAR );
  245. }
  246. void WX_INFOBAR::onShowInfoBar( wxCommandEvent& aEvent )
  247. {
  248. RemoveAllButtons();
  249. AddCloseButton();
  250. ShowMessage( aEvent.GetString(), aEvent.GetInt() );
  251. }
  252. void WX_INFOBAR::onDismissInfoBar( wxCommandEvent& aEvent )
  253. {
  254. Dismiss();
  255. }
  256. void WX_INFOBAR::onCloseButton( wxCommandEvent& aEvent )
  257. {
  258. Dismiss();
  259. }
  260. void WX_INFOBAR::onTimer( wxTimerEvent& aEvent )
  261. {
  262. // Reset and clear the timer
  263. m_showTimer->Stop();
  264. m_showTime = 0;
  265. Dismiss();
  266. }
  267. EDA_INFOBAR_PANEL::EDA_INFOBAR_PANEL( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos,
  268. const wxSize& aSize, long aStyle, const wxString& aName )
  269. : wxPanel( aParent, aId, aPos, aSize, aStyle, aName )
  270. {
  271. m_mainSizer = new wxFlexGridSizer( 1, 0, 0 );
  272. m_mainSizer->SetFlexibleDirection( wxBOTH );
  273. m_mainSizer->AddGrowableCol( 0, 1 );
  274. SetSizer( m_mainSizer );
  275. }
  276. void EDA_INFOBAR_PANEL::AddInfoBar( WX_INFOBAR* aInfoBar )
  277. {
  278. wxASSERT( aInfoBar );
  279. aInfoBar->Reparent( this );
  280. m_mainSizer->Add( aInfoBar, 1, wxEXPAND, 0 );
  281. m_mainSizer->Layout();
  282. }
  283. void EDA_INFOBAR_PANEL::AddOtherItem( wxWindow* aOtherItem )
  284. {
  285. wxASSERT( aOtherItem );
  286. aOtherItem->Reparent( this );
  287. m_mainSizer->Add( aOtherItem, 1, wxEXPAND, 0 );
  288. m_mainSizer->AddGrowableRow( 1, 1 );
  289. m_mainSizer->Layout();
  290. }
  291. REPORTER& INFOBAR_REPORTER::Report( const wxString& aText, SEVERITY aSeverity )
  292. {
  293. m_message.reset( new wxString( aText ) );
  294. m_severity = aSeverity;
  295. m_messageSet = true;
  296. return *this;
  297. }
  298. bool INFOBAR_REPORTER::HasMessage() const
  299. {
  300. return m_message && !m_message->IsEmpty();
  301. }
  302. void INFOBAR_REPORTER::Finalize()
  303. {
  304. // Don't do anything if no message was ever given
  305. if( !m_infoBar || !m_messageSet )
  306. return;
  307. // Short circuit if the message is empty and it is already hidden
  308. if( !HasMessage() && !m_infoBar->IsShownOnScreen() )
  309. return;
  310. int icon = wxICON_NONE;
  311. switch( m_severity )
  312. {
  313. case RPT_SEVERITY_UNDEFINED: icon = wxICON_INFORMATION; break;
  314. case RPT_SEVERITY_INFO: icon = wxICON_INFORMATION; break;
  315. case RPT_SEVERITY_EXCLUSION: icon = wxICON_WARNING; break;
  316. case RPT_SEVERITY_ACTION: icon = wxICON_WARNING; break;
  317. case RPT_SEVERITY_WARNING: icon = wxICON_WARNING; break;
  318. case RPT_SEVERITY_ERROR: icon = wxICON_ERROR; break;
  319. case RPT_SEVERITY_IGNORE: icon = wxICON_INFORMATION; break;
  320. case RPT_SEVERITY_DEBUG: icon = wxICON_INFORMATION; break;
  321. }
  322. if( m_message->EndsWith( wxS( "\n" ) ) )
  323. *m_message = m_message->Left( m_message->Length() - 1 );
  324. if( HasMessage() )
  325. m_infoBar->QueueShowMessage( *m_message, icon );
  326. else
  327. m_infoBar->QueueDismiss();
  328. }