2 * RichEdit - functions working on paragraphs of text (diParagraph).
4 * Copyright 2004 by Krzysztof Foltman
5 * Copyright 2006 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
24 WINE_DEFAULT_DEBUG_CHANNEL(richedit
);
26 static ME_DisplayItem
*make_para(ME_TextEditor
*editor
)
28 ME_DisplayItem
*item
= ME_MakeDI(diParagraph
);
30 item
->member
.para
.pFmt
= ALLOC_OBJ(PARAFORMAT2
);
31 ME_SetDefaultParaFormat(editor
, item
->member
.para
.pFmt
);
32 item
->member
.para
.nFlags
= MEPF_REWRAP
;
36 void ME_MakeFirstParagraph(ME_TextEditor
*editor
)
42 ME_TextBuffer
*text
= editor
->pBuffer
;
43 ME_DisplayItem
*para
= make_para(editor
);
47 WCHAR cr_lf
[] = {'\r','\n',0};
49 ME_InitContext(&c
, editor
, ITextHost_TxGetDC(editor
->texthost
));
51 hf
= GetStockObject(SYSTEM_FONT
);
53 GetObjectW(hf
, sizeof(LOGFONTW
), &lf
);
54 ZeroMemory(&cf
, sizeof(cf
));
55 cf
.cbSize
= sizeof(cf
);
56 cf
.dwMask
= CFM_BACKCOLOR
|CFM_COLOR
|CFM_FACE
|CFM_SIZE
|CFM_CHARSET
;
57 cf
.dwMask
|= CFM_ALLCAPS
|CFM_BOLD
|CFM_DISABLED
|CFM_EMBOSS
|CFM_HIDDEN
;
58 cf
.dwMask
|= CFM_IMPRINT
|CFM_ITALIC
|CFM_LINK
|CFM_OUTLINE
|CFM_PROTECTED
;
59 cf
.dwMask
|= CFM_REVISED
|CFM_SHADOW
|CFM_SMALLCAPS
|CFM_STRIKEOUT
;
60 cf
.dwMask
|= CFM_SUBSCRIPT
|CFM_UNDERLINETYPE
|CFM_WEIGHT
;
62 cf
.dwEffects
= CFE_AUTOCOLOR
| CFE_AUTOBACKCOLOR
;
63 lstrcpyW(cf
.szFaceName
, lf
.lfFaceName
);
64 /* Convert system font height from logical units to twips for cf.yHeight */
65 cf
.yHeight
= (lf
.lfHeight
* 72 * 1440) / (c
.dpi
.cy
* c
.dpi
.cy
);
66 if (lf
.lfWeight
> FW_NORMAL
) cf
.dwEffects
|= CFE_BOLD
;
67 cf
.wWeight
= lf
.lfWeight
;
68 if (lf
.lfItalic
) cf
.dwEffects
|= CFE_ITALIC
;
69 cf
.bUnderlineType
= (lf
.lfUnderline
) ? CFU_CF1UNDERLINE
: CFU_UNDERLINENONE
;
70 if (lf
.lfStrikeOut
) cf
.dwEffects
|= CFE_STRIKEOUT
;
71 cf
.bPitchAndFamily
= lf
.lfPitchAndFamily
;
72 cf
.bCharSet
= lf
.lfCharSet
;
74 style
= ME_MakeStyle(&cf
);
75 text
->pDefaultStyle
= style
;
77 eol_len
= editor
->bEmulateVersion10
? 2 : 1;
78 para
->member
.para
.text
= ME_MakeStringN( cr_lf
, eol_len
);
80 run
= ME_MakeRun(style
, MERF_ENDPARA
);
81 run
->member
.run
.nCharOfs
= 0;
82 run
->member
.run
.len
= eol_len
;
83 run
->member
.run
.para
= ¶
->member
.para
;
85 ME_InsertBefore(text
->pLast
, para
);
86 ME_InsertBefore(text
->pLast
, run
);
87 para
->member
.para
.prev_para
= text
->pFirst
;
88 para
->member
.para
.next_para
= text
->pLast
;
89 text
->pFirst
->member
.para
.next_para
= para
;
90 text
->pLast
->member
.para
.prev_para
= para
;
92 text
->pLast
->member
.para
.nCharOfs
= editor
->bEmulateVersion10
? 2 : 1;
94 ME_DestroyContext(&c
);
97 static void ME_MarkForWrapping(ME_TextEditor
*editor
, ME_DisplayItem
*first
, const ME_DisplayItem
*last
)
101 first
->member
.para
.nFlags
|= MEPF_REWRAP
;
102 first
= first
->member
.para
.next_para
;
106 void ME_MarkAllForWrapping(ME_TextEditor
*editor
)
108 ME_MarkForWrapping(editor
, editor
->pBuffer
->pFirst
->member
.para
.next_para
, editor
->pBuffer
->pLast
);
111 static void ME_UpdateTableFlags(ME_DisplayItem
*para
)
113 para
->member
.para
.pFmt
->dwMask
|= PFM_TABLE
|PFM_TABLEROWDELIMITER
;
114 if (para
->member
.para
.pCell
) {
115 para
->member
.para
.nFlags
|= MEPF_CELL
;
117 para
->member
.para
.nFlags
&= ~MEPF_CELL
;
119 if (para
->member
.para
.nFlags
& MEPF_ROWEND
) {
120 para
->member
.para
.pFmt
->wEffects
|= PFE_TABLEROWDELIMITER
;
122 para
->member
.para
.pFmt
->wEffects
&= ~PFE_TABLEROWDELIMITER
;
124 if (para
->member
.para
.nFlags
& (MEPF_ROWSTART
|MEPF_CELL
|MEPF_ROWEND
))
125 para
->member
.para
.pFmt
->wEffects
|= PFE_TABLE
;
127 para
->member
.para
.pFmt
->wEffects
&= ~PFE_TABLE
;
130 static BOOL
ME_SetParaFormat(ME_TextEditor
*editor
, ME_DisplayItem
*para
, const PARAFORMAT2
*pFmt
)
135 assert(para
->member
.para
.pFmt
->cbSize
== sizeof(PARAFORMAT2
));
136 dwMask
= pFmt
->dwMask
;
137 if (pFmt
->cbSize
< sizeof(PARAFORMAT
))
139 else if (pFmt
->cbSize
< sizeof(PARAFORMAT2
))
144 add_undo_set_para_fmt( editor
, ¶
->member
.para
);
146 copy
= *para
->member
.para
.pFmt
;
148 #define COPY_FIELD(m, f) \
149 if (dwMask & (m)) { \
150 para->member.para.pFmt->dwMask |= m; \
151 para->member.para.pFmt->f = pFmt->f; \
154 COPY_FIELD(PFM_NUMBERING
, wNumbering
);
155 COPY_FIELD(PFM_STARTINDENT
, dxStartIndent
);
156 if (dwMask
& PFM_OFFSETINDENT
)
157 para
->member
.para
.pFmt
->dxStartIndent
+= pFmt
->dxStartIndent
;
158 COPY_FIELD(PFM_RIGHTINDENT
, dxRightIndent
);
159 COPY_FIELD(PFM_OFFSET
, dxOffset
);
160 COPY_FIELD(PFM_ALIGNMENT
, wAlignment
);
161 if (dwMask
& PFM_TABSTOPS
)
163 para
->member
.para
.pFmt
->cTabCount
= pFmt
->cTabCount
;
164 memcpy(para
->member
.para
.pFmt
->rgxTabs
, pFmt
->rgxTabs
, pFmt
->cTabCount
*sizeof(LONG
));
167 #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \
168 PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \
170 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
171 if (dwMask
& EFFECTS_MASK
)
173 para
->member
.para
.pFmt
->dwMask
|= dwMask
& EFFECTS_MASK
;
174 para
->member
.para
.pFmt
->wEffects
&= ~HIWORD(dwMask
);
175 para
->member
.para
.pFmt
->wEffects
|= pFmt
->wEffects
& HIWORD(dwMask
);
179 COPY_FIELD(PFM_SPACEBEFORE
, dySpaceBefore
);
180 COPY_FIELD(PFM_SPACEAFTER
, dySpaceAfter
);
181 COPY_FIELD(PFM_LINESPACING
, dyLineSpacing
);
182 COPY_FIELD(PFM_STYLE
, sStyle
);
183 COPY_FIELD(PFM_LINESPACING
, bLineSpacingRule
);
184 COPY_FIELD(PFM_SHADING
, wShadingWeight
);
185 COPY_FIELD(PFM_SHADING
, wShadingStyle
);
186 COPY_FIELD(PFM_NUMBERINGSTART
, wNumberingStart
);
187 COPY_FIELD(PFM_NUMBERINGSTYLE
, wNumberingStyle
);
188 COPY_FIELD(PFM_NUMBERINGTAB
, wNumberingTab
);
189 COPY_FIELD(PFM_BORDER
, wBorderSpace
);
190 COPY_FIELD(PFM_BORDER
, wBorderWidth
);
191 COPY_FIELD(PFM_BORDER
, wBorders
);
193 para
->member
.para
.pFmt
->dwMask
|= dwMask
;
196 if (memcmp(©
, para
->member
.para
.pFmt
, sizeof(PARAFORMAT2
)))
197 para
->member
.para
.nFlags
|= MEPF_REWRAP
;
202 /* split paragraph at the beginning of the run */
203 ME_DisplayItem
*ME_SplitParagraph(ME_TextEditor
*editor
, ME_DisplayItem
*run
,
204 ME_Style
*style
, const WCHAR
*eol_str
, int eol_len
,
207 ME_DisplayItem
*next_para
= NULL
;
208 ME_DisplayItem
*run_para
= NULL
;
209 ME_DisplayItem
*new_para
= make_para(editor
);
210 ME_DisplayItem
*end_run
;
213 int run_flags
= MERF_ENDPARA
;
215 if (!editor
->bEmulateVersion10
) { /* v4.1 */
216 /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */
217 assert(!(paraFlags
& ~(MEPF_CELL
|MEPF_ROWSTART
|MEPF_ROWEND
)));
218 assert(!(paraFlags
& (paraFlags
-1)));
219 if (paraFlags
== MEPF_CELL
)
220 run_flags
|= MERF_ENDCELL
;
221 else if (paraFlags
== MEPF_ROWSTART
)
222 run_flags
|= MERF_TABLESTART
|MERF_HIDDEN
;
223 } else { /* v1.0 - v3.0 */
224 assert(!(paraFlags
& (MEPF_CELL
|MEPF_ROWSTART
|MEPF_ROWEND
)));
226 assert(run
->type
== diRun
);
227 run_para
= ME_GetParagraph(run
);
228 assert(run_para
->member
.para
.pFmt
->cbSize
== sizeof(PARAFORMAT2
));
230 new_para
->member
.para
.text
= ME_VSplitString( run_para
->member
.para
.text
, run
->member
.run
.nCharOfs
);
232 end_run
= ME_MakeRun(style
, run_flags
);
233 ofs
= end_run
->member
.run
.nCharOfs
= run
->member
.run
.nCharOfs
;
234 end_run
->member
.run
.len
= eol_len
;
235 end_run
->member
.run
.para
= run
->member
.run
.para
;
236 ME_AppendString( run_para
->member
.para
.text
, eol_str
, eol_len
);
237 next_para
= run_para
->member
.para
.next_para
;
238 assert(next_para
== ME_FindItemFwd(run_para
, diParagraphOrEnd
));
240 add_undo_join_paras( editor
, run_para
->member
.para
.nCharOfs
+ ofs
);
242 /* Update selection cursors to point to the correct paragraph. */
243 for (i
= 0; i
< editor
->nCursors
; i
++) {
244 if (editor
->pCursors
[i
].pPara
== run_para
&&
245 run
->member
.run
.nCharOfs
<= editor
->pCursors
[i
].pRun
->member
.run
.nCharOfs
)
247 editor
->pCursors
[i
].pPara
= new_para
;
251 /* the new paragraph will have a different starting offset, so let's update its runs */
253 while(pp
->type
== diRun
) {
254 pp
->member
.run
.nCharOfs
-= ofs
;
255 pp
->member
.run
.para
= &new_para
->member
.para
;
256 pp
= ME_FindItemFwd(pp
, diRunOrParagraphOrEnd
);
258 new_para
->member
.para
.nCharOfs
= run_para
->member
.para
.nCharOfs
+ ofs
;
259 new_para
->member
.para
.nCharOfs
+= eol_len
;
260 new_para
->member
.para
.nFlags
= MEPF_REWRAP
;
262 /* FIXME initialize format style and call ME_SetParaFormat blah blah */
263 *new_para
->member
.para
.pFmt
= *run_para
->member
.para
.pFmt
;
264 new_para
->member
.para
.border
= run_para
->member
.para
.border
;
266 /* insert paragraph into paragraph double linked list */
267 new_para
->member
.para
.prev_para
= run_para
;
268 new_para
->member
.para
.next_para
= next_para
;
269 run_para
->member
.para
.next_para
= new_para
;
270 next_para
->member
.para
.prev_para
= new_para
;
272 /* insert end run of the old paragraph, and new paragraph, into DI double linked list */
273 ME_InsertBefore(run
, new_para
);
274 ME_InsertBefore(new_para
, end_run
);
276 if (!editor
->bEmulateVersion10
) { /* v4.1 */
277 if (paraFlags
& (MEPF_ROWSTART
|MEPF_CELL
))
279 ME_DisplayItem
*cell
= ME_MakeDI(diCell
);
280 ME_InsertBefore(new_para
, cell
);
281 new_para
->member
.para
.pCell
= cell
;
282 cell
->member
.cell
.next_cell
= NULL
;
283 if (paraFlags
& MEPF_ROWSTART
)
285 run_para
->member
.para
.nFlags
|= MEPF_ROWSTART
;
286 cell
->member
.cell
.prev_cell
= NULL
;
287 cell
->member
.cell
.parent_cell
= run_para
->member
.para
.pCell
;
288 if (run_para
->member
.para
.pCell
)
289 cell
->member
.cell
.nNestingLevel
= run_para
->member
.para
.pCell
->member
.cell
.nNestingLevel
+ 1;
291 cell
->member
.cell
.nNestingLevel
= 1;
293 cell
->member
.cell
.prev_cell
= run_para
->member
.para
.pCell
;
294 assert(cell
->member
.cell
.prev_cell
);
295 cell
->member
.cell
.prev_cell
->member
.cell
.next_cell
= cell
;
296 assert(run_para
->member
.para
.nFlags
& MEPF_CELL
);
297 assert(!(run_para
->member
.para
.nFlags
& MEPF_ROWSTART
));
298 cell
->member
.cell
.nNestingLevel
= cell
->member
.cell
.prev_cell
->member
.cell
.nNestingLevel
;
299 cell
->member
.cell
.parent_cell
= cell
->member
.cell
.prev_cell
->member
.cell
.parent_cell
;
301 } else if (paraFlags
& MEPF_ROWEND
) {
302 run_para
->member
.para
.nFlags
|= MEPF_ROWEND
;
303 run_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
->member
.cell
.parent_cell
;
304 new_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
;
305 assert(run_para
->member
.para
.prev_para
->member
.para
.nFlags
& MEPF_CELL
);
306 assert(!(run_para
->member
.para
.prev_para
->member
.para
.nFlags
& MEPF_ROWSTART
));
307 if (new_para
->member
.para
.pCell
!= new_para
->member
.para
.next_para
->member
.para
.pCell
308 && new_para
->member
.para
.next_para
->member
.para
.pCell
309 && !new_para
->member
.para
.next_para
->member
.para
.pCell
->member
.cell
.prev_cell
)
311 /* Row starts just after the row that was ended. */
312 new_para
->member
.para
.nFlags
|= MEPF_ROWSTART
;
315 new_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
;
317 ME_UpdateTableFlags(run_para
);
318 ME_UpdateTableFlags(new_para
);
321 /* force rewrap of the */
322 run_para
->member
.para
.prev_para
->member
.para
.nFlags
|= MEPF_REWRAP
;
323 new_para
->member
.para
.prev_para
->member
.para
.nFlags
|= MEPF_REWRAP
;
325 /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */
326 ME_PropagateCharOffset(next_para
, eol_len
);
327 editor
->nParagraphs
++;
332 /* join tp with tp->member.para.next_para, keeping tp's style; this
333 * is consistent with the original */
334 ME_DisplayItem
*ME_JoinParagraphs(ME_TextEditor
*editor
, ME_DisplayItem
*tp
,
335 BOOL keepFirstParaFormat
)
337 ME_DisplayItem
*pNext
, *pFirstRunInNext
, *pRun
, *pTmp
, *pCell
= NULL
;
341 ME_Cursor startCur
, endCur
;
344 assert(tp
->type
== diParagraph
);
345 assert(tp
->member
.para
.next_para
);
346 assert(tp
->member
.para
.next_para
->type
== diParagraph
);
348 pNext
= tp
->member
.para
.next_para
;
350 /* Need to locate end-of-paragraph run here, in order to know end_len */
351 pRun
= ME_FindItemBack(pNext
, diRunOrParagraph
);
354 assert(pRun
->type
== diRun
);
355 assert(pRun
->member
.run
.nFlags
& MERF_ENDPARA
);
357 end_len
= pRun
->member
.run
.len
;
358 eol_str
= ME_VSplitString( tp
->member
.para
.text
, pRun
->member
.run
.nCharOfs
);
359 ME_AppendString( tp
->member
.para
.text
, pNext
->member
.para
.text
->szData
, pNext
->member
.para
.text
->nLen
);
361 /* null char format operation to store the original char format for the ENDPARA run */
362 ME_InitCharFormat2W(&fmt
);
363 endCur
.pPara
= pNext
;
364 endCur
.pRun
= ME_FindItemFwd(pNext
, diRun
);
367 ME_PrevRun(&startCur
.pPara
, &startCur
.pRun
);
368 ME_SetCharFormat(editor
, &startCur
, &endCur
, &fmt
);
370 if (!editor
->bEmulateVersion10
) { /* v4.1 */
371 /* Table cell/row properties are always moved over from the removed para. */
372 tp
->member
.para
.nFlags
= pNext
->member
.para
.nFlags
;
373 tp
->member
.para
.pCell
= pNext
->member
.para
.pCell
;
375 /* Remove cell boundary if it is between the end paragraph run and the next
376 * paragraph display item. */
377 for (pTmp
= pRun
->next
; pTmp
!= pNext
; pTmp
= pTmp
->next
)
379 if (pTmp
->type
== diCell
)
387 add_undo_split_para( editor
, &pNext
->member
.para
, eol_str
, pCell
? &pCell
->member
.cell
: NULL
);
392 if (pCell
->member
.cell
.prev_cell
)
393 pCell
->member
.cell
.prev_cell
->member
.cell
.next_cell
= pCell
->member
.cell
.next_cell
;
394 if (pCell
->member
.cell
.next_cell
)
395 pCell
->member
.cell
.next_cell
->member
.cell
.prev_cell
= pCell
->member
.cell
.prev_cell
;
396 ME_DestroyDisplayItem( pCell
);
399 if (!keepFirstParaFormat
)
401 add_undo_set_para_fmt( editor
, &tp
->member
.para
);
402 *tp
->member
.para
.pFmt
= *pNext
->member
.para
.pFmt
;
403 tp
->member
.para
.border
= pNext
->member
.para
.border
;
406 shift
= pNext
->member
.para
.nCharOfs
- tp
->member
.para
.nCharOfs
- end_len
;
408 pFirstRunInNext
= ME_FindItemFwd(pNext
, diRunOrParagraph
);
410 assert(pFirstRunInNext
->type
== diRun
);
412 /* Update selection cursors so they don't point to the removed end
413 * paragraph run, and point to the correct paragraph. */
414 for (i
=0; i
< editor
->nCursors
; i
++) {
415 if (editor
->pCursors
[i
].pRun
== pRun
) {
416 editor
->pCursors
[i
].pRun
= pFirstRunInNext
;
417 editor
->pCursors
[i
].nOffset
= 0;
418 } else if (editor
->pCursors
[i
].pPara
== pNext
) {
419 editor
->pCursors
[i
].pPara
= tp
;
425 pTmp
= ME_FindItemFwd(pTmp
, diRunOrParagraphOrEnd
);
426 if (pTmp
->type
!= diRun
)
428 TRACE("shifting %s by %d (previous %d)\n", debugstr_run( &pTmp
->member
.run
), shift
, pTmp
->member
.run
.nCharOfs
);
429 pTmp
->member
.run
.nCharOfs
+= shift
;
430 pTmp
->member
.run
.para
= &tp
->member
.para
;
434 ME_DestroyDisplayItem(pRun
);
436 if (editor
->pLastSelStartPara
== pNext
)
437 editor
->pLastSelStartPara
= tp
;
438 if (editor
->pLastSelEndPara
== pNext
)
439 editor
->pLastSelEndPara
= tp
;
441 tp
->member
.para
.next_para
= pNext
->member
.para
.next_para
;
442 pNext
->member
.para
.next_para
->member
.para
.prev_para
= tp
;
444 ME_DestroyDisplayItem(pNext
);
446 ME_PropagateCharOffset(tp
->member
.para
.next_para
, -end_len
);
448 ME_CheckCharOffsets(editor
);
450 editor
->nParagraphs
--;
451 tp
->member
.para
.nFlags
|= MEPF_REWRAP
;
455 ME_DisplayItem
*ME_GetParagraph(ME_DisplayItem
*item
) {
456 return ME_FindItemBackOrHere(item
, diParagraph
);
459 void ME_DumpParaStyleToBuf(const PARAFORMAT2
*pFmt
, char buf
[2048])
464 #define DUMP(mask, name, fmt, field) \
465 if (pFmt->dwMask & (mask)) p += sprintf(p, "%-22s" fmt "\n", name, pFmt->field); \
466 else p += sprintf(p, "%-22sN/A\n", name);
468 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
469 #define DUMP_EFFECT(mask, name) \
470 p += sprintf(p, "%-22s%s\n", name, (pFmt->dwMask & (mask)) ? ((pFmt->wEffects & ((mask) >> 16)) ? "yes" : "no") : "N/A");
472 DUMP(PFM_NUMBERING
, "Numbering:", "%u", wNumbering
);
473 DUMP_EFFECT(PFM_DONOTHYPHEN
, "Disable auto-hyphen:");
474 DUMP_EFFECT(PFM_KEEP
, "No page break in para:");
475 DUMP_EFFECT(PFM_KEEPNEXT
, "No page break in para & next:");
476 DUMP_EFFECT(PFM_NOLINENUMBER
, "No line number:");
477 DUMP_EFFECT(PFM_NOWIDOWCONTROL
, "No widow & orphan:");
478 DUMP_EFFECT(PFM_PAGEBREAKBEFORE
, "Page break before:");
479 DUMP_EFFECT(PFM_RTLPARA
, "RTL para:");
480 DUMP_EFFECT(PFM_SIDEBYSIDE
, "Side by side:");
481 DUMP_EFFECT(PFM_TABLE
, "Table:");
482 DUMP(PFM_OFFSETINDENT
, "Offset indent:", "%d", dxStartIndent
);
483 DUMP(PFM_STARTINDENT
, "Start indent:", "%d", dxStartIndent
);
484 DUMP(PFM_RIGHTINDENT
, "Right indent:", "%d", dxRightIndent
);
485 DUMP(PFM_OFFSET
, "Offset:", "%d", dxOffset
);
486 if (pFmt
->dwMask
& PFM_ALIGNMENT
) {
487 switch (pFmt
->wAlignment
) {
488 case PFA_LEFT
: p
+= sprintf(p
, "Alignment: left\n"); break;
489 case PFA_RIGHT
: p
+= sprintf(p
, "Alignment: right\n"); break;
490 case PFA_CENTER
: p
+= sprintf(p
, "Alignment: center\n"); break;
491 case PFA_JUSTIFY
: p
+= sprintf(p
, "Alignment: justify\n"); break;
492 default : p
+= sprintf(p
, "Alignment: incorrect %d\n", pFmt
->wAlignment
); break;
495 else p
+= sprintf(p
, "Alignment: N/A\n");
496 DUMP(PFM_TABSTOPS
, "Tab Stops:", "%d", cTabCount
);
497 if (pFmt
->dwMask
& PFM_TABSTOPS
) {
499 p
+= sprintf(p
, "\t");
500 for (i
= 0; i
< pFmt
->cTabCount
; i
++) p
+= sprintf(p
, "%x ", pFmt
->rgxTabs
[i
]);
501 p
+= sprintf(p
, "\n");
503 DUMP(PFM_SPACEBEFORE
, "Space Before:", "%d", dySpaceBefore
);
504 DUMP(PFM_SPACEAFTER
, "Space After:", "%d", dySpaceAfter
);
505 DUMP(PFM_LINESPACING
, "Line spacing:", "%d", dyLineSpacing
);
506 DUMP(PFM_STYLE
, "Text style:", "%d", sStyle
);
507 DUMP(PFM_LINESPACING
, "Line spacing rule:", "%u", bLineSpacingRule
);
508 /* bOutlineLevel should be 0 */
509 DUMP(PFM_SHADING
, "Shading Weight:", "%u", wShadingWeight
);
510 DUMP(PFM_SHADING
, "Shading Style:", "%u", wShadingStyle
);
511 DUMP(PFM_NUMBERINGSTART
, "Numbering Start:", "%u", wNumberingStart
);
512 DUMP(PFM_NUMBERINGSTYLE
, "Numbering Style:", "0x%x", wNumberingStyle
);
513 DUMP(PFM_NUMBERINGTAB
, "Numbering Tab:", "%u", wNumberingStyle
);
514 DUMP(PFM_BORDER
, "Border Space:", "%u", wBorderSpace
);
515 DUMP(PFM_BORDER
, "Border Width:", "%u", wBorderWidth
);
516 DUMP(PFM_BORDER
, "Borders:", "%u", wBorders
);
523 ME_GetSelectionParas(ME_TextEditor
*editor
, ME_DisplayItem
**para
, ME_DisplayItem
**para_end
)
525 ME_Cursor
*pEndCursor
= &editor
->pCursors
[1];
527 *para
= editor
->pCursors
[0].pPara
;
528 *para_end
= editor
->pCursors
[1].pPara
;
529 if (*para
== *para_end
)
532 if ((*para_end
)->member
.para
.nCharOfs
< (*para
)->member
.para
.nCharOfs
) {
533 ME_DisplayItem
*tmp
= *para
;
537 pEndCursor
= &editor
->pCursors
[0];
540 /* The paragraph at the end of a non-empty selection isn't included
541 * if the selection ends at the start of the paragraph. */
542 if (!pEndCursor
->pRun
->member
.run
.nCharOfs
&& !pEndCursor
->nOffset
)
543 *para_end
= (*para_end
)->member
.para
.prev_para
;
547 BOOL
ME_SetSelectionParaFormat(ME_TextEditor
*editor
, const PARAFORMAT2
*pFmt
)
549 ME_DisplayItem
*para
, *para_end
;
551 ME_GetSelectionParas(editor
, ¶
, ¶_end
);
554 ME_SetParaFormat(editor
, para
, pFmt
);
555 if (para
== para_end
)
557 para
= para
->member
.para
.next_para
;
563 static void ME_GetParaFormat(ME_TextEditor
*editor
,
564 const ME_DisplayItem
*para
,
567 UINT cbSize
= pFmt
->cbSize
;
568 if (pFmt
->cbSize
>= sizeof(PARAFORMAT2
)) {
569 *pFmt
= *para
->member
.para
.pFmt
;
571 CopyMemory(pFmt
, para
->member
.para
.pFmt
, pFmt
->cbSize
);
572 pFmt
->dwMask
&= PFM_ALL
;
574 pFmt
->cbSize
= cbSize
;
577 void ME_GetSelectionParaFormat(ME_TextEditor
*editor
, PARAFORMAT2
*pFmt
)
579 ME_DisplayItem
*para
, *para_end
;
582 if (pFmt
->cbSize
< sizeof(PARAFORMAT
)) {
587 ME_GetSelectionParas(editor
, ¶
, ¶_end
);
589 ME_GetParaFormat(editor
, para
, pFmt
);
591 /* Invalidate values that change across the selected paragraphs. */
592 while (para
!= para_end
)
594 para
= para
->member
.para
.next_para
;
595 curFmt
= para
->member
.para
.pFmt
;
597 #define CHECK_FIELD(m, f) \
598 if (pFmt->f != curFmt->f) pFmt->dwMask &= ~(m);
600 CHECK_FIELD(PFM_NUMBERING
, wNumbering
);
601 CHECK_FIELD(PFM_STARTINDENT
, dxStartIndent
);
602 CHECK_FIELD(PFM_RIGHTINDENT
, dxRightIndent
);
603 CHECK_FIELD(PFM_OFFSET
, dxOffset
);
604 CHECK_FIELD(PFM_ALIGNMENT
, wAlignment
);
605 if (pFmt
->dwMask
& PFM_TABSTOPS
) {
606 if (pFmt
->cTabCount
!= para
->member
.para
.pFmt
->cTabCount
||
607 memcmp(pFmt
->rgxTabs
, curFmt
->rgxTabs
, curFmt
->cTabCount
*sizeof(int)))
608 pFmt
->dwMask
&= ~PFM_TABSTOPS
;
611 if (pFmt
->dwMask
>= sizeof(PARAFORMAT2
))
613 pFmt
->dwMask
&= ~((pFmt
->wEffects
^ curFmt
->wEffects
) << 16);
614 CHECK_FIELD(PFM_SPACEBEFORE
, dySpaceBefore
);
615 CHECK_FIELD(PFM_SPACEAFTER
, dySpaceAfter
);
616 CHECK_FIELD(PFM_LINESPACING
, dyLineSpacing
);
617 CHECK_FIELD(PFM_STYLE
, sStyle
);
618 CHECK_FIELD(PFM_SPACEAFTER
, bLineSpacingRule
);
619 CHECK_FIELD(PFM_SHADING
, wShadingWeight
);
620 CHECK_FIELD(PFM_SHADING
, wShadingStyle
);
621 CHECK_FIELD(PFM_NUMBERINGSTART
, wNumberingStart
);
622 CHECK_FIELD(PFM_NUMBERINGSTYLE
, wNumberingStyle
);
623 CHECK_FIELD(PFM_NUMBERINGTAB
, wNumberingTab
);
624 CHECK_FIELD(PFM_BORDER
, wBorderSpace
);
625 CHECK_FIELD(PFM_BORDER
, wBorderWidth
);
626 CHECK_FIELD(PFM_BORDER
, wBorders
);
632 void ME_SetDefaultParaFormat(ME_TextEditor
*editor
, PARAFORMAT2
*pFmt
)
634 ZeroMemory(pFmt
, sizeof(PARAFORMAT2
));
635 pFmt
->cbSize
= sizeof(PARAFORMAT2
);
636 pFmt
->dwMask
= PFM_ALL2
;
637 pFmt
->wAlignment
= editor
->alignStyle
;
639 pFmt
->bOutlineLevel
= TRUE
;