xref: /reactos/dll/win32/riched20/para.c (revision 40955b44)
1 /*
2  * RichEdit - functions working on paragraphs of text (diParagraph).
3  *
4  * Copyright 2004 by Krzysztof Foltman
5  * Copyright 2006 by Phil Krylov
6  *
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.
11  *
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.
16  *
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
20  */
21 
22 #include "editor.h"
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
25 
mark_para_rewrap(ME_TextEditor * editor,ME_DisplayItem * para)26 void mark_para_rewrap(ME_TextEditor *editor, ME_DisplayItem *para)
27 {
28     para->member.para.nFlags |= MEPF_REWRAP;
29     add_marked_para(editor, para);
30 }
31 
get_di_from_para(ME_Paragraph * para)32 ME_DisplayItem *get_di_from_para(ME_Paragraph *para)
33 {
34     return (ME_DisplayItem *)((ptrdiff_t)para - offsetof(ME_DisplayItem, member));
35 }
36 
make_para(ME_TextEditor * editor)37 static ME_DisplayItem *make_para(ME_TextEditor *editor)
38 {
39     ME_DisplayItem *item = ME_MakeDI(diParagraph);
40 
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;
44 
45     return item;
46 }
47 
destroy_para(ME_TextEditor * editor,ME_DisplayItem * item)48 void destroy_para(ME_TextEditor *editor, ME_DisplayItem *item)
49 {
50     assert(item->type == diParagraph);
51 
52     if (item->member.para.nWidth == editor->nTotalWidth)
53     {
54         item->member.para.nWidth = 0;
55         editor->nTotalWidth = get_total_width(editor);
56     }
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);
62 }
63 
get_total_width(ME_TextEditor * editor)64 int get_total_width(ME_TextEditor *editor)
65 {
66     ME_Paragraph *para;
67     int total_width = 0;
68 
69     if (editor->pBuffer->pFirst && editor->pBuffer->pLast)
70     {
71         para = &editor->pBuffer->pFirst->next->member.para;
72         while (para != &editor->pBuffer->pLast->member.para && para->next_para)
73         {
74             total_width = max(total_width, para->nWidth);
75             para = &para->next_para->member.para;
76         }
77     }
78 
79     return total_width;
80 }
81 
remove_marked_para(ME_TextEditor * editor,ME_DisplayItem * di)82 void remove_marked_para(ME_TextEditor *editor, ME_DisplayItem *di)
83 {
84     ME_DisplayItem *head = editor->first_marked_para;
85 
86     assert(di->type == diParagraph);
87     if (!di->member.para.next_marked && !di->member.para.prev_marked)
88     {
89         if (di == head)
90             editor->first_marked_para = NULL;
91     }
92     else if (di->member.para.next_marked && di->member.para.prev_marked)
93     {
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;
97     }
98     else if (di->member.para.next_marked)
99     {
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;
104     }
105     else
106     {
107         di->member.para.prev_marked->member.para.next_marked = NULL;
108         di->member.para.prev_marked = NULL;
109     }
110 }
111 
add_marked_para(ME_TextEditor * editor,ME_DisplayItem * di)112 void add_marked_para(ME_TextEditor *editor, ME_DisplayItem *di)
113 {
114     ME_DisplayItem *iter = editor->first_marked_para;
115 
116     if (!iter)
117     {
118         editor->first_marked_para = di;
119         return;
120     }
121     while (iter)
122     {
123         if (iter == di)
124             return;
125         else if (di->member.para.nCharOfs < iter->member.para.nCharOfs)
126         {
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;
131             break;
132         }
133         else if (di->member.para.nCharOfs >= iter->member.para.nCharOfs)
134         {
135             if (!iter->member.para.next_marked || di->member.para.nCharOfs < iter->member.para.next_marked->member.para.nCharOfs)
136             {
137                 if (iter->member.para.next_marked)
138                 {
139                     di->member.para.next_marked = iter->member.para.next_marked;
140                     iter->member.para.next_marked->member.para.prev_marked = di;
141                 }
142                 di->member.para.prev_marked = iter;
143                 iter->member.para.next_marked = di;
144                 break;
145             }
146         }
147         iter = iter->member.para.next_marked;
148     }
149 }
150 
ME_MakeFirstParagraph(ME_TextEditor * editor)151 void ME_MakeFirstParagraph(ME_TextEditor *editor)
152 {
153   static const WCHAR cr_lf[] = {'\r','\n',0};
154   ME_Context c;
155   CHARFORMAT2W cf;
156   const CHARFORMATW *host_cf;
157   LOGFONTW lf;
158   HFONT hf;
159   ME_TextBuffer *text = editor->pBuffer;
160   ME_DisplayItem *para = make_para(editor);
161   ME_DisplayItem *run;
162   ME_Style *style;
163   int eol_len;
164 
165   ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
166 
167   hf = GetStockObject(SYSTEM_FONT);
168   assert(hf);
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;
178 
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();
192 
193   style = ME_MakeStyle(&cf);
194   text->pDefaultStyle = style;
195 
196   if (ITextHost_TxGetCharFormat(editor->texthost, &host_cf) == S_OK)
197   {
198     ZeroMemory(&cf, sizeof(cf));
199     cf.cbSize = sizeof(cf);
200     cfany_to_cf2w(&cf, (CHARFORMAT2W *)host_cf);
201     ME_SetDefaultCharFormat(editor, &cf);
202   }
203 
204   eol_len = editor->bEmulateVersion10 ? 2 : 1;
205   para->member.para.text = ME_MakeStringN( cr_lf, eol_len );
206 
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 = &para->member.para;
211 
212   para->member.para.eop_run = &run->member.run;
213 
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;
220 
221   text->pLast->member.para.nCharOfs = editor->bEmulateVersion10 ? 2 : 1;
222 
223   add_marked_para(editor, para);
224   ME_DestroyContext(&c);
225 }
226 
ME_MarkForWrapping(ME_TextEditor * editor,ME_DisplayItem * first,const ME_DisplayItem * last)227 static void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, const ME_DisplayItem *last)
228 {
229   while(first != last)
230   {
231     mark_para_rewrap(editor, first);
232     first = first->member.para.next_para;
233   }
234 }
235 
ME_MarkAllForWrapping(ME_TextEditor * editor)236 void ME_MarkAllForWrapping(ME_TextEditor *editor)
237 {
238   ME_MarkForWrapping(editor, editor->pBuffer->pFirst->member.para.next_para, editor->pBuffer->pLast);
239 }
240 
ME_UpdateTableFlags(ME_DisplayItem * para)241 static void ME_UpdateTableFlags(ME_DisplayItem *para)
242 {
243   para->member.para.fmt.dwMask |= PFM_TABLE|PFM_TABLEROWDELIMITER;
244   if (para->member.para.pCell) {
245     para->member.para.nFlags |= MEPF_CELL;
246   } else {
247     para->member.para.nFlags &= ~MEPF_CELL;
248   }
249   if (para->member.para.nFlags & MEPF_ROWEND) {
250     para->member.para.fmt.wEffects |= PFE_TABLEROWDELIMITER;
251   } else {
252     para->member.para.fmt.wEffects &= ~PFE_TABLEROWDELIMITER;
253   }
254   if (para->member.para.nFlags & (MEPF_ROWSTART|MEPF_CELL|MEPF_ROWEND))
255     para->member.para.fmt.wEffects |= PFE_TABLE;
256   else
257     para->member.para.fmt.wEffects &= ~PFE_TABLE;
258 }
259 
para_num_same_list(const PARAFORMAT2 * item,const PARAFORMAT2 * base)260 static inline BOOL para_num_same_list( const PARAFORMAT2 *item, const PARAFORMAT2 *base )
261 {
262     return item->wNumbering == base->wNumbering &&
263         item->wNumberingStart == base->wNumberingStart &&
264         item->wNumberingStyle == base->wNumberingStyle &&
265         !(item->wNumberingStyle & PFNS_NEWNUMBER);
266 }
267 
para_num_get_num(ME_Paragraph * para)268 static int para_num_get_num( ME_Paragraph *para )
269 {
270     ME_DisplayItem *prev;
271     int num = para->fmt.wNumberingStart;
272 
273     for (prev = para->prev_para; prev->type == diParagraph;
274          para = &prev->member.para, prev = prev->member.para.prev_para, num++)
275     {
276         if (!para_num_same_list( &prev->member.para.fmt, &para->fmt )) break;
277     }
278     return num;
279 }
280 
para_num_get_str(ME_Paragraph * para,WORD num)281 static ME_String *para_num_get_str( ME_Paragraph *para, WORD num )
282 {
283     /* max 4 Roman letters (representing '8') / decade + '(' + ')' */
284     ME_String *str = ME_MakeStringEmpty( 20 + 2 );
285     WCHAR *p;
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. */
291     static const struct
292     {
293         int base;
294         char letter;
295     }
296     roman_base[] =
297     {
298         {50000, 'P'}, {10000, 'O'}, {5000, 'N'}, {1000, 'M'},
299         {500, 'D'}, {100, 'C'}, {50, 'L'}, {10, 'X'}, {5, 'V'}, {1, 'I'}
300     };
301     int i, len;
302     WORD letter, total, char_offset = 0;
303 
304     if (!str) return NULL;
305 
306     p = str->szData;
307 
308     if ((para->fmt.wNumberingStyle & 0xf00) == PFNS_PARENS)
309         *p++ = '(';
310 
311     switch (para->fmt.wNumbering)
312     {
313     case PFN_ARABIC:
314     default:
315         p += swprintf( p, fmtW, num );
316         break;
317 
318     case PFN_LCLETTER:
319         char_offset = 'a' - 'A';
320         /* fall through */
321     case PFN_UCLETTER:
322         if (!num) num = 1;
323 
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++)
327         {
328             total += letter_base[i];
329             if (num < total) break;
330         }
331         len = i;
332         for (i = 0; i < len; i++)
333         {
334             num -= letter_base[i];
335             letter = (num / letter_base[i]) % 26;
336             p[len - i - 1] = letter + 'A' + char_offset;
337         }
338         p += len;
339         *p = 0;
340         break;
341 
342     case PFN_LCROMAN:
343         char_offset = 'a' - 'A';
344         /* fall through */
345     case PFN_UCROMAN:
346         if (!num) num = 1;
347 
348         for (i = 0; i < ARRAY_SIZE( roman_base ); i++)
349         {
350             if (i > 0)
351             {
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;
356 
357                 if (num / total)
358                 {
359                     *p++ = roman_base[(i & ~1) + 1].letter + char_offset;
360                     *p++ = roman_base[i - 1].letter + char_offset;
361                     num -= total;
362                     continue;
363                 }
364             }
365 
366             len = num / roman_base[i].base;
367             while (len--)
368             {
369                 *p++ = roman_base[i].letter + char_offset;
370                 num -= roman_base[i].base;
371             }
372         }
373         *p = 0;
374         break;
375     }
376 
377     switch (para->fmt.wNumberingStyle & 0xf00)
378     {
379     case PFNS_PARENS:
380     case PFNS_PAREN:
381         *p++ = ')';
382         *p = 0;
383         break;
384 
385     case PFNS_PERIOD:
386         *p++ = '.';
387         *p = 0;
388         break;
389     }
390 
391     str->nLen = p - str->szData;
392     return str;
393 }
394 
para_num_init(ME_Context * c,ME_Paragraph * para)395 void para_num_init( ME_Context *c, ME_Paragraph *para )
396 {
397     ME_Style *style;
398     CHARFORMAT2W cf;
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};
402     SIZE sz;
403 
404     if (!para->fmt.wNumbering) return;
405     if (para->para_num.style && para->para_num.text) return;
406 
407     if (!para->para_num.style)
408     {
409         style = para->eop_run->style;
410 
411         if (para->fmt.wNumbering == PFN_BULLET)
412         {
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 );
418         }
419         else
420         {
421             ME_AddRefStyle( style );
422         }
423 
424         para->para_num.style = style;
425     }
426 
427     if (!para->para_num.text)
428     {
429         if (para->fmt.wNumbering != PFN_BULLET)
430             para->para_num.text = para_num_get_str( para, para_num_get_num( para ) );
431         else
432             para->para_num.text = ME_MakeStringConst( bullet_str, 1 );
433     }
434 
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;
440 }
441 
para_num_clear(struct para_num * pn)442 void para_num_clear( struct para_num *pn )
443 {
444     if (pn->style)
445     {
446         ME_ReleaseStyle( pn->style );
447         pn->style = NULL;
448     }
449     ME_DestroyString( pn->text );
450     pn->text = NULL;
451 }
452 
para_num_clear_list(ME_TextEditor * editor,ME_Paragraph * para,const PARAFORMAT2 * orig_fmt)453 static void para_num_clear_list( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *orig_fmt )
454 {
455     do
456     {
457         mark_para_rewrap(editor, get_di_from_para(para));
458         para_num_clear( &para->para_num );
459         if (para->next_para->type != diParagraph) break;
460         para = &para->next_para->member.para;
461     } while (para_num_same_list( &para->fmt, orig_fmt ));
462 }
463 
ME_SetParaFormat(ME_TextEditor * editor,ME_Paragraph * para,const PARAFORMAT2 * pFmt)464 static BOOL ME_SetParaFormat(ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *pFmt)
465 {
466   PARAFORMAT2 copy;
467   DWORD dwMask;
468 
469   assert(para->fmt.cbSize == sizeof(PARAFORMAT2));
470   dwMask = pFmt->dwMask;
471   if (pFmt->cbSize < sizeof(PARAFORMAT))
472     return FALSE;
473   else if (pFmt->cbSize < sizeof(PARAFORMAT2))
474     dwMask &= PFM_ALL;
475   else
476     dwMask &= PFM_ALL2;
477 
478   add_undo_set_para_fmt( editor, para );
479 
480   copy = para->fmt;
481 
482 #define COPY_FIELD(m, f) \
483   if (dwMask & (m)) {                           \
484     para->fmt.dwMask |= m;                      \
485     para->fmt.f = pFmt->f;                      \
486   }
487 
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)
496   {
497     para->fmt.cTabCount = max(0, min(pFmt->cTabCount, MAX_TAB_STOPS)); /* Clamp between 0 and MAX_TAB_STOPS */
498     memcpy(para->fmt.rgxTabs, pFmt->rgxTabs, para->fmt.cTabCount*sizeof(LONG));
499   }
500 
501 #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \
502                       PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \
503                       PFM_TABLE)
504   /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
505   if (dwMask & EFFECTS_MASK)
506   {
507     para->fmt.dwMask |= dwMask & EFFECTS_MASK;
508     para->fmt.wEffects &= ~HIWORD(dwMask);
509     para->fmt.wEffects |= pFmt->wEffects & HIWORD(dwMask);
510   }
511 #undef EFFECTS_MASK
512 
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);
526 
527   para->fmt.dwMask |= dwMask;
528 #undef COPY_FIELD
529 
530   if (memcmp(&copy, &para->fmt, sizeof(PARAFORMAT2)))
531   {
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)))
536     {
537         para_num_clear_list( editor, para, &copy );
538     }
539   }
540 
541   return TRUE;
542 }
543 
544 /* split paragraph at the beginning of the run */
ME_SplitParagraph(ME_TextEditor * editor,ME_DisplayItem * run,ME_Style * style,const WCHAR * eol_str,int eol_len,int paraFlags)545 ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run,
546                                   ME_Style *style, const WCHAR *eol_str, int eol_len,
547                                   int paraFlags)
548 {
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;
553   int ofs, i;
554   ME_DisplayItem *pp;
555   int run_flags = MERF_ENDPARA;
556 
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)));
567   }
568   assert(run->type == diRun);
569   run_para = ME_GetParagraph(run);
570   assert(run_para->member.para.fmt.cbSize == sizeof(PARAFORMAT2));
571 
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 );
575 
576   new_para->member.para.text = ME_VSplitString( run_para->member.para.text, run->member.run.nCharOfs );
577 
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));
585 
586   add_undo_join_paras( editor, run_para->member.para.nCharOfs + ofs );
587 
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)
592     {
593       editor->pCursors[i].pPara = new_para;
594     }
595   }
596 
597   /* the new paragraph will have a different starting offset, so let's update its runs */
598   pp = run;
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);
603   }
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);
608 
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;
612 
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;
618 
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);
622 
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;
626 
627   if (!editor->bEmulateVersion10) { /* v4.1 */
628     if (paraFlags & (MEPF_ROWSTART|MEPF_CELL))
629     {
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)
635       {
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;
641         else
642           cell->member.cell.nNestingLevel = 1;
643       } else {
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;
651       }
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)
661       {
662         /* Row starts just after the row that was ended. */
663         new_para->member.para.nFlags |= MEPF_ROWSTART;
664       }
665     } else {
666       new_para->member.para.pCell = run_para->member.para.pCell;
667     }
668     ME_UpdateTableFlags(run_para);
669     ME_UpdateTableFlags(new_para);
670   }
671 
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);
675 
676   mark_para_rewrap(editor, new_para->member.para.prev_para);
677 
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++;
681 
682   return new_para;
683 }
684 
685 /* join tp with tp->member.para.next_para, keeping tp's style; this
686  * is consistent with the original */
ME_JoinParagraphs(ME_TextEditor * editor,ME_DisplayItem * tp,BOOL keepFirstParaFormat)687 ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp,
688                                   BOOL keepFirstParaFormat)
689 {
690   ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp, *pCell = NULL;
691   int i, shift;
692   int end_len;
693   CHARFORMAT2W fmt;
694   ME_Cursor startCur, endCur;
695   ME_String *eol_str;
696 
697   assert(tp->type == diParagraph);
698   assert(tp->member.para.next_para);
699   assert(tp->member.para.next_para->type == diParagraph);
700 
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 );
704 
705   pNext = tp->member.para.next_para;
706 
707   /* Need to locate end-of-paragraph run here, in order to know end_len */
708   pRun = ME_FindItemBack(pNext, diRunOrParagraph);
709 
710   assert(pRun);
711   assert(pRun->type == diRun);
712   assert(pRun->member.run.nFlags & MERF_ENDPARA);
713 
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 );
717 
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);
722   endCur.nOffset = 0;
723   startCur = endCur;
724   ME_PrevRun(&startCur.pPara, &startCur.pRun, TRUE);
725   ME_SetCharFormat(editor, &startCur, &endCur, &fmt);
726 
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;
731 
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)
735     {
736       if (pTmp->type == diCell)
737       {
738         pCell = pTmp;
739         break;
740       }
741     }
742   }
743 
744   add_undo_split_para( editor, &pNext->member.para, eol_str, pCell ? &pCell->member.cell : NULL );
745 
746   if (pCell)
747   {
748     ME_Remove( pCell );
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 );
754   }
755 
756   if (!keepFirstParaFormat)
757   {
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;
761   }
762 
763   shift = pNext->member.para.nCharOfs - tp->member.para.nCharOfs - end_len;
764 
765   pFirstRunInNext = ME_FindItemFwd(pNext, diRunOrParagraph);
766 
767   assert(pFirstRunInNext->type == diRun);
768 
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;
777     }
778   }
779 
780   pTmp = pNext;
781   do {
782     pTmp = ME_FindItemFwd(pTmp, diRunOrParagraphOrEnd);
783     if (pTmp->type != diRun)
784       break;
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;
788   } while(1);
789 
790   /* Fix up the para's eop_run ptr */
791   tp->member.para.eop_run = pNext->member.para.eop_run;
792 
793   ME_Remove(pRun);
794   ME_DestroyDisplayItem(pRun);
795 
796   if (editor->pLastSelStartPara == pNext)
797     editor->pLastSelStartPara = tp;
798   if (editor->pLastSelEndPara == pNext)
799     editor->pLastSelEndPara = tp;
800 
801   tp->member.para.next_para = pNext->member.para.next_para;
802   pNext->member.para.next_para->member.para.prev_para = tp;
803   ME_Remove(pNext);
804   destroy_para(editor, pNext);
805 
806   ME_PropagateCharOffset(tp->member.para.next_para, -end_len);
807 
808   ME_CheckCharOffsets(editor);
809 
810   editor->nParagraphs--;
811   mark_para_rewrap(editor, tp);
812   return tp;
813 }
814 
ME_GetParagraph(ME_DisplayItem * item)815 ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *item) {
816   return ME_FindItemBackOrHere(item, diParagraph);
817 }
818 
ME_DumpParaStyleToBuf(const PARAFORMAT2 * pFmt,char buf[2048])819 void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048])
820 {
821   char *p;
822   p = buf;
823 
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);
827 
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");
831 
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;
853     }
854   }
855   else p += sprintf(p, "Alignment:            N/A\n");
856   DUMP(PFM_TABSTOPS,       "Tab Stops:",         "%d", cTabCount);
857   if (pFmt->dwMask & PFM_TABSTOPS) {
858     int i;
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");
862   }
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);
877 
878 #undef DUMP
879 #undef DUMP_EFFECT
880 }
881 
882 void
ME_GetSelectionParas(ME_TextEditor * editor,ME_DisplayItem ** para,ME_DisplayItem ** para_end)883 ME_GetSelectionParas(ME_TextEditor *editor, ME_DisplayItem **para, ME_DisplayItem **para_end)
884 {
885   ME_Cursor *pEndCursor = &editor->pCursors[1];
886 
887   *para = editor->pCursors[0].pPara;
888   *para_end = editor->pCursors[1].pPara;
889   if (*para == *para_end)
890     return;
891 
892   if ((*para_end)->member.para.nCharOfs < (*para)->member.para.nCharOfs) {
893     ME_DisplayItem *tmp = *para;
894 
895     *para = *para_end;
896     *para_end = tmp;
897     pEndCursor = &editor->pCursors[0];
898   }
899 
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;
904 }
905 
906 
ME_SetSelectionParaFormat(ME_TextEditor * editor,const PARAFORMAT2 * pFmt)907 BOOL ME_SetSelectionParaFormat(ME_TextEditor *editor, const PARAFORMAT2 *pFmt)
908 {
909   ME_DisplayItem *para, *para_end;
910 
911   ME_GetSelectionParas(editor, &para, &para_end);
912 
913   do {
914     ME_SetParaFormat(editor, &para->member.para, pFmt);
915     if (para == para_end)
916       break;
917     para = para->member.para.next_para;
918   } while(1);
919 
920   return TRUE;
921 }
922 
ME_GetParaFormat(ME_TextEditor * editor,const ME_DisplayItem * para,PARAFORMAT2 * pFmt)923 static void ME_GetParaFormat(ME_TextEditor *editor,
924                              const ME_DisplayItem *para,
925                              PARAFORMAT2 *pFmt)
926 {
927   UINT cbSize = pFmt->cbSize;
928   if (pFmt->cbSize >= sizeof(PARAFORMAT2)) {
929     *pFmt = para->member.para.fmt;
930   } else {
931     CopyMemory(pFmt, &para->member.para.fmt, pFmt->cbSize);
932     pFmt->dwMask &= PFM_ALL;
933   }
934   pFmt->cbSize = cbSize;
935 }
936 
ME_GetSelectionParaFormat(ME_TextEditor * editor,PARAFORMAT2 * pFmt)937 void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
938 {
939   ME_DisplayItem *para, *para_end;
940   PARAFORMAT2 *curFmt;
941 
942   if (pFmt->cbSize < sizeof(PARAFORMAT)) {
943     pFmt->dwMask = 0;
944     return;
945   }
946 
947   ME_GetSelectionParas(editor, &para, &para_end);
948 
949   ME_GetParaFormat(editor, para, pFmt);
950 
951   /* Invalidate values that change across the selected paragraphs. */
952   while (para != para_end)
953   {
954     para = para->member.para.next_para;
955     curFmt = &para->member.para.fmt;
956 
957 #define CHECK_FIELD(m, f) \
958     if (pFmt->f != curFmt->f) pFmt->dwMask &= ~(m);
959 
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;
969     }
970 
971     if (pFmt->dwMask >= sizeof(PARAFORMAT2))
972     {
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);
987     }
988 #undef CHECK_FIELD
989   }
990 }
991 
ME_SetDefaultParaFormat(ME_TextEditor * editor,PARAFORMAT2 * pFmt)992 void ME_SetDefaultParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
993 {
994     const PARAFORMAT2 *host_fmt;
995     HRESULT hr;
996 
997     ZeroMemory(pFmt, sizeof(PARAFORMAT2));
998     pFmt->cbSize = sizeof(PARAFORMAT2);
999     pFmt->dwMask = PFM_ALL2;
1000     pFmt->wAlignment = PFA_LEFT;
1001     pFmt->sStyle = -1;
1002     pFmt->bOutlineLevel = TRUE;
1003 
1004     hr = ITextHost_TxGetParaFormat( editor->texthost, (const PARAFORMAT **)&host_fmt );
1005     if (SUCCEEDED(hr))
1006     {
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 );
1011     }
1012 }
1013