makefiles: Explicitly create destination dirs when installing symlinks.
[wine/zf.git] / dlls / riched20 / para.c
blobdb258947916240a7db9eeaea383a6eb51d20c447
1 /*
2 * RichEdit - functions working on paragraphs of text (diParagraph).
3 *
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
20 */
22 #include "editor.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
26 void para_mark_rewrap( ME_TextEditor *editor, ME_Paragraph *para )
28 para->nFlags |= MEPF_REWRAP;
29 para_mark_add( editor, para );
32 static ME_Paragraph *para_create( ME_TextEditor *editor )
34 ME_DisplayItem *item = ME_MakeDI(diParagraph);
36 ME_SetDefaultParaFormat(editor, &item->member.para.fmt);
37 item->member.para.nFlags = MEPF_REWRAP;
39 return &item->member.para;
42 void para_destroy( ME_TextEditor *editor, ME_Paragraph *para )
44 if (para->nWidth == editor->nTotalWidth)
46 para->nWidth = 0;
47 editor->nTotalWidth = get_total_width(editor);
49 editor->total_rows -= para->nRows;
50 ME_DestroyString( para->text );
51 para_num_clear( &para->para_num );
52 para_mark_remove( editor, para );
53 ME_DestroyDisplayItem( para_get_di( para ) );
56 /* Note para_next/prev will return the start and end doc nodes */
57 ME_Paragraph *para_next( ME_Paragraph *para )
59 if (para->next_para) return &para->next_para->member.para;
60 return NULL;
63 ME_Paragraph *para_prev( ME_Paragraph *para )
65 if (para->prev_para && para->prev_para->type == diParagraph) return &para->prev_para->member.para;
66 return NULL;
69 int get_total_width(ME_TextEditor *editor)
71 ME_Paragraph *para;
72 int total_width = 0;
74 if (editor->pBuffer->pFirst && editor->pBuffer->pLast)
76 para = &editor->pBuffer->pFirst->next->member.para;
77 while (para != &editor->pBuffer->pLast->member.para && para->next_para)
79 total_width = max(total_width, para->nWidth);
80 para = &para->next_para->member.para;
84 return total_width;
87 static int para_mark_compare( const void *key, const struct wine_rb_entry *entry )
89 ME_Paragraph *para = WINE_RB_ENTRY_VALUE( entry, ME_Paragraph, marked_entry );
91 return *(int *)key - para->nCharOfs;
94 void para_mark_remove( ME_TextEditor *editor, ME_Paragraph *para )
96 wine_rb_remove_key( &editor->marked_paras, &para->nCharOfs );
99 void para_mark_add( ME_TextEditor *editor, ME_Paragraph *para )
101 wine_rb_put( &editor->marked_paras, &para->nCharOfs, &para->marked_entry );
104 ME_Run *para_first_run( ME_Paragraph *para )
106 ME_DisplayItem *di;
108 for (di = para_get_di( para ); di != para->next_para; di = di->next )
110 if (di->type != diRun) continue;
111 return &di->member.run;
113 ERR( "failed to find run in paragraph\n" );
114 return NULL;
117 ME_Run *para_end_run( ME_Paragraph *para )
119 return para->eop_run;
122 BOOL para_in_table( ME_Paragraph *para )
124 return para->fmt.wEffects & PFE_TABLE;
127 ME_Cell *para_cell( ME_Paragraph *para )
129 if (!para->pCell) return NULL;
130 return &para->pCell->member.cell;
133 void ME_MakeFirstParagraph(ME_TextEditor *editor)
135 static const WCHAR cr_lf[] = {'\r','\n',0};
136 ME_Context c;
137 CHARFORMAT2W cf;
138 const CHARFORMATW *host_cf;
139 LOGFONTW lf;
140 HFONT hf;
141 ME_TextBuffer *text = editor->pBuffer;
142 ME_Paragraph *para = para_create( editor );
143 ME_Run *run;
144 ME_Style *style;
145 int eol_len;
147 ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
149 hf = GetStockObject(SYSTEM_FONT);
150 assert(hf);
151 GetObjectW(hf, sizeof(LOGFONTW), &lf);
152 ZeroMemory(&cf, sizeof(cf));
153 cf.cbSize = sizeof(cf);
154 cf.dwMask = CFM_ANIMATION|CFM_BACKCOLOR|CFM_CHARSET|CFM_COLOR|CFM_FACE|CFM_KERNING|CFM_LCID|CFM_OFFSET;
155 cf.dwMask |= CFM_REVAUTHOR|CFM_SIZE|CFM_SPACING|CFM_STYLE|CFM_UNDERLINETYPE|CFM_WEIGHT;
156 cf.dwMask |= CFM_ALLCAPS|CFM_BOLD|CFM_DISABLED|CFM_EMBOSS|CFM_HIDDEN;
157 cf.dwMask |= CFM_IMPRINT|CFM_ITALIC|CFM_LINK|CFM_OUTLINE|CFM_PROTECTED;
158 cf.dwMask |= CFM_REVISED|CFM_SHADOW|CFM_SMALLCAPS|CFM_STRIKEOUT;
159 cf.dwMask |= CFM_SUBSCRIPT|CFM_UNDERLINE;
161 cf.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
162 lstrcpyW(cf.szFaceName, lf.lfFaceName);
163 /* Convert system font height from logical units to twips for cf.yHeight */
164 cf.yHeight = (lf.lfHeight * 72 * 1440) / (c.dpi.cy * c.dpi.cy);
165 if (lf.lfWeight > FW_NORMAL) cf.dwEffects |= CFE_BOLD;
166 cf.wWeight = lf.lfWeight;
167 if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC;
168 if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE;
169 cf.bUnderlineType = CFU_UNDERLINE;
170 if (lf.lfStrikeOut) cf.dwEffects |= CFE_STRIKEOUT;
171 cf.bPitchAndFamily = lf.lfPitchAndFamily;
172 cf.bCharSet = lf.lfCharSet;
173 cf.lcid = GetSystemDefaultLCID();
175 style = ME_MakeStyle(&cf);
176 text->pDefaultStyle = style;
178 if (ITextHost_TxGetCharFormat(editor->texthost, &host_cf) == S_OK)
180 ZeroMemory(&cf, sizeof(cf));
181 cf.cbSize = sizeof(cf);
182 cfany_to_cf2w(&cf, (CHARFORMAT2W *)host_cf);
183 ME_SetDefaultCharFormat(editor, &cf);
186 eol_len = editor->bEmulateVersion10 ? 2 : 1;
187 para->text = ME_MakeStringN( cr_lf, eol_len );
189 run = run_create( style, MERF_ENDPARA );
190 run->nCharOfs = 0;
191 run->len = eol_len;
192 run->para = para;
193 para->eop_run = run;
195 ME_InsertBefore( text->pLast, para_get_di( para) );
196 ME_InsertBefore( text->pLast, run_get_di( run ) );
197 para->prev_para = text->pFirst;
198 para->next_para = text->pLast;
199 text->pFirst->member.para.next_para = para_get_di( para );
200 text->pLast->member.para.prev_para = para_get_di( para );
202 text->pLast->member.para.nCharOfs = editor->bEmulateVersion10 ? 2 : 1;
204 wine_rb_init( &editor->marked_paras, para_mark_compare );
205 para_mark_add( editor, para );
206 ME_DestroyContext(&c);
209 static void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, const ME_DisplayItem *last)
211 while(first != last)
213 para_mark_rewrap( editor, &first->member.para );
214 first = first->member.para.next_para;
218 void ME_MarkAllForWrapping(ME_TextEditor *editor)
220 ME_MarkForWrapping(editor, editor->pBuffer->pFirst->member.para.next_para, editor->pBuffer->pLast);
223 static void table_update_flags( ME_Paragraph *para )
225 para->fmt.dwMask |= PFM_TABLE | PFM_TABLEROWDELIMITER;
227 if (para->pCell) para->nFlags |= MEPF_CELL;
228 else para->nFlags &= ~MEPF_CELL;
230 if (para->nFlags & MEPF_ROWEND) para->fmt.wEffects |= PFE_TABLEROWDELIMITER;
231 else para->fmt.wEffects &= ~PFE_TABLEROWDELIMITER;
233 if (para->nFlags & (MEPF_ROWSTART | MEPF_CELL | MEPF_ROWEND))
234 para->fmt.wEffects |= PFE_TABLE;
235 else
236 para->fmt.wEffects &= ~PFE_TABLE;
239 static inline BOOL para_num_same_list( const PARAFORMAT2 *item, const PARAFORMAT2 *base )
241 return item->wNumbering == base->wNumbering &&
242 item->wNumberingStart == base->wNumberingStart &&
243 item->wNumberingStyle == base->wNumberingStyle &&
244 !(item->wNumberingStyle & PFNS_NEWNUMBER);
247 static int para_num_get_num( ME_Paragraph *para )
249 ME_DisplayItem *prev;
250 int num = para->fmt.wNumberingStart;
252 for (prev = para->prev_para; prev->type == diParagraph;
253 para = &prev->member.para, prev = prev->member.para.prev_para, num++)
255 if (!para_num_same_list( &prev->member.para.fmt, &para->fmt )) break;
257 return num;
260 static ME_String *para_num_get_str( ME_Paragraph *para, WORD num )
262 /* max 4 Roman letters (representing '8') / decade + '(' + ')' */
263 ME_String *str = ME_MakeStringEmpty( 20 + 2 );
264 WCHAR *p;
265 static const WCHAR fmtW[] = {'%', 'd', 0};
266 static const WORD letter_base[] = { 1, 26, 26 * 26, 26 * 26 * 26 };
267 /* roman_base should start on a '5' not a '1', otherwise the 'total' code will need adjusting.
268 'N' and 'O' are what MS uses for 5000 and 10000, their version doesn't work well above 30000,
269 but we'll use 'P' as the obvious extension, this gets us up to 2^16, which is all we care about. */
270 static const struct
272 int base;
273 char letter;
275 roman_base[] =
277 {50000, 'P'}, {10000, 'O'}, {5000, 'N'}, {1000, 'M'},
278 {500, 'D'}, {100, 'C'}, {50, 'L'}, {10, 'X'}, {5, 'V'}, {1, 'I'}
280 int i, len;
281 WORD letter, total, char_offset = 0;
283 if (!str) return NULL;
285 p = str->szData;
287 if ((para->fmt.wNumberingStyle & 0xf00) == PFNS_PARENS)
288 *p++ = '(';
290 switch (para->fmt.wNumbering)
292 case PFN_ARABIC:
293 default:
294 p += swprintf( p, 20, fmtW, num );
295 break;
297 case PFN_LCLETTER:
298 char_offset = 'a' - 'A';
299 /* fall through */
300 case PFN_UCLETTER:
301 if (!num) num = 1;
303 /* This is not base-26 (or 27) as zeros don't count unless they are leading zeros.
304 It's simplest to start with the least significant letter, so first calculate how many letters are needed. */
305 for (i = 0, total = 0; i < ARRAY_SIZE( letter_base ); i++)
307 total += letter_base[i];
308 if (num < total) break;
310 len = i;
311 for (i = 0; i < len; i++)
313 num -= letter_base[i];
314 letter = (num / letter_base[i]) % 26;
315 p[len - i - 1] = letter + 'A' + char_offset;
317 p += len;
318 *p = 0;
319 break;
321 case PFN_LCROMAN:
322 char_offset = 'a' - 'A';
323 /* fall through */
324 case PFN_UCROMAN:
325 if (!num) num = 1;
327 for (i = 0; i < ARRAY_SIZE( roman_base ); i++)
329 if (i > 0)
331 if (i % 2 == 0) /* eg 5000, check for 9000 */
332 total = roman_base[i].base + 4 * roman_base[i + 1].base;
333 else /* eg 1000, check for 4000 */
334 total = 4 * roman_base[i].base;
336 if (num / total)
338 *p++ = roman_base[(i & ~1) + 1].letter + char_offset;
339 *p++ = roman_base[i - 1].letter + char_offset;
340 num -= total;
341 continue;
345 len = num / roman_base[i].base;
346 while (len--)
348 *p++ = roman_base[i].letter + char_offset;
349 num -= roman_base[i].base;
352 *p = 0;
353 break;
356 switch (para->fmt.wNumberingStyle & 0xf00)
358 case PFNS_PARENS:
359 case PFNS_PAREN:
360 *p++ = ')';
361 *p = 0;
362 break;
364 case PFNS_PERIOD:
365 *p++ = '.';
366 *p = 0;
367 break;
370 str->nLen = p - str->szData;
371 return str;
374 void para_num_init( ME_Context *c, ME_Paragraph *para )
376 ME_Style *style;
377 CHARFORMAT2W cf;
378 static const WCHAR bullet_font[] = {'S','y','m','b','o','l',0};
379 static const WCHAR bullet_str[] = {0xb7, 0};
380 static const WCHAR spaceW[] = {' ', 0};
381 SIZE sz;
383 if (!para->fmt.wNumbering) return;
384 if (para->para_num.style && para->para_num.text) return;
386 if (!para->para_num.style)
388 style = para->eop_run->style;
390 if (para->fmt.wNumbering == PFN_BULLET)
392 cf.cbSize = sizeof(cf);
393 cf.dwMask = CFM_FACE | CFM_CHARSET;
394 memcpy( cf.szFaceName, bullet_font, sizeof(bullet_font) );
395 cf.bCharSet = SYMBOL_CHARSET;
396 style = ME_ApplyStyle( c->editor, style, &cf );
398 else
400 ME_AddRefStyle( style );
403 para->para_num.style = style;
406 if (!para->para_num.text)
408 if (para->fmt.wNumbering != PFN_BULLET)
409 para->para_num.text = para_num_get_str( para, para_num_get_num( para ) );
410 else
411 para->para_num.text = ME_MakeStringConst( bullet_str, 1 );
414 select_style( c, para->para_num.style );
415 GetTextExtentPointW( c->hDC, para->para_num.text->szData, para->para_num.text->nLen, &sz );
416 para->para_num.width = sz.cx;
417 GetTextExtentPointW( c->hDC, spaceW, 1, &sz );
418 para->para_num.width += sz.cx;
421 void para_num_clear( struct para_num *pn )
423 if (pn->style)
425 ME_ReleaseStyle( pn->style );
426 pn->style = NULL;
428 ME_DestroyString( pn->text );
429 pn->text = NULL;
432 static void para_num_clear_list( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *orig_fmt )
436 para_mark_rewrap( editor, para );
437 para_num_clear( &para->para_num );
438 if (para->next_para->type != diParagraph) break;
439 para = &para->next_para->member.para;
440 } while (para_num_same_list( &para->fmt, orig_fmt ));
443 static BOOL ME_SetParaFormat(ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *pFmt)
445 PARAFORMAT2 copy;
446 DWORD dwMask;
448 assert(para->fmt.cbSize == sizeof(PARAFORMAT2));
449 dwMask = pFmt->dwMask;
450 if (pFmt->cbSize < sizeof(PARAFORMAT))
451 return FALSE;
452 else if (pFmt->cbSize < sizeof(PARAFORMAT2))
453 dwMask &= PFM_ALL;
454 else
455 dwMask &= PFM_ALL2;
457 add_undo_set_para_fmt( editor, para );
459 copy = para->fmt;
461 #define COPY_FIELD(m, f) \
462 if (dwMask & (m)) { \
463 para->fmt.dwMask |= m; \
464 para->fmt.f = pFmt->f; \
467 COPY_FIELD(PFM_NUMBERING, wNumbering);
468 COPY_FIELD(PFM_STARTINDENT, dxStartIndent);
469 if (dwMask & PFM_OFFSETINDENT)
470 para->fmt.dxStartIndent += pFmt->dxStartIndent;
471 COPY_FIELD(PFM_RIGHTINDENT, dxRightIndent);
472 COPY_FIELD(PFM_OFFSET, dxOffset);
473 COPY_FIELD(PFM_ALIGNMENT, wAlignment);
474 if (dwMask & PFM_TABSTOPS)
476 para->fmt.cTabCount = pFmt->cTabCount;
477 memcpy(para->fmt.rgxTabs, pFmt->rgxTabs, pFmt->cTabCount*sizeof(LONG));
480 #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \
481 PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \
482 PFM_TABLE)
483 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
484 if (dwMask & EFFECTS_MASK)
486 para->fmt.dwMask |= dwMask & EFFECTS_MASK;
487 para->fmt.wEffects &= ~HIWORD(dwMask);
488 para->fmt.wEffects |= pFmt->wEffects & HIWORD(dwMask);
490 #undef EFFECTS_MASK
492 COPY_FIELD(PFM_SPACEBEFORE, dySpaceBefore);
493 COPY_FIELD(PFM_SPACEAFTER, dySpaceAfter);
494 COPY_FIELD(PFM_LINESPACING, dyLineSpacing);
495 COPY_FIELD(PFM_STYLE, sStyle);
496 COPY_FIELD(PFM_LINESPACING, bLineSpacingRule);
497 COPY_FIELD(PFM_SHADING, wShadingWeight);
498 COPY_FIELD(PFM_SHADING, wShadingStyle);
499 COPY_FIELD(PFM_NUMBERINGSTART, wNumberingStart);
500 COPY_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle);
501 COPY_FIELD(PFM_NUMBERINGTAB, wNumberingTab);
502 COPY_FIELD(PFM_BORDER, wBorderSpace);
503 COPY_FIELD(PFM_BORDER, wBorderWidth);
504 COPY_FIELD(PFM_BORDER, wBorders);
506 para->fmt.dwMask |= dwMask;
507 #undef COPY_FIELD
509 if (memcmp(&copy, &para->fmt, sizeof(PARAFORMAT2)))
511 para_mark_rewrap( editor, para );
512 if (((dwMask & PFM_NUMBERING) && (copy.wNumbering != para->fmt.wNumbering)) ||
513 ((dwMask & PFM_NUMBERINGSTART) && (copy.wNumberingStart != para->fmt.wNumberingStart)) ||
514 ((dwMask & PFM_NUMBERINGSTYLE) && (copy.wNumberingStyle != para->fmt.wNumberingStyle)))
516 para_num_clear_list( editor, para, &copy );
520 return TRUE;
523 /* split paragraph at the beginning of the run */
524 ME_Paragraph *para_split( ME_TextEditor *editor, ME_Run *run, ME_Style *style,
525 const WCHAR *eol_str, int eol_len, int paraFlags )
527 ME_Paragraph *new_para = para_create( editor ), *old_para, *next_para;
528 ME_Run *end_run, *next_run;
529 int ofs, i;
530 int run_flags = MERF_ENDPARA;
532 if (!editor->bEmulateVersion10) /* v4.1 */
534 /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */
535 assert( !(paraFlags & ~(MEPF_CELL | MEPF_ROWSTART | MEPF_ROWEND)) );
536 assert( !(paraFlags & (paraFlags-1)) );
537 if (paraFlags == MEPF_CELL)
538 run_flags |= MERF_ENDCELL;
539 else if (paraFlags == MEPF_ROWSTART)
540 run_flags |= MERF_TABLESTART | MERF_HIDDEN;
542 else /* v1.0 - v3.0 */
543 assert( !(paraFlags & (MEPF_CELL |MEPF_ROWSTART | MEPF_ROWEND)) );
545 old_para = run->para;
546 assert( old_para->fmt.cbSize == sizeof(PARAFORMAT2) );
548 /* Clear any cached para numbering following this paragraph */
549 if (old_para->fmt.wNumbering)
550 para_num_clear_list( editor, old_para, &old_para->fmt );
552 new_para->text = ME_VSplitString( old_para->text, run->nCharOfs );
554 end_run = run_create( style, run_flags );
555 ofs = end_run->nCharOfs = run->nCharOfs;
556 end_run->len = eol_len;
557 end_run->para = run->para;
558 ME_AppendString( old_para->text, eol_str, eol_len );
559 next_para = &old_para->next_para->member.para;
561 add_undo_join_paras( editor, old_para->nCharOfs + ofs );
563 /* Update selection cursors to point to the correct paragraph. */
564 for (i = 0; i < editor->nCursors; i++)
566 if (editor->pCursors[i].pPara == para_get_di( old_para ) &&
567 run->nCharOfs <= editor->pCursors[i].pRun->member.run.nCharOfs)
569 editor->pCursors[i].pPara = para_get_di( new_para );
573 /* the new paragraph will have a different starting offset, so update its runs */
574 for (next_run = run; next_run; next_run = run_next( next_run ))
576 next_run->nCharOfs -= ofs;
577 next_run->para = new_para;
580 new_para->nCharOfs = old_para->nCharOfs + ofs;
581 new_para->nCharOfs += eol_len;
582 new_para->nFlags = 0;
583 para_mark_rewrap( editor, new_para );
585 /* FIXME initialize format style and call ME_SetParaFormat blah blah */
586 new_para->fmt = old_para->fmt;
587 new_para->border = old_para->border;
589 /* insert paragraph into paragraph double linked list */
590 new_para->prev_para = para_get_di( old_para );
591 new_para->next_para = para_get_di( next_para );
592 old_para->next_para = para_get_di( new_para );
593 next_para->prev_para = para_get_di( new_para );
595 /* insert end run of the old paragraph, and new paragraph, into DI double linked list */
596 ME_InsertBefore( run_get_di( run ), para_get_di( new_para ) );
597 ME_InsertBefore( para_get_di( new_para ), run_get_di( end_run ) );
599 /* Fix up the paras' eop_run ptrs */
600 new_para->eop_run = old_para->eop_run;
601 old_para->eop_run = end_run;
603 if (!editor->bEmulateVersion10) /* v4.1 */
605 if (paraFlags & (MEPF_ROWSTART | MEPF_CELL))
607 ME_DisplayItem *cell = ME_MakeDI(diCell);
608 ME_InsertBefore( para_get_di( new_para ), cell );
609 new_para->pCell = cell;
610 cell->member.cell.next_cell = NULL;
611 if (paraFlags & MEPF_ROWSTART)
613 old_para->nFlags |= MEPF_ROWSTART;
614 cell->member.cell.prev_cell = NULL;
615 cell->member.cell.parent_cell = old_para->pCell;
616 if (old_para->pCell)
617 cell->member.cell.nNestingLevel = old_para->pCell->member.cell.nNestingLevel + 1;
618 else
619 cell->member.cell.nNestingLevel = 1;
621 else
623 cell->member.cell.prev_cell = old_para->pCell;
624 assert(cell->member.cell.prev_cell);
625 cell->member.cell.prev_cell->member.cell.next_cell = cell;
626 assert( old_para->nFlags & MEPF_CELL );
627 assert( !(old_para->nFlags & MEPF_ROWSTART) );
628 cell->member.cell.nNestingLevel = cell->member.cell.prev_cell->member.cell.nNestingLevel;
629 cell->member.cell.parent_cell = cell->member.cell.prev_cell->member.cell.parent_cell;
632 else if (paraFlags & MEPF_ROWEND)
634 old_para->nFlags |= MEPF_ROWEND;
635 old_para->pCell = old_para->pCell->member.cell.parent_cell;
636 new_para->pCell = old_para->pCell;
637 assert( old_para->prev_para->member.para.nFlags & MEPF_CELL );
638 assert( !(old_para->prev_para->member.para.nFlags & MEPF_ROWSTART) );
639 if (new_para->pCell != new_para->next_para->member.para.pCell
640 && new_para->next_para->member.para.pCell
641 && !new_para->next_para->member.para.pCell->member.cell.prev_cell)
643 /* Row starts just after the row that was ended. */
644 new_para->nFlags |= MEPF_ROWSTART;
647 else new_para->pCell = old_para->pCell;
649 table_update_flags( old_para );
650 table_update_flags( new_para );
653 /* force rewrap of the */
654 if (old_para->prev_para->type == diParagraph)
655 para_mark_rewrap( editor, &old_para->prev_para->member.para );
657 para_mark_rewrap( editor, &new_para->prev_para->member.para );
659 /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */
660 ME_PropagateCharOffset( para_get_di( next_para ), eol_len );
661 editor->nParagraphs++;
663 return new_para;
666 /* join para with the next para keeping para's style using the paragraph fmt
667 specified in use_first_fmt */
668 ME_Paragraph *para_join( ME_TextEditor *editor, ME_Paragraph *para, BOOL use_first_fmt )
670 ME_DisplayItem *tmp, *pCell = NULL;
671 ME_Paragraph *next = para_next( para );
672 ME_Run *end_run, *next_first_run, *tmp_run;
673 int i, shift;
674 int end_len;
675 CHARFORMAT2W fmt;
676 ME_Cursor startCur, endCur;
677 ME_String *eol_str;
679 assert( next && para_next( next ) );
681 /* Clear any cached para numbering following this paragraph */
682 if (para->fmt.wNumbering) para_num_clear_list( editor, para, &para->fmt );
684 end_run = para_end_run( para );
685 next_first_run = para_first_run( next );
687 end_len = end_run->len;
688 eol_str = ME_VSplitString( para->text, end_run->nCharOfs );
689 ME_AppendString( para->text, next->text->szData, next->text->nLen );
691 /* null char format operation to store the original char format for the ENDPARA run */
692 ME_InitCharFormat2W(&fmt);
693 startCur.pPara = para_get_di( para );
694 startCur.pRun = run_get_di( end_run );
695 endCur.pPara = para_get_di( next );
696 endCur.pRun = run_get_di( next_first_run );
697 startCur.nOffset = endCur.nOffset = 0;
699 ME_SetCharFormat(editor, &startCur, &endCur, &fmt);
701 if (!editor->bEmulateVersion10) /* v4.1 */
703 /* Table cell/row properties are always moved over from the removed para. */
704 para->nFlags = next->nFlags;
705 para->pCell = next->pCell;
707 /* Remove cell boundary if it is between the end paragraph run and the next
708 * paragraph display item. */
709 for (tmp = run_get_di( end_run ); tmp != para_get_di( next ); tmp = tmp->next)
711 if (tmp->type == diCell)
713 pCell = tmp;
714 break;
719 add_undo_split_para( editor, next, eol_str, pCell ? &pCell->member.cell : NULL );
721 if (pCell)
723 ME_Remove( pCell );
724 if (pCell->member.cell.prev_cell)
725 pCell->member.cell.prev_cell->member.cell.next_cell = pCell->member.cell.next_cell;
726 if (pCell->member.cell.next_cell)
727 pCell->member.cell.next_cell->member.cell.prev_cell = pCell->member.cell.prev_cell;
728 ME_DestroyDisplayItem( pCell );
731 if (!use_first_fmt)
733 add_undo_set_para_fmt( editor, para );
734 para->fmt = next->fmt;
735 para->border = next->border;
738 shift = next->nCharOfs - para->nCharOfs - end_len;
740 /* Update selection cursors so they don't point to the removed end
741 * paragraph run, and point to the correct paragraph. */
742 for (i = 0; i < editor->nCursors; i++)
744 if (editor->pCursors[i].pRun == run_get_di( end_run ))
746 editor->pCursors[i].pRun = run_get_di( next_first_run );
747 editor->pCursors[i].nOffset = 0;
749 else if (editor->pCursors[i].pPara == para_get_di( next ))
750 editor->pCursors[i].pPara = para_get_di( para );
753 for (tmp_run = next_first_run; tmp_run; tmp_run = run_next( tmp_run ))
755 TRACE( "shifting %s by %d (previous %d)\n", debugstr_run( tmp_run ), shift, tmp_run->nCharOfs );
756 tmp_run->nCharOfs += shift;
757 tmp_run->para = para;
760 /* Fix up the para's eop_run ptr */
761 para->eop_run = next->eop_run;
763 ME_Remove( run_get_di( end_run ) );
764 ME_DestroyDisplayItem( run_get_di( end_run) );
766 if (editor->pLastSelStartPara == para_get_di( next ))
767 editor->pLastSelStartPara = para_get_di( para );
768 if (editor->pLastSelEndPara == para_get_di( next ))
769 editor->pLastSelEndPara = para_get_di( para );
771 para->next_para = next->next_para;
772 next->next_para->member.para.prev_para = para_get_di( para );
773 ME_Remove( para_get_di(next) );
774 para_destroy( editor, next );
776 ME_PropagateCharOffset( para->next_para, -end_len );
778 ME_CheckCharOffsets(editor);
780 editor->nParagraphs--;
781 para_mark_rewrap( editor, para );
782 return para;
785 ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *item) {
786 return ME_FindItemBackOrHere(item, diParagraph);
789 void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048])
791 char *p;
792 p = buf;
794 #define DUMP(mask, name, fmt, field) \
795 if (pFmt->dwMask & (mask)) p += sprintf(p, "%-22s" fmt "\n", name, pFmt->field); \
796 else p += sprintf(p, "%-22sN/A\n", name);
798 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
799 #define DUMP_EFFECT(mask, name) \
800 p += sprintf(p, "%-22s%s\n", name, (pFmt->dwMask & (mask)) ? ((pFmt->wEffects & ((mask) >> 16)) ? "yes" : "no") : "N/A");
802 DUMP(PFM_NUMBERING, "Numbering:", "%u", wNumbering);
803 DUMP_EFFECT(PFM_DONOTHYPHEN, "Disable auto-hyphen:");
804 DUMP_EFFECT(PFM_KEEP, "No page break in para:");
805 DUMP_EFFECT(PFM_KEEPNEXT, "No page break in para & next:");
806 DUMP_EFFECT(PFM_NOLINENUMBER, "No line number:");
807 DUMP_EFFECT(PFM_NOWIDOWCONTROL, "No widow & orphan:");
808 DUMP_EFFECT(PFM_PAGEBREAKBEFORE, "Page break before:");
809 DUMP_EFFECT(PFM_RTLPARA, "RTL para:");
810 DUMP_EFFECT(PFM_SIDEBYSIDE, "Side by side:");
811 DUMP_EFFECT(PFM_TABLE, "Table:");
812 DUMP(PFM_OFFSETINDENT, "Offset indent:", "%d", dxStartIndent);
813 DUMP(PFM_STARTINDENT, "Start indent:", "%d", dxStartIndent);
814 DUMP(PFM_RIGHTINDENT, "Right indent:", "%d", dxRightIndent);
815 DUMP(PFM_OFFSET, "Offset:", "%d", dxOffset);
816 if (pFmt->dwMask & PFM_ALIGNMENT) {
817 switch (pFmt->wAlignment) {
818 case PFA_LEFT : p += sprintf(p, "Alignment: left\n"); break;
819 case PFA_RIGHT : p += sprintf(p, "Alignment: right\n"); break;
820 case PFA_CENTER : p += sprintf(p, "Alignment: center\n"); break;
821 case PFA_JUSTIFY: p += sprintf(p, "Alignment: justify\n"); break;
822 default : p += sprintf(p, "Alignment: incorrect %d\n", pFmt->wAlignment); break;
825 else p += sprintf(p, "Alignment: N/A\n");
826 DUMP(PFM_TABSTOPS, "Tab Stops:", "%d", cTabCount);
827 if (pFmt->dwMask & PFM_TABSTOPS) {
828 int i;
829 p += sprintf(p, "\t");
830 for (i = 0; i < pFmt->cTabCount; i++) p += sprintf(p, "%x ", pFmt->rgxTabs[i]);
831 p += sprintf(p, "\n");
833 DUMP(PFM_SPACEBEFORE, "Space Before:", "%d", dySpaceBefore);
834 DUMP(PFM_SPACEAFTER, "Space After:", "%d", dySpaceAfter);
835 DUMP(PFM_LINESPACING, "Line spacing:", "%d", dyLineSpacing);
836 DUMP(PFM_STYLE, "Text style:", "%d", sStyle);
837 DUMP(PFM_LINESPACING, "Line spacing rule:", "%u", bLineSpacingRule);
838 /* bOutlineLevel should be 0 */
839 DUMP(PFM_SHADING, "Shading Weight:", "%u", wShadingWeight);
840 DUMP(PFM_SHADING, "Shading Style:", "%u", wShadingStyle);
841 DUMP(PFM_NUMBERINGSTART, "Numbering Start:", "%u", wNumberingStart);
842 DUMP(PFM_NUMBERINGSTYLE, "Numbering Style:", "0x%x", wNumberingStyle);
843 DUMP(PFM_NUMBERINGTAB, "Numbering Tab:", "%u", wNumberingStyle);
844 DUMP(PFM_BORDER, "Border Space:", "%u", wBorderSpace);
845 DUMP(PFM_BORDER, "Border Width:", "%u", wBorderWidth);
846 DUMP(PFM_BORDER, "Borders:", "%u", wBorders);
848 #undef DUMP
849 #undef DUMP_EFFECT
852 void
853 ME_GetSelectionParas(ME_TextEditor *editor, ME_DisplayItem **para, ME_DisplayItem **para_end)
855 ME_Cursor *pEndCursor = &editor->pCursors[1];
857 *para = editor->pCursors[0].pPara;
858 *para_end = editor->pCursors[1].pPara;
859 if (*para == *para_end)
860 return;
862 if ((*para_end)->member.para.nCharOfs < (*para)->member.para.nCharOfs) {
863 ME_DisplayItem *tmp = *para;
865 *para = *para_end;
866 *para_end = tmp;
867 pEndCursor = &editor->pCursors[0];
870 /* The paragraph at the end of a non-empty selection isn't included
871 * if the selection ends at the start of the paragraph. */
872 if (!pEndCursor->pRun->member.run.nCharOfs && !pEndCursor->nOffset)
873 *para_end = (*para_end)->member.para.prev_para;
877 BOOL ME_SetSelectionParaFormat(ME_TextEditor *editor, const PARAFORMAT2 *pFmt)
879 ME_DisplayItem *para, *para_end;
881 ME_GetSelectionParas(editor, &para, &para_end);
883 do {
884 ME_SetParaFormat(editor, &para->member.para, pFmt);
885 if (para == para_end)
886 break;
887 para = para->member.para.next_para;
888 } while(1);
890 return TRUE;
893 static void ME_GetParaFormat(ME_TextEditor *editor,
894 const ME_DisplayItem *para,
895 PARAFORMAT2 *pFmt)
897 UINT cbSize = pFmt->cbSize;
898 if (pFmt->cbSize >= sizeof(PARAFORMAT2)) {
899 *pFmt = para->member.para.fmt;
900 } else {
901 CopyMemory(pFmt, &para->member.para.fmt, pFmt->cbSize);
902 pFmt->dwMask &= PFM_ALL;
904 pFmt->cbSize = cbSize;
907 void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
909 ME_DisplayItem *para, *para_end;
910 PARAFORMAT2 *curFmt;
912 if (pFmt->cbSize < sizeof(PARAFORMAT)) {
913 pFmt->dwMask = 0;
914 return;
917 ME_GetSelectionParas(editor, &para, &para_end);
919 ME_GetParaFormat(editor, para, pFmt);
921 /* Invalidate values that change across the selected paragraphs. */
922 while (para != para_end)
924 para = para->member.para.next_para;
925 curFmt = &para->member.para.fmt;
927 #define CHECK_FIELD(m, f) \
928 if (pFmt->f != curFmt->f) pFmt->dwMask &= ~(m);
930 CHECK_FIELD(PFM_NUMBERING, wNumbering);
931 CHECK_FIELD(PFM_STARTINDENT, dxStartIndent);
932 CHECK_FIELD(PFM_RIGHTINDENT, dxRightIndent);
933 CHECK_FIELD(PFM_OFFSET, dxOffset);
934 CHECK_FIELD(PFM_ALIGNMENT, wAlignment);
935 if (pFmt->dwMask & PFM_TABSTOPS) {
936 if (pFmt->cTabCount != para->member.para.fmt.cTabCount ||
937 memcmp(pFmt->rgxTabs, curFmt->rgxTabs, curFmt->cTabCount*sizeof(int)))
938 pFmt->dwMask &= ~PFM_TABSTOPS;
941 if (pFmt->dwMask >= sizeof(PARAFORMAT2))
943 pFmt->dwMask &= ~((pFmt->wEffects ^ curFmt->wEffects) << 16);
944 CHECK_FIELD(PFM_SPACEBEFORE, dySpaceBefore);
945 CHECK_FIELD(PFM_SPACEAFTER, dySpaceAfter);
946 CHECK_FIELD(PFM_LINESPACING, dyLineSpacing);
947 CHECK_FIELD(PFM_STYLE, sStyle);
948 CHECK_FIELD(PFM_SPACEAFTER, bLineSpacingRule);
949 CHECK_FIELD(PFM_SHADING, wShadingWeight);
950 CHECK_FIELD(PFM_SHADING, wShadingStyle);
951 CHECK_FIELD(PFM_NUMBERINGSTART, wNumberingStart);
952 CHECK_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle);
953 CHECK_FIELD(PFM_NUMBERINGTAB, wNumberingTab);
954 CHECK_FIELD(PFM_BORDER, wBorderSpace);
955 CHECK_FIELD(PFM_BORDER, wBorderWidth);
956 CHECK_FIELD(PFM_BORDER, wBorders);
958 #undef CHECK_FIELD
962 void ME_SetDefaultParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
964 const PARAFORMAT2 *host_fmt;
965 HRESULT hr;
967 ZeroMemory(pFmt, sizeof(PARAFORMAT2));
968 pFmt->cbSize = sizeof(PARAFORMAT2);
969 pFmt->dwMask = PFM_ALL2;
970 pFmt->wAlignment = PFA_LEFT;
971 pFmt->sStyle = -1;
972 pFmt->bOutlineLevel = TRUE;
974 hr = ITextHost_TxGetParaFormat( editor->texthost, (const PARAFORMAT **)&host_fmt );
975 if (SUCCEEDED(hr))
977 /* Just use the alignment for now */
978 if (host_fmt->dwMask & PFM_ALIGNMENT)
979 pFmt->wAlignment = host_fmt->wAlignment;
980 ITextHost_OnTxParaFormatChange( editor->texthost, (PARAFORMAT *)pFmt );