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 
21 #ifndef INFOBAR_H_
22 #define INFOBAR_H_
23 
24 #include <core/optional.h>
25 #include <wx/event.h>
26 #include <wx/infobar.h>
27 #include <wx/timer.h>
28 #include <wx/panel.h>
29 #include <wx/sizer.h>
30 
31 
32 class wxAuiManager;
33 class wxHyperlinkCtrl;
34 
35 
36 enum
37 {
38     /// ID for the close button on the frame's infobar
39     ID_CLOSE_INFOBAR = 2000,
40 };
41 
42 
43 wxDECLARE_EVENT( KIEVT_SHOW_INFOBAR,    wxCommandEvent );
44 wxDECLARE_EVENT( KIEVT_DISMISS_INFOBAR, wxCommandEvent );
45 
46 /**
47  * A modified version of the wxInfoBar class that allows us to:
48  *     * Show the close button along with the other buttons
49  *     * Remove all user-provided buttons at once
50  *     * Allow automatically hiding the infobar after a time period
51  *     * Show/hide using events
52  *     * Place it inside an AUI manager
53  *
54  * This inherits from the generic infobar because the native infobar
55  * on GTK doesn't include the icon on the left and it looks worse.
56  *
57  * There are 2 events associated with the infobar:
58  *
59  * KIEVT_SHOW_INFOBAR:
60  *   An event that tells the infobar to show a message.
61  *
62  *   The message text is contained inside the string component,
63  *   and the message flag is contained inside the int component.
64  *
65  *   Sample event creation code:
66  *       wxCommandEvent* evt = new wxCommandEvent( KIEVT_SHOW_INFOBAR );
67  *       evt->SetString( "A message to show" );
68  *       evt->SetInt( wxICON_WARNING );
69  *
70  * KIEVT_DISMISS_INFOBAR:
71  *   An event that tells the infobar to hide itself.
72  */
73 class WX_INFOBAR : public wxInfoBarGeneric
74 {
75 public:
76     /**
77      * Construct an infobar that can exist inside an AUI managed frame.
78      *
79      * @param aParent is the parent
80      * @param aMgr is the AUI manager that this infobar is added to
81      * @param aWinId is the ID for this infobar object
82      */
83     WX_INFOBAR( wxWindow* aParent, wxAuiManager* aMgr = nullptr, wxWindowID aWinid = wxID_ANY );
84 
85     ~WX_INFOBAR();
86 
87 
88     /**
89      * Sets the type of message for special handling if needed
90      */
91     enum class MESSAGE_TYPE
92     {
93         GENERIC,          /**< GENERIC Are messages that do not have special handling */
94         OUTDATED_SAVE,    /**< OUTDATED_SAVE Messages that should be cleared on save */
95         DRC_RULES_ERROR,
96         DRC_VIOLATION
97     };
98 
GetMessageType()99     MESSAGE_TYPE GetMessageType() const { return m_type; }
100 
101     /**
102      * Set the time period to show the infobar.
103      *
104      * This only applies for the next showing of the infobar,
105      * so it must be reset every time. A value of 0 disables
106      * the automatic hiding (this is the default).
107      *
108      * @param aTime is the time in milliseconds to show the infobar
109      */
110     void SetShowTime( int aTime );
111 
112     /**
113      * Add the default close button to the infobar on the right side.
114      *
115      * @param aTooltip is the tooltip to give the close button
116      */
117     void AddCloseButton( const wxString& aTooltip = _( "Hide this message." ) );
118 
119     /**
120      * Add an already created button to the infobar.
121      * New buttons are added in the right-most position.
122      *
123      * @param aButton is the button to add
124      */
125     void AddButton( wxButton* aButton );
126 
127     /**
128      * Add an already created hypertext link to the infobar.
129      * New buttons are added in the right-most position.
130      *
131      * @param aHypertextButton is the button to add
132      */
133     void AddButton( wxHyperlinkCtrl* aHypertextButton );
134 
135     /**
136      * Add a button with the provided ID and text.
137      * The new button is created on the right-most position.
138      *
139      * @param aId is the ID to assign to the button
140      * @param aLabel is the text for the button
141      */
142     void AddButton( wxWindowID aId, const wxString& aLabel = wxEmptyString ) override;
143 
144     /**
145      * Remove all the buttons that have been added by the user.
146      */
147     void RemoveAllButtons();
148 
149     bool HasCloseButton() const;
150 
151     /**
152      * Provide a callback to be called when the infobar is dismissed (either by user action
153      * or timer).
154      * @param aCallback
155      */
SetCallback(std::function<void (void)> aCallback)156     void SetCallback( std::function<void(void)> aCallback )
157     {
158         m_callback = aCallback;
159     }
160 
161     /**
162      * Show the infobar with the provided message and icon for a specific period
163      * of time.
164      *
165      * @param aMessage is the message to display
166      * @param aTime is the amount of time in milliseconds to show the infobar
167      * @param aFlags is the flag containing the icon to display on the left side of the infobar
168      */
169     void ShowMessageFor( const wxString& aMessage, int aTime, int aFlags = wxICON_INFORMATION,
170                          MESSAGE_TYPE aType = WX_INFOBAR::MESSAGE_TYPE::GENERIC );
171 
172     /**
173      * Show the info bar with the provided message and icon.
174      *
175      * @param aMessage is the message to display
176      * @param aFlags is the flag containing the icon to display on the left side of the infobar
177      */
178     void ShowMessage( const wxString& aMessage, int aFlags = wxICON_INFORMATION ) override;
179 
180     /**
181      * Show the info bar with the provided message and icon, setting the type
182      *
183      * @param aMessage is the message to display
184      * @param aFlags is the flag containing the icon to display on the left side of the infobar
185      * @param aType is the type of message being displayed
186      */
187     void ShowMessage( const wxString& aMessage, int aFlags, MESSAGE_TYPE aType );
188 
189     /**
190      * Dismisses the infobar and updates the containing layout and AUI manager
191      * (if one is provided).
192      */
193     void Dismiss() override;
194 
195     /**
196      * Send the infobar an event telling it to show a message.
197      *
198      * @param aMessage is the message to display
199      * @param aFlags is the flag containing the icon to display on the left side of the infobar
200      */
201     void QueueShowMessage( const wxString& aMessage, int aFlags = wxICON_INFORMATION );
202 
203     /**
204      * Send the infobar an event telling it to hide itself.
205      */
206     void QueueDismiss();
207 
208     /**
209      * Returns true if the infobar is being updated.
210      */
IsLocked()211     bool IsLocked()
212     {
213         return m_updateLock;
214     }
215 
216 protected:
217     /**
218      * Event handler for showing the infobar using a wxCommandEvent of the type
219      * KIEVT_SHOW_INFOBAR. The message is stored inside the string field, and the
220      * icon flag is stored inside the int field.
221      */
222     void onShowInfoBar( wxCommandEvent& aEvent );
223 
224     /**
225      * Event handler for dismissing the infobar using a wxCommandEvent of the type
226      * KIEVT_DISMISS_INFOBAR.
227      */
228     void onDismissInfoBar( wxCommandEvent& aEvent );
229 
230     /**
231      * Event handler for the close button.
232      * This is bound to ID_CLOSE_INFOBAR on the infobar.
233      */
234     void onCloseButton( wxCommandEvent& aEvent );
235 
236     /**
237      * Event handler for the automatic closing timer.
238      */
239     void onTimer( wxTimerEvent& aEvent );
240 
241     void onSize( wxSizeEvent& aEvent );
242 
243     /**
244      * Update the AUI pane to show or hide this infobar.
245      *
246      * @param aShow is true to show the pane
247      */
248     void updateAuiLayout( bool aShow );
249 
250 protected:
251     int           m_showTime;       ///< The time to show the infobar. 0 = don't auto hide
252     bool          m_updateLock;     ///< True if this infobar requested the UI update
253     wxTimer*      m_showTimer;      ///< The timer counting the autoclose period
254     wxAuiManager* m_auiManager;     ///< The AUI manager that contains this infobar
255     MESSAGE_TYPE  m_type;           ///< The type of message being displayed
256 
257     OPT<std::function<void(void)>> m_callback;   ///< Optional callback made when closing infobar
258 
259     DECLARE_EVENT_TABLE()
260 };
261 
262 
263 /**
264  * A wxPanel derived class that hold an infobar and another control.
265  * The infobar is located at the top of the panel, and the other control
266  * is located below it.
267  *
268  * This allows the infobar to be controlled nicely by an AUI manager,
269  * since adding the infobar on its own to the AUI manager produces
270  * artifacts when showing/hiding it due to the AUI pane layout.
271  *
272  * Note that this implementation currently has issues on Windows with
273  * event processing inside the GAL canvas, see:
274  * https://gitlab.com/kicad/code/kicad/-/issues/4501
275  *
276  */
277 class EDA_INFOBAR_PANEL : public wxPanel
278 {
279 public:
280     EDA_INFOBAR_PANEL( wxWindow* aParent, wxWindowID aId = wxID_ANY,
281                        const wxPoint& aPos = wxDefaultPosition,
282                        const wxSize& aSize = wxSize( -1,-1 ),
283                        long aStyle = wxTAB_TRAVERSAL,
284                        const wxString& aName = wxEmptyString );
285 
286     /**
287      * Add the given infobar object to the panel
288      *
289      * @param aInfoBar is the infobar to add
290      */
291     void AddInfoBar( WX_INFOBAR* aInfoBar );
292 
293     /**
294      * Add the other item to the panel.
295      * This item will expand to fill up the vertical space left.
296      *
297      * @param aOtherItem is the item to add
298      */
299     void AddOtherItem( wxWindow* aOtherItem );
300 
301 protected:
302     // The sizer containing the infobar and the other object
303     wxFlexGridSizer* m_mainSizer;
304 };
305 
306 #endif // INFOBAR_H_
307