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 void mark_para_rewrap(ME_TextEditor
*editor
, ME_DisplayItem
*para
)
28 para
->member
.para
.nFlags
|= MEPF_REWRAP
;
29 add_marked_para(editor
, para
);
32 ME_DisplayItem
*get_di_from_para(ME_Paragraph
*para
)
34 return (ME_DisplayItem
*)((ptrdiff_t)para
- offsetof(ME_DisplayItem
, member
));
37 static ME_DisplayItem
*make_para(ME_TextEditor
*editor
)
39 ME_DisplayItem
*item
= ME_MakeDI(diParagraph
);
41 ME_SetDefaultParaFormat(editor
, &item
->member
.para
.fmt
);
42 item
->member
.para
.nFlags
= MEPF_REWRAP
;
43 item
->member
.para
.next_marked
= item
->member
.para
.prev_marked
= NULL
;
48 void destroy_para(ME_TextEditor
*editor
, ME_DisplayItem
*item
)
50 assert(item
->type
== diParagraph
);
52 if (item
->member
.para
.nWidth
== editor
->nTotalWidth
)
54 item
->member
.para
.nWidth
= 0;
55 editor
->nTotalWidth
= get_total_width(editor
);
57 editor
->total_rows
-= item
->member
.para
.nRows
;
58 ME_DestroyString(item
->member
.para
.text
);
59 para_num_clear( &item
->member
.para
.para_num
);
60 remove_marked_para(editor
, item
);
61 ME_DestroyDisplayItem(item
);
64 int get_total_width(ME_TextEditor
*editor
)
69 if (editor
->pBuffer
->pFirst
&& editor
->pBuffer
->pLast
)
71 para
= &editor
->pBuffer
->pFirst
->next
->member
.para
;
72 while (para
!= &editor
->pBuffer
->pLast
->member
.para
&& para
->next_para
)
74 total_width
= max(total_width
, para
->nWidth
);
75 para
= ¶
->next_para
->member
.para
;
82 void remove_marked_para(ME_TextEditor
*editor
, ME_DisplayItem
*di
)
84 ME_DisplayItem
*head
= editor
->first_marked_para
;
86 assert(di
->type
== diParagraph
);
87 if (!di
->member
.para
.next_marked
&& !di
->member
.para
.prev_marked
)
90 editor
->first_marked_para
= NULL
;
92 else if (di
->member
.para
.next_marked
&& di
->member
.para
.prev_marked
)
94 di
->member
.para
.prev_marked
->member
.para
.next_marked
= di
->member
.para
.next_marked
;
95 di
->member
.para
.next_marked
->member
.para
.prev_marked
= di
->member
.para
.prev_marked
;
96 di
->member
.para
.prev_marked
= di
->member
.para
.next_marked
= NULL
;
98 else if (di
->member
.para
.next_marked
)
100 assert(di
== editor
->first_marked_para
);
101 editor
->first_marked_para
= di
->member
.para
.next_marked
;
102 di
->member
.para
.next_marked
->member
.para
.prev_marked
= NULL
;
103 di
->member
.para
.next_marked
= NULL
;
107 di
->member
.para
.prev_marked
->member
.para
.next_marked
= NULL
;
108 di
->member
.para
.prev_marked
= NULL
;
112 void add_marked_para(ME_TextEditor
*editor
, ME_DisplayItem
*di
)
114 ME_DisplayItem
*iter
= editor
->first_marked_para
;
118 editor
->first_marked_para
= di
;
125 else if (di
->member
.para
.nCharOfs
< iter
->member
.para
.nCharOfs
)
127 if (iter
== editor
->first_marked_para
)
128 editor
->first_marked_para
= di
;
129 di
->member
.para
.next_marked
= iter
;
130 iter
->member
.para
.prev_marked
= di
;
133 else if (di
->member
.para
.nCharOfs
>= iter
->member
.para
.nCharOfs
)
135 if (!iter
->member
.para
.next_marked
|| di
->member
.para
.nCharOfs
< iter
->member
.para
.next_marked
->member
.para
.nCharOfs
)
137 if (iter
->member
.para
.next_marked
)
139 di
->member
.para
.next_marked
= iter
->member
.para
.next_marked
;
140 iter
->member
.para
.next_marked
->member
.para
.prev_marked
= di
;
142 di
->member
.para
.prev_marked
= iter
;
143 iter
->member
.para
.next_marked
= di
;
147 iter
= iter
->member
.para
.next_marked
;
151 void ME_MakeFirstParagraph(ME_TextEditor
*editor
)
153 static const WCHAR cr_lf
[] = {'\r','\n',0};
156 const CHARFORMATW
*host_cf
;
159 ME_TextBuffer
*text
= editor
->pBuffer
;
160 ME_DisplayItem
*para
= make_para(editor
);
165 ME_InitContext(&c
, editor
, ITextHost_TxGetDC(editor
->texthost
));
167 hf
= GetStockObject(SYSTEM_FONT
);
169 GetObjectW(hf
, sizeof(LOGFONTW
), &lf
);
170 ZeroMemory(&cf
, sizeof(cf
));
171 cf
.cbSize
= sizeof(cf
);
172 cf
.dwMask
= CFM_ANIMATION
|CFM_BACKCOLOR
|CFM_CHARSET
|CFM_COLOR
|CFM_FACE
|CFM_KERNING
|CFM_LCID
|CFM_OFFSET
;
173 cf
.dwMask
|= CFM_REVAUTHOR
|CFM_SIZE
|CFM_SPACING
|CFM_STYLE
|CFM_UNDERLINETYPE
|CFM_WEIGHT
;
174 cf
.dwMask
|= CFM_ALLCAPS
|CFM_BOLD
|CFM_DISABLED
|CFM_EMBOSS
|CFM_HIDDEN
;
175 cf
.dwMask
|= CFM_IMPRINT
|CFM_ITALIC
|CFM_LINK
|CFM_OUTLINE
|CFM_PROTECTED
;
176 cf
.dwMask
|= CFM_REVISED
|CFM_SHADOW
|CFM_SMALLCAPS
|CFM_STRIKEOUT
;
177 cf
.dwMask
|= CFM_SUBSCRIPT
|CFM_UNDERLINE
;
179 cf
.dwEffects
= CFE_AUTOCOLOR
| CFE_AUTOBACKCOLOR
;
180 lstrcpyW(cf
.szFaceName
, lf
.lfFaceName
);
181 /* Convert system font height from logical units to twips for cf.yHeight */
182 cf
.yHeight
= (lf
.lfHeight
* 72 * 1440) / (c
.dpi
.cy
* c
.dpi
.cy
);
183 if (lf
.lfWeight
> FW_NORMAL
) cf
.dwEffects
|= CFE_BOLD
;
184 cf
.wWeight
= lf
.lfWeight
;
185 if (lf
.lfItalic
) cf
.dwEffects
|= CFE_ITALIC
;
186 if (lf
.lfUnderline
) cf
.dwEffects
|= CFE_UNDERLINE
;
187 cf
.bUnderlineType
= CFU_UNDERLINE
;
188 if (lf
.lfStrikeOut
) cf
.dwEffects
|= CFE_STRIKEOUT
;
189 cf
.bPitchAndFamily
= lf
.lfPitchAndFamily
;
190 cf
.bCharSet
= lf
.lfCharSet
;
191 cf
.lcid
= GetSystemDefaultLCID();
193 style
= ME_MakeStyle(&cf
);
194 text
->pDefaultStyle
= style
;
196 if (ITextHost_TxGetCharFormat(editor
->texthost
, &host_cf
) == S_OK
)
198 ZeroMemory(&cf
, sizeof(cf
));
199 cf
.cbSize
= sizeof(cf
);
200 cfany_to_cf2w(&cf
, (CHARFORMAT2W
*)host_cf
);
201 ME_SetDefaultCharFormat(editor
, &cf
);
204 eol_len
= editor
->bEmulateVersion10
? 2 : 1;
205 para
->member
.para
.text
= ME_MakeStringN( cr_lf
, eol_len
);
207 run
= ME_MakeRun(style
, MERF_ENDPARA
);
208 run
->member
.run
.nCharOfs
= 0;
209 run
->member
.run
.len
= eol_len
;
210 run
->member
.run
.para
= ¶
->member
.para
;
212 para
->member
.para
.eop_run
= &run
->member
.run
;
214 ME_InsertBefore(text
->pLast
, para
);
215 ME_InsertBefore(text
->pLast
, run
);
216 para
->member
.para
.prev_para
= text
->pFirst
;
217 para
->member
.para
.next_para
= text
->pLast
;
218 text
->pFirst
->member
.para
.next_para
= para
;
219 text
->pLast
->member
.para
.prev_para
= para
;
221 text
->pLast
->member
.para
.nCharOfs
= editor
->bEmulateVersion10
? 2 : 1;
223 add_marked_para(editor
, para
);
224 ME_DestroyContext(&c
);
227 static void ME_MarkForWrapping(ME_TextEditor
*editor
, ME_DisplayItem
*first
, const ME_DisplayItem
*last
)
231 mark_para_rewrap(editor
, first
);
232 first
= first
->member
.para
.next_para
;
236 void ME_MarkAllForWrapping(ME_TextEditor
*editor
)
238 ME_MarkForWrapping(editor
, editor
->pBuffer
->pFirst
->member
.para
.next_para
, editor
->pBuffer
->pLast
);
241 static void ME_UpdateTableFlags(ME_DisplayItem
*para
)
243 para
->member
.para
.fmt
.dwMask
|= PFM_TABLE
|PFM_TABLEROWDELIMITER
;
244 if (para
->member
.para
.pCell
) {
245 para
->member
.para
.nFlags
|= MEPF_CELL
;
247 para
->member
.para
.nFlags
&= ~MEPF_CELL
;
249 if (para
->member
.para
.nFlags
& MEPF_ROWEND
) {
250 para
->member
.para
.fmt
.wEffects
|= PFE_TABLEROWDELIMITER
;
252 para
->member
.para
.fmt
.wEffects
&= ~PFE_TABLEROWDELIMITER
;
254 if (para
->member
.para
.nFlags
& (MEPF_ROWSTART
|MEPF_CELL
|MEPF_ROWEND
))
255 para
->member
.para
.fmt
.wEffects
|= PFE_TABLE
;
257 para
->member
.para
.fmt
.wEffects
&= ~PFE_TABLE
;
260 static inline BOOL
para_num_same_list( const PARAFORMAT2
*item
, const PARAFORMAT2
*base
)
262 return item
->wNumbering
== base
->wNumbering
&&
263 item
->wNumberingStart
== base
->wNumberingStart
&&
264 item
->wNumberingStyle
== base
->wNumberingStyle
&&
265 !(item
->wNumberingStyle
& PFNS_NEWNUMBER
);
268 static int para_num_get_num( ME_Paragraph
*para
)
270 ME_DisplayItem
*prev
;
271 int num
= para
->fmt
.wNumberingStart
;
273 for (prev
= para
->prev_para
; prev
->type
== diParagraph
;
274 para
= &prev
->member
.para
, prev
= prev
->member
.para
.prev_para
, num
++)
276 if (!para_num_same_list( &prev
->member
.para
.fmt
, ¶
->fmt
)) break;
281 static ME_String
*para_num_get_str( ME_Paragraph
*para
, WORD num
)
283 /* max 4 Roman letters (representing '8') / decade + '(' + ')' */
284 ME_String
*str
= ME_MakeStringEmpty( 20 + 2 );
286 static const WCHAR fmtW
[] = {'%', 'd', 0};
287 static const WORD letter_base
[] = { 1, 26, 26 * 26, 26 * 26 * 26 };
288 /* roman_base should start on a '5' not a '1', otherwise the 'total' code will need adjusting.
289 'N' and 'O' are what MS uses for 5000 and 10000, their version doesn't work well above 30000,
290 but we'll use 'P' as the obvious extension, this gets us up to 2^16, which is all we care about. */
298 {50000, 'P'}, {10000, 'O'}, {5000, 'N'}, {1000, 'M'},
299 {500, 'D'}, {100, 'C'}, {50, 'L'}, {10, 'X'}, {5, 'V'}, {1, 'I'}
302 WORD letter
, total
, char_offset
= 0;
304 if (!str
) return NULL
;
308 if ((para
->fmt
.wNumberingStyle
& 0xf00) == PFNS_PARENS
)
311 switch (para
->fmt
.wNumbering
)
315 p
+= swprintf( p
, 20, fmtW
, num
);
319 char_offset
= 'a' - 'A';
324 /* This is not base-26 (or 27) as zeros don't count unless they are leading zeros.
325 It's simplest to start with the least significant letter, so first calculate how many letters are needed. */
326 for (i
= 0, total
= 0; i
< ARRAY_SIZE( letter_base
); i
++)
328 total
+= letter_base
[i
];
329 if (num
< total
) break;
332 for (i
= 0; i
< len
; i
++)
334 num
-= letter_base
[i
];
335 letter
= (num
/ letter_base
[i
]) % 26;
336 p
[len
- i
- 1] = letter
+ 'A' + char_offset
;
343 char_offset
= 'a' - 'A';
348 for (i
= 0; i
< ARRAY_SIZE( roman_base
); i
++)
352 if (i
% 2 == 0) /* eg 5000, check for 9000 */
353 total
= roman_base
[i
].base
+ 4 * roman_base
[i
+ 1].base
;
354 else /* eg 1000, check for 4000 */
355 total
= 4 * roman_base
[i
].base
;
359 *p
++ = roman_base
[(i
& ~1) + 1].letter
+ char_offset
;
360 *p
++ = roman_base
[i
- 1].letter
+ char_offset
;
366 len
= num
/ roman_base
[i
].base
;
369 *p
++ = roman_base
[i
].letter
+ char_offset
;
370 num
-= roman_base
[i
].base
;
377 switch (para
->fmt
.wNumberingStyle
& 0xf00)
391 str
->nLen
= p
- str
->szData
;
395 void para_num_init( ME_Context
*c
, ME_Paragraph
*para
)
399 static const WCHAR bullet_font
[] = {'S','y','m','b','o','l',0};
400 static const WCHAR bullet_str
[] = {0xb7, 0};
401 static const WCHAR spaceW
[] = {' ', 0};
404 if (!para
->fmt
.wNumbering
) return;
405 if (para
->para_num
.style
&& para
->para_num
.text
) return;
407 if (!para
->para_num
.style
)
409 style
= para
->eop_run
->style
;
411 if (para
->fmt
.wNumbering
== PFN_BULLET
)
413 cf
.cbSize
= sizeof(cf
);
414 cf
.dwMask
= CFM_FACE
| CFM_CHARSET
;
415 memcpy( cf
.szFaceName
, bullet_font
, sizeof(bullet_font
) );
416 cf
.bCharSet
= SYMBOL_CHARSET
;
417 style
= ME_ApplyStyle( c
->editor
, style
, &cf
);
421 ME_AddRefStyle( style
);
424 para
->para_num
.style
= style
;
427 if (!para
->para_num
.text
)
429 if (para
->fmt
.wNumbering
!= PFN_BULLET
)
430 para
->para_num
.text
= para_num_get_str( para
, para_num_get_num( para
) );
432 para
->para_num
.text
= ME_MakeStringConst( bullet_str
, 1 );
435 select_style( c
, para
->para_num
.style
);
436 GetTextExtentPointW( c
->hDC
, para
->para_num
.text
->szData
, para
->para_num
.text
->nLen
, &sz
);
437 para
->para_num
.width
= sz
.cx
;
438 GetTextExtentPointW( c
->hDC
, spaceW
, 1, &sz
);
439 para
->para_num
.width
+= sz
.cx
;
442 void para_num_clear( struct para_num
*pn
)
446 ME_ReleaseStyle( pn
->style
);
449 ME_DestroyString( pn
->text
);
453 static void para_num_clear_list( ME_TextEditor
*editor
, ME_Paragraph
*para
, const PARAFORMAT2
*orig_fmt
)
457 mark_para_rewrap(editor
, get_di_from_para(para
));
458 para_num_clear( ¶
->para_num
);
459 if (para
->next_para
->type
!= diParagraph
) break;
460 para
= ¶
->next_para
->member
.para
;
461 } while (para_num_same_list( ¶
->fmt
, orig_fmt
));
464 static BOOL
ME_SetParaFormat(ME_TextEditor
*editor
, ME_Paragraph
*para
, const PARAFORMAT2
*pFmt
)
469 assert(para
->fmt
.cbSize
== sizeof(PARAFORMAT2
));
470 dwMask
= pFmt
->dwMask
;
471 if (pFmt
->cbSize
< sizeof(PARAFORMAT
))
473 else if (pFmt
->cbSize
< sizeof(PARAFORMAT2
))
478 add_undo_set_para_fmt( editor
, para
);
482 #define COPY_FIELD(m, f) \
483 if (dwMask & (m)) { \
484 para->fmt.dwMask |= m; \
485 para->fmt.f = pFmt->f; \
488 COPY_FIELD(PFM_NUMBERING
, wNumbering
);
489 COPY_FIELD(PFM_STARTINDENT
, dxStartIndent
);
490 if (dwMask
& PFM_OFFSETINDENT
)
491 para
->fmt
.dxStartIndent
+= pFmt
->dxStartIndent
;
492 COPY_FIELD(PFM_RIGHTINDENT
, dxRightIndent
);
493 COPY_FIELD(PFM_OFFSET
, dxOffset
);
494 COPY_FIELD(PFM_ALIGNMENT
, wAlignment
);
495 if (dwMask
& PFM_TABSTOPS
)
497 para
->fmt
.cTabCount
= pFmt
->cTabCount
;
498 memcpy(para
->fmt
.rgxTabs
, pFmt
->rgxTabs
, pFmt
->cTabCount
*sizeof(LONG
));
501 #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \
502 PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \
504 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
505 if (dwMask
& EFFECTS_MASK
)
507 para
->fmt
.dwMask
|= dwMask
& EFFECTS_MASK
;
508 para
->fmt
.wEffects
&= ~HIWORD(dwMask
);
509 para
->fmt
.wEffects
|= pFmt
->wEffects
& HIWORD(dwMask
);
513 COPY_FIELD(PFM_SPACEBEFORE
, dySpaceBefore
);
514 COPY_FIELD(PFM_SPACEAFTER
, dySpaceAfter
);
515 COPY_FIELD(PFM_LINESPACING
, dyLineSpacing
);
516 COPY_FIELD(PFM_STYLE
, sStyle
);
517 COPY_FIELD(PFM_LINESPACING
, bLineSpacingRule
);
518 COPY_FIELD(PFM_SHADING
, wShadingWeight
);
519 COPY_FIELD(PFM_SHADING
, wShadingStyle
);
520 COPY_FIELD(PFM_NUMBERINGSTART
, wNumberingStart
);
521 COPY_FIELD(PFM_NUMBERINGSTYLE
, wNumberingStyle
);
522 COPY_FIELD(PFM_NUMBERINGTAB
, wNumberingTab
);
523 COPY_FIELD(PFM_BORDER
, wBorderSpace
);
524 COPY_FIELD(PFM_BORDER
, wBorderWidth
);
525 COPY_FIELD(PFM_BORDER
, wBorders
);
527 para
->fmt
.dwMask
|= dwMask
;
530 if (memcmp(©
, ¶
->fmt
, sizeof(PARAFORMAT2
)))
532 mark_para_rewrap(editor
, get_di_from_para(para
));
533 if (((dwMask
& PFM_NUMBERING
) && (copy
.wNumbering
!= para
->fmt
.wNumbering
)) ||
534 ((dwMask
& PFM_NUMBERINGSTART
) && (copy
.wNumberingStart
!= para
->fmt
.wNumberingStart
)) ||
535 ((dwMask
& PFM_NUMBERINGSTYLE
) && (copy
.wNumberingStyle
!= para
->fmt
.wNumberingStyle
)))
537 para_num_clear_list( editor
, para
, ©
);
544 /* split paragraph at the beginning of the run */
545 ME_DisplayItem
*ME_SplitParagraph(ME_TextEditor
*editor
, ME_DisplayItem
*run
,
546 ME_Style
*style
, const WCHAR
*eol_str
, int eol_len
,
549 ME_DisplayItem
*next_para
= NULL
;
550 ME_DisplayItem
*run_para
= NULL
;
551 ME_DisplayItem
*new_para
= make_para(editor
);
552 ME_DisplayItem
*end_run
;
555 int run_flags
= MERF_ENDPARA
;
557 if (!editor
->bEmulateVersion10
) { /* v4.1 */
558 /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */
559 assert(!(paraFlags
& ~(MEPF_CELL
|MEPF_ROWSTART
|MEPF_ROWEND
)));
560 assert(!(paraFlags
& (paraFlags
-1)));
561 if (paraFlags
== MEPF_CELL
)
562 run_flags
|= MERF_ENDCELL
;
563 else if (paraFlags
== MEPF_ROWSTART
)
564 run_flags
|= MERF_TABLESTART
|MERF_HIDDEN
;
565 } else { /* v1.0 - v3.0 */
566 assert(!(paraFlags
& (MEPF_CELL
|MEPF_ROWSTART
|MEPF_ROWEND
)));
568 assert(run
->type
== diRun
);
569 run_para
= ME_GetParagraph(run
);
570 assert(run_para
->member
.para
.fmt
.cbSize
== sizeof(PARAFORMAT2
));
572 /* Clear any cached para numbering following this paragraph */
573 if (run_para
->member
.para
.fmt
.wNumbering
)
574 para_num_clear_list( editor
, &run_para
->member
.para
, &run_para
->member
.para
.fmt
);
576 new_para
->member
.para
.text
= ME_VSplitString( run_para
->member
.para
.text
, run
->member
.run
.nCharOfs
);
578 end_run
= ME_MakeRun(style
, run_flags
);
579 ofs
= end_run
->member
.run
.nCharOfs
= run
->member
.run
.nCharOfs
;
580 end_run
->member
.run
.len
= eol_len
;
581 end_run
->member
.run
.para
= run
->member
.run
.para
;
582 ME_AppendString( run_para
->member
.para
.text
, eol_str
, eol_len
);
583 next_para
= run_para
->member
.para
.next_para
;
584 assert(next_para
== ME_FindItemFwd(run_para
, diParagraphOrEnd
));
586 add_undo_join_paras( editor
, run_para
->member
.para
.nCharOfs
+ ofs
);
588 /* Update selection cursors to point to the correct paragraph. */
589 for (i
= 0; i
< editor
->nCursors
; i
++) {
590 if (editor
->pCursors
[i
].pPara
== run_para
&&
591 run
->member
.run
.nCharOfs
<= editor
->pCursors
[i
].pRun
->member
.run
.nCharOfs
)
593 editor
->pCursors
[i
].pPara
= new_para
;
597 /* the new paragraph will have a different starting offset, so let's update its runs */
599 while(pp
->type
== diRun
) {
600 pp
->member
.run
.nCharOfs
-= ofs
;
601 pp
->member
.run
.para
= &new_para
->member
.para
;
602 pp
= ME_FindItemFwd(pp
, diRunOrParagraphOrEnd
);
604 new_para
->member
.para
.nCharOfs
= run_para
->member
.para
.nCharOfs
+ ofs
;
605 new_para
->member
.para
.nCharOfs
+= eol_len
;
606 new_para
->member
.para
.nFlags
= 0;
607 mark_para_rewrap(editor
, new_para
);
609 /* FIXME initialize format style and call ME_SetParaFormat blah blah */
610 new_para
->member
.para
.fmt
= run_para
->member
.para
.fmt
;
611 new_para
->member
.para
.border
= run_para
->member
.para
.border
;
613 /* insert paragraph into paragraph double linked list */
614 new_para
->member
.para
.prev_para
= run_para
;
615 new_para
->member
.para
.next_para
= next_para
;
616 run_para
->member
.para
.next_para
= new_para
;
617 next_para
->member
.para
.prev_para
= new_para
;
619 /* insert end run of the old paragraph, and new paragraph, into DI double linked list */
620 ME_InsertBefore(run
, new_para
);
621 ME_InsertBefore(new_para
, end_run
);
623 /* Fix up the paras' eop_run ptrs */
624 new_para
->member
.para
.eop_run
= run_para
->member
.para
.eop_run
;
625 run_para
->member
.para
.eop_run
= &end_run
->member
.run
;
627 if (!editor
->bEmulateVersion10
) { /* v4.1 */
628 if (paraFlags
& (MEPF_ROWSTART
|MEPF_CELL
))
630 ME_DisplayItem
*cell
= ME_MakeDI(diCell
);
631 ME_InsertBefore(new_para
, cell
);
632 new_para
->member
.para
.pCell
= cell
;
633 cell
->member
.cell
.next_cell
= NULL
;
634 if (paraFlags
& MEPF_ROWSTART
)
636 run_para
->member
.para
.nFlags
|= MEPF_ROWSTART
;
637 cell
->member
.cell
.prev_cell
= NULL
;
638 cell
->member
.cell
.parent_cell
= run_para
->member
.para
.pCell
;
639 if (run_para
->member
.para
.pCell
)
640 cell
->member
.cell
.nNestingLevel
= run_para
->member
.para
.pCell
->member
.cell
.nNestingLevel
+ 1;
642 cell
->member
.cell
.nNestingLevel
= 1;
644 cell
->member
.cell
.prev_cell
= run_para
->member
.para
.pCell
;
645 assert(cell
->member
.cell
.prev_cell
);
646 cell
->member
.cell
.prev_cell
->member
.cell
.next_cell
= cell
;
647 assert(run_para
->member
.para
.nFlags
& MEPF_CELL
);
648 assert(!(run_para
->member
.para
.nFlags
& MEPF_ROWSTART
));
649 cell
->member
.cell
.nNestingLevel
= cell
->member
.cell
.prev_cell
->member
.cell
.nNestingLevel
;
650 cell
->member
.cell
.parent_cell
= cell
->member
.cell
.prev_cell
->member
.cell
.parent_cell
;
652 } else if (paraFlags
& MEPF_ROWEND
) {
653 run_para
->member
.para
.nFlags
|= MEPF_ROWEND
;
654 run_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
->member
.cell
.parent_cell
;
655 new_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
;
656 assert(run_para
->member
.para
.prev_para
->member
.para
.nFlags
& MEPF_CELL
);
657 assert(!(run_para
->member
.para
.prev_para
->member
.para
.nFlags
& MEPF_ROWSTART
));
658 if (new_para
->member
.para
.pCell
!= new_para
->member
.para
.next_para
->member
.para
.pCell
659 && new_para
->member
.para
.next_para
->member
.para
.pCell
660 && !new_para
->member
.para
.next_para
->member
.para
.pCell
->member
.cell
.prev_cell
)
662 /* Row starts just after the row that was ended. */
663 new_para
->member
.para
.nFlags
|= MEPF_ROWSTART
;
666 new_para
->member
.para
.pCell
= run_para
->member
.para
.pCell
;
668 ME_UpdateTableFlags(run_para
);
669 ME_UpdateTableFlags(new_para
);
672 /* force rewrap of the */
673 if (run_para
->member
.para
.prev_para
->type
== diParagraph
)
674 mark_para_rewrap(editor
, run_para
->member
.para
.prev_para
);
676 mark_para_rewrap(editor
, new_para
->member
.para
.prev_para
);
678 /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */
679 ME_PropagateCharOffset(next_para
, eol_len
);
680 editor
->nParagraphs
++;
685 /* join tp with tp->member.para.next_para, keeping tp's style; this
686 * is consistent with the original */
687 ME_DisplayItem
*ME_JoinParagraphs(ME_TextEditor
*editor
, ME_DisplayItem
*tp
,
688 BOOL keepFirstParaFormat
)
690 ME_DisplayItem
*pNext
, *pFirstRunInNext
, *pRun
, *pTmp
, *pCell
= NULL
;
694 ME_Cursor startCur
, endCur
;
697 assert(tp
->type
== diParagraph
);
698 assert(tp
->member
.para
.next_para
);
699 assert(tp
->member
.para
.next_para
->type
== diParagraph
);
701 /* Clear any cached para numbering following this paragraph */
702 if (tp
->member
.para
.fmt
.wNumbering
)
703 para_num_clear_list( editor
, &tp
->member
.para
, &tp
->member
.para
.fmt
);
705 pNext
= tp
->member
.para
.next_para
;
707 /* Need to locate end-of-paragraph run here, in order to know end_len */
708 pRun
= ME_FindItemBack(pNext
, diRunOrParagraph
);
711 assert(pRun
->type
== diRun
);
712 assert(pRun
->member
.run
.nFlags
& MERF_ENDPARA
);
714 end_len
= pRun
->member
.run
.len
;
715 eol_str
= ME_VSplitString( tp
->member
.para
.text
, pRun
->member
.run
.nCharOfs
);
716 ME_AppendString( tp
->member
.para
.text
, pNext
->member
.para
.text
->szData
, pNext
->member
.para
.text
->nLen
);
718 /* null char format operation to store the original char format for the ENDPARA run */
719 ME_InitCharFormat2W(&fmt
);
720 endCur
.pPara
= pNext
;
721 endCur
.pRun
= ME_FindItemFwd(pNext
, diRun
);
724 ME_PrevRun(&startCur
.pPara
, &startCur
.pRun
, TRUE
);
725 ME_SetCharFormat(editor
, &startCur
, &endCur
, &fmt
);
727 if (!editor
->bEmulateVersion10
) { /* v4.1 */
728 /* Table cell/row properties are always moved over from the removed para. */
729 tp
->member
.para
.nFlags
= pNext
->member
.para
.nFlags
;
730 tp
->member
.para
.pCell
= pNext
->member
.para
.pCell
;
732 /* Remove cell boundary if it is between the end paragraph run and the next
733 * paragraph display item. */
734 for (pTmp
= pRun
->next
; pTmp
!= pNext
; pTmp
= pTmp
->next
)
736 if (pTmp
->type
== diCell
)
744 add_undo_split_para( editor
, &pNext
->member
.para
, eol_str
, pCell
? &pCell
->member
.cell
: NULL
);
749 if (pCell
->member
.cell
.prev_cell
)
750 pCell
->member
.cell
.prev_cell
->member
.cell
.next_cell
= pCell
->member
.cell
.next_cell
;
751 if (pCell
->member
.cell
.next_cell
)
752 pCell
->member
.cell
.next_cell
->member
.cell
.prev_cell
= pCell
->member
.cell
.prev_cell
;
753 ME_DestroyDisplayItem( pCell
);
756 if (!keepFirstParaFormat
)
758 add_undo_set_para_fmt( editor
, &tp
->member
.para
);
759 tp
->member
.para
.fmt
= pNext
->member
.para
.fmt
;
760 tp
->member
.para
.border
= pNext
->member
.para
.border
;
763 shift
= pNext
->member
.para
.nCharOfs
- tp
->member
.para
.nCharOfs
- end_len
;
765 pFirstRunInNext
= ME_FindItemFwd(pNext
, diRunOrParagraph
);
767 assert(pFirstRunInNext
->type
== diRun
);
769 /* Update selection cursors so they don't point to the removed end
770 * paragraph run, and point to the correct paragraph. */
771 for (i
=0; i
< editor
->nCursors
; i
++) {
772 if (editor
->pCursors
[i
].pRun
== pRun
) {
773 editor
->pCursors
[i
].pRun
= pFirstRunInNext
;
774 editor
->pCursors
[i
].nOffset
= 0;
775 } else if (editor
->pCursors
[i
].pPara
== pNext
) {
776 editor
->pCursors
[i
].pPara
= tp
;
782 pTmp
= ME_FindItemFwd(pTmp
, diRunOrParagraphOrEnd
);
783 if (pTmp
->type
!= diRun
)
785 TRACE("shifting %s by %d (previous %d)\n", debugstr_run( &pTmp
->member
.run
), shift
, pTmp
->member
.run
.nCharOfs
);
786 pTmp
->member
.run
.nCharOfs
+= shift
;
787 pTmp
->member
.run
.para
= &tp
->member
.para
;
790 /* Fix up the para's eop_run ptr */
791 tp
->member
.para
.eop_run
= pNext
->member
.para
.eop_run
;
794 ME_DestroyDisplayItem(pRun
);
796 if (editor
->pLastSelStartPara
== pNext
)
797 editor
->pLastSelStartPara
= tp
;
798 if (editor
->pLastSelEndPara
== pNext
)
799 editor
->pLastSelEndPara
= tp
;
801 tp
->member
.para
.next_para
= pNext
->member
.para
.next_para
;
802 pNext
->member
.para
.next_para
->member
.para
.prev_para
= tp
;
804 destroy_para(editor
, pNext
);
806 ME_PropagateCharOffset(tp
->member
.para
.next_para
, -end_len
);
808 ME_CheckCharOffsets(editor
);
810 editor
->nParagraphs
--;
811 mark_para_rewrap(editor
, tp
);
815 ME_DisplayItem
*ME_GetParagraph(ME_DisplayItem
*item
) {
816 return ME_FindItemBackOrHere(item
, diParagraph
);
819 void ME_DumpParaStyleToBuf(const PARAFORMAT2
*pFmt
, char buf
[2048])
824 #define DUMP(mask, name, fmt, field) \
825 if (pFmt->dwMask & (mask)) p += sprintf(p, "%-22s" fmt "\n", name, pFmt->field); \
826 else p += sprintf(p, "%-22sN/A\n", name);
828 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
829 #define DUMP_EFFECT(mask, name) \
830 p += sprintf(p, "%-22s%s\n", name, (pFmt->dwMask & (mask)) ? ((pFmt->wEffects & ((mask) >> 16)) ? "yes" : "no") : "N/A");
832 DUMP(PFM_NUMBERING
, "Numbering:", "%u", wNumbering
);
833 DUMP_EFFECT(PFM_DONOTHYPHEN
, "Disable auto-hyphen:");
834 DUMP_EFFECT(PFM_KEEP
, "No page break in para:");
835 DUMP_EFFECT(PFM_KEEPNEXT
, "No page break in para & next:");
836 DUMP_EFFECT(PFM_NOLINENUMBER
, "No line number:");
837 DUMP_EFFECT(PFM_NOWIDOWCONTROL
, "No widow & orphan:");
838 DUMP_EFFECT(PFM_PAGEBREAKBEFORE
, "Page break before:");
839 DUMP_EFFECT(PFM_RTLPARA
, "RTL para:");
840 DUMP_EFFECT(PFM_SIDEBYSIDE
, "Side by side:");
841 DUMP_EFFECT(PFM_TABLE
, "Table:");
842 DUMP(PFM_OFFSETINDENT
, "Offset indent:", "%d", dxStartIndent
);
843 DUMP(PFM_STARTINDENT
, "Start indent:", "%d", dxStartIndent
);
844 DUMP(PFM_RIGHTINDENT
, "Right indent:", "%d", dxRightIndent
);
845 DUMP(PFM_OFFSET
, "Offset:", "%d", dxOffset
);
846 if (pFmt
->dwMask
& PFM_ALIGNMENT
) {
847 switch (pFmt
->wAlignment
) {
848 case PFA_LEFT
: p
+= sprintf(p
, "Alignment: left\n"); break;
849 case PFA_RIGHT
: p
+= sprintf(p
, "Alignment: right\n"); break;
850 case PFA_CENTER
: p
+= sprintf(p
, "Alignment: center\n"); break;
851 case PFA_JUSTIFY
: p
+= sprintf(p
, "Alignment: justify\n"); break;
852 default : p
+= sprintf(p
, "Alignment: incorrect %d\n", pFmt
->wAlignment
); break;
855 else p
+= sprintf(p
, "Alignment: N/A\n");
856 DUMP(PFM_TABSTOPS
, "Tab Stops:", "%d", cTabCount
);
857 if (pFmt
->dwMask
& PFM_TABSTOPS
) {
859 p
+= sprintf(p
, "\t");
860 for (i
= 0; i
< pFmt
->cTabCount
; i
++) p
+= sprintf(p
, "%x ", pFmt
->rgxTabs
[i
]);
861 p
+= sprintf(p
, "\n");
863 DUMP(PFM_SPACEBEFORE
, "Space Before:", "%d", dySpaceBefore
);
864 DUMP(PFM_SPACEAFTER
, "Space After:", "%d", dySpaceAfter
);
865 DUMP(PFM_LINESPACING
, "Line spacing:", "%d", dyLineSpacing
);
866 DUMP(PFM_STYLE
, "Text style:", "%d", sStyle
);
867 DUMP(PFM_LINESPACING
, "Line spacing rule:", "%u", bLineSpacingRule
);
868 /* bOutlineLevel should be 0 */
869 DUMP(PFM_SHADING
, "Shading Weight:", "%u", wShadingWeight
);
870 DUMP(PFM_SHADING
, "Shading Style:", "%u", wShadingStyle
);
871 DUMP(PFM_NUMBERINGSTART
, "Numbering Start:", "%u", wNumberingStart
);
872 DUMP(PFM_NUMBERINGSTYLE
, "Numbering Style:", "0x%x", wNumberingStyle
);
873 DUMP(PFM_NUMBERINGTAB
, "Numbering Tab:", "%u", wNumberingStyle
);
874 DUMP(PFM_BORDER
, "Border Space:", "%u", wBorderSpace
);
875 DUMP(PFM_BORDER
, "Border Width:", "%u", wBorderWidth
);
876 DUMP(PFM_BORDER
, "Borders:", "%u", wBorders
);
883 ME_GetSelectionParas(ME_TextEditor
*editor
, ME_DisplayItem
**para
, ME_DisplayItem
**para_end
)
885 ME_Cursor
*pEndCursor
= &editor
->pCursors
[1];
887 *para
= editor
->pCursors
[0].pPara
;
888 *para_end
= editor
->pCursors
[1].pPara
;
889 if (*para
== *para_end
)
892 if ((*para_end
)->member
.para
.nCharOfs
< (*para
)->member
.para
.nCharOfs
) {
893 ME_DisplayItem
*tmp
= *para
;
897 pEndCursor
= &editor
->pCursors
[0];
900 /* The paragraph at the end of a non-empty selection isn't included
901 * if the selection ends at the start of the paragraph. */
902 if (!pEndCursor
->pRun
->member
.run
.nCharOfs
&& !pEndCursor
->nOffset
)
903 *para_end
= (*para_end
)->member
.para
.prev_para
;
907 BOOL
ME_SetSelectionParaFormat(ME_TextEditor
*editor
, const PARAFORMAT2
*pFmt
)
909 ME_DisplayItem
*para
, *para_end
;
911 ME_GetSelectionParas(editor
, ¶
, ¶_end
);
914 ME_SetParaFormat(editor
, ¶
->member
.para
, pFmt
);
915 if (para
== para_end
)
917 para
= para
->member
.para
.next_para
;
923 static void ME_GetParaFormat(ME_TextEditor
*editor
,
924 const ME_DisplayItem
*para
,
927 UINT cbSize
= pFmt
->cbSize
;
928 if (pFmt
->cbSize
>= sizeof(PARAFORMAT2
)) {
929 *pFmt
= para
->member
.para
.fmt
;
931 CopyMemory(pFmt
, ¶
->member
.para
.fmt
, pFmt
->cbSize
);
932 pFmt
->dwMask
&= PFM_ALL
;
934 pFmt
->cbSize
= cbSize
;
937 void ME_GetSelectionParaFormat(ME_TextEditor
*editor
, PARAFORMAT2
*pFmt
)
939 ME_DisplayItem
*para
, *para_end
;
942 if (pFmt
->cbSize
< sizeof(PARAFORMAT
)) {
947 ME_GetSelectionParas(editor
, ¶
, ¶_end
);
949 ME_GetParaFormat(editor
, para
, pFmt
);
951 /* Invalidate values that change across the selected paragraphs. */
952 while (para
!= para_end
)
954 para
= para
->member
.para
.next_para
;
955 curFmt
= ¶
->member
.para
.fmt
;
957 #define CHECK_FIELD(m, f) \
958 if (pFmt->f != curFmt->f) pFmt->dwMask &= ~(m);
960 CHECK_FIELD(PFM_NUMBERING
, wNumbering
);
961 CHECK_FIELD(PFM_STARTINDENT
, dxStartIndent
);
962 CHECK_FIELD(PFM_RIGHTINDENT
, dxRightIndent
);
963 CHECK_FIELD(PFM_OFFSET
, dxOffset
);
964 CHECK_FIELD(PFM_ALIGNMENT
, wAlignment
);
965 if (pFmt
->dwMask
& PFM_TABSTOPS
) {
966 if (pFmt
->cTabCount
!= para
->member
.para
.fmt
.cTabCount
||
967 memcmp(pFmt
->rgxTabs
, curFmt
->rgxTabs
, curFmt
->cTabCount
*sizeof(int)))
968 pFmt
->dwMask
&= ~PFM_TABSTOPS
;
971 if (pFmt
->dwMask
>= sizeof(PARAFORMAT2
))
973 pFmt
->dwMask
&= ~((pFmt
->wEffects
^ curFmt
->wEffects
) << 16);
974 CHECK_FIELD(PFM_SPACEBEFORE
, dySpaceBefore
);
975 CHECK_FIELD(PFM_SPACEAFTER
, dySpaceAfter
);
976 CHECK_FIELD(PFM_LINESPACING
, dyLineSpacing
);
977 CHECK_FIELD(PFM_STYLE
, sStyle
);
978 CHECK_FIELD(PFM_SPACEAFTER
, bLineSpacingRule
);
979 CHECK_FIELD(PFM_SHADING
, wShadingWeight
);
980 CHECK_FIELD(PFM_SHADING
, wShadingStyle
);
981 CHECK_FIELD(PFM_NUMBERINGSTART
, wNumberingStart
);
982 CHECK_FIELD(PFM_NUMBERINGSTYLE
, wNumberingStyle
);
983 CHECK_FIELD(PFM_NUMBERINGTAB
, wNumberingTab
);
984 CHECK_FIELD(PFM_BORDER
, wBorderSpace
);
985 CHECK_FIELD(PFM_BORDER
, wBorderWidth
);
986 CHECK_FIELD(PFM_BORDER
, wBorders
);
992 void ME_SetDefaultParaFormat(ME_TextEditor
*editor
, PARAFORMAT2
*pFmt
)
994 const PARAFORMAT2
*host_fmt
;
997 ZeroMemory(pFmt
, sizeof(PARAFORMAT2
));
998 pFmt
->cbSize
= sizeof(PARAFORMAT2
);
999 pFmt
->dwMask
= PFM_ALL2
;
1000 pFmt
->wAlignment
= PFA_LEFT
;
1002 pFmt
->bOutlineLevel
= TRUE
;
1004 hr
= ITextHost_TxGetParaFormat( editor
->texthost
, (const PARAFORMAT
**)&host_fmt
);
1007 /* Just use the alignment for now */
1008 if (host_fmt
->dwMask
& PFM_ALIGNMENT
)
1009 pFmt
->wAlignment
= host_fmt
->wAlignment
;
1010 ITextHost_OnTxParaFormatChange( editor
->texthost
, (PARAFORMAT
*)pFmt
);