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 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 32 ME_DisplayItem *get_di_from_para(ME_Paragraph *para) 33 { 34 return (ME_DisplayItem *)((ptrdiff_t)para - offsetof(ME_DisplayItem, member)); 35 } 36 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 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 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 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 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 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 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 236 void ME_MarkAllForWrapping(ME_TextEditor *editor) 237 { 238 ME_MarkForWrapping(editor, editor->pBuffer->pFirst->member.para.next_para, editor->pBuffer->pLast); 239 } 240 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 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 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 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 += sprintfW( 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 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 HFONT old_font; 403 SIZE sz; 404 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 old_font = ME_SelectStyleFont( 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 ME_UnselectStyleFont( c, para->para_num.style, old_font ); 441 } 442 443 void para_num_clear( struct para_num *pn ) 444 { 445 if (pn->style) 446 { 447 ME_ReleaseStyle( pn->style ); 448 pn->style = NULL; 449 } 450 ME_DestroyString( pn->text ); 451 pn->text = NULL; 452 } 453 454 static void para_num_clear_list( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *orig_fmt ) 455 { 456 do 457 { 458 mark_para_rewrap(editor, get_di_from_para(para)); 459 para_num_clear( ¶->para_num ); 460 if (para->next_para->type != diParagraph) break; 461 para = ¶->next_para->member.para; 462 } while (para_num_same_list( ¶->fmt, orig_fmt )); 463 } 464 465 static BOOL ME_SetParaFormat(ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *pFmt) 466 { 467 PARAFORMAT2 copy; 468 DWORD dwMask; 469 470 assert(para->fmt.cbSize == sizeof(PARAFORMAT2)); 471 dwMask = pFmt->dwMask; 472 if (pFmt->cbSize < sizeof(PARAFORMAT)) 473 return FALSE; 474 else if (pFmt->cbSize < sizeof(PARAFORMAT2)) 475 dwMask &= PFM_ALL; 476 else 477 dwMask &= PFM_ALL2; 478 479 add_undo_set_para_fmt( editor, para ); 480 481 copy = para->fmt; 482 483 #define COPY_FIELD(m, f) \ 484 if (dwMask & (m)) { \ 485 para->fmt.dwMask |= m; \ 486 para->fmt.f = pFmt->f; \ 487 } 488 489 COPY_FIELD(PFM_NUMBERING, wNumbering); 490 COPY_FIELD(PFM_STARTINDENT, dxStartIndent); 491 if (dwMask & PFM_OFFSETINDENT) 492 para->fmt.dxStartIndent += pFmt->dxStartIndent; 493 COPY_FIELD(PFM_RIGHTINDENT, dxRightIndent); 494 COPY_FIELD(PFM_OFFSET, dxOffset); 495 COPY_FIELD(PFM_ALIGNMENT, wAlignment); 496 if (dwMask & PFM_TABSTOPS) 497 { 498 para->fmt.cTabCount = pFmt->cTabCount; 499 memcpy(para->fmt.rgxTabs, pFmt->rgxTabs, pFmt->cTabCount*sizeof(LONG)); 500 } 501 502 #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \ 503 PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \ 504 PFM_TABLE) 505 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */ 506 if (dwMask & EFFECTS_MASK) 507 { 508 para->fmt.dwMask |= dwMask & EFFECTS_MASK; 509 para->fmt.wEffects &= ~HIWORD(dwMask); 510 para->fmt.wEffects |= pFmt->wEffects & HIWORD(dwMask); 511 } 512 #undef EFFECTS_MASK 513 514 COPY_FIELD(PFM_SPACEBEFORE, dySpaceBefore); 515 COPY_FIELD(PFM_SPACEAFTER, dySpaceAfter); 516 COPY_FIELD(PFM_LINESPACING, dyLineSpacing); 517 COPY_FIELD(PFM_STYLE, sStyle); 518 COPY_FIELD(PFM_LINESPACING, bLineSpacingRule); 519 COPY_FIELD(PFM_SHADING, wShadingWeight); 520 COPY_FIELD(PFM_SHADING, wShadingStyle); 521 COPY_FIELD(PFM_NUMBERINGSTART, wNumberingStart); 522 COPY_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle); 523 COPY_FIELD(PFM_NUMBERINGTAB, wNumberingTab); 524 COPY_FIELD(PFM_BORDER, wBorderSpace); 525 COPY_FIELD(PFM_BORDER, wBorderWidth); 526 COPY_FIELD(PFM_BORDER, wBorders); 527 528 para->fmt.dwMask |= dwMask; 529 #undef COPY_FIELD 530 531 if (memcmp(©, ¶->fmt, sizeof(PARAFORMAT2))) 532 { 533 mark_para_rewrap(editor, get_di_from_para(para)); 534 if (((dwMask & PFM_NUMBERING) && (copy.wNumbering != para->fmt.wNumbering)) || 535 ((dwMask & PFM_NUMBERINGSTART) && (copy.wNumberingStart != para->fmt.wNumberingStart)) || 536 ((dwMask & PFM_NUMBERINGSTYLE) && (copy.wNumberingStyle != para->fmt.wNumberingStyle))) 537 { 538 para_num_clear_list( editor, para, © ); 539 } 540 } 541 542 return TRUE; 543 } 544 545 /* split paragraph at the beginning of the run */ 546 ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, 547 ME_Style *style, const WCHAR *eol_str, int eol_len, 548 int paraFlags) 549 { 550 ME_DisplayItem *next_para = NULL; 551 ME_DisplayItem *run_para = NULL; 552 ME_DisplayItem *new_para = make_para(editor); 553 ME_DisplayItem *end_run; 554 int ofs, i; 555 ME_DisplayItem *pp; 556 int run_flags = MERF_ENDPARA; 557 558 if (!editor->bEmulateVersion10) { /* v4.1 */ 559 /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */ 560 assert(!(paraFlags & ~(MEPF_CELL|MEPF_ROWSTART|MEPF_ROWEND))); 561 assert(!(paraFlags & (paraFlags-1))); 562 if (paraFlags == MEPF_CELL) 563 run_flags |= MERF_ENDCELL; 564 else if (paraFlags == MEPF_ROWSTART) 565 run_flags |= MERF_TABLESTART|MERF_HIDDEN; 566 } else { /* v1.0 - v3.0 */ 567 assert(!(paraFlags & (MEPF_CELL|MEPF_ROWSTART|MEPF_ROWEND))); 568 } 569 assert(run->type == diRun); 570 run_para = ME_GetParagraph(run); 571 assert(run_para->member.para.fmt.cbSize == sizeof(PARAFORMAT2)); 572 573 /* Clear any cached para numbering following this paragraph */ 574 if (run_para->member.para.fmt.wNumbering) 575 para_num_clear_list( editor, &run_para->member.para, &run_para->member.para.fmt ); 576 577 new_para->member.para.text = ME_VSplitString( run_para->member.para.text, run->member.run.nCharOfs ); 578 579 end_run = ME_MakeRun(style, run_flags); 580 ofs = end_run->member.run.nCharOfs = run->member.run.nCharOfs; 581 end_run->member.run.len = eol_len; 582 end_run->member.run.para = run->member.run.para; 583 ME_AppendString( run_para->member.para.text, eol_str, eol_len ); 584 next_para = run_para->member.para.next_para; 585 assert(next_para == ME_FindItemFwd(run_para, diParagraphOrEnd)); 586 587 add_undo_join_paras( editor, run_para->member.para.nCharOfs + ofs ); 588 589 /* Update selection cursors to point to the correct paragraph. */ 590 for (i = 0; i < editor->nCursors; i++) { 591 if (editor->pCursors[i].pPara == run_para && 592 run->member.run.nCharOfs <= editor->pCursors[i].pRun->member.run.nCharOfs) 593 { 594 editor->pCursors[i].pPara = new_para; 595 } 596 } 597 598 /* the new paragraph will have a different starting offset, so let's update its runs */ 599 pp = run; 600 while(pp->type == diRun) { 601 pp->member.run.nCharOfs -= ofs; 602 pp->member.run.para = &new_para->member.para; 603 pp = ME_FindItemFwd(pp, diRunOrParagraphOrEnd); 604 } 605 new_para->member.para.nCharOfs = run_para->member.para.nCharOfs + ofs; 606 new_para->member.para.nCharOfs += eol_len; 607 new_para->member.para.nFlags = 0; 608 mark_para_rewrap(editor, new_para); 609 610 /* FIXME initialize format style and call ME_SetParaFormat blah blah */ 611 new_para->member.para.fmt = run_para->member.para.fmt; 612 new_para->member.para.border = run_para->member.para.border; 613 614 /* insert paragraph into paragraph double linked list */ 615 new_para->member.para.prev_para = run_para; 616 new_para->member.para.next_para = next_para; 617 run_para->member.para.next_para = new_para; 618 next_para->member.para.prev_para = new_para; 619 620 /* insert end run of the old paragraph, and new paragraph, into DI double linked list */ 621 ME_InsertBefore(run, new_para); 622 ME_InsertBefore(new_para, end_run); 623 624 /* Fix up the paras' eop_run ptrs */ 625 new_para->member.para.eop_run = run_para->member.para.eop_run; 626 run_para->member.para.eop_run = &end_run->member.run; 627 628 if (!editor->bEmulateVersion10) { /* v4.1 */ 629 if (paraFlags & (MEPF_ROWSTART|MEPF_CELL)) 630 { 631 ME_DisplayItem *cell = ME_MakeDI(diCell); 632 ME_InsertBefore(new_para, cell); 633 new_para->member.para.pCell = cell; 634 cell->member.cell.next_cell = NULL; 635 if (paraFlags & MEPF_ROWSTART) 636 { 637 run_para->member.para.nFlags |= MEPF_ROWSTART; 638 cell->member.cell.prev_cell = NULL; 639 cell->member.cell.parent_cell = run_para->member.para.pCell; 640 if (run_para->member.para.pCell) 641 cell->member.cell.nNestingLevel = run_para->member.para.pCell->member.cell.nNestingLevel + 1; 642 else 643 cell->member.cell.nNestingLevel = 1; 644 } else { 645 cell->member.cell.prev_cell = run_para->member.para.pCell; 646 assert(cell->member.cell.prev_cell); 647 cell->member.cell.prev_cell->member.cell.next_cell = cell; 648 assert(run_para->member.para.nFlags & MEPF_CELL); 649 assert(!(run_para->member.para.nFlags & MEPF_ROWSTART)); 650 cell->member.cell.nNestingLevel = cell->member.cell.prev_cell->member.cell.nNestingLevel; 651 cell->member.cell.parent_cell = cell->member.cell.prev_cell->member.cell.parent_cell; 652 } 653 } else if (paraFlags & MEPF_ROWEND) { 654 run_para->member.para.nFlags |= MEPF_ROWEND; 655 run_para->member.para.pCell = run_para->member.para.pCell->member.cell.parent_cell; 656 new_para->member.para.pCell = run_para->member.para.pCell; 657 assert(run_para->member.para.prev_para->member.para.nFlags & MEPF_CELL); 658 assert(!(run_para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART)); 659 if (new_para->member.para.pCell != new_para->member.para.next_para->member.para.pCell 660 && new_para->member.para.next_para->member.para.pCell 661 && !new_para->member.para.next_para->member.para.pCell->member.cell.prev_cell) 662 { 663 /* Row starts just after the row that was ended. */ 664 new_para->member.para.nFlags |= MEPF_ROWSTART; 665 } 666 } else { 667 new_para->member.para.pCell = run_para->member.para.pCell; 668 } 669 ME_UpdateTableFlags(run_para); 670 ME_UpdateTableFlags(new_para); 671 } 672 673 /* force rewrap of the */ 674 if (run_para->member.para.prev_para->type == diParagraph) 675 mark_para_rewrap(editor, run_para->member.para.prev_para); 676 677 mark_para_rewrap(editor, new_para->member.para.prev_para); 678 679 /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */ 680 ME_PropagateCharOffset(next_para, eol_len); 681 editor->nParagraphs++; 682 683 return new_para; 684 } 685 686 /* join tp with tp->member.para.next_para, keeping tp's style; this 687 * is consistent with the original */ 688 ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp, 689 BOOL keepFirstParaFormat) 690 { 691 ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp, *pCell = NULL; 692 int i, shift; 693 int end_len; 694 CHARFORMAT2W fmt; 695 ME_Cursor startCur, endCur; 696 ME_String *eol_str; 697 698 assert(tp->type == diParagraph); 699 assert(tp->member.para.next_para); 700 assert(tp->member.para.next_para->type == diParagraph); 701 702 /* Clear any cached para numbering following this paragraph */ 703 if (tp->member.para.fmt.wNumbering) 704 para_num_clear_list( editor, &tp->member.para, &tp->member.para.fmt ); 705 706 pNext = tp->member.para.next_para; 707 708 /* Need to locate end-of-paragraph run here, in order to know end_len */ 709 pRun = ME_FindItemBack(pNext, diRunOrParagraph); 710 711 assert(pRun); 712 assert(pRun->type == diRun); 713 assert(pRun->member.run.nFlags & MERF_ENDPARA); 714 715 end_len = pRun->member.run.len; 716 eol_str = ME_VSplitString( tp->member.para.text, pRun->member.run.nCharOfs ); 717 ME_AppendString( tp->member.para.text, pNext->member.para.text->szData, pNext->member.para.text->nLen ); 718 719 /* null char format operation to store the original char format for the ENDPARA run */ 720 ME_InitCharFormat2W(&fmt); 721 endCur.pPara = pNext; 722 endCur.pRun = ME_FindItemFwd(pNext, diRun); 723 endCur.nOffset = 0; 724 startCur = endCur; 725 ME_PrevRun(&startCur.pPara, &startCur.pRun, TRUE); 726 ME_SetCharFormat(editor, &startCur, &endCur, &fmt); 727 728 if (!editor->bEmulateVersion10) { /* v4.1 */ 729 /* Table cell/row properties are always moved over from the removed para. */ 730 tp->member.para.nFlags = pNext->member.para.nFlags; 731 tp->member.para.pCell = pNext->member.para.pCell; 732 733 /* Remove cell boundary if it is between the end paragraph run and the next 734 * paragraph display item. */ 735 for (pTmp = pRun->next; pTmp != pNext; pTmp = pTmp->next) 736 { 737 if (pTmp->type == diCell) 738 { 739 pCell = pTmp; 740 break; 741 } 742 } 743 } 744 745 add_undo_split_para( editor, &pNext->member.para, eol_str, pCell ? &pCell->member.cell : NULL ); 746 747 if (pCell) 748 { 749 ME_Remove( pCell ); 750 if (pCell->member.cell.prev_cell) 751 pCell->member.cell.prev_cell->member.cell.next_cell = pCell->member.cell.next_cell; 752 if (pCell->member.cell.next_cell) 753 pCell->member.cell.next_cell->member.cell.prev_cell = pCell->member.cell.prev_cell; 754 ME_DestroyDisplayItem( pCell ); 755 } 756 757 if (!keepFirstParaFormat) 758 { 759 add_undo_set_para_fmt( editor, &tp->member.para ); 760 tp->member.para.fmt = pNext->member.para.fmt; 761 tp->member.para.border = pNext->member.para.border; 762 } 763 764 shift = pNext->member.para.nCharOfs - tp->member.para.nCharOfs - end_len; 765 766 pFirstRunInNext = ME_FindItemFwd(pNext, diRunOrParagraph); 767 768 assert(pFirstRunInNext->type == diRun); 769 770 /* Update selection cursors so they don't point to the removed end 771 * paragraph run, and point to the correct paragraph. */ 772 for (i=0; i < editor->nCursors; i++) { 773 if (editor->pCursors[i].pRun == pRun) { 774 editor->pCursors[i].pRun = pFirstRunInNext; 775 editor->pCursors[i].nOffset = 0; 776 } else if (editor->pCursors[i].pPara == pNext) { 777 editor->pCursors[i].pPara = tp; 778 } 779 } 780 781 pTmp = pNext; 782 do { 783 pTmp = ME_FindItemFwd(pTmp, diRunOrParagraphOrEnd); 784 if (pTmp->type != diRun) 785 break; 786 TRACE("shifting %s by %d (previous %d)\n", debugstr_run( &pTmp->member.run ), shift, pTmp->member.run.nCharOfs); 787 pTmp->member.run.nCharOfs += shift; 788 pTmp->member.run.para = &tp->member.para; 789 } while(1); 790 791 /* Fix up the para's eop_run ptr */ 792 tp->member.para.eop_run = pNext->member.para.eop_run; 793 794 ME_Remove(pRun); 795 ME_DestroyDisplayItem(pRun); 796 797 if (editor->pLastSelStartPara == pNext) 798 editor->pLastSelStartPara = tp; 799 if (editor->pLastSelEndPara == pNext) 800 editor->pLastSelEndPara = tp; 801 802 tp->member.para.next_para = pNext->member.para.next_para; 803 pNext->member.para.next_para->member.para.prev_para = tp; 804 ME_Remove(pNext); 805 destroy_para(editor, pNext); 806 807 ME_PropagateCharOffset(tp->member.para.next_para, -end_len); 808 809 ME_CheckCharOffsets(editor); 810 811 editor->nParagraphs--; 812 mark_para_rewrap(editor, tp); 813 return tp; 814 } 815 816 ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *item) { 817 return ME_FindItemBackOrHere(item, diParagraph); 818 } 819 820 void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048]) 821 { 822 char *p; 823 p = buf; 824 825 #define DUMP(mask, name, fmt, field) \ 826 if (pFmt->dwMask & (mask)) p += sprintf(p, "%-22s" fmt "\n", name, pFmt->field); \ 827 else p += sprintf(p, "%-22sN/A\n", name); 828 829 /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */ 830 #define DUMP_EFFECT(mask, name) \ 831 p += sprintf(p, "%-22s%s\n", name, (pFmt->dwMask & (mask)) ? ((pFmt->wEffects & ((mask) >> 16)) ? "yes" : "no") : "N/A"); 832 833 DUMP(PFM_NUMBERING, "Numbering:", "%u", wNumbering); 834 DUMP_EFFECT(PFM_DONOTHYPHEN, "Disable auto-hyphen:"); 835 DUMP_EFFECT(PFM_KEEP, "No page break in para:"); 836 DUMP_EFFECT(PFM_KEEPNEXT, "No page break in para & next:"); 837 DUMP_EFFECT(PFM_NOLINENUMBER, "No line number:"); 838 DUMP_EFFECT(PFM_NOWIDOWCONTROL, "No widow & orphan:"); 839 DUMP_EFFECT(PFM_PAGEBREAKBEFORE, "Page break before:"); 840 DUMP_EFFECT(PFM_RTLPARA, "RTL para:"); 841 DUMP_EFFECT(PFM_SIDEBYSIDE, "Side by side:"); 842 DUMP_EFFECT(PFM_TABLE, "Table:"); 843 DUMP(PFM_OFFSETINDENT, "Offset indent:", "%d", dxStartIndent); 844 DUMP(PFM_STARTINDENT, "Start indent:", "%d", dxStartIndent); 845 DUMP(PFM_RIGHTINDENT, "Right indent:", "%d", dxRightIndent); 846 DUMP(PFM_OFFSET, "Offset:", "%d", dxOffset); 847 if (pFmt->dwMask & PFM_ALIGNMENT) { 848 switch (pFmt->wAlignment) { 849 case PFA_LEFT : p += sprintf(p, "Alignment: left\n"); break; 850 case PFA_RIGHT : p += sprintf(p, "Alignment: right\n"); break; 851 case PFA_CENTER : p += sprintf(p, "Alignment: center\n"); break; 852 case PFA_JUSTIFY: p += sprintf(p, "Alignment: justify\n"); break; 853 default : p += sprintf(p, "Alignment: incorrect %d\n", pFmt->wAlignment); break; 854 } 855 } 856 else p += sprintf(p, "Alignment: N/A\n"); 857 DUMP(PFM_TABSTOPS, "Tab Stops:", "%d", cTabCount); 858 if (pFmt->dwMask & PFM_TABSTOPS) { 859 int i; 860 p += sprintf(p, "\t"); 861 for (i = 0; i < pFmt->cTabCount; i++) p += sprintf(p, "%x ", pFmt->rgxTabs[i]); 862 p += sprintf(p, "\n"); 863 } 864 DUMP(PFM_SPACEBEFORE, "Space Before:", "%d", dySpaceBefore); 865 DUMP(PFM_SPACEAFTER, "Space After:", "%d", dySpaceAfter); 866 DUMP(PFM_LINESPACING, "Line spacing:", "%d", dyLineSpacing); 867 DUMP(PFM_STYLE, "Text style:", "%d", sStyle); 868 DUMP(PFM_LINESPACING, "Line spacing rule:", "%u", bLineSpacingRule); 869 /* bOutlineLevel should be 0 */ 870 DUMP(PFM_SHADING, "Shading Weight:", "%u", wShadingWeight); 871 DUMP(PFM_SHADING, "Shading Style:", "%u", wShadingStyle); 872 DUMP(PFM_NUMBERINGSTART, "Numbering Start:", "%u", wNumberingStart); 873 DUMP(PFM_NUMBERINGSTYLE, "Numbering Style:", "0x%x", wNumberingStyle); 874 DUMP(PFM_NUMBERINGTAB, "Numbering Tab:", "%u", wNumberingStyle); 875 DUMP(PFM_BORDER, "Border Space:", "%u", wBorderSpace); 876 DUMP(PFM_BORDER, "Border Width:", "%u", wBorderWidth); 877 DUMP(PFM_BORDER, "Borders:", "%u", wBorders); 878 879 #undef DUMP 880 #undef DUMP_EFFECT 881 } 882 883 void 884 ME_GetSelectionParas(ME_TextEditor *editor, ME_DisplayItem **para, ME_DisplayItem **para_end) 885 { 886 ME_Cursor *pEndCursor = &editor->pCursors[1]; 887 888 *para = editor->pCursors[0].pPara; 889 *para_end = editor->pCursors[1].pPara; 890 if (*para == *para_end) 891 return; 892 893 if ((*para_end)->member.para.nCharOfs < (*para)->member.para.nCharOfs) { 894 ME_DisplayItem *tmp = *para; 895 896 *para = *para_end; 897 *para_end = tmp; 898 pEndCursor = &editor->pCursors[0]; 899 } 900 901 /* The paragraph at the end of a non-empty selection isn't included 902 * if the selection ends at the start of the paragraph. */ 903 if (!pEndCursor->pRun->member.run.nCharOfs && !pEndCursor->nOffset) 904 *para_end = (*para_end)->member.para.prev_para; 905 } 906 907 908 BOOL ME_SetSelectionParaFormat(ME_TextEditor *editor, const PARAFORMAT2 *pFmt) 909 { 910 ME_DisplayItem *para, *para_end; 911 912 ME_GetSelectionParas(editor, ¶, ¶_end); 913 914 do { 915 ME_SetParaFormat(editor, ¶->member.para, pFmt); 916 if (para == para_end) 917 break; 918 para = para->member.para.next_para; 919 } while(1); 920 921 return TRUE; 922 } 923 924 static void ME_GetParaFormat(ME_TextEditor *editor, 925 const ME_DisplayItem *para, 926 PARAFORMAT2 *pFmt) 927 { 928 UINT cbSize = pFmt->cbSize; 929 if (pFmt->cbSize >= sizeof(PARAFORMAT2)) { 930 *pFmt = para->member.para.fmt; 931 } else { 932 CopyMemory(pFmt, ¶->member.para.fmt, pFmt->cbSize); 933 pFmt->dwMask &= PFM_ALL; 934 } 935 pFmt->cbSize = cbSize; 936 } 937 938 void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt) 939 { 940 ME_DisplayItem *para, *para_end; 941 PARAFORMAT2 *curFmt; 942 943 if (pFmt->cbSize < sizeof(PARAFORMAT)) { 944 pFmt->dwMask = 0; 945 return; 946 } 947 948 ME_GetSelectionParas(editor, ¶, ¶_end); 949 950 ME_GetParaFormat(editor, para, pFmt); 951 952 /* Invalidate values that change across the selected paragraphs. */ 953 while (para != para_end) 954 { 955 para = para->member.para.next_para; 956 curFmt = ¶->member.para.fmt; 957 958 #define CHECK_FIELD(m, f) \ 959 if (pFmt->f != curFmt->f) pFmt->dwMask &= ~(m); 960 961 CHECK_FIELD(PFM_NUMBERING, wNumbering); 962 CHECK_FIELD(PFM_STARTINDENT, dxStartIndent); 963 CHECK_FIELD(PFM_RIGHTINDENT, dxRightIndent); 964 CHECK_FIELD(PFM_OFFSET, dxOffset); 965 CHECK_FIELD(PFM_ALIGNMENT, wAlignment); 966 if (pFmt->dwMask & PFM_TABSTOPS) { 967 if (pFmt->cTabCount != para->member.para.fmt.cTabCount || 968 memcmp(pFmt->rgxTabs, curFmt->rgxTabs, curFmt->cTabCount*sizeof(int))) 969 pFmt->dwMask &= ~PFM_TABSTOPS; 970 } 971 972 if (pFmt->dwMask >= sizeof(PARAFORMAT2)) 973 { 974 pFmt->dwMask &= ~((pFmt->wEffects ^ curFmt->wEffects) << 16); 975 CHECK_FIELD(PFM_SPACEBEFORE, dySpaceBefore); 976 CHECK_FIELD(PFM_SPACEAFTER, dySpaceAfter); 977 CHECK_FIELD(PFM_LINESPACING, dyLineSpacing); 978 CHECK_FIELD(PFM_STYLE, sStyle); 979 CHECK_FIELD(PFM_SPACEAFTER, bLineSpacingRule); 980 CHECK_FIELD(PFM_SHADING, wShadingWeight); 981 CHECK_FIELD(PFM_SHADING, wShadingStyle); 982 CHECK_FIELD(PFM_NUMBERINGSTART, wNumberingStart); 983 CHECK_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle); 984 CHECK_FIELD(PFM_NUMBERINGTAB, wNumberingTab); 985 CHECK_FIELD(PFM_BORDER, wBorderSpace); 986 CHECK_FIELD(PFM_BORDER, wBorderWidth); 987 CHECK_FIELD(PFM_BORDER, wBorders); 988 } 989 #undef CHECK_FIELD 990 } 991 } 992 993 void ME_SetDefaultParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt) 994 { 995 const PARAFORMAT2 *host_fmt; 996 HRESULT hr; 997 998 ZeroMemory(pFmt, sizeof(PARAFORMAT2)); 999 pFmt->cbSize = sizeof(PARAFORMAT2); 1000 pFmt->dwMask = PFM_ALL2; 1001 pFmt->wAlignment = PFA_LEFT; 1002 pFmt->sStyle = -1; 1003 pFmt->bOutlineLevel = TRUE; 1004 1005 hr = ITextHost_TxGetParaFormat( editor->texthost, (const PARAFORMAT **)&host_fmt ); 1006 if (SUCCEEDED(hr)) 1007 { 1008 /* Just use the alignment for now */ 1009 if (host_fmt->dwMask & PFM_ALIGNMENT) 1010 pFmt->wAlignment = host_fmt->wAlignment; 1011 ITextHost_OnTxParaFormatChange( editor->texthost, (PARAFORMAT *)pFmt ); 1012 } 1013 } 1014