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 ME_SetDefaultParaFormat(editor
, &item
->member
.para
.fmt
);
31 item
->member
.para
.nFlags
= MEPF_REWRAP
;
35 void ME_MakeFirstParagraph(ME_TextEditor
*editor
)
41 ME_TextBuffer
*text
= editor
->pBuffer
;
42 ME_DisplayItem
*para
= make_para(editor
);
46 WCHAR cr_lf
[] = {'\r','\n',0};
48 ME_InitContext(&c
, editor
, ITextHost_TxGetDC(editor
->texthost
));
50 hf
= GetStockObject(SYSTEM_FONT
);
52 GetObjectW(hf
, sizeof(LOGFONTW
), &lf
);
53 ZeroMemory(&cf
, sizeof(cf
));
54 cf
.cbSize
= sizeof(cf
);
55 cf
.dwMask
= CFM_ANIMATION
|CFM_BACKCOLOR
|CFM_CHARSET
|CFM_COLOR
|CFM_FACE
|CFM_KERNING
|CFM_LCID
|CFM_OFFSET
;
56 cf
.dwMask
|= CFM_REVAUTHOR
|CFM_SIZE
|CFM_SPACING
|CFM_STYLE
|CFM_UNDERLINETYPE
|CFM_WEIGHT
;
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_UNDERLINE
;
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 if (lf
.lfUnderline
) cf
.dwEffects
|= CFE_UNDERLINE
;
70 cf
.bUnderlineType
= CFU_UNDERLINE
;
71 if (lf
.lfStrikeOut
) cf
.dwEffects
|= CFE_STRIKEOUT
;
72 cf
.bPitchAndFamily
= lf
.lfPitchAndFamily
;
73 cf
.bCharSet
= lf
.lfCharSet
;
74 cf
.lcid
= GetSystemDefaultLCID();
76 style
= ME_MakeStyle(&cf
);
77 text
->pDefaultStyle
= style
;
79 eol_len
= editor
->bEmulateVersion10
? 2 : 1;
80 para
->member
.para
.text
= ME_MakeStringN( cr_lf
, eol_len
);
82 run
= ME_MakeRun(style
, MERF_ENDPARA
);
83 run
->member
.run
.nCharOfs
= 0;
84 run
->member
.run
.len
= eol_len
;
85 run
->member
.run
.para
= ¶
->member
.para
;
87 para
->member
.para
.eop_run
= &run
->member
.run
;
89 ME_InsertBefore(text
->pLast
, para
);
90 ME_InsertBefore(text
->pLast
, run
);
91 para
->member
.para
.prev_para
= text
->pFirst
;
92 para
->member
.para
.next_para
= text
->pLast
;
93 text
->pFirst
->member
.para
.next_para
= para
;
94 text
->pLast
->member
.para
.prev_para
= para
;
96 text
->pLast
->member
.para
.nCharOfs
= editor
->bEmulateVersion10
? 2 : 1;
98 ME_DestroyContext(&c
);
101 static void ME_MarkForWrapping(ME_TextEditor
*editor
, ME_DisplayItem
*first
, const ME_DisplayItem
*last
)
105 first
->member
.para
.nFlags
|= MEPF_REWRAP
;
106 first
= first
->member
.para
.next_para
;
110 void ME_MarkAllForWrapping(ME_TextEditor
*editor
)
112 ME_MarkForWrapping(editor
, editor
->pBuffer
->pFirst
->member
.para
.next_para
, editor
->pBuffer
->pLast
);
115 static void ME_UpdateTableFlags(ME_DisplayItem
*para
)
117 para
->member
.para
.fmt
.dwMask
|= PFM_TABLE
|PFM_TABLEROWDELIMITER
;
118 if (para
->member
.para
.pCell
) {
119 para
->member
.para
.nFlags
|= MEPF_CELL
;
121 para
->member
.para
.nFlags
&= ~MEPF_CELL
;
123 if (para
->member
.para
.nFlags
& MEPF_ROWEND
) {
124 para
->member
.para
.fmt
.wEffects
|= PFE_TABLEROWDELIMITER
;
126 para
->member
.para
.fmt
.wEffects
&= ~PFE_TABLEROWDELIMITER
;
128 if (para
->member
.para
.nFlags
& (MEPF_ROWSTART
|MEPF_CELL
|MEPF_ROWEND
))
129 para
->member
.para
.fmt
.wEffects
|= PFE_TABLE
;
131 para
->member
.para
.fmt
.wEffects
&= ~PFE_TABLE
;
134 static inline BOOL
para_num_same_list( const PARAFORMAT2
*item
, const PARAFORMAT2
*base
)
136 return item
->wNumbering
== base
->wNumbering
&&
137 item
->wNumberingStart
== base
->wNumberingStart
&&
138 item
->wNumberingStyle
== base
->wNumberingStyle
&&
139 !(item
->wNumberingStyle
& PFNS_NEWNUMBER
);
142 static int para_num_get_num( ME_Paragraph
*para
)
144 ME_DisplayItem
*prev
;
145 int num
= para
->fmt
.wNumberingStart
;
147 for (prev
= para
->prev_para
; prev
->type
== diParagraph
;
148 para
= &prev
->member
.para
, prev
= prev
->member
.para
.prev_para
, num
++)
150 if (!para_num_same_list( &prev
->member
.para
.fmt
, ¶
->fmt
)) break;
155 static ME_String
*para_num_get_str( ME_Paragraph
*para
, WORD num
)
157 /* max 4 Roman letters (representing '8') / decade + '(' + ')' */
158 ME_String
*str
= ME_MakeStringEmpty( 20 + 2 );
160 static const WCHAR fmtW
[] = {'%', 'd', 0};
161 static const WORD letter_base
[] = { 1, 26, 26 * 26, 26 * 26 * 26 };
162 /* roman_base should start on a '5' not a '1', otherwise the 'total' code will need adjusting.
163 'N' and 'O' are what MS uses for 5000 and 10000, their version doesn't work well above 30000,
164 but we'll use 'P' as the obvious extension, this gets us up to 2^16, which is all we care about. */
172 {50000, 'P'}, {10000, 'O'}, {5000, 'N'}, {1000, 'M'},
173 {500, 'D'}, {100, 'C'}, {50, 'L'}, {10, 'X'}, {5, 'V'}, {1, 'I'}
176 WORD letter
, total
, char_offset
= 0;
178 if (!str
) return NULL
;
182 if ((para
->fmt
.wNumberingStyle
& 0xf00) == PFNS_PARENS
)
185 switch (para
->fmt
.wNumbering
)
189 p
+= sprintfW( p
, fmtW
, num
);
193 char_offset
= 'a' - 'A';
198 /* This is not base-26 (or 27) as zeros don't count unless they are leading zeros.
199 It's simplest to start with the least significant letter, so first calculate how many letters are needed. */
200 for (i
= 0, total
= 0; i
< sizeof(letter_base
) / sizeof(letter_base
[0]); i
++)
202 total
+= letter_base
[i
];
203 if (num
< total
) break;
206 for (i
= 0; i
< len
; i
++)
208 num
-= letter_base
[i
];
209 letter
= (num
/ letter_base
[i
]) % 26;
210 p
[len
- i
- 1] = letter
+ 'A' + char_offset
;
217 char_offset
= 'a' - 'A';
222 for (i
= 0; i
< sizeof(roman_base
) / sizeof(roman_base
[0]); i
++)
226 if (i
% 2 == 0) /* eg 5000, check for 9000 */
227 total
= roman_base
[i
].base
+ 4 * roman_base
[i
+ 1].base
;
228 else /* eg 1000, check for 4000 */
229 total
= 4 * roman_base
[i
].base
;
233 *p
++ = roman_base
[(i
& ~1) + 1].letter
+ char_offset
;
234 *p
++ = roman_base
[i
- 1].letter
+ char_offset
;
240 len
= num
/ roman_base
[i
].base
;
243 *p
++ = roman_base
[i
].letter
+ char_offset
;
244 num
-= roman_base
[i
].base
;
251 switch (para
->fmt
.wNumberingStyle
& 0xf00)
265 str
->nLen
= p
- str
->szData
;
269 void para_num_init( ME_Context
*c
, ME_Paragraph
*para
)
273 static const WCHAR bullet_font
[] = {'S','y','m','b','o','l',0};
274 static const WCHAR bullet_str
[] = {0xb7, 0};
275 static const WCHAR spaceW
[] = {' ', 0};
279 if (para
->para_num
.style
&& para
->para_num
.text
) return;
281 if (!para
->para_num
.style
)
283 style
= para
->eop_run
->style
;
285 if (para
->fmt
.wNumbering
== PFN_BULLET
)
287 cf
.cbSize
= sizeof(cf
);
288 cf
.dwMask
= CFM_FACE
| CFM_CHARSET
;
289 memcpy( cf
.szFaceName
, bullet_font
, sizeof(bullet_font
) );
290 cf
.bCharSet
= SYMBOL_CHARSET
;
291 style
= ME_ApplyStyle( c
->editor
, style
, &cf
);
295 ME_AddRefStyle( style
);
298 para
->para_num
.style
= style
;
301 if (!para
->para_num
.text
)
303 if (para
->fmt
.wNumbering
!= PFN_BULLET
)
304 para
->para_num
.text
= para_num_get_str( para
, para_num_get_num( para
) );
306 para
->para_num
.text
= ME_MakeStringConst( bullet_str
, 1 );
309 old_font
= ME_SelectStyleFont( c
, para
->para_num
.style
);
310 GetTextExtentPointW( c
->hDC
, para
->para_num
.text
->szData
, para
->para_num
.text
->nLen
, &sz
);
311 para
->para_num
.width
= sz
.cx
;
312 GetTextExtentPointW( c
->hDC
, spaceW
, 1, &sz
);
313 para
->para_num
.width
+= sz
.cx
;
314 ME_UnselectStyleFont( c
, para
->para_num
.style
, old_font
);
317 void para_num_clear( struct para_num
*pn
)
321 ME_ReleaseStyle( pn
->style
);
324 ME_DestroyString( pn
->text
);
328 static void para_num_clear_list( ME_Paragraph
*para
, const PARAFORMAT2
*orig_fmt
)
332 para
->nFlags
|= MEPF_REWRAP
;
333 para_num_clear( ¶
->para_num
);
334 if (para
->next_para
->type
!= diParagraph
) break;
335 para
= ¶
->next_para
->member
.para
;
336 } while (para_num_same_list( ¶
->fmt
, orig_fmt
));
339 static BOOL
ME_SetParaFormat(ME_TextEditor
*editor
, ME_Paragraph
*para
, const PARAFORMAT2
*pFmt
)
344 assert(para
->fmt
.cbSize
== sizeof(PARAFORMAT2
));
345 dwMask
= pFmt
->dwMask
;
346 if (pFmt
->cbSize
< sizeof(PARAFORMAT
))
348 else if (pFmt
->cbSize
< sizeof(PARAFORMAT2
))
353 add_undo_set_para_fmt( editor
, para
);
357 #define COPY_FIELD(m, f) \
358 if (dwMask & (m)) { \
359 para->fmt.dwMask |= m; \
360 para->fmt.f = pFmt->f; \
363 COPY_FIELD(PFM_NUMBERING
, wNumbering
);
364 COPY_FIELD(PFM_STARTINDENT
, dxStartIndent
);
365 if (dwMask
& PFM_OFFSETINDENT
)
366 para
->fmt
.dxStartIndent
+= pFmt
->dxStartIndent
;
367 COPY_FIELD(PFM_RIGHTINDENT
, dxRightIndent
);
368 COPY_FIELD(PFM_OFFSET
, dxOffset
);
369 COPY_FIELD(PFM_ALIGNMENT
, wAlignment
);
370 if (dwMask
& PFM_TABSTOPS
)
372 para
->fmt
.cTabCount
= pFmt
->cTabCount
;
373 memcpy(para
->fmt
.rgxTabs
, pFmt
->rgxTabs
, pFmt
->cTabCount
*sizeof(LONG
));
376 #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \
377 PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \
379 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
380 if (dwMask
& EFFECTS_MASK
)
382 para
->fmt
.dwMask
|= dwMask
& EFFECTS_MASK
;
383 para
->fmt
.wEffects
&= ~HIWORD(dwMask
);
384 para
->fmt
.wEffects
|= pFmt
->wEffects
& HIWORD(dwMask
);
388 COPY_FIELD(PFM_SPACEBEFORE
, dySpaceBefore
);
389 COPY_FIELD(PFM_SPACEAFTER
, dySpaceAfter
);
390 COPY_FIELD(PFM_LINESPACING
, dyLineSpacing
);
391 COPY_FIELD(PFM_STYLE
, sStyle
);
392 COPY_FIELD(PFM_LINESPACING
, bLineSpacingRule
);
393 COPY_FIELD(PFM_SHADING
, wShadingWeight
);
394 COPY_FIELD(PFM_SHADING
, wShadingStyle
);
395 COPY_FIELD(PFM_NUMBERINGSTART
, wNumberingStart
);
396 COPY_FIELD(PFM_NUMBERINGSTYLE
, wNumberingStyle
);
397 COPY_FIELD(PFM_NUMBERINGTAB
, wNumberingTab
);
398 COPY_FIELD(PFM_BORDER
, wBorderSpace
);
399 COPY_FIELD(PFM_BORDER
, wBorderWidth
);
400 COPY_FIELD(PFM_BORDER
, wBorders
);
402 para
->fmt
.dwMask
|= dwMask
;
405 if (memcmp(©
, ¶
->fmt
, sizeof(PARAFORMAT2
)))
407 para
->nFlags
|= MEPF_REWRAP
;
408 if (((dwMask
& PFM_NUMBERING
) && (copy
.wNumbering
!= para
->fmt
.wNumbering
)) ||
409 ((dwMask
& PFM_NUMBERINGSTART
) && (copy
.wNumberingStart
!= para
->fmt
.wNumberingStart
)) ||
410 ((dwMask
& PFM_NUMBERINGSTYLE
) && (copy
.wNumberingStyle
!= para
->fmt
.wNumberingStyle
)))
412 para_num_clear_list( para
, ©
);
419 /* split paragraph at the beginning of the run */
420 ME_DisplayItem
*ME_SplitParagraph(ME_TextEditor
*editor
, ME_DisplayItem
*run
,
421 ME_Style
*style
, const WCHAR
*eol_str
, int eol_len
,
424 ME_DisplayItem
*next_para
= NULL
;
425 ME_DisplayItem
*run_para
= NULL
;
426 ME_DisplayItem
*new_para
= make_para(editor
);
427 ME_DisplayItem
*end_run
;
430 int run_flags
= MERF_ENDPARA
;
432 if (!editor
->bEmulateVersion10
) { /* v4.1 */
433 /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */
434 assert(!(paraFlags
& ~(MEPF_CELL
|MEPF_ROWSTART
|MEPF_ROWEND
)));
435 assert(!(paraFlags
& (paraFlags
-1)));
436 if (paraFlags
== MEPF_CELL
)
437 run_flags
|= MERF_ENDCELL
;
438 else if (paraFlags
== MEPF_ROWSTART
)
439 run_flags
|= MERF_TABLESTART
|MERF_HIDDEN
;
440 } else { /* v1.0 - v3.0 */
441 assert(!(paraFlags
& (MEPF_CELL
|MEPF_ROWSTART
|MEPF_ROWEND
)));
443 assert(run
->type
== diRun
);
444 run_para
= ME_GetParagraph(run
);
445 assert(run_para
->member
.para
.fmt
.cbSize
== sizeof(PARAFORMAT2
));
447 /* Clear any cached para numbering following this paragraph */
448 if (run_para
->member
.para
.fmt
.wNumbering
)
449 para_num_clear_list( &run_para
->member
.para
, &run_para
->member
.para
.fmt
);
451 new_para
->member
.para
.text
= ME_VSplitString( run_para
->member
.para
.text
, run
->member
.run
.nCharOfs
);
453 end_run
= ME_MakeRun(style
, run_flags
);
454 ofs
= end_run
->member
.run
.nCharOfs
= run
->member
.run
.nCharOfs
;
455 end_run
->member
.run
.len
= eol_len
;
456 end_run
->member
.run
.para
= run
->member
.run
.para
;
457 ME_AppendString( run_para
->member
.para
.text
, eol_str
, eol_len
);
458 next_para
= run_para
->member
.para
.next_para
;
459 assert(next_para
== ME_FindItemFwd(run_para
, diParagraphOrEnd
));
461 add_undo_join_paras( editor
, run_para
->member
.para
.nCharOfs
+ ofs
);
463 /* Update selection cursors to point to the correct paragraph. */
464 for (i
= 0; i
< editor
->nCursors
; i
++) {
465 if (editor
->pCursors
[i
].pPara
== run_para
&&
466 run
->member
.run
.nCharOfs
<= editor
->pCursors
[i
].pRun
->member
.run
.nCharOfs
)
468 editor
->pCursors
[i
].pPara
= new_para
;
472 /* the new paragraph will have a different starting offset, so let's update its runs */
474 while(pp
->type
== diRun
) {
475 pp
->member
.run
.nCharOfs
-= ofs
;
476 pp
->member
.run
.para
= &new_para
->member
.para
;
477 pp
= ME_FindItemFwd(pp
, diRunOrParagraphOrEnd
);
479 new_para
->member
.para
.nCharOfs
= run_para
->member
.para
.nCharOfs
+ ofs
;
480 new_para
->member
.para
.nCharOfs
+= eol_len
;
481 new_para
->member
.para
.nFlags
= MEPF_REWRAP
;
483 /* FIXME initialize format style and call ME_SetParaFormat blah blah */
484 new_para
->member
.para
.fmt
= run_para
->member
.para
.fmt
;
485 new_para
->member
.para
.border
= run_para
->member
.para
.border
;
487 /* insert paragraph into paragraph double linked list */
488 new_para
->member
.para
.prev_para
= run_para
;
489 new_para
->member
.para
.next_para
= next_para
;
490 run_para
->member
.para
.next_para
= new_para
;
491 next_para
->member
.para
.prev_para
= new_para
;
493 /* insert end run of the old paragraph, and new paragraph, into DI double linked list */
494 ME_InsertBefore(run
, new_para
);
495 ME_InsertBefore(new_para
, end_run
);
497 /* Fix up the paras' eop_run ptrs */
498 new_para
->member
.para
.eop_run
= run_para
->member
.para
.eop_run
;
499 run_para
->member
.para
.eop_run
= &end_run
->member
.run
;
501 if (!editor
->bEmulateVersion10
) { /* v4.1 */
502 if (paraFlags
& (MEPF_ROWSTART
|MEPF_CELL
))
504 ME_DisplayItem
*cell
= ME_MakeDI(diCell
);
505 ME_InsertBefore(new_para
, cell
);
506 new_para
->member
.para
.pCell
= cell
;
507 cell
->member
.cell
.next_cell
= NULL
;
508 if (paraFlags
& MEPF_ROWSTART
)
510 run_para
->member
.para
.nFlags
|= MEPF_ROWSTART
;
511 cell
->member
.cell
.prev_cell
= NULL
;
512 cell
->member
.cell
.parent_cell
= run_para
->member
.para
.pCell
;
513 if (run_para
->member
.para
.pCell
)
514 cell
->member
.cell
.nNestingLevel
= run_para
->member
.para
.pCell
->member
.cell
.nNestingLevel
+ 1;
516 cell
->member
.cell
.nNestingLevel
= 1;
518 cell
->member
.cell
.prev_cell
= run_para
->member
.para
.pCell
;
519 assert(cell
->member
.cell
.prev_cell
);
520 cell
->member
.cell
.prev_cell
->member
.cell
.next_cell
= cell
;
521 assert(run_para
->member
.para
.nFlags
& MEPF_CELL
);
522 assert(!(run_para
->member
.para
.nFlags
& MEPF_ROWSTART
));
523 cell
->member
.cell
.nNestingLevel
= cell
->member
.cell
.prev_cell
->member
.cell
.nNestingLevel
;
524 cell
->member
.cell
.parent_cell
= cell
->member
.cell
.prev_cell
->member
.cell
.parent_cell
;
526 } else if (paraFlags
& MEPF_ROWEND
) {
527 run_para
->member
.para
.nFlags
|= MEPF_ROWEND
;
528 run_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
->member
.cell
.parent_cell
;
529 new_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
;
530 assert(run_para
->member
.para
.prev_para
->member
.para
.nFlags
& MEPF_CELL
);
531 assert(!(run_para
->member
.para
.prev_para
->member
.para
.nFlags
& MEPF_ROWSTART
));
532 if (new_para
->member
.para
.pCell
!= new_para
->member
.para
.next_para
->member
.para
.pCell
533 && new_para
->member
.para
.next_para
->member
.para
.pCell
534 && !new_para
->member
.para
.next_para
->member
.para
.pCell
->member
.cell
.prev_cell
)
536 /* Row starts just after the row that was ended. */
537 new_para
->member
.para
.nFlags
|= MEPF_ROWSTART
;
540 new_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
;
542 ME_UpdateTableFlags(run_para
);
543 ME_UpdateTableFlags(new_para
);
546 /* force rewrap of the */
547 run_para
->member
.para
.prev_para
->member
.para
.nFlags
|= MEPF_REWRAP
;
548 new_para
->member
.para
.prev_para
->member
.para
.nFlags
|= MEPF_REWRAP
;
550 /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */
551 ME_PropagateCharOffset(next_para
, eol_len
);
552 editor
->nParagraphs
++;
557 /* join tp with tp->member.para.next_para, keeping tp's style; this
558 * is consistent with the original */
559 ME_DisplayItem
*ME_JoinParagraphs(ME_TextEditor
*editor
, ME_DisplayItem
*tp
,
560 BOOL keepFirstParaFormat
)
562 ME_DisplayItem
*pNext
, *pFirstRunInNext
, *pRun
, *pTmp
, *pCell
= NULL
;
566 ME_Cursor startCur
, endCur
;
569 assert(tp
->type
== diParagraph
);
570 assert(tp
->member
.para
.next_para
);
571 assert(tp
->member
.para
.next_para
->type
== diParagraph
);
573 /* Clear any cached para numbering following this paragraph */
574 if (tp
->member
.para
.fmt
.wNumbering
)
575 para_num_clear_list( &tp
->member
.para
, &tp
->member
.para
.fmt
);
577 pNext
= tp
->member
.para
.next_para
;
579 /* Need to locate end-of-paragraph run here, in order to know end_len */
580 pRun
= ME_FindItemBack(pNext
, diRunOrParagraph
);
583 assert(pRun
->type
== diRun
);
584 assert(pRun
->member
.run
.nFlags
& MERF_ENDPARA
);
586 end_len
= pRun
->member
.run
.len
;
587 eol_str
= ME_VSplitString( tp
->member
.para
.text
, pRun
->member
.run
.nCharOfs
);
588 ME_AppendString( tp
->member
.para
.text
, pNext
->member
.para
.text
->szData
, pNext
->member
.para
.text
->nLen
);
590 /* null char format operation to store the original char format for the ENDPARA run */
591 ME_InitCharFormat2W(&fmt
);
592 endCur
.pPara
= pNext
;
593 endCur
.pRun
= ME_FindItemFwd(pNext
, diRun
);
596 ME_PrevRun(&startCur
.pPara
, &startCur
.pRun
, TRUE
);
597 ME_SetCharFormat(editor
, &startCur
, &endCur
, &fmt
);
599 if (!editor
->bEmulateVersion10
) { /* v4.1 */
600 /* Table cell/row properties are always moved over from the removed para. */
601 tp
->member
.para
.nFlags
= pNext
->member
.para
.nFlags
;
602 tp
->member
.para
.pCell
= pNext
->member
.para
.pCell
;
604 /* Remove cell boundary if it is between the end paragraph run and the next
605 * paragraph display item. */
606 for (pTmp
= pRun
->next
; pTmp
!= pNext
; pTmp
= pTmp
->next
)
608 if (pTmp
->type
== diCell
)
616 add_undo_split_para( editor
, &pNext
->member
.para
, eol_str
, pCell
? &pCell
->member
.cell
: NULL
);
621 if (pCell
->member
.cell
.prev_cell
)
622 pCell
->member
.cell
.prev_cell
->member
.cell
.next_cell
= pCell
->member
.cell
.next_cell
;
623 if (pCell
->member
.cell
.next_cell
)
624 pCell
->member
.cell
.next_cell
->member
.cell
.prev_cell
= pCell
->member
.cell
.prev_cell
;
625 ME_DestroyDisplayItem( pCell
);
628 if (!keepFirstParaFormat
)
630 add_undo_set_para_fmt( editor
, &tp
->member
.para
);
631 tp
->member
.para
.fmt
= pNext
->member
.para
.fmt
;
632 tp
->member
.para
.border
= pNext
->member
.para
.border
;
635 shift
= pNext
->member
.para
.nCharOfs
- tp
->member
.para
.nCharOfs
- end_len
;
637 pFirstRunInNext
= ME_FindItemFwd(pNext
, diRunOrParagraph
);
639 assert(pFirstRunInNext
->type
== diRun
);
641 /* Update selection cursors so they don't point to the removed end
642 * paragraph run, and point to the correct paragraph. */
643 for (i
=0; i
< editor
->nCursors
; i
++) {
644 if (editor
->pCursors
[i
].pRun
== pRun
) {
645 editor
->pCursors
[i
].pRun
= pFirstRunInNext
;
646 editor
->pCursors
[i
].nOffset
= 0;
647 } else if (editor
->pCursors
[i
].pPara
== pNext
) {
648 editor
->pCursors
[i
].pPara
= tp
;
654 pTmp
= ME_FindItemFwd(pTmp
, diRunOrParagraphOrEnd
);
655 if (pTmp
->type
!= diRun
)
657 TRACE("shifting %s by %d (previous %d)\n", debugstr_run( &pTmp
->member
.run
), shift
, pTmp
->member
.run
.nCharOfs
);
658 pTmp
->member
.run
.nCharOfs
+= shift
;
659 pTmp
->member
.run
.para
= &tp
->member
.para
;
662 /* Fix up the para's eop_run ptr */
663 tp
->member
.para
.eop_run
= pNext
->member
.para
.eop_run
;
666 ME_DestroyDisplayItem(pRun
);
668 if (editor
->pLastSelStartPara
== pNext
)
669 editor
->pLastSelStartPara
= tp
;
670 if (editor
->pLastSelEndPara
== pNext
)
671 editor
->pLastSelEndPara
= tp
;
673 tp
->member
.para
.next_para
= pNext
->member
.para
.next_para
;
674 pNext
->member
.para
.next_para
->member
.para
.prev_para
= tp
;
676 ME_DestroyDisplayItem(pNext
);
678 ME_PropagateCharOffset(tp
->member
.para
.next_para
, -end_len
);
680 ME_CheckCharOffsets(editor
);
682 editor
->nParagraphs
--;
683 tp
->member
.para
.nFlags
|= MEPF_REWRAP
;
687 ME_DisplayItem
*ME_GetParagraph(ME_DisplayItem
*item
) {
688 return ME_FindItemBackOrHere(item
, diParagraph
);
691 void ME_DumpParaStyleToBuf(const PARAFORMAT2
*pFmt
, char buf
[2048])
696 #define DUMP(mask, name, fmt, field) \
697 if (pFmt->dwMask & (mask)) p += sprintf(p, "%-22s" fmt "\n", name, pFmt->field); \
698 else p += sprintf(p, "%-22sN/A\n", name);
700 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
701 #define DUMP_EFFECT(mask, name) \
702 p += sprintf(p, "%-22s%s\n", name, (pFmt->dwMask & (mask)) ? ((pFmt->wEffects & ((mask) >> 16)) ? "yes" : "no") : "N/A");
704 DUMP(PFM_NUMBERING
, "Numbering:", "%u", wNumbering
);
705 DUMP_EFFECT(PFM_DONOTHYPHEN
, "Disable auto-hyphen:");
706 DUMP_EFFECT(PFM_KEEP
, "No page break in para:");
707 DUMP_EFFECT(PFM_KEEPNEXT
, "No page break in para & next:");
708 DUMP_EFFECT(PFM_NOLINENUMBER
, "No line number:");
709 DUMP_EFFECT(PFM_NOWIDOWCONTROL
, "No widow & orphan:");
710 DUMP_EFFECT(PFM_PAGEBREAKBEFORE
, "Page break before:");
711 DUMP_EFFECT(PFM_RTLPARA
, "RTL para:");
712 DUMP_EFFECT(PFM_SIDEBYSIDE
, "Side by side:");
713 DUMP_EFFECT(PFM_TABLE
, "Table:");
714 DUMP(PFM_OFFSETINDENT
, "Offset indent:", "%d", dxStartIndent
);
715 DUMP(PFM_STARTINDENT
, "Start indent:", "%d", dxStartIndent
);
716 DUMP(PFM_RIGHTINDENT
, "Right indent:", "%d", dxRightIndent
);
717 DUMP(PFM_OFFSET
, "Offset:", "%d", dxOffset
);
718 if (pFmt
->dwMask
& PFM_ALIGNMENT
) {
719 switch (pFmt
->wAlignment
) {
720 case PFA_LEFT
: p
+= sprintf(p
, "Alignment: left\n"); break;
721 case PFA_RIGHT
: p
+= sprintf(p
, "Alignment: right\n"); break;
722 case PFA_CENTER
: p
+= sprintf(p
, "Alignment: center\n"); break;
723 case PFA_JUSTIFY
: p
+= sprintf(p
, "Alignment: justify\n"); break;
724 default : p
+= sprintf(p
, "Alignment: incorrect %d\n", pFmt
->wAlignment
); break;
727 else p
+= sprintf(p
, "Alignment: N/A\n");
728 DUMP(PFM_TABSTOPS
, "Tab Stops:", "%d", cTabCount
);
729 if (pFmt
->dwMask
& PFM_TABSTOPS
) {
731 p
+= sprintf(p
, "\t");
732 for (i
= 0; i
< pFmt
->cTabCount
; i
++) p
+= sprintf(p
, "%x ", pFmt
->rgxTabs
[i
]);
733 p
+= sprintf(p
, "\n");
735 DUMP(PFM_SPACEBEFORE
, "Space Before:", "%d", dySpaceBefore
);
736 DUMP(PFM_SPACEAFTER
, "Space After:", "%d", dySpaceAfter
);
737 DUMP(PFM_LINESPACING
, "Line spacing:", "%d", dyLineSpacing
);
738 DUMP(PFM_STYLE
, "Text style:", "%d", sStyle
);
739 DUMP(PFM_LINESPACING
, "Line spacing rule:", "%u", bLineSpacingRule
);
740 /* bOutlineLevel should be 0 */
741 DUMP(PFM_SHADING
, "Shading Weight:", "%u", wShadingWeight
);
742 DUMP(PFM_SHADING
, "Shading Style:", "%u", wShadingStyle
);
743 DUMP(PFM_NUMBERINGSTART
, "Numbering Start:", "%u", wNumberingStart
);
744 DUMP(PFM_NUMBERINGSTYLE
, "Numbering Style:", "0x%x", wNumberingStyle
);
745 DUMP(PFM_NUMBERINGTAB
, "Numbering Tab:", "%u", wNumberingStyle
);
746 DUMP(PFM_BORDER
, "Border Space:", "%u", wBorderSpace
);
747 DUMP(PFM_BORDER
, "Border Width:", "%u", wBorderWidth
);
748 DUMP(PFM_BORDER
, "Borders:", "%u", wBorders
);
755 ME_GetSelectionParas(ME_TextEditor
*editor
, ME_DisplayItem
**para
, ME_DisplayItem
**para_end
)
757 ME_Cursor
*pEndCursor
= &editor
->pCursors
[1];
759 *para
= editor
->pCursors
[0].pPara
;
760 *para_end
= editor
->pCursors
[1].pPara
;
761 if (*para
== *para_end
)
764 if ((*para_end
)->member
.para
.nCharOfs
< (*para
)->member
.para
.nCharOfs
) {
765 ME_DisplayItem
*tmp
= *para
;
769 pEndCursor
= &editor
->pCursors
[0];
772 /* The paragraph at the end of a non-empty selection isn't included
773 * if the selection ends at the start of the paragraph. */
774 if (!pEndCursor
->pRun
->member
.run
.nCharOfs
&& !pEndCursor
->nOffset
)
775 *para_end
= (*para_end
)->member
.para
.prev_para
;
779 BOOL
ME_SetSelectionParaFormat(ME_TextEditor
*editor
, const PARAFORMAT2
*pFmt
)
781 ME_DisplayItem
*para
, *para_end
;
783 ME_GetSelectionParas(editor
, ¶
, ¶_end
);
786 ME_SetParaFormat(editor
, ¶
->member
.para
, pFmt
);
787 if (para
== para_end
)
789 para
= para
->member
.para
.next_para
;
795 static void ME_GetParaFormat(ME_TextEditor
*editor
,
796 const ME_DisplayItem
*para
,
799 UINT cbSize
= pFmt
->cbSize
;
800 if (pFmt
->cbSize
>= sizeof(PARAFORMAT2
)) {
801 *pFmt
= para
->member
.para
.fmt
;
803 CopyMemory(pFmt
, ¶
->member
.para
.fmt
, pFmt
->cbSize
);
804 pFmt
->dwMask
&= PFM_ALL
;
806 pFmt
->cbSize
= cbSize
;
809 void ME_GetSelectionParaFormat(ME_TextEditor
*editor
, PARAFORMAT2
*pFmt
)
811 ME_DisplayItem
*para
, *para_end
;
814 if (pFmt
->cbSize
< sizeof(PARAFORMAT
)) {
819 ME_GetSelectionParas(editor
, ¶
, ¶_end
);
821 ME_GetParaFormat(editor
, para
, pFmt
);
823 /* Invalidate values that change across the selected paragraphs. */
824 while (para
!= para_end
)
826 para
= para
->member
.para
.next_para
;
827 curFmt
= ¶
->member
.para
.fmt
;
829 #define CHECK_FIELD(m, f) \
830 if (pFmt->f != curFmt->f) pFmt->dwMask &= ~(m);
832 CHECK_FIELD(PFM_NUMBERING
, wNumbering
);
833 CHECK_FIELD(PFM_STARTINDENT
, dxStartIndent
);
834 CHECK_FIELD(PFM_RIGHTINDENT
, dxRightIndent
);
835 CHECK_FIELD(PFM_OFFSET
, dxOffset
);
836 CHECK_FIELD(PFM_ALIGNMENT
, wAlignment
);
837 if (pFmt
->dwMask
& PFM_TABSTOPS
) {
838 if (pFmt
->cTabCount
!= para
->member
.para
.fmt
.cTabCount
||
839 memcmp(pFmt
->rgxTabs
, curFmt
->rgxTabs
, curFmt
->cTabCount
*sizeof(int)))
840 pFmt
->dwMask
&= ~PFM_TABSTOPS
;
843 if (pFmt
->dwMask
>= sizeof(PARAFORMAT2
))
845 pFmt
->dwMask
&= ~((pFmt
->wEffects
^ curFmt
->wEffects
) << 16);
846 CHECK_FIELD(PFM_SPACEBEFORE
, dySpaceBefore
);
847 CHECK_FIELD(PFM_SPACEAFTER
, dySpaceAfter
);
848 CHECK_FIELD(PFM_LINESPACING
, dyLineSpacing
);
849 CHECK_FIELD(PFM_STYLE
, sStyle
);
850 CHECK_FIELD(PFM_SPACEAFTER
, bLineSpacingRule
);
851 CHECK_FIELD(PFM_SHADING
, wShadingWeight
);
852 CHECK_FIELD(PFM_SHADING
, wShadingStyle
);
853 CHECK_FIELD(PFM_NUMBERINGSTART
, wNumberingStart
);
854 CHECK_FIELD(PFM_NUMBERINGSTYLE
, wNumberingStyle
);
855 CHECK_FIELD(PFM_NUMBERINGTAB
, wNumberingTab
);
856 CHECK_FIELD(PFM_BORDER
, wBorderSpace
);
857 CHECK_FIELD(PFM_BORDER
, wBorderWidth
);
858 CHECK_FIELD(PFM_BORDER
, wBorders
);
864 void ME_SetDefaultParaFormat(ME_TextEditor
*editor
, PARAFORMAT2
*pFmt
)
866 const PARAFORMAT2
*host_fmt
;
869 ZeroMemory(pFmt
, sizeof(PARAFORMAT2
));
870 pFmt
->cbSize
= sizeof(PARAFORMAT2
);
871 pFmt
->dwMask
= PFM_ALL2
;
872 pFmt
->wAlignment
= PFA_LEFT
;
874 pFmt
->bOutlineLevel
= TRUE
;
876 hr
= ITextHost_TxGetParaFormat( editor
->texthost
, (const PARAFORMAT
**)&host_fmt
);
879 /* Just use the alignment for now */
880 if (host_fmt
->dwMask
& PFM_ALIGNMENT
)
881 pFmt
->wAlignment
= host_fmt
->wAlignment
;
882 ITextHost_OnTxParaFormatChange( editor
->texthost
, (PARAFORMAT
*)pFmt
);