1use winapi::shared::windef::{HBITMAP, HBRUSH};
2use winapi::um::winuser::{WS_VISIBLE, WS_DISABLED, WS_TABSTOP};
3use winapi::um::commctrl::{
4 LVS_ICON, LVS_SMALLICON, LVS_LIST, LVS_REPORT, LVS_NOCOLUMNHEADER, LVCOLUMNW, LVCFMT_LEFT, LVCFMT_RIGHT, LVCFMT_CENTER, LVCFMT_JUSTIFYMASK,
5 LVCFMT_IMAGE, LVCFMT_BITMAP_ON_RIGHT, LVCFMT_COL_HAS_IMAGES, LVITEMW, LVIF_TEXT, LVCF_WIDTH, LVCF_TEXT, LVS_EX_GRIDLINES, LVS_EX_BORDERSELECT,
6 LVS_EX_AUTOSIZECOLUMNS, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_SINGLESEL, LVCF_FMT, LVIF_IMAGE, LVS_SHOWSELALWAYS,
7 LVS_EX_HEADERDRAGDROP, LVS_EX_HEADERINALLVIEWS, LVM_GETHEADER, HDITEMW, HDI_FORMAT, HDM_GETITEMW, HDF_SORTUP, HDF_SORTDOWN, HDM_SETITEMW
8};
9use super::{ControlBase, ControlHandle};
10use crate::win32::window_helper as wh;
11use crate::win32::base_helper::{to_utf16, from_utf16, check_hwnd};
12use crate::{NwgError, RawEventHandler, unbind_raw_event_handler};
13use std::{mem, ptr, rc::Rc, cell::RefCell};
14
15#[cfg(feature="image-list")]
16use crate::ImageList;
17
18
19const NOT_BOUND: &'static str = "ListView is not yet bound to a winapi object";
20const BAD_HANDLE: &'static str = "INTERNAL ERROR: ListView handle is not HWND!";
21
22
23bitflags! {
24 pub struct ListViewFlags: u32 {
35 const VISIBLE = WS_VISIBLE;
36 const DISABLED = WS_DISABLED;
37 const TAB_STOP = WS_TABSTOP;
38
39 const SINGLE_SELECTION = LVS_SINGLESEL;
40
41 const ALWAYS_SHOW_SELECTION = LVS_SHOWSELALWAYS;
42
43 const NO_HEADER = LVS_NOCOLUMNHEADER;
46 }
47}
48
49bitflags! {
50 pub struct ListViewExFlags: u32 {
62 const NONE = 0;
63 const GRID = LVS_EX_GRIDLINES;
64 const BORDER_SELECT = LVS_EX_BORDERSELECT;
65 const AUTO_COLUMN_SIZE = LVS_EX_AUTOSIZECOLUMNS;
66 const FULL_ROW_SELECT = LVS_EX_FULLROWSELECT;
67 const HEADER_DRAG_DROP = LVS_EX_HEADERDRAGDROP;
68 const HEADER_IN_ALL_VIEW = LVS_EX_HEADERINALLVIEWS;
69
70 }
71}
72
73bitflags! {
74 pub struct ListViewColumnFlags: u32 {
87 const LEFT = LVCFMT_LEFT as u32;
88 const RIGHT = LVCFMT_RIGHT as u32;
89 const CENTER = LVCFMT_CENTER as u32;
90 const JUSTIFY_MASK = LVCFMT_JUSTIFYMASK as u32;
91 const IMAGE = LVCFMT_IMAGE as u32;
92 const IMAGE_RIGHT = LVCFMT_BITMAP_ON_RIGHT as u32;
93 const IMAGE_COL = LVCFMT_COL_HAS_IMAGES as u32;
94 }
95}
96
97#[derive(Copy, Clone, Debug)]
101#[repr(u8)]
102pub enum ListViewStyle {
103 Simple,
104 Detailed,
105 Icon,
106 SmallIcon,
107}
108
109impl ListViewStyle {
110 fn from_bits(bits: u32) -> ListViewStyle {
111 let bits = bits & 0b11;
112 match bits {
113 LVS_ICON => ListViewStyle::Icon,
114 LVS_REPORT => ListViewStyle::Detailed,
115 LVS_SMALLICON => ListViewStyle::SmallIcon,
116 LVS_LIST => ListViewStyle::Simple,
117 _ => unreachable!()
118 }
119 }
120
121 fn bits(&self) -> u32 {
122 match self {
123 ListViewStyle::Simple => LVS_LIST,
124 ListViewStyle::Detailed => LVS_REPORT,
125 ListViewStyle::Icon => LVS_ICON,
126 ListViewStyle::SmallIcon => LVS_SMALLICON,
127 }
128 }
129}
130
131#[cfg(feature="image-list")]
136#[derive(Copy, Clone, Debug)]
137pub enum ListViewImageListType {
138 Normal,
140
141 Small,
143
144 State,
146
147 GroupHeader
149}
150
151#[cfg(feature="image-list")]
152impl ListViewImageListType {
153
154 fn to_raw(&self) -> i32 {
155 use winapi::um::commctrl::{LVSIL_NORMAL, LVSIL_SMALL, LVSIL_STATE, LVSIL_GROUPHEADER};
156
157 match self {
158 Self::Normal => LVSIL_NORMAL,
159 Self::Small => LVSIL_SMALL,
160 Self::State => LVSIL_STATE,
161 Self::GroupHeader => LVSIL_GROUPHEADER,
162 }
163 }
164
165}
166
167#[derive(Default, Clone, Debug)]
168pub struct InsertListViewColumn {
170 pub index: Option<i32>,
172
173 pub fmt: Option<ListViewColumnFlags>,
175
176 pub width: Option<i32>,
178
179 pub text: Option<String>
181}
182
183#[derive(Default, Clone, Debug)]
185pub struct ListViewColumn {
186 pub fmt: i32,
187 pub width: i32,
188 pub text: String,
189}
190
191#[derive(Copy, Clone, Debug)]
193pub enum ListViewColumnSortArrow {
194 Up,
195 Down,
196}
197
198
199#[derive(Default, Clone, Debug)]
201pub struct InsertListViewItem {
202 pub index: Option<i32>,
205
206 pub column_index: i32,
208
209 pub text: Option<String>,
211
212 #[cfg(feature="image-list")]
215 pub image: Option<i32>
216}
217
218#[derive(Default, Clone, Debug)]
220pub struct ListViewItem {
221 pub row_index: i32,
222 pub column_index: i32,
223 pub text: String,
224
225 pub selected: bool,
227
228 #[cfg(feature="image-list")]
229 pub image: i32,
230}
231
232struct ListViewDoubleBuffer {
233 buffer: HBITMAP,
234 size: [i32; 2],
235 bg: HBRUSH,
236}
237
238#[derive(Default)]
279pub struct ListView {
280 pub handle: ControlHandle,
281 double_buffer: Option<Rc<RefCell<ListViewDoubleBuffer>>>,
282 handler0: Option<RawEventHandler>,
283}
284
285impl ListView {
286
287 pub fn builder() -> ListViewBuilder {
288 ListViewBuilder {
289 size: (300, 300),
290 position: (0, 0),
291 background_color: None,
292 double_buffer: true,
293 text_color: None,
294 focus: false,
295 flags: None,
296 ex_flags: None,
297 ex_window_flags: 0,
298 style: ListViewStyle::Simple,
299 parent: None,
300 item_count: 0
301 }
302 }
303
304 #[cfg(feature="image-list")]
307 pub fn set_image_list(&self, list: Option<&ImageList>, list_type: ListViewImageListType) {
308 use winapi::um::commctrl::LVM_SETIMAGELIST;
309
310 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
311
312 let list_handle = list.map(|l| l.handle).unwrap_or(ptr::null_mut());
313 wh::send_message(handle, LVM_SETIMAGELIST, list_type.to_raw() as _, list_handle as _);
314
315 self.invalidate();
316 }
317
318 #[cfg(feature="image-list")]
321 pub fn image_list(&self, list_type: ListViewImageListType) -> Option<ImageList> {
322 use winapi::um::commctrl::LVM_GETIMAGELIST;
323
324 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
325
326 match wh::send_message(handle, LVM_GETIMAGELIST, list_type.to_raw() as _, 0) {
327 0 => None,
328 handle => Some( ImageList {
329 handle: handle as _,
330 owned: false
331 })
332 }
333 }
334
335 pub fn set_text_color(&self, r: u8, g: u8, b: u8) {
337 use winapi::um::commctrl::LVM_SETTEXTCOLOR;
338 use winapi::um::wingdi::RGB;
339
340 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
341
342 let color = RGB(r, g, b);
343
344 wh::send_message(handle, LVM_SETTEXTCOLOR, 0, color as _);
345
346 self.invalidate();
347 }
348
349 pub fn text_color(&self) -> [u8; 3] {
351 use winapi::um::commctrl::LVM_GETTEXTCOLOR;
352 use winapi::um::wingdi::{GetRValue, GetGValue, GetBValue};
353
354 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
355 let col = wh::send_message(handle, LVM_GETTEXTCOLOR, 0, 0) as u32;
356
357 [
358 GetRValue(col),
359 GetGValue(col),
360 GetBValue(col),
361 ]
362 }
363
364 pub fn set_background_color(&self, r: u8, g: u8, b: u8) {
366 use winapi::um::commctrl::LVM_SETBKCOLOR;
367 use winapi::um::wingdi::RGB;
368
369 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
370
371 let color = RGB(r, g, b);
372
373 wh::send_message(handle, LVM_SETBKCOLOR, 0, color as _);
374
375 self.invalidate();
376 }
377
378 pub fn background_color(&self) -> [u8; 3] {
380 use winapi::um::commctrl::LVM_GETBKCOLOR;
381 use winapi::um::wingdi::{GetRValue, GetGValue, GetBValue};
382
383 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
384 let col = wh::send_message(handle, LVM_GETBKCOLOR, 0, 0) as u32;
385
386 [
387 GetRValue(col),
388 GetGValue(col),
389 GetBValue(col),
390 ]
391 }
392
393 pub fn selected_column(&self) -> usize {
395 use winapi::um::commctrl::LVM_GETSELECTEDCOLUMN;
396
397 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
398 wh::send_message(handle, LVM_GETSELECTEDCOLUMN, 0, 0) as usize
399 }
400
401 pub fn set_selected_column(&self, index: usize) {
403 use winapi::um::commctrl::LVM_SETSELECTEDCOLUMN;
404
405 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
406 wh::send_message(handle, LVM_SETSELECTEDCOLUMN, index as _, 0);
407 }
408
409 pub fn selected_count(&self) -> usize {
411 use winapi::um::commctrl::LVM_GETSELECTEDCOUNT;
412
413 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
414 wh::send_message(handle, LVM_GETSELECTEDCOUNT, 0, 0) as usize
415 }
416
417 pub fn insert_column<I: Into<InsertListViewColumn>>(&self, insert: I) {
419 use winapi::um::commctrl::LVM_INSERTCOLUMNW;
420
421 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
422
423 match self.list_style() {
424 ListViewStyle::Detailed => {},
425 _ => { return; }
426 }
427
428 let insert = insert.into();
429
430 let mut mask = LVCF_TEXT | LVCF_WIDTH;
431
432 let text = insert.text.unwrap_or("".to_string());
433 let mut text = to_utf16(&text);
434
435 let col_width = insert.width.unwrap_or(100) as f64 * crate::win32::high_dpi::scale_factor();
436
437 if insert.fmt.is_some() { mask |= LVCF_FMT; }
438
439 let mut item: LVCOLUMNW = unsafe { mem::zeroed() };
440 item.mask = mask;
441 item.fmt = insert.fmt.unwrap_or(ListViewColumnFlags::empty()).bits() as _;
442 item.cx = col_width as i32;
443 item.pszText = text.as_mut_ptr();
444 item.cchTextMax = text.len() as i32;
445
446 let col_count = self.column_len() as i32;
447
448 wh::send_message(
449 handle,
450 LVM_INSERTCOLUMNW,
451 insert.index.unwrap_or(col_count) as usize,
452 (&item as *const LVCOLUMNW) as _
453 );
454 }
455
456 pub fn has_column(&self, index: usize) -> bool {
458 use winapi::um::commctrl::LVM_GETCOLUMNW;
459
460 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
461
462 let mut col: LVCOLUMNW = unsafe { mem::zeroed() };
463
464 wh::send_message(handle, LVM_GETCOLUMNW, index as _, &mut col as *mut LVCOLUMNW as _) != 0
465 }
466
467 pub fn column(&self, index: usize, text_buffer_size: i32) -> Option<ListViewColumn> {
470 use winapi::um::commctrl::LVM_GETCOLUMNW;
471
472 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
473
474 let mut text_buffer: Vec<u16> = Vec::with_capacity(text_buffer_size as _);
475 unsafe { text_buffer.set_len(text_buffer_size as _); }
476
477 let mut col: LVCOLUMNW = unsafe { mem::zeroed() };
478 col.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
479 col.pszText = text_buffer.as_mut_ptr();
480 col.cchTextMax = text_buffer_size;
481
482 match wh::send_message(handle, LVM_GETCOLUMNW, index as _, &mut col as *mut LVCOLUMNW as _) == 0 {
483 true => None,
484 false => Some(ListViewColumn {
485 fmt: col.fmt,
486 width: col.cx,
487 text: from_utf16(&text_buffer),
488 })
489 }
490 }
491
492 pub fn update_column<I: Into<InsertListViewColumn>>(&self, index: usize, column: I) {
494 use winapi::um::commctrl::LVM_SETCOLUMNW;
495
496 if !self.has_column(index) {
497 return;
498 }
499
500 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
501 let insert = column.into();
502
503 let use_text = insert.text.is_some();
504 let use_width = insert.width.is_some();
505 let use_fmt = insert.fmt.is_some();
506
507 let mut mask = 0;
508 if use_text { mask |= LVCF_TEXT; }
509 if use_width { mask |= LVCF_WIDTH; }
510 if use_fmt { mask |= LVCF_FMT; }
511
512 let text = insert.text.unwrap_or("".to_string());
513 let mut text = to_utf16(&text);
514
515 let mut item: LVCOLUMNW = unsafe { mem::zeroed() };
516 item.mask = mask;
517 item.fmt = insert.fmt.unwrap_or(ListViewColumnFlags::empty()).bits() as _;
518 item.cx = insert.width.unwrap_or(0);
519
520 if use_text {
521 item.pszText = text.as_mut_ptr();
522 item.cchTextMax = text.len() as i32;
523 }
524
525 wh::send_message(handle, LVM_SETCOLUMNW, index as _, &mut item as *mut LVCOLUMNW as _);
526 }
527
528 pub fn remove_column(&self, column_index: usize) {
530 use winapi::um::commctrl::LVM_DELETECOLUMN;
531
532 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
533 wh::send_message(handle, LVM_DELETECOLUMN , column_index as _, 0);
534 }
535
536 pub fn headers_enabled(&self) -> bool {
538 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
539 let style = wh::get_style(handle);
540 (style & LVS_REPORT == LVS_REPORT) &&
541 (style & LVS_NOCOLUMNHEADER != LVS_NOCOLUMNHEADER)
542 }
543
544 pub fn set_headers_enabled(&self, enable: bool) {
546 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
547 let style = wh::get_style(handle);
548 if style & LVS_REPORT == LVS_REPORT {
549 if !enable {
550 wh::set_style(handle, style | LVS_NOCOLUMNHEADER);
551 } else {
552 wh::set_style(handle, style & !LVS_NOCOLUMNHEADER);
553 }
554 }
555 }
556
557 pub fn column_sort_arrow(&self, column_index: usize) -> Option<ListViewColumnSortArrow> {
559 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
560
561 let headers = wh::send_message(handle, LVM_GETHEADER, 0, 0);
562 if headers == 0 { return None; }
563
564 let mut header: HDITEMW = unsafe { mem::zeroed() };
565 header.mask = HDI_FORMAT;
566
567 let l = &mut header as *mut HDITEMW as _;
568 wh::send_message(headers as *mut _, HDM_GETITEMW, column_index, l);
569
570 match header.fmt & (HDF_SORTUP | HDF_SORTDOWN) {
571 HDF_SORTUP => Some(ListViewColumnSortArrow::Up),
572 HDF_SORTDOWN => Some(ListViewColumnSortArrow::Down),
573 _ => None,
574 }
575 }
576
577 pub fn set_column_sort_arrow(&self, column_index: usize, sort: Option<ListViewColumnSortArrow>) {
579 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
580
581 let headers = wh::send_message(handle, LVM_GETHEADER, 0, 0);
582 if headers != 0 {
583 let mut header: HDITEMW = unsafe { mem::zeroed() };
584 header.mask = HDI_FORMAT;
585
586 let l = &mut header as *mut HDITEMW as _;
587 wh::send_message(headers as *mut _, HDM_GETITEMW, column_index, l);
588
589 header.fmt &= !(HDF_SORTUP | HDF_SORTDOWN);
590 match sort {
591 Some(ListViewColumnSortArrow::Up) => header.fmt |= HDF_SORTUP,
592 Some(ListViewColumnSortArrow::Down) => header.fmt |= HDF_SORTDOWN,
593 _ => {}
594 };
595
596 let l = &mut header as *mut HDITEMW as _;
597 wh::send_message(headers as *mut _, HDM_SETITEMW, column_index, l);
598 }
599 }
600
601 pub fn set_column_width(&self, column_index: usize, width: isize) {
603 use winapi::um::commctrl::LVM_SETCOLUMNWIDTH;
604
605 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
606 wh::send_message(handle, LVM_SETCOLUMNWIDTH , column_index as _, width);
607 }
608
609 pub fn column_width(&self) -> usize {
611 use winapi::um::commctrl::LVM_GETCOLUMNWIDTH;
612
613 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
614 wh::send_message(handle, LVM_GETCOLUMNWIDTH, 0, 0) as usize
615 }
616
617 pub fn select_item(&self, row_index: usize, selected: bool) {
619 use winapi::um::commctrl::{LVM_SETITEMW, LVIF_STATE, LVIS_SELECTED};
620
621 if !self.has_item(row_index, 0) {
622 return;
623 }
624
625 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
626
627 let mut item: LVITEMW = unsafe { mem::zeroed() };
628 item.iItem = row_index as _;
629 item.mask = LVIF_STATE;
630 item.state = match selected { true => LVIS_SELECTED, false => 0 };
631 item.stateMask = LVIS_SELECTED;
632
633 wh::send_message(handle, LVM_SETITEMW , 0, &mut item as *mut LVITEMW as _);
634 }
635
636 pub fn selected_item(&self) -> Option<usize> {
639 use winapi::um::commctrl::{LVM_GETNEXTITEMINDEX, LVNI_SELECTED, LVITEMINDEX};
640
641 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
642 let mut index = None;
643
644 let mut i_data = LVITEMINDEX { iItem: -1, iGroup: -1 };
645
646 if wh::send_message(handle, LVM_GETNEXTITEMINDEX, &mut i_data as *mut LVITEMINDEX as _, LVNI_SELECTED) != 0 {
647 index = Some(i_data.iItem as usize);
648 }
649
650 index
651 }
652
653 pub fn selected_items(&self) -> Vec<usize> {
655 use winapi::um::commctrl::{LVM_GETNEXTITEMINDEX, LVNI_SELECTED, LVITEMINDEX};
656
657 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
658 let mut indices = Vec::with_capacity(self.len());
659
660 let mut i_data = LVITEMINDEX { iItem: -1, iGroup: -1 };
661
662 while wh::send_message(handle, LVM_GETNEXTITEMINDEX, &mut i_data as *mut LVITEMINDEX as _, LVNI_SELECTED) != 0 {
663 indices.push(i_data.iItem as usize);
664 }
665
666 indices
667 }
668
669 pub fn insert_item<I: Into<InsertListViewItem>>(&self, insert: I) {
671 use winapi::um::commctrl::{LVM_INSERTITEMW, LVM_SETITEMW};
672
673 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
674 let insert = insert.into();
675
676 let row_insert = insert.index.unwrap_or(i32::max_value());
677 let column_insert = insert.column_index;
678 if column_insert > 0 && !self.has_item(row_insert as _, 0) {
679 self.insert_item(InsertListViewItem {
680 index: Some(row_insert),
681 column_index: 0,
682 text: None,
683
684 #[cfg(feature="image-list")]
685 image: None,
686 });
687 }
688
689 let mask = LVIF_TEXT | check_image_mask(&insert);
690 let image = check_image(&insert);
691 let text = insert.text.unwrap_or("".to_string());
692 let mut text = to_utf16(&text);
693
694 let mut item: LVITEMW = unsafe { mem::zeroed() };
695 item.mask = mask;
696 item.iItem = row_insert;
697 item.iImage = image;
698 item.iSubItem = column_insert;
699 item.pszText = text.as_mut_ptr();
700 item.cchTextMax = text.len() as i32;
701
702 if column_insert == 0 {
703 wh::send_message(handle, LVM_INSERTITEMW , 0, &mut item as *mut LVITEMW as _);
704 } else {
705 wh::send_message(handle, LVM_SETITEMW , 0, &mut item as *mut LVITEMW as _);
706 }
707 }
708
709 pub fn item_is_visible(&self, index: usize) -> bool {
711 use winapi::um::commctrl::LVM_ISITEMVISIBLE;
712
713 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
714 wh::send_message(handle, LVM_ISITEMVISIBLE , index as _, 0) == 1
715 }
716
717 pub fn has_item(&self, row_index: usize, column_index: usize) -> bool {
719 use winapi::um::commctrl::LVM_GETITEMW;
720
721 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
722
723 let mut item: LVITEMW = unsafe { mem::zeroed() };
724 item.iItem = row_index as _;
725 item.iSubItem = column_index as _;
726
727 wh::send_message(handle, LVM_GETITEMW , 0, &mut item as *mut LVITEMW as _) == 1
728 }
729
730 pub fn item(&self, row_index: usize, column_index: usize, text_buffer_size: usize) -> Option<ListViewItem> {
733 use winapi::um::commctrl::{LVM_GETITEMW, LVIF_STATE, LVIS_SELECTED};
734
735 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
736
737 let mut item: LVITEMW = unsafe { mem::zeroed() };
738 item.iItem = row_index as _;
739 item.iSubItem = column_index as _;
740 item.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_STATE;
741 item.stateMask = LVIS_SELECTED;
742
743 let mut text_buffer: Vec<u16> = Vec::with_capacity(text_buffer_size);
744 unsafe { text_buffer.set_len(text_buffer_size); }
745 item.pszText = text_buffer.as_mut_ptr();
746 item.cchTextMax = text_buffer_size as _;
747
748
749 let found = wh::send_message(handle, LVM_GETITEMW , 0, &mut item as *mut LVITEMW as _) == 1;
750 if !found {
751 return None;
752 }
753
754 Some ( build_list_view_image(row_index, column_index, item.state, &text_buffer, item.iImage) )
755 }
756
757 pub fn update_item<I: Into<InsertListViewItem>>(&self, row_index: usize, data: I) {
760 use winapi::um::commctrl::LVM_SETITEMW;
761
762 if !self.has_item(row_index, 0) {
763 return;
764 }
765
766 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
767 let insert = data.into();
768
769 let mut mask = check_image_mask(&insert);
770 if insert.text.is_some() {
771 mask |= LVIF_TEXT;
772 }
773
774 let image = check_image(&insert);
775
776 let use_text = insert.text.is_some();
777 let text = insert.text.unwrap_or("".to_string());
778 let mut text = to_utf16(&text);
779
780 let mut item: LVITEMW = unsafe { mem::zeroed() };
781 item.mask = mask;
782 item.iItem = row_index as _;
783 item.iImage = image;
784 item.iSubItem = insert.column_index as _;
785
786 if use_text {
787 item.pszText = text.as_mut_ptr();
788 item.cchTextMax = text.len() as i32;
789 }
790
791 wh::send_message(handle, LVM_SETITEMW , 0, &mut item as *mut LVITEMW as _);
792 }
793
794 pub fn remove_item(&self, row_index: usize) -> bool {
797 use winapi::um::commctrl::LVM_DELETEITEM;
798
799 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
800 wh::send_message(handle, LVM_DELETEITEM , row_index as _, 0) == 1
801 }
802
803 pub fn insert_items<I: Clone+Into<InsertListViewItem>>(&self, insert: &[I]) {
805 for i in insert.iter() {
806 self.insert_item(i.clone());
807 }
808 }
809
810 pub fn insert_items_row<I: Clone+Into<InsertListViewItem>>(&self, row_index: Option<i32>, insert: &[I]) {
814 let mut column_index = 0;
815 let row_index = row_index.or(Some(self.len() as _));
816
817 for i in insert.iter() {
818 let mut item: InsertListViewItem = i.clone().into();
819 item.index = row_index;
820 item.column_index = column_index;
821
822 self.insert_item(item);
823
824 column_index += 1;
825 }
826 }
827
828 pub fn list_style(&self) -> ListViewStyle {
830 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
831 ListViewStyle::from_bits(wh::get_style(handle))
832 }
833
834 pub fn set_list_style(&self, style: ListViewStyle) {
836 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
837
838 let mut old_style = wh::get_style(handle);
839 old_style = old_style & !0b11;
840
841 wh::set_style(handle, old_style | style.bits());
842 }
843
844 pub fn len(&self) -> usize {
846 use winapi::um::commctrl::LVM_GETITEMCOUNT;
847 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
848 wh::send_message(handle, LVM_GETITEMCOUNT , 0, 0) as usize
849 }
850
851 pub fn column_len(&self) -> usize {
853 use winapi::um::commctrl::LVM_GETCOLUMNWIDTH;
854
855 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
856
857 let mut count = 0;
858 while wh::send_message(handle, LVM_GETCOLUMNWIDTH, count, 0) != 0 {
859 count += 1;
860 }
861
862 count
863 }
864
865 pub fn set_item_count(&self, n: u32) {
868 use winapi::um::commctrl::LVM_SETITEMCOUNT;
869
870 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
871 wh::send_message(handle, LVM_SETITEMCOUNT, n as _, 0);
872 }
873
874 pub fn set_redraw(&self, enabled: bool) {
877 use winapi::um::winuser::WM_SETREDRAW;
878
879 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
880 wh::send_message(handle, WM_SETREDRAW, enabled as _, 0);
881 }
882
883 pub fn set_icon_spacing(&self, dx: u16, dy: u16) {
887 use winapi::um::commctrl::LVM_SETICONSPACING;
888 use winapi::shared::minwindef::MAKELONG;
889
890 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
891 let spacing = MAKELONG(dx, dy);
892 wh::send_message(handle, LVM_SETICONSPACING, 0 as _, spacing as _);
893
894 self.invalidate();
895 }
896
897 pub fn invalidate(&self) {
901 use winapi::um::winuser::InvalidateRect;
902 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
903 unsafe { InvalidateRect(handle, ptr::null(), 1); }
904 }
905
906 pub fn clear(&self) {
908 use winapi::um::commctrl::LVM_DELETEALLITEMS;
909
910 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
911 wh::send_message(handle, LVM_DELETEALLITEMS, 0, 0);
912 }
913
914 pub fn focus(&self) -> bool {
916 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
917 unsafe { wh::get_focus(handle) }
918 }
919
920 pub fn set_focus(&self) {
922 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
923 unsafe { wh::set_focus(handle); }
924 }
925
926 pub fn enabled(&self) -> bool {
928 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
929 unsafe { wh::get_window_enabled(handle) }
930 }
931
932 pub fn set_enabled(&self, v: bool) {
934 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
935 unsafe { wh::set_window_enabled(handle, v) }
936 }
937
938 pub fn visible(&self) -> bool {
941 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
942 unsafe { wh::get_window_visibility(handle) }
943 }
944
945 pub fn set_visible(&self, v: bool) {
947 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
948 unsafe { wh::set_window_visibility(handle, v) }
949 }
950
951 pub fn size(&self) -> (u32, u32) {
953 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
954 unsafe { wh::get_window_size(handle) }
955 }
956
957 pub fn set_size(&self, x: u32, y: u32) {
959 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
960 unsafe { wh::set_window_size(handle, x, y, true) }
961 }
962
963 pub fn position(&self) -> (i32, i32) {
965 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
966 unsafe { wh::get_window_position(handle) }
967 }
968
969 pub fn set_position(&self, x: i32, y: i32) {
971 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
972 unsafe { wh::set_window_position(handle, x, y) }
973 }
974
975 pub fn class_name(&self) -> &'static str {
977 ::winapi::um::commctrl::WC_LISTVIEW
978 }
979
980 pub fn flags(&self) -> u32 {
982 WS_VISIBLE | WS_TABSTOP | LVS_SHOWSELALWAYS
983 }
984
985 pub fn forced_flags(&self) -> u32 {
987 use winapi::um::winuser::{WS_CHILD, WS_BORDER};
988
989 WS_CHILD | WS_BORDER | LVS_NOCOLUMNHEADER
990 }
991
992 fn set_double_buffered(&mut self) {
993 use crate::bind_raw_event_handler_inner;
994 use winapi::um::wingdi::{CreateSolidBrush, RGB};
995
996 let double_buffer = ListViewDoubleBuffer {
997 buffer: ptr::null_mut(),
998 size: [0, 0],
999 bg: unsafe { CreateSolidBrush(RGB(255, 255, 255)) },
1000 };
1001
1002 let rc_double_buffer = Rc::new(RefCell::new(double_buffer));
1003 let callback_double_buffer = rc_double_buffer.clone();
1004
1005 let handler = bind_raw_event_handler_inner(&self.handle, 0x020, move |hwnd, msg, _, _| {
1006 use winapi::um::winuser::{GetClientRect, BeginPaint, EndPaint, FillRect, SendMessageW, RedrawWindow, RDW_ERASENOW, RDW_UPDATENOW, RDW_INVALIDATE};
1007 use winapi::um::winuser::{WM_PAINT, WM_ERASEBKGND, WM_PRINTCLIENT, PAINTSTRUCT};
1008 use winapi::um::wingdi::{CreateCompatibleDC, CreateCompatibleBitmap, SelectObject, BitBlt, DeleteDC, DeleteObject, SRCCOPY};
1009
1010 match msg {
1011 WM_PAINT => unsafe {
1012 let mut double_buffer = callback_double_buffer.borrow_mut();
1013
1014 let mut r = mem::zeroed();
1015 GetClientRect(hwnd, &mut r);
1016 let client_width = r.right - r.left;
1017 let client_height = r.bottom - r.top;
1018
1019 let mut paint: PAINTSTRUCT = mem::zeroed();
1020 BeginPaint(hwnd, &mut paint);
1021
1022 if double_buffer.buffer.is_null() || double_buffer.size != [client_width, client_height] {
1023 if !double_buffer.buffer.is_null() {
1024 DeleteObject(double_buffer.buffer as _);
1025 }
1026
1027 double_buffer.size = [client_width, client_height];
1028 double_buffer.buffer = CreateCompatibleBitmap(paint.hdc, client_width, client_height);
1029 }
1030
1031 let backbuffer = double_buffer.buffer;
1032 let backbuffer_dc = CreateCompatibleDC(paint.hdc);
1033
1034 let old = SelectObject(backbuffer_dc, backbuffer as _);
1036 FillRect(backbuffer_dc, &r, double_buffer.bg as _);
1037
1038 SendMessageW(hwnd, WM_PRINTCLIENT, backbuffer_dc as _, 0);
1040 BitBlt(
1041 paint.hdc as _,
1042 0, 0,
1043 client_width, client_height,
1044 backbuffer_dc,
1045 0, 0,
1046 SRCCOPY
1047 );
1048
1049 SelectObject(backbuffer_dc, old);
1051 DeleteDC(backbuffer_dc);
1052 EndPaint(hwnd, &paint);
1053
1054 let header = SendMessageW(hwnd, LVM_GETHEADER, 0, 0);
1056 if header != 0 {
1057 let mut r = mem::zeroed();
1058 GetClientRect(header as _, &mut r);
1059 RedrawWindow(header as _, ptr::null_mut(), ptr::null_mut(), RDW_ERASENOW|RDW_UPDATENOW|RDW_INVALIDATE);
1060 }
1061
1062 Some(1)
1063 },
1064 WM_ERASEBKGND => {
1065 Some(1)
1066 },
1067 _ => None
1068 }
1069 }).unwrap();
1070
1071 self.handler0 = Some(handler);
1072 self.double_buffer = Some(rc_double_buffer);
1073 }
1074
1075}
1076
1077impl Drop for ListView {
1078 fn drop(&mut self) {
1079 use winapi::um::wingdi::DeleteObject;
1080
1081 if let Some(backbuffer) = self.double_buffer.take() {
1082 let double_buffer = backbuffer.borrow();
1083 unsafe {
1084 DeleteObject(double_buffer.buffer as _);
1085 DeleteObject(double_buffer.bg as _);
1086 }
1087 }
1088
1089 if let Some(h) = self.handler0.as_ref() {
1090 drop(unbind_raw_event_handler(h));
1091 }
1092
1093 self.handle.destroy();
1094 }
1095}
1096
1097pub struct ListViewBuilder {
1098 size: (i32, i32),
1099 position: (i32, i32),
1100 background_color: Option<[u8; 3]>,
1101 text_color: Option<[u8; 3]>,
1102 double_buffer: bool,
1103 focus: bool,
1104 flags: Option<ListViewFlags>,
1105 ex_flags: Option<ListViewExFlags>,
1106 ex_window_flags: u32,
1107 style: ListViewStyle,
1108 item_count: u32,
1109 parent: Option<ControlHandle>
1110}
1111
1112impl ListViewBuilder {
1113
1114 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ListViewBuilder {
1115 self.parent = Some(p.into());
1116 self
1117 }
1118
1119 pub fn flags(mut self, flags: ListViewFlags) -> ListViewBuilder {
1120 self.flags = Some(flags);
1121 self
1122 }
1123
1124 pub fn ex_flags(mut self, flags: ListViewExFlags) -> ListViewBuilder {
1125 self.ex_flags = Some(flags);
1126 self
1127 }
1128
1129 pub fn ex_window_flags(mut self, flags: u32) -> ListViewBuilder {
1130 self.ex_window_flags = flags;
1131 self
1132 }
1133
1134
1135 pub fn size(mut self, size: (i32, i32)) -> ListViewBuilder {
1136 self.size = size;
1137 self
1138 }
1139
1140 pub fn position(mut self, position: (i32, i32)) -> ListViewBuilder {
1141 self.position = position;
1142 self
1143 }
1144
1145 pub fn double_buffer(mut self, buffer: bool) -> ListViewBuilder {
1146 self.double_buffer = buffer;
1147 self
1148 }
1149
1150 pub fn background_color(mut self, color: [u8; 3]) -> ListViewBuilder {
1151 self.background_color = Some(color);
1152 self
1153 }
1154
1155 pub fn text_color(mut self, color: [u8; 3]) -> ListViewBuilder {
1156 self.text_color = Some(color);
1157 self
1158 }
1159
1160 pub fn item_count(mut self, count: u32) -> ListViewBuilder {
1161 self.item_count = count;
1162 self
1163 }
1164
1165 pub fn list_style(mut self, style: ListViewStyle) -> ListViewBuilder {
1166 self.style = style;
1167 self
1168 }
1169
1170 pub fn focus(mut self, focus: bool) -> ListViewBuilder {
1171 self.focus = focus;
1172 self
1173 }
1174
1175 pub fn build(self, out: &mut ListView) -> Result<(), NwgError> {
1176 let mut flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
1177 flags |= self.style.bits();
1178
1179 let parent = match self.parent {
1180 Some(p) => Ok(p),
1181 None => Err(NwgError::no_parent("ListView"))
1182 }?;
1183
1184 *out = Default::default();
1185
1186 out.handle = ControlBase::build_hwnd()
1187 .class_name(out.class_name())
1188 .forced_flags(out.forced_flags())
1189 .flags(flags)
1190 .ex_flags(self.ex_window_flags)
1191 .size(self.size)
1192 .position(self.position)
1193 .text("")
1194 .parent(Some(parent))
1195 .build()?;
1196
1197 if self.double_buffer {
1198 out.set_double_buffered();
1199 }
1200
1201 if self.item_count > 0 {
1202 out.set_item_count(self.item_count);
1203 }
1204
1205 if self.focus {
1206 out.set_focus();
1207 }
1208
1209 if let Some(flags) = self.ex_flags {
1210 let flags = flags.bits();
1211 wh::send_message(out.handle.hwnd().unwrap(), LVM_SETEXTENDEDLISTVIEWSTYLE, flags as _, flags as _);
1212 }
1213
1214 if let Some([r, g, b]) = self.background_color {
1215 out.set_background_color(r, g, b);
1216 }
1217
1218 if let Some([r, g, b]) = self.text_color {
1219 out.set_text_color(r, g, b);
1220 }
1221
1222 Ok(())
1223 }
1224
1225}
1226
1227impl<'a> From<&'a str> for InsertListViewItem {
1228 fn from(i: &'a str) -> Self {
1229 InsertListViewItem {
1230 index: None,
1231 column_index: 0,
1232 text: Some(i.to_string()),
1233
1234 #[cfg(feature="image-list")]
1235 image: None
1236 }
1237 }
1238}
1239
1240impl From<String> for InsertListViewItem {
1241 fn from(i: String) -> Self {
1242 InsertListViewItem {
1243 index: None,
1244 column_index: 0,
1245 text: Some(i),
1246
1247 #[cfg(feature="image-list")]
1248 image: None
1249 }
1250 }
1251}
1252
1253impl<'a> From<&'a str> for InsertListViewColumn {
1254 fn from(i: &'a str) -> Self {
1255 InsertListViewColumn {
1256 index: None,
1257 fmt: None,
1258 width: Some(100),
1259 text: Some(i.to_string())
1260 }
1261 }
1262}
1263
1264impl From<String> for InsertListViewColumn {
1265 fn from(i: String) -> Self {
1266 InsertListViewColumn {
1267 index: None,
1268 fmt: None,
1269 width: Some(100),
1270 text: Some(i)
1271 }
1272 }
1273}
1274
1275 #[cfg(feature="image-list")]
1278fn check_image_mask(i: &InsertListViewItem) -> u32 {
1279 if i.image.is_some() {
1280 LVIF_IMAGE
1281 } else {
1282 0
1283 }
1284}
1285
1286#[cfg(feature="image-list")]
1287fn check_image(i: &InsertListViewItem) -> i32 { i.image.unwrap_or(0) }
1288
1289#[cfg(not(feature="image-list"))]
1290fn check_image_mask(_i: &InsertListViewItem) -> u32 { 0 }
1291
1292#[cfg(not(feature="image-list"))]
1293fn check_image(_i: &InsertListViewItem) -> i32 { 0 }
1294
1295#[cfg(feature="image-list")]
1296fn build_list_view_image(row_index: usize, column_index: usize, state: u32, text_buffer: &[u16], image: i32) -> ListViewItem {
1297 use winapi::um::commctrl::LVIS_SELECTED;
1298
1299 ListViewItem {
1300 row_index: row_index as _,
1301 column_index: column_index as _,
1302 text: from_utf16(&text_buffer),
1303 selected: state & LVIS_SELECTED == LVIS_SELECTED,
1304 image
1305 }
1306}
1307
1308#[cfg(not(feature="image-list"))]
1309fn build_list_view_image(row_index: usize, column_index: usize, state: u32, text_buffer: &[u16], _image: i32) -> ListViewItem {
1310 use winapi::um::commctrl::LVIS_SELECTED;
1311
1312 ListViewItem {
1313 row_index: row_index as _,
1314 column_index: column_index as _,
1315 text: from_utf16(&text_buffer),
1316 selected: state & LVIS_SELECTED == LVIS_SELECTED,
1317 }
1318}