title: Show tooltip on invalid input in edit control
body: <p>I have subclassed edit control to accept only floating numbers. I would like to pop a tooltip when user makes an invalid input. The behavior I target is like the one edit control with <code>ES_NUMBER</code> has :</p>
  
  <p><img src="https://i.stack.imgur.com/Jsjbl.png" alt="enter image description here"></p>
  
  <p>So far I was able to implement tracking tooltip and display it when user makes invalid input.</p>
  
  <p>However, the tooltip is misplaced. I have tried to use <code>ScreenToClient</code> and <code>ClientToScreen</code> to fix this but have failed.</p>
  
  <p>Here are the instructions for creating <em>SCCE</em> :</p>
  
  <p>1) Create default Win32 project in Visual Studio.</p>
  
  <p>2) Add the following includes in your <code>stdafx.h</code>, just under <code>#include &lt;windows.h&gt;</code> :</p>
  
  <pre><code>#include &lt;windowsx.h&gt;
  #include &lt;commctrl.h&gt;
  
  #pragma comment( lib, "comctl32.lib")
  
  #pragma comment(linker, \
      "\"/manifestdependency:type='Win32' "\
      "name='Microsoft.Windows.Common-Controls' "\
      "version='6.0.0.0' "\
      "processorArchitecture='*' "\
      "publicKeyToken='6595b64144ccf1df' "\
      "language='*'\"")
  </code></pre>
  
  <p>3) Add these global variables:</p>
  
  <pre><code>HWND g_hwndTT;
  TOOLINFO g_ti;
  </code></pre>
  
  <p>4) Here is a simple subclass procedure for edit controls ( just for testing purposes ) :</p>
  
  <pre><code>LRESULT CALLBACK EditSubProc ( HWND hwnd, UINT message, 
      WPARAM wParam, LPARAM lParam, 
      UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
  {
      switch (message)
      {
      case WM_CHAR:
          {
              POINT pt;
              if( ! isdigit( wParam ) )  // if not a number pop a tooltip!
              {
                  if (GetCaretPos(&amp;pt))  // here comes the problem
                  {
                      // coordinates are not good, so tooltip is misplaced
                      ClientToScreen( hwnd, &amp;pt );
  
  
                      /************************** EDIT #1 ****************************/
                      /******* If I delete this line x-coordinate is OK *************/
                      /*** y-coordinate should be little lower, but it is still OK **/
                      /**************************************************************/
  
                      ScreenToClient( GetParent(hwnd), &amp;pt );
  
                      /************************* Edit #2 ****************************/
  
                      // this adjusts the y-coordinate, see the second edit
                      RECT rcClientRect;
                      Edit_GetRect( hwnd, &amp;rcClientRect );
                      pt.y = rcClientRect.bottom;
  
                      /**************************************************************/
  
                      SendMessage(g_hwndTT, TTM_TRACKACTIVATE, 
                          TRUE, (LPARAM)&amp;g_ti);
                      SendMessage(g_hwndTT, TTM_TRACKPOSITION, 
                          0, MAKELPARAM(pt.x, pt.y));
                  }
                  return FALSE;
              }
              else
              {
                  SendMessage(g_hwndTT, TTM_TRACKACTIVATE, 
                      FALSE, (LPARAM)&amp;g_ti);
                  return ::DefSubclassProc( hwnd, message, wParam, lParam );
              }
          }
          break;
      case WM_NCDESTROY:
          ::RemoveWindowSubclass( hwnd, EditSubProc, 0 );
          return DefSubclassProc( hwnd, message, wParam, lParam);
          break;
      }
      return DefSubclassProc( hwnd, message, wParam, lParam);
  } 
  </code></pre>
  
  <p>5) Add the following <code>WM_CREATE</code> handler :</p>
  
  <pre><code>case WM_CREATE:
      {
          HWND hEdit = CreateWindowEx( 0, L"EDIT", L"edit", WS_CHILD | WS_VISIBLE |
              WS_BORDER | ES_CENTER, 150, 150, 100, 30, hWnd, (HMENU)1000, hInst, 0 );
  
          // try with tooltip
          g_hwndTT = CreateWindow(TOOLTIPS_CLASS, NULL,
              WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
              0, 0, 0, 0, hWnd, NULL, hInst, NULL);
  
          if( !g_hwndTT )
              MessageBeep(0);  // just to signal error somehow
  
          g_ti.cbSize = sizeof(TOOLINFO);
          g_ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;
          g_ti.hwnd = hWnd;
          g_ti.hinst = hInst;
          g_ti.lpszText = TEXT("Hi there");
  
          if( ! SendMessage(g_hwndTT, TTM_ADDTOOL, 0, (LPARAM)&amp;g_ti) )
              MessageBeep(0);  // just to have some error signal
  
          // subclass edit control
          SetWindowSubclass( hEdit, EditSubProc, 0, 0 );
      }
      return 0L;  
  </code></pre>
  
  <p>6) Initialize common controls in <code>MyRegisterClass</code> ( before <code>return</code> statement ) :</p>
  
  <pre><code>// initialize common controls
  INITCOMMONCONTROLSEX iccex;
  iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  iccex.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES | 
      ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | ICC_STANDARD_CLASSES ;
  
  if( !InitCommonControlsEx(&amp;iccex) ) 
      MessageBeep(0);   // signal error 
  </code></pre>
  
  <p>That's it, for the <em>SSCCE</em>.</p>
  
  <p>My questions are following :</p>
  
  <ol>
  <li><p>How can I properly position tooltip in my main window? How should I manipulate with caret coordinates?</p></li>
  <li><p>Is there a way for tooltip handle and toolinfo structure to not be global?</p></li>
  </ol>
  
  <p>Thank you for your time.</p>
  
  <p>Best regards. </p>
  
  <h2>EDIT #1:</h2>
  
  <p>I have managed to achieve quite an improvement by deleting <code>ScreenToClient</code> call in the subclass procedure. The x-coordinate is good, y-coordinate could be slightly lower. I still would like to remove global variables somehow...</p>
  
  <h2>EDIT #2:</h2>
  
  <p>I was able to adjust y-coordinate by using <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb761596%28v=vs.85%29.aspx"><code>EM_GETRECT</code></a> message and setting y-coordinate to the bottom of the formatting rectangle:</p>
  
  <pre><code>RECT rcClientRect;
  Edit_GetRect( hwnd, &amp;rcClientRect );
  pt.y = rcClient.bottom;
  </code></pre>
  
  <p>Now the end-result is much better. All that is left is to remove global variables...</p>
  
  <h2>EDIT #3:</h2>
  
  <p>It seems that I have cracked it! The solution is in <code>EM_SHOWBALLOONTIP</code> and <code>EM_HIDEBALLOONTIP</code> messages! Tooltip is placed at the caret position, ballon shape is the same as the one on the picture, and it auto-dismisses itself properly. And the best thing is that <strong>I do not need global variables!</strong></p>
  
  <p>Here is my subclass procedure snippet:</p>
  
  <pre><code>case WM_CHAR:
  {
      // whatever... This condition is for testing purpose only
      if( ! IsCharAlpha( wParam ) &amp;&amp; IsCharAlphaNumeric( wParam ) )
      {
          SendMessage(hwnd, EM_HIDEBALLOONTIP, 0, 0);
          return ::DefSubclassProc( hwnd, message, wParam, lParam );
      }
      else
      {
          EDITBALLOONTIP ebt;
  
          ebt.cbStruct = sizeof( EDITBALLOONTIP );
          ebt.pszText = L" Tooltip text! ";
          ebt.pszTitle = L" Tooltip title!!! ";
          ebt.ttiIcon = TTI_ERROR_LARGE;    // tooltip icon
  
          SendMessage(hwnd, EM_SHOWBALLOONTIP, 0, (LPARAM)&amp;ebt);
  
          return FALSE;
      }
   }
   break;
  </code></pre>