]> git.saurik.com Git - wxWidgets.git/blob - src/msw/listctrl.cpp
Rotated text patch from Hans-Joachim Baader (with some corrections)
[wxWidgets.git] / src / msw / listctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: listctrl.cpp
3 // Purpose: wxListCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "listctrl.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/wx.h"
33 #endif
34
35 #ifdef __WIN95__
36
37 #include "wx/listctrl.h"
38 #include "wx/log.h"
39
40 #include "wx/msw/private.h"
41
42 #if defined(__GNUWIN32__) && !defined(wxUSE_NORLANDER_HEADERS)
43 #include "wx/msw/gnuwin32/extra.h"
44 #else
45 #include <commctrl.h>
46 #endif
47
48 #ifndef LVHT_ONITEM
49 #define LVHT_ONITEM \
50 (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON)
51 #endif
52
53 // ----------------------------------------------------------------------------
54 // private functions
55 // ----------------------------------------------------------------------------
56
57 static void wxConvertToMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& tvItem);
58 static void wxConvertFromMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& tvItem, HWND getFullInfo = 0);
59
60 // ----------------------------------------------------------------------------
61 // macros
62 // ----------------------------------------------------------------------------
63
64 #if !USE_SHARED_LIBRARY
65 IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
66 IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
67 #endif // USE_SHARED_LIBRARY
68
69 // ============================================================================
70 // implementation
71 // ============================================================================
72
73 // ----------------------------------------------------------------------------
74 // wxListCtrl construction
75 // ----------------------------------------------------------------------------
76
77 void wxListCtrl::Init()
78 {
79 m_imageListNormal = NULL;
80 m_imageListSmall = NULL;
81 m_imageListState = NULL;
82 m_baseStyle = 0;
83 m_colCount = 0;
84 m_textCtrl = NULL;
85 m_hasAnyAttr = FALSE;
86 }
87
88 bool wxListCtrl::Create(wxWindow *parent,
89 wxWindowID id,
90 const wxPoint& pos,
91 const wxSize& size,
92 long style,
93 const wxValidator& validator,
94 const wxString& name)
95 {
96 SetValidator(validator);
97 SetName(name);
98
99 int x = pos.x;
100 int y = pos.y;
101 int width = size.x;
102 int height = size.y;
103
104 m_windowStyle = style;
105
106 SetParent(parent);
107
108 if (width <= 0)
109 width = 100;
110 if (height <= 0)
111 height = 30;
112 if (x < 0)
113 x = 0;
114 if (y < 0)
115 y = 0;
116
117 m_windowId = (id == -1) ? NewControlId() : id;
118
119 DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP |
120 LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS;
121 if ( wxStyleHasBorder(m_windowStyle) )
122 wstyle |= WS_BORDER;
123 m_baseStyle = wstyle;
124
125 if ( !DoCreateControl(x, y, width, height) )
126 return FALSE;
127
128 if (parent)
129 parent->AddChild(this);
130
131 return TRUE;
132 }
133
134 bool wxListCtrl::DoCreateControl(int x, int y, int w, int h)
135 {
136 DWORD wstyle = m_baseStyle;
137
138 bool want3D;
139 WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
140
141 // Even with extended styles, need to combine with WS_BORDER
142 // for them to look right.
143 if ( want3D )
144 wstyle |= WS_BORDER;
145
146 long oldStyle = 0; // Dummy
147 wstyle |= ConvertToMSWStyle(oldStyle, m_windowStyle);
148
149 // Create the ListView control.
150 m_hWnd = (WXHWND)CreateWindowEx(exStyle,
151 WC_LISTVIEW,
152 wxT(""),
153 wstyle,
154 x, y, w, h,
155 GetWinHwnd(GetParent()),
156 (HMENU)m_windowId,
157 wxGetInstance(),
158 NULL);
159
160 if ( !m_hWnd )
161 {
162 wxLogError(wxT("Can't create list control window."));
163
164 return FALSE;
165 }
166
167 // for comctl32.dll v 4.70+ we want to have this attribute because it's
168 // prettier (and also because wxGTK does it like this)
169 #ifdef ListView_SetExtendedListViewStyle
170 if ( wstyle & LVS_REPORT )
171 {
172 ListView_SetExtendedListViewStyle(GetHwnd(),
173 LVS_EX_FULLROWSELECT);
174 }
175 #endif // ListView_SetExtendedListViewStyle
176
177 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
178 SetForegroundColour(GetParent()->GetForegroundColour());
179
180 SubclassWin(m_hWnd);
181
182 return TRUE;
183 }
184
185 void wxListCtrl::UpdateStyle()
186 {
187 if ( GetHWND() )
188 {
189 // The new window view style
190 long dummy;
191 DWORD dwStyleNew = ConvertToMSWStyle(dummy, m_windowStyle);
192 dwStyleNew |= m_baseStyle;
193
194 // Get the current window style.
195 DWORD dwStyleOld = ::GetWindowLong(GetHwnd(), GWL_STYLE);
196
197 // Only set the window style if the view bits have changed.
198 if ( dwStyleOld != dwStyleNew )
199 {
200 ::SetWindowLong(GetHwnd(), GWL_STYLE, dwStyleNew);
201 }
202 }
203 }
204
205 wxListCtrl::~wxListCtrl()
206 {
207 if ( m_hasAnyAttr )
208 {
209 for ( wxNode *node = m_attrs.Next(); node; node = m_attrs.Next() )
210 {
211 delete (wxListItemAttr *)node->Data();
212 }
213 }
214
215 if ( m_textCtrl )
216 {
217 m_textCtrl->UnsubclassWin();
218 m_textCtrl->SetHWND(0);
219 delete m_textCtrl;
220 m_textCtrl = NULL;
221 }
222 }
223
224 // ----------------------------------------------------------------------------
225 // set/get/change style
226 // ----------------------------------------------------------------------------
227
228 // Add or remove a single window style
229 void wxListCtrl::SetSingleStyle(long style, bool add)
230 {
231 long flag = GetWindowStyleFlag();
232
233 // Get rid of conflicting styles
234 if ( add )
235 {
236 if ( style & wxLC_MASK_TYPE)
237 flag = flag & ~wxLC_MASK_TYPE;
238 if ( style & wxLC_MASK_ALIGN )
239 flag = flag & ~wxLC_MASK_ALIGN;
240 if ( style & wxLC_MASK_SORT )
241 flag = flag & ~wxLC_MASK_SORT;
242 }
243
244 if ( flag & style )
245 {
246 if ( !add )
247 flag -= style;
248 }
249 else
250 {
251 if ( add )
252 {
253 flag |= style;
254 }
255 }
256
257 m_windowStyle = flag;
258
259 UpdateStyle();
260 }
261
262 // Set the whole window style
263 void wxListCtrl::SetWindowStyleFlag(long flag)
264 {
265 m_windowStyle = flag;
266
267 UpdateStyle();
268 }
269
270 // Can be just a single style, or a bitlist
271 long wxListCtrl::ConvertToMSWStyle(long& oldStyle, long style) const
272 {
273 long wstyle = 0;
274 if ( style & wxLC_ICON )
275 {
276 if ( (oldStyle & LVS_TYPEMASK) == LVS_SMALLICON )
277 oldStyle -= LVS_SMALLICON;
278 if ( (oldStyle & LVS_TYPEMASK) == LVS_REPORT )
279 oldStyle -= LVS_REPORT;
280 if ( (oldStyle & LVS_TYPEMASK) == LVS_LIST )
281 oldStyle -= LVS_LIST;
282 wstyle |= LVS_ICON;
283 }
284
285 if ( style & wxLC_SMALL_ICON )
286 {
287 if ( (oldStyle & LVS_TYPEMASK) == LVS_ICON )
288 oldStyle -= LVS_ICON;
289 if ( (oldStyle & LVS_TYPEMASK) == LVS_REPORT )
290 oldStyle -= LVS_REPORT;
291 if ( (oldStyle & LVS_TYPEMASK) == LVS_LIST )
292 oldStyle -= LVS_LIST;
293 wstyle |= LVS_SMALLICON;
294 }
295
296 if ( style & wxLC_LIST )
297 {
298 if ( (oldStyle & LVS_TYPEMASK) == LVS_ICON )
299 oldStyle -= LVS_ICON;
300 if ( (oldStyle & LVS_TYPEMASK) == LVS_REPORT )
301 oldStyle -= LVS_REPORT;
302 if ( (oldStyle & LVS_TYPEMASK) == LVS_SMALLICON )
303 oldStyle -= LVS_SMALLICON;
304 wstyle |= LVS_LIST;
305 }
306
307 if ( style & wxLC_REPORT )
308 {
309 if ( (oldStyle & LVS_TYPEMASK) == LVS_ICON )
310 oldStyle -= LVS_ICON;
311 if ( (oldStyle & LVS_TYPEMASK) == LVS_LIST )
312 oldStyle -= LVS_LIST;
313 if ( (oldStyle & LVS_TYPEMASK) == LVS_SMALLICON )
314 oldStyle -= LVS_SMALLICON;
315
316 wstyle |= LVS_REPORT;
317 }
318
319 if ( style & wxLC_ALIGN_LEFT )
320 {
321 if ( oldStyle & LVS_ALIGNTOP )
322 oldStyle -= LVS_ALIGNTOP;
323 wstyle |= LVS_ALIGNLEFT;
324 }
325
326 if ( style & wxLC_ALIGN_TOP )
327 {
328 if ( oldStyle & LVS_ALIGNLEFT )
329 oldStyle -= LVS_ALIGNLEFT;
330 wstyle |= LVS_ALIGNTOP;
331 }
332
333 if ( style & wxLC_AUTOARRANGE )
334 wstyle |= LVS_AUTOARRANGE;
335
336 // Apparently, no such style (documentation wrong?)
337 /*
338 if ( style & wxLC_BUTTON )
339 wstyle |= LVS_BUTTON;
340 */
341
342 if ( style & wxLC_NO_SORT_HEADER )
343 wstyle |= LVS_NOSORTHEADER;
344
345 if ( style & wxLC_NO_HEADER )
346 wstyle |= LVS_NOCOLUMNHEADER;
347
348 if ( style & wxLC_EDIT_LABELS )
349 wstyle |= LVS_EDITLABELS;
350
351 if ( style & wxLC_SINGLE_SEL )
352 wstyle |= LVS_SINGLESEL;
353
354 if ( style & wxLC_SORT_ASCENDING )
355 {
356 if ( oldStyle & LVS_SORTDESCENDING )
357 oldStyle -= LVS_SORTDESCENDING;
358 wstyle |= LVS_SORTASCENDING;
359 }
360
361 if ( style & wxLC_SORT_DESCENDING )
362 {
363 if ( oldStyle & LVS_SORTASCENDING )
364 oldStyle -= LVS_SORTASCENDING;
365 wstyle |= LVS_SORTDESCENDING;
366 }
367
368 return wstyle;
369 }
370
371 // ----------------------------------------------------------------------------
372 // accessors
373 // ----------------------------------------------------------------------------
374
375 // Sets the background colour (GetBackgroundColour already implicit in
376 // wxWindow class)
377 bool wxListCtrl::SetBackgroundColour(const wxColour& col)
378 {
379 if ( !wxWindow::SetBackgroundColour(col) )
380 return FALSE;
381
382 ListView_SetBkColor(GetHwnd(), PALETTERGB(col.Red(), col.Green(), col.Blue()));
383
384 return TRUE;
385 }
386
387 // Gets information about this column
388 bool wxListCtrl::GetColumn(int col, wxListItem& item) const
389 {
390 LV_COLUMN lvCol;
391 lvCol.mask = 0;
392 lvCol.fmt = 0;
393 lvCol.pszText = NULL;
394
395 if ( item.m_mask & wxLIST_MASK_TEXT )
396 {
397 lvCol.mask |= LVCF_TEXT;
398 lvCol.pszText = new wxChar[513];
399 lvCol.cchTextMax = 512;
400 }
401
402 bool success = (ListView_GetColumn(GetHwnd(), col, & lvCol) != 0);
403
404 // item.m_subItem = lvCol.iSubItem;
405 item.m_width = lvCol.cx;
406
407 if ( (item.m_mask & wxLIST_MASK_TEXT) && lvCol.pszText )
408 {
409 item.m_text = lvCol.pszText;
410 delete[] lvCol.pszText;
411 }
412
413 if ( item.m_mask & wxLIST_MASK_FORMAT )
414 {
415 if (lvCol.fmt == LVCFMT_LEFT)
416 item.m_format = wxLIST_FORMAT_LEFT;
417 else if (lvCol.fmt == LVCFMT_RIGHT)
418 item.m_format = wxLIST_FORMAT_RIGHT;
419 else if (lvCol.fmt == LVCFMT_CENTER)
420 item.m_format = wxLIST_FORMAT_CENTRE;
421 }
422
423 return success;
424 }
425
426 // Sets information about this column
427 bool wxListCtrl::SetColumn(int col, wxListItem& item)
428 {
429 LV_COLUMN lvCol;
430 lvCol.mask = 0;
431 lvCol.fmt = 0;
432 lvCol.pszText = NULL;
433
434 if ( item.m_mask & wxLIST_MASK_TEXT )
435 {
436 lvCol.mask |= LVCF_TEXT;
437 lvCol.pszText = WXSTRINGCAST item.m_text;
438 lvCol.cchTextMax = 0; // Ignored
439 }
440 if ( item.m_mask & wxLIST_MASK_FORMAT )
441 {
442 lvCol.mask |= LVCF_FMT;
443
444 if ( item.m_format == wxLIST_FORMAT_LEFT )
445 lvCol.fmt = LVCFMT_LEFT;
446 if ( item.m_format == wxLIST_FORMAT_RIGHT )
447 lvCol.fmt = LVCFMT_RIGHT;
448 if ( item.m_format == wxLIST_FORMAT_CENTRE )
449 lvCol.fmt = LVCFMT_CENTER;
450 }
451
452 if ( item.m_mask & wxLIST_MASK_WIDTH )
453 {
454 lvCol.mask |= LVCF_WIDTH;
455 lvCol.cx = item.m_width;
456
457 if ( lvCol.cx == wxLIST_AUTOSIZE)
458 lvCol.cx = LVSCW_AUTOSIZE;
459 else if ( lvCol.cx == wxLIST_AUTOSIZE_USEHEADER)
460 lvCol.cx = LVSCW_AUTOSIZE_USEHEADER;
461 }
462 lvCol.mask |= LVCF_SUBITEM;
463 lvCol.iSubItem = col;
464 return (ListView_SetColumn(GetHwnd(), col, & lvCol) != 0);
465 }
466
467 // Gets the column width
468 int wxListCtrl::GetColumnWidth(int col) const
469 {
470 return ListView_GetColumnWidth(GetHwnd(), col);
471 }
472
473 // Sets the column width
474 bool wxListCtrl::SetColumnWidth(int col, int width)
475 {
476 int col2 = col;
477 if ( m_windowStyle & wxLC_LIST )
478 col2 = -1;
479
480 int width2 = width;
481 if ( width2 == wxLIST_AUTOSIZE)
482 width2 = LVSCW_AUTOSIZE;
483 else if ( width2 == wxLIST_AUTOSIZE_USEHEADER)
484 width2 = LVSCW_AUTOSIZE_USEHEADER;
485
486 return (ListView_SetColumnWidth(GetHwnd(), col2, width2) != 0);
487 }
488
489 // Gets the number of items that can fit vertically in the
490 // visible area of the list control (list or report view)
491 // or the total number of items in the list control (icon
492 // or small icon view)
493 int wxListCtrl::GetCountPerPage(void) const
494 {
495 return ListView_GetCountPerPage(GetHwnd());
496 }
497
498 // Gets the edit control for editing labels.
499 wxTextCtrl* wxListCtrl::GetEditControl(void) const
500 {
501 return m_textCtrl;
502 }
503
504 // Gets information about the item
505 bool wxListCtrl::GetItem(wxListItem& info) const
506 {
507 LV_ITEM lvItem;
508 wxZeroMemory(lvItem);
509
510 lvItem.iItem = info.m_itemId;
511 lvItem.iSubItem = info.m_col;
512
513 if ( info.m_mask & wxLIST_MASK_TEXT )
514 {
515 lvItem.mask |= LVIF_TEXT;
516 lvItem.pszText = new wxChar[513];
517 lvItem.cchTextMax = 512;
518 }
519 else
520 {
521 lvItem.pszText = NULL;
522 }
523
524 if (info.m_mask & wxLIST_MASK_DATA)
525 lvItem.mask |= LVIF_PARAM;
526
527 if ( info.m_mask & wxLIST_MASK_STATE )
528 {
529 lvItem.mask |= LVIF_STATE;
530 // the other bits are hardly interesting anyhow
531 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
532 }
533
534 bool success = ListView_GetItem((HWND)GetHWND(), &lvItem) != 0;
535 if ( !success )
536 {
537 wxLogError(_("Couldn't retrieve information about list control item %d."),
538 lvItem.iItem);
539 }
540 else
541 {
542 wxConvertFromMSWListItem(this, info, lvItem);
543 }
544
545 if (lvItem.pszText)
546 delete[] lvItem.pszText;
547
548 return success;
549 }
550
551 // Sets information about the item
552 bool wxListCtrl::SetItem(wxListItem& info)
553 {
554 LV_ITEM item;
555 wxConvertToMSWListItem(this, info, item);
556
557 // check whether it has any custom attributes
558 if ( info.HasAttributes() )
559 {
560 m_attrs.Put(item.iItem, (wxObject *)new wxListItemAttr(*info.GetAttributes()));
561
562 m_hasAnyAttr = TRUE;
563 }
564
565 item.cchTextMax = 0;
566 bool ok = ListView_SetItem(GetHwnd(), &item) != 0;
567 if ( ok && (info.m_mask & wxLIST_MASK_IMAGE) )
568 {
569 // make the change visible
570 ListView_Update(GetHwnd(), item.iItem);
571 }
572
573 return ok;
574 }
575
576 long wxListCtrl::SetItem(long index, int col, const wxString& label, int imageId)
577 {
578 wxListItem info;
579 info.m_text = label;
580 info.m_mask = wxLIST_MASK_TEXT;
581 info.m_itemId = index;
582 info.m_col = col;
583 if ( imageId > -1 )
584 {
585 info.m_image = imageId;
586 info.m_mask |= wxLIST_MASK_IMAGE;
587 }
588 return SetItem(info);
589 }
590
591
592 // Gets the item state
593 int wxListCtrl::GetItemState(long item, long stateMask) const
594 {
595 wxListItem info;
596
597 info.m_mask = wxLIST_MASK_STATE;
598 info.m_stateMask = stateMask;
599 info.m_itemId = item;
600
601 if (!GetItem(info))
602 return 0;
603
604 return info.m_state;
605 }
606
607 // Sets the item state
608 bool wxListCtrl::SetItemState(long item, long state, long stateMask)
609 {
610 wxListItem info;
611
612 info.m_mask = wxLIST_MASK_STATE;
613 info.m_state = state;
614 info.m_stateMask = stateMask;
615 info.m_itemId = item;
616
617 return SetItem(info);
618 }
619
620 // Sets the item image
621 bool wxListCtrl::SetItemImage(long item, int image, int selImage)
622 {
623 wxListItem info;
624
625 info.m_mask = wxLIST_MASK_IMAGE;
626 info.m_image = image;
627 info.m_itemId = item;
628
629 return SetItem(info);
630 }
631
632 // Gets the item text
633 wxString wxListCtrl::GetItemText(long item) const
634 {
635 wxListItem info;
636
637 info.m_mask = wxLIST_MASK_TEXT;
638 info.m_itemId = item;
639
640 if (!GetItem(info))
641 return wxString("");
642 return info.m_text;
643 }
644
645 // Sets the item text
646 void wxListCtrl::SetItemText(long item, const wxString& str)
647 {
648 wxListItem info;
649
650 info.m_mask = wxLIST_MASK_TEXT;
651 info.m_itemId = item;
652 info.m_text = str;
653
654 SetItem(info);
655 }
656
657 // Gets the item data
658 long wxListCtrl::GetItemData(long item) const
659 {
660 wxListItem info;
661
662 info.m_mask = wxLIST_MASK_DATA;
663 info.m_itemId = item;
664
665 if (!GetItem(info))
666 return 0;
667 return info.m_data;
668 }
669
670 // Sets the item data
671 bool wxListCtrl::SetItemData(long item, long data)
672 {
673 wxListItem info;
674
675 info.m_mask = wxLIST_MASK_DATA;
676 info.m_itemId = item;
677 info.m_data = data;
678
679 return SetItem(info);
680 }
681
682 // Gets the item rectangle
683 bool wxListCtrl::GetItemRect(long item, wxRect& rect, int code) const
684 {
685 RECT rect2;
686
687 int code2 = LVIR_BOUNDS;
688 if ( code == wxLIST_RECT_BOUNDS )
689 code2 = LVIR_BOUNDS;
690 else if ( code == wxLIST_RECT_ICON )
691 code2 = LVIR_ICON;
692 else if ( code == wxLIST_RECT_LABEL )
693 code2 = LVIR_LABEL;
694
695 #ifdef __WXWINE__
696 bool success = (ListView_GetItemRect(GetHwnd(), (int) item, &rect2 ) != 0);
697 #else
698 bool success = (ListView_GetItemRect(GetHwnd(), (int) item, &rect2, code2) != 0);
699 #endif
700
701 rect.x = rect2.left;
702 rect.y = rect2.top;
703 rect.width = rect2.right - rect2.left;
704 rect.height = rect2.bottom - rect2.left;
705 return success;
706 }
707
708 // Gets the item position
709 bool wxListCtrl::GetItemPosition(long item, wxPoint& pos) const
710 {
711 POINT pt;
712
713 bool success = (ListView_GetItemPosition(GetHwnd(), (int) item, &pt) != 0);
714
715 pos.x = pt.x; pos.y = pt.y;
716 return success;
717 }
718
719 // Sets the item position.
720 bool wxListCtrl::SetItemPosition(long item, const wxPoint& pos)
721 {
722 return (ListView_SetItemPosition(GetHwnd(), (int) item, pos.x, pos.y) != 0);
723 }
724
725 // Gets the number of items in the list control
726 int wxListCtrl::GetItemCount(void) const
727 {
728 return ListView_GetItemCount(GetHwnd());
729 }
730
731 // Retrieves the spacing between icons in pixels.
732 // If small is TRUE, gets the spacing for the small icon
733 // view, otherwise the large icon view.
734 int wxListCtrl::GetItemSpacing(bool isSmall) const
735 {
736 return ListView_GetItemSpacing(GetHwnd(), (BOOL) isSmall);
737 }
738
739 // Gets the number of selected items in the list control
740 int wxListCtrl::GetSelectedItemCount(void) const
741 {
742 return ListView_GetSelectedCount(GetHwnd());
743 }
744
745 // Gets the text colour of the listview
746 wxColour wxListCtrl::GetTextColour(void) const
747 {
748 COLORREF ref = ListView_GetTextColor(GetHwnd());
749 wxColour col(GetRValue(ref), GetGValue(ref), GetBValue(ref));
750 return col;
751 }
752
753 // Sets the text colour of the listview
754 void wxListCtrl::SetTextColour(const wxColour& col)
755 {
756 ListView_SetTextColor(GetHwnd(), PALETTERGB(col.Red(), col.Green(), col.Blue()));
757 }
758
759 // Gets the index of the topmost visible item when in
760 // list or report view
761 long wxListCtrl::GetTopItem(void) const
762 {
763 return (long) ListView_GetTopIndex(GetHwnd());
764 }
765
766 // Searches for an item, starting from 'item'.
767 // 'geometry' is one of
768 // wxLIST_NEXT_ABOVE/ALL/BELOW/LEFT/RIGHT.
769 // 'state' is a state bit flag, one or more of
770 // wxLIST_STATE_DROPHILITED/FOCUSED/SELECTED/CUT.
771 // item can be -1 to find the first item that matches the
772 // specified flags.
773 // Returns the item or -1 if unsuccessful.
774 long wxListCtrl::GetNextItem(long item, int geom, int state) const
775 {
776 long flags = 0;
777
778 if ( geom == wxLIST_NEXT_ABOVE )
779 flags |= LVNI_ABOVE;
780 if ( geom == wxLIST_NEXT_ALL )
781 flags |= LVNI_ALL;
782 if ( geom == wxLIST_NEXT_BELOW )
783 flags |= LVNI_BELOW;
784 if ( geom == wxLIST_NEXT_LEFT )
785 flags |= LVNI_TOLEFT;
786 if ( geom == wxLIST_NEXT_RIGHT )
787 flags |= LVNI_TORIGHT;
788
789 if ( state & wxLIST_STATE_CUT )
790 flags |= LVNI_CUT;
791 if ( state & wxLIST_STATE_DROPHILITED )
792 flags |= LVNI_DROPHILITED;
793 if ( state & wxLIST_STATE_FOCUSED )
794 flags |= LVNI_FOCUSED;
795 if ( state & wxLIST_STATE_SELECTED )
796 flags |= LVNI_SELECTED;
797
798 return (long) ListView_GetNextItem(GetHwnd(), item, flags);
799 }
800
801
802 wxImageList *wxListCtrl::GetImageList(int which) const
803 {
804 if ( which == wxIMAGE_LIST_NORMAL )
805 {
806 return m_imageListNormal;
807 }
808 else if ( which == wxIMAGE_LIST_SMALL )
809 {
810 return m_imageListSmall;
811 }
812 else if ( which == wxIMAGE_LIST_STATE )
813 {
814 return m_imageListState;
815 }
816 return NULL;
817 }
818
819 void wxListCtrl::SetImageList(wxImageList *imageList, int which)
820 {
821 int flags = 0;
822 if ( which == wxIMAGE_LIST_NORMAL )
823 {
824 flags = LVSIL_NORMAL;
825 m_imageListNormal = imageList;
826 }
827 else if ( which == wxIMAGE_LIST_SMALL )
828 {
829 flags = LVSIL_SMALL;
830 m_imageListSmall = imageList;
831 }
832 else if ( which == wxIMAGE_LIST_STATE )
833 {
834 flags = LVSIL_STATE;
835 m_imageListState = imageList;
836 }
837 ListView_SetImageList(GetHwnd(), (HIMAGELIST) imageList ? imageList->GetHIMAGELIST() : 0, flags);
838 }
839
840 // ----------------------------------------------------------------------------
841 // Operations
842 // ----------------------------------------------------------------------------
843
844 // Arranges the items
845 bool wxListCtrl::Arrange(int flag)
846 {
847 UINT code = 0;
848 if ( flag == wxLIST_ALIGN_LEFT )
849 code = LVA_ALIGNLEFT;
850 else if ( flag == wxLIST_ALIGN_TOP )
851 code = LVA_ALIGNTOP;
852 else if ( flag == wxLIST_ALIGN_DEFAULT )
853 code = LVA_DEFAULT;
854 else if ( flag == wxLIST_ALIGN_SNAP_TO_GRID )
855 code = LVA_SNAPTOGRID;
856
857 return (ListView_Arrange(GetHwnd(), code) != 0);
858 }
859
860 // Deletes an item
861 bool wxListCtrl::DeleteItem(long item)
862 {
863 return (ListView_DeleteItem(GetHwnd(), (int) item) != 0);
864 }
865
866 // Deletes all items
867 bool wxListCtrl::DeleteAllItems()
868 {
869 return (ListView_DeleteAllItems(GetHwnd()) != 0);
870 }
871
872 // Deletes all items
873 bool wxListCtrl::DeleteAllColumns()
874 {
875 while ( m_colCount > 0 )
876 {
877 if ( ListView_DeleteColumn(GetHwnd(), 0) == 0 )
878 {
879 wxLogLastError("ListView_DeleteColumn");
880
881 return FALSE;
882 }
883
884 m_colCount--;
885 }
886
887 wxASSERT_MSG( m_colCount == 0, wxT("no columns should be left") );
888
889 return TRUE;
890 }
891
892 // Deletes a column
893 bool wxListCtrl::DeleteColumn(int col)
894 {
895 bool success = (ListView_DeleteColumn(GetHwnd(), col) != 0);
896
897 if ( success && (m_colCount > 0) )
898 m_colCount --;
899 return success;
900 }
901
902 // Clears items, and columns if there are any.
903 void wxListCtrl::ClearAll()
904 {
905 DeleteAllItems();
906 if ( m_colCount > 0 )
907 DeleteAllColumns();
908 }
909
910 wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass)
911 {
912 wxASSERT( (textControlClass->IsKindOf(CLASSINFO(wxTextCtrl))) );
913
914 HWND hWnd = (HWND) ListView_EditLabel(GetHwnd(), item);
915
916 if (m_textCtrl)
917 {
918 m_textCtrl->UnsubclassWin();
919 m_textCtrl->SetHWND(0);
920 delete m_textCtrl;
921 m_textCtrl = NULL;
922 }
923
924 m_textCtrl = (wxTextCtrl*) textControlClass->CreateObject();
925 m_textCtrl->SetHWND((WXHWND) hWnd);
926 m_textCtrl->SubclassWin((WXHWND) hWnd);
927
928 return m_textCtrl;
929 }
930
931 // End label editing, optionally cancelling the edit
932 bool wxListCtrl::EndEditLabel(bool cancel)
933 {
934 wxFAIL;
935
936 /* I don't know how to implement this: there's no such macro as ListView_EndEditLabelNow.
937 * ???
938 bool success = (ListView_EndEditLabelNow(GetHwnd(), cancel) != 0);
939
940 if (m_textCtrl)
941 {
942 m_textCtrl->UnsubclassWin();
943 m_textCtrl->SetHWND(0);
944 delete m_textCtrl;
945 m_textCtrl = NULL;
946 }
947 return success;
948 */
949 return FALSE;
950 }
951
952
953 // Ensures this item is visible
954 bool wxListCtrl::EnsureVisible(long item)
955 {
956 return (ListView_EnsureVisible(GetHwnd(), (int) item, FALSE) != 0);
957 }
958
959 // Find an item whose label matches this string, starting from the item after 'start'
960 // or the beginning if 'start' is -1.
961 long wxListCtrl::FindItem(long start, const wxString& str, bool partial)
962 {
963 LV_FINDINFO findInfo;
964
965 findInfo.flags = LVFI_STRING;
966 if ( partial )
967 findInfo.flags |= LVFI_STRING;
968 findInfo.psz = WXSTRINGCAST str;
969
970 return ListView_FindItem(GetHwnd(), (int) start, & findInfo);
971 }
972
973 // Find an item whose data matches this data, starting from the item after 'start'
974 // or the beginning if 'start' is -1.
975 long wxListCtrl::FindItem(long start, long data)
976 {
977 LV_FINDINFO findInfo;
978
979 findInfo.flags = LVFI_PARAM;
980 findInfo.lParam = data;
981
982 return ListView_FindItem(GetHwnd(), (int) start, & findInfo);
983 }
984
985 // Find an item nearest this position in the specified direction, starting from
986 // the item after 'start' or the beginning if 'start' is -1.
987 long wxListCtrl::FindItem(long start, const wxPoint& pt, int direction)
988 {
989 LV_FINDINFO findInfo;
990
991 findInfo.flags = LVFI_NEARESTXY;
992 findInfo.pt.x = pt.x;
993 findInfo.pt.y = pt.y;
994 findInfo.vkDirection = VK_RIGHT;
995
996 if ( direction == wxLIST_FIND_UP )
997 findInfo.vkDirection = VK_UP;
998 else if ( direction == wxLIST_FIND_DOWN )
999 findInfo.vkDirection = VK_DOWN;
1000 else if ( direction == wxLIST_FIND_LEFT )
1001 findInfo.vkDirection = VK_LEFT;
1002 else if ( direction == wxLIST_FIND_RIGHT )
1003 findInfo.vkDirection = VK_RIGHT;
1004
1005 return ListView_FindItem(GetHwnd(), (int) start, & findInfo);
1006 }
1007
1008 // Determines which item (if any) is at the specified point,
1009 // giving details in 'flags' (see wxLIST_HITTEST_... flags above)
1010 long wxListCtrl::HitTest(const wxPoint& point, int& flags)
1011 {
1012 LV_HITTESTINFO hitTestInfo;
1013 hitTestInfo.pt.x = (int) point.x;
1014 hitTestInfo.pt.y = (int) point.y;
1015
1016 ListView_HitTest(GetHwnd(), & hitTestInfo);
1017
1018 flags = 0;
1019 if ( hitTestInfo.flags & LVHT_ABOVE )
1020 flags |= wxLIST_HITTEST_ABOVE;
1021 if ( hitTestInfo.flags & LVHT_BELOW )
1022 flags |= wxLIST_HITTEST_BELOW;
1023 if ( hitTestInfo.flags & LVHT_NOWHERE )
1024 flags |= wxLIST_HITTEST_NOWHERE;
1025 if ( hitTestInfo.flags & LVHT_ONITEMICON )
1026 flags |= wxLIST_HITTEST_ONITEMICON;
1027 if ( hitTestInfo.flags & LVHT_ONITEMLABEL )
1028 flags |= wxLIST_HITTEST_ONITEMLABEL;
1029 if ( hitTestInfo.flags & LVHT_ONITEMSTATEICON )
1030 flags |= wxLIST_HITTEST_ONITEMSTATEICON;
1031 if ( hitTestInfo.flags & LVHT_TOLEFT )
1032 flags |= wxLIST_HITTEST_TOLEFT;
1033 if ( hitTestInfo.flags & LVHT_TORIGHT )
1034 flags |= wxLIST_HITTEST_TORIGHT;
1035
1036 return (long) hitTestInfo.iItem;
1037 }
1038
1039 // Inserts an item, returning the index of the new item if successful,
1040 // -1 otherwise.
1041 long wxListCtrl::InsertItem(wxListItem& info)
1042 {
1043 LV_ITEM item;
1044 wxConvertToMSWListItem(this, info, item);
1045
1046 // check whether it has any custom attributes
1047 if ( info.HasAttributes() )
1048 {
1049 m_attrs.Put(item.iItem, (wxObject *)new wxListItemAttr(*info.GetAttributes()));
1050
1051 m_hasAnyAttr = TRUE;
1052 }
1053
1054 return (long) ListView_InsertItem(GetHwnd(), & item);
1055 }
1056
1057 long wxListCtrl::InsertItem(long index, const wxString& label)
1058 {
1059 wxListItem info;
1060 info.m_text = label;
1061 info.m_mask = wxLIST_MASK_TEXT;
1062 info.m_itemId = index;
1063 return InsertItem(info);
1064 }
1065
1066 // Inserts an image item
1067 long wxListCtrl::InsertItem(long index, int imageIndex)
1068 {
1069 wxListItem info;
1070 info.m_image = imageIndex;
1071 info.m_mask = wxLIST_MASK_IMAGE;
1072 info.m_itemId = index;
1073 return InsertItem(info);
1074 }
1075
1076 // Inserts an image/string item
1077 long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex)
1078 {
1079 wxListItem info;
1080 info.m_image = imageIndex;
1081 info.m_text = label;
1082 info.m_mask = wxLIST_MASK_IMAGE | wxLIST_MASK_TEXT;
1083 info.m_itemId = index;
1084 return InsertItem(info);
1085 }
1086
1087 // For list view mode (only), inserts a column.
1088 long wxListCtrl::InsertColumn(long col, wxListItem& item)
1089 {
1090 LV_COLUMN lvCol;
1091 lvCol.mask = 0;
1092 lvCol.fmt = 0;
1093 lvCol.pszText = NULL;
1094
1095 if ( item.m_mask & wxLIST_MASK_TEXT )
1096 {
1097 lvCol.mask |= LVCF_TEXT;
1098 lvCol.pszText = WXSTRINGCAST item.m_text;
1099 lvCol.cchTextMax = 0; // Ignored
1100 }
1101 if ( item.m_mask & wxLIST_MASK_FORMAT )
1102 {
1103 lvCol.mask |= LVCF_FMT;
1104
1105 if ( item.m_format == wxLIST_FORMAT_LEFT )
1106 lvCol.fmt = LVCFMT_LEFT;
1107 if ( item.m_format == wxLIST_FORMAT_RIGHT )
1108 lvCol.fmt = LVCFMT_RIGHT;
1109 if ( item.m_format == wxLIST_FORMAT_CENTRE )
1110 lvCol.fmt = LVCFMT_CENTER;
1111 }
1112
1113 lvCol.mask |= LVCF_WIDTH;
1114 if ( item.m_mask & wxLIST_MASK_WIDTH )
1115 {
1116 if ( item.m_width == wxLIST_AUTOSIZE)
1117 lvCol.cx = LVSCW_AUTOSIZE;
1118 else if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER)
1119 lvCol.cx = LVSCW_AUTOSIZE_USEHEADER;
1120 else
1121 lvCol.cx = item.m_width;
1122 }
1123 else
1124 {
1125 // always give some width to the new column: this one is compatible
1126 // with wxGTK
1127 lvCol.cx = 80;
1128 }
1129
1130 lvCol.mask |= LVCF_SUBITEM;
1131 lvCol.iSubItem = col;
1132
1133 bool success = ListView_InsertColumn(GetHwnd(), col, & lvCol) != -1;
1134 if ( success )
1135 {
1136 m_colCount++;
1137 }
1138 else
1139 {
1140 wxLogDebug(wxT("Failed to insert the column '%s' into listview!"),
1141 lvCol.pszText);
1142 }
1143
1144 return success;
1145 }
1146
1147 long wxListCtrl::InsertColumn(long col,
1148 const wxString& heading,
1149 int format,
1150 int width)
1151 {
1152 wxListItem item;
1153 item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
1154 item.m_text = heading;
1155 if ( width > -1 )
1156 {
1157 item.m_mask |= wxLIST_MASK_WIDTH;
1158 item.m_width = width;
1159 }
1160 item.m_format = format;
1161
1162 return InsertColumn(col, item);
1163 }
1164
1165 // Scrolls the list control. If in icon, small icon or report view mode,
1166 // x specifies the number of pixels to scroll. If in list view mode, x
1167 // specifies the number of columns to scroll.
1168 // If in icon, small icon or list view mode, y specifies the number of pixels
1169 // to scroll. If in report view mode, y specifies the number of lines to scroll.
1170 bool wxListCtrl::ScrollList(int dx, int dy)
1171 {
1172 return (ListView_Scroll(GetHwnd(), dx, dy) != 0);
1173 }
1174
1175 // Sort items.
1176
1177 // fn is a function which takes 3 long arguments: item1, item2, data.
1178 // item1 is the long data associated with a first item (NOT the index).
1179 // item2 is the long data associated with a second item (NOT the index).
1180 // data is the same value as passed to SortItems.
1181 // The return value is a negative number if the first item should precede the second
1182 // item, a positive number of the second item should precede the first,
1183 // or zero if the two items are equivalent.
1184
1185 // data is arbitrary data to be passed to the sort function.
1186 bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data)
1187 {
1188 return (ListView_SortItems(GetHwnd(), (PFNLVCOMPARE) fn, data) != 0);
1189 }
1190
1191 // ----------------------------------------------------------------------------
1192 // message processing
1193 // ----------------------------------------------------------------------------
1194
1195 bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id)
1196 {
1197 if (cmd == EN_UPDATE)
1198 {
1199 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
1200 event.SetEventObject( this );
1201 ProcessCommand(event);
1202 return TRUE;
1203 }
1204 else if (cmd == EN_KILLFOCUS)
1205 {
1206 wxCommandEvent event(wxEVT_KILL_FOCUS, id);
1207 event.SetEventObject( this );
1208 ProcessCommand(event);
1209 return TRUE;
1210 }
1211 else
1212 return FALSE;
1213 }
1214
1215 bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1216 {
1217 // prepare the event
1218 // -----------------
1219
1220 wxListEvent event(wxEVT_NULL, m_windowId);
1221 wxEventType eventType = wxEVT_NULL;
1222 NMHDR *nmhdr = (NMHDR *)lParam;
1223 switch ( nmhdr->code )
1224 {
1225 case LVN_BEGINRDRAG:
1226 eventType = wxEVT_COMMAND_LIST_BEGIN_RDRAG;
1227 // fall through
1228
1229 case LVN_BEGINDRAG:
1230 if ( eventType == wxEVT_NULL )
1231 {
1232 eventType = wxEVT_COMMAND_LIST_BEGIN_DRAG;
1233 }
1234
1235 {
1236 NM_LISTVIEW *hdr = (NM_LISTVIEW *)lParam;
1237 event.m_itemIndex = hdr->iItem;
1238 event.m_pointDrag.x = hdr->ptAction.x;
1239 event.m_pointDrag.y = hdr->ptAction.y;
1240 }
1241 break;
1242
1243 case LVN_BEGINLABELEDIT:
1244 {
1245 eventType = wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT;
1246 LV_DISPINFO *info = (LV_DISPINFO *)lParam;
1247 wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
1248 break;
1249 }
1250
1251 case LVN_COLUMNCLICK:
1252 {
1253 eventType = wxEVT_COMMAND_LIST_COL_CLICK;
1254 NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam;
1255 event.m_itemIndex = -1;
1256 event.m_col = hdr->iSubItem;
1257 break;
1258 }
1259
1260 case LVN_DELETEALLITEMS:
1261 // what's the sense of generating a wxWin event for this when
1262 // it's absolutely not portable?
1263 #if 0
1264 eventType = wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS;
1265 event.m_itemIndex = -1;
1266 #endif // 0
1267
1268 // return TRUE to suppress all additional LVN_DELETEITEM
1269 // notifications - this makes deleting all items from a list ctrl
1270 // much faster
1271 *result = TRUE;
1272
1273 return TRUE;
1274
1275 case LVN_DELETEITEM:
1276 {
1277 eventType = wxEVT_COMMAND_LIST_DELETE_ITEM;
1278 NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam;
1279 event.m_itemIndex = hdr->iItem;
1280
1281 if ( m_hasAnyAttr )
1282 {
1283 delete m_attrs.Delete(hdr->iItem);
1284 }
1285 }
1286 break;
1287
1288 case LVN_ENDLABELEDIT:
1289 {
1290 eventType = wxEVT_COMMAND_LIST_END_LABEL_EDIT;
1291 LV_DISPINFO *info = (LV_DISPINFO *)lParam;
1292 wxConvertFromMSWListItem(this, event.m_item, info->item);
1293 if ( info->item.pszText == NULL || info->item.iItem == -1 )
1294 return FALSE;
1295 }
1296 break;
1297
1298 case LVN_SETDISPINFO:
1299 {
1300 eventType = wxEVT_COMMAND_LIST_SET_INFO;
1301 LV_DISPINFO *info = (LV_DISPINFO *)lParam;
1302 wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
1303 }
1304 break;
1305
1306 case LVN_GETDISPINFO:
1307 // this provokes stack overflow: indeed, wxConvertFromMSWListItem()
1308 // sends us WM_NOTIFY! As it doesn't do anything for now, just leave
1309 // it out.
1310 #if 0
1311 {
1312 // TODO: some text buffering here, I think
1313 // TODO: API for getting Windows to retrieve values
1314 // on demand.
1315 eventType = wxEVT_COMMAND_LIST_GET_INFO;
1316 LV_DISPINFO *info = (LV_DISPINFO *)lParam;
1317 wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
1318 break;
1319 }
1320 #endif // 0
1321 return FALSE;
1322
1323
1324 case LVN_INSERTITEM:
1325 {
1326 eventType = wxEVT_COMMAND_LIST_INSERT_ITEM;
1327 NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam;
1328 event.m_itemIndex = hdr->iItem;
1329 break;
1330 }
1331
1332 case LVN_ITEMCHANGED:
1333 {
1334 // This needs to be sent to wxListCtrl as a rather more
1335 // concrete event. For now, just detect a selection
1336 // or deselection.
1337 NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam;
1338 if ( (hdr->uNewState & LVIS_SELECTED) && !(hdr->uOldState & LVIS_SELECTED) )
1339 {
1340 eventType = wxEVT_COMMAND_LIST_ITEM_SELECTED;
1341 event.m_itemIndex = hdr->iItem;
1342 }
1343 else if ( !(hdr->uNewState & LVIS_SELECTED) && (hdr->uOldState & LVIS_SELECTED) )
1344 {
1345 eventType = wxEVT_COMMAND_LIST_ITEM_DESELECTED;
1346 event.m_itemIndex = hdr->iItem;
1347 }
1348 else
1349 return FALSE;
1350 break;
1351 }
1352
1353 case LVN_KEYDOWN:
1354 {
1355 LV_KEYDOWN *info = (LV_KEYDOWN *)lParam;
1356 WORD wVKey = info->wVKey;
1357
1358 // get the current selection
1359 long lItem = GetNextItem(-1,
1360 wxLIST_NEXT_ALL,
1361 wxLIST_STATE_SELECTED);
1362
1363 // <Enter> or <Space> activate the selected item if any
1364 if ( lItem != -1 && (wVKey == VK_RETURN || wVKey == VK_SPACE) )
1365 {
1366 // TODO this behaviour probably should be optional
1367 eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
1368 event.m_itemIndex = lItem;
1369 }
1370 else
1371 {
1372 eventType = wxEVT_COMMAND_LIST_KEY_DOWN;
1373 event.m_code = wxCharCodeMSWToWX(wVKey);
1374 }
1375 break;
1376 }
1377
1378 case NM_DBLCLK:
1379 // if the user processes it in wxEVT_COMMAND_LEFT_CLICK(), don't do
1380 // anything else
1381 if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
1382 {
1383 return TRUE;
1384 }
1385
1386 // else translate it into wxEVT_COMMAND_LIST_ITEM_ACTIVATED event
1387 {
1388 eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
1389 NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam;
1390 event.m_itemIndex = hdr->iItem;
1391 }
1392 break;
1393
1394 case NM_RCLICK:
1395 /* TECH NOTE: NM_RCLICK isn't really good enough here. We want to
1396 subclass and check for the actual WM_RBUTTONDOWN message, because
1397 NM_RCLICK waits for the WM_RBUTTONUP message as well before firing off.
1398 We want to have notify events for both down -and- up. */
1399 {
1400 // if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(), don't do
1401 // anything else
1402 if ( wxControl::MSWOnNotify(idCtrl, lParam, result) ) {
1403 return TRUE;
1404 }
1405
1406 // else translate it into wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK event
1407 LV_HITTESTINFO lvhti;
1408 wxZeroMemory(lvhti);
1409
1410 ::GetCursorPos(&(lvhti.pt));
1411 ::ScreenToClient(GetHwnd(),&(lvhti.pt));
1412 if ( ListView_HitTest(GetHwnd(),&lvhti) != -1 )
1413 {
1414 if ( lvhti.flags & LVHT_ONITEM )
1415 {
1416 eventType = wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK;
1417 event.m_itemIndex = lvhti.iItem;
1418 }
1419 }
1420 }
1421 break;
1422
1423 #if 0
1424 case NM_MCLICK: // ***** THERE IS NO NM_MCLICK. Subclass anyone? ******
1425 {
1426 // if the user processes it in wxEVT_COMMAND_MIDDLE_CLICK(), don't do
1427 // anything else
1428 if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
1429 {
1430 return TRUE;
1431 }
1432
1433 // else translate it into wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK event
1434 eventType = wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK;
1435 NMITEMACTIVATE* hdr = (NMITEMACTIVATE*)lParam;
1436 event.m_itemIndex = hdr->iItem;
1437 }
1438 break;
1439 #endif // 0
1440
1441 #ifdef NM_CUSTOMDRAW
1442 case NM_CUSTOMDRAW:
1443 {
1444 LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
1445 NMCUSTOMDRAW& nmcd = lplvcd->nmcd;
1446 switch( nmcd.dwDrawStage )
1447 {
1448 case CDDS_PREPAINT:
1449 // if we've got any items with non standard attributes,
1450 // notify us before painting each item
1451 *result = m_hasAnyAttr ? CDRF_NOTIFYITEMDRAW
1452 : CDRF_DODEFAULT;
1453 return TRUE;
1454
1455 case CDDS_ITEMPREPAINT:
1456 {
1457 wxListItemAttr *attr =
1458 (wxListItemAttr *)m_attrs.Get(nmcd.dwItemSpec);
1459
1460 if ( !attr )
1461 {
1462 // nothing to do for this item
1463 return CDRF_DODEFAULT;
1464 }
1465
1466 HFONT hFont;
1467 wxColour colText, colBack;
1468 if ( attr->HasFont() )
1469 {
1470 wxFont font = attr->GetFont();
1471 hFont = (HFONT)font.GetResourceHandle();
1472 }
1473 else
1474 {
1475 hFont = 0;
1476 }
1477
1478 if ( attr->HasTextColour() )
1479 {
1480 colText = attr->GetTextColour();
1481 }
1482 else
1483 {
1484 colText = GetTextColour();
1485 }
1486
1487 if ( attr->HasBackgroundColour() )
1488 {
1489 colBack = attr->GetBackgroundColour();
1490 }
1491 else
1492 {
1493 colBack = GetBackgroundColour();
1494 }
1495
1496 // note that if we wanted to set colours for
1497 // individual columns (subitems), we would have
1498 // returned CDRF_NOTIFYSUBITEMREDRAW from here
1499 if ( hFont )
1500 {
1501 ::SelectObject(nmcd.hdc, hFont);
1502
1503 *result = CDRF_NEWFONT;
1504 }
1505 else
1506 {
1507 *result = CDRF_DODEFAULT;
1508 }
1509
1510 lplvcd->clrText = wxColourToRGB(colText);
1511 lplvcd->clrTextBk = wxColourToRGB(colBack);
1512
1513 return TRUE;
1514 }
1515
1516 default:
1517 *result = CDRF_DODEFAULT;
1518 return TRUE;
1519 }
1520 }
1521 break;
1522 #endif // NM_CUSTOMDRAW
1523
1524 default:
1525 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1526 }
1527
1528 // process the event
1529 // -----------------
1530
1531 event.SetEventObject( this );
1532 event.SetEventType(eventType);
1533
1534 if ( !GetEventHandler()->ProcessEvent(event) )
1535 return FALSE;
1536
1537 // post processing
1538 // ---------------
1539
1540 switch ( (int)nmhdr->code )
1541 {
1542 case LVN_GETDISPINFO:
1543 {
1544 LV_DISPINFO *info = (LV_DISPINFO *)lParam;
1545 if ( info->item.mask & LVIF_TEXT )
1546 {
1547 if ( !event.m_item.m_text.IsNull() )
1548 {
1549 info->item.pszText = AddPool(event.m_item.m_text);
1550 info->item.cchTextMax = wxStrlen(info->item.pszText) + 1;
1551 }
1552 }
1553 // wxConvertToMSWListItem(this, event.m_item, info->item);
1554 break;
1555 }
1556 case LVN_ENDLABELEDIT:
1557 {
1558 *result = event.IsAllowed();
1559 return TRUE;
1560 }
1561 default:
1562 break;
1563 }
1564
1565 *result = !event.IsAllowed();
1566
1567 return TRUE;
1568 }
1569
1570 wxChar *wxListCtrl::AddPool(const wxString& str)
1571 {
1572 // Remove the first element if 3 strings exist
1573 if ( m_stringPool.Number() == 3 )
1574 {
1575 wxNode *node = m_stringPool.First();
1576 delete[] (char *)node->Data();
1577 delete node;
1578 }
1579 wxNode *node = m_stringPool.Add(WXSTRINGCAST str);
1580 return (wxChar *)node->Data();
1581 }
1582
1583 // ----------------------------------------------------------------------------
1584 // wxListItem
1585 // ----------------------------------------------------------------------------
1586
1587 // List item structure
1588 wxListItem::wxListItem()
1589 {
1590 m_mask = 0;
1591 m_itemId = 0;
1592 m_col = 0;
1593 m_state = 0;
1594 m_stateMask = 0;
1595 m_image = 0;
1596 m_data = 0;
1597
1598 m_format = wxLIST_FORMAT_CENTRE;
1599 m_width = 0;
1600
1601 m_attr = NULL;
1602 }
1603
1604 static void wxConvertFromMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& lvItem, HWND getFullInfo)
1605 {
1606 info.m_data = lvItem.lParam;
1607 info.m_mask = 0;
1608 info.m_state = 0;
1609 info.m_stateMask = 0;
1610 info.m_itemId = lvItem.iItem;
1611
1612 long oldMask = lvItem.mask;
1613
1614 bool needText = FALSE;
1615 if (getFullInfo != 0)
1616 {
1617 if ( lvItem.mask & LVIF_TEXT )
1618 needText = FALSE;
1619 else
1620 needText = TRUE;
1621
1622 if ( needText )
1623 {
1624 lvItem.pszText = new wxChar[513];
1625 lvItem.cchTextMax = 512;
1626 }
1627 // lvItem.mask |= TVIF_HANDLE | TVIF_STATE | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
1628 lvItem.mask |= LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
1629 ::SendMessage(getFullInfo, LVM_GETITEM, 0, (LPARAM)& lvItem);
1630 }
1631
1632 if ( lvItem.mask & LVIF_STATE )
1633 {
1634 info.m_mask |= wxLIST_MASK_STATE;
1635
1636 if ( lvItem.stateMask & LVIS_CUT)
1637 {
1638 info.m_stateMask |= wxLIST_STATE_CUT;
1639 if ( lvItem.state & LVIS_CUT )
1640 info.m_state |= wxLIST_STATE_CUT;
1641 }
1642 if ( lvItem.stateMask & LVIS_DROPHILITED)
1643 {
1644 info.m_stateMask |= wxLIST_STATE_DROPHILITED;
1645 if ( lvItem.state & LVIS_DROPHILITED )
1646 info.m_state |= wxLIST_STATE_DROPHILITED;
1647 }
1648 if ( lvItem.stateMask & LVIS_FOCUSED)
1649 {
1650 info.m_stateMask |= wxLIST_STATE_FOCUSED;
1651 if ( lvItem.state & LVIS_FOCUSED )
1652 info.m_state |= wxLIST_STATE_FOCUSED;
1653 }
1654 if ( lvItem.stateMask & LVIS_SELECTED)
1655 {
1656 info.m_stateMask |= wxLIST_STATE_SELECTED;
1657 if ( lvItem.state & LVIS_SELECTED )
1658 info.m_state |= wxLIST_STATE_SELECTED;
1659 }
1660 }
1661
1662 if ( lvItem.mask & LVIF_TEXT )
1663 {
1664 info.m_mask |= wxLIST_MASK_TEXT;
1665 info.m_text = lvItem.pszText;
1666 }
1667 if ( lvItem.mask & LVIF_IMAGE )
1668 {
1669 info.m_mask |= wxLIST_MASK_IMAGE;
1670 info.m_image = lvItem.iImage;
1671 }
1672 if ( lvItem.mask & LVIF_PARAM )
1673 info.m_mask |= wxLIST_MASK_DATA;
1674 if ( lvItem.mask & LVIF_DI_SETITEM )
1675 info.m_mask |= wxLIST_SET_ITEM;
1676 info.m_col = lvItem.iSubItem;
1677
1678 if (needText)
1679 {
1680 if (lvItem.pszText)
1681 delete[] lvItem.pszText;
1682 }
1683 lvItem.mask = oldMask;
1684 }
1685
1686 static void wxConvertToMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& lvItem)
1687 {
1688 lvItem.iItem = (int) info.m_itemId;
1689
1690 lvItem.iImage = info.m_image;
1691 lvItem.lParam = info.m_data;
1692 lvItem.stateMask = 0;
1693 lvItem.state = 0;
1694 lvItem.mask = 0;
1695 lvItem.iSubItem = info.m_col;
1696
1697 if (info.m_mask & wxLIST_MASK_STATE)
1698 {
1699 lvItem.mask |= LVIF_STATE;
1700 if (info.m_stateMask & wxLIST_STATE_CUT)
1701 {
1702 lvItem.stateMask |= LVIS_CUT;
1703 if (info.m_state & wxLIST_STATE_CUT)
1704 lvItem.state |= LVIS_CUT;
1705 }
1706 if (info.m_stateMask & wxLIST_STATE_DROPHILITED)
1707 {
1708 lvItem.stateMask |= LVIS_DROPHILITED;
1709 if (info.m_state & wxLIST_STATE_DROPHILITED)
1710 lvItem.state |= LVIS_DROPHILITED;
1711 }
1712 if (info.m_stateMask & wxLIST_STATE_FOCUSED)
1713 {
1714 lvItem.stateMask |= LVIS_FOCUSED;
1715 if (info.m_state & wxLIST_STATE_FOCUSED)
1716 lvItem.state |= LVIS_FOCUSED;
1717 }
1718 if (info.m_stateMask & wxLIST_STATE_SELECTED)
1719 {
1720 lvItem.stateMask |= LVIS_SELECTED;
1721 if (info.m_state & wxLIST_STATE_SELECTED)
1722 lvItem.state |= LVIS_SELECTED;
1723 }
1724 }
1725
1726 if (info.m_mask & wxLIST_MASK_TEXT)
1727 {
1728 lvItem.mask |= LVIF_TEXT;
1729 if ( ctrl->GetWindowStyleFlag() & wxLC_USER_TEXT )
1730 {
1731 lvItem.pszText = LPSTR_TEXTCALLBACK;
1732 }
1733 else
1734 {
1735 lvItem.pszText = WXSTRINGCAST info.m_text;
1736 if ( lvItem.pszText )
1737 lvItem.cchTextMax = info.m_text.Length();
1738 else
1739 lvItem.cchTextMax = 0;
1740 }
1741 }
1742 if (info.m_mask & wxLIST_MASK_IMAGE)
1743 lvItem.mask |= LVIF_IMAGE;
1744 if (info.m_mask & wxLIST_MASK_DATA)
1745 lvItem.mask |= LVIF_PARAM;
1746 }
1747
1748 // ----------------------------------------------------------------------------
1749 // List event
1750 // ----------------------------------------------------------------------------
1751
1752 IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
1753
1754 wxListEvent::wxListEvent(wxEventType commandType, int id)
1755 : wxNotifyEvent(commandType, id)
1756 {
1757 m_code = 0;
1758 m_itemIndex = 0;
1759 m_oldItemIndex = 0;
1760 m_col = 0;
1761 m_cancelled = FALSE;
1762 }
1763
1764 #endif // __WIN95__
1765