- removed trailing spaces from some files
[wine/wine-kai.git] / dlls / riched20 / run.c
blob533aae2a642ae1725b0ec096b43d424bf79441e4
1 /*
2 * RichEdit - operations on runs (diRun, rectangular pieces of paragraphs).
3 * Splitting/joining runs. Adjusting offsets after deleting/adding content.
4 * Character/pixel conversions.
6 * Copyright 2004 by Krzysztof Foltman
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "editor.h"
25 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
27 int ME_CanJoinRuns(ME_Run *run1, ME_Run *run2)
29 if ((run1->nFlags | run2->nFlags) & MERF_NOJOIN)
30 return 0;
31 if (run1->style != run2->style)
32 return 0;
33 if ((run1->nFlags & MERF_STYLEFLAGS) != (run2->nFlags & MERF_STYLEFLAGS))
34 return 0;
35 return 1;
38 void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift)
40 p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
41 assert(p);
42 ME_PropagateCharOffset(p, shift);
45 void ME_PropagateCharOffset(ME_DisplayItem *p, int shift)
47 if (p->type == diRun) /* propagate in all runs in this para */
49 TRACE("PropagateCharOffset(%s, %d)\n", debugstr_w(p->member.run.strText->szData), shift);
50 do {
51 p->member.run.nCharOfs += shift;
52 assert(p->member.run.nCharOfs >= 0);
53 p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
54 } while(p->type == diRun);
56 if (p->type == diParagraph) /* propagate in all next paras */
58 do {
59 p->member.para.nCharOfs += shift;
60 assert(p->member.para.nCharOfs >= 0);
61 p = p->member.para.next_para;
62 } while(p->type == diParagraph);
64 if (p->type == diTextEnd)
66 p->member.para.nCharOfs += shift;
67 assert(p->member.para.nCharOfs >= 0);
71 void ME_CheckCharOffsets(ME_TextEditor *editor)
73 ME_DisplayItem *p = editor->pBuffer->pFirst;
74 int ofs = 0, ofsp = 0;
75 if(TRACE_ON(richedit))
77 TRACE("---\n");
78 ME_DumpDocument(editor->pBuffer);
80 do {
81 p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
82 switch(p->type) {
83 case diTextEnd:
84 TRACE("tend, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
85 assert(ofsp+ofs == p->member.para.nCharOfs);
86 return;
87 case diParagraph:
88 TRACE("para, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
89 assert(ofsp+ofs == p->member.para.nCharOfs);
90 ofsp = p->member.para.nCharOfs;
91 ofs = 0;
92 break;
93 case diRun:
94 TRACE("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = \"%s\", flags=%08x, fx&mask = %08lx\n",
95 p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs,
96 p->member.run.strText->nLen, debugstr_w(p->member.run.strText->szData),
97 p->member.run.nFlags,
98 p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects);
99 assert(ofs == p->member.run.nCharOfs);
100 ofs += ME_StrLen(p->member.run.strText);
101 break;
102 default:
103 assert(0);
105 } while(1);
108 int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs)
110 ME_DisplayItem *pPara;
112 assert(pRun->type == diRun);
113 assert(pRun->member.run.nCharOfs != -1);
115 pPara = ME_FindItemBack(pRun, diParagraph);
116 assert(pPara);
117 assert(pPara->type==diParagraph);
118 return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs
119 + ME_VPosToPos(pRun->member.run.strText, nOfs);
122 void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor)
124 ME_RunOfsFromCharOfs(editor, nCharOfs, &pCursor->pRun, &pCursor->nOffset);
127 void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppRun, int *pOfs)
129 ME_DisplayItem *pPara;
130 int nParaOfs;
132 pPara = editor->pBuffer->pFirst->member.para.next_para;
133 assert(pPara);
134 assert(ppRun);
135 assert(pOfs);
136 while (pPara->type == diParagraph)
138 nParaOfs = pPara->member.para.nCharOfs;
139 assert(nCharOfs >= nParaOfs);
141 if (nCharOfs < pPara->member.para.next_para->member.para.nCharOfs)
143 *ppRun = ME_FindItemFwd(pPara, diRun);
144 assert(*ppRun);
145 while (!((*ppRun)->member.run.nFlags & MERF_ENDPARA))
147 ME_DisplayItem *pNext = ME_FindItemFwd(*ppRun, diRun);
148 assert(pNext);
149 assert(pNext->type == diRun);
150 if (nCharOfs < nParaOfs + pNext->member.run.nCharOfs) {
151 *pOfs = ME_PosToVPos((*ppRun)->member.run.strText,
152 nCharOfs - nParaOfs - (*ppRun)->member.run.nCharOfs);
153 return;
155 *ppRun = pNext;
157 if (nCharOfs == nParaOfs + (*ppRun)->member.run.nCharOfs) {
158 *pOfs = 0;
159 return;
162 pPara = pPara->member.para.next_para;
164 *ppRun = ME_FindItemBack(editor->pBuffer->pLast, diRun);
165 *pOfs = 0;
166 assert((*ppRun)->member.run.nFlags & MERF_ENDPARA);
169 void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p)
171 ME_DisplayItem *pNext = p->next;
172 int i;
173 assert(p->type == diRun && pNext->type == diRun);
174 assert(p->member.run.nCharOfs != -1);
175 ME_GetParagraph(p)->member.para.nFlags |= MEPF_REWRAP;
177 if (editor->bCaretAtEnd && editor->pCursors[0].pRun == pNext)
178 editor->bCaretAtEnd = FALSE;
179 for (i=0; i<editor->nCursors; i++) {
180 if (editor->pCursors[i].pRun == pNext) {
181 editor->pCursors[i].pRun = p;
182 editor->pCursors[i].nOffset += ME_StrVLen(p->member.run.strText);
186 ME_AppendString(p->member.run.strText, pNext->member.run.strText);
187 ME_Remove(pNext);
188 ME_DestroyDisplayItem(pNext);
189 ME_UpdateRunFlags(editor, &p->member.run);
190 if(TRACE_ON(richedit))
192 TRACE("Before check after join\n");
193 ME_CheckCharOffsets(editor);
194 TRACE("After check after join\n");
198 ME_DisplayItem *ME_SplitRun(ME_Context *c, ME_DisplayItem *item, int nVChar)
200 ME_TextEditor *editor = c->editor;
201 ME_DisplayItem *item2 = NULL;
202 ME_Run *run, *run2;
203 ME_Paragraph *para = &ME_GetParagraph(item)->member.para;
205 assert(item->member.run.nCharOfs != -1);
206 if(TRACE_ON(richedit))
208 TRACE("Before check before split\n");
209 ME_CheckCharOffsets(editor);
210 TRACE("After check before split\n");
213 run = &item->member.run;
215 TRACE("Before split: %s(%ld, %ld)\n", debugstr_w(run->strText->szData),
216 run->pt.x, run->pt.y);
218 item2 = ME_SplitRunSimple(editor, item, nVChar);
220 run2 = &item2->member.run;
222 ME_CalcRunExtent(c, para, run);
223 ME_CalcRunExtent(c, para, run2);
225 run2->pt.x = run->pt.x+run->nWidth;
226 run2->pt.y = run->pt.y;
228 if(TRACE_ON(richedit))
230 TRACE("Before check after split\n");
231 ME_CheckCharOffsets(editor);
232 TRACE("After check after split\n");
233 TRACE("After split: %s(%ld, %ld), %s(%ld, %ld)\n",
234 debugstr_w(run->strText->szData), run->pt.x, run->pt.y,
235 debugstr_w(run2->strText->szData), run2->pt.x, run2->pt.y);
238 return item2;
241 /* split a run starting from voffset */
242 ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nVChar)
244 ME_Run *run = &item->member.run;
245 ME_DisplayItem *item2;
246 ME_Run *run2;
247 int i;
248 assert(nVChar > 0 && nVChar < ME_StrVLen(run->strText));
249 assert(item->type == diRun);
250 assert(!(item->member.run.nFlags & (MERF_GRAPHICS | MERF_TAB)));
251 assert(item->member.run.nCharOfs != -1);
253 item2 = ME_MakeRun(run->style,
254 ME_VSplitString(run->strText, nVChar), run->nFlags&MERF_SPLITMASK);
256 item2->member.run.nCharOfs = item->member.run.nCharOfs+
257 ME_VPosToPos(item->member.run.strText, nVChar);
259 run2 = &item2->member.run;
260 ME_InsertBefore(item->next, item2);
262 ME_UpdateRunFlags(editor, run);
263 ME_UpdateRunFlags(editor, run2);
264 for (i=0; i<editor->nCursors; i++) {
265 if (editor->pCursors[i].pRun == item &&
266 editor->pCursors[i].nOffset >= nVChar) {
267 assert(item2->type == diRun);
268 editor->pCursors[i].pRun = item2;
269 editor->pCursors[i].nOffset -= nVChar;
272 ME_GetParagraph(item)->member.para.nFlags |= MEPF_REWRAP;
273 return item2;
276 ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags)
278 ME_DisplayItem *item = ME_MakeDI(diRun);
279 item->member.run.style = s;
280 item->member.run.strText = strData;
281 item->member.run.nFlags = nFlags;
282 item->member.run.nCharOfs = -1;
283 ME_AddRefStyle(s);
284 return item;
287 ME_DisplayItem *ME_InsertRun(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem *pItem)
289 ME_Cursor tmp;
290 ME_DisplayItem *pDI;
291 ME_UndoItem *pUI;
293 assert(pItem->type == diRun || pItem->type == diUndoInsertRun);
295 pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
296 if (pUI) {
297 pUI->nStart = nCharOfs;
298 pUI->nLen = pItem->member.run.strText->nLen;
300 ME_CursorFromCharOfs(editor, nCharOfs, &tmp);
301 if (tmp.nOffset) {
302 tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
303 tmp.nOffset = 0;
305 pDI = ME_MakeRun(pItem->member.run.style, ME_StrDup(pItem->member.run.strText), pItem->member.run.nFlags);
306 pDI->member.run.nCharOfs = tmp.pRun->member.run.nCharOfs;
307 ME_InsertBefore(tmp.pRun, pDI);
308 TRACE("Shift length:%d\n", pDI->member.run.strText->nLen);
309 ME_PropagateCharOffset(tmp.pRun, pDI->member.run.strText->nLen);
310 ME_GetParagraph(tmp.pRun)->member.para.nFlags |= MEPF_REWRAP;
312 return pDI;
315 void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run)
317 assert(run->nCharOfs != -1);
318 if (ME_IsSplitable(run->strText))
319 run->nFlags |= MERF_SPLITTABLE;
320 else
321 run->nFlags &= ~MERF_SPLITTABLE;
323 if (!(run->nFlags & MERF_NOTEXT)) {
324 if (ME_IsWhitespaces(run->strText))
325 run->nFlags |= MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE;
326 else
328 run->nFlags &= ~MERF_WHITESPACE;
330 if (ME_IsWSpace(ME_GetCharFwd(run->strText,0)))
331 run->nFlags |= MERF_STARTWHITE;
332 else
333 run->nFlags &= ~MERF_STARTWHITE;
335 if (ME_IsWSpace(ME_GetCharBack(run->strText,0)))
336 run->nFlags |= MERF_ENDWHITE;
337 else
338 run->nFlags &= ~MERF_ENDWHITE;
341 else
342 run->nFlags &= ~(MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE);
345 void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize)
347 assert(run->nFlags & MERF_GRAPHICS);
348 pSize->cx = 64;
349 pSize->cy = 64;
352 int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Paragraph *para, ME_Run *run)
354 int fit = 0;
355 HGDIOBJ hOldFont;
356 HDC hDC;
357 SIZE sz;
358 if (!run->strText->nLen)
359 return 0;
361 if (run->nFlags & MERF_TAB)
363 if (cx < run->nWidth/2)
364 return 0;
365 return 1;
367 if (run->nFlags & MERF_GRAPHICS)
369 SIZE sz;
370 ME_GetGraphicsSize(editor, run, &sz);
371 if (cx < sz.cx)
372 return 0;
373 return 1;
375 hDC = GetDC(editor->hWnd);
376 hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
377 GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
378 cx, &fit, NULL, &sz);
379 ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
380 ReleaseDC(editor->hWnd, hDC);
381 return fit;
384 int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run)
386 int fit = 0, fit1 = 0;
387 HGDIOBJ hOldFont;
388 HDC hDC;
389 SIZE sz, sz2, sz3;
390 if (!run->strText->nLen)
391 return 0;
393 if (run->nFlags & MERF_TAB)
395 if (cx < run->nWidth/2)
396 return 0;
397 return 1;
399 if (run->nFlags & MERF_GRAPHICS)
401 SIZE sz;
402 ME_GetGraphicsSize(editor, run, &sz);
403 if (cx < sz.cx/2)
404 return 0;
405 return 1;
408 hDC = GetDC(editor->hWnd);
409 hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
410 GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
411 cx, &fit, NULL, &sz);
412 if (fit != run->strText->nLen)
414 int chars = 1;
416 GetTextExtentPoint32W(hDC, run->strText->szData, fit, &sz2);
417 fit1 = ME_StrRelPos(run->strText, fit, &chars);
418 GetTextExtentPoint32W(hDC, run->strText->szData, fit1, &sz3);
419 if (cx >= (sz2.cx+sz3.cx)/2)
420 fit = fit1;
422 ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
423 ReleaseDC(editor->hWnd, hDC);
424 return fit;
427 int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset)
429 SIZE size;
430 HDC hDC = GetDC(editor->hWnd);
431 HGDIOBJ hOldFont;
433 if (pRun->nFlags & MERF_GRAPHICS)
435 if (!nOffset) return 0;
436 ME_GetGraphicsSize(editor, pRun, &size);
437 return 1;
439 hOldFont = ME_SelectStyleFont(editor, hDC, pRun->style);
440 GetTextExtentPoint32W(hDC, pRun->strText->szData, nOffset, &size);
441 ME_UnselectStyleFont(editor, hDC, pRun->style, hOldFont);
442 ReleaseDC(editor->hWnd, hDC);
443 return size.cx;
446 void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s,
447 SIZE *size)
449 HDC hDC = c->hDC;
450 HGDIOBJ hOldFont;
451 hOldFont = ME_SelectStyleFont(c->editor, hDC, s);
452 GetTextExtentPoint32W(hDC, szText, nChars, size);
453 ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);
456 SIZE ME_GetRunSizeCommon(ME_Context *c, ME_Paragraph *para, ME_Run *run, int nLen, int *pAscent, int *pDescent)
458 SIZE size;
459 int nMaxLen = ME_StrVLen(run->strText);
461 if (nLen>nMaxLen)
462 nLen = nMaxLen;
464 /* FIXME the following call also ensures that TEXTMETRIC structure is filled
465 * this is wasteful for graphics and TAB runs, but that shouldn't matter
466 * in practice
468 ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size);
469 assert(run->style->tm.tmAscent>0);
470 assert(run->style->tm.tmDescent>0);
471 *pAscent = run->style->tm.tmAscent;
472 *pDescent = run->style->tm.tmDescent;
473 size.cy = *pAscent + *pDescent;
475 if (run->nFlags & MERF_TAB)
477 int pos = 0, i = 0, ppos;
478 int lpsx = GetDeviceCaps(c->hDC, LOGPIXELSX);
479 PARAFORMAT2 *pFmt = para->pFmt;
480 do {
481 if (i < pFmt->cTabCount)
483 pos = pFmt->rgxTabs[i]&0x00FFFFFF;
484 i++;
486 else
488 pos += 720-(pos%720);
490 ppos = pos*lpsx/1440;
491 if (ppos>run->pt.x) {
492 size.cx = ppos - run->pt.x;
493 break;
495 } while(1);
496 size.cy = *pAscent + *pDescent;
497 return size;
499 if (run->nFlags & MERF_GRAPHICS)
501 ME_GetGraphicsSize(c->editor, run, &size);
502 if (size.cy > *pAscent)
503 *pAscent = size.cy;
504 /* descent is unchanged */
505 return size;
508 return size;
511 SIZE ME_GetRunSize(ME_Context *c, ME_Paragraph *para, ME_Run *run, int nLen)
513 int asc, desc;
514 return ME_GetRunSizeCommon(c, para, run, nLen, &asc, &desc);
517 void ME_CalcRunExtent(ME_Context *c, ME_Paragraph *para, ME_Run *run)
519 int nEnd = ME_StrVLen(run->strText);
520 SIZE size = ME_GetRunSizeCommon(c, para, run, nEnd, &run->nAscent, &run->nDescent);
521 run->nWidth = size.cx;
522 assert(size.cx);
525 void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para)
527 assert(para->type == diParagraph);
528 /* FIXME */
531 void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
533 int nFrom, nTo;
534 ME_GetSelection(editor, &nFrom, &nTo);
535 if (nFrom == nTo)
537 ME_Style *s;
538 if (!editor->pBuffer->pCharStyle)
539 editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0);
540 s = ME_ApplyStyle(editor->pBuffer->pCharStyle, pFmt);
541 ME_ReleaseStyle(editor->pBuffer->pCharStyle);
542 editor->pBuffer->pCharStyle = s;
544 else
545 ME_SetCharFormat(editor, nFrom, nTo-nFrom, pFmt);
548 void ME_SetCharFormat(ME_TextEditor *editor, int nOfs, int nChars, CHARFORMAT2W *pFmt)
550 ME_Cursor tmp, tmp2;
551 ME_DisplayItem *para;
553 ME_CursorFromCharOfs(editor, nOfs, &tmp);
554 if (tmp.nOffset)
555 tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
557 ME_CursorFromCharOfs(editor, nOfs+nChars, &tmp2);
558 if (tmp2.nOffset)
559 tmp2.pRun = ME_SplitRunSimple(editor, tmp2.pRun, tmp2.nOffset);
561 para = ME_GetParagraph(tmp.pRun);
562 para->member.para.nFlags |= MEPF_REWRAP;
564 while(tmp.pRun != tmp2.pRun)
566 ME_UndoItem *undo = NULL;
567 ME_Style *new_style = ME_ApplyStyle(tmp.pRun->member.run.style, pFmt);
568 /* ME_DumpStyle(new_style); */
569 undo = ME_AddUndoItem(editor, diUndoSetCharFormat, NULL);
570 if (undo) {
571 undo->nStart = tmp.pRun->member.run.nCharOfs+para->member.para.nCharOfs;
572 undo->nLen = tmp.pRun->member.run.strText->nLen;
573 undo->di.member.ustyle = tmp.pRun->member.run.style;
574 /* we'd have to addref undo..ustyle and release tmp...style
575 but they'd cancel each other out so we can do nothing instead */
577 else
578 ME_ReleaseStyle(tmp.pRun->member.run.style);
579 tmp.pRun->member.run.style = new_style;
580 tmp.pRun = ME_FindItemFwd(tmp.pRun, diRunOrParagraph);
581 if (tmp.pRun->type == diParagraph)
583 para = tmp.pRun;
584 tmp.pRun = ME_FindItemFwd(tmp.pRun, diRun);
585 if (tmp.pRun != tmp2.pRun)
586 para->member.para.nFlags |= MEPF_REWRAP;
588 assert(tmp.pRun);
592 void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod)
594 ME_Style *style;
595 ME_UndoItem *undo;
597 assert(mod->cbSize == sizeof(CHARFORMAT2W));
598 undo = ME_AddUndoItem(editor, diUndoSetDefaultCharFormat, NULL);
599 if (undo) {
600 undo->nStart = -1;
601 undo->nLen = -1;
602 undo->di.member.ustyle = editor->pBuffer->pDefaultStyle;
603 ME_AddRefStyle(undo->di.member.ustyle);
605 style = ME_ApplyStyle(editor->pBuffer->pDefaultStyle, mod);
606 editor->pBuffer->pDefaultStyle->fmt = style->fmt;
607 editor->pBuffer->pDefaultStyle->tm = style->tm;
608 ME_ReleaseStyle(style);
609 ME_MarkAllForWrapping(editor);
610 /* pcf = editor->pBuffer->pDefaultStyle->fmt; */
613 void ME_GetRunCharFormat(ME_TextEditor *editor, ME_DisplayItem *run, CHARFORMAT2W *pFmt)
615 ME_CopyCharFormat(pFmt, &run->member.run.style->fmt);
618 void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
620 int nFrom, nTo;
621 ME_GetSelection(editor, &nFrom, &nTo);
622 ME_CopyCharFormat(pFmt, &editor->pBuffer->pDefaultStyle->fmt);
625 void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
627 int nFrom, nTo;
628 ME_GetSelection(editor, &nFrom, &nTo);
629 if (nFrom == nTo && editor->pBuffer->pCharStyle)
631 ME_CopyCharFormat(pFmt, &editor->pBuffer->pCharStyle->fmt);
632 return;
634 ME_GetCharFormat(editor, nFrom, nTo, pFmt);
637 void ME_GetCharFormat(ME_TextEditor *editor, int nFrom, int nTo, CHARFORMAT2W *pFmt)
639 ME_DisplayItem *run, *run_end;
640 int nOffset, nOffset2;
641 CHARFORMAT2W tmp;
643 if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */
644 nTo--;
646 ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset);
647 if (nFrom == nTo) /* special case - if selection is empty, take previous char's formatting */
649 if (!nOffset)
651 ME_DisplayItem *tmp_run = ME_FindItemBack(run, diRunOrParagraph);
652 if (tmp_run->type == diRun) {
653 ME_GetRunCharFormat(editor, tmp_run, pFmt);
654 return;
657 ME_GetRunCharFormat(editor, run, pFmt);
658 return;
660 ME_RunOfsFromCharOfs(editor, nTo, &run_end, &nOffset2);
662 ME_GetRunCharFormat(editor, run, pFmt);
664 if (run == run_end) return;
666 do {
667 /* FIXME add more style feature comparisons */
668 int nAttribs = CFM_SIZE | CFM_FACE | CFM_COLOR;
669 int nEffects = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
671 run = ME_FindItemFwd(run, diRun);
673 ZeroMemory(&tmp, sizeof(tmp));
674 tmp.cbSize = sizeof(tmp);
675 ME_GetRunCharFormat(editor, run, &tmp);
677 assert((tmp.dwMask & nAttribs) == nAttribs);
678 assert((tmp.dwMask & nEffects) == nEffects);
679 /* reset flags that differ */
681 if (pFmt->yHeight != tmp.yHeight)
682 pFmt->dwMask &= ~CFM_SIZE;
683 if (pFmt->dwMask & CFM_FACE)
685 if (!(tmp.dwMask & CFM_FACE))
686 pFmt->dwMask &= ~CFM_FACE;
687 else if (lstrcmpW(pFmt->szFaceName, tmp.szFaceName))
688 pFmt->dwMask &= ~CFM_FACE;
690 if (pFmt->yHeight != tmp.yHeight)
691 pFmt->dwMask &= ~CFM_SIZE;
692 if (pFmt->dwMask & CFM_COLOR)
694 if (!((pFmt->dwEffects&CFE_AUTOCOLOR) & (tmp.dwEffects&CFE_AUTOCOLOR)))
696 if (pFmt->crTextColor != tmp.crTextColor)
697 pFmt->dwMask &= ~CFM_COLOR;
701 pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & nEffects);
703 } while(run != run_end);