win32u: Respect per-monitor thread dpi awareness when getting window from point.
[wine.git] / dlls / riched20 / para.c
bloba380f45e5567301fbef5bdfd01b011a80c222534
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, HDC hdc)
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;
164 ME_InitContext( &c, editor, hdc );
166 hf = GetStockObject(SYSTEM_FONT);
167 assert(hf);
168 GetObjectW(hf, sizeof(LOGFONTW), &lf);
169 ZeroMemory(&cf, sizeof(cf));
170 cf.cbSize = sizeof(cf);
171 cf.dwMask = CFM_ANIMATION|CFM_BACKCOLOR|CFM_CHARSET|CFM_COLOR|CFM_FACE|CFM_KERNING|CFM_LCID|CFM_OFFSET;
172 cf.dwMask |= CFM_REVAUTHOR|CFM_SIZE|CFM_SPACING|CFM_STYLE|CFM_UNDERLINETYPE|CFM_WEIGHT;
173 cf.dwMask |= CFM_ALLCAPS|CFM_BOLD|CFM_DISABLED|CFM_EMBOSS|CFM_HIDDEN;
174 cf.dwMask |= CFM_IMPRINT|CFM_ITALIC|CFM_LINK|CFM_OUTLINE|CFM_PROTECTED;
175 cf.dwMask |= CFM_REVISED|CFM_SHADOW|CFM_SMALLCAPS|CFM_STRIKEOUT;
176 cf.dwMask |= CFM_SUBSCRIPT|CFM_UNDERLINE;
178 cf.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
179 lstrcpyW(cf.szFaceName, lf.lfFaceName);
180 /* Convert system font height from logical units to twips for cf.yHeight */
181 cf.yHeight = (lf.lfHeight * 72 * 1440) / (c.dpi.cy * c.dpi.cy);
182 if (lf.lfWeight > FW_NORMAL) cf.dwEffects |= CFE_BOLD;
183 cf.wWeight = lf.lfWeight;
184 if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC;
185 if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE;
186 cf.bUnderlineType = CFU_UNDERLINE;
187 if (lf.lfStrikeOut) cf.dwEffects |= CFE_STRIKEOUT;
188 cf.bPitchAndFamily = lf.lfPitchAndFamily;
189 cf.bCharSet = lf.lfCharSet;
190 cf.lcid = GetSystemDefaultLCID();
192 style = ME_MakeStyle(&cf);
193 text->pDefaultStyle = style;
195 if (ITextHost_TxGetCharFormat(editor->texthost, &host_cf) == S_OK)
197 ZeroMemory(&cf, sizeof(cf));
198 cf.cbSize = sizeof(cf);
199 cfany_to_cf2w(&cf, (CHARFORMAT2W *)host_cf);
200 ME_SetDefaultCharFormat(editor, &cf);
203 eol_len = editor->bEmulateVersion10 ? 2 : 1;
204 para->text = ME_MakeStringN( L"\r\n", eol_len );
206 run = run_create( style, MERF_ENDPARA );
207 run->nCharOfs = 0;
208 run->len = eol_len;
209 run->para = para;
210 para->eop_run = run;
212 ME_InsertBefore( text->pLast, para_get_di( para) );
213 ME_InsertBefore( text->pLast, run_get_di( run ) );
214 para->prev_para = text->pFirst;
215 para->next_para = text->pLast;
216 text->pFirst->member.para.next_para = para_get_di( para );
217 text->pLast->member.para.prev_para = para_get_di( para );
219 text->pLast->member.para.nCharOfs = editor->bEmulateVersion10 ? 2 : 1;
221 wine_rb_init( &editor->marked_paras, para_mark_compare );
222 para_mark_add( editor, para );
223 ME_DestroyContext(&c);
226 static void para_mark_rewrap_paras( ME_TextEditor *editor, ME_Paragraph *first, const ME_Paragraph *end )
228 while (first != end)
230 para_mark_rewrap( editor, first );
231 first = para_next( first );
235 void editor_mark_rewrap_all( ME_TextEditor *editor )
237 para_mark_rewrap_paras( editor, editor_first_para( editor ), editor_end_para( editor ) );
240 static void table_update_flags( ME_Paragraph *para )
242 para->fmt.dwMask |= PFM_TABLE | PFM_TABLEROWDELIMITER;
244 if (para_cell( para )) para->nFlags |= MEPF_CELL;
245 else para->nFlags &= ~MEPF_CELL;
247 if (para->nFlags & MEPF_ROWEND) para->fmt.wEffects |= PFE_TABLEROWDELIMITER;
248 else para->fmt.wEffects &= ~PFE_TABLEROWDELIMITER;
250 if (para->nFlags & (MEPF_ROWSTART | MEPF_CELL | MEPF_ROWEND))
251 para->fmt.wEffects |= PFE_TABLE;
252 else
253 para->fmt.wEffects &= ~PFE_TABLE;
256 static inline BOOL para_num_same_list( const PARAFORMAT2 *item, const PARAFORMAT2 *base )
258 return item->wNumbering == base->wNumbering &&
259 item->wNumberingStart == base->wNumberingStart &&
260 item->wNumberingStyle == base->wNumberingStyle &&
261 !(item->wNumberingStyle & PFNS_NEWNUMBER);
264 static int para_num_get_num( ME_Paragraph *para )
266 ME_DisplayItem *prev;
267 int num = para->fmt.wNumberingStart;
269 for (prev = para->prev_para; prev->type == diParagraph;
270 para = &prev->member.para, prev = prev->member.para.prev_para, num++)
272 if (!para_num_same_list( &prev->member.para.fmt, &para->fmt )) break;
274 return num;
277 static ME_String *para_num_get_str( ME_Paragraph *para, WORD num )
279 /* max 4 Roman letters (representing '8') / decade + '(' + ')' */
280 ME_String *str = ME_MakeStringEmpty( 20 + 2 );
281 WCHAR *p;
282 static const WORD letter_base[] = { 1, 26, 26 * 26, 26 * 26 * 26 };
283 /* roman_base should start on a '5' not a '1', otherwise the 'total' code will need adjusting.
284 'N' and 'O' are what MS uses for 5000 and 10000, their version doesn't work well above 30000,
285 but we'll use 'P' as the obvious extension, this gets us up to 2^16, which is all we care about. */
286 static const struct
288 int base;
289 char letter;
291 roman_base[] =
293 {50000, 'P'}, {10000, 'O'}, {5000, 'N'}, {1000, 'M'},
294 {500, 'D'}, {100, 'C'}, {50, 'L'}, {10, 'X'}, {5, 'V'}, {1, 'I'}
296 int i, len;
297 WORD letter, total, char_offset = 0;
299 if (!str) return NULL;
301 p = str->szData;
303 if ((para->fmt.wNumberingStyle & 0xf00) == PFNS_PARENS)
304 *p++ = '(';
306 switch (para->fmt.wNumbering)
308 case PFN_ARABIC:
309 default:
310 p += swprintf( p, 20, L"%d", num );
311 break;
313 case PFN_LCLETTER:
314 char_offset = 'a' - 'A';
315 /* fall through */
316 case PFN_UCLETTER:
317 if (!num) num = 1;
319 /* This is not base-26 (or 27) as zeros don't count unless they are leading zeros.
320 It's simplest to start with the least significant letter, so first calculate how many letters are needed. */
321 for (i = 0, total = 0; i < ARRAY_SIZE( letter_base ); i++)
323 total += letter_base[i];
324 if (num < total) break;
326 len = i;
327 for (i = 0; i < len; i++)
329 num -= letter_base[i];
330 letter = (num / letter_base[i]) % 26;
331 p[len - i - 1] = letter + 'A' + char_offset;
333 p += len;
334 *p = 0;
335 break;
337 case PFN_LCROMAN:
338 char_offset = 'a' - 'A';
339 /* fall through */
340 case PFN_UCROMAN:
341 if (!num) num = 1;
343 for (i = 0; i < ARRAY_SIZE( roman_base ); i++)
345 if (i > 0)
347 if (i % 2 == 0) /* eg 5000, check for 9000 */
348 total = roman_base[i].base + 4 * roman_base[i + 1].base;
349 else /* eg 1000, check for 4000 */
350 total = 4 * roman_base[i].base;
352 if (num / total)
354 *p++ = roman_base[(i & ~1) + 1].letter + char_offset;
355 *p++ = roman_base[i - 1].letter + char_offset;
356 num -= total;
357 continue;
361 len = num / roman_base[i].base;
362 while (len--)
364 *p++ = roman_base[i].letter + char_offset;
365 num -= roman_base[i].base;
368 *p = 0;
369 break;
372 switch (para->fmt.wNumberingStyle & 0xf00)
374 case PFNS_PARENS:
375 case PFNS_PAREN:
376 *p++ = ')';
377 *p = 0;
378 break;
380 case PFNS_PERIOD:
381 *p++ = '.';
382 *p = 0;
383 break;
386 str->nLen = p - str->szData;
387 return str;
390 void para_num_init( ME_Context *c, ME_Paragraph *para )
392 ME_Style *style;
393 CHARFORMAT2W cf;
394 SIZE sz;
396 if (!para->fmt.wNumbering) return;
397 if (para->para_num.style && para->para_num.text) return;
399 if (!para->para_num.style)
401 style = para->eop_run->style;
403 if (para->fmt.wNumbering == PFN_BULLET)
405 cf.cbSize = sizeof(cf);
406 cf.dwMask = CFM_FACE | CFM_CHARSET;
407 lstrcpyW( cf.szFaceName, L"Symbol" );
408 cf.bCharSet = SYMBOL_CHARSET;
409 style = ME_ApplyStyle( c->editor, style, &cf );
411 else
413 ME_AddRefStyle( style );
416 para->para_num.style = style;
419 if (!para->para_num.text)
421 if (para->fmt.wNumbering != PFN_BULLET)
422 para->para_num.text = para_num_get_str( para, para_num_get_num( para ) );
423 else
424 para->para_num.text = ME_MakeStringConst( L"\x00b7", 1 );
427 select_style( c, para->para_num.style );
428 GetTextExtentPointW( c->hDC, para->para_num.text->szData, para->para_num.text->nLen, &sz );
429 para->para_num.width = sz.cx;
430 GetTextExtentPointW( c->hDC, L" ", 1, &sz );
431 para->para_num.width += sz.cx;
434 void para_num_clear( struct para_num *pn )
436 if (pn->style)
438 ME_ReleaseStyle( pn->style );
439 pn->style = NULL;
441 ME_DestroyString( pn->text );
442 pn->text = NULL;
445 static void para_num_clear_list( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *orig_fmt )
449 para_mark_rewrap( editor, para );
450 para_num_clear( &para->para_num );
451 if (para->next_para->type != diParagraph) break;
452 para = &para->next_para->member.para;
453 } while (para_num_same_list( &para->fmt, orig_fmt ));
456 static BOOL para_set_fmt( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *pFmt )
458 PARAFORMAT2 copy;
459 DWORD dwMask;
461 assert(para->fmt.cbSize == sizeof(PARAFORMAT2));
462 dwMask = pFmt->dwMask;
463 if (pFmt->cbSize < sizeof(PARAFORMAT))
464 return FALSE;
465 else if (pFmt->cbSize < sizeof(PARAFORMAT2))
466 dwMask &= PFM_ALL;
467 else
468 dwMask &= PFM_ALL2;
470 add_undo_set_para_fmt( editor, para );
472 copy = para->fmt;
474 #define COPY_FIELD(m, f) \
475 if (dwMask & (m)) { \
476 para->fmt.dwMask |= m; \
477 para->fmt.f = pFmt->f; \
480 COPY_FIELD(PFM_NUMBERING, wNumbering);
481 COPY_FIELD(PFM_STARTINDENT, dxStartIndent);
482 if (dwMask & PFM_OFFSETINDENT)
483 para->fmt.dxStartIndent += pFmt->dxStartIndent;
484 COPY_FIELD(PFM_RIGHTINDENT, dxRightIndent);
485 COPY_FIELD(PFM_OFFSET, dxOffset);
486 COPY_FIELD(PFM_ALIGNMENT, wAlignment);
487 if (dwMask & PFM_TABSTOPS)
489 para->fmt.cTabCount = pFmt->cTabCount;
490 memcpy(para->fmt.rgxTabs, pFmt->rgxTabs, pFmt->cTabCount*sizeof(LONG));
493 #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \
494 PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \
495 PFM_TABLE)
496 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
497 if (dwMask & EFFECTS_MASK)
499 para->fmt.dwMask |= dwMask & EFFECTS_MASK;
500 para->fmt.wEffects &= ~HIWORD(dwMask);
501 para->fmt.wEffects |= pFmt->wEffects & HIWORD(dwMask);
503 #undef EFFECTS_MASK
505 COPY_FIELD(PFM_SPACEBEFORE, dySpaceBefore);
506 COPY_FIELD(PFM_SPACEAFTER, dySpaceAfter);
507 COPY_FIELD(PFM_LINESPACING, dyLineSpacing);
508 COPY_FIELD(PFM_STYLE, sStyle);
509 COPY_FIELD(PFM_LINESPACING, bLineSpacingRule);
510 COPY_FIELD(PFM_SHADING, wShadingWeight);
511 COPY_FIELD(PFM_SHADING, wShadingStyle);
512 COPY_FIELD(PFM_NUMBERINGSTART, wNumberingStart);
513 COPY_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle);
514 COPY_FIELD(PFM_NUMBERINGTAB, wNumberingTab);
515 COPY_FIELD(PFM_BORDER, wBorderSpace);
516 COPY_FIELD(PFM_BORDER, wBorderWidth);
517 COPY_FIELD(PFM_BORDER, wBorders);
519 para->fmt.dwMask |= dwMask;
520 #undef COPY_FIELD
522 if (memcmp(&copy, &para->fmt, sizeof(PARAFORMAT2)))
524 para_mark_rewrap( editor, para );
525 if (((dwMask & PFM_NUMBERING) && (copy.wNumbering != para->fmt.wNumbering)) ||
526 ((dwMask & PFM_NUMBERINGSTART) && (copy.wNumberingStart != para->fmt.wNumberingStart)) ||
527 ((dwMask & PFM_NUMBERINGSTYLE) && (copy.wNumberingStyle != para->fmt.wNumberingStyle)))
529 para_num_clear_list( editor, para, &copy );
533 return TRUE;
536 /* split paragraph at the beginning of the run */
537 ME_Paragraph *para_split( ME_TextEditor *editor, ME_Run *run, ME_Style *style,
538 const WCHAR *eol_str, int eol_len, int paraFlags )
540 ME_Paragraph *new_para = para_create( editor ), *old_para, *next_para;
541 ME_Run *end_run, *next_run;
542 int ofs, i;
543 int run_flags = MERF_ENDPARA;
545 if (!editor->bEmulateVersion10) /* v4.1 */
547 /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */
548 assert( !(paraFlags & ~(MEPF_CELL | MEPF_ROWSTART | MEPF_ROWEND)) );
549 assert( !(paraFlags & (paraFlags-1)) );
550 if (paraFlags == MEPF_CELL)
551 run_flags |= MERF_ENDCELL;
552 else if (paraFlags == MEPF_ROWSTART)
553 run_flags |= MERF_TABLESTART | MERF_HIDDEN;
555 else /* v1.0 - v3.0 */
556 assert( !(paraFlags & (MEPF_CELL |MEPF_ROWSTART | MEPF_ROWEND)) );
558 old_para = run->para;
559 assert( old_para->fmt.cbSize == sizeof(PARAFORMAT2) );
561 /* Clear any cached para numbering following this paragraph */
562 if (old_para->fmt.wNumbering)
563 para_num_clear_list( editor, old_para, &old_para->fmt );
565 new_para->text = ME_VSplitString( old_para->text, run->nCharOfs );
567 end_run = run_create( style, run_flags );
568 ofs = end_run->nCharOfs = run->nCharOfs;
569 end_run->len = eol_len;
570 end_run->para = run->para;
571 ME_AppendString( old_para->text, eol_str, eol_len );
572 next_para = &old_para->next_para->member.para;
574 add_undo_join_paras( editor, old_para->nCharOfs + ofs );
576 /* Update selection cursors to point to the correct paragraph. */
577 for (i = 0; i < editor->nCursors; i++)
579 if (editor->pCursors[i].para == old_para &&
580 run->nCharOfs <= editor->pCursors[i].run->nCharOfs)
582 editor->pCursors[i].para = new_para;
586 /* the new paragraph will have a different starting offset, so update its runs */
587 for (next_run = run; next_run; next_run = run_next( next_run ))
589 next_run->nCharOfs -= ofs;
590 next_run->para = new_para;
593 new_para->nCharOfs = old_para->nCharOfs + ofs;
594 new_para->nCharOfs += eol_len;
595 new_para->nFlags = 0;
596 para_mark_rewrap( editor, new_para );
598 /* FIXME initialize format style and call ME_SetParaFormat blah blah */
599 new_para->fmt = old_para->fmt;
600 new_para->border = old_para->border;
602 /* insert paragraph into paragraph double linked list */
603 new_para->prev_para = para_get_di( old_para );
604 new_para->next_para = para_get_di( next_para );
605 old_para->next_para = para_get_di( new_para );
606 next_para->prev_para = para_get_di( new_para );
608 /* insert end run of the old paragraph, and new paragraph, into DI double linked list */
609 ME_InsertBefore( run_get_di( run ), para_get_di( new_para ) );
610 ME_InsertBefore( para_get_di( new_para ), run_get_di( end_run ) );
612 /* Fix up the paras' eop_run ptrs */
613 new_para->eop_run = old_para->eop_run;
614 old_para->eop_run = end_run;
616 if (!editor->bEmulateVersion10) /* v4.1 */
618 if (paraFlags & (MEPF_ROWSTART | MEPF_CELL))
620 ME_Cell *cell = cell_create();
621 ME_InsertBefore( para_get_di( new_para ), cell_get_di( cell ) );
622 new_para->cell = cell;
623 cell->next_cell = NULL;
624 if (paraFlags & MEPF_ROWSTART)
626 old_para->nFlags |= MEPF_ROWSTART;
627 cell->prev_cell = NULL;
628 cell->parent_cell = old_para->cell;
629 if (para_cell( old_para ))
630 cell->nNestingLevel = para_cell( old_para )->nNestingLevel + 1;
631 else
632 cell->nNestingLevel = 1;
634 else
636 cell->prev_cell = old_para->cell;
637 cell_prev( cell )->next_cell = cell;
638 assert( old_para->nFlags & MEPF_CELL );
639 assert( !(old_para->nFlags & MEPF_ROWSTART) );
640 cell->nNestingLevel = cell_prev( cell )->nNestingLevel;
641 cell->parent_cell = cell_prev( cell )->parent_cell;
644 else if (paraFlags & MEPF_ROWEND)
646 old_para->nFlags |= MEPF_ROWEND;
647 old_para->cell = old_para->cell->parent_cell;
648 new_para->cell = old_para->cell;
649 assert( para_prev( old_para )->nFlags & MEPF_CELL );
650 assert( !(para_prev( old_para )->nFlags & MEPF_ROWSTART) );
651 if (new_para->cell != para_next( new_para )->cell
652 && para_next( new_para )->cell
653 && !cell_prev( para_next( new_para )->cell ))
655 /* Row starts just after the row that was ended. */
656 new_para->nFlags |= MEPF_ROWSTART;
659 else new_para->cell = old_para->cell;
661 table_update_flags( old_para );
662 table_update_flags( new_para );
665 /* force rewrap of the */
666 if (old_para->prev_para->type == diParagraph)
667 para_mark_rewrap( editor, &old_para->prev_para->member.para );
669 para_mark_rewrap( editor, &new_para->prev_para->member.para );
671 /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */
672 editor_propagate_char_ofs( editor, next_para, NULL, eol_len );
673 editor->nParagraphs++;
675 return new_para;
678 /* join para with the next para keeping para's style using the paragraph fmt
679 specified in use_first_fmt */
680 ME_Paragraph *para_join( ME_TextEditor *editor, ME_Paragraph *para, BOOL use_first_fmt )
682 ME_Paragraph *next = para_next( para );
683 ME_Run *end_run, *next_first_run, *tmp_run;
684 ME_Cell *cell = NULL;
685 int i, end_len;
686 CHARFORMAT2W fmt;
687 ME_Cursor startCur, endCur;
688 ME_String *eol_str;
690 assert( next && para_next( next ) );
692 /* Clear any cached para numbering following this paragraph */
693 if (para->fmt.wNumbering) para_num_clear_list( editor, para, &para->fmt );
695 end_run = para_end_run( para );
696 next_first_run = para_first_run( next );
698 end_len = end_run->len;
699 eol_str = ME_VSplitString( para->text, end_run->nCharOfs );
700 ME_AppendString( para->text, next->text->szData, next->text->nLen );
702 /* null char format operation to store the original char format for the ENDPARA run */
703 ME_InitCharFormat2W(&fmt);
704 startCur.para = para;
705 startCur.run = end_run;
706 endCur.para = next;
707 endCur.run = next_first_run;
708 startCur.nOffset = endCur.nOffset = 0;
710 ME_SetCharFormat(editor, &startCur, &endCur, &fmt);
712 if (!editor->bEmulateVersion10) /* v4.1 */
714 /* Remove cell boundary if it is between the end paragraph run and the next
715 * paragraph display item. */
716 if (para->cell != next->cell) cell = next->cell;
718 /* Table cell/row properties are always moved over from the removed para. */
719 para->nFlags = next->nFlags;
720 para->cell = next->cell;
723 add_undo_split_para( editor, next, eol_str, cell );
725 if (cell)
727 ME_Remove( cell_get_di( cell ) );
728 if (cell_prev( cell )) cell_prev( cell )->next_cell = cell_next( cell );
729 if (cell_next( cell )) cell_next( cell )->prev_cell = cell_prev( cell );
730 ME_DestroyDisplayItem( cell_get_di( cell ) );
733 if (!use_first_fmt)
735 add_undo_set_para_fmt( editor, para );
736 para->fmt = next->fmt;
737 para->border = next->border;
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].run == end_run)
746 editor->pCursors[i].run = next_first_run;
747 editor->pCursors[i].nOffset = 0;
749 else if (editor->pCursors[i].para == next)
750 editor->pCursors[i].para = para;
753 for (tmp_run = next_first_run; tmp_run; tmp_run = run_next( tmp_run ))
755 tmp_run->nCharOfs += next->nCharOfs - para->nCharOfs - end_len;
756 tmp_run->para = para;
759 /* Fix up the para's eop_run ptr */
760 para->eop_run = next->eop_run;
762 ME_Remove( run_get_di( end_run ) );
763 ME_DestroyDisplayItem( run_get_di( end_run) );
765 if (editor->last_sel_start_para == next)
766 editor->last_sel_start_para = para;
767 if (editor->last_sel_end_para == next)
768 editor->last_sel_end_para = para;
770 para->next_para = next->next_para;
771 next->next_para->member.para.prev_para = para_get_di( para );
772 ME_Remove( para_get_di(next) );
773 para_destroy( editor, next );
775 editor_propagate_char_ofs( editor, para_next( para ), NULL, -end_len );
777 ME_CheckCharOffsets(editor);
779 editor->nParagraphs--;
780 para_mark_rewrap( editor, para );
781 return para;
784 void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048])
786 char *p;
787 p = buf;
789 #define DUMP(mask, name, fmt, field) \
790 if (pFmt->dwMask & (mask)) p += sprintf(p, "%-22s" fmt "\n", name, pFmt->field); \
791 else p += sprintf(p, "%-22sN/A\n", name);
793 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
794 #define DUMP_EFFECT(mask, name) \
795 p += sprintf(p, "%-22s%s\n", name, (pFmt->dwMask & (mask)) ? ((pFmt->wEffects & ((mask) >> 16)) ? "yes" : "no") : "N/A");
797 DUMP(PFM_NUMBERING, "Numbering:", "%u", wNumbering);
798 DUMP_EFFECT(PFM_DONOTHYPHEN, "Disable auto-hyphen:");
799 DUMP_EFFECT(PFM_KEEP, "No page break in para:");
800 DUMP_EFFECT(PFM_KEEPNEXT, "No page break in para & next:");
801 DUMP_EFFECT(PFM_NOLINENUMBER, "No line number:");
802 DUMP_EFFECT(PFM_NOWIDOWCONTROL, "No widow & orphan:");
803 DUMP_EFFECT(PFM_PAGEBREAKBEFORE, "Page break before:");
804 DUMP_EFFECT(PFM_RTLPARA, "RTL para:");
805 DUMP_EFFECT(PFM_SIDEBYSIDE, "Side by side:");
806 DUMP_EFFECT(PFM_TABLE, "Table:");
807 DUMP(PFM_OFFSETINDENT, "Offset indent:", "%ld", dxStartIndent);
808 DUMP(PFM_STARTINDENT, "Start indent:", "%ld", dxStartIndent);
809 DUMP(PFM_RIGHTINDENT, "Right indent:", "%ld", dxRightIndent);
810 DUMP(PFM_OFFSET, "Offset:", "%ld", dxOffset);
811 if (pFmt->dwMask & PFM_ALIGNMENT) {
812 switch (pFmt->wAlignment) {
813 case PFA_LEFT : p += sprintf(p, "Alignment: left\n"); break;
814 case PFA_RIGHT : p += sprintf(p, "Alignment: right\n"); break;
815 case PFA_CENTER : p += sprintf(p, "Alignment: center\n"); break;
816 case PFA_JUSTIFY: p += sprintf(p, "Alignment: justify\n"); break;
817 default : p += sprintf(p, "Alignment: incorrect %d\n", pFmt->wAlignment); break;
820 else p += sprintf(p, "Alignment: N/A\n");
821 DUMP(PFM_TABSTOPS, "Tab Stops:", "%d", cTabCount);
822 if (pFmt->dwMask & PFM_TABSTOPS) {
823 int i;
824 p += sprintf(p, "\t");
825 for (i = 0; i < pFmt->cTabCount; i++) p += sprintf(p, "%lx ", pFmt->rgxTabs[i]);
826 p += sprintf(p, "\n");
828 DUMP(PFM_SPACEBEFORE, "Space Before:", "%ld", dySpaceBefore);
829 DUMP(PFM_SPACEAFTER, "Space After:", "%ld", dySpaceAfter);
830 DUMP(PFM_LINESPACING, "Line spacing:", "%ld", dyLineSpacing);
831 DUMP(PFM_STYLE, "Text style:", "%d", sStyle);
832 DUMP(PFM_LINESPACING, "Line spacing rule:", "%u", bLineSpacingRule);
833 /* bOutlineLevel should be 0 */
834 DUMP(PFM_SHADING, "Shading Weight:", "%u", wShadingWeight);
835 DUMP(PFM_SHADING, "Shading Style:", "%u", wShadingStyle);
836 DUMP(PFM_NUMBERINGSTART, "Numbering Start:", "%u", wNumberingStart);
837 DUMP(PFM_NUMBERINGSTYLE, "Numbering Style:", "0x%x", wNumberingStyle);
838 DUMP(PFM_NUMBERINGTAB, "Numbering Tab:", "%u", wNumberingStyle);
839 DUMP(PFM_BORDER, "Border Space:", "%u", wBorderSpace);
840 DUMP(PFM_BORDER, "Border Width:", "%u", wBorderWidth);
841 DUMP(PFM_BORDER, "Borders:", "%u", wBorders);
843 #undef DUMP
844 #undef DUMP_EFFECT
847 void editor_get_selection_paras( ME_TextEditor *editor, ME_Paragraph **para, ME_Paragraph **para_end )
849 ME_Cursor *pEndCursor = &editor->pCursors[1];
851 *para = editor->pCursors[0].para;
852 *para_end = editor->pCursors[1].para;
853 if (*para == *para_end)
854 return;
856 if ((*para_end)->nCharOfs < (*para)->nCharOfs)
858 ME_Paragraph *tmp = *para;
860 *para = *para_end;
861 *para_end = tmp;
862 pEndCursor = &editor->pCursors[0];
865 /* The paragraph at the end of a non-empty selection isn't included
866 * if the selection ends at the start of the paragraph. */
867 if (!pEndCursor->run->nCharOfs && !pEndCursor->nOffset)
868 *para_end = para_prev( *para_end );
872 BOOL editor_set_selection_para_fmt( ME_TextEditor *editor, const PARAFORMAT2 *fmt )
874 ME_Paragraph *para, *para_end;
876 editor_get_selection_paras( editor, &para, &para_end );
880 para_set_fmt( editor, para, fmt );
881 if (para == para_end) break;
882 para = para_next( para );
883 } while(1);
885 return TRUE;
888 static void para_copy_fmt( const ME_Paragraph *para, PARAFORMAT2 *fmt )
890 UINT size = fmt->cbSize;
892 if (fmt->cbSize >= sizeof(PARAFORMAT2))
893 *fmt = para->fmt;
894 else
896 memcpy( fmt, &para->fmt, fmt->cbSize );
897 fmt->dwMask &= PFM_ALL;
899 fmt->cbSize = size;
902 void editor_get_selection_para_fmt( ME_TextEditor *editor, PARAFORMAT2 *fmt )
904 ME_Paragraph *para, *para_end;
906 if (fmt->cbSize < sizeof(PARAFORMAT))
908 fmt->dwMask = 0;
909 return;
912 editor_get_selection_paras( editor, &para, &para_end );
914 para_copy_fmt( para, fmt );
916 /* Invalidate values that change across the selected paragraphs. */
917 while (para != para_end)
919 para = para_next( para );
921 #define CHECK_FIELD(m, f) \
922 if (fmt->f != para->fmt.f) fmt->dwMask &= ~(m);
924 CHECK_FIELD(PFM_NUMBERING, wNumbering);
925 CHECK_FIELD(PFM_STARTINDENT, dxStartIndent);
926 CHECK_FIELD(PFM_RIGHTINDENT, dxRightIndent);
927 CHECK_FIELD(PFM_OFFSET, dxOffset);
928 CHECK_FIELD(PFM_ALIGNMENT, wAlignment);
929 if (fmt->dwMask & PFM_TABSTOPS)
931 if (fmt->cTabCount != para->fmt.cTabCount ||
932 memcmp(fmt->rgxTabs, para->fmt.rgxTabs, para->fmt.cTabCount * sizeof(int) ))
933 fmt->dwMask &= ~PFM_TABSTOPS;
936 if (fmt->cbSize >= sizeof(PARAFORMAT2))
938 fmt->dwMask &= ~((fmt->wEffects ^ para->fmt.wEffects) << 16);
939 CHECK_FIELD(PFM_SPACEBEFORE, dySpaceBefore);
940 CHECK_FIELD(PFM_SPACEAFTER, dySpaceAfter);
941 CHECK_FIELD(PFM_LINESPACING, dyLineSpacing);
942 CHECK_FIELD(PFM_STYLE, sStyle);
943 CHECK_FIELD(PFM_SPACEAFTER, bLineSpacingRule);
944 CHECK_FIELD(PFM_SHADING, wShadingWeight);
945 CHECK_FIELD(PFM_SHADING, wShadingStyle);
946 CHECK_FIELD(PFM_NUMBERINGSTART, wNumberingStart);
947 CHECK_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle);
948 CHECK_FIELD(PFM_NUMBERINGTAB, wNumberingTab);
949 CHECK_FIELD(PFM_BORDER, wBorderSpace);
950 CHECK_FIELD(PFM_BORDER, wBorderWidth);
951 CHECK_FIELD(PFM_BORDER, wBorders);
953 #undef CHECK_FIELD
957 void editor_set_default_para_fmt( ME_TextEditor *editor, PARAFORMAT2 *pFmt )
959 const PARAFORMAT2 *host_fmt;
960 HRESULT hr;
962 ZeroMemory(pFmt, sizeof(PARAFORMAT2));
963 pFmt->cbSize = sizeof(PARAFORMAT2);
964 pFmt->dwMask = PFM_ALL2;
965 pFmt->wAlignment = PFA_LEFT;
966 pFmt->sStyle = -1;
967 pFmt->bOutlineLevel = TRUE;
969 hr = ITextHost_TxGetParaFormat( editor->texthost, (const PARAFORMAT **)&host_fmt );
970 if (SUCCEEDED(hr))
972 /* Just use the alignment for now */
973 if (host_fmt->dwMask & PFM_ALIGNMENT)
974 pFmt->wAlignment = host_fmt->wAlignment;
975 ITextHost_OnTxParaFormatChange( editor->texthost, (PARAFORMAT *)pFmt );