How to change icon for a some files of file type? - visual-c++

I have IconHandler to change icon for some files. But other files icons becomes blank. How to leave default icon for other files?
HRESULT CSimpleShlExt::GetIconLocation(UINT uFlags,
PTSTR pszIconFile,
UINT cchMax,
int *piIndex,
UINT *pwFlags)
{
if (condition)){
// works well
lstrcpyn(pszIconFile, L"C:\\Windows\\System32\\shell32.dll", cchMax);
*piIndex = 5;
*pwFlags = 0;
} else {
// blank icon :(
*pwFlags = GIL_PERINSTANCE | GIL_NOTFILENAME;// | GIL_DONTCACHE ;
}
return S_OK;
}
Here is my .rgs file:
HKCR
{
NoRemove CLSID
{
ForceRemove {B70B7A24-5180-4092-B3BA-6266F914C053} = s 'My Shell Extension'
{
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}
TypeLib = s '{62C6D1EB-C172-4E05-BFD2-5F9313832CC3}'
Version = s '1.0'
}
}
NoRemove txtfile
{
NoRemove ShellEx
{
ForceRemove IconHandler = s '{B70B7A24-5180-4092-B3BA-6266F914C053}'
}
}
}

This code works:
HRESULT CSimpleShlExt::GetIconLocation(UINT uFlags,
PTSTR pszIconFile,
UINT cchMax,
int *piIndex,
UINT *pwFlags)
{
if (condition))
{
lstrcpyn(pszIconFile, L"C:\\Windows\\System32\\Test.dll", cchMax);
*piIndex = 0;
}
else
{
*piIndex = 1;
}
*pwFlags = 0;
return S_OK;
}
HRESULT CSimpleShlExt::Extract(
LPCTSTR pszFile,
UINT nIconIndex,
HICON *phiconLarge,
HICON *phiconSmall,
UINT nIconSize)
{
return S_FALSE;
}
Changed .rgs file:
HKCR
{
NoRemove CLSID
{
ForceRemove {B70B7A24-5180-4092-B3BA-6266F914C053} = s 'My Shell Extension'
{
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}
TypeLib = s '{62C6D1EB-C172-4E05-BFD2-5F9313832CC3}'
Version = s '1.0'
}
}
NoRemove txtfile
{
NoRemove DefaultIcon = s '%%1'
NoRemove ShellEx
{
ForceRemove IconHandler = s '{B70B7A24-5180-4092-B3BA-6266F914C053}'
}
}
}
From 'MSDN' - How to Create Icon Handlers:
Registering Icon Handlers
When you statically register an icon for a file type, you create a
DefaultIcon subkey under the ProgID for the file type. Its default
value is set to the file that contains the icon. To register an icon
handler, you must still have the DefaultIcon subkey, but its default
value must be set to "%1".

You could do this by passing through a dummy name to SHGetFileInfo. For example,
HRESULT CSimpleShlExt::GetIconLocation(UINT uFlags,
PTSTR pszIconFile,
UINT cchMax,
int *piIndex,
UINT *pwFlags)
{
if (condition){
// works well
lstrcpyn(pszIconFile, L"C:\\Windows\\System32\\shell32.dll", cchMax);
*piIndex = 5;
*pwFlags = 0;
} else {
SHFILEINFO sfi;
SHGetFileInfo(L"dummy", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi),
SHGFI_ICONLOCATION | SHGFI_USEFILEATTRIBUTES);
StringCchCopy(pszIconFile, cchMax, sfi.szDisplayName);
*piIndex = sfi.iIcon;
*pwFlags = 0;
}
return S_OK;
}
The key is to pass the SHGFI_USEFILEATTRIBUTES flag, which means the filename you provide does not need to refer to a real file.
Providing a filename without a file extension (as in the above example) will mean you get back the system's default file icon.
And finally the SHGFI_ICONLOCATION flag returns the icon path and index in the fields of the SHFILEINFO structure.

Related

When MFC (Feature Pack) calling for CDockablePane::Serialize()?

Does CDockablePane::Serialize() method is calling from MFC Feature Pack core?
I have dockable window class that inherited from CDockablePane class. My class override virtual Serialize() method and declared as serial DECLARE_SERIAL/IMPLEMENT_SERIAL. But MFC does not call my Serialize() method! Why ?
MSDN say that CDockablePane class have serialization methods: SaveState(), LoadState() and Serialize(). First two (SaveState(), LoadState()) are used internally and Serialize() used for "serializes the pane". But it is not calling!
No, the Serialize() method is not called by MFC framework automatically. You have to call it manually usually from CDocument::Serialize(). LoadState() and SaveState() methods are used to save/restore pane position and state. They do use Registry as storage.
As for DECLARE_SERIAL and IMPLEMENT_SERIAL they are used to support operator>> and other CArchive-specific things plus runtime class information. So you'll be able to use BOOL IsKindOf(RUNTIME_CLASS(...)) automagically.
My answer proposition. My class CSerializableDockablePane performs call for Serialize method when this docked pane is created or stored by framework (framework calls methods LoadState() and SaveState()).
You can use CSerializableDockablePane as base class for your dockable pane and overwrite virtual Serialize() method for your need.
Code:
class CSerializableDockablePane
: public CDockablePane
{
DECLARE_SERIAL(CSerializableDockablePane)
public:
typedef CDockablePane TBase;
public:
CSerializableDockablePane();
virtual ~CSerializableDockablePane();
virtual BOOL LoadState(LPCTSTR lpszProfileName = NULL, int nIndex = -1, UINT uiID = (UINT)-1);
virtual BOOL SaveState(LPCTSTR lpszProfileName = NULL, int nIndex = -1, UINT uiID = (UINT)-1);
virtual void Serialize(CArchive& ar);
};
/////////////////////////////////////////////////////////////////////////////
// CSerializableDockablePane
#define _MFC_DOCVIEW_PROFILE _T("DockableViews")
#define _REG_UI_DOCVIEWSECTION_FMT _T("%TsSerializableDockablePane-%d")
#define _REG_UI_DOCVIEWSECTION_FMT_EX _T("%TsSerializableDockablePane-%d%x")
#define _REG_UI_SETTINGS _T("Settings")
IMPLEMENT_SERIAL(CSerializableDockablePane, CSerializableDockablePane::TBase, VERSIONABLE_SCHEMA | 2)
CSerializableDockablePane::CSerializableDockablePane()
{
}
CSerializableDockablePane::~CSerializableDockablePane()
{
}
BOOL CSerializableDockablePane::LoadState(LPCTSTR lpszProfileName /*= NULL*/, int nIndex /*= -1*/, UINT uiID /*= (UINT)-1*/)
{
BOOL bRes = TBase::LoadState(lpszProfileName, nIndex, uiID);
if (bRes) {
const CString strProfileName = ::AFXGetRegPath(_MFC_DOCVIEW_PROFILE, lpszProfileName);
if (nIndex == -1) {
nIndex = GetDlgCtrlID();
}
CString strSection;
if (uiID == (UINT)-1) {
strSection.Format(_REG_UI_DOCVIEWSECTION_FMT, (LPCTSTR)strProfileName, nIndex);
}
else {
strSection.Format(_REG_UI_DOCVIEWSECTION_FMT_EX, (LPCTSTR)strProfileName, nIndex, uiID);
}
LPBYTE lpbData = nullptr;
UINT uiDataSize = 0;
CSettingsStoreSP regSP;
CSettingsStore& reg = regSP.Create(FALSE, TRUE);
if (!reg.Open(strSection)) {
return FALSE;
}
if (!reg.Read(_REG_UI_SETTINGS, &lpbData, &uiDataSize)) {
return FALSE;
}
try
{
CMemFile file(lpbData, uiDataSize);
CArchive ar(&file, CArchive::load);
Serialize(ar);
bRes = TRUE;
}
catch (CMemoryException* pEx)
{
pEx->Delete();
TRACE(_T("Memory exception in CSerializableDockablePane::LoadState()!\n"));
}
catch (CArchiveException* pEx)
{
pEx->Delete();
TRACE(_T("CArchiveException exception in CSerializableDockablePane::LoadState()!\n"));
}
if (lpbData != nullptr) {
delete[] lpbData;
}
}
return bRes;
}
BOOL CSerializableDockablePane::SaveState(LPCTSTR lpszProfileName /*= NULL*/, int nIndex /*= -1*/, UINT uiID /*= (UINT)-1*/)
{
BOOL bRes = TBase::SaveState(lpszProfileName, nIndex, uiID);
if (bRes) {
const CString strProfileName = ::AFXGetRegPath(_MFC_DOCVIEW_PROFILE, lpszProfileName);
if (nIndex == -1) {
nIndex = GetDlgCtrlID();
}
CString strSection;
if (uiID == (UINT)-1) {
strSection.Format(_REG_UI_DOCVIEWSECTION_FMT, (LPCTSTR)strProfileName, nIndex);
}
else {
strSection.Format(_REG_UI_DOCVIEWSECTION_FMT_EX, (LPCTSTR)strProfileName, nIndex, uiID);
}
try
{
CMemFile file;
{
CArchive ar(&file, CArchive::store);
Serialize(ar);
ar.Flush();
}
UINT uiDataSize = (UINT)file.GetLength();
LPBYTE lpbData = file.Detach();
if (lpbData != NULL)
{
CSettingsStoreSP regSP;
CSettingsStore& reg = regSP.Create(FALSE, FALSE);
if (reg.CreateKey(strSection)) {
bRes = reg.Write(_REG_UI_SETTINGS, lpbData, uiDataSize);
}
free(lpbData);
}
}
catch (CMemoryException* pEx)
{
pEx->Delete();
TRACE(_T("Memory exception in CSerializableDockablePane::SaveState()!\n"));
}
}
return bRes;
}
void CSerializableDockablePane::Serialize(CArchive& ar)
{
TBase::Serialize(ar);
}
// CSerializableDockablePane
/////////////////////////////////////////////////////////////////////////////

How to Drag and Drop multiple files between Namespace extensions

I am working on a namespace extension project, I need to implement multiple files drag and drop between Namespace Extensions. I have used DragQueryFile API to find the the number of files. But Always this Function give a crash.
Could anyone help me by explaining how we can implement this multiple files Drag and drop.
Thanks,
Robin
Here is how I use it
void yourclass::OnDropFiles(HDROP hDropInfo)
{
TCHAR lpszFile[MAX_PATH] = { 0 };
UINT uFile = 0;
uFile = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, NULL);
if (uFile != 0)
{
for (int i = 0; i < uFile; i++)
{
lpszFile[0] = '\0';
if (DragQueryFile(hDropInfo, i, lpszFile, MAX_PATH))
{
std::wstring directory;
std::wstring filename;
LVITEM lvi = { 0 };
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
SplitPath(lpszFile, directory, filename);
lvi.iSubItem = 0;
lvi.pszText = LPSTR_TEXTCALLBACK;
lvi.cchTextMax = MAX_PATH;
int n = m_wndFileList.InsertItem(&lvi);
m_wndFileList.SetItemText(n, 0, filename.c_str());
m_wndFileList.SetItemText(n, 1, directory.c_str());
}
}
}
DragFinish(hDropInfo);
return;
CDialogEx::OnDropFiles(hDropInfo);
}

SHBrowseForFolder "Make New Folder" behavior in Windows XP

I am using SHBrowseForFolder with the new dialog style which gives you an 'Make New Folder' button on
I am getting some problems with this in Windows XP.
The behavior is like this:
1) First when I invoke the dialog, the behaviour is usual(i.e, It is selecting the current folder. But
the focus is not on the tree item(dimmed).
If I click Make new folder button with this state a new folder is creating but it is not in selected
state(i.e, When ever we create a new folder it will allow you to rename folder with the selection on
the item and editbox).
If i select the directory(i.e, setting focus to the item) and then clicking on new folder creating folder
in selected state.
(In Windows 8,windows 7 and Windows Vista it is working Fine)
Anyone faced this problem.
Is there any solution for this?
bool GetFolder(std::string& folderpath, const char* szCaption = NULL, HWND hOwner = NULL)
{
bool retVal = false;
// The BROWSEINFO struct tells the shell
// how it should display the dialog.
BROWSEINFO bi;
memset(&bi, 0, sizeof(bi));
bi.ulFlags = BIF_NEWDIALOGSTYLE|BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS;
bi.hwndOwner = hOwner;
bi.lpszTitle = szCaption;
// must call this if using BIF_USENEWUI
::OleInitialize(NULL);
// Show the dialog and get the itemIDList for the selected folder.
LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);
if(pIDL != NULL)
{
// Create a buffer to store the path, then get the path.
char buffer[_MAX_PATH] = {'\0'};
if(::SHGetPathFromIDList(pIDL, buffer) != 0)
{
// Set the string value.
folderpath = "";
retVal = true;
}
// free the item id list
CoTaskMemFree(pIDL);
}
::OleUninitialize();
return retVal;
}
This seems to be a bug in Windows XP.
Try the code below. This may be not quite what you want, it automatically expands the default folder, but at least it results in a more or less correct behaviour. I tested it on Windows XP and on Windows 7. On later versions of Windows this might not work as expected, so possibily you should check if you are running under XP and do that quirk only if you are running under XP.
int CALLBACK BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM pData)
{
if (uMsg == BFFM_INITIALIZED)
{
HWND hchild = GetWindow(hWnd, GW_CHILD) ;
while (hchild != NULL)
{
TCHAR classname[200] ;
GetClassName(hchild, classname, 200) ;
if (lstrcmp(classname, "SHBrowseForFolder ShellNameSpace Control") == 0)
{
// hchild is the handle to the ShellName Space Control
HWND hlistctrl = GetWindow(hchild, GW_CHILD) ;
do
{
// browse through all controls of the ShellnameSpace control
GetClassName(hlistctrl, classname, 200) ;
if (lstrcmp(classname, "SysTreeView32") == 0)
break ; // we've got the list control
hlistctrl = GetWindow(hlistctrl, GW_HWNDNEXT) ;
} while (hlistctrl != NULL) ;
if (hlistctrl != NULL)
{
// get the handle to the selected item
HTREEITEM ht = (HTREEITEM)::SendMessage(hlistctrl, TVM_GETNEXTITEM, TVGN_CARET, 0) ;
if (ht != NULL)
{
// expand the selected item
::SendMessage(hlistctrl, TVM_EXPAND, TVE_EXPAND, (LPARAM)ht) ;
}
}
}
hchild = GetWindow(hchild, GW_HWNDNEXT) ;
}
}
return 0;
}
bool GetFolder(CString& folderpath, const char* szCaption, HWND hOwner)
{
bool retVal = false;
// how it should display the dialog.
BROWSEINFO bi;
memset(&bi, 0, sizeof(bi));
bi.ulFlags = BIF_NEWDIALOGSTYLE|BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS;
bi.lpfn = BrowseCallbackProc ;
bi.hwndOwner = hOwner;
bi.lpszTitle = szCaption;
// must call this if using BIF_USENEWUI
::OleInitialize(NULL);
// Show the dialog and get the itemIDList for the selected folder.
LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);
if (pIDL != NULL)
{
// Create a buffer to store the path, then get the path.
char buffer[_MAX_PATH] = {'\0'};
if(::SHGetPathFromIDList(pIDL, buffer) != 0)
{
// Set the string value.
folderpath = buffer;
retVal = true;
}
// free the item id list
CoTaskMemFree(pIDL);
}
::OleUninitialize();
return retVal;
}
Another solution would be using the BFFM_SETEXPANDED message as shown below. In this example the "C:" folder is automatically expanded and then the "New Folder" button behaves correctly.
int CALLBACK BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM pData)
{
if (uMsg == BFFM_INITIALIZED)
{
::SendMessage(hWnd, BFFM_SETEXPANDED, TRUE, (LPARAM)L"C:\\") ;
}
return 0;
}

Is there a way to use normal ASCII characters (like a comma) as wxWidgets menu accelerators?

I want a few menu entries that show accelerators that are normal keys, like the space-bar or comma key, but I don't want wxWidgets to make those accelerators itself (because then they can't be used anywhere in the program, including in things like edit boxes).
Unfortunately, wxWidgets insists on always making anything it recognizes in that column into an accelerator under its control, and simply erases anything it doesn't recognize.
I'm looking for some way to either put arbitrary text into the accelerator column (which I don't think exists, I've looked at the source code), or get 'hold of the accelerator table used for the menus so I can modify it myself (haven't found it yet). Can anyone point me in the right direction?
You can try wxKeyBinder. It allows you to bind hotkeys to commands (usually menu entries), save/load/add/remove/modify ... them easily
I couldn't find a way to access the menu's accelerator keys directly, but modifying the accelerator menu text works just as well. Here's the code I came up with:
In a header file:
class accel_t {
public:
// If idcount == -1, idlist must be null or terminated with a -1 entry.
accel_t(): mMenu(0) { }
accel_t(wxMenuBar *m, int *idlist = 0, int idcount = -1);
void reset(wxMenuBar *m, int *idlist = 0, int idcount = -1);
void restore() const;
void remove() const;
private: //
struct accelitem_t {
accelitem_t(int _id, wxAcceleratorEntry _key): id(_id), hotkey(_key) { }
int id;
wxAcceleratorEntry hotkey;
};
typedef std::vector<accelitem_t> data_t;
void noteProblemMenuItems(wxMenu *m);
static bool isProblemAccelerator(wxAcceleratorEntry *a);
wxMenuBar *mMenu;
data_t mData;
};
In a cpp file:
accel_t::accel_t(wxMenuBar *m, int *idlist, int idcount) {
reset(m, idlist, idcount);
}
void accel_t::reset(wxMenuBar *m, int *idlist, int idcount) {
mMenu = m;
mData.clear();
if (idlist == 0) {
for (int i = 0, ie = m->GetMenuCount(); i != ie; ++i)
noteProblemMenuItems(m->GetMenu(i));
} else {
if (idcount < 0) {
int *i = idlist;
while (*i != -1) ++i;
idcount = (i - idlist);
}
for (int *i = idlist, *ie = i + idcount; i != ie; ++i) {
wxMenuItem *item = mMenu->FindItem(*i);
if (item) {
wxAcceleratorEntry *a = item->GetAccel();
if (a != 0) mData.push_back(accelitem_t(*i, *a));
}
}
}
}
bool accel_t::isProblemAccelerator(wxAcceleratorEntry *a) {
if (a == 0) return false;
int flags = a->GetFlags(), keycode = a->GetKeyCode();
// Normal ASCII characters, when used with no modifier or Shift-only, would
// interfere with editing.
if ((flags == wxACCEL_NORMAL || flags == wxACCEL_SHIFT) &&
(keycode >= 32 && keycode < 127)) return true;
// Certain other values, when used as normal accelerators, could cause
// problems too.
if (flags == wxACCEL_NORMAL) {
if (keycode == WXK_RETURN ||
keycode == WXK_DELETE ||
keycode == WXK_BACK) return true;
}
return false;
}
void accel_t::noteProblemMenuItems(wxMenu *m) {
// Problem menu items have hotkeys that are ASCII characters with normal or
// shift-only modifiers.
for (size_t i = 0, ie = m->GetMenuItemCount(); i != ie; ++i) {
wxMenuItem *item = m->FindItemByPosition(i);
if (item->IsSubMenu())
noteProblemMenuItems(item->GetSubMenu());
else {
wxAcceleratorEntry *a = item->GetAccel();
if (isProblemAccelerator(a))
mData.push_back(accelitem_t(item->GetId(), *a));
}
}
}
void accel_t::restore() const {
if (mMenu == 0) return;
for (data_t::const_iterator i = mData.begin(), ie = mData.end(); i != ie;
++i)
{
wxMenuItem *item = mMenu->FindItem(i->id);
if (item) {
wxString text = item->GetItemLabel().BeforeFirst(wxT('\t'));
wxString hotkey = i->hotkey.ToString();
if (hotkey.empty()) {
// The wxWidgets authors apparently don't expect ASCII
// characters to be used for accelerators, because
// wxAcceleratorEntry::ToString just returns an empty string for
// them. This code deals with that.
int flags = i->hotkey.GetFlags(), key = i->hotkey.GetKeyCode();
if (flags == wxACCEL_SHIFT) hotkey = wx("Shift-") + wxChar(key);
else hotkey = wxChar(key);
}
item->SetItemLabel(text + '\t' + hotkey);
}
}
}
void accel_t::remove() const {
if (mMenu == 0) return;
for (data_t::const_iterator i = mData.begin(), ie = mData.end(); i != ie;
++i)
{
wxMenuItem *item = mMenu->FindItem(i->id);
if (item) {
wxString text = item->GetItemLabel().BeforeFirst(wxT('\t'));
item->SetItemLabel(text);
}
}
}
The easiest way to use it is to create your menu-bar as normal, with all accelerator keys (including the problematic ones) in place, then create an accel_t item from it something like this:
// mProblemAccelerators is an accel_t item in the private part of my frame class.
// This code is in the frame class's constructor.
wxMenuBar *menubar = _createMenuBar();
SetMenuBar(menubar);
mProblemAccelerators.reset(menubar);
It will identify and record the accelerator keys that pose problems. Finally, call the remove and restore functions as needed, to remove or restore the problematic accelerator keys. I'm calling them via messages passed to the frame whenever I open a window that needs to do standard editing.

Can't display Tool Tips in VC++6.0

I'm missing something fundamental, and probably both simple and obvious.
My issue:
I have a view (CPlaybackView, derived from CView).
The view displays a bunch of objects derived from CRectTracker (CMpRectTracker).
These objects each contain a floating point member.
I want to display that floating point member when the mouse hovers over the CMpRectTracker.
The handler method is never executed, although I can trace through OnIntitialUpdate, PreTranslateMessage,
and OnMouseMove.
This is in Visual C++ v. 6.0.
Here's what I've done to try to accomplish this:
1. In the view's header file:
public:
BOOL OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult);
private:
CToolTipCtrl m_ToolTip;
CMpRectTracker *m_pCurrentRectTracker;//Derived from CRectTracker
2. In the view's implementation file:
a. In Message Map:
ON_NOTIFY_EX(TTN_NEEDTEXT,0,OnToolTipNeedText)
b. In CPlaybackView::OnInitialUpdate:
if (m_ToolTip.Create(this, TTS_ALWAYSTIP) && m_ToolTip.AddTool(this))
{
m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_AUTOPOP, SHRT_MAX);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_INITIAL, 200);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_RESHOW, 200);
}
else
{
TRACE("Error in creating ToolTip");
}
this->EnableToolTips();
c. In CPlaybackView::OnMouseMove:
if (::IsWindow(m_ToolTip.m_hWnd))
{
m_pCurrentRectTracker = NULL;
m_ToolTip.Activate(FALSE);
if(m_rtMilepostRect.HitTest(point) >= 0)
{
POSITION pos = pDoc->m_rtMilepostList.GetHeadPosition();
while(pos)
{
CMpRectTracker tracker = pDoc->m_rtMilepostList.GetNext(pos);
if(tracker.HitTest(point) >= 0)
{
m_pCurrentRectTracker = &tracker;
m_ToolTip.Activate(TRUE);
break;
}
}
}
}
d. In CPlaybackView::PreTranslateMessage:
if (::IsWindow(m_ToolTip.m_hWnd) && pMsg->hwnd == m_hWnd)
{
switch(pMsg->message)
{
case WM_LBUTTONDOWN:
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
m_ToolTip.RelayEvent(pMsg);
break;
}
}
e. Finally, the handler method:
BOOL CPlaybackView::OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult)
{
BOOL bHandledNotify = FALSE;
CPoint CursorPos;
VERIFY(::GetCursorPos(&CursorPos));
ScreenToClient(&CursorPos);
CRect ClientRect;
GetClientRect(ClientRect);
// Make certain that the cursor is in the client rect, because the
// mainframe also wants these messages to provide tooltips for the
// toolbar.
if (ClientRect.PtInRect(CursorPos))
{
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
CString str;
str.Format("%f", m_pCurrentRectTracker->GetMilepost());
ASSERT(str.GetLength() < sizeof(pTTT->szText));
::strcpy(pTTT->szText, str);
bHandledNotify = TRUE;
}
return bHandledNotify;
}
Got it:
Just to put it to bed, here's the result:
BEGIN_MESSAGE_MAP(CPlaybackView, CThreadView)
ON_NOTIFY_EX(TTN_NEEDTEXT,0,OnToolTipNeedText)
END_MESSAGE_MAP()
void CPlaybackView::OnInitialUpdate()
{
if (m_ToolTip.Create(this, TTS_ALWAYSTIP) && m_ToolTip.AddTool(this))
{
m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_AUTOPOP, 2000);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_INITIAL, 1);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_RESHOW, 1);
m_ToolTip.Activate(FALSE);
BOOL ena = EnableToolTips(TRUE);
}
else
{
TRACE("Error in creating ToolTip");
}
}
void CPlaybackView::OnMouseMove(UINT nFlags, CPoint point)
{
CPlaybackDoc* pDoc = (CPlaybackDoc*) GetDocument();
if (::IsWindow(m_ToolTip.m_hWnd))
{
CMpRectTracker *pRectTracker = HitTest(point);
if(pRectTracker)
{
m_ToolTip.Activate(TRUE);
}
else
{
m_ToolTip.Activate(FALSE);
}
}
}
BOOL CPlaybackView::OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult)
{
BOOL bHandledNotify = FALSE;
CPoint CursorPos;
VERIFY(::GetCursorPos(&CursorPos));
ScreenToClient(&CursorPos);
CRect MilepostRect;
m_rtMilepostRect.GetTrueRect(MilepostRect);
// Make certain that the cursor is in the client rect, because the
// mainframe also wants these messages to provide tooltips for the
// toolbar.
if(MilepostRect.PtInRect(CursorPos))
{
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
CMpRectTracker *pRectTracker = HitTest(CursorPos);
if(pRectTracker)
{
CString str;
str.Format("%f", pRectTracker->GetMilepost());
ASSERT(str.GetLength() < sizeof(pTTT->szText));
::strcpy(pTTT->szText, str);
bHandledNotify = TRUE;
}
}
return bHandledNotify;
}
BOOL CPlaybackView::PreTranslateMessage(MSG* pMsg)
{
if (::IsWindow(m_ToolTip.m_hWnd) && pMsg->hwnd == m_hWnd)
{
switch(pMsg->message)
{
case WM_LBUTTONDOWN:
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
m_ToolTip.RelayEvent(pMsg);
break;
}
}
return CView::PreTranslateMessage(pMsg);
}
CMpRectTracker* CPlaybackView::HitTest(CPoint &point)
{
CPlaybackDoc* pDoc = (CPlaybackDoc*) GetDocument();
ASSERT_VALID(pDoc);
CMpRectTracker *pTracker = NULL;
POSITION pos = pDoc->m_rtMilepostList.GetHeadPosition();
while(pos)
{
CMpRectTracker tracker = pDoc->m_rtMilepostList.GetNext(pos);
if(tracker.HitTest(point) >= 0)
{
m_CurrentRectTracker = tracker;
pTracker = &m_CurrentRectTracker;
break;
}
}
return pTracker;
}

Resources