home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / ue312os2.zip / src / mswdisp.c < prev    next >
C/C++ Source or Header  |  1994-08-26  |  21KB  |  632 lines

  1. /* The routines in this file provide display support under
  2.    the Microsoft Windows environment on an IBM-PC or compatible
  3.    computer.
  4.  
  5.    Must be compiled with Borland C++ 2.0 or MSC 6.0 or later versions
  6.  
  7.    It should not be compiled if the WINDOW_MSWIN symbol is not set */
  8.  
  9. /* The functions in this module are mostly concerned with the mapping
  10.    between character cells and client coordinates. */
  11.  
  12. #include    "estruct.h"
  13. #include    "elang.h"
  14. #include    <stdio.h>
  15. #include    <time.h>
  16. #include    "eproto.h"
  17. #include    "edef.h"
  18.  
  19. #include    "mswin.h"
  20.  
  21. #if     COLOR
  22. /* palette table. It is initialized for the default colors, but can be
  23.    changed by the spal function */
  24. static COLORREF EmacsPalette [16] = {
  25.     0x00000000,     /* black */
  26.     0x00000080,     /* red */
  27.     0x00008000,     /* green */
  28.     0x0000FFFF,     /* yellow */
  29.     0x00800000,     /* blue */
  30.     0x00800080,     /* magenta */
  31.     0x00808000,     /* cyan */
  32.     0x00C0C0C0,     /* grey (light) */
  33.     0x00808080,     /* gray (dark) */
  34.     0x000000FF,     /* light red */
  35.     0x0000FF00,     /* light green */
  36.     0x0080FFFF,     /* light yellow*/
  37.     0x00FF0000,     /* light  blue */
  38.     0x00800080,     /* light magenta*/
  39.     0x00FFFF00,     /* light cyan */
  40.     0x00FFFFFF      /* white */
  41. };
  42. #endif
  43.  
  44. static HWND hCaretWnd = 0;          /* window where the caret belongs */
  45. static int  CaretVisible = 0;       /* the caret should be visible if not 0 */
  46. static int  CaretCol, CaretRow;     /* caret position */
  47. /* Text Metrics values (from the CellMetrics structure) are used as follows:
  48.  
  49.     -------------------------------- Client area upper boundary
  50.     |          ^
  51.     |       OffsetY
  52.     |          v
  53.     |          ^
  54.     |      HalfLeadingY
  55.     |          v
  56.     |       ---------- ----------
  57.     |<---->|          |          |  ^
  58.   OffsetX  |   cell   |   cell   |  |    . . .
  59.     |      |    0,0   |    1,0   | SizeY
  60.     |      |          |          |  |
  61.     |      |          |          |  v
  62.     |       ---------- ----------
  63.     |                         ^
  64.     |       <- SizeX->     LeadingY = 2 * HalfLeadingY
  65.     |                         v
  66.     |       ---------- ----------
  67.     |      |          |          |
  68.     |      |   cell   |   cell   |
  69.     |      |    0,1   |    1,1   |
  70.     |      |          |          |
  71.     |
  72.     |                ...
  73.     |
  74.    Client area left boundary
  75. */
  76.  
  77. /* BuildCellMetrics:   fills a CellMetrics structure from a font description */
  78. /* ================                                                          */
  79.  
  80. void FAR PASCAL BuildCellMetrics (CellMetrics *cm, HFONT hFont)
  81. {
  82.     HDC     hDC;
  83.     TEXTMETRIC Metrics;
  84.     HANDLE  hPrevFont;
  85.  
  86.     hDC = GetDC (hFrameWnd);
  87.     hPrevFont = SelectFont (hDC, hFont);
  88.     GetTextMetrics (hDC, &Metrics);
  89.     SelectObject (hDC, hPrevFont);
  90.     ReleaseDC (hFrameWnd, hDC);
  91.     cm->SizeX = Metrics.tmAveCharWidth;
  92.     if (cm->SizeX == 0) cm->SizeX = 1;  /* ATM gives 0 sometimes !!! */
  93.     cm->SizeY = Metrics.tmHeight;
  94.     if (cm->SizeY == 0) cm->SizeY = 1;
  95.     cm->HalfLeadingY = Metrics.tmExternalLeading / 2;
  96.     cm->OffsetX = cm->SizeX / 4;
  97.     if ((cm->OffsetY = (cm->SizeY / 8) - cm->HalfLeadingY) < 0) {
  98.     cm->OffsetY = 0;
  99.     /* for a reasonable upper boundary separation, we want
  100.        (SizeY / 8) <= OffsetY + HalfLeadingY */
  101.     }
  102.     cm->LeadingY = 2 * cm->HalfLeadingY;
  103.     cm->MLHeight = cm->SizeY + cm->LeadingY + (2 * cm->OffsetY) +
  104.                    GetSystemMetrics(SM_CYBORDER);
  105. } /* BuildCellMetrics */
  106.  
  107. /* InvalidateCells: marks character cells for repaint */
  108. /* ===============                                    */
  109.  
  110. void FAR PASCAL InvalidateCells (HWND hWnd, int leftcol, int toprow,
  111.                                  int rightcol, int bottomrow)
  112. {
  113.     RECT    Rect;           /* used for Cell and Client coordinates */
  114.     RECT    ClientRect;     /* MDI window client area */
  115.     POINT   MaxBottomRight; /* Cell coordinates of bottom right of MDI
  116.                    window */
  117.  
  118.     GetClientRect (hWnd, &ClientRect);
  119.     ClientToCell (hWnd, *(POINT *)&ClientRect.right, &MaxBottomRight);
  120.  
  121.     Rect.left = leftcol;
  122.     Rect.top = toprow;
  123.     CellToClient (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
  124.     if (leftcol == 0) Rect.left = 0;
  125.     else Rect.left -= EmacsCM.SizeX / 2;    /* see Rect.right below... */
  126.     if (toprow == 0) Rect.top = 0;
  127.  
  128.     Rect.right = rightcol + 1;
  129.     Rect.bottom = bottomrow + 1;
  130.     CellToClient (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
  131.     if (rightcol + 1 >= MaxBottomRight.x) Rect.right = ClientRect.right;
  132.     else Rect.right += EmacsCM.SizeX / 2;
  133.         /* this adjustment is done to avoid left-over pixels caused by
  134.            characters which display an overhang outside their cells
  135.            (for instance: w & W in Courier New). The corresponding
  136.            adjustment is done on Rect.left a few lines above */
  137.     if (bottomrow + 1 >= MaxBottomRight.y) Rect.bottom = ClientRect.bottom;
  138.  
  139.     InvalidateRect (hWnd, &Rect, FALSE);
  140.         /* the background does not need erasing since Paint will
  141.        completely fill the invalid rectangle */
  142. } /* InvalidateCells */
  143.  
  144. /* MinimumClientSize:  computes the minimum client area size */
  145. /* =================                                         */
  146.  
  147. void FAR PASCAL MinimumClientSize (HWND hWnd, int NCols, int NRows,
  148.                    int *Width, int *Height)
  149.  
  150. /* The values pointed by Height and Width are set to the smallest value
  151.    that allows displaying NRows rows and NCols columns. A NULL pointer
  152.    disables the computing of the associated value */
  153. {
  154.     if (Height) *Height = (2 * EmacsCM.OffsetY) +
  155.                           (NRows * (EmacsCM.SizeY + EmacsCM.LeadingY));
  156.     if (Width) *Width = (2 * EmacsCM.OffsetX) + (NCols * EmacsCM.SizeX);
  157. } /* MinimumClientSize */
  158.  
  159. /* DisplayableRows: returns the number of rows displayable in the client area */
  160. /* ===============                                                            */
  161.  
  162. int FAR PASCAL DisplayableRows (HWND hWnd, int Height, CellMetrics *cm)
  163.  
  164. /* Heigh is the hypothetic heigh of the client area. If this parameter
  165.    is 0, the real client area is measured. If it is negative, the
  166.    maximal client area (maximized child in a maximized frame) is used.
  167.    */
  168. {
  169.     RECT    Rect;
  170.     int     x;
  171.  
  172.     if (Height == 0) GetClientRect (hWnd, &Rect);
  173.     else {
  174.     if (Height > 0) {
  175.         Rect.bottom = Height;
  176.     }
  177.     else {
  178.         Rect.bottom = GetSystemMetrics (SM_CYFULLSCREEN) -
  179.                           (GetSystemMetrics (SM_CYMENU) + cm->MLHeight);
  180.     }
  181.     }
  182.     x = (Rect.bottom - (2 * cm->OffsetY)) / (cm->SizeY + cm->LeadingY);
  183.     if (x < 0) return 0;
  184.     return x;
  185. } /* DisplayableRows */
  186.  
  187. /* DisplayableColumns:  returns the number of columns displayable in the client area */
  188. /* ==================                                                                */
  189.  
  190. int FAR PASCAL DisplayableColumns (HWND hWnd, int Width, CellMetrics *cm)
  191.  
  192. /* Width is the hypothetic width of the client area. If this parameter
  193.    is 0, the real client area is measured. If it is negative, the
  194.    maximal client area (maximized child in a maximized frame) is used.
  195.    */
  196. {
  197.     RECT    Rect;
  198.     int     x;
  199.  
  200.     if (Width== 0) GetClientRect (hWnd, &Rect);
  201.     else {
  202.     if (Width > 0) {
  203.         Rect.right = Width;
  204.     }
  205.     else {
  206.         Rect.right = GetSystemMetrics (SM_CXFULLSCREEN);
  207.     }
  208.     }
  209.     x = (Rect.right - (2 * cm->OffsetX)) / cm->SizeX;
  210.     if (x < 0) return 0;
  211.     return x;
  212. } /* DisplayableColumns */
  213.  
  214. /* UpdateEmacsCaretPos: position the caret according to CaretCol/CaretRow */
  215. /* ===================                                                    */
  216.  
  217. static void PASCAL UpdateEmacsCaretPos (void)
  218. {
  219.     POINT   pt;
  220.  
  221.     pt.x = CaretCol;
  222.     pt.y = CaretRow;
  223.     CellToClient (hCaretWnd, pt, &pt);
  224. #if CARETSHAPE == 0
  225.     if (hCaretWnd != hFrameWnd) {
  226.     pt.y += EmacsCM.SizeY - (EmacsCM.SizeY / 4);
  227.     }
  228. #endif
  229.     SetCaretPos (pt.x, pt.y + EmacsCM.HalfLeadingY);
  230. } /* UpdateEmacsCaretPos */
  231.  
  232. /* EmacsCaret:  Creates or destroys the caret */
  233. /* ==========                                 */
  234.  
  235. void FAR PASCAL EmacsCaret (BOOL Show)
  236.  
  237. /* the Show parameter is TRUE if the caret should be created and FALSE
  238.    if it should be destroyed */
  239. {
  240.     if (Show) {
  241.         if (hCaretWnd == 0) return;
  242.     if (hFrameWnd == GetActiveWindow ()) {
  243.         if (!IsWindow (hCaretWnd)) {
  244.             /* this may happen in some transient cases when closing
  245.            down a screen */
  246.             hCaretWnd = 0;
  247.             return;
  248.         }
  249.             CreateCaret (hCaretWnd, NULL,
  250.              hCaretWnd == hFrameWnd ?
  251.                        GetSystemMetrics (SM_CXBORDER) :
  252. #if CARETSHAPE == 1
  253.                       EmacsCM.SizeX / 4,
  254. #else
  255.                       EmacsCM.SizeX,
  256. #endif
  257. #if CARETSHAPE == 0
  258.              hCaretWnd == hFrameWnd ?
  259.                        EmacsCM.SizeY :
  260.                                       EmacsCM.SizeY / 4);
  261. #else
  262.                          EmacsCM.SizeY);
  263. #endif
  264.             UpdateEmacsCaretPos ();
  265.             if (CaretVisible && !IsIconic (hCaretWnd)) ShowCaret (hCaretWnd);
  266.         }
  267.     }
  268.     else {
  269.     /* destroy the caret */
  270.     DestroyCaret ();
  271.     }
  272. } /* EmacsCaret */
  273.  
  274. /* MoveEmacsCaret:  updates the caret position */
  275. /* ==============                              */
  276.  
  277. void FAR PASCAL MoveEmacsCaret (HWND hWnd, int col, int row)
  278. {
  279.     CaretCol = col;
  280.     CaretRow = row;
  281.     if (hWnd != hCaretWnd) {
  282.     hCaretWnd = hWnd;
  283.         EmacsCaret (TRUE);
  284.     }
  285.     else {
  286.     hCaretWnd = hWnd;
  287.     UpdateEmacsCaretPos ();
  288.     }
  289. } /* MoveEmacsCaret */
  290.  
  291. /* ShowEmacsCaret:  shows or hides the caret used by emacs */
  292. /* ==============                                          */
  293.  
  294. void FAR PASCAL ShowEmacsCaret (BOOL Show)
  295.  
  296. /* this function is used to make the caret visible only when waiting for
  297.    user input */
  298. {
  299.     if (Show) {
  300.         if (!CaretVisible) {
  301.             if (!IsIconic (hCaretWnd)) ShowCaret (hCaretWnd);
  302.         }
  303.     ++CaretVisible;
  304.     }
  305.     else {
  306.     --CaretVisible;
  307.         if (!CaretVisible) {
  308.             HideCaret (NULL);
  309.         }
  310.     }
  311. } /* ShowEmacsCaret */
  312.  
  313. /* InMessageLine:   non-zero if caret currently in the message line */
  314. /* =============                                                    */
  315.  
  316. BOOL FAR PASCAL InMessageLine (void)
  317. {
  318.     return (hCaretWnd == hFrameWnd);
  319. } /* InMessageLine */
  320.  
  321. /* CellToClient:    converts character cell coordinates into client coordinates */
  322. /* ============                                                                 */
  323.  
  324. void FAR PASCAL CellToClient (HWND hWnd, POINT Cell, LPPOINT Client)
  325.  
  326. /* The resulting Client coordinates indicate the upper left pixel one
  327.    HalfLeadingY above the character cell */
  328. {
  329.     Client->x = (Cell.x * EmacsCM.SizeX) + EmacsCM.OffsetX;
  330.     if (hWnd == hFrameWnd ) {
  331.         RECT    Rect;
  332.  
  333.         GetClientRect (hWnd, &Rect);
  334.         Client->y = (Rect.bottom - EmacsCM.MLHeight) + EmacsCM.OffsetY +
  335.                     GetSystemMetrics (SM_CYBORDER);
  336.     }
  337.     else Client->y = (Cell.y * (EmacsCM.SizeY + EmacsCM.LeadingY)) +
  338.                      EmacsCM.OffsetY;
  339. } /* CellToClient */
  340.  
  341. /* ClientToCell:    converts client coordinates into character cell coordinates */
  342. /* ============                                                                 */
  343.  
  344. void FAR PASCAL ClientToCell (HWND hWnd, POINT Client, LPPOINT Cell)
  345.  
  346. /* The area associated with a Cell is the character cell itself, plus
  347.    the HalfLeadingY-high areas above and under the cell */
  348. {
  349.     int     MaxCol, MaxRow;
  350.     
  351.     if (hWnd == hFrameWnd) {    /* message line case */
  352.         MaxCol = MLSIZE - 1;
  353.     MaxRow = 0;
  354.     }
  355.     else {                      /* screen case */
  356.         register SCREEN *sp;
  357.  
  358.         sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
  359.         MaxCol = sp->s_ncol - 1;
  360.         MaxRow = sp->s_nrow - 1;
  361.     }
  362.     if ((Cell->x = (Client.x - EmacsCM.OffsetX) /
  363.                    EmacsCM.SizeX) < 0) Cell->x = 0;
  364.     else Cell->x = min (Cell->x, MaxCol);
  365.     if ((Cell->y = (Client.y - EmacsCM.OffsetY) /
  366.                    (EmacsCM.SizeY + EmacsCM.LeadingY)) < 0) Cell->y = 0;
  367.     else Cell->y = min (Cell->y, MaxRow);
  368. } /* ClientToCell */
  369.  
  370. /* GetMinMaxInfo:  processes the WM_GETMINMAXINFO message for a screen */
  371. /* =============                                                       */
  372.  
  373. void FAR PASCAL GetMinMaxInfo (HWND hWnd, LPPOINT rgpt)
  374. {
  375.     if (InternalRequest) return;    /* none of our business */
  376.  
  377.     if (notquiescent) {
  378.     /* forbid all size changes */
  379.     RECT    Rect;
  380.  
  381.     GetWindowRect (hWnd, &Rect);
  382.     rgpt[1].x = Rect.right - Rect.left;
  383.     rgpt[1].y = Rect.bottom - Rect.top;
  384.     rgpt[2] = *(POINT*)&Rect.left;
  385.     rgpt[3] = rgpt[1];
  386.     rgpt[4] = rgpt[1];
  387.     }
  388.     else {                  /* compute minimum tracking size */
  389.         int     X, Y;
  390.  
  391.         /* minimum displayed text width = 3 */
  392.         /* minimum displayed text  height = 10 */
  393.         MinimumClientSize (hWnd, term.t_margin, 2, &X, &Y);
  394.         rgpt[3].x = X + (2 * GetSystemMetrics (SM_CXFRAME)) +
  395.                     GetSystemMetrics (SM_CXVSCROLL);
  396.         rgpt[3].y = Y + GetSystemMetrics (SM_CYCAPTION) +
  397.                     (2 * GetSystemMetrics (SM_CYFRAME)) +
  398.                     GetSystemMetrics (SM_CYHSCROLL);
  399.     }
  400. } /* GetMinMaxInfo */
  401.  
  402. /* ScrReSize:    processes the WM_SIZE message */
  403. /* =========                                   */
  404.  
  405. BOOL FAR PASCAL ScrReSize (HWND hWnd, UINT wParam, WORD cx, WORD cy)
  406.  
  407. /* returns TRUE only if real resizing performed */
  408. {
  409.     BOOL    ChgWidth, ChgHeight;
  410.  
  411.     if ((wParam != SIZENORMAL) && (wParam != SIZEFULLSCREEN)) {
  412.         return FALSE;
  413.     }
  414.     ChgWidth = (cx != GetWindowWord (hWnd, GWW_SCRCX));
  415.     ChgHeight = (cy != GetWindowWord (hWnd, GWW_SCRCY));
  416.     if (!ChgWidth && !ChgHeight) return FALSE;
  417.  
  418.     SetWindowWord (hWnd, GWW_SCRCX, cx);
  419.     SetWindowWord (hWnd, GWW_SCRCY, cy);
  420.     if (!InternalRequest) {
  421.         SCREEN  *TopScreen;
  422.  
  423.         InternalRequest = TRUE;
  424.         TopScreen = first_screen;
  425.     select_screen ((SCREEN *)GetWindowLong (hWnd, GWL_SCRPTR), FALSE);
  426.     if (ChgWidth) {
  427.             newwidth (TRUE, DisplayableColumns (hWnd, cx, &EmacsCM));
  428.         }
  429.     if (ChgHeight) {
  430.         newsize (TRUE, DisplayableRows (hWnd, cy, &EmacsCM));
  431.     }
  432.     select_screen (TopScreen, FALSE);
  433.     update (FALSE);
  434.     InternalRequest = FALSE;
  435.     }
  436.     return TRUE;
  437. } /* ScrReSize */
  438.  
  439. /* ScrPaint:   processes WM_PAINT messages for emacs screens */
  440. /* ========                                                  */
  441.  
  442. void FAR PASCAL ScrPaint (HWND hWnd)
  443. {
  444.     SCREEN  *sp;
  445.     PAINTSTRUCT ps;
  446.     HANDLE  hPrevFont;
  447.     RECT    Rect;
  448.     int     Row, BottomRow;
  449.     int     Col;
  450.     int     Length;
  451.  
  452.     /* note that if the WM_PAINT is not passed through the message loop
  453.        (as would hapen with use of UpdateWindow), it is possible to
  454.        attempt repainting while defferupdate is TRUE which means update
  455.        has been deffered */
  456.  
  457.     BeginPaint (hWnd, &ps);
  458.     sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
  459.  
  460.     /*-calculate the row/col loop control variables and normalize the
  461.        coordinates of the first line's rectangle into Rect */
  462.     CopyRect (&Rect, &ps.rcPaint);
  463.     ClientToCell (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
  464.     Col = Rect.left;
  465.     Row = Rect.top;
  466.     --Rect.right;       /* in rectangle conventions, */
  467.     --Rect.bottom;      /* the lower/right border is excluded */
  468.     ClientToCell (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
  469.     ++Rect.right;
  470.     Length = Rect.right - Col;
  471.     BottomRow = Rect.bottom;
  472.     Rect.bottom = Rect.top + 1;
  473.     CellToClient (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
  474.     CellToClient (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
  475.     /* Rect now contains a bounding rectangle for the 1st row. This will
  476.        be used to paint the row's background and will be moved down one
  477.        row's height at each iteration of the painting loop */
  478.  
  479.     /*-loop from top to bottom, writing one line at a time */
  480.     hPrevFont = SelectFont (ps.hdc, hEmacsFont);
  481.     do {
  482.     VIDEO   *vp;
  483.  
  484.     vp = sp->s_physical[Row];
  485.     if (!(vp->v_flag & VFNEW)) {    /* valid contents */
  486.         COLORREF FColor, BColor;
  487.             POINT   RevPt;
  488.             int     i;
  489.         RECT    BoundingRect;   /* clipping rectangle */
  490.             int     Boundary[4];    /* horizontal paint boundaries:
  491.                                        [0] to [1] is painted in normal video,
  492.                                        [1] to [2] is painted in reverse video,
  493.                                        [2] to [3] is painted in normal video */
  494.  
  495.         CopyRect (&BoundingRect, &ps.rcPaint);
  496.         if (Row > 0) BoundingRect.top = Rect.top;
  497.         if (Row < BottomRow) BoundingRect.bottom = Rect.bottom;
  498.  
  499.         Boundary[0] = BoundingRect.left;
  500.         Boundary[3] = BoundingRect.right;
  501.         if (vp->v_right) {  /* there is a reverse video area */
  502.             if (vp->v_left <= 0) {
  503.                 /* special case: flush to the border */
  504.                     Boundary[1] = 0;
  505.                 }
  506.                 else {
  507.                     RevPt.y = 0;
  508.                 RevPt.x = vp->v_left;
  509.                 CellToClient (hWnd, RevPt, &RevPt);
  510.                 Boundary[1] = max(BoundingRect.left,RevPt.x);
  511.             }
  512.             RevPt.y = 0;
  513.             RevPt.x = vp->v_right;
  514.             CellToClient (hWnd, RevPt, &RevPt);
  515.             Boundary[2] = min(BoundingRect.right,RevPt.x);
  516.         }
  517.         else {  /* only normal video */
  518.             Boundary[1] = Boundary [0];
  519.             Boundary[2] = Boundary [0];
  520.         }
  521.  
  522.         /*-Paint the row in up to three parts, between the computed
  523.                boundaries (reverse video is applied between [1] and [2] */
  524.         for (i = 0; i < 3; i++) if (Boundary[i] < Boundary [i+1]) {
  525.             BoundingRect.left = Boundary[i];
  526.             BoundingRect.right = Boundary[i+1];
  527. #if     COLOR
  528.             if (ColorDisplay) {
  529.                 FColor = EmacsPalette[i == 1 ?
  530.                                           vp->v_bcolor : vp->v_fcolor];
  531.                 BColor = EmacsPalette[i == 1 ?
  532.                                           vp->v_fcolor : vp->v_bcolor];
  533.                 }
  534.                 else {  /* monochrome display */
  535. #else
  536.                 {
  537. #endif
  538.             if (i == 1) {   /* "reverse" video */
  539.                 FColor = GetSysColor (COLOR_HIGHLIGHTTEXT);
  540.                 BColor = GetSysColor (COLOR_HIGHLIGHT);
  541.             }
  542.             else {
  543.                 FColor = GetSysColor (COLOR_WINDOWTEXT);
  544.                 BColor = GetSysColor (COLOR_WINDOW);
  545.             }
  546.             }
  547.             SetBkColor (ps.hdc, BColor);
  548.             SetTextColor (ps.hdc, FColor);
  549.             ExtTextOut (ps.hdc,
  550.                     Rect.left, Rect.top + EmacsCM.HalfLeadingY,
  551.                 ETO_OPAQUE | ETO_CLIPPED, &BoundingRect,
  552.                 &vp->v_text[Col],
  553.                 Length, NULL);
  554.         }
  555.     }
  556.     Rect.top = Rect.bottom;
  557.     Rect.bottom += EmacsCM.SizeY + EmacsCM.LeadingY;
  558.     } while (++Row <= BottomRow);
  559.  
  560.     SelectObject (ps.hdc, hPrevFont);
  561. EndScrPaint:
  562.     EndPaint (hWnd, &ps);
  563. } /* ScrPaint */
  564.  
  565. /* MLPaint: processes WM_PAINT messages for the Message Line */
  566. /* =======                                                   */
  567.  
  568. void FAR PASCAL MLPaint (void)
  569. {
  570.     PAINTSTRUCT ps;
  571.     HANDLE  hPrev, hPen;
  572.     RECT    Rect;
  573.     POINT   Client;
  574.     
  575.     BeginPaint (hFrameWnd, &ps);
  576.     
  577.     /*-draw the text portion targetted for repaint */
  578.     hPrev = SelectFont (ps.hdc, hEmacsFont);
  579.     ClientToCell (hFrameWnd, *(POINT*)&ps.rcPaint.left, (POINT*)&Rect.left);
  580.     ClientToCell (hFrameWnd, *(POINT*)&ps.rcPaint.right, (POINT*)&Rect.right);
  581.     CellToClient (hFrameWnd, *(POINT*)&Rect.left, &Client);
  582.     ExtTextOut (ps.hdc,
  583.                 Client.x, Client.y + EmacsCM.HalfLeadingY,
  584.                 ETO_OPAQUE, &ps.rcPaint,
  585.                 &MLBuf[Rect.left],
  586.                 Rect.right - Rect.left + 1, NULL);
  587.     SelectObject (ps.hdc, hPrev);
  588.     
  589.     /*-draw the separation line at top of message line */
  590.     hPen = CreatePen (PS_SOLID, GetSystemMetrics (SM_CYBORDER), RGB(0,0,0));
  591.     hPrev = SelectObject (ps.hdc, hPen);
  592.     GetClientRect (hFrameWnd, &Rect);
  593.     Rect.top = Rect.bottom - EmacsCM.MLHeight;
  594. #if WINDOW_MSWIN32
  595.     MoveToEx (ps.hdc, 0, Rect.top, NULL);
  596. #else
  597.     MoveTo (ps.hdc, 0, Rect.top);
  598. #endif
  599.     LineTo (ps.hdc, Rect.right, Rect.top);
  600.     SelectObject (ps.hdc, hPrev);
  601.     DeleteObject (hPen);
  602.  
  603.     EndPaint (hFrameWnd, &ps);
  604. } /* MLPaint */
  605.  
  606. /* spal:    set palette from $palette string */
  607. /* ====                                      */
  608.  
  609. PASCAL spal (char *pstr)
  610. {
  611. #if     COLOR
  612.     int     pal;    /* current palette position */
  613.  
  614.     for (pal = 0; pal < 16; pal++) {
  615.     DWORD   clr;    /* current color value */
  616.     int     i;
  617.         unsigned char n;
  618.  
  619.     if (*pstr== 0) break;
  620.     clr = 0;
  621.     for (i = 0; i < 3; i++) if (*pstr) {
  622.         n = *pstr++ - '0';
  623.         if (n >= 8) n = 255;
  624.         else n *= 32;
  625.         clr |= (DWORD)n << (i * 8);
  626.     }
  627.     EmacsPalette[pal] = clr;
  628.     }
  629. #endif
  630.     return 0;
  631. } /* spal */
  632.