1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxListBox sample
4 // Author: Vadim Zeitlin
6 // Copyright: (c) 2000 Vadim Zeitlin
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
13 +1. horz scrollbar doesn't appear in listbox
14 +2. truncating text ctrl doesn't update display
15 +3. deleting last listbox item doesn't update display
16 4. text ctrl background corrupted after resize
19 // ============================================================================
21 // ============================================================================
23 // ----------------------------------------------------------------------------
25 // ----------------------------------------------------------------------------
27 // for compilers that support precompilation, includes "wx/wx.h".
28 #include "wx/wxprec.h"
34 // for all others, include the necessary headers
39 #include "wx/dcclient.h"
41 #include "wx/button.h"
42 #include "wx/checkbox.h"
43 #include "wx/checklst.h"
44 #include "wx/listbox.h"
45 #include "wx/radiobox.h"
46 #include "wx/radiobut.h"
47 #include "wx/statbox.h"
48 #include "wx/stattext.h"
49 #include "wx/textctrl.h"
54 #ifdef __WXUNIVERSAL__
55 #include "wx/univ/theme.h"
56 #endif // __WXUNIVERSAL__
58 // ----------------------------------------------------------------------------
60 // ----------------------------------------------------------------------------
84 // ----------------------------------------------------------------------------
86 // ----------------------------------------------------------------------------
88 // Define a new application type, each program should derive a class from wxApp
89 class LboxTestApp
: public wxApp
92 // override base class virtuals
93 // ----------------------------
95 // this one is called on application startup and is a good place for the app
96 // initialization (doing it here and not in the ctor allows to have an error
97 // return: if OnInit() returns false, the application terminates)
98 virtual bool OnInit();
101 // Define a new frame type: this is going to be our main frame
102 class LboxTestFrame
: public wxFrame
106 LboxTestFrame(const wxString
& title
);
107 virtual ~LboxTestFrame();
111 void OnButtonReset(wxCommandEvent
& event
);
112 void OnButtonCreate(wxCommandEvent
& event
);
113 void OnButtonChange(wxCommandEvent
& event
);
114 void OnButtonDelete(wxCommandEvent
& event
);
115 void OnButtonDeleteSel(wxCommandEvent
& event
);
116 void OnButtonClear(wxCommandEvent
& event
);
118 void OnButtonClearLog(wxCommandEvent
& event
);
120 void OnButtonAdd(wxCommandEvent
& event
);
121 void OnButtonAddSeveral(wxCommandEvent
& event
);
122 void OnButtonAddMany(wxCommandEvent
& event
);
123 void OnButtonQuit(wxCommandEvent
& event
);
125 void OnListbox(wxCommandEvent
& event
);
126 void OnListboxDClick(wxCommandEvent
& event
);
127 void OnListboxRDown(wxMouseEvent
& event
);
129 void OnCheckOrRadioBox(wxCommandEvent
& event
);
131 void OnUpdateUIAddSeveral(wxUpdateUIEvent
& event
);
132 void OnUpdateUICreateButton(wxUpdateUIEvent
& event
);
133 void OnUpdateUIClearButton(wxUpdateUIEvent
& event
);
134 void OnUpdateUIDeleteButton(wxUpdateUIEvent
& event
);
135 void OnUpdateUIDeleteSelButton(wxUpdateUIEvent
& event
);
137 // reset the listbox parameters
140 // (re)create the listbox
143 // listbox parameters
144 // ------------------
146 // the selection mode
154 // should it be sorted?
157 // should it have horz scroll/vert scrollbar permanently shown?
161 // should the recreate button be enabled?
167 // the sel mode radiobox
168 wxRadioBox
*m_radioSelMode
;
171 wxCheckBox
*m_chkSort
,
175 // the listbox itself and the sizer it is in
177 wxSizer
*m_sizerLbox
;
179 // panel the controls such as the listbox are in
183 // the listbox for logging messages
184 wxListBox
*m_lboxLog
;
187 // the text entries for "Add/change string" and "Delete" buttons
188 wxTextCtrl
*m_textAdd
,
194 // the log target we use to redirect messages to the listbox
198 // any class wishing to process wxWidgets events must use this macro
199 DECLARE_EVENT_TABLE()
203 // A log target which just redirects the messages to a listbox
204 class LboxLogger
: public wxLog
207 LboxLogger(wxListBox
*lbox
, wxLog
*logOld
)
210 //m_lbox->Disable(); -- looks ugly under MSW
214 virtual ~LboxLogger()
216 wxLog::SetActiveTarget(m_logOld
);
220 // implement sink functions
221 virtual void DoLog(wxLogLevel level
, const wxChar
*szString
, time_t t
)
223 // don't put trace messages into listbox or we can get into infinite
225 if ( level
== wxLOG_Trace
)
229 // cast is needed to call protected method
230 ((LboxLogger
*)m_logOld
)->DoLog(level
, szString
, t
);
235 wxLog::DoLog(level
, szString
, t
);
239 virtual void DoLogString(const wxChar
*szString
, time_t WXUNUSED(t
))
244 #ifdef __WXUNIVERSAL__
245 m_lbox
->AppendAndEnsureVisible(msg
);
246 #else // other ports don't have this method yet
249 // SetFirstItem() isn't implemented in wxGTK
251 m_lbox
->SetFirstItem(m_lbox
->GetCount() - 1);
256 // the control we use
259 // the old log target
264 // ----------------------------------------------------------------------------
266 // ----------------------------------------------------------------------------
268 IMPLEMENT_APP(LboxTestApp
)
270 // ----------------------------------------------------------------------------
272 // ----------------------------------------------------------------------------
274 BEGIN_EVENT_TABLE(LboxTestFrame
, wxFrame
)
275 EVT_BUTTON(LboxTest_Reset
, LboxTestFrame::OnButtonReset
)
276 EVT_BUTTON(LboxTest_Create
, LboxTestFrame::OnButtonCreate
)
277 EVT_BUTTON(LboxTest_Change
, LboxTestFrame::OnButtonChange
)
278 EVT_BUTTON(LboxTest_Delete
, LboxTestFrame::OnButtonDelete
)
279 EVT_BUTTON(LboxTest_DeleteSel
, LboxTestFrame::OnButtonDeleteSel
)
280 EVT_BUTTON(LboxTest_Clear
, LboxTestFrame::OnButtonClear
)
282 EVT_BUTTON(LboxTest_ClearLog
, LboxTestFrame::OnButtonClearLog
)
284 EVT_BUTTON(LboxTest_Add
, LboxTestFrame::OnButtonAdd
)
285 EVT_BUTTON(LboxTest_AddSeveral
, LboxTestFrame::OnButtonAddSeveral
)
286 EVT_BUTTON(LboxTest_AddMany
, LboxTestFrame::OnButtonAddMany
)
287 EVT_BUTTON(LboxTest_Quit
, LboxTestFrame::OnButtonQuit
)
289 EVT_TEXT_ENTER(LboxTest_AddText
, LboxTestFrame::OnButtonAdd
)
290 EVT_TEXT_ENTER(LboxTest_DeleteText
, LboxTestFrame::OnButtonDelete
)
292 EVT_UPDATE_UI_RANGE(LboxTest_Reset
, LboxTest_Create
,
293 LboxTestFrame::OnUpdateUICreateButton
)
295 EVT_UPDATE_UI(LboxTest_AddSeveral
, LboxTestFrame::OnUpdateUIAddSeveral
)
296 EVT_UPDATE_UI(LboxTest_Clear
, LboxTestFrame::OnUpdateUIClearButton
)
297 EVT_UPDATE_UI(LboxTest_DeleteText
, LboxTestFrame::OnUpdateUIClearButton
)
298 EVT_UPDATE_UI(LboxTest_Delete
, LboxTestFrame::OnUpdateUIDeleteButton
)
299 EVT_UPDATE_UI(LboxTest_Change
, LboxTestFrame::OnUpdateUIDeleteSelButton
)
300 EVT_UPDATE_UI(LboxTest_ChangeText
, LboxTestFrame::OnUpdateUIDeleteSelButton
)
301 EVT_UPDATE_UI(LboxTest_DeleteSel
, LboxTestFrame::OnUpdateUIDeleteSelButton
)
303 EVT_LISTBOX(LboxTest_Listbox
, LboxTestFrame::OnListbox
)
304 EVT_LISTBOX_DCLICK(wxID_ANY
, LboxTestFrame::OnListboxDClick
)
305 EVT_CHECKBOX(wxID_ANY
, LboxTestFrame::OnCheckOrRadioBox
)
306 EVT_RADIOBOX(wxID_ANY
, LboxTestFrame::OnCheckOrRadioBox
)
309 // ============================================================================
311 // ============================================================================
313 // ----------------------------------------------------------------------------
315 // ----------------------------------------------------------------------------
317 bool LboxTestApp::OnInit()
319 wxFrame
*frame
= new LboxTestFrame(_T("wxListBox sample"));
323 //wxLog::AddTraceMask(_T("listbox"));
324 wxLog::AddTraceMask(_T("scrollbar"));
330 // ----------------------------------------------------------------------------
331 // top level frame class
332 // ----------------------------------------------------------------------------
334 LboxTestFrame::LboxTestFrame(const wxString
& title
)
335 : wxFrame(NULL
, wxID_ANY
, title
, wxPoint(100, 100))
339 m_radioSelMode
= (wxRadioBox
*)NULL
;
343 m_chkSort
= (wxCheckBox
*)NULL
;
345 m_lbox
= (wxListBox
*)NULL
;
347 m_lboxLog
= (wxListBox
*)NULL
;
349 m_sizerLbox
= (wxSizer
*)NULL
;
352 m_logTarget
= (wxLog
*)NULL
;
355 m_panel
= new wxPanel(this, wxID_ANY
);
358 What we create here is a frame having 3 panes: the explanatory pane to
359 the left allowing to set the listbox styles and recreate the control,
360 the pane containing the listbox itself and the lower pane containing
361 the buttons which allow to add/change/delete strings to/from it.
363 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
),
364 *sizerUp
= new wxBoxSizer(wxHORIZONTAL
),
366 *sizerRight
= new wxBoxSizer(wxVERTICAL
);
369 static const wxString modes
[] =
376 wxStaticBox
*box
= new wxStaticBox(m_panel
, wxID_ANY
, _T("&Set listbox parameters"));
377 m_radioSelMode
= new wxRadioBox(m_panel
, wxID_ANY
, _T("Selection &mode:"),
378 wxDefaultPosition
, wxDefaultSize
,
379 WXSIZEOF(modes
), modes
,
380 1, wxRA_SPECIFY_COLS
);
382 m_chkVScroll
= new wxCheckBox(m_panel
, wxID_ANY
, _T("Always show &vertical scrollbar"));
383 m_chkHScroll
= new wxCheckBox(m_panel
, wxID_ANY
, _T("Show &horizontal scrollbar"));
384 m_chkSort
= new wxCheckBox(m_panel
, wxID_ANY
, _T("&Sort items"));
386 sizerLeft
= new wxStaticBoxSizer(box
, wxVERTICAL
);
388 sizerLeft
->Add(m_chkVScroll
, 0, wxLEFT
| wxRIGHT
, 5);
389 sizerLeft
->Add(m_chkHScroll
, 0, wxLEFT
| wxRIGHT
, 5);
390 sizerLeft
->Add(m_chkSort
, 0, wxLEFT
| wxRIGHT
, 5);
391 sizerLeft
->Add(5, 5, 0, wxGROW
| wxALL
, 5); // spacer
392 sizerLeft
->Add(m_radioSelMode
, 0, wxGROW
| wxALL
, 5);
394 wxSizer
*sizerBtn
= new wxBoxSizer(wxHORIZONTAL
);
395 wxButton
*btn
= new wxButton(m_panel
, LboxTest_Reset
, _T("&Reset"));
396 sizerBtn
->Add(btn
, 0, wxLEFT
| wxRIGHT
, 5);
397 btn
= new wxButton(m_panel
, LboxTest_Create
, _T("&Create"));
398 sizerBtn
->Add(btn
, 0, wxLEFT
| wxRIGHT
, 5);
399 sizerLeft
->Add(sizerBtn
, 0, wxALIGN_CENTRE_HORIZONTAL
| wxALL
, 15);
402 wxStaticBox
*box2
= new wxStaticBox(m_panel
, wxID_ANY
, _T("&Change listbox contents"));
403 wxSizer
*sizerMiddle
= new wxStaticBoxSizer(box2
, wxVERTICAL
);
405 wxSizer
*sizerRow
= new wxBoxSizer(wxHORIZONTAL
);
406 btn
= new wxButton(m_panel
, LboxTest_Add
, _T("&Add this string"));
407 m_textAdd
= new wxTextCtrl(m_panel
, LboxTest_AddText
, _T("test item 0"));
408 sizerRow
->Add(btn
, 0, wxRIGHT
, 5);
409 sizerRow
->Add(m_textAdd
, 1, wxLEFT
, 5);
410 sizerMiddle
->Add(sizerRow
, 0, wxALL
| wxGROW
, 5);
412 btn
= new wxButton(m_panel
, LboxTest_AddSeveral
, _T("&Insert a few strings"));
413 sizerMiddle
->Add(btn
, 0, wxALL
| wxGROW
, 5);
415 btn
= new wxButton(m_panel
, LboxTest_AddMany
, _T("Add &many strings"));
416 sizerMiddle
->Add(btn
, 0, wxALL
| wxGROW
, 5);
418 sizerRow
= new wxBoxSizer(wxHORIZONTAL
);
419 btn
= new wxButton(m_panel
, LboxTest_Change
, _T("C&hange current"));
420 m_textChange
= new wxTextCtrl(m_panel
, LboxTest_ChangeText
, wxEmptyString
);
421 sizerRow
->Add(btn
, 0, wxRIGHT
, 5);
422 sizerRow
->Add(m_textChange
, 1, wxLEFT
, 5);
423 sizerMiddle
->Add(sizerRow
, 0, wxALL
| wxGROW
, 5);
425 sizerRow
= new wxBoxSizer(wxHORIZONTAL
);
426 btn
= new wxButton(m_panel
, LboxTest_Delete
, _T("&Delete this item"));
427 m_textDelete
= new wxTextCtrl(m_panel
, LboxTest_DeleteText
, wxEmptyString
);
428 sizerRow
->Add(btn
, 0, wxRIGHT
, 5);
429 sizerRow
->Add(m_textDelete
, 1, wxLEFT
, 5);
430 sizerMiddle
->Add(sizerRow
, 0, wxALL
| wxGROW
, 5);
432 btn
= new wxButton(m_panel
, LboxTest_DeleteSel
, _T("Delete &selection"));
433 sizerMiddle
->Add(btn
, 0, wxALL
| wxGROW
, 5);
435 btn
= new wxButton(m_panel
, LboxTest_Clear
, _T("&Clear"));
436 sizerMiddle
->Add(btn
, 0, wxALL
| wxGROW
, 5);
439 sizerRight
->SetMinSize(250, 0);
440 m_sizerLbox
= sizerRight
; // save it to modify it later
442 // the 3 panes panes compose the upper part of the window
443 sizerUp
->Add(sizerLeft
, 0, wxGROW
| (wxALL
& ~wxLEFT
), 10);
444 sizerUp
->Add(sizerMiddle
, 1, wxGROW
| wxALL
, 10);
445 sizerUp
->Add(sizerRight
, 1, wxGROW
| (wxALL
& ~wxRIGHT
), 10);
447 // the lower one only has the log listbox and a button to clear it
449 wxSizer
*sizerDown
= new wxStaticBoxSizer
451 new wxStaticBox(m_panel
, wxID_ANY
, _T("&Log window")),
454 m_lboxLog
= new wxListBox(m_panel
, wxID_ANY
);
455 sizerDown
->Add(m_lboxLog
, 1, wxGROW
| wxALL
, 5);
457 wxSizer
*sizerDown
= new wxBoxSizer(wxVERTICAL
);
459 wxBoxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
461 btn
= new wxButton(m_panel
, LboxTest_ClearLog
, _T("Clear &log"));
463 sizerBtns
->Add(10, 0); // spacer
465 btn
= new wxButton(m_panel
, LboxTest_Quit
, _T("E&xit"));
467 sizerDown
->Add(sizerBtns
, 0, wxALL
| wxALIGN_RIGHT
, 5);
469 // put everything together
470 sizerTop
->Add(sizerUp
, 1, wxGROW
| (wxALL
& ~wxBOTTOM
), 10);
471 sizerTop
->Add(0, 5, 0, wxGROW
); // spacer in between
472 sizerTop
->Add(sizerDown
, 0, wxGROW
| (wxALL
& ~wxTOP
), 10);
474 // final initialization and create the listbox
478 m_panel
->SetSizer(sizerTop
);
481 sizerTop
->SetSizeHints(this);
484 // now that everything is created we can redirect the log messages to the
486 m_logTarget
= new LboxLogger(m_lboxLog
, wxLog::GetActiveTarget());
487 wxLog::SetActiveTarget(m_logTarget
);
491 LboxTestFrame::~LboxTestFrame()
498 // ----------------------------------------------------------------------------
500 // ----------------------------------------------------------------------------
502 void LboxTestFrame::Reset()
504 if ( m_radioSelMode
->GetSelection() == LboxSel_Single
&&
505 !m_chkSort
->GetValue() &&
506 m_chkHScroll
->GetValue() &&
507 !m_chkVScroll
->GetValue() )
513 m_radioSelMode
->SetSelection(LboxSel_Single
);
514 m_chkSort
->SetValue(false);
515 m_chkHScroll
->SetValue(true);
516 m_chkVScroll
->SetValue(false);
521 void LboxTestFrame::CreateLbox()
524 switch ( m_radioSelMode
->GetSelection() )
527 wxFAIL_MSG( _T("unexpected radio box selection") );
529 case LboxSel_Single
: flags
|= wxLB_SINGLE
; break;
530 case LboxSel_Extended
: flags
|= wxLB_EXTENDED
; break;
531 case LboxSel_Multiple
: flags
|= wxLB_MULTIPLE
; break;
534 if ( m_chkVScroll
->GetValue() )
535 flags
|= wxLB_ALWAYS_SB
;
536 if ( m_chkHScroll
->GetValue() )
537 flags
|= wxLB_HSCROLL
;
538 if ( m_chkSort
->GetValue() )
543 if ( m_lbox
) // cache old items to restore later if listbox existed
545 int count
= m_lbox
->GetCount();
546 for ( int n
= 0; n
< count
; n
++ )
548 items
.Add(m_lbox
->GetString(n
));
551 m_sizerLbox
->Detach(m_lbox
);
555 m_lbox
= new wxListBox(m_panel
, LboxTest_Listbox
,
556 wxDefaultPosition
, wxDefaultSize
,
561 m_sizerLbox
->Add(m_lbox
, 1, wxGROW
| wxALL
, 5);
562 m_sizerLbox
->Layout();
566 m_lbox
->Connect(wxEVT_RIGHT_DOWN
,
567 wxMouseEventHandler(LboxTestFrame::OnListboxRDown
), NULL
, this);
570 // ----------------------------------------------------------------------------
572 // ----------------------------------------------------------------------------
574 void LboxTestFrame::OnButtonQuit(wxCommandEvent
& WXUNUSED(event
))
579 void LboxTestFrame::OnButtonReset(wxCommandEvent
& WXUNUSED(event
))
584 void LboxTestFrame::OnButtonCreate(wxCommandEvent
& WXUNUSED(event
))
589 void LboxTestFrame::OnButtonChange(wxCommandEvent
& WXUNUSED(event
))
591 wxArrayInt selections
;
592 int count
= m_lbox
->GetSelections(selections
);
593 wxString s
= m_textChange
->GetValue();
594 for ( int n
= 0; n
< count
; n
++ )
596 m_lbox
->SetString(selections
[n
], s
);
600 void LboxTestFrame::OnButtonDelete(wxCommandEvent
& WXUNUSED(event
))
603 if ( !m_textDelete
->GetValue().ToULong(&n
) ||
604 (n
>= (unsigned)m_lbox
->GetCount()) )
612 void LboxTestFrame::OnButtonDeleteSel(wxCommandEvent
& WXUNUSED(event
))
614 wxArrayInt selections
;
615 int n
= m_lbox
->GetSelections(selections
);
618 m_lbox
->Delete(selections
[--n
]);
622 void LboxTestFrame::OnButtonClear(wxCommandEvent
& WXUNUSED(event
))
628 void LboxTestFrame::OnButtonClearLog(wxCommandEvent
& WXUNUSED(event
))
634 void LboxTestFrame::OnButtonAdd(wxCommandEvent
& WXUNUSED(event
))
636 static size_t s_item
= 0;
638 wxString s
= m_textAdd
->GetValue();
639 if ( !m_textAdd
->IsModified() )
641 // update the default string
642 m_textAdd
->SetValue(wxString::Format(_T("test item %u"), ++s_item
));
648 void LboxTestFrame::OnButtonAddMany(wxCommandEvent
& WXUNUSED(event
))
650 // "many" means 1000 here
651 for ( size_t n
= 0; n
< 1000; n
++ )
653 m_lbox
->Append(wxString::Format(_T("item #%u"), n
));
657 void LboxTestFrame::OnButtonAddSeveral(wxCommandEvent
& WXUNUSED(event
))
660 items
.Add(_T("First"));
661 items
.Add(_T("another one"));
662 items
.Add(_T("and the last (very very very very very very very very very very long) one"));
663 m_lbox
->InsertItems(items
, 0);
666 void LboxTestFrame::OnUpdateUICreateButton(wxUpdateUIEvent
& event
)
668 event
.Enable(m_dirty
);
671 void LboxTestFrame::OnUpdateUIDeleteButton(wxUpdateUIEvent
& event
)
674 event
.Enable(m_textDelete
->GetValue().ToULong(&n
) &&
675 (n
< (unsigned)m_lbox
->GetCount()));
678 void LboxTestFrame::OnUpdateUIDeleteSelButton(wxUpdateUIEvent
& event
)
680 wxArrayInt selections
;
681 event
.Enable(m_lbox
->GetSelections(selections
) != 0);
684 void LboxTestFrame::OnUpdateUIClearButton(wxUpdateUIEvent
& event
)
686 event
.Enable(m_lbox
->GetCount() != 0);
689 void LboxTestFrame::OnUpdateUIAddSeveral(wxUpdateUIEvent
& event
)
691 event
.Enable(!(m_lbox
->GetWindowStyle() & wxLB_SORT
));
694 void LboxTestFrame::OnListbox(wxCommandEvent
& event
)
696 int sel
= event
.GetInt();
697 m_textDelete
->SetValue(wxString::Format(_T("%d"), sel
));
699 wxLogMessage(_T("Listbox item %d selected"), sel
);
702 void LboxTestFrame::OnListboxDClick(wxCommandEvent
& event
)
704 int sel
= event
.GetInt();
705 wxLogMessage(_T("Listbox item %d double clicked"), sel
);
708 void LboxTestFrame::OnListboxRDown(wxMouseEvent
& event
)
710 int item
= m_lbox
->HitTest(event
.GetPosition());
712 if ( item
!= wxNOT_FOUND
)
713 wxLogMessage(_T("Listbox item %d right clicked"), item
);
715 wxLogMessage(_T("Listbox right clicked but no item clicked upon"));
720 void LboxTestFrame::OnCheckOrRadioBox(wxCommandEvent
& WXUNUSED(event
))