Compare file paths using wxFileName, not wxString, in the sample.
[wxWidgets.git] / samples / event / event.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: event.cpp
3 // Purpose: wxWidgets sample demonstrating different event usage
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 31.01.01
7 // RCS-ID: $Id$
8 // Copyright: (c) 2001-2009 Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx/wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 // for all others, include the necessary headers (this file is usually all you
28 // need because it includes almost all "standard" wxWidgets headers)
29 #ifndef WX_PRECOMP
30 #include "wx/wx.h"
31 #endif
32
33 #ifndef wxHAS_IMAGES_IN_RESOURCES
34 #include "../sample.xpm"
35 #endif
36
37 #include <wx/statline.h>
38 #include <wx/log.h>
39
40 // ----------------------------------------------------------------------------
41 // event constants
42 // ----------------------------------------------------------------------------
43
44 // define a custom event type (we don't need a separate declaration here but
45 // usually you would use a matching wxDECLARE_EVENT in a header)
46 wxDEFINE_EVENT(wxEVT_MY_CUSTOM_COMMAND, wxCommandEvent);
47
48 // it may also be convenient to define an event table macro for this event type
49 #define EVT_MY_CUSTOM_COMMAND(id, fn) \
50 DECLARE_EVENT_TABLE_ENTRY( \
51 wxEVT_MY_CUSTOM_COMMAND, id, wxID_ANY, \
52 wxCommandEventHandler(fn), \
53 (wxObject *) NULL \
54 ),
55
56 // ----------------------------------------------------------------------------
57 // private classes
58 // ----------------------------------------------------------------------------
59
60 // Define a new application type, each program should derive a class from wxApp
61 class MyApp : public wxApp
62 {
63 public:
64 // override base class virtuals
65 // ----------------------------
66
67 // this one is called on application startup and is a good place for the app
68 // initialization (doing it here and not in the ctor allows to have an error
69 // return: if OnInit() returns false, the application terminates)
70 virtual bool OnInit();
71
72 // these are regular event handlers used to highlight the events handling
73 // order
74 void OnClickDynamicHandlerApp(wxCommandEvent& event);
75 void OnClickStaticHandlerApp(wxCommandEvent& event);
76
77 // we override wxConsoleApp::FilterEvent used to highlight the events
78 // handling order
79 virtual int FilterEvent(wxEvent& event);
80
81 private:
82 DECLARE_EVENT_TABLE()
83 };
84
85 // Define a custom button used to highlight the events handling order
86 class MyEvtTestButton : public wxButton
87 {
88 public:
89 static long BUTTON_ID;
90
91 MyEvtTestButton(wxWindow *parent, const wxString& label)
92 : wxButton(parent, BUTTON_ID, label)
93 {
94 // Add a dynamic handler for this button event to button itself
95 Connect(wxEVT_COMMAND_BUTTON_CLICKED,
96 wxCommandEventHandler(MyEvtTestButton::OnClickDynamicHandler));
97 }
98
99 private:
100 void OnClickDynamicHandler(wxCommandEvent& event)
101 {
102 wxLogMessage("Step 3 in \"How Events are Processed\":\n"
103 "Button::ownDynamicHandler");
104
105 event.Skip();
106 }
107
108 void OnClickStaticHandler(wxCommandEvent& event)
109 {
110 wxLogMessage("Step 4 in \"How Events are Processed\":\n"
111 "Button::ownStaticHandler");
112
113 event.Skip();
114 }
115
116 DECLARE_EVENT_TABLE()
117 };
118
119 long MyEvtTestButton::BUTTON_ID = wxNewId();
120
121 // Define a new frame type: this is going to be our main frame
122 class MyFrame : public wxFrame
123 {
124 public:
125 // ctor(s)
126 MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
127 virtual ~MyFrame();
128
129 void OnQuit(wxCommandEvent& event);
130 void OnAbout(wxCommandEvent& event);
131 #ifdef wxHAS_EVENT_BIND
132 void OnBind(wxCommandEvent& event);
133 #endif // wxHAS_EVENT_BIND
134 void OnConnect(wxCommandEvent& event);
135 void OnDynamic(wxCommandEvent& event);
136 void OnPushEventHandler(wxCommandEvent& event);
137 void OnPopEventHandler(wxCommandEvent& event);
138 void OnTest(wxCommandEvent& event);
139
140 void OnFireCustom(wxCommandEvent& event);
141 void OnProcessCustom(wxCommandEvent& event);
142
143 void OnUpdateUIPop(wxUpdateUIEvent& event);
144
145 // regular event handlers used to highlight the events handling order
146 void OnClickDynamicHandlerFrame(wxCommandEvent& event);
147 void OnClickDynamicHandlerButton(wxCommandEvent& event);
148 void OnClickStaticHandlerFrame(wxCommandEvent& event);
149
150 private:
151 // symbolic names for the status bar fields
152 enum
153 {
154 Status_Main = 0,
155 Status_Dynamic,
156 Status_Push
157 };
158
159 void UpdateDynamicStatus(bool on)
160 {
161 #if wxUSE_STATUSBAR
162 if ( on )
163 {
164 SetStatusText("You can now use \"Dynamic\" item in the menu");
165 SetStatusText("Dynamic: on", Status_Dynamic);
166 }
167 else
168 {
169 SetStatusText("You can no more use \"Dynamic\" item in the menu");
170 SetStatusText("Dynamic: off", Status_Dynamic);
171 }
172 #endif // wxUSE_STATUSBAR
173 }
174
175 // number of pushed event handlers
176 unsigned m_nPush;
177
178 // the button to whose event we connect dynamically
179 wxButton *m_btnDynamic;
180
181 // the button used to highlight the event handlers execution order
182 MyEvtTestButton *m_testBtn;
183
184
185 // any class wishing to process wxWidgets events must use this macro
186 DECLARE_EVENT_TABLE()
187 };
188
189 // Define a custom event handler
190 class MyEvtHandler : public wxEvtHandler
191 {
192 public:
193 MyEvtHandler(size_t level) { m_level = level; }
194
195 void OnTest(wxCommandEvent& event)
196 {
197 wxLogMessage(wxT("This is the pushed test event handler #%u"), m_level);
198
199 // if we don't skip the event, the other event handlers won't get it:
200 // try commenting out this line and see what changes
201 event.Skip();
202 }
203
204 private:
205 unsigned m_level;
206
207 DECLARE_EVENT_TABLE()
208 };
209
210 // ----------------------------------------------------------------------------
211 // constants
212 // ----------------------------------------------------------------------------
213
214 // IDs for the controls and the menu commands
215 enum
216 {
217 // menu items
218 Event_Quit = 1,
219 Event_About,
220 Event_Bind,
221 Event_Connect,
222 Event_Dynamic,
223 Event_Push,
224 Event_Pop,
225 Event_Custom,
226 Event_Test
227 };
228
229 // ----------------------------------------------------------------------------
230 // event tables and other macros for wxWidgets
231 // ----------------------------------------------------------------------------
232
233 // The event tables connect the wxWidgets events with the functions (event
234 // handlers) which process them.
235 BEGIN_EVENT_TABLE(MyApp, wxApp)
236 // Add a static handler for button Click event in the app
237 EVT_BUTTON(MyEvtTestButton::BUTTON_ID, MyApp::OnClickStaticHandlerApp)
238 END_EVENT_TABLE()
239
240 BEGIN_EVENT_TABLE(MyEvtTestButton, wxButton)
241 // Add a static handler to this button itself for its own event
242 EVT_BUTTON(BUTTON_ID, MyEvtTestButton::OnClickStaticHandler)
243 END_EVENT_TABLE()
244
245 // This can be also done at run-time, but for the
246 // simple menu events like this the static method is much simpler.
247 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
248 EVT_MENU(Event_Quit, MyFrame::OnQuit)
249 EVT_MENU(Event_About, MyFrame::OnAbout)
250
251 #ifdef wxHAS_EVENT_BIND
252 EVT_MENU(Event_Bind, MyFrame::OnBind)
253 #endif // wxHAS_EVENT_BIND
254 EVT_MENU(Event_Connect, MyFrame::OnConnect)
255
256 EVT_MENU(Event_Custom, MyFrame::OnFireCustom)
257 EVT_MENU(Event_Test, MyFrame::OnTest)
258 EVT_MENU(Event_Push, MyFrame::OnPushEventHandler)
259 EVT_MENU(Event_Pop, MyFrame::OnPopEventHandler)
260
261 EVT_UPDATE_UI(Event_Pop, MyFrame::OnUpdateUIPop)
262
263 EVT_MY_CUSTOM_COMMAND(wxID_ANY, MyFrame::OnProcessCustom)
264
265 // the line below would also work if OnProcessCustom() were defined as
266 // taking a wxEvent (as required by EVT_CUSTOM) and not wxCommandEvent
267 //EVT_CUSTOM(wxEVT_MY_CUSTOM_COMMAND, wxID_ANY, MyFrame::OnProcessCustom)
268
269 // Add a static handler in the parent frame for button event
270 EVT_BUTTON(MyEvtTestButton::BUTTON_ID, MyFrame::OnClickStaticHandlerFrame)
271 END_EVENT_TABLE()
272
273 BEGIN_EVENT_TABLE(MyEvtHandler, wxEvtHandler)
274 EVT_MENU(Event_Test, MyEvtHandler::OnTest)
275 END_EVENT_TABLE()
276
277 // Create a new application object: this macro will allow wxWidgets to create
278 // the application object during program execution (it's better than using a
279 // static object for many reasons) and also declares the accessor function
280 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
281 // not wxApp)
282 IMPLEMENT_APP(MyApp)
283
284 // ============================================================================
285 // implementation
286 // ============================================================================
287
288 // ----------------------------------------------------------------------------
289 // the application class
290 // ----------------------------------------------------------------------------
291
292 // 'Main program' equivalent: the program execution "starts" here
293 bool MyApp::OnInit()
294 {
295 if ( !wxApp::OnInit() )
296 return false;
297
298 // create the main application window
299 MyFrame *frame = new MyFrame(wxT("Event wxWidgets Sample"),
300 wxPoint(50, 50), wxSize(600, 340));
301
302 // and show it (the frames, unlike simple controls, are not shown when
303 // created initially)
304 frame->Show(true);
305
306 // Add a dynamic handler at the application level for the test button
307 Connect(MyEvtTestButton::BUTTON_ID, wxEVT_COMMAND_BUTTON_CLICKED,
308 wxCommandEventHandler(MyApp::OnClickDynamicHandlerApp));
309
310 // success: wxApp::OnRun() will be called which will enter the main message
311 // loop and the application will run. If we returned false here, the
312 // application would exit immediately.
313 return true;
314 }
315
316 // This is always the first to handle an event !
317 int MyApp::FilterEvent(wxEvent& event)
318 {
319 if ( event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED &&
320 event.GetId() == MyEvtTestButton::BUTTON_ID )
321 {
322 wxLogMessage("Step 0 in \"How Events are Processed\":\n"
323 "App::FilterEvent");
324 }
325
326 return wxApp::FilterEvent(event);
327 }
328
329 void MyApp::OnClickDynamicHandlerApp(wxCommandEvent& event)
330 {
331 wxLogMessage("Step 7, 3 in \"How Events are Processed\":\n"
332 "App::DynamicHandler_InAppTable");
333
334 event.Skip();
335 }
336
337 void MyApp::OnClickStaticHandlerApp(wxCommandEvent& event)
338 {
339 wxLogMessage("Step 7, 4 in \"How Events are Processed\":\n"
340 "App::StaticHandler_InAppTable");
341
342 wxLogMessage("Button click processed, there should be no more messages "
343 "about handling events from the button.\n\n"
344 "The log below shows the order in which the handlers "
345 "were executed.");
346 wxLog::FlushActive();
347
348 event.Skip();
349 }
350
351 // ----------------------------------------------------------------------------
352 // main frame
353 // ----------------------------------------------------------------------------
354
355 // frame constructor
356 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
357 : wxFrame(NULL, wxID_ANY, title, pos, size)
358 {
359 SetIcon(wxICON(sample));
360
361 // init members
362 m_nPush = 0;
363 m_btnDynamic = NULL;
364
365 // create a menu bar
366 wxMenu *menuFile = new wxMenu;
367
368 menuFile->Append(Event_About, wxT("&About\tCtrl-A"), wxT("Show about dialog"));
369 menuFile->AppendSeparator();
370 menuFile->Append(Event_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
371
372 wxMenu *menuEvent = new wxMenu;
373 #ifdef wxHAS_EVENT_BIND
374 menuEvent->AppendCheckItem(Event_Bind, "&Bind\tCtrl-B",
375 "Bind or unbind a dynamic event handler");
376 #endif // wxHAS_EVENT_BIND
377 menuEvent->AppendCheckItem(Event_Connect, wxT("&Connect\tCtrl-C"),
378 wxT("Connect or disconnect the dynamic event handler"));
379 menuEvent->Append(Event_Dynamic, wxT("&Dynamic event\tCtrl-D"),
380 wxT("Dynamic event sample - only works after Connect"));
381 menuEvent->AppendSeparator();
382 menuEvent->Append(Event_Push, wxT("&Push event handler\tCtrl-P"),
383 wxT("Push event handler for test event"));
384 menuEvent->Append(Event_Pop, wxT("P&op event handler\tCtrl-O"),
385 wxT("Pop event handler for test event"));
386 menuEvent->Append(Event_Test, wxT("Test event\tCtrl-T"),
387 wxT("Test event processed by pushed event handler"));
388 menuEvent->AppendSeparator();
389 menuEvent->Append(Event_Custom, wxT("Fire c&ustom event\tCtrl-U"),
390 wxT("Generate a custom event"));
391
392 // now append the freshly created menu to the menu bar...
393 wxMenuBar *menuBar = new wxMenuBar();
394 menuBar->Append(menuFile, wxT("&File"));
395 menuBar->Append(menuEvent, wxT("&Event"));
396
397 // ... and attach this menu bar to the frame
398 SetMenuBar(menuBar);
399
400 #if wxUSE_STATUSBAR
401 CreateStatusBar(3);
402 SetStatusText(wxT("Welcome to wxWidgets event sample"));
403 SetStatusText(wxT("Dynamic: off"), Status_Dynamic);
404 SetStatusText(wxT("Push count: 0"), Status_Push);
405 #endif // wxUSE_STATUSBAR
406
407 wxPanel * const panel = new wxPanel(this);
408 wxSizer * const mainSizer = new wxBoxSizer(wxVERTICAL);
409 wxSizer * const sizer = new wxBoxSizer(wxHORIZONTAL);
410 const wxSizerFlags centreY(wxSizerFlags().Centre().Border());
411 sizer->Add(new wxStaticText(panel, wxID_ANY,
412 "This button will only work if its handler is dynamically connected"),
413 centreY);
414 m_btnDynamic = new wxButton(panel, Event_Dynamic, "&Dynamic button");
415 sizer->Add(m_btnDynamic, centreY);
416
417 mainSizer->Add(sizer, 1, wxEXPAND);
418 mainSizer->Add(new wxStaticLine(panel), 0, wxEXPAND);
419 mainSizer->Add(new wxStaticLine(panel), 0, wxEXPAND);
420
421 m_testBtn = new MyEvtTestButton(panel, "Test Event Handlers Execution Order");
422
423 // After being created, an instance of MyEvtTestButton already has its own
424 // event handlers (see class definition);
425
426 // Add a dynamic handler for this button event in the parent frame
427 Connect(m_testBtn->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
428 wxCommandEventHandler(MyFrame::OnClickDynamicHandlerFrame));
429
430 // Bind a method of this frame (notice "this" argument!) to the button
431 // itself
432 m_testBtn->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
433 wxCommandEventHandler(MyFrame::OnClickDynamicHandlerButton),
434 NULL,
435 this);
436
437 mainSizer->Add(m_testBtn);
438 panel->SetSizer(mainSizer);
439 }
440
441 MyFrame::~MyFrame()
442 {
443 // we must pop any remaining event handlers to avoid memory leaks and
444 // crashes!
445 while ( m_nPush-- != 0 )
446 {
447 PopEventHandler(true /* delete handler */);
448 }
449 }
450
451 // ----------------------------------------------------------------------------
452 // standard event handlers
453 // ----------------------------------------------------------------------------
454
455 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
456 {
457 // true is to force the frame to close
458 Close(true);
459 }
460
461 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
462 {
463 wxMessageBox("Event sample shows different ways of using events\n"
464 "(c) 2001-2009 Vadim Zeitlin",
465 "About wxWidgets Event Sample",
466 wxOK | wxICON_INFORMATION, this);
467 }
468
469 void MyFrame::OnClickStaticHandlerFrame(wxCommandEvent& event)
470 {
471 wxLogMessage("Step 6, 4 in \"How Events are Processed\":\n"
472 "parentWin::StaticHandler_InFrameTable");
473
474 event.Skip();
475 }
476
477 // ----------------------------------------------------------------------------
478 // dynamic event handling stuff
479 // ----------------------------------------------------------------------------
480
481 void MyFrame::OnClickDynamicHandlerFrame(wxCommandEvent& event)
482 {
483 wxLogMessage("Step 6, 3 in \"How Events are Processed\":\n"
484 "parentWin::DynamicHandler_InFrameTable");
485
486 event.Skip();
487 }
488
489 void MyFrame::OnClickDynamicHandlerButton(wxCommandEvent& event)
490 {
491 wxLogMessage("Step 3 in \"How Events are Processed\":\n"
492 "parentWin::DynamicHandler_InButtonTable");
493
494 event.Skip();
495 }
496
497 void MyFrame::OnDynamic(wxCommandEvent& event)
498 {
499 wxString origin;
500 if ( event.GetEventObject() == this )
501 origin = "menu item";
502 else if ( event.GetEventObject() == m_btnDynamic )
503 origin = "button";
504 else
505 origin = "unknown event source";
506
507 wxMessageBox
508 (
509 "This message box is shown from the dynamically connected "
510 "event handler in response to event generated by " + origin,
511 "wxWidgets Event Sample", wxOK | wxICON_INFORMATION, this
512 );
513 }
514
515 #ifdef wxHAS_EVENT_BIND
516
517 void MyFrame::OnBind(wxCommandEvent& event)
518 {
519 if ( event.IsChecked() )
520 {
521 // as we bind directly to the button, there is no need to use an id
522 // here: the button will only ever get its own events
523 m_btnDynamic->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnDynamic,
524 this);
525
526 // but we do need the id for the menu command as the frame gets all of
527 // them
528 Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnDynamic, this,
529 Event_Dynamic);
530 }
531 else // disconnect
532 {
533 m_btnDynamic->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
534 &MyFrame::OnDynamic, this);
535 Unbind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnDynamic, this,
536 Event_Dynamic);
537 }
538
539 UpdateDynamicStatus(event.IsChecked());
540 }
541
542 #endif // wxHAS_EVENT_BIND
543
544 void MyFrame::OnConnect(wxCommandEvent& event)
545 {
546 if ( event.IsChecked() )
547 {
548 m_btnDynamic->Connect(wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,
549 wxCommandEventHandler(MyFrame::OnDynamic),
550 NULL, this);
551 Connect(Event_Dynamic, wxEVT_COMMAND_MENU_SELECTED,
552 wxCommandEventHandler(MyFrame::OnDynamic));
553 }
554 else // disconnect
555 {
556 m_btnDynamic->Disconnect(wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,
557 wxCommandEventHandler(MyFrame::OnDynamic),
558 NULL, this);
559 Disconnect(Event_Dynamic, wxEVT_COMMAND_MENU_SELECTED,
560 wxCommandEventHandler(MyFrame::OnDynamic));
561 }
562
563 UpdateDynamicStatus(event.IsChecked());
564 }
565
566 // ----------------------------------------------------------------------------
567 // push/pop event handlers support
568 // ----------------------------------------------------------------------------
569
570 void MyFrame::OnPushEventHandler(wxCommandEvent& WXUNUSED(event))
571 {
572 PushEventHandler(new MyEvtHandler(++m_nPush));
573
574 #if wxUSE_STATUSBAR
575 SetStatusText(wxString::Format(wxT("Push count: %u"), m_nPush), Status_Push);
576 #endif // wxUSE_STATUSBAR
577 }
578
579 void MyFrame::OnPopEventHandler(wxCommandEvent& WXUNUSED(event))
580 {
581 wxCHECK_RET( m_nPush, wxT("this command should be disabled!") );
582
583 PopEventHandler(true /* delete handler */);
584 m_nPush--;
585
586 #if wxUSE_STATUSBAR
587 SetStatusText(wxString::Format(wxT("Push count: %u"), m_nPush), Status_Push);
588 #endif // wxUSE_STATUSBAR
589 }
590
591 void MyFrame::OnTest(wxCommandEvent& WXUNUSED(event))
592 {
593 wxLogMessage(wxT("This is the test event handler in the main frame"));
594 }
595
596 void MyFrame::OnUpdateUIPop(wxUpdateUIEvent& event)
597 {
598 event.Enable( m_nPush > 0 );
599 }
600
601 // ----------------------------------------------------------------------------
602 // custom event methods
603 // ----------------------------------------------------------------------------
604
605 void MyFrame::OnFireCustom(wxCommandEvent& WXUNUSED(event))
606 {
607 wxCommandEvent eventCustom(wxEVT_MY_CUSTOM_COMMAND);
608
609 wxPostEvent(this, eventCustom);
610 }
611
612 void MyFrame::OnProcessCustom(wxCommandEvent& WXUNUSED(event))
613 {
614 wxLogMessage(wxT("Got a custom event!"));
615 }
616