riched20: Implement EM_FORMATRANGE and add test cases
Lei Zhang
thestig at google.com
Sun Jun 10 04:33:24 CDT 2007
Hi,
Troy Rollo wrote this patch well over a year ago, but it never got
accepted. I updated it with a conformance test and made a small
modification so it passes the conformance test.
-------------- next part --------------
From e5782c40c7bb2e0b92718bc47cf3f61fed31f367 Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig at google.com>
Date: Sun, 10 Jun 2007 02:27:49 -0700
Subject: [PATCH] riched20: Implement EM_FORMATRANGE and add test cases
---
dlls/riched20/context.c | 16 ++
dlls/riched20/editor.c | 6 -
dlls/riched20/editor.h | 9 +
dlls/riched20/editstr.h | 26 +++
dlls/riched20/list.c | 154 ++++++++++++++++++++
dlls/riched20/paint.c | 316 ++++++++++++++++++++++++++++++++++++++----
dlls/riched20/run.c | 42 ++++--
dlls/riched20/style.c | 117 +++++++++++-----
dlls/riched20/tests/editor.c | 38 +++++
dlls/riched20/wrap.c | 19 ++-
10 files changed, 664 insertions(+), 79 deletions(-)
diff --git a/dlls/riched20/context.c b/dlls/riched20/context.c
index 7eef556..5ca22e5 100644
--- a/dlls/riched20/context.c
+++ b/dlls/riched20/context.c
@@ -24,6 +24,22 @@ void ME_InitContext(ME_Context *c, ME_Te
{
c->nSequence = editor->nSequence++;
c->hDC = hDC;
+ c->hdcMeasure = hDC;
+ c->fr.hdc = hDC;
+ c->fr.hdcTarget = hDC;
+ c->fr.rc.left = 0;
+ c->fr.rc.top = ME_GetYScrollPos(editor);
+ c->fr.rc.right = editor->sizeWindow.cx;
+ c->fr.rc.bottom = editor->sizeWindow.cy + ME_GetYScrollPos(editor);
+ c->fr.rcPage = c->fr.rc;
+ c->fr.chrg.cpMin = 0;
+ c->fr.chrg.cpMax = -1;
+ c->bClip = FALSE;
+ c->bHideSelection = FALSE;
+ c->dDraw.cx = GetDeviceCaps(hDC, LOGPIXELSX);
+ c->dDraw.cy = GetDeviceCaps(hDC, LOGPIXELSY);
+ c->dTarget = c->dDraw;
+ c->mRender = 0;
c->editor = editor;
c->pt.x = 0;
c->pt.y = 0;
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index b83fb41..597c1dc 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -39,7 +39,7 @@
+ EM_FINDTEXTEX (only FR_DOWN flag implemented)
- EM_FINDWORDBREAK
- EM_FMTLINES
- - EM_FORMATRANGE
+ + EM_FORMATRANGE
+ EM_GETAUTOURLDETECT 2.0
- EM_GETBIDIOPTIONS 3.0
- EM_GETCHARFORMAT (partly done)
@@ -198,7 +198,6 @@
* - horizontal scrolling (not even started)
* - hysteresis during wrapping (related to scrollbars appearing/disappearing)
* - find/replace
- * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
* - italic caret with italic fonts
* - IME
* - most notifications aren't sent at all (the most important ones are)
@@ -1430,7 +1429,6 @@ static LRESULT RichEditWndProc_common(HW
UNSUPPORTED_MSG(EM_DISPLAYBAND)
UNSUPPORTED_MSG(EM_FINDWORDBREAK)
UNSUPPORTED_MSG(EM_FMTLINES)
- UNSUPPORTED_MSG(EM_FORMATRANGE)
UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
UNSUPPORTED_MSG(EM_GETEDITSTYLE)
UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
@@ -2287,6 +2285,8 @@ static LRESULT RichEditWndProc_common(HW
}
return MAKELONG( pt.x, pt.y );
}
+ case EM_FORMATRANGE:
+ return ME_FormatContent(editor, (FORMATRANGE *) lParam, (BOOL) wParam);
case WM_CREATE:
if (GetWindowLongW(hWnd, GWL_STYLE) & WS_HSCROLL)
{ /* Squelch the default horizontal scrollbar it would make */
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h
index f71842b..f4e10d4 100644
--- a/dlls/riched20/editor.h
+++ b/dlls/riched20/editor.h
@@ -73,6 +73,9 @@ CHARFORMAT2W *ME_ToCFAny(CHARFORMAT2W *t
void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from);
void ME_CopyCharFormat(CHARFORMAT2W *pDest, CHARFORMAT2W *pSrc); /* only works with 2W structs */
void ME_CharFormatFromLogFont(HDC hDC, LOGFONTW *lf, CHARFORMAT2W *fmt); /* ditto */
+ME_Style *ME_MapStyle(ME_StyleMap *m, ME_Style *s);
+ME_StyleMap *ME_MakeStyleMap(void);
+void ME_FreeStyleMap(ME_StyleMap *m);
/* list.c */
void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat);
@@ -87,6 +90,7 @@ void ME_DestroyDisplayItem(ME_DisplayIte
void ME_DestroyTableCellList(ME_DisplayItem *item);
void ME_DumpDocument(ME_TextBuffer *buffer);
const char *ME_GetDITypeName(ME_DIType type);
+void ME_DuplicateText(ME_DisplayItem *pFirst, ME_DisplayItem *pLast, ME_DisplayItem **ppNew, ME_DisplayItem **ppNewLast);
/* string.c */
int ME_GetOptimalBuffer(int nLen);
@@ -149,8 +153,8 @@ ME_DisplayItem *ME_InsertRunAtCursor(ME_
ME_Style *style, const WCHAR *str, int len, int flags);
void ME_CheckCharOffsets(ME_TextEditor *editor);
void ME_PropagateCharOffset(ME_DisplayItem *p, int shift);
-void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize);
-int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run);
+void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize, ME_Dimension *pd);
+int ME_CharFromPoint(ME_Context *c, int cx, ME_Run *run);
/* this one accounts for 1/2 char tolerance */
int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run);
int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset);
@@ -237,6 +241,7 @@ void ME_MarkForPainting(ME_TextEditor *e
void ME_MarkAllForWrapping(ME_TextEditor *editor);
/* paint.c */
+LPARAM ME_FormatContent(ME_TextEditor *editor, FORMATRANGE *pfr, BOOL bNoOutput);
void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, RECT *rcUpdate);
void ME_Repaint(ME_TextEditor *editor);
void ME_RewrapRepaint(ME_TextEditor *editor);
diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h
index ee5dfe3..22103bb 100644
--- a/dlls/riched20/editstr.h
+++ b/dlls/riched20/editstr.h
@@ -324,9 +324,34 @@ typedef struct tagME_TextEditor
BOOL bHaveFocus;
} ME_TextEditor;
+typedef struct tagME_Dimension
+{
+ unsigned cx;
+ unsigned cy;
+} ME_Dimension;
+
+typedef struct tagME_StyleMapping
+{
+ ME_Style *from;
+ ME_Style *to;
+ struct tagME_StyleMapping *next;
+} ME_StyleMapping;
+
+typedef struct tagME_StyleMap
+{
+ ME_StyleMapping *first;
+} ME_StyleMap;
+
typedef struct tagME_Context
{
HDC hDC;
+ HDC hdcMeasure;
+ FORMATRANGE fr;
+ BOOL bClip;
+ BOOL bHideSelection;
+ ME_Dimension dDraw;
+ ME_Dimension dTarget;
+ ME_StyleMap *mRender;
POINT pt;
POINT ptRowOffset;
RECT rcView;
@@ -352,5 +377,4 @@ typedef struct tagME_WrapContext
ME_DisplayItem *pLastSplittableRun;
POINT ptLastSplittableRun;
} ME_WrapContext;
-
#endif
diff --git a/dlls/riched20/list.c b/dlls/riched20/list.c
index ad7b4b6..14db791 100644
--- a/dlls/riched20/list.c
+++ b/dlls/riched20/list.c
@@ -2,6 +2,7 @@
* RichEdit - Basic operations on double linked lists.
*
* Copyright 2004 by Krzysztof Foltman
+ * Copyright 2006 CorVu Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -126,6 +127,139 @@ void ME_DestroyDisplayItem(ME_DisplayIte
FREE_OBJ(item);
}
+static void ME_DuplicateTableCellList(ME_DisplayItem *dst, ME_DisplayItem *src);
+
+static ME_DisplayItem *ME_DuplicateDisplayItem(ME_DisplayItem *item,
+ ME_DisplayItem *prev,
+ ME_StyleMap *m)
+{
+ ME_DisplayItem *mdi = ALLOC_OBJ(ME_DisplayItem);
+
+ *mdi = *item;
+ mdi->prev = prev;
+ mdi->next = 0;
+ if (prev)
+ prev->next = mdi;
+
+ if (item->type == diParagraph || item->type == diUndoSplitParagraph)
+ {
+ ME_DuplicateTableCellList(mdi, item);
+ mdi->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
+ *mdi->member.para.pFmt = *item->member.para.pFmt;
+ mdi->member.para.next_para = 0;
+
+ /* Now link up the top level paragraph with its preceding top
+ * level paragraph.
+ */
+ if (mdi->member.para.prev_para)
+ {
+ ME_DisplayItem *from, *to;
+ ME_DisplayItem *target = mdi->member.para.prev_para;
+
+ mdi->member.para.prev_para = 0;
+
+ for (from = item, to = mdi;
+ from && to;
+ from = from->prev, to = to->prev)
+ {
+ if (from == target)
+ {
+ mdi->member.para.prev_para = to;
+ if (to->type == diParagraph || to->type == diUndoSplitParagraph)
+ to->member.para.next_para = mdi;
+ break;
+ }
+ }
+ }
+ }
+
+ if (item->type == diRun || item->type == diUndoInsertRun)
+ {
+ /* We need to copy the style and text, in the case of styles
+ * we need to reduce duplication which we can do with a style
+ * map.
+ */
+ mdi->member.run.style = ME_MapStyle(m, mdi->member.run.style);
+ mdi->member.run.strText = ME_StrDup(item->member.run.strText);
+
+ if (mdi->member.run.pCell)
+ {
+ /* Find the right ME_TableCell to link to by walking both the
+ * old and new list backwards.
+ */
+ ME_DisplayItem *from, *to;
+ ME_TableCell *target = mdi->member.run.pCell;
+
+ mdi->member.run.pCell = 0;
+ for (from = item, to = mdi;
+ !mdi->member.run.pCell && from && to;
+ from = from->prev, to = to->prev)
+ {
+ ME_TableCell *tt, *tf;
+
+ if (from->type != diParagraph && from->type != diUndoSplitParagraph)
+ continue;
+
+ for (tf = from->member.para.pCells, tt = from->member.para.pCells;
+ tf && tt;
+ tf = tf->next, tt = tt->next)
+ {
+ if (tf == target)
+ {
+ mdi->member.run.pCell = tt;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (item->type == diUndoSetCharFormat || item->type == diUndoSetDefaultCharFormat)
+ {
+ /* Just map the style back */
+ mdi->member.ustyle = ME_MapStyle(m, mdi->member.ustyle);
+ }
+ if (item->type == diTextEnd)
+ {
+ /* Make sure the last top level paragraph is linked up */
+ ME_DisplayItem *from, *to;
+
+ mdi->member.para.prev_para = 0;
+
+ for (from = item, to = mdi;
+ from && to;
+ from = from->prev, to = to->prev)
+ {
+ if ((from->type == diParagraph || from->type == diUndoSplitParagraph) &&
+ from->member.para.next_para == item)
+ {
+ to->member.para.next_para = mdi;
+ break;
+ }
+ }
+ }
+ return mdi;
+}
+
+/* ME_DuplicateText makes a duplicate of all ME_DisplayItem structures starting
+ * with pFirst and ending with pLast.
+ */
+void ME_DuplicateText(ME_DisplayItem *pFirst, ME_DisplayItem *pLast, ME_DisplayItem **ppNew, ME_DisplayItem **ppNewLast)
+{
+ ME_DisplayItem *pNow;
+ ME_StyleMap *m = ME_MakeStyleMap();
+
+ pNow = ME_DuplicateDisplayItem(pFirst, NULL, m);
+ *ppNew = pNow;
+ while (pFirst != pLast)
+ {
+ pFirst = pFirst->next;
+ pNow = ME_DuplicateDisplayItem(pFirst, pNow, m);
+ *ppNewLast = pNow;
+ }
+ ME_FreeStyleMap(m);
+}
+
void
ME_DestroyTableCellList(ME_DisplayItem *item)
{
@@ -143,6 +277,26 @@ ME_DestroyTableCellList(ME_DisplayItem *
}
}
+static void
+ME_DuplicateTableCellList(ME_DisplayItem *dst,
+ ME_DisplayItem *src)
+{
+ ME_TableCell **tail = &dst->member.para.pCells;
+ ME_TableCell *cell = src->member.para.pCells;
+
+ *tail = dst->member.para.pLastCell = NULL;
+
+ while (cell)
+ {
+ ME_TableCell *p = ALLOC_OBJ(ME_TableCell);
+
+ p->nRightBoundary = cell->nRightBoundary;
+ *tail = dst->member.para.pLastCell = p;
+ cell = cell->next;
+ tail = &p->next;
+ }
+}
+
ME_DisplayItem *ME_MakeDI(ME_DIType type) {
ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem);
ZeroMemory(item, sizeof(ME_DisplayItem));
diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c
index 63a0c4c..a1a32ec 100644
--- a/dlls/riched20/paint.c
+++ b/dlls/riched20/paint.c
@@ -3,6 +3,7 @@
*
* Copyright 2004 by Krzysztof Foltman
* Copyright 2005 by Phil Krylov
+ * Copyright 2006 CorVu Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,6 +24,189 @@ #include "editor.h"
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
+static int transform_x(ME_Context *c, int x)
+{
+ if (c->dTarget.cx == c->dDraw.cx)
+ return x;
+ return MulDiv(x, c->dDraw.cx, c->dTarget.cx);
+}
+
+static int transform_y(ME_Context *c, int y)
+{
+ if (c->dTarget.cy == c->dDraw.cy)
+ return y;
+ return MulDiv(y, c->dDraw.cy, c->dTarget.cy);
+}
+
+/*
+ * Support EM_FORMATRANGE.
+ *
+ * *** Notes on tables ***
+ *
+ * The CHARRANGE structure passed in the FORMATRANGE structure is not
+ * sufficient to deal with tables, where we would need information on the
+ * start position for text in each column of the table.
+ *
+ * Testing on Windows NT shows that the Microsoft Rich Edit control gives
+ * pathological results when there are tables in the document - both in edit
+ * mode and for EM_FORMATRANGE. The Microsoft Rich Edit control uses the
+ * table solely as a hint to where the paragraph should start. The table
+ * does not expand to fit its text. Long paragraphs wrap when you hit the
+ * right page margin and continue at the left page margin.
+ *
+ * If the paragraph ends outside its cell, the paragraph in the next cell
+ * continues at the X position immediately after the next cell boundary after
+ * the position of the last character in the paragraph. This may not be the
+ * cell associated with the paragraph, and may be the right hand table
+ * boundary (so that the paragraph commences after the right hand edge of the
+ * table). If the paragraph ends after the right hand table boundary, then the
+ * next paragraph begins on the next row of output at the X position for the
+ * second cell in the table.
+ *
+ * While all this wrapping is going on, the table borders will be positioned
+ * as if there were only one line of text in each cell of the table and the
+ * text fits neatly within the cells.
+ *
+ * This behaviour is sufficiently useless that it is probably not worth trying
+ * to duplicate, and given that CHARRANGE is insufficient to deal with tables,
+ * there seems little point in attempting to make our implementation of
+ * EM_FORMATRANGE do something sensible for tables. A lot of effort would be
+ * involved in doing so, and we would be doing something Microsoft has not
+ * done.
+ */
+
+LPARAM ME_FormatContent(ME_TextEditor *editor, FORMATRANGE *pfr, BOOL bDraw)
+{
+ ME_Context c;
+ int iBGMode;
+ ME_DisplayItem *pFirst;
+ ME_DisplayItem *pLast;
+ ME_DisplayItem *pNow;
+ ME_DisplayItem *pIntra;
+ int yNow;
+ int cyRow;
+ int cyOffset = -1;
+ int iEndOffset = -1;
+
+ if (!pfr)
+ return ME_GetTextLength(editor);
+
+ ME_InitContext(&c, editor, pfr->hdcTarget);
+ c.fr = *pfr;
+ c.bHideSelection = TRUE;
+ c.bClip = TRUE;
+
+ c.rcView.left = MulDiv(c.fr.rc.left, c.dTarget.cx, 1440);
+ c.rcView.right = MulDiv(c.fr.rc.right, c.dTarget.cx, 1440);
+ c.rcView.top = MulDiv(c.fr.rc.top, c.dTarget.cy, 1440);
+ c.rcView.bottom = MulDiv(c.fr.rc.bottom, c.dTarget.cy, 1440);
+
+ c.pt.x = 0;
+ c.pt.y = 0;
+
+ /* Get a copy of all the text in the control so we can perform calculations
+ * without interfering with the values cached for the on-screen data.
+ */
+
+ ME_DuplicateText(editor->pBuffer->pFirst, editor->pBuffer->pLast, &pFirst, &pLast);
+
+ /* Now perform wrapping on the resulting data, and find the vertical offset of
+ * the row at the start of the character range, and the character offset of the
+ * first non-printable character.
+ */
+
+ for (pNow = pFirst->next; pNow != pLast; pNow = pNow->member.para.next_para)
+ {
+ pNow->member.para.nYPos = c.pt.y;
+ pNow->member.para.nFlags |= MEPF_REWRAP;
+ ME_WrapTextParagraph(&c, pNow);
+
+ if (iEndOffset == -1)
+ {
+ int iParaOfs = pNow->member.para.nCharOfs;
+
+ yNow = c.pt.y;
+ cyRow = 0;
+
+ for (pIntra = pNow; pIntra != pNow->member.para.next_para; pIntra = pIntra->next)
+ {
+ if (pIntra->type == diStartRow)
+ {
+ yNow += cyRow;
+ cyRow = pIntra->member.row.nHeight;
+ }
+ else if (pIntra->type == diRun)
+ {
+ int iRunOfs = iParaOfs + pIntra->member.run.nCharOfs;
+ int iRunEndOfs = iRunOfs + ME_VPosToPos(pIntra->member.run.strText,
+ pIntra->member.run.strText ?
+ pIntra->member.run.strText->nLen : 1);
+ if (cyOffset == -1 && iRunEndOfs > c.fr.chrg.cpMin)
+ cyOffset = yNow;
+ if (cyOffset != -1 && iEndOffset == -1)
+ {
+ if (yNow > cyOffset &&
+ yNow + cyRow > c.rcView.bottom - c.rcView.top + cyOffset)
+ {
+ iEndOffset = iRunOfs;
+ break;
+ }
+ if (c.fr.chrg.cpMax >= 0 &&
+ iRunEndOfs > c.fr.chrg.cpMax)
+ {
+ int iBottom = yNow + cyRow - cyOffset + c.rcView.top;;
+
+ if (c.rcView.bottom > iBottom)
+ c.rcView.bottom = iBottom;
+ iEndOffset = iRunOfs;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ c.pt.y += pNow->member.para.nHeight;
+ }
+
+ if (iEndOffset == -1)
+ iEndOffset = ME_CharOfsFromRunOfs(editor, ME_FindItemBack(pLast, diRun), 0);
+
+ /* Then draw the data */
+ /* FIXME - transformation of non-text runs is not done yet */
+
+ if (bDraw)
+ {
+ c.mRender = ME_MakeStyleMap();
+ iBGMode = SetBkMode(c.hDC, TRANSPARENT);
+ c.dDraw.cx = GetDeviceCaps(c.fr.hdc, LOGPIXELSX);
+ c.dDraw.cy = GetDeviceCaps(c.fr.hdc, LOGPIXELSY);
+ c.hDC = c.fr.hdc;
+ c.hdcMeasure = c.fr.hdcTarget;
+ c.pt.x = 0;
+ c.pt.y = c.rcView.top - cyOffset;
+
+ for (pNow = pFirst->next; pNow != pLast; pNow = pNow->member.para.next_para)
+ {
+ if (c.pt.y + pNow->member.para.nHeight > c.rcView.top &&
+ c.pt.y < c.rcView.bottom)
+ ME_DrawParagraph(&c, pNow);
+ c.pt.y += pNow->member.para.nHeight;
+ }
+ SetBkMode(c.hDC, iBGMode);
+ ME_FreeStyleMap(c.mRender);
+ }
+
+ while (pFirst)
+ {
+ pNow = pFirst;
+ pFirst = pFirst->next;
+ ME_DestroyDisplayItem(pNow);
+ }
+
+ return iEndOffset;
+}
+
void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, RECT *rcUpdate) {
ME_DisplayItem *item;
ME_Context c;
@@ -141,12 +325,15 @@ ME_RewrapRepaint(ME_TextEditor *editor)
static void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText, int nChars,
- ME_Style *s, int *width, int nSelFrom, int nSelTo, int ymin, int cy) {
+ ME_Style *sIn, int *width, int nSelFrom, int nSelTo, int ymin, int cy) {
+ ME_Style *s = (c->mRender ? ME_MapStyle(c->mRender, sIn) : sIn);
HDC hDC = c->hDC;
+ HDC hdcTarget = c->hdcMeasure;
+ ME_TextEditor *editor = ((hDC == hdcTarget) ? c->editor : NULL);
HGDIOBJ hOldFont;
COLORREF rgbOld, rgbBack;
int yOffset = 0, yTwipsOffset = 0;
- hOldFont = ME_SelectStyleFont(c->editor, hDC, s);
+ hOldFont = ME_SelectStyleFont(editor, hDC, s);
rgbBack = ME_GetBackColor(c->editor);
if ((s->fmt.dwMask & CFM_LINK) && (s->fmt.dwEffects & CFE_LINK))
rgbOld = SetTextColor(hDC, RGB(0,0,255));
@@ -166,14 +353,68 @@ static void ME_DrawTextWithStyle(ME_Cont
int numerator = 1;
int denominator = 1;
- if (c->editor->nZoomNumerator)
+ if (editor && editor->nZoomNumerator)
{
- numerator = c->editor->nZoomNumerator;
- denominator = c->editor->nZoomDenominator;
+ numerator = editor->nZoomNumerator;
+ denominator = editor->nZoomDenominator;
}
yOffset = yTwipsOffset * GetDeviceCaps(hDC, LOGPIXELSY) * numerator / denominator / 1440;
}
- ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nChars, NULL);
+ if (hDC != hdcTarget)
+ {
+ WCHAR *szTmp = ALLOC_N_OBJ(WCHAR, nChars);
+ WCHAR *szGlyphs = ALLOC_N_OBJ(WCHAR, nChars * 2);
+ int *lpDx = ALLOC_N_OBJ(int, nChars * 2);
+ HFONT holdFont = ME_SelectStyleFont(NULL, hdcTarget, sIn);
+ int i;
+
+ GCP_RESULTSW gcpr;
+
+ /* First, deal with any re-ordering in the text */
+
+ memset(&gcpr, 0, sizeof(gcpr));
+ gcpr.lStructSize = sizeof(gcpr);
+ gcpr.nGlyphs = nChars;
+ gcpr.lpOutString = szTmp;
+ GetCharacterPlacementW(hDC, szText, nChars, 0, &gcpr, GCP_REORDER);
+
+ /* Then convert the string to glyph indeces and position according to target */
+
+ gcpr.lpOutString = 0;
+ gcpr.lpGlyphs = szGlyphs;
+ gcpr.lpDx = lpDx;
+ gcpr.nGlyphs = nChars * 2;
+
+ GetCharacterPlacementW(hdcTarget, szTmp, nChars, 0, &gcpr, 0);
+ /* FIXME - the flags should be GCP_DIACRITIC | GCP_GLYPHSHAPE, but
+ * GetCharacterPlacement ignores these and spews FIXME lines for each call.
+ */
+
+ /* Then scale the horizontal positions for WYSIWYG output */
+
+ for (i = 1; i < gcpr.nGlyphs; ++i)
+ lpDx[i] += lpDx[i - 1];
+ for (i = 0; i < gcpr.nGlyphs; ++i)
+ lpDx[i] = transform_x(c, lpDx[i]);
+ for (i = gcpr.nGlyphs - 1; i > 0; --i)
+ lpDx[i] -= lpDx[i - 1];
+
+ /* Finally, output the glyphs to the real output device */
+
+ ExtTextOutW(hDC, transform_x(c, x), transform_y(c, y - yOffset),
+ ETO_GLYPH_INDEX, NULL, szGlyphs, gcpr.nGlyphs, lpDx);
+
+ /* And clean up */
+
+ FREE_OBJ(lpDx);
+ FREE_OBJ(szGlyphs);
+ FREE_OBJ(szTmp);
+ ME_UnselectStyleFont(NULL, hdcTarget, sIn, holdFont);
+ }
+ else
+ {
+ ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nChars, NULL);
+ }
if (width) {
SIZE sz;
GetTextExtentPoint32W(hDC, szText, nChars, &sz);
@@ -189,11 +430,13 @@ static void ME_DrawTextWithStyle(ME_Cont
GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
/* Invert selection if not hidden by EM_HIDESELECTION */
- if (c->editor->bHideSelection == FALSE)
+ if (editor && editor->bHideSelection == FALSE && c->bHideSelection == FALSE)
PatBlt(hDC, x, ymin, sz.cx, cy, DSTINVERT);
}
SetTextColor(hDC, rgbOld);
- ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);
+ ME_UnselectStyleFont(editor, hDC, s, hOldFont);
+ if (c->mRender)
+ ME_ReleaseStyle(s);
}
static void ME_DebugWrite(HDC hDC, POINT *pt, WCHAR *szText) {
@@ -210,7 +453,7 @@ static void ME_DrawGraphics(ME_Context *
ME_Paragraph *para, BOOL selected) {
SIZE sz;
int xs, ys, xe, ye, h, ym, width, eyes;
- ME_GetGraphicsSize(c->editor, run, &sz);
+ ME_GetGraphicsSize(c->editor, run, &sz, &c->dDraw);
xs = run->pt.x;
ys = y-sz.cy;
xe = xs+sz.cx;
@@ -258,7 +501,8 @@ static void ME_DrawRun(ME_Context *c, in
return;
if (run->nFlags & MERF_GRAPHICS)
- ME_DrawGraphics(c, x, y, run, para, (runofs >= nSelFrom) && (runofs < nSelTo));
+ ME_DrawGraphics(c, x, y, run, para,
+ !c->editor->bHideSelection && !c->bHideSelection && (runofs >= nSelFrom) && (runofs < nSelTo));
else
{
if (c->editor->cPasswordMask)
@@ -292,7 +536,7 @@ void ME_DrawParagraph(ME_Context *c, ME_
ME_DisplayItem *p;
ME_Run *run;
ME_Paragraph *para = NULL;
- RECT rc, rcPara;
+ RECT rc, rcPara, rcParaDraw;
int y = c->pt.y;
int height = 0, baseline = 0, no=0, pno = 0;
int xs, xe;
@@ -315,22 +559,40 @@ void ME_DrawParagraph(ME_Context *c, ME_
y += height;
rcPara.top = y;
rcPara.bottom = y+p->member.row.nHeight;
- visible = RectVisible(c->hDC, &rcPara);
+
+ /* A row is visible if:
+ *
+ * Part of the rectangle is visible AND
+ * (The bottom part of the row is in the clip range OR
+ * Some part of the row is in the clip range and the top starts
+ * at or before before the top of the clip range)
+ */
+
+ rcParaDraw.left = transform_x(c, rcPara.left);
+ rcParaDraw.top = transform_y(c, rcPara.top);
+ rcParaDraw.right = transform_x(c, rcPara.right);
+ rcParaDraw.bottom = transform_y(c, rcPara.bottom);
+
+ visible = RectVisible(c->hDC, &rcParaDraw) &&
+ (!c->bClip ||
+ (rcPara.bottom > c->rcView.top &&
+ (rcPara.bottom <= c->rcView.bottom ||
+ rcPara.top <= c->rcView.top)));
if (visible) {
HBRUSH hbr;
hbr = CreateSolidBrush(ME_GetBackColor(c->editor));
/* left margin */
- rc.left = c->rcView.left;
- rc.right = c->rcView.left+nMargWidth;
- rc.top = y;
- rc.bottom = y+p->member.row.nHeight;
+ rc.left = transform_x(c, c->rcView.left);
+ rc.right = transform_x(c, c->rcView.left+nMargWidth);
+ rc.top = transform_y(c, y);
+ rc.bottom = transform_y(c, y+p->member.row.nHeight);
FillRect(c->hDC, &rc, hbr/* c->hbrMargin */);
/* right margin */
- rc.left = xe;
- rc.right = c->rcView.right;
+ rc.left = transform_x(c, xe);
+ rc.right = transform_x(c, c->rcView.right);
FillRect(c->hDC, &rc, hbr/* c->hbrMargin */);
- rc.left = c->rcView.left+nMargWidth;
- rc.right = xe;
+ rc.left = transform_x(c, c->rcView.left+nMargWidth);
+ rc.right = transform_x(c, xe);
FillRect(c->hDC, &rc, hbr);
DeleteObject(hbr);
}
@@ -352,10 +614,10 @@ void ME_DrawParagraph(ME_Context *c, ME_
assert(para);
run = &p->member.run;
if (visible && me_debug) {
- rc.left = c->rcView.left+run->pt.x;
- rc.right = c->rcView.left+run->pt.x+run->nWidth;
- rc.top = c->pt.y+run->pt.y;
- rc.bottom = c->pt.y+run->pt.y+height;
+ rc.left = transform_x(c, c->rcView.left+run->pt.x);
+ rc.right = transform_x(c, c->rcView.left+run->pt.x+run->nWidth);
+ rc.top = transform_y(c, c->pt.y+run->pt.y);
+ rc.bottom = transform_y(c, c->pt.y+run->pt.y+height);
TRACE("rc = (%d, %d, %d, %d)\n", rc.left, rc.top, rc.right, rc.bottom);
if (run->nFlags & MERF_SKIPPED)
DrawFocusRect(c->hDC, &rc);
@@ -363,15 +625,15 @@ void ME_DrawParagraph(ME_Context *c, ME_
FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
}
if (visible)
- ME_DrawRun(c, run->pt.x, c->pt.y+run->pt.y+baseline, p, ¶graph->member.para);
+ ME_DrawRun(c, c->rcView.left+run->pt.x, c->pt.y+run->pt.y+baseline, p, ¶graph->member.para);
if (me_debug)
{
/* I'm using %ls, hope wsprintfW is not going to use wrong (4-byte) WCHAR version */
const WCHAR wszRunDebug[] = {'[','%','d',':','%','x',']',' ','%','l','s',0};
WCHAR buf[2560];
POINT pt;
- pt.x = run->pt.x;
- pt.y = c->pt.y + run->pt.y;
+ pt.x = transform_x(c, run->pt.x);
+ pt.y = transform_y(c, c->pt.y + run->pt.y);
wsprintfW(buf, wszRunDebug, no, p->member.run.nFlags, p->member.run.strText->szData);
ME_DebugWrite(c->hDC, &pt, buf);
}
diff --git a/dlls/riched20/run.c b/dlls/riched20/run.c
index 29fc309..e9bc7a8 100644
--- a/dlls/riched20/run.c
+++ b/dlls/riched20/run.c
@@ -473,11 +473,26 @@ void ME_UpdateRunFlags(ME_TextEditor *ed
* Sets run extent for graphics runs. This functionality is just a placeholder
* for future OLE object support, and will be removed.
*/
-void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize)
+void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize, ME_Dimension *pd)
{
+ ME_Dimension d;
+
assert(run->nFlags & MERF_GRAPHICS);
- pSize->cx = 64;
- pSize->cy = 64;
+ if (pd)
+ {
+ d = *pd;
+ }
+ else
+ {
+ HDC hdc = GetDC(editor->hWnd);
+
+ d.cx = GetDeviceCaps(hdc, LOGPIXELSX);
+ d.cy = GetDeviceCaps(hdc, LOGPIXELSY);
+ ReleaseDC(editor->hWnd, hdc);
+ }
+ /* Return a square 1/3rd of an inch wide */
+ pSize->cx = 2 * d.cx / 3;
+ pSize->cy = 2 * d.cy / 3;
}
/******************************************************************************
@@ -487,7 +502,7 @@ void ME_GetGraphicsSize(ME_TextEditor *e
* pixel horizontal position. This version rounds left (ie. if the second
* character is at pixel position 8, then for cx=0..7 it returns 0).
*/
-int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run)
+int ME_CharFromPoint(ME_Context *c, int cx, ME_Run *run)
{
int fit = 0;
HGDIOBJ hOldFont;
@@ -505,17 +520,17 @@ int ME_CharFromPoint(ME_TextEditor *edit
if (run->nFlags & MERF_GRAPHICS)
{
SIZE sz;
- ME_GetGraphicsSize(editor, run, &sz);
+ ME_GetGraphicsSize(c->editor, run, &sz, &c->dDraw);
if (cx < sz.cx)
return 0;
return 1;
}
- hDC = GetDC(editor->hWnd);
- hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
+ hDC = c->hDC;
+ hOldFont = ME_SelectStyleFont(c->editor, hDC, run->style);
- if (editor->cPasswordMask)
+ if (c->editor->cPasswordMask)
{
- ME_String *strMasked = ME_MakeStringR(editor->cPasswordMask,ME_StrVLen(run->strText));
+ ME_String *strMasked = ME_MakeStringR(c->editor->cPasswordMask,ME_StrVLen(run->strText));
GetTextExtentExPointW(hDC, strMasked->szData, run->strText->nLen,
cx, &fit, NULL, &sz);
ME_DestroyString(strMasked);
@@ -526,8 +541,7 @@ int ME_CharFromPoint(ME_TextEditor *edit
cx, &fit, NULL, &sz);
}
- ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
- ReleaseDC(editor->hWnd, hDC);
+ ME_UnselectStyleFont(c->editor, hDC, run->style, hOldFont);
return fit;
}
@@ -563,7 +577,7 @@ int ME_CharFromPointCursor(ME_TextEditor
if (run->nFlags & MERF_GRAPHICS)
{
SIZE sz;
- ME_GetGraphicsSize(editor, run, &sz);
+ ME_GetGraphicsSize(editor, run, &sz, NULL);
if (cx < sz.cx/2)
return 0;
return 1;
@@ -614,7 +628,7 @@ int ME_PointFromChar(ME_TextEditor *edit
if (pRun->nFlags & MERF_GRAPHICS)
{
if (!nOffset) return 0;
- ME_GetGraphicsSize(editor, pRun, &size);
+ ME_GetGraphicsSize(editor, pRun, &size, NULL);
return 1;
}
@@ -706,7 +720,7 @@ static SIZE ME_GetRunSizeCommon(ME_Conte
}
if (run->nFlags & MERF_GRAPHICS)
{
- ME_GetGraphicsSize(c->editor, run, &size);
+ ME_GetGraphicsSize(c->editor, run, &size, &c->dDraw);
if (size.cy > *pAscent)
*pAscent = size.cy;
/* descent is unchanged */
diff --git a/dlls/riched20/style.c b/dlls/riched20/style.c
index 1dfdef1..ef19306 100644
--- a/dlls/riched20/style.c
+++ b/dlls/riched20/style.c
@@ -323,48 +323,57 @@ HFONT ME_SelectStyleFont(ME_TextEditor *
LOGFONTW lf;
int i, nEmpty, nAge = 0x7FFFFFFF;
ME_FontCacheItem *item;
+ int nZoomNumerator = editor ? editor->nZoomNumerator : 1;
+ int nZoomDenominator = editor ? editor->nZoomDenominator : 1;
assert(hDC);
assert(s);
- ME_LogFontFromStyle(hDC, &lf, s, editor->nZoomNumerator, editor->nZoomDenominator);
+ ME_LogFontFromStyle(hDC, &lf, s, nZoomNumerator, nZoomDenominator);
- for (i=0; i<HFONT_CACHE_SIZE; i++)
- editor->pFontCache[i].nAge++;
- for (i=0, nEmpty=-1, nAge=0; i<HFONT_CACHE_SIZE; i++)
+ if (editor)
{
- item = &editor->pFontCache[i];
- if (!item->nRefs)
+ for (i=0; i<HFONT_CACHE_SIZE; i++)
+ editor->pFontCache[i].nAge++;
+ for (i=0, nEmpty=-1, nAge=0; i<HFONT_CACHE_SIZE; i++)
{
- if (item->nAge > nAge)
- nEmpty = i, nAge = item->nAge;
+ item = &editor->pFontCache[i];
+ if (!item->nRefs)
+ {
+ if (item->nAge > nAge)
+ nEmpty = i, nAge = item->nAge;
+ }
+ if (item->hFont && ME_IsFontEqual(&item->lfSpecs, &lf))
+ break;
}
- if (item->hFont && ME_IsFontEqual(&item->lfSpecs, &lf))
- break;
- }
- if (i < HFONT_CACHE_SIZE) /* found */
- {
- item = &editor->pFontCache[i];
- TRACE_(richedit_style)("font reused %d\n", i);
+ if (i < HFONT_CACHE_SIZE) /* found */
+ {
+ item = &editor->pFontCache[i];
+ TRACE_(richedit_style)("font reused %d\n", i);
- s->hFont = item->hFont;
- item->nRefs++;
+ s->hFont = item->hFont;
+ item->nRefs++;
+ }
+ else
+ {
+ item = &editor->pFontCache[nEmpty]; /* this legal even when nEmpty == -1, as we don't dereference it */
+
+ assert(nEmpty != -1); /* otherwise we leak cache entries or get too many fonts at once*/
+ if (item->hFont) {
+ TRACE_(richedit_style)("font deleted %d\n", nEmpty);
+ DeleteObject(item->hFont);
+ item->hFont = NULL;
+ }
+ s->hFont = CreateFontIndirectW(&lf);
+ assert(s->hFont);
+ TRACE_(richedit_style)("font created %d\n", nEmpty);
+ item->hFont = s->hFont;
+ item->nRefs = 1;
+ memcpy(&item->lfSpecs, &lf, sizeof(LOGFONTW));
+ }
}
- else
+ else if (!s->hFont)
{
- item = &editor->pFontCache[nEmpty]; /* this legal even when nEmpty == -1, as we don't dereference it */
-
- assert(nEmpty != -1); /* otherwise we leak cache entries or get too many fonts at once*/
- if (item->hFont) {
- TRACE_(richedit_style)("font deleted %d\n", nEmpty);
- DeleteObject(item->hFont);
- item->hFont = NULL;
- }
s->hFont = CreateFontIndirectW(&lf);
- assert(s->hFont);
- TRACE_(richedit_style)("font created %d\n", nEmpty);
- item->hFont = s->hFont;
- item->nRefs = 1;
- memcpy(&item->lfSpecs, &lf, sizeof(LOGFONTW));
}
hOldFont = SelectObject(hDC, s->hFont);
/* should be cached too, maybe ? */
@@ -379,6 +388,8 @@ void ME_UnselectStyleFont(ME_TextEditor
assert(hDC);
assert(s);
SelectObject(hDC, hOldFont);
+ if (!editor)
+ return;
for (i=0; i<HFONT_CACHE_SIZE; i++)
{
ME_FontCacheItem *pItem = &editor->pFontCache[i];
@@ -423,6 +434,50 @@ void ME_ReleaseStyle(ME_Style *s)
ME_DestroyStyle(s);
}
+ME_Style *ME_MapStyle(ME_StyleMap *m, ME_Style *s)
+{
+ ME_StyleMapping **tail;
+
+ if (!s)
+ return s;
+
+ for (tail = &m->first; *tail; tail = &(*tail)->next)
+ {
+ if ((*tail)->from == s)
+ {
+ ME_AddRefStyle((*tail)->to);
+ return (*tail)->to;
+ }
+ }
+ *tail = ALLOC_OBJ(ME_StyleMapping);
+ (*tail)->from = s;
+ (*tail)->to = ME_MakeStyle(&s->fmt);
+ ME_AddRefStyle((*tail)->to);
+ (*tail)->next = 0;
+ return (*tail)->to;
+}
+
+ME_StyleMap *ME_MakeStyleMap(void)
+{
+ ME_StyleMap *sm = ALLOC_OBJ(ME_StyleMap);
+
+ sm->first = 0;
+ return sm;
+}
+
+void ME_FreeStyleMap(ME_StyleMap *m)
+{
+ while (m->first)
+ {
+ ME_StyleMapping *p = m->first;
+
+ m->first = p->next;
+ ME_ReleaseStyle(p->to);
+ FREE_OBJ(p);
+ }
+ FREE_OBJ(m);
+}
+
ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor) {
if (ME_IsSelection(editor))
{
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 1881005..e93d45d 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -1491,6 +1491,43 @@ static void test_WM_PASTE(void)
DestroyWindow(hwndRichEdit);
}
+static void test_EM_FORMATRANGE(void)
+{
+ int r;
+ FORMATRANGE fr;
+ HDC hdc;
+ HWND hwndRichEdit = new_richedit(NULL);
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
+
+ hdc = GetDC(hwndRichEdit);
+ ok(hdc != NULL, "Could not get HDC\n");
+
+ fr.hdc = fr.hdcTarget = hdc;
+ fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
+ fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
+ fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
+ fr.chrg.cpMin = 0;
+ fr.chrg.cpMax = 20;
+
+ r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
+ ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
+
+ r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
+ ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
+
+ fr.chrg.cpMin = 0;
+ fr.chrg.cpMax = 10;
+
+ r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
+ ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
+
+ r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
+ ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
+
+ DestroyWindow(hwndRichEdit);
+}
+
static int nCallbackCount = 0;
static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
@@ -1808,6 +1845,7 @@ START_TEST( editor )
test_EM_EXSETSEL();
test_WM_PASTE();
test_EM_StreamIn_Undo();
+ test_EM_FORMATRANGE();
test_unicode_conversions();
/* Set the environment variable WINETEST_RICHED20 to keep windows
diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c
index af6e9a9..c3e5d70 100644
--- a/dlls/riched20/wrap.c
+++ b/dlls/riched20/wrap.c
@@ -168,7 +168,7 @@ static ME_DisplayItem *ME_SplitByBacktra
int i, idesp, len;
ME_Run *run = &p->member.run;
- idesp = i = ME_CharFromPoint(wc->context->editor, loc, run);
+ idesp = i = ME_CharFromPoint(wc->context, loc, run);
len = ME_StrVLen(run->strText);
assert(len>0);
assert(i<len);
@@ -367,6 +367,23 @@ void ME_WrapTextParagraph(ME_Context *c,
for (p = tp->next; p!=tp->member.para.next_para; ) {
assert(p->type != diStartRow);
if (p->type == diRun) {
+ int iStartOffset = tp->member.para.nCharOfs + p->member.run.nCharOfs;
+ int iEndOffset = iStartOffset + ME_VPosToPos(p->member.run.strText,
+ p->member.run.strText ?
+ p->member.run.strText->nLen : 0);
+
+ if (iStartOffset < c->fr.chrg.cpMin && iEndOffset > c->fr.chrg.cpMin)
+ {
+ ME_SplitRun(c, p, ME_PosToVPos(p->member.run.strText, c->fr.chrg.cpMin - iStartOffset));
+ } else if (iStartOffset < c->fr.chrg.cpMax && iEndOffset > c->fr.chrg.cpMax)
+ {
+ ME_SplitRun(c, p, ME_PosToVPos(p->member.run.strText, c->fr.chrg.cpMax - iStartOffset));
+ }
+ if ((iStartOffset == c->fr.chrg.cpMin || iStartOffset == c->fr.chrg.cpMax) &&
+ wc.pRowStart && wc.pRowStart != p)
+ {
+ ME_InsertRowStart(&wc, p);
+ }
p = ME_WrapHandleRun(&wc, p);
continue;
}
--
1.4.2.1
More information about the wine-patches
mailing list