ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

paint.c
Go to the documentation of this file.
00001 /*
00002  * RichEdit - painting functions
00003  *
00004  * Copyright 2004 by Krzysztof Foltman
00005  * Copyright 2005 by Phil Krylov
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00020  */
00021 
00022 #include "editor.h"
00023 
00024 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
00025 
00026 static void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph);
00027 
00028 void ME_PaintContent(ME_TextEditor *editor, HDC hDC, const RECT *rcUpdate)
00029 {
00030   ME_DisplayItem *item;
00031   ME_Context c;
00032   int ys, ye;
00033   HRGN oldRgn;
00034 
00035   oldRgn = CreateRectRgn(0, 0, 0, 0);
00036   if (!GetClipRgn(hDC, oldRgn))
00037   {
00038     DeleteObject(oldRgn);
00039     oldRgn = NULL;
00040   }
00041   IntersectClipRect(hDC, rcUpdate->left, rcUpdate->top,
00042                      rcUpdate->right, rcUpdate->bottom);
00043 
00044   editor->nSequence++;
00045   ME_InitContext(&c, editor, hDC);
00046   SetBkMode(hDC, TRANSPARENT);
00047   ME_MoveCaret(editor);
00048   item = editor->pBuffer->pFirst->next;
00049   /* This context point is an offset for the paragraph positions stored
00050    * during wrapping. It shouldn't be modified during painting. */
00051   c.pt.x = c.rcView.left - editor->horz_si.nPos;
00052   c.pt.y = c.rcView.top - editor->vert_si.nPos;
00053   while(item != editor->pBuffer->pLast)
00054   {
00055     assert(item->type == diParagraph);
00056 
00057     ys = c.pt.y + item->member.para.pt.y;
00058     if (item->member.para.pCell
00059         != item->member.para.next_para->member.para.pCell)
00060     {
00061       ME_Cell *cell = NULL;
00062       cell = &ME_FindItemBack(item->member.para.next_para, diCell)->member.cell;
00063       ye = c.pt.y + cell->pt.y + cell->nHeight;
00064     } else {
00065       ye = ys + item->member.para.nHeight;
00066     }
00067     if (item->member.para.pCell && !(item->member.para.nFlags & MEPF_ROWEND) &&
00068         item->member.para.pCell != item->member.para.prev_para->member.para.pCell)
00069     {
00070       /* the border shifts the text down */
00071       ys -= item->member.para.pCell->member.cell.yTextOffset;
00072     }
00073 
00074     /* Draw the paragraph if any of the paragraph is in the update region. */
00075     if (ys < rcUpdate->bottom && ye > rcUpdate->top)
00076       ME_DrawParagraph(&c, item);
00077     item = item->member.para.next_para;
00078   }
00079   if (c.pt.y + editor->nTotalLength < c.rcView.bottom)
00080   {
00081     /* Fill space after the end of the text. */
00082     RECT rc;
00083     rc.top = c.pt.y + editor->nTotalLength;
00084     rc.left = c.rcView.left;
00085     rc.bottom = c.rcView.bottom;
00086     rc.right = c.rcView.right;
00087 
00088     IntersectRect(&rc, &rc, rcUpdate);
00089 
00090     if (!IsRectEmpty(&rc))
00091       FillRect(hDC, &rc, c.editor->hbrBackground);
00092   }
00093   if (editor->nTotalLength != editor->nLastTotalLength ||
00094       editor->nTotalWidth != editor->nLastTotalWidth)
00095     ME_SendRequestResize(editor, FALSE);
00096   editor->nLastTotalLength = editor->nTotalLength;
00097   editor->nLastTotalWidth = editor->nTotalWidth;
00098 
00099   SelectClipRgn(hDC, oldRgn);
00100   if (oldRgn)
00101     DeleteObject(oldRgn);
00102 
00103   c.hDC = NULL;
00104   ME_DestroyContext(&c);
00105 }
00106 
00107 void ME_Repaint(ME_TextEditor *editor)
00108 {
00109   if (ME_WrapMarkedParagraphs(editor))
00110   {
00111     ME_UpdateScrollBar(editor);
00112     FIXME("ME_Repaint had to call ME_WrapMarkedParagraphs\n");
00113   }
00114   ITextHost_TxViewChange(editor->texthost, TRUE);
00115 }
00116 
00117 void ME_UpdateRepaint(ME_TextEditor *editor, BOOL update_now)
00118 {
00119   /* Should be called whenever the contents of the control have changed */
00120   BOOL wrappedParagraphs;
00121 
00122   wrappedParagraphs = ME_WrapMarkedParagraphs(editor);
00123   if (wrappedParagraphs)
00124     ME_UpdateScrollBar(editor);
00125 
00126   /* Ensure that the cursor is visible */
00127   ME_EnsureVisible(editor, &editor->pCursors[0]);
00128 
00129   ITextHost_TxViewChange(editor->texthost, update_now);
00130 
00131   ME_SendSelChange(editor);
00132 
00133   /* send EN_CHANGE if the event mask asks for it */
00134   if(editor->nEventMask & ENM_CHANGE)
00135   {
00136     editor->nEventMask &= ~ENM_CHANGE;
00137     ME_SendOldNotify(editor, EN_CHANGE);
00138     editor->nEventMask |= ENM_CHANGE;
00139   }
00140 }
00141 
00142 void
00143 ME_RewrapRepaint(ME_TextEditor *editor)
00144 {
00145   /* RewrapRepaint should be called whenever the control has changed in
00146    * looks, but not content. Like resizing. */
00147   
00148   ME_MarkAllForWrapping(editor);
00149   ME_WrapMarkedParagraphs(editor);
00150   ME_UpdateScrollBar(editor);
00151   ME_Repaint(editor);
00152 }
00153 
00154 int ME_twips2pointsX(const ME_Context *c, int x)
00155 {
00156   if (c->editor->nZoomNumerator == 0)
00157     return x * c->dpi.cx / 1440;
00158   else
00159     return x * c->dpi.cx * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator;
00160 }
00161 
00162 int ME_twips2pointsY(const ME_Context *c, int y)
00163 {
00164   if (c->editor->nZoomNumerator == 0)
00165     return y * c->dpi.cy / 1440;
00166   else
00167     return y * c->dpi.cy * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator;
00168 }
00169 
00170 static void ME_HighlightSpace(ME_Context *c, int x, int y, LPCWSTR szText,
00171                               int nChars, ME_Style *s, int width,
00172                               int nSelFrom, int nSelTo, int ymin, int cy)
00173 {
00174   HDC hDC = c->hDC;
00175   HGDIOBJ hOldFont = NULL;
00176   SIZE sz;
00177   int selWidth;
00178   /* Only highlight if there is a selection in the run and when
00179    * EM_HIDESELECTION is not being used to hide the selection. */
00180   if (nSelFrom >= nChars || nSelTo < 0 || nSelFrom >= nSelTo
00181       || c->editor->bHideSelection)
00182     return;
00183   hOldFont = ME_SelectStyleFont(c, s);
00184   if (width <= 0)
00185   {
00186     GetTextExtentPoint32W(hDC, szText, nChars, &sz);
00187     width = sz.cx;
00188   }
00189   if (nSelFrom < 0) nSelFrom = 0;
00190   if (nSelTo > nChars) nSelTo = nChars;
00191   GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
00192   x += sz.cx;
00193   if (nSelTo != nChars)
00194   {
00195     GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
00196     selWidth = sz.cx;
00197   } else {
00198     selWidth = width - sz.cx;
00199   }
00200   ME_UnselectStyleFont(c, s, hOldFont);
00201 
00202   if (c->editor->bEmulateVersion10)
00203     PatBlt(hDC, x, ymin, selWidth, cy, DSTINVERT);
00204   else
00205   {
00206     RECT rect;
00207     HBRUSH hBrush;
00208     rect.left = x;
00209     rect.top = ymin;
00210     rect.right = x + selWidth;
00211     rect.bottom = ymin + cy;
00212     hBrush = CreateSolidBrush(ITextHost_TxGetSysColor(c->editor->texthost,
00213                                                       COLOR_HIGHLIGHT));
00214     FillRect(hDC, &rect, hBrush);
00215     DeleteObject(hBrush);
00216   }
00217 }
00218 
00219 static void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText,
00220                                  int nChars, ME_Style *s, int width,
00221                                  int nSelFrom, int nSelTo, int ymin, int cy)
00222 {
00223   HDC hDC = c->hDC;
00224   HGDIOBJ hOldFont;
00225   COLORREF rgbOld;
00226   int yOffset = 0, yTwipsOffset = 0;
00227   SIZE          sz;
00228   COLORREF      rgb;
00229   HPEN hPen = NULL, hOldPen = NULL;
00230   BOOL bHighlightedText = (nSelFrom < nChars && nSelTo >= 0
00231                            && nSelFrom < nSelTo && !c->editor->bHideSelection);
00232   int xSelStart = x, xSelEnd = x;
00233   int *lpDx = NULL;
00234   /* lpDx is only needed for tabs to make sure the underline done automatically
00235    * by the font extends to the end of the tab. Tabs are always stored as
00236    * a single character run, so we can handle this case separately, since
00237    * otherwise lpDx would need to specify the lengths of each character. */
00238   if (width && nChars == 1)
00239       lpDx = &width; /* Make sure underline for tab extends across tab space */
00240 
00241   hOldFont = ME_SelectStyleFont(c, s);
00242   if ((s->fmt.dwMask & s->fmt.dwEffects) & CFM_OFFSET) {
00243     yTwipsOffset = s->fmt.yOffset;
00244   }
00245   if ((s->fmt.dwMask & s->fmt.dwEffects) & (CFM_SUPERSCRIPT | CFM_SUBSCRIPT)) {
00246     if (s->fmt.dwEffects & CFE_SUPERSCRIPT) yTwipsOffset = s->fmt.yHeight/3;
00247     if (s->fmt.dwEffects & CFE_SUBSCRIPT) yTwipsOffset = -s->fmt.yHeight/12;
00248   }
00249   if (yTwipsOffset)
00250     yOffset = ME_twips2pointsY(c, yTwipsOffset);
00251 
00252   if ((s->fmt.dwMask & CFM_LINK) && (s->fmt.dwEffects & CFE_LINK))
00253     rgb = RGB(0,0,255);
00254   else if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR))
00255     rgb = ITextHost_TxGetSysColor(c->editor->texthost, COLOR_WINDOWTEXT);
00256   else
00257     rgb = s->fmt.crTextColor;
00258 
00259   /* Determine the area that is selected in the run. */
00260   GetTextExtentPoint32W(hDC, szText, nChars, &sz);
00261   /* Treat width as an optional parameter.  We can get the width from the
00262    * text extent of the string if it isn't specified. */
00263   if (!width) width = sz.cx;
00264   if (bHighlightedText)
00265   {
00266     if (nSelFrom <= 0)
00267     {
00268       nSelFrom = 0;
00269     }
00270     else
00271     {
00272       GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
00273       xSelStart = x + sz.cx;
00274     }
00275     if (nSelTo >= nChars)
00276     {
00277       nSelTo = nChars;
00278       xSelEnd = x + width;
00279     }
00280     else
00281     {
00282       GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
00283       xSelEnd = xSelStart + sz.cx;
00284     }
00285   }
00286 
00287   /* Choose the pen type for underlining the text. */
00288   if (s->fmt.dwMask & CFM_UNDERLINETYPE)
00289   {
00290     switch (s->fmt.bUnderlineType)
00291     {
00292     case CFU_UNDERLINE:
00293     case CFU_UNDERLINEWORD: /* native seems to map it to simple underline (MSDN) */
00294     case CFU_UNDERLINEDOUBLE: /* native seems to map it to simple underline (MSDN) */
00295       hPen = CreatePen(PS_SOLID, 1, rgb);
00296       break;
00297     case CFU_UNDERLINEDOTTED:
00298       hPen = CreatePen(PS_DOT, 1, rgb);
00299       break;
00300     default:
00301       FIXME("Unknown underline type (%u)\n", s->fmt.bUnderlineType);
00302       /* fall through */
00303     case CFU_CF1UNDERLINE: /* this type is supported in the font, do nothing */
00304     case CFU_UNDERLINENONE:
00305       hPen = NULL;
00306       break;
00307     }
00308     if (hPen)
00309     {
00310       hOldPen = SelectObject(hDC, hPen);
00311     }
00312   }
00313 
00314   rgbOld = SetTextColor(hDC, rgb);
00315   if (bHighlightedText && !c->editor->bEmulateVersion10)
00316   {
00317     COLORREF rgbBackOld;
00318     RECT dim;
00319     /* FIXME: should use textmetrics info for Descent info */
00320     if (hPen)
00321       MoveToEx(hDC, x, y - yOffset + 1, NULL);
00322     if (xSelStart > x)
00323     {
00324       ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nSelFrom, NULL);
00325       if (hPen)
00326         LineTo(hDC, xSelStart, y - yOffset + 1);
00327     }
00328     dim.top = ymin;
00329     dim.bottom = ymin + cy;
00330     dim.left = xSelStart;
00331     dim.right = xSelEnd;
00332     SetTextColor(hDC, ITextHost_TxGetSysColor(c->editor->texthost,
00333                                               COLOR_HIGHLIGHTTEXT));
00334     rgbBackOld = SetBkColor(hDC, ITextHost_TxGetSysColor(c->editor->texthost,
00335                                                          COLOR_HIGHLIGHT));
00336     ExtTextOutW(hDC, xSelStart, y-yOffset, ETO_OPAQUE, &dim,
00337                 szText+nSelFrom, nSelTo-nSelFrom, lpDx);
00338     if (hPen)
00339       LineTo(hDC, xSelEnd, y - yOffset + 1);
00340     SetBkColor(hDC, rgbBackOld);
00341     if (xSelEnd < x + width)
00342     {
00343       SetTextColor(hDC, rgb);
00344       ExtTextOutW(hDC, xSelEnd, y-yOffset, 0, NULL, szText+nSelTo,
00345                   nChars-nSelTo, NULL);
00346       if (hPen)
00347         LineTo(hDC, x + width, y - yOffset + 1);
00348     }
00349   }
00350   else
00351   {
00352     ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nChars, lpDx);
00353 
00354     /* FIXME: should use textmetrics info for Descent info */
00355     if (hPen)
00356     {
00357       MoveToEx(hDC, x, y - yOffset + 1, NULL);
00358       LineTo(hDC, x + width, y - yOffset + 1);
00359     }
00360 
00361     if (bHighlightedText) /* v1.0 inverts the selection */
00362     {
00363       PatBlt(hDC, xSelStart, ymin, xSelEnd-xSelStart, cy, DSTINVERT);
00364     }
00365   }
00366 
00367   if (hPen)
00368   {
00369     SelectObject(hDC, hOldPen);
00370     DeleteObject(hPen);
00371   }
00372   SetTextColor(hDC, rgbOld);
00373   ME_UnselectStyleFont(c, s, hOldFont);
00374 }
00375 
00376 static void ME_DebugWrite(HDC hDC, const POINT *pt, LPCWSTR szText) {
00377   int align = SetTextAlign(hDC, TA_LEFT|TA_TOP);
00378   HGDIOBJ hFont = SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
00379   COLORREF color = SetTextColor(hDC, RGB(128,128,128));
00380   TextOutW(hDC, pt->x, pt->y, szText, lstrlenW(szText));
00381   SelectObject(hDC, hFont);
00382   SetTextAlign(hDC, align);
00383   SetTextColor(hDC, color);
00384 }
00385 
00386 static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Paragraph *para) 
00387 {
00388   ME_Run *run = &rundi->member.run;
00389   ME_DisplayItem *start;
00390   int runofs = run->nCharOfs+para->nCharOfs;
00391   int nSelFrom, nSelTo;
00392   const WCHAR wszSpace[] = {' ', 0};
00393   
00394   if (run->nFlags & MERF_HIDDEN)
00395     return;
00396 
00397   start = ME_FindItemBack(rundi, diStartRow);
00398   ME_GetSelectionOfs(c->editor, &nSelFrom, &nSelTo);
00399 
00400   /* Draw selected end-of-paragraph mark */
00401   if (run->nFlags & MERF_ENDPARA)
00402   {
00403     if (runofs >= nSelFrom && runofs < nSelTo)
00404     {
00405       ME_HighlightSpace(c, x, y, wszSpace, 1, run->style, 0, 0, 1,
00406                         c->pt.y + para->pt.y + start->member.row.pt.y,
00407                         start->member.row.nHeight);
00408     }
00409     return;
00410   }
00411 
00412   if (run->nFlags & (MERF_TAB | MERF_ENDCELL))
00413   {
00414     /* wszSpace is used instead of the tab character because otherwise
00415      * an unwanted symbol can be inserted instead. */
00416     ME_DrawTextWithStyle(c, x, y, wszSpace, 1, run->style, run->nWidth,
00417                          nSelFrom-runofs, nSelTo-runofs,
00418                          c->pt.y + para->pt.y + start->member.row.pt.y,
00419                          start->member.row.nHeight);
00420     return;
00421   }
00422 
00423   if (run->nFlags & MERF_GRAPHICS)
00424     ME_DrawOLE(c, x, y, run, para, (runofs >= nSelFrom) && (runofs < nSelTo));
00425   else
00426   {
00427     if (c->editor->cPasswordMask)
00428     {
00429       ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask, run->strText->nLen);
00430       ME_DrawTextWithStyle(c, x, y,
00431         szMasked->szData, szMasked->nLen, run->style, run->nWidth,
00432         nSelFrom-runofs,nSelTo-runofs,
00433         c->pt.y + para->pt.y + start->member.row.pt.y,
00434         start->member.row.nHeight);
00435       ME_DestroyString(szMasked);
00436     }
00437     else
00438       ME_DrawTextWithStyle(c, x, y,
00439         run->strText->szData, run->strText->nLen, run->style, run->nWidth,
00440         nSelFrom-runofs,nSelTo-runofs,
00441         c->pt.y + para->pt.y + start->member.row.pt.y,
00442         start->member.row.nHeight);
00443   }
00444 }
00445 
00446 /* The documented widths are in points (72 dpi), but converting them to
00447  * 96 dpi (standard display resolution) avoids dealing with fractions. */
00448 static const struct {unsigned width : 8, pen_style : 4, dble : 1;} border_details[] = {
00449   /* none */            {0, PS_SOLID, FALSE},
00450   /* 3/4 */             {1, PS_SOLID, FALSE},
00451   /* 1 1/2 */           {2, PS_SOLID, FALSE},
00452   /* 2 1/4 */           {3, PS_SOLID, FALSE},
00453   /* 3 */               {4, PS_SOLID, FALSE},
00454   /* 4 1/2 */           {6, PS_SOLID, FALSE},
00455   /* 6 */               {8, PS_SOLID, FALSE},
00456   /* 3/4 double */      {1, PS_SOLID, TRUE},
00457   /* 1 1/2 double */    {2, PS_SOLID, TRUE},
00458   /* 2 1/4 double */    {3, PS_SOLID, TRUE},
00459   /* 3/4 gray */        {1, PS_DOT /* FIXME */, FALSE},
00460   /* 1 1/2 dashed */    {2, PS_DASH, FALSE},
00461 };
00462 
00463 static const COLORREF pen_colors[16] = {
00464   /* Black */           RGB(0x00, 0x00, 0x00),  /* Blue */            RGB(0x00, 0x00, 0xFF),
00465   /* Cyan */            RGB(0x00, 0xFF, 0xFF),  /* Green */           RGB(0x00, 0xFF, 0x00),
00466   /* Magenta */         RGB(0xFF, 0x00, 0xFF),  /* Red */             RGB(0xFF, 0x00, 0x00),
00467   /* Yellow */          RGB(0xFF, 0xFF, 0x00),  /* White */           RGB(0xFF, 0xFF, 0xFF),
00468   /* Dark blue */       RGB(0x00, 0x00, 0x80),  /* Dark cyan */       RGB(0x00, 0x80, 0x80),
00469   /* Dark green */      RGB(0x00, 0x80, 0x80),  /* Dark magenta */    RGB(0x80, 0x00, 0x80),
00470   /* Dark red */        RGB(0x80, 0x00, 0x00),  /* Dark yellow */     RGB(0x80, 0x80, 0x00),
00471   /* Dark gray */       RGB(0x80, 0x80, 0x80),  /* Light gray */      RGB(0xc0, 0xc0, 0xc0),
00472 };
00473 
00474 static int ME_GetBorderPenWidth(const ME_Context* c, int idx)
00475 {
00476   int width = border_details[idx].width;
00477 
00478   if (c->dpi.cx != 96)
00479     width = MulDiv(width, c->dpi.cx, 96);
00480 
00481   if (c->editor->nZoomNumerator != 0)
00482     width = MulDiv(width, c->editor->nZoomNumerator, c->editor->nZoomDenominator);
00483 
00484   return width;
00485 }
00486 
00487 int ME_GetParaBorderWidth(const ME_Context* c, int flags)
00488 {
00489   int idx = (flags >> 8) & 0xF;
00490   int width;
00491 
00492   if (idx >= sizeof(border_details) / sizeof(border_details[0]))
00493   {
00494       FIXME("Unsupported border value %d\n", idx);
00495       return 0;
00496   }
00497   width = ME_GetBorderPenWidth(c, idx);
00498   if (border_details[idx].dble) width = width * 2 + 1;
00499   return width;
00500 }
00501 
00502 static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT* bounds)
00503 {
00504   int           idx, border_width, top_border, bottom_border;
00505   RECT          rc;
00506   BOOL          hasParaBorder;
00507 
00508   SetRectEmpty(bounds);
00509   if (!(para->pFmt->dwMask & (PFM_BORDER | PFM_SPACEBEFORE | PFM_SPACEAFTER))) return;
00510 
00511   border_width = top_border = bottom_border = 0;
00512   idx = (para->pFmt->wBorders >> 8) & 0xF;
00513   hasParaBorder = (!(c->editor->bEmulateVersion10 &&
00514                      para->pFmt->dwMask & PFM_TABLE &&
00515                      para->pFmt->wEffects & PFE_TABLE) &&
00516                    (para->pFmt->dwMask & PFM_BORDER) &&
00517                     idx != 0 &&
00518                     (para->pFmt->wBorders & 0xF));
00519   if (hasParaBorder)
00520   {
00521     /* FIXME: wBorders is not stored as MSDN says in v1.0 - 4.1 of richedit
00522      * controls. It actually stores the paragraph or row border style. Although
00523      * the value isn't used for drawing, it is used for streaming out rich text.
00524      *
00525      * wBorders stores the border style for each side (top, left, bottom, right)
00526      * using nibble (4 bits) to store each border style.  The rich text format
00527      * control words, and their associated value are the following:
00528      *   \brdrdash       0
00529      *   \brdrdashsm     1
00530      *   \brdrdb         2
00531      *   \brdrdot        3
00532      *   \brdrhair       4
00533      *   \brdrs          5
00534      *   \brdrth         6
00535      *   \brdrtriple     7
00536      *
00537      * The order of the sides stored actually differs from v1.0 to 3.0 and v4.1.
00538      * The mask corresponding to each side for the version are the following:
00539      *     mask       v1.0-3.0    v4.1
00540      *     0x000F     top         left
00541      *     0x00F0     left        top
00542      *     0x0F00     bottom      right
00543      *     0xF000     right       bottom
00544      */
00545     if (para->pFmt->wBorders & 0x00B0)
00546       FIXME("Unsupported border flags %x\n", para->pFmt->wBorders);
00547     border_width = ME_GetParaBorderWidth(c, para->pFmt->wBorders);
00548     if (para->pFmt->wBorders & 4)       top_border = border_width;
00549     if (para->pFmt->wBorders & 8)       bottom_border = border_width;
00550   }
00551 
00552   if (para->pFmt->dwMask & PFM_SPACEBEFORE)
00553   {
00554     rc.left = c->rcView.left;
00555     rc.right = c->rcView.right;
00556     rc.top = y;
00557     bounds->top = ME_twips2pointsY(c, para->pFmt->dySpaceBefore);
00558     rc.bottom = y + bounds->top + top_border;
00559     FillRect(c->hDC, &rc, c->editor->hbrBackground);
00560   }
00561 
00562   if (para->pFmt->dwMask & PFM_SPACEAFTER)
00563   {
00564     rc.left = c->rcView.left;
00565     rc.right = c->rcView.right;
00566     rc.bottom = y + para->nHeight;
00567     bounds->bottom = ME_twips2pointsY(c, para->pFmt->dySpaceAfter);
00568     rc.top = rc.bottom - bounds->bottom - bottom_border;
00569     FillRect(c->hDC, &rc, c->editor->hbrBackground);
00570   }
00571 
00572   /* Native richedit doesn't support paragraph borders in v1.0 - 4.1,
00573    * but might support it in later versions. */
00574   if (hasParaBorder) {
00575     int         pen_width, rightEdge;
00576     COLORREF    pencr;
00577     HPEN        pen = NULL, oldpen = NULL;
00578     POINT       pt;
00579 
00580     if (para->pFmt->wBorders & 64) /* autocolor */
00581       pencr = ITextHost_TxGetSysColor(c->editor->texthost,
00582                                       COLOR_WINDOWTEXT);
00583     else
00584       pencr = pen_colors[(para->pFmt->wBorders >> 12) & 0xF];
00585 
00586     rightEdge = c->pt.x + max(c->editor->sizeWindow.cx,
00587                               c->editor->nTotalWidth);
00588 
00589     pen_width = ME_GetBorderPenWidth(c, idx);
00590     pen = CreatePen(border_details[idx].pen_style, pen_width, pencr);
00591     oldpen = SelectObject(c->hDC, pen);
00592     MoveToEx(c->hDC, 0, 0, &pt);
00593 
00594     /* before & after spaces are not included in border */
00595 
00596     /* helper to draw the double lines in case of corner */
00597 #define DD(x)   ((para->pFmt->wBorders & (x)) ? (pen_width + 1) : 0)
00598 
00599     if (para->pFmt->wBorders & 1)
00600     {
00601       MoveToEx(c->hDC, c->pt.x, y + bounds->top, NULL);
00602       LineTo(c->hDC, c->pt.x, y + para->nHeight - bounds->bottom);
00603       if (border_details[idx].dble) {
00604         rc.left = c->pt.x + 1;
00605         rc.right = rc.left + border_width;
00606         rc.top = y + bounds->top;
00607         rc.bottom = y + para->nHeight - bounds->bottom;
00608         FillRect(c->hDC, &rc, c->editor->hbrBackground);
00609         MoveToEx(c->hDC, c->pt.x + pen_width + 1, y + bounds->top + DD(4), NULL);
00610         LineTo(c->hDC, c->pt.x + pen_width + 1, y + para->nHeight - bounds->bottom - DD(8));
00611       }
00612       bounds->left += border_width;
00613     }
00614     if (para->pFmt->wBorders & 2)
00615     {
00616       MoveToEx(c->hDC, rightEdge - 1, y + bounds->top, NULL);
00617       LineTo(c->hDC, rightEdge - 1, y + para->nHeight - bounds->bottom);
00618       if (border_details[idx].dble) {
00619         rc.left = rightEdge - pen_width - 1;
00620         rc.right = rc.left + pen_width;
00621         rc.top = y + bounds->top;
00622         rc.bottom = y + para->nHeight - bounds->bottom;
00623         FillRect(c->hDC, &rc, c->editor->hbrBackground);
00624         MoveToEx(c->hDC, rightEdge - 1 - pen_width - 1, y + bounds->top + DD(4), NULL);
00625         LineTo(c->hDC, rightEdge - 1 - pen_width - 1, y + para->nHeight - bounds->bottom - DD(8));
00626       }
00627       bounds->right += border_width;
00628     }
00629     if (para->pFmt->wBorders & 4)
00630     {
00631       MoveToEx(c->hDC, c->pt.x, y + bounds->top, NULL);
00632       LineTo(c->hDC, rightEdge, y + bounds->top);
00633       if (border_details[idx].dble) {
00634         MoveToEx(c->hDC, c->pt.x + DD(1), y + bounds->top + pen_width + 1, NULL);
00635         LineTo(c->hDC, rightEdge - DD(2), y + bounds->top + pen_width + 1);
00636       }
00637       bounds->top += border_width;
00638     }
00639     if (para->pFmt->wBorders & 8)
00640     {
00641       MoveToEx(c->hDC, c->pt.x, y + para->nHeight - bounds->bottom - 1, NULL);
00642       LineTo(c->hDC, rightEdge, y + para->nHeight - bounds->bottom - 1);
00643       if (border_details[idx].dble) {
00644         MoveToEx(c->hDC, c->pt.x + DD(1), y + para->nHeight - bounds->bottom - 1 - pen_width - 1, NULL);
00645         LineTo(c->hDC, rightEdge - DD(2), y + para->nHeight - bounds->bottom - 1 - pen_width - 1);
00646       }
00647       bounds->bottom += border_width;
00648     }
00649 #undef DD
00650 
00651     MoveToEx(c->hDC, pt.x, pt.y, NULL);
00652     SelectObject(c->hDC, oldpen);
00653     DeleteObject(pen);
00654   }
00655 }
00656 
00657 static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph)
00658 {
00659   ME_Paragraph *para = &paragraph->member.para;
00660   if (!c->editor->bEmulateVersion10) /* v4.1 */
00661   {
00662     if (para->pCell)
00663     {
00664       RECT rc;
00665       ME_Cell *cell = &para->pCell->member.cell;
00666       ME_DisplayItem *paraAfterRow;
00667       HPEN pen, oldPen;
00668       LOGBRUSH logBrush;
00669       HBRUSH brush;
00670       COLORREF color;
00671       POINT oldPt;
00672       int width;
00673       BOOL atTop = (para->pCell != para->prev_para->member.para.pCell);
00674       BOOL atBottom = (para->pCell != para->next_para->member.para.pCell);
00675       int top = c->pt.y + (atTop ? cell->pt.y : para->pt.y);
00676       int bottom = (atBottom ?
00677                     c->pt.y + cell->pt.y + cell->nHeight :
00678                     top + para->nHeight + (atTop ? cell->yTextOffset : 0));
00679       rc.left = c->pt.x + cell->pt.x;
00680       rc.right = rc.left + cell->nWidth;
00681       if (atTop) {
00682         /* Erase gap before text if not all borders are the same height. */
00683         width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
00684         rc.top = top + width;
00685         width = cell->yTextOffset - width;
00686         rc.bottom = rc.top + width;
00687         if (width) {
00688           FillRect(c->hDC, &rc, c->editor->hbrBackground);
00689         }
00690       }
00691       /* Draw cell borders.
00692        * The order borders are draw in is left, top, bottom, right in order
00693        * to be consistent with native richedit.  This is noticeable from the
00694        * overlap of borders of different colours. */
00695       if (!(para->nFlags & MEPF_ROWEND)) {
00696         rc.top = top;
00697         rc.bottom = bottom;
00698         if (cell->border.left.width > 0)
00699         {
00700           color = cell->border.left.colorRef;
00701           width = max(ME_twips2pointsX(c, cell->border.left.width), 1);
00702         } else {
00703           color = RGB(192,192,192);
00704           width = 1;
00705         }
00706         logBrush.lbStyle = BS_SOLID;
00707         logBrush.lbColor = color;
00708         logBrush.lbHatch = 0;
00709         pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
00710                            width, &logBrush, 0, NULL);
00711         oldPen = SelectObject(c->hDC, pen);
00712         MoveToEx(c->hDC, rc.left, rc.top, &oldPt);
00713         LineTo(c->hDC, rc.left, rc.bottom);
00714         SelectObject(c->hDC, oldPen);
00715         DeleteObject(pen);
00716         MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
00717       }
00718 
00719       if (atTop) {
00720         if (cell->border.top.width > 0)
00721         {
00722           brush = CreateSolidBrush(cell->border.top.colorRef);
00723           width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
00724         } else {
00725           brush = GetStockObject(LTGRAY_BRUSH);
00726           width = 1;
00727         }
00728         rc.top = top;
00729         rc.bottom = rc.top + width;
00730         FillRect(c->hDC, &rc, brush);
00731         if (cell->border.top.width > 0)
00732           DeleteObject(brush);
00733       }
00734 
00735       /* Draw the bottom border if at the last paragraph in the cell, and when
00736        * in the last row of the table. */
00737       if (atBottom) {
00738         int oldLeft = rc.left;
00739         width = max(ME_twips2pointsY(c, cell->border.bottom.width), 1);
00740         paraAfterRow = ME_GetTableRowEnd(paragraph)->member.para.next_para;
00741         if (paraAfterRow->member.para.nFlags & MEPF_ROWSTART) {
00742           ME_DisplayItem *nextEndCell;
00743           nextEndCell = ME_FindItemBack(ME_GetTableRowEnd(paraAfterRow), diCell);
00744           assert(nextEndCell && !nextEndCell->member.cell.next_cell);
00745           rc.left = c->pt.x + nextEndCell->member.cell.pt.x;
00746           /* FIXME: Native draws FROM the bottom of the table rather than
00747            * TO the bottom of the table in this case, but just doing so here
00748            * will cause the next row to erase the border. */
00749           /*
00750           rc.top = bottom;
00751           rc.bottom = rc.top + width;
00752            */
00753         }
00754         if (rc.left < rc.right) {
00755           if (cell->border.bottom.width > 0) {
00756             brush = CreateSolidBrush(cell->border.bottom.colorRef);
00757           } else {
00758             brush = GetStockObject(LTGRAY_BRUSH);
00759           }
00760           rc.bottom = bottom;
00761           rc.top = rc.bottom - width;
00762           FillRect(c->hDC, &rc, brush);
00763           if (cell->border.bottom.width > 0)
00764             DeleteObject(brush);
00765         }
00766         rc.left = oldLeft;
00767       }
00768 
00769       /* Right border only drawn if at the end of the table row. */
00770       if (!cell->next_cell->member.cell.next_cell &&
00771           !(para->nFlags & MEPF_ROWSTART))
00772       {
00773         rc.top = top;
00774         rc.bottom = bottom;
00775         if (cell->border.right.width > 0) {
00776           color = cell->border.right.colorRef;
00777           width = max(ME_twips2pointsX(c, cell->border.right.width), 1);
00778         } else {
00779           color = RGB(192,192,192);
00780           width = 1;
00781         }
00782         logBrush.lbStyle = BS_SOLID;
00783         logBrush.lbColor = color;
00784         logBrush.lbHatch = 0;
00785         pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
00786                            width, &logBrush, 0, NULL);
00787         oldPen = SelectObject(c->hDC, pen);
00788         MoveToEx(c->hDC, rc.right - 1, rc.top, &oldPt);
00789         LineTo(c->hDC, rc.right - 1, rc.bottom);
00790         SelectObject(c->hDC, oldPen);
00791         DeleteObject(pen);
00792         MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
00793       }
00794     }
00795   } else { /* v1.0 - 3.0 */
00796     /* Draw simple table border */
00797     if (para->pFmt->dwMask & PFM_TABLE && para->pFmt->wEffects & PFE_TABLE) {
00798       HPEN pen = NULL, oldpen = NULL;
00799       int i, firstX, startX, endX, rowY, rowBottom, nHeight;
00800       POINT oldPt;
00801       PARAFORMAT2 *pNextFmt;
00802 
00803       pen = CreatePen(PS_SOLID, 0, para->border.top.colorRef);
00804       oldpen = SelectObject(c->hDC, pen);
00805 
00806       /* Find the start relative to the text */
00807       firstX = c->pt.x + ME_FindItemFwd(paragraph, diRun)->member.run.pt.x;
00808       /* Go back by the horizontal gap, which is stored in dxOffset */
00809       firstX -= ME_twips2pointsX(c, para->pFmt->dxOffset);
00810       /* The left edge, stored in dxStartIndent affected just the first edge */
00811       startX = firstX - ME_twips2pointsX(c, para->pFmt->dxStartIndent);
00812       rowY = c->pt.y + para->pt.y;
00813       if (para->pFmt->dwMask & PFM_SPACEBEFORE)
00814         rowY += ME_twips2pointsY(c, para->pFmt->dySpaceBefore);
00815       nHeight = ME_FindItemFwd(paragraph, diStartRow)->member.row.nHeight;
00816       rowBottom = rowY + nHeight;
00817 
00818       /* Draw horizontal lines */
00819       MoveToEx(c->hDC, firstX, rowY, &oldPt);
00820       i = para->pFmt->cTabCount - 1;
00821       endX = startX + ME_twips2pointsX(c, para->pFmt->rgxTabs[i] & 0x00ffffff) + 1;
00822       LineTo(c->hDC, endX, rowY);
00823       pNextFmt = para->next_para->member.para.pFmt;
00824       /* The bottom of the row only needs to be drawn if the next row is
00825        * not a table. */
00826       if (!(pNextFmt && pNextFmt->dwMask & PFM_TABLE && pNextFmt->wEffects &&
00827             para->nRows == 1))
00828       {
00829         /* Decrement rowBottom to draw the bottom line within the row, and
00830          * to not draw over this line when drawing the vertical lines. */
00831         rowBottom--;
00832         MoveToEx(c->hDC, firstX, rowBottom, NULL);
00833         LineTo(c->hDC, endX, rowBottom);
00834       }
00835 
00836       /* Draw vertical lines */
00837       MoveToEx(c->hDC, firstX, rowY, NULL);
00838       LineTo(c->hDC, firstX, rowBottom);
00839       for (i = 0; i < para->pFmt->cTabCount; i++)
00840       {
00841         int rightBoundary = para->pFmt->rgxTabs[i] & 0x00ffffff;
00842         endX = startX + ME_twips2pointsX(c, rightBoundary);
00843         MoveToEx(c->hDC, endX, rowY, NULL);
00844         LineTo(c->hDC, endX, rowBottom);
00845       }
00846 
00847       MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
00848       SelectObject(c->hDC, oldpen);
00849       DeleteObject(pen);
00850     }
00851   }
00852 }
00853 
00854 static void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph)
00855 {
00856   int align = SetTextAlign(c->hDC, TA_BASELINE);
00857   ME_DisplayItem *p;
00858   ME_Run *run;
00859   ME_Paragraph *para = NULL;
00860   RECT rc, bounds;
00861   int y;
00862   int height = 0, baseline = 0, no=0;
00863   BOOL visible = FALSE;
00864 
00865   rc.left = c->pt.x;
00866   rc.right = c->rcView.right;
00867 
00868   assert(paragraph);
00869   para = &paragraph->member.para;
00870   y = c->pt.y + para->pt.y;
00871   if (para->pCell)
00872   {
00873     ME_Cell *cell = &para->pCell->member.cell;
00874     rc.left = c->pt.x + cell->pt.x;
00875     rc.right = rc.left + cell->nWidth;
00876   }
00877   if (para->nFlags & MEPF_ROWSTART) {
00878     ME_Cell *cell = &para->next_para->member.para.pCell->member.cell;
00879     rc.right = c->pt.x + cell->pt.x;
00880   } else if (para->nFlags & MEPF_ROWEND) {
00881     ME_Cell *cell = &para->prev_para->member.para.pCell->member.cell;
00882     rc.left = c->pt.x + cell->pt.x + cell->nWidth;
00883   }
00884   ME_DrawParaDecoration(c, para, y, &bounds);
00885   y += bounds.top;
00886   if (bounds.left || bounds.right) {
00887     rc.left = max(rc.left, c->pt.x + bounds.left);
00888     rc.right = min(rc.right, c->pt.x - bounds.right
00889                              + max(c->editor->sizeWindow.cx,
00890                                    c->editor->nTotalWidth));
00891   }
00892 
00893   for (p = paragraph->next; p != para->next_para; p = p->next)
00894   {
00895     switch(p->type) {
00896       case diParagraph:
00897         assert(FALSE);
00898         break;
00899       case diStartRow:
00900         y += height;
00901         rc.top = y;
00902         if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) {
00903           rc.bottom = y + para->nHeight;
00904         } else {
00905           rc.bottom = y + p->member.row.nHeight;
00906         }
00907         visible = RectVisible(c->hDC, &rc);
00908         if (visible) {
00909           FillRect(c->hDC, &rc, c->editor->hbrBackground);
00910         }
00911         if (bounds.right)
00912         {
00913           /* If scrolled to the right past the end of the text, then
00914            * there may be space to the right of the paragraph border. */
00915           RECT rcAfterBrdr = rc;
00916           rcAfterBrdr.left = rc.right + bounds.right;
00917           rcAfterBrdr.right = c->rcView.right;
00918           if (RectVisible(c->hDC, &rcAfterBrdr))
00919             FillRect(c->hDC, &rcAfterBrdr, c->editor->hbrBackground);
00920         }
00921         if (me_debug)
00922         {
00923           const WCHAR wszRowDebug[] = {'r','o','w','[','%','d',']',0};
00924           WCHAR buf[128];
00925           POINT pt = c->pt;
00926           wsprintfW(buf, wszRowDebug, no);
00927           pt.y = 12+y;
00928           ME_DebugWrite(c->hDC, &pt, buf);
00929         }
00930 
00931         height = p->member.row.nHeight;
00932         baseline = p->member.row.nBaseline;
00933         break;
00934       case diRun:
00935         assert(para);
00936         run = &p->member.run;
00937         if (visible && me_debug) {
00938           RECT rc;
00939           rc.left = c->pt.x + run->pt.x;
00940           rc.right = rc.left + run->nWidth;
00941           rc.top = c->pt.y + para->pt.y + run->pt.y;
00942           rc.bottom = rc.top + height;
00943           TRACE("rc = (%d, %d, %d, %d)\n", rc.left, rc.top, rc.right, rc.bottom);
00944           FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
00945         }
00946         if (visible)
00947           ME_DrawRun(c, c->pt.x + run->pt.x,
00948                      c->pt.y + para->pt.y + run->pt.y + baseline, p, para);
00949         if (me_debug)
00950         {
00951           /* I'm using %ls, hope wsprintfW is not going to use wrong (4-byte) WCHAR version */
00952           const WCHAR wszRunDebug[] = {'[','%','d',':','%','x',']',' ','%','l','s',0};
00953           WCHAR buf[2560];
00954           POINT pt;
00955           pt.x = c->pt.x + run->pt.x;
00956           pt.y = c->pt.y + para->pt.y + run->pt.y;
00957           wsprintfW(buf, wszRunDebug, no, p->member.run.nFlags, p->member.run.strText->szData);
00958           ME_DebugWrite(c->hDC, &pt, buf);
00959         }
00960         break;
00961       case diCell:
00962         /* Clear any space at the bottom of the cell after the text. */
00963         if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND))
00964           break;
00965         y += height;
00966         rc.top = c->pt.y + para->pt.y + para->nHeight;
00967         rc.bottom = c->pt.y + p->member.cell.pt.y + p->member.cell.nHeight;
00968         if (RectVisible(c->hDC, &rc))
00969         {
00970           FillRect(c->hDC, &rc, c->editor->hbrBackground);
00971         }
00972         break;
00973       default:
00974         break;
00975     }
00976     no++;
00977   }
00978 
00979   ME_DrawTableBorders(c, paragraph);
00980 
00981   SetTextAlign(c->hDC, align);
00982 }
00983 
00984 void ME_ScrollAbs(ME_TextEditor *editor, int x, int y)
00985 {
00986   BOOL bScrollBarIsVisible, bScrollBarWillBeVisible;
00987   int scrollX = 0, scrollY = 0;
00988 
00989   if (editor->horz_si.nPos != x) {
00990     x = min(x, editor->horz_si.nMax);
00991     x = max(x, editor->horz_si.nMin);
00992     scrollX = editor->horz_si.nPos - x;
00993     editor->horz_si.nPos = x;
00994     if (editor->horz_si.nMax > 0xFFFF) /* scale to 16-bit value */
00995       x = MulDiv(x, 0xFFFF, editor->horz_si.nMax);
00996     ITextHost_TxSetScrollPos(editor->texthost, SB_HORZ, x, TRUE);
00997   }
00998 
00999   if (editor->vert_si.nPos != y) {
01000     y = min(y, editor->vert_si.nMax - (int)editor->vert_si.nPage);
01001     y = max(y, editor->vert_si.nMin);
01002     scrollY = editor->vert_si.nPos - y;
01003     editor->vert_si.nPos = y;
01004     if (editor->vert_si.nMax > 0xFFFF) /* scale to 16-bit value */
01005       y = MulDiv(y, 0xFFFF, editor->vert_si.nMax);
01006     ITextHost_TxSetScrollPos(editor->texthost, SB_VERT, y, TRUE);
01007   }
01008 
01009   if (abs(scrollX) > editor->sizeWindow.cx ||
01010       abs(scrollY) > editor->sizeWindow.cy)
01011     ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE);
01012   else
01013     ITextHost_TxScrollWindowEx(editor->texthost, scrollX, scrollY,
01014                                &editor->rcFormat, &editor->rcFormat,
01015                                NULL, NULL, SW_INVALIDATE);
01016   ME_Repaint(editor);
01017 
01018   if (editor->hWnd)
01019   {
01020     LONG winStyle = GetWindowLongW(editor->hWnd, GWL_STYLE);
01021     if (editor->styleFlags & WS_HSCROLL)
01022     {
01023       bScrollBarIsVisible = (winStyle & WS_HSCROLL) != 0;
01024       bScrollBarWillBeVisible = (editor->nTotalWidth > editor->sizeWindow.cx
01025                                  && (editor->styleFlags & WS_HSCROLL))
01026                                 || (editor->styleFlags & ES_DISABLENOSCROLL);
01027       if (bScrollBarIsVisible != bScrollBarWillBeVisible)
01028         ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ,
01029                                   bScrollBarWillBeVisible);
01030     }
01031 
01032     if (editor->styleFlags & WS_VSCROLL)
01033     {
01034       bScrollBarIsVisible = (winStyle & WS_VSCROLL) != 0;
01035       bScrollBarWillBeVisible = (editor->nTotalLength > editor->sizeWindow.cy
01036                                  && (editor->styleFlags & WS_VSCROLL)
01037                                  && (editor->styleFlags & ES_MULTILINE))
01038                                 || (editor->styleFlags & ES_DISABLENOSCROLL);
01039       if (bScrollBarIsVisible != bScrollBarWillBeVisible)
01040         ITextHost_TxShowScrollBar(editor->texthost, SB_VERT,
01041                                   bScrollBarWillBeVisible);
01042     }
01043   }
01044   ME_UpdateScrollBar(editor);
01045 }
01046 
01047 void ME_HScrollAbs(ME_TextEditor *editor, int x)
01048 {
01049   ME_ScrollAbs(editor, x, editor->vert_si.nPos);
01050 }
01051 
01052 void ME_VScrollAbs(ME_TextEditor *editor, int y)
01053 {
01054   ME_ScrollAbs(editor, editor->horz_si.nPos, y);
01055 }
01056 
01057 void ME_ScrollUp(ME_TextEditor *editor, int cy)
01058 {
01059   ME_VScrollAbs(editor, editor->vert_si.nPos - cy);
01060 }
01061 
01062 void ME_ScrollDown(ME_TextEditor *editor, int cy)
01063 {
01064   ME_VScrollAbs(editor, editor->vert_si.nPos + cy);
01065 }
01066 
01067 void ME_ScrollLeft(ME_TextEditor *editor, int cx)
01068 {
01069   ME_HScrollAbs(editor, editor->horz_si.nPos - cx);
01070 }
01071 
01072 void ME_ScrollRight(ME_TextEditor *editor, int cx)
01073 {
01074   ME_HScrollAbs(editor, editor->horz_si.nPos + cx);
01075 }
01076 
01077 /* Calculates the visibility after a call to SetScrollRange or
01078  * SetScrollInfo with SIF_RANGE. */
01079 static BOOL ME_PostSetScrollRangeVisibility(SCROLLINFO *si)
01080 {
01081   if (si->fMask & SIF_DISABLENOSCROLL)
01082     return TRUE;
01083 
01084   /* This must match the check in SetScrollInfo to determine whether
01085    * to show or hide the scrollbars. */
01086   return si->nMin < si->nMax - max(si->nPage - 1, 0);
01087 }
01088 
01089 void ME_UpdateScrollBar(ME_TextEditor *editor)
01090 {
01091   /* Note that this is the only function that should ever call
01092    * SetScrollInfo with SIF_PAGE or SIF_RANGE. */
01093 
01094   SCROLLINFO si;
01095   BOOL bScrollBarWasVisible, bScrollBarWillBeVisible;
01096 
01097   if (ME_WrapMarkedParagraphs(editor))
01098     FIXME("ME_UpdateScrollBar had to call ME_WrapMarkedParagraphs\n");
01099 
01100   si.cbSize = sizeof(si);
01101   si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
01102   si.nMin = 0;
01103   if (editor->styleFlags & ES_DISABLENOSCROLL)
01104     si.fMask |= SIF_DISABLENOSCROLL;
01105 
01106   /* Update horizontal scrollbar */
01107   bScrollBarWasVisible = editor->horz_si.nMax > editor->horz_si.nPage;
01108   bScrollBarWillBeVisible = editor->nTotalWidth > editor->sizeWindow.cx;
01109   if (editor->horz_si.nPos && !bScrollBarWillBeVisible)
01110   {
01111     ME_HScrollAbs(editor, 0);
01112     /* ME_HScrollAbs will call this function,
01113      * so nothing else needs to be done here. */
01114     return;
01115   }
01116 
01117   si.nMax = editor->nTotalWidth;
01118   si.nPos = editor->horz_si.nPos;
01119   si.nPage = editor->sizeWindow.cx;
01120 
01121   if (si.nMax != editor->horz_si.nMax ||
01122       si.nPage != editor->horz_si.nPage)
01123   {
01124     TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
01125     editor->horz_si.nMax = si.nMax;
01126     editor->horz_si.nPage = si.nPage;
01127     if ((bScrollBarWillBeVisible || bScrollBarWasVisible) &&
01128         editor->styleFlags & WS_HSCROLL)
01129     {
01130       if (si.nMax > 0xFFFF)
01131       {
01132         /* Native scales the scrollbar info to 16-bit external values. */
01133         si.nPos = MulDiv(si.nPos, 0xFFFF, si.nMax);
01134         si.nMax = 0xFFFF;
01135       }
01136       if (editor->hWnd) {
01137         SetScrollInfo(editor->hWnd, SB_HORZ, &si, TRUE);
01138       } else {
01139         ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, si.nMin, si.nMax, FALSE);
01140         ITextHost_TxSetScrollPos(editor->texthost, SB_HORZ, si.nPos, TRUE);
01141       }
01142       /* SetScrollInfo or SetScrollRange change scrollbar visibility. */
01143       bScrollBarWasVisible = ME_PostSetScrollRangeVisibility(&si);
01144     }
01145   }
01146 
01147   if (editor->styleFlags & WS_HSCROLL)
01148   {
01149     if (si.fMask & SIF_DISABLENOSCROLL) {
01150       bScrollBarWillBeVisible = TRUE;
01151     } else if (!(editor->styleFlags & WS_HSCROLL)) {
01152       bScrollBarWillBeVisible = FALSE;
01153     }
01154 
01155     if (bScrollBarWasVisible != bScrollBarWillBeVisible)
01156       ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, bScrollBarWillBeVisible);
01157   }
01158 
01159   /* Update vertical scrollbar */
01160   bScrollBarWasVisible = editor->vert_si.nMax > editor->vert_si.nPage;
01161   bScrollBarWillBeVisible = editor->nTotalLength > editor->sizeWindow.cy &&
01162                             (editor->styleFlags & ES_MULTILINE);
01163 
01164   if (editor->vert_si.nPos && !bScrollBarWillBeVisible)
01165   {
01166     ME_VScrollAbs(editor, 0);
01167     /* ME_VScrollAbs will call this function,
01168      * so nothing else needs to be done here. */
01169     return;
01170   }
01171 
01172   si.nMax = editor->nTotalLength;
01173   si.nPos = editor->vert_si.nPos;
01174   si.nPage = editor->sizeWindow.cy;
01175 
01176   if (si.nMax != editor->vert_si.nMax ||
01177       si.nPage != editor->vert_si.nPage)
01178   {
01179     TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
01180     editor->vert_si.nMax = si.nMax;
01181     editor->vert_si.nPage = si.nPage;
01182     if ((bScrollBarWillBeVisible || bScrollBarWasVisible) &&
01183         editor->styleFlags & WS_VSCROLL)
01184     {
01185       if (si.nMax > 0xFFFF)
01186       {
01187         /* Native scales the scrollbar info to 16-bit external values. */
01188         si.nPos = MulDiv(si.nPos, 0xFFFF, si.nMax);
01189         si.nMax = 0xFFFF;
01190       }
01191       if (editor->hWnd) {
01192         SetScrollInfo(editor->hWnd, SB_VERT, &si, TRUE);
01193       } else {
01194         ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, si.nMin, si.nMax, FALSE);
01195         ITextHost_TxSetScrollPos(editor->texthost, SB_VERT, si.nPos, TRUE);
01196       }
01197       /* SetScrollInfo or SetScrollRange change scrollbar visibility. */
01198       bScrollBarWasVisible = ME_PostSetScrollRangeVisibility(&si);
01199     }
01200   }
01201 
01202   if (editor->styleFlags & WS_VSCROLL)
01203   {
01204     if (si.fMask & SIF_DISABLENOSCROLL) {
01205       bScrollBarWillBeVisible = TRUE;
01206     } else if (!(editor->styleFlags & WS_VSCROLL)) {
01207       bScrollBarWillBeVisible = FALSE;
01208     }
01209 
01210     if (bScrollBarWasVisible != bScrollBarWillBeVisible)
01211       ITextHost_TxShowScrollBar(editor->texthost, SB_VERT,
01212                                 bScrollBarWillBeVisible);
01213   }
01214 }
01215 
01216 void ME_EnsureVisible(ME_TextEditor *editor, ME_Cursor *pCursor)
01217 {
01218   ME_Run *pRun = &pCursor->pRun->member.run;
01219   ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diStartRow);
01220   ME_DisplayItem *pPara = pCursor->pPara;
01221   int x, y, yheight;
01222 
01223   assert(pRow);
01224   assert(pPara);
01225 
01226   if (editor->styleFlags & ES_AUTOHSCROLL)
01227   {
01228     x = pRun->pt.x + ME_PointFromChar(editor, pRun, pCursor->nOffset);
01229     if (x > editor->horz_si.nPos + editor->sizeWindow.cx)
01230       x = x + 1 - editor->sizeWindow.cx;
01231     else if (x > editor->horz_si.nPos)
01232       x = editor->horz_si.nPos;
01233 
01234     if (~editor->styleFlags & ES_AUTOVSCROLL)
01235     {
01236       ME_HScrollAbs(editor, x);
01237       return;
01238     }
01239   } else {
01240     if (~editor->styleFlags & ES_AUTOVSCROLL)
01241       return;
01242     x = editor->horz_si.nPos;
01243   }
01244 
01245   y = pPara->member.para.pt.y + pRow->member.row.pt.y;
01246   yheight = pRow->member.row.nHeight;
01247 
01248   if (y < editor->vert_si.nPos)
01249     ME_ScrollAbs(editor, x, y);
01250   else if (y + yheight > editor->vert_si.nPos + editor->sizeWindow.cy)
01251     ME_ScrollAbs(editor, x, y + yheight - editor->sizeWindow.cy);
01252   else if (x != editor->horz_si.nPos)
01253     ME_ScrollAbs(editor, x, editor->vert_si.nPos);
01254 }
01255 
01256 
01257 void
01258 ME_InvalidateSelection(ME_TextEditor *editor)
01259 {
01260   ME_DisplayItem *sel_start, *sel_end;
01261   ME_DisplayItem *repaint_start = NULL, *repaint_end = NULL;
01262   int nStart, nEnd;
01263   int len = ME_GetTextLength(editor);
01264 
01265   ME_GetSelectionOfs(editor, &nStart, &nEnd);
01266   /* if both old and new selection are 0-char (= caret only), then
01267   there's no (inverted) area to be repainted, neither old nor new */
01268   if (nStart == nEnd && editor->nLastSelStart == editor->nLastSelEnd)
01269     return;
01270   ME_WrapMarkedParagraphs(editor);
01271   ME_GetSelectionParas(editor, &sel_start, &sel_end);
01272   assert(sel_start->type == diParagraph);
01273   assert(sel_end->type == diParagraph);
01274   /* last selection markers aren't always updated, which means
01275    * they can point past the end of the document */
01276   if (editor->nLastSelStart > len || editor->nLastSelEnd > len) {
01277     repaint_start = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
01278     repaint_end = editor->pBuffer->pLast;
01279     ME_MarkForPainting(editor, repaint_start, repaint_end);
01280   } else {
01281     /* if the start part of selection is being expanded or contracted... */
01282     if (nStart < editor->nLastSelStart) {
01283       repaint_start = sel_start;
01284       repaint_end = editor->pLastSelStartPara->member.para.next_para;
01285     } else if (nStart > editor->nLastSelStart) {
01286       repaint_start = editor->pLastSelStartPara;
01287       repaint_end = sel_start->member.para.next_para;
01288     }
01289     ME_MarkForPainting(editor, repaint_start, repaint_end);
01290 
01291     /* if the end part of selection is being contracted or expanded... */
01292     if (nEnd < editor->nLastSelEnd) {
01293       if (!repaint_start) repaint_start = sel_end;
01294       repaint_end = editor->pLastSelEndPara->member.para.next_para;
01295       ME_MarkForPainting(editor, sel_end, repaint_end);
01296     } else if (nEnd > editor->nLastSelEnd) {
01297       if (!repaint_start) repaint_start = editor->pLastSelEndPara;
01298       repaint_end = sel_end->member.para.next_para;
01299       ME_MarkForPainting(editor, editor->pLastSelEndPara, repaint_end);
01300     }
01301   }
01302 
01303   ME_InvalidateMarkedParagraphs(editor, repaint_start, repaint_end);
01304   /* remember the last invalidated position */
01305   ME_GetSelectionOfs(editor, &editor->nLastSelStart, &editor->nLastSelEnd);
01306   ME_GetSelectionParas(editor, &editor->pLastSelStartPara, &editor->pLastSelEndPara);
01307   assert(editor->pLastSelStartPara->type == diParagraph);
01308   assert(editor->pLastSelEndPara->type == diParagraph);
01309 }
01310 
01311 BOOL
01312 ME_SetZoom(ME_TextEditor *editor, int numerator, int denominator)
01313 {
01314   /* TODO: Zoom images and objects */
01315 
01316   if (numerator == 0 && denominator == 0)
01317   {
01318     editor->nZoomNumerator = editor->nZoomDenominator = 0;
01319     return TRUE;
01320   }
01321   if (numerator <= 0 || denominator <= 0)
01322     return FALSE;
01323   if (numerator * 64 <= denominator || numerator / denominator >= 64)
01324     return FALSE;
01325 
01326   editor->nZoomNumerator = numerator;
01327   editor->nZoomDenominator = denominator;
01328 
01329   ME_RewrapRepaint(editor);
01330   return TRUE;
01331 }

Generated on Sun May 27 2012 04:25:57 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.