trackwnd.c
上传用户:xmgzy123
上传日期:2007-01-07
资源大小:373k
文件大小:35k
源码类别:

SCSI/ASPI

开发平台:

WINDOWS

  1. /*
  2.  * trackwnd.c - Copyright (C) 1999,2000 Jay A. Key
  3.  *
  4.  * Implementation of the TrackWnd window class.  TrackWnd is intended to
  5.  * be a child window of the main application window, and supports the 
  6.  * displaying, editing and selectind of CD audio tracks.  Includes a header
  7.  * control for changing the width of the items displayed on screen.
  8.  *
  9.  **********************************************************************
  10.  *
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  24.  *
  25.  */
  26. #include <windows.h>
  27. #include <commctrl.h>
  28. #include <string.h>
  29. #include "resources.h"
  30. #include "trackwnd.h"
  31. #include "globals.h"
  32. #include "statusbar.h"
  33. typedef struct
  34. {
  35.   int iNum;
  36.   int iMax;
  37.   int iTextHeight;
  38.   int vw, vh;
  39.   int dx, dy;              // scroll bar offsets into virtual window
  40.   int iHeaderHeight;
  41.   int hx[5];               // header x offsets
  42.   int hw[5];               // header widths
  43.   int iFound;
  44.   HWND hHeader;
  45.   BOOL bVScroll, bHScroll;
  46.   HPEN hBrownPen, hBlackPen;
  47.   int iSelected;
  48.   ADDTRACK *tracks;
  49. } TRACKWNDEXTRA, *LPTRACKWNDEXTRA;
  50. LRESULT CALLBACK TrackWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  51. void doTrackWndPaint( HWND hWnd, LPTRACKWNDEXTRA lpe );
  52. void RecalcWindow( HWND hWnd, LPTRACKWNDEXTRA lpe );
  53. void DeleteTrack( HWND hWnd, int iTrack, LPTRACKWNDEXTRA lpe );
  54. void HandleVScroll( HWND, int, int, LPTRACKWNDEXTRA );
  55. void HandleHScroll( HWND, int, int, LPTRACKWNDEXTRA );
  56. void ComputeHeaderPos( HWND hHdr, HWND hParent );
  57. HWND createHeader( HWND hWnd, HINSTANCE hInst );
  58. char *notifyString( UINT code );
  59. BOOL handleHeaderNotify( HWND hWnd, LPARAM lParam, LPTRACKWNDEXTRA lpe );
  60. BOOL HandleLMouseDown( HWND hWnd, LPTRACKWNDEXTRA lpe, LPARAM lParam );
  61. BOOL HandleRMouseDown( HWND hWnd, LPTRACKWNDEXTRA lpe, LPARAM lParam );
  62. int CheckBoxHit( LPTRACKWNDEXTRA lpe, LPARAM lParam );
  63. BOOL HandleCheckTrack( HWND hWnd, LPTRACKWNDEXTRA lpe, int trackNo, BOOL bChecked );
  64. BOOL HandleInvertCheck( HWND hWnd, LPTRACKWNDEXTRA lpe, int trackNo );
  65. int doContextMenu( HWND hWnd, LPARAM lParam );
  66. BOOL RenameSelectedTrack( HWND hWnd, LPTRACKWNDEXTRA lpe );
  67. LRESULT processFindFirst( LPTRACKWNDEXTRA lpe, LPADDTRACK lpAddTrack );
  68. LRESULT processFindNext( LPTRACKWNDEXTRA lpe, LPADDTRACK lpAddTrack );
  69. void convertuMsg( UINT uMsg, char * s );
  70. void logWindowsProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  71. LRESULT CALLBACK EditSubclassWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  72. LRESULT CALLBACK FooWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  73. void SetTrackWndText( HWND hWnd );
  74. LRESULT numChecked( LPTRACKWNDEXTRA lpe );
  75. int doTrackContextMenu( HWND hParent, HWND hChild, LPARAM lParam );
  76. BOOL setTrackStatus( LPTRACKWNDEXTRA lpe, int idx, LPCSTR s );
  77. static char szTrackWndClass[] = "akripTrackWnd";
  78. //static char szFooClass[] = "fooclass";
  79. //static ADDTRACK tmpAddTrack;
  80. HWND hHeader;
  81. static int iCtlId = 10000;
  82. static BOOL bInEditTrack = FALSE;
  83. static HWND hEdit = NULL;
  84. int InitTrackWnd( HINSTANCE hInst )
  85. {
  86.   WNDCLASSEX wc;
  87.   LOGBRUSH lb;
  88.   HBRUSH hBrush;
  89.   ZeroMemory( &lb, sizeof(lb) );
  90.   lb.lbStyle = BS_SOLID;
  91.   lb.lbColor = RGB( 240, 255, 220 );
  92.   lb.lbHatch = 0;
  93.   hBrush = CreateBrushIndirect( &lb );
  94.   ZeroMemory( &wc, sizeof(wc) );
  95.   wc.cbSize          = sizeof(wc);
  96.   wc.style           = CS_HREDRAW | CS_VREDRAW;
  97.   wc.lpfnWndProc     = (WNDPROC)TrackWndProc;
  98.   wc.hInstance       = hInst;
  99.   wc.hCursor         = LoadCursor( NULL, IDC_ARROW );
  100.   wc.hbrBackground   = hBrush;
  101.   wc.lpszClassName   = szTrackWndClass;
  102.   if ( !RegisterClassEx( &wc ) )
  103.     return 0;
  104. #if 0
  105.   ZeroMemory( &wc, sizeof(wc) );
  106.   wc.cbSize          = sizeof(wc);
  107.   wc.style           = CS_HREDRAW | CS_VREDRAW;
  108.   wc.lpfnWndProc     = (WNDPROC)FooWndProc;
  109.   wc.hInstance       = hInst;
  110.   wc.hCursor         = LoadCursor( NULL, IDC_ARROW );
  111.   wc.hbrBackground   = hBrush;
  112.   wc.lpszClassName   = szFooClass;
  113.   if ( !RegisterClassEx( &wc ) )
  114.     return 0;
  115. #endif
  116.   return -1;
  117. }
  118. LRESULT CALLBACK TrackWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  119. {
  120.   LPTRACKWNDEXTRA lpe;
  121.   RECT rc;
  122.   logWindowsProc( hWnd, uMsg, wParam, lParam );
  123.   lpe = (LPTRACKWNDEXTRA)GetWindowLong( hWnd, GWL_USERDATA);
  124.   if ( !lpe && ( uMsg != WM_CREATE ) )
  125.     return DefWindowProc( hWnd, uMsg, wParam, lParam );
  126.   switch( uMsg )
  127.     {
  128.     case WM_COMMAND:
  129.       switch( HIWORD(wParam) )
  130.         {
  131.         case EN_SETFOCUS: break;
  132.         case EN_KILLFOCUS: break;
  133.         }
  134.       break;
  135.     case WM_CONTEXTMENU:
  136.       doTrackContextMenu( GetParent(hWnd), hWnd, lParam );
  137.       break;
  138.     case WM_SETFOCUS:
  139.       //OutputDebugString( "TrackWnd gaining focus" );
  140.       break;
  141.     case WM_KILLFOCUS:
  142.       //OutputDebugString( "TrackWnd losing focus" );
  143.       break;
  144.     case WM_NOTIFY:
  145.       if ( wParam == IDM_HEADER )
  146.         return handleHeaderNotify( hWnd, lParam, lpe );
  147.       break;
  148.     case WM_DESTROY:
  149.       lpe = (LPTRACKWNDEXTRA)GetWindowLong( hWnd, GWL_USERDATA );
  150.       if ( lpe )
  151.         {
  152.           if ( lpe->tracks )
  153.             GlobalFree( (HGLOBAL)lpe->tracks );
  154.           if ( lpe->hBrownPen )
  155.             DeleteObject( lpe->hBrownPen );
  156.           if ( lpe->hBlackPen )
  157.             DeleteObject( lpe->hBlackPen );
  158.           GlobalFree( lpe );
  159.         }
  160.       return DefWindowProc( hWnd, uMsg, wParam, lParam );
  161.     case WM_CREATE:
  162.       lpe = (LPTRACKWNDEXTRA)GlobalAlloc( GPTR, sizeof(TRACKWNDEXTRA) );
  163.       lpe->tracks = (LPADDTRACK)GlobalAlloc( GPTR, sizeof(ADDTRACK)*100 );
  164.       lpe->iNum = 0;
  165.       lpe->iMax = 100;
  166.       lpe->hBrownPen = CreatePen( PS_SOLID, 1, 0x00003384 );
  167.       lpe->hBlackPen = CreatePen( PS_SOLID, 1, 0x00000000 );
  168.       lpe->iSelected = -1;
  169.       lpe->iFound = -1;
  170.       SetWindowLong( hWnd, GWL_USERDATA, (LONG)lpe );
  171.       UpdateStatusBar();
  172.       break;
  173.     case WM_PAINT:
  174.       doTrackWndPaint( hWnd, lpe );
  175.       break;
  176.     case WM_VSCROLL:
  177.       HandleVScroll( hWnd, (int)LOWORD(wParam), (int)HIWORD(wParam), lpe );
  178.       RecalcWindow( hWnd, lpe );
  179.       GetClientRect( hWnd, &rc );
  180.       rc.top += (lpe->iHeaderHeight+1);
  181.       InvalidateRect( hWnd, &rc, TRUE );
  182.       UpdateWindow( hWnd );
  183.       break;
  184.     case WM_HSCROLL:
  185.       HandleHScroll( hWnd, (int)LOWORD(wParam), (int)HIWORD(wParam), lpe );
  186.       RecalcWindow( hWnd, lpe );
  187.       GetClientRect( hWnd, &rc );
  188.       rc.top += (lpe->iHeaderHeight+1);
  189.       InvalidateRect( hWnd, &rc, TRUE );
  190.       UpdateWindow( hWnd );
  191.       break;
  192.     case WM_SIZE:
  193.       RecalcWindow( hWnd, lpe );
  194.       break;
  195.     case WM_ADDTRACK:
  196.       if ( lpe->iNum >= lpe->iMax )
  197.         break;
  198.       memcpy( &(lpe->tracks[lpe->iNum]), (void *)lParam, sizeof(ADDTRACK) );
  199.       lpe->iNum++;
  200.       RecalcWindow( hWnd, lpe );
  201.       GetClientRect( hWnd, &rc );
  202.       rc.top += (lpe->iHeaderHeight+1);
  203.       InvalidateRect( hWnd, &rc, TRUE );
  204.       UpdateWindow( hWnd );
  205.       UpdateStatusBar();
  206.       break;
  207.     case WM_CHECKTRACK:
  208.       if ( HandleCheckTrack( hWnd, lpe, (int)wParam, (BOOL)lParam ) )
  209.         {
  210.           GetClientRect( hWnd, &rc );
  211.           rc.top += (lpe->iHeaderHeight+1);
  212.           InvalidateRect( hWnd, &rc, TRUE );
  213.           UpdateWindow( hWnd );
  214.         }
  215.       UpdateStatusBar();
  216.       break;
  217.     case WM_INVERTCHECK:
  218.       if ( HandleInvertCheck( hWnd, lpe, (int)wParam ) )
  219.         {
  220.           GetClientRect( hWnd, &rc );
  221.           rc.top += (lpe->iHeaderHeight+1);
  222.           InvalidateRect( hWnd, &rc, TRUE );
  223.           UpdateWindow( hWnd );
  224.         }
  225.       UpdateStatusBar();
  226.       break;
  227.     case WM_DELTRACK:
  228.       DeleteTrack( hWnd, (int)wParam, lpe );
  229.       RecalcWindow( hWnd, lpe );
  230.       GetClientRect( hWnd, &rc );
  231.       rc.top += (lpe->iHeaderHeight+1);
  232.       InvalidateRect( hWnd, &rc, TRUE );
  233.       UpdateWindow( hWnd );
  234.       UpdateStatusBar();
  235.       break;
  236.     case WM_SELTRACK:
  237.       if ( lpe->iSelected != (int)wParam )
  238.         {
  239.           lpe->iSelected = (int)wParam;
  240.           GetClientRect( hWnd, &rc );
  241.           rc.top += (lpe->iHeaderHeight+1);
  242.           InvalidateRect( hWnd, &rc, TRUE );
  243.           UpdateWindow( hWnd );
  244.         }
  245.       break;
  246.     case WM_MOUSEMOVE:
  247.       // grrr... it's an evil hack, but I need for the MouseCapture to 
  248.       // remain with the edit control.  When the user selects text with the
  249.       // mouse, the edit control automatically captures the mouse (even if
  250.       // it already has the mouse captured.  Then it releases it, but not
  251.       // until after the WM_*BUTTONUP message.  Until I write a custom
  252.       // edit control, I'll have to use this workaround.
  253.       if ( bInEditTrack && hEdit )
  254.         SetCapture( hEdit );
  255.       break;
  256.     case WM_LBUTTONDOWN:
  257.       if ( HandleLMouseDown( hWnd, lpe, lParam ) )
  258.         {
  259.           GetClientRect( hWnd, &rc );
  260.           rc.top += (lpe->iHeaderHeight+1);
  261.           InvalidateRect( hWnd, &rc, TRUE );
  262.           UpdateWindow( hWnd );
  263.         }
  264.       break;
  265.     case WM_RBUTTONDOWN:
  266.       if ( HandleRMouseDown( hWnd, lpe, lParam ) )
  267.         {
  268.           GetClientRect( hWnd, &rc );
  269.           rc.top += (lpe->iHeaderHeight+1);
  270.           InvalidateRect( hWnd, &rc, TRUE );
  271.           UpdateWindow( hWnd );
  272.         }
  273.       break;
  274.     case WM_RENSELTRACK:
  275.       if ( RenameSelectedTrack( hWnd, lpe ) )
  276.         {
  277.           GetClientRect( hWnd, &rc );
  278.           rc.top += (lpe->iHeaderHeight+1);
  279.           InvalidateRect( hWnd, &rc, TRUE );
  280.           UpdateWindow( hWnd );
  281.         }
  282.       break;
  283.     // copies the first checked track found into (LPADDTRACK)lParam .
  284.     // Returns index of the track, or -1 if no track found
  285.     case WM_FINDFIRSTTRACK:
  286.       return processFindFirst( lpe, (LPADDTRACK)lParam );
  287.       break;
  288.     // returns NULL if no more tracks are selected, otherwise LPADDTRACK
  289.     case WM_FINDNEXTTRACK:
  290.       return processFindNext( lpe, (LPADDTRACK)lParam );
  291.       break;
  292.     case WM_SETSELTEXT:
  293.       lstrcpy( lpe->tracks[lpe->iSelected].name, (char *)lParam );
  294.       GetClientRect( hWnd, &rc );
  295.       rc.top += (lpe->iHeaderHeight+1);
  296.       InvalidateRect( hWnd, &rc, TRUE );
  297.       UpdateWindow( hWnd );
  298.       break;
  299.     case WM_SETTRACKTEXT:
  300.       if ( (int)wParam < lpe->iNum )
  301.         {
  302.           lstrcpyn( lpe->tracks[wParam].name, (char *)lParam, MAX_PATH );
  303.           lpe->tracks[wParam].name[MAX_PATH] = '';
  304.           GetClientRect( hWnd, &rc );
  305.           rc.top += (lpe->iHeaderHeight+1);
  306.           InvalidateRect( hWnd, &rc, TRUE );
  307.           UpdateWindow( hWnd );
  308.         }
  309.       break;
  310.     case WM_SETTRACKSTATUS:
  311.       if ( setTrackStatus( lpe, (int)wParam, (LPCSTR)lParam ) )
  312.         {
  313.           GetClientRect( hWnd, &rc );
  314.           rc.top += (lpe->iHeaderHeight+1);
  315.           InvalidateRect( hWnd, &rc, TRUE );
  316.           UpdateWindow( hWnd );
  317.         }
  318.       break;
  319.     case WM_NUMCHECKED:
  320.       return numChecked( lpe );
  321.     case WM_NUMTRACKS:
  322.       return (LRESULT)lpe->iNum;
  323.     case WM_GETTRACK:
  324.       if ( lParam )
  325.         {
  326.           memcpy( (BYTE *)lParam, (BYTE *)&lpe->tracks[wParam], sizeof(ADDTRACK) );
  327.           return (LRESULT)lParam;
  328.         }
  329.       break;
  330.     case WM_CAPTURECHANGED:
  331.       //OutputDebugString( "TrackWndProc: WM_CAPTURECHANGED" );
  332.       break;
  333.     default:
  334.       return DefWindowProc( hWnd, uMsg, wParam, lParam );
  335.     }
  336.   return 0L;
  337. }
  338. HWND createTrackWnd( HWND hParent, HINSTANCE hInst, int idCtrl, LPRECT lprc )
  339. {
  340.   LPTRACKWNDEXTRA lpe;
  341.   HWND hRet;
  342.   HDC hDC;
  343.   SIZE s;
  344.   RECT rc;
  345.   hRet = CreateWindowEx( 0, szTrackWndClass, "A Bogus Name",
  346.                          WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL,
  347.                          lprc->left, lprc->top,
  348.                          lprc->right - lprc->left, lprc->bottom - lprc->top,
  349.                          hParent, (HMENU)idCtrl, hInst, NULL );
  350.   /*
  351.    * compute text height
  352.    */
  353.   lpe = (LPTRACKWNDEXTRA)GetWindowLong( hRet, GWL_USERDATA );
  354.   hDC = GetWindowDC( hRet );
  355.   GetTextExtentPoint32( hDC, "ABCDEFGHIJKLMNOPQRSTUVWYXZabcdefghijklmnopqrstuvwxyz", 52, &s );
  356.   lpe->iTextHeight = s.cy;
  357.   ReleaseDC( hRet, hDC );
  358.   lpe->hHeader = createHeader( hRet, hInst );
  359.   GetClientRect( lpe->hHeader, &rc );
  360.   lpe->iHeaderHeight = rc.bottom - rc.top + 1;
  361.   return hRet;
  362. }
  363. void doTrackWndPaint( HWND hWnd, LPTRACKWNDEXTRA lpe )
  364. {
  365.   PAINTSTRUCT p;
  366.   HDC hDC;
  367.   RECT rc;
  368.   int i;
  369.   int oldBkMode;
  370.   int hw[5];            // width of header items
  371.   int hx[5];            // x offset of header items
  372.   DWORD len;
  373.   //char buf[32];
  374.   POINT pts[5];
  375.   HPEN hOldPen;
  376.   COLORREF oldBkColor, oldTextColor;
  377.   char buf[81];
  378.   GetClientRect( hWnd, &rc );
  379.   hDC = BeginPaint( hWnd, &p );
  380.   /*
  381.    * compute the offsets for each header section
  382.    */
  383.   ZeroMemory( hw, sizeof(int[5]) );
  384.   ZeroMemory( hx, sizeof(int[5]) );
  385.   for( i = 0; i < 4; i++ )
  386.     {
  387.       HD_ITEM hi;
  388.       ZeroMemory( &hi, sizeof(hi) );
  389.       hi.mask = HDI_WIDTH;
  390.       SendMessage( lpe->hHeader, HDM_GETITEM, i, (LPARAM)&hi );
  391.       hw[i] = hi.cxy;
  392.       hx[i+1] = hx[i] + hw[i];
  393.     }
  394.   oldBkMode = SetBkMode( hDC, TRANSPARENT );
  395.   for( i = 0; i < lpe->iNum; i++ )
  396.     {
  397.       // compute rectangle for track title
  398.       rc.left = hx[1] + 5 - lpe->dx;
  399.       rc.right = rc.left + hw[1] - 10;
  400.       rc.top = 6 + (lpe->iHeaderHeight + i*(lpe->iTextHeight+4) ) - lpe->dy;
  401.       rc.bottom = rc.top + 16;
  402.       if ( i == lpe->iSelected )
  403.         {
  404.           SetBkMode( hDC, OPAQUE );
  405.           oldBkColor = SetBkColor( hDC, RGB(20,20,140) );
  406.           oldTextColor = SetTextColor( hDC, oldBkColor );
  407.         }
  408.       DrawText( hDC, lpe->tracks[i].name, lstrlen( lpe->tracks[i].name ), &rc, DT_VCENTER );
  409.       if ( i == lpe->iSelected )
  410.         {
  411.           SetBkMode( hDC, TRANSPARENT );
  412.           SetBkColor( hDC, oldBkColor );
  413.           SetTextColor( hDC, oldTextColor );
  414.         }
  415.       // compute rectangle for status text
  416.       rc.left = hx[3] + 5 - lpe->dx;
  417.       rc.right = rc.left + hw[3] - 10;
  418.       DrawText( hDC, lpe->tracks[i].status, lstrlen( lpe->tracks[i].status ),
  419.                 &rc, DT_VCENTER );
  420.       rc.left = hx[2] + 5 - lpe->dx;
  421.       rc.right = rc.left + hw[2] - 10;
  422.       len = lpe->tracks[i].len / 75;
  423.       wsprintf( buf, "%02d:%02d", len / 60, len % 60 );
  424.       DrawText( hDC, buf, lstrlen( buf ), &rc, DT_VCENTER );
  425.       // draw check box
  426.       pts[0].x = pts[3].x = pts[4].x = ((hx[1]+1)/2 - 4) - lpe->dx;
  427.       pts[1].x = pts[2].x = pts[0].x + 8;
  428.       pts[0].y = pts[1].y = pts[4].y = rc.top + 5;
  429.       pts[2].y = pts[3].y = rc.top + 13;
  430.       hOldPen = SelectObject( hDC, lpe->hBrownPen );
  431.       Polyline( hDC, pts, 5 );
  432.       if ( lpe->tracks[i].bChecked )
  433.         {
  434.           SelectObject( hDC, lpe->hBlackPen );
  435.           pts[0].x += 2;
  436.           pts[0].y += 3;
  437.           pts[1].x = pts[0].x + 2;
  438.           pts[1].y = pts[0].y + 3;
  439.           pts[2].x = pts[1].x + 7;
  440.           pts[2].y = pts[1].y - 7;
  441.           Polyline( hDC, pts, 3 );
  442.           pts[0].x += 1;
  443.           pts[1].x = pts[0].x + 2;
  444.           pts[1].y = pts[0].y + 3;
  445.           pts[2].x = pts[1].x + 7;
  446.           pts[2].y = pts[1].y - 7;
  447.           Polyline( hDC, pts, 3 );
  448.         }
  449.       SelectObject( hDC, hOldPen );
  450.     }
  451.   SetBkMode( hDC, oldBkMode );
  452.   EndPaint( hWnd, &p );
  453. }
  454. HFONT CreateTrackWndFont( HDC hDC )
  455. {
  456.   LOGFONT lf;
  457.   ZeroMemory( &lf, sizeof(lf) );
  458.   lf.lfHeight = -MulDiv( 12, GetDeviceCaps( hDC, LOGPIXELSY), 72 );
  459.   lf.lfCharSet = DEFAULT_CHARSET;
  460.   lf.lfOutPrecision = OUT_TT_PRECIS;
  461.   lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  462.   lf.lfQuality = DEFAULT_QUALITY;
  463.   lf.lfPitchAndFamily = FF_MODERN | DEFAULT_PITCH;
  464.   strcpy( lf.lfFaceName, "Courier New" );
  465.   return CreateFontIndirect( &lf );
  466. }
  467. void RecalcWindow( HWND hWnd, LPTRACKWNDEXTRA lpe )
  468. {
  469.   RECT rc;
  470.   SCROLLINFO si;
  471.   int i;
  472.   /*
  473.    * compute the offsets for each header section, used for mouse hit testing
  474.    * The paint code will recompute on a per paint basis
  475.    */
  476.   ZeroMemory( lpe->hw, sizeof(int[5]) );
  477.   ZeroMemory( lpe->hx, sizeof(int[5]) );
  478.   for( i = 0; i < 3; i++ )
  479.     {
  480.       HD_ITEM hi;
  481.       ZeroMemory( &hi, sizeof(hi) );
  482.       hi.mask = HDI_WIDTH;
  483.       SendMessage( lpe->hHeader, HDM_GETITEM, i, (LPARAM)&hi );
  484.       lpe->hw[i] = hi.cxy;
  485.       lpe->hx[i+1] = lpe->hx[i] + lpe->hw[i];
  486.     }
  487.   GetClientRect( hWnd, &rc );
  488.   lpe->vh = lpe->iNum * (lpe->iTextHeight + 4) + lpe->iHeaderHeight + 5;
  489.   ZeroMemory( &si, sizeof(si) );
  490.   si.cbSize = sizeof(si);
  491.   si.fMask  = SIF_PAGE | SIF_RANGE;
  492.   si.nMin   = 0;
  493.   si.nMax   = lpe->vh;
  494.   si.nPage  = rc.bottom;
  495.   si.nPos   = lpe->dy;
  496. #if 0
  497.   if ( !lpe->bVScroll )
  498. #endif
  499.     si.fMask |= SIF_POS;
  500.   SetScrollInfo( hWnd, SB_VERT, &si, TRUE );
  501.   if ( lpe->bVScroll && ( lpe->vh < rc.bottom ) )
  502.     lpe->bVScroll = FALSE;
  503.   else if ( !lpe->bVScroll && ( lpe->vh > rc.bottom ) )
  504.     lpe->bVScroll = TRUE;
  505.   if ( lpe->bVScroll == FALSE )
  506.     lpe->dy = 0;
  507.   ZeroMemory( &si, sizeof(si) );
  508.   si.cbSize = sizeof(si);
  509.   si.fMask  = SIF_PAGE | SIF_RANGE;
  510.   si.nMin   = 0;
  511.   si.nMax   = lpe->vw;
  512.   si.nPage  = rc.right;
  513.   si.nPos   = lpe->dx;
  514.   if ( !lpe->bHScroll )
  515.     si.fMask |= SIF_POS;
  516.   SetScrollInfo( hWnd, SB_HORZ, &si, TRUE );
  517.   if ( lpe->bHScroll && ( lpe->vw < rc.right ) )
  518.     lpe->bHScroll = FALSE;
  519.   else if ( !lpe->bHScroll && ( lpe->vw > rc.right ) )
  520.     lpe->bHScroll = TRUE;
  521.   if ( lpe->bHScroll == FALSE )
  522.     lpe->dx = 0;
  523.   ComputeHeaderPos( lpe->hHeader, hWnd );
  524. }
  525. void DeleteTrack( HWND hWnd, int iTrack, LPTRACKWNDEXTRA lpe )
  526. {
  527.   int i;
  528.   hWnd = hWnd;
  529.   if ( iTrack == (int)ALLTRACKS )
  530.     {
  531.       lpe->iSelected = -1;
  532.       lpe->iNum = 0;
  533.       return;
  534.     }
  535.   if ( iTrack > lpe->iNum )
  536.     return;
  537.   lpe->iSelected = -1;
  538.   for( i = iTrack; i < lpe->iNum; i++ )
  539.     {
  540.       lpe->tracks[i] = lpe->tracks[i+1];
  541.     }
  542.   lpe->iNum--;
  543. }
  544. void HandleHScroll( HWND hWnd, int nScrollCode, int nPos, LPTRACKWNDEXTRA lpe )
  545. {
  546.   hWnd = hWnd; nScrollCode = nScrollCode; nPos = nPos; lpe = lpe;
  547. }
  548. void HandleVScroll( HWND hWnd, int nScrollCode, int nPos, LPTRACKWNDEXTRA lpe )
  549. {
  550.   RECT rc;
  551.   SCROLLINFO si;
  552.   GetClientRect( hWnd, &rc );
  553.   si.cbSize   = sizeof(si);
  554.   si.fMask    = SIF_ALL;
  555.   GetScrollInfo( hWnd, SB_VERT, &si );
  556.   // one line of text takes up lpe->iTextHeight + 4 units
  557.   switch( nScrollCode )
  558.     {
  559.     case SB_BOTTOM: break;
  560.     case SB_ENDSCROLL: break;
  561.     case SB_LINEDOWN:
  562.       lpe->dy = min( lpe->dy + lpe->iTextHeight + 4, 
  563.                      lpe->vh - (rc.bottom-1) );
  564.       break;
  565.     case SB_LINEUP:
  566.       lpe->dy = max( 0, lpe->dy - (lpe->iTextHeight+4) );
  567.       break;
  568.     case SB_PAGEDOWN:
  569.       lpe->dy = min( lpe->dy + rc.bottom, lpe->vh - (rc.bottom-1) );
  570.       break;
  571.     case SB_PAGEUP:
  572.       lpe->dy = max( 0, lpe->dy - rc.bottom );
  573.       break;
  574.     case SB_THUMBPOSITION:
  575.       lpe->dy = nPos;
  576.       break;
  577.     case SB_THUMBTRACK:
  578.       lpe->dy = nPos;
  579.       break;
  580.     case SB_TOP: break;
  581.       break;
  582.     default: 
  583.       //OutputDebugString( "Unknown scroll-bar message" );
  584.       break;
  585.     }
  586. }
  587. HWND createHeader( HWND hWnd, HINSTANCE hInst )
  588. {
  589.   HD_ITEM hdi;
  590.   HWND hRet;
  591.   hRet = CreateWindowEx( 0L, WC_HEADER, "",
  592.                          WS_CHILD | WS_BORDER | HDS_HORZ,
  593.                          0, 0, 0, 0,
  594.                          hWnd, (HMENU)IDM_HEADER, hInst, NULL );
  595.   if ( !hRet )
  596.     return NULL;
  597.   ComputeHeaderPos( hRet, hWnd );
  598.   hdi.mask    = HDI_FORMAT | HDI_WIDTH;
  599.   hdi.cxy     = 30;
  600.   hdi.fmt     = HDF_LEFT | HDF_STRING;
  601.   SendMessage( hRet, HDM_INSERTITEM, 9999, (LPARAM)&hdi );
  602.   hdi.mask    = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
  603.   hdi.pszText = "Title";
  604.   hdi.cxy     = 200;
  605.   hdi.cchTextMax = lstrlen( hdi.pszText );
  606.   hdi.fmt     = HDF_LEFT | HDF_STRING;
  607.   SendMessage( hRet, HDM_INSERTITEM, 9999, (LPARAM)&hdi );
  608.   hdi.mask    = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
  609.   hdi.pszText = "Time";
  610.   hdi.cxy     = 70;
  611.   hdi.cchTextMax = lstrlen( hdi.pszText );
  612.   hdi.fmt     = HDF_LEFT | HDF_STRING;
  613.   SendMessage( hRet, HDM_INSERTITEM, 9999, (LPARAM)&hdi );
  614.   hdi.mask    = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
  615.   hdi.pszText = "Status";
  616.   hdi.cxy     = 200;
  617.   hdi.cchTextMax = lstrlen( hdi.pszText );
  618.   hdi.fmt     = HDF_LEFT | HDF_STRING;
  619.   SendMessage( hRet, HDM_INSERTITEM, 9999, (LPARAM)&hdi );
  620.   return hRet;
  621. }
  622. void ComputeHeaderPos( HWND hHdr, HWND hParent )
  623. {
  624.   WINDOWPOS wp;
  625.   HD_LAYOUT hdl;
  626.   RECT rc;
  627.   GetClientRect( hParent, &rc );
  628.   hdl.prc = &rc;
  629.   hdl.pwpos = &wp;
  630.   SendMessage( hHdr, HDM_LAYOUT, 0, (LPARAM)&hdl );
  631.   SetWindowPos( hHdr, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
  632.                 wp.flags | SWP_SHOWWINDOW );
  633. }
  634. BOOL handleHeaderNotify( HWND hWnd, LPARAM lParam, LPTRACKWNDEXTRA lpe )
  635. {
  636.   HD_NOTIFY *lphd = (HD_NOTIFY *)lParam;
  637.   LPNMHDR lpnm = (LPNMHDR)lParam;
  638. #if 0
  639.   if ( lpnm->code <= HDN_FIRST && lpnm->code >= HDN_LAST )
  640.     {
  641.       wsprintf( buf, "Header notify[%04X:%d:%d]: %s", lphd->hdr.hwndFrom, lphd->iItem,lphd->iButton,  notifyString( lphd->hdr.code ) );
  642.     }
  643.   else
  644.     wsprintf( buf, "Header notify[%04X]: %s", lphd->hdr.hwndFrom,  notifyString( lphd->hdr.code ) );
  645.   OutputDebugString( buf );
  646. #endif
  647.   switch( lpnm->code )
  648.     {
  649.     case HDN_ITEMCHANGED:
  650.       RecalcWindow( hWnd, lpe );
  651.       InvalidateRect( hWnd, NULL, TRUE );
  652.       UpdateWindow( hWnd );
  653.       break;
  654.     case HDN_BEGINTRACK:
  655.       SetFocus( hWnd );
  656.       // prevent the first header from changing
  657.       if ( lphd->iItem == 0 )
  658.         return TRUE;
  659.       break;
  660.     }
  661.   return FALSE;
  662. }
  663. #ifndef NM_CUSTOMDRAW
  664. #define NM_CUSTOMDRAW     (NM_FIRST-12)
  665. #endif
  666. #ifndef NM_RELEASEDCAPTURE
  667. #define NM_RELEASEDCAPTURE   (NM_FIRST-16)
  668. #endif
  669. char *notifyString( UINT code )
  670. {
  671.   static char buf[60];
  672.   switch( code )
  673.     {
  674.     case NM_CUSTOMDRAW: return "NM_CUSTOMDRAW";
  675.     case NM_CLICK: return "NM_CLICK";
  676.     case NM_DBLCLK: return "NM_DBLCLK";
  677.     case NM_KILLFOCUS: return "NM_KILLFOCUS";
  678.     case NM_OUTOFMEMORY: return "NM_OUTOFMEMORY";
  679.     case NM_RCLICK: return "NM_RCLICK";
  680.     case NM_RDBLCLK: return "NM_RDBLCLK";
  681.     case NM_RETURN: return "NM_RETURN";
  682.     case NM_SETFOCUS: return "NM_SETFOCUS";
  683.     case NM_RELEASEDCAPTURE: return "NM_RELEASEDCAPTURE";
  684.     case HDN_BEGINTRACK: return "HDN_BEGINTRACK";
  685.     case HDN_DIVIDERDBLCLICK: return "NM_DIVIDERDBLCLICK";
  686.     case HDN_ENDTRACK: return "HDN_ENDTRACK";
  687.     case HDN_ITEMCHANGED: return "HDN_ITEMCHANGED";
  688.     case HDN_ITEMCHANGING: return "HDN_ITEMCHANGING";
  689.     case HDN_ITEMCLICK: return "HDN_ITEMCLICK";
  690.     case HDN_ITEMDBLCLICK: return "HDN_ITEMDBLCLICK";
  691.     case HDN_TRACK: return "HDN_TRACK";
  692.     default: wsprintf( buf, "unknown %04X (%d)", code, code ); return buf;
  693.     }
  694. }
  695. /*
  696.  * determines if a mouse click (in window coordinates) was on a check box.
  697.  * If so, returns the index of the track.  If not, returns -1
  698.  */
  699. int CheckBoxHit( LPTRACKWNDEXTRA lpe, LPARAM lParam )
  700. {
  701.   int i, x, y;
  702.   int mx, my;
  703.   mx = (int)LOWORD(lParam) + lpe->dx;
  704.   my = (int)HIWORD(lParam) + lpe->dy;
  705.   for( i = 0; i < lpe->iNum; i++ )
  706.     {
  707.       x = (lpe->hx[1] + 1) / 2 - 4;   // left side x value
  708.       y = 11 + lpe->iHeaderHeight + i*(lpe->iTextHeight + 4);
  709.       if ( (mx >= x) && (mx <= (x+8)) && (my >= y) && (my <= (y+8)) )
  710.         return i;
  711.     }
  712.   return -1;
  713. }
  714. /*
  715.  * Determines if a mouse click was on a track name on the screen.  lParam
  716.  * is expected to be in client coordinates, as sent with the WM_LBUTTONDOWN,
  717.  * etc. messages.  Returns the index of the track hit, or -1 if not on a
  718.  * track name
  719.  */
  720. int TrackNameHit( LPTRACKWNDEXTRA lpe, LPARAM lParam )
  721. {
  722.   int i, x, y;
  723.   int mx, my;
  724.   RECT rc;
  725.   // if it's within the header, return -1 (no track hit)
  726.   if ( ((int)HIWORD(lParam)) <= lpe->iHeaderHeight )
  727.     return -1;
  728.   mx = (int)LOWORD(lParam) + lpe->dx;
  729.   my = (int)HIWORD(lParam) + lpe->dy;
  730.   for( i = 0; i < lpe->iNum; i++ )
  731.     {
  732.       x = lpe->hx[1] + 1;
  733.       y = 6 + lpe->iHeaderHeight + i*(lpe->iTextHeight + 4);
  734.       rc.left = x;
  735.       rc.right = x + lpe->hw[1];
  736.       rc.top = y;
  737.       rc.bottom = y + lpe->iTextHeight;
  738.       if ( (mx >= rc.left) && (mx <= rc.right) && (my >= rc.top) &&
  739.            (my <= rc.bottom) )
  740.         return i;
  741.     }
  742.   return -1;
  743. }
  744. /*
  745.  * returns TRUE if the mouse click was on either a check box or a track name,
  746.  * and will require a repaint
  747.  */
  748. BOOL HandleLMouseDown( HWND hWnd, LPTRACKWNDEXTRA lpe, LPARAM lParam )
  749. {
  750.   int i;
  751.   SetFocus( hWnd );
  752.   // determine if the click was on a check box
  753.   i = CheckBoxHit( lpe, lParam );
  754.   if ( i != -1 )
  755.     {
  756.       lpe->tracks[i].bChecked = !lpe->tracks[i].bChecked;
  757.       UpdateStatusBar();
  758.       return TRUE;
  759.     }
  760.   i = TrackNameHit( lpe, lParam );
  761.   if ( i != -1 )
  762.     {
  763.       lpe->iSelected = i;
  764.       return TRUE;
  765.     }
  766.   return FALSE;
  767. }
  768. /*
  769.  * returns TRUE if the mouse click was on either a check box or a track name,
  770.  * and will require a repaint
  771.  */
  772. BOOL HandleRMouseDown( HWND hWnd, LPTRACKWNDEXTRA lpe, LPARAM lParam )
  773. {
  774.   int i;
  775.   SetFocus( hWnd );
  776.   // did we hit a track name?
  777.   i = TrackNameHit( lpe, lParam );
  778.   if ( i != -1 )
  779.     {
  780.       lpe->iSelected = i;
  781.       return TRUE;
  782.     }
  783.   return FALSE;
  784. }
  785. /*
  786.  * if trackNo == -1, then check/uncheck all
  787.  */
  788. BOOL HandleCheckTrack( HWND hWnd, LPTRACKWNDEXTRA lpe, int trackNo, BOOL bChecked )
  789. {
  790.   int i;
  791.   hWnd = hWnd;
  792.   if ( trackNo == (int)ALLTRACKS )
  793.     {
  794.       for( i = 0; i < lpe->iNum; i++ )
  795.         lpe->tracks[i].bChecked = bChecked;
  796.       return TRUE;
  797.     }
  798.   if ( trackNo < 0 || trackNo >= lpe->iNum )
  799.     return FALSE;
  800.   lpe->tracks[trackNo].bChecked = bChecked;
  801.   return TRUE;
  802. }
  803. /*
  804.  * if trackNo == -1, then check/uncheck all
  805.  */
  806. BOOL HandleInvertCheck( HWND hWnd, LPTRACKWNDEXTRA lpe, int trackNo )
  807. {
  808.   int i;
  809.   hWnd = hWnd;
  810.   if ( trackNo == (int)ALLTRACKS )
  811.     {
  812.       for( i = 0; i < lpe->iNum; i++ )
  813.         lpe->tracks[i].bChecked = !lpe->tracks[i].bChecked;
  814.       return TRUE;
  815.     }
  816.   if ( trackNo < 0 || trackNo >= lpe->iNum )
  817.     return FALSE;
  818.   lpe->tracks[trackNo].bChecked = !lpe->tracks[trackNo].bChecked;
  819.   return TRUE;
  820. }
  821. int doTrackContextMenu( HWND hParent, HWND hChild, LPARAM lParam )
  822. {
  823.   HMENU hMenu, hPopup;
  824.   LPTRACKWNDEXTRA lpe;
  825.   int i;
  826.   POINT p;
  827.   LPARAM lp2;
  828.   p.x = (LONG)LOWORD(lParam);
  829.   p.y = (LONG)HIWORD(lParam);
  830.   ScreenToClient( hChild, &p );
  831.   lp2 = MAKELPARAM( p.x, p.y );
  832.   lpe = (LPTRACKWNDEXTRA)GetWindowLong( hChild, GWL_USERDATA);
  833.   if ( p.y < lpe->iHeaderHeight )
  834.     return 0;
  835.   // if it hit a track name, then we need to pop up the track context menu
  836.   // otherwise, we need a generic context menu
  837.   i = TrackNameHit( lpe, lp2 );
  838.   if ( i != -1 )
  839.     {
  840.       hMenu = LoadMenu( ghInstance, "trackPopupMenu" );
  841.       if ( !hMenu )
  842.         return 0;
  843.       hPopup = GetSubMenu( hMenu, 0 );
  844.       TrackPopupMenu( hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  845.                       LOWORD(lParam), HIWORD(lParam), 0, hParent, NULL );
  846.     }
  847.   else
  848.     {
  849.       hMenu = LoadMenu( ghInstance, "genericPopupMenu" );
  850.       if ( !hMenu )
  851.         return 0;
  852.       // pop up a more generic menu
  853.       hPopup = GetSubMenu( hMenu, 0 );
  854.       TrackPopupMenu( hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  855.                       LOWORD(lParam), HIWORD(lParam), 0, hParent, NULL );
  856.     }
  857.   DestroyMenu( hMenu );
  858.   return 1;
  859. }
  860. /*
  861.  * Pops up a sub-classed single-line edit on top of track title.  Must ensure
  862.  * that the selected track is visible.  Captures mouse input to catch when
  863.  * user clicks elsewhere, and saves the text.
  864.  */
  865. BOOL RenameSelectedTrack( HWND hWnd, LPTRACKWNDEXTRA lpe )
  866. {
  867.   int hw[5];            // width of header items
  868.   int hx[5];            // x offset of header items
  869.   int i;
  870.   RECT rc;
  871.   //HWND hEdit;
  872.   if ( bInEditTrack )
  873.     return FALSE;
  874.   bInEditTrack = TRUE;
  875.   if ( (lpe->iSelected < 0) || (lpe->iSelected >= lpe->iNum) )
  876.     return FALSE;
  877.   /*
  878.    * compute the offsets for each header section
  879.    */
  880.   ZeroMemory( hw, sizeof(int[5]) );
  881.   ZeroMemory( hx, sizeof(int[5]) );
  882.   for( i = 0; i < 3; i++ )
  883.     {
  884.       HD_ITEM hi;
  885.       ZeroMemory( &hi, sizeof(hi) );
  886.       hi.mask = HDI_WIDTH;
  887.       SendMessage( lpe->hHeader, HDM_GETITEM, i, (LPARAM)&hi );
  888.       hw[i] = hi.cxy;
  889.       hx[i+1] = hx[i] + hw[i];
  890.     }
  891.   i = lpe->iSelected;
  892.   rc.left = hx[1] + 5 - lpe->dx;
  893.   rc.right = rc.left + hw[1] - 10;
  894.   rc.top = 4 + (lpe->iHeaderHeight + i*(lpe->iTextHeight+4) ) - lpe->dy;
  895.   rc.bottom = rc.top + 20;
  896.   hEdit = CreateWindowEx( 0L, "EDIT", lpe->tracks[lpe->iSelected].name,
  897.                           WS_BORDER | WS_CHILD | WS_VISIBLE
  898.                           | ES_AUTOHSCROLL | ES_LEFT,
  899.                           rc.left, rc.top,
  900.                           rc.right - rc.left, rc.bottom - rc.top,
  901.                           hWnd, (HMENU)++iCtlId, ghInstance, NULL );
  902.   // Save old window proc
  903.   SetWindowLong( hEdit, GWL_USERDATA, GetWindowLong( hEdit, GWL_WNDPROC ) );
  904.   // Set new window proc
  905.   SetWindowLong( hEdit, GWL_WNDPROC, (LONG)EditSubclassWndProc );
  906.   SetFocus( hEdit );
  907.   SendMessage( hEdit, EM_SETLIMITTEXT, (WPARAM)(MAX_PATH+1), 0L );
  908.   SendMessage( hEdit, EM_SETSEL, 0, -1L );
  909.   //SendMessage( hEdit, WM_STARTMOUSECAPTURE, 0, 0L );
  910.   SetCapture( hEdit );
  911.   return TRUE;
  912. }
  913. LRESULT processFindFirst( LPTRACKWNDEXTRA lpe, LPADDTRACK lpAddTrack )
  914. {
  915.   int i;
  916.   if ( lpAddTrack == NULL )
  917.     return -1L;
  918.   for( i = 0; i < lpe->iNum; i++ )
  919.     {
  920.       if ( lpe->tracks[i].bChecked )
  921.         {
  922.           *lpAddTrack = lpe->tracks[i];
  923.           lpe->iFound = i;
  924.           return i;
  925.         }
  926.     }
  927.   return -1L;
  928. }
  929. LRESULT processFindNext( LPTRACKWNDEXTRA lpe, LPADDTRACK lpAddTrack )
  930. {
  931.   int i;
  932.   if ( lpAddTrack == NULL )
  933.     return -1L;
  934.   for( i = lpe->iFound + 1; i < lpe->iNum; i++ )
  935.     {
  936.       if ( lpe->tracks[i].bChecked )
  937.         {
  938.           *lpAddTrack = lpe->tracks[i];
  939.           lpe->iFound = i;
  940.           return i;
  941.         }
  942.     }
  943.   return -1L;
  944. }
  945. BOOL setTrackStatus( LPTRACKWNDEXTRA lpe, int idx, LPCSTR s )
  946. {
  947.   if ( !lpe || !s )
  948.     return FALSE;
  949.   lstrcpyn( lpe->tracks[idx].status, s, 61 );
  950.   return TRUE;
  951. }
  952. #ifndef WM_MOUSEHOVER
  953. #define WM_MOUSEHOVER 0x2A1
  954. #endif
  955. #ifndef WM_MOUSELEAVE
  956. #define WM_MOUSELEAVE 0x2A3
  957. #endif
  958. void logWindowsProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  959. {
  960. #if 0
  961.   char szMsg[40];
  962.   static UINT filtered[] = {
  963.     WM_MOUSEMOVE, WM_MOUSEHOVER, WM_MOUSELEAVE, WM_SETCURSOR, WM_NCHITTEST, 0
  964.   };
  965.   int i;
  966.   hWnd = hWnd, wParam = wParam; lParam = lParam;
  967.   for( i = 0; filtered[i]; i++ )
  968.     if ( uMsg == filtered[i] )
  969.       return;
  970.   
  971.   convertuMsg( uMsg, szMsg );
  972. #else
  973.   hWnd = hWnd; uMsg = uMsg; wParam = wParam; lParam = lParam;
  974. #endif
  975. }
  976. void convertuMsg( UINT uMsg, char * s )
  977. {
  978.   switch( uMsg )
  979.     {
  980.     case WM_COMMAND: lstrcpy( s, "WM_COMMAND" ); break;
  981.     case WM_INITDIALOG: lstrcpy( s, "WM_INITDIALOG" ); break;
  982.     default:
  983.       wsprintf( s, "%d", uMsg );
  984.       break;
  985.     }
  986. }
  987. LRESULT CALLBACK EditSubclassWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  988. {
  989.   WNDPROC lpfnWndProc;
  990.   int x, y;
  991.   RECT rc;
  992.   lpfnWndProc = (WNDPROC)GetWindowLong( hWnd, GWL_USERDATA );
  993. #if 0
  994.   switch( uMsg )
  995.     {
  996.     case EM_CANUNDO: OutputDebugString( "EM_CANUNDO" ); break;
  997.     case EM_CHARFROMPOS: OutputDebugString( "EM_CHARFROMPOS" ); break;
  998.     case EM_EMPTYUNDOBUFFER: OutputDebugString( "EM_EMPTYUNDOBUFFER" ); break;
  999.     case EM_GETFIRSTVISIBLELINE: OutputDebugString( "EM_GETFIRSTVISIBLELINE" ); break;
  1000.     case EM_GETLIMITTEXT: OutputDebugString( "EM_GETLIMITTEXT" ); break;
  1001.     case EM_GETLINE: OutputDebugString( "EM_GETLINE" ); break;
  1002.     case EM_GETMARGINS: OutputDebugString( "EM_GETMARGINS" ); break;
  1003.     case EM_GETMODIFY: OutputDebugString( "EM_GETMODIFY" ); break;
  1004.     case EM_GETRECT: OutputDebugString( "EM_GETRECT" ); break;
  1005.     case EM_GETSEL: OutputDebugString( "EM_GETSEL" ); break;
  1006.     case EM_LINELENGTH: OutputDebugString( "EM_LINELENGTH" ); break;
  1007.     case EM_POSFROMCHAR: OutputDebugString( "EM_POSFROMCHAR" ); break;
  1008.     case EM_REPLACESEL: OutputDebugString( "EM_REPLACESEL" ); break;
  1009.     case EM_SETLIMITTEXT: OutputDebugString( "EM_SETLIMITTEXT" ); break;
  1010.     case EM_SETMARGINS: OutputDebugString( "EM_SETMARGINS" ); break;
  1011.     case EM_SETMODIFY: OutputDebugString( "EM_SETMODIFY" ); break;
  1012.     case EM_SETREADONLY: OutputDebugString( "EM_SETREADONLY" ); break;
  1013.     case EM_SETSEL: OutputDebugString( "EM_SETSEL" ); break;
  1014.     case EM_UNDO: OutputDebugString( "EM_UNDO" ); break;
  1015.     case WM_COMMAND:
  1016.       switch( HIWORD(wParam) )
  1017.         {
  1018.         case EN_SETFOCUS: OutputDebugString( "EN_SETFOCUS: edit control" ); break;
  1019.         case EN_KILLFOCUS: OutputDebugString( "EN_KILLFOCUS: edit control" ); break;
  1020.         case EN_CHANGE: OutputDebugString( "EN_CHANGE" ); break;
  1021.         case EN_ERRSPACE: OutputDebugString( "EN_ERRSPACE" ); break;
  1022.         case EN_MAXTEXT: OutputDebugString( "EN_MAXTEXT" ); break;
  1023.         case EN_UPDATE: OutputDebugString( "EN_UPDATE" ); break;
  1024.         }
  1025.       break;
  1026.     }
  1027. #endif
  1028.   switch( uMsg )
  1029.     {
  1030.     case WM_LBUTTONDOWN:
  1031.     case WM_RBUTTONDOWN:
  1032.       GetClientRect( hWnd, &rc );
  1033.       x = (int)LOWORD( lParam );
  1034.       y = (int)HIWORD( lParam );
  1035.       if ( (x < 0) || ( y < 0) || (x > rc.right) || (y > rc.bottom) )
  1036.         {
  1037.           POINT p;
  1038.           SetTrackWndText( hWnd );
  1039.           ReleaseCapture();
  1040.           bInEditTrack = FALSE;
  1041.           p.x = x; p.y = y; MapWindowPoints( hWnd, GetParent(hWnd ), &p, 1 );
  1042.           SendMessage( GetParent(hWnd), uMsg, wParam,
  1043.                        (LPARAM)MAKELONG( p.x, p.y ) );
  1044.           DestroyWindow( hWnd );
  1045.           return 0L;
  1046.         }
  1047.       break;
  1048.     case WM_LBUTTONUP:
  1049.       break;
  1050.     case WM_KEYDOWN:
  1051.       if ( (int)wParam == 27 ) // escape key
  1052.         {
  1053.           //SetTrackWndText( hWnd );
  1054.   bInEditTrack = FALSE;
  1055.           DestroyWindow( hWnd );
  1056.           return 0L;
  1057.         }
  1058.       break;
  1059.     case WM_CAPTURECHANGED:
  1060.       break;
  1061.     }
  1062.   return CallWindowProc( lpfnWndProc, hWnd, uMsg, wParam, lParam );
  1063. }
  1064. void SetTrackWndText( HWND hWnd )
  1065. {
  1066.   char buf[MAX_PATH+1];
  1067.   ZeroMemory( buf, MAX_PATH+1 );
  1068.   SendMessage( hWnd, WM_GETTEXT, MAX_PATH+1, (LPARAM)buf );
  1069.   SendMessage( GetParent( hWnd ), WM_SETSELTEXT, 0, (LPARAM)buf );
  1070. }
  1071. LRESULT CALLBACK FooWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  1072. {
  1073.   return DefWindowProc( hWnd, uMsg, wParam, lParam );
  1074. }
  1075. LRESULT numChecked( LPTRACKWNDEXTRA lpe )
  1076. {
  1077.   int i;
  1078.   LRESULT retVal = 0;
  1079.   for( i = 0; lpe && (i < lpe->iNum); i++ )
  1080.     if ( lpe->tracks[i].bChecked )
  1081.       retVal++;
  1082.   return retVal;
  1083. }