push edc49a132052b6e245fe2c0c0797f387fa16f3c6
[wine/hacks.git] / dlls / riched20 / paint.c
blob94a1f87d9cf2d3785fad37525dd2706aef7c5c4f
1 /*
2 * RichEdit - painting functions
4 * Copyright 2004 by Krzysztof Foltman
5 * Copyright 2005 by Phil Krylov
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "editor.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
26 void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *rcUpdate)
28 ME_DisplayItem *item;
29 ME_Context c;
30 int yoffset;
31 int ys, ye;
33 editor->nSequence++;
34 yoffset = ME_GetYScrollPos(editor);
35 ME_InitContext(&c, editor, hDC);
36 SetBkMode(hDC, TRANSPARENT);
37 ME_MoveCaret(editor); /* Calls ME_WrapMarkedParagraphs */
38 item = editor->pBuffer->pFirst->next;
39 /* This context point is an offset for the paragraph positions stored
40 * during wrapping. It shouldn't be modified during painting. */
41 c.pt.x = c.rcView.left;
42 c.pt.y = c.rcView.top - yoffset;
43 while(item != editor->pBuffer->pLast)
45 assert(item->type == diParagraph);
47 ys = c.pt.y + item->member.para.pt.y;
48 if (item->member.para.pCell
49 != item->member.para.next_para->member.para.pCell)
51 ME_Cell *cell = NULL;
52 cell = &ME_FindItemBack(item->member.para.next_para, diCell)->member.cell;
53 ye = c.pt.y + cell->pt.y + cell->nHeight;
54 } else {
55 ye = ys + item->member.para.nHeight;
57 if (item->member.para.pCell && !(item->member.para.nFlags & MEPF_ROWEND) &&
58 item->member.para.pCell != item->member.para.prev_para->member.para.pCell)
60 /* the border shifts the text down */
61 ys -= item->member.para.pCell->member.cell.yTextOffset;
64 if (!bOnlyNew || (item->member.para.nFlags & MEPF_REPAINT))
66 /* Draw the pargraph if any of the paragraph is in the update region. */
67 BOOL bPaint = (rcUpdate == NULL);
68 if (rcUpdate)
69 bPaint = ys < rcUpdate->bottom && ye > rcUpdate->top;
70 if (bPaint)
72 ME_DrawParagraph(&c, item);
73 /* Clear the repaint flag if the whole paragraph is in the
74 * update region. */
75 if (!rcUpdate || (rcUpdate->top <= ys && rcUpdate->bottom >= ye))
76 item->member.para.nFlags &= ~MEPF_REPAINT;
79 item = item->member.para.next_para;
81 if (c.pt.y + editor->nTotalLength < c.rcView.bottom)
83 /* Fill space after the end of the text. */
84 RECT rc;
85 rc.top = c.pt.y + editor->nTotalLength;
86 rc.left = c.rcView.left;
87 rc.bottom = c.rcView.bottom;
88 rc.right = c.rcView.right;
90 if (bOnlyNew)
92 /* Only erase region drawn from previous call to ME_PaintContent */
93 if (editor->nTotalLength < editor->nLastTotalLength)
94 rc.bottom = c.pt.y + editor->nLastTotalLength;
95 else
96 SetRectEmpty(&rc);
99 IntersectRect(&rc, &rc, rcUpdate);
101 if (!IsRectEmpty(&rc))
102 FillRect(hDC, &rc, c.editor->hbrBackground);
104 if (editor->nTotalLength != editor->nLastTotalLength)
105 ME_SendRequestResize(editor, FALSE);
106 editor->nLastTotalLength = editor->nTotalLength;
107 ME_DestroyContext(&c, NULL);
110 void ME_Repaint(ME_TextEditor *editor)
112 if (ME_WrapMarkedParagraphs(editor))
114 ME_UpdateScrollBar(editor);
115 FIXME("ME_Repaint had to call ME_WrapMarkedParagraphs\n");
117 if (!IsWindowVisible(editor->hWnd))
118 return;
119 if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE))
120 ME_SendOldNotify(editor, EN_UPDATE);
121 UpdateWindow(editor->hWnd);
124 void ME_UpdateRepaint(ME_TextEditor *editor)
126 /* Should be called whenever the contents of the control have changed */
127 ME_Cursor *pCursor;
128 BOOL wrappedParagraphs;
130 wrappedParagraphs = ME_WrapMarkedParagraphs(editor);
131 if (wrappedParagraphs)
132 ME_UpdateScrollBar(editor);
134 /* Ensure that the cursor is visible */
135 pCursor = &editor->pCursors[0];
136 ME_EnsureVisible(editor, pCursor->pRun);
138 /* send EN_CHANGE if the event mask asks for it */
139 if(editor->nEventMask & ENM_CHANGE)
141 editor->nEventMask &= ~ENM_CHANGE;
142 ME_SendOldNotify(editor, EN_CHANGE);
143 editor->nEventMask |= ENM_CHANGE;
145 ME_Repaint(editor);
146 ME_SendSelChange(editor);
149 void
150 ME_RewrapRepaint(ME_TextEditor *editor)
152 /* RewrapRepaint should be called whenever the control has changed in
153 * looks, but not content. Like resizing. */
155 ME_MarkAllForWrapping(editor);
156 ME_WrapMarkedParagraphs(editor);
157 ME_UpdateScrollBar(editor);
158 ME_Repaint(editor);
161 int ME_twips2pointsX(ME_Context *c, int x)
163 if (c->editor->nZoomNumerator == 0)
164 return x * c->dpi.cx / 1440;
165 else
166 return x * c->dpi.cx * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator;
169 int ME_twips2pointsY(ME_Context *c, int y)
171 if (c->editor->nZoomNumerator == 0)
172 return y * c->dpi.cy / 1440;
173 else
174 return y * c->dpi.cy * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator;
177 static void ME_HighlightSpace(ME_Context *c, int x, int y, LPCWSTR szText,
178 int nChars, ME_Style *s, int width,
179 int nSelFrom, int nSelTo, int ymin, int cy)
181 HDC hDC = c->hDC;
182 HGDIOBJ hOldFont = NULL;
183 SIZE sz;
184 int selWidth;
185 /* Only highlight if there is a selection in the run and when
186 * EM_HIDESELECTION is not being used to hide the selection. */
187 if (nSelFrom >= nChars || nSelTo < 0 || nSelFrom >= nSelTo
188 || c->editor->bHideSelection)
189 return;
190 hOldFont = ME_SelectStyleFont(c, s);
191 if (width <= 0)
193 GetTextExtentPoint32W(hDC, szText, nChars, &sz);
194 width = sz.cx;
196 if (nSelFrom < 0) nSelFrom = 0;
197 if (nSelTo > nChars) nSelTo = nChars;
198 GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
199 x += sz.cx;
200 if (nSelTo != nChars)
202 GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
203 selWidth = sz.cx;
204 } else {
205 selWidth = width - sz.cx;
207 ME_UnselectStyleFont(c, s, hOldFont);
209 if (c->editor->bEmulateVersion10)
210 PatBlt(hDC, x, ymin, selWidth, cy, DSTINVERT);
211 else
213 RECT rect;
214 HBRUSH hBrush;
215 rect.left = x;
216 rect.top = ymin;
217 rect.right = x + selWidth;
218 rect.bottom = ymin + cy;
219 hBrush = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
220 FillRect(hDC, &rect, hBrush);
221 DeleteObject(hBrush);
225 static void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText,
226 int nChars, ME_Style *s, int width,
227 int nSelFrom, int nSelTo, int ymin, int cy)
229 HDC hDC = c->hDC;
230 HGDIOBJ hOldFont;
231 COLORREF rgbOld;
232 int yOffset = 0, yTwipsOffset = 0;
233 SIZE sz;
234 COLORREF rgb;
235 HPEN hPen = NULL, hOldPen = NULL;
236 BOOL bHighlightedText = (nSelFrom < nChars && nSelTo >= 0
237 && nSelFrom < nSelTo && !c->editor->bHideSelection);
238 int xSelStart = x, xSelEnd = x;
239 int *lpDx = NULL;
240 /* lpDx is only needed for tabs to make sure the underline done automatically
241 * by the font extends to the end of the tab. Tabs are always stored as
242 * a single character run, so we can handle this case separately, since
243 * otherwise lpDx would need to specify the lengths of each character. */
244 if (width && nChars == 1)
245 lpDx = &width; /* Make sure underline for tab extends across tab space */
247 hOldFont = ME_SelectStyleFont(c, s);
248 if ((s->fmt.dwMask & s->fmt.dwEffects) & CFM_OFFSET) {
249 yTwipsOffset = s->fmt.yOffset;
251 if ((s->fmt.dwMask & s->fmt.dwEffects) & (CFM_SUPERSCRIPT | CFM_SUBSCRIPT)) {
252 if (s->fmt.dwEffects & CFE_SUPERSCRIPT) yTwipsOffset = s->fmt.yHeight/3;
253 if (s->fmt.dwEffects & CFE_SUBSCRIPT) yTwipsOffset = -s->fmt.yHeight/12;
255 if (yTwipsOffset)
256 yOffset = ME_twips2pointsY(c, yTwipsOffset);
258 if ((s->fmt.dwMask & CFM_LINK) && (s->fmt.dwEffects & CFE_LINK))
259 rgb = RGB(0,0,255);
260 else if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR))
261 rgb = GetSysColor(COLOR_WINDOWTEXT);
262 else
263 rgb = s->fmt.crTextColor;
265 /* Determine the area that is selected in the run. */
266 GetTextExtentPoint32W(hDC, szText, nChars, &sz);
267 /* Treat width as an optional parameter. We can get the width from the
268 * text extent of the string if it isn't specified. */
269 if (!width) width = sz.cx;
270 if (bHighlightedText)
272 if (nSelFrom <= 0)
274 nSelFrom = 0;
276 else
278 GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
279 xSelStart = x + sz.cx;
281 if (nSelTo >= nChars)
283 nSelTo = nChars;
284 xSelEnd = x + width;
286 else
288 GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
289 xSelEnd = xSelStart + sz.cx;
293 /* Choose the pen type for underlining the text. */
294 if (s->fmt.dwMask & CFM_UNDERLINETYPE)
296 switch (s->fmt.bUnderlineType)
298 case CFU_UNDERLINE:
299 case CFU_UNDERLINEWORD: /* native seems to map it to simple underline (MSDN) */
300 case CFU_UNDERLINEDOUBLE: /* native seems to map it to simple underline (MSDN) */
301 hPen = CreatePen(PS_SOLID, 1, rgb);
302 break;
303 case CFU_UNDERLINEDOTTED:
304 hPen = CreatePen(PS_DOT, 1, rgb);
305 break;
306 default:
307 WINE_FIXME("Unknown underline type (%u)\n", s->fmt.bUnderlineType);
308 /* fall through */
309 case CFU_CF1UNDERLINE: /* this type is supported in the font, do nothing */
310 case CFU_UNDERLINENONE:
311 hPen = NULL;
312 break;
314 if (hPen)
316 hOldPen = SelectObject(hDC, hPen);
320 rgbOld = SetTextColor(hDC, rgb);
321 if (bHighlightedText && !c->editor->bEmulateVersion10)
323 COLORREF rgbBackOld;
324 RECT dim;
325 /* FIXME: should use textmetrics info for Descent info */
326 if (hPen)
327 MoveToEx(hDC, x, y - yOffset + 1, NULL);
328 if (xSelStart > x)
330 ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nSelFrom, NULL);
331 if (hPen)
332 LineTo(hDC, xSelStart, y - yOffset + 1);
334 dim.top = ymin;
335 dim.bottom = ymin + cy;
336 dim.left = xSelStart;
337 dim.right = xSelEnd;
338 SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
339 rgbBackOld = SetBkColor(hDC, GetSysColor(COLOR_HIGHLIGHT));
340 ExtTextOutW(hDC, xSelStart, y-yOffset, ETO_OPAQUE, &dim,
341 szText+nSelFrom, nSelTo-nSelFrom, lpDx);
342 if (hPen)
343 LineTo(hDC, xSelEnd, y - yOffset + 1);
344 SetBkColor(hDC, rgbBackOld);
345 if (xSelEnd < x + width)
347 SetTextColor(hDC, rgb);
348 ExtTextOutW(hDC, xSelEnd, y-yOffset, 0, NULL, szText+nSelTo,
349 nChars-nSelTo, NULL);
350 if (hPen)
351 LineTo(hDC, x + width, y - yOffset + 1);
354 else
356 ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nChars, lpDx);
358 /* FIXME: should use textmetrics info for Descent info */
359 if (hPen)
361 MoveToEx(hDC, x, y - yOffset + 1, NULL);
362 LineTo(hDC, x + width, y - yOffset + 1);
365 if (bHighlightedText) /* v1.0 inverts the selection */
367 PatBlt(hDC, xSelStart, ymin, xSelEnd-xSelStart, cy, DSTINVERT);
371 if (hPen)
373 SelectObject(hDC, hOldPen);
374 DeleteObject(hPen);
376 SetTextColor(hDC, rgbOld);
377 ME_UnselectStyleFont(c, s, hOldFont);
380 static void ME_DebugWrite(HDC hDC, const POINT *pt, LPCWSTR szText) {
381 int align = SetTextAlign(hDC, TA_LEFT|TA_TOP);
382 HGDIOBJ hFont = SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
383 COLORREF color = SetTextColor(hDC, RGB(128,128,128));
384 TextOutW(hDC, pt->x, pt->y, szText, lstrlenW(szText));
385 SelectObject(hDC, hFont);
386 SetTextAlign(hDC, align);
387 SetTextColor(hDC, color);
390 static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Paragraph *para)
392 ME_Run *run = &rundi->member.run;
393 ME_DisplayItem *start;
394 int runofs = run->nCharOfs+para->nCharOfs;
395 int nSelFrom, nSelTo;
396 const WCHAR wszSpace[] = {' ', 0};
398 if (run->nFlags & MERF_HIDDEN)
399 return;
401 start = ME_FindItemBack(rundi, diStartRow);
402 ME_GetSelection(c->editor, &nSelFrom, &nSelTo);
404 /* Draw selected end-of-paragraph mark */
405 /* you can always comment it out if you need visible paragraph marks */
406 if (run->nFlags & MERF_ENDPARA)
408 if (runofs >= nSelFrom && runofs < nSelTo)
410 ME_HighlightSpace(c, x, y, wszSpace, 1, run->style, 0, 0, 1,
411 c->pt.y + para->pt.y + start->member.row.pt.y,
412 start->member.row.nHeight);
414 return;
417 if (run->nFlags & (MERF_TAB | MERF_ENDCELL))
419 /* wszSpace is used instead of the tab character because otherwise
420 * an unwanted symbol can be inserted instead. */
421 ME_DrawTextWithStyle(c, x, y, wszSpace, 1, run->style, run->nWidth,
422 nSelFrom-runofs, nSelTo-runofs,
423 c->pt.y + para->pt.y + start->member.row.pt.y,
424 start->member.row.nHeight);
425 return;
428 if (run->nFlags & MERF_GRAPHICS)
429 ME_DrawOLE(c, x, y, run, para, (runofs >= nSelFrom) && (runofs < nSelTo));
430 else
432 if (c->editor->cPasswordMask)
434 ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,ME_StrVLen(run->strText));
435 ME_DrawTextWithStyle(c, x, y,
436 szMasked->szData, ME_StrVLen(szMasked), run->style, run->nWidth,
437 nSelFrom-runofs,nSelTo-runofs,
438 c->pt.y + para->pt.y + start->member.row.pt.y,
439 start->member.row.nHeight);
440 ME_DestroyString(szMasked);
442 else
443 ME_DrawTextWithStyle(c, x, y,
444 run->strText->szData, ME_StrVLen(run->strText), run->style, run->nWidth,
445 nSelFrom-runofs,nSelTo-runofs,
446 c->pt.y + para->pt.y + start->member.row.pt.y,
447 start->member.row.nHeight);
451 static const struct {unsigned width_num : 4, width_den : 4, pen_style : 4, dble : 1;} border_details[] = {
452 /* none */ {0, 1, PS_SOLID, FALSE},
453 /* 3/4 */ {3, 4, PS_SOLID, FALSE},
454 /* 1 1/2 */ {3, 2, PS_SOLID, FALSE},
455 /* 2 1/4 */ {9, 4, PS_SOLID, FALSE},
456 /* 3 */ {3, 1, PS_SOLID, FALSE},
457 /* 4 1/2 */ {9, 2, PS_SOLID, FALSE},
458 /* 6 */ {6, 1, PS_SOLID, FALSE},
459 /* 3/4 double */ {3, 4, PS_SOLID, TRUE},
460 /* 1 1/2 double */ {3, 2, PS_SOLID, TRUE},
461 /* 2 1/4 double */ {9, 4, PS_SOLID, TRUE},
462 /* 3/4 gray */ {3, 4, PS_DOT /* FIXME */, FALSE},
463 /* 1 1/2 dashed */ {3, 2, PS_DASH, FALSE},
466 static const COLORREF pen_colors[16] = {
467 /* Black */ RGB(0x00, 0x00, 0x00), /* Blue */ RGB(0x00, 0x00, 0xFF),
468 /* Cyan */ RGB(0x00, 0xFF, 0xFF), /* Green */ RGB(0x00, 0xFF, 0x00),
469 /* Magenta */ RGB(0xFF, 0x00, 0xFF), /* Red */ RGB(0xFF, 0x00, 0x00),
470 /* Yellow */ RGB(0xFF, 0xFF, 0x00), /* White */ RGB(0xFF, 0xFF, 0xFF),
471 /* Dark blue */ RGB(0x00, 0x00, 0x80), /* Dark cyan */ RGB(0x00, 0x80, 0x80),
472 /* Dark green */ RGB(0x00, 0x80, 0x80), /* Dark magenta */ RGB(0x80, 0x00, 0x80),
473 /* Dark red */ RGB(0x80, 0x00, 0x00), /* Dark yellow */ RGB(0x80, 0x80, 0x00),
474 /* Dark gray */ RGB(0x80, 0x80, 0x80), /* Light gray */ RGB(0xc0, 0xc0, 0xc0),
477 static int ME_GetBorderPenWidth(ME_TextEditor* editor, int idx)
479 int width;
481 if (editor->nZoomNumerator == 0)
483 width = border_details[idx].width_num + border_details[idx].width_den / 2;
484 width /= border_details[idx].width_den;
486 else
488 width = border_details[idx].width_num * editor->nZoomNumerator;
489 width += border_details[idx].width_den * editor->nZoomNumerator / 2;
490 width /= border_details[idx].width_den * editor->nZoomDenominator;
492 return width;
495 int ME_GetParaBorderWidth(ME_TextEditor* editor, int flags)
497 int idx = (flags >> 8) & 0xF;
498 int width;
500 if (idx >= sizeof(border_details) / sizeof(border_details[0]))
502 FIXME("Unsupported border value %d\n", idx);
503 return 0;
505 width = ME_GetBorderPenWidth(editor, idx);
506 if (border_details[idx].dble) width = width * 2 + 1;
507 return width;
510 int ME_GetParaLineSpace(ME_Context* c, ME_Paragraph* para)
512 int sp = 0, ls = 0;
513 if (!(para->pFmt->dwMask & PFM_LINESPACING)) return 0;
515 /* FIXME: how to compute simply the line space in ls ??? */
516 /* FIXME: does line spacing include the line itself ??? */
517 switch (para->pFmt->bLineSpacingRule)
519 case 0: sp = ls; break;
520 case 1: sp = (3 * ls) / 2; break;
521 case 2: sp = 2 * ls; break;
522 case 3: sp = ME_twips2pointsY(c, para->pFmt->dyLineSpacing); if (sp < ls) sp = ls; break;
523 case 4: sp = ME_twips2pointsY(c, para->pFmt->dyLineSpacing); break;
524 case 5: sp = para->pFmt->dyLineSpacing / 20; break;
525 default: FIXME("Unsupported spacing rule value %d\n", para->pFmt->bLineSpacingRule);
527 if (c->editor->nZoomNumerator == 0)
528 return sp;
529 else
530 return sp * c->editor->nZoomNumerator / c->editor->nZoomDenominator;
533 static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT* bounds)
535 int idx, border_width, top_border, bottom_border;
536 RECT rc;
537 BOOL hasParaBorder;
539 SetRectEmpty(bounds);
540 if (!(para->pFmt->dwMask & (PFM_BORDER | PFM_SPACEBEFORE | PFM_SPACEAFTER))) return;
542 border_width = top_border = bottom_border = 0;
543 idx = (para->pFmt->wBorders >> 8) & 0xF;
544 hasParaBorder = (!(c->editor->bEmulateVersion10 &&
545 para->pFmt->dwMask & PFM_TABLE &&
546 para->pFmt->wEffects & PFE_TABLE) &&
547 (para->pFmt->dwMask & PFM_BORDER) &&
548 idx != 0 &&
549 (para->pFmt->wBorders & 0xF));
550 if (hasParaBorder)
552 /* FIXME: wBorders is not stored as MSDN says in v1.0 - 4.1 of richedit
553 * controls. It actually stores the paragraph or row border style. Although
554 * the value isn't used for drawing, it is used for streaming out rich text.
556 * wBorders stores the border style for each side (top, left, bottom, right)
557 * using nibble (4 bits) to store each border style. The rich text format
558 * control words, and their associated value are the following:
559 * \brdrdash 0
560 * \brdrdashsm 1
561 * \brdrdb 2
562 * \brdrdot 3
563 * \brdrhair 4
564 * \brdrs 5
565 * \brdrth 6
566 * \brdrtriple 7
568 * The order of the sides stored actually differs from v1.0 to 3.0 and v4.1.
569 * The mask corresponding to each side for the version are the following:
570 * mask v1.0-3.0 v4.1
571 * 0x000F top left
572 * 0x00F0 left top
573 * 0x0F00 bottom right
574 * 0xF000 right bottom
576 if (para->pFmt->wBorders & 0x00B0)
577 FIXME("Unsupported border flags %x\n", para->pFmt->wBorders);
578 border_width = ME_GetParaBorderWidth(c->editor, para->pFmt->wBorders);
579 if (para->pFmt->wBorders & 4) top_border = border_width;
580 if (para->pFmt->wBorders & 8) bottom_border = border_width;
583 if (para->pFmt->dwMask & PFM_SPACEBEFORE)
585 rc.left = c->rcView.left;
586 rc.right = c->rcView.right;
587 rc.top = y;
588 bounds->top = ME_twips2pointsY(c, para->pFmt->dySpaceBefore);
589 rc.bottom = y + bounds->top + top_border;
590 FillRect(c->hDC, &rc, c->editor->hbrBackground);
593 if (para->pFmt->dwMask & PFM_SPACEAFTER)
595 rc.left = c->rcView.left;
596 rc.right = c->rcView.right;
597 rc.bottom = y + para->nHeight;
598 bounds->bottom = ME_twips2pointsY(c, para->pFmt->dySpaceAfter);
599 rc.top = rc.bottom - bounds->bottom - bottom_border;
600 FillRect(c->hDC, &rc, c->editor->hbrBackground);
603 /* Native richedit doesn't support paragraph borders in v1.0 - 4.1,
604 * but might support it in later versions. */
605 if (hasParaBorder) {
606 int pen_width;
607 COLORREF pencr;
608 HPEN pen = NULL, oldpen = NULL;
609 POINT pt;
611 if (para->pFmt->wBorders & 64) /* autocolor */
612 pencr = GetSysColor(COLOR_WINDOWTEXT);
613 else
614 pencr = pen_colors[(para->pFmt->wBorders >> 12) & 0xF];
616 pen_width = ME_GetBorderPenWidth(c->editor, idx);
617 pen = CreatePen(border_details[idx].pen_style, pen_width, pencr);
618 oldpen = SelectObject(c->hDC, pen);
619 MoveToEx(c->hDC, 0, 0, &pt);
621 /* before & after spaces are not included in border */
623 /* helper to draw the double lines in case of corner */
624 #define DD(x) ((para->pFmt->wBorders & (x)) ? (pen_width + 1) : 0)
626 if (para->pFmt->wBorders & 1)
628 MoveToEx(c->hDC, c->rcView.left, y + bounds->top, NULL);
629 LineTo(c->hDC, c->rcView.left, y + para->nHeight - bounds->bottom);
630 if (border_details[idx].dble) {
631 rc.left = c->rcView.left + 1;
632 rc.right = rc.left + border_width;
633 rc.top = y + bounds->top;
634 rc.bottom = y + para->nHeight - bounds->bottom;
635 FillRect(c->hDC, &rc, c->editor->hbrBackground);
636 MoveToEx(c->hDC, c->rcView.left + pen_width + 1, y + bounds->top + DD(4), NULL);
637 LineTo(c->hDC, c->rcView.left + pen_width + 1, y + para->nHeight - bounds->bottom - DD(8));
639 bounds->left += border_width;
641 if (para->pFmt->wBorders & 2)
643 MoveToEx(c->hDC, c->rcView.right - 1, y + bounds->top, NULL);
644 LineTo(c->hDC, c->rcView.right - 1, y + para->nHeight - bounds->bottom);
645 if (border_details[idx].dble) {
646 rc.left = c->rcView.right - pen_width - 1;
647 rc.right = c->rcView.right - 1;
648 rc.top = y + bounds->top;
649 rc.bottom = y + para->nHeight - bounds->bottom;
650 FillRect(c->hDC, &rc, c->editor->hbrBackground);
651 MoveToEx(c->hDC, c->rcView.right - 1 - pen_width - 1, y + bounds->top + DD(4), NULL);
652 LineTo(c->hDC, c->rcView.right - 1 - pen_width - 1, y + para->nHeight - bounds->bottom - DD(8));
654 bounds->right += border_width;
656 if (para->pFmt->wBorders & 4)
658 MoveToEx(c->hDC, c->rcView.left, y + bounds->top, NULL);
659 LineTo(c->hDC, c->rcView.right, y + bounds->top);
660 if (border_details[idx].dble) {
661 MoveToEx(c->hDC, c->rcView.left + DD(1), y + bounds->top + pen_width + 1, NULL);
662 LineTo(c->hDC, c->rcView.right - DD(2), y + bounds->top + pen_width + 1);
664 bounds->top += border_width;
666 if (para->pFmt->wBorders & 8)
668 MoveToEx(c->hDC, c->rcView.left, y + para->nHeight - bounds->bottom - 1, NULL);
669 LineTo(c->hDC, c->rcView.right, y + para->nHeight - bounds->bottom - 1);
670 if (border_details[idx].dble) {
671 MoveToEx(c->hDC, c->rcView.left + DD(1), y + para->nHeight - bounds->bottom - 1 - pen_width - 1, NULL);
672 LineTo(c->hDC, c->rcView.right - DD(2), y + para->nHeight - bounds->bottom - 1 - pen_width - 1);
674 bounds->bottom += border_width;
676 #undef DD
678 MoveToEx(c->hDC, pt.x, pt.y, NULL);
679 SelectObject(c->hDC, oldpen);
680 DeleteObject(pen);
684 static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph)
686 ME_Paragraph *para = &paragraph->member.para;
687 if (!c->editor->bEmulateVersion10) /* v4.1 */
689 if (para->pCell)
691 RECT rc;
692 ME_Cell *cell = &para->pCell->member.cell;
693 ME_DisplayItem *paraAfterRow;
694 HPEN pen, oldPen;
695 LOGBRUSH logBrush;
696 HBRUSH brush;
697 COLORREF color;
698 POINT oldPt;
699 int width;
700 BOOL atTop = (para->pCell != para->prev_para->member.para.pCell);
701 BOOL atBottom = (para->pCell != para->next_para->member.para.pCell);
702 int top = c->pt.y + (atTop ? cell->pt.y : para->pt.y);
703 int bottom = (atBottom ?
704 c->pt.y + cell->pt.y + cell->nHeight :
705 top + para->nHeight + (atTop ? cell->yTextOffset : 0));
706 rc.left = c->pt.x + cell->pt.x;
707 rc.right = rc.left + cell->nWidth;
708 if (atTop) {
709 /* Erase gap before text if not all borders are the same height. */
710 width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
711 rc.top = top + width;
712 width = cell->yTextOffset - width;
713 rc.bottom = rc.top + width;
714 if (width) {
715 FillRect(c->hDC, &rc, c->editor->hbrBackground);
718 /* Draw cell borders.
719 * The order borders are draw in is left, top, bottom, right in order
720 * to be consistent with native richedit. This is noticeable from the
721 * overlap of borders of different colours. */
722 if (!(para->nFlags & MEPF_ROWEND)) {
723 rc.top = top;
724 rc.bottom = bottom;
725 if (cell->border.left.width > 0)
727 color = cell->border.left.colorRef;
728 width = max(ME_twips2pointsX(c, cell->border.left.width), 1);
729 } else {
730 color = RGB(192,192,192);
731 width = 1;
733 logBrush.lbStyle = BS_SOLID;
734 logBrush.lbColor = color;
735 logBrush.lbHatch = 0;
736 pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
737 width, &logBrush, 0, NULL);
738 oldPen = SelectObject(c->hDC, pen);
739 MoveToEx(c->hDC, rc.left, rc.top, &oldPt);
740 LineTo(c->hDC, rc.left, rc.bottom);
741 SelectObject(c->hDC, oldPen);
742 DeleteObject(pen);
743 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
746 if (atTop) {
747 if (cell->border.top.width > 0)
749 brush = CreateSolidBrush(cell->border.top.colorRef);
750 width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
751 } else {
752 brush = GetStockObject(LTGRAY_BRUSH);
753 width = 1;
755 rc.top = top;
756 rc.bottom = rc.top + width;
757 FillRect(c->hDC, &rc, brush);
758 if (cell->border.top.width > 0)
759 DeleteObject(brush);
762 /* Draw the bottom border if at the last paragraph in the cell, and when
763 * in the last row of the table. */
764 if (atBottom) {
765 int oldLeft = rc.left;
766 width = max(ME_twips2pointsY(c, cell->border.bottom.width), 1);
767 paraAfterRow = ME_GetTableRowEnd(paragraph)->member.para.next_para;
768 if (paraAfterRow->member.para.nFlags & MEPF_ROWSTART) {
769 ME_DisplayItem *nextEndCell;
770 nextEndCell = ME_FindItemBack(ME_GetTableRowEnd(paraAfterRow), diCell);
771 assert(nextEndCell && !nextEndCell->member.cell.next_cell);
772 rc.left = c->pt.x + nextEndCell->member.cell.pt.x;
773 /* FIXME: Native draws FROM the bottom of the table rather than
774 * TO the bottom of the table in this case, but just doing so here
775 * will cause the next row to erase the border. */
777 rc.top = bottom;
778 rc.bottom = rc.top + width;
781 if (rc.left < rc.right) {
782 if (cell->border.bottom.width > 0) {
783 brush = CreateSolidBrush(cell->border.bottom.colorRef);
784 } else {
785 brush = GetStockObject(LTGRAY_BRUSH);
787 rc.bottom = bottom;
788 rc.top = rc.bottom - width;
789 FillRect(c->hDC, &rc, brush);
790 if (cell->border.bottom.width > 0)
791 DeleteObject(brush);
793 rc.left = oldLeft;
796 /* Right border only drawn if at the end of the table row. */
797 if (!cell->next_cell->member.cell.next_cell &&
798 !(para->nFlags & MEPF_ROWSTART))
800 rc.top = top;
801 rc.bottom = bottom;
802 if (cell->border.right.width > 0) {
803 color = cell->border.right.colorRef;
804 width = max(ME_twips2pointsX(c, cell->border.right.width), 1);
805 } else {
806 color = RGB(192,192,192);
807 width = 1;
809 logBrush.lbStyle = BS_SOLID;
810 logBrush.lbColor = color;
811 logBrush.lbHatch = 0;
812 pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
813 width, &logBrush, 0, NULL);
814 oldPen = SelectObject(c->hDC, pen);
815 MoveToEx(c->hDC, rc.right - 1, rc.top, &oldPt);
816 LineTo(c->hDC, rc.right - 1, rc.bottom);
817 SelectObject(c->hDC, oldPen);
818 DeleteObject(pen);
819 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
822 } else { /* v1.0 - 3.0 */
823 /* Draw simple table border */
824 if (para->pFmt->dwMask & PFM_TABLE && para->pFmt->wEffects & PFE_TABLE) {
825 HPEN pen = NULL, oldpen = NULL;
826 int i, firstX, startX, endX, rowY, rowBottom, nHeight;
827 POINT oldPt;
828 PARAFORMAT2 *pNextFmt;
830 pen = CreatePen(PS_SOLID, 0, para->border.top.colorRef);
831 oldpen = SelectObject(c->hDC, pen);
833 /* Find the start relative to the text */
834 firstX = c->pt.x + ME_FindItemFwd(paragraph, diRun)->member.run.pt.x;
835 /* Go back by the horizontal gap, which is stored in dxOffset */
836 firstX -= ME_twips2pointsX(c, para->pFmt->dxOffset);
837 /* The left edge, stored in dxStartIndent affected just the first edge */
838 startX = firstX - ME_twips2pointsX(c, para->pFmt->dxStartIndent);
839 rowY = c->pt.y + para->pt.y;
840 if (para->pFmt->dwMask & PFM_SPACEBEFORE)
841 rowY += ME_twips2pointsY(c, para->pFmt->dySpaceBefore);
842 nHeight = ME_FindItemFwd(paragraph, diStartRow)->member.row.nHeight;
843 rowBottom = rowY + nHeight;
845 /* Draw horizontal lines */
846 MoveToEx(c->hDC, firstX, rowY, &oldPt);
847 i = para->pFmt->cTabCount - 1;
848 endX = startX + ME_twips2pointsX(c, para->pFmt->rgxTabs[i] & 0x00ffffff) + 1;
849 LineTo(c->hDC, endX, rowY);
850 pNextFmt = para->next_para->member.para.pFmt;
851 /* The bottom of the row only needs to be drawn if the next row is
852 * not a table. */
853 if (!(pNextFmt && pNextFmt->dwMask & PFM_TABLE && pNextFmt->wEffects &&
854 para->nRows == 1))
856 /* Decrement rowBottom to draw the bottom line within the row, and
857 * to not draw over this line when drawing the vertical lines. */
858 rowBottom--;
859 MoveToEx(c->hDC, firstX, rowBottom, NULL);
860 LineTo(c->hDC, endX, rowBottom);
863 /* Draw vertical lines */
864 MoveToEx(c->hDC, firstX, rowY, NULL);
865 LineTo(c->hDC, firstX, rowBottom);
866 for (i = 0; i < para->pFmt->cTabCount; i++)
868 int rightBoundary = para->pFmt->rgxTabs[i] & 0x00ffffff;
869 endX = startX + ME_twips2pointsX(c, rightBoundary);
870 MoveToEx(c->hDC, endX, rowY, NULL);
871 LineTo(c->hDC, endX, rowBottom);
874 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
875 SelectObject(c->hDC, oldpen);
876 DeleteObject(pen);
881 void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph)
883 int align = SetTextAlign(c->hDC, TA_BASELINE);
884 ME_DisplayItem *p;
885 ME_Run *run;
886 ME_Paragraph *para = NULL;
887 RECT rc, bounds;
888 int y;
889 int height = 0, baseline = 0, no=0;
890 BOOL visible = FALSE;
892 rc.left = c->pt.x;
893 rc.right = c->rcView.right;
895 assert(paragraph);
896 para = &paragraph->member.para;
897 y = c->pt.y + para->pt.y;
898 if (para->pCell)
900 ME_Cell *cell = &para->pCell->member.cell;
901 rc.left = c->pt.x + cell->pt.x;
902 rc.right = rc.left + cell->nWidth;
904 if (para->nFlags & MEPF_ROWSTART) {
905 ME_Cell *cell = &para->next_para->member.para.pCell->member.cell;
906 rc.right = c->pt.x + cell->pt.x;
907 } else if (para->nFlags & MEPF_ROWEND) {
908 ME_Cell *cell = &para->prev_para->member.para.pCell->member.cell;
909 rc.left = c->pt.x + cell->pt.x + cell->nWidth;
911 ME_DrawParaDecoration(c, para, y, &bounds);
912 y += bounds.top;
913 rc.left += bounds.left;
914 rc.right -= bounds.right;
916 for (p = paragraph->next; p != para->next_para; p = p->next)
918 switch(p->type) {
919 case diParagraph:
920 assert(FALSE);
921 break;
922 case diStartRow:
923 y += height;
924 rc.top = y;
925 if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) {
926 rc.bottom = y + para->nHeight;
927 } else {
928 rc.bottom = y + p->member.row.nHeight;
930 visible = RectVisible(c->hDC, &rc);
931 if (visible) {
932 FillRect(c->hDC, &rc, c->editor->hbrBackground);
934 if (me_debug)
936 const WCHAR wszRowDebug[] = {'r','o','w','[','%','d',']',0};
937 WCHAR buf[128];
938 POINT pt = c->pt;
939 wsprintfW(buf, wszRowDebug, no);
940 pt.y = 12+y;
941 ME_DebugWrite(c->hDC, &pt, buf);
944 height = p->member.row.nHeight;
945 baseline = p->member.row.nBaseline;
946 break;
947 case diRun:
948 assert(para);
949 run = &p->member.run;
950 if (visible && me_debug) {
951 RECT rc;
952 rc.left = c->pt.x + run->pt.x;
953 rc.right = rc.left + run->nWidth;
954 rc.top = c->pt.y + para->pt.y + run->pt.y;
955 rc.bottom = rc.bottom + height;
956 TRACE("rc = (%d, %d, %d, %d)\n", rc.left, rc.top, rc.right, rc.bottom);
957 if (run->nFlags & MERF_SKIPPED)
958 DrawFocusRect(c->hDC, &rc);
959 else
960 FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
962 if (visible)
963 ME_DrawRun(c, c->pt.x + run->pt.x,
964 c->pt.y + para->pt.y + run->pt.y + baseline, p, para);
965 if (me_debug)
967 /* I'm using %ls, hope wsprintfW is not going to use wrong (4-byte) WCHAR version */
968 const WCHAR wszRunDebug[] = {'[','%','d',':','%','x',']',' ','%','l','s',0};
969 WCHAR buf[2560];
970 POINT pt;
971 pt.x = c->pt.x + run->pt.x;
972 pt.y = c->pt.y + para->pt.y + run->pt.y;
973 wsprintfW(buf, wszRunDebug, no, p->member.run.nFlags, p->member.run.strText->szData);
974 ME_DebugWrite(c->hDC, &pt, buf);
976 break;
977 case diCell:
978 /* Clear any space at the bottom of the cell after the text. */
979 if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND))
980 break;
981 y += height;
982 rc.top = c->pt.y + para->pt.y + para->nHeight;
983 rc.bottom = c->pt.y + p->member.cell.pt.y + p->member.cell.nHeight;
984 if (RectVisible(c->hDC, &rc))
986 FillRect(c->hDC, &rc, c->editor->hbrBackground);
988 break;
989 default:
990 break;
992 no++;
995 ME_DrawTableBorders(c, paragraph);
997 SetTextAlign(c->hDC, align);
1000 void ME_ScrollAbs(ME_TextEditor *editor, int absY)
1002 ME_Scroll(editor, absY, 1);
1005 void ME_ScrollUp(ME_TextEditor *editor, int cy)
1007 ME_Scroll(editor, cy, 2);
1010 void ME_ScrollDown(ME_TextEditor *editor, int cy)
1012 ME_Scroll(editor, cy, 3);
1015 void ME_Scroll(ME_TextEditor *editor, int value, int type)
1017 SCROLLINFO si;
1018 int nOrigPos, nNewPos, nActualScroll;
1019 HWND hWnd;
1020 LONG winStyle;
1021 BOOL bScrollBarIsVisible, bScrollBarWillBeVisible;
1023 nOrigPos = ME_GetYScrollPos(editor);
1025 si.cbSize = sizeof(SCROLLINFO);
1026 si.fMask = SIF_POS;
1028 switch (type)
1030 case 1:
1031 /*Scroll absolutely*/
1032 si.nPos = value;
1033 break;
1034 case 2:
1035 /* Scroll up - towards the beginning of the document */
1036 si.nPos = nOrigPos - value;
1037 break;
1038 case 3:
1039 /* Scroll down - towards the end of the document */
1040 si.nPos = nOrigPos + value;
1041 break;
1042 default:
1043 FIXME("ME_Scroll called incorrectly\n");
1044 si.nPos = 0;
1047 nNewPos = SetScrollInfo(editor->hWnd, SB_VERT, &si, TRUE);
1048 editor->vert_si.nPos = nNewPos;
1049 nActualScroll = nOrigPos - nNewPos;
1050 if (abs(nActualScroll) > editor->sizeWindow.cy)
1051 InvalidateRect(editor->hWnd, NULL, TRUE);
1052 else
1053 ScrollWindowEx(editor->hWnd, 0, nActualScroll, NULL, NULL, NULL, NULL, SW_INVALIDATE);
1054 ME_Repaint(editor);
1056 hWnd = editor->hWnd;
1057 winStyle = GetWindowLongW(hWnd, GWL_STYLE);
1058 bScrollBarIsVisible = (winStyle & WS_VSCROLL) != 0;
1059 bScrollBarWillBeVisible = (editor->nTotalLength > editor->sizeWindow.cy)
1060 || (winStyle & ES_DISABLENOSCROLL);
1061 if (bScrollBarIsVisible != bScrollBarWillBeVisible)
1063 ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
1065 ME_UpdateScrollBar(editor);
1069 void ME_UpdateScrollBar(ME_TextEditor *editor)
1071 /* Note that this is the only function that should ever call SetScrolLInfo
1072 * with SIF_PAGE or SIF_RANGE. SetScrollPos and SetScrollRange should never
1073 * be used at all. */
1075 HWND hWnd;
1076 SCROLLINFO si;
1077 BOOL bScrollBarWasVisible,bScrollBarWillBeVisible;
1079 if (ME_WrapMarkedParagraphs(editor))
1080 FIXME("ME_UpdateScrollBar had to call ME_WrapMarkedParagraphs\n");
1082 hWnd = editor->hWnd;
1083 si.cbSize = sizeof(si);
1084 bScrollBarWasVisible = ME_GetYScrollVisible(editor);
1085 bScrollBarWillBeVisible = editor->nTotalLength > editor->sizeWindow.cy;
1087 si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
1088 if (GetWindowLongW(hWnd, GWL_STYLE) & ES_DISABLENOSCROLL)
1090 si.fMask |= SIF_DISABLENOSCROLL;
1091 bScrollBarWillBeVisible = TRUE;
1094 if (bScrollBarWasVisible != bScrollBarWillBeVisible)
1096 ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
1097 ME_MarkAllForWrapping(editor);
1098 ME_WrapMarkedParagraphs(editor);
1101 si.nMin = 0;
1102 si.nMax = editor->nTotalLength;
1103 si.nPos = editor->vert_si.nPos;
1104 si.nPage = editor->sizeWindow.cy;
1106 if (!(si.nMin == editor->vert_si.nMin && si.nMax == editor->vert_si.nMax && si.nPage == editor->vert_si.nPage))
1108 TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
1109 editor->vert_si.nMin = si.nMin;
1110 editor->vert_si.nMax = si.nMax;
1111 editor->vert_si.nPage = si.nPage;
1112 if (bScrollBarWillBeVisible)
1114 SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
1116 else
1118 if (bScrollBarWasVisible && !(si.fMask & SIF_DISABLENOSCROLL))
1120 SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
1121 ShowScrollBar(hWnd, SB_VERT, FALSE);
1122 ME_ScrollAbs(editor, 0);
1128 int ME_GetYScrollPos(ME_TextEditor *editor)
1130 return editor->vert_si.nPos;
1133 BOOL ME_GetYScrollVisible(ME_TextEditor *editor)
1134 { /* Returns true if the scrollbar is visible */
1135 return (editor->vert_si.nMax - editor->vert_si.nMin >= max(editor->vert_si.nPage - 1, 0));
1138 void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun)
1140 ME_DisplayItem *pRow = ME_FindItemBack(pRun, diStartRow);
1141 ME_DisplayItem *pPara = ME_FindItemBack(pRun, diParagraph);
1142 int y, yrel, yheight, yold;
1144 assert(pRow);
1145 assert(pPara);
1147 y = pPara->member.para.pt.y+pRow->member.row.pt.y;
1148 yheight = pRow->member.row.nHeight;
1149 yold = ME_GetYScrollPos(editor);
1150 yrel = y - yold;
1152 if (y < yold)
1153 ME_ScrollAbs(editor,y);
1154 else if (yrel + yheight > editor->sizeWindow.cy)
1155 ME_ScrollAbs(editor,y+yheight-editor->sizeWindow.cy);
1159 void
1160 ME_InvalidateFromOfs(ME_TextEditor *editor, int nCharOfs)
1162 RECT rc;
1163 int x, y, height;
1164 ME_Cursor tmp;
1166 ME_RunOfsFromCharOfs(editor, nCharOfs, &tmp.pRun, &tmp.nOffset);
1167 ME_GetCursorCoordinates(editor, &tmp, &x, &y, &height);
1169 rc.left = 0;
1170 rc.top = y;
1171 rc.bottom = y + height;
1172 rc.right = editor->rcFormat.right;
1173 InvalidateRect(editor->hWnd, &rc, FALSE);
1177 void
1178 ME_InvalidateSelection(ME_TextEditor *editor)
1180 ME_DisplayItem *para1, *para2;
1181 int nStart, nEnd;
1182 int len = ME_GetTextLength(editor);
1184 ME_GetSelection(editor, &nStart, &nEnd);
1185 /* if both old and new selection are 0-char (= caret only), then
1186 there's no (inverted) area to be repainted, neither old nor new */
1187 if (nStart == nEnd && editor->nLastSelStart == editor->nLastSelEnd)
1188 return;
1189 ME_WrapMarkedParagraphs(editor);
1190 ME_GetSelectionParas(editor, &para1, &para2);
1191 assert(para1->type == diParagraph);
1192 assert(para2->type == diParagraph);
1193 /* last selection markers aren't always updated, which means
1194 they can point past the end of the document */
1195 if (editor->nLastSelStart > len || editor->nLastSelEnd > len) {
1196 ME_MarkForPainting(editor,
1197 ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph),
1198 ME_FindItemFwd(editor->pBuffer->pFirst, diTextEnd));
1199 } else {
1200 /* if the start part of selection is being expanded or contracted... */
1201 if (nStart < editor->nLastSelStart) {
1202 ME_MarkForPainting(editor, para1, ME_FindItemFwd(editor->pLastSelStartPara, diParagraphOrEnd));
1203 } else
1204 if (nStart > editor->nLastSelStart) {
1205 ME_MarkForPainting(editor, editor->pLastSelStartPara, ME_FindItemFwd(para1, diParagraphOrEnd));
1208 /* if the end part of selection is being contracted or expanded... */
1209 if (nEnd < editor->nLastSelEnd) {
1210 ME_MarkForPainting(editor, para2, ME_FindItemFwd(editor->pLastSelEndPara, diParagraphOrEnd));
1211 } else
1212 if (nEnd > editor->nLastSelEnd) {
1213 ME_MarkForPainting(editor, editor->pLastSelEndPara, ME_FindItemFwd(para2, diParagraphOrEnd));
1217 ME_InvalidateMarkedParagraphs(editor);
1218 /* remember the last invalidated position */
1219 ME_GetSelection(editor, &editor->nLastSelStart, &editor->nLastSelEnd);
1220 ME_GetSelectionParas(editor, &editor->pLastSelStartPara, &editor->pLastSelEndPara);
1221 assert(editor->pLastSelStartPara->type == diParagraph);
1222 assert(editor->pLastSelEndPara->type == diParagraph);
1225 void
1226 ME_QueueInvalidateFromCursor(ME_TextEditor *editor, int nCursor)
1228 editor->nInvalidOfs = ME_GetCursorOfs(editor, nCursor);
1232 BOOL
1233 ME_SetZoom(ME_TextEditor *editor, int numerator, int denominator)
1235 /* TODO: Zoom images and objects */
1237 if (numerator != 0)
1239 if (denominator == 0)
1240 return FALSE;
1241 if (1.0 / 64.0 > (float)numerator / (float)denominator
1242 || (float)numerator / (float)denominator > 64.0)
1243 return FALSE;
1246 editor->nZoomNumerator = numerator;
1247 editor->nZoomDenominator = denominator;
1249 ME_RewrapRepaint(editor);
1250 return TRUE;