]> git.saurik.com Git - wxWidgets.git/blame - samples/treelist/treelist.cpp
Support using GetTextExtent() with empty string to get descent in wxOSX.
[wxWidgets.git] / samples / treelist / treelist.cpp
CommitLineData
524cb040
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: samples/treelist/treelist.cpp
3// Purpose: Sample showing wxTreeListCtrl.
4// Author: Vadim Zeitlin
5// Created: 2011-08-19
6// RCS-ID: $Id$
7// Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
8// Licence: wxWindows licence
9///////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// Declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// Headers
17// ----------------------------------------------------------------------------
18
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
22 #pragma hdrstop
23#endif
24
25#if !wxUSE_TREELISTCTRL
26 #error "wxUSE_TREELISTCTRL must be 1 for this sample."
27#endif
28
29#ifndef WX_PRECOMP
30 #include "wx/app.h"
31 #include "wx/frame.h"
32 #include "wx/log.h"
33 #include "wx/menu.h"
34 #include "wx/sizer.h"
35 #include "wx/statusbr.h"
36 #include "wx/textctrl.h"
37#endif
38
39#include "wx/treelist.h"
40
41#include "wx/aboutdlg.h"
42#include "wx/artprov.h"
43
44// ----------------------------------------------------------------------------
45// Resources
46// ----------------------------------------------------------------------------
47
e7092398 48#ifndef wxHAS_IMAGES_IN_RESOURCES
524cb040
VZ
49 #include "../sample.xpm"
50#endif
51
52// ----------------------------------------------------------------------------
da2e758f 53// Constants
524cb040
VZ
54// ----------------------------------------------------------------------------
55
da2e758f 56// Menu items.
524cb040
VZ
57enum
58{
59 Id_MultiSelect = 100,
26693191 60 Id_FlatList,
524cb040
VZ
61
62 Id_Checkboxes_Start,
63 Id_NoCheckboxes = Id_Checkboxes_Start,
64 Id_Checkboxes2State,
65 Id_Checkboxes3State,
66 Id_CheckboxesUser3State,
67 Id_Checkboxes_End,
68
69 Id_DumpSelection,
70 Id_Check_HTMLDocs,
71 Id_Uncheck_HTMLDocs,
72 Id_Indet_HTMLDocs,
73 Id_Select_HTMLDocs
74};
75
da2e758f
VZ
76// Tree list columns.
77enum
78{
79 Col_Component,
80 Col_Files,
81 Col_Size
82};
83
84// ----------------------------------------------------------------------------
85// Custom comparator for tree list items comparison
86// ----------------------------------------------------------------------------
87
88// This is a toy class as in a real program you would have the original numeric
89// data somewhere and wouldn't need to parse it back from strings presumably.
90// Nevertheless it shows how to implement a custom comparator which is needed
91// if you want to sort by a column with non-textual contents.
92class MyComparator : public wxTreeListItemComparator
93{
94public:
95 virtual int
96 Compare(wxTreeListCtrl* treelist,
97 unsigned column,
98 wxTreeListItem item1,
99 wxTreeListItem item2)
100 {
101 wxString text1 = treelist->GetItemText(item1, column),
102 text2 = treelist->GetItemText(item2, column);
103
104 switch ( column )
105 {
106 case Col_Component:
107 // Simple alphabetical comparison is fine for those.
108 return text1.CmpNoCase(text2);
109
110 case Col_Files:
111 // Compare strings as numbers.
112 return GetNumFilesFromText(text1) - GetNumFilesFromText(text2);
113
114 case Col_Size:
115 // Compare strings as numbers but also take care of "KiB" and
116 // "MiB" suffixes.
117 return GetSizeFromText(text1) - GetSizeFromText(text2);
118 }
119
120 wxFAIL_MSG( "Sorting on unknown column?" );
121
122 return 0;
123 }
124
125private:
126 // Return the number of files handling special value "many". Notice that
127 // the returned value is signed to allow using it in subtraction above.
128 int GetNumFilesFromText(const wxString& text) const
129 {
130 unsigned long n;
131 if ( !text.ToULong(&n) )
132 {
133 if ( text == "many" )
134 n = 9999;
135 else
136 n = 0;
137 }
138
139 return n;
140 }
141
142 // Return the size in KiB from a string with either KiB or MiB suffix.
143 int GetSizeFromText(const wxString& text) const
144 {
145 wxString size;
146 unsigned factor = 1;
147 if ( text.EndsWith(" MiB", &size) )
148 factor = 1024;
149 else if ( !text.EndsWith(" KiB", &size) )
150 return 0;
151
152 unsigned long n = 0;
153 size.ToULong(&n);
154
155 return n*factor;
156 }
157};
158
524cb040
VZ
159// ----------------------------------------------------------------------------
160// Application class
161// ----------------------------------------------------------------------------
162
163class MyApp : public wxApp
164{
165public:
166 virtual bool OnInit();
167};
168
169// ----------------------------------------------------------------------------
170// Main window class
171// ----------------------------------------------------------------------------
172
173class MyFrame : public wxFrame
174{
175public:
176 MyFrame();
177 virtual ~MyFrame();
178
179private:
180 // Event handlers for the menu and wxTreeListCtrl events.
181 void OnMultiSelect(wxCommandEvent& event);
26693191 182 void OnFlatList(wxCommandEvent& event);
524cb040
VZ
183 void OnCheckboxes(wxCommandEvent& event);
184 void OnDumpSelection(wxCommandEvent& event);
185 void OnCheckHTMLDocs(wxCommandEvent& event);
186 void OnSelectHTMLDocs(wxCommandEvent& event);
187
188 void OnAbout(wxCommandEvent& event);
189 void OnExit(wxCommandEvent& event);
190
191 void OnSelectionChanged(wxTreeListEvent& event);
192 void OnItemExpanding(wxTreeListEvent& event);
193 void OnItemExpanded(wxTreeListEvent& event);
194 void OnItemChecked(wxTreeListEvent& event);
195 void OnItemActivated(wxTreeListEvent& event);
196 void OnItemContextMenu(wxTreeListEvent& event);
197
198
199 enum
200 {
201 Icon_File,
202 Icon_FolderClosed,
203 Icon_FolderOpened
204 };
205
206 // Create the image list, called once only. Should add images to it in the
207 // same order as they appear in the enum above.
208 void InitImageList();
209
210 // Create the control with the given styles.
211 wxTreeListCtrl* CreateTreeListCtrl(long style);
212
213 // Recreate an already existing control.
214 void RecreateTreeListCtrl(long style);
215
216 // Helper: return the text of the item or "NONE" if the item is invalid.
217 wxString DumpItem(wxTreeListItem item) const;
218
219 // Another helper: just translate wxCheckBoxState to user-readable text.
220 static const char* CheckedStateString(wxCheckBoxState state);
221
222
223 wxImageList* m_imageList;
224
225 wxTreeListCtrl* m_treelist;
226
da2e758f
VZ
227 MyComparator m_comparator;
228
524cb040
VZ
229 wxTreeListItem m_itemHTMLDocs;
230
231 wxLog* m_oldLogTarget;
232
26693191
VZ
233 bool m_isFlat;
234
524cb040
VZ
235 wxDECLARE_EVENT_TABLE();
236};
237
238// ============================================================================
239// Implementation
240// ============================================================================
241
242// ----------------------------------------------------------------------------
243// Application class
244// ----------------------------------------------------------------------------
245
246wxIMPLEMENT_APP(MyApp);
247
248bool MyApp::OnInit()
249{
250 if ( !wxApp::OnInit() )
251 return false;
252
253 new MyFrame;
254
255 return true;
256}
257
258// ----------------------------------------------------------------------------
259// Main window class
260// ----------------------------------------------------------------------------
261
262BEGIN_EVENT_TABLE(MyFrame, wxFrame)
263 EVT_MENU(Id_MultiSelect, MyFrame::OnMultiSelect)
26693191 264 EVT_MENU(Id_FlatList, MyFrame::OnFlatList)
524cb040
VZ
265 EVT_MENU_RANGE(Id_Checkboxes_Start, Id_Checkboxes_End,
266 MyFrame::OnCheckboxes)
267
268 EVT_MENU(Id_DumpSelection, MyFrame::OnDumpSelection)
269 EVT_MENU_RANGE(Id_Check_HTMLDocs, Id_Indet_HTMLDocs,
270 MyFrame::OnCheckHTMLDocs)
271 EVT_MENU(Id_Select_HTMLDocs, MyFrame::OnSelectHTMLDocs)
272
273 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
274 EVT_MENU(wxID_EXIT, MyFrame::OnExit)
275
276 EVT_TREELIST_SELECTION_CHANGED(wxID_ANY, MyFrame::OnSelectionChanged)
277 EVT_TREELIST_ITEM_EXPANDING(wxID_ANY, MyFrame::OnItemExpanding)
278 EVT_TREELIST_ITEM_EXPANDED(wxID_ANY, MyFrame::OnItemExpanded)
279 EVT_TREELIST_ITEM_CHECKED(wxID_ANY, MyFrame::OnItemChecked)
280 EVT_TREELIST_ITEM_ACTIVATED(wxID_ANY, MyFrame::OnItemActivated)
281 EVT_TREELIST_ITEM_CONTEXT_MENU(wxID_ANY, MyFrame::OnItemContextMenu)
282END_EVENT_TABLE()
283
284MyFrame::MyFrame()
285 : wxFrame(NULL, wxID_ANY, "wxWidgets tree/list control sample",
286 wxDefaultPosition, wxSize(600, 450))
287{
26693191
VZ
288 m_isFlat = false;
289
524cb040
VZ
290 // Create menus and status bar.
291 SetIcon(wxICON(sample));
292
293 wxMenu* fileMenu = new wxMenu;
294 fileMenu->Append(wxID_EXIT);
295
296 wxMenu* treeStyle = new wxMenu;
297 treeStyle->AppendCheckItem(Id_MultiSelect, "&Multiple selections\tCtrl-M");
298 treeStyle->AppendSeparator();
299 treeStyle->AppendRadioItem(Id_NoCheckboxes,
300 "&No checkboxes\tCtrl-1");
301 treeStyle->AppendRadioItem(Id_Checkboxes2State,
302 "&2-state checkboxes\tCtrl-2");
303 treeStyle->AppendRadioItem(Id_Checkboxes3State,
304 "&3-state checkboxes\tCtrl-3");
305 treeStyle->AppendRadioItem(Id_CheckboxesUser3State,
306 "&User-settable 3-state checkboxes\tCtrl-4");
26693191
VZ
307 treeStyle->AppendSeparator();
308 treeStyle->AppendCheckItem(Id_FlatList, "&Flat list");
524cb040
VZ
309
310 wxMenu* treeOper = new wxMenu;
311 treeOper->Append(Id_DumpSelection, "&Dump selection\tCtrl-D");
312 treeOper->AppendSeparator();
313 treeOper->Append(Id_Check_HTMLDocs, "&Check Doc/HTML item\tCtrl-C");
314 treeOper->Append(Id_Uncheck_HTMLDocs, "&Uncheck Doc/HTML item\tCtrl-U");
315 treeOper->Append(Id_Indet_HTMLDocs, "Make Doc/HTML &indeterminate\tCtrl-I");
316 treeOper->Append(Id_Select_HTMLDocs, "&Select Doc/HTML item\tCtrl-S");
317
318 wxMenu* helpMenu = new wxMenu;
319 helpMenu->Append(wxID_ABOUT);
320
321 wxMenuBar* menuBar = new wxMenuBar();
322 menuBar->Append(fileMenu, "&File");
323 menuBar->Append(treeStyle, "&Style");
324 menuBar->Append(treeOper, "&Operations");
325 menuBar->Append(helpMenu, "&Help");
326 SetMenuBar(menuBar);
327
328 CreateStatusBar(1);
329
330
331 // Construct the image list with the standard images.
332 InitImageList();
333
334
335 // Create and layout child controls.
336 m_treelist = CreateTreeListCtrl(wxTL_DEFAULT_STYLE);
337
338 wxTextCtrl* textLog = new wxTextCtrl(this, wxID_ANY, "",
339 wxDefaultPosition, wxDefaultSize,
340 wxTE_READONLY | wxTE_MULTILINE);
341 m_oldLogTarget = wxLog::SetActiveTarget(new wxLogTextCtrl(textLog));
342
343 wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
344 sizer->Add(m_treelist, wxSizerFlags(2).Expand());
345 sizer->Add(textLog, wxSizerFlags(1).Expand());
346 SetSizer(sizer);
347
348
349 // Finally show everything.
350 Show();
351}
352
353MyFrame::~MyFrame()
354{
355 delete m_imageList;
356
357 delete wxLog::SetActiveTarget(m_oldLogTarget);
358}
359
360void MyFrame::InitImageList()
361{
362 wxSize iconSize = wxArtProvider::GetSizeHint(wxART_LIST);
363 if ( iconSize == wxDefaultSize )
364 iconSize = wxSize(16, 16);
365
366 m_imageList = new wxImageList(iconSize.x, iconSize.y);
367
368 // The order should be the same as for the enum elements.
369 static const char* const icons[] =
370 {
371 wxART_NORMAL_FILE,
372 wxART_FOLDER,
373 wxART_FOLDER_OPEN
374 };
375
376 for ( unsigned n = 0; n < WXSIZEOF(icons); n++ )
377 {
378 m_imageList->Add
379 (
380 wxArtProvider::GetIcon(icons[n], wxART_LIST, iconSize)
381 );
382 }
383}
384
385wxTreeListCtrl* MyFrame::CreateTreeListCtrl(long style)
386{
387 wxTreeListCtrl* const
388 tree = new wxTreeListCtrl(this, wxID_ANY,
389 wxDefaultPosition, wxDefaultSize,
390 style);
391 tree->SetImageList(m_imageList);
392
da2e758f
VZ
393 tree->AppendColumn("Component",
394 wxCOL_WIDTH_AUTOSIZE,
395 wxALIGN_LEFT,
396 wxCOL_RESIZABLE | wxCOL_SORTABLE);
524cb040
VZ
397 tree->AppendColumn("# Files",
398 tree->WidthFor("1,000,000"),
da2e758f
VZ
399 wxALIGN_RIGHT,
400 wxCOL_RESIZABLE | wxCOL_SORTABLE);
524cb040
VZ
401 tree->AppendColumn("Size",
402 tree->WidthFor("1,000,000 KiB"),
da2e758f
VZ
403 wxALIGN_RIGHT,
404 wxCOL_RESIZABLE | wxCOL_SORTABLE);
524cb040
VZ
405
406 // Define a shortcut to save on typing here.
407 #define ADD_ITEM(item, parent, files, size) \
26693191
VZ
408 wxTreeListItem item = tree->AppendItem(m_isFlat ? root : parent, \
409 #item, \
524cb040
VZ
410 Icon_FolderClosed, \
411 Icon_FolderOpened); \
412 tree->SetItemText(item, Col_Files, files); \
413 tree->SetItemText(item, Col_Size, size)
414
415 wxTreeListItem root = tree->GetRootItem();
416 ADD_ITEM(Code, root, "", "");
417 ADD_ITEM(wxMSW, Code, "313", "3.94 MiB");
418 ADD_ITEM(wxGTK, Code, "180", "1.66 MiB");
419
420 ADD_ITEM(wxOSX, Code, "265", "2.36 MiB");
421 ADD_ITEM(Core, wxOSX, "31", "347 KiB");
422 ADD_ITEM(Carbon, wxOSX, "91", "1.34 MiB");
423 ADD_ITEM(Cocoa, wxOSX, "46", "512 KiB");
424
425 ADD_ITEM(Documentation, root, "", "");
426 ADD_ITEM(HTML, Documentation, "many", "");
427 ADD_ITEM(CHM, Documentation, "1", "");
428
429 ADD_ITEM(Samples, root, "", "");
430 ADD_ITEM(minimal, Samples, "1", "7 KiB");
431 ADD_ITEM(widgets, Samples, "28", "419 KiB");
432
433 #undef ADD_ITEM
434
435 // Remember this one for subsequent tests.
436 m_itemHTMLDocs = HTML;
437
da2e758f
VZ
438 // Set a custom comparator to compare strings containing numbers correctly.
439 tree->SetItemComparator(&m_comparator);
440
524cb040
VZ
441 return tree;
442}
443
444void MyFrame::RecreateTreeListCtrl(long style)
445{
446 wxTreeListCtrl* const treelist = CreateTreeListCtrl(style);
447 GetSizer()->Replace(m_treelist, treelist);
448
449 delete m_treelist;
450 m_treelist = treelist;
451
452 Layout();
453}
454
455void MyFrame::OnMultiSelect(wxCommandEvent& event)
456{
457 long style = m_treelist->GetWindowStyle();
458
459 if ( event.IsChecked() )
460 style |= wxTL_MULTIPLE;
461 else
462 style &= ~wxTL_MULTIPLE;
463
464 RecreateTreeListCtrl(style);
465}
466
26693191
VZ
467void MyFrame::OnFlatList(wxCommandEvent& event)
468{
469 m_isFlat = event.IsChecked();
470
471 RecreateTreeListCtrl(m_treelist->GetWindowStyle());
472}
473
524cb040
VZ
474void MyFrame::OnCheckboxes(wxCommandEvent& event)
475{
476 long style = m_treelist->GetWindowStyle();
477 style &= ~(wxTL_CHECKBOX | wxTL_3STATE | wxTL_USER_3STATE);
478
479 switch ( event.GetId() )
480 {
481 case Id_NoCheckboxes:
482 break;
483
484 case Id_Checkboxes2State:
485 style |= wxTL_CHECKBOX;
486 break;
487
488 case Id_Checkboxes3State:
489 style |= wxTL_3STATE;
490 break;
491
492 case Id_CheckboxesUser3State:
493 style |= wxTL_USER_3STATE;
494 break;
495
496 default:
497 wxFAIL_MSG( "Unknown checkbox style" );
498 return;
499 }
500
501 RecreateTreeListCtrl(style);
502}
503
504void MyFrame::OnDumpSelection(wxCommandEvent& WXUNUSED(event))
505{
506 if ( m_treelist->HasFlag(wxTL_MULTIPLE) )
507 {
508 wxTreeListItems selections;
509 const unsigned numSelected = m_treelist->GetSelections(selections);
510
511 switch ( numSelected )
512 {
513 case 0:
514 wxLogMessage("No items selected");
515 break;
516
517 case 1:
518 wxLogMessage("Single item selected: %s",
519 DumpItem(selections[0]));
520 break;
521
522 default:
523 wxLogMessage("%u items selected:", numSelected);
524 for ( unsigned n = 0; n < numSelected; n++ )
525 {
526 wxLogMessage("\t%s", DumpItem(selections[n]));
527 }
528 }
529 }
530 else // Single selection
531 {
532 wxLogMessage("Selection: %s", DumpItem(m_treelist->GetSelection()));
533 }
534}
535
536void MyFrame::OnCheckHTMLDocs(wxCommandEvent& event)
537{
538 wxCheckBoxState state;
539
540 switch ( event.GetId() )
541 {
542 case Id_Uncheck_HTMLDocs:
543 state = wxCHK_UNCHECKED;
544 break;
545
546 case Id_Check_HTMLDocs:
547 state = wxCHK_CHECKED;
548 break;
549
550 case Id_Indet_HTMLDocs:
551 state = wxCHK_UNDETERMINED;
552 break;
553
554 default:
555 wxFAIL_MSG( "Unknown check state" );
556 return;
557 }
558
559 m_treelist->CheckItem(m_itemHTMLDocs, state);
560}
561
562void MyFrame::OnSelectHTMLDocs(wxCommandEvent& WXUNUSED(event))
563{
564 m_treelist->Select(m_itemHTMLDocs);
565}
566
567void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
568{
569 wxAboutDialogInfo info;
570 info.SetDescription("wxTreeListCtrl wxWidgets sample.");
571 info.SetCopyright("(C) 2011 Vadim Zeitlin <vadim@wxwidgets.org>");
572
573 wxAboutBox(info);
574}
575
576void MyFrame::OnExit(wxCommandEvent& WXUNUSED(event))
577{
578 Close(true);
579}
580
581wxString MyFrame::DumpItem(wxTreeListItem item) const
582{
583 return item.IsOk() ? m_treelist->GetItemText(item) : wxString("NONE");
584}
585
586/* static */
587const char* MyFrame::CheckedStateString(wxCheckBoxState state)
588{
589 switch ( state )
590 {
591 case wxCHK_UNCHECKED:
592 return "unchecked";
593
594 case wxCHK_UNDETERMINED:
595 return "undetermined";
596
597 case wxCHK_CHECKED:
598 return "checked";
599 }
600
601 return "invalid";
602}
603
604void MyFrame::OnSelectionChanged(wxTreeListEvent& event)
605{
d50fc4dc
VZ
606 const char* msg;
607
608 if ( m_treelist->HasFlag(wxTL_MULTIPLE) )
609 msg = "Selection of the \"%s\" item changed.";
610 else
611 msg = "Selection changed, now is \"%s\".";
612
613 wxLogMessage(msg, DumpItem(event.GetItem()));
524cb040
VZ
614}
615
616void MyFrame::OnItemExpanding(wxTreeListEvent& event)
617{
618 wxLogMessage("Item \"%s\" is expanding", DumpItem(event.GetItem()));
619}
620
621void MyFrame::OnItemExpanded(wxTreeListEvent& event)
622{
623 wxLogMessage("Item \"%s\" expanded", DumpItem(event.GetItem()));
624}
625
626void MyFrame::OnItemChecked(wxTreeListEvent& event)
627{
628 wxTreeListItem item = event.GetItem();
629
630 wxLogMessage("Item \"%s\" toggled, now %s (was %s)",
631 DumpItem(item),
632 CheckedStateString(m_treelist->GetCheckedState(item)),
633 CheckedStateString(event.GetOldCheckedState()));
634}
635
636void MyFrame::OnItemActivated(wxTreeListEvent& event)
637{
638 wxLogMessage("Item \"%s\" activated", DumpItem(event.GetItem()));
639}
640
641void MyFrame::OnItemContextMenu(wxTreeListEvent& event)
642{
643 enum
644 {
645 Id_Check_Item,
646 Id_Uncheck_Item,
647 Id_Indet_Item,
648 Id_Check_Recursively,
649 Id_Update_Parent
650 };
651
652 wxMenu menu;
653 menu.Append(Id_Check_Item, "&Check item");
654 menu.Append(Id_Uncheck_Item, "&Uncheck item");
655 if ( m_treelist->HasFlag(wxTL_3STATE) )
656 menu.Append(Id_Indet_Item, "Make item &indeterminate");
657 menu.AppendSeparator();
658 menu.Append(Id_Check_Recursively, "Check &recursively");
659 menu.Append(Id_Update_Parent, "Update &parent");
660
661 const wxTreeListItem item = event.GetItem();
662 switch ( m_treelist->GetPopupMenuSelectionFromUser(menu) )
663 {
664 case Id_Check_Item:
665 m_treelist->CheckItem(item);
666 break;
667
668 case Id_Uncheck_Item:
669 m_treelist->UncheckItem(item);
670 break;
671
672 case Id_Indet_Item:
673 m_treelist->CheckItem(item, wxCHK_UNDETERMINED);
674 break;
675
676 case Id_Check_Recursively:
677 m_treelist->CheckItemRecursively(item);
678 break;
679
680 case Id_Update_Parent:
681 m_treelist->UpdateItemParentStateRecursively(item);
682 break;
683
684 default:
685 wxFAIL_MSG( "Unexpected menu selection" );
686 // Fall through.
687
688 case wxID_NONE:
689 return;
690 }
691}