kernelbase: Reimplement number formatting values in GetLocaleInfoW/Ex using the local...
[wine.git] / dlls / riched20 / paint.c
blob245afff77dccc0f66936b2907aa2d877514b5c48
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 static void draw_paragraph( ME_Context *c, ME_Paragraph *para );
28 static inline BOOL editor_opaque( ME_TextEditor *editor )
30 return editor->back_style != TXTBACK_TRANSPARENT;
33 void editor_draw( ME_TextEditor *editor, HDC hDC, const RECT *update )
35 ME_Paragraph *para;
36 ME_Context c;
37 ME_Cell *cell;
38 int ys, ye;
39 HRGN oldRgn;
40 RECT rc, client;
41 HBRUSH brush = CreateSolidBrush( ITextHost_TxGetSysColor( editor->texthost, COLOR_WINDOW ) );
43 ME_InitContext( &c, editor, hDC );
44 if (!update)
46 client = c.rcView;
47 client.left -= editor->selofs;
48 update = &client;
51 oldRgn = CreateRectRgn(0, 0, 0, 0);
52 if (!GetClipRgn(hDC, oldRgn))
54 DeleteObject(oldRgn);
55 oldRgn = NULL;
57 IntersectClipRect( hDC, update->left, update->top, update->right, update->bottom );
59 brush = SelectObject( hDC, brush );
60 SetBkMode(hDC, TRANSPARENT);
62 para = editor_first_para( editor );
63 /* This context point is an offset for the paragraph positions stored
64 * during wrapping. It shouldn't be modified during painting. */
65 c.pt.x = c.rcView.left - editor->horz_si.nPos;
66 c.pt.y = c.rcView.top - editor->vert_si.nPos;
67 while (para_next( para ))
69 ys = c.pt.y + para->pt.y;
70 cell = para_cell( para );
71 if (cell && para == cell_end_para( cell ))
72 ye = c.pt.y + cell->pt.y + cell->nHeight;
73 else ye = ys + para->nHeight;
75 if (cell && !(para->nFlags & MEPF_ROWEND) && para == cell_first_para( cell ))
77 /* the border shifts the text down */
78 ys -= para_cell( para )->yTextOffset;
81 /* Draw the paragraph if any of the paragraph is in the update region. */
82 if (ys < update->bottom && ye > update->top)
83 draw_paragraph( &c, para );
84 para = para_next( para );
86 if (editor_opaque( editor ))
88 if (c.pt.y + editor->nTotalLength < c.rcView.bottom)
89 { /* space after the end of the text */
90 rc.top = c.pt.y + editor->nTotalLength;
91 rc.left = c.rcView.left;
92 rc.bottom = c.rcView.bottom;
93 rc.right = c.rcView.right;
94 if (IntersectRect( &rc, &rc, update ))
95 PatBlt(hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
97 if (editor->selofs)
98 { /* selection bar */
99 rc.left = c.rcView.left - editor->selofs;
100 rc.top = c.rcView.top;
101 rc.right = c.rcView.left;
102 rc.bottom = c.rcView.bottom;
103 if (IntersectRect( &rc, &rc, update ))
104 PatBlt( hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY );
108 DeleteObject( SelectObject( hDC, brush ) );
109 SelectClipRgn( hDC, oldRgn );
110 if (oldRgn) DeleteObject( oldRgn );
111 ME_DestroyContext( &c );
113 if (editor->nTotalLength != editor->nLastTotalLength || editor->nTotalWidth != editor->nLastTotalWidth)
114 ME_SendRequestResize(editor, FALSE);
115 editor->nLastTotalLength = editor->nTotalLength;
116 editor->nLastTotalWidth = editor->nTotalWidth;
119 void ME_Repaint(ME_TextEditor *editor)
121 if (ME_WrapMarkedParagraphs(editor))
123 ME_UpdateScrollBar(editor);
124 FIXME("ME_Repaint had to call ME_WrapMarkedParagraphs\n");
126 ITextHost_TxViewChange(editor->texthost, TRUE);
129 void ME_UpdateRepaint(ME_TextEditor *editor, BOOL update_now)
131 /* Should be called whenever the contents of the control have changed */
132 BOOL wrappedParagraphs;
134 wrappedParagraphs = ME_WrapMarkedParagraphs(editor);
135 if (wrappedParagraphs)
136 ME_UpdateScrollBar(editor);
138 /* Ensure that the cursor is visible */
139 editor_ensure_visible( editor, &editor->pCursors[0] );
141 update_caret( editor );
143 ITextHost_TxViewChange(editor->texthost, update_now);
145 ME_SendSelChange(editor);
147 if(editor->nEventMask & ENM_CHANGE)
149 editor->nEventMask &= ~ENM_CHANGE;
150 ITextHost_TxNotify( editor->texthost, EN_CHANGE, NULL );
151 editor->nEventMask |= ENM_CHANGE;
155 void
156 ME_RewrapRepaint(ME_TextEditor *editor)
158 /* RewrapRepaint should be called whenever the control has changed in
159 * looks, but not content. Like resizing. */
161 editor_mark_rewrap_all( editor );
162 ME_WrapMarkedParagraphs(editor);
163 ME_UpdateScrollBar(editor);
164 ME_Repaint(editor);
167 int ME_twips2pointsX(const ME_Context *c, int x)
169 if (c->editor->nZoomNumerator == 0)
170 return x * c->dpi.cx / 1440;
171 else
172 return x * c->dpi.cx * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator;
175 int ME_twips2pointsY(const ME_Context *c, int y)
177 if (c->editor->nZoomNumerator == 0)
178 return y * c->dpi.cy / 1440;
179 else
180 return y * c->dpi.cy * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator;
184 static int calc_y_offset( const ME_Context *c, ME_Style *style )
186 int offs = 0, twips = 0;
188 if ((style->fmt.dwMask & style->fmt.dwEffects) & CFM_OFFSET)
189 twips = style->fmt.yOffset;
191 if ((style->fmt.dwMask & style->fmt.dwEffects) & (CFM_SUPERSCRIPT | CFM_SUBSCRIPT))
193 if (style->fmt.dwEffects & CFE_SUPERSCRIPT) twips = style->fmt.yHeight/3;
194 if (style->fmt.dwEffects & CFE_SUBSCRIPT) twips = -style->fmt.yHeight/12;
197 if (twips) offs = ME_twips2pointsY( c, twips );
199 return offs;
202 static COLORREF get_text_color( ME_Context *c, ME_Style *style, BOOL highlight )
204 COLORREF color;
206 if (highlight)
207 color = ITextHost_TxGetSysColor( c->editor->texthost, COLOR_HIGHLIGHTTEXT );
208 else if ((style->fmt.dwMask & CFM_LINK) && (style->fmt.dwEffects & CFE_LINK))
209 color = RGB(0,0,255);
210 else if ((style->fmt.dwMask & CFM_COLOR) && (style->fmt.dwEffects & CFE_AUTOCOLOR))
211 color = ITextHost_TxGetSysColor( c->editor->texthost, COLOR_WINDOWTEXT );
212 else
213 color = style->fmt.crTextColor;
215 return color;
218 static COLORREF get_back_color( ME_Context *c, ME_Style *style, BOOL highlight )
220 COLORREF color;
222 if (highlight)
223 color = ITextHost_TxGetSysColor( c->editor->texthost, COLOR_HIGHLIGHT );
224 else if ( (style->fmt.dwMask & CFM_BACKCOLOR)
225 && !(style->fmt.dwEffects & CFE_AUTOBACKCOLOR) )
226 color = style->fmt.crBackColor;
227 else
228 color = ITextHost_TxGetSysColor( c->editor->texthost, COLOR_WINDOW );
230 return color;
233 static HPEN get_underline_pen( ME_Style *style, COLORREF color )
235 if (style->fmt.dwEffects & CFE_LINK)
236 return CreatePen( PS_SOLID, 1, color );
238 /* Choose the pen type for underlining the text. */
239 if (style->fmt.dwEffects & CFE_UNDERLINE)
241 switch (style->fmt.bUnderlineType)
243 case CFU_UNDERLINE:
244 case CFU_UNDERLINEWORD: /* native seems to map it to simple underline (MSDN) */
245 case CFU_UNDERLINEDOUBLE: /* native seems to map it to simple underline (MSDN) */
246 return CreatePen( PS_SOLID, 1, color );
247 case CFU_UNDERLINEDOTTED:
248 return CreatePen( PS_DOT, 1, color );
249 default:
250 FIXME( "Unknown underline type (%u)\n", style->fmt.bUnderlineType );
251 /* fall through */
252 case CFU_CF1UNDERLINE: /* this type is supported in the font, do nothing */
253 case CFU_UNDERLINENONE:
254 break;
257 return NULL;
260 static void draw_underline( ME_Context *c, ME_Run *run, int x, int y, COLORREF color )
262 HPEN pen;
264 pen = get_underline_pen( run->style, color );
265 if (pen)
267 HPEN old_pen = SelectObject( c->hDC, pen );
268 MoveToEx( c->hDC, x, y + 1, NULL );
269 LineTo( c->hDC, x + run->nWidth, y + 1 );
270 SelectObject( c->hDC, old_pen );
271 DeleteObject( pen );
273 return;
276 /*********************************************************************
277 * draw_space
279 * Draw the end-of-paragraph or tab space.
281 * If actually_draw is TRUE then ensure any underline is drawn.
283 static void draw_space( ME_Context *c, ME_Run *run, int x, int y,
284 BOOL selected, BOOL actually_draw, int ymin, int cy )
286 HDC hdc = c->hDC;
287 BOOL old_style_selected = FALSE;
288 RECT rect;
289 COLORREF back_color = 0;
291 SetRect( &rect, x, ymin, x + run->nWidth, ymin + cy );
293 if (c->editor->bHideSelection || (!c->editor->bHaveFocus && (c->editor->props & TXTBIT_HIDESELECTION)))
294 selected = FALSE;
295 if (c->editor->bEmulateVersion10)
297 old_style_selected = selected;
298 selected = FALSE;
301 if (selected)
302 back_color = ITextHost_TxGetSysColor( c->editor->texthost, COLOR_HIGHLIGHT );
304 if (actually_draw)
306 COLORREF text_color = get_text_color( c, run->style, selected );
307 COLORREF old_text, old_back;
308 int y_offset = calc_y_offset( c, run->style );
309 static const WCHAR space[1] = {' '};
311 select_style( c, run->style );
312 old_text = SetTextColor( hdc, text_color );
313 if (selected) old_back = SetBkColor( hdc, back_color );
315 ExtTextOutW( hdc, x, y - y_offset, selected ? ETO_OPAQUE : 0, &rect, space, 1, &run->nWidth );
317 if (selected) SetBkColor( hdc, old_back );
318 SetTextColor( hdc, old_text );
320 draw_underline( c, run, x, y - y_offset, text_color );
322 else if (selected)
324 HBRUSH brush = CreateSolidBrush( back_color );
325 FillRect( hdc, &rect, brush );
326 DeleteObject( brush );
329 if (old_style_selected)
330 PatBlt( hdc, x, ymin, run->nWidth, cy, DSTINVERT );
333 static void get_selection_rect( ME_Context *c, ME_Run *run, int from, int to, int cy, RECT *r )
335 from = max( 0, from );
336 to = min( run->len, to );
337 r->left = ME_PointFromCharContext( c, run, from, TRUE );
338 r->top = 0;
339 r->right = ME_PointFromCharContext( c, run, to, TRUE );
340 r->bottom = cy;
341 return;
344 static void draw_text( ME_Context *c, ME_Run *run, int x, int y, BOOL selected, RECT *sel_rect )
346 COLORREF text_color = get_text_color( c, run->style, selected );
347 COLORREF back_color = get_back_color( c, run->style, selected );
348 COLORREF old_text, old_back = 0;
349 const WCHAR *text = get_text( run, 0 );
350 ME_String *masked = NULL;
351 const BOOL paint_bg = ( selected
352 || ( ( run->style->fmt.dwMask & CFM_BACKCOLOR )
353 && !(CFE_AUTOBACKCOLOR & run->style->fmt.dwEffects) )
356 if (c->editor->password_char)
358 masked = ME_MakeStringR( c->editor->password_char, run->len );
359 text = masked->szData;
362 old_text = SetTextColor( c->hDC, text_color );
363 if (paint_bg) old_back = SetBkColor( c->hDC, back_color );
365 if (run->para->nFlags & MEPF_COMPLEX)
366 ScriptTextOut( c->hDC, &run->style->script_cache, x, y, paint_bg ? ETO_OPAQUE : 0, sel_rect,
367 &run->script_analysis, NULL, 0, run->glyphs, run->num_glyphs, run->advances,
368 NULL, run->offsets );
369 else
370 ExtTextOutW( c->hDC, x, y, paint_bg ? ETO_OPAQUE : 0, sel_rect, text, run->len, NULL );
372 if (paint_bg) SetBkColor( c->hDC, old_back );
373 SetTextColor( c->hDC, old_text );
375 draw_underline( c, run, x, y, text_color );
377 ME_DestroyString( masked );
378 return;
382 static void draw_text_with_style( ME_Context *c, ME_Run *run, int x, int y,
383 int nSelFrom, int nSelTo, int ymin, int cy )
385 HDC hDC = c->hDC;
386 int yOffset = 0;
387 BOOL selected = (nSelFrom < run->len && nSelTo >= 0
388 && nSelFrom < nSelTo && !c->editor->bHideSelection &&
389 (c->editor->bHaveFocus || !(c->editor->props & TXTBIT_HIDESELECTION)));
390 BOOL old_style_selected = FALSE;
391 RECT sel_rect;
392 HRGN clip = NULL, sel_rgn = NULL;
394 yOffset = calc_y_offset( c, run->style );
396 if (selected)
398 get_selection_rect( c, run, nSelFrom, nSelTo, cy, &sel_rect );
399 OffsetRect( &sel_rect, x, ymin );
401 if (c->editor->bEmulateVersion10)
403 old_style_selected = TRUE;
404 selected = FALSE;
406 else
408 sel_rgn = CreateRectRgnIndirect( &sel_rect );
409 clip = CreateRectRgn( 0, 0, 0, 0 );
410 if (GetClipRgn( hDC, clip ) != 1)
412 DeleteObject( clip );
413 clip = NULL;
418 select_style( c, run->style );
420 if (sel_rgn) ExtSelectClipRgn( hDC, sel_rgn, RGN_DIFF );
422 if (!(run->style->fmt.dwEffects & CFE_AUTOBACKCOLOR)
423 && (run->style->fmt.dwMask & CFM_BACKCOLOR) )
425 RECT tmp_rect;
426 get_selection_rect( c, run, 0, run->len, cy, &tmp_rect );
427 OffsetRect( &tmp_rect, x, ymin );
428 draw_text( c, run, x, y - yOffset, FALSE, &tmp_rect );
430 else
431 draw_text( c, run, x, y - yOffset, FALSE, NULL );
433 if (sel_rgn)
435 ExtSelectClipRgn( hDC, clip, RGN_COPY );
436 ExtSelectClipRgn( hDC, sel_rgn, RGN_AND );
437 draw_text( c, run, x, y - yOffset, TRUE, &sel_rect );
438 ExtSelectClipRgn( hDC, clip, RGN_COPY );
439 if (clip) DeleteObject( clip );
440 DeleteObject( sel_rgn );
443 if (old_style_selected)
444 PatBlt( hDC, sel_rect.left, ymin, sel_rect.right - sel_rect.left, cy, DSTINVERT );
447 static void ME_DebugWrite(HDC hDC, const POINT *pt, LPCWSTR szText) {
448 int align = SetTextAlign(hDC, TA_LEFT|TA_TOP);
449 HGDIOBJ hFont = SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
450 COLORREF color = SetTextColor(hDC, RGB(128,128,128));
451 TextOutW(hDC, pt->x, pt->y, szText, lstrlenW(szText));
452 SelectObject(hDC, hFont);
453 SetTextAlign(hDC, align);
454 SetTextColor(hDC, color);
457 static void draw_run( ME_Context *c, int x, int y, ME_Cursor *cursor )
459 ME_Row *row;
460 ME_Run *run = cursor->run;
461 int runofs = run_char_ofs( run, cursor->nOffset );
462 LONG nSelFrom, nSelTo;
464 if (run->nFlags & MERF_HIDDEN) return;
466 row = row_from_cursor( cursor );
467 ME_GetSelectionOfs(c->editor, &nSelFrom, &nSelTo);
469 /* Draw selected end-of-paragraph mark */
470 if (run->nFlags & MERF_ENDPARA)
472 if (runofs >= nSelFrom && runofs < nSelTo)
474 draw_space( c, run, x, y, TRUE, FALSE,
475 c->pt.y + run->para->pt.y + row->pt.y, row->nHeight );
477 return;
480 if (run->nFlags & (MERF_TAB | MERF_ENDCELL))
482 BOOL selected = runofs >= nSelFrom && runofs < nSelTo;
484 draw_space( c, run, x, y, selected, TRUE,
485 c->pt.y + run->para->pt.y + row->pt.y, row->nHeight );
486 return;
489 if (run->nFlags & MERF_GRAPHICS)
490 draw_ole( c, x, y, run, (runofs >= nSelFrom) && (runofs < nSelTo) );
491 else
492 draw_text_with_style( c, run, x, y, nSelFrom - runofs, nSelTo - runofs,
493 c->pt.y + run->para->pt.y + row->pt.y, row->nHeight );
496 /* The documented widths are in points (72 dpi), but converting them to
497 * 96 dpi (standard display resolution) avoids dealing with fractions. */
498 static const struct {unsigned width : 8, pen_style : 4, dble : 1;} border_details[] = {
499 /* none */ {0, PS_SOLID, FALSE},
500 /* 3/4 */ {1, PS_SOLID, FALSE},
501 /* 1 1/2 */ {2, PS_SOLID, FALSE},
502 /* 2 1/4 */ {3, PS_SOLID, FALSE},
503 /* 3 */ {4, PS_SOLID, FALSE},
504 /* 4 1/2 */ {6, PS_SOLID, FALSE},
505 /* 6 */ {8, PS_SOLID, FALSE},
506 /* 3/4 double */ {1, PS_SOLID, TRUE},
507 /* 1 1/2 double */ {2, PS_SOLID, TRUE},
508 /* 2 1/4 double */ {3, PS_SOLID, TRUE},
509 /* 3/4 gray */ {1, PS_DOT /* FIXME */, FALSE},
510 /* 1 1/2 dashed */ {2, PS_DASH, FALSE},
513 static const COLORREF pen_colors[16] = {
514 /* Black */ RGB(0x00, 0x00, 0x00), /* Blue */ RGB(0x00, 0x00, 0xFF),
515 /* Cyan */ RGB(0x00, 0xFF, 0xFF), /* Green */ RGB(0x00, 0xFF, 0x00),
516 /* Magenta */ RGB(0xFF, 0x00, 0xFF), /* Red */ RGB(0xFF, 0x00, 0x00),
517 /* Yellow */ RGB(0xFF, 0xFF, 0x00), /* White */ RGB(0xFF, 0xFF, 0xFF),
518 /* Dark blue */ RGB(0x00, 0x00, 0x80), /* Dark cyan */ RGB(0x00, 0x80, 0x80),
519 /* Dark green */ RGB(0x00, 0x80, 0x80), /* Dark magenta */ RGB(0x80, 0x00, 0x80),
520 /* Dark red */ RGB(0x80, 0x00, 0x00), /* Dark yellow */ RGB(0x80, 0x80, 0x00),
521 /* Dark gray */ RGB(0x80, 0x80, 0x80), /* Light gray */ RGB(0xc0, 0xc0, 0xc0),
524 static int ME_GetBorderPenWidth(const ME_Context* c, int idx)
526 int width = border_details[idx].width;
528 if (c->dpi.cx != 96)
529 width = MulDiv(width, c->dpi.cx, 96);
531 if (c->editor->nZoomNumerator != 0)
532 width = MulDiv(width, c->editor->nZoomNumerator, c->editor->nZoomDenominator);
534 return width;
537 int ME_GetParaBorderWidth(const ME_Context* c, int flags)
539 int idx = (flags >> 8) & 0xF;
540 int width;
542 if (idx >= ARRAY_SIZE(border_details))
544 FIXME("Unsupported border value %d\n", idx);
545 return 0;
547 width = ME_GetBorderPenWidth(c, idx);
548 if (border_details[idx].dble) width = width * 2 + 1;
549 return width;
552 static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT* bounds)
554 int idx, border_width, top_border, bottom_border;
555 RECT rc;
556 BOOL hasParaBorder;
558 SetRectEmpty(bounds);
559 if (!(para->fmt.dwMask & (PFM_BORDER | PFM_SPACEBEFORE | PFM_SPACEAFTER))) return;
561 border_width = top_border = bottom_border = 0;
562 idx = (para->fmt.wBorders >> 8) & 0xF;
563 hasParaBorder = (!(c->editor->bEmulateVersion10 &&
564 para->fmt.dwMask & PFM_TABLE &&
565 para->fmt.wEffects & PFE_TABLE) &&
566 (para->fmt.dwMask & PFM_BORDER) &&
567 idx != 0 &&
568 (para->fmt.wBorders & 0xF));
569 if (hasParaBorder)
571 /* FIXME: wBorders is not stored as MSDN says in v1.0 - 4.1 of richedit
572 * controls. It actually stores the paragraph or row border style. Although
573 * the value isn't used for drawing, it is used for streaming out rich text.
575 * wBorders stores the border style for each side (top, left, bottom, right)
576 * using nibble (4 bits) to store each border style. The rich text format
577 * control words, and their associated value are the following:
578 * \brdrdash 0
579 * \brdrdashsm 1
580 * \brdrdb 2
581 * \brdrdot 3
582 * \brdrhair 4
583 * \brdrs 5
584 * \brdrth 6
585 * \brdrtriple 7
587 * The order of the sides stored actually differs from v1.0 to 3.0 and v4.1.
588 * The mask corresponding to each side for the version are the following:
589 * mask v1.0-3.0 v4.1
590 * 0x000F top left
591 * 0x00F0 left top
592 * 0x0F00 bottom right
593 * 0xF000 right bottom
595 if (para->fmt.wBorders & 0x00B0)
596 FIXME("Unsupported border flags %x\n", para->fmt.wBorders);
597 border_width = ME_GetParaBorderWidth(c, para->fmt.wBorders);
598 if (para->fmt.wBorders & 4) top_border = border_width;
599 if (para->fmt.wBorders & 8) bottom_border = border_width;
602 if (para->fmt.dwMask & PFM_SPACEBEFORE)
604 bounds->top = ME_twips2pointsY(c, para->fmt.dySpaceBefore);
605 if (editor_opaque( c->editor ))
607 rc.left = c->rcView.left;
608 rc.right = c->rcView.right;
609 rc.top = y;
610 rc.bottom = y + bounds->top + top_border;
611 PatBlt(c->hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
615 if (para->fmt.dwMask & PFM_SPACEAFTER)
617 bounds->bottom = ME_twips2pointsY(c, para->fmt.dySpaceAfter);
618 if (editor_opaque( c->editor ))
620 rc.left = c->rcView.left;
621 rc.right = c->rcView.right;
622 rc.bottom = y + para->nHeight;
623 rc.top = rc.bottom - bounds->bottom - bottom_border;
624 PatBlt(c->hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
628 /* Native richedit doesn't support paragraph borders in v1.0 - 4.1,
629 * but might support it in later versions. */
630 if (hasParaBorder) {
631 int pen_width, rightEdge;
632 COLORREF pencr;
633 HPEN pen = NULL, oldpen = NULL;
634 POINT pt;
636 if (para->fmt.wBorders & 64) /* autocolor */
637 pencr = ITextHost_TxGetSysColor(c->editor->texthost,
638 COLOR_WINDOWTEXT);
639 else
640 pencr = pen_colors[(para->fmt.wBorders >> 12) & 0xF];
642 rightEdge = c->pt.x + max(c->editor->sizeWindow.cx,
643 c->editor->nTotalWidth);
645 pen_width = ME_GetBorderPenWidth(c, idx);
646 pen = CreatePen(border_details[idx].pen_style, pen_width, pencr);
647 oldpen = SelectObject(c->hDC, pen);
648 MoveToEx(c->hDC, 0, 0, &pt);
650 /* before & after spaces are not included in border */
652 /* helper to draw the double lines in case of corner */
653 #define DD(x) ((para->fmt.wBorders & (x)) ? (pen_width + 1) : 0)
655 if (para->fmt.wBorders & 1)
657 MoveToEx(c->hDC, c->pt.x, y + bounds->top, NULL);
658 LineTo(c->hDC, c->pt.x, y + para->nHeight - bounds->bottom);
659 if (border_details[idx].dble) {
660 rc.left = c->pt.x + 1;
661 rc.right = rc.left + border_width;
662 rc.top = y + bounds->top;
663 rc.bottom = y + para->nHeight - bounds->bottom;
664 PatBlt(c->hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
665 MoveToEx(c->hDC, c->pt.x + pen_width + 1, y + bounds->top + DD(4), NULL);
666 LineTo(c->hDC, c->pt.x + pen_width + 1, y + para->nHeight - bounds->bottom - DD(8));
668 bounds->left += border_width;
670 if (para->fmt.wBorders & 2)
672 MoveToEx(c->hDC, rightEdge - 1, y + bounds->top, NULL);
673 LineTo(c->hDC, rightEdge - 1, y + para->nHeight - bounds->bottom);
674 if (border_details[idx].dble) {
675 rc.left = rightEdge - pen_width - 1;
676 rc.right = rc.left + pen_width;
677 rc.top = y + bounds->top;
678 rc.bottom = y + para->nHeight - bounds->bottom;
679 PatBlt(c->hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
680 MoveToEx(c->hDC, rightEdge - 1 - pen_width - 1, y + bounds->top + DD(4), NULL);
681 LineTo(c->hDC, rightEdge - 1 - pen_width - 1, y + para->nHeight - bounds->bottom - DD(8));
683 bounds->right += border_width;
685 if (para->fmt.wBorders & 4)
687 MoveToEx(c->hDC, c->pt.x, y + bounds->top, NULL);
688 LineTo(c->hDC, rightEdge, y + bounds->top);
689 if (border_details[idx].dble) {
690 MoveToEx(c->hDC, c->pt.x + DD(1), y + bounds->top + pen_width + 1, NULL);
691 LineTo(c->hDC, rightEdge - DD(2), y + bounds->top + pen_width + 1);
693 bounds->top += border_width;
695 if (para->fmt.wBorders & 8)
697 MoveToEx(c->hDC, c->pt.x, y + para->nHeight - bounds->bottom - 1, NULL);
698 LineTo(c->hDC, rightEdge, y + para->nHeight - bounds->bottom - 1);
699 if (border_details[idx].dble) {
700 MoveToEx(c->hDC, c->pt.x + DD(1), y + para->nHeight - bounds->bottom - 1 - pen_width - 1, NULL);
701 LineTo(c->hDC, rightEdge - DD(2), y + para->nHeight - bounds->bottom - 1 - pen_width - 1);
703 bounds->bottom += border_width;
705 #undef DD
707 MoveToEx(c->hDC, pt.x, pt.y, NULL);
708 SelectObject(c->hDC, oldpen);
709 DeleteObject(pen);
713 static void draw_table_borders( ME_Context *c, ME_Paragraph *para )
715 if (!c->editor->bEmulateVersion10) /* v4.1 */
717 if (para_cell( para ))
719 RECT rc;
720 ME_Cell *cell = para_cell( para );
721 ME_Paragraph *after_row;
722 HPEN pen, oldPen;
723 LOGBRUSH logBrush;
724 HBRUSH brush;
725 COLORREF color;
726 POINT oldPt;
727 int width;
728 BOOL atTop = (para == cell_first_para( cell ));
729 BOOL atBottom = (para == cell_end_para( cell ));
730 int top = c->pt.y + (atTop ? cell->pt.y : para->pt.y);
731 int bottom = (atBottom ?
732 c->pt.y + cell->pt.y + cell->nHeight :
733 top + para->nHeight + (atTop ? cell->yTextOffset : 0));
734 rc.left = c->pt.x + cell->pt.x;
735 rc.right = rc.left + cell->nWidth;
736 if (atTop)
738 /* Erase gap before text if not all borders are the same height. */
739 width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
740 rc.top = top + width;
741 width = cell->yTextOffset - width;
742 rc.bottom = rc.top + width;
743 if (width && editor_opaque( c->editor ))
744 PatBlt(c->hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
746 /* Draw cell borders.
747 * The order borders are draw in is left, top, bottom, right in order
748 * to be consistent with native richedit. This is noticeable from the
749 * overlap of borders of different colours. */
750 if (!(para->nFlags & MEPF_ROWEND))
752 rc.top = top;
753 rc.bottom = bottom;
754 if (cell->border.left.width > 0)
756 color = cell->border.left.colorRef;
757 width = max(ME_twips2pointsX(c, cell->border.left.width), 1);
759 else
761 color = RGB(192,192,192);
762 width = 1;
764 logBrush.lbStyle = BS_SOLID;
765 logBrush.lbColor = color;
766 logBrush.lbHatch = 0;
767 pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
768 width, &logBrush, 0, NULL);
769 oldPen = SelectObject(c->hDC, pen);
770 MoveToEx(c->hDC, rc.left, rc.top, &oldPt);
771 LineTo(c->hDC, rc.left, rc.bottom);
772 SelectObject(c->hDC, oldPen);
773 DeleteObject(pen);
774 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
777 if (atTop)
779 if (cell->border.top.width > 0)
781 brush = CreateSolidBrush(cell->border.top.colorRef);
782 width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
784 else
786 brush = GetStockObject(LTGRAY_BRUSH);
787 width = 1;
789 rc.top = top;
790 rc.bottom = rc.top + width;
791 FillRect(c->hDC, &rc, brush);
792 if (cell->border.top.width > 0)
793 DeleteObject(brush);
796 /* Draw the bottom border if at the last paragraph in the cell, and when
797 * in the last row of the table. */
798 if (atBottom)
800 int oldLeft = rc.left;
801 width = max(ME_twips2pointsY(c, cell->border.bottom.width), 1);
802 after_row = para_next( table_row_end( para ) );
803 if (after_row->nFlags & MEPF_ROWSTART)
805 ME_Cell *next_end;
806 next_end = table_row_end_cell( after_row );
807 assert( next_end && !cell_next( next_end ) );
808 rc.left = c->pt.x + next_end->pt.x;
810 if (rc.left < rc.right)
812 if (cell->border.bottom.width > 0)
813 brush = CreateSolidBrush(cell->border.bottom.colorRef);
814 else
815 brush = GetStockObject(LTGRAY_BRUSH);
816 rc.bottom = bottom;
817 rc.top = rc.bottom - width;
818 FillRect(c->hDC, &rc, brush);
819 if (cell->border.bottom.width > 0)
820 DeleteObject(brush);
822 rc.left = oldLeft;
825 /* Right border only drawn if at the end of the table row. */
826 if (!cell_next( cell_next( cell ) ) && !(para->nFlags & MEPF_ROWSTART))
828 rc.top = top;
829 rc.bottom = bottom;
830 if (cell->border.right.width > 0)
832 color = cell->border.right.colorRef;
833 width = max(ME_twips2pointsX(c, cell->border.right.width), 1);
835 else
837 color = RGB(192,192,192);
838 width = 1;
840 logBrush.lbStyle = BS_SOLID;
841 logBrush.lbColor = color;
842 logBrush.lbHatch = 0;
843 pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
844 width, &logBrush, 0, NULL);
845 oldPen = SelectObject(c->hDC, pen);
846 MoveToEx(c->hDC, rc.right - 1, rc.top, &oldPt);
847 LineTo(c->hDC, rc.right - 1, rc.bottom);
848 SelectObject(c->hDC, oldPen);
849 DeleteObject(pen);
850 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
854 else /* v1.0 - 3.0 */
856 /* Draw simple table border */
857 if (para_in_table( para ))
859 HPEN pen = NULL, oldpen = NULL;
860 int i, firstX, startX, endX, rowY, rowBottom, nHeight;
861 POINT oldPt;
863 pen = CreatePen(PS_SOLID, 0, para->border.top.colorRef);
864 oldpen = SelectObject(c->hDC, pen);
866 /* Find the start relative to the text */
867 firstX = c->pt.x + para_first_run( para )->pt.x;
868 /* Go back by the horizontal gap, which is stored in dxOffset */
869 firstX -= ME_twips2pointsX(c, para->fmt.dxOffset);
870 /* The left edge, stored in dxStartIndent affected just the first edge */
871 startX = firstX - ME_twips2pointsX(c, para->fmt.dxStartIndent);
872 rowY = c->pt.y + para->pt.y;
873 if (para->fmt.dwMask & PFM_SPACEBEFORE)
874 rowY += ME_twips2pointsY(c, para->fmt.dySpaceBefore);
875 nHeight = para_first_row( para )->nHeight;
876 rowBottom = rowY + nHeight;
878 /* Draw horizontal lines */
879 MoveToEx(c->hDC, firstX, rowY, &oldPt);
880 i = para->fmt.cTabCount - 1;
881 endX = startX + ME_twips2pointsX(c, para->fmt.rgxTabs[i] & 0x00ffffff) + 1;
882 LineTo(c->hDC, endX, rowY);
883 /* The bottom of the row only needs to be drawn if the next row is
884 * not a table. */
885 if (!(para_next( para ) && para_in_table( para_next( para ) ) && para->nRows == 1))
887 /* Decrement rowBottom to draw the bottom line within the row, and
888 * to not draw over this line when drawing the vertical lines. */
889 rowBottom--;
890 MoveToEx(c->hDC, firstX, rowBottom, NULL);
891 LineTo(c->hDC, endX, rowBottom);
894 /* Draw vertical lines */
895 MoveToEx(c->hDC, firstX, rowY, NULL);
896 LineTo(c->hDC, firstX, rowBottom);
897 for (i = 0; i < para->fmt.cTabCount; i++)
899 int rightBoundary = para->fmt.rgxTabs[i] & 0x00ffffff;
900 endX = startX + ME_twips2pointsX(c, rightBoundary);
901 MoveToEx(c->hDC, endX, rowY, NULL);
902 LineTo(c->hDC, endX, rowBottom);
905 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
906 SelectObject(c->hDC, oldpen);
907 DeleteObject(pen);
912 static void draw_para_number( ME_Context *c, ME_Paragraph *para )
914 int x, y;
915 COLORREF old_text;
917 if (para->fmt.wNumbering)
919 select_style( c, para->para_num.style );
920 old_text = SetTextColor( c->hDC, get_text_color( c, para->para_num.style, FALSE ) );
922 x = c->pt.x + para->para_num.pt.x;
923 y = c->pt.y + para->pt.y + para->para_num.pt.y;
925 ExtTextOutW( c->hDC, x, y, 0, NULL, para->para_num.text->szData, para->para_num.text->nLen, NULL );
927 SetTextColor( c->hDC, old_text );
931 static void draw_paragraph( ME_Context *c, ME_Paragraph *para )
933 int align = SetTextAlign(c->hDC, TA_BASELINE);
934 ME_DisplayItem *p;
935 ME_Cell *cell;
936 ME_Run *run;
937 RECT rc, bounds;
938 int y;
939 int height = 0, baseline = 0, no=0;
940 BOOL visible = FALSE;
942 rc.left = c->pt.x;
943 rc.right = c->rcView.right;
945 y = c->pt.y + para->pt.y;
946 if ((cell = para_cell( para )))
948 rc.left = c->pt.x + cell->pt.x;
949 rc.right = rc.left + cell->nWidth;
951 if (para->nFlags & MEPF_ROWSTART)
953 cell = table_row_first_cell( para );
954 rc.right = c->pt.x + cell->pt.x;
956 else if (para->nFlags & MEPF_ROWEND)
958 cell = table_row_end_cell( para );
959 rc.left = c->pt.x + cell->pt.x + cell->nWidth;
961 ME_DrawParaDecoration(c, para, y, &bounds);
962 y += bounds.top;
963 if (bounds.left || bounds.right)
965 rc.left = max(rc.left, c->pt.x + bounds.left);
966 rc.right = min(rc.right, c->pt.x - bounds.right
967 + max(c->editor->sizeWindow.cx,
968 c->editor->nTotalWidth));
971 for (p = para_get_di( para )->next; p != para_get_di( para_next( para ) ); p = p->next)
973 switch(p->type) {
974 case diParagraph:
975 assert(FALSE);
976 break;
977 case diStartRow:
978 y += height;
979 rc.top = y;
980 if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) {
981 rc.bottom = y + para->nHeight;
982 } else {
983 rc.bottom = y + p->member.row.nHeight;
985 visible = RectVisible(c->hDC, &rc);
986 if (editor_opaque( c->editor ) && visible)
987 PatBlt(c->hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
988 if (bounds.right)
990 /* If scrolled to the right past the end of the text, then
991 * there may be space to the right of the paragraph border. */
992 RECT after_bdr = rc;
993 after_bdr.left = rc.right + bounds.right;
994 after_bdr.right = c->rcView.right;
995 if (editor_opaque( c->editor ) && RectVisible( c->hDC, &after_bdr ))
996 PatBlt(c->hDC, after_bdr.left, after_bdr.top, after_bdr.right - after_bdr.left,
997 after_bdr.bottom - after_bdr.top, PATCOPY);
999 if (me_debug)
1001 WCHAR buf[128];
1002 POINT pt = c->pt;
1003 wsprintfW( buf, L"row[%d]", no );
1004 pt.y = 12+y;
1005 ME_DebugWrite(c->hDC, &pt, buf);
1008 height = p->member.row.nHeight;
1009 baseline = p->member.row.nBaseline;
1010 break;
1011 case diRun:
1012 assert(para);
1013 run = &p->member.run;
1014 if (visible && me_debug) {
1015 RECT rc;
1016 rc.left = c->pt.x + run->pt.x;
1017 rc.right = rc.left + run->nWidth;
1018 rc.top = c->pt.y + para->pt.y + run->pt.y;
1019 rc.bottom = rc.top + height;
1020 TRACE("rc = %s\n", wine_dbgstr_rect(&rc));
1021 FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
1023 if (visible)
1025 ME_Cursor cursor;
1027 cursor.run = run;
1028 cursor.para = para;
1029 cursor.nOffset = 0;
1030 draw_run( c, c->pt.x + run->pt.x, c->pt.y + para->pt.y + run->pt.y + baseline, &cursor );
1032 if (me_debug)
1034 WCHAR buf[2560];
1035 POINT pt;
1036 pt.x = c->pt.x + run->pt.x;
1037 pt.y = c->pt.y + para->pt.y + run->pt.y;
1038 wsprintfW( buf, L"[%d:%x] %ls", no, p->member.run.nFlags, get_text( &p->member.run, 0 ));
1039 ME_DebugWrite(c->hDC, &pt, buf);
1041 break;
1042 default:
1043 break;
1045 no++;
1048 if (editor_opaque( c->editor ) && para_cell( para ))
1050 /* Clear any space at the bottom of the cell after the text. */
1051 rc.top = c->pt.y + para->pt.y + para->nHeight;
1052 rc.bottom = c->pt.y + cell->pt.y + cell->nHeight;
1053 PatBlt( c->hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY );
1056 draw_table_borders( c, para );
1057 draw_para_number( c, para );
1059 SetTextAlign( c->hDC, align );
1062 static void enable_show_scrollbar( ME_TextEditor *editor, INT bar, BOOL enable )
1064 if (enable || editor->scrollbars & ES_DISABLENOSCROLL)
1065 ITextHost_TxEnableScrollBar( editor->texthost, bar, enable ? 0 : ESB_DISABLE_BOTH );
1066 if (!(editor->scrollbars & ES_DISABLENOSCROLL))
1067 ITextHost_TxShowScrollBar( editor->texthost, bar, enable );
1070 static void set_scroll_range_pos( ME_TextEditor *editor, INT bar, SCROLLINFO *info, BOOL set_range, BOOL notify )
1072 LONG max_pos = info->nMax, pos = info->nPos;
1074 /* Scale the scrollbar info to 16-bit values. */
1075 if (max_pos > 0xffff)
1077 pos = MulDiv( pos, 0xffff, max_pos );
1078 max_pos = 0xffff;
1080 if (set_range) ITextHost_TxSetScrollRange( editor->texthost, bar, 0, max_pos, FALSE );
1081 ITextHost_TxSetScrollPos( editor->texthost, bar, pos, TRUE );
1083 if (notify && editor->nEventMask & ENM_SCROLL)
1084 ITextHost_TxNotify( editor->texthost, bar == SB_VERT ? EN_VSCROLL : EN_HSCROLL, NULL );
1087 void scroll_abs( ME_TextEditor *editor, int x, int y, BOOL notify )
1089 int scrollX = 0, scrollY = 0;
1091 if (editor->horz_si.nPos != x) {
1092 x = min(x, editor->horz_si.nMax);
1093 x = max(x, editor->horz_si.nMin);
1094 scrollX = editor->horz_si.nPos - x;
1095 editor->horz_si.nPos = x;
1096 set_scroll_range_pos( editor, SB_HORZ, &editor->horz_si, FALSE, notify );
1099 if (editor->vert_si.nPos != y) {
1100 y = min(y, editor->vert_si.nMax - (int)editor->vert_si.nPage);
1101 y = max(y, editor->vert_si.nMin);
1102 scrollY = editor->vert_si.nPos - y;
1103 editor->vert_si.nPos = y;
1104 set_scroll_range_pos( editor, SB_VERT, &editor->vert_si, FALSE, notify );
1107 if (abs(scrollX) > editor->sizeWindow.cx || abs(scrollY) > editor->sizeWindow.cy)
1108 ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE);
1109 else
1110 ITextHost_TxScrollWindowEx(editor->texthost, scrollX, scrollY,
1111 &editor->rcFormat, &editor->rcFormat,
1112 NULL, NULL, SW_INVALIDATE);
1113 ME_UpdateScrollBar(editor);
1114 ME_Repaint(editor);
1117 void scroll_h_abs( ME_TextEditor *editor, int x, BOOL notify )
1119 scroll_abs( editor, x, editor->vert_si.nPos, notify );
1122 void scroll_v_abs( ME_TextEditor *editor, int y, BOOL notify )
1124 scroll_abs( editor, editor->horz_si.nPos, y, notify );
1127 void ME_ScrollUp(ME_TextEditor *editor, int cy)
1129 scroll_v_abs( editor, editor->vert_si.nPos - cy, TRUE );
1132 void ME_ScrollDown(ME_TextEditor *editor, int cy)
1134 scroll_v_abs( editor, editor->vert_si.nPos + cy, TRUE );
1137 void ME_ScrollLeft(ME_TextEditor *editor, int cx)
1139 scroll_h_abs( editor, editor->horz_si.nPos - cx, TRUE );
1142 void ME_ScrollRight(ME_TextEditor *editor, int cx)
1144 scroll_h_abs( editor, editor->horz_si.nPos + cx, TRUE );
1147 void ME_UpdateScrollBar(ME_TextEditor *editor)
1149 /* Note that this is the only function that should ever call
1150 * SetScrollInfo with SIF_PAGE or SIF_RANGE. */
1151 BOOL enable;
1153 if (ME_WrapMarkedParagraphs(editor))
1154 FIXME("ME_UpdateScrollBar had to call ME_WrapMarkedParagraphs\n");
1156 /* Update horizontal scrollbar */
1157 enable = editor->nTotalWidth > editor->sizeWindow.cx;
1158 if (editor->horz_si.nPos && !enable)
1160 scroll_h_abs( editor, 0, TRUE );
1161 /* ME_HScrollAbs will call this function, so nothing else needs to be done here. */
1162 return;
1165 if (editor->scrollbars & WS_HSCROLL && !enable ^ !editor->horz_sb_enabled)
1167 editor->horz_sb_enabled = enable;
1168 enable_show_scrollbar( editor, SB_HORZ, enable );
1171 if (editor->horz_si.nMax != editor->nTotalWidth || editor->horz_si.nPage != editor->sizeWindow.cx)
1173 editor->horz_si.nMax = editor->nTotalWidth;
1174 editor->horz_si.nPage = editor->sizeWindow.cx;
1175 TRACE( "min = %d max = %d page = %d\n", editor->horz_si.nMin, editor->horz_si.nMax, editor->horz_si.nPage );
1176 if ((enable || editor->horz_sb_enabled) && editor->scrollbars & WS_HSCROLL)
1177 set_scroll_range_pos( editor, SB_HORZ, &editor->horz_si, TRUE, TRUE );
1180 /* Update vertical scrollbar */
1181 enable = editor->nTotalLength > editor->sizeWindow.cy && (editor->props & TXTBIT_MULTILINE);
1183 if (editor->vert_si.nPos && !enable)
1185 scroll_v_abs( editor, 0, TRUE );
1186 /* ME_VScrollAbs will call this function, so nothing else needs to be done here. */
1187 return;
1190 if (editor->scrollbars & WS_VSCROLL && !enable ^ !editor->vert_sb_enabled)
1192 editor->vert_sb_enabled = enable;
1193 enable_show_scrollbar( editor, SB_VERT, enable );
1196 if (editor->vert_si.nMax != editor->nTotalLength || editor->vert_si.nPage != editor->sizeWindow.cy)
1198 editor->vert_si.nMax = editor->nTotalLength;
1199 editor->vert_si.nPage = editor->sizeWindow.cy;
1200 TRACE( "min = %d max = %d page = %d\n", editor->vert_si.nMin, editor->vert_si.nMax, editor->vert_si.nPage );
1201 if ((enable || editor->vert_sb_enabled) && editor->scrollbars & WS_VSCROLL)
1202 set_scroll_range_pos( editor, SB_VERT, &editor->vert_si, TRUE, TRUE );
1206 void editor_ensure_visible( ME_TextEditor *editor, ME_Cursor *cursor )
1208 ME_Run *run = cursor->run;
1209 ME_Row *row = row_from_cursor( cursor );
1210 ME_Paragraph *para = cursor->para;
1211 int x, y, yheight;
1214 if (editor->scrollbars & ES_AUTOHSCROLL)
1216 x = run->pt.x + ME_PointFromChar( editor, run, cursor->nOffset, TRUE );
1217 if (x > editor->horz_si.nPos + editor->sizeWindow.cx)
1218 x = x + 1 - editor->sizeWindow.cx;
1219 else if (x > editor->horz_si.nPos)
1220 x = editor->horz_si.nPos;
1222 if (~editor->scrollbars & ES_AUTOVSCROLL)
1224 scroll_h_abs( editor, x, TRUE );
1225 return;
1228 else
1230 if (~editor->scrollbars & ES_AUTOVSCROLL) return;
1231 x = editor->horz_si.nPos;
1234 y = para->pt.y + row->pt.y;
1235 yheight = row->nHeight;
1237 if (y < editor->vert_si.nPos)
1238 scroll_abs( editor, x, y, TRUE );
1239 else if (y + yheight > editor->vert_si.nPos + editor->sizeWindow.cy)
1240 scroll_abs( editor, x, y + yheight - editor->sizeWindow.cy, TRUE );
1241 else if (x != editor->horz_si.nPos)
1242 scroll_abs( editor, x, editor->vert_si.nPos, TRUE );
1246 void
1247 ME_InvalidateSelection(ME_TextEditor *editor)
1249 ME_Paragraph *sel_start, *sel_end;
1250 ME_Paragraph *repaint_start = NULL, *repaint_end = NULL;
1251 LONG nStart, nEnd;
1252 int len = ME_GetTextLength(editor);
1254 ME_GetSelectionOfs(editor, &nStart, &nEnd);
1255 /* if both old and new selection are 0-char (= caret only), then
1256 there's no (inverted) area to be repainted, neither old nor new */
1257 if (nStart == nEnd && editor->nLastSelStart == editor->nLastSelEnd)
1258 return;
1259 ME_WrapMarkedParagraphs(editor);
1260 editor_get_selection_paras( editor, &sel_start, &sel_end );
1262 /* last selection markers aren't always updated, which means
1263 * they can point past the end of the document */
1264 if (editor->nLastSelStart > len || editor->nLastSelEnd > len)
1266 repaint_start = editor_first_para( editor );
1267 repaint_end = para_prev( editor_end_para( editor ) );
1269 else
1271 /* if the start part of selection is being expanded or contracted... */
1272 if (nStart < editor->nLastSelStart)
1274 repaint_start = sel_start;
1275 repaint_end = editor->last_sel_start_para;
1277 else if (nStart > editor->nLastSelStart)
1279 repaint_start = editor->last_sel_start_para;
1280 repaint_end = sel_start;
1283 /* if the end part of selection is being contracted or expanded... */
1284 if (nEnd < editor->nLastSelEnd)
1286 if (!repaint_start) repaint_start = sel_end;
1287 repaint_end = editor->last_sel_end_para;
1289 else if (nEnd > editor->nLastSelEnd)
1291 if (!repaint_start) repaint_start = editor->last_sel_end_para;
1292 repaint_end = sel_end;
1296 if (repaint_start)
1297 para_range_invalidate( editor, repaint_start, repaint_end );
1298 /* remember the last invalidated position */
1299 ME_GetSelectionOfs(editor, &editor->nLastSelStart, &editor->nLastSelEnd);
1300 editor_get_selection_paras( editor, &editor->last_sel_start_para, &editor->last_sel_end_para );
1303 BOOL
1304 ME_SetZoom(ME_TextEditor *editor, int numerator, int denominator)
1306 /* TODO: Zoom images and objects */
1308 if (numerator == 0 && denominator == 0)
1310 editor->nZoomNumerator = editor->nZoomDenominator = 0;
1311 return TRUE;
1313 if (numerator <= 0 || denominator <= 0)
1314 return FALSE;
1315 if (numerator * 64 <= denominator || numerator / denominator >= 64)
1316 return FALSE;
1318 editor->nZoomNumerator = numerator;
1319 editor->nZoomDenominator = denominator;
1321 ME_RewrapRepaint(editor);
1322 return TRUE;