native_windows_gui/controls/
list_view.rs

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    /**
25        The list view flags:
26
27        * VISIBLE:  The list view is immediatly visible after creation
28        * 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.
29        * TAB_STOP: The control can be selected using tab navigation
30        * NO_HEADER: Remove the headers in Detailed view (ON by default, use `ListView::set_headers_enabled` to enable headers)
31        * SINGLE_SELECTION: Only one item can be selected
32        * ALWAYS_SHOW_SELECTION: Shows the selected list view item when the control is not in focus
33    */
34    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        // Remove the headers in Detailed view (ON by default due to backward compatibility)
44        // TODO: OFF by default in next major releases
45        const NO_HEADER = LVS_NOCOLUMNHEADER;
46    }
47}
48
49bitflags! {
50    /**
51        The list view extended flags (to use with ListViewBuilder::ex_flags):
52
53        * NONE:  Do not use any extended styles
54        * GRID:  The list view has a grid. Only if the list view is in report mode.
55        * BORDER_SELECT: Only highlight the border instead of the full item. COMMCTRL version 4.71 or later
56        * AUTO_COLUMN_SIZE: Automatically resize to column
57        * FULL_ROW_SELECT: When an item is selected, the item and all its subitems are highlighted. Only in detailed view 
58        * HEADER_DRAG_DROP: The user can drag and drop the headers to rearrage them 
59        * HEADER_IN_ALL_VIEW: Show the header in all view (not just report)
60    */
61    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    /**
75        The format flags for a list view column. Not all combination are valid.
76        The alignment of the leftmost column is always LEFT.
77
78        * LEFT: Text is left-aligned. 
79        * RIGHT: Text is right-aligned
80        * CENTER: Text is centered
81        * JUSTIFY_MASK: A bitmask used to select those bits of fmt that control field justification. 
82        * IMAGE: The items under to column displays an image from an image list
83        * IMAGE_RIGHT: The bitmap appears to the right of text
84        * IMAGE_COL: The header item contains an image in the image list.
85    */
86    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/**
98    The display style for the items in a list view
99*/
100#[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/**
132    Items in a list view can be associated with multiple image list.
133    This identify which image list to set/get using the ListView api.
134*/
135#[cfg(feature="image-list")]
136#[derive(Copy, Clone, Debug)]
137pub enum ListViewImageListType {
138    /// Normal sized icons
139    Normal,
140
141    /// Small icons
142    Small,
143
144    /// State icons
145    State,
146
147    /// Group header list (not yet implemented)
148    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)]
168/// Represents a column in a detailed list view
169pub struct InsertListViewColumn {
170    /// Index of the column
171    pub index: Option<i32>,
172
173    /// Format of the column
174    pub fmt: Option<ListViewColumnFlags>,
175
176    /// Width of the column in pixels
177    pub width: Option<i32>,
178
179    /// Text of the column to insert
180    pub text: Option<String>
181}
182
183/// The data of a list view column
184#[derive(Default, Clone, Debug)]
185pub struct ListViewColumn {
186    pub fmt: i32,
187    pub width: i32,
188    pub text: String,
189}
190
191/// Represents a column sort indicator in a detailed list view
192#[derive(Copy, Clone, Debug)]
193pub enum ListViewColumnSortArrow {
194    Up,
195    Down,
196}
197
198
199/// Represents a list view item parameters
200#[derive(Default, Clone, Debug)]
201pub struct InsertListViewItem {
202    /// Index of the item to be inserted
203    /// If None and `insert_item` is used, the item is added at the end of the list
204    pub index: Option<i32>,
205
206    /// Index of the column
207    pub column_index: i32,
208
209    /// Text of the item to insert
210    pub text: Option<String>,
211
212    /// Index of the image in the image list
213    /// Icons are only supported at column 0
214    #[cfg(feature="image-list")]
215    pub image: Option<i32>
216}
217
218/// The data of a list view item
219#[derive(Default, Clone, Debug)]
220pub struct ListViewItem {
221    pub row_index: i32,
222    pub column_index: i32,
223    pub text: String,
224
225    /// If the item is currently selected
226    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/**
239A list-view control is a window that displays a collection of items.
240List-view controls provide several ways to arrange and display items and are much more flexible than simple ListBox.
241
242Requires the `list-view` feature. 
243
244Builder parameters:
245  * `parent`:           **Required.** The list view parent container.
246  * `size`:             The list view size.
247  * `position`:         The list view position.
248  * `background_color`: The list view background color in RGB format
249  * `double_buffer`:    If the list view should be double buffered (defaults to true)
250  * `text_color`:       The list view text color in RGB format
251  * `flags`:            A combination of the ListViewFlags values.
252  * `ex_flags`:         A combination of the ListViewExFlags values. Not to be confused with `ex_window_flags` 
253  * `ex_window_flags`:  A combination of win32 window extended flags. This is the equivalent to `ex_flags` in the other controls
254  * `style`:            One of the value of `ListViewStyle`
255  * `item_count`:       Number of item to preallocate
256  * `list_style`:       The default style of the listview
257  * `focus`:            The control receive focus after being created
258
259**Control events:**
260  * `MousePress(_)`:   Generic mouse press events on the tree view
261  * `OnMouseMove`:     Generic mouse mouse event
262  * `OnMouseWheel`:    Generic mouse wheel event
263  * `OnKeyPress`:      Generic key press event
264  * `OnKeyRelease`:    Generic key release event
265  * `OnListViewClear`: When all the items in a list view are destroyed
266  * `OnListViewItemRemoved`: When an item is about to be removed from the list view
267  * `OnListViewItemInsert`: When a new item is inserted in the list view
268  * `OnListViewItemActivated`: When an item in the list view is activated by the user
269  * `OnListViewClick`: When the user has clicked the left mouse button within the control
270  * `OnListViewRightClick`: When the user has clicked the right mouse button within the control
271  * `OnListViewDoubleClick`: When the user has clicked the left mouse button within the control twice rapidly
272  * `OnListViewColumnClick`: When the user has clicked the left mouse button on ListView header column
273  * `OnListViewItemChanged`: When an item is selected/unselected in the listview
274  * `OnListViewFocus`: When the list view has received focus
275  * `OnListViewFocusLost`: When the list view has lost focus
276
277*/
278#[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    /// Sets the image list of the listview
305    /// A listview can accept different kinds of image list. See `ListViewImageListType`
306    #[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    /// Returns the current image list for the selected type. The returned image list will not be owned.
319    /// Can return `None` if there is no assocaited image list
320    #[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    /// Sets the text color of the list view
336    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    /// Returns the current text color
350    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    /// Sets the background color of the list view
365    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    /// Returns the background color of the list view
379    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    /// Returns the index of the selected column. Only available if Comclt32.dll version is >= 6.0.
394    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    /// Sets the selected column. Only available if Comclt32.dll version is >= 6.0.
402    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    /// Returns the number of selected items
410    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    /// Inserts a column in the report. Column are only used with the Detailed list view style.
418    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    /// Checks if there is a column at the selected index
457    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    /// Returns the information of a column.
468    /// Because there's no way to fetch the actual text length, it's up to you to set the maximum buffer size
469    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    /// Sets the information of a column. Does nothing if there is no column at the selected index
493    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    /// Deletes a column in a list view. Removing the column at index 0 is only available if ComCtl32.dll is version 6 or later.
529    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    /// Returns true if list view headers are visible
537    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    /// Enable or disable list view headers
545    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    /// Returns column sort indicator
558    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    /// Enable or disable column sort indicator. Draws a up-arrow / down-arrow.
578    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    /// Set the width of a column
602    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    /// Returns the width of a column
610    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    /// Select or unselect an item at `row_index`. Does nothing if the index is out of bounds.
618    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    /// Returns the index of the first selected item.
637    /// If there's more than one item selected, use `selected_items`
638    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    /// Returns the indices of every selected items.
654    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    /// Inserts a new item into the list view
670    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    /// Checks if the item at the selected row is visible
710    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    /// Returns `true` if an item exists at the selected index or `false` otherwise.
718    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    /// Returns data of an item in the list view. Returns `None` if there is no data at the selected index
731    /// Because there is no way to fetch the actual text size, `text_buffer_size` must be set manually
732    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    /// Updates the item at the selected position
758    /// Does nothing if there is no item at the selected position
759    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    /// Remove all items on the seleted row. Returns `true` if an item was removed or false otherwise.
795    /// To "remove" an item without deleting the row, use `update_item` and set the text to "".
796    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    /// Inserts multiple items into the control. Basically a loop over `insert_item`.
804    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    /// Insert multiple item at the selected row or at the end of the list if `None` was used.
811    /// This method overrides the `index` and the `column_index` of the items.
812    /// Useful when inserting strings into a single row. Ex: `list.insert_items_row(None, &["Hello", "World"]);`
813    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    /// Returns the current style of the list view
829    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    /// Sets the list view style of the control
835    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    /// Returns the number of items in the list view
845    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    /// Returns the number of columns in the list view
852    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    /// Preallocate space for n number of item in the whole control.
866    /// For example calling this method with n=1000 while the list has 500 items will add space for 500 new items.
867    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    /// Enable or disable the redrawing of the control when a new item is added.
875    /// When inserting a large number of items, it's better to disable redraw and reenable it after the items are inserted.
876    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    /// Sets the spacing between icons in list-view controls that have the ICON style.
884    /// `dx` specifies the distance, in pixels, to set between icons on the x-axis
885    /// `dy` specifies the distance, in pixels, to set between icons on the y-axis
886    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    // Common methods
898
899    /// Invalidate the whole drawing region.
900    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    /// Removes all item from the listview
907    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    /// Returns true if the control currently has the keyboard focus
915    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    /// Sets the keyboard focus on the button
921    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    /// Returns true if the control user can interact with the control, return false otherwise
927    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    /// Enable or disable the control
933    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    /// Returns true if the control is visible to the user. Will return true even if the 
939    /// control is outside of the parent client view (ex: at the position (10000, 10000))
940    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    /// Show or hide the control to the user
946    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    /// Returns the size of the button in the parent window
952    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    /// Sets the size of the button in the parent window
958    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    /// Returns the position of the button in the parent window
964    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    /// Sets the position of the button in the parent window
970    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    /// Winapi class name used during control creation
976    pub fn class_name(&self) -> &'static str {
977        ::winapi::um::commctrl::WC_LISTVIEW
978    }
979
980    /// Winapi base flags used during window creation
981    pub fn flags(&self) -> u32 {
982        WS_VISIBLE | WS_TABSTOP | LVS_SHOWSELALWAYS
983    }
984
985    /// Winapi flags required by the control
986    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                    // Clear the backbuffer
1035                    let old = SelectObject(backbuffer_dc, backbuffer as _);
1036                    FillRect(backbuffer_dc, &r, double_buffer.bg as _);
1037
1038                    // Draw to the backbuffer and copy the result to the front buffer
1039                    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                    // Cleanup
1050                    SelectObject(backbuffer_dc, old);
1051                    DeleteDC(backbuffer_dc);
1052                    EndPaint(hwnd, &paint);
1053
1054                    // Redraw header
1055                    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 // Feature check
1276
1277#[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}