1 /* 2 * RichEdit - operations on runs (diRun, rectangular pieces of paragraphs). 3 * Splitting/joining runs. Adjusting offsets after deleting/adding content. 4 * Character/pixel conversions. 5 * 6 * Copyright 2004 by Krzysztof Foltman 7 * Copyright 2006 by Phil Krylov 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 22 */ 23 24 #include "editor.h" 25 26 WINE_DEFAULT_DEBUG_CHANNEL(richedit); 27 WINE_DECLARE_DEBUG_CHANNEL(richedit_check); 28 WINE_DECLARE_DEBUG_CHANNEL(richedit_lists); 29 30 /****************************************************************************** 31 * ME_CanJoinRuns 32 * 33 * Returns 1 if two runs can be safely merged into one, 0 otherwise. 34 */ 35 int ME_CanJoinRuns(const ME_Run *run1, const ME_Run *run2) 36 { 37 if ((run1->nFlags | run2->nFlags) & MERF_NOJOIN) 38 return 0; 39 if (run1->style != run2->style) 40 return 0; 41 if ((run1->nFlags & MERF_STYLEFLAGS) != (run2->nFlags & MERF_STYLEFLAGS)) 42 return 0; 43 return 1; 44 } 45 46 void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift) 47 { 48 p = ME_FindItemFwd(p, diRunOrParagraphOrEnd); 49 assert(p); 50 ME_PropagateCharOffset(p, shift); 51 } 52 53 /****************************************************************************** 54 * ME_PropagateCharOffsets 55 * 56 * Shifts (increases or decreases) character offset (relative to beginning of 57 * the document) of the part of the text starting from given place. 58 */ 59 void ME_PropagateCharOffset(ME_DisplayItem *p, int shift) 60 { 61 /* Runs in one paragraph contain character offset relative to their owning 62 * paragraph. If we start the shifting from the run, we need to shift 63 * all the relative offsets until the end of the paragraph 64 */ 65 if (p->type == diRun) /* propagate in all runs in this para */ 66 { 67 TRACE("PropagateCharOffset(%s, %d)\n", debugstr_w(p->member.run.strText->szData), shift); 68 do { 69 p->member.run.nCharOfs += shift; 70 assert(p->member.run.nCharOfs >= 0); 71 p = ME_FindItemFwd(p, diRunOrParagraphOrEnd); 72 } while(p->type == diRun); 73 } 74 /* Runs in next paragraphs don't need their offsets updated, because they, 75 * again, those offsets are relative to their respective paragraphs. 76 * Instead of that, we're updating paragraphs' character offsets. 77 */ 78 if (p->type == diParagraph) /* propagate in all next paras */ 79 { 80 do { 81 p->member.para.nCharOfs += shift; 82 assert(p->member.para.nCharOfs >= 0); 83 p = p->member.para.next_para; 84 } while(p->type == diParagraph); 85 } 86 /* diTextEnd also has character offset in it, which makes finding text length 87 * easier. But it needs to be up to date first. 88 */ 89 if (p->type == diTextEnd) 90 { 91 p->member.para.nCharOfs += shift; 92 assert(p->member.para.nCharOfs >= 0); 93 } 94 } 95 96 /****************************************************************************** 97 * ME_CheckCharOffsets 98 * 99 * Checks if editor lists' validity and optionally dumps the document structure 100 */ 101 void ME_CheckCharOffsets(ME_TextEditor *editor) 102 { 103 ME_DisplayItem *p = editor->pBuffer->pFirst; 104 int ofs = 0, ofsp = 0; 105 if(TRACE_ON(richedit_lists)) 106 { 107 TRACE_(richedit_lists)("---\n"); 108 ME_DumpDocument(editor->pBuffer); 109 } 110 do { 111 p = ME_FindItemFwd(p, diRunOrParagraphOrEnd); 112 switch(p->type) { 113 case diTextEnd: 114 TRACE_(richedit_check)("tend, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs); 115 assert(ofsp+ofs == p->member.para.nCharOfs); 116 return; 117 case diParagraph: 118 TRACE_(richedit_check)("para, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs); 119 assert(ofsp+ofs == p->member.para.nCharOfs); 120 ofsp = p->member.para.nCharOfs; 121 ofs = 0; 122 break; 123 case diRun: 124 TRACE_(richedit_check)("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = \"%s\", flags=%08x, fx&mask = %08x\n", 125 p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs, 126 p->member.run.strText->nLen, debugstr_w(p->member.run.strText->szData), 127 p->member.run.nFlags, 128 p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects); 129 assert(ofs == p->member.run.nCharOfs); 130 assert(p->member.run.strText->nLen); 131 ofs += p->member.run.strText->nLen; 132 break; 133 case diCell: 134 TRACE_(richedit_check)("cell\n"); 135 break; 136 default: 137 assert(0); 138 } 139 } while(1); 140 } 141 142 /****************************************************************************** 143 * ME_CharOfsFromRunOfs 144 * 145 * Converts a character position relative to the start of the run, to a 146 * character position relative to the start of the document. 147 * Kind of a "local to global" offset conversion. 148 */ 149 int ME_CharOfsFromRunOfs(ME_TextEditor *editor, const ME_DisplayItem *pPara, 150 const ME_DisplayItem *pRun, int nOfs) 151 { 152 assert(pRun && pRun->type == diRun); 153 assert(pPara && pPara->type == diParagraph); 154 return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs + nOfs; 155 } 156 157 /****************************************************************************** 158 * ME_CursorFromCharOfs 159 * 160 * Converts a character offset (relative to the start of the document) to 161 * a cursor structure (which contains a run and a position relative to that 162 * run). 163 */ 164 void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor) 165 { 166 ME_RunOfsFromCharOfs(editor, nCharOfs, &pCursor->pPara, 167 &pCursor->pRun, &pCursor->nOffset); 168 } 169 170 /****************************************************************************** 171 * ME_RunOfsFromCharOfs 172 * 173 * Find a run and relative character offset given an absolute character offset 174 * (absolute offset being an offset relative to the start of the document). 175 * Kind of a "global to local" offset conversion. 176 */ 177 void ME_RunOfsFromCharOfs(ME_TextEditor *editor, 178 int nCharOfs, 179 ME_DisplayItem **ppPara, 180 ME_DisplayItem **ppRun, 181 int *pOfs) 182 { 183 ME_DisplayItem *item, *next_item; 184 185 nCharOfs = max(nCharOfs, 0); 186 nCharOfs = min(nCharOfs, ME_GetTextLength(editor)); 187 188 /* Find the paragraph at the offset. */ 189 next_item = editor->pBuffer->pFirst->member.para.next_para; 190 do { 191 item = next_item; 192 next_item = item->member.para.next_para; 193 } while (next_item->member.para.nCharOfs <= nCharOfs); 194 assert(item->type == diParagraph); 195 nCharOfs -= item->member.para.nCharOfs; 196 if (ppPara) *ppPara = item; 197 198 /* Find the run at the offset. */ 199 next_item = ME_FindItemFwd(item, diRun); 200 do { 201 item = next_item; 202 next_item = ME_FindItemFwd(item, diRunOrParagraphOrEnd); 203 } while (next_item->type == diRun && 204 next_item->member.run.nCharOfs <= nCharOfs); 205 assert(item->type == diRun); 206 nCharOfs -= item->member.run.nCharOfs; 207 208 if (ppRun) *ppRun = item; 209 if (pOfs) *pOfs = nCharOfs; 210 } 211 212 /****************************************************************************** 213 * ME_JoinRuns 214 * 215 * Merges two adjacent runs, the one given as a parameter and the next one. 216 */ 217 void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p) 218 { 219 ME_DisplayItem *pNext = p->next; 220 int i; 221 assert(p->type == diRun && pNext->type == diRun); 222 assert(p->member.run.nCharOfs != -1); 223 ME_GetParagraph(p)->member.para.nFlags |= MEPF_REWRAP; 224 225 /* Update all cursors so that they don't contain the soon deleted run */ 226 for (i=0; i<editor->nCursors; i++) { 227 if (editor->pCursors[i].pRun == pNext) { 228 editor->pCursors[i].pRun = p; 229 editor->pCursors[i].nOffset += p->member.run.strText->nLen; 230 } 231 } 232 233 ME_AppendString(p->member.run.strText, pNext->member.run.strText); 234 ME_Remove(pNext); 235 ME_DestroyDisplayItem(pNext); 236 ME_UpdateRunFlags(editor, &p->member.run); 237 if(TRACE_ON(richedit)) 238 { 239 TRACE("Before check after join\n"); 240 ME_CheckCharOffsets(editor); 241 TRACE("After check after join\n"); 242 } 243 } 244 245 /****************************************************************************** 246 * ME_SplitRun 247 * 248 * Splits a run into two in a given place. It also updates the screen position 249 * and size (extent) of the newly generated runs. 250 */ 251 ME_DisplayItem *ME_SplitRun(ME_WrapContext *wc, ME_DisplayItem *item, int nVChar) 252 { 253 ME_TextEditor *editor = wc->context->editor; 254 ME_DisplayItem *item2 = NULL; 255 ME_Run *run, *run2; 256 ME_Paragraph *para = &ME_GetParagraph(item)->member.para; 257 258 assert(item->member.run.nCharOfs != -1); 259 if(TRACE_ON(richedit)) 260 { 261 TRACE("Before check before split\n"); 262 ME_CheckCharOffsets(editor); 263 TRACE("After check before split\n"); 264 } 265 266 run = &item->member.run; 267 268 TRACE("Before split: %s(%d, %d)\n", debugstr_w(run->strText->szData), 269 run->pt.x, run->pt.y); 270 271 item2 = ME_SplitRunSimple(editor, item, nVChar); 272 273 run2 = &item2->member.run; 274 275 ME_CalcRunExtent(wc->context, para, wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, run); 276 ME_CalcRunExtent(wc->context, para, wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, run2); 277 278 run2->pt.x = run->pt.x+run->nWidth; 279 run2->pt.y = run->pt.y; 280 281 if(TRACE_ON(richedit)) 282 { 283 TRACE("Before check after split\n"); 284 ME_CheckCharOffsets(editor); 285 TRACE("After check after split\n"); 286 TRACE("After split: %s(%d, %d), %s(%d, %d)\n", 287 debugstr_w(run->strText->szData), run->pt.x, run->pt.y, 288 debugstr_w(run2->strText->szData), run2->pt.x, run2->pt.y); 289 } 290 291 return item2; 292 } 293 294 /****************************************************************************** 295 * ME_SplitRunSimple 296 * 297 * Does the most basic job of splitting a run into two - it does not 298 * update the positions and extents. 299 */ 300 ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nVChar) 301 { 302 ME_Run *run = &item->member.run; 303 ME_DisplayItem *item2; 304 ME_Run *run2; 305 int i; 306 assert(nVChar > 0 && nVChar < run->strText->nLen); 307 assert(item->type == diRun); 308 assert(!(item->member.run.nFlags & MERF_NONTEXT)); 309 assert(item->member.run.nCharOfs != -1); 310 311 item2 = ME_MakeRun(run->style, 312 ME_VSplitString(run->strText, nVChar), run->nFlags&MERF_SPLITMASK); 313 314 item2->member.run.nCharOfs = item->member.run.nCharOfs + nVChar; 315 316 run2 = &item2->member.run; 317 ME_InsertBefore(item->next, item2); 318 319 ME_UpdateRunFlags(editor, run); 320 ME_UpdateRunFlags(editor, run2); 321 for (i=0; i<editor->nCursors; i++) { 322 if (editor->pCursors[i].pRun == item && 323 editor->pCursors[i].nOffset >= nVChar) { 324 assert(item2->type == diRun); 325 editor->pCursors[i].pRun = item2; 326 editor->pCursors[i].nOffset -= nVChar; 327 } 328 } 329 ME_GetParagraph(item)->member.para.nFlags |= MEPF_REWRAP; 330 return item2; 331 } 332 333 /****************************************************************************** 334 * ME_MakeRun 335 * 336 * A helper function to create run structures quickly. 337 */ 338 ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags) 339 { 340 ME_DisplayItem *item = ME_MakeDI(diRun); 341 item->member.run.style = s; 342 item->member.run.ole_obj = NULL; 343 item->member.run.strText = strData; 344 item->member.run.nFlags = nFlags; 345 item->member.run.nCharOfs = -1; 346 ME_AddRefStyle(s); 347 return item; 348 } 349 350 /****************************************************************************** 351 * ME_InsertRunAtCursor 352 * 353 * Inserts a new run with given style, flags and content at a given position, 354 * which is passed as a cursor structure (which consists of a run and 355 * a run-relative character offset). 356 */ 357 ME_DisplayItem * 358 ME_InsertRunAtCursor(ME_TextEditor *editor, ME_Cursor *cursor, ME_Style *style, 359 const WCHAR *str, int len, int flags) 360 { 361 ME_DisplayItem *pDI; 362 ME_UndoItem *pUI; 363 364 if (cursor->nOffset) { 365 /* We're inserting at the middle of the existing run, which means that 366 * that run must be split. It isn't always necessary, but */ 367 cursor->pRun = ME_SplitRunSimple(editor, cursor->pRun, cursor->nOffset); 368 cursor->nOffset = 0; 369 } 370 371 pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL); 372 if (pUI) { 373 pUI->nStart = cursor->pPara->member.para.nCharOfs 374 + cursor->pRun->member.run.nCharOfs; 375 pUI->nLen = len; 376 } 377 378 pDI = ME_MakeRun(style, ME_MakeStringN(str, len), flags); 379 pDI->member.run.nCharOfs = cursor->pRun->member.run.nCharOfs; 380 ME_InsertBefore(cursor->pRun, pDI); 381 TRACE("Shift length:%d\n", len); 382 ME_PropagateCharOffset(cursor->pRun, len); 383 cursor->pPara->member.para.nFlags |= MEPF_REWRAP; 384 return pDI; 385 } 386 387 /****************************************************************************** 388 * ME_UpdateRunFlags 389 * 390 * Determine some of run attributes given its content (style, text content). 391 * Some flags cannot be determined by this function (MERF_GRAPHICS, 392 * MERF_ENDPARA) 393 */ 394 void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run) 395 { 396 ME_String *strText = run->strText; 397 assert(run->nCharOfs >= 0); 398 399 if (RUN_IS_HIDDEN(run) || run->nFlags & MERF_TABLESTART) 400 run->nFlags |= MERF_HIDDEN; 401 else 402 run->nFlags &= ~MERF_HIDDEN; 403 404 if (ME_IsSplitable(strText)) 405 run->nFlags |= MERF_SPLITTABLE; 406 else 407 run->nFlags &= ~MERF_SPLITTABLE; 408 409 if (!(run->nFlags & MERF_NOTEXT)) { 410 if (ME_IsWhitespaces(strText)) 411 run->nFlags |= MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE; 412 else 413 { 414 run->nFlags &= ~MERF_WHITESPACE; 415 416 if (ME_IsWSpace(strText->szData[0])) 417 run->nFlags |= MERF_STARTWHITE; 418 else 419 run->nFlags &= ~MERF_STARTWHITE; 420 421 if (ME_IsWSpace(strText->szData[strText->nLen - 1])) 422 run->nFlags |= MERF_ENDWHITE; 423 else 424 run->nFlags &= ~MERF_ENDWHITE; 425 } 426 } 427 else 428 run->nFlags &= ~(MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE); 429 } 430 431 /****************************************************************************** 432 * ME_CharFromPoint 433 * 434 * Returns a character position inside the run given a run-relative 435 * pixel horizontal position. This version rounds left (ie. if the second 436 * character is at pixel position 8, then for cx=0..7 it returns 0). 437 */ 438 int ME_CharFromPoint(ME_Context *c, int cx, ME_Run *run) 439 { 440 int fit = 0; 441 HGDIOBJ hOldFont; 442 SIZE sz; 443 if (!run->strText->nLen) 444 return 0; 445 446 if (run->nFlags & MERF_TAB || 447 (run->nFlags & (MERF_ENDCELL|MERF_ENDPARA)) == MERF_ENDCELL) 448 { 449 if (cx < run->nWidth/2) 450 return 0; 451 return 1; 452 } 453 if (run->nFlags & MERF_GRAPHICS) 454 { 455 SIZE sz; 456 ME_GetOLEObjectSize(c, run, &sz); 457 if (cx < sz.cx) 458 return 0; 459 return 1; 460 } 461 hOldFont = ME_SelectStyleFont(c, run->style); 462 463 if (c->editor->cPasswordMask) 464 { 465 ME_String *strMasked = ME_MakeStringR(c->editor->cPasswordMask, run->strText->nLen); 466 GetTextExtentExPointW(c->hDC, strMasked->szData, run->strText->nLen, 467 cx, &fit, NULL, &sz); 468 ME_DestroyString(strMasked); 469 } 470 else 471 { 472 GetTextExtentExPointW(c->hDC, run->strText->szData, run->strText->nLen, 473 cx, &fit, NULL, &sz); 474 } 475 476 ME_UnselectStyleFont(c, run->style, hOldFont); 477 478 return fit; 479 } 480 481 /****************************************************************************** 482 * ME_CharFromPointCursor 483 * 484 * Returns a character position inside the run given a run-relative 485 * pixel horizontal position. This version rounds to the nearest character edge 486 * (ie. if the second character is at pixel position 8, then for cx=0..3 487 * it returns 0, and for cx=4..7 it returns 1). 488 * 489 * It is used for mouse click handling, for better usability (and compatibility 490 * with the native control). 491 */ 492 int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run) 493 { 494 ME_String *strRunText; 495 /* This could point to either the run's real text, or it's masked form in a password control */ 496 497 int fit = 0; 498 ME_Context c; 499 HGDIOBJ hOldFont; 500 SIZE sz, sz2, sz3; 501 if (!run->strText->nLen) 502 return 0; 503 504 if (run->nFlags & (MERF_TAB | MERF_ENDCELL)) 505 { 506 if (cx < run->nWidth/2) 507 return 0; 508 return 1; 509 } 510 ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost)); 511 if (run->nFlags & MERF_GRAPHICS) 512 { 513 SIZE sz; 514 ME_GetOLEObjectSize(&c, run, &sz); 515 ME_DestroyContext(&c); 516 if (cx < sz.cx/2) 517 return 0; 518 return 1; 519 } 520 521 if (editor->cPasswordMask) 522 strRunText = ME_MakeStringR(editor->cPasswordMask, run->strText->nLen); 523 else 524 strRunText = run->strText; 525 526 hOldFont = ME_SelectStyleFont(&c, run->style); 527 GetTextExtentExPointW(c.hDC, strRunText->szData, strRunText->nLen, 528 cx, &fit, NULL, &sz); 529 if (fit != strRunText->nLen) 530 { 531 GetTextExtentPoint32W(c.hDC, strRunText->szData, fit, &sz2); 532 GetTextExtentPoint32W(c.hDC, strRunText->szData, fit + 1, &sz3); 533 if (cx >= (sz2.cx+sz3.cx)/2) 534 fit = fit + 1; 535 } 536 537 if (editor->cPasswordMask) 538 ME_DestroyString(strRunText); 539 540 ME_UnselectStyleFont(&c, run->style, hOldFont); 541 ME_DestroyContext(&c); 542 return fit; 543 } 544 545 /****************************************************************************** 546 * ME_GetTextExtent 547 * 548 * Finds a width and a height of the text using a specified style 549 */ 550 static void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s, SIZE *size) 551 { 552 HGDIOBJ hOldFont; 553 if (c->hDC) { 554 hOldFont = ME_SelectStyleFont(c, s); 555 GetTextExtentPoint32W(c->hDC, szText, nChars, size); 556 ME_UnselectStyleFont(c, s, hOldFont); 557 } else { 558 size->cx = 0; 559 size->cy = 0; 560 } 561 } 562 563 /****************************************************************************** 564 * ME_PointFromChar 565 * 566 * Returns a run-relative pixel position given a run-relative character 567 * position (character offset) 568 */ 569 int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset) 570 { 571 SIZE size; 572 ME_Context c; 573 ME_String *strRunText; 574 /* This could point to either the run's real text, or it's masked form in a password control */ 575 576 ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost)); 577 if (pRun->nFlags & MERF_GRAPHICS) 578 { 579 if (nOffset) 580 ME_GetOLEObjectSize(&c, pRun, &size); 581 ME_DestroyContext(&c); 582 return nOffset != 0; 583 } else if (pRun->nFlags & MERF_ENDPARA) { 584 nOffset = 0; 585 } 586 587 if (editor->cPasswordMask) 588 strRunText = ME_MakeStringR(editor->cPasswordMask, pRun->strText->nLen); 589 else 590 strRunText = pRun->strText; 591 592 ME_GetTextExtent(&c, strRunText->szData, nOffset, pRun->style, &size); 593 ME_DestroyContext(&c); 594 if (editor->cPasswordMask) 595 ME_DestroyString(strRunText); 596 return size.cx; 597 } 598 599 /****************************************************************************** 600 * ME_GetRunSizeCommon 601 * 602 * Finds width, height, ascent and descent of a run, up to given character 603 * (nLen). 604 */ 605 static SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, int nLen, 606 int startx, int *pAscent, int *pDescent) 607 { 608 SIZE size; 609 int nMaxLen = run->strText->nLen; 610 611 if (nLen>nMaxLen) 612 nLen = nMaxLen; 613 614 /* FIXME the following call also ensures that TEXTMETRIC structure is filled 615 * this is wasteful for MERF_NONTEXT runs, but that shouldn't matter 616 * in practice 617 */ 618 619 if (c->editor->cPasswordMask) 620 { 621 ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,nLen); 622 ME_GetTextExtent(c, szMasked->szData, nLen,run->style, &size); 623 ME_DestroyString(szMasked); 624 } 625 else 626 { 627 ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size); 628 } 629 *pAscent = run->style->tm.tmAscent; 630 *pDescent = run->style->tm.tmDescent; 631 size.cy = *pAscent + *pDescent; 632 633 if (run->nFlags & MERF_TAB) 634 { 635 int pos = 0, i = 0, ppos, shift = 0; 636 PARAFORMAT2 *pFmt = para->pFmt; 637 638 if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ 639 pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) 640 /* The horizontal gap shifts the tab positions to leave the gap. */ 641 shift = pFmt->dxOffset * 2; 642 do { 643 if (i < pFmt->cTabCount) 644 { 645 /* Only one side of the horizontal gap is needed at the end of 646 * the table row. */ 647 if (i == pFmt->cTabCount -1) 648 shift = shift >> 1; 649 pos = shift + (pFmt->rgxTabs[i]&0x00FFFFFF); 650 i++; 651 } 652 else 653 { 654 pos += lDefaultTab - (pos % lDefaultTab); 655 } 656 ppos = ME_twips2pointsX(c, pos); 657 if (ppos > startx + run->pt.x) { 658 size.cx = ppos - startx - run->pt.x; 659 break; 660 } 661 } while(1); 662 size.cy = *pAscent + *pDescent; 663 return size; 664 } 665 if (run->nFlags & MERF_GRAPHICS) 666 { 667 ME_GetOLEObjectSize(c, run, &size); 668 if (size.cy > *pAscent) 669 *pAscent = size.cy; 670 /* descent is unchanged */ 671 return size; 672 } 673 return size; 674 } 675 676 /****************************************************************************** 677 * ME_GetRunSize 678 * 679 * Finds width and height (but not ascent and descent) of a part of the run 680 * up to given character. 681 */ 682 SIZE ME_GetRunSize(ME_Context *c, const ME_Paragraph *para, 683 ME_Run *run, int nLen, int startx) 684 { 685 int asc, desc; 686 return ME_GetRunSizeCommon(c, para, run, nLen, startx, &asc, &desc); 687 } 688 689 /****************************************************************************** 690 * ME_CalcRunExtent 691 * 692 * Updates the size of the run (fills width, ascent and descent). The height 693 * is calculated based on whole row's ascent and descent anyway, so no need 694 * to use it here. 695 */ 696 void ME_CalcRunExtent(ME_Context *c, const ME_Paragraph *para, int startx, ME_Run *run) 697 { 698 if (run->nFlags & MERF_HIDDEN) 699 run->nWidth = 0; 700 else 701 { 702 int nEnd = run->strText->nLen; 703 SIZE size = ME_GetRunSizeCommon(c, para, run, nEnd, startx, 704 &run->nAscent, &run->nDescent); 705 run->nWidth = size.cx; 706 if (!size.cx) 707 WARN("size.cx == 0\n"); 708 } 709 } 710 711 /****************************************************************************** 712 * ME_SetSelectionCharFormat 713 * 714 * Applies a style change, either to a current selection, or to insert cursor 715 * (ie. the style next typed characters will use). 716 */ 717 void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt) 718 { 719 if (!ME_IsSelection(editor)) 720 { 721 ME_Style *s; 722 if (!editor->pBuffer->pCharStyle) 723 editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0); 724 s = ME_ApplyStyle(editor->pBuffer->pCharStyle, pFmt); 725 ME_ReleaseStyle(editor->pBuffer->pCharStyle); 726 editor->pBuffer->pCharStyle = s; 727 } else { 728 ME_Cursor *from, *to; 729 ME_GetSelection(editor, &from, &to); 730 ME_SetCharFormat(editor, from, to, pFmt); 731 } 732 } 733 734 /****************************************************************************** 735 * ME_SetCharFormat 736 * 737 * Applies a style change to the specified part of the text 738 * 739 * The start and end cursors specify the part of the text. These cursors will 740 * be updated to stay valid, but this function may invalidate other 741 * non-selection cursors. The end cursor may be NULL to specify all the text 742 * following the start cursor. 743 * 744 * If no text is selected, then nothing is done. 745 */ 746 void ME_SetCharFormat(ME_TextEditor *editor, ME_Cursor *start, ME_Cursor *end, CHARFORMAT2W *pFmt) 747 { 748 ME_DisplayItem *para; 749 ME_DisplayItem *run; 750 ME_DisplayItem *end_run = NULL; 751 752 if (end && start->pRun == end->pRun && start->nOffset == end->nOffset) 753 return; 754 755 if (start->nOffset) 756 { 757 /* SplitRunSimple may or may not update the cursors, depending on whether they 758 * are selection cursors, but we need to make sure they are valid. */ 759 ME_DisplayItem *split_run = start->pRun; 760 int split_offset = start->nOffset; 761 start->pRun = ME_SplitRunSimple(editor, split_run, split_offset); 762 start->nOffset = 0; 763 if (end && end->pRun == split_run) 764 { 765 end->pRun = start->pRun; 766 end->nOffset -= split_offset; 767 } 768 } 769 770 if (end && end->nOffset) 771 { 772 end_run = end->pRun = ME_SplitRunSimple(editor, end->pRun, end->nOffset); 773 end->nOffset = 0; 774 } else if (end) { 775 end_run = end->pRun; 776 } 777 778 run = start->pRun; 779 para = start->pPara; 780 para->member.para.nFlags |= MEPF_REWRAP; 781 782 while(run != end_run) 783 { 784 ME_UndoItem *undo = NULL; 785 ME_Style *new_style = ME_ApplyStyle(run->member.run.style, pFmt); 786 /* ME_DumpStyle(new_style); */ 787 undo = ME_AddUndoItem(editor, diUndoSetCharFormat, NULL); 788 if (undo) { 789 undo->nStart = run->member.run.nCharOfs+para->member.para.nCharOfs; 790 undo->nLen = run->member.run.strText->nLen; 791 undo->di.member.ustyle = run->member.run.style; 792 /* we'd have to addref undo...ustyle and release tmp...style 793 but they'd cancel each other out so we can do nothing instead */ 794 } 795 else 796 ME_ReleaseStyle(run->member.run.style); 797 run->member.run.style = new_style; 798 run = ME_FindItemFwd(run, diRunOrParagraph); 799 if (run && run->type == diParagraph) 800 { 801 para = run; 802 run = ME_FindItemFwd(run, diRun); 803 if (run != end_run) 804 para->member.para.nFlags |= MEPF_REWRAP; 805 } 806 } 807 } 808 809 /****************************************************************************** 810 * ME_SetDefaultCharFormat 811 * 812 * Applies a style change to the default character style. 813 */ 814 void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod) 815 { 816 ME_Style *style; 817 818 assert(mod->cbSize == sizeof(CHARFORMAT2W)); 819 style = ME_ApplyStyle(editor->pBuffer->pDefaultStyle, mod); 820 editor->pBuffer->pDefaultStyle->fmt = style->fmt; 821 editor->pBuffer->pDefaultStyle->tm = style->tm; 822 ME_ReleaseStyle(style); 823 ME_MarkAllForWrapping(editor); 824 /* pcf = editor->pBuffer->pDefaultStyle->fmt; */ 825 } 826 827 static void ME_GetRunCharFormat(ME_TextEditor *editor, ME_DisplayItem *run, CHARFORMAT2W *pFmt) 828 { 829 ME_CopyCharFormat(pFmt, &run->member.run.style->fmt); 830 if ((pFmt->dwMask & CFM_UNDERLINETYPE) && (pFmt->bUnderlineType == CFU_CF1UNDERLINE)) 831 { 832 pFmt->dwMask |= CFM_UNDERLINE; 833 pFmt->dwEffects |= CFE_UNDERLINE; 834 } 835 if ((pFmt->dwMask & CFM_UNDERLINETYPE) && (pFmt->bUnderlineType == CFU_UNDERLINENONE)) 836 { 837 pFmt->dwMask |= CFM_UNDERLINE; 838 pFmt->dwEffects &= ~CFE_UNDERLINE; 839 } 840 } 841 842 /****************************************************************************** 843 * ME_GetDefaultCharFormat 844 * 845 * Retrieves the current default character style (the one applied where no 846 * other style was applied) . 847 */ 848 void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt) 849 { 850 ME_CopyCharFormat(pFmt, &editor->pBuffer->pDefaultStyle->fmt); 851 } 852 853 /****************************************************************************** 854 * ME_GetSelectionCharFormat 855 * 856 * If selection exists, it returns all style elements that are set consistently 857 * in the whole selection. If not, it just returns the current style. 858 */ 859 void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt) 860 { 861 ME_Cursor *from, *to; 862 if (!ME_IsSelection(editor) && editor->pBuffer->pCharStyle) 863 { 864 ME_CopyCharFormat(pFmt, &editor->pBuffer->pCharStyle->fmt); 865 return; 866 } 867 ME_GetSelection(editor, &from, &to); 868 ME_GetCharFormat(editor, from, to, pFmt); 869 } 870 871 /****************************************************************************** 872 * ME_GetCharFormat 873 * 874 * Returns the style consisting of those attributes which are consistently set 875 * in the whole character range. 876 */ 877 void ME_GetCharFormat(ME_TextEditor *editor, const ME_Cursor *from, 878 const ME_Cursor *to, CHARFORMAT2W *pFmt) 879 { 880 ME_DisplayItem *run, *run_end; 881 CHARFORMAT2W tmp; 882 883 run = from->pRun; 884 /* special case - if selection is empty, take previous char's formatting */ 885 if (from->pRun == to->pRun && from->nOffset == to->nOffset) 886 { 887 if (!from->nOffset) 888 { 889 ME_DisplayItem *tmp_run = ME_FindItemBack(run, diRunOrParagraph); 890 if (tmp_run->type == diRun) { 891 ME_GetRunCharFormat(editor, tmp_run, pFmt); 892 return; 893 } 894 } 895 ME_GetRunCharFormat(editor, run, pFmt); 896 return; 897 } 898 899 run_end = to->pRun; 900 if (!to->nOffset) 901 run_end = ME_FindItemBack(run_end, diRun); 902 903 ME_GetRunCharFormat(editor, run, pFmt); 904 905 if (run == run_end) return; 906 907 do { 908 /* FIXME add more style feature comparisons */ 909 int nAttribs = CFM_SIZE | CFM_FACE | CFM_COLOR | CFM_UNDERLINETYPE; 910 int nEffects = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_PROTECTED | CFM_LINK | CFM_SUPERSCRIPT; 911 912 run = ME_FindItemFwd(run, diRun); 913 914 ZeroMemory(&tmp, sizeof(tmp)); 915 tmp.cbSize = sizeof(tmp); 916 ME_GetRunCharFormat(editor, run, &tmp); 917 918 assert((tmp.dwMask & nAttribs) == nAttribs); 919 /* reset flags that differ */ 920 921 if (pFmt->yHeight != tmp.yHeight) 922 pFmt->dwMask &= ~CFM_SIZE; 923 if (pFmt->dwMask & CFM_FACE) 924 { 925 if (!(tmp.dwMask & CFM_FACE)) 926 pFmt->dwMask &= ~CFM_FACE; 927 else if (lstrcmpW(pFmt->szFaceName, tmp.szFaceName) || 928 pFmt->bPitchAndFamily != tmp.bPitchAndFamily) 929 pFmt->dwMask &= ~CFM_FACE; 930 } 931 if (pFmt->yHeight != tmp.yHeight) 932 pFmt->dwMask &= ~CFM_SIZE; 933 if (pFmt->bUnderlineType != tmp.bUnderlineType) 934 pFmt->dwMask &= ~CFM_UNDERLINETYPE; 935 if (pFmt->dwMask & CFM_COLOR) 936 { 937 if (!((pFmt->dwEffects&CFE_AUTOCOLOR) & (tmp.dwEffects&CFE_AUTOCOLOR))) 938 { 939 if (pFmt->crTextColor != tmp.crTextColor) 940 pFmt->dwMask &= ~CFM_COLOR; 941 } 942 } 943 944 pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & nEffects); 945 pFmt->dwEffects = tmp.dwEffects; 946 947 } while(run != run_end); 948 } 949