native_windows_gui/controls/
treeview.rs

1/*!
2A tree-view control is a window that displays a hierarchical list of items
3*/
4
5use winapi::shared::minwindef::{WPARAM, LPARAM};
6use winapi::um::winuser::{WS_VISIBLE, WS_DISABLED, WS_TABSTOP};
7use winapi::um::commctrl::{HTREEITEM, TVIS_EXPANDED, TVIS_SELECTED, TVS_SHOWSELALWAYS, TVITEMW};
8use crate::win32::window_helper as wh;
9use crate::win32::base_helper::{check_hwnd, to_utf16, from_utf16};
10use crate::{Font, NwgError};
11use super::{ControlBase, ControlHandle};
12use std::{mem, ptr};
13
14#[cfg(feature="image-list")]
15use winapi::um::commctrl::HIMAGELIST;
16
17#[cfg(feature="image-list")]
18use crate::ImageList;
19
20const NOT_BOUND: &'static str = "TreeView is not yet bound to a winapi object";
21const BAD_HANDLE: &'static str = "INTERNAL ERROR: TreeView handle is not HWND!";
22
23
24bitflags! {
25    /**
26        The tree view flags
27
28        * VISIBLE:  The tree view is immediatly visible after creation
29        * DISABLED: The tree view cannot be interacted with by the user. It also has a grayed out look.
30        * TAB_STOP: The tree view can be selected using tab navigation
31    */
32    pub struct TreeViewFlags: u32 {
33        const VISIBLE = WS_VISIBLE;
34        const DISABLED = WS_DISABLED;
35        const TAB_STOP = WS_TABSTOP;
36        const ALWAYS_SHOW_SELECTION = TVS_SHOWSELALWAYS;
37    }
38}
39
40bitflags! {
41    /**
42        A tree item state
43
44        * SELECTED:  The tree view is immediatly visible after creation
45        * DISABLED: The tree view cannot be interacted with by the user. It also has a grayed out look.
46        * TAB_STOP: The tree view can be selected using tab navigation
47    */
48    pub struct TreeItemState: u32 {
49        const SELECTED = TVIS_SELECTED;
50        const EXPANDED = TVIS_EXPANDED;
51    }
52}
53
54
55/// Select the position of a new item that is about to be inserted in a TreeView
56#[derive(Copy, Clone, Debug)]
57pub enum TreeInsert {
58    /// Inserts the item at the beginning of the list. 
59    First,
60
61    /// Inserts the item at the end of the list. 
62    Last,
63
64    /// Add the item as a root item 
65    Root,
66
67    /// Inserts the item into the list in alphabetical order
68    Sort,
69
70    /// Insert the item after the choosen item
71    After(HTREEITEM)
72}
73
74/// Possible state of a tree item regarding the "expanded/collapsed" state
75#[derive(Copy, Clone, Debug)]
76#[repr(u8)]
77pub enum ExpandState {
78    Collapse,
79    CollapseReset,
80    Expand,
81    ExpandPartial,
82    Toggle
83}
84
85
86/// An action that can be applied to a tree item. Used in events
87#[derive(Copy, Clone, Debug)]
88pub enum TreeItemAction {
89    /// An unexpected value was passed to NWG
90    Unknown,
91
92    /// A tree item was expanded or collapsed. 
93    Expand(ExpandState),
94
95    /// The state of the item was changed
96    State { old: TreeItemState, new: TreeItemState }
97}
98
99/// A reference to an item in a TreeView
100#[derive(Debug)]
101pub struct TreeItem {
102    pub handle: HTREEITEM
103}
104
105impl TreeItem {
106    /// Checks if the inner handle is null
107    pub fn is_null(&self) -> bool {
108        self.handle.is_null()
109    }
110}
111
112/**
113A tree-view control is a window that displays a hierarchical list of items.
114
115While a treeview can support selected multiple item programatically (using `select_item`), this is not fully supported
116by the winapi implementation.
117
118Requires the `tree-view` feature
119
120**Builder parameters:**
121  * `parent`:     **Required.** The tree-view parent container.
122  * `position`:   The treeview position.
123  * `enabled`:    If the treeview can be used by the user. It also has a grayed out look if disabled.
124  * `focus`:      The control receive focus after being created
125  * `flags`:      A combination of the `TreeViewFlags` values.
126  * `ex_flags`:   A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
127  * `font`:       The font used for the treeview text
128  * `parent`:     The treeview parent container.
129  * `image_list`: Image list containing the icon to use in the tree-view
130
131**Control events:**
132  * `MousePress(_)`: Generic mouse press events on the tree view
133  * `OnMouseMove`: Generic mouse mouse event
134  * `OnMouseWheel`: Generic mouse wheel event
135  * `OnTreeViewClick`: When the user has clicked the left mouse button within the control.
136  * `OnTreeViewDoubleClick`: When the user has clicked the left mouse button within the control twice rapidly.
137  * `OnTreeViewRightClick`: When the user has clicked the right mouse button within the control.
138  * `OnTreeFocusLost`: When the control has lost the input focus
139  * `OnTreeFocus`: When the control has acquired the input focus
140  * `OnTreeItemDelete`: Just before an item is deleted. Also sent for all the children.
141  * `OnTreeItemExpanded`: After an item was expanded or collapsed. Sends a `EventData::OnTreeItemUpdate`.
142  * `OnTreeItemChanged`: After the state of an item was changed. Sends a `EventData::OnTreeItemUpdate`.
143  * `OnTreeItemSelectionChanged`: After the current selection was changed. Sends a `EventData::OnTreeItemChanged`.
144*/
145#[derive(Default, PartialEq, Eq)]
146pub struct TreeView {
147    pub handle: ControlHandle
148} 
149
150
151impl TreeView {
152
153    pub fn builder<'a>() -> TreeViewBuilder<'a> {
154        TreeViewBuilder {
155            size: (100, 200),
156            position: (0, 0),
157            enabled: true,
158            focus: false,
159            flags: None,
160            ex_flags: 0,
161            font: None,
162            parent: None,
163
164            #[cfg(feature="image-list")]
165            image_list: None,
166        }
167    }
168
169    /// Sets the image list of the treeview
170    #[cfg(feature="image-list")]
171    pub fn set_image_list(&self, list: Option<&ImageList>) {
172        use winapi::um::commctrl::{TVM_SETIMAGELIST, TVSIL_NORMAL};
173
174        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
175        let list_handle = list.map(|l| l.handle).unwrap_or(ptr::null_mut());
176
177        wh::send_message(handle, TVM_SETIMAGELIST, TVSIL_NORMAL, list_handle as _);
178    }
179
180    /// Returns the image list of the treeview or None if there is none.
181    /// The returned image list is not owned
182    #[cfg(feature="image-list")]
183    pub fn image_list(&self) -> Option<ImageList> {
184        use winapi::um::commctrl::{TVM_GETIMAGELIST, TVSIL_NORMAL};
185
186        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
187        let handle = wh::send_message(handle, TVM_GETIMAGELIST, TVSIL_NORMAL, 0) as HIMAGELIST;
188        if handle.is_null() {
189            None
190        } else {
191            Some(ImageList { handle, owned: false })
192        }
193    }
194
195    /// Sets the image that will appear left to the item text. `index` is the index of the image in the image-list
196    /// Won't do anything if the control do not have a image list or if the item is not in the tree
197    /// If `on_select` is set to true, sets the icon that is used when an item is active
198    #[cfg(feature="image-list")]
199    pub fn set_item_image(&self, item: &TreeItem, index: i32, on_select: bool) {
200        use winapi::um::commctrl::{TVM_SETITEMW, TVIF_IMAGE, TVIF_SELECTEDIMAGE};
201
202        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
203        let mut tree_item = blank_item();
204        tree_item.hItem = item.handle;
205
206        tree_item.mask = match on_select {
207            true => TVIF_SELECTEDIMAGE,
208            false => TVIF_IMAGE
209        };
210
211        match on_select {
212            true => { tree_item.iSelectedImage = index; },
213            false => { tree_item.iImage = index; }
214        }
215
216        wh::send_message(handle, TVM_SETITEMW, 0, &mut tree_item as *mut TVITEMW as LPARAM);
217    }
218
219    /// Returns the index of the image in the tree view image list.
220    /// If there is no image list in the control or the item is not in the control, 0 will be returned.
221    /// If `on_select` is set to true, returns the icon that is used when an item is active
222    #[cfg(feature="image-list")]
223    pub fn item_image(&self, item: &TreeItem, on_select: bool) -> i32 {
224        use winapi::um::commctrl::{TVM_GETITEMW, TVIF_IMAGE, TVIF_SELECTEDIMAGE};
225
226        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
227        let mut tree_item = blank_item();
228        tree_item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
229        tree_item.hItem = item.handle;
230
231        match wh::send_message(handle, TVM_GETITEMW, 0, &mut tree_item as *mut TVITEMW as LPARAM) {
232            0 => 0,
233            _ => match on_select {
234                true => tree_item.iSelectedImage,
235                false => tree_item.iImage
236            }
237        }
238    }
239
240    /// Sets the text color in the treeview
241    pub fn set_text_color(&self, r: u8, g: u8, b: u8) {
242        use winapi::um::commctrl::TVM_SETTEXTCOLOR;
243        use winapi::um::wingdi::RGB;
244
245        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
246        let color = RGB(r, g, b);
247
248        wh::send_message(handle, TVM_SETTEXTCOLOR, 0, color as _);
249
250        self.invalidate();
251    }
252
253    /// Returns the text color in the treeview
254    pub fn text_color(&self) -> [u8; 3] {
255        use winapi::um::commctrl::TVM_GETTEXTCOLOR;
256        use winapi::um::wingdi::{GetRValue, GetGValue, GetBValue};
257        
258        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
259
260        let col = wh::send_message(handle, TVM_GETTEXTCOLOR, 0, 0) as u32;
261
262        [
263            GetRValue(col),
264            GetGValue(col),
265            GetBValue(col),
266        ]
267    }
268
269    /// Retrieves the amount, in pixels, that child items are indented relative to their parent items. 
270    pub fn indent(&self) -> u32 {
271        use winapi::um::commctrl::TVM_GETINDENT;
272
273        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
274        wh::send_message(handle, TVM_GETINDENT, 0, 0) as u32
275    }
276
277    /// Sets the width of indentation for a tree-view control and redraws the control to reflect the new width.
278    pub fn set_indent(&self, indent: u32) {
279        use winapi::um::commctrl::TVM_SETINDENT;
280
281        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
282        wh::send_message(handle, TVM_SETINDENT, indent as _, 0);
283    }
284
285    /// Return the root item of the tree view if one is present.
286    /// If there is no root in the tree, returns `None`.
287    pub fn root(&self) -> Option<TreeItem> {
288        use winapi::um::commctrl::TVGN_ROOT;
289        next_treeview_item(&self.handle, TVGN_ROOT, ptr::null_mut())
290    }
291
292    /// Returns the first child of an item or `None` if the item has no child or if it's not part of the tree view 
293    /// To iterate over all the children, use `TreeView.iter_item(&parent_item)`
294    pub fn first_child(&self, item: &TreeItem) ->  Option<TreeItem> {
295        use winapi::um::commctrl::TVGN_CHILD;
296        next_treeview_item(&self.handle, TVGN_CHILD, item.handle)
297    }
298
299    /// Returns the next sibling in the tree or `None` if the item has no more sibling or if it's not part of the tree view 
300    pub fn next_sibling(&self, item: &TreeItem) ->  Option<TreeItem> {
301        use winapi::um::commctrl::TVGN_NEXT;
302        next_treeview_item(&self.handle, TVGN_NEXT, item.handle)
303    }
304
305    /// Returns the previous sibling in the tree or `None` if the item has no more sibling or if it's not part of the tree view
306    pub fn previous_sibling(&self, item: &TreeItem) -> Option<TreeItem> {
307        use winapi::um::commctrl::TVGN_PREVIOUS;
308        next_treeview_item(&self.handle, TVGN_PREVIOUS, item.handle)
309    }
310
311    /// Returns the parent of the item in the tree or `None` if the item is root
312    pub fn parent(&self, item: &TreeItem) -> Option<TreeItem> {
313        use winapi::um::commctrl::TVGN_PARENT;
314        next_treeview_item(&self.handle, TVGN_PARENT, item.handle)
315    }
316
317    /// Return the currently selected item. If there are more than one selected item, returns the first one.
318    /// If there is no selected item, returns `None`.
319    pub fn selected_item(&self) -> Option<TreeItem> {
320        use winapi::um::commctrl::{TVM_GETNEXTITEM, TVGN_NEXTSELECTED};
321
322        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
323        let tree_handle = wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, 0) as HTREEITEM;
324        if tree_handle.is_null() {
325            None
326        } else {
327            Some(TreeItem { handle: tree_handle })
328        }
329    }
330
331    /// Returns the selected items in a Treeview
332    /// If there is no selected items, returns an empty `Vec`.
333    pub fn selected_items(&self) -> Vec<TreeItem> {
334        use winapi::um::commctrl::{TVM_GETNEXTITEM, TVGN_NEXTSELECTED};
335
336        let mut items = Vec::new();
337
338        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
339        let mut last_handle = wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, 0);
340        while last_handle != 0 {
341            items.push(TreeItem { handle: last_handle as _ } );
342            last_handle = wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, last_handle as _);
343        }
344
345        items
346    }
347
348    /// Returns the number of selected item in the tree view
349    pub fn selected_item_count(&self) -> usize {
350        use winapi::um::commctrl::{TVM_GETNEXTITEM, TVGN_NEXTSELECTED};
351
352        let mut count = 0;
353        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
354
355        let mut last_handle = wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, 0);
356        while last_handle != 0 {
357            count += 1;
358            last_handle = wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, last_handle as _);
359        }
360
361        count
362    }
363
364    /// Insert a new item into the TreeView and return a reference to new newly added item
365    pub fn insert_item<'a>(&self, new: &'a str, parent: Option<&TreeItem>, position: TreeInsert) -> TreeItem {
366        use winapi::um::commctrl::{TVM_INSERTITEMW, TVINSERTSTRUCTW, TVI_FIRST, TVI_LAST, TVI_ROOT, TVI_SORT, TVIF_TEXT};
367        use winapi::um::commctrl::TVINSERTSTRUCTW_u;
368        use winapi::um::winnt::LPWSTR;
369
370        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
371
372        let insert = match position {
373            TreeInsert::First => TVI_FIRST,
374            TreeInsert::Last => TVI_LAST,
375            TreeInsert::Root => TVI_ROOT,
376            TreeInsert::Sort => TVI_SORT,
377            TreeInsert::After(i) => i
378        };
379
380        let text = to_utf16(new);
381
382        let item = {
383            let mut item: TVINSERTSTRUCTW_u = unsafe { mem::zeroed() };
384            let i = unsafe { item.item_mut() };
385            i.mask = TVIF_TEXT;
386            i.pszText = text.as_ptr() as LPWSTR;
387            item
388        };
389
390        let new_item = TVINSERTSTRUCTW {
391            hParent: parent.map(|p| p.handle ).unwrap_or(ptr::null_mut()),
392            hInsertAfter: insert,
393            u: item
394        };
395
396        let ptr = &new_item as *const TVINSERTSTRUCTW;
397        let handle = wh::send_message(handle, TVM_INSERTITEMW, 0, ptr as LPARAM) as HTREEITEM;
398
399        self.invalidate();
400
401        TreeItem { handle }
402    }
403
404    /// Insert a new item into the TreeView with associated lParam and return a reference to new newly added item
405    pub fn insert_item_with_param<'a>(&self, new: &'a str, parent: Option<&TreeItem>, position: TreeInsert, data: isize) -> TreeItem {
406        use winapi::um::commctrl::{TVM_INSERTITEMW, TVINSERTSTRUCTW, TVI_FIRST, TVI_LAST, TVI_ROOT, TVI_SORT, TVIF_TEXT, TVIF_PARAM};
407        use winapi::um::commctrl::TVINSERTSTRUCTW_u;
408        use winapi::um::winnt::LPWSTR;
409
410        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
411
412        let insert = match position {
413            TreeInsert::First => TVI_FIRST,
414            TreeInsert::Last => TVI_LAST,
415            TreeInsert::Root => TVI_ROOT,
416            TreeInsert::Sort => TVI_SORT,
417            TreeInsert::After(i) => i
418        };
419
420        let text = to_utf16(new);
421
422        let item = {
423            let mut item: TVINSERTSTRUCTW_u = unsafe { mem::zeroed() };
424            let i = unsafe { item.item_mut() };
425            i.mask = TVIF_TEXT | TVIF_PARAM;            
426            i.pszText = text.as_ptr() as LPWSTR;
427            i.lParam = data;
428            item
429        };
430
431        let new_item = TVINSERTSTRUCTW {
432            hParent: parent.map(|p| p.handle ).unwrap_or(ptr::null_mut()),
433            hInsertAfter: insert,
434            u: item
435        };
436
437        let ptr = &new_item as *const TVINSERTSTRUCTW;
438        let handle = wh::send_message(handle, TVM_INSERTITEMW, 0, ptr as LPARAM) as HTREEITEM;
439
440        self.invalidate();
441
442        TreeItem { handle }
443    }
444
445    /// Remove an item and its children from the tree view
446    pub fn remove_item(&self, item: &TreeItem) {
447        use winapi::um::commctrl::{TVM_DELETEITEM};
448
449        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
450        wh::send_message(handle, TVM_DELETEITEM, 0, item.handle as LPARAM);
451    }
452
453    /// Selects the specified tree-view item and scrolls the item into view.
454    pub fn select_item(&self, item: &TreeItem) {
455        use winapi::um::commctrl::{TVM_SETITEMW, TVIF_STATE};
456
457        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
458        let mut tree_item = blank_item();
459        tree_item.mask = TVIF_STATE;
460        tree_item.hItem = item.handle;
461        tree_item.state = TVIS_SELECTED;
462        tree_item.stateMask = TVIS_SELECTED;
463
464        wh::send_message(handle, TVM_SETITEMW, 0, &mut tree_item as *mut TVITEMW as LPARAM);
465    }
466
467    /// Unselects an item from the treeview
468    pub fn unselect_item(&self, item: &TreeItem) {
469        use winapi::um::commctrl::{TVM_SETITEMW, TVIF_STATE};
470
471        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
472        let mut tree_item = blank_item();
473        tree_item.mask = TVIF_STATE;
474        tree_item.hItem = item.handle;
475        tree_item.state = 0;
476        tree_item.stateMask = TVIS_SELECTED;
477
478        wh::send_message(handle, TVM_SETITEMW, 0, &mut tree_item as *mut TVITEMW as LPARAM);
479    }
480
481    /// Creates an iterator over the tree view items
482    #[cfg(feature="tree-view-iterator")]
483    pub fn iter<'a>(&'a self) -> crate::TreeViewIterator<'a> {
484        check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
485        crate::TreeViewIterator::new(self, ptr::null_mut())
486    }
487
488    /// Creates an iterator over the children of an item. This does not include the item itself.
489    #[cfg(feature="tree-view-iterator")]
490    pub fn iter_item<'a>(&'a self, item: &TreeItem) -> crate::TreeViewIterator<'a> {
491        check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
492        crate::TreeViewIterator::new(self, item.handle)
493    }
494
495    /// Returns the text of the selected item. Return None if the item is not in the tree view.
496    /// The returned text value cannot be bigger than 260 characters
497    pub fn item_text(&self, tree_item: &TreeItem) -> Option<String> {
498        use winapi::um::commctrl::{TVM_GETITEMW, TVIF_TEXT, TVIF_HANDLE};
499        const BUFFER_MAX: usize = 260;
500
501        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
502
503        let mut text_buffer = Vec::with_capacity(BUFFER_MAX);
504        unsafe { text_buffer.set_len(BUFFER_MAX); }
505
506        let mut item: TVITEMW = blank_item();
507        item.mask = TVIF_TEXT | TVIF_HANDLE;
508        item.hItem = tree_item.handle;
509        item.pszText = text_buffer.as_mut_ptr();
510        item.cchTextMax = BUFFER_MAX as _;
511        
512        let result = wh::send_message(handle, TVM_GETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
513        if result == 0 {
514            return None;
515        }
516
517        Some(from_utf16(&text_buffer))
518    }
519    
520    /// Set the text for specified item in the treeview.
521    pub fn set_item_text(&self, tree_item: &TreeItem, new_text: &str) {
522        use winapi::um::commctrl::{TVM_SETITEMW, TVIF_TEXT};
523        use winapi::um::winnt::LPWSTR;
524
525        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
526        let text = to_utf16(new_text);
527
528        let mut item: TVITEMW = blank_item();
529        item.mask = TVIF_TEXT;
530        item.hItem = tree_item.handle;
531        item.pszText = text.as_ptr() as LPWSTR;
532
533        wh::send_message(handle, TVM_SETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
534    }
535
536    /// Returns the lParam of the selected item. Return None if the item is not in the tree view.
537    pub fn item_param(&self, tree_item: &TreeItem) -> Option<isize> {
538        use winapi::um::commctrl::{TVM_GETITEMW, TVIF_PARAM, TVIF_HANDLE};
539
540        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
541
542        let mut item: TVITEMW = blank_item();
543        item.mask = TVIF_HANDLE | TVIF_PARAM;
544        item.hItem = tree_item.handle;
545        
546        let result = wh::send_message(handle, TVM_GETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
547        if result == 0 {
548            return None;
549        }
550
551        Some(item.lParam)
552    }
553
554    /// Returns `true` if the tree view item has children. Returns `None` if the item is not in the tree view.
555    pub fn item_has_children(&self, tree_item: &TreeItem) -> Option<bool> {
556        use winapi::um::commctrl::{TVM_GETITEMW, TVIF_CHILDREN, TVIF_HANDLE};
557
558        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
559
560        let mut item: TVITEMW = blank_item();
561        item.hItem = tree_item.handle;
562        item.mask = TVIF_CHILDREN | TVIF_HANDLE;
563        
564        let result = wh::send_message(handle, TVM_GETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
565        if result == 0 {
566            return None;
567        }
568
569        Some(item.cChildren != 0)
570    }
571
572    /// Returns the item state in the tree view or `None` if the item is not in the tree view
573    pub fn item_state(&self, tree_item: &TreeItem) -> Option<TreeItemState> {
574        use winapi::um::commctrl::{TVM_GETITEMW, TVIF_STATE, TVIF_HANDLE};
575
576        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
577
578        let mut item: TVITEMW = unsafe { mem::zeroed() };
579        item.hItem = tree_item.handle;
580        item.mask = TVIF_STATE | TVIF_HANDLE;
581        item.stateMask = 0xFF;
582        
583        let result = wh::send_message(handle, TVM_GETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
584        if result == 0 {
585            return None;
586        }
587
588        Some(TreeItemState::from_bits_truncate(item.state))
589    }
590
591    /// Expands or collapses the list of child items associated with the specified parent item, if any. 
592    pub fn set_expand_state(&self, item: &TreeItem, state: ExpandState) {
593        use winapi::um::commctrl::{TVM_EXPAND, TVE_COLLAPSE, TVE_COLLAPSERESET, TVE_EXPAND, TVE_EXPANDPARTIAL, TVE_TOGGLE};
594
595        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
596
597        let state = match state {
598            ExpandState::Collapse => TVE_COLLAPSE,
599            ExpandState::CollapseReset => TVE_COLLAPSE | TVE_COLLAPSERESET,
600            ExpandState::Expand => TVE_EXPAND,
601            ExpandState::ExpandPartial => TVE_EXPANDPARTIAL,
602            ExpandState::Toggle => TVE_TOGGLE,
603        };
604
605        wh::send_message(handle, TVM_EXPAND, state as WPARAM, item.handle as LPARAM);
606    }
607
608    /// Ensures that a tree-view item is visible, expanding the parent item or scrolling the tree-view control, if necessary.
609    pub fn ensure_visible(&self, item: &TreeItem) {
610        use winapi::um::commctrl::{TVM_ENSUREVISIBLE};
611
612        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
613        wh::send_message(handle, TVM_ENSUREVISIBLE, 0, item.handle as LPARAM);
614    }
615
616    /// Remove every item from the treeview by removing the root item
617    pub fn clear(&self) {
618        use winapi::um::commctrl::{TVM_DELETEITEM, TVI_ROOT};
619
620        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
621        wh::send_message(handle, TVM_DELETEITEM, 0, TVI_ROOT  as LPARAM);
622    }
623
624    /// Return the total number of item in the tree view
625    pub fn len(&self) -> usize {
626        use winapi::um::commctrl::TVM_GETCOUNT;
627
628        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
629        wh::send_message(handle, TVM_GETCOUNT, 0, 0) as usize
630    }
631
632    /// Return the number of item in the tree view visible by the user
633    pub fn visible_len(&self) -> usize {
634        use winapi::um::commctrl::TVM_GETVISIBLECOUNT;
635
636        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
637        wh::send_message(handle, TVM_GETVISIBLECOUNT, 0, 0) as usize
638    }
639
640
641    //
642    // Common methods
643    //
644
645    /// Invalidate the whole drawing region.
646    pub fn invalidate(&self) {
647        use winapi::um::winuser::InvalidateRect;
648
649        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
650        unsafe { InvalidateRect(handle, ptr::null(), 1); }
651    }
652
653
654    /// Return the font of the control
655    pub fn font(&self) -> Option<Font> {
656        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
657
658        let font_handle = wh::get_window_font(handle);
659        if font_handle.is_null() {
660            None
661        } else {
662            Some(Font { handle: font_handle })
663        }
664    }
665
666    /// Set the font of the control
667    pub fn set_font(&self, font: Option<&Font>) {
668        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
669        unsafe { wh::set_window_font(handle, font.map(|f| f.handle), true); }
670    }
671
672    /// Return true if the control currently has the keyboard focus
673    pub fn focus(&self) -> bool {
674        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
675        unsafe { wh::get_focus(handle) }
676    }
677
678    /// Set the keyboard focus on the button.
679    pub fn set_focus(&self) {
680        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
681        unsafe { wh::set_focus(handle); }
682    }
683
684    /// Return true if the control user can interact with the control, return false otherwise
685    pub fn enabled(&self) -> bool {
686        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
687        unsafe { wh::get_window_enabled(handle) }
688    }
689
690    /// Enable or disable the control
691    pub fn set_enabled(&self, v: bool) {
692        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
693        unsafe { wh::set_window_enabled(handle, v) }
694    }
695
696    /// Return true if the control is visible to the user. Will return true even if the 
697    /// control is outside of the parent client view (ex: at the position (10000, 10000))
698    pub fn visible(&self) -> bool {
699        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
700        unsafe { wh::get_window_visibility(handle) }
701    }
702
703    /// Show or hide the control to the user
704    pub fn set_visible(&self, v: bool) {
705        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
706        unsafe { wh::set_window_visibility(handle, v) }
707    }
708
709    /// Return the size of the button in the parent window
710    pub fn size(&self) -> (u32, u32) {
711        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
712        unsafe { wh::get_window_size(handle) }
713    }
714
715    /// Set the size of the button in the parent window
716    pub fn set_size(&self, x: u32, y: u32) {
717        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
718        unsafe { wh::set_window_size(handle, x, y, false) }
719    }
720
721    /// Return the position of the button in the parent window
722    pub fn position(&self) -> (i32, i32) {
723        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
724        unsafe { wh::get_window_position(handle) }
725    }
726
727    /// Set the position of the button in the parent window
728    pub fn set_position(&self, x: i32, y: i32) {
729        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
730        unsafe { wh::set_window_position(handle, x, y) }
731    }
732
733    /// Winapi class name used during control creation
734    pub fn class_name(&self) -> &'static str {
735        winapi::um::commctrl::WC_TREEVIEW
736    }
737
738    /// Winapi base flags used during window creation
739    pub fn flags(&self) -> u32 {
740        use winapi::um::commctrl::{TVS_HASBUTTONS, TVS_LINESATROOT, TVS_HASLINES, TVS_EDITLABELS};
741
742        WS_VISIBLE | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | WS_TABSTOP | TVS_SHOWSELALWAYS | TVS_EDITLABELS
743    }
744
745    /// Winapi flags required by the control
746    pub fn forced_flags(&self) -> u32 {
747        use winapi::um::winuser::{WS_CHILD, WS_BORDER};
748        use winapi::um::commctrl::TVS_NOTOOLTIPS;
749
750        WS_CHILD | WS_BORDER | TVS_NOTOOLTIPS
751    }
752
753    /// Begins to in-place edit the specified item's text.
754    /// Return None if Failed.
755    /// Return the treeview's handle if successful.
756    pub fn edit_label(&self, item: &TreeItem) -> Option<ControlHandle> {
757        use winapi::um::commctrl::TVM_EDITLABELW;
758        use winapi::shared::windef::HWND;
759        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
760    
761        let result = wh::send_message(handle,  TVM_EDITLABELW, 0, item.handle as HTREEITEM as LPARAM); 
762        
763        if result == 0 {
764            return None;
765        }
766        Some(ControlHandle::Hwnd(result as HWND))
767    } 
768
769    /// End the in-place editing of the tree item's label.
770    /// The parameter f_cancel indicates whether the editing is canceled without being saved to the label. 
771    /// If this parameter is TRUE, the system cancels editing without saving the changes. Otherwise, the system saves the changes to the label.
772    /// Return true if successful, otherwise return false.
773    pub fn end_edit_label_now(&self, f_cancel: bool) -> bool {
774        use winapi::um::commctrl::TVM_ENDEDITLABELNOW;
775        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
776    
777        wh::send_message(handle,  TVM_ENDEDITLABELNOW, f_cancel as WPARAM, 0) != 0
778    }
779}
780
781impl Drop for TreeView {
782    fn drop(&mut self) {
783        self.handle.destroy();
784    }
785}
786
787
788/// Builder for a TreeView
789pub struct TreeViewBuilder<'a> {
790    size: (i32, i32),
791    position: (i32, i32),
792    enabled: bool,
793    focus: bool,
794    flags: Option<TreeViewFlags>,
795    ex_flags: u32,
796    font: Option<&'a Font>,
797    parent: Option<ControlHandle>,
798
799    #[cfg(feature="image-list")]
800    image_list: Option<&'a ImageList>,
801}
802
803
804impl<'a> TreeViewBuilder<'a> {
805
806    pub fn flags(mut self, flags: TreeViewFlags) -> TreeViewBuilder<'a> {
807        self.flags = Some(flags);
808        self
809    }
810
811    pub fn ex_flags(mut self, flags: u32) -> TreeViewBuilder<'a> {
812        self.ex_flags = flags;
813        self
814    }
815
816    pub fn size(mut self, size: (i32, i32)) -> TreeViewBuilder<'a> {
817        self.size = size;
818        self
819    }
820
821    pub fn position(mut self, pos: (i32, i32)) -> TreeViewBuilder<'a> {
822        self.position = pos;
823        self
824    }
825
826    pub fn enabled(mut self, e: bool) -> TreeViewBuilder<'a> {
827        self.enabled = e;
828        self
829    }
830
831    pub fn focus(mut self, focus: bool) -> TreeViewBuilder<'a> {
832        self.focus = focus;
833        self
834    }
835
836    pub fn font(mut self, font: Option<&'a Font>) -> TreeViewBuilder<'a> {
837        self.font = font;
838        self
839    }
840
841    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> TreeViewBuilder<'a> {
842        self.parent = Some(p.into());
843        self
844    }
845
846    #[cfg(feature="image-list")]
847    pub fn image_list(mut self, list: Option<&'a ImageList>) -> TreeViewBuilder<'a> {
848        self.image_list = list;
849        self
850    }
851
852    pub fn build(self, out: &mut TreeView) -> Result<(), NwgError> {
853        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
854
855        let parent = match self.parent {
856            Some(p) => Ok(p),
857            None => Err(NwgError::no_parent("TreeView"))
858        }?;
859
860        *out = Default::default();
861
862        out.handle = ControlBase::build_hwnd()
863            .class_name(out.class_name())
864            .forced_flags(out.forced_flags())
865            .flags(flags)
866            .ex_flags(self.ex_flags)
867            .size(self.size)
868            .position(self.position)
869            .parent(Some(parent))
870            .build()?;
871
872        if self.font.is_some() {
873            out.set_font(self.font);
874        } else {
875            out.set_font(Font::global_default().as_ref());
876        }
877
878        builder_set_image_list(&self, out);
879
880        if self.focus {
881            out.set_focus();
882        }
883
884        out.set_enabled(self.enabled);
885
886        Ok(())
887    }
888
889}
890
891
892impl PartialEq for TreeItem {
893    fn eq(&self, other: &Self) -> bool {
894        self.handle == other.handle
895    }
896}
897
898impl Eq for TreeItem {}
899
900
901fn next_treeview_item(handle: &ControlHandle, action: usize, item: HTREEITEM) -> Option<TreeItem> {
902    use winapi::um::commctrl::TVM_GETNEXTITEM;
903
904    if handle.blank() { panic!("{}", NOT_BOUND); }
905    let handle = handle.hwnd().expect(BAD_HANDLE);
906
907    let handle = wh::send_message(handle, TVM_GETNEXTITEM, action as _, item as _) as HTREEITEM;
908    if handle.is_null() {
909        None
910    } else {
911        Some(TreeItem { handle })
912    }
913}
914
915#[cfg(feature="image-list")]
916fn builder_set_image_list(builder: &TreeViewBuilder, out: &TreeView) {
917    if builder.image_list.is_some() {
918        out.set_image_list(builder.image_list);
919    }
920}
921
922#[cfg(not(feature="image-list"))]
923fn builder_set_image_list(_builder: &TreeViewBuilder, _out: &TreeView) {
924}
925
926fn blank_item() -> TVITEMW {
927    TVITEMW {
928        mask: 0,
929        hItem: ptr::null_mut(),
930        state: 0,
931        stateMask: 0,
932        pszText: ptr::null_mut(),
933        cchTextMax: 0,
934        iImage: 0,
935        iSelectedImage: 0,
936        cChildren: 0,
937        lParam: 0
938    }
939}