user32: Load uxtheme when theming is active.
[wine.git] / dlls / riched20 / para.c
blob78148ae779305881295b4f1a039ea7e12ebc126d
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 editor_set_default_para_fmt( 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 return para->cell;
132 ME_Row *para_first_row( ME_Paragraph *para )
134 ME_DisplayItem *item;
136 item = ME_FindItemFwd( para_get_di( para ), diStartRowOrParagraph );
137 if (!item || item->type != diStartRow) return NULL;
138 return &item->member.row;
141 ME_Row *para_end_row( ME_Paragraph *para )
143 ME_DisplayItem *item;
145 para = para_next( para );
146 item = ME_FindItemBack( para_get_di( para ), diStartRowOrParagraph );
147 if (!item || item->type != diStartRow) return NULL;
148 return &item->member.row;
151 void ME_MakeFirstParagraph(ME_TextEditor *editor)
153 ME_Context c;
154 CHARFORMAT2W cf;
155 const CHARFORMATW *host_cf;
156 LOGFONTW lf;
157 HFONT hf;
158 ME_TextBuffer *text = editor->pBuffer;
159 ME_Paragraph *para = para_create( editor );
160 ME_Run *run;
161 ME_Style *style;
162 int eol_len;
163 HDC hdc = ITextHost_TxGetDC( editor->texthost );
165 ME_InitContext( &c, editor, hdc );
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;
179 cf.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
180 lstrcpyW(cf.szFaceName, lf.lfFaceName);
181 /* Convert system font height from logical units to twips for cf.yHeight */
182 cf.yHeight = (lf.lfHeight * 72 * 1440) / (c.dpi.cy * c.dpi.cy);
183 if (lf.lfWeight > FW_NORMAL) cf.dwEffects |= CFE_BOLD;
184 cf.wWeight = lf.lfWeight;
185 if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC;
186 if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE;
187 cf.bUnderlineType = CFU_UNDERLINE;
188 if (lf.lfStrikeOut) cf.dwEffects |= CFE_STRIKEOUT;
189 cf.bPitchAndFamily = lf.lfPitchAndFamily;
190 cf.bCharSet = lf.lfCharSet;
191 cf.lcid = GetSystemDefaultLCID();
193 style = ME_MakeStyle(&cf);
194 text->pDefaultStyle = style;
196 if (ITextHost_TxGetCharFormat(editor->texthost, &host_cf) == S_OK)
198 ZeroMemory(&cf, sizeof(cf));
199 cf.cbSize = sizeof(cf);
200 cfany_to_cf2w(&cf, (CHARFORMAT2W *)host_cf);
201 ME_SetDefaultCharFormat(editor, &cf);
204 eol_len = editor->bEmulateVersion10 ? 2 : 1;
205 para->text = ME_MakeStringN( L"\r\n", eol_len );
207 run = run_create( style, MERF_ENDPARA );
208 run->nCharOfs = 0;
209 run->len = eol_len;
210 run->para = para;
211 para->eop_run = run;
213 ME_InsertBefore( text->pLast, para_get_di( para) );
214 ME_InsertBefore( text->pLast, run_get_di( run ) );
215 para->prev_para = text->pFirst;
216 para->next_para = text->pLast;
217 text->pFirst->member.para.next_para = para_get_di( para );
218 text->pLast->member.para.prev_para = para_get_di( para );
220 text->pLast->member.para.nCharOfs = editor->bEmulateVersion10 ? 2 : 1;
222 wine_rb_init( &editor->marked_paras, para_mark_compare );
223 para_mark_add( editor, para );
224 ME_DestroyContext(&c);
225 ITextHost_TxReleaseDC( editor->texthost, hdc );
228 static void para_mark_rewrap_paras( ME_TextEditor *editor, ME_Paragraph *first, const ME_Paragraph *end )
230 while (first != end)
232 para_mark_rewrap( editor, first );
233 first = para_next( first );
237 void editor_mark_rewrap_all( ME_TextEditor *editor )
239 para_mark_rewrap_paras( editor, editor_first_para( editor ), editor_end_para( editor ) );
242 static void table_update_flags( ME_Paragraph *para )
244 para->fmt.dwMask |= PFM_TABLE | PFM_TABLEROWDELIMITER;
246 if (para_cell( para )) para->nFlags |= MEPF_CELL;
247 else para->nFlags &= ~MEPF_CELL;
249 if (para->nFlags & MEPF_ROWEND) para->fmt.wEffects |= PFE_TABLEROWDELIMITER;
250 else para->fmt.wEffects &= ~PFE_TABLEROWDELIMITER;
252 if (para->nFlags & (MEPF_ROWSTART | MEPF_CELL | MEPF_ROWEND))
253 para->fmt.wEffects |= PFE_TABLE;
254 else
255 para->fmt.wEffects &= ~PFE_TABLE;
258 static inline BOOL para_num_same_list( const PARAFORMAT2 *item, const PARAFORMAT2 *base )
260 return item->wNumbering == base->wNumbering &&
261 item->wNumberingStart == base->wNumberingStart &&
262 item->wNumberingStyle == base->wNumberingStyle &&
263 !(item->wNumberingStyle & PFNS_NEWNUMBER);
266 static int para_num_get_num( ME_Paragraph *para )
268 ME_DisplayItem *prev;
269 int num = para->fmt.wNumberingStart;
271 for (prev = para->prev_para; prev->type == diParagraph;
272 para = &prev->member.para, prev = prev->member.para.prev_para, num++)
274 if (!para_num_same_list( &prev->member.para.fmt, &para->fmt )) break;
276 return num;
279 static ME_String *para_num_get_str( ME_Paragraph *para, WORD num )
281 /* max 4 Roman letters (representing '8') / decade + '(' + ')' */
282 ME_String *str = ME_MakeStringEmpty( 20 + 2 );
283 WCHAR *p;
284 static const WORD letter_base[] = { 1, 26, 26 * 26, 26 * 26 * 26 };
285 /* roman_base should start on a '5' not a '1', otherwise the 'total' code will need adjusting.
286 'N' and 'O' are what MS uses for 5000 and 10000, their version doesn't work well above 30000,
287 but we'll use 'P' as the obvious extension, this gets us up to 2^16, which is all we care about. */
288 static const struct
290 int base;
291 char letter;
293 roman_base[] =
295 {50000, 'P'}, {10000, 'O'}, {5000, 'N'}, {1000, 'M'},
296 {500, 'D'}, {100, 'C'}, {50, 'L'}, {10, 'X'}, {5, 'V'}, {1, 'I'}
298 int i, len;
299 WORD letter, total, char_offset = 0;
301 if (!str) return NULL;
303 p = str->szData;
305 if ((para->fmt.wNumberingStyle & 0xf00) == PFNS_PARENS)
306 *p++ = '(';
308 switch (para->fmt.wNumbering)
310 case PFN_ARABIC:
311 default:
312 p += swprintf( p, 20, L"%d", num );
313 break;
315 case PFN_LCLETTER:
316 char_offset = 'a' - 'A';
317 /* fall through */
318 case PFN_UCLETTER:
319 if (!num) num = 1;
321 /* This is not base-26 (or 27) as zeros don't count unless they are leading zeros.
322 It's simplest to start with the least significant letter, so first calculate how many letters are needed. */
323 for (i = 0, total = 0; i < ARRAY_SIZE( letter_base ); i++)
325 total += letter_base[i];
326 if (num < total) break;
328 len = i;
329 for (i = 0; i < len; i++)
331 num -= letter_base[i];
332 letter = (num / letter_base[i]) % 26;
333 p[len - i - 1] = letter + 'A' + char_offset;
335 p += len;
336 *p = 0;
337 break;
339 case PFN_LCROMAN:
340 char_offset = 'a' - 'A';
341 /* fall through */
342 case PFN_UCROMAN:
343 if (!num) num = 1;
345 for (i = 0; i < ARRAY_SIZE( roman_base ); i++)
347 if (i > 0)
349 if (i % 2 == 0) /* eg 5000, check for 9000 */
350 total = roman_base[i].base + 4 * roman_base[i + 1].base;
351 else /* eg 1000, check for 4000 */
352 total = 4 * roman_base[i].base;
354 if (num / total)
356 *p++ = roman_base[(i & ~1) + 1].letter + char_offset;
357 *p++ = roman_base[i - 1].letter + char_offset;
358 num -= total;
359 continue;
363 len = num / roman_base[i].base;
364 while (len--)
366 *p++ = roman_base[i].letter + char_offset;
367 num -= roman_base[i].base;
370 *p = 0;
371 break;
374 switch (para->fmt.wNumberingStyle & 0xf00)
376 case PFNS_PARENS:
377 case PFNS_PAREN:
378 *p++ = ')';
379 *p = 0;
380 break;
382 case PFNS_PERIOD:
383 *p++ = '.';
384 *p = 0;
385 break;
388 str->nLen = p - str->szData;
389 return str;
392 void para_num_init( ME_Context *c, ME_Paragraph *para )
394 ME_Style *style;
395 CHARFORMAT2W cf;
396 SIZE sz;
398 if (!para->fmt.wNumbering) return;
399 if (para->para_num.style && para->para_num.text) return;
401 if (!para->para_num.style)
403 style = para->eop_run->style;
405 if (para->fmt.wNumbering == PFN_BULLET)
407 cf.cbSize = sizeof(cf);
408 cf.dwMask = CFM_FACE | CFM_CHARSET;
409 lstrcpyW( cf.szFaceName, L"Symbol" );
410 cf.bCharSet = SYMBOL_CHARSET;
411 style = ME_ApplyStyle( c->editor, style, &cf );
413 else
415 ME_AddRefStyle( style );
418 para->para_num.style = style;
421 if (!para->para_num.text)
423 if (para->fmt.wNumbering != PFN_BULLET)
424 para->para_num.text = para_num_get_str( para, para_num_get_num( para ) );
425 else
426 para->para_num.text = ME_MakeStringConst( L"\x00b7", 1 );
429 select_style( c, para->para_num.style );
430 GetTextExtentPointW( c->hDC, para->para_num.text->szData, para->para_num.text->nLen, &sz );
431 para->para_num.width = sz.cx;
432 GetTextExtentPointW( c->hDC, L" ", 1, &sz );
433 para->para_num.width += sz.cx;
436 void para_num_clear( struct para_num *pn )
438 if (pn->style)
440 ME_ReleaseStyle( pn->style );
441 pn->style = NULL;
443 ME_DestroyString( pn->text );
444 pn->text = NULL;
447 static void para_num_clear_list( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *orig_fmt )
451 para_mark_rewrap( editor, para );
452 para_num_clear( &para->para_num );
453 if (para->next_para->type != diParagraph) break;
454 para = &para->next_para->member.para;
455 } while (para_num_same_list( &para->fmt, orig_fmt ));
458 static BOOL para_set_fmt( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *pFmt )
460 PARAFORMAT2 copy;
461 DWORD dwMask;
463 assert(para->fmt.cbSize == sizeof(PARAFORMAT2));
464 dwMask = pFmt->dwMask;
465 if (pFmt->cbSize < sizeof(PARAFORMAT))
466 return FALSE;
467 else if (pFmt->cbSize < sizeof(PARAFORMAT2))
468 dwMask &= PFM_ALL;
469 else
470 dwMask &= PFM_ALL2;
472 add_undo_set_para_fmt( editor, para );
474 copy = para->fmt;
476 #define COPY_FIELD(m, f) \
477 if (dwMask & (m)) { \
478 para->fmt.dwMask |= m; \
479 para->fmt.f = pFmt->f; \
482 COPY_FIELD(PFM_NUMBERING, wNumbering);
483 COPY_FIELD(PFM_STARTINDENT, dxStartIndent);
484 if (dwMask & PFM_OFFSETINDENT)
485 para->fmt.dxStartIndent += pFmt->dxStartIndent;
486 COPY_FIELD(PFM_RIGHTINDENT, dxRightIndent);
487 COPY_FIELD(PFM_OFFSET, dxOffset);
488 COPY_FIELD(PFM_ALIGNMENT, wAlignment);
489 if (dwMask & PFM_TABSTOPS)
491 para->fmt.cTabCount = pFmt->cTabCount;
492 memcpy(para->fmt.rgxTabs, pFmt->rgxTabs, pFmt->cTabCount*sizeof(LONG));
495 #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \
496 PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \
497 PFM_TABLE)
498 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
499 if (dwMask & EFFECTS_MASK)
501 para->fmt.dwMask |= dwMask & EFFECTS_MASK;
502 para->fmt.wEffects &= ~HIWORD(dwMask);
503 para->fmt.wEffects |= pFmt->wEffects & HIWORD(dwMask);
505 #undef EFFECTS_MASK
507 COPY_FIELD(PFM_SPACEBEFORE, dySpaceBefore);
508 COPY_FIELD(PFM_SPACEAFTER, dySpaceAfter);
509 COPY_FIELD(PFM_LINESPACING, dyLineSpacing);
510 COPY_FIELD(PFM_STYLE, sStyle);
511 COPY_FIELD(PFM_LINESPACING, bLineSpacingRule);
512 COPY_FIELD(PFM_SHADING, wShadingWeight);
513 COPY_FIELD(PFM_SHADING, wShadingStyle);
514 COPY_FIELD(PFM_NUMBERINGSTART, wNumberingStart);
515 COPY_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle);
516 COPY_FIELD(PFM_NUMBERINGTAB, wNumberingTab);
517 COPY_FIELD(PFM_BORDER, wBorderSpace);
518 COPY_FIELD(PFM_BORDER, wBorderWidth);
519 COPY_FIELD(PFM_BORDER, wBorders);
521 para->fmt.dwMask |= dwMask;
522 #undef COPY_FIELD
524 if (memcmp(&copy, &para->fmt, sizeof(PARAFORMAT2)))
526 para_mark_rewrap( editor, para );
527 if (((dwMask & PFM_NUMBERING) && (copy.wNumbering != para->fmt.wNumbering)) ||
528 ((dwMask & PFM_NUMBERINGSTART) && (copy.wNumberingStart != para->fmt.wNumberingStart)) ||
529 ((dwMask & PFM_NUMBERINGSTYLE) && (copy.wNumberingStyle != para->fmt.wNumberingStyle)))
531 para_num_clear_list( editor, para, &copy );
535 return TRUE;
538 /* split paragraph at the beginning of the run */
539 ME_Paragraph *para_split( ME_TextEditor *editor, ME_Run *run, ME_Style *style,
540 const WCHAR *eol_str, int eol_len, int paraFlags )
542 ME_Paragraph *new_para = para_create( editor ), *old_para, *next_para;
543 ME_Run *end_run, *next_run;
544 int ofs, i;
545 int run_flags = MERF_ENDPARA;
547 if (!editor->bEmulateVersion10) /* v4.1 */
549 /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */
550 assert( !(paraFlags & ~(MEPF_CELL | MEPF_ROWSTART | MEPF_ROWEND)) );
551 assert( !(paraFlags & (paraFlags-1)) );
552 if (paraFlags == MEPF_CELL)
553 run_flags |= MERF_ENDCELL;
554 else if (paraFlags == MEPF_ROWSTART)
555 run_flags |= MERF_TABLESTART | MERF_HIDDEN;
557 else /* v1.0 - v3.0 */
558 assert( !(paraFlags & (MEPF_CELL |MEPF_ROWSTART | MEPF_ROWEND)) );
560 old_para = run->para;
561 assert( old_para->fmt.cbSize == sizeof(PARAFORMAT2) );
563 /* Clear any cached para numbering following this paragraph */
564 if (old_para->fmt.wNumbering)
565 para_num_clear_list( editor, old_para, &old_para->fmt );
567 new_para->text = ME_VSplitString( old_para->text, run->nCharOfs );
569 end_run = run_create( style, run_flags );
570 ofs = end_run->nCharOfs = run->nCharOfs;
571 end_run->len = eol_len;
572 end_run->para = run->para;
573 ME_AppendString( old_para->text, eol_str, eol_len );
574 next_para = &old_para->next_para->member.para;
576 add_undo_join_paras( editor, old_para->nCharOfs + ofs );
578 /* Update selection cursors to point to the correct paragraph. */
579 for (i = 0; i < editor->nCursors; i++)
581 if (editor->pCursors[i].para == old_para &&
582 run->nCharOfs <= editor->pCursors[i].run->nCharOfs)
584 editor->pCursors[i].para = new_para;
588 /* the new paragraph will have a different starting offset, so update its runs */
589 for (next_run = run; next_run; next_run = run_next( next_run ))
591 next_run->nCharOfs -= ofs;
592 next_run->para = new_para;
595 new_para->nCharOfs = old_para->nCharOfs + ofs;
596 new_para->nCharOfs += eol_len;
597 new_para->nFlags = 0;
598 para_mark_rewrap( editor, new_para );
600 /* FIXME initialize format style and call ME_SetParaFormat blah blah */
601 new_para->fmt = old_para->fmt;
602 new_para->border = old_para->border;
604 /* insert paragraph into paragraph double linked list */
605 new_para->prev_para = para_get_di( old_para );
606 new_para->next_para = para_get_di( next_para );
607 old_para->next_para = para_get_di( new_para );
608 next_para->prev_para = para_get_di( new_para );
610 /* insert end run of the old paragraph, and new paragraph, into DI double linked list */
611 ME_InsertBefore( run_get_di( run ), para_get_di( new_para ) );
612 ME_InsertBefore( para_get_di( new_para ), run_get_di( end_run ) );
614 /* Fix up the paras' eop_run ptrs */
615 new_para->eop_run = old_para->eop_run;
616 old_para->eop_run = end_run;
618 if (!editor->bEmulateVersion10) /* v4.1 */
620 if (paraFlags & (MEPF_ROWSTART | MEPF_CELL))
622 ME_Cell *cell = cell_create();
623 ME_InsertBefore( para_get_di( new_para ), cell_get_di( cell ) );
624 new_para->cell = cell;
625 cell->next_cell = NULL;
626 if (paraFlags & MEPF_ROWSTART)
628 old_para->nFlags |= MEPF_ROWSTART;
629 cell->prev_cell = NULL;
630 cell->parent_cell = old_para->cell;
631 if (para_cell( old_para ))
632 cell->nNestingLevel = para_cell( old_para )->nNestingLevel + 1;
633 else
634 cell->nNestingLevel = 1;
636 else
638 cell->prev_cell = old_para->cell;
639 cell_prev( cell )->next_cell = cell;
640 assert( old_para->nFlags & MEPF_CELL );
641 assert( !(old_para->nFlags & MEPF_ROWSTART) );
642 cell->nNestingLevel = cell_prev( cell )->nNestingLevel;
643 cell->parent_cell = cell_prev( cell )->parent_cell;
646 else if (paraFlags & MEPF_ROWEND)
648 old_para->nFlags |= MEPF_ROWEND;
649 old_para->cell = old_para->cell->parent_cell;
650 new_para->cell = old_para->cell;
651 assert( para_prev( old_para )->nFlags & MEPF_CELL );
652 assert( !(para_prev( old_para )->nFlags & MEPF_ROWSTART) );
653 if (new_para->cell != para_next( new_para )->cell
654 && para_next( new_para )->cell
655 && !cell_prev( para_next( new_para )->cell ))
657 /* Row starts just after the row that was ended. */
658 new_para->nFlags |= MEPF_ROWSTART;
661 else new_para->cell = old_para->cell;
663 table_update_flags( old_para );
664 table_update_flags( new_para );
667 /* force rewrap of the */
668 if (old_para->prev_para->type == diParagraph)
669 para_mark_rewrap( editor, &old_para->prev_para->member.para );
671 para_mark_rewrap( editor, &new_para->prev_para->member.para );
673 /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */
674 editor_propagate_char_ofs( next_para, NULL, eol_len );
675 editor->nParagraphs++;
677 return new_para;
680 /* join para with the next para keeping para's style using the paragraph fmt
681 specified in use_first_fmt */
682 ME_Paragraph *para_join( ME_TextEditor *editor, ME_Paragraph *para, BOOL use_first_fmt )
684 ME_Paragraph *next = para_next( para );
685 ME_Run *end_run, *next_first_run, *tmp_run;
686 ME_Cell *cell = NULL;
687 int i, end_len;
688 CHARFORMAT2W fmt;
689 ME_Cursor startCur, endCur;
690 ME_String *eol_str;
692 assert( next && para_next( next ) );
694 /* Clear any cached para numbering following this paragraph */
695 if (para->fmt.wNumbering) para_num_clear_list( editor, para, &para->fmt );
697 end_run = para_end_run( para );
698 next_first_run = para_first_run( next );
700 end_len = end_run->len;
701 eol_str = ME_VSplitString( para->text, end_run->nCharOfs );
702 ME_AppendString( para->text, next->text->szData, next->text->nLen );
704 /* null char format operation to store the original char format for the ENDPARA run */
705 ME_InitCharFormat2W(&fmt);
706 startCur.para = para;
707 startCur.run = end_run;
708 endCur.para = next;
709 endCur.run = next_first_run;
710 startCur.nOffset = endCur.nOffset = 0;
712 ME_SetCharFormat(editor, &startCur, &endCur, &fmt);
714 if (!editor->bEmulateVersion10) /* v4.1 */
716 /* Remove cell boundary if it is between the end paragraph run and the next
717 * paragraph display item. */
718 if (para->cell != next->cell) cell = next->cell;
720 /* Table cell/row properties are always moved over from the removed para. */
721 para->nFlags = next->nFlags;
722 para->cell = next->cell;
725 add_undo_split_para( editor, next, eol_str, cell );
727 if (cell)
729 ME_Remove( cell_get_di( cell ) );
730 if (cell_prev( cell )) cell_prev( cell )->next_cell = cell_next( cell );
731 if (cell_next( cell )) cell_next( cell )->prev_cell = cell_prev( cell );
732 ME_DestroyDisplayItem( cell_get_di( cell ) );
735 if (!use_first_fmt)
737 add_undo_set_para_fmt( editor, para );
738 para->fmt = next->fmt;
739 para->border = next->border;
742 /* Update selection cursors so they don't point to the removed end
743 * paragraph run, and point to the correct paragraph. */
744 for (i = 0; i < editor->nCursors; i++)
746 if (editor->pCursors[i].run == end_run)
748 editor->pCursors[i].run = next_first_run;
749 editor->pCursors[i].nOffset = 0;
751 else if (editor->pCursors[i].para == next)
752 editor->pCursors[i].para = para;
755 for (tmp_run = next_first_run; tmp_run; tmp_run = run_next( tmp_run ))
757 tmp_run->nCharOfs += next->nCharOfs - para->nCharOfs - end_len;
758 tmp_run->para = para;
761 /* Fix up the para's eop_run ptr */
762 para->eop_run = next->eop_run;
764 ME_Remove( run_get_di( end_run ) );
765 ME_DestroyDisplayItem( run_get_di( end_run) );
767 if (editor->last_sel_start_para == next)
768 editor->last_sel_start_para = para;
769 if (editor->last_sel_end_para == next)
770 editor->last_sel_end_para = para;
772 para->next_para = next->next_para;
773 next->next_para->member.para.prev_para = para_get_di( para );
774 ME_Remove( para_get_di(next) );
775 para_destroy( editor, next );
777 editor_propagate_char_ofs( para_next( para ), NULL, -end_len );
779 ME_CheckCharOffsets(editor);
781 editor->nParagraphs--;
782 para_mark_rewrap( editor, para );
783 return para;
786 void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048])
788 char *p;
789 p = buf;
791 #define DUMP(mask, name, fmt, field) \
792 if (pFmt->dwMask & (mask)) p += sprintf(p, "%-22s" fmt "\n", name, pFmt->field); \
793 else p += sprintf(p, "%-22sN/A\n", name);
795 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
796 #define DUMP_EFFECT(mask, name) \
797 p += sprintf(p, "%-22s%s\n", name, (pFmt->dwMask & (mask)) ? ((pFmt->wEffects & ((mask) >> 16)) ? "yes" : "no") : "N/A");
799 DUMP(PFM_NUMBERING, "Numbering:", "%u", wNumbering);
800 DUMP_EFFECT(PFM_DONOTHYPHEN, "Disable auto-hyphen:");
801 DUMP_EFFECT(PFM_KEEP, "No page break in para:");
802 DUMP_EFFECT(PFM_KEEPNEXT, "No page break in para & next:");
803 DUMP_EFFECT(PFM_NOLINENUMBER, "No line number:");
804 DUMP_EFFECT(PFM_NOWIDOWCONTROL, "No widow & orphan:");
805 DUMP_EFFECT(PFM_PAGEBREAKBEFORE, "Page break before:");
806 DUMP_EFFECT(PFM_RTLPARA, "RTL para:");
807 DUMP_EFFECT(PFM_SIDEBYSIDE, "Side by side:");
808 DUMP_EFFECT(PFM_TABLE, "Table:");
809 DUMP(PFM_OFFSETINDENT, "Offset indent:", "%d", dxStartIndent);
810 DUMP(PFM_STARTINDENT, "Start indent:", "%d", dxStartIndent);
811 DUMP(PFM_RIGHTINDENT, "Right indent:", "%d", dxRightIndent);
812 DUMP(PFM_OFFSET, "Offset:", "%d", dxOffset);
813 if (pFmt->dwMask & PFM_ALIGNMENT) {
814 switch (pFmt->wAlignment) {
815 case PFA_LEFT : p += sprintf(p, "Alignment: left\n"); break;
816 case PFA_RIGHT : p += sprintf(p, "Alignment: right\n"); break;
817 case PFA_CENTER : p += sprintf(p, "Alignment: center\n"); break;
818 case PFA_JUSTIFY: p += sprintf(p, "Alignment: justify\n"); break;
819 default : p += sprintf(p, "Alignment: incorrect %d\n", pFmt->wAlignment); break;
822 else p += sprintf(p, "Alignment: N/A\n");
823 DUMP(PFM_TABSTOPS, "Tab Stops:", "%d", cTabCount);
824 if (pFmt->dwMask & PFM_TABSTOPS) {
825 int i;
826 p += sprintf(p, "\t");
827 for (i = 0; i < pFmt->cTabCount; i++) p += sprintf(p, "%x ", pFmt->rgxTabs[i]);
828 p += sprintf(p, "\n");
830 DUMP(PFM_SPACEBEFORE, "Space Before:", "%d", dySpaceBefore);
831 DUMP(PFM_SPACEAFTER, "Space After:", "%d", dySpaceAfter);
832 DUMP(PFM_LINESPACING, "Line spacing:", "%d", dyLineSpacing);
833 DUMP(PFM_STYLE, "Text style:", "%d", sStyle);
834 DUMP(PFM_LINESPACING, "Line spacing rule:", "%u", bLineSpacingRule);
835 /* bOutlineLevel should be 0 */
836 DUMP(PFM_SHADING, "Shading Weight:", "%u", wShadingWeight);
837 DUMP(PFM_SHADING, "Shading Style:", "%u", wShadingStyle);
838 DUMP(PFM_NUMBERINGSTART, "Numbering Start:", "%u", wNumberingStart);
839 DUMP(PFM_NUMBERINGSTYLE, "Numbering Style:", "0x%x", wNumberingStyle);
840 DUMP(PFM_NUMBERINGTAB, "Numbering Tab:", "%u", wNumberingStyle);
841 DUMP(PFM_BORDER, "Border Space:", "%u", wBorderSpace);
842 DUMP(PFM_BORDER, "Border Width:", "%u", wBorderWidth);
843 DUMP(PFM_BORDER, "Borders:", "%u", wBorders);
845 #undef DUMP
846 #undef DUMP_EFFECT
849 void editor_get_selection_paras( ME_TextEditor *editor, ME_Paragraph **para, ME_Paragraph **para_end )
851 ME_Cursor *pEndCursor = &editor->pCursors[1];
853 *para = editor->pCursors[0].para;
854 *para_end = editor->pCursors[1].para;
855 if (*para == *para_end)
856 return;
858 if ((*para_end)->nCharOfs < (*para)->nCharOfs)
860 ME_Paragraph *tmp = *para;
862 *para = *para_end;
863 *para_end = tmp;
864 pEndCursor = &editor->pCursors[0];
867 /* The paragraph at the end of a non-empty selection isn't included
868 * if the selection ends at the start of the paragraph. */
869 if (!pEndCursor->run->nCharOfs && !pEndCursor->nOffset)
870 *para_end = para_prev( *para_end );
874 BOOL editor_set_selection_para_fmt( ME_TextEditor *editor, const PARAFORMAT2 *fmt )
876 ME_Paragraph *para, *para_end;
878 editor_get_selection_paras( editor, &para, &para_end );
882 para_set_fmt( editor, para, fmt );
883 if (para == para_end) break;
884 para = para_next( para );
885 } while(1);
887 return TRUE;
890 static void para_copy_fmt( const ME_Paragraph *para, PARAFORMAT2 *fmt )
892 UINT size = fmt->cbSize;
894 if (fmt->cbSize >= sizeof(PARAFORMAT2))
895 *fmt = para->fmt;
896 else
898 memcpy( fmt, &para->fmt, fmt->cbSize );
899 fmt->dwMask &= PFM_ALL;
901 fmt->cbSize = size;
904 void editor_get_selection_para_fmt( ME_TextEditor *editor, PARAFORMAT2 *fmt )
906 ME_Paragraph *para, *para_end;
908 if (fmt->cbSize < sizeof(PARAFORMAT))
910 fmt->dwMask = 0;
911 return;
914 editor_get_selection_paras( editor, &para, &para_end );
916 para_copy_fmt( para, fmt );
918 /* Invalidate values that change across the selected paragraphs. */
919 while (para != para_end)
921 para = para_next( para );
923 #define CHECK_FIELD(m, f) \
924 if (fmt->f != para->fmt.f) fmt->dwMask &= ~(m);
926 CHECK_FIELD(PFM_NUMBERING, wNumbering);
927 CHECK_FIELD(PFM_STARTINDENT, dxStartIndent);
928 CHECK_FIELD(PFM_RIGHTINDENT, dxRightIndent);
929 CHECK_FIELD(PFM_OFFSET, dxOffset);
930 CHECK_FIELD(PFM_ALIGNMENT, wAlignment);
931 if (fmt->dwMask & PFM_TABSTOPS)
933 if (fmt->cTabCount != para->fmt.cTabCount ||
934 memcmp(fmt->rgxTabs, para->fmt.rgxTabs, para->fmt.cTabCount * sizeof(int) ))
935 fmt->dwMask &= ~PFM_TABSTOPS;
938 if (fmt->cbSize >= sizeof(PARAFORMAT2))
940 fmt->dwMask &= ~((fmt->wEffects ^ para->fmt.wEffects) << 16);
941 CHECK_FIELD(PFM_SPACEBEFORE, dySpaceBefore);
942 CHECK_FIELD(PFM_SPACEAFTER, dySpaceAfter);
943 CHECK_FIELD(PFM_LINESPACING, dyLineSpacing);
944 CHECK_FIELD(PFM_STYLE, sStyle);
945 CHECK_FIELD(PFM_SPACEAFTER, bLineSpacingRule);
946 CHECK_FIELD(PFM_SHADING, wShadingWeight);
947 CHECK_FIELD(PFM_SHADING, wShadingStyle);
948 CHECK_FIELD(PFM_NUMBERINGSTART, wNumberingStart);
949 CHECK_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle);
950 CHECK_FIELD(PFM_NUMBERINGTAB, wNumberingTab);
951 CHECK_FIELD(PFM_BORDER, wBorderSpace);
952 CHECK_FIELD(PFM_BORDER, wBorderWidth);
953 CHECK_FIELD(PFM_BORDER, wBorders);
955 #undef CHECK_FIELD
959 void editor_set_default_para_fmt( ME_TextEditor *editor, PARAFORMAT2 *pFmt )
961 const PARAFORMAT2 *host_fmt;
962 HRESULT hr;
964 ZeroMemory(pFmt, sizeof(PARAFORMAT2));
965 pFmt->cbSize = sizeof(PARAFORMAT2);
966 pFmt->dwMask = PFM_ALL2;
967 pFmt->wAlignment = PFA_LEFT;
968 pFmt->sStyle = -1;
969 pFmt->bOutlineLevel = TRUE;
971 hr = ITextHost_TxGetParaFormat( editor->texthost, (const PARAFORMAT **)&host_fmt );
972 if (SUCCEEDED(hr))
974 /* Just use the alignment for now */
975 if (host_fmt->dwMask & PFM_ALIGNMENT)
976 pFmt->wAlignment = host_fmt->wAlignment;
977 ITextHost_OnTxParaFormatChange( editor->texthost, (PARAFORMAT *)pFmt );