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