Link to home
Start Free TrialLog in
Avatar of polyanovsky
polyanovsky

asked on

Multi-line Tooltip for embedded ActiveX

I have a CFormView-derived class which contains an ActiveX. ActiveX displays a diagram and,unfortunately, supports only one-line tooltips. How can I implement multi-line tooltips for this ActiveX? Tooltip should appear every time mouse pause over a diagram node. Given mouse coordinates ActiveX provides info whether mouse is over node or not, and it is possible to shut off ActiveX own tooltips.
I can provide additional info and rise points if necessary.
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

Have you tried "Line 1\r\nLine 2" as the tip to display?
You might also require
m_tip.SetMaxTipWidth(300);   //m_tip is your tooltip control, set your width here

I seem to recall without setting the width the tips will always try to be on one line
Avatar of polyanovsky
polyanovsky

ASKER

"Line 1\r\nLine 2" does not work.
I do not have access to ActiveX tooltip control. Changes in CFormView tooltip control does not affect ActiveX tooltip behaviour.
<I do not have access to ActiveX tooltip control.>
Can you supress the default tooltip and provide your own?
"it is possible to shut off ActiveX own tooltips". However, there is no way to pass a pointer to my tooltip to ActiveX. Probably, everything should be done at the ActiveX parent window level.
What I meant was stop the ActiveX tooltip completely and supply your own tip from your view.  (From what you write I believe you are handling a callback from the ActiveX for the text to display)
   I do not handle a callback from ActiveX for the text to display as it is not provided by ActiveX. Tooltip text is a node property and can not be supplied "on demand". However, I can stop the ActiveX tooltip completely.
    You should think about ActiveX as a "black box". The only info it provides - coordinates of the nodes. Thus I have to create a tooltip on CFormView level which would appear when mouse enters specific rectangles on ActiveX diagram and disappears when mouse leaves those rectangles. Each rectangle will have specific text to display. In this sense there is no difference between ActiveX and a picture where different tooltips should appear when mouse hovers over different parts of the picture.
Create a tooltip for the complete view, check if the mouse is over a 'node' and display tip else hide tip.

      m_tooltip.AddTool(this, LPSTR_TEXTCALLBACK, &rc, 1);   //tooltip, callsback for text to be displayed, rc is the rectangle to tip over - your activeX control in this case

In the OnTooltipNotify (callback for text) check if it is over a node, return false if you don't want to display text.


BOOL CMyView::OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
.....

TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
 pTTT->lpszText = "Some Text Here";
  return true;
}

BOOL CMyView::PreTranslateMessage(MSG* pMsg)
{
      // CG: The following block was added by the ToolTips component.
      {
            // Let the ToolTip process this message.
            m_tooltip.RelayEvent(pMsg);
      }
    if(pMsg->message == WM_MOUSEMOVE)  //Crude - mouse moved, prompt for new text to display
      {
            m_tooltip.Update();
      }

In your scheme if I click on ActiveX diagram tooltip disappears and will not appear until mouse leaves ActiveX and enters again (because tooltip serves ActiveX as a whole rather than individual nodes). Tooltip text is not updated when moving from one node to another. Besides that tooltip is shown outside of ActiveX (but it is, probably, easy to correct).
Don't forget you are attempting to change the behaviour of another element you haven't got the source code for - always full of pitfalls.

Click on ActiveX - Focus is changing?  You might require a mouse hook to make certain your app still gets the mouse move messages.

Text not updating - You probably haven't coded correctly to determine the node it is over.  You get the WM_MOUSEMOVE and tell the tip to update.

Tip outside of ActiveX - again probably a mistake in your code.  (Screen/Client co-ordinate mixup?)
I could implement desirable behaviuor as following:

class CMainFrame : public CMDIFrameWnd

BOOL CMainFrame::OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
     static CToolTipCtrl* pToolTip = NULL;
     CToolTipCtrl* ptt= AfxGetThreadState()->m_pToolTip;
     if (ptt != pToolTip) { // new tooltip
         CRect rc(4,4,4,4); // 4 pixel margin
         ptt->SetMargin(&rc);
         ptt->SetTipBkColor( RGB(128,255,128));
         ptt->SetMaxTipWidth(128);
         ptt->SetDelayTime(TTDT_AUTOPOP, SHRT_MAX);
         ptt->SetDelayTime(TTDT_INITIAL, TOOLTIP_DELAY);
       ptt->SetDelayTime(TTDT_RESHOW, TOOLTIP_DELAY);

         pToolTip = ptt;
     }
     return CFrameWnd::OnToolTipText(id, pNMHDR, pResult);
}
class CWfView : public CFormView

void CWfView::OnMouseMoveActiveX(short Button, short Shift, long x, long y)//mouse move handler supplied by ActiveX
{
      // TODO: Add your control notification handler code here
      if(m_ActiveX.IsNewPointedNode(x,y))//ActiveX checks if pointed node has changed
      {
       CToolTipCtrl* ptt= AfxGetThreadState()->m_pToolTip;
       if (ptt)
        ptt->Update();
      }
}

BOOL CWfView::OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
      TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
      CString str=m_ActiveX.GetToolTipText();
      if(!str.IsEmpty())
      {
            strcpy(pTTT->szText,str);
            return TRUE;
      }
      return(FALSE);
}

The only remaining problem is  "if I click on ActiveX diagram tooltip disappears and will not appear until mouse leaves ActiveX and enters again ". I guess it is because AfxGetThreadState()->m_pToolTip is NULL after a mouse click untill mouse leaves ActiveX and enters it again.
Tooltip stays at the outer ActiveX border but it should be easy to correct.
I would award all points for resolving just the remaining problem.
An additional, generic technique:  
Use Spy++ to examine the messages being sent as you move the mouse outside of and inside of the control, before and after clicking, with and without the "default" tooltip handling enabled.  Often times, I've found that Spy++ provides the clue that makes it obvious how to get something done.

-- Dan
O-o-ops
Missed two more relevant lines of code:

void CWfView::OnInitialUpdate()
{
      CFormView::OnInitialUpdate();
      
        m_ActiveX.SetShowToolTip(FALSE);
      EnableToolTips(TRUE);
}
A couple of things to try.

Put a TRACE into the OnMouseMoveActiveX function - is it being called after the click on the ActiveX control?

Don't use the generic tooltip control.  Create your own as a member of your frame window - this should always be non-null.

If neither work you might have to write a mouse hook so your app can see the mouse messages even when the focus is on the ActiveX control.
Tried the following implementation:

class CWfView : public CFormView
{
private:
      CToolTipCtrl m_ToolTip;
}

void CWfView::OnInitialUpdate()
{
    CFormView::OnInitialUpdate();
    CRect Rect;
    GetClientRect(Rect);
    Rect.DeflateRect(20,20,20,20);//easy to check that ActiveX is properly displayed
    m_ActiveX.MoveWindow(Rect);
    m_ActiveX.SetShowToolTip(FALSE);
//  EnableToolTips(TRUE);
    m_ToolTip.Create(this);
    CRect rc(4,4,4,4); // 4 pixel margin
    m_ToolTip.SetMargin(&rc);
    m_ToolTip.SetTipBkColor( RGB(128,255,128));
    m_ToolTip.SetMaxTipWidth(128);
    m_ToolTip.SetDelayTime(TTDT_AUTOPOP, SHRT_MAX);
    m_ToolTip.SetDelayTime(TTDT_INITIAL, TOOLTIP_DELAY);
    m_ToolTip.SetDelayTime(TTDT_RESHOW, TOOLTIP_DELAY);
    m_ToolTip.AddTool(this,"GHGHHDF", &Rect, 1);
    m_ToolTip.Activate(TRUE);
}

BOOL CWfView::PreTranslateMessage(MSG* pMsg)
{
      // TODO: Add your specialized code here and/or call the base class
      m_ToolTip.RelayEvent(pMsg);
      TRACE("\nPreTranslate()");
      return CFormView::PreTranslateMessage(pMsg);
}

BOOL CWfView::OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
     TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
     CString str=m_ActiveX.GetToolTipText();
     if(!str.IsEmpty())
     {
          strcpy(pTTT->szText,str);
          return TRUE;
     }
     return(FALSE);
}


All tooltip related code is erased in CMainFrame.

Tooltip "GHGHHDF" does not show up. If uncomment EnableToolTips(TRUE) default tooltip appears for ActiveX as a whole with the text provided by CWfView::OnToolTipNotify.
PreTranslateMessage is called as expected, e.g. on mouse move over ActiveX.

excerpt from earlier post

BOOL CMyView::PreTranslateMessage(MSG* pMsg)
{
     // CG: The following block was added by the ToolTips component.
     {
          // Let the ToolTip process this message.
          m_tooltip.RelayEvent(pMsg);
     }
    if(pMsg->message == WM_MOUSEMOVE)  //Crude - mouse moved, prompt for new text to display  <<<------------------
     {
          m_tooltip.Update();
     }
Adding

if(pMsg->message == WM_MOUSEMOVE)  //Crude - mouse moved, prompt for new text to display  <<<------------------
     {
          m_tooltip.Update();
     }

to CWfView::PreTranslateMessage does not change anything as tooltip has static text (nevertheless I tried it, but tooltip does not show up anyway).
SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Getting mouse messages is not a problem here - as I mentioned before OnMouseMoveActiveX handler is supplied by ActiveX and is working fine, i.e. is called whenever mouse is moving. Also default (i.e. application supplied)tooltip appears with no problem when mouse is over ActiveX. The only problem with default tooltip is that it is destroyed after the mouse click on ActiveX and is not created until mouse leaves ActiveX and enters it again as it serves ActiveX as a whole. Embedded tooltip does not show up for ActiveX.

I found that http://www.codeproject.com/miscctrl/xinfotip.asp
tooltip gives enough degree of flexibility to implement desired behaviour. I used its Show and Hide methods on appropriate events (mouse move over ActiveX, mouse click, ActiveX scroll, and mouse leaving ActiveX)in order to simulate default tooltip behaviour.
In order to catch when mouse is leaving a window I used a timer. In timer handler I use GetCursorPos and check whether it is in the window rectangle. Is there more elegant way of knowing that mouse is leaving window rectangle (may be application-supplied event)?
Andy,
Thank you very much for your time. I appreciate your help and stimulating discussion.