]> cygwin.com Git - cygwin-apps/setup.git/blobdiff - ListView.cc
Suppress bogus free-nonheap-object warning in iniparse.cc
[cygwin-apps/setup.git] / ListView.cc
index a555caafa70fba16bebbce706a22707c84309c5d..62a37ab173b6bf0bec3a130378425c3dd6e667fb 100644 (file)
@@ -13,6 +13,8 @@
 
 #include "ListView.h"
 #include "LogSingleton.h"
+#include "resource.h"
+#include "String++.h"
 
 #include <commctrl.h>
 
@@ -54,6 +56,49 @@ ListView::init(HWND parent, int id, HeaderList headers)
 
   // populate with columns
   initColumns(headers);
+
+  // create a small icon imagelist
+  // (the order of images matches ListViewLine::State enum)
+  hImgList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
+                              GetSystemMetrics(SM_CYSMICON),
+                              ILC_COLOR32, 2, 0);
+  ImageList_AddIcon(hImgList, LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_TREE_PLUS)));
+  ImageList_AddIcon(hImgList, LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_TREE_MINUS)));
+
+  // create an empty imagelist, used to reset the indent
+  hEmptyImgList = ImageList_Create(1, 1,
+                                   ILC_COLOR32, 2, 0);
+
+  // LVS_EX_INFOTIP/LVN_GETINFOTIP doesn't work for subitems, so we have to do
+  // our own tooltip handling
+  hWndTip = CreateWindowEx (0,
+                            (LPCTSTR) TOOLTIPS_CLASS,
+                            NULL,
+                            WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+                            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+                            hWndParent,
+                            (HMENU) 0,
+                            GetModuleHandle(NULL),
+                            NULL);
+  // must be topmost so that tooltips will display on top
+  SetWindowPos(hWndTip, HWND_TOPMOST,0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+  TOOLINFO ti;
+  memset ((void *)&ti, 0, sizeof(ti));
+  ti.cbSize = sizeof(ti);
+  ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+  ti.hwnd = hWndParent;
+  ti.uId = (UINT_PTR)hWndListView;
+  ti.lpszText =  LPSTR_TEXTCALLBACK; // use TTN_GETDISPINFO
+  SendMessage(hWndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+  // match long delay for tooltip to disappear used elsewhere (30s)
+  SendMessage(hWndTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM) MAKELONG (30000, 0));
+  // match tip width used elsewhere
+  SendMessage(hWndTip, TTM_SETMAXTIPWIDTH, 0, 450);
+
+  // switch to using wide-char WM_NOTIFY messages
+  ListView_SetUnicodeFormat(hWndListView, TRUE);
 }
 
 void
@@ -63,18 +108,20 @@ ListView::initColumns(HeaderList headers_)
   headers = headers_;
 
   // create the columns
-  LVCOLUMN lvc;
+  LVCOLUMNW lvc;
   lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
 
   int i;
   for (i = 0; headers[i].text != 0; i++)
     {
+      std::wstring h = LoadStringW(headers[i].text);
+
       lvc.iSubItem = i;
-      lvc.pszText = const_cast <char *> (headers[i].text);
+      lvc.pszText = const_cast <wchar_t *> (h.c_str());
       lvc.cx = 100;
       lvc.fmt = headers[i].fmt;
 
-      ListView_InsertColumn(hWndListView, i, &lvc);
+      SendMessage(hWndListView, LVM_INSERTCOLUMNW, i, (LPARAM)&lvc);
     }
 
   // now do some width calculations
@@ -104,8 +151,23 @@ ListView::noteColumnWidthStart()
     }
 }
 
+// wrappers to help instantiations of the noteColumnWidth() template call the
+// right version of GetTextExtentPoint32
+#undef GetTextExtentPoint32
+
+static BOOL GetTextExtentPoint32(HDC hdc, LPCSTR lpString, int c, LPSIZE psizl)
+{
+  return GetTextExtentPoint32A(hdc, lpString, c, psizl);
+}
+
+static BOOL GetTextExtentPoint32(HDC hdc, LPCWSTR lpString, int c, LPSIZE psizl)
+{
+  return GetTextExtentPoint32W(hdc, lpString, c, psizl);
+}
+
+template <typename T>
 void
-ListView::noteColumnWidth(int col_num, const std::string& string)
+ListView::noteColumnWidth(int col_num, const T& string)
 {
   SIZE s = { 0, 0 };
 
@@ -118,10 +180,20 @@ ListView::noteColumnWidth(int col_num, const std::string& string)
 
   int width = addend + s.cx;
 
+  // allow for width of dropdown button in popup columns
+  if (headers[col_num].type == ListView::ControlType::popup)
+    {
+      width += GetSystemMetrics(SM_CXVSCROLL);
+    }
+
   if (width > headers[col_num].width)
     headers[col_num].width = width;
 }
 
+// explicit instantiation
+template void ListView::noteColumnWidth(int col_num, const std::string& string);
+template void ListView::noteColumnWidth(int col_num, const std::wstring& wstring);
+
 void
 ListView::noteColumnWidthEnd()
 {
@@ -162,7 +234,7 @@ ListView::resizeColumns(void)
 }
 
 void
-ListView::setContents(ListViewContents *_contents)
+ListView::setContents(ListViewContents *_contents, bool tree)
 {
   contents = _contents;
 
@@ -175,14 +247,25 @@ ListView::setContents(ListViewContents *_contents)
 
   empty();
 
+  // assign imagelist to listview control (this also sets the size for indents)
+  if (tree)
+    ListView_SetImageList(hWndListView, hImgList, LVSIL_SMALL);
+  else
+    ListView_SetImageList(hWndListView, hEmptyImgList, LVSIL_SMALL);
+
   size_t i;
   for (i = 0; i < contents->size();  i++)
     {
       LVITEM lvi;
-      lvi.mask = LVIF_TEXT;
+      lvi.mask = LVIF_TEXT | (tree ? LVIF_IMAGE | LVIF_INDENT : 0);
       lvi.iItem = i;
       lvi.iSubItem = 0;
       lvi.pszText = LPSTR_TEXTCALLBACK;
+      if (tree)
+        {
+          lvi.iImage = I_IMAGECALLBACK;
+          lvi.iIndent = (*contents)[i]->get_indent();
+        }
 
       ListView_InsertItem(hWndListView, &lvi);
     }
@@ -198,29 +281,30 @@ ListView::setContents(ListViewContents *_contents)
   RedrawWindow(hWndListView, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
 }
 
-// Helper class: The char * pointer we hand back needs to remain valid for some
-// time after OnNotify returns, when the std::string we have retrieved has gone
-// out of scope, so a static instance of this class maintains a local cache.
-class StringCache
+// Helper class: The pointer we hand back needs to remain valid for some time
+// after OnNotify returns, when the string object we have retrieved has gone out
+// of scope, so a static instance of this class maintains a local cache.
+template <class T> class StringCache
 {
+  typedef typename T::traits_type::char_type char_type;
 public:
   StringCache() : cache(NULL), cache_size(0) { }
-  StringCache & operator = (const std::string & s)
+  StringCache & operator = (const T & s)
   {
     if ((s.length() + 1) > cache_size)
       {
         cache_size = s.length() + 1;
-        cache = (char *)realloc(cache, cache_size);
+        cache = (char_type *)realloc(cache, cache_size * sizeof(char_type));
       }
-    strcpy(cache, s.c_str());
+    memcpy(cache, s.c_str(), (s.length() + 1) * sizeof(char_type));
     return *this;
   }
-  operator char *() const
+  operator char_type *() const
   {
     return cache;
   }
 private:
-  char *cache;
+  char_type *cache;
   size_t cache_size;
 };
 
@@ -233,9 +317,9 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
 
   switch (pNmHdr->code)
   {
-  case LVN_GETDISPINFO:
+  case LVN_GETDISPINFOW:
     {
-      NMLVDISPINFO *pNmLvDispInfo = (NMLVDISPINFO *)pNmHdr;
+      NMLVDISPINFOW *pNmLvDispInfo = (NMLVDISPINFOW *)pNmHdr;
 #if DEBUG
       Log (LOG_BABBLE) << "LVN_GETDISPINFO " << pNmLvDispInfo->item.iItem << endLog;
 #endif
@@ -244,9 +328,14 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
           int iRow = pNmLvDispInfo->item.iItem;
           int iCol = pNmLvDispInfo->item.iSubItem;
 
-          static StringCache s;
+          static StringCache<std::wstring> s;
           s = (*contents)[iRow]->get_text(iCol);
           pNmLvDispInfo->item.pszText = s;
+
+          if (pNmLvDispInfo->item.iSubItem == 0)
+            {
+              pNmLvDispInfo->item.iImage = (int)((*contents)[pNmLvDispInfo->item.iItem]->get_state());
+            }
         }
 
       return true;
@@ -256,11 +345,7 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
   case LVN_GETEMPTYMARKUP:
     {
       NMLVEMPTYMARKUP *pNmMarkup = (NMLVEMPTYMARKUP*) pNmHdr;
-
-      MultiByteToWideChar(CP_UTF8, 0,
-                          empty_list_text, -1,
-                          pNmMarkup->szMarkup, L_MAX_URL_LENGTH);
-
+      wcsncpy(pNmMarkup->szMarkup, empty_list_text.c_str(), L_MAX_URL_LENGTH);
       *pResult = true;
       return true;
     }
@@ -282,9 +367,19 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
       if (headers[iCol].type == ListView::ControlType::popup)
         {
           POINT p;
-          // position pop-up menu at the location of the click
           GetCursorPos(&p);
 
+          RECT r;
+          ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r);
+          POINT cp = p;
+          ::ScreenToClient(hWndListView, &cp);
+
+          // if the click isn't over the pop-up button, do nothing yet (but this
+          // might be followed by a NM_DBLCLK)
+          if (cp.x < r.right - GetSystemMetrics(SM_CXVSCROLL))
+            return true;
+
+          // position pop-up menu at the location of the click
           update = popup_menu(iRow, iCol, p);
         }
       else
@@ -303,6 +398,32 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
     }
     break;
 
+  case NM_DBLCLK:
+    {
+      NMITEMACTIVATE *pNmItemAct = (NMITEMACTIVATE *) pNmHdr;
+#if DEBUG
+      Log (LOG_BABBLE) << "NM_DBLCLICK: pnmitem->iItem " << pNmItemAct->iItem << " pNmItemAct->iSubItem " << pNmItemAct->iSubItem << endLog;
+#endif
+      int iRow = pNmItemAct->iItem;
+      int iCol = pNmItemAct->iSubItem;
+      if (iRow < 0)
+        return false;
+
+      int update = 0;
+
+      // Inform the item of the double-click
+      update = (*contents)[iRow]->do_default_action(iCol );
+
+      // Update items, if needed
+      if (update > 0)
+        {
+          ListView_RedrawItems(hWndListView, iRow, iRow + update -1);
+        }
+
+      return true;
+    }
+    break;
+
   case NM_CUSTOMDRAW:
     {
       NMLVCUSTOMDRAW *pNmLvCustomDraw = (NMLVCUSTOMDRAW *)pNmHdr;
@@ -330,7 +451,7 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
 
               case ListView::ControlType::checkbox:
                 {
-                  // get the subitem text
+                  // get the subitem text (as ASCII)
                   char buf[3];
                   ListView_GetItemText(hWndListView, iRow, iCol, buf, _countof(buf));
 
@@ -351,7 +472,12 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
                   // erase and draw a checkbox
                   RECT r;
                   ListView_GetSubItemRect(hWndListView, iRow, iCol, LVIR_BOUNDS, &r);
-                  HBRUSH hBrush = CreateSolidBrush(ListView_GetBkColor(hWndListView));
+                  DWORD bkg_color;
+                  if (pNmLvCustomDraw->nmcd.uItemState & CDIS_SELECTED)
+                    bkg_color = GetSysColor(COLOR_HIGHLIGHT);
+                  else
+                    bkg_color = ListView_GetBkColor(hWndListView);
+                  HBRUSH hBrush = CreateSolidBrush(bkg_color);
                   FillRect(pNmLvCustomDraw->nmcd.hdc, &r, hBrush);
                   DeleteObject(hBrush);
                   DrawFrameControl(pNmLvCustomDraw->nmcd.hdc, &r, DFC_BUTTON, state);
@@ -400,6 +526,108 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
           }
         }
     }
+    break;
+
+  case LVN_HOTTRACK:
+    {
+      NMLISTVIEW *pNmListView = (NMLISTVIEW *)pNmHdr;
+      int iRow = pNmListView->iItem;
+      int iCol = pNmListView->iSubItem;
+#if DEBUG
+      Log (LOG_BABBLE) << "LVN_HOTTRACK " << iRow << " " << iCol << endLog;
+#endif
+      if (iRow < 0)
+        return true;
+
+      // if we've tracked off to a different cell
+      if ((iRow != iRow_track) || (iCol != iCol_track))
+        {
+#if DEBUG
+          Log (LOG_BABBLE) << "LVN_HOTTRACK changed cell" << endLog;
+#endif
+
+          // if the tooltip for previous cell is displayed, remove it
+          // restart the tooltip AUTOPOP timer for this cell
+          SendMessage(hWndTip, TTM_ACTIVATE, FALSE, 0);
+          SendMessage(hWndTip, TTM_ACTIVATE, TRUE, 0);
+
+          iRow_track = iRow;
+          iCol_track = iCol;
+        }
+
+      return true;
+    }
+    break;
+
+  case LVN_KEYDOWN:
+    {
+      NMLVKEYDOWN *pNmLvKeyDown = (NMLVKEYDOWN *)pNmHdr;
+      int iRow = ListView_GetSelectionMark(hWndListView);
+#if DEBUG
+      Log (LOG_PLAIN) << "LVN_KEYDOWN vkey " << pNmLvKeyDown->wVKey << " on row " << iRow << endLog;
+#endif
+
+      if (contents && iRow >= 0)
+        {
+          int col_num;
+          int action_id;
+          if ((*contents)[iRow]->map_key_to_action(pNmLvKeyDown->wVKey, &col_num, &action_id))
+            {
+              int update;
+              if (action_id >= 0)
+                update = (*contents)[iRow]->do_action(col_num, action_id);
+              else
+                {
+                  POINT p;
+                  RECT r;
+                  ListView_GetSubItemRect(hWndListView, iRow, col_num, LVIR_BOUNDS, &r);
+                  p.x = r.left;
+                  p.y = r.top;
+                  ClientToScreen(hWndListView, &p);
+
+                  update = popup_menu(iRow, col_num, p);
+                }
+
+              if (update > 0)
+                ListView_RedrawItems(hWndListView, iRow, iRow + update -1);
+            }
+        }
+    }
+    break;
+
+  case TTN_GETDISPINFO:
+    {
+      // convert mouse position to item/subitem
+      LVHITTESTINFO lvHitTestInfo;
+      lvHitTestInfo.flags = LVHT_ONITEM;
+      GetCursorPos(&lvHitTestInfo.pt);
+      ::ScreenToClient(hWndListView, &lvHitTestInfo.pt);
+      ListView_SubItemHitTest(hWndListView, &lvHitTestInfo);
+
+      int iRow = lvHitTestInfo.iItem;
+      int iCol = lvHitTestInfo.iSubItem;
+      if (iRow < 0)
+        return false;
+
+#if DEBUG
+      Log (LOG_BABBLE) << "TTN_GETDISPINFO " << iRow << " " << iCol << endLog;
+#endif
+
+      // get the tooltip text for that item/subitem
+      static StringCache<std::string> tooltip;
+      tooltip = "";
+      if (contents)
+        tooltip = (*contents)[iRow]->get_tooltip(iCol);
+
+      // set the tooltip text
+      NMTTDISPINFO *pNmTTDispInfo = (NMTTDISPINFO *)pNmHdr;
+      pNmTTDispInfo->lpszText = tooltip;
+      pNmTTDispInfo->hinst = NULL;
+      pNmTTDispInfo->uFlags = 0;
+
+      return true;
+    }
+    break;
   }
 
   // We don't care.
@@ -413,9 +641,9 @@ ListView::empty(void)
 }
 
 void
-ListView::setEmptyText(const char *text)
+ListView::setEmptyText(unsigned int text)
 {
-  empty_list_text = text;
+  empty_list_text = LoadStringW(text);
 }
 
 int
@@ -425,7 +653,7 @@ ListView::popup_menu(int iRow, int iCol, POINT p)
   // construct menu
   HMENU hMenu = CreatePopupMenu();
 
-  MENUITEMINFO mii;
+  MENUITEMINFOW mii;
   memset(&mii, 0, sizeof(mii));
   mii.cbSize = sizeof(mii);
   mii.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_STRING | MIIM_ID;
@@ -438,12 +666,12 @@ ListView::popup_menu(int iRow, int iCol, POINT p)
   for (i = al->list.begin (); i != al->list.end (); ++i, ++j)
     {
       BOOL res;
-      mii.dwTypeData = (char *)i->name.c_str();
+      mii.dwTypeData = const_cast <wchar_t *> (i->name.c_str());
       mii.fState = (i->selected ? MFS_CHECKED : MFS_UNCHECKED |
                     i->enabled ? MFS_ENABLED : MFS_DISABLED);
       mii.wID = j;
 
-      res = InsertMenuItem(hMenu, -1, TRUE, &mii);
+      res = InsertMenuItemW(hMenu, -1, TRUE, &mii);
       if (!res) Log (LOG_BABBLE) << "InsertMenuItem failed " << endLog;
     }
 
This page took 0.03251 seconds and 5 git commands to generate.