home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / ole / oleview / iviewers / idataobj.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-27  |  33.3 KB  |  1,256 lines

  1. // idataobj.cpp
  2. //
  3. // Implementation file for the IDataObject interface viewer.
  4. //
  5.  
  6. // This is a part of the Microsoft Foundation Classes C++ library.
  7. // Copyright (C) 1992-1998 Microsoft Corporation
  8. // All rights reserved.
  9. //
  10. // This source code is only intended as a supplement to the
  11. // Microsoft Foundation Classes Reference and related
  12. // electronic documentation provided with the library.
  13. // See these sources for detailed information regarding the
  14. // Microsoft Foundation Classes product.
  15. //
  16. // Revisions:
  17. //  August 7, 1993  cek     First implementation.
  18. //  February 19, 1994 cek   Removed XRT stuff.  Fixed minor bugs.
  19. //  April 9, 1994 cek       Pulled from XRTView and genericized again.
  20. //
  21. #include "stdafx.h"
  22. #include "iview.h"
  23. #include "util.h"
  24. #include "idataobj.h"
  25. #include "iadvsink.h"
  26.  
  27. static HFONT      g_hFont ;
  28. static HFONT      g_hFontBold ;
  29. static UINT        g_cyFont ;
  30.  
  31. static WNDPROC     g_pfnEdit = NULL ;
  32.  
  33. #define I_NORMAL        0x0000
  34. #define I_LABEL         0x0001
  35. #define I_COLUMNHEAD    0x0002
  36. #define I_COLUMNENTRY   0x0003
  37.  
  38. typedef struct FAR tagITEMDATA
  39. {
  40.     UINT            nLevel ;
  41.     UINT            uiType ;
  42.     int             cColumns ;
  43.     LPCOLUMNSTRUCT  rgCol ;
  44.     LPVOID          lpData ;
  45.  
  46. } ITEMDATA, *PITEMDATA, FAR*LPITEMDATA ;
  47.  
  48. extern "C"
  49. LRESULT EXPORT CALLBACK fnEditSubclass( HWND hwnd, UINT uiMsg, WPARAM wP, LPARAM lP ) ;
  50.  
  51. LPCTSTR MyGetClipboardFormatName( UINT cf ) ;
  52.  
  53. /////////////////////////////////////////////////////////////////////////////
  54. // CIDataObjectDlg dialog
  55. //
  56. CIDataObjectDlg::CIDataObjectDlg( HWND hwnd, LPDATAOBJECT lpDO, REFIID /*riid*/, LPTSTR lpszName )
  57. {
  58.     m_lbFmtEtc = NULL ;
  59.     m_edtAdvise = NULL ;
  60.     m_MetaFile.hMF = NULL ;
  61.  
  62.     m_hWndParent = hwnd ;
  63.     m_lpDO = lpDO ;
  64.     m_lpszName = lpszName ;
  65.  
  66.     m_pSink = NULL ;
  67.     m_dwConn = 0 ;
  68.     m_advf = ADVF_PRIMEFIRST ;
  69.  
  70.     m_fetc.cfFormat = CF_TEXT ;
  71.     m_fetc.dwAspect = DVASPECT_CONTENT ;
  72.     m_fetc.ptd = NULL ;
  73.     m_fetc.tymed = TYMED_HGLOBAL ;
  74.     m_fetc.lindex = - 1 ;
  75.  
  76.     m_fDoOnGetDataPosted = FALSE ;
  77.  
  78.     m_cchOutput = 0 ;
  79.     m_cLinesOutput = 0 ;
  80. }
  81.  
  82. CIDataObjectDlg::~CIDataObjectDlg()
  83. {
  84.     if (m_MetaFile.hMF != NULL)
  85.         DeleteMetaFile( m_MetaFile.hMF ) ;
  86. }
  87.  
  88. int CIDataObjectDlg::DoModal( void )
  89. {
  90.     if (-1 == DialogBoxParam( GetModuleHandle(_T("IVIEWERS.DLL")), MAKEINTRESOURCE( IDD_IDATAOBJDLG ), m_hWndParent, (DLGPROC)fnIDataObjectDlg, (LONG)this ))
  91.     {
  92.         DWORD dw = GetLastError() ;
  93.         ErrorMessage( "Could not open dialog box", dw ) ;
  94.         return -1 ;
  95.     }
  96.     else
  97.         return 0 ;
  98. }
  99.  
  100. extern "C"
  101. BOOL EXPORT CALLBACK fnIDataObjectDlg( HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam )
  102. {
  103.     CIDataObjectDlg * pIDOD =(CIDataObjectDlg *)GetWindowLong( hDlg, DWL_USER ) ;
  104.  
  105.     switch (uiMsg)
  106.     {
  107.         case WM_INITDIALOG:
  108.             pIDOD=(CIDataObjectDlg *)lParam ;
  109.             if (pIDOD==NULL)
  110.             {
  111.                 EndDialog( hDlg, 0 ) ;
  112.                 return TRUE ;
  113.             }
  114.             SetWindowLong( hDlg, DWL_USER, (LONG)pIDOD ) ;
  115.             pIDOD->m_hDlg = hDlg ;
  116.             return pIDOD->OnInitDialog() ;
  117.         break ;
  118.  
  119.         case WM_DESTROY:
  120.             if (pIDOD)
  121.                 pIDOD->OnDestroy() ;
  122.         break ;
  123.  
  124.         case WM_SIZE:
  125.             if (pIDOD)
  126.                 pIDOD->OnSize( (UINT)wParam, LOWORD( lParam ), HIWORD( lParam ) ) ;
  127.         break ;
  128.  
  129.         case WM_COMMAND:
  130.         {
  131.             WORD wNotifyCode = HIWORD(wParam);
  132.             WORD wID = LOWORD(wParam);
  133.  
  134.             switch (wID)
  135.             {
  136.                 case IDCANCEL:
  137.                     EndDialog( hDlg, IDCANCEL ) ;
  138.                 break ;
  139.  
  140.                 case IDC_DOGETDATA:
  141.                     pIDOD->OnDoGetData() ;
  142.                 break ;
  143.  
  144.                 case IDC_SETUPADVISE:
  145.                     if (pIDOD->m_dwConn != 0)
  146.                         pIDOD->OnKillAdvise() ;
  147.                     else
  148.                         pIDOD->OnSetupAdvise() ;
  149.                 break ;
  150.  
  151.                 case IDC_CLEAROUTPUT:
  152.                     if (pIDOD)
  153.                     {
  154.                         pIDOD->m_cchOutput = 0 ;
  155.                         pIDOD->m_cLinesOutput = 0 ;
  156.                         if (pIDOD->m_MetaFile.hMF != NULL)
  157.                         {
  158.                             DeleteMetaFile( pIDOD->m_MetaFile.hMF ) ;
  159.                             pIDOD->m_MetaFile.hMF = NULL ;
  160.                         }
  161.                         SetWindowText( pIDOD->m_edtAdvise, _T("") ) ;
  162.                         InvalidateRect( pIDOD->m_edtAdvise, NULL, TRUE ) ;
  163.                         UpdateWindow( pIDOD->m_edtAdvise ) ;
  164.                     }
  165.                 break ;
  166.  
  167.                 case IDC_FORMATETC:
  168.                     if (wNotifyCode == LBN_SELCHANGE)
  169.                         pIDOD->OnSelChangeFormatEtc() ;
  170.                 break ;
  171.  
  172.                 case IDC_UPDATEDISPLAY:
  173.                     pIDOD->m_fUpdateDisplay = Button_GetCheck( pIDOD->m_chkUpdateDisplay ) ;
  174.                 break ;
  175.  
  176.                 case IDC_PRIMEFIRST:
  177.                     if (Button_GetCheck( pIDOD->m_chkPrimeFirst ))
  178.                         pIDOD->m_advf = ADVF_PRIMEFIRST ;
  179.                     else
  180.                         pIDOD->m_advf = ADVF_NODATA ;
  181.                 break ;
  182.  
  183.                 case IDC_ADVISEDATA:
  184.                     if (wNotifyCode == EN_ERRSPACE)
  185.                     {
  186.                         #ifdef _DEBUG
  187.                         OutputDebugString(_T("Output Edit Control reports EN_ERRSPACE\r\n")) ;
  188.                         #endif
  189.                     }
  190.                 break ;
  191.             }
  192.         }
  193.         break ;
  194.  
  195.         case WM_DRAWITEM:
  196.             if (pIDOD)
  197.                 pIDOD->OnDrawItem( wParam, (LPDRAWITEMSTRUCT)lParam ) ;
  198.         break ;
  199.  
  200.         case WM_MEASUREITEM:
  201.             if (pIDOD)
  202.                 pIDOD->OnMeasureItem( wParam, (LPMEASUREITEMSTRUCT)lParam ) ;
  203.         break ;
  204.  
  205.         case WM_OUTPUTBUFFERHASDATA:
  206.             if (pIDOD)
  207.                 pIDOD->OnOutputBufferHasData() ;
  208.         break ;
  209.  
  210.         default:
  211.            return FALSE ;
  212.     }
  213.     return TRUE ;
  214. }
  215.  
  216.  
  217. /////////////////////////////////////////////////////////////////////////////
  218. // CIDataObjectDlg message handlers
  219.  
  220. BOOL CIDataObjectDlg::OnInitDialog()
  221. {
  222.     m_btnDoGetData = GetDlgItem( m_hDlg, IDC_DOGETDATA ) ;
  223.     m_btnSetupAdvise = GetDlgItem( m_hDlg, IDC_SETUPADVISE ) ;
  224.  
  225.     m_lbGetData = GetDlgItem( m_hDlg, IDC_GETDATA ) ;
  226.     m_lbFmtEtc = GetDlgItem( m_hDlg, IDC_FORMATETC ) ;
  227.  
  228.     m_edtAdvise = GetDlgItem( m_hDlg, IDC_ADVISEDATA ) ;
  229.  
  230.     // Sublcass the edit so we can override it's WM_PAINT and
  231.     // draw a metafile if we want to...
  232.     if (g_pfnEdit == NULL)
  233.         g_pfnEdit = SubclassWindow( m_edtAdvise, fnEditSubclass) ;
  234.  
  235.     m_chkUpdateDisplay = GetDlgItem( m_hDlg, IDC_UPDATEDISPLAY );
  236.     m_fUpdateDisplay = TRUE ;
  237.     Button_SetCheck( m_chkUpdateDisplay, m_fUpdateDisplay ) ;
  238.  
  239.     m_chkPrimeFirst = GetDlgItem( m_hDlg, IDC_PRIMEFIRST );
  240.     Button_SetCheck( m_chkPrimeFirst, m_advf == ADVF_PRIMEFIRST ) ;
  241.  
  242.     //m_chkDump = GetDlgItem( m_hDlg, IDC_DUMPTOFILE ) ;
  243.     //EnableWindow( m_chkDump, FALSE ) ;
  244.  
  245.     TEXTMETRIC  tm ;
  246.     HDC hdc = GetDC(NULL);
  247.     g_hFont = CreateFont( -8, 0, 0, 0, 0, 0,
  248.                 0, 0, ANSI_CHARSET, 0, 0, 0,
  249.                 FF_SWISS, _T("MS Sans Serif") ) ;
  250.     g_hFontBold = CreateFont( -8, 0, 0, 0, 0, FW_BOLD,
  251.                 0, 0, ANSI_CHARSET, 0, 0, 0,
  252.                 FF_SWISS, _T("MS Sans Serif") ) ;
  253.     HFONT hFont = (HFONT)SelectObject( hdc, g_hFont ) ;
  254.     GetTextMetrics( hdc, &tm ) ;
  255.     SelectObject( hdc, hFont ) ;
  256.     ReleaseDC( NULL, hdc ) ;
  257.     g_cyFont = tm.tmHeight + tm.tmExternalLeading ;
  258.  
  259.     SetWindowFont( m_lbFmtEtc, g_hFont, TRUE ) ;
  260.     SetWindowFont( m_lbGetData, g_hFont, TRUE ) ;
  261.     SetWindowFont( m_edtAdvise, g_hFont, TRUE ) ;
  262.  
  263.     DoIDataObject( 0, (LPDATAOBJECT)m_lpDO ) ;
  264.  
  265.     DlgCenter( m_hDlg, m_hWndParent, FALSE ) ;
  266.     RECT rc ;
  267.     GetWindowRect( m_hDlg, &rc ) ;
  268.     SetWindowPos( m_hDlg, NULL, rc.left, rc.top, rc.right - rc.left + 1,
  269.                   rc.bottom - rc.top +1, SWP_NOMOVE|SWP_NOZORDER | SWP_NOACTIVATE ) ;
  270.  
  271.     //SetWindowText( m_hDlg, m_lpszName ) ;
  272.  
  273.     return TRUE;  // return TRUE  unless you set the focus to a control
  274. }
  275.  
  276. void CIDataObjectDlg::OnDestroy()
  277. {
  278.     OnKillAdvise() ;
  279.  
  280.     if (g_pfnEdit != NULL && m_edtAdvise && IsWindow(m_edtAdvise))
  281.     {
  282.         SubclassWindow(m_edtAdvise, g_pfnEdit) ;
  283.         g_pfnEdit = NULL ;
  284.     }
  285.  
  286.     int c = ListBox_GetCount( m_lbFmtEtc ) ;
  287.     for (int i = 0 ; i < c ; i++)
  288.     {
  289.         LPITEMDATA lpID = (LPITEMDATA)ListBox_GetItemData( m_lbFmtEtc, i ) ;
  290.         if (lpID)
  291.         {
  292.             if (lpID->uiType == I_COLUMNHEAD)
  293.                 delete lpID->rgCol ;
  294.  
  295.             if (lpID->lpData != NULL)
  296.                 delete (LPFORMATETC)lpID->lpData ;
  297.             delete lpID ;
  298.         }
  299.     }
  300.  
  301.     if (g_hFont)
  302.         DeleteObject( g_hFont );
  303.  
  304.     if (g_hFontBold)
  305.         DeleteObject (g_hFontBold) ;
  306. }
  307.  
  308. void CIDataObjectDlg::OnSize(UINT, int cx, int cy)
  309. {
  310.     if (m_edtAdvise && !IsWindow( m_edtAdvise ) )
  311.         return ;
  312.  
  313.     RECT    rc ;
  314.     RECT    rcWnd ;
  315.     GetClientRect( m_hDlg, &rcWnd ) ;
  316.     GetWindowRect( m_edtAdvise, &rc ) ;
  317.     MapWindowPoints( NULL, m_hDlg, (POINT *)&rc, 2 ) ;
  318.     SetWindowPos( m_edtAdvise, NULL, -1, rc.top, cx+2, cy - rc.top + 1,
  319.                 SWP_NOZORDER | SWP_NOACTIVATE ) ;
  320. }
  321.  
  322.  
  323. void CIDataObjectDlg::OnDrawItem(int, LPDRAWITEMSTRUCT lpDIS )
  324. {
  325.     if (lpDIS->itemID == LB_ERR)
  326.         return ;
  327.  
  328.     LPITEMDATA      lpID ;
  329.     COLORREF        rgbBack ;
  330.     RECT            rcFocus ;
  331.     BOOL            fSelected ;
  332.     int             x, y, cy ;
  333.     TCHAR           szItem[128] ;
  334.     HFONT           hFont = 0;
  335.  
  336.     lpID = (LPITEMDATA)lpDIS->itemData ;
  337.     rcFocus = lpDIS->rcItem ;
  338.     ListBox_GetText( m_lbFmtEtc, lpDIS->itemID, szItem ) ;
  339.  
  340.     if (fSelected = (lpDIS->itemState & ODS_SELECTED) ? TRUE : FALSE)
  341.     {
  342.         SetTextColor( lpDIS->hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) ) ;
  343.         SetBkColor( lpDIS->hDC, rgbBack = GetSysColor( COLOR_HIGHLIGHT ) ) ;
  344.     }
  345.     else
  346.     {
  347.         SetTextColor( lpDIS->hDC, GetSysColor( COLOR_WINDOWTEXT ) ) ;
  348.         SetBkColor( lpDIS->hDC, rgbBack = GetSysColor( COLOR_WINDOW ) ) ;
  349.     }
  350.  
  351.     // if we are loosing focus, remove the focus rect before
  352.     // drawing.
  353.     //
  354.     if ((lpDIS->itemAction) & (ODA_FOCUS))
  355.         if (!((lpDIS->itemState) & (ODS_FOCUS)))
  356.             DrawFocusRect( lpDIS->hDC, &rcFocus ) ;
  357.  
  358.     y = lpDIS->rcItem.top ;
  359.     x = lpDIS->rcItem.left ;
  360.  
  361.     int cxChar = GetTextMetricsCorrectly( lpDIS->hDC, NULL ) ;
  362.  
  363.     if (lpID && (lpID->uiType == I_COLUMNHEAD || lpID->uiType == I_LABEL))
  364.         hFont = (HFONT)SelectObject( lpDIS->hDC, g_hFontBold ) ;
  365.  
  366.     cy = (rcFocus.bottom - rcFocus.top - g_cyFont) / 2 ;
  367.  
  368.     ExtTextOut( lpDIS->hDC, x+2, y + cy, ETO_OPAQUE, &lpDIS->rcItem, NULL, 0, NULL ) ;
  369.     if (lpID)
  370.         ColumnTextOut( lpDIS->hDC, x + 2 + ((cxChar*3) * lpID->nLevel), y + cy, szItem, lpID->cColumns, lpID->rgCol ) ;
  371.  
  372.     if (lpID && lpID->uiType == I_COLUMNHEAD )
  373.     {
  374.         COLORREF    rgb ;
  375.         RECT        rc = rcFocus ;
  376.         rgb = SetBkColor( lpDIS->hDC, GetTextColor( lpDIS->hDC ) ) ;
  377.         rc.top = rc.bottom - 1 ;
  378.         rc.left = x + 2 + ((cxChar*3) * lpID->nLevel) ;
  379.         ExtTextOut( lpDIS->hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL ) ;
  380.         SetBkColor( lpDIS->hDC, rgb ) ;
  381.     }
  382.  
  383.     if (lpID && (lpID->uiType == I_COLUMNHEAD || lpID->uiType == I_LABEL) && hFont)
  384.         (HFONT)SelectObject( lpDIS->hDC, hFont ) ;
  385.  
  386.     // if we are gaining focus draw the focus rect after drawing
  387.     // the text.
  388.     if ((lpDIS->itemAction) & (ODA_FOCUS))
  389.         if ((lpDIS->itemState) & (ODS_FOCUS))
  390.              DrawFocusRect( lpDIS->hDC, &rcFocus ) ;
  391.  
  392.     if (fSelected)
  393.     {
  394.         SetTextColor( lpDIS->hDC, GetSysColor( COLOR_WINDOWTEXT ) ) ;
  395.         SetBkColor( lpDIS->hDC, GetSysColor( COLOR_WINDOW ) ) ;
  396.     }
  397.  
  398. }
  399.  
  400. void CIDataObjectDlg::OnMeasureItem(int, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
  401. {
  402.     lpMeasureItemStruct->itemHeight = g_cyFont ;
  403. }
  404.  
  405. void CIDataObjectDlg::OnDblClkFormatEtc()
  406. {
  407. }
  408.  
  409. void CIDataObjectDlg::OnSelChangeFormatEtc()
  410. {
  411.  
  412.     if (m_MetaFile.hMF != NULL)
  413.     {
  414.         DeleteMetaFile( m_MetaFile.hMF ) ;
  415.         m_MetaFile.hMF = NULL ;
  416.     }
  417.     InvalidateRect( m_edtAdvise, NULL, TRUE ) ;
  418.     SetWindowText( m_edtAdvise, _T("") ) ;
  419.  
  420.     int i = ListBox_GetCurSel( m_lbFmtEtc ) ;
  421.     if (i != LB_ERR)
  422.     {
  423.         LPITEMDATA lpid = (LPITEMDATA)ListBox_GetItemData( m_lbFmtEtc, i ) ;
  424.         if (lpid && lpid->lpData)
  425.         {
  426.             m_fetc = *(LPFORMATETC)lpid->lpData ;
  427.             return ;
  428.         }
  429.     }
  430.  
  431.     m_fetc.cfFormat = CF_TEXT ;
  432.     m_fetc.dwAspect = DVASPECT_CONTENT ;
  433.     m_fetc.ptd = NULL ;
  434.     m_fetc.tymed = TYMED_HGLOBAL ;
  435.     m_fetc.lindex = - 1 ;
  436. }
  437.  
  438.  
  439. void CIDataObjectDlg::OnDoGetData()
  440. {
  441.     HRESULT     hr ;
  442.     STGMEDIUM   stm ;
  443.     TCHAR       szBuf[256] ;
  444.  
  445.     if (m_lpDO == NULL)
  446.     {
  447.         ListBox_AddString( m_lbGetData, _T( "IDataObject viewer internal error: m_lpDO == NULL" ) ) ;
  448.         return ;
  449.     }
  450.  
  451.     wsprintf( szBuf, _T( "Calling GetData for %s" ), (LPCTSTR)MyGetClipboardFormatName( m_fetc.cfFormat ) ) ;
  452.     ListBox_AddString( m_lbGetData, szBuf ) ;
  453.  
  454.     hr = m_lpDO->QueryGetData( &m_fetc ) ;
  455.     if (FAILED( hr ))
  456.     {
  457.         wsprintf( szBuf, _T( "QueryGetData() failed:  %s" ),
  458.                     (LPTSTR)HRtoString( hr ) ) ;
  459.         ListBox_AddString( m_lbGetData, szBuf ) ;
  460.         return ;
  461.     }
  462.  
  463.     stm.tymed = m_fetc.tymed ;
  464.     hr = m_lpDO->GetData( &m_fetc, &stm ) ;
  465.     if (FAILED( hr ))
  466.     {
  467.         wsprintf( szBuf, _T( "GetData() failed:  %s" ),
  468.                     (LPTSTR)HRtoString( hr ) ) ;
  469.         ListBox_AddString( m_lbGetData, szBuf ) ;
  470.         return ;
  471.     }
  472.  
  473.     if (FAILED( hr = GotData( &m_fetc, &stm ) ))
  474.     {
  475.         wsprintf( szBuf, _T( "IDataDlg::GotData() failed:  %s" ),
  476.                     (LPTSTR)HRtoString( hr ) ) ;
  477.         ListBox_AddString( m_lbGetData, szBuf ) ;
  478.         return ;
  479.     }
  480.  
  481.     // If we're warm linked, then don't let the queue overfill.  Allow only one
  482.     // message at a time.  This is similar to the way Excel handles it's DDE Links.
  483.     //
  484.     m_fDoOnGetDataPosted = FALSE ;
  485. }
  486.  
  487.  
  488. HRESULT CIDataObjectDlg::GotData( LPFORMATETC lpfetc, LPSTGMEDIUM lpstm )
  489. {
  490.     HRESULT hr = NOERROR ;
  491.     BOOL    fHandled = FALSE ;
  492.  
  493.     if ((lpfetc->cfFormat == CF_TEXT  || lpfetc->cfFormat == CF_UNICODETEXT)
  494.             && lpstm->tymed & TYMED_HGLOBAL)
  495.     {
  496.         LPTSTR lpsz = (LPTSTR)GlobalLock( lpstm->hGlobal ) ;
  497.         if (lpsz == NULL)
  498.         {
  499.             ListBox_AddString( m_lbGetData, _T( "hGlobal returned by GetData is NULL!" ) ) ;
  500.             ReleaseStgMedium( lpstm );
  501.             return ResultFromScode( E_FAIL ) ;
  502.         }
  503.  
  504.         if (m_fUpdateDisplay == TRUE)
  505.         {
  506.         #ifndef _UNICODE
  507.             if (lpfetc->cfFormat == CF_UNICODETEXT)
  508.             {
  509.                 UINT n = wcslen( (LPWSTR)lpsz ) ;
  510.                 TCHAR* sztemp = new TCHAR[n+1] ;
  511.                 wcstombs( sztemp, (LPWSTR)lpsz, n ) ;
  512.                 WriteToOutput( sztemp ) ;
  513.                 delete [] sztemp ;
  514.             }
  515.             else
  516.                 WriteToOutput( lpsz ) ;
  517.         #else
  518.             if (lpfetc->cfFormat == CF_TEXT)
  519.             {
  520.                 UINT n = _mbstrlen( (LPCSTR)lpsz ) ;
  521.                 TCHAR* sztemp = new TCHAR[n+1] ;
  522.                 mbstowcs( sztemp, (LPSTR)lpsz, n ) ;
  523.                 WriteToOutput( sztemp ) ;
  524.                 delete [] sztemp ;
  525.             }
  526.             else
  527.                 WriteToOutput( lpsz ) ;
  528.         #endif
  529.         }
  530.  
  531.         GlobalUnlock( lpstm->hGlobal ) ;
  532.  
  533.         fHandled = TRUE ;
  534.     }
  535.  
  536.     if (lpfetc->cfFormat == CF_METAFILEPICT && lpstm->tymed & TYMED_MFPICT)
  537.     {
  538.         LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(lpstm->hGlobal) ;
  539.         if (pMF)
  540.         {
  541.             if (m_MetaFile.hMF)
  542.             {
  543.                 DeleteMetaFile( m_MetaFile.hMF ) ;
  544.                 m_MetaFile.hMF = NULL ;
  545.             }
  546.  
  547.             if (m_fUpdateDisplay)
  548.             {
  549.                 m_MetaFile = *pMF ;
  550.                 m_MetaFile.hMF = CopyMetaFile( pMF->hMF, NULL ) ;
  551.                 InvalidateRect( m_edtAdvise, NULL, FALSE ) ;
  552.             }
  553.         }
  554.         fHandled = TRUE ;
  555.     }
  556.  
  557.     m_cOnDataChanges++ ;
  558.  
  559.     ReleaseStgMedium( lpstm );
  560.  
  561.     if (fHandled == FALSE)
  562.     {
  563.         ListBox_AddString( m_lbGetData, _T( "The IDataObject viewer does not know how to decode this FORMATETC" ) ) ;
  564.     }
  565.  
  566.     return  hr;
  567. }
  568.  
  569. void CIDataObjectDlg::OnSetupAdvise()
  570. {
  571.     TCHAR szBuf[128] ;
  572.     OnKillAdvise() ;
  573.  
  574.     m_pSink = new CImpIAdviseSink( this ) ;
  575.  
  576.     if (m_pSink == NULL)
  577.     {
  578.         ListBox_AddString( m_lbGetData, _T( "CImpIAdviseSink constructor failed" ) ) ;
  579.         return ;
  580.     }
  581.  
  582.     SetWindowText( m_btnSetupAdvise, _T( "&Kill Advise" ) ) ;
  583.  
  584.     wsprintf( szBuf, _T( "Advise started with a cfFormat of %s" ),
  585.                 (LPCTSTR)MyGetClipboardFormatName( m_fetc.cfFormat ) ) ;
  586.     ListBox_AddString( m_lbGetData, szBuf ) ;
  587.  
  588.     m_dwTime = GetTickCount() ;
  589.     m_cOnDataChanges = 0 ;
  590.  
  591.     HRESULT hr = m_lpDO->DAdvise( &m_fetc, m_advf, m_pSink, &m_dwConn ) ;
  592.     if (FAILED( hr ))
  593.     {
  594.         wsprintf( szBuf, _T( "DAdvise() failed:  %s" ), (LPTSTR)HRtoString( hr ) ) ;
  595.         ListBox_AddString( m_lbGetData, szBuf ) ;
  596.         SetWindowText( m_btnSetupAdvise, _T( "&DAdvise" ) ) ;
  597.         return ;
  598.     }
  599.  
  600. }
  601.  
  602. void CIDataObjectDlg::OnKillAdvise()
  603. {
  604.     if (m_dwConn != 0)
  605.     {
  606.         TCHAR szBuf[128] ;
  607.         DWORD dwTime = max( 1000, GetTickCount() - m_dwTime ) ;    // avoid div by zero
  608.  
  609.         m_lpDO->DUnadvise( m_dwConn ) ;
  610.         m_dwConn = 0 ;
  611.         wsprintf( szBuf, _T( "  Advise Killed after %lu milliseconds, and %lu OnDataChanges;" ), dwTime, m_cOnDataChanges ) ;
  612.         ListBox_AddString( m_lbGetData, szBuf ) ;
  613.         wsprintf( szBuf, _T( "  That's %lu.%lu OnDataChange calls per second." ),
  614.                         m_cOnDataChanges / (dwTime / 1000L),
  615.                         m_cOnDataChanges % (dwTime / 1000L) ) ;
  616.         ListBox_AddString( m_lbGetData, szBuf ) ;
  617.     }
  618.  
  619.     if (m_pSink != NULL)
  620.     {
  621.         m_pSink->Release() ;
  622.         m_pSink = NULL ;
  623.     }
  624.  
  625.     SetWindowText( m_btnSetupAdvise, _T( "&DAdvise" ) ) ;
  626. }
  627.  
  628. // DoIDataObject
  629. //
  630. // This is where we start doing IDataObject dirty work.
  631. // This function calls pIDataObject->EnumFormatEtc twice, once
  632. // each for DATADIR_GET and DATADIR_SET, to get a pIEnumFORMATETC.
  633. //
  634. // We then call DoIEnumFormatEtc() with this pointer to actually
  635. // perform the enumaration.
  636. //
  637. BOOL CIDataObjectDlg::DoIDataObject( UINT nLevel, LPDATAOBJECT pIDataObject )
  638. {
  639.     LPENUMFORMATETC pEFE = NULL ;
  640.     HRESULT     hr1 ;
  641.  
  642.     hr1 = pIDataObject->EnumFormatEtc( DATADIR_GET, &pEFE ) ;
  643.     if (SUCCEEDED( hr1 ))
  644.     {
  645.         if (pEFE == NULL)
  646.         {
  647.             AfxMessageBox(_T("IDataObject::EnumFormatEtc returned success, but returned IEnumFORMATETC pointer is NULL.")) ;
  648.             AddItem( nLevel, _T( "<<DATA_DIRGET failed>>" ), NULL, I_LABEL ) ;
  649.             return FALSE ;
  650.         }
  651.  
  652.         DoIEnumFormatEtc( nLevel, pEFE ) ;
  653.         pEFE->Release() ;
  654.     }
  655.     else
  656.         AddItem( nLevel, _T( "<<DATA_DIRGET failed>>" ), NULL, I_LABEL ) ;
  657.  
  658.     if (SUCCEEDED(hr1))
  659.     {
  660.         ListBox_SetCurSel( m_lbFmtEtc, 0 ) ;
  661.         OnSelChangeFormatEtc() ;
  662.     }
  663.     return TRUE ;
  664. }
  665.  
  666. // This member takes a pointer to an IEnumFORMATETC and enumerates
  667. // the FORMATETC structures avaialbe, inserting them into our
  668. // listbox.
  669. //
  670. BOOL CIDataObjectDlg::DoIEnumFormatEtc( UINT nLevel, LPENUMFORMATETC pIFormatEtc )
  671. {
  672.     LPCOLUMNSTRUCT  rgCol = new COLUMNSTRUCT[5] ;
  673.     UINT    cxChar ;
  674.     HDC     hdc  = GetDC( NULL) ;
  675.  
  676.     // We use the "ColumnTextOut" code in util.c to create nice
  677.     // columns in our list box.
  678.     //
  679.     HFONT hFont = (HFONT)SelectObject( hdc, g_hFontBold ) ;
  680.     cxChar = GetTextMetricsCorrectly( hdc, NULL ) ;
  681.     rgCol[0].nLeft = 0 ;
  682.     rgCol[0].nRight = cxChar * lstrlen( _T( "CF_METAFILEPICT_" ) ) ;
  683.     rgCol[0].uiFlags = DT_LEFT ;
  684.  
  685.     rgCol[1].nLeft = rgCol[0].nRight + cxChar ;
  686.     rgCol[1].nRight = rgCol[1].nLeft + cxChar * lstrlen( _T( "0000:0000_" ) ) ;
  687.     rgCol[1].uiFlags = DT_LEFT ;
  688.  
  689.     rgCol[2].nLeft = rgCol[1].nRight + cxChar ;
  690.     rgCol[2].nRight = rgCol[2].nLeft + cxChar * lstrlen( _T( "THUMBNAIL_" ) ) ;
  691.     rgCol[2].uiFlags = DT_LEFT ;
  692.  
  693.     rgCol[3].nLeft = rgCol[2].nRight + cxChar ;
  694.     rgCol[3].nRight = rgCol[3].nLeft + cxChar * lstrlen( _T( "0000:0000_" ) ) ;
  695.     rgCol[3].uiFlags = DT_LEFT ;
  696.  
  697.     rgCol[4].nLeft = rgCol[3].nRight + cxChar ;
  698.     rgCol[4].nRight = rgCol[4].nLeft + cxChar * lstrlen( _T( "_HGLOBAL_" ) ) ;
  699.     rgCol[4].uiFlags = DT_LEFT ;
  700.  
  701.     (HFONT)SelectObject( hdc, hFont ) ;
  702.     ReleaseDC( NULL, hdc ) ;
  703.  
  704.     AddItem( nLevel, _T( "cfFormat\tptd\tdwAspect\tlindex\ttymed" ), NULL, I_COLUMNHEAD, 5, rgCol ) ;
  705.     LPFORMATETC     pfetc ;
  706.     FORMATETC       fetc ;
  707.     TCHAR           sz[512] ;
  708.     ULONG           ulFE ; // number returned
  709.  
  710.     while (pIFormatEtc->Next( 1, &fetc, &ulFE ) == S_OK)
  711.     {
  712.         // now insert into list
  713.         pfetc = new FORMATETC ;
  714.         *pfetc = fetc ;
  715.         lstrcpy( sz, MyGetClipboardFormatName( (UINT)fetc.cfFormat ) ) ;
  716.         lstrcat( sz, _T("\t") ) ;
  717.  
  718.         TCHAR szTemp[32] ;
  719.         wsprintf( szTemp, _T( "%04lX:%04lX\t" ), (DWORD)HIWORD( (DWORD)fetc.ptd ), (DWORD)LOWORD( (DWORD)fetc.ptd ) ) ;
  720.         lstrcat( sz, szTemp ) ;
  721.  
  722.         switch( fetc.dwAspect )
  723.         {
  724.             case DVASPECT_CONTENT: lstrcat( sz, _T( "CONTENT\t" ) ) ; break ;
  725.             case DVASPECT_THUMBNAIL: lstrcat( sz, _T( "THUMBNAIL\t" ) ) ; break ;
  726.             case DVASPECT_ICON: lstrcat( sz, _T( "ICON\t" ) ) ; break ;
  727.             case DVASPECT_DOCPRINT: lstrcat( sz, _T( "DOCPRINT\t" ) ) ; break ;
  728.             default:
  729.                 wsprintf( szTemp, _T( "%08lX\t" ), (DWORD)fetc.dwAspect ) ;
  730.                 lstrcat( sz, szTemp ) ;
  731.             break ;
  732.         }
  733.  
  734.         wsprintf( szTemp, _T( "%04lX:%04lX\t" ), (DWORD)HIWORD( fetc.lindex ), (DWORD)LOWORD( fetc.lindex ) ) ;
  735.         lstrcat( sz, szTemp ) ;
  736.  
  737.         switch( fetc.tymed )
  738.         {
  739.             case TYMED_HGLOBAL: lstrcat( sz, _T( "HGLOBAL\t" ) ) ; break ;
  740.             case TYMED_FILE: lstrcat( sz, _T( "FILE\t" ) ) ; break ;
  741.             case TYMED_ISTREAM: lstrcat( sz, _T( "ISTREAM\t" ) ) ; break ;
  742.             case TYMED_ISTORAGE: lstrcat( sz, _T( "ISTORAGE\t" ) ) ; break ;
  743.             case TYMED_GDI: lstrcat( sz, _T( "GDI\t" ) ) ; break ;
  744.             case TYMED_MFPICT: lstrcat( sz, _T( "MFPICT\t" ) ) ; break ;
  745.             case TYMED_NULL: lstrcat( sz, _T( "NULL\t" ) ) ; break ;
  746.             default:
  747.                 wsprintf( szTemp, _T( "%08lX\t" ), (DWORD)fetc.tymed ) ;
  748.                 lstrcat( sz, szTemp ) ;
  749.             break ;
  750.         }
  751.  
  752.         AddItem( nLevel, sz,
  753.                  pfetc, I_COLUMNENTRY, 5, rgCol ) ;
  754.     }
  755.  
  756.     return TRUE ;
  757. }
  758.  
  759. LPCTSTR MyGetClipboardFormatName( UINT cf )
  760. {
  761.     static TCHAR sz[256] ;
  762.     switch( cf )
  763.     {
  764.  
  765.         case CF_UNICODETEXT: lstrcpy( sz, _T( "CF_UNICODETEXT" ) ) ; break ;
  766.         case CF_ENHMETAFILE: lstrcpy( sz, _T( "CF_ENHMETAFILE" ) ) ; break ;
  767.         case CF_TEXT: lstrcpy( sz, _T( "CF_TEXT" ) ) ; break ;
  768.         case CF_BITMAP: lstrcpy( sz, _T( "CF_BITMAP" ) ) ; break ;
  769.         case CF_METAFILEPICT: lstrcpy( sz, _T( "CF_METAFILEPICT" ) ) ; break ;
  770.         case CF_SYLK: lstrcpy( sz, _T( "CF_SYLK" ) ) ; break ;
  771.         case CF_DIF: lstrcpy( sz, _T( "CF_DIF" ) ) ; break ;
  772.         case CF_TIFF: lstrcpy( sz, _T( "CF_TIFF" ) ) ; break ;
  773.         case CF_OEMTEXT: lstrcpy( sz, _T( "CF_OEMTEXT" ) ) ; break ;
  774.         case CF_DIB: lstrcpy( sz, _T( "CF_DIB" ) ) ; break ;
  775.         case CF_PALETTE: lstrcpy( sz, _T( "CF_PALETTE" ) ) ; break ;
  776.         case CF_PENDATA: lstrcpy( sz, _T( "CF_PENDATA" ) ) ; break ;
  777.         case CF_RIFF: lstrcpy( sz, _T( "CF_RIFF" ) ) ; break ;
  778.         case CF_WAVE: lstrcpy( sz, _T( "CF_WAVE" ) ) ; break ;
  779.         case 0: lstrcpy( sz, _T( "\"Wildcard Advise\"" ) ) ; break ;
  780.         default:
  781.             if (!GetClipboardFormatName( (UINT)cf, sz, 254 ))
  782.                 wsprintf( sz, _T( "%#08lX" ), (DWORD)cf ) ;
  783.         break ;
  784.     }
  785.  
  786.     return sz ;
  787. }
  788.  
  789.  
  790. UINT DBStrCpy(LPTSTR pszDst, LPCTSTR pszSrc, int far* pcLines) ;
  791.  
  792. BOOL  CIDataObjectDlg::WriteToOutput( LPTSTR lpsz )
  793. {
  794.     UINT cch;
  795.     int cLines;
  796.  
  797.     cch = DBStrCpy(NULL, lpsz, &cLines);
  798.  
  799.     if (cch > (CCHOUTPUTMAX - m_cchOutput))
  800.     {
  801.         MessageBeep(0);
  802.         return FALSE;
  803.     }
  804.  
  805.     DBStrCpy(&m_szOutput[m_cchOutput], lpsz, NULL);
  806.  
  807.     m_cchOutput += cch;
  808.     m_cLinesOutput += cLines;
  809.  
  810.     // If buffer was originally empty, wake up the main loop.
  811.     //
  812.     if (m_cchOutput == cch)
  813.         PostMessage( m_hDlg, WM_OUTPUTBUFFERHASDATA, 0, 0L);
  814.  
  815.     return TRUE;
  816. }
  817.  
  818. // This function performs a string copy (or string length if pszDst is NULL)
  819. // It also ensures the destination string is properly formatted for the
  820. // edit controls: line terminators must be CR followed by LF, in that orderc.
  821. // If pcLines != NULL, returns number of lines in the string in *pcLines
  822. //
  823. // (pulled from the dbwin sample)
  824. //
  825. UINT DBStrCpy(LPTSTR pszDst, LPCTSTR pszSrc, int far* pcLines)
  826. {
  827.     TCHAR ch;
  828.     UINT cch = 0;
  829.     int cLines = 0;
  830.  
  831.     while (ch = *pszSrc++)
  832.     {
  833.         // If we find a CR or LF, then store a CR/LF in
  834.         // the output string.
  835.         //
  836.         if (ch == 0x0d || ch == 0x0a)
  837.         {
  838.             cch += 2;
  839.             cLines++;
  840.             if (pszDst)
  841.             {
  842.                 *pszDst++ = 0x0d;
  843.                 *pszDst++ = 0x0a;
  844.             }
  845.  
  846.             // Skip any other CRs or LFs we find.
  847.             //
  848.             while ((ch = *pszSrc) && (ch == 0x0d || ch == 0x0a))
  849.                 ++pszSrc;
  850.         }
  851.         else
  852.         {
  853.             cch++;
  854.             if (pszDst)
  855.                 *pszDst++ = ch;
  856.         }
  857.     }
  858.     if (pszDst)
  859.         *pszDst = 0;
  860.  
  861.     if (pcLines)
  862.         *pcLines = cLines;
  863.  
  864.     return cch;
  865. }
  866.  
  867. // Whenever something happens that causes output, it calls the WriteToOutput function
  868. // which puts the data into a buffer (m_szOutput) and posts a message.  This is
  869. // the message handler.
  870. //
  871. void CIDataObjectDlg::OnOutputBufferHasData()
  872. {
  873.     if (m_szOutput != NULL && m_cchOutput != 0)
  874.     {
  875.         int cLines = Edit_GetLineCount(m_edtAdvise) + m_cLinesOutput ;
  876.  
  877.         if (cLines > CLINESMAX)
  878.         {
  879.             SetWindowRedraw( m_edtAdvise, FALSE ) ;
  880.             // If the total # of lines is greater than our arbitrary max lines
  881.             // remove some from the top.
  882.             //
  883.             Edit_SetSel( m_edtAdvise, 0, Edit_LineIndex( m_edtAdvise, cLines - CLINESMAX));
  884.             Edit_ReplaceSel( m_edtAdvise, _T(""));
  885.         }
  886.  
  887.         // Put current output buffer at the end of the edit control
  888.         //
  889.         Edit_SetSel( m_edtAdvise, (UINT)-1, (UINT)-1 )  ;   // select the end
  890.         Edit_ReplaceSel( m_edtAdvise, m_szOutput ) ;
  891.         Edit_SetSel( m_edtAdvise, (UINT)-1, (UINT)-1 )  ;   // select the end
  892.  
  893.         if (cLines > CLINESMAX)
  894.             SetWindowRedraw( m_edtAdvise, TRUE ) ;
  895.  
  896.         // Reset the output buffer
  897.         m_cLinesOutput = 0 ;
  898.         m_cchOutput = 0 ;
  899.     }
  900. }
  901.  
  902.  
  903. // AddItem does a ListBox_AddString() plus a ListBox_SetItemData().  The
  904. // item data that is set tells our owner-draw code what 'level' this
  905. // item is at (indentation) and whether it is a label (bold), column
  906. // head (column info is stored), or column entry (no column info is
  907. // stored, but previous column head is used in WM_DRAWITEM).
  908. //
  909. int CIDataObjectDlg::AddItem( UINT nLevel, LPTSTR sz, LPVOID lpData, UINT uiType, int cColumns, LPCOLUMNSTRUCT  rgCol )
  910. {
  911.     int i ;
  912.     LPITEMDATA      lpID ;
  913.  
  914.     i = ListBox_AddString( m_lbFmtEtc,  sz ) ;
  915.     lpID = new ITEMDATA ;
  916.     lpID->uiType = uiType ;
  917.     lpID->cColumns = cColumns ;
  918.     lpID->rgCol = rgCol ;
  919.     lpID->nLevel = nLevel ;
  920.     lpID->lpData = lpData ;
  921.     ListBox_SetItemData( m_lbFmtEtc, i, (DWORD)lpID ) ;
  922.     return i ;
  923. }
  924.  
  925. int CIDataObjectDlg::AddItem( UINT nLevel, LPTSTR sz, LPVOID lpData, UINT uiType )
  926. {
  927.     return AddItem( nLevel, sz, lpData, uiType, 0, NULL ) ;
  928. }
  929.  
  930. int CIDataObjectDlg::AddItem( UINT nLevel, LPTSTR sz, LPVOID lpData )
  931. {
  932.     return AddItem( nLevel, sz, lpData, I_NORMAL, 0, NULL ) ;
  933. }
  934.  
  935. extern "C"
  936. LRESULT EXPORT CALLBACK fnEditSubclass( HWND hwnd, UINT uiMsg, WPARAM wP, LPARAM lP )
  937. {
  938.     if (uiMsg == WM_PAINT)
  939.     {
  940.         HWND hwndParent = GetParent(hwnd) ;
  941.         CIDataObjectDlg * pIDOD =(CIDataObjectDlg *)GetWindowLong( hwndParent, DWL_USER ) ;
  942.         if (pIDOD && pIDOD->m_MetaFile.hMF != NULL)
  943.         {
  944.             PAINTSTRUCT ps ;
  945.             BeginPaint( hwnd, &ps ) ;
  946.  
  947. /*
  948.             if (pIDOD->m_MetaFile.mm == MM_ISOTROPIC || pIDOD->m_MetaFile.mm == MM_ANISOTROPIC)
  949.             {
  950.                 if (pIDOD->m_MetaFile.xExt != 0 && pIDOD->m_MetaFile.yExt != 0)
  951.                 {
  952.  
  953.                 }
  954.             }
  955.             else
  956.             {
  957.                 SetMapMode( ps.hdc, MM_ANISOTROPIC ) ;
  958.                 SetWindowOrg( ps.hdc, 0, 0 ) ;
  959.                 SetWindowExt( ps.hdc, pIDOD->m_MetaFile.xExt, pIDOD->m_MetaFile.yExt ) ;
  960.                 SetViewportExt( ps.hdc, pIDOD->m_MetaFile.xExt, pIDOD->m_MetaFile.yExt ) ;
  961.             }
  962. */
  963.             PlayMetaFile( ps.hdc, pIDOD->m_MetaFile.hMF ) ;
  964.  
  965.             EndPaint( hwnd, &ps ) ;
  966.             return 0L ;
  967.         }
  968.     }
  969.  
  970.     return CallWindowProc( g_pfnEdit, hwnd, uiMsg, wP, lP ) ;
  971. }
  972.  
  973. /****************************************************************
  974.  *
  975.  *  Description:
  976.  *
  977.  *    The ColumntTextOut function writes a character string at
  978.  *    the specified location, using tabs to identify column breaks.  Each
  979.  *    column is aligned according to the array of COLUMNSTRUCTs.
  980.  *
  981.  *    A COLUMNSTRUCT looks like this:
  982.  *
  983.  *    {
  984.  *       int   nLeft ;       // starting x position of the column
  985.  *       int   nRight ;      // ending x position of the column
  986.  *       UINT  uiFlags ;      // format flags
  987.  *    }
  988.  *
  989.  *    If a column has another column to the left of it, it's nLeft member
  990.  *    is ignored.
  991.  *
  992.  *    uiFlags can be any of the following:
  993.  *
  994.  *          #define DT_LEFT      0x0000
  995.  *          #define DT_CENTER    0x0001
  996.  *          #define DT_RIGHT     0x0002
  997.  *
  998.  *  Comments:
  999.  *
  1000.  ****************************************************************/
  1001. void WINAPI ColumnTextOut( HDC hdc, int nX, int nY, LPTSTR lpszIN,
  1002.                             int cColumns, LPCOLUMNSTRUCT rgColumns )
  1003. {
  1004.  
  1005. #define ETOFLAGS ETO_CLIPPED
  1006.  
  1007.     COLUMNSTRUCT   CS ;              // local copy for speed
  1008.     RECT           rc ;              // current cell rect
  1009.     int            cIteration = 0 ;  // what column
  1010.     LPTSTR          lpNext ;          // next string (current is lpsz)
  1011.     int            cch ;             // count of chars in current string
  1012.     SIZE           size ;            // extent of current string
  1013.     int            dx ;              // column width
  1014.  
  1015.     rc.left = nX ;
  1016.     rc.top = nY ;
  1017.     rc.right = 0 ;
  1018.  
  1019.     if (rgColumns == NULL || cColumns <= 1)
  1020.     {
  1021.         int Tab = 15 ;
  1022.         TabbedTextOut( hdc, nX, nY, lpszIN, lstrlen(lpszIN), 1, &Tab, nX ) ;
  1023.         return ;
  1024.     }
  1025.  
  1026.     // For each sub string (bracketed by a tab)
  1027.     //
  1028.     LPTSTR lpsz = lpszIN ;
  1029.     while (*lpsz)
  1030.     {
  1031.         if (cIteration >= cColumns)
  1032.             return ;
  1033.  
  1034.         for (cch = 0, lpNext = lpsz ;
  1035.              *lpNext != '\t' && *lpNext ;
  1036.              lpNext++, cch++)
  1037.             ;
  1038.  
  1039.         CS = rgColumns[cIteration] ;
  1040.  
  1041.         // If it's the leftmost column use
  1042.         //
  1043.         if (cIteration == 0)
  1044.         {
  1045.             rc.left = nX + CS.nLeft ;
  1046.             rc.right = nX + CS.nRight ;
  1047.         }
  1048.         else
  1049.             rc.right = nX + CS.nRight ;
  1050.  
  1051.         GetTextExtentPoint( hdc, lpsz, cch, &size ) ;
  1052.         rc.bottom = rc.top + size.cy ;
  1053.  
  1054.         // If the width of the column is 0 do nothing
  1055.         //
  1056.         if ((dx = (rc.right - rc.left)) > 0)
  1057.         {
  1058.             switch(CS.uiFlags)
  1059.             {
  1060.                 case DT_CENTER:
  1061.                     ExtTextOut( hdc, rc.left + ((dx - size.cx) / (int)2),
  1062.                     rc.top, ETOFLAGS, &rc, lpsz, cch, NULL ) ;
  1063.                 break ;
  1064.  
  1065.                 case DT_RIGHT:
  1066.                     // If it's right justified then make the left border 0
  1067.                     //
  1068.                     //rc.left = nX + rgColumns[0].nLeft ;
  1069.                     ExtTextOut( hdc, rc.left + (dx - size.cx),
  1070.                     rc.top, ETOFLAGS, &rc, lpsz, cch, NULL ) ;
  1071.                 break ;
  1072.  
  1073.                 case DT_LEFT:
  1074.                 default:
  1075.                     ExtTextOut( hdc, rc.left, rc.top, ETOFLAGS, &rc, lpsz, cch, NULL ) ;
  1076.                 break ;
  1077.             }
  1078.         }
  1079.         rc.left = rc.right ;
  1080.         cIteration++ ;
  1081.         lpsz = lpNext + 1 ;
  1082.     }
  1083.  
  1084. } // ColumnTextOut()
  1085.  
  1086.  
  1087. /*************************************************************************
  1088.  *  void WINAPI
  1089.  *  DlgCenter( HWND hwndCenter, HWND hwndWithin, BOOL fClient )
  1090.  *
  1091.  *  Description:
  1092.  *
  1093.  *    Centers a window within another window.
  1094.  *
  1095.  *  Type/Parameter
  1096.  *          Description
  1097.  *
  1098.  *    HWND  hwndCenter
  1099.  *          Window to center.  This does not have to be a child of
  1100.  *          hwndWithin.
  1101.  *
  1102.  *    HWND  hwndWithin
  1103.  *          Window to center the above window within.  Can be NULL.
  1104.  *
  1105.  *    BOOL  fClient
  1106.  *          If TRUE the window is centered within the available client
  1107.  *          area.  Otherwise it's centered within the entire window area.
  1108.  *
  1109.  *  Comments:
  1110.  *
  1111.  *************************************************************************/
  1112. void WINAPI
  1113. DlgCenter( HWND hwndCenter, HWND hwndWithin, BOOL fClient )
  1114. {
  1115.    RECT rcWithin ;
  1116.    RECT rcCenter ;
  1117.    int   x, y ;
  1118.    int   dxCenter, dyCenter ;
  1119.    int   dxScreen, dyScreen ;
  1120.  
  1121.    dxScreen = GetSystemMetrics( SM_CXSCREEN ) ;
  1122.    dyScreen = GetSystemMetrics( SM_CYSCREEN ) ;
  1123.  
  1124.    if (!IsWindow(hwndCenter))
  1125.       return ;
  1126.  
  1127.    if (hwndWithin && !IsWindow(hwndWithin))
  1128.       return ;
  1129.  
  1130.    if (hwndWithin == NULL)
  1131.    {
  1132.       rcWithin.left = rcWithin.top = 0 ;
  1133.       rcWithin.right = dxScreen ;
  1134.       rcWithin.bottom = dyScreen ;
  1135.    }
  1136.    else
  1137.    {
  1138.       if (fClient)
  1139.       {
  1140.          /*
  1141.           * First get screen coords of rectangle we're going to be
  1142.           * centered within.
  1143.           */
  1144.          GetClientRect( hwndWithin, (LPRECT)&rcWithin ) ;
  1145.          ClientToScreen( hwndWithin, (LPPOINT)&rcWithin.left ) ;
  1146.          ClientToScreen( hwndWithin, (LPPOINT)&rcWithin.right ) ;
  1147.       }
  1148.       else
  1149.          GetWindowRect( hwndWithin, (LPRECT)&rcWithin ) ;
  1150.    }
  1151.  
  1152.    GetWindowRect( hwndCenter, (LPRECT)&rcCenter ) ;
  1153.    dxCenter = rcCenter.right - rcCenter.left ;
  1154.    dyCenter = rcCenter.bottom - rcCenter.top ;
  1155.  
  1156.    /*
  1157.     * Now we have both the within and center rects in screen coords.
  1158.     *
  1159.     * SetWindowPos behaves differently for Non child windows
  1160.     * than for child windows.  For popups it's coordinates
  1161.     * are relative to the upper left corner of the screen.  For
  1162.     * children it's coords are relative to the upper left corner
  1163.     * of the client area of the parent.
  1164.     */
  1165.    x = ((rcWithin.right - rcWithin.left) - dxCenter) / 2 ;
  1166.    y = ((rcWithin.bottom - rcWithin.top) - dyCenter) / 2 ;
  1167.  
  1168.    if (hwndWithin == GetParent( hwndCenter ) &&
  1169.        !(GetWindowLong( hwndCenter, GWL_STYLE ) & WS_CHILD ))
  1170.    {
  1171.       x += rcWithin.left ;
  1172.       y += rcWithin.top ;
  1173.  
  1174.       if (x + dxCenter > dxScreen )
  1175.          x = dxScreen - dxCenter ;
  1176.  
  1177.       if (y + dyCenter > dyScreen )
  1178.          y = dyScreen - dyCenter ;
  1179.  
  1180.       SetWindowPos( hwndCenter, NULL,
  1181.                     max(x,0),
  1182.                     max(y,0),
  1183.                     0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER ) ;
  1184.  
  1185.       return ;
  1186.    }
  1187.  
  1188.    SetWindowPos( hwndCenter, NULL,
  1189.                  max(x,0),
  1190.                  max(y,0),
  1191.                  0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER ) ;
  1192.  
  1193.    return ;
  1194.  
  1195. }/* DlgCenter() */
  1196.  
  1197. /****************************************************************
  1198.  *  int WINAPI
  1199.  *    GetTextMetricsCorrectly( HDC hDC, LPTEXTMETRIC lpTextMetrics )
  1200.  *
  1201.  *  Description:
  1202.  *
  1203.  *    This function gets the textmetrics of the font currently
  1204.  *    selected into the hDC.  It returns the average char width as
  1205.  *    the return value.
  1206.  *
  1207.  *    This function computes the average character width correctly
  1208.  *    by using GetTextExtent() on the string "abc...xzyABC...XYZ"
  1209.  *    which works out much better for proportional fonts.
  1210.  *
  1211.  *    Note that this function returns the same TEXTMETRIC
  1212.  *    values that GetTextMetrics() does, it simply has a different
  1213.  *    return value.
  1214.  *
  1215.  *  Comments:
  1216.  *
  1217.  ****************************************************************/
  1218. int WINAPI
  1219.    GetTextMetricsCorrectly( HDC hDC, LPTEXTMETRIC lpTM )
  1220. {
  1221.    int    nAveWidth ;
  1222.    TCHAR  rgchAlphabet[52] ;
  1223.    int  i ;
  1224.    SIZE size ;
  1225.  
  1226.    // Get the TM of the current font.  Note that GetTextMetrics
  1227.    // gets the incorrect AveCharWidth value for proportional fonts.
  1228.    // This is the whole poshort in this exercise.
  1229.    //
  1230.    if (lpTM)
  1231.     GetTextMetrics(hDC, lpTM);
  1232.  
  1233.    // If it's not a variable pitch font GetTextMetrics was correct
  1234.    // so just return.
  1235.    //
  1236.    if (lpTM && !(lpTM->tmPitchAndFamily & FIXED_PITCH))
  1237.       return lpTM->tmAveCharWidth ;
  1238.    else
  1239.    {
  1240.       for ( i = 0 ; i <= 25 ; i++)
  1241.          rgchAlphabet[i] = (TCHAR)(i+(int)'a') ;
  1242.  
  1243.       for ( i = 0 ; i<=25 ; i++)
  1244.          rgchAlphabet[i+25] = (TCHAR)(i+(int)'A') ;
  1245.  
  1246.       GetTextExtentPoint( hDC, (LPTSTR)rgchAlphabet, 52, &size ) ;
  1247.       nAveWidth = size.cx / 26 ;
  1248.       nAveWidth = (nAveWidth + 1) / 2 ;
  1249.    }
  1250.  
  1251.    // Return the calculated average char width
  1252.    //
  1253.    return nAveWidth ;
  1254.  
  1255. } /* GetTextMetricsCorrectly()  */
  1256.