Listview's built-in tooltip support doesn't support subitems, so we have to
build our own

v2: Use string cache
---
 ListView.cc         | 94 +++++++++++++++++++++++++++++++++++++++++++++
 ListView.h          |  4 ++
 PickCategoryLine.cc |  6 +++
 PickCategoryLine.h  |  1 +
 PickPackageLine.cc  | 11 ++++++
 PickPackageLine.h   |  1 +
 6 files changed, 117 insertions(+)

diff --git a/ListView.cc b/ListView.cc
index b0351cd..e287270 100644
--- a/ListView.cc
+++ b/ListView.cc
@@ -67,6 +67,34 @@ ListView::init(HWND parent, int id, HeaderList headers)
   // 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);
 }
 
 void
@@ -429,6 +457,72 @@ 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 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 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.
diff --git a/ListView.h b/ListView.h
index 34d6267..b4fd1fd 100644
--- a/ListView.h
+++ b/ListView.h
@@ -33,6 +33,7 @@ class ListViewLine
   virtual ~ListViewLine() {};
   virtual const std::string get_text(int col) const = 0;
   virtual State get_state() const = 0;
+  virtual const std::string get_tooltip(int col) const = 0;
   virtual int get_indent() const = 0;
   virtual ActionList *get_actions(int col) const = 0;
   virtual int do_action(int col, int id) = 0;
@@ -76,6 +77,7 @@ class ListView
  private:
   HWND hWndParent;
   HWND hWndListView;
+  HWND hWndTip;
   HDC dc;
   HIMAGELIST hImgList;
   HIMAGELIST hEmptyImgList;
@@ -83,6 +85,8 @@ class ListView
   ListViewContents *contents;
   HeaderList headers;
   const char *empty_list_text;
+  int iRow_track;
+  int iCol_track;
 
   void initColumns(HeaderList hl);
   void empty(void);
diff --git a/PickCategoryLine.cc b/PickCategoryLine.cc
index e206aee..1dbecf2 100644
--- a/PickCategoryLine.cc
+++ b/PickCategoryLine.cc
@@ -82,3 +82,9 @@ PickCategoryLine::get_indent() const
 {
   return indent;
 }
+
+const std::string
+PickCategoryLine::get_tooltip(int col_num) const
+{
+  return "";
+}
diff --git a/PickCategoryLine.h b/PickCategoryLine.h
index a5daa8c..9c5e996 100644
--- a/PickCategoryLine.h
+++ b/PickCategoryLine.h
@@ -36,6 +36,7 @@ public:
 
   const std::string get_text(int col) const;
   State get_state() const;
+  const std::string get_tooltip(int col) const;
   int get_indent() const;
   ActionList *get_actions(int col) const;
   int do_action(int col, int action_id);
diff --git a/PickPackageLine.cc b/PickPackageLine.cc
index a602c90..133720b 100644
--- a/PickPackageLine.cc
+++ b/PickPackageLine.cc
@@ -99,6 +99,17 @@ PickPackageLine::get_text(int col_num) const
   return "unknown";
 }
 
+const std::string
+PickPackageLine::get_tooltip(int col_num) const
+{
+  if (col_num == pkg_col)
+    {
+      return pkg.LDesc();
+    }
+
+  return "";
+}
+
 int
 PickPackageLine::do_action(int col_num, int action_id)
 {
diff --git a/PickPackageLine.h b/PickPackageLine.h
index 53c389b..12c7636 100644
--- a/PickPackageLine.h
+++ b/PickPackageLine.h
@@ -32,6 +32,7 @@ public:
   };
   const std::string get_text(int col) const;
   State get_state() const { return State::nothing; }
+  const std::string get_tooltip(int col) const;
   int get_indent() const;
   ActionList *get_actions(int col_num) const;
   int do_action(int col, int action_id);
-- 
2.17.0

Reply via email to