1use 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 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 pub struct TreeItemState: u32 {
49 const SELECTED = TVIS_SELECTED;
50 const EXPANDED = TVIS_EXPANDED;
51 }
52}
53
54
55#[derive(Copy, Clone, Debug)]
57pub enum TreeInsert {
58 First,
60
61 Last,
63
64 Root,
66
67 Sort,
69
70 After(HTREEITEM)
72}
73
74#[derive(Copy, Clone, Debug)]
76#[repr(u8)]
77pub enum ExpandState {
78 Collapse,
79 CollapseReset,
80 Expand,
81 ExpandPartial,
82 Toggle
83}
84
85
86#[derive(Copy, Clone, Debug)]
88pub enum TreeItemAction {
89 Unknown,
91
92 Expand(ExpandState),
94
95 State { old: TreeItemState, new: TreeItemState }
97}
98
99#[derive(Debug)]
101pub struct TreeItem {
102 pub handle: HTREEITEM
103}
104
105impl TreeItem {
106 pub fn is_null(&self) -> bool {
108 self.handle.is_null()
109 }
110}
111
112#[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 #[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 #[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 #[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 #[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 #[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 #[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn class_name(&self) -> &'static str {
735 winapi::um::commctrl::WC_TREEVIEW
736 }
737
738 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 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 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 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
788pub 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}