native_windows_gui2/controls/
list_view.rs

1use super::{ControlBase, ControlHandle};
2use crate::win32::base_helper::{check_hwnd, from_utf16, to_utf16};
3use crate::win32::window_helper as wh;
4use crate::{NwgError, RawEventHandler, unbind_raw_event_handler};
5use std::{cell::RefCell, mem, ptr, rc::Rc};
6use winapi::shared::windef::{HBITMAP, HBRUSH};
7use winapi::um::commctrl::{
8    HDF_SORTDOWN, HDF_SORTUP, HDI_FORMAT, HDITEMW, HDM_GETITEMW, HDM_SETITEMW, LVCF_FMT, LVCF_TEXT,
9    LVCF_WIDTH, LVCFMT_BITMAP_ON_RIGHT, LVCFMT_CENTER, LVCFMT_COL_HAS_IMAGES, LVCFMT_IMAGE,
10    LVCFMT_JUSTIFYMASK, LVCFMT_LEFT, LVCFMT_RIGHT, LVCOLUMNW, LVIF_IMAGE, LVIF_TEXT, LVITEMW,
11    LVM_GETHEADER, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_AUTOSIZECOLUMNS, LVS_EX_BORDERSELECT,
12    LVS_EX_FULLROWSELECT, LVS_EX_GRIDLINES, LVS_EX_HEADERDRAGDROP, LVS_EX_HEADERINALLVIEWS,
13    LVS_ICON, LVS_LIST, LVS_NOCOLUMNHEADER, LVS_REPORT, LVS_SHOWSELALWAYS, LVS_SINGLESEL,
14    LVS_SMALLICON,
15};
16use winapi::um::winuser::{WS_DISABLED, WS_TABSTOP, WS_VISIBLE};
17
18#[cfg(feature = "image-list")]
19use crate::ImageList;
20
21const NOT_BOUND: &'static str = "ListView is not yet bound to a winapi object";
22const BAD_HANDLE: &'static str = "INTERNAL ERROR: ListView handle is not HWND!";
23
24bitflags! {
25    /**
26        The list view flags:
27
28        * VISIBLE:  The list view is immediatly visible after creation
29        * DISABLED: The list view cannot be interacted with by the user. It also has a grayed out look. The user can drag the items to any location in the list-view window.
30        * TAB_STOP: The control can be selected using tab navigation
31        * NO_HEADER: Remove the headers in Detailed view (ON by default, use `ListView::set_headers_enabled` to enable headers)
32        * SINGLE_SELECTION: Only one item can be selected
33        * ALWAYS_SHOW_SELECTION: Shows the selected list view item when the control is not in focus
34    */
35    pub struct ListViewFlags: u32 {
36        const VISIBLE = WS_VISIBLE;
37        const DISABLED = WS_DISABLED;
38        const TAB_STOP = WS_TABSTOP;
39
40        const SINGLE_SELECTION = LVS_SINGLESEL;
41
42        const ALWAYS_SHOW_SELECTION = LVS_SHOWSELALWAYS;
43
44        // Remove the headers in Detailed view (ON by default due to backward compatibility)
45        // TODO: OFF by default in next major releases
46        const NO_HEADER = LVS_NOCOLUMNHEADER;
47    }
48}
49
50bitflags! {
51    /**
52        The list view extended flags (to use with ListViewBuilder::ex_flags):
53
54        * NONE:  Do not use any extended styles
55        * GRID:  The list view has a grid. Only if the list view is in report mode.
56        * BORDER_SELECT: Only highlight the border instead of the full item. COMMCTRL version 4.71 or later
57        * AUTO_COLUMN_SIZE: Automatically resize to column
58        * FULL_ROW_SELECT: When an item is selected, the item and all its subitems are highlighted. Only in detailed view
59        * HEADER_DRAG_DROP: The user can drag and drop the headers to rearrage them
60        * HEADER_IN_ALL_VIEW: Show the header in all view (not just report)
61    */
62    pub struct ListViewExFlags: u32 {
63        const NONE = 0;
64        const GRID = LVS_EX_GRIDLINES;
65        const BORDER_SELECT = LVS_EX_BORDERSELECT;
66        const AUTO_COLUMN_SIZE = LVS_EX_AUTOSIZECOLUMNS;
67        const FULL_ROW_SELECT = LVS_EX_FULLROWSELECT;
68        const HEADER_DRAG_DROP = LVS_EX_HEADERDRAGDROP;
69        const HEADER_IN_ALL_VIEW = LVS_EX_HEADERINALLVIEWS;
70
71    }
72}
73
74bitflags! {
75    /**
76        The format flags for a list view column. Not all combination are valid.
77        The alignment of the leftmost column is always LEFT.
78
79        * LEFT: Text is left-aligned.
80        * RIGHT: Text is right-aligned
81        * CENTER: Text is centered
82        * JUSTIFY_MASK: A bitmask used to select those bits of fmt that control field justification.
83        * IMAGE: The items under to column displays an image from an image list
84        * IMAGE_RIGHT: The bitmap appears to the right of text
85        * IMAGE_COL: The header item contains an image in the image list.
86    */
87    pub struct ListViewColumnFlags: u32 {
88        const LEFT = LVCFMT_LEFT as u32;
89        const RIGHT = LVCFMT_RIGHT as u32;
90        const CENTER = LVCFMT_CENTER as u32;
91        const JUSTIFY_MASK = LVCFMT_JUSTIFYMASK as u32;
92        const IMAGE = LVCFMT_IMAGE as u32;
93        const IMAGE_RIGHT = LVCFMT_BITMAP_ON_RIGHT as u32;
94        const IMAGE_COL = LVCFMT_COL_HAS_IMAGES as u32;
95    }
96}
97
98/**
99    The display style for the items in a list view
100*/
101#[derive(Copy, Clone, Debug)]
102#[repr(u8)]
103pub enum ListViewStyle {
104    Simple,
105    Detailed,
106    Icon,
107    SmallIcon,
108}
109
110impl ListViewStyle {
111    fn from_bits(bits: u32) -> ListViewStyle {
112        let bits = bits & 0b11;
113        match bits {
114            LVS_ICON => ListViewStyle::Icon,
115            LVS_REPORT => ListViewStyle::Detailed,
116            LVS_SMALLICON => ListViewStyle::SmallIcon,
117            LVS_LIST => ListViewStyle::Simple,
118            _ => unreachable!(),
119        }
120    }
121
122    fn bits(&self) -> u32 {
123        match self {
124            ListViewStyle::Simple => LVS_LIST,
125            ListViewStyle::Detailed => LVS_REPORT,
126            ListViewStyle::Icon => LVS_ICON,
127            ListViewStyle::SmallIcon => LVS_SMALLICON,
128        }
129    }
130}
131
132/**
133    Items in a list view can be associated with multiple image list.
134    This identify which image list to set/get using the ListView api.
135*/
136#[cfg(feature = "image-list")]
137#[derive(Copy, Clone, Debug)]
138pub enum ListViewImageListType {
139    /// Normal sized icons
140    Normal,
141
142    /// Small icons
143    Small,
144
145    /// State icons
146    State,
147
148    /// Group header list (not yet implemented)
149    GroupHeader,
150}
151
152#[cfg(feature = "image-list")]
153impl ListViewImageListType {
154    fn to_raw(&self) -> i32 {
155        use winapi::um::commctrl::{LVSIL_GROUPHEADER, LVSIL_NORMAL, LVSIL_SMALL, LVSIL_STATE};
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#[derive(Default, Clone, Debug)]
167/// Represents a column in a detailed list view
168pub struct InsertListViewColumn {
169    /// Index of the column
170    pub index: Option<i32>,
171
172    /// Format of the column
173    pub fmt: Option<ListViewColumnFlags>,
174
175    /// Width of the column in pixels
176    pub width: Option<i32>,
177
178    /// Text of the column to insert
179    pub text: Option<String>,
180}
181
182/// The data of a list view column
183#[derive(Default, Clone, Debug)]
184pub struct ListViewColumn {
185    pub fmt: i32,
186    pub width: i32,
187    pub text: String,
188}
189
190/// Represents a column sort indicator in a detailed list view
191#[derive(Copy, Clone, Debug)]
192pub enum ListViewColumnSortArrow {
193    Up,
194    Down,
195}
196
197/// Represents a list view item parameters
198#[derive(Default, Clone, Debug)]
199pub struct InsertListViewItem {
200    /// Index of the item to be inserted
201    /// If None and `insert_item` is used, the item is added at the end of the list
202    pub index: Option<i32>,
203
204    /// Index of the column
205    pub column_index: i32,
206
207    /// Text of the item to insert
208    pub text: Option<String>,
209
210    /// Index of the image in the image list
211    /// Icons are only supported at column 0
212    #[cfg(feature = "image-list")]
213    pub image: Option<i32>,
214}
215
216/// The data of a list view item
217#[derive(Default, Clone, Debug)]
218pub struct ListViewItem {
219    pub row_index: i32,
220    pub column_index: i32,
221    pub text: String,
222
223    /// If the item is currently selected
224    pub selected: bool,
225
226    #[cfg(feature = "image-list")]
227    pub image: i32,
228}
229
230struct ListViewDoubleBuffer {
231    buffer: HBITMAP,
232    size: [i32; 2],
233    bg: HBRUSH,
234}
235
236/**
237A list-view control is a window that displays a collection of items.
238List-view controls provide several ways to arrange and display items and are much more flexible than simple ListBox.
239
240Requires the `list-view` feature.
241
242Builder parameters:
243  * `parent`:           **Required.** The list view parent container.
244  * `size`:             The list view size.
245  * `position`:         The list view position.
246  * `background_color`: The list view background color in RGB format
247  * `double_buffer`:    If the list view should be double buffered (defaults to true)
248  * `text_color`:       The list view text color in RGB format
249  * `flags`:            A combination of the ListViewFlags values.
250  * `ex_flags`:         A combination of the ListViewExFlags values. Not to be confused with `ex_window_flags`
251  * `ex_window_flags`:  A combination of win32 window extended flags. This is the equivalent to `ex_flags` in the other controls
252  * `style`:            One of the value of `ListViewStyle`
253  * `item_count`:       Number of item to preallocate
254  * `list_style`:       The default style of the listview
255  * `focus`:            The control receive focus after being created
256
257**Control events:**
258  * `MousePress(_)`:   Generic mouse press events on the tree view
259  * `OnMouseMove`:     Generic mouse mouse event
260  * `OnMouseWheel`:    Generic mouse wheel event
261  * `OnKeyPress`:      Generic key press event
262  * `OnKeyRelease`:    Generic key release event
263  * `OnListViewClear`: When all the items in a list view are destroyed
264  * `OnListViewItemRemoved`: When an item is about to be removed from the list view
265  * `OnListViewItemInsert`: When a new item is inserted in the list view
266  * `OnListViewItemActivated`: When an item in the list view is activated by the user
267  * `OnListViewClick`: When the user has clicked the left mouse button within the control
268  * `OnListViewRightClick`: When the user has clicked the right mouse button within the control
269  * `OnListViewDoubleClick`: When the user has clicked the left mouse button within the control twice rapidly
270  * `OnListViewColumnClick`: When the user has clicked the left mouse button on ListView header column
271  * `OnListViewItemChanged`: When an item is selected/unselected in the listview
272  * `OnListViewFocus`: When the list view has received focus
273  * `OnListViewFocusLost`: When the list view has lost focus
274
275*/
276#[derive(Default)]
277pub struct ListView {
278    pub handle: ControlHandle,
279    double_buffer: Option<Rc<RefCell<ListViewDoubleBuffer>>>,
280    handler0: Option<RawEventHandler>,
281}
282
283impl ListView {
284    pub fn builder() -> ListViewBuilder {
285        ListViewBuilder {
286            size: (300, 300),
287            position: (0, 0),
288            background_color: None,
289            double_buffer: true,
290            text_color: None,
291            focus: false,
292            flags: None,
293            ex_flags: None,
294            ex_window_flags: 0,
295            style: ListViewStyle::Simple,
296            parent: None,
297            item_count: 0,
298        }
299    }
300
301    /// Sets the image list of the listview
302    /// A listview can accept different kinds of image list. See `ListViewImageListType`
303    #[cfg(feature = "image-list")]
304    pub fn set_image_list(&self, list: Option<&ImageList>, list_type: ListViewImageListType) {
305        use winapi::um::commctrl::LVM_SETIMAGELIST;
306
307        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
308
309        let list_handle = list.map(|l| l.handle).unwrap_or(ptr::null_mut());
310        wh::send_message(
311            handle,
312            LVM_SETIMAGELIST,
313            list_type.to_raw() as _,
314            list_handle as _,
315        );
316
317        self.invalidate();
318    }
319
320    /// Returns the current image list for the selected type. The returned image list will not be owned.
321    /// Can return `None` if there is no assocaited image list
322    #[cfg(feature = "image-list")]
323    pub fn image_list(&self, list_type: ListViewImageListType) -> Option<ImageList> {
324        use winapi::um::commctrl::LVM_GETIMAGELIST;
325
326        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
327
328        match wh::send_message(handle, LVM_GETIMAGELIST, list_type.to_raw() as _, 0) {
329            0 => None,
330            handle => Some(ImageList {
331                handle: handle as _,
332                owned: false,
333            }),
334        }
335    }
336
337    /// Sets the text color of the list view
338    pub fn set_text_color(&self, r: u8, g: u8, b: u8) {
339        use winapi::um::commctrl::LVM_SETTEXTCOLOR;
340        use winapi::um::wingdi::RGB;
341
342        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
343
344        let color = RGB(r, g, b);
345
346        wh::send_message(handle, LVM_SETTEXTCOLOR, 0, color as _);
347
348        self.invalidate();
349    }
350
351    /// Returns the current text color
352    pub fn text_color(&self) -> [u8; 3] {
353        use winapi::um::commctrl::LVM_GETTEXTCOLOR;
354        use winapi::um::wingdi::{GetBValue, GetGValue, GetRValue};
355
356        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
357        let col = wh::send_message(handle, LVM_GETTEXTCOLOR, 0, 0) as u32;
358
359        [GetRValue(col), GetGValue(col), GetBValue(col)]
360    }
361
362    /// Sets the background color of the list view
363    pub fn set_background_color(&self, r: u8, g: u8, b: u8) {
364        use winapi::um::commctrl::LVM_SETBKCOLOR;
365        use winapi::um::wingdi::RGB;
366
367        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
368
369        let color = RGB(r, g, b);
370
371        wh::send_message(handle, LVM_SETBKCOLOR, 0, color as _);
372
373        self.invalidate();
374    }
375
376    /// Returns the background color of the list view
377    pub fn background_color(&self) -> [u8; 3] {
378        use winapi::um::commctrl::LVM_GETBKCOLOR;
379        use winapi::um::wingdi::{GetBValue, GetGValue, GetRValue};
380
381        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
382        let col = wh::send_message(handle, LVM_GETBKCOLOR, 0, 0) as u32;
383
384        [GetRValue(col), GetGValue(col), GetBValue(col)]
385    }
386
387    /// Returns the index of the selected column. Only available if Comclt32.dll version is >= 6.0.
388    pub fn selected_column(&self) -> usize {
389        use winapi::um::commctrl::LVM_GETSELECTEDCOLUMN;
390
391        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
392        wh::send_message(handle, LVM_GETSELECTEDCOLUMN, 0, 0) as usize
393    }
394
395    /// Sets the selected column. Only available if Comclt32.dll version is >= 6.0.
396    pub fn set_selected_column(&self, index: usize) {
397        use winapi::um::commctrl::LVM_SETSELECTEDCOLUMN;
398
399        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
400        wh::send_message(handle, LVM_SETSELECTEDCOLUMN, index as _, 0);
401    }
402
403    /// Returns the number of selected items
404    pub fn selected_count(&self) -> usize {
405        use winapi::um::commctrl::LVM_GETSELECTEDCOUNT;
406
407        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
408        wh::send_message(handle, LVM_GETSELECTEDCOUNT, 0, 0) as usize
409    }
410
411    /// Inserts a column in the report. Column are only used with the Detailed list view style.
412    pub fn insert_column<I: Into<InsertListViewColumn>>(&self, insert: I) {
413        use winapi::um::commctrl::LVM_INSERTCOLUMNW;
414
415        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
416
417        match self.list_style() {
418            ListViewStyle::Detailed => {}
419            _ => {
420                return;
421            }
422        }
423
424        let insert = insert.into();
425
426        let mut mask = LVCF_TEXT | LVCF_WIDTH;
427
428        let text = insert.text.unwrap_or("".to_string());
429        let mut text = to_utf16(&text);
430
431        let col_width = insert.width.unwrap_or(100) as f64 * crate::win32::high_dpi::scale_factor();
432
433        if insert.fmt.is_some() {
434            mask |= LVCF_FMT;
435        }
436
437        let mut item: LVCOLUMNW = unsafe { mem::zeroed() };
438        item.mask = mask;
439        item.fmt = insert.fmt.unwrap_or(ListViewColumnFlags::empty()).bits() as _;
440        item.cx = col_width as i32;
441        item.pszText = text.as_mut_ptr();
442        item.cchTextMax = text.len() as i32;
443
444        let col_count = self.column_len() as i32;
445
446        wh::send_message(
447            handle,
448            LVM_INSERTCOLUMNW,
449            insert.index.unwrap_or(col_count) as usize,
450            (&item as *const LVCOLUMNW) as _,
451        );
452    }
453
454    /// Checks if there is a column at the selected index
455    pub fn has_column(&self, index: usize) -> bool {
456        use winapi::um::commctrl::LVM_GETCOLUMNW;
457
458        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
459
460        let mut col: LVCOLUMNW = unsafe { mem::zeroed() };
461
462        wh::send_message(
463            handle,
464            LVM_GETCOLUMNW,
465            index as _,
466            &mut col as *mut LVCOLUMNW as _,
467        ) != 0
468    }
469
470    /// Returns the information of a column.
471    /// Because there's no way to fetch the actual text length, it's up to you to set the maximum buffer size
472    pub fn column(&self, index: usize, text_buffer_size: i32) -> Option<ListViewColumn> {
473        use winapi::um::commctrl::LVM_GETCOLUMNW;
474
475        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
476
477        let mut text_buffer: Vec<u16> = Vec::with_capacity(text_buffer_size as _);
478        unsafe {
479            text_buffer.set_len(text_buffer_size as _);
480        }
481
482        let mut col: LVCOLUMNW = unsafe { mem::zeroed() };
483        col.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
484        col.pszText = text_buffer.as_mut_ptr();
485        col.cchTextMax = text_buffer_size;
486
487        match wh::send_message(
488            handle,
489            LVM_GETCOLUMNW,
490            index as _,
491            &mut col as *mut LVCOLUMNW as _,
492        ) == 0
493        {
494            true => None,
495            false => Some(ListViewColumn {
496                fmt: col.fmt,
497                width: col.cx,
498                text: from_utf16(&text_buffer),
499            }),
500        }
501    }
502
503    /// Sets the information of a column. Does nothing if there is no column at the selected index
504    pub fn update_column<I: Into<InsertListViewColumn>>(&self, index: usize, column: I) {
505        use winapi::um::commctrl::LVM_SETCOLUMNW;
506
507        if !self.has_column(index) {
508            return;
509        }
510
511        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
512        let insert = column.into();
513
514        let use_text = insert.text.is_some();
515        let use_width = insert.width.is_some();
516        let use_fmt = insert.fmt.is_some();
517
518        let mut mask = 0;
519        if use_text {
520            mask |= LVCF_TEXT;
521        }
522        if use_width {
523            mask |= LVCF_WIDTH;
524        }
525        if use_fmt {
526            mask |= LVCF_FMT;
527        }
528
529        let text = insert.text.unwrap_or("".to_string());
530        let mut text = to_utf16(&text);
531
532        let mut item: LVCOLUMNW = unsafe { mem::zeroed() };
533        item.mask = mask;
534        item.fmt = insert.fmt.unwrap_or(ListViewColumnFlags::empty()).bits() as _;
535        item.cx = insert.width.unwrap_or(0);
536
537        if use_text {
538            item.pszText = text.as_mut_ptr();
539            item.cchTextMax = text.len() as i32;
540        }
541
542        wh::send_message(
543            handle,
544            LVM_SETCOLUMNW,
545            index as _,
546            &mut item as *mut LVCOLUMNW as _,
547        );
548    }
549
550    /// Deletes a column in a list view. Removing the column at index 0 is only available if ComCtl32.dll is version 6 or later.
551    pub fn remove_column(&self, column_index: usize) {
552        use winapi::um::commctrl::LVM_DELETECOLUMN;
553
554        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
555        wh::send_message(handle, LVM_DELETECOLUMN, column_index as _, 0);
556    }
557
558    /// Returns true if list view headers are visible
559    pub fn headers_enabled(&self) -> bool {
560        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
561        let style = wh::get_style(handle);
562        (style & LVS_REPORT == LVS_REPORT) && (style & LVS_NOCOLUMNHEADER != LVS_NOCOLUMNHEADER)
563    }
564
565    /// Enable or disable list view headers
566    pub fn set_headers_enabled(&self, enable: bool) {
567        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
568        let style = wh::get_style(handle);
569        if style & LVS_REPORT == LVS_REPORT {
570            if !enable {
571                wh::set_style(handle, style | LVS_NOCOLUMNHEADER);
572            } else {
573                wh::set_style(handle, style & !LVS_NOCOLUMNHEADER);
574            }
575        }
576    }
577
578    /// Returns column sort indicator
579    pub fn column_sort_arrow(&self, column_index: usize) -> Option<ListViewColumnSortArrow> {
580        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
581
582        let headers = wh::send_message(handle, LVM_GETHEADER, 0, 0);
583        if headers == 0 {
584            return None;
585        }
586
587        let mut header: HDITEMW = unsafe { mem::zeroed() };
588        header.mask = HDI_FORMAT;
589
590        let l = &mut header as *mut HDITEMW as _;
591        wh::send_message(headers as *mut _, HDM_GETITEMW, column_index, l);
592
593        match header.fmt & (HDF_SORTUP | HDF_SORTDOWN) {
594            HDF_SORTUP => Some(ListViewColumnSortArrow::Up),
595            HDF_SORTDOWN => Some(ListViewColumnSortArrow::Down),
596            _ => None,
597        }
598    }
599
600    /// Enable or disable column sort indicator. Draws a up-arrow / down-arrow.
601    pub fn set_column_sort_arrow(
602        &self,
603        column_index: usize,
604        sort: Option<ListViewColumnSortArrow>,
605    ) {
606        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
607
608        let headers = wh::send_message(handle, LVM_GETHEADER, 0, 0);
609        if headers != 0 {
610            let mut header: HDITEMW = unsafe { mem::zeroed() };
611            header.mask = HDI_FORMAT;
612
613            let l = &mut header as *mut HDITEMW as _;
614            wh::send_message(headers as *mut _, HDM_GETITEMW, column_index, l);
615
616            header.fmt &= !(HDF_SORTUP | HDF_SORTDOWN);
617            match sort {
618                Some(ListViewColumnSortArrow::Up) => header.fmt |= HDF_SORTUP,
619                Some(ListViewColumnSortArrow::Down) => header.fmt |= HDF_SORTDOWN,
620                _ => {}
621            };
622
623            let l = &mut header as *mut HDITEMW as _;
624            wh::send_message(headers as *mut _, HDM_SETITEMW, column_index, l);
625        }
626    }
627
628    /// Set the width of a column
629    pub fn set_column_width(&self, column_index: usize, width: isize) {
630        use winapi::um::commctrl::LVM_SETCOLUMNWIDTH;
631
632        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
633        wh::send_message(handle, LVM_SETCOLUMNWIDTH, column_index as _, width);
634    }
635
636    /// Returns the width of a column
637    pub fn column_width(&self) -> usize {
638        use winapi::um::commctrl::LVM_GETCOLUMNWIDTH;
639
640        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
641        wh::send_message(handle, LVM_GETCOLUMNWIDTH, 0, 0) as usize
642    }
643
644    /// Select or unselect an item at `row_index`. Does nothing if the index is out of bounds.
645    pub fn select_item(&self, row_index: usize, selected: bool) {
646        use winapi::um::commctrl::{LVIF_STATE, LVIS_SELECTED, LVM_SETITEMW};
647
648        if !self.has_item(row_index, 0) {
649            return;
650        }
651
652        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
653
654        let mut item: LVITEMW = unsafe { mem::zeroed() };
655        item.iItem = row_index as _;
656        item.mask = LVIF_STATE;
657        item.state = match selected {
658            true => LVIS_SELECTED,
659            false => 0,
660        };
661        item.stateMask = LVIS_SELECTED;
662
663        wh::send_message(handle, LVM_SETITEMW, 0, &mut item as *mut LVITEMW as _);
664    }
665
666    /// Returns the index of the first selected item.
667    /// If there's more than one item selected, use `selected_items`
668    pub fn selected_item(&self) -> Option<usize> {
669        use winapi::um::commctrl::{LVITEMINDEX, LVM_GETNEXTITEMINDEX, LVNI_SELECTED};
670
671        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
672        let mut index = None;
673
674        let mut i_data = LVITEMINDEX {
675            iItem: -1,
676            iGroup: -1,
677        };
678
679        if wh::send_message(
680            handle,
681            LVM_GETNEXTITEMINDEX,
682            &mut i_data as *mut LVITEMINDEX as _,
683            LVNI_SELECTED,
684        ) != 0
685        {
686            index = Some(i_data.iItem as usize);
687        }
688
689        index
690    }
691
692    /// Returns the indices of every selected items.
693    pub fn selected_items(&self) -> Vec<usize> {
694        use winapi::um::commctrl::{LVITEMINDEX, LVM_GETNEXTITEMINDEX, LVNI_SELECTED};
695
696        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
697        let mut indices = Vec::with_capacity(self.len());
698
699        let mut i_data = LVITEMINDEX {
700            iItem: -1,
701            iGroup: -1,
702        };
703
704        while wh::send_message(
705            handle,
706            LVM_GETNEXTITEMINDEX,
707            &mut i_data as *mut LVITEMINDEX as _,
708            LVNI_SELECTED,
709        ) != 0
710        {
711            indices.push(i_data.iItem as usize);
712        }
713
714        indices
715    }
716
717    /// Inserts a new item into the list view
718    pub fn insert_item<I: Into<InsertListViewItem>>(&self, insert: I) {
719        use winapi::um::commctrl::{LVM_INSERTITEMW, LVM_SETITEMW};
720
721        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
722        let insert = insert.into();
723
724        let row_insert = insert.index.unwrap_or(i32::max_value());
725        let column_insert = insert.column_index;
726        if column_insert > 0 && !self.has_item(row_insert as _, 0) {
727            self.insert_item(InsertListViewItem {
728                index: Some(row_insert),
729                column_index: 0,
730                text: None,
731
732                #[cfg(feature = "image-list")]
733                image: None,
734            });
735        }
736
737        let mask = LVIF_TEXT | check_image_mask(&insert);
738        let image = check_image(&insert);
739        let text = insert.text.unwrap_or("".to_string());
740        let mut text = to_utf16(&text);
741
742        let mut item: LVITEMW = unsafe { mem::zeroed() };
743        item.mask = mask;
744        item.iItem = row_insert;
745        item.iImage = image;
746        item.iSubItem = column_insert;
747        item.pszText = text.as_mut_ptr();
748        item.cchTextMax = text.len() as i32;
749
750        if column_insert == 0 {
751            wh::send_message(handle, LVM_INSERTITEMW, 0, &mut item as *mut LVITEMW as _);
752        } else {
753            wh::send_message(handle, LVM_SETITEMW, 0, &mut item as *mut LVITEMW as _);
754        }
755    }
756
757    /// Checks if the item at the selected row is visible
758    pub fn item_is_visible(&self, index: usize) -> bool {
759        use winapi::um::commctrl::LVM_ISITEMVISIBLE;
760
761        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
762        wh::send_message(handle, LVM_ISITEMVISIBLE, index as _, 0) == 1
763    }
764
765    /// Returns `true` if an item exists at the selected index or `false` otherwise.
766    pub fn has_item(&self, row_index: usize, column_index: usize) -> bool {
767        use winapi::um::commctrl::LVM_GETITEMW;
768
769        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
770
771        let mut item: LVITEMW = unsafe { mem::zeroed() };
772        item.iItem = row_index as _;
773        item.iSubItem = column_index as _;
774
775        wh::send_message(handle, LVM_GETITEMW, 0, &mut item as *mut LVITEMW as _) == 1
776    }
777
778    /// Returns data of an item in the list view. Returns `None` if there is no data at the selected index
779    /// Because there is no way to fetch the actual text size, `text_buffer_size` must be set manually
780    pub fn item(
781        &self,
782        row_index: usize,
783        column_index: usize,
784        text_buffer_size: usize,
785    ) -> Option<ListViewItem> {
786        use winapi::um::commctrl::{LVIF_STATE, LVIS_SELECTED, LVM_GETITEMW};
787
788        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
789
790        let mut item: LVITEMW = unsafe { mem::zeroed() };
791        item.iItem = row_index as _;
792        item.iSubItem = column_index as _;
793        item.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_STATE;
794        item.stateMask = LVIS_SELECTED;
795
796        let mut text_buffer: Vec<u16> = Vec::with_capacity(text_buffer_size);
797        unsafe {
798            text_buffer.set_len(text_buffer_size);
799        }
800        item.pszText = text_buffer.as_mut_ptr();
801        item.cchTextMax = text_buffer_size as _;
802
803        let found = wh::send_message(handle, LVM_GETITEMW, 0, &mut item as *mut LVITEMW as _) == 1;
804        if !found {
805            return None;
806        }
807
808        Some(build_list_view_image(
809            row_index,
810            column_index,
811            item.state,
812            &text_buffer,
813            item.iImage,
814        ))
815    }
816
817    /// Updates the item at the selected position
818    /// Does nothing if there is no item at the selected position
819    pub fn update_item<I: Into<InsertListViewItem>>(&self, row_index: usize, data: I) {
820        use winapi::um::commctrl::LVM_SETITEMW;
821
822        if !self.has_item(row_index, 0) {
823            return;
824        }
825
826        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
827        let insert = data.into();
828
829        let mut mask = check_image_mask(&insert);
830        if insert.text.is_some() {
831            mask |= LVIF_TEXT;
832        }
833
834        let image = check_image(&insert);
835
836        let use_text = insert.text.is_some();
837        let text = insert.text.unwrap_or("".to_string());
838        let mut text = to_utf16(&text);
839
840        let mut item: LVITEMW = unsafe { mem::zeroed() };
841        item.mask = mask;
842        item.iItem = row_index as _;
843        item.iImage = image;
844        item.iSubItem = insert.column_index as _;
845
846        if use_text {
847            item.pszText = text.as_mut_ptr();
848            item.cchTextMax = text.len() as i32;
849        }
850
851        wh::send_message(handle, LVM_SETITEMW, 0, &mut item as *mut LVITEMW as _);
852    }
853
854    /// Remove all items on the seleted row. Returns `true` if an item was removed or false otherwise.
855    /// To "remove" an item without deleting the row, use `update_item` and set the text to "".
856    pub fn remove_item(&self, row_index: usize) -> bool {
857        use winapi::um::commctrl::LVM_DELETEITEM;
858
859        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
860        wh::send_message(handle, LVM_DELETEITEM, row_index as _, 0) == 1
861    }
862
863    /// Inserts multiple items into the control. Basically a loop over `insert_item`.
864    pub fn insert_items<I: Clone + Into<InsertListViewItem>>(&self, insert: &[I]) {
865        for i in insert.iter() {
866            self.insert_item(i.clone());
867        }
868    }
869
870    /// Insert multiple item at the selected row or at the end of the list if `None` was used.
871    /// This method overrides the `index` and the `column_index` of the items.
872    /// Useful when inserting strings into a single row. Ex: `list.insert_items_row(None, &["Hello", "World"]);`
873    pub fn insert_items_row<I: Clone + Into<InsertListViewItem>>(
874        &self,
875        row_index: Option<i32>,
876        insert: &[I],
877    ) {
878        let mut column_index = 0;
879        let row_index = row_index.or(Some(self.len() as _));
880
881        for i in insert.iter() {
882            let mut item: InsertListViewItem = i.clone().into();
883            item.index = row_index;
884            item.column_index = column_index;
885
886            self.insert_item(item);
887
888            column_index += 1;
889        }
890    }
891
892    /// Returns the current style of the list view
893    pub fn list_style(&self) -> ListViewStyle {
894        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
895        ListViewStyle::from_bits(wh::get_style(handle))
896    }
897
898    /// Sets the list view style of the control
899    pub fn set_list_style(&self, style: ListViewStyle) {
900        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
901
902        let mut old_style = wh::get_style(handle);
903        old_style = old_style & !0b11;
904
905        wh::set_style(handle, old_style | style.bits());
906    }
907
908    /// Returns the number of items in the list view
909    pub fn len(&self) -> usize {
910        use winapi::um::commctrl::LVM_GETITEMCOUNT;
911        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
912        wh::send_message(handle, LVM_GETITEMCOUNT, 0, 0) as usize
913    }
914
915    /// Returns the number of columns in the list view
916    pub fn column_len(&self) -> usize {
917        use winapi::um::commctrl::LVM_GETCOLUMNWIDTH;
918
919        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
920
921        let mut count = 0;
922        while wh::send_message(handle, LVM_GETCOLUMNWIDTH, count, 0) != 0 {
923            count += 1;
924        }
925
926        count
927    }
928
929    /// Preallocate space for n number of item in the whole control.
930    /// For example calling this method with n=1000 while the list has 500 items will add space for 500 new items.
931    pub fn set_item_count(&self, n: u32) {
932        use winapi::um::commctrl::LVM_SETITEMCOUNT;
933
934        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
935        wh::send_message(handle, LVM_SETITEMCOUNT, n as _, 0);
936    }
937
938    /// Enable or disable the redrawing of the control when a new item is added.
939    /// When inserting a large number of items, it's better to disable redraw and reenable it after the items are inserted.
940    pub fn set_redraw(&self, enabled: bool) {
941        use winapi::um::winuser::WM_SETREDRAW;
942
943        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
944        wh::send_message(handle, WM_SETREDRAW, enabled as _, 0);
945    }
946
947    /// Sets the spacing between icons in list-view controls that have the ICON style.
948    /// `dx` specifies the distance, in pixels, to set between icons on the x-axis
949    /// `dy` specifies the distance, in pixels, to set between icons on the y-axis
950    pub fn set_icon_spacing(&self, dx: u16, dy: u16) {
951        use winapi::shared::minwindef::MAKELONG;
952        use winapi::um::commctrl::LVM_SETICONSPACING;
953
954        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
955        let spacing = MAKELONG(dx, dy);
956        wh::send_message(handle, LVM_SETICONSPACING, 0 as _, spacing as _);
957
958        self.invalidate();
959    }
960
961    // Common methods
962
963    /// Invalidate the whole drawing region.
964    pub fn invalidate(&self) {
965        use winapi::um::winuser::InvalidateRect;
966        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
967        unsafe {
968            InvalidateRect(handle, ptr::null(), 1);
969        }
970    }
971
972    /// Removes all item from the listview
973    pub fn clear(&self) {
974        use winapi::um::commctrl::LVM_DELETEALLITEMS;
975
976        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
977        wh::send_message(handle, LVM_DELETEALLITEMS, 0, 0);
978    }
979
980    /// Returns true if the control currently has the keyboard focus
981    pub fn focus(&self) -> bool {
982        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
983        wh::get_focus(handle)
984    }
985
986    /// Sets the keyboard focus on the button
987    pub fn set_focus(&self) {
988        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
989
990        wh::set_focus(handle);
991    }
992
993    /// Returns true if the control user can interact with the control, return false otherwise
994    pub fn enabled(&self) -> bool {
995        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
996        wh::get_window_enabled(handle)
997    }
998
999    /// Enable or disable the control
1000    pub fn set_enabled(&self, v: bool) {
1001        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1002        wh::set_window_enabled(handle, v)
1003    }
1004
1005    /// Returns true if the control is visible to the user. Will return true even if the
1006    /// control is outside of the parent client view (ex: at the position (10000, 10000))
1007    pub fn visible(&self) -> bool {
1008        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1009        wh::get_window_visibility(handle)
1010    }
1011
1012    /// Show or hide the control to the user
1013    pub fn set_visible(&self, v: bool) {
1014        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1015        wh::set_window_visibility(handle, v)
1016    }
1017
1018    /// Returns the size of the button in the parent window
1019    pub fn size(&self) -> (u32, u32) {
1020        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1021        wh::get_window_size(handle)
1022    }
1023
1024    /// Sets the size of the button in the parent window
1025    pub fn set_size(&self, x: u32, y: u32) {
1026        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1027        wh::set_window_size(handle, x, y, true)
1028    }
1029
1030    /// Returns the position of the button in the parent window
1031    pub fn position(&self) -> (i32, i32) {
1032        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1033        wh::get_window_position(handle)
1034    }
1035
1036    /// Sets the position of the button in the parent window
1037    pub fn set_position(&self, x: i32, y: i32) {
1038        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1039        wh::set_window_position(handle, x, y)
1040    }
1041
1042    /// Winapi class name used during control creation
1043    pub fn class_name(&self) -> &'static str {
1044        ::winapi::um::commctrl::WC_LISTVIEW
1045    }
1046
1047    /// Winapi base flags used during window creation
1048    pub fn flags(&self) -> u32 {
1049        WS_VISIBLE | WS_TABSTOP | LVS_SHOWSELALWAYS
1050    }
1051
1052    /// Winapi flags required by the control
1053    pub fn forced_flags(&self) -> u32 {
1054        use winapi::um::winuser::{WS_BORDER, WS_CHILD};
1055
1056        WS_CHILD | WS_BORDER | LVS_NOCOLUMNHEADER
1057    }
1058
1059    fn set_double_buffered(&mut self) {
1060        use crate::bind_raw_event_handler_inner;
1061        use winapi::um::wingdi::{CreateSolidBrush, RGB};
1062
1063        let double_buffer = ListViewDoubleBuffer {
1064            buffer: ptr::null_mut(),
1065            size: [0, 0],
1066            bg: unsafe { CreateSolidBrush(RGB(255, 255, 255)) },
1067        };
1068
1069        let rc_double_buffer = Rc::new(RefCell::new(double_buffer));
1070        let callback_double_buffer = rc_double_buffer.clone();
1071
1072        let handler = bind_raw_event_handler_inner(&self.handle, 0x020, move |hwnd, msg, _, _| {
1073            use winapi::um::wingdi::{
1074                BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject,
1075                SRCCOPY, SelectObject,
1076            };
1077            use winapi::um::winuser::{
1078                BeginPaint, EndPaint, FillRect, GetClientRect, RDW_ERASENOW, RDW_INVALIDATE,
1079                RDW_UPDATENOW, RedrawWindow, SendMessageW,
1080            };
1081            use winapi::um::winuser::{PAINTSTRUCT, WM_ERASEBKGND, WM_PAINT, WM_PRINTCLIENT};
1082
1083            match msg {
1084                WM_PAINT => unsafe {
1085                    let mut double_buffer = callback_double_buffer.borrow_mut();
1086
1087                    let mut r = mem::zeroed();
1088                    GetClientRect(hwnd, &mut r);
1089                    let client_width = r.right - r.left;
1090                    let client_height = r.bottom - r.top;
1091
1092                    let mut paint: PAINTSTRUCT = mem::zeroed();
1093                    BeginPaint(hwnd, &mut paint);
1094
1095                    if double_buffer.buffer.is_null()
1096                        || double_buffer.size != [client_width, client_height]
1097                    {
1098                        if !double_buffer.buffer.is_null() {
1099                            DeleteObject(double_buffer.buffer as _);
1100                        }
1101
1102                        double_buffer.size = [client_width, client_height];
1103                        double_buffer.buffer =
1104                            CreateCompatibleBitmap(paint.hdc, client_width, client_height);
1105                    }
1106
1107                    let backbuffer = double_buffer.buffer;
1108                    let backbuffer_dc = CreateCompatibleDC(paint.hdc);
1109
1110                    // Clear the backbuffer
1111                    let old = SelectObject(backbuffer_dc, backbuffer as _);
1112                    FillRect(backbuffer_dc, &r, double_buffer.bg as _);
1113
1114                    // Draw to the backbuffer and copy the result to the front buffer
1115                    SendMessageW(hwnd, WM_PRINTCLIENT, backbuffer_dc as _, 0);
1116                    BitBlt(
1117                        paint.hdc as _,
1118                        0,
1119                        0,
1120                        client_width,
1121                        client_height,
1122                        backbuffer_dc,
1123                        0,
1124                        0,
1125                        SRCCOPY,
1126                    );
1127
1128                    // Cleanup
1129                    SelectObject(backbuffer_dc, old);
1130                    DeleteDC(backbuffer_dc);
1131                    EndPaint(hwnd, &paint);
1132
1133                    // Redraw header
1134                    let header = SendMessageW(hwnd, LVM_GETHEADER, 0, 0);
1135                    if header != 0 {
1136                        let mut r = mem::zeroed();
1137                        GetClientRect(header as _, &mut r);
1138                        RedrawWindow(
1139                            header as _,
1140                            ptr::null_mut(),
1141                            ptr::null_mut(),
1142                            RDW_ERASENOW | RDW_UPDATENOW | RDW_INVALIDATE,
1143                        );
1144                    }
1145
1146                    Some(1)
1147                },
1148                WM_ERASEBKGND => Some(1),
1149                _ => None,
1150            }
1151        })
1152        .unwrap();
1153
1154        self.handler0 = Some(handler);
1155        self.double_buffer = Some(rc_double_buffer);
1156    }
1157}
1158
1159impl Drop for ListView {
1160    fn drop(&mut self) {
1161        use winapi::um::wingdi::DeleteObject;
1162
1163        if let Some(backbuffer) = self.double_buffer.take() {
1164            let double_buffer = backbuffer.borrow();
1165            unsafe {
1166                DeleteObject(double_buffer.buffer as _);
1167                DeleteObject(double_buffer.bg as _);
1168            }
1169        }
1170
1171        if let Some(h) = self.handler0.as_ref() {
1172            drop(unbind_raw_event_handler(h));
1173        }
1174
1175        self.handle.destroy();
1176    }
1177}
1178
1179pub struct ListViewBuilder {
1180    size: (i32, i32),
1181    position: (i32, i32),
1182    background_color: Option<[u8; 3]>,
1183    text_color: Option<[u8; 3]>,
1184    double_buffer: bool,
1185    focus: bool,
1186    flags: Option<ListViewFlags>,
1187    ex_flags: Option<ListViewExFlags>,
1188    ex_window_flags: u32,
1189    style: ListViewStyle,
1190    item_count: u32,
1191    parent: Option<ControlHandle>,
1192}
1193
1194impl ListViewBuilder {
1195    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ListViewBuilder {
1196        self.parent = Some(p.into());
1197        self
1198    }
1199
1200    pub fn flags(mut self, flags: ListViewFlags) -> ListViewBuilder {
1201        self.flags = Some(flags);
1202        self
1203    }
1204
1205    pub fn ex_flags(mut self, flags: ListViewExFlags) -> ListViewBuilder {
1206        self.ex_flags = Some(flags);
1207        self
1208    }
1209
1210    pub fn ex_window_flags(mut self, flags: u32) -> ListViewBuilder {
1211        self.ex_window_flags = flags;
1212        self
1213    }
1214
1215    pub fn size(mut self, size: (i32, i32)) -> ListViewBuilder {
1216        self.size = size;
1217        self
1218    }
1219
1220    pub fn position(mut self, position: (i32, i32)) -> ListViewBuilder {
1221        self.position = position;
1222        self
1223    }
1224
1225    pub fn double_buffer(mut self, buffer: bool) -> ListViewBuilder {
1226        self.double_buffer = buffer;
1227        self
1228    }
1229
1230    pub fn background_color(mut self, color: [u8; 3]) -> ListViewBuilder {
1231        self.background_color = Some(color);
1232        self
1233    }
1234
1235    pub fn text_color(mut self, color: [u8; 3]) -> ListViewBuilder {
1236        self.text_color = Some(color);
1237        self
1238    }
1239
1240    pub fn item_count(mut self, count: u32) -> ListViewBuilder {
1241        self.item_count = count;
1242        self
1243    }
1244
1245    pub fn list_style(mut self, style: ListViewStyle) -> ListViewBuilder {
1246        self.style = style;
1247        self
1248    }
1249
1250    pub fn focus(mut self, focus: bool) -> ListViewBuilder {
1251        self.focus = focus;
1252        self
1253    }
1254
1255    pub fn build(self, out: &mut ListView) -> Result<(), NwgError> {
1256        let mut flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
1257        flags |= self.style.bits();
1258
1259        let parent = match self.parent {
1260            Some(p) => Ok(p),
1261            None => Err(NwgError::no_parent("ListView")),
1262        }?;
1263
1264        *out = Default::default();
1265
1266        out.handle = ControlBase::build_hwnd()
1267            .class_name(out.class_name())
1268            .forced_flags(out.forced_flags())
1269            .flags(flags)
1270            .ex_flags(self.ex_window_flags)
1271            .size(self.size)
1272            .position(self.position)
1273            .text("")
1274            .parent(Some(parent))
1275            .build()?;
1276
1277        if self.double_buffer {
1278            out.set_double_buffered();
1279        }
1280
1281        if self.item_count > 0 {
1282            out.set_item_count(self.item_count);
1283        }
1284
1285        if self.focus {
1286            out.set_focus();
1287        }
1288
1289        if let Some(flags) = self.ex_flags {
1290            let flags = flags.bits();
1291            wh::send_message(
1292                out.handle.hwnd().unwrap(),
1293                LVM_SETEXTENDEDLISTVIEWSTYLE,
1294                flags as _,
1295                flags as _,
1296            );
1297        }
1298
1299        if let Some([r, g, b]) = self.background_color {
1300            out.set_background_color(r, g, b);
1301        }
1302
1303        if let Some([r, g, b]) = self.text_color {
1304            out.set_text_color(r, g, b);
1305        }
1306
1307        Ok(())
1308    }
1309}
1310
1311impl<'a> From<&'a str> for InsertListViewItem {
1312    fn from(i: &'a str) -> Self {
1313        InsertListViewItem {
1314            index: None,
1315            column_index: 0,
1316            text: Some(i.to_string()),
1317
1318            #[cfg(feature = "image-list")]
1319            image: None,
1320        }
1321    }
1322}
1323
1324impl From<String> for InsertListViewItem {
1325    fn from(i: String) -> Self {
1326        InsertListViewItem {
1327            index: None,
1328            column_index: 0,
1329            text: Some(i),
1330
1331            #[cfg(feature = "image-list")]
1332            image: None,
1333        }
1334    }
1335}
1336
1337impl<'a> From<&'a str> for InsertListViewColumn {
1338    fn from(i: &'a str) -> Self {
1339        InsertListViewColumn {
1340            index: None,
1341            fmt: None,
1342            width: Some(100),
1343            text: Some(i.to_string()),
1344        }
1345    }
1346}
1347
1348impl From<String> for InsertListViewColumn {
1349    fn from(i: String) -> Self {
1350        InsertListViewColumn {
1351            index: None,
1352            fmt: None,
1353            width: Some(100),
1354            text: Some(i),
1355        }
1356    }
1357}
1358
1359// Feature check
1360
1361#[cfg(feature = "image-list")]
1362fn check_image_mask(i: &InsertListViewItem) -> u32 {
1363    if i.image.is_some() { LVIF_IMAGE } else { 0 }
1364}
1365
1366#[cfg(feature = "image-list")]
1367fn check_image(i: &InsertListViewItem) -> i32 {
1368    i.image.unwrap_or(0)
1369}
1370
1371#[cfg(not(feature = "image-list"))]
1372fn check_image_mask(_i: &InsertListViewItem) -> u32 {
1373    0
1374}
1375
1376#[cfg(not(feature = "image-list"))]
1377fn check_image(_i: &InsertListViewItem) -> i32 {
1378    0
1379}
1380
1381#[cfg(feature = "image-list")]
1382fn build_list_view_image(
1383    row_index: usize,
1384    column_index: usize,
1385    state: u32,
1386    text_buffer: &[u16],
1387    image: i32,
1388) -> ListViewItem {
1389    use winapi::um::commctrl::LVIS_SELECTED;
1390
1391    ListViewItem {
1392        row_index: row_index as _,
1393        column_index: column_index as _,
1394        text: from_utf16(&text_buffer),
1395        selected: state & LVIS_SELECTED == LVIS_SELECTED,
1396        image,
1397    }
1398}
1399
1400#[cfg(not(feature = "image-list"))]
1401fn build_list_view_image(
1402    row_index: usize,
1403    column_index: usize,
1404    state: u32,
1405    text_buffer: &[u16],
1406    _image: i32,
1407) -> ListViewItem {
1408    use winapi::um::commctrl::LVIS_SELECTED;
1409
1410    ListViewItem {
1411        row_index: row_index as _,
1412        column_index: column_index as _,
1413        text: from_utf16(&text_buffer),
1414        selected: state & LVIS_SELECTED == LVIS_SELECTED,
1415    }
1416}