linkwnd.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 95k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //-------------------------------------------------------------------------//
  2. //  linkwnd.cpp - implementation of CLinkWindow
  3. //
  4. //  [scotthan] - created 10/7/98
  5. #include "shellprv.h"
  6. #include "ids.h"
  7. #include <oleacc.h>
  8. //-------------------------------------------------------------------------//
  9. #define IS_LINK(pBlock)     ((pBlock) && (pBlock)->iLink != INVALID_LINK_INDEX)
  10. #ifndef ARRAYSIZE
  11. #define ARRAYSIZE(a)        (sizeof(a)/sizeof(*a))
  12. #endif
  13. #ifndef POINTSPERRECT
  14. #define POINTSPERRECT       (sizeof(RECT)/sizeof(POINT))
  15. #endif
  16. #ifndef RECTWIDTH
  17. #define RECTWIDTH(prc)      ((prc)->right - (prc)->left)
  18. #endif
  19. #ifndef RECTHEIGHT
  20. #define RECTHEIGHT(prc)     ((prc)->bottom - (prc)->top)
  21. #endif
  22. #define TESTKEYSTATE(vk)   ((GetKeyState(vk) & 0x8000)!=0)
  23. #define LINKCOLOR_BKGND     COLOR_WINDOW
  24. #define LINKCOLOR_ENABLED   GetSysColor( GetCOLOR_HOTLIGHT() )
  25. #define LINKCOLOR_DISABLED  GetSysColor( COLOR_GRAYTEXT )
  26. #define CF_SETCAPTURE  0x0001
  27. #define CF_SETFOCUS    0x0002
  28. //  KEYBOARDCUES helpes
  29. #ifdef KEYBOARDCUES
  30. void _InitializeUISTATE( IN HWND hwnd, IN OUT UINT* puFlags );
  31. BOOL _HandleWM_UPDATEUISTATE( IN WPARAM wParam, IN LPARAM lParam, IN OUT UINT* puFlags );
  32. #endif
  33. //-------------------------------------------------------------------------//
  34. //  class CAccessibleBase
  35. //
  36. //  common IAccessible implementation.
  37. //
  38. class CAccessibleBase : public IAccessible, public IOleWindow
  39. //-------------------------------------------------------------------------//
  40. {
  41. public:
  42.     CAccessibleBase( const HWND& hwnd )
  43.         :   _cRef(1), _ptiAcc(NULL), _hwnd(hwnd)
  44.     { 
  45.         DllAddRef();
  46.     }
  47.     
  48.     virtual ~CAccessibleBase()
  49.     { 
  50.         ATOMICRELEASE(_ptiAcc);
  51.     }
  52.     //  IUnknown
  53.     STDMETHODIMP         QueryInterface( REFIID riid, void** ppvObj );
  54.     STDMETHODIMP_(ULONG) AddRef();
  55.     STDMETHODIMP_(ULONG) Release();
  56.     //  IOleWindow
  57.     STDMETHODIMP GetWindow( HWND* phwnd );
  58.     STDMETHODIMP ContextSensitiveHelp( BOOL fEnterMode ) { return E_NOTIMPL; }
  59.     // IDispatch
  60.     STDMETHODIMP GetTypeInfoCount( UINT * pctinfo );
  61.     STDMETHODIMP GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo** pptinfo );
  62.     STDMETHODIMP GetIDsOfNames( REFIID riid, OLECHAR** rgszNames, UINT cNames,
  63.                                 LCID lcid, DISPID * rgdispid );
  64.     STDMETHODIMP Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  65.                          DISPPARAMS * pdispparams, VARIANT * pvarResult, 
  66.                          EXCEPINFO * pexcepinfo, UINT * puArgErr );
  67.     //  IAccessible
  68.     STDMETHODIMP get_accParent( IDispatch ** ppdispParent);
  69.     STDMETHODIMP get_accChildCount( long * pcChildren );
  70.     STDMETHODIMP get_accChild( VARIANT varChildIndex, IDispatch ** ppdispChild);
  71.     STDMETHODIMP get_accValue( VARIANT varChild, BSTR* pbstrValue);
  72.     STDMETHODIMP get_accDescription( VARIANT varChild, BSTR * pbstrDescription);
  73.     STDMETHODIMP get_accRole( VARIANT varChild, VARIANT *pvarRole );
  74.     STDMETHODIMP get_accState( VARIANT varChild, VARIANT *pvarState );
  75.     STDMETHODIMP get_accHelp( VARIANT varChild, BSTR* pbstrHelp );
  76.     STDMETHODIMP get_accHelpTopic( BSTR* pbstrHelpFile, VARIANT varChild, long* pidTopic );
  77.     STDMETHODIMP get_accKeyboardShortcut( VARIANT varChild, BSTR* pbstrKeyboardShortcut );
  78.     STDMETHODIMP get_accFocus( VARIANT FAR * pvarFocusChild );
  79.     STDMETHODIMP get_accSelection( VARIANT FAR * pvarSelectedChildren );
  80.     STDMETHODIMP get_accDefaultAction( VARIANT varChild, BSTR* pbstrDefaultAction );
  81.     STDMETHODIMP accSelect( long flagsSelect, VARIANT varChild );
  82.     STDMETHODIMP accLocation( long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild );
  83.     STDMETHODIMP accNavigate( long navDir, VARIANT varStart, VARIANT * pvarEndUpAt );
  84.     STDMETHODIMP accHitTest( long xLeft, long yTop, VARIANT * pvarChildAtPoint );
  85.     STDMETHODIMP put_accName( VARIANT varChild, BSTR bstrName );
  86.     STDMETHODIMP put_accValue( VARIANT varChild, BSTR bstrValue );
  87. protected:
  88.     virtual UINT GetDefaultActionStringID() const = 0;
  89.     
  90. private:
  91.     ULONG       _cRef;
  92.     ITypeInfo*  _ptiAcc;
  93.     const HWND& _hwnd;
  94.     //  Thunked OLEACC defs from winuser.h
  95.     #ifndef OBJID_WINDOW
  96.     #define OBJID_WINDOW        0x00000000
  97.     #endif//OBJID_WINDOW
  98.     #ifndef OBJID_TITLEBAR
  99.     #define OBJID_TITLEBAR      0xFFFFFFFE
  100.     #endif//OBJID_TITLEBAR
  101.     #ifndef OBJID_CLIENT
  102.     #define OBJID_CLIENT        0xFFFFFFFC
  103.     #endif//OBJID_CLIENT
  104.     #ifndef CHILDID_SELF
  105.     #define CHILDID_SELF        0
  106.     #endif//CHILDID_SELF
  107.     #define VALIDATEACCCHILD( varChild, idChild, hrFail ) 
  108.         if( !(VT_I4 == varChild.vt && idChild == varChild.lVal) ) {return hrFail;}
  109. } ;
  110. //-------------------------------------------------------------------------//
  111. #define TEST_CAPTURE(fTest)           ((_fCapture & fTest) != 0)
  112. #define MODIFY_CAPTURE(fSet, fRemove) {if(fSet){_fCapture |= fSet;} if(fRemove){_fCapture &= ~fRemove;}}
  113. #define RESET_CAPTURE()               {_fCapture=0;}
  114. //-------------------------------------------------------------------------//
  115. //  class CLinkWindow
  116. class CLinkWindow : public CAccessibleBase
  117. //-------------------------------------------------------------------------//
  118. {
  119. public:
  120.     CLinkWindow();
  121.     virtual ~CLinkWindow();
  122.     //  IAccessible specialization
  123.     STDMETHODIMP get_accName( VARIANT varChild, BSTR* pbstrName);
  124.     STDMETHODIMP accDoDefaultAction( VARIANT varChild );
  125. private:
  126.     //  CAccessibleBase overrides
  127.     UINT GetDefaultActionStringID() const   { return IDS_LINKWINDOW_DEFAULTACTION; }
  128.     //  Private types
  129.     struct RECTLISTENTRY          // rect list member
  130.     {
  131.         RECT            rc;
  132.         RECTLISTENTRY*  next;
  133.     };
  134.     struct TEXTBLOCK              // text segment data
  135.     {
  136.         int             iLink;   // index of link (INVALID_LINK_INDEX if static text)
  137.         DWORD           state;   // state bits
  138.         TCHAR           szID[MAX_LINKID_TEXT]; // link identifier.
  139.         TEXTBLOCK*      next;    // next block
  140.         RECTLISTENTRY*  rgrle;   // list of bounding rectangle(s)
  141.         TCHAR*          pszText; // text
  142.         TCHAR*          pszUrl;  // URL.
  143.         TEXTBLOCK();
  144.         ~TEXTBLOCK();
  145.         void AddRect( const RECT& rc );
  146.         void FreeRects();
  147.     };
  148.     //  Utility methods
  149.     BOOL    CreateFonts( BOOL bRecreate = FALSE );
  150.     void    DestroyFonts();
  151.     HCURSOR GetLinkCursor();
  152.     void        Parse( LPCTSTR pszText = NULL );
  153.     BOOL        Add( TEXTBLOCK* pAdd );
  154.     TEXTBLOCK*  FindLink( int iLink ) const;
  155.     void        FreeBlocks();
  156.     void    SetText( LPCTSTR pszText );
  157.     int     GetText( BOOL bForParsing, LPTSTR pszText, int cchText ) const;
  158.     int     GetTextW( BOOL bForParsing, LPWSTR pwszText, int cchText ) const;
  159.     int     GetTextLength( BOOL bForParsing ) const;
  160.     void    Paint( HDC hdc, IN OPTIONAL LPCRECT prcClient = NULL, LPCRECT prcClip = NULL );
  161.     int     CalcIdealHeight( int cx );
  162.     int     HitTest( const POINT& pt ) const;
  163.     BOOL    WantTab( BOOL* biFocus = NULL ) const;
  164.     void    AssignTabFocus( int nDirection );
  165.     int     GetNextEnabledLink( int iStart, int nDir ) const;
  166.     int     StateCount( DWORD dwStateMask, DWORD dwState ) const;
  167.     LONG    EnableNotifications( BOOL bEnable );
  168.     static  TEXTBLOCK* CreateBlock( LPCTSTR pszStart, LPCTSTR pszEnd, int iLink );
  169.     
  170.     //  Message handlers
  171.     static  LRESULT WINAPI WndProc( HWND, UINT, WPARAM, LPARAM );
  172.     void    OnButtonDown( WPARAM fwKeys, const POINT& pt );
  173.     void    OnButtonUp( WPARAM fwKeys, const POINT& pt );
  174.     void    OnCaptureLost( HWND hwndNew ) {RESET_CAPTURE();}
  175.     LRESULT OnFocus( HWND hwndPrev );
  176.     void    OnKeyDown( UINT virtKey );
  177.     LRESULT SendNotify( UINT nCode, int iLink, LPCTSTR pszLinkID = NULL ) const;
  178.     LRESULT GetItem( OUT LWITEM* pItem );
  179.     LRESULT SetItem( IN LWITEM* pItem );
  180.     //  Data
  181.     TEXTBLOCK*   _rgBlocks;        // linked list of text blocks
  182.     int          _cBlocks;         // block count
  183.     int          _cLinks;          // link count
  184.     int          _iFocus;          // index of focus link
  185.     int          _cyIdeal;
  186.     LPTSTR       _pszCaption;      
  187.     HWND         _hwnd;
  188.     HFONT        _hfStatic, 
  189.                  _hfLink;
  190.     UINT         _fCapture;
  191.     UINT         _fKeyboardCues;                 
  192.     POINT        _ptCapture;
  193.     HCURSOR      _hcurHand;
  194.     LONG         _cNotifyLocks;
  195.     friend BOOL LinkWindow_RegisterClass();
  196. };
  197. //-------------------------------------------------------------------------//
  198. LPTSTR _AllocAndCopy( LPTSTR& pszDest, LPCTSTR pszSrc )
  199. {
  200.     if( pszDest )
  201.     {
  202.         delete [] pszDest;
  203.         pszDest = NULL;
  204.     }
  205.     if( pszSrc && (pszDest = new TCHAR[lstrlen(pszSrc)+1]) != NULL )
  206.         lstrcpy( pszDest, pszSrc );
  207.     return pszDest;
  208. }
  209. BOOL _AssignBit( const DWORD dwBit, DWORD& dwDest, const DWORD dwSrc )    // returns TRUE if changed
  210. {
  211.     if( ((dwSrc & dwBit) != 0) != ((dwDest & dwBit) != 0) )
  212.     {
  213.         if( ((dwSrc & dwBit) != 0) )
  214.             dwDest |= dwBit;
  215.         else
  216.             dwDest &= ~dwBit;
  217.         return TRUE;
  218.     }
  219.     return FALSE;
  220. }
  221. //-------------------------------------------------------------------------//
  222. BOOL WINAPI LinkWindow_RegisterClass()
  223. {
  224.     WNDCLASSEX wc;
  225.     ZeroMemory( &wc, sizeof(wc) );
  226.     wc.cbSize        = sizeof(wc);
  227.     wc.style         = CS_GLOBALCLASS;
  228.     wc.lpfnWndProc   = CLinkWindow::WndProc;
  229.     wc.hInstance     = HINST_THISDLL;
  230.     wc.hIcon         = NULL;
  231.     wc.hCursor       = LoadCursor( NULL, IDC_ARROW );
  232.     wc.hbrBackground = (HBRUSH)(LINKCOLOR_BKGND+1);
  233.     wc.lpszClassName = LINKWINDOW_CLASS;
  234.     return ::RegisterClassEx( &wc ) != 0 || 
  235.              GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
  236. }
  237. //-------------------------------------------------------------------------//
  238. BOOL WINAPI LinkWindow_UnregisterClass( HINSTANCE hInst )
  239. {
  240.     return ::UnregisterClass( LINKWINDOW_CLASS, hInst );
  241. }
  242. //-------------------------------------------------------------------------//
  243. CLinkWindow::CLinkWindow()
  244.     :   CAccessibleBase( _hwnd ),
  245.         _rgBlocks(NULL),
  246.         _cBlocks(0),
  247.         _cLinks(0),
  248.         _cyIdeal(0),
  249.         _hwnd(NULL),
  250.         _hfStatic(NULL),
  251.         _hfLink(NULL),
  252.         _iFocus(INVALID_LINK_INDEX),
  253.         _fCapture(0),
  254.         _pszCaption(NULL),
  255.         _fKeyboardCues(0),
  256.         _hcurHand(NULL),
  257.         _cNotifyLocks(0)
  258. {
  259.     _ptCapture.x = _ptCapture.y = 0;
  260. }
  261. //-------------------------------------------------------------------------//
  262. CLinkWindow::~CLinkWindow()
  263. {
  264.     FreeBlocks();
  265.     DestroyFonts();
  266.     SetText( NULL );
  267. }
  268. //-------------------------------------------------------------------------//
  269. //  CLinkWindow IAccessible impl
  270. //
  271. //  Note: Currently, this IAccessible implementation does not supports only
  272. //  single links; multiple links are not supported.   All child delegation
  273. //  is to/from self.  This allows us to blow off the IEnumVARIANT and IDispatch
  274. //  implementations.
  275. //
  276. //  To shore this up the implementation, we need to implement each link
  277. //  as a child IAccessible object and delegate accordingly.
  278. //
  279. STDMETHODIMP CLinkWindow::get_accName( VARIANT varChild, BSTR* pbstrName)
  280. {
  281.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  282.     if( NULL == pbstrName )
  283.         return E_POINTER;
  284.     *pbstrName = 0;
  285.     int cch = GetTextLength( FALSE );
  286.     if( (*pbstrName = SysAllocStringLen(NULL, cch + 1)) != NULL )
  287.     {
  288.         GetTextW( FALSE, *pbstrName, cch + 1 );
  289.         return S_OK;
  290.     }
  291.     return E_OUTOFMEMORY;
  292. }
  293. STDMETHODIMP CLinkWindow::accDoDefaultAction( VARIANT varChild )
  294. {
  295.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  296.     SendNotify( NM_RETURN, _iFocus );
  297.     return S_OK;
  298. }
  299. //-------------------------------------------------------------------------//
  300. //  CLinkWindow window implementation
  301. //-------------------------------------------------------------------------//
  302. //-------------------------------------------------------------------------//
  303. void CLinkWindow::FreeBlocks()
  304. {
  305.     for( TEXTBLOCK* pBlock = _rgBlocks; pBlock;  )
  306.     {
  307.         TEXTBLOCK* pNext = pBlock->next;
  308.         delete pBlock;
  309.         pBlock = pNext;
  310.     }
  311.     _rgBlocks = NULL;
  312.     _cBlocks = _cLinks = 0;
  313. }
  314. //-------------------------------------------------------------------------//
  315. CLinkWindow::TEXTBLOCK* CLinkWindow::CreateBlock( LPCTSTR pszStart, LPCTSTR pszEnd, int iLink )
  316. {
  317.     TEXTBLOCK* pBlock = NULL;
  318.     int cch = (int)(pszEnd - pszStart) + 1;
  319.     if( cch > 0 )
  320.     {
  321.         if( (pBlock = new TEXTBLOCK) != NULL )
  322.         {
  323.             if( (pBlock->pszText = new TCHAR[cch]) == NULL )
  324.             {
  325.                 delete pBlock;
  326.                 pBlock = NULL;
  327.             }
  328.             else
  329.             {
  330.                 lstrcpyn( pBlock->pszText, pszStart, cch );
  331.                 pBlock->iLink = iLink;
  332.             }
  333.         }
  334.     }
  335.     return pBlock;
  336. }
  337. //-------------------------------------------------------------------------//
  338. BOOL CLinkWindow::CreateFonts( BOOL bRecreate )
  339. {
  340.     if( _hfStatic && _hfLink && !bRecreate )
  341.         return TRUE;
  342.     
  343.     BOOL  bRet = FALSE;
  344.     HFONT hfStatic = NULL;
  345.     for( HWND hwnd = _hwnd; NULL == hfStatic && hwnd != NULL; hwnd = GetParent(hwnd) )
  346.         hfStatic = (HFONT)::SendMessage( hwnd, WM_GETFONT, 0, 0L );
  347.     
  348.     if( hfStatic )
  349.     {
  350.         DestroyFonts();
  351.         LOGFONT lf;
  352.         if( GetObject( hfStatic, sizeof(lf), &lf ) )
  353.         {
  354.             //  static text has no underline
  355.             lf.lfUnderline = FALSE;
  356.             _hfStatic = CreateFontIndirect( &lf );
  357.             //  link text has underline
  358.             lf.lfUnderline = TRUE;
  359.             _hfLink = CreateFontIndirect( &lf );
  360.             bRet = _hfLink != NULL && _hfStatic != NULL;
  361.         }
  362.     }
  363.     return bRet;
  364. }
  365. //-------------------------------------------------------------------------//
  366. HCURSOR CLinkWindow::GetLinkCursor()
  367. {
  368.     if( !_hcurHand )
  369.         _hcurHand = LoadHandCursor(0);
  370.     return _hcurHand;
  371. }
  372. //-------------------------------------------------------------------------//
  373. void CLinkWindow::DestroyFonts()
  374. {
  375.     if( _hfStatic )
  376.     {
  377.         DeleteObject( _hfStatic );
  378.         _hfStatic = NULL;
  379.     }
  380.     if( _hfLink )
  381.     {
  382.         DeleteObject( _hfLink );
  383.         _hfLink = NULL;
  384.     }
  385. }
  386. //-------------------------------------------------------------------------//
  387. void CLinkWindow::Parse( LPCTSTR pszText )
  388. {
  389.     TEXTBLOCK*  pBlock;
  390.     int         cBlocks = 0, cLinks  = 0;
  391.     LPCTSTR     psz1, psz2, pszBlock;
  392.     LPTSTR      pszBuf = NULL;
  393.     FreeBlocks(); // free existing blocks
  394.     
  395.     if( !pszText )
  396.     {
  397.         int cch = GetWindowTextLength( _hwnd )+1;
  398.         if( cch <= 0 ||
  399.             (pszBuf = new TCHAR[cch+1]) == NULL )
  400.             goto exit;
  401.         GetWindowText( _hwnd, pszBuf, cch );
  402.     }
  403.     else
  404.         pszBuf = (LPTSTR)pszText;
  405.     
  406.     if( !(pszBuf && *pszBuf) )
  407.         goto exit;
  408.     #define LINKTAG1L    TEXT("<a>")
  409.     #define LINKTAG1U    TEXT("<A>")
  410.     #define cchLINKTAG1  3
  411.     #define LINKTAG2L    TEXT("</a>")
  412.     #define LINKTAG2U    TEXT("</A>")
  413.     #define cchLINKTAG2  4
  414.     for( pszBlock = pszBuf; pszBlock && *pszBlock; )
  415.     {
  416.         //  Search for "<a>" tag
  417.         if( ((psz1 = StrStrI( pszBlock, LINKTAG1L )) != NULL ||
  418.              (psz1 = StrStrI( pszBlock, LINKTAG1U )) != NULL) )
  419.         {
  420.             //  Add run between psz1 and pszBlock as static text
  421.             if( psz1 > pszBlock )
  422.             {
  423.                 if( (pBlock = CreateBlock( pszBlock, psz1, INVALID_LINK_INDEX )) != NULL )
  424.                 {
  425.                     Add( pBlock );
  426.                     cBlocks++;
  427.                 }
  428.             }
  429.         
  430.             //  safe-skip over tag
  431.             for( int i = 0; i < cchLINKTAG1 && psz1 && *psz1; 
  432.                  i++, psz1 = CharNext(psz1) );
  433.             pszBlock = psz1;
  434.             if( psz1 && *psz1 )
  435.             {
  436.                 if( (psz2 = StrStrI( pszBlock, LINKTAG2L )) != NULL ||
  437.                     (psz2 = StrStrI( pszBlock, LINKTAG2U )) != NULL )
  438.                 {
  439.                     if( (pBlock = CreateBlock( psz1, psz2, cLinks )) != NULL )
  440.                     {
  441.                         Add( pBlock );
  442.                         cBlocks++;
  443.                         cLinks++;
  444.                     }
  445.                     //  safe-skip over tag
  446.                     for( int i = 0; 
  447.                          i < cchLINKTAG2 && psz2 && *psz2; 
  448.                          i++, psz2 = CharNext(psz2) );
  449.                     pszBlock = psz2;
  450.                 }
  451.                 else // syntax error; mark trailing run is static text.
  452.                 {
  453.                     psz2 = pszBlock + lstrlen( pszBlock );
  454.                     if( (pBlock = CreateBlock( psz1, psz2, INVALID_LINK_INDEX )) != NULL )
  455.                     {
  456.                         Add( pBlock );
  457.                         cBlocks++;
  458.                     }
  459.                     pszBlock = psz2;
  460.                 }
  461.             }
  462.         }
  463.         else // no more tags.  Mark the last run of static text
  464.         {
  465.             psz2 = pszBlock + lstrlen( pszBlock );
  466.             if( (pBlock = CreateBlock( pszBlock, psz2, INVALID_LINK_INDEX )) != NULL )
  467.             {
  468.                 Add( pBlock );
  469.                 cBlocks++;
  470.             }
  471.             pszBlock = psz2;
  472.         }
  473.     }
  474.     ASSERT( cBlocks == _cBlocks );
  475.     ASSERT( cLinks  == _cLinks );
  476. exit:
  477.     if( !pszText && pszBuf ) // delete text buffer if we had alloc'd it.
  478.         delete [] pszBuf;
  479. }
  480. //-------------------------------------------------------------------------//
  481. BOOL CLinkWindow::Add( TEXTBLOCK* pAdd )
  482. {
  483.     BOOL bAdded = FALSE;
  484.     pAdd->next = NULL;
  485.     if( !_rgBlocks )    {
  486.         _rgBlocks = pAdd;
  487.         bAdded = TRUE;
  488.     }
  489.     else    {    
  490.         for( TEXTBLOCK* pBlock = _rgBlocks; pBlock && !bAdded; pBlock = pBlock->next )   {
  491.             if( !pBlock->next ) {
  492.                 pBlock->next = pAdd;
  493.                 bAdded = TRUE;
  494.             }
  495.         }
  496.     }
  497.     if( bAdded )    {
  498.         _cBlocks++;
  499.         if( IS_LINK( pAdd ) )
  500.             _cLinks++;
  501.     }
  502.     return bAdded;
  503. }
  504. //-------------------------------------------------------------------------//
  505. CLinkWindow::TEXTBLOCK*  CLinkWindow::FindLink( int iLink ) const
  506. {
  507.     if( iLink == INVALID_LINK_INDEX )
  508.         return NULL;
  509.     for( TEXTBLOCK* pBlock = _rgBlocks; pBlock; pBlock = pBlock->next )
  510.     {
  511.         if( IS_LINK( pBlock ) && pBlock->iLink == iLink )
  512.             return pBlock;
  513.     }
  514.     return NULL;
  515. }
  516. //-------------------------------------------------------------------------//
  517. int _IsLineBreakChar( LPCTSTR psz, int ich, TCHAR chBreak, OUT BOOL* pbRemove )
  518. {
  519.     LPTSTR pch;
  520.     *pbRemove = FALSE;
  521.     ASSERT( psz != NULL )
  522.     ASSERT( psz[ich] != 0 );
  523.     
  524.     //  Try caller-provided break character (assumed a 'remove' break char).
  525.     if( psz[ich] == chBreak )
  526.     {
  527.         *pbRemove = TRUE;
  528.         return ich;
  529.     }
  530.     #define MAX_LINEBREAK_RESOURCE   128
  531.     static TCHAR _szBreakRemove   [MAX_LINEBREAK_RESOURCE] = {0};
  532.     static TCHAR _szBreakPreserve [MAX_LINEBREAK_RESOURCE] = {0};
  533.     #define LOAD_BREAKCHAR_RESOURCE( nIDS, buff ) 
  534.         if(0==*buff) { LoadString(HINST_THISDLL, nIDS, buff, ARRAYSIZE(buff)); }
  535.     //  Try 'remove' break chars
  536.     LOAD_BREAKCHAR_RESOURCE( IDS_LINEBREAK_REMOVE, _szBreakRemove );
  537.     for( pch = _szBreakRemove; *pch; pch = CharNext(pch) )
  538.     {
  539.         if( psz[ich] == *pch )
  540.         {
  541.             *pbRemove = TRUE;
  542.             return ich;
  543.         }
  544.     }
  545.     //  Try 'preserve prior' break chars:
  546.     LOAD_BREAKCHAR_RESOURCE( IDS_LINEBREAK_PRESERVE, _szBreakPreserve );
  547.     for( pch = _szBreakPreserve; *pch; pch = CharNext(pch) )
  548.     {
  549.         if( psz[ich] == *pch )
  550.             return ++ich;
  551.     }
  552.     return -1;
  553. }
  554. //-------------------------------------------------------------------------//
  555. BOOL _FindLastBreakChar( 
  556.     IN LPCTSTR pszText, 
  557.     IN int cchText, 
  558.     IN TCHAR chBreak,   // official break char (from TEXTMETRIC).
  559.     OUT int* piLast, 
  560.     OUT BOOL* pbRemove )
  561. {
  562.     *piLast   = 0;
  563.     *pbRemove = FALSE;
  564.     for( int i = cchText-1; i >= 0; i-- )
  565.     {
  566. #ifndef UNICODE
  567.         if( IsDBCSLeadByte( pszText[i] ) )
  568.             continue;
  569. #endif
  570.         int ich = _IsLineBreakChar( pszText, i, chBreak, pbRemove );
  571.         if( ich >= 0 )
  572.         {
  573.             *piLast = ich;
  574.             return TRUE;
  575.         }
  576.     }
  577.     return FALSE;
  578. }
  579. //-------------------------------------------------------------------------//
  580. int CLinkWindow::CalcIdealHeight( int cx )
  581. {
  582.     int   cyRet = -1;
  583.     HDC   hdc;
  584.     RECT  rc;
  585.     SIZE  sizeDC;
  586.     if( NULL == _rgBlocks || 0 == _cBlocks )
  587.         return -1;
  588.     
  589.     GetClientRect( _hwnd, &rc );
  590.     if( cx <= 0 )
  591.         cx = RECTWIDTH( &rc );
  592.     else
  593.         rc.right = cx;
  594.     if( cx <= 0 )
  595.         return -1;
  596.     //  Come up with a conservative estimate for the new height.
  597.     sizeDC.cy = MulDiv( RECTHEIGHT( &rc ), cx, RECTWIDTH( &rc ) ) * 2;
  598.     sizeDC.cx = cx;
  599.     if( (hdc = GetDC( _hwnd )) != NULL )
  600.     {
  601.         // prepare memory DC
  602.         HDC hdcMem;
  603.         if( (hdcMem = CreateCompatibleDC( hdc )) )
  604.         {
  605.             // [scotthan]: BUGBUG - Probably don't need anything but a monochrome DC
  606.             // but should test before removing the code.
  607.             HBITMAP hbm = CreateCompatibleBitmap( hdc, sizeDC.cx, sizeDC.cy );
  608.             HBITMAP hbmPrev = (HBITMAP)SelectObject( hdcMem, hbm );
  609.             
  610.             int cyPrev = _cyIdeal;   // push ideal
  611.             //  paint into memory DC to determine height
  612.             SetRect( &rc, 0, 0, sizeDC.cx, sizeDC.cy );
  613.             Paint( hdcMem, &rc );
  614.             cyRet = _cyIdeal;
  615.             _cyIdeal = cyPrev;       // pop ideal
  616.             SelectObject( hdcMem, hbmPrev );
  617.             DeleteObject( hbm );
  618.             DeleteDC( hdcMem );
  619.         }
  620.         ReleaseDC( _hwnd, hdc );
  621.     }
  622.     return cyRet;
  623. }
  624. //-------------------------------------------------------------------------//
  625. void CLinkWindow::Paint( HDC hdcClient, LPCRECT prcClient, LPCRECT prcClip )
  626. {
  627.     RECT rcClient;
  628.     if( !prcClient )
  629.     {
  630.         GetClientRect( _hwnd, &rcClient );
  631.         prcClient = &rcClient;
  632.     }
  633.     if( RECTWIDTH( prcClient )<=0 ||RECTHEIGHT( prcClient )<=0 )
  634.         return;
  635.     HDC             hdc = hdcClient ? hdcClient : GetDC( _hwnd );
  636.     TEXTBLOCK*      pBlock;
  637.     TEXTMETRIC      tm;
  638.     int             iLine = 0,  // current line index         
  639.                     cyLine = 0, // line height.
  640.                     cyLeading = 0; // internal leading
  641.     COLORREF        rgbOld = GetTextColor( hdc );  // save text color
  642.     RECT            rcFill, 
  643.                     rcDraw = *prcClient;             // initialize line rect
  644.     const ULONG     dwFlags = DT_TOP|DT_LEFT;
  645.     BOOL            fFocus = GetFocus()==_hwnd;
  646.     //  initialize background
  647.     SendMessage( GetParent( _hwnd ), WM_CTLCOLORSTATIC, 
  648.                  (WPARAM)hdc, (LPARAM)_hwnd );
  649.     SetBkMode( hdc, OPAQUE ); 
  650.     _cyIdeal = 0;
  651.     //  For each block of text...
  652.     for( pBlock = _rgBlocks; pBlock; pBlock = pBlock->next )
  653.     {
  654.         BOOL bLink = IS_LINK(pBlock);
  655.         SelectObject( hdc, bLink ? _hfLink : _hfStatic );
  656.         int  cchDraw = lstrlen( pBlock->pszText ), // chars to draw, this block
  657.              cchDrawn = 0;  // chars to draw, this block
  658.         LPTSTR pszText = &pBlock->pszText[cchDrawn];
  659.         
  660.         pBlock->FreeRects();   // free hit/focus rects; we're going to recompute.
  661.         
  662.         //  Get font metrics
  663.         GetTextMetrics( hdc, &tm );
  664.         if( tm.tmExternalLeading > cyLeading )
  665.             cyLeading = tm.tmExternalLeading;
  666.         //  initialize foreground color
  667.         if( bLink )
  668.         {
  669.             BOOL bEnabled = pBlock->state & LWIS_ENABLED;
  670.             SetTextColor( hdc, bEnabled ? LINKCOLOR_ENABLED : LINKCOLOR_DISABLED );
  671.         }
  672.         else
  673.             SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) );
  674.         //  while text remains...
  675.         while( cchDraw > 0 )
  676.         {
  677.             //  compute line height and maximum text width to rcBlock
  678.             RECT rcBlock;
  679.             BOOL bShy = FALSE;
  680.             int  cchTry = cchDraw;
  681.             int  cchFit = 0;
  682.             SIZE sizeFit;
  683.             BOOL bRemoveBreak = FALSE;
  684.             
  685.             for(;;)
  686.             {
  687.                 if( !GetTextExtentExPoint( hdc, pszText, cchTry, RECTWIDTH(&rcDraw),
  688.                                            &cchFit, NULL, &sizeFit ) )
  689.                 {
  690.                     cchTry--;
  691.                     continue;
  692.                 }
  693.                 else if( cchFit < cchTry )
  694.                 {
  695.                     BOOL fBreak = _FindLastBreakChar( pszText, cchFit, tm.tmBreakChar, &cchTry, &bRemoveBreak );
  696.                     if( 0 == cchTry )
  697.                     {
  698.                         if( !fBreak && (0 == rcDraw.left /* nothing drawn on this line*/) )
  699.                             cchTry = cchFit; // no break character found, so force a break.
  700.                     }
  701.                     else
  702.                     {
  703.                         GetTextExtentExPoint( hdc, pszText, cchTry, RECTWIDTH(&rcDraw),
  704.                                               &cchFit, NULL, &sizeFit );
  705.                     }
  706.                     break;
  707.                 }
  708.                 else
  709.                     break;
  710.             }
  711.             
  712.             cyLine = sizeFit.cy;
  713.             SetRect( &rcBlock, 0, 0, sizeFit.cx, sizeFit.cy );
  714.             OffsetRect( &rcBlock, rcDraw.left - rcBlock.left, 0 );
  715.                 
  716.             //  initialize drawing rectangle
  717.             rcDraw.right  = min( rcDraw.left + RECTWIDTH(&rcBlock), prcClient->right );
  718.             rcDraw.bottom = rcDraw.top + cyLine;
  719.             
  720.             //  draw the text
  721.             ExtTextOut( hdc, rcDraw.left, rcDraw.top, ETO_OPAQUE|ETO_CLIPPED,
  722.                         &rcDraw, pszText, cchTry, NULL );
  723.             //  Add rectangle to block's list
  724.             if( bLink && cchTry )
  725.                 pBlock->AddRect( rcDraw );
  726.             cchDrawn = cchTry;
  727.             if( cchTry < cchDraw ) // we got clipped
  728.             {
  729.                 //  fill line to right boundary
  730.                 SetRect( &rcFill, rcDraw.right, rcDraw.top, prcClient->right, rcDraw.bottom );
  731.                 ExtTextOut( hdc, rcFill.left, rcFill.top, ETO_OPAQUE,
  732.                             &rcFill, NULL, 0, NULL );
  733.                 //  adjust text
  734.                 if( bRemoveBreak )
  735.                     cchDrawn++;
  736.                 pszText += cchDrawn;
  737.                 //  advance to next line
  738.                 iLine++;
  739.                 rcDraw.left = 0;
  740.                 rcDraw.top  = iLine * cyLine;
  741.                 rcDraw.bottom = rcDraw.top + cyLine + cyLeading;
  742.                 rcDraw.right = prcClient->right;
  743.             }
  744.             else //  we were able to draw the entire text
  745.             {
  746.                 //  adjust drawing rectangle
  747.                 rcDraw.left += RECTWIDTH(&rcBlock);
  748.                 rcDraw.right = prcClient->right;
  749.                 //  if this is the last block of text, fill line to right boundary
  750.                 if( pBlock->next == NULL )
  751.                 {
  752.                     rcFill = rcDraw;
  753.                     rcFill.right = prcClient->right;
  754.                     ExtTextOut( hdc, rcFill.left, rcFill.top, ETO_OPAQUE,
  755.                                 &rcFill, NULL, 0, NULL );
  756.                 }
  757.             }
  758.             _cyIdeal = rcDraw.bottom;
  759.             cchDraw -= cchDrawn;
  760.         }
  761.         //  Draw focus rect(s)
  762. #ifdef KEYBOARDCUES
  763.         if( 0 == (_fKeyboardCues & UISF_HIDEFOCUS) )
  764. #endif
  765.         {
  766.             if( fFocus && pBlock->iLink == _iFocus )
  767.             {
  768.                 HBRUSH hbr, hbrOld;
  769.                 COLORREF rgbBkgnd = GetBkColor( hdc );
  770.                 if( (hbr = CreateSolidBrush( rgbBkgnd )) )
  771.                 {
  772.                     hbrOld = (HBRUSH)SelectObject( hdc, hbr );
  773.                 
  774.                     for( RECTLISTENTRY* prce = pBlock->rgrle; prce && hbr; prce = prce->next )
  775.                     {
  776.                         RECT rc = prce->rc;
  777.                         FrameRect( hdc, &rc, hbr );
  778.                         SetTextColor( hdc, rgbOld );
  779.                         DrawFocusRect( hdc, &rc );
  780.                     }
  781.                     SelectObject( hdc, hbrOld );
  782.                     DeleteObject( hbr );
  783.                 }
  784.             }
  785.         }
  786.     }
  787.     //  Fill remainder of client rect.
  788.     RECT rcX;
  789.     rcFill = *prcClient;
  790.     rcFill.top = rcDraw.top + cyLine;
  791.     if( prcClip == NULL )
  792.     {
  793.         ExtTextOut( hdc, rcFill.left, rcFill.right, ETO_OPAQUE,
  794.                     &rcFill, NULL, 0, NULL );
  795.     }
  796.     else if( IntersectRect( &rcX, prcClip, &rcFill ) )
  797.     {
  798.         ExtTextOut( hdc, rcX.left, rcX.right, ETO_OPAQUE,
  799.                     &rcX, NULL, 0, NULL );
  800.     }
  801.     SetTextColor( hdc, rgbOld );   // restore text color
  802.     if( NULL == hdcClient && hdc )  // release DC if we acquired it.
  803.         ReleaseDC( _hwnd, hdc );
  804. }
  805. //-------------------------------------------------------------------------//
  806. int CLinkWindow::HitTest( const POINT& pt ) const
  807. {
  808.     //  Walk blocks until we find a link rect that contains the point
  809.     TEXTBLOCK* pBlock;
  810.     for( pBlock = _rgBlocks; pBlock; pBlock = pBlock->next )
  811.     {
  812.         if( IS_LINK(pBlock) && (pBlock->state & LWIS_ENABLED)!=0 )
  813.         {
  814.             RECTLISTENTRY* prce;
  815.             for( prce = pBlock->rgrle; prce; prce = prce->next )
  816.             {
  817.                 if( PtInRect( &prce->rc, pt ) )
  818.                 {
  819.                     return pBlock->iLink;
  820.                 }
  821.             }
  822.         }
  823.     }
  824.     return INVALID_LINK_INDEX;
  825. }
  826. //-------------------------------------------------------------------------//
  827. LRESULT CLinkWindow::SetItem( IN LWITEM* pItem )
  828. {
  829.     TEXTBLOCK*  pBlock;
  830.     BOOL        bRedraw = FALSE;
  831.     LRESULT     lRet = 0L;
  832.     if( NULL == pItem || 0 == (pItem->mask & LWIF_ITEMINDEX) )
  833.         return lRet; //BUGBUG: need to open up search keys to LWIF_ITEMID and LWIF_URL.
  834.     if( (pBlock = FindLink( pItem->iLink )) != NULL )
  835.     {
  836.         if( pItem->mask & LWIF_STATE )
  837.         {
  838.             if( pItem->stateMask & LWIS_ENABLED )
  839.             {
  840.                 bRedraw |= _AssignBit( LWIS_ENABLED, pBlock->state, pItem->state );
  841.                 BOOL bEnabled = IsWindowEnabled( _hwnd );
  842.                 int  cEnabledLinks = StateCount( LWIS_ENABLED, LWIS_ENABLED );
  843.                 if( bEnabled )
  844.                 {
  845.                     if( bEnabled && 0 == cEnabledLinks )
  846.                         EnableWindow( _hwnd, FALSE );
  847.                     else if( !bEnabled && cEnabledLinks!=0 )
  848.                         EnableWindow( _hwnd, TRUE );
  849.                 }
  850.             }
  851.             if( pItem->stateMask & LWIS_VISITED )
  852.                 bRedraw |= _AssignBit( LWIS_VISITED, pBlock->state, pItem->state );
  853.             if( pItem->stateMask & LWIS_FOCUSED )
  854.             {
  855.                 //  Focus assignment is handled differently;
  856.                 //  one and only one link can have focus...
  857.                 if( pItem->state & LWIS_FOCUSED )
  858.                 {
  859.                     bRedraw |= (_iFocus != pItem->iLink);
  860.                     _iFocus = pItem->iLink;
  861.                 }
  862.                 else
  863.                 {
  864.                     bRedraw |= (_iFocus == pItem->iLink);
  865.                     _iFocus = INVALID_LINK_INDEX;
  866.                 }
  867.             }
  868.         }
  869.         if( pItem->mask & LWIF_ITEMID )
  870.         {
  871.             lstrcpyn( pBlock->szID, pItem->szID, sizeof(pBlock->szID) );
  872.             lRet = 1L;        
  873.         }
  874.         if( pItem->mask & LWIF_URL )
  875.         {
  876.             _AllocAndCopy( pBlock->pszUrl, pItem->szUrl );
  877.             lRet = 1L;
  878.         }
  879.     }
  880.     if( bRedraw )
  881.     {
  882.         InvalidateRect( _hwnd, NULL, TRUE );
  883.         UpdateWindow( _hwnd );
  884.     }
  885.     return lRet;
  886. }
  887. //-------------------------------------------------------------------------//
  888. LRESULT CLinkWindow::GetItem( OUT LWITEM* pItem )
  889. {
  890.     TEXTBLOCK*  pBlock;
  891.     LRESULT     lRet = 0L;
  892.     if( NULL == pItem || 0 == (pItem->mask & LWIF_ITEMINDEX) )
  893.         return lRet; //BUGBUG: need to open up search keys to LWIF_ITEMID and LWIF_URL.
  894.     if( (pBlock = FindLink( pItem->iLink )) != NULL )
  895.     {
  896.         if( pItem->mask & LWIF_STATE )
  897.         {
  898.             pItem->state = 0L;
  899.             if( pItem->stateMask & LWIS_FOCUSED )
  900.             {
  901.                 if( _iFocus == pItem->iLink )
  902.                     pItem->state |= LWIS_FOCUSED;
  903.             }
  904.             if( pItem->stateMask & LWIS_ENABLED )
  905.             {
  906.                 if( pBlock->state & LWIS_ENABLED )
  907.                     pItem->state |= LWIS_ENABLED;
  908.             }
  909.             if( pItem->stateMask & LWIS_VISITED )
  910.             {
  911.                 if( pBlock->state & LWIS_VISITED )
  912.                     pItem->state |= LWIS_VISITED;
  913.             }
  914.         }
  915.         if( pItem->mask & LWIF_ITEMID )
  916.         {
  917.             lstrcpyn( pItem->szID, pBlock->szID, sizeof(pBlock->szID) );
  918.             lRet = 1L;        
  919.         }
  920.         if( pItem->mask & LWIF_URL )
  921.         {
  922.             *pItem->szUrl = 0;
  923.             if( pBlock->pszUrl )
  924.                 lstrcpyn( pItem->szUrl, pBlock->pszUrl, sizeof(pItem->szUrl) );
  925.             lRet = 1L;
  926.         }
  927.     }
  928.     return lRet;
  929. }
  930. //-------------------------------------------------------------------------//
  931. void CLinkWindow::OnButtonDown( WPARAM fwKeys, const POINT& pt )
  932. {
  933.     int iLink;
  934.     if( (iLink = HitTest( pt )) != INVALID_LINK_INDEX )
  935.     {
  936.         SetCursor( GetLinkCursor() );
  937.         _iFocus = iLink;
  938.         MODIFY_CAPTURE( CF_SETCAPTURE, 0 );
  939.         _ptCapture = pt;
  940.         if( GetFocus() != _hwnd )
  941.         {
  942.             MODIFY_CAPTURE( CF_SETFOCUS, 0 );
  943.             EnableNotifications( FALSE ); // so the host doesn't reposition the link.
  944.             SetFocus( _hwnd );
  945.             EnableNotifications( TRUE );
  946.         }
  947.         InvalidateRect( _hwnd, NULL, FALSE );
  948.         SetCapture( _hwnd );
  949.     }
  950. }
  951. //-------------------------------------------------------------------------//
  952. void CLinkWindow::OnButtonUp( WPARAM fwKeys, const POINT& pt )
  953. {
  954.     if( TEST_CAPTURE(CF_SETCAPTURE) )
  955.     {
  956.         ReleaseCapture();
  957.         MODIFY_CAPTURE( 0, CF_SETCAPTURE );
  958.         //  if the focus link contains the point, we can 
  959.         //  notify the parent window of a click event.
  960.         TEXTBLOCK* pBlock;
  961.         if( (pBlock = FindLink( _iFocus )) != NULL && 
  962.             (pBlock->state & LWIS_ENABLED) != 0 &&
  963.             _iFocus == HitTest( pt ) )
  964.         {
  965.             SendNotify( NM_CLICK, _iFocus );        
  966.         }
  967.     }
  968.     if( TEST_CAPTURE(CF_SETFOCUS) )
  969.     {
  970.         MODIFY_CAPTURE( 0, CF_SETFOCUS );
  971.         if( GetFocus() == _hwnd ) // if we still have the focus...
  972.             SendNotify( NM_SETFOCUS, _iFocus );
  973.     }
  974. }
  975. //-------------------------------------------------------------------------//
  976. //  WM_SETTEXT handler
  977. void CLinkWindow::SetText( LPCTSTR pszText )
  978. {
  979.     if( pszText && _pszCaption && 0 == lstrcmp( pszText, _pszCaption ) )
  980.         return; // nothing to do.
  981.     
  982.     if( _pszCaption )
  983.     {
  984.         delete [] _pszCaption;
  985.         _pszCaption = NULL;
  986.     }
  987.     if( pszText && *pszText )
  988.     {
  989.         int cch = lstrlen( pszText );
  990.         if( (_pszCaption = new TCHAR[cch+1]) != NULL )
  991.             lstrcpy( _pszCaption, pszText );
  992.     }
  993. }
  994. //-------------------------------------------------------------------------//
  995. //  WM_GETTEXT handler
  996. int CLinkWindow::GetText( BOOL bForParsing, LPTSTR pszText, int cchText ) const
  997. {
  998.     int cchRet = 0;
  999.     if( pszText && cchText )
  1000.     {
  1001.         *pszText = 0;
  1002.         if( bForParsing )
  1003.         {
  1004.             if( _pszCaption && *_pszCaption &&
  1005.                 lstrcpyn( pszText, _pszCaption, cchText ) )
  1006.                 return lstrlen( pszText ) + 1;
  1007.         }
  1008.         else
  1009.         {
  1010.             TEXTBLOCK* pBlock;
  1011.             for( pBlock = _rgBlocks; cchText > 0 && pBlock; pBlock = pBlock->next )
  1012.             {
  1013.                 if( pBlock->pszText )
  1014.                 {
  1015.                     int cchBlock = lstrlen( pBlock->pszText );
  1016.                     StrNCat( pszText, pBlock->pszText, min( cchBlock + 1, cchText ) );
  1017.                     cchRet  += min( cchBlock, cchText );
  1018.                     cchText -= min( cchBlock, cchText );
  1019.                 }
  1020.             }
  1021.             if( cchRet )
  1022.                 cchRet++; // terminating NULL
  1023.         }
  1024.     }
  1025.     return cchRet;
  1026. }
  1027. //-------------------------------------------------------------------------//
  1028. //  WM_GETTEXT handler
  1029. int CLinkWindow::GetTextW( BOOL bForParsing, LPWSTR pwszText, int cchText ) const
  1030. {
  1031. #ifdef UNICODE
  1032.     return GetText( bForParsing, pwszText, cchText );
  1033. #else  UNICODE
  1034.     int   cchRet = 0;
  1035.     LPSTR pszText = new CHAR[cchText];
  1036.     
  1037.     if( pszText )
  1038.     {
  1039.         cchRet = GetText( bForParsing, pszText, cchText );
  1040.         if( cchRet )
  1041.         {
  1042.             SHAnsiToUnicode( pszText, pwszText, cchText );
  1043.         }
  1044.         delete [] pszText;
  1045.     }
  1046.     return cchRet;
  1047. #endif UNICODE
  1048. }
  1049. //-------------------------------------------------------------------------//
  1050. //  WM_GETTEXTLENGTH handler
  1051. int CLinkWindow::GetTextLength( BOOL bForParsing ) const
  1052. {
  1053.     int cnt = 0;
  1054.     if( bForParsing )
  1055.         cnt = (_pszCaption && *_pszCaption) ? lstrlen(_pszCaption) : 0;
  1056.     else
  1057.     {
  1058.         TEXTBLOCK* pBlock;
  1059.         for( pBlock = _rgBlocks; pBlock; pBlock = pBlock->next )
  1060.         {
  1061.             cnt += (pBlock->pszText && *pBlock->pszText) ? lstrlen(pBlock->pszText) : 0;
  1062.         }
  1063.     }
  1064.     return cnt;
  1065. }
  1066. //-------------------------------------------------------------------------//
  1067. LONG CLinkWindow::EnableNotifications( BOOL bEnable )
  1068. {
  1069.     if( bEnable )
  1070.     {
  1071.         if( _cNotifyLocks > 0 )
  1072.             _cNotifyLocks--;
  1073.     }
  1074.     else
  1075.         _cNotifyLocks++;
  1076.     
  1077.     return _cNotifyLocks;
  1078. }
  1079. //-------------------------------------------------------------------------//
  1080. LRESULT CLinkWindow::SendNotify( UINT nCode, int iLink, LPCTSTR pszLinkID ) const
  1081. {
  1082.     if( 0 == _cNotifyLocks )
  1083.     {
  1084.         NMLINKWND nm;
  1085.         ZeroMemory( &nm, sizeof(nm) );
  1086.         nm.hdr.hwndFrom = _hwnd;
  1087.         nm.hdr.idFrom   = (UINT_PTR)GetWindowLong( _hwnd, GWL_ID );
  1088.         nm.hdr.code     = nCode;
  1089.         nm.item.iLink   = iLink;
  1090.         if( pszLinkID && *pszLinkID )
  1091.             lstrcpyn( nm.item.szID, pszLinkID, ARRAYSIZE(nm.item.szID) );
  1092.         return SendMessage( GetParent( _hwnd ), WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm );
  1093.     }
  1094.     return 0L;
  1095. }
  1096. //-------------------------------------------------------------------------//
  1097. inline void MakePoint( LPARAM lParam, OUT LPPOINT ppt )
  1098. {
  1099.     POINTS pts = MAKEPOINTS( lParam );
  1100.     ppt->x = pts.x;
  1101.     ppt->y = pts.y;
  1102. }
  1103. //-------------------------------------------------------------------------//
  1104. int CLinkWindow::GetNextEnabledLink( int iStart, int nDir ) const
  1105. {
  1106.     ASSERT( -1 == nDir || 1 == nDir );
  1107.     if( _cLinks > 0 )
  1108.     {
  1109.         if( INVALID_LINK_INDEX == iStart )
  1110.             iStart = nDir > 0 ? -1 : _cLinks;
  1111.         for( iStart += nDir; iStart >= 0; iStart += nDir )
  1112.         {
  1113.             TEXTBLOCK* pBlock;
  1114.             if( NULL == (pBlock = FindLink( iStart )) )
  1115.                 return INVALID_LINK_INDEX;
  1116.             if( pBlock->state & LWIS_ENABLED )
  1117.             {
  1118.                 ASSERT( iStart == pBlock->iLink );
  1119.                 return iStart;
  1120.             }
  1121.         }
  1122.     }
  1123.     return INVALID_LINK_INDEX;
  1124. }
  1125. //-------------------------------------------------------------------------//
  1126. int CLinkWindow::StateCount( DWORD dwStateMask, DWORD dwState ) const
  1127. {
  1128.     TEXTBLOCK* pBlock;
  1129.     int cnt = 0;
  1130.     for( pBlock = _rgBlocks; pBlock; pBlock = pBlock->next )
  1131.     {
  1132.         if( IS_LINK(pBlock) && 
  1133.             (pBlock->state & dwStateMask) == dwState )
  1134.             cnt++;
  1135.     }
  1136.     return cnt;
  1137. }
  1138. //-------------------------------------------------------------------------//
  1139. BOOL CLinkWindow::WantTab( BOOL* biFocus ) const
  1140. {
  1141.     int nDir  = TESTKEYSTATE( VK_SHIFT ) ? -1 : 1;
  1142.     int iFocus = GetNextEnabledLink( _iFocus, nDir );
  1143.     if( INVALID_LINK_INDEX != iFocus )
  1144.     {
  1145.         if( biFocus )
  1146.             *biFocus = iFocus;
  1147.         return TRUE;
  1148.     }
  1149.     return FALSE;
  1150. }
  1151. //-------------------------------------------------------------------------//
  1152. //  WM_SETFOCUS handler
  1153. LRESULT CLinkWindow::OnFocus( HWND hwndPrev )
  1154. {
  1155.     if( !TEST_CAPTURE(CF_SETCAPTURE) ) // if we got focus by something other than a mouse click
  1156.     {
  1157.         int nDir = 0;
  1158.         if( TESTKEYSTATE( VK_TAB ) )
  1159.             nDir = TESTKEYSTATE( VK_SHIFT ) ? -1 : 1;
  1160.         AssignTabFocus( nDir ); // move internal focus
  1161.     }
  1162.     InvalidateRect( _hwnd, NULL, FALSE );
  1163.     SendNotify( NM_SETFOCUS, _iFocus ); // notify parent
  1164.     return 0L;
  1165. }
  1166. //-------------------------------------------------------------------------//
  1167. void CLinkWindow::AssignTabFocus( int nDirection )
  1168. {
  1169.     if( _cLinks )
  1170.     {
  1171.         if( 0 == nDirection )
  1172.         {
  1173.             if( INVALID_LINK_INDEX != _iFocus )
  1174.                 return;
  1175.             nDirection = 1;
  1176.         }
  1177.         _iFocus = GetNextEnabledLink( _iFocus, nDirection );
  1178.     }
  1179. }
  1180. //-------------------------------------------------------------------------//
  1181. //  WM_KEYDOWN handler
  1182. void CLinkWindow::OnKeyDown( UINT virtKey )
  1183. {
  1184.     switch( virtKey )
  1185.     {
  1186.         case VK_TAB:
  1187.             if( WantTab( &_iFocus ) )
  1188.                 InvalidateRect( _hwnd, NULL, FALSE );
  1189.             break;
  1190.         
  1191.         case VK_RETURN:
  1192.         case VK_SPACE:
  1193.             if( FindLink( _iFocus ) )
  1194.                 SendNotify( NM_RETURN, _iFocus );
  1195.             break;
  1196.     }
  1197. }
  1198. //-------------------------------------------------------------------------//
  1199. LRESULT WINAPI CLinkWindow::WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  1200. {
  1201.     LRESULT lRet = 0L;
  1202.     CLinkWindow* pThis = NULL;
  1203.     if( uMsg == WM_NCCREATE )
  1204.     {
  1205.         pThis = new CLinkWindow;
  1206.         if( NULL == pThis )
  1207.         {
  1208.             ASSERT( pThis );
  1209.             return FALSE;
  1210.         }
  1211.         pThis->_hwnd = hwnd;
  1212.         SetWindowPtr( hwnd, GWLP_USERDATA, pThis );
  1213.         return TRUE;
  1214.     }
  1215.     else
  1216.     {
  1217.         pThis = (CLinkWindow*)GetWindowPtr( hwnd, GWLP_USERDATA );
  1218.         ASSERT( pThis );
  1219.         ASSERT( pThis->_hwnd == hwnd );
  1220.     }
  1221.     switch( uMsg )
  1222.     {
  1223.         case WM_NCHITTEST:
  1224.         {
  1225.             POINT pt;
  1226.             MakePoint( lParam, &pt );
  1227.             MapWindowPoints( HWND_DESKTOP, hwnd, &pt, 1 );
  1228.             if( pThis->HitTest( pt ) != INVALID_LINK_INDEX )
  1229.                 return HTCLIENT;
  1230.             return HTTRANSPARENT;
  1231.         }
  1232.         case WM_PAINT:
  1233.         {
  1234.             PAINTSTRUCT ps;
  1235.             HDC         hdc;
  1236.             if( (hdc = BeginPaint( pThis->_hwnd, &ps )) != NULL )
  1237.             {
  1238.                 pThis->Paint( hdc );
  1239.                 EndPaint( pThis->_hwnd, &ps );
  1240.             }
  1241.             return lRet;
  1242.         }
  1243.         case WM_WINDOWPOSCHANGING:
  1244.         {
  1245.             WINDOWPOS* pwp = (WINDOWPOS*)lParam;
  1246.             RECT rc;
  1247.             GetClientRect( pThis->_hwnd, &rc );
  1248.             if( 0 == (pwp->flags & SWP_NOSIZE) &&
  1249.                 !( pwp->cx == RECTWIDTH(&rc) &&
  1250.                    pwp->cy == RECTHEIGHT(&rc) ) )
  1251.             {
  1252.                 //  BUGBUG: implement LWS_AUTOHEIGHT style by
  1253.                 //  calling CalcIdealHeight() to compute the height for
  1254.                 //  the given width.
  1255.             }
  1256.             break;
  1257.         }
  1258.         case WM_SIZE:
  1259.         {
  1260.             pThis->Paint( NULL );
  1261.             break;
  1262.         }
  1263.         case WM_CREATE:
  1264.         {
  1265.             if( (lRet = DefWindowProc( hwnd, uMsg, wParam, lParam )) == 0 )
  1266.             {
  1267.                 CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
  1268. #ifdef KEYBOARDCUES
  1269.                 _InitializeUISTATE( hwnd, &pThis->_fKeyboardCues );
  1270. #endif//KEYBOARDCUES
  1271.                 pThis->CreateFonts();
  1272.                 pThis->SetText( pcs->lpszName );
  1273.                 pThis->Parse( pThis->_pszCaption );
  1274.             }
  1275.             return lRet;
  1276.         }
  1277.         case WM_SETTEXT:
  1278.             pThis->SetText( (LPCTSTR)lParam );
  1279.             pThis->Parse( pThis->_pszCaption );
  1280.             InvalidateRect( pThis->_hwnd, NULL, FALSE );
  1281.             break;
  1282.         case WM_GETTEXT:
  1283.             return pThis->GetText( TRUE, (LPTSTR)lParam, (int)wParam );
  1284.         case WM_GETTEXTLENGTH:
  1285.             return pThis->GetTextLength( TRUE );
  1286.         case WM_SETFOCUS:
  1287.             return pThis->OnFocus( (HWND)wParam );
  1288.         case WM_KILLFOCUS:
  1289.             pThis->SendNotify( NM_KILLFOCUS, pThis->_iFocus );
  1290.             pThis->_iFocus = INVALID_LINK_INDEX;
  1291.             InvalidateRect( pThis->_hwnd, NULL, FALSE );
  1292.             return lRet;
  1293.         case WM_LBUTTONDOWN:
  1294.         {
  1295.             POINT pt;
  1296.             MakePoint( lParam, &pt );
  1297.             pThis->OnButtonDown( wParam, pt );
  1298.             break;
  1299.         }
  1300.         case WM_LBUTTONUP:
  1301.         {
  1302.             POINT pt;
  1303.             MakePoint( lParam, &pt );
  1304.             pThis->OnButtonUp( wParam, pt );
  1305.             break;
  1306.         }
  1307.         case WM_MOUSEMOVE:
  1308.         {
  1309.             POINT pt;
  1310.             MakePoint( lParam, &pt );
  1311.             if( pThis->HitTest( pt ) != INVALID_LINK_INDEX ) 
  1312.                 SetCursor( pThis->GetLinkCursor() );
  1313.             break;
  1314.         }
  1315.     
  1316.         case WM_CAPTURECHANGED:
  1317.             if( lParam /* NULL if we called ReleaseCapture() */ )
  1318.                 pThis->OnCaptureLost( (HWND)lParam );
  1319.             break;
  1320.         case LWM_HITTEST:  // wParam: n/a, lparam: LPLWITEM, ret: BOOL
  1321.         {
  1322.             LWHITTESTINFO* phti = (LWHITTESTINFO*)lParam;
  1323.             if( phti )
  1324.             {
  1325.                 TEXTBLOCK* pBlock;
  1326.                 *phti->item.szID = 0;
  1327.                 if( (phti->item.iLink = pThis->HitTest( phti->pt )) != INVALID_LINK_INDEX &&
  1328.                     (pBlock = pThis->FindLink( phti->item.iLink )) != NULL )
  1329.                 {
  1330.                     lstrcpyn( phti->item.szID, pBlock->szID, ARRAYSIZE(phti->item.szID) );
  1331.                     return TRUE;
  1332.                 }
  1333.             }
  1334.             return lRet;
  1335.         }
  1336.         case LWM_SETITEM:
  1337.             return pThis->SetItem( (LWITEM*)lParam );
  1338.         case LWM_GETITEM:
  1339.             return pThis->GetItem( (LWITEM*)lParam );
  1340.         case LWM_GETIDEALHEIGHT:  // wParam: cx, lparam: n/a, ret: cy
  1341.             //  force a recalc if we've never done so
  1342.             return pThis->CalcIdealHeight( (int)wParam );
  1343.         case WM_NCDESTROY:
  1344.         {
  1345.             lRet = DefWindowProc( hwnd, uMsg, wParam, lParam );
  1346.             SetWindowPtr( hwnd, GWLP_USERDATA, 0 );
  1347.             pThis->_hwnd = NULL;
  1348.             pThis->Release();
  1349.             return lRet;
  1350.         }
  1351.         case WM_GETDLGCODE:
  1352.         {
  1353.             MSG* pmsg;
  1354.             lRet = DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
  1355.             if( (pmsg = (MSG*)lParam) )
  1356.             {
  1357.                 if( (WM_KEYDOWN == pmsg->message || WM_KEYUP == pmsg->message) )
  1358.                 {
  1359.                     switch( pmsg->wParam )
  1360.                     {
  1361.                     case VK_TAB:
  1362.                         if( pThis->WantTab() )
  1363.                             lRet |= DLGC_WANTTAB;
  1364.                         break;
  1365.                     case VK_RETURN:
  1366.                     case VK_SPACE:
  1367.                         lRet |= DLGC_WANTALLKEYS;
  1368.                         break;
  1369.                     }
  1370.                 }
  1371.                 else if( WM_CHAR == pmsg->message && VK_RETURN == pmsg->wParam )
  1372.                 {
  1373.                     //  Eat VK_RETURN WM_CHARs; we don't want
  1374.                     //  Dialog manager to beep when IsDialogMessage gets it.
  1375.                     return lRet |= DLGC_WANTMESSAGE;
  1376.                 }
  1377.             }
  1378.             return lRet;
  1379.         }
  1380.         case WM_KEYDOWN:
  1381.             pThis->OnKeyDown( (UINT)wParam );
  1382.         case WM_KEYUP:
  1383.         case WM_CHAR:
  1384.             return lRet;
  1385. #ifdef KEYBOARDCUES
  1386.         case WM_UPDATEUISTATE:
  1387.             if( _HandleWM_UPDATEUISTATE( wParam, lParam, &pThis->_fKeyboardCues ) )
  1388.                 RedrawWindow( hwnd, NULL, NULL, 
  1389.                               RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW );
  1390.             break;
  1391. #endif
  1392.         default:
  1393.             // oleacc defs thunked for WINVER < 0x0500
  1394.             if( IsWM_GETOBJECT( uMsg ) && OBJID_CLIENT == lParam )
  1395.                 return LresultFromObject( IID_IAccessible, wParam, SAFECAST(pThis, IAccessible*) );
  1396.             break;
  1397.     }
  1398.     return DefWindowProc( hwnd, uMsg, wParam, lParam );
  1399. }
  1400. //-------------------------------------------------------------------------//
  1401. CLinkWindow::TEXTBLOCK::TEXTBLOCK()
  1402.     :   iLink(INVALID_LINK_INDEX), 
  1403.         next(NULL), 
  1404.         state(LWIS_ENABLED),
  1405.         pszText(NULL),
  1406.         pszUrl(NULL),
  1407.         rgrle(NULL)
  1408. {
  1409.     *szID = 0;
  1410. }
  1411. //-------------------------------------------------------------------------//
  1412. CLinkWindow::TEXTBLOCK::~TEXTBLOCK()
  1413. {
  1414.     //  free block text
  1415.     _AllocAndCopy( pszText, NULL );
  1416.     _AllocAndCopy( pszUrl, NULL );
  1417.     //  free rectangle(s)
  1418.     FreeRects();
  1419. }
  1420. //-------------------------------------------------------------------------//
  1421. void CLinkWindow::TEXTBLOCK::AddRect( const RECT& rc )
  1422. {
  1423.     RECTLISTENTRY* prce;
  1424.     if( (prce = new RECTLISTENTRY) != NULL )
  1425.     {
  1426.         prce->rc = rc;
  1427.         prce->next = NULL;
  1428.     }
  1429.     if( rgrle == NULL )
  1430.         rgrle = prce;
  1431.     else
  1432.     {
  1433.         for( RECTLISTENTRY* p = rgrle; p; p = p->next )
  1434.         {
  1435.             if( p->next == NULL )
  1436.             {
  1437.                 p->next = prce;
  1438.                 break;
  1439.             }
  1440.         }
  1441.     }
  1442. }
  1443. //-------------------------------------------------------------------------//
  1444. void CLinkWindow::TEXTBLOCK::FreeRects()
  1445. {
  1446.     for( RECTLISTENTRY* p = rgrle; p; )
  1447.     {
  1448.         RECTLISTENTRY* next = p->next;
  1449.         delete p;
  1450.         p = next;
  1451.     }
  1452.     rgrle = NULL;
  1453. }
  1454. //-------------------------------------------------------------------------//
  1455. #define GROUPBTN_BKCOLOR    COLOR_WINDOW
  1456. #define CAPTION_VPADDING    3
  1457. #define CAPTION_HPADDING    2
  1458. #define GBM_SENDNOTIFY      (GBM_LAST + 1)
  1459. //-------------------------------------------------------------------------//
  1460. //  class CGroupBtn
  1461. class CGroupBtn : public CAccessibleBase
  1462. //-------------------------------------------------------------------------//
  1463. { // all members private:
  1464.     CGroupBtn( HWND hwnd );
  1465.     ~CGroupBtn();
  1466.     //  IAccessible specialization
  1467.     STDMETHODIMP get_accName( VARIANT varChild, BSTR* pbstrName);
  1468.     STDMETHODIMP accDoDefaultAction( VARIANT varChild );
  1469.     //  CAccessibleBase overrides
  1470.     UINT GetDefaultActionStringID() const   { return IDS_GROUPBTN_DEFAULTACTION; }
  1471.     //  window procedures
  1472.     static LRESULT WINAPI WndProc( HWND, UINT, WPARAM, LPARAM );
  1473.     static LRESULT WINAPI BuddyProc( HWND, UINT, WPARAM, LPARAM );
  1474.     //  message handlers
  1475.     BOOL    NcCreate( LPCREATESTRUCT lpcs );
  1476.     LRESULT NcCalcSize( BOOL, LPNCCALCSIZE_PARAMS );
  1477.     void    NcPaint( HRGN );
  1478.     LRESULT NcMouseMove( WPARAM, LONG, LONG );
  1479.     LRESULT NcHitTest( LONG, LONG );
  1480.     LRESULT NcButtonDown( UINT nMsg, WPARAM nHittest, const POINTS& pts );
  1481.     LRESULT NcDblClick( UINT nMsg, WPARAM nHittest, LPARAM lParam );
  1482.     LRESULT ButtonUp( UINT nMsg, WPARAM nHittest, const POINTS& pts );
  1483.     void    OnCaptureLost( HWND hwndNew ) {RESET_CAPTURE();}
  1484.     LRESULT WindowPosChanging( LPWINDOWPOS );
  1485.     LRESULT OnSize( WPARAM, LONG, LONG);
  1486.     BOOL    SetPlacement( PGBPLACEMENT );
  1487.     BOOL    SetBuddy( HWND, ULONG );
  1488.     BOOL    SetDropState( BOOL );
  1489.     void    SetText( LPCTSTR );
  1490.     int     GetText( LPTSTR, int );
  1491.     int     GetTextW( LPWSTR, int );
  1492.     int     GetTextLength();
  1493.     void    SetFont( HFONT );
  1494.     HFONT   GetFont();
  1495.     //  utility methods
  1496.     static void _MapWindowRect( HWND hwnd, HWND hwndRelative, OUT LPRECT prcWindow );
  1497.     void        _MapWindowRect( HWND hwndRelative, OUT LPRECT prcWindow );
  1498.     HCURSOR     GetHandCursor();
  1499.     void        CalcCaptionSize();
  1500.     BOOL        CalcClientRect( IN OPTIONAL LPCRECT prcWindow, OUT LPRECT prcClient );
  1501.     BOOL        CalcWindowSizeForClient( IN OPTIONAL LPCRECT prcClient, 
  1502.                                          IN OPTIONAL LPCRECT prcWindow, 
  1503.                                          IN LPCRECT prcNewClient, 
  1504.                                          OUT LPSIZE psizeWindow );
  1505.     void    DoLayout( BOOL bNewBuddy = FALSE );
  1506.     LONG    EnableNotifications( BOOL bEnable );
  1507.     LRESULT SendNotify( int nCode, IN OPTIONAL NMHDR* pnmh = NULL );
  1508.     void    PostNotify( int nCode );
  1509.     
  1510.     
  1511.     //  instance and static data
  1512.     HWND        _hwnd;
  1513.     HWND        _hwndBuddy;
  1514.     WNDPROC     _pfnBuddy;
  1515.     ULONG       _dwBuddyFlags;
  1516.     SIZE        _sizeBuddyMargin;
  1517.     HFONT       _hf;
  1518.     static ATOM _atom;
  1519.     LPTSTR      _pszCaption;
  1520.     SIZE        _sizeCaption;
  1521.     int         _yDrop;
  1522.     BOOL        _fDropped : 1,
  1523.                 _fInLayout : 1;
  1524.     UINT        _fCapture;
  1525.     UINT        _fKeyboardCues;
  1526.     HCURSOR     _hcurHand;
  1527.     LONG        _cNotifyLocks;
  1528.     friend ATOM GroupButton_RegisterClass();
  1529.     friend HWND CreateGroupBtn( DWORD, LPCTSTR, DWORD, 
  1530.             int x, int y, HWND hwndParent, UINT nID );
  1531. };
  1532. //-------------------------------------------------------------------------//
  1533. ATOM GroupButton_RegisterClass()
  1534. {
  1535.     if( CGroupBtn::_atom != 0 )
  1536.         return CGroupBtn::_atom;
  1537.     WNDCLASSEX wc;
  1538.     ZeroMemory( &wc, sizeof(wc) );
  1539.     
  1540.     wc.cbSize         = sizeof(wc);
  1541.     wc.style          = CS_GLOBALCLASS;
  1542.     wc.lpfnWndProc    = CGroupBtn::WndProc;
  1543.     wc.hInstance      = HINST_THISDLL;
  1544.     wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
  1545.     wc.hbrBackground  = (HBRUSH)(GROUPBTN_BKCOLOR+1);
  1546.     wc.lpszClassName  = GROUPBUTTON_CLASS;
  1547.     //wc.lpszMenuName 
  1548.     //wc.hIcon
  1549.     //wc.hIconSm
  1550.     return (CGroupBtn::_atom = RegisterClassEx( &wc ));
  1551. }
  1552. //-------------------------------------------------------------------------//
  1553. BOOL GroupButton_UnregisterClass()
  1554. {
  1555.     return UnregisterClass( GROUPBUTTON_CLASS, HINST_THISDLL );
  1556. }
  1557. //-------------------------------------------------------------------------//
  1558. CGroupBtn::CGroupBtn( HWND hwnd ) 
  1559.     :   CAccessibleBase( _hwnd ),
  1560.         _hwnd(hwnd), 
  1561.         _hwndBuddy(NULL), 
  1562.         _pfnBuddy(NULL),
  1563.         _dwBuddyFlags(GBBF_HRESIZE|GBBF_VRESIZE),
  1564.         _fInLayout(FALSE),
  1565.         _hf(NULL), 
  1566.         _pszCaption(NULL),
  1567.         _fDropped(TRUE),
  1568.         _fKeyboardCues(0),
  1569.         _yDrop(0),
  1570.         _fCapture(0),
  1571.         _hcurHand(NULL),
  1572.         _cNotifyLocks(0)
  1573. {
  1574.     _sizeCaption.cx = _sizeCaption.cy = 0;
  1575.     _sizeBuddyMargin.cx = _sizeBuddyMargin.cy = 0;
  1576. }
  1577. ATOM    CGroupBtn::_atom = 0;
  1578. //-------------------------------------------------------------------------//
  1579. CGroupBtn::~CGroupBtn() 
  1580. {
  1581.     SetFont( NULL );
  1582.     SetText( NULL );
  1583. }
  1584. //-------------------------------------------------------------------------//
  1585. //  CGroupBtn IAccessible impl
  1586. STDMETHODIMP CGroupBtn::get_accName( VARIANT varChild, BSTR* pbstrName)
  1587. {
  1588.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  1589.     if( NULL == pbstrName )
  1590.         return E_POINTER;
  1591.     *pbstrName = 0;
  1592.     int cch = GetTextLength();
  1593.     if( (*pbstrName = SysAllocStringLen(NULL, cch + 1)) != NULL )
  1594.     {
  1595.         GetTextW( *pbstrName, cch + 1 );
  1596.         return S_OK;
  1597.     }
  1598.     return E_OUTOFMEMORY;
  1599. }
  1600. STDMETHODIMP CGroupBtn::accDoDefaultAction( VARIANT varChild )
  1601. {
  1602.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  1603.     SendNotify( NM_RETURN );
  1604.     return S_OK;
  1605. }
  1606. //-------------------------------------------------------------------------//
  1607. //  CGroupBtn window impl
  1608. //-------------------------------------------------------------------------//
  1609. //-------------------------------------------------------------------------//
  1610. //  WM_SETTEXT handler
  1611. void CGroupBtn::SetText( LPCTSTR pszText )
  1612. {
  1613.     if( _pszCaption )
  1614.     {
  1615.         if( pszText && 0==lstrcmp( _pszCaption, pszText ) )
  1616.             return;
  1617.         delete [] _pszCaption;
  1618.         _pszCaption = NULL;
  1619.     }
  1620.     if( pszText && *pszText )
  1621.     {
  1622.         if( (_pszCaption = new TCHAR[lstrlen(pszText)+1]) != NULL )
  1623.             lstrcpy( _pszCaption, pszText );
  1624.     }
  1625.     
  1626.     if( IsWindow( _hwnd ) )
  1627.         CalcCaptionSize();
  1628. }
  1629. //-------------------------------------------------------------------------//
  1630. //  WM_GETTEXT handler
  1631. int CGroupBtn::GetText( LPTSTR pszText, int cchText )
  1632. {
  1633.     int cch = 0;
  1634.     if( pszText && cchText > 0 )
  1635.     {
  1636.         *pszText = 0;
  1637.         if( _pszCaption && lstrcpyn( pszText, _pszCaption, cchText ) )
  1638.             cch = min( lstrlen( _pszCaption ), cchText );
  1639.     }
  1640.     return cch;
  1641. }
  1642. //-------------------------------------------------------------------------//
  1643. int CGroupBtn::GetTextW( LPWSTR pwszText, int cchText )
  1644. {
  1645. #ifdef UNICODE
  1646.     return GetText( pwszText, cchText );
  1647. #else //UNICODE
  1648.     int   cchRet = 0;
  1649.     LPSTR pszText = new CHAR[cchText];
  1650.     
  1651.     if( pszText )
  1652.     {
  1653.         cchRet = GetText( pszText, cchText );
  1654.         if( cchRet )
  1655.         {
  1656.             SHAnsiToUnicode( pszText, pwszText, cchText );
  1657.         }
  1658.         delete [] pszText;
  1659.     }
  1660.     return cchRet;
  1661. #endif //UNICODE
  1662. }
  1663. //-------------------------------------------------------------------------//
  1664. //  WM_GETTEXTLENGTH handler
  1665. int CGroupBtn::GetTextLength()
  1666. {
  1667.     return (_pszCaption && *_pszCaption) ? lstrlen( _pszCaption ) : 0 ;
  1668. }
  1669. //-------------------------------------------------------------------------//
  1670. //  WM_SETFONT handler
  1671. void CGroupBtn::SetFont( HFONT hf )
  1672. {
  1673.     if( _hf )
  1674.     {
  1675.         DeleteObject( _hf );
  1676.         _hf = NULL;
  1677.     }
  1678.     _hf = hf;
  1679. }
  1680. //-------------------------------------------------------------------------//
  1681. //  WM_GETFONT handler
  1682. HFONT CGroupBtn::GetFont()
  1683. {
  1684.     if( _hf == NULL )
  1685.     {
  1686.         //  if we don't have a font, use the parent's font
  1687.         HFONT hfParent = (HFONT)SendMessage( GetParent( _hwnd ), WM_GETFONT, 0, 0L );
  1688.         if( hfParent )
  1689.         {
  1690.             LOGFONT lf;
  1691.             if( GetObject( hfParent, sizeof(LOGFONT), &lf ) >0 )
  1692.                 _hf = CreateFontIndirect( &lf );
  1693.         }
  1694.     }
  1695.     return _hf;
  1696. }
  1697. //-------------------------------------------------------------------------//
  1698. //  Hand cursor load
  1699. HCURSOR CGroupBtn::GetHandCursor()
  1700. {
  1701.     if( !_hcurHand )
  1702.         _hcurHand = LoadHandCursor(0);
  1703.     return _hcurHand;
  1704. }
  1705. //-------------------------------------------------------------------------//
  1706. //  Retrieves the window rect in relative coords.
  1707. void CGroupBtn::_MapWindowRect( HWND hwnd, HWND hwndRelative, OUT LPRECT prcWindow )
  1708. {
  1709.     ASSERT( IsWindow( hwnd ) );
  1710.     GetWindowRect( hwnd, prcWindow );
  1711.     MapWindowPoints( HWND_DESKTOP, hwndRelative, (LPPOINT)prcWindow, 2 );
  1712. }
  1713. //-------------------------------------------------------------------------//
  1714. //  Retrieves the window rect in relative coords.
  1715. inline void CGroupBtn::_MapWindowRect( HWND hwndRelative, OUT LPRECT prcWindow )
  1716. {
  1717.     _MapWindowRect( _hwnd, hwndRelative, prcWindow );
  1718. }
  1719. //-------------------------------------------------------------------------//
  1720. //  Caches the size of the caption 'bar'.
  1721. void CGroupBtn::CalcCaptionSize()
  1722. {
  1723.     SIZE    sizeCaption = {0,0};
  1724.     LPCTSTR pszCaption = (_pszCaption && *_pszCaption) ? _pszCaption : TEXT("|");
  1725.     HDC     hdc;
  1726.     //  compute caption size based on window text:
  1727.     if( (hdc = GetDC( _hwnd )) )
  1728.     {
  1729.         HFONT hf = GetFont(),
  1730.               hfPrev = (HFONT)SelectObject( hdc, hf );
  1731.         
  1732.         if( GetTextExtentPoint32( hdc, pszCaption, lstrlen( pszCaption ),
  1733.                                   &sizeCaption ) )
  1734.             sizeCaption.cy += CAPTION_VPADDING; // add some vertical padding
  1735.         SelectObject( hdc, hfPrev );
  1736.         ReleaseDC( _hwnd, hdc );
  1737.     }
  1738.     _sizeCaption = sizeCaption;
  1739. }
  1740. //-------------------------------------------------------------------------//
  1741. //  Computes the size and position of the client area
  1742. BOOL CGroupBtn::CalcClientRect( IN OPTIONAL LPCRECT prcWindow, OUT LPRECT prcClient )
  1743. {
  1744.     DWORD dwStyle = GetWindowLong( _hwnd, GWL_STYLE );
  1745.     RECT  rcWindow;
  1746.     if( !prcWindow )
  1747.     {
  1748.         //  Get parent-relative coords
  1749.         _MapWindowRect( GetParent( _hwnd ), &rcWindow );
  1750.         prcWindow = &rcWindow;
  1751.     }
  1752.     *prcClient = *prcWindow;
  1753.     //  compute client rectangle:
  1754.     //  allow for border
  1755.     if( dwStyle & WS_BORDER )
  1756.         InflateRect( prcClient, -1, -1 );
  1757.     //  allow for caption 'bar'
  1758.     prcClient->top += _sizeCaption.cy;
  1759.     //  Normalize for NULL rect.
  1760.     if( RECTWIDTH(prcWindow) <=0 )
  1761.         prcClient->left = prcClient->right = prcWindow->left;
  1762.     if( RECTHEIGHT(prcWindow) <=0 )
  1763.         prcClient->bottom = prcClient->top = prcWindow->top;
  1764.     return TRUE;
  1765. }
  1766. //-------------------------------------------------------------------------//
  1767. BOOL CGroupBtn::CalcWindowSizeForClient( 
  1768.     IN OPTIONAL LPCRECT prcClient, 
  1769.     IN OPTIONAL LPCRECT prcWindow, 
  1770.     IN LPCRECT prcNewClient, 
  1771.     OUT LPSIZE psizeWindow )
  1772. {
  1773.     if( !(prcNewClient && psizeWindow ) )
  1774.     {
  1775.         ASSERT(FALSE);
  1776.         return FALSE;
  1777.     }
  1778.     RECT rcWindow, rcClient;
  1779.     if( NULL == prcWindow )
  1780.     {
  1781.         GetWindowRect( _hwnd, &rcWindow );
  1782.         prcWindow = &rcWindow;
  1783.     }
  1784.     if( NULL == prcClient )
  1785.     {
  1786.         GetClientRect( _hwnd, &rcClient );
  1787.         prcClient = &rcClient;
  1788.     }
  1789.     SIZE sizeDelta;
  1790.     sizeDelta.cx = RECTWIDTH(prcWindow)   - RECTWIDTH(prcClient);
  1791.     sizeDelta.cy = RECTHEIGHT(prcWindow)  - RECTHEIGHT(prcClient);
  1792.     psizeWindow->cx = RECTWIDTH(prcNewClient)  + sizeDelta.cx;
  1793.     psizeWindow->cy = RECTHEIGHT(prcNewClient) + sizeDelta.cy;
  1794.     
  1795.     return TRUE;
  1796. }
  1797. //-------------------------------------------------------------------------//
  1798. //  WM_WINDOWPOSCHANGING handler
  1799. LRESULT CGroupBtn::WindowPosChanging( LPWINDOWPOS pwp )
  1800. {
  1801.     if( pwp->flags & SWP_NOSIZE )
  1802.         return DefWindowProc( _hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)pwp );
  1803.     //  disallow sizing in buddy slave dimension(s).
  1804.     if( IsWindow( _hwndBuddy ) && _dwBuddyFlags & (GBBF_HSLAVE|GBBF_VSLAVE) && !_fInLayout )
  1805.     {
  1806.         RECT rcWindow, rcClient;
  1807.         BOOL fResizeBuddy = FALSE;
  1808.         GetWindowRect( _hwnd, &rcWindow );
  1809.         GetClientRect( _hwnd, &rcClient );
  1810.         //  Prepare a buddy size data block
  1811.         GBNQUERYBUDDYSIZE qbs;
  1812.         qbs.cy = pwp->cy - (RECTHEIGHT(&rcWindow) - RECTHEIGHT(&rcClient));
  1813.         qbs.cx = pwp->cx - (RECTWIDTH(&rcWindow) - RECTWIDTH(&rcClient));
  1814.         
  1815.         if( _dwBuddyFlags & GBBF_HSLAVE ) // prevent external horz resizing
  1816.         {
  1817.             pwp->cx = RECTWIDTH( &rcWindow );
  1818.             //  If we're being resized in the vert dir, query for
  1819.             //  optimal buddy width for this height and adjust
  1820.             if( _dwBuddyFlags & GBBF_VRESIZE && RECTHEIGHT( &rcWindow ) != pwp->cy )
  1821.             {
  1822.                 if( SendNotify( GBN_QUERYBUDDYWIDTH, (NMHDR*)&qbs ) && qbs.cx >= 0 )
  1823.                 {
  1824.                     //  if the owner wants the buddy width to change, do it now.
  1825.                     LONG cxNew = qbs.cx + (RECTWIDTH( &rcWindow ) - RECTWIDTH( &rcClient ));
  1826.                     fResizeBuddy = cxNew != pwp->cx;
  1827.                     pwp->cx = cxNew;
  1828.                 }
  1829.             }
  1830.         }
  1831.         
  1832.         if( _dwBuddyFlags & GBBF_VSLAVE ) // prevent external vert resizing
  1833.         {
  1834.             pwp->cy = RECTHEIGHT( &rcWindow );
  1835.             //  If we're being resized in the horz dir, query for
  1836.             //  optimal buddy height for this horizontal and adjust
  1837.             if( _dwBuddyFlags & GBBF_HRESIZE && RECTWIDTH( &rcWindow ) != pwp->cx )
  1838.             {
  1839.                 if( SendNotify( GBN_QUERYBUDDYHEIGHT, (NMHDR*)&qbs ) && qbs.cy >= 0 )
  1840.                 {
  1841.                     LONG cyNew = qbs.cy + (RECTHEIGHT( &rcWindow ) - RECTHEIGHT( &rcClient ));
  1842.                     fResizeBuddy = cyNew != pwp->cy;
  1843.                     pwp->cy = cyNew;
  1844.                 }
  1845.             }
  1846.         }
  1847.         if( fResizeBuddy )
  1848.         {
  1849.             _fInLayout = TRUE;
  1850.             SetWindowPos( _hwndBuddy, NULL, 0, 0, qbs.cx, qbs.cy,
  1851.                           SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE );
  1852.             _fInLayout = FALSE;
  1853.         }
  1854.     }
  1855.     //  enforce minimum height:
  1856.     if( pwp->cy < _sizeCaption.cy )
  1857.         pwp->cy = _sizeCaption.cy;
  1858.     return 0L;
  1859. }
  1860. //-------------------------------------------------------------------------//
  1861. LRESULT CGroupBtn::OnSize( WPARAM flags, LONG cx, LONG cy )
  1862. {
  1863.     DoLayout();
  1864.     return 0L;
  1865. //-------------------------------------------------------------------------//
  1866. void CGroupBtn::DoLayout( BOOL bNewBuddy )
  1867. {
  1868.     if( !_fInLayout && IsWindow( _hwndBuddy ) )
  1869.     {
  1870.         RECT  rcWindow, rcThis, rcBuddy;
  1871.         DWORD dwSwpBuddy = SWP_NOACTIVATE,
  1872.               dwSwpThis  = SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE;
  1873.         BOOL  fReposThis = FALSE;
  1874.         SIZE  sizeNew;
  1875.         GetClientRect( _hwnd, &rcThis );
  1876.         GetWindowRect( _hwnd, &rcWindow );
  1877.         //  get rectangles in parent coords
  1878.         MapWindowPoints( _hwnd, GetParent( _hwnd ), (LPPOINT)&rcThis,    POINTSPERRECT ); 
  1879.         MapWindowPoints( HWND_DESKTOP, GetParent( _hwnd ), (LPPOINT)&rcWindow,  POINTSPERRECT ); 
  1880.         _MapWindowRect( _hwndBuddy, GetParent( _hwnd ), &rcBuddy );
  1881.         //  If we need to reposition ourself to the buddy, 
  1882.         //  calculate the new size now.
  1883.         if( _dwBuddyFlags & (GBBF_HSLAVE|GBBF_VSLAVE) )
  1884.             CalcWindowSizeForClient( &rcThis, &rcWindow, &rcBuddy, &sizeNew );
  1885.         //  Resize buddy according to size.
  1886.         if( _dwBuddyFlags & GBBF_HRESIZE )
  1887.         {
  1888.             rcBuddy.right = rcBuddy.left + RECTWIDTH(&rcThis);
  1889.             if( bNewBuddy && 0 == (_dwBuddyFlags & GBBF_VRESIZE) ) 
  1890.             {
  1891.                 // query height
  1892.                 GBNQUERYBUDDYSIZE qbs;
  1893.                 qbs.cx = RECTWIDTH( &rcThis );
  1894.                 qbs.cy = -1;
  1895.                 if( SendNotify( GBN_QUERYBUDDYHEIGHT, (NMHDR*)&qbs ) && qbs.cy >= 0 )
  1896.                     rcBuddy.bottom = rcBuddy.top + qbs.cy;
  1897.             }
  1898.         }
  1899.         else if( _dwBuddyFlags & GBBF_HSLAVE )
  1900.         { 
  1901.             rcWindow.right = rcWindow.left + sizeNew.cx;
  1902.             fReposThis = TRUE;
  1903.         }
  1904.         if( _dwBuddyFlags & GBBF_VRESIZE )
  1905.         {
  1906.             rcBuddy.bottom = rcBuddy.top + RECTHEIGHT(&rcThis);
  1907.             if( bNewBuddy && 0 == (_dwBuddyFlags & GBBF_HRESIZE) ) 
  1908.             {
  1909.                 // query width
  1910.                 GBNQUERYBUDDYSIZE qbs;
  1911.                 qbs.cx = -1;
  1912.                 qbs.cy = RECTHEIGHT( &rcThis );
  1913.                 if( SendNotify( GBN_QUERYBUDDYWIDTH, (NMHDR*)&qbs ) && qbs.cx >= 0 )
  1914.                     rcBuddy.right = rcBuddy.left + qbs.cx;
  1915.             }
  1916.         }
  1917.         else if( _dwBuddyFlags & GBBF_VSLAVE )
  1918.         { 
  1919.             rcWindow.bottom = rcWindow.top + sizeNew.cy;
  1920.             fReposThis = TRUE;
  1921.         }
  1922.         
  1923.         if( _dwBuddyFlags & GBBF_HSCROLL ) 
  1924.         { 
  1925.             /* not implemented */
  1926.         }
  1927.         if( _dwBuddyFlags & GBBF_VSCROLL )
  1928.         { 
  1929.             /* not implemented */
  1930.         }
  1931.         //  reposition ourself and update our client rect.
  1932.         if( fReposThis )
  1933.          {
  1934.             _fInLayout = TRUE;
  1935.             SetWindowPos( _hwnd, NULL, 0, 0, 
  1936.                           RECTWIDTH( &rcWindow ), RECTHEIGHT( &rcWindow ), dwSwpThis );
  1937.             _fInLayout = FALSE;
  1938.             GetClientRect( _hwnd, &rcThis );
  1939.             MapWindowPoints( _hwnd, GetParent( _hwnd ), (LPPOINT)&rcThis,  POINTSPERRECT ); 
  1940.         }
  1941.         
  1942.         //  slide buddy into client area and reposition
  1943.         OffsetRect( &rcBuddy, rcThis.left - rcBuddy.left, rcThis.top - rcBuddy.top );
  1944.         
  1945.         _fInLayout = TRUE;
  1946.         SetWindowPos( _hwndBuddy, _hwnd, rcBuddy.left, rcBuddy.top,
  1947.                       RECTWIDTH( &rcBuddy ), RECTHEIGHT( &rcBuddy ), dwSwpBuddy );
  1948.         _fInLayout = FALSE;
  1949.     }
  1950. }
  1951. //-------------------------------------------------------------------------//
  1952. //  GBM_SETPLACEMENT handler
  1953. BOOL CGroupBtn::SetPlacement( PGBPLACEMENT pgbp )
  1954. {
  1955.     RECT  rcWindow, rcClient;
  1956.     SIZE  sizeDelta;
  1957.     DWORD dwFlags = SWP_NOZORDER|SWP_NOACTIVATE;
  1958.     ZeroMemory( &sizeDelta, sizeof(sizeDelta) );
  1959.     _MapWindowRect( GetParent( _hwnd ), &rcWindow );
  1960.     CalcClientRect( &rcWindow, &rcClient );
  1961.     //  establish whether we need to resize
  1962.     if( (pgbp->x < 0 || pgbp->x == rcWindow.left) && 
  1963.         (pgbp->y < 0 || pgbp->y == rcWindow.top) )
  1964.         dwFlags |= SWP_NOMOVE;
  1965.     //  compute horizontal placement
  1966.     if( pgbp->x >= 0 )  // fixed horz origin requested
  1967.         OffsetRect( &rcWindow, pgbp->x - rcWindow.left, 0 );
  1968.     if( pgbp->cx >= 0 ) // fixed width requested
  1969.         rcWindow.right = rcWindow.left + pgbp->cx;
  1970.     else
  1971.     {
  1972.         if( pgbp->cxBuddy >= 0 ) // client width requested
  1973.             sizeDelta.cx = pgbp->cxBuddy - RECTWIDTH(&rcClient);
  1974.         rcWindow.right  += sizeDelta.cx;
  1975.     }
  1976.                           
  1977.     //  compute vertical placement
  1978.     if( pgbp->y >= 0 )  // fixed vert origin requested
  1979.         OffsetRect( &rcWindow, 0, pgbp->y - rcWindow.top );
  1980.     if( pgbp->cy >= 0 ) // fixed height requested
  1981.         rcWindow.bottom = rcWindow.top + pgbp->cy;
  1982.     else
  1983.     {
  1984.         if( pgbp->cyBuddy >= 0 ) // client height requested
  1985.             sizeDelta.cy = pgbp->cyBuddy - RECTHEIGHT(&rcClient);
  1986.         rcWindow.bottom += sizeDelta.cy;
  1987.     }
  1988.     if( pgbp->hdwp && (-1 != (LONG_PTR)pgbp->hdwp) )
  1989.         DeferWindowPos( pgbp->hdwp, _hwnd, NULL, rcWindow.left, rcWindow.top, 
  1990.                         RECTWIDTH( &rcWindow ), RECTHEIGHT( &rcWindow ),
  1991.                         dwFlags );
  1992.     else
  1993.         SetWindowPos( _hwnd, NULL, rcWindow.left, rcWindow.top, 
  1994.                       RECTWIDTH( &rcWindow ), RECTHEIGHT( &rcWindow ),
  1995.                       dwFlags );
  1996.     //  stuff resulting rects
  1997.     pgbp->rcWindow = rcWindow;
  1998.     return CalcClientRect( &rcWindow, &pgbp->rcBuddy );
  1999. }
  2000. //-------------------------------------------------------------------------//
  2001. BOOL CGroupBtn::SetBuddy( HWND hwnd, ULONG dwFlags )
  2002. {
  2003.     if( !IsWindow( hwnd ) )
  2004.         hwnd = NULL;
  2005.     if( hwnd && (dwFlags & (GBBF_HSLAVE|GBBF_VSLAVE)) )
  2006.     {
  2007.         //  subclass the buddy 
  2008.         _pfnBuddy = (WNDPROC)SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR)BuddyProc );
  2009.         SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)this );
  2010.     }
  2011.     else if( IsWindow( _hwndBuddy ) && _pfnBuddy )
  2012.     {
  2013.         SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)NULL );
  2014.         SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR)_pfnBuddy );
  2015.         _pfnBuddy = NULL;
  2016.     }
  2017.     _hwndBuddy = hwnd;
  2018.     _dwBuddyFlags = dwFlags;
  2019.     DoLayout( TRUE );
  2020.     return TRUE;
  2021. }
  2022. //-------------------------------------------------------------------------//
  2023. BOOL CGroupBtn::SetDropState( BOOL bDropped )
  2024. {
  2025.     _fDropped = bDropped;
  2026.     return TRUE;
  2027. }
  2028. //-------------------------------------------------------------------------//
  2029. //  WM_NCCREATE handler
  2030. BOOL CGroupBtn::NcCreate( LPCREATESTRUCT lpcs )
  2031. {
  2032.     //  assign user data
  2033.     SetWindowLongPtr( _hwnd, GWLP_USERDATA, (LONG_PTR)this );
  2034.     
  2035.     //  enforce window style bits
  2036.     lpcs->style     |= WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
  2037.     lpcs->dwExStyle |= WS_EX_TRANSPARENT;
  2038.     SetWindowLong( _hwnd, GWL_STYLE, lpcs->style );
  2039.     SetWindowLong( _hwnd, GWL_EXSTYLE, lpcs->dwExStyle );
  2040.     //  enforce min height
  2041.     SetText( lpcs->lpszName );
  2042.     if( lpcs->cy < _sizeCaption.cy )
  2043.     { 
  2044.         lpcs->cy = _sizeCaption.cy;
  2045.         SetWindowPos( _hwnd, NULL, 0,0, lpcs->cx, lpcs->cy,
  2046.                       SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE );
  2047.     }
  2048.     return TRUE;
  2049. }
  2050. //-------------------------------------------------------------------------//
  2051. //  WM_NCCALCSIZE handler
  2052. LRESULT CGroupBtn::NcCalcSize( BOOL fCalcValidRects, LPNCCALCSIZE_PARAMS pnccs )
  2053. {
  2054.     LRESULT lRet = FALSE;
  2055.     RECT   rcClient;
  2056.     if( fCalcValidRects && CalcClientRect( &pnccs->rgrc[0], &rcClient ) )
  2057.     {
  2058.         pnccs->rgrc[1] = pnccs->rgrc[2];
  2059.         pnccs->rgrc[0] = pnccs->rgrc[2] = rcClient;
  2060.         return WVR_VALIDRECTS;
  2061.     }
  2062.     
  2063.     return lRet;
  2064. }
  2065. //-------------------------------------------------------------------------//
  2066. //  WM_NCPAINT handler
  2067. void CGroupBtn::NcPaint( HRGN hrgn )
  2068. {
  2069.     RECT    rcWindow;
  2070.     DWORD   dwStyle = GetWindowLong( _hwnd, GWL_STYLE );
  2071.     HDC     hdc;
  2072.     GetWindowRect( _hwnd, &rcWindow );
  2073.     OffsetRect( &rcWindow, -rcWindow.left, -rcWindow.top );
  2074.     if( (hdc = GetWindowDC( _hwnd )) != NULL )
  2075.     {
  2076.         if( dwStyle & WS_BORDER )
  2077.         {
  2078.             HBRUSH hbr = CreateSolidBrush( COLOR_WINDOWFRAME );
  2079.             FrameRect( hdc, &rcWindow, hbr );
  2080.             DeleteObject( hbr );
  2081.         }
  2082.         rcWindow.bottom = rcWindow.top + _sizeCaption.cy;
  2083.         SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
  2084.         SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
  2085.         ExtTextOut( hdc, rcWindow.left, rcWindow.top, 
  2086.                     ETO_OPAQUE, &rcWindow, NULL, 0, NULL );
  2087.         InflateRect( &rcWindow, -CAPTION_HPADDING, -(CAPTION_VPADDING/2) );
  2088.         HFONT hfPrev = (HFONT)SelectObject( hdc, GetFont() );
  2089.         ExtTextOut( hdc, rcWindow.left, rcWindow.top, 
  2090.                     ETO_OPAQUE, &rcWindow, _pszCaption, 
  2091.                     _pszCaption ? lstrlen(_pszCaption) : 0, NULL );
  2092.         SelectObject( hdc, hfPrev );
  2093. #ifdef KEYBOARDCUES
  2094.         if( 0 == (_fKeyboardCues & UISF_HIDEFOCUS) )
  2095. #endif
  2096.         {
  2097.             if( GetFocus() == _hwnd )
  2098.             {
  2099.                 rcWindow.right = rcWindow.left + _sizeCaption.cx + 1;
  2100.                 InflateRect( &rcWindow, 1, 0 );
  2101.                 DrawFocusRect( hdc, &rcWindow );
  2102.             }
  2103.         }
  2104.         ReleaseDC( _hwnd, hdc );
  2105.     }
  2106. }
  2107. //-------------------------------------------------------------------------//
  2108. //  WM_NCMOUSEMOVE handler
  2109. //-------------------------------------------------------------------------//
  2110. LRESULT CGroupBtn::NcMouseMove( WPARAM nHittest, LONG x, LONG y )
  2111. {
  2112.     if( HTCAPTION == nHittest )
  2113.     {
  2114.         RECT  rc;
  2115.         POINT pt;
  2116.         GetWindowRect( _hwnd, &rc );
  2117.         rc.bottom = rc.top + _sizeCaption.cy;
  2118.         rc.right  = rc.left + _sizeCaption.cx;
  2119.         InflateRect( &rc, 0, -(CAPTION_VPADDING/2) );
  2120.         pt.x = x;
  2121.         pt.y = y;
  2122.         if( PtInRect( &rc, pt ) )
  2123.         {
  2124.             HCURSOR hc = GetHandCursor();
  2125.             if( hc != NULL )
  2126.             {
  2127.                 SetCursor( hc );
  2128.                 return 0L;
  2129.             }
  2130.         }
  2131.     }
  2132.     return DefWindowProc( _hwnd, WM_NCMOUSEMOVE, nHittest, MAKELPARAM( x, y ) );
  2133. }
  2134. //-------------------------------------------------------------------------//
  2135. //  WM_NCHITTEST handler
  2136. LRESULT CGroupBtn::NcHitTest( LONG x, LONG y )
  2137. {
  2138.     POINT pt;
  2139.     RECT  rc, rcClient;
  2140.     DWORD dwStyle = GetWindowLong( _hwnd, GWL_STYLE );
  2141.     
  2142.     pt.x = x;
  2143.     pt.y = y;
  2144.     GetWindowRect( _hwnd, &rc );
  2145.     CalcClientRect( &rc, &rcClient );
  2146.     if( PtInRect( &rcClient, pt ) )
  2147.         return HTTRANSPARENT;
  2148.     if( PtInRect( &rc, pt ) )
  2149.     {
  2150.         if( dwStyle & WS_BORDER )
  2151.         {
  2152.             if( pt.x == rc.left ||
  2153.                 pt.x == rc.right ||
  2154.                 pt.y == rc.bottom )
  2155.                 return HTBORDER;
  2156.         }
  2157.         return HTCAPTION;
  2158.     }
  2159.     return HTNOWHERE;
  2160. }
  2161. //-------------------------------------------------------------------------//
  2162. LRESULT CGroupBtn::NcButtonDown( UINT nMsg, WPARAM nHittest, const POINTS& pts )
  2163. {
  2164.     LRESULT lRet = 0L;
  2165.     if( HTCAPTION == nHittest )
  2166.     {
  2167.         SetCursor( GetHandCursor() );
  2168.         MODIFY_CAPTURE( CF_SETCAPTURE, 0 );
  2169.         if( GetFocus() != _hwnd )
  2170.         {
  2171.             MODIFY_CAPTURE( CF_SETFOCUS, 0 );
  2172.             EnableNotifications( FALSE ); // so the host doesn't reposition the link.
  2173.             SetFocus( _hwnd );
  2174.             EnableNotifications( TRUE );
  2175.         }
  2176.         SetCapture( _hwnd );
  2177.     }
  2178.     else
  2179.         lRet = DefWindowProc( _hwnd, nMsg, nHittest, MAKELONG(pts.x, pts.y) );
  2180.         
  2181.     return lRet;
  2182. }
  2183. //-------------------------------------------------------------------------//
  2184. LRESULT CGroupBtn::ButtonUp( UINT nMsg, WPARAM nHittest, const POINTS& pts )
  2185. {
  2186.     if( TEST_CAPTURE(CF_SETCAPTURE) )
  2187.     {
  2188.         ReleaseCapture();
  2189.         MODIFY_CAPTURE( 0, CF_SETCAPTURE );
  2190.         POINT ptScrn;
  2191.         ptScrn.x = pts.x;
  2192.         ptScrn.y = pts.y;
  2193.         MapWindowPoints( _hwnd, HWND_DESKTOP, &ptScrn, 1 );
  2194.         LRESULT nHittest = SendMessage( _hwnd, WM_NCHITTEST, 0, MAKELONG( ptScrn.x, ptScrn.y ) );
  2195.         if( HTCAPTION == nHittest )
  2196.         {
  2197.             switch( nMsg )
  2198.             {
  2199.             case WM_LBUTTONUP:
  2200.                 SendNotify( NM_CLICK );
  2201.                 break;
  2202.             case WM_RBUTTONUP:
  2203.                 SendNotify( NM_RCLICK );
  2204.                 break;
  2205.             }
  2206.         }
  2207.     }
  2208.     if( TEST_CAPTURE(CF_SETFOCUS) )
  2209.     {
  2210.         MODIFY_CAPTURE( 0, CF_SETFOCUS );
  2211.         if( GetFocus() == _hwnd ) // if we still have the focus...
  2212.             SendNotify( NM_SETFOCUS );
  2213.     }
  2214.     return 0L;
  2215. }
  2216. //-------------------------------------------------------------------------//
  2217. //  Non-client mouse click/dblclk handler
  2218. LRESULT CGroupBtn::NcDblClick( UINT nMsg, WPARAM nHittest, LPARAM lParam )
  2219. {
  2220.     LRESULT lRet = 0L;
  2221.     
  2222.     if( HTCAPTION == nHittest )
  2223.     {
  2224.         SetFocus( _hwnd );
  2225.         lRet = DefWindowProc( _hwnd, nMsg, HTCLIENT, lParam );
  2226.         switch( nMsg )
  2227.         {
  2228.             case WM_NCLBUTTONDBLCLK:
  2229.                 SendNotify( NM_DBLCLK );
  2230.                 break;
  2231.             case WM_NCRBUTTONDBLCLK:
  2232.                 SendNotify( NM_RDBLCLK );
  2233.                 break;
  2234.         }
  2235.     }
  2236.     else
  2237.         lRet = DefWindowProc( _hwnd, nMsg, nHittest, lParam );
  2238.     return lRet;
  2239. }
  2240. //-------------------------------------------------------------------------//
  2241. LONG CGroupBtn::EnableNotifications( BOOL bEnable )
  2242. {
  2243.     if( bEnable )
  2244.     {
  2245.         if( _cNotifyLocks > 0 )
  2246.             _cNotifyLocks--;
  2247.     }
  2248.     else
  2249.         _cNotifyLocks++;
  2250.     
  2251.     return _cNotifyLocks;
  2252. }
  2253. //-------------------------------------------------------------------------//
  2254. //  WM_NOTIFY transmit helper
  2255. LRESULT CGroupBtn::SendNotify( int nCode, IN OPTIONAL NMHDR* pnmh )
  2256. {
  2257.     if( 0 == _cNotifyLocks )
  2258.     {
  2259.         NMHDR hdr;
  2260.         if( NULL == pnmh )
  2261.             pnmh = &hdr; 
  2262.         pnmh->hwndFrom = _hwnd;
  2263.         pnmh->idFrom   = GetDlgCtrlID( _hwnd );
  2264.         pnmh->code     = nCode;
  2265.         return SendMessage( GetParent( _hwnd ), WM_NOTIFY, hdr.idFrom, (LPARAM)pnmh );
  2266.     }
  2267.     return 0;
  2268. }
  2269. //-------------------------------------------------------------------------//
  2270. //  WM_NOTIFY transmit helper
  2271. void CGroupBtn::PostNotify( int nCode )
  2272. {
  2273.     if( 0 == _cNotifyLocks )
  2274.         PostMessage( _hwnd, GBM_SENDNOTIFY, nCode, 0L );
  2275. }
  2276. //-------------------------------------------------------------------------//
  2277. LRESULT CGroupBtn::WndProc( HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
  2278. {
  2279.     CGroupBtn *pThis = (CGroupBtn*)GetWindowLongPtr( hwnd, GWLP_USERDATA );
  2280.     LRESULT lRet = 0;
  2281.     
  2282.     switch( nMsg )
  2283.     {
  2284.         case WM_NCHITTEST:
  2285.         {
  2286.             POINTS pts = MAKEPOINTS( lParam );
  2287.             return pThis->NcHitTest( pts.x, pts.y );
  2288.         }
  2289.         case WM_NCMOUSEMOVE:
  2290.         {
  2291.             POINTS pts = MAKEPOINTS( lParam );
  2292.             return pThis->NcMouseMove( wParam, pts.x, pts.y );
  2293.         }
  2294.         case WM_NCCALCSIZE:
  2295.             return pThis->NcCalcSize( (BOOL)wParam, (LPNCCALCSIZE_PARAMS)lParam );
  2296.         case WM_NCPAINT:
  2297.             pThis->NcPaint( (HRGN)wParam );
  2298.             return 0;
  2299.         case WM_WINDOWPOSCHANGING:
  2300.             return pThis->WindowPosChanging( (LPWINDOWPOS)lParam );
  2301.         case WM_SIZE:
  2302.         {
  2303.             POINTS pts = MAKEPOINTS( lParam );
  2304.             return pThis->OnSize( wParam, pts.x, pts.y );
  2305.         }
  2306.         case WM_DESTROY:
  2307.             if( IsWindow( pThis->_hwndBuddy ) )
  2308.                 DestroyWindow( pThis->_hwndBuddy );
  2309.             break;
  2310.         case WM_ERASEBKGND:
  2311.             return TRUE; // transparent: no erase bkgnd
  2312.         case WM_NCLBUTTONDOWN:
  2313.         case WM_NCRBUTTONDOWN:
  2314.         {
  2315.             POINTS pts = MAKEPOINTS(lParam);
  2316.             return pThis->NcButtonDown( nMsg, wParam, pts );
  2317.         }
  2318.         case WM_LBUTTONUP:
  2319.         case WM_RBUTTONUP:
  2320.         {
  2321.             POINTS pts = MAKEPOINTS(lParam);
  2322.             return pThis->ButtonUp( nMsg, wParam, pts );
  2323.         }
  2324.         case WM_NCLBUTTONDBLCLK:
  2325.         case WM_NCRBUTTONDBLCLK:
  2326.             return pThis->NcDblClick( nMsg, wParam, lParam );
  2327.         case WM_SHOWWINDOW:
  2328.             if( IsWindow( pThis->_hwndBuddy ) )
  2329.                 ShowWindow( pThis->_hwndBuddy, wParam ? SW_SHOW : SW_HIDE );
  2330.             break;
  2331.         case WM_SETTEXT:
  2332.             pThis->SetText( (LPCTSTR)lParam );
  2333.             return TRUE;
  2334.         case WM_GETTEXT:
  2335.             return pThis->GetText( (LPTSTR)lParam, (int)wParam );
  2336.         case WM_SETFONT:
  2337.             pThis->SetFont( (HFONT)wParam );
  2338.             if( lParam /* fRedraw */)
  2339.                 InvalidateRect( hwnd, NULL, TRUE );
  2340.             break;
  2341.         case WM_CAPTURECHANGED:
  2342.             if( lParam /* NULL if we called ReleaseCapture() */)
  2343.                 pThis->OnCaptureLost( (HWND)lParam );
  2344.             break;
  2345.         case WM_SETFOCUS:
  2346.             pThis->NcPaint( (HRGN)1 );
  2347.             pThis->SendNotify( NM_SETFOCUS );
  2348.             break;
  2349.              
  2350.         case WM_KILLFOCUS:
  2351.             pThis->NcPaint( (HRGN)1 );
  2352.             pThis->SendNotify( NM_KILLFOCUS );
  2353.             break;
  2354.         case WM_GETDLGCODE:
  2355.         {
  2356.             MSG* pmsg;
  2357.             lRet = DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
  2358.             if( (pmsg = (MSG*)lParam) )
  2359.             {
  2360.                 if( (WM_KEYDOWN == pmsg->message || WM_KEYUP == pmsg->message) )
  2361.                 {
  2362.                     switch( pmsg->wParam )
  2363.                     {
  2364.                     case VK_RETURN:
  2365.                     case VK_SPACE:
  2366.                         lRet |= DLGC_WANTALLKEYS;
  2367.                         break;
  2368.                     }
  2369.                 }
  2370.                 else if( WM_CHAR == pmsg->message && VK_RETURN == pmsg->wParam )
  2371.                 {
  2372.                     //  Eat VK_RETURN WM_CHARs; we don't want
  2373.                     //  Dialog manager to beep when IsDialogMessage gets it.
  2374.                     return lRet |= DLGC_WANTMESSAGE;
  2375.                 }
  2376.             }
  2377.             return lRet;
  2378.         }
  2379.         case WM_KEYDOWN:
  2380.         case WM_KEYUP:
  2381.         case WM_CHAR:
  2382.             switch( wParam )
  2383.             {
  2384.                 case VK_RETURN:
  2385.                 case VK_SPACE:
  2386.                     if( WM_KEYDOWN == nMsg )
  2387.                         pThis->SendNotify( NM_RETURN );
  2388.                     return 0L;
  2389.             }
  2390.             break;
  2391. #ifdef KEYBOARDCUES
  2392.         case WM_UPDATEUISTATE:
  2393.             if( _HandleWM_UPDATEUISTATE( wParam, lParam, &pThis->_fKeyboardCues ) )
  2394.                 SendMessage( hwnd, WM_NCPAINT, 1, 0L );
  2395.             break;
  2396. #endif
  2397.         case GBM_SETPLACEMENT:
  2398.             if( lParam )
  2399.                 return pThis->SetPlacement( (PGBPLACEMENT)lParam );
  2400.             return 0L;
  2401.         case GBM_SETDROPSTATE: // WPARAM: BOOL fDropped, LPARAM: n/a, return: BOOL
  2402.             return 0L;
  2403.         case GBM_GETDROPSTATE: // WPARAM: n/a, LPARAM: n/a, return: BOOL fDropped
  2404.             return 0L;
  2405.         case GBM_SENDNOTIFY:
  2406.             pThis->SendNotify( (int)wParam );
  2407.             break;
  2408.         case WM_NCCREATE:
  2409.         {
  2410.             LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
  2411.             if( (pThis = new CGroupBtn(hwnd) ) == NULL )
  2412.                 break;
  2413.             
  2414.             if( !pThis->NcCreate( (LPCREATESTRUCT)lParam ) )
  2415.                 return FALSE;
  2416.             break;
  2417.         }
  2418.         case WM_NCDESTROY:
  2419.             lRet = DefWindowProc( hwnd, nMsg, wParam, lParam );
  2420.             SetWindowPtr( hwnd, GWLP_USERDATA, NULL );
  2421.             pThis->_hwnd = NULL;
  2422.             pThis->Release();
  2423.             return lRet;
  2424.         case WM_CREATE:
  2425. #ifdef KEYBOARDCUES
  2426.             _InitializeUISTATE( hwnd, &pThis->_fKeyboardCues );
  2427. #endif//KEYBOARDCUES
  2428.             pThis->SetText( ((LPCREATESTRUCT)lParam)->lpszName );
  2429.             break;
  2430.         case GBM_SETBUDDY:     // WPARAM: HWND hwndBuddy, LPARAM: MAKELPARAM(cxMargin, cyMargin), return: BOOL
  2431.             return pThis->SetBuddy( (HWND)wParam, (ULONG)lParam );
  2432.         case GBM_GETBUDDY:     // WPARAM: n/a, LPARAM: n/a, return: HWND
  2433.             return (LRESULT)pThis->_hwndBuddy;
  2434.         default:
  2435.             // oleacc defs thunked for WINVER < 0x0500
  2436.             if( IsWM_GETOBJECT( nMsg ) && (OBJID_CLIENT == lParam || OBJID_TITLEBAR == lParam) ) 
  2437.                 return LresultFromObject( IID_IAccessible, wParam, SAFECAST(pThis, IAccessible*) );
  2438.             break;
  2439.     }
  2440.     return DefWindowProc( hwnd, nMsg, wParam, lParam );
  2441. }
  2442.                                                     
  2443. //-------------------------------------------------------------------------//
  2444. LRESULT CGroupBtn::BuddyProc( HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
  2445. {
  2446.     CGroupBtn *pBtn = (CGroupBtn*)GetWindowLongPtr( hwnd, GWLP_USERDATA );
  2447.     ASSERT( pBtn );
  2448.     switch( nMsg )
  2449.     {
  2450.         case WM_SIZE:
  2451.         {
  2452.             LRESULT lRet = CallWindowProc( pBtn->_pfnBuddy, hwnd, nMsg, wParam, lParam );
  2453.             if( !pBtn->_fInLayout )
  2454.                 pBtn->DoLayout();
  2455.             return lRet;
  2456.         }
  2457.         case WM_DESTROY:
  2458.         {
  2459.             WNDPROC pfn = pBtn->_pfnBuddy;
  2460.             pBtn->SetBuddy( NULL, 0 );
  2461.             return CallWindowProc( pfn, hwnd, nMsg, wParam, lParam );
  2462.         }
  2463.         break;
  2464.     }
  2465.     return pBtn->_pfnBuddy ? CallWindowProc( pBtn->_pfnBuddy, hwnd, nMsg, wParam, lParam ) :
  2466.            0L;
  2467. }
  2468. //-------------------------------------------------------------------------//
  2469. //  CAccessibleBase IUnknown impl
  2470. STDMETHODIMP CAccessibleBase::QueryInterface( REFIID riid, void** ppvObj )
  2471. {
  2472.     HRESULT hres;
  2473.     static const QITAB qit[] = 
  2474.     {
  2475.         QITABENT(CAccessibleBase, IDispatch),
  2476.         QITABENT(CAccessibleBase, IAccessible),
  2477.         QITABENT(CAccessibleBase, IOleWindow),
  2478.         { 0 },
  2479.     };
  2480.     hres = QISearch(this, (LPCQITAB)qit, riid, ppvObj);
  2481.     return hres;
  2482. }
  2483. STDMETHODIMP_(ULONG) CAccessibleBase::AddRef()
  2484. {
  2485.     return InterlockedIncrement( (LONG*)&_cRef );
  2486. }
  2487. STDMETHODIMP_(ULONG) CAccessibleBase::Release()
  2488. {
  2489.     ULONG cRef = InterlockedDecrement( (LONG*)&_cRef );
  2490.     if( cRef <= 0 )
  2491.     {
  2492.         DllRelease();
  2493.         delete this;
  2494.     }
  2495.     return cRef;
  2496. }
  2497. //-------------------------------------------------------------------------//
  2498. //  CAccessibleBase IOleWindow impl
  2499. STDMETHODIMP CAccessibleBase::GetWindow( HWND* phwnd )
  2500. {
  2501.     *phwnd = _hwnd;
  2502.     return IsWindow( _hwnd ) ? S_OK : S_FALSE;
  2503. }
  2504. //-------------------------------------------------------------------------//
  2505. //  CAccessibleBase IDispatch impl
  2506. //-------------------------------------------------------------------------//
  2507. static BOOL _accLoadTypeInfo( ITypeInfo** ppti )
  2508. {
  2509.     ITypeLib* ptl;
  2510.     HRESULT hr = LoadTypeLib(L"oleacc.dll", &ptl);
  2511.     if( SUCCEEDED( hr ) )
  2512.     {
  2513.         hr = ptl->GetTypeInfoOfGuid( IID_IAccessible, ppti );
  2514.         ATOMICRELEASE( ptl );
  2515.     }
  2516.     return hr;
  2517. }
  2518. STDMETHODIMP CAccessibleBase::GetTypeInfoCount( UINT * pctinfo ) 
  2519.     *pctinfo = 1;
  2520.     return S_OK;
  2521. }
  2522. STDMETHODIMP CAccessibleBase::GetTypeInfo( UINT itinfo, LCID lcid, ITypeInfo** pptinfo )
  2523.     HRESULT hr = E_FAIL;
  2524.     if( NULL == _ptiAcc && FAILED( (hr = _accLoadTypeInfo( &_ptiAcc )) ) )
  2525.         return hr;
  2526.     *pptinfo = _ptiAcc;
  2527.     (*pptinfo)->AddRef();
  2528.     return S_OK;
  2529. }
  2530. STDMETHODIMP CAccessibleBase::GetIDsOfNames( 
  2531.     REFIID riid, 
  2532.     OLECHAR** rgszNames, 
  2533.     UINT cNames,
  2534.     LCID lcid, DISPID * rgdispid )
  2535. {
  2536.     HRESULT hr = E_FAIL;
  2537.     if( IID_NULL != riid && IID_IAccessible != riid )
  2538.         return DISP_E_UNKNOWNINTERFACE;
  2539.     if( NULL == _ptiAcc && FAILED( (hr = _accLoadTypeInfo( &_ptiAcc )) ) )
  2540.         return hr;
  2541.     return _ptiAcc->GetIDsOfNames( rgszNames, cNames, rgdispid );
  2542. }
  2543. STDMETHODIMP CAccessibleBase::Invoke( 
  2544.     DISPID  dispidMember, 
  2545.     REFIID  riid, 
  2546.     LCID    lcid, 
  2547.     WORD    wFlags,
  2548.     DISPPARAMS * pdispparams, 
  2549.     VARIANT * pvarResult, 
  2550.     EXCEPINFO * pexcepinfo, 
  2551.     UINT * puArgErr )
  2552. {
  2553.     HRESULT hr = E_FAIL;
  2554.     if( IID_NULL != riid && IID_IAccessible != riid )
  2555.         return DISP_E_UNKNOWNINTERFACE;
  2556.     if( NULL == _ptiAcc && FAILED( (hr = _accLoadTypeInfo( &_ptiAcc )) ) )
  2557.         return hr;
  2558.     return _ptiAcc->Invoke( this, dispidMember, wFlags, pdispparams, 
  2559.                             pvarResult, pexcepinfo, puArgErr );
  2560. }
  2561. STDMETHODIMP CAccessibleBase::get_accParent( IDispatch ** ppdispParent )
  2562. {
  2563.     *ppdispParent = NULL;
  2564.     if( IsWindow(_hwnd) )
  2565.         return AccessibleObjectFromWindow( _hwnd, OBJID_WINDOW,
  2566.                                            IID_IDispatch, (void **)ppdispParent );
  2567.     return S_FALSE;
  2568. }
  2569. STDMETHODIMP CAccessibleBase::get_accChildCount( long * pcChildren )
  2570. {
  2571.     *pcChildren = 0;
  2572.     return S_OK;
  2573. }
  2574. STDMETHODIMP CAccessibleBase::get_accChild( VARIANT varChildIndex, IDispatch ** ppdispChild)
  2575. {
  2576.     *ppdispChild = NULL;
  2577.     return S_FALSE;
  2578. }
  2579. STDMETHODIMP CAccessibleBase::get_accValue( VARIANT varChild, BSTR* pbstrValue)
  2580. {
  2581.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2582.     *pbstrValue = NULL;
  2583.     return S_FALSE;
  2584. }
  2585. STDMETHODIMP CAccessibleBase::get_accDescription( VARIANT varChild, BSTR * pbstrDescription)
  2586. {
  2587.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2588.     *pbstrDescription = NULL;
  2589.     return S_FALSE;
  2590. }
  2591. STDMETHODIMP CAccessibleBase::get_accRole( VARIANT varChild, VARIANT *pvarRole )
  2592. {
  2593.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2594.     pvarRole->vt    = VT_I4;
  2595.     pvarRole->lVal  = ROLE_SYSTEM_LINK;
  2596.     return S_OK;
  2597. }
  2598. STDMETHODIMP CAccessibleBase::get_accState( VARIANT varChild, VARIANT *pvarState )
  2599. {
  2600.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2601.     pvarState->vt = VT_I4;
  2602.     pvarState->lVal = STATE_SYSTEM_DEFAULT ;
  2603.     if( GetFocus() == _hwnd )
  2604.         pvarState->lVal |= STATE_SYSTEM_FOCUSED;
  2605.     else if( IsWindowEnabled( _hwnd ) )
  2606.         pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
  2607.     if( !IsWindowVisible( _hwnd ) )
  2608.         pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
  2609.     return S_OK;
  2610. }
  2611. STDMETHODIMP CAccessibleBase::get_accHelp( VARIANT varChild, BSTR* pbstrHelp )
  2612. {
  2613.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2614.     *pbstrHelp = NULL;
  2615.     return S_FALSE;
  2616. }
  2617. STDMETHODIMP CAccessibleBase::get_accHelpTopic( BSTR* pbstrHelpFile, VARIANT varChild, long* pidTopic )
  2618. {
  2619.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2620.     *pbstrHelpFile = NULL;
  2621.     *pidTopic    = -1;
  2622.     return S_FALSE;
  2623. }
  2624. STDMETHODIMP CAccessibleBase::get_accKeyboardShortcut( VARIANT varChild, BSTR* pbstrKeyboardShortcut )
  2625. {
  2626.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2627.     *pbstrKeyboardShortcut = NULL;
  2628.     return S_FALSE;
  2629. }
  2630. STDMETHODIMP CAccessibleBase::get_accFocus( VARIANT FAR * pvarFocusChild )
  2631. {
  2632.     HWND hwndFocus;
  2633.     if( (hwndFocus = GetFocus()) == _hwnd || IsChild( _hwnd, hwndFocus ) )
  2634.     {
  2635.         pvarFocusChild->vt = VT_I4;
  2636.         pvarFocusChild->lVal = CHILDID_SELF;
  2637.         return S_OK;
  2638.     }
  2639.     return S_FALSE;
  2640. }
  2641. STDMETHODIMP CAccessibleBase::get_accSelection( VARIANT FAR * pvarSelectedChildren )
  2642. {
  2643.     return get_accFocus( pvarSelectedChildren );  // implemented same as focus.
  2644. }
  2645. STDMETHODIMP CAccessibleBase::get_accDefaultAction( VARIANT varChild, BSTR* pbstrDefaultAction )
  2646. {
  2647.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2648.     WCHAR wsz[128];
  2649.     if( LoadStringW( HINST_THISDLL, GetDefaultActionStringID(), wsz, ARRAYSIZE(wsz) ) )
  2650.     {
  2651.         if( NULL == (*pbstrDefaultAction = SysAllocString( wsz )) )
  2652.             return E_OUTOFMEMORY;
  2653.         return S_OK;
  2654.     }
  2655.     return E_FAIL;
  2656. }
  2657. STDMETHODIMP CAccessibleBase::accSelect( long flagsSelect, VARIANT varChild )
  2658. {
  2659.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2660.     if( flagsSelect & SELFLAG_TAKEFOCUS )
  2661.     {
  2662.         SetFocus( _hwnd );
  2663.         return S_OK;
  2664.     }
  2665.     return S_FALSE;
  2666. }
  2667. STDMETHODIMP CAccessibleBase::accLocation( long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild )
  2668. {
  2669.     RECT rc;
  2670.     GetWindowRect( _hwnd, &rc );
  2671.     *pxLeft = rc.left;
  2672.     *pyTop  = rc.top;
  2673.     *pcxWidth  = RECTWIDTH(&rc);
  2674.     *pcyHeight = RECTHEIGHT(&rc);
  2675.     varChild.vt = VT_I4;
  2676.     varChild.lVal = CHILDID_SELF;
  2677.     return S_OK;
  2678. }
  2679. STDMETHODIMP CAccessibleBase::accNavigate( long navDir, VARIANT varStart, VARIANT * pvarEndUpAt )
  2680. {
  2681.     return S_FALSE;
  2682. }
  2683. STDMETHODIMP CAccessibleBase::accHitTest( long xLeft, long yTop, VARIANT * pvarChildAtPoint )
  2684. {
  2685.     pvarChildAtPoint->vt   = VT_I4;
  2686.     pvarChildAtPoint->lVal = CHILDID_SELF;
  2687.     return S_OK;
  2688. }
  2689. STDMETHODIMP CAccessibleBase::put_accName( VARIANT varChild, BSTR bstrName )
  2690. {
  2691.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2692.     return S_FALSE;
  2693. }
  2694. STDMETHODIMP CAccessibleBase::put_accValue( VARIANT varChild, BSTR bstrValue )
  2695. {
  2696.     VALIDATEACCCHILD( varChild, CHILDID_SELF, E_INVALIDARG );
  2697.     return S_FALSE;
  2698. }
  2699. #ifdef KEYBOARDCUES
  2700. //-------------------------------------------------------------------------//
  2701. //  KEYBOARDCUES helpes
  2702. BOOL _HandleWM_UPDATEUISTATE( 
  2703.     IN WPARAM wParam, 
  2704.     IN LPARAM lParam, 
  2705.     IN OUT UINT* puFlags )
  2706. {
  2707.     UINT uFlags = *puFlags;
  2708.     switch( LOWORD(wParam) )
  2709.     {
  2710.     case UIS_CLEAR:
  2711.         *puFlags &= ~(HIWORD(wParam));
  2712.         break;
  2713.     case UIS_SET:
  2714.         *puFlags |= HIWORD(wParam);
  2715.         break;
  2716.     }
  2717.     return uFlags != *puFlags;
  2718. }
  2719. void _InitializeUISTATE( IN HWND hwnd, IN OUT UINT* puFlags )
  2720. {
  2721.     HWND hwndParent = GetParent( hwnd );
  2722.     *puFlags = (UINT)SendMessage( hwndParent, WM_QUERYUISTATE, 0, 0L );
  2723. }
  2724. #endif//KEYBOARDCUES