Showing Tooltips/Icons for Status Bar Panes

Class to show tooltips and icons for individual panes and to allow changing of text and background color of individual panes.

Environment: VC6

Contents

Introduction

Of late, I had seen some questions going back and forth in the forums about handling tooltips for status bar panes. Hence, I sat down to put together a class to simplify this process to make it easy to support this in a MFC framework.

The class, which will do this, is MMStatusBar, derived from MFC's CStatusBar. One can use MMStatusBar to do any of the following:

How to go about implementing each of these using the MMStatusBar class is explained below.

How to...

Miscellaneous Public Methods of MMStatusBar

Apart from the CStatusBar public methods, MMStatusBar has a few more helper methods that can be useful.

  • void SetDelayTime(UINT uDelayTime);
  • This method can be used to set the tooltip delay time. This is the time (in milliseconds) for which the mouse has to be over a pane before the tooltip can appear (if the pane has tooltip text).
  • void SetPaneTextColor(int nIndex,COLORREF crTextColor);
  • This method can be used to set the color of the text of a particular pane. It works by sending a SB_SETTEXT message to the statusbar with the SBT_OWNERDRAW set. The drawing is handled in the the MMStatusBar's DrawItem method.
  • void SetPaneTextBkColor(int nIndex,COLORREF crTextBkColor);
  • This method can be used to set the color of text background of a particular pane. It works by sending a SB_SETTEXT message to the statusbar with the SBT_OWNERDRAW set. The drawing is handled in the the MMStatusBar's DrawItem method.

Miscellaneous Public Methods of MMStatusCmdUI

  • virtual void Enable(BOOL bOn);
  • To enable or disable a pane. The pane text will not appear in that case.
  • virtual void SetCheck(int nCheck);
  • If nCheck is non-zero, will make the pane pop out.
  • virtual void SetWidth(int cxWidth);
  • Can be used to set the pane width in pixels.

MMStatusBar overrides the OnUpdateCmdUI method and uses this to pass an UPDATE_COMMAND_UI message to each of the panes.

How to Integrate MMStatusBar into Your Project

  1. Include MMStatusBar.cpp and MMStatusBar.h into your project.
  2. In the Mainframe, replace the CStatusBar with MMStatusBar.
  3. Depending on how you would want the tooltips to be set, you could use one of the approaches as listed in How to use MMStatusBar class.
  4. If you are using MMStatusBar in a dialog, the following knowledge base article will be of help: KB 123158.

Implementation Details of the MMStatusBar Class

How does MMStatusBar class do all these? Let us take it one by one.

Creation: The MMStatusBar class traps WM_CREATE and creates the m_oToolTip tooltip control and activates it.

Setting tooltip text and icons: In its SetIndicators method, MMStatusBar calls CStatusBar::Setindicators to let the baseclass do the preparation of the panes. It then checks to see whether it was passed a lpTooltipArray, and if so, adds a tool to the tooltip control specifying the pane rectangle and the ID for tooltip text as shown below. That makes the tooltip control show the tooltip whenever the mouse hovers over and stays within the rectangle.

for(int i =0;i&ltnIDCount;i++)             // each (new) indicator
    {
      CRect oRect;
      GetItemRect(i,&oRect);
      UINT uID     = lpIDArray[i];
      UINT uIDText = lpIDToolTipArray[i];
      if (uID     != ID_SEPARATOR &&      // have indicator and
        uIDText   != 0)                   // want tooltip text for it
        VERIFY(m_oToolTip.AddTool(this,uIDText,oRect,uID));
    }

Icons are added in a similar way. Note that when there is an icon associated with a pane, one would have to add the icon's width to the pane width and set the new pane width as is done below.

for(int i =0;i&ltnIDCount;i++)        // each (new) indicator
    {
      UINT uID  = lpIDArray[i];
      if (uID  != ID_SEPARATOR &&     // have indicator and
          lphIconArray[i] != NULL)    // want icon
      {
        GetStatusBarCtrl().SetIcon(i,lphIconArray[i]);
        int nWidth;
        UINT nID,nStyle;
        GetPaneInfo(i,nID,nStyle,nWidth);
        SetPaneInfo(i,nID,nStyle,nWidth +
                    GetSystemMetrics(SM_CXSMICON));
      }
    }

Considerations during resizing: When the status bar is resized, the toolrectanges have to be recomputed so the tooltips appear in the proper panes. The same scenario exists for cases when the pane width changes for some reason. It may be when one sets a new pane text or when an icon is shown and removed. The MMStatusBar::Redraw method does this recomputing. It loops through all the panes, gets the pane rectangles, and sets the new tool rectangles.

void MMStatusBar::Redraw()
{
  CToolInfo oToolInfo;
  memset(&oToolInfo,0,sizeof(TOOLINFO));
  oToolInfo.cbSize = sizeof(TOOLINFO) ;

  CRect oRect;
  UINT uId;
  for(int i = 0 ; i < m_nCount ; i++)
  {
    uId = GetItemID(i);
    GetItemRect(i,&oRect);

    oToolInfo.hwnd        = this->GetSafeHwnd();
    oToolInfo.uId         = uId;
    oToolInfo.rect.left   = oRect.left;
    oToolInfo.rect.right  = oRect.right;
    oToolInfo.rect.top    = oRect.top;
    oToolInfo.rect.bottom = oRect.bottom;

    m_oToolTip.SendMessage(TTM_NEWTOOLRECT,0,(LPARAM)
                           (TOOLINFO*)&oToolInfo);
  }
}

The Redraw method is called from MMStatusCmdUI::SetWidth for this exact same reason. Any change in pane width should result in a recomputing of the tool rectangles.

How does MMStatusBar enable pane texts, tooltips, and icons to be updated through UPDATE_COMMAND_UI?

MMStatusBar overrides the OnUpdateCmdUI() virtual method. This method gets called by the framework during idle processing. So, I figured out that this would be a good place to handle updating the tooltips and pane texts. In this handler, MMStatusBar will instantiate a MMStatusCmdUI object and set its values. The most important thing here is state.m_pMenu = (CMenu*)MM_STATUS_PANE_UPDATE. This is used by the UPDATE_COMMAND_UI handler to know that this is a status bar update and so it can handle it appropriately. The reason for doing this is that if one has a menu with the same ID as the pane, UPDATE_COMMAND_UI would come in two forms for the same command ID: one when the status bar needs to be udpated, and another when the menu status for the command ID needs to be updated.

void MMStatusBar::OnUpdateCmdUI(CFrameWnd* pTarget,
                                BOOL bDisableIfNoHndler)
{
  MMStatusCmdUI state;
  state.m_pOther      = this;
  state.m_pMenu       = (CMenu*)MM_STATUS_PANE_UPDATE;
  state.m_nIndexMax   = (UINT)m_nCount;
  for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
    state.m_nIndex++)
  {
    state.m_nID = GetItemID(state.m_nIndex);

    // allow the statusbar itself to have update handlers
    if (CWnd::OnCmdMsg(state.m_nID, CN_UPDATE_COMMAND_UI,
                       &state, NULL))
      continue;

    // allow target (owner) to handle the remaining updates
    state.DoUpdate(pTarget, FALSE);
  }

  // update the dialog controls added to the status bar
  UpdateDialogControls(pTarget, bDisableIfNoHndler);
}

How does MMStatusBar generate WM_COMMAND on double-clicking in any pane? To acheive this, MMStatusBar overrides the virtual function OnChildNotify(). If the notification received is a NM_DBLCLK, it gets the command ID for the item and then posts a WM_COMMAND message to the statusbar's parent window.

And the one single line which makes all this possible... is below. For tooltips to show, all mouse events on the statusbar have to be relayed to the tooltip control. Otherwise, the tooltip control will not be aware of when to show tooltips. This is because we do not use the TTF_SUBCLASS flag. So, MMStatusBar overrides another virtual function, PreTranslateMessage, and relays all mouse events to the tooltip control.

BOOL MMStatusBar::PreTranslateMessage(MSG *pMsg)
{
  switch(pMsg->message)
  {
    case WM_MOUSEMOVE:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONUP:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    m_oToolTip.RelayEvent (pMsg);
    default :break;
  }
  return CStatusBar::PreTranslateMessage(pMsg);
}

I hope this provided a small insight to the inner workings of MMStatusBar. I have tried to provide as many comments as possible in the source code. The code can be used with UNICODE or non-UNICODE settings.

About the Included Demo Project

The demo project included shows a way of implementing all of those listed above.

  • Pane 1 shows a static pane text and a static tooltip.
  • Pane 2 shows a dynamic pane text and a dynamic tooltip. To change the pane text and/or tooltip, go to menu Pane->Pane 2 Properties and set the texts.
  • Pane 3 shows a static text with an icon and a dynamic tooltip. The icon can be shown and hidden by toggling the menu Pane->Icon in Pane 3.
  • On double-clicking on Pane 1, Mainframe traps the command to show a message box.
  • To change the tooltip delay time, go to menu Pane->Delay Time and set the new delay time for the tooltips.
  • To change the text color of Pane 2, go to menu Pane->Change Pane 2 Color and select the new text color for the pane.
  • To change the text background color of Pane 2, go to menu Pane->Change Pane 2 Bkgnd Color and select the new bkgnd color for the pane.

Acknowledgements

Thanks to all the CodeGuru readers for their encouraging comments and suggestions. These comments encouraged me to provide an update with any bug fixes and improvements.

  • Update 1. Thanks to Roberto Cagnetti, Mark Williams, Robin Bannister, and Jose Ramos for providing their useful comments, which I have attempted to incorporate in this update. They were mainly bug fixes.
  • Update 2. Thanks to zephyrer and matro for their suggestions. I have tried to address their concerns in this update. zephyrer's suggestion lead to a feature enhancement to the MMStatusBar class to support setting of text color and background color for individual panes.


Downloads

Comments

  • http://www.oakleysunglassesoutc.com/ whwhkg

    Posted by http://www.oakleysunglassesoutc.com/ Suttonfmi on 03/31/2013 02:01pm

    ghd australia,Exhibition has not yet begun, you had sales of nearly one hundred thousand! The Beijing headquarters office, I looked grin on his face some frustration apprentice. Your kid to have a cheap cold to it, the hearts must appreciate terribly, and this time even the skill of the people ghd hair straightener dismissive, can not say that this is your work level is not high! I know she is in disguise to protect my reputation, know all the story of people to see the comic ground, even if very picky. It can only be accused animation community's level of bad. No one would dare to pick and choose my plot, because the novel was there. If you have questions about it first reading to say! But this time involve students puppy love, you want good way to deal with? Measures Well ... no! But the saying goes, lice do not bite, accounts more worry about, anyway, I was also accused used to, even if ghd hair straightener,hairstraighteneraul.ghd,com/" title="cheap ghd"cheap ghd yell and then worse I'm afraid it can not touch my roots, with its carefully not as good as on the toe high gas Aung put love gnaw, gnaw expression!

    Reply
  • ugg ブーツ 激安

    Posted by EScocheNeonge on 11/09/2012 03:05am

    内部内と全体の周り 印象 加えて、 高度 工業。 このこれらのすべて salesuggbootsjapan, ディスク 関数 大いに 約 ストッキング販売 種類の異なるタイプの に関して それに加えて データファイル また アイテム。 彼らは可能性があります 絶妙な非常に同様にと一緒に、追加 スタイリッシュ 採用応用利用などと比べて http://www.cheapuggbootssalejp.com 手頃な価格 得るために 維持 使用し??てスルー ペット ペン hardrivesを書面で置く。 主、人々は に慣れするために使用される以下を過ごす救うブーツ, で保存フロッピー システム とは 受け入れる これらの種類の 一緒に使って 最高 作曲 ハード·ディスク·ドライブ link, 供給 安定性、柔軟性 と同様と一緒に加えて 易 組み合わせてと非常に手頃な経済値札 すべてに に関してと買い物消費者 と同様 彼らはあるかもしれないは使用のなさ使用適切 維持省エネ データベース uggブーツ, と お客さまバイヤー とか、そういうところは。

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • Hurricane Sandy was one of the most destructive natural disasters that the United States has ever experienced. Read this success story to learn how Datto protected its partners and their customers with proactive business continuity planning, heroic employee efforts, and the right mix of technology and support. With storm surges over 12 feet, winds that exceeded 90 mph, and a diameter spanning more than 900 miles, Sandy resulted in power outages to approximately 7.5 million people, and caused an estimated $50 …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds