Only link with libwxscintilla if using Scintilla is enabled.
[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 // 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
48 #ifndef wxHAS_IMAGES_IN_RESOURCES
49 #include "../sample.xpm"
50 #endif
51
52 // ----------------------------------------------------------------------------
53 // Constants
54 // ----------------------------------------------------------------------------
55
56 // Menu items.
57 enum
58 {
59 Id_MultiSelect = 100,
60 Id_FlatList,
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
76 // Tree list columns.
77 enum
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.
92 class MyComparator : public wxTreeListItemComparator
93 {
94 public:
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
125 private:
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
159 // ----------------------------------------------------------------------------
160 // Application class
161 // ----------------------------------------------------------------------------
162
163 class MyApp : public wxApp
164 {
165 public:
166 virtual bool OnInit();
167 };
168
169 // ----------------------------------------------------------------------------
170 // Main window class
171 // ----------------------------------------------------------------------------
172
173 class MyFrame : public wxFrame
174 {
175 public:
176 MyFrame();
177 virtual ~MyFrame();
178
179 private:
180 // Event handlers for the menu and wxTreeListCtrl events.
181 void OnMultiSelect(wxCommandEvent& event);
182 void OnFlatList(wxCommandEvent& event);
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
227 MyComparator m_comparator;
228
229 wxTreeListItem m_itemHTMLDocs;
230
231 wxLog* m_oldLogTarget;
232
233 bool m_isFlat;
234
235 wxDECLARE_EVENT_TABLE();
236 };
237
238 // ============================================================================
239 // Implementation
240 // ============================================================================
241
242 // ----------------------------------------------------------------------------
243 // Application class
244 // ----------------------------------------------------------------------------
245
246 wxIMPLEMENT_APP(MyApp);
247
248 bool 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
262 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
263 EVT_MENU(Id_MultiSelect, MyFrame::OnMultiSelect)
264 EVT_MENU(Id_FlatList, MyFrame::OnFlatList)
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)
282 END_EVENT_TABLE()
283
284 MyFrame::MyFrame()
285 : wxFrame(NULL, wxID_ANY, "wxWidgets tree/list control sample",
286 wxDefaultPosition, wxSize(600, 450))
287 {
288 m_isFlat = false;
289
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");
307 treeStyle->AppendSeparator();
308 treeStyle->AppendCheckItem(Id_FlatList, "&Flat list");
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
353 MyFrame::~MyFrame()
354 {
355 delete m_imageList;
356
357 delete wxLog::SetActiveTarget(m_oldLogTarget);
358 }
359
360 void 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
385 wxTreeListCtrl* 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
393 tree->AppendColumn("Component",
394 wxCOL_WIDTH_AUTOSIZE,
395 wxALIGN_LEFT,
396 wxCOL_RESIZABLE | wxCOL_SORTABLE);
397 tree->AppendColumn("# Files",
398 tree->WidthFor("1,000,000"),
399 wxALIGN_RIGHT,
400 wxCOL_RESIZABLE | wxCOL_SORTABLE);
401 tree->AppendColumn("Size",
402 tree->WidthFor("1,000,000 KiB"),
403 wxALIGN_RIGHT,
404 wxCOL_RESIZABLE | wxCOL_SORTABLE);
405
406 // Define a shortcut to save on typing here.
407 #define ADD_ITEM(item, parent, files, size) \
408 wxTreeListItem item = tree->AppendItem(m_isFlat ? root : parent, \
409 #item, \
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
438 // Set a custom comparator to compare strings containing numbers correctly.
439 tree->SetItemComparator(&m_comparator);
440
441 return tree;
442 }
443
444 void 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
455 void 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
467 void MyFrame::OnFlatList(wxCommandEvent& event)
468 {
469 m_isFlat = event.IsChecked();
470
471 RecreateTreeListCtrl(m_treelist->GetWindowStyle());
472 }
473
474 void 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
504 void 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
536 void 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
562 void MyFrame::OnSelectHTMLDocs(wxCommandEvent& WXUNUSED(event))
563 {
564 m_treelist->Select(m_itemHTMLDocs);
565 }
566
567 void 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
576 void MyFrame::OnExit(wxCommandEvent& WXUNUSED(event))
577 {
578 Close(true);
579 }
580
581 wxString MyFrame::DumpItem(wxTreeListItem item) const
582 {
583 return item.IsOk() ? m_treelist->GetItemText(item) : wxString("NONE");
584 }
585
586 /* static */
587 const 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
604 void MyFrame::OnSelectionChanged(wxTreeListEvent& event)
605 {
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()));
614 }
615
616 void MyFrame::OnItemExpanding(wxTreeListEvent& event)
617 {
618 wxLogMessage("Item \"%s\" is expanding", DumpItem(event.GetItem()));
619 }
620
621 void MyFrame::OnItemExpanded(wxTreeListEvent& event)
622 {
623 wxLogMessage("Item \"%s\" expanded", DumpItem(event.GetItem()));
624 }
625
626 void 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
636 void MyFrame::OnItemActivated(wxTreeListEvent& event)
637 {
638 wxLogMessage("Item \"%s\" activated", DumpItem(event.GetItem()));
639 }
640
641 void 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 }