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 = ¶->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 = ¶->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, ¶->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_num );
459 if (para->next_para->type != diParagraph) break;
460 para = ¶->next_para->member.para;
461 } while (para_num_same_list( ¶->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(©, ¶->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, © );
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, ¶, ¶_end);
912
913 do {
914 ME_SetParaFormat(editor, ¶->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, ¶->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, ¶, ¶_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 = ¶->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