card.c
Upload User: caisha3
Upload Date: 2013-09-21
Package Size: 208739k
Code Size: 20k
Category:

Windows Develop

Development Platform:

Visual C++

  1. #include "precomp.h"
  2. /************************************************************************/
  3. /*                                                                      */
  4. /*  Windows Cardfile - Written by Mark Cliggett                         */
  5. /*  (c) Copyright Microsoft Corp. 1985, 1994 - All Rights Reserved      */
  6. /*                                                                      */
  7. /************************************************************************/
  8. #if !defined(UNICODE) && defined(JAPAN)
  9. /* Edit Control tune up routine */
  10. WORD NEAR PASCAL EatOneCharacter(HWND);
  11. /*
  12.  * routine to retrieve WM_CHAR from the message queue associated with hwnd.
  13.  * this is called by EatString.
  14.  */
  15. WORD NEAR PASCAL EatOneCharacter(hwnd)
  16. register HWND hwnd;
  17. {
  18.     MSG msg;
  19.     register int i = 10;
  20.     while (!PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE))
  21.     {
  22.         if (--i == 0)
  23.             return -1;
  24.         Yield ();
  25.     }
  26.     return (msg.wParam & 0x00FF);
  27. }
  28. BOOL FAR PASCAL EatString(HWND,LPSTR,WORD);
  29. /*
  30.  * This routine is called when the Edit Control receives WM_IME_REPORT
  31.  * with IR_STRINGSTART message. The purpose of this function is to eat
  32.  * all WM_CHARs between IR_STRINGSTART and IR_STRINGEND and to build a
  33.  * string block.
  34.  */
  35. BOOL FAR PASCAL EatString(hwnd, lpSp, cchLen)
  36. register HWND hwnd;
  37. LPSTR lpSp;
  38. WORD cchLen;
  39. {
  40.     MSG msg;
  41.     int i = 10; /* loop counter for avoid infinite loop */
  42.     int w;
  43.     *lpSp = '';
  44.     if (cchLen < 4)
  45.         return NULL;    /* not enough */
  46.     cchLen -= 2;
  47.     while (i--)
  48.     {
  49.         while (PeekMessage (&msg, hwnd, NULL, NULL, PM_REMOVE))
  50.         {
  51.             i = 10;
  52.             switch (msg.message)
  53.             {
  54.                 case WM_CHAR:
  55.                     *lpSp++ = (BYTE)msg.wParam;
  56.                     cchLen--;
  57.                     if (IsDBCSLeadByte((BYTE)msg.wParam))
  58.                     {
  59.                         if ((w = EatOneCharacter(hwnd)) == -1)
  60.                         {
  61.                             /* Bad DBCS sequence - abort */
  62.                             lpSp--;
  63.                             goto WillBeDone;
  64.                         }
  65.                         *lpSp++ = (BYTE)w;
  66.                         cchLen--;
  67.                     }
  68.                     if (cchLen <= 0)
  69.                         goto WillBeDone;   /* buffer exhausted */
  70.                     break;
  71.                 case WM_IME_REPORT:
  72.                     if (msg.wParam == IR_STRINGEND)
  73.                     {
  74.                         if (cchLen <= 0)
  75.                             goto WillBeDone; /* no more room to stuff */
  76.                         if ((w = EatOneCharacter(hwnd)) == -1)
  77.                             goto WillBeDone;
  78.                         *lpSp++ = (BYTE)w;
  79.                         if (IsDBCSLeadByte((BYTE)w))
  80.                         {
  81.                             if ((w = EatOneCharacter(hwnd)) == -1)
  82.                             {
  83.                                 /* Bad DBCS sequence - abort */
  84.                                 lpSp--;
  85.                                 goto WillBeDone;
  86.                             }
  87.                             *lpSp++ = (BYTE)w;
  88.                         }
  89.                         goto WillBeDone;
  90.                     }
  91.                     /* Fall through */
  92.                 default:
  93.                     TranslateMessage(&msg);
  94.                     DispatchMessage(&msg);
  95.                     break;
  96.             }
  97.         }
  98.     }
  99.     /* We don't get WM_IME_REPORT + IR_STRINGEND
  100.      * But received string will be OK
  101.      */
  102. WillBeDone:
  103.     *lpSp = '';
  104.     return TRUE;
  105. }
  106. #endif
  107. #ifdef WIN32
  108. /* These are C equivalents to functions that were in INDOS2.ASM */
  109. VOID RepMov(
  110.     LPBYTE lpDest,
  111.     LPBYTE lpSrc,
  112.     UINT n)
  113. {
  114.    while (n--)
  115.    {
  116.       *lpDest++ = *lpSrc++;
  117.    }
  118. }
  119. VOID RepMovDown(
  120.     LPBYTE lpDest,
  121.     LPBYTE lpSrc,
  122.     UINT n)
  123. {
  124.    lpDest += n;
  125.    lpSrc  += n;
  126.    lpDest--;
  127.    lpSrc--;
  128.    while (n--)
  129.    {
  130.       *lpDest-- = *lpSrc--;
  131.    }
  132. }
  133. #endif
  134. NOEXPORT BOOL NEAR CardKey( INT wParam);
  135. /*
  136.  * Hook Proc for the multi line Edit control.
  137.  * In picture mode,
  138.  *      WM_SETCURSOR - sets the arrow cursor,
  139.  *      mouse msgs - responds to left button down, up, dbl click and mouse move
  140.  *      WM_KEYDOWN - responds to VK_UP/DOWN/LEFT/RIGHT/INSERT/DELETE
  141.  * In text mode,
  142.  *      implements hot key to move to a card(e.g. using Ctrl+A, goto first/last/
  143.  *      next/prev card), by responding to WM_CHAR and WM_KEYDOWN.
  144.  */
  145. LONG EditWndProc(
  146.     HWND   hwnd,
  147.     UINT   message,
  148.     WPARAM wParam,
  149.     LONG   lParam)
  150. {
  151.     PAINTSTRUCT ps;
  152. #if !defined(UNICODE) && defined(JAPAN)
  153.     LPSTR lpP;
  154.     HANDLE hMem;
  155.     HANDLE hClipSave;
  156. #endif
  157.     switch (message) {
  158. #if !defined(UNICODE) && defined(JAPAN)
  159. /*
  160.  *
  161.  *
  162.  *
  163.  */
  164.         case  WM_IME_REPORT:
  165.             if (EditMode != I_TEXT)
  166.                 break;
  167.             switch (wParam)
  168.             {
  169.                 case IR_STRING:
  170.                     /*OutputDebugString("IR_STRINGrn");*/
  171.                     if (lpP = (LPSTR) GlobalLock((HANDLE)lParam))
  172.                     {
  173.                         CallWindowProc(lpEditWndProc, hwnd, EM_REPLACESEL, 0, (DWORD)lpP);
  174.                         GlobalUnlock((HANDLE)lParam);
  175.                         return 1L; /* processed */
  176.                     }
  177.                     break;
  178.                 case IR_STRINGSTART:
  179.                     /* OutputDebugString("IR_STRINGSTARTrn"); */
  180.                     if ((hMem = GlobalAlloc(GMEM_MOVEABLE, 512)) == NULL)
  181.                     {
  182.                         /* OutputDebugString("Ga failedrn");*/
  183.                         goto PassMessageOn;
  184.                     }
  185.                     if ((lpP = (LPSTR) GlobalLock(hMem)) == NULL)
  186.                     {
  187.                         /* OutputDebugString("Lock failedrn"); */
  188.                         GlobalFree(hMem);
  189.                         goto PassMessageOn;
  190.                     }
  191.                     if (EatString(hwnd, lpP, 512))
  192.                     {
  193.                         /* OutputDebugString("Eat okrn"); */
  194.                         CallWindowProc(lpEditWndProc, hwnd, EM_REPLACESEL, 0, (DWORD)lpP);
  195.                         GlobalUnlock(hMem);
  196.                         GlobalFree(hMem);
  197.                         break;
  198.                     }
  199.                     GlobalUnlock(hMem);
  200.                     GlobalFree(hMem);
  201.             }
  202.             break;
  203. #endif
  204.         case WM_DROPFILES:
  205.             DoDragDrop(hwnd, (HANDLE)wParam, TRUE);
  206.             break;
  207.         case WM_SETCURSOR:
  208.             /* use arrow cursor when in picture mode */
  209.             if (EditMode == I_OBJECT)
  210.                 SetCursor(hArrowCurs);
  211.             else
  212.                 goto PassMessageOn;
  213.             break;
  214.         case WM_LBUTTONDOWN:
  215.         case WM_LBUTTONDBLCLK:
  216.         case WM_MOUSEMOVE:
  217.         case WM_LBUTTONUP:
  218.             if (EditMode == I_OBJECT)
  219.                 BMMouse(hwnd, message, wParam, MYMAKEPOINT(lParam));
  220.             else    /* In text mode, pass it to edit control */
  221.                 goto PassMessageOn;
  222.             break;
  223.         case WM_CHAR:
  224.             /* In text mode, Ctrl+'char' acts as a hot key to move to a
  225.              * specific card. Respond to Ctrl+'A' etc */
  226.             if (!CardChar(wParam) && (EditMode == I_TEXT))
  227.                 /* If it doesn't move our object, pass it to edit control */
  228.                 goto PassMessageOn;
  229.             break;
  230.         case WM_KEYDOWN:
  231.             /* In text mode, respond to Ctrl+HOME, Ctrl+END(move to first/last card),
  232.              * VK_NEXT/VK_PRIOR(goto next and prev cards).
  233.              * In picture mode, respond to VK_UP/DOWN/LEFT/RIGHT/VK_INSERT/VK_DELETE
  234.              * keys */
  235.             if (!CardKey(wParam) && (EditMode == I_TEXT))
  236.                 /* If it doesn't affect our object, ... */
  237.                 goto PassMessageOn;
  238.             break;
  239.         case WM_PAINT:
  240.             BeginPaint(hwnd, &ps);
  241.             CallWindowProc((WNDPROC)lpEditWndProc, hEditWnd, message, (LONG)ps.hdc, 0L);
  242.             CardPaint(ps.hdc);
  243.             EndPaint(hwnd, &ps);
  244.             break;
  245.         default:
  246. PassMessageOn:
  247.             return CallWindowProc((WNDPROC)lpEditWndProc, hEditWnd, message, wParam, lParam);
  248.     }
  249.     return(0L);
  250. }
  251. /*
  252.  * Responds to WM_KEYDOWN message.
  253.  * In text mode,
  254.  *      Ctrl+HOME/END - goto first/last card
  255.  *      VK_PRIOR/NEXT - goto previous/next card
  256.  *
  257.  * In picture mode,
  258.  *      VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_INSERT, VK_DELETE handled
  259.  *      in BMKey
  260.  */
  261. NOEXPORT BOOL NEAR CardKey( INT wParam)
  262. {
  263.     switch(wParam)
  264.     {
  265.         case VK_HOME:
  266.             /* Home         Beginning of line (don't process)
  267.              * Shft+Home    Extend selection to beg. of line (don't process)
  268.              * Ctrl+Home    Go to first card (process here)
  269.              */
  270.             if (GetKeyState(VK_CONTROL) >= 0)
  271.                 return(FALSE);
  272.             ScrollCards(hCardWnd, SB_THUMBPOSITION, 0);
  273.             break;
  274.         case VK_END:
  275.             /* End          End of line (don't process)
  276.              * Shft+End     Extend selection to end of line (don't process)
  277.              * Ctrl+End     Go to last card (process here)
  278.              */
  279.             if (GetKeyState(VK_CONTROL) >= 0)
  280.                 return(FALSE);
  281.             ScrollCards(hCardWnd, SB_THUMBPOSITION, cCards-1);
  282.             break;
  283.         case VK_PRIOR:
  284.             ScrollCards(hCardWnd, SB_LINEUP, 0);
  285.             break;
  286.         case VK_NEXT:
  287.             ScrollCards(hCardWnd, SB_LINEDOWN, 0);
  288.             break;
  289.         case VK_UP:
  290.         case VK_DOWN:
  291.         case VK_LEFT:
  292.         case VK_RIGHT:
  293.         case VK_INSERT:
  294.         case VK_DELETE:
  295.             if (EditMode == I_TEXT) /* should be handled by the edit control */
  296.                 return(FALSE);
  297.             else
  298.                 BMKey((WORD) wParam);      /* we will handle this */
  299.             return(TRUE);
  300.         default:
  301.             return(FALSE);
  302.     }
  303.     ScrollCards(hCardWnd, SB_ENDSCROLL, 0);
  304.     return(TRUE);
  305. }
  306. /*
  307.  * Checks if the user is trying to move to a specific card using a hot key
  308.  * e.g. Ctrl+A will move it to a card starting with A
  309.  */
  310. BOOL CardChar(
  311.     int ch)
  312. {
  313.     int fControl;
  314.     LPCARDHEADER Cards;
  315.     int i;
  316.     int iCard;
  317.     fControl = (GetKeyState(VK_CONTROL) < 0) && (GetKeyState(VK_SHIFT) < 0);
  318.     if (!fControl || ch >= TEXT(' '))
  319.         return(FALSE);
  320.     /* convert control char, i.e. from Ctrl+A get 'A' */
  321.     ch += TEXT('A') - 1;
  322.     Cards = (LPCARDHEADER) GlobalLock(hCards);
  323.     for (i = 0, iCard = iFirstCard+1; i < cCards; ++i, iCard++) {
  324.         if (iCard == cCards)
  325.             iCard = 0;
  326.         if ((TCHAR)(DWORD)CharUpper((LPTSTR)(DWORD)(BYTE)*(Cards[iCard].line)) == ch)
  327.             break;
  328.     }
  329.     GlobalUnlock(hCards);
  330.     if (i < cCards)     /* make sure a card was found */
  331.         ScrollCards(hCardWnd, SB_THUMBPOSITION, iCard);
  332.     return(TRUE);
  333. }
  334. /*
  335.  * this paints the bitmap in the edit window.
  336.  */
  337. void CardPaint(
  338.     HDC hDC)
  339. {
  340.     RECT rc;
  341.     if (CardPhone == PHONEBOOK ||   /* list mode */
  342.         !CurCard.lpObject ||        /* no object to draw */
  343.         fInsertComplete == FALSE)   /* InsertObject in progress */
  344.     {
  345.         return;
  346.     }
  347.     Hourglass(TRUE);
  348.     /* If RECT is null, reget scaled object size */
  349.     /* This will never happen with a plain static BITMAP */
  350.     if (!CurCard.rcObject.right)
  351.     {
  352.         if (OleQueryBounds(CurCard.lpObject, &rc) != OLE_OK)
  353.         {
  354.             Hourglass(FALSE);
  355.             ErrorMessage(E_BOUNDS_QUERY_FAILED);
  356.             return;
  357.         }
  358.         FixBounds(&rc);
  359.         SetRect(&(CurCard.rcObject),
  360.             CurCard.rcObject.left, CurCard.rcObject.top,
  361.             CurCard.rcObject.left + (rc.right - rc.left),
  362.             CurCard.rcObject.top + (rc.bottom - rc.top));
  363.     }
  364.     /* Draw the object */
  365.     PicDraw(&CurCard, hDC, FALSE);
  366.     Hourglass(FALSE);
  367. }
  368. /*
  369.  * Delete the ith card by removing its header
  370.  */
  371. void DeleteCard(
  372.     int iCard)
  373. {
  374.     LPCARDHEADER Cards;
  375.     cCards--;
  376.     Cards = (LPCARDHEADER) GlobalLock(hCards);
  377.     RepMov((LPBYTE)&Cards[iCard], (LPBYTE)&Cards[iCard+1], (cCards-iCard)*sizeof(CARDHEADER));
  378.     GlobalUnlock(hCards);
  379.     InitPhoneList(hListWnd, iFirstCard);
  380. }
  381. /*
  382.  * Add a card in order in the card array.
  383.  */
  384. int AddCurCard(
  385.     void)
  386. {
  387.     LPCARDHEADER Cards;
  388.     int i;
  389.     Cards = (LPCARDHEADER) GlobalLock(hCards);
  390.     for (i = 0; i < cCards; i++)
  391.     {
  392.         if (lstrcmp(CurCardHead.line, Cards[i].line) <= 0)
  393.             break;
  394.     }
  395.     if (i != cCards)
  396.         RepMovDown((LPBYTE)&Cards[i+1], (LPBYTE)&Cards[i], (cCards - i) * sizeof(CARDHEADER));
  397.     Cards[i] = CurCardHead;
  398.     GlobalUnlock(hCards);
  399.     cCards++;
  400.     /* Set highlight to the new card */
  401.     InitPhoneList(hListWnd, i);
  402.     return(i);
  403. }
  404. /*
  405.  *  save CurCardHead and assorted things??
  406.  */
  407. BOOL SaveCurrentCard(
  408.     int iCard)
  409. {
  410.     LPCARDHEADER Cards;
  411.     /* save the card if it's dirty */
  412.     /* dirty if bitmap has changed or edittext has changed */
  413.     if (CurCardHead.flags & (FDIRTY+FNEW) || SendMessage(hEditWnd, EM_GETMODIFY, 0, 0L))
  414.     {
  415.         /* get modified text from edit window */
  416.         GetWindowText(hEditWnd, szText, CARDTEXTSIZE);
  417.         if (WriteCurCard(&CurCardHead, &CurCard, szText))
  418.         {
  419.             if (CurCardHead.flags & FDIRTY || SendMessage(hEditWnd, EM_GETMODIFY, 0, 0L))
  420.                 fFileDirty = TRUE;
  421.             SendMessage(hEditWnd, EM_SETMODIFY, FALSE, 0L);
  422.             CurCardHead.flags &= (!FNEW);
  423.             CurCardHead.flags &= (!FDIRTY);
  424.             CurCardHead.flags |= FTMPFILE;
  425.             Cards = (LPCARDHEADER) GlobalLock(hCards);
  426.             Cards[iCard] = CurCardHead;
  427.             GlobalUnlock(hCards);
  428.         }
  429.         else
  430.             return(FALSE);
  431.     }
  432.     if (CurCard.lpObject)
  433.         PicDelete(&CurCard);
  434.     return(TRUE);
  435. }
  436. /*
  437.  * make card # iCard the current (editable and displayed) card
  438.  *
  439.  * copy the header into CurCardHead    (from global memory)
  440.  * copy the bitmap into CurCard        (from the file)
  441.  * copy the text into the Edit window    (from the file)
  442.  *
  443.  */
  444. void SetCurCard(
  445.     int iCard)
  446. {
  447.     LPCARDHEADER Cards;
  448.     /* Setting new card, remove undo possibility... */
  449.     DeleteUndoObject();
  450.     /* copy the header info */
  451.     Cards = (LPCARDHEADER) GlobalLock(hCards);
  452.     CurCardHead = Cards[iCard];
  453.     GlobalUnlock(hCards);
  454.     /* read in the bitmap and text stuff from the file */
  455.     if (ReadCurCardData(&CurCardHead, &CurCard, szText))
  456.     {
  457.         SetEditText(szText);
  458.         DoSetHostNames(CurCard.lpObject, CurCard.otObject);
  459.     }
  460. }
  461. /*
  462.  * Set edit windows text
  463.  */
  464. void SetEditText(
  465.     TCHAR *pText)
  466. {
  467.     fNeedToUpdateObject = TRUE;
  468.     SendMessage(hEditWnd, WM_SETTEXT, 0, (LONG)pText);
  469. }
  470. int iCardStartScroll;
  471. int fScrolling = FALSE;
  472. /*
  473.  * scroll through the stack of cards
  474.  *
  475.  * this gets called when using the scroll bar control
  476.  * or when a card is selected by clicking on on or
  477.  * when someone presses movement keys
  478.  *
  479.  * the user can hold down a key or a mouse button and cause repetitive
  480.  * scroll messages to be sent.  this action will update the headers but
  481.  * the edit window should only be updated when the key or mouse is
  482.  * released.  saving of data also follows this.
  483.  * see the fScrolling stuff below.
  484.  *
  485.  * also note: some routines send SB_THUMBPOS messages here (we won't
  486.  * get a SB_ENDSCROLL).  This case works because !fScrolling and
  487.  * iCardStartScroll gets set correctly.
  488.  */
  489. BOOL ScrollCards(
  490.     HWND hWindow,
  491.     int cmd,
  492.     int pos)
  493. {
  494.     int OldFirst = iFirstCard;    /* note where we started */
  495.     if (cCards < 2)
  496.         return TRUE;        /* only one card, can't scroll */
  497.     /* two states:
  498.         fScrolling  - we are receiving scroll messages (mouse is not released)
  499.         !fScrolling - enter scrolling state, save current position
  500.      */
  501.     if (!fScrolling)
  502.     {
  503.         iCardStartScroll = iFirstCard;
  504.         fScrolling = TRUE;
  505.     }
  506.     switch (cmd)
  507.     {
  508.         /* these cases always change the card and leave us in
  509.          * Scrolling state */
  510.         case SB_LINEUP:
  511.             iFirstCard--;        /* next card */
  512.             if (iFirstCard < 0)
  513.                 iFirstCard = cCards-1;
  514.             break;
  515.         case SB_LINEDOWN:
  516.             iFirstCard++;        /* prev card */
  517.             if (iFirstCard == cCards)
  518.                 iFirstCard = 0;
  519.             break;
  520.         case SB_PAGEUP:
  521.             if (cFSHeads == cCards)    /* already at top? */
  522.                 break;
  523.             iFirstCard -= cFSHeads;
  524.             if (iFirstCard < 0)
  525.                 iFirstCard += cCards; /* a negative number */
  526.             break;
  527.         case SB_PAGEDOWN:
  528.             if (cFSHeads == cCards)    /* already at bottom? */
  529.                 break;
  530.             iFirstCard += cFSHeads;
  531.             if (iFirstCard >= cCards)
  532.                 iFirstCard -= cCards;
  533.             break;
  534.             /* these cases may change the current card and
  535.              * always leave Scrolling state */
  536.         case SB_THUMBPOSITION:
  537.             iFirstCard = pos;        /* for single SB_THUMB calls */
  538.             /* fall through... */
  539.         case SB_ENDSCROLL:
  540.             fScrolling = FALSE;        /* leave scrolling mode */
  541.             if (iFirstCard != iCardStartScroll)
  542.             {
  543.                 if (SaveCurrentCard(iCardStartScroll))
  544.                 {
  545.                     SetCurCard(iFirstCard);
  546.                 }
  547.                 else
  548.                 {
  549.                     iFirstCard = iCardStartScroll;
  550.                     return FALSE;        /* save failed */
  551.                 }
  552.             }
  553.             break;
  554.     }
  555.     if (iFirstCard != OldFirst)        /* did the above change anything? */
  556.     {
  557.         HDC hDC;
  558.         hDC= GetDC( hWindow );
  559.         PaintNewHeaders( hDC );    /* yes, so redraw headers */
  560.         ReleaseDC( hWindow, hDC );
  561.     }
  562.     return TRUE;            /* sucessful scroll */
  563. }
  564. void DoCutCopy(int event)
  565. {
  566.     if (EditMode == I_TEXT)
  567.         SendMessage(hEditWnd, event == CUT ? WM_CUT : WM_COPY, 0, 0L);
  568.     else if (CurCard.lpObject)
  569.         PicCutCopy(&CurCard, (event == CUT));
  570. }
  571. void DoPaste(int event)
  572. {
  573.     if (EditMode == I_TEXT) {
  574.         if (!SendMessage(hEditWnd, WM_PASTE, 0, 0L))
  575.             IndexOkError(ECLIPEMPTYTEXT);
  576.     } else
  577.         PicPaste(&CurCard, (event == PASTE), 0);
  578. }
  579. /*
  580.  * update the text in the card headers
  581.  */
  582. void PaintNewHeaders( HDC hDC )
  583. {
  584.     int idCard;
  585.     LPCARDHEADER Cards;
  586.     LPCARDHEADER lpTCards;
  587.     int xCur;
  588.     int yCur;
  589.     int i;
  590.     RECT rect;
  591.     DWORD rgbOld;
  592.     DWORD rgbTextOld;
  593.     HFONT  hOldFont;
  594.     yCur = yFirstCard - (cScreenHeads - 1) * ySpacing;
  595.     xCur = xFirstCard + (cScreenHeads - 1) * (2 * CharFixWidth);
  596.     idCard = (iFirstCard + cScreenHeads-1) % cCards;
  597.     Cards = (LPCARDHEADER) GlobalLock(hCards);
  598.     lpTCards = &Cards[idCard];
  599.     hOldFont= SelectObject(hDC, hFont);  // use our selected font
  600.     /* for all cards with headers showing */
  601.     rgbOld = SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
  602.     rgbTextOld = SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
  603.     for (i = 0; i < cScreenHeads; i++)  /* cScreenHeads */
  604.     {
  605.         SetRect(&rect, xCur+1, yCur+1, xCur+CardWidth-1, yCur+ySpacing);
  606.         ExtTextOut(hDC,
  607.                    xCur+1, yCur+1+(ExtLeading/2),
  608.                    ETO_OPAQUE|ETO_CLIPPED,    // use background color as fill
  609.                    &rect,                     // clipping rect
  610.                    lpTCards->line,            // title
  611.                    lstrlen(lpTCards->line),   // length of title
  612.                    NULL);                     // interchar spacing
  613.         xCur -= (2*CharFixWidth);
  614.         yCur += ySpacing;
  615.         lpTCards--;
  616.         idCard--;
  617.         if (idCard < 0)
  618.         {
  619.             idCard = cCards - 1;
  620.             lpTCards = &Cards[idCard];
  621.         }
  622.     }
  623.     SetBkColor(hDC, rgbOld);
  624.     SetTextColor(hDC, rgbTextOld);
  625.     SelectObject(hDC, hOldFont);
  626.     GlobalUnlock(hCards);
  627. }