1 /////////////////////////////////////////////////////////////////////////////
2 // Program:     wxWidgets Widgets Sample
3 // Name:        samples/widgets/widgets.cpp
4 // Purpose:     Sample showing most of the simple wxWidgets widgets
5 // Author:      Vadim Zeitlin
6 // Created:     27.03.01
7 // Copyright:   (c) 2001 Vadim Zeitlin
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // for compilers that support precompilation, includes "wx/wx.h".
20 #include "wx/wxprec.h"
21 
22 #ifdef __BORLANDC__
23     #pragma hdrstop
24 #endif
25 
26 // for all others, include the necessary headers
27 #ifndef WX_PRECOMP
28     #include "wx/app.h"
29     #include "wx/log.h"
30     #include "wx/frame.h"
31     #include "wx/menu.h"
32     #include "wx/image.h"
33 
34     #include "wx/button.h"
35     #include "wx/checkbox.h"
36     #include "wx/listbox.h"
37     #include "wx/statbox.h"
38     #include "wx/stattext.h"
39     #include "wx/textctrl.h"
40     #include "wx/msgdlg.h"
41 #endif
42 
43 #include "wx/sysopt.h"
44 #include "wx/bookctrl.h"
45 #include "wx/treebook.h"
46 #include "wx/sizer.h"
47 #include "wx/colordlg.h"
48 #include "wx/fontdlg.h"
49 #include "wx/textdlg.h"
50 #include "wx/imaglist.h"
51 #include "wx/wupdlock.h"
52 #include "wx/textcompleter.h"
53 
54 #include "wx/persist/toplevel.h"
55 #include "wx/persist/treebook.h"
56 
57 #include "widgets.h"
58 
59 #include "../sample.xpm"
60 
61 // ----------------------------------------------------------------------------
62 // constants
63 // ----------------------------------------------------------------------------
64 
65 // control ids
66 enum
67 {
68     Widgets_ClearLog = 100,
69     Widgets_Quit,
70 
71     Widgets_BookCtrl,
72 
73 #if wxUSE_TOOLTIPS
74     Widgets_SetTooltip,
75 #endif // wxUSE_TOOLTIPS
76     Widgets_SetFgColour,
77     Widgets_SetBgColour,
78     Widgets_SetPageBg,
79     Widgets_SetFont,
80     Widgets_Enable,
81 
82     Widgets_BorderNone,
83     Widgets_BorderStatic,
84     Widgets_BorderSimple,
85     Widgets_BorderRaised,
86     Widgets_BorderSunken,
87     Widgets_BorderDouble,
88     Widgets_BorderDefault,
89 
90     Widgets_LayoutDirection,
91 
92     Widgets_GlobalBusyCursor,
93     Widgets_BusyCursor,
94 
95     Widgets_GoToPage,
96     Widgets_GoToPageLast = Widgets_GoToPage + 100,
97 
98 
99     TextEntry_Begin,
100     TextEntry_DisableAutoComplete = TextEntry_Begin,
101     TextEntry_AutoCompleteFixed,
102     TextEntry_AutoCompleteFilenames,
103     TextEntry_AutoCompleteDirectories,
104     TextEntry_AutoCompleteCustom,
105 
106     TextEntry_SetHint,
107     TextEntry_End
108 };
109 
110 const wxChar *WidgetsCategories[MAX_PAGES] = {
111 #if defined(__WXUNIVERSAL__)
112     wxT("Universal"),
113 #else
114     wxT("Native"),
115 #endif
116     wxT("Generic"),
117     wxT("Pickers"),
118     wxT("Comboboxes"),
119     wxT("With items"),
120     wxT("Editable"),
121     wxT("Books"),
122     wxT("All controls")
123 };
124 
125 // ----------------------------------------------------------------------------
126 // our classes
127 // ----------------------------------------------------------------------------
128 
129 // Define a new application type, each program should derive a class from wxApp
130 class WidgetsApp : public wxApp
131 {
132 public:
133     // override base class virtuals
134     // ----------------------------
135 
136     // this one is called on application startup and is a good place for the app
137     // initialization (doing it here and not in the ctor allows to have an error
138     // return: if OnInit() returns false, the application terminates)
139     virtual bool OnInit();
140 };
141 
142 // Define a new frame type: this is going to be our main frame
143 class WidgetsFrame : public wxFrame
144 {
145 public:
146     // ctor(s) and dtor
147     WidgetsFrame(const wxString& title);
148     virtual ~WidgetsFrame();
149 
150 protected:
151     // event handlers
152 #if USE_LOG
153     void OnButtonClearLog(wxCommandEvent& event);
154 #endif // USE_LOG
155     void OnExit(wxCommandEvent& event);
156 
157 #if wxUSE_MENUS
158     void OnPageChanging(WidgetsBookCtrlEvent& event);
159     void OnPageChanged(WidgetsBookCtrlEvent& event);
160     void OnGoToPage(wxCommandEvent& event);
161 
162 #if wxUSE_TOOLTIPS
163     void OnSetTooltip(wxCommandEvent& event);
164 #endif // wxUSE_TOOLTIPS
165     void OnSetFgCol(wxCommandEvent& event);
166     void OnSetBgCol(wxCommandEvent& event);
167     void OnSetPageBg(wxCommandEvent& event);
168     void OnSetFont(wxCommandEvent& event);
169     void OnEnable(wxCommandEvent& event);
170     void OnSetBorder(wxCommandEvent& event);
171 
172     void OnToggleLayoutDirection(wxCommandEvent& event);
173 
174     void OnToggleGlobalBusyCursor(wxCommandEvent& event);
175     void OnToggleBusyCursor(wxCommandEvent& event);
176 
177     // wxTextEntry-specific tests
178     void OnDisableAutoComplete(wxCommandEvent& event);
179     void OnAutoCompleteFixed(wxCommandEvent& event);
180     void OnAutoCompleteFilenames(wxCommandEvent& event);
181     void OnAutoCompleteDirectories(wxCommandEvent& event);
182     void OnAutoCompleteCustom(wxCommandEvent& event);
183 
184     void OnSetHint(wxCommandEvent& event);
185 
OnUpdateTextUI(wxUpdateUIEvent & event)186     void OnUpdateTextUI(wxUpdateUIEvent& event)
187     {
188         event.Enable( CurrentPage()->GetTextEntry() != NULL );
189     }
190 #endif // wxUSE_MENUS
191 
192     // initialize the book: add all pages to it
193     void InitBook();
194 
195     // return the currently selected page (never NULL)
196     WidgetsPage *CurrentPage();
197 
198 private:
199     // the panel containing everything
200     wxPanel *m_panel;
201 
202 #if USE_LOG
203     // the listbox for logging messages
204     wxListBox *m_lboxLog;
205 
206     // the log target we use to redirect messages to the listbox
207     wxLog *m_logTarget;
208 #endif // USE_LOG
209 
210     // the book containing the test pages
211     WidgetsBookCtrl *m_book;
212 
213 #if wxUSE_MENUS
214     // last chosen fg/bg colours and font
215     wxColour m_colFg,
216              m_colBg;
217     wxFont   m_font;
218 #endif // wxUSE_MENUS
219 
220     // any class wishing to process wxWidgets events must use this macro
221     wxDECLARE_EVENT_TABLE();
222 };
223 
224 #if USE_LOG
225 // A log target which just redirects the messages to a listbox
226 class LboxLogger : public wxLog
227 {
228 public:
LboxLogger(wxListBox * lbox,wxLog * logOld)229     LboxLogger(wxListBox *lbox, wxLog *logOld)
230     {
231         m_lbox = lbox;
232         //m_lbox->Disable(); -- looks ugly under MSW
233         m_logOld = logOld;
234     }
235 
~LboxLogger()236     virtual ~LboxLogger()
237     {
238         wxLog::SetActiveTarget(m_logOld);
239     }
240 
241 private:
242     // implement sink functions
DoLogTextAtLevel(wxLogLevel level,const wxString & msg)243     virtual void DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
244     {
245         if ( level == wxLOG_Trace )
246         {
247             if ( m_logOld )
248                 m_logOld->LogTextAtLevel(level, msg);
249             return;
250         }
251 
252         #ifdef __WXUNIVERSAL__
253             m_lbox->AppendAndEnsureVisible(msg);
254         #else // other ports don't have this method yet
255             m_lbox->Append(msg);
256             m_lbox->SetFirstItem(m_lbox->GetCount() - 1);
257         #endif
258     }
259 
260     // the control we use
261     wxListBox *m_lbox;
262 
263     // the old log target
264     wxLog *m_logOld;
265 };
266 #endif // USE_LOG
267 
268 // array of pages
269 WX_DEFINE_ARRAY_PTR(WidgetsPage *, ArrayWidgetsPage);
270 
271 // ----------------------------------------------------------------------------
272 // misc macros
273 // ----------------------------------------------------------------------------
274 
275 IMPLEMENT_APP(WidgetsApp)
276 
277 // ----------------------------------------------------------------------------
278 // event tables
279 // ----------------------------------------------------------------------------
280 
wxBEGIN_EVENT_TABLE(WidgetsFrame,wxFrame)281 wxBEGIN_EVENT_TABLE(WidgetsFrame, wxFrame)
282 #if USE_LOG
283     EVT_BUTTON(Widgets_ClearLog, WidgetsFrame::OnButtonClearLog)
284 #endif // USE_LOG
285     EVT_BUTTON(Widgets_Quit, WidgetsFrame::OnExit)
286 
287 #if wxUSE_TOOLTIPS
288     EVT_MENU(Widgets_SetTooltip, WidgetsFrame::OnSetTooltip)
289 #endif // wxUSE_TOOLTIPS
290 
291 #if wxUSE_MENUS
292     EVT_WIDGETS_PAGE_CHANGING(wxID_ANY, WidgetsFrame::OnPageChanging)
293     EVT_MENU_RANGE(Widgets_GoToPage, Widgets_GoToPageLast,
294                    WidgetsFrame::OnGoToPage)
295 
296     EVT_MENU(Widgets_SetFgColour, WidgetsFrame::OnSetFgCol)
297     EVT_MENU(Widgets_SetBgColour, WidgetsFrame::OnSetBgCol)
298     EVT_MENU(Widgets_SetPageBg,   WidgetsFrame::OnSetPageBg)
299     EVT_MENU(Widgets_SetFont,     WidgetsFrame::OnSetFont)
300     EVT_MENU(Widgets_Enable,      WidgetsFrame::OnEnable)
301 
302     EVT_MENU_RANGE(Widgets_BorderNone, Widgets_BorderDefault,
303                    WidgetsFrame::OnSetBorder)
304 
305     EVT_MENU(Widgets_LayoutDirection,   WidgetsFrame::OnToggleLayoutDirection)
306 
307     EVT_MENU(Widgets_GlobalBusyCursor,  WidgetsFrame::OnToggleGlobalBusyCursor)
308     EVT_MENU(Widgets_BusyCursor,        WidgetsFrame::OnToggleBusyCursor)
309 
310     EVT_MENU(TextEntry_DisableAutoComplete,   WidgetsFrame::OnDisableAutoComplete)
311     EVT_MENU(TextEntry_AutoCompleteFixed,     WidgetsFrame::OnAutoCompleteFixed)
312     EVT_MENU(TextEntry_AutoCompleteFilenames, WidgetsFrame::OnAutoCompleteFilenames)
313     EVT_MENU(TextEntry_AutoCompleteDirectories, WidgetsFrame::OnAutoCompleteDirectories)
314     EVT_MENU(TextEntry_AutoCompleteCustom,    WidgetsFrame::OnAutoCompleteCustom)
315 
316     EVT_MENU(TextEntry_SetHint, WidgetsFrame::OnSetHint)
317 
318     EVT_UPDATE_UI_RANGE(TextEntry_Begin, TextEntry_End - 1,
319                         WidgetsFrame::OnUpdateTextUI)
320 
321     EVT_MENU(wxID_EXIT, WidgetsFrame::OnExit)
322 #endif // wxUSE_MENUS
323 wxEND_EVENT_TABLE()
324 
325 // ============================================================================
326 // implementation
327 // ============================================================================
328 
329 // ----------------------------------------------------------------------------
330 // app class
331 // ----------------------------------------------------------------------------
332 
333 bool WidgetsApp::OnInit()
334 {
335     if ( !wxApp::OnInit() )
336         return false;
337 
338     SetVendorName("wxWidgets_Samples");
339 
340     // the reason for having these ifdef's is that I often run two copies of
341     // this sample side by side and it is useful to see which one is which
342     wxString title;
343 #if defined(__WXUNIVERSAL__)
344     title = wxT("wxUniv/");
345 #endif
346 
347 #if defined(__WXMSW__)
348     title += wxT("wxMSW");
349 #elif defined(__WXGTK__)
350     title += wxT("wxGTK");
351 #elif defined(__WXMAC__)
352     title += wxT("wxMAC");
353 #elif defined(__WXMOTIF__)
354     title += wxT("wxMOTIF");
355 #else
356     title += wxT("wxWidgets");
357 #endif
358 
359     wxFrame *frame = new WidgetsFrame(title + wxT(" widgets demo"));
360     frame->Show();
361 
362     return true;
363 }
364 
365 // ----------------------------------------------------------------------------
366 // WidgetsFrame construction
367 // ----------------------------------------------------------------------------
368 
WidgetsFrame(const wxString & title)369 WidgetsFrame::WidgetsFrame(const wxString& title)
370             : wxFrame(NULL, wxID_ANY, title)
371 {
372     const bool sizeSet = wxPersistentRegisterAndRestore(this, "Main");
373 
374     // set the frame icon
375     SetIcon(wxICON(sample));
376 
377     // init everything
378 #if USE_LOG
379     m_lboxLog = NULL;
380     m_logTarget = NULL;
381 #endif // USE_LOG
382     m_book = NULL;
383 
384 #if wxUSE_MENUS
385     // create the menubar
386     wxMenuBar *mbar = new wxMenuBar;
387     wxMenu *menuWidget = new wxMenu;
388 #if wxUSE_TOOLTIPS
389     menuWidget->Append(Widgets_SetTooltip, wxT("Set &tooltip...\tCtrl-T"));
390     menuWidget->AppendSeparator();
391 #endif // wxUSE_TOOLTIPS
392     menuWidget->Append(Widgets_SetFgColour, wxT("Set &foreground...\tCtrl-F"));
393     menuWidget->Append(Widgets_SetBgColour, wxT("Set &background...\tCtrl-B"));
394     menuWidget->Append(Widgets_SetPageBg,   wxT("Set &page background...\tShift-Ctrl-B"));
395     menuWidget->Append(Widgets_SetFont,     wxT("Set f&ont...\tCtrl-O"));
396     menuWidget->AppendCheckItem(Widgets_Enable,  wxT("&Enable/disable\tCtrl-E"));
397 
398     wxMenu *menuBorders = new wxMenu;
399     menuBorders->AppendRadioItem(Widgets_BorderDefault, wxT("De&fault\tCtrl-Shift-9"));
400     menuBorders->AppendRadioItem(Widgets_BorderNone,   wxT("&None\tCtrl-Shift-0"));
401     menuBorders->AppendRadioItem(Widgets_BorderSimple, wxT("&Simple\tCtrl-Shift-1"));
402     menuBorders->AppendRadioItem(Widgets_BorderDouble, wxT("&Double\tCtrl-Shift-2"));
403     menuBorders->AppendRadioItem(Widgets_BorderStatic, wxT("Stati&c\tCtrl-Shift-3"));
404     menuBorders->AppendRadioItem(Widgets_BorderRaised, wxT("&Raised\tCtrl-Shift-4"));
405     menuBorders->AppendRadioItem(Widgets_BorderSunken, wxT("S&unken\tCtrl-Shift-5"));
406     menuWidget->AppendSubMenu(menuBorders, wxT("Set &border"));
407 
408     menuWidget->AppendSeparator();
409     menuWidget->AppendCheckItem(Widgets_LayoutDirection,
410                                 "Toggle &layout direction\tCtrl-L");
411 
412     menuWidget->AppendSeparator();
413     menuWidget->AppendCheckItem(Widgets_GlobalBusyCursor,
414                                 wxT("Toggle &global busy cursor\tCtrl-Shift-U"));
415     menuWidget->AppendCheckItem(Widgets_BusyCursor,
416                                 wxT("Toggle b&usy cursor\tCtrl-U"));
417 
418     menuWidget->AppendSeparator();
419     menuWidget->Append(wxID_EXIT, wxT("&Quit\tCtrl-Q"));
420     mbar->Append(menuWidget, wxT("&Widget"));
421 
422     wxMenu *menuTextEntry = new wxMenu;
423     menuTextEntry->AppendRadioItem(TextEntry_DisableAutoComplete,
424                                    wxT("&Disable auto-completion"));
425     menuTextEntry->AppendRadioItem(TextEntry_AutoCompleteFixed,
426                                    wxT("Fixed-&list auto-completion"));
427     menuTextEntry->AppendRadioItem(TextEntry_AutoCompleteFilenames,
428                                    wxT("&Files names auto-completion"));
429     menuTextEntry->AppendRadioItem(TextEntry_AutoCompleteDirectories,
430                                    wxT("&Directories names auto-completion"));
431     menuTextEntry->AppendRadioItem(TextEntry_AutoCompleteCustom,
432                                    wxT("&Custom auto-completion"));
433     menuTextEntry->AppendSeparator();
434     menuTextEntry->Append(TextEntry_SetHint, "Set help &hint");
435 
436     mbar->Append(menuTextEntry, wxT("&Text"));
437 
438     SetMenuBar(mbar);
439 
440     mbar->Check(Widgets_Enable, true);
441 #endif // wxUSE_MENUS
442 
443     // create controls
444     m_panel = new wxPanel(this, wxID_ANY);
445 
446     wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
447 
448     // we have 2 panes: book with pages demonstrating the controls in the
449     // upper one and the log window with some buttons in the lower
450 
451     int style = wxBK_DEFAULT;
452     // Uncomment to suppress page theme (draw in solid colour)
453     //style |= wxNB_NOPAGETHEME;
454 
455     m_book = new WidgetsBookCtrl(m_panel, Widgets_BookCtrl,
456                                  wxDefaultPosition, wxDefaultSize,
457                                  style, "Widgets");
458 
459     InitBook();
460 
461 #ifndef __WXHANDHELD__
462     // the lower one only has the log listbox and a button to clear it
463 #if USE_LOG
464     wxSizer *sizerDown = new wxStaticBoxSizer(
465         new wxStaticBox( m_panel, wxID_ANY, wxT("&Log window") ),
466         wxVERTICAL);
467 
468     m_lboxLog = new wxListBox(m_panel, wxID_ANY);
469     sizerDown->Add(m_lboxLog, 1, wxGROW | wxALL, 5);
470     sizerDown->SetMinSize(100, 150);
471 #else
472     wxSizer *sizerDown = new wxBoxSizer(wxVERTICAL);
473 #endif // USE_LOG
474 
475     wxBoxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
476     wxButton *btn;
477 #if USE_LOG
478     btn = new wxButton(m_panel, Widgets_ClearLog, wxT("Clear &log"));
479     sizerBtns->Add(btn);
480     sizerBtns->Add(10, 0); // spacer
481 #endif // USE_LOG
482     btn = new wxButton(m_panel, Widgets_Quit, wxT("E&xit"));
483     sizerBtns->Add(btn);
484     sizerDown->Add(sizerBtns, 0, wxALL | wxALIGN_RIGHT, 5);
485 
486     // put everything together
487     sizerTop->Add(m_book, 1, wxGROW | (wxALL & ~(wxTOP | wxBOTTOM)), 10);
488     sizerTop->Add(0, 5, 0, wxGROW); // spacer in between
489     sizerTop->Add(sizerDown, 0,  wxGROW | (wxALL & ~wxTOP), 10);
490 
491 #else // !__WXHANDHELD__/__WXHANDHELD__
492 
493     sizerTop->Add(m_book, 1, wxGROW | wxALL );
494 
495 #endif // __WXHANDHELD__
496 
497     m_panel->SetSizer(sizerTop);
498 
499     const wxSize sizeMin = m_panel->GetBestSize();
500     if ( !sizeSet )
501         SetClientSize(sizeMin);
502     SetMinClientSize(sizeMin);
503 
504 #if USE_LOG && !defined(__WXCOCOA__)
505     // wxCocoa's listbox is too flakey to use for logging right now
506     // now that everything is created we can redirect the log messages to the
507     // listbox
508     m_logTarget = new LboxLogger(m_lboxLog, wxLog::GetActiveTarget());
509     wxLog::SetActiveTarget(m_logTarget);
510 #endif
511 }
512 
InitBook()513 void WidgetsFrame::InitBook()
514 {
515 #if USE_ICONS_IN_BOOK
516     wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
517 
518     wxImage img(sample_xpm);
519     imageList->Add(wxBitmap(img.Scale(ICON_SIZE, ICON_SIZE)));
520 #else
521     wxImageList *imageList = NULL;
522 #endif
523 
524 #if !USE_TREEBOOK
525     WidgetsBookCtrl *books[MAX_PAGES];
526 #endif
527 
528     ArrayWidgetsPage pages[MAX_PAGES];
529     wxArrayString labels[MAX_PAGES];
530 
531     wxMenu *menuPages = new wxMenu;
532     unsigned int nPage = 0, nFKey = 0;
533     int cat, imageId = 1;
534 
535     // we need to first create all pages and only then add them to the book
536     // as we need the image list first
537     //
538     // we also construct the pages menu during this first iteration
539     for ( cat = 0; cat < MAX_PAGES; cat++ )
540     {
541 #if USE_TREEBOOK
542         nPage++; // increase for parent page
543 #else
544         books[cat] = new WidgetsBookCtrl(m_book,
545                                          wxID_ANY,
546                                          wxDefaultPosition,
547                                          wxDefaultSize,
548                                          wxBK_DEFAULT);
549 #endif
550 
551         for ( WidgetsPageInfo *info = WidgetsPage::ms_widgetPages;
552               info;
553               info = info->GetNext() )
554         {
555             if( (info->GetCategories() & ( 1 << cat )) == 0)
556                 continue;
557 
558             WidgetsPage *page = (*info->GetCtor())(
559 #if USE_TREEBOOK
560                                  m_book
561 #else
562                                  books[cat]
563 #endif
564                                  , imageList);
565             pages[cat].Add(page);
566 
567             labels[cat].Add(info->GetLabel());
568             if ( cat == ALL_PAGE )
569             {
570                 wxString radioLabel(info->GetLabel());
571                 nFKey++;
572                 if ( nFKey <= 12 )
573                 {
574                     radioLabel << wxT("\tF" ) << nFKey;
575                 }
576 
577                 menuPages->AppendRadioItem(
578                             Widgets_GoToPage + nPage,
579                             radioLabel
580                            );
581 #if !USE_TREEBOOK
582                 // consider only for book in book architecture
583                 nPage++;
584 #endif
585             }
586 
587 #if USE_TREEBOOK
588             // consider only for treebook architecture (with subpages)
589             nPage++;
590 #endif
591         }
592     }
593 
594     GetMenuBar()->Append(menuPages, wxT("&Page"));
595 
596 #if USE_ICONS_IN_BOOK
597     m_book->AssignImageList(imageList);
598 #endif
599 
600     for ( cat = 0; cat < MAX_PAGES; cat++ )
601     {
602 #if USE_TREEBOOK
603         m_book->AddPage(NULL,WidgetsCategories[cat],false,0);
604 #else
605         m_book->AddPage(books[cat],WidgetsCategories[cat],false,0);
606 #if USE_ICONS_IN_BOOK
607         books[cat]->SetImageList(imageList);
608 #endif
609 #endif
610 
611         // now do add them
612         size_t count = pages[cat].GetCount();
613         for ( size_t n = 0; n < count; n++ )
614         {
615 #if USE_TREEBOOK
616             m_book->AddSubPage
617 #else
618             books[cat]->AddPage
619 #endif
620                            (
621                             pages[cat][n],
622                             labels[cat][n],
623                             false, // don't select
624                             imageId++
625                            );
626         }
627     }
628 
629     Connect( wxID_ANY,
630              wxEVT_COMMAND_WIDGETS_PAGE_CHANGED,
631              wxWidgetsbookEventHandler(WidgetsFrame::OnPageChanged) );
632 
633     const bool pageSet = wxPersistentRegisterAndRestore(m_book);
634 
635 #if USE_TREEBOOK
636     // for treebook page #0 is empty parent page only so select the first page
637     // with some contents
638     if ( !pageSet )
639         m_book->SetSelection(1);
640 
641     // but ensure that the top of the tree is shown nevertheless
642     wxTreeCtrl * const tree = m_book->GetTreeCtrl();
643 
644     wxTreeItemIdValue cookie;
645     tree->EnsureVisible(tree->GetFirstChild(tree->GetRootItem(), cookie));
646 #else
647     if ( !pageSet )
648     {
649         // for other books set selection twice to force connected event handler
650         // to force lazy creation of initial visible content
651         m_book->SetSelection(1);
652         m_book->SetSelection(0);
653     }
654 #endif // USE_TREEBOOK
655 }
656 
CurrentPage()657 WidgetsPage *WidgetsFrame::CurrentPage()
658 {
659     wxWindow *page = m_book->GetCurrentPage();
660 
661 #if !USE_TREEBOOK
662     WidgetsBookCtrl *subBook = wxStaticCast(page, WidgetsBookCtrl);
663     wxCHECK_MSG( subBook, NULL, wxT("no WidgetsBookCtrl?") );
664 
665     page = subBook->GetCurrentPage();
666 #endif // !USE_TREEBOOK
667 
668     return wxStaticCast(page, WidgetsPage);
669 }
670 
~WidgetsFrame()671 WidgetsFrame::~WidgetsFrame()
672 {
673 #if USE_LOG
674     delete m_logTarget;
675 #endif // USE_LOG
676 }
677 
678 // ----------------------------------------------------------------------------
679 // WidgetsFrame event handlers
680 // ----------------------------------------------------------------------------
681 
OnExit(wxCommandEvent & WXUNUSED (event))682 void WidgetsFrame::OnExit(wxCommandEvent& WXUNUSED(event))
683 {
684     Close();
685 }
686 
687 #if USE_LOG
OnButtonClearLog(wxCommandEvent & WXUNUSED (event))688 void WidgetsFrame::OnButtonClearLog(wxCommandEvent& WXUNUSED(event))
689 {
690     m_lboxLog->Clear();
691 }
692 #endif // USE_LOG
693 
694 #if wxUSE_MENUS
695 
OnPageChanging(WidgetsBookCtrlEvent & event)696 void WidgetsFrame::OnPageChanging(WidgetsBookCtrlEvent& event)
697 {
698 #if USE_TREEBOOK
699     // don't allow selection of entries without pages (categories)
700     if ( !m_book->GetPage(event.GetSelection()) )
701         event.Veto();
702 #else
703     wxUnusedVar(event);
704 #endif
705 }
706 
OnPageChanged(WidgetsBookCtrlEvent & event)707 void WidgetsFrame::OnPageChanged(WidgetsBookCtrlEvent& event)
708 {
709     const int sel = event.GetSelection();
710 
711     // adjust "Page" menu selection
712     wxMenuItem *item = GetMenuBar()->FindItem(Widgets_GoToPage + sel);
713     if ( item )
714         item->Check();
715 
716     GetMenuBar()->Check(Widgets_BusyCursor, false);
717 
718     // create the pages on demand, otherwise the sample startup is too slow as
719     // it creates hundreds of controls
720     WidgetsPage *page = CurrentPage();
721     if ( page->GetChildren().empty() )
722     {
723         wxWindowUpdateLocker noUpdates(page);
724         page->CreateContent();
725         //page->Layout();
726         page->GetSizer()->Fit(page);
727 
728         WidgetsBookCtrl *book = wxStaticCast(page->GetParent(), WidgetsBookCtrl);
729         wxSize size;
730         for ( size_t i = 0; i < book->GetPageCount(); ++i )
731         {
732             wxWindow *page = book->GetPage(i);
733             if ( page )
734             {
735                 size.IncTo(page->GetSize());
736             }
737         }
738         page->SetSize(size);
739     }
740 
741     event.Skip();
742 }
743 
OnGoToPage(wxCommandEvent & event)744 void WidgetsFrame::OnGoToPage(wxCommandEvent& event)
745 {
746 #if USE_TREEBOOK
747     m_book->SetSelection(event.GetId() - Widgets_GoToPage);
748 #else
749     m_book->SetSelection(m_book->GetPageCount()-1);
750     WidgetsBookCtrl *book = wxStaticCast(m_book->GetCurrentPage(), WidgetsBookCtrl);
751     book->SetSelection(event.GetId() - Widgets_GoToPage);
752 #endif
753 }
754 
755 #if wxUSE_TOOLTIPS
756 
OnSetTooltip(wxCommandEvent & WXUNUSED (event))757 void WidgetsFrame::OnSetTooltip(wxCommandEvent& WXUNUSED(event))
758 {
759     static wxString s_tip = wxT("This is a tooltip");
760 
761     wxTextEntryDialog dialog
762                       (
763                         this,
764                         wxT("Tooltip text (may use \\n, leave empty to remove): "),
765                         wxT("Widgets sample"),
766                         s_tip
767                       );
768 
769     if ( dialog.ShowModal() != wxID_OK )
770         return;
771 
772     s_tip = dialog.GetValue();
773     s_tip.Replace(wxT("\\n"), wxT("\n"));
774 
775     WidgetsPage *page = CurrentPage();
776 
777     const Widgets widgets = page->GetWidgets();
778     for ( Widgets::const_iterator it = widgets.begin();
779           it != widgets.end();
780           ++it )
781     {
782         (*it)->SetToolTip(s_tip);
783     }
784 }
785 
786 #endif // wxUSE_TOOLTIPS
787 
788 namespace
789 {
790 
791 // Trivial wrapper for wxGetColourFromUser() which also does something even if
792 // the colour dialog is not available in the current build (which may happen
793 // for the ports in development and it is still useful to see how colours work)
GetColourFromUser(wxWindow * parent,const wxColour & colDefault)794 wxColour GetColourFromUser(wxWindow *parent, const wxColour& colDefault)
795 {
796 #if wxUSE_COLOURDLG
797     return wxGetColourFromUser(parent, colDefault);
798 #else // !wxUSE_COLOURDLG
799     if ( colDefault == *wxBLACK )
800         return *wxWHITE;
801     else
802         return *wxBLACK;
803 #endif // wxUSE_COLOURDLG/!wxUSE_COLOURDLG
804 }
805 
806 } // anonymous namespace
807 
OnSetFgCol(wxCommandEvent & WXUNUSED (event))808 void WidgetsFrame::OnSetFgCol(wxCommandEvent& WXUNUSED(event))
809 {
810     // allow for debugging the default colour the first time this is called
811     WidgetsPage *page = CurrentPage();
812 
813     if (!m_colFg.IsOk())
814         m_colFg = page->GetForegroundColour();
815 
816     wxColour col = GetColourFromUser(this, m_colFg);
817     if ( !col.IsOk() )
818         return;
819 
820     m_colFg = col;
821 
822     const Widgets widgets = page->GetWidgets();
823     for ( Widgets::const_iterator it = widgets.begin();
824           it != widgets.end();
825           ++it )
826     {
827         (*it)->SetForegroundColour(m_colFg);
828         (*it)->Refresh();
829     }
830 }
831 
OnSetBgCol(wxCommandEvent & WXUNUSED (event))832 void WidgetsFrame::OnSetBgCol(wxCommandEvent& WXUNUSED(event))
833 {
834     WidgetsPage *page = CurrentPage();
835 
836     if ( !m_colBg.IsOk() )
837         m_colBg = page->GetBackgroundColour();
838 
839     wxColour col = GetColourFromUser(this, m_colBg);
840     if ( !col.IsOk() )
841         return;
842 
843     m_colBg = col;
844 
845     const Widgets widgets = page->GetWidgets();
846     for ( Widgets::const_iterator it = widgets.begin();
847           it != widgets.end();
848           ++it )
849     {
850         (*it)->SetBackgroundColour(m_colBg);
851         (*it)->Refresh();
852     }
853 }
854 
OnSetPageBg(wxCommandEvent & WXUNUSED (event))855 void WidgetsFrame::OnSetPageBg(wxCommandEvent& WXUNUSED(event))
856 {
857     wxColour col = GetColourFromUser(this, GetBackgroundColour());
858     if ( !col.IsOk() )
859         return;
860 
861     CurrentPage()->SetBackgroundColour(col);
862     CurrentPage()->Refresh();
863 }
864 
OnSetFont(wxCommandEvent & WXUNUSED (event))865 void WidgetsFrame::OnSetFont(wxCommandEvent& WXUNUSED(event))
866 {
867 #if wxUSE_FONTDLG
868     WidgetsPage *page = CurrentPage();
869 
870     if (!m_font.IsOk())
871         m_font = page->GetFont();
872 
873     wxFont font = wxGetFontFromUser(this, m_font);
874     if ( !font.IsOk() )
875         return;
876 
877     m_font = font;
878 
879     const Widgets widgets = page->GetWidgets();
880     for ( Widgets::const_iterator it = widgets.begin();
881           it != widgets.end();
882           ++it )
883     {
884         (*it)->SetFont(m_font);
885         (*it)->Refresh();
886     }
887 
888     // The best size of the widget could have changed after changing its font,
889     // so re-layout to show it correctly.
890     page->Layout();
891 #else
892     wxLogMessage(wxT("Font selection dialog not available in current build."));
893 #endif
894 }
895 
OnEnable(wxCommandEvent & event)896 void WidgetsFrame::OnEnable(wxCommandEvent& event)
897 {
898     const Widgets widgets = CurrentPage()->GetWidgets();
899     for ( Widgets::const_iterator it = widgets.begin();
900           it != widgets.end();
901           ++it )
902     {
903         (*it)->Enable(event.IsChecked());
904     }
905 }
906 
OnSetBorder(wxCommandEvent & event)907 void WidgetsFrame::OnSetBorder(wxCommandEvent& event)
908 {
909     int border;
910     switch ( event.GetId() )
911     {
912         case Widgets_BorderNone:   border = wxBORDER_NONE;   break;
913         case Widgets_BorderStatic: border = wxBORDER_STATIC; break;
914         case Widgets_BorderSimple: border = wxBORDER_SIMPLE; break;
915         case Widgets_BorderRaised: border = wxBORDER_RAISED; break;
916         case Widgets_BorderSunken: border = wxBORDER_SUNKEN; break;
917         case Widgets_BorderDouble: border = wxBORDER_DOUBLE; break;
918 
919         default:
920             wxFAIL_MSG( wxT("unknown border style") );
921             // fall through
922 
923         case Widgets_BorderDefault: border = wxBORDER_DEFAULT; break;
924     }
925 
926     WidgetsPage::ms_defaultFlags &= ~wxBORDER_MASK;
927     WidgetsPage::ms_defaultFlags |= border;
928 
929     WidgetsPage *page = CurrentPage();
930 
931     page->RecreateWidget();
932 }
933 
OnToggleLayoutDirection(wxCommandEvent & event)934 void WidgetsFrame::OnToggleLayoutDirection(wxCommandEvent& event)
935 {
936     wxLayoutDirection dir = event.IsChecked() ? wxLayout_RightToLeft
937                                               : wxLayout_LeftToRight;
938 
939     const Widgets widgets = CurrentPage()->GetWidgets();
940     for ( Widgets::const_iterator it = widgets.begin();
941           it != widgets.end();
942           ++it )
943     {
944         (*it)->SetLayoutDirection(dir);
945         (*it)->Refresh();
946     }
947 }
948 
OnToggleGlobalBusyCursor(wxCommandEvent & event)949 void WidgetsFrame::OnToggleGlobalBusyCursor(wxCommandEvent& event)
950 {
951     if ( event.IsChecked() )
952         wxBeginBusyCursor();
953     else
954         wxEndBusyCursor();
955 }
956 
OnToggleBusyCursor(wxCommandEvent & event)957 void WidgetsFrame::OnToggleBusyCursor(wxCommandEvent& event)
958 {
959     wxCursor cursor(*(event.IsChecked() ? wxHOURGLASS_CURSOR
960                                         : wxSTANDARD_CURSOR));
961 
962     const Widgets widgets = CurrentPage()->GetWidgets();
963     for ( Widgets::const_iterator it = widgets.begin();
964           it != widgets.end();
965           ++it )
966     {
967         (*it)->SetCursor(cursor);
968     }
969 }
970 
OnDisableAutoComplete(wxCommandEvent & WXUNUSED (event))971 void WidgetsFrame::OnDisableAutoComplete(wxCommandEvent& WXUNUSED(event))
972 {
973     wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
974     wxCHECK_RET( entry, "menu item should be disabled" );
975 
976     if ( entry->AutoComplete(wxArrayString()) )
977     {
978         wxLogMessage("Disabled auto completion.");
979     }
980     else
981     {
982         wxLogMessage("AutoComplete() failed.");
983     }
984 }
985 
OnAutoCompleteFixed(wxCommandEvent & WXUNUSED (event))986 void WidgetsFrame::OnAutoCompleteFixed(wxCommandEvent& WXUNUSED(event))
987 {
988     wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
989     wxCHECK_RET( entry, "menu item should be disabled" );
990 
991     wxArrayString completion_choices;
992 
993     // add a few strings so a completion occurs on any letter typed
994     for ( char idxc = 'a'; idxc < 'z'; ++idxc )
995         completion_choices.push_back(wxString::Format("%c%c", idxc, idxc));
996 
997     completion_choices.push_back("is this string for test?");
998     completion_choices.push_back("this is a test string");
999     completion_choices.push_back("this is another test string");
1000     completion_choices.push_back("this string is for test");
1001 
1002     if ( entry->AutoComplete(completion_choices) )
1003     {
1004         wxLogMessage("Enabled auto completion of a set of fixed strings.");
1005     }
1006     else
1007     {
1008         wxLogMessage("AutoComplete() failed.");
1009     }
1010 }
1011 
OnAutoCompleteFilenames(wxCommandEvent & WXUNUSED (event))1012 void WidgetsFrame::OnAutoCompleteFilenames(wxCommandEvent& WXUNUSED(event))
1013 {
1014     wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
1015     wxCHECK_RET( entry, "menu item should be disabled" );
1016 
1017     if ( entry->AutoCompleteFileNames() )
1018     {
1019         wxLogMessage("Enabled auto completion of file names.");
1020     }
1021     else
1022     {
1023         wxLogMessage("AutoCompleteFileNames() failed.");
1024     }
1025 }
1026 
OnAutoCompleteDirectories(wxCommandEvent & WXUNUSED (event))1027 void WidgetsFrame::OnAutoCompleteDirectories(wxCommandEvent& WXUNUSED(event))
1028 {
1029     wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
1030     wxCHECK_RET( entry, "menu item should be disabled" );
1031 
1032     if ( entry->AutoCompleteDirectories() )
1033     {
1034         wxLogMessage("Enabled auto completion of directories.");
1035     }
1036     else
1037     {
1038         wxLogMessage("AutoCompleteDirectories() failed.");
1039     }
1040 }
1041 
OnAutoCompleteCustom(wxCommandEvent & WXUNUSED (event))1042 void WidgetsFrame::OnAutoCompleteCustom(wxCommandEvent& WXUNUSED(event))
1043 {
1044     wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
1045     wxCHECK_RET( entry, "menu item should be disabled" );
1046 
1047     // This is a simple (and hence rather useless) example of a custom
1048     // completer class that completes the first word (only) initially and only
1049     // build the list of the possible second words once the first word is
1050     // known. This allows to avoid building the full 676000 item list of
1051     // possible strings all at once as the we have 1000 possibilities for the
1052     // first word (000..999) and 676 (aa..zz) for the second one.
1053     class CustomTextCompleter : public wxTextCompleterSimple
1054     {
1055     public:
1056         virtual void GetCompletions(const wxString& prefix, wxArrayString& res)
1057         {
1058             // This is used for illustrative purposes only and shows how many
1059             // completions we return every time when we're called.
1060             class LogCompletions
1061             {
1062             public:
1063                 LogCompletions(const wxString& prefix, const wxArrayString& res)
1064                     : m_prefix(prefix),
1065                       m_res(res)
1066                 {
1067                 }
1068 
1069                 ~LogCompletions()
1070                 {
1071                     wxLogMessage("Returning %lu possible completions for "
1072                                  "prefix \"%s\"",
1073                                  m_res.size(), m_prefix);
1074                 }
1075 
1076             private:
1077                 const wxString& m_prefix;
1078                 const wxArrayString& m_res;
1079             } logCompletions(prefix, res);
1080 
1081 
1082             // Normally it doesn't make sense to complete empty control, there
1083             // are too many choices and listing them all wouldn't be helpful.
1084             if ( prefix.empty() )
1085                 return;
1086 
1087             // The only valid strings start with 3 digits so check for their
1088             // presence proposing to complete the remaining ones.
1089             if ( !wxIsdigit(prefix[0]) )
1090                 return;
1091 
1092             if ( prefix.length() == 1 )
1093             {
1094                 for ( int i = 0; i < 10; i++ )
1095                     for ( int j = 0; j < 10; j++ )
1096                         res.push_back(wxString::Format("%s%02d",
1097                                                        prefix, 10*i + j));
1098                 return;
1099             }
1100             else if ( !wxIsdigit(prefix[1]) )
1101                 return;
1102 
1103             if ( prefix.length() == 2 )
1104             {
1105                 for ( int i = 0; i < 10; i++ )
1106                     res.push_back(wxString::Format("%s%d", prefix, i));
1107                 return;
1108             }
1109             else if ( !wxIsdigit(prefix[2]) )
1110                 return;
1111 
1112             // Next we must have a space and two letters.
1113             wxString prefix2(prefix);
1114             if ( prefix.length() == 3 )
1115                 prefix2 += ' ';
1116             else if ( prefix[3] != ' ' )
1117                 return;
1118 
1119             if ( prefix2.length() == 4 )
1120             {
1121                 for ( char c = 'a'; c <= 'z'; c++ )
1122                     for ( char d = 'a'; d <= 'z'; d++ )
1123                         res.push_back(wxString::Format("%s%c%c", prefix2, c, d));
1124                 return;
1125             }
1126             else if ( !wxIslower(prefix[4]) )
1127                 return;
1128 
1129             if ( prefix.length() == 5 )
1130             {
1131                 for ( char c = 'a'; c <= 'z'; c++ )
1132                     res.push_back(prefix + c);
1133             }
1134         }
1135     };
1136 
1137     if ( entry->AutoComplete(new CustomTextCompleter) )
1138     {
1139         wxLogMessage("Enabled custom auto completer for \"NNN XX\" items "
1140                      "(where N is a digit and X is a letter).");
1141     }
1142     else
1143     {
1144         wxLogMessage("AutoComplete() failed.");
1145     }
1146 }
1147 
OnSetHint(wxCommandEvent & WXUNUSED (event))1148 void WidgetsFrame::OnSetHint(wxCommandEvent& WXUNUSED(event))
1149 {
1150     wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
1151     wxCHECK_RET( entry, "menu item should be disabled" );
1152 
1153     static wxString s_hint("Type here");
1154     wxString
1155         hint = wxGetTextFromUser("Text hint:", "Widgets sample", s_hint, this);
1156     if ( hint.empty() )
1157         return;
1158 
1159     s_hint = hint;
1160 
1161     if ( entry->SetHint(hint) )
1162     {
1163         wxLogMessage("Set hint to \"%s\".", hint);
1164     }
1165     else
1166     {
1167         wxLogMessage("Text hints not supported.");
1168     }
1169 }
1170 
1171 #endif // wxUSE_MENUS
1172 
1173 // ----------------------------------------------------------------------------
1174 // WidgetsPageInfo
1175 // ----------------------------------------------------------------------------
1176 
WidgetsPageInfo(Constructor ctor,const wxChar * label,int categories)1177 WidgetsPageInfo::WidgetsPageInfo(Constructor ctor, const wxChar *label, int categories)
1178                : m_label(label)
1179                , m_categories(categories)
1180 {
1181     m_ctor = ctor;
1182 
1183     m_next = NULL;
1184 
1185     // dummy sorting: add and immediately sort in the list according to label
1186     if ( WidgetsPage::ms_widgetPages )
1187     {
1188         WidgetsPageInfo *node_prev = WidgetsPage::ms_widgetPages;
1189         if ( wxStrcmp(label, node_prev->GetLabel().c_str()) < 0 )
1190         {
1191             // add as first
1192             m_next = node_prev;
1193             WidgetsPage::ms_widgetPages = this;
1194         }
1195         else
1196         {
1197             WidgetsPageInfo *node_next;
1198             do
1199             {
1200                 node_next = node_prev->GetNext();
1201                 if ( node_next )
1202                 {
1203                     // add if between two
1204                     if ( wxStrcmp(label, node_next->GetLabel().c_str()) < 0 )
1205                     {
1206                         node_prev->SetNext(this);
1207                         m_next = node_next;
1208                         // force to break loop
1209                         node_next = NULL;
1210                     }
1211                 }
1212                 else
1213                 {
1214                     // add as last
1215                     node_prev->SetNext(this);
1216                     m_next = node_next;
1217                 }
1218                 node_prev = node_next;
1219             }
1220             while ( node_next );
1221         }
1222     }
1223     else
1224     {
1225         // add when first
1226         WidgetsPage::ms_widgetPages = this;
1227     }
1228 }
1229 
1230 // ----------------------------------------------------------------------------
1231 // WidgetsPage
1232 // ----------------------------------------------------------------------------
1233 
1234 int WidgetsPage::ms_defaultFlags = wxBORDER_DEFAULT;
1235 WidgetsPageInfo *WidgetsPage::ms_widgetPages = NULL;
1236 
WidgetsPage(WidgetsBookCtrl * book,wxImageList * imaglist,const char * const icon[])1237 WidgetsPage::WidgetsPage(WidgetsBookCtrl *book,
1238                          wxImageList *imaglist,
1239                          const char *const icon[])
1240            : wxPanel(book, wxID_ANY,
1241                      wxDefaultPosition, wxDefaultSize,
1242                      wxNO_FULL_REPAINT_ON_RESIZE |
1243                      wxCLIP_CHILDREN |
1244                      wxTAB_TRAVERSAL)
1245 {
1246 #if USE_ICONS_IN_BOOK
1247     imaglist->Add(wxBitmap(wxImage(icon).Scale(ICON_SIZE, ICON_SIZE)));
1248 #else
1249     wxUnusedVar(imaglist);
1250     wxUnusedVar(icon);
1251 #endif
1252 }
1253 
CreateSizerWithText(wxControl * control,wxWindowID id,wxTextCtrl ** ppText)1254 wxSizer *WidgetsPage::CreateSizerWithText(wxControl *control,
1255                                           wxWindowID id,
1256                                           wxTextCtrl **ppText)
1257 {
1258     wxSizer *sizerRow = new wxBoxSizer(wxHORIZONTAL);
1259     wxTextCtrl *text = new wxTextCtrl(this, id, wxEmptyString,
1260         wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
1261 
1262     sizerRow->Add(control, 0, wxRIGHT | wxALIGN_CENTRE_VERTICAL, 5);
1263     sizerRow->Add(text, 1, wxLEFT | wxALIGN_CENTRE_VERTICAL, 5);
1264 
1265     if ( ppText )
1266         *ppText = text;
1267 
1268     return sizerRow;
1269 }
1270 
1271 // create a sizer containing a label and a text ctrl
CreateSizerWithTextAndLabel(const wxString & label,wxWindowID id,wxTextCtrl ** ppText)1272 wxSizer *WidgetsPage::CreateSizerWithTextAndLabel(const wxString& label,
1273                                                   wxWindowID id,
1274                                                   wxTextCtrl **ppText)
1275 {
1276     return CreateSizerWithText(new wxStaticText(this, wxID_ANY, label),
1277         id, ppText);
1278 }
1279 
1280 // create a sizer containing a button and a text ctrl
CreateSizerWithTextAndButton(wxWindowID idBtn,const wxString & label,wxWindowID id,wxTextCtrl ** ppText)1281 wxSizer *WidgetsPage::CreateSizerWithTextAndButton(wxWindowID idBtn,
1282                                                    const wxString& label,
1283                                                    wxWindowID id,
1284                                                    wxTextCtrl **ppText)
1285 {
1286     return CreateSizerWithText(new wxButton(this, idBtn, label), id, ppText);
1287 }
1288 
CreateCheckBoxAndAddToSizer(wxSizer * sizer,const wxString & label,wxWindowID id)1289 wxCheckBox *WidgetsPage::CreateCheckBoxAndAddToSizer(wxSizer *sizer,
1290                                                      const wxString& label,
1291                                                      wxWindowID id)
1292 {
1293     wxCheckBox *checkbox = new wxCheckBox(this, id, label);
1294     sizer->Add(checkbox, 0, wxLEFT | wxRIGHT, 5);
1295     sizer->Add(0, 2, 0, wxGROW); // spacer
1296 
1297     return checkbox;
1298 }
1299