]> cygwin.com Git - cygwin-apps/setup.git/blob - ListView.cc
Move column header strings to string table resource
[cygwin-apps/setup.git] / ListView.cc
1 /*
2 * Copyright (c) 2016 Jon Turney
3 *
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.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 */
13
14 #include "ListView.h"
15 #include "LogSingleton.h"
16 #include "resource.h"
17 #include "String++.h"
18
19 #include <commctrl.h>
20
21 // ---------------------------------------------------------------------------
22 // implements class ListView
23 //
24 // ListView Common Control
25 // ---------------------------------------------------------------------------
26
27 void
28 ListView::init(HWND parent, int id, HeaderList headers)
29 {
30 hWndParent = parent;
31
32 // locate the listview control
33 hWndListView = ::GetDlgItem(parent, id);
34
35 // configure the listview control
36 SendMessage(hWndListView, CCM_SETVERSION, 6, 0);
37
38 ListView_SetExtendedListViewStyle(hWndListView,
39 LVS_EX_COLUMNSNAPPOINTS | // use cxMin
40 LVS_EX_FULLROWSELECT |
41 LVS_EX_GRIDLINES |
42 LVS_EX_HEADERDRAGDROP); // headers can be re-ordered
43
44 // give the header control a border
45 HWND hWndHeader = ListView_GetHeader(hWndListView);
46 SetWindowLongPtr(hWndHeader, GWL_STYLE,
47 GetWindowLongPtr(hWndHeader, GWL_STYLE) | WS_BORDER);
48
49 // ensure an initial item exists for width calculations...
50 LVITEM lvi;
51 lvi.mask = LVIF_TEXT;
52 lvi.iItem = 0;
53 lvi.iSubItem = 0;
54 lvi.pszText = const_cast <char *> ("Working...");
55 ListView_InsertItem(hWndListView, &lvi);
56
57 // populate with columns
58 initColumns(headers);
59
60 // create a small icon imagelist
61 // (the order of images matches ListViewLine::State enum)
62 hImgList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
63 GetSystemMetrics(SM_CYSMICON),
64 ILC_COLOR32, 2, 0);
65 ImageList_AddIcon(hImgList, LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_TREE_PLUS)));
66 ImageList_AddIcon(hImgList, LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_TREE_MINUS)));
67
68 // create an empty imagelist, used to reset the indent
69 hEmptyImgList = ImageList_Create(1, 1,
70 ILC_COLOR32, 2, 0);
71
72 // LVS_EX_INFOTIP/LVN_GETINFOTIP doesn't work for subitems, so we have to do
73 // our own tooltip handling
74 hWndTip = CreateWindowEx (0,
75 (LPCTSTR) TOOLTIPS_CLASS,
76 NULL,
77 WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
78 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
79 hWndParent,
80 (HMENU) 0,
81 GetModuleHandle(NULL),
82 NULL);
83 // must be topmost so that tooltips will display on top
84 SetWindowPos(hWndTip, HWND_TOPMOST,0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
85
86 TOOLINFO ti;
87 memset ((void *)&ti, 0, sizeof(ti));
88 ti.cbSize = sizeof(ti);
89 ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
90 ti.hwnd = hWndParent;
91 ti.uId = (UINT_PTR)hWndListView;
92 ti.lpszText = LPSTR_TEXTCALLBACK; // use TTN_GETDISPINFO
93 SendMessage(hWndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
94
95 // match long delay for tooltip to disappear used elsewhere (30s)
96 SendMessage(hWndTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM) MAKELONG (30000, 0));
97 // match tip width used elsewhere
98 SendMessage(hWndTip, TTM_SETMAXTIPWIDTH, 0, 450);
99
100 // switch to using wide-char WM_NOTIFY messages
101 ListView_SetUnicodeFormat(hWndListView, TRUE);
102 }
103
104 void
105 ListView::initColumns(HeaderList headers_)
106 {
107 // store HeaderList for later use
108 headers = headers_;
109
110 // create the columns
111 LVCOLUMNW lvc;
112 lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
113
114 int i;
115 for (i = 0; headers[i].text != 0; i++)
116 {
117 std::wstring h = LoadStringW(headers[i].text);
118
119 lvc.iSubItem = i;
120 lvc.pszText = const_cast <wchar_t *> (h.c_str());
121 lvc.cx = 100;
122 lvc.fmt = headers[i].fmt;
123
124 SendMessage(hWndListView, LVM_INSERTCOLUMNW, i, (LPARAM)&lvc);
125 }
126
127 // now do some width calculations
128 for (i = 0; headers[i].text != 0; i++)
129 {
130 headers[i].width = 0;
131
132 ListView_SetColumnWidth(hWndListView, i, LVSCW_AUTOSIZE_USEHEADER);
133 headers[i].hdr_width = ListView_GetColumnWidth(hWndListView, i);
134 }
135 }
136
137 void
138 ListView::noteColumnWidthStart()
139 {
140 dc = GetDC (hWndListView);
141
142 // we must set the font of the DC here, otherwise the width calculations
143 // will be off because the system will use the wrong font metrics
144 HANDLE sysfont = GetStockObject (DEFAULT_GUI_FONT);
145 SelectObject (dc, sysfont);
146
147 int i;
148 for (i = 0; headers[i].text != 0; i++)
149 {
150 headers[i].width = 0;
151 }
152 }
153
154 // wrappers to help instantiations of the noteColumnWidth() template call the
155 // right version of GetTextExtentPoint32
156 #undef GetTextExtentPoint32
157
158 static BOOL GetTextExtentPoint32(HDC hdc, LPCSTR lpString, int c, LPSIZE psizl)
159 {
160 return GetTextExtentPoint32A(hdc, lpString, c, psizl);
161 }
162
163 static BOOL GetTextExtentPoint32(HDC hdc, LPCWSTR lpString, int c, LPSIZE psizl)
164 {
165 return GetTextExtentPoint32W(hdc, lpString, c, psizl);
166 }
167
168 template <typename T>
169 void
170 ListView::noteColumnWidth(int col_num, const T& string)
171 {
172 SIZE s = { 0, 0 };
173
174 // A margin of 3*GetSystemMetrics(SM_CXEDGE) is used at each side of the
175 // header text.
176 int addend = 2*3*GetSystemMetrics(SM_CXEDGE);
177
178 if (string.size())
179 GetTextExtentPoint32 (dc, string.c_str(), string.size(), &s);
180
181 int width = addend + s.cx;
182
183 // allow for width of dropdown button in popup columns
184 if (headers[col_num].type == ListView::ControlType::popup)
185 {
186 width += GetSystemMetrics(SM_CXVSCROLL);
187 }
188
189 if (width > headers[col_num].width)
190 headers[col_num].width = width;
191 }
192
193 // explicit instantiation
194 template void ListView::noteColumnWidth(int col_num, const std::string& string);
195 template void ListView::noteColumnWidth(int col_num, const std::wstring& wstring);
196
197 void
198 ListView::noteColumnWidthEnd()
199 {
200 ReleaseDC(hWndListView, dc);
201 }
202
203 void
204 ListView::resizeColumns(void)
205 {
206 // ensure the last column stretches all the way to the right-hand side of the
207 // listview control
208 int i;
209 int total = 0;
210 for (i = 0; headers[i].text != 0; i++)
211 total = total + headers[i].width;
212
213 RECT r;
214 GetClientRect(hWndListView, &r);
215 int width = r.right - r.left;
216
217 if (total < width)
218 headers[i-1].width += width - total;
219
220 // size each column
221 LVCOLUMN lvc;
222 lvc.mask = LVCF_WIDTH | LVCF_MINWIDTH;
223 for (i = 0; headers[i].text != 0; i++)
224 {
225 lvc.iSubItem = i;
226 lvc.cx = (headers[i].width < headers[i].hdr_width) ? headers[i].hdr_width : headers[i].width;
227 lvc.cxMin = headers[i].hdr_width;
228 #if DEBUG
229 Log (LOG_BABBLE) << "resizeColumns: " << i << " cx " << lvc.cx << " cxMin " << lvc.cxMin <<endLog;
230 #endif
231
232 ListView_SetColumn(hWndListView, i, &lvc);
233 }
234 }
235
236 void
237 ListView::setContents(ListViewContents *_contents, bool tree)
238 {
239 contents = _contents;
240
241 // disable redrawing of ListView
242 // (otherwise it will redraw every time a row is added, which makes this very slow)
243 SendMessage(hWndListView, WM_SETREDRAW, FALSE, 0);
244
245 // preserve focus/selection
246 int iRow = ListView_GetSelectionMark(hWndListView);
247
248 empty();
249
250 // assign imagelist to listview control (this also sets the size for indents)
251 if (tree)
252 ListView_SetImageList(hWndListView, hImgList, LVSIL_SMALL);
253 else
254 ListView_SetImageList(hWndListView, hEmptyImgList, LVSIL_SMALL);
255
256 size_t i;
257 for (i = 0; i < contents->size(); i++)
258 {
259 LVITEM lvi;
260 lvi.mask = LVIF_TEXT | (tree ? LVIF_IMAGE | LVIF_INDENT : 0);
261 lvi.iItem = i;
262 lvi.iSubItem = 0;
263 lvi.pszText = LPSTR_TEXTCALLBACK;
264 if (tree)
265 {
266 lvi.iImage = I_IMAGECALLBACK;
267 lvi.iIndent = (*contents)[i]->get_indent();
268 }
269
270 ListView_InsertItem(hWndListView, &lvi);
271 }
272
273 if (iRow >= 0)
274 {
275 ListView_SetItemState(hWndListView, iRow, LVNI_SELECTED | LVNI_FOCUSED, LVNI_SELECTED | LVNI_FOCUSED);
276 ListView_EnsureVisible(hWndListView, iRow, false);
277 }
278
279 // enable redrawing of ListView and redraw
280 SendMessage(hWndListView, WM_SETREDRAW, TRUE, 0);
281 RedrawWindow(hWndListView, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
282 }
283
284 // Helper class: The pointer we hand back needs to remain valid for some time
285 // after OnNotify returns, when the string object we have retrieved has gone out
286 // of scope, so a static instance of this class maintains a local cache.
287 template <class T> class StringCache
288 {
289 typedef typename T::traits_type::char_type char_type;
290 public:
291 StringCache() : cache(NULL), cache_size(0) { }
292 StringCache & operator = (const T & s)
293 {
294 if ((s.length() + 1) > cache_size)
295 {
296 cache_size = s.length() + 1;
297 cache = (char_type *)realloc(cache, cache_size * sizeof(char_type));
298 }
299 memcpy(cache, s.c_str(), (s.length() + 1) * sizeof(char_type));
300 return *this;
301 }
302 operator char_type *() const
303 {
304 return cache;
305 }
306 private:
307 char_type *cache;
308 size_t cache_size;
309 };
310
311 bool
312 ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
313 {
314 #if DEBUG
315 Log (LOG_BABBLE) << "ListView::OnNotify id:" << pNmHdr->idFrom << " hwnd:" << pNmHdr->hwndFrom << " code:" << (int)pNmHdr->code << endLog;
316 #endif
317
318 switch (pNmHdr->code)
319 {
320 case LVN_GETDISPINFOW:
321 {
322 NMLVDISPINFOW *pNmLvDispInfo = (NMLVDISPINFOW *)pNmHdr;
323 #if DEBUG
324 Log (LOG_BABBLE) << "LVN_GETDISPINFO " << pNmLvDispInfo->item.iItem << endLog;
325 #endif
326 if (contents)
327 {
328 int iRow = pNmLvDispInfo->item.iItem;
329 int iCol = pNmLvDispInfo->item.iSubItem;
330
331 static StringCache<std::wstring> s;
332 s = (*contents)[iRow]->get_text(iCol);
333 pNmLvDispInfo->item.pszText = s;
334
335 if (pNmLvDispInfo->item.iSubItem == 0)
336 {
337 pNmLvDispInfo->item.iImage = (int)((*contents)[pNmLvDispInfo->item.iItem]->get_state());
338 }
339 }
340
341 return true;
342 }
343 break;
344
345 case LVN_GETEMPTYMARKUP:
346 {
347 NMLVEMPTYMARKUP *pNmMarkup = (NMLVEMPTYMARKUP*) pNmHdr;
348
349 MultiByteToWideChar(CP_UTF8, 0,
350 empty_list_text, -1,
351 pNmMarkup->szMarkup, L_MAX_URL_LENGTH);
352
353 *pResult = true;
354 return true;
355 }
356 break;
357
358 case NM_CLICK:
359 {
360 NMITEMACTIVATE *pNmItemAct = (NMITEMACTIVATE *) pNmHdr;
361 #if DEBUG
362 Log (LOG_BABBLE) << "NM_CLICK: pnmitem->iItem " << pNmItemAct->iItem << " pNmItemAct->iSubItem " << pNmItemAct->iSubItem << endLog;
363 #endif
364 int iRow = pNmItemAct->iItem;
365 int iCol = pNmItemAct->iSubItem;
366 if (iRow < 0)
367 return false;
368
369 int update = 0;
370
371 if (headers[iCol].type == ListView::ControlType::popup)
372 {
373 POINT p;
374 GetCursorPos(&p);
375
376 RECT r;
377 ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r);
378 POINT cp = p;
379 ::ScreenToClient(hWndListView, &cp);
380
381 // if the click isn't over the pop-up button, do nothing yet (but this
382 // might be followed by a NM_DBLCLK)
383 if (cp.x < r.right - GetSystemMetrics(SM_CXVSCROLL))
384 return true;
385
386 // position pop-up menu at the location of the click
387 update = popup_menu(iRow, iCol, p);
388 }
389 else
390 {
391 // Inform the item of the click
392 update = (*contents)[iRow]->do_action(iCol, 0);
393 }
394
395 // Update items, if needed
396 if (update > 0)
397 {
398 ListView_RedrawItems(hWndListView, iRow, iRow + update -1);
399 }
400
401 return true;
402 }
403 break;
404
405 case NM_DBLCLK:
406 {
407 NMITEMACTIVATE *pNmItemAct = (NMITEMACTIVATE *) pNmHdr;
408 #if DEBUG
409 Log (LOG_BABBLE) << "NM_DBLCLICK: pnmitem->iItem " << pNmItemAct->iItem << " pNmItemAct->iSubItem " << pNmItemAct->iSubItem << endLog;
410 #endif
411 int iRow = pNmItemAct->iItem;
412 int iCol = pNmItemAct->iSubItem;
413 if (iRow < 0)
414 return false;
415
416 int update = 0;
417
418 // Inform the item of the double-click
419 update = (*contents)[iRow]->do_default_action(iCol );
420
421 // Update items, if needed
422 if (update > 0)
423 {
424 ListView_RedrawItems(hWndListView, iRow, iRow + update -1);
425 }
426
427 return true;
428 }
429 break;
430
431 case NM_CUSTOMDRAW:
432 {
433 NMLVCUSTOMDRAW *pNmLvCustomDraw = (NMLVCUSTOMDRAW *)pNmHdr;
434
435 switch(pNmLvCustomDraw->nmcd.dwDrawStage)
436 {
437 case CDDS_PREPAINT:
438 *pResult = CDRF_NOTIFYITEMDRAW;
439 return true;
440 case CDDS_ITEMPREPAINT:
441 *pResult = CDRF_NOTIFYSUBITEMDRAW;
442 return true;
443 case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
444 {
445 LRESULT result = CDRF_DODEFAULT;
446 int iCol = pNmLvCustomDraw->iSubItem;
447 int iRow = pNmLvCustomDraw->nmcd.dwItemSpec;
448
449 switch (headers[iCol].type)
450 {
451 default:
452 case ListView::ControlType::text:
453 result = CDRF_DODEFAULT;
454 break;
455
456 case ListView::ControlType::checkbox:
457 {
458 // get the subitem text (as ASCII)
459 char buf[3];
460 ListView_GetItemText(hWndListView, iRow, iCol, buf, _countof(buf));
461
462 // map the subitem text to a checkbox state
463 UINT state = DFCS_BUTTONCHECK | DFCS_FLAT;
464 if (buf[0] == '\0') // empty
465 {
466 result = CDRF_DODEFAULT;
467 break;
468 }
469 else if (buf[0] == 'y') // yes
470 state |= DFCS_CHECKED;
471 else if ((buf[0] == 'n') && (buf[1] == 'o')) // no
472 state |= 0;
473 else // n/a
474 state |= DFCS_INACTIVE;
475
476 // erase and draw a checkbox
477 RECT r;
478 ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r);
479 DWORD bkg_color;
480 if (pNmLvCustomDraw->nmcd.uItemState & CDIS_SELECTED)
481 bkg_color = GetSysColor(COLOR_HIGHLIGHT);
482 else
483 bkg_color = ListView_GetBkColor(hWndListView);
484 HBRUSH hBrush = CreateSolidBrush(bkg_color);
485 FillRect(pNmLvCustomDraw->nmcd.hdc, &r, hBrush);
486 DeleteObject(hBrush);
487 DrawFrameControl(pNmLvCustomDraw->nmcd.hdc, &r, DFC_BUTTON, state);
488
489 result = CDRF_SKIPDEFAULT;
490 }
491 break;
492
493 case ListView::ControlType::popup:
494 {
495 // let the control draw the text, but notify us afterwards
496 result = CDRF_NOTIFYPOSTPAINT;
497 }
498 break;
499 }
500
501 *pResult = result;
502 return true;
503 }
504 case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT:
505 {
506 LRESULT result = CDRF_DODEFAULT;
507 int iCol = pNmLvCustomDraw->iSubItem;
508 int iRow = pNmLvCustomDraw->nmcd.dwItemSpec;
509
510 switch (headers[iCol].type)
511 {
512 default:
513 result = CDRF_DODEFAULT;
514 break;
515
516 case ListView::ControlType::popup:
517 {
518 // draw the control at the RHS of the cell
519 RECT r;
520 ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r);
521 r.left = r.right - GetSystemMetrics(SM_CXVSCROLL);
522 DrawFrameControl(pNmLvCustomDraw->nmcd.hdc, &r, DFC_SCROLL,DFCS_SCROLLCOMBOBOX);
523
524 result = CDRF_DODEFAULT;
525 }
526 break;
527 }
528 *pResult = result;
529 return true;
530 }
531 }
532 }
533 break;
534
535 case LVN_HOTTRACK:
536 {
537 NMLISTVIEW *pNmListView = (NMLISTVIEW *)pNmHdr;
538 int iRow = pNmListView->iItem;
539 int iCol = pNmListView->iSubItem;
540 #if DEBUG
541 Log (LOG_BABBLE) << "LVN_HOTTRACK " << iRow << " " << iCol << endLog;
542 #endif
543 if (iRow < 0)
544 return true;
545
546 // if we've tracked off to a different cell
547 if ((iRow != iRow_track) || (iCol != iCol_track))
548 {
549 #if DEBUG
550 Log (LOG_BABBLE) << "LVN_HOTTRACK changed cell" << endLog;
551 #endif
552
553 // if the tooltip for previous cell is displayed, remove it
554 // restart the tooltip AUTOPOP timer for this cell
555 SendMessage(hWndTip, TTM_ACTIVATE, FALSE, 0);
556 SendMessage(hWndTip, TTM_ACTIVATE, TRUE, 0);
557
558 iRow_track = iRow;
559 iCol_track = iCol;
560 }
561
562 return true;
563 }
564 break;
565
566 case LVN_KEYDOWN:
567 {
568 NMLVKEYDOWN *pNmLvKeyDown = (NMLVKEYDOWN *)pNmHdr;
569 int iRow = ListView_GetSelectionMark(hWndListView);
570 #if DEBUG
571 Log (LOG_PLAIN) << "LVN_KEYDOWN vkey " << pNmLvKeyDown->wVKey << " on row " << iRow << endLog;
572 #endif
573
574 if (contents && iRow >= 0)
575 {
576 int col_num;
577 int action_id;
578 if ((*contents)[iRow]->map_key_to_action(pNmLvKeyDown->wVKey, &col_num, &action_id))
579 {
580 int update;
581 if (action_id >= 0)
582 update = (*contents)[iRow]->do_action(col_num, action_id);
583 else
584 {
585 POINT p;
586 RECT r;
587 ListView_GetSubItemRect(hWndListView, iRow, col_num, LVIR_BOUNDS, &r);
588 p.x = r.left;
589 p.y = r.top;
590 ClientToScreen(hWndListView, &p);
591
592 update = popup_menu(iRow, col_num, p);
593 }
594
595 if (update > 0)
596 ListView_RedrawItems(hWndListView, iRow, iRow + update -1);
597 }
598 }
599 }
600 break;
601
602 case TTN_GETDISPINFO:
603 {
604 // convert mouse position to item/subitem
605 LVHITTESTINFO lvHitTestInfo;
606 lvHitTestInfo.flags = LVHT_ONITEM;
607 GetCursorPos(&lvHitTestInfo.pt);
608 ::ScreenToClient(hWndListView, &lvHitTestInfo.pt);
609 ListView_SubItemHitTest(hWndListView, &lvHitTestInfo);
610
611 int iRow = lvHitTestInfo.iItem;
612 int iCol = lvHitTestInfo.iSubItem;
613 if (iRow < 0)
614 return false;
615
616 #if DEBUG
617 Log (LOG_BABBLE) << "TTN_GETDISPINFO " << iRow << " " << iCol << endLog;
618 #endif
619
620 // get the tooltip text for that item/subitem
621 static StringCache<std::string> tooltip;
622 tooltip = "";
623 if (contents)
624 tooltip = (*contents)[iRow]->get_tooltip(iCol);
625
626 // set the tooltip text
627 NMTTDISPINFO *pNmTTDispInfo = (NMTTDISPINFO *)pNmHdr;
628 pNmTTDispInfo->lpszText = tooltip;
629 pNmTTDispInfo->hinst = NULL;
630 pNmTTDispInfo->uFlags = 0;
631
632 return true;
633 }
634 break;
635 }
636
637 // We don't care.
638 return false;
639 }
640
641 void
642 ListView::empty(void)
643 {
644 ListView_DeleteAllItems(hWndListView);
645 }
646
647 void
648 ListView::setEmptyText(const char *text)
649 {
650 empty_list_text = text;
651 }
652
653 int
654 ListView::popup_menu(int iRow, int iCol, POINT p)
655 {
656 int update = 0;
657 // construct menu
658 HMENU hMenu = CreatePopupMenu();
659
660 MENUITEMINFOW mii;
661 memset(&mii, 0, sizeof(mii));
662 mii.cbSize = sizeof(mii);
663 mii.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_STRING | MIIM_ID;
664 mii.fType = MFT_STRING;
665
666 ActionList *al = (*contents)[iRow]->get_actions(iCol);
667
668 Actions::iterator i;
669 int j = 1;
670 for (i = al->list.begin (); i != al->list.end (); ++i, ++j)
671 {
672 BOOL res;
673 mii.dwTypeData = const_cast <wchar_t *> (i->name.c_str());
674 mii.fState = (i->selected ? MFS_CHECKED : MFS_UNCHECKED |
675 i->enabled ? MFS_ENABLED : MFS_DISABLED);
676 mii.wID = j;
677
678 res = InsertMenuItemW(hMenu, -1, TRUE, &mii);
679 if (!res) Log (LOG_BABBLE) << "InsertMenuItem failed " << endLog;
680 }
681
682 int id = TrackPopupMenu(hMenu,
683 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_NOANIMATION,
684 p.x, p.y, 0, hWndListView, NULL);
685
686 // Inform the item of the menu choice
687 if (id)
688 update = (*contents)[iRow]->do_action(iCol, al->list[id-1].id);
689
690 DestroyMenu(hMenu);
691 delete al;
692
693 return update;
694 }
This page took 0.068039 seconds and 6 git commands to generate.