Added translation using Weblate (Italian)
[cygwin-setup.git] / ListView.cc
blob1bf60ea4e62ebea2e7692739b0c48b6b1853c74d
1 /*
2 * Copyright (c) 2016 Jon Turney
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
14 #include "ListView.h"
15 #include "LogSingleton.h"
16 #include "resource.h"
17 #include "String++.h"
19 #include <commctrl.h>
21 // ---------------------------------------------------------------------------
22 // implements class ListView
24 // ListView Common Control
25 // ---------------------------------------------------------------------------
27 int ModifierKeys::get()
29 int keys = 0;
30 if (GetKeyState(VK_SHIFT) & 0x8000)
31 keys |= Shift;
32 if (GetKeyState(VK_CONTROL) & 0x8000)
33 keys |= Control;
34 if (GetKeyState(VK_MENU) & 0x8000)
35 keys |= Alt;
36 return keys;
39 void
40 ListView::init(HWND parent, int id, HeaderList headers)
42 hWndParent = parent;
44 // locate the listview control
45 hWndListView = ::GetDlgItem(parent, id);
47 // configure the listview control
48 SendMessage(hWndListView, CCM_SETVERSION, 6, 0);
50 ListView_SetExtendedListViewStyle(hWndListView,
51 LVS_EX_COLUMNSNAPPOINTS | // use cxMin
52 LVS_EX_FULLROWSELECT |
53 LVS_EX_GRIDLINES |
54 LVS_EX_HEADERDRAGDROP); // headers can be re-ordered
56 // give the header control a border
57 HWND hWndHeader = ListView_GetHeader(hWndListView);
58 SetWindowLongPtr(hWndHeader, GWL_STYLE,
59 GetWindowLongPtr(hWndHeader, GWL_STYLE) | WS_BORDER);
61 // ensure an initial item exists for width calculations...
62 LVITEM lvi;
63 lvi.mask = LVIF_TEXT;
64 lvi.iItem = 0;
65 lvi.iSubItem = 0;
66 lvi.pszText = const_cast <char *> ("Working...");
67 ListView_InsertItem(hWndListView, &lvi);
69 // populate with columns
70 initColumns(headers);
72 // create a small icon imagelist
73 // (the order of images matches ListViewLine::State enum)
74 hImgList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
75 GetSystemMetrics(SM_CYSMICON),
76 ILC_COLOR32, 2, 0);
77 ImageList_AddIcon(hImgList, LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_TREE_PLUS)));
78 ImageList_AddIcon(hImgList, LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_TREE_MINUS)));
80 // create an empty imagelist, used to reset the indent
81 hEmptyImgList = ImageList_Create(1, 1,
82 ILC_COLOR32, 2, 0);
84 // LVS_EX_INFOTIP/LVN_GETINFOTIP doesn't work for subitems, so we have to do
85 // our own tooltip handling
86 hWndTip = CreateWindowEx (0,
87 (LPCTSTR) TOOLTIPS_CLASS,
88 NULL,
89 WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
90 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
91 hWndParent,
92 (HMENU) 0,
93 GetModuleHandle(NULL),
94 NULL);
95 // must be topmost so that tooltips will display on top
96 SetWindowPos(hWndTip, HWND_TOPMOST,0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
98 TOOLINFO ti;
99 memset ((void *)&ti, 0, sizeof(ti));
100 ti.cbSize = sizeof(ti);
101 ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
102 ti.hwnd = hWndParent;
103 ti.uId = (UINT_PTR)hWndListView;
104 ti.lpszText = LPSTR_TEXTCALLBACK; // use TTN_GETDISPINFO
105 SendMessage(hWndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
107 // match long delay for tooltip to disappear used elsewhere (30s)
108 SendMessage(hWndTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM) MAKELONG (30000, 0));
109 // match tip width used elsewhere
110 SendMessage(hWndTip, TTM_SETMAXTIPWIDTH, 0, 450);
112 // switch to using wide-char WM_NOTIFY messages
113 ListView_SetUnicodeFormat(hWndListView, TRUE);
116 void
117 ListView::initColumns(HeaderList headers_)
119 // store HeaderList for later use
120 headers = headers_;
122 // create the columns
123 LVCOLUMNW lvc;
124 lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
126 int i;
127 for (i = 0; headers[i].text != 0; i++)
129 std::wstring h = LoadStringW(headers[i].text);
131 lvc.iSubItem = i;
132 lvc.pszText = const_cast <wchar_t *> (h.c_str());
133 lvc.cx = 100;
134 lvc.fmt = headers[i].fmt;
136 SendMessage(hWndListView, LVM_INSERTCOLUMNW, i, (LPARAM)&lvc);
139 // now do some width calculations
140 for (i = 0; headers[i].text != 0; i++)
142 headers[i].width = 0;
144 ListView_SetColumnWidth(hWndListView, i, LVSCW_AUTOSIZE_USEHEADER);
145 headers[i].hdr_width = ListView_GetColumnWidth(hWndListView, i);
149 void
150 ListView::noteColumnWidthStart()
152 dc = GetDC (hWndListView);
154 // we must set the font of the DC here, otherwise the width calculations
155 // will be off because the system will use the wrong font metrics
156 HANDLE sysfont = GetStockObject (DEFAULT_GUI_FONT);
157 SelectObject (dc, sysfont);
159 int i;
160 for (i = 0; headers[i].text != 0; i++)
162 headers[i].width = 0;
166 // wrappers to help instantiations of the noteColumnWidth() template call the
167 // right version of GetTextExtentPoint32
168 #undef GetTextExtentPoint32
170 static BOOL GetTextExtentPoint32(HDC hdc, LPCSTR lpString, int c, LPSIZE psizl)
172 return GetTextExtentPoint32A(hdc, lpString, c, psizl);
175 static BOOL GetTextExtentPoint32(HDC hdc, LPCWSTR lpString, int c, LPSIZE psizl)
177 return GetTextExtentPoint32W(hdc, lpString, c, psizl);
180 template <typename T>
181 void
182 ListView::noteColumnWidth(int col_num, const T& string)
184 SIZE s = { 0, 0 };
186 // A margin of 3*GetSystemMetrics(SM_CXEDGE) is used at each side of the
187 // header text.
188 int addend = 2*3*GetSystemMetrics(SM_CXEDGE);
190 if (string.size())
191 GetTextExtentPoint32 (dc, string.c_str(), string.size(), &s);
193 int width = addend + s.cx;
195 // allow for width of dropdown button in popup columns
196 if (headers[col_num].type == ListView::ControlType::popup)
198 width += GetSystemMetrics(SM_CXVSCROLL);
201 if (width > headers[col_num].width)
202 headers[col_num].width = width;
205 // explicit instantiation
206 template void ListView::noteColumnWidth(int col_num, const std::string& string);
207 template void ListView::noteColumnWidth(int col_num, const std::wstring& wstring);
209 void
210 ListView::noteColumnWidthEnd()
212 ReleaseDC(hWndListView, dc);
215 void
216 ListView::resizeColumns(void)
218 // ensure the last column stretches all the way to the right-hand side of the
219 // listview control
220 int i;
221 int total = 0;
222 for (i = 0; headers[i].text != 0; i++)
223 total = total + headers[i].width;
225 RECT r;
226 GetClientRect(hWndListView, &r);
227 int width = r.right - r.left;
229 if (total < width)
230 headers[i-1].width += width - total;
232 // size each column
233 LVCOLUMN lvc;
234 lvc.mask = LVCF_WIDTH | LVCF_MINWIDTH;
235 for (i = 0; headers[i].text != 0; i++)
237 lvc.iSubItem = i;
238 lvc.cx = (headers[i].width < headers[i].hdr_width) ? headers[i].hdr_width : headers[i].width;
239 lvc.cxMin = headers[i].hdr_width;
240 #if DEBUG
241 Log (LOG_BABBLE) << "resizeColumns: " << i << " cx " << lvc.cx << " cxMin " << lvc.cxMin <<endLog;
242 #endif
244 ListView_SetColumn(hWndListView, i, &lvc);
248 void
249 ListView::setContents(ListViewContents *_contents, bool tree)
251 contents = _contents;
253 // disable redrawing of ListView
254 // (otherwise it will redraw every time a row is added, which makes this very slow)
255 SendMessage(hWndListView, WM_SETREDRAW, FALSE, 0);
257 // preserve focus/selection
258 int iRow = ListView_GetSelectionMark(hWndListView);
260 empty();
262 // assign imagelist to listview control (this also sets the size for indents)
263 if (tree)
264 ListView_SetImageList(hWndListView, hImgList, LVSIL_SMALL);
265 else
266 ListView_SetImageList(hWndListView, hEmptyImgList, LVSIL_SMALL);
268 size_t i;
269 for (i = 0; i < contents->size(); i++)
271 LVITEM lvi;
272 lvi.mask = LVIF_TEXT | (tree ? LVIF_IMAGE | LVIF_INDENT : 0);
273 lvi.iItem = i;
274 lvi.iSubItem = 0;
275 lvi.pszText = LPSTR_TEXTCALLBACK;
276 if (tree)
278 lvi.iImage = I_IMAGECALLBACK;
279 lvi.iIndent = (*contents)[i]->get_indent();
282 ListView_InsertItem(hWndListView, &lvi);
285 if (iRow >= 0)
287 ListView_SetItemState(hWndListView, iRow, LVNI_SELECTED | LVNI_FOCUSED, LVNI_SELECTED | LVNI_FOCUSED);
288 ListView_EnsureVisible(hWndListView, iRow, false);
291 // enable redrawing of ListView and redraw
292 SendMessage(hWndListView, WM_SETREDRAW, TRUE, 0);
293 RedrawWindow(hWndListView, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
296 // Helper class: The pointer we hand back needs to remain valid for some time
297 // after OnNotify returns, when the string object we have retrieved has gone out
298 // of scope, so a static instance of this class maintains a local cache.
299 template <class T> class StringCache
301 typedef typename T::traits_type::char_type char_type;
302 public:
303 StringCache() : cache(NULL), cache_size(0) { }
304 StringCache & operator = (const T & s)
306 if ((s.length() + 1) > cache_size)
308 cache_size = s.length() + 1;
309 cache = (char_type *)realloc(cache, cache_size * sizeof(char_type));
311 memcpy(cache, s.c_str(), (s.length() + 1) * sizeof(char_type));
312 return *this;
314 operator char_type *() const
316 return cache;
318 private:
319 char_type *cache;
320 size_t cache_size;
323 bool
324 ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
326 #if DEBUG
327 Log (LOG_BABBLE) << "ListView::OnNotify id:" << pNmHdr->idFrom << " hwnd:" << pNmHdr->hwndFrom << " code:" << (int)pNmHdr->code << endLog;
328 #endif
330 switch (pNmHdr->code)
332 case LVN_GETDISPINFOW:
334 NMLVDISPINFOW *pNmLvDispInfo = (NMLVDISPINFOW *)pNmHdr;
335 #if DEBUG
336 Log (LOG_BABBLE) << "LVN_GETDISPINFO " << pNmLvDispInfo->item.iItem << endLog;
337 #endif
338 if (contents)
340 int iRow = pNmLvDispInfo->item.iItem;
341 int iCol = pNmLvDispInfo->item.iSubItem;
343 static StringCache<std::wstring> s;
344 s = (*contents)[iRow]->get_text(iCol);
345 pNmLvDispInfo->item.pszText = s;
347 if (pNmLvDispInfo->item.iSubItem == 0)
349 pNmLvDispInfo->item.iImage = (int)((*contents)[pNmLvDispInfo->item.iItem]->get_state());
353 return true;
355 break;
357 case LVN_GETEMPTYMARKUP:
359 NMLVEMPTYMARKUP *pNmMarkup = (NMLVEMPTYMARKUP*) pNmHdr;
360 wcsncpy(pNmMarkup->szMarkup, empty_list_text.c_str(), L_MAX_URL_LENGTH);
361 *pResult = true;
362 return true;
364 break;
366 case NM_CLICK:
368 NMITEMACTIVATE *pNmItemAct = (NMITEMACTIVATE *) pNmHdr;
369 #if DEBUG
370 Log (LOG_BABBLE) << "NM_CLICK: pnmitem->iItem " << pNmItemAct->iItem << " pNmItemAct->iSubItem " << pNmItemAct->iSubItem << endLog;
371 #endif
372 int iRow = pNmItemAct->iItem;
373 int iCol = pNmItemAct->iSubItem;
374 if (iRow < 0)
375 return false;
377 int update = 0;
379 if (headers[iCol].type == ListView::ControlType::popup)
381 POINT p;
382 GetCursorPos(&p);
384 RECT r;
385 ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r);
386 POINT cp = p;
387 ::ScreenToClient(hWndListView, &cp);
389 // if the click isn't over the pop-up button, do nothing yet (but this
390 // might be followed by a NM_DBLCLK)
391 if (cp.x < r.right - GetSystemMetrics(SM_CXVSCROLL))
392 return true;
394 // position pop-up menu at the location of the click
395 update = popup_menu(iRow, iCol, p);
397 else
399 // Inform the item of the click
400 update = (*contents)[iRow]->do_action(iCol, 0);
403 // Update items, if needed
404 if (update > 0)
406 ListView_RedrawItems(hWndListView, iRow, iRow + update -1);
409 return true;
411 break;
413 case NM_DBLCLK:
415 NMITEMACTIVATE *pNmItemAct = (NMITEMACTIVATE *) pNmHdr;
416 #if DEBUG
417 Log (LOG_BABBLE) << "NM_DBLCLICK: pnmitem->iItem " << pNmItemAct->iItem << " pNmItemAct->iSubItem " << pNmItemAct->iSubItem << endLog;
418 #endif
419 int iRow = pNmItemAct->iItem;
420 int iCol = pNmItemAct->iSubItem;
421 if (iRow < 0)
422 return false;
424 int update = 0;
426 // Inform the item of the double-click
427 update = (*contents)[iRow]->do_default_action(iCol );
429 // Update items, if needed
430 if (update > 0)
432 ListView_RedrawItems(hWndListView, iRow, iRow + update -1);
435 return true;
437 break;
439 case NM_CUSTOMDRAW:
441 NMLVCUSTOMDRAW *pNmLvCustomDraw = (NMLVCUSTOMDRAW *)pNmHdr;
443 switch(pNmLvCustomDraw->nmcd.dwDrawStage)
445 case CDDS_PREPAINT:
446 *pResult = CDRF_NOTIFYITEMDRAW;
447 return true;
448 case CDDS_ITEMPREPAINT:
449 *pResult = CDRF_NOTIFYSUBITEMDRAW;
450 return true;
451 case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
453 LRESULT result = CDRF_DODEFAULT;
454 int iCol = pNmLvCustomDraw->iSubItem;
455 int iRow = pNmLvCustomDraw->nmcd.dwItemSpec;
457 switch (headers[iCol].type)
459 default:
460 case ListView::ControlType::text:
461 result = CDRF_DODEFAULT;
462 break;
464 case ListView::ControlType::checkbox:
466 // get the subitem text (as ASCII)
467 char buf[3];
468 ListView_GetItemText(hWndListView, iRow, iCol, buf, _countof(buf));
470 // map the subitem text to a checkbox state
471 UINT state = DFCS_BUTTONCHECK | DFCS_FLAT;
472 if (buf[0] == '\0') // empty
474 result = CDRF_DODEFAULT;
475 break;
477 else if (buf[0] == 'y') // yes
478 state |= DFCS_CHECKED;
479 else if ((buf[0] == 'n') && (buf[1] == 'o')) // no
480 state |= 0;
481 else // n/a
482 state |= DFCS_INACTIVE;
484 // erase and draw a checkbox
485 RECT r;
486 ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r);
487 DWORD bkg_color;
488 if (pNmLvCustomDraw->nmcd.uItemState & CDIS_SELECTED)
489 bkg_color = GetSysColor(COLOR_HIGHLIGHT);
490 else
491 bkg_color = ListView_GetBkColor(hWndListView);
492 HBRUSH hBrush = CreateSolidBrush(bkg_color);
493 FillRect(pNmLvCustomDraw->nmcd.hdc, &r, hBrush);
494 DeleteObject(hBrush);
495 DrawFrameControl(pNmLvCustomDraw->nmcd.hdc, &r, DFC_BUTTON, state);
497 result = CDRF_SKIPDEFAULT;
499 break;
501 case ListView::ControlType::popup:
503 // let the control draw the text, but notify us afterwards
504 result = CDRF_NOTIFYPOSTPAINT;
506 break;
509 *pResult = result;
510 return true;
512 case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT:
514 LRESULT result = CDRF_DODEFAULT;
515 int iCol = pNmLvCustomDraw->iSubItem;
516 int iRow = pNmLvCustomDraw->nmcd.dwItemSpec;
518 switch (headers[iCol].type)
520 default:
521 result = CDRF_DODEFAULT;
522 break;
524 case ListView::ControlType::popup:
526 // draw the control at the RHS of the cell
527 RECT r;
528 ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r);
529 r.left = r.right - GetSystemMetrics(SM_CXVSCROLL);
530 DrawFrameControl(pNmLvCustomDraw->nmcd.hdc, &r, DFC_SCROLL,DFCS_SCROLLCOMBOBOX);
532 result = CDRF_DODEFAULT;
534 break;
536 *pResult = result;
537 return true;
541 break;
543 case LVN_HOTTRACK:
545 NMLISTVIEW *pNmListView = (NMLISTVIEW *)pNmHdr;
546 int iRow = pNmListView->iItem;
547 int iCol = pNmListView->iSubItem;
548 #if DEBUG
549 Log (LOG_BABBLE) << "LVN_HOTTRACK " << iRow << " " << iCol << endLog;
550 #endif
551 if (iRow < 0)
552 return true;
554 // if we've tracked off to a different cell
555 if ((iRow != iRow_track) || (iCol != iCol_track))
557 #if DEBUG
558 Log (LOG_BABBLE) << "LVN_HOTTRACK changed cell" << endLog;
559 #endif
561 // if the tooltip for previous cell is displayed, remove it
562 // restart the tooltip AUTOPOP timer for this cell
563 SendMessage(hWndTip, TTM_ACTIVATE, FALSE, 0);
564 SendMessage(hWndTip, TTM_ACTIVATE, TRUE, 0);
566 iRow_track = iRow;
567 iCol_track = iCol;
570 return true;
572 break;
574 case LVN_KEYDOWN:
576 NMLVKEYDOWN *pNmLvKeyDown = (NMLVKEYDOWN *)pNmHdr;
577 int iRow = ListView_GetSelectionMark(hWndListView);
578 int modkeys = ModifierKeys::get();
579 #if DEBUG
580 Log (LOG_PLAIN) << "LVN_KEYDOWN vkey " << pNmLvKeyDown->wVKey << " on row " << iRow
581 << " Shift:" << !!(modkeys & ModifierKeys::Shift)
582 << " Ctrl:" << !!(modkeys & ModifierKeys::Control)
583 << " Alt:" << !!(modkeys & ModifierKeys::Alt) << endLog;
584 #endif
586 if (contents && iRow >= 0)
588 int col_num = 0;
589 int action_id = 0;
590 int todo = (*contents)[iRow]->map_key_to_action(pNmLvKeyDown->wVKey, modkeys,
591 col_num, action_id);
592 int update = 0;
593 if (todo & ListViewLine::Action::Direct)
594 update = (*contents)[iRow]->do_action(col_num, action_id);
595 else if (todo & ListViewLine::Action::PopUp)
597 POINT p;
598 RECT r;
599 ListView_GetSubItemRect(hWndListView, iRow, col_num, LVIR_BOUNDS, &r);
600 p.x = r.left;
601 p.y = r.top;
602 ClientToScreen(hWndListView, &p);
604 update = popup_menu(iRow, col_num, p);
607 if (update > 0)
608 ListView_RedrawItems(hWndListView, iRow, iRow + update -1);
610 if ((todo & ListViewLine::Action::NextRow)
611 && iRow + 1 < ListView_GetItemCount(hWndListView))
613 // move selection to next row
614 ListView_SetItemState(hWndListView, -1, 0, LVIS_SELECTED | LVIS_FOCUSED);
615 ListView_SetItemState(hWndListView, iRow + 1, LVIS_SELECTED | LVIS_FOCUSED,
616 LVIS_SELECTED | LVIS_FOCUSED);
617 ListView_SetSelectionMark(hWndListView, iRow + 1);
618 ListView_EnsureVisible(hWndListView, iRow + 1, false);
622 break;
624 case TTN_GETDISPINFOW:
626 // convert mouse position to item/subitem
627 LVHITTESTINFO lvHitTestInfo;
628 lvHitTestInfo.flags = LVHT_ONITEM;
629 GetCursorPos(&lvHitTestInfo.pt);
630 ::ScreenToClient(hWndListView, &lvHitTestInfo.pt);
631 ListView_SubItemHitTest(hWndListView, &lvHitTestInfo);
633 int iRow = lvHitTestInfo.iItem;
634 int iCol = lvHitTestInfo.iSubItem;
635 if (iRow < 0)
636 return false;
638 #if DEBUG
639 Log (LOG_BABBLE) << "TTN_GETDISPINFO " << iRow << " " << iCol << endLog;
640 #endif
642 // get the tooltip text for that item/subitem
643 static StringCache<std::wstring> wtooltip;
644 std::string tooltip = "";
645 if (contents)
646 tooltip = (*contents)[iRow]->get_tooltip(iCol);
648 wtooltip = string_to_wstring(tooltip);
650 // set the tooltip text
651 NMTTDISPINFOW *pNmTTDispInfo = (NMTTDISPINFOW *)pNmHdr;
652 pNmTTDispInfo->lpszText = wtooltip;
653 pNmTTDispInfo->hinst = NULL;
654 pNmTTDispInfo->uFlags = 0;
656 return true;
658 break;
661 // We don't care.
662 return false;
665 void
666 ListView::empty(void)
668 ListView_DeleteAllItems(hWndListView);
671 void
672 ListView::setEmptyText(unsigned int text)
674 empty_list_text = LoadStringW(text);
678 ListView::popup_menu(int iRow, int iCol, POINT p)
680 int update = 0;
681 // construct menu
682 HMENU hMenu = CreatePopupMenu();
684 MENUITEMINFOW mii;
685 memset(&mii, 0, sizeof(mii));
686 mii.cbSize = sizeof(mii);
687 mii.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_STRING | MIIM_ID;
688 mii.fType = MFT_STRING;
690 ActionList *al = (*contents)[iRow]->get_actions(iCol);
692 Actions::iterator i;
693 int j = 1;
694 for (i = al->list.begin (); i != al->list.end (); ++i, ++j)
696 BOOL res;
697 mii.dwTypeData = const_cast <wchar_t *> (i->name.c_str());
698 mii.fState = (i->selected ? MFS_CHECKED : MFS_UNCHECKED |
699 i->enabled ? MFS_ENABLED : MFS_DISABLED);
700 mii.wID = j;
702 res = InsertMenuItemW(hMenu, -1, TRUE, &mii);
703 if (!res) Log (LOG_BABBLE) << "InsertMenuItem failed " << endLog;
706 int id = TrackPopupMenu(hMenu,
707 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_NOANIMATION,
708 p.x, p.y, 0, hWndListView, NULL);
710 // Inform the item of the menu choice
711 if (id)
712 update = (*contents)[iRow]->do_action(iCol, al->list[id-1].id);
714 DestroyMenu(hMenu);
715 delete al;
717 return update;