1 /* 2 * RichEdit functions dealing with on tables 3 * 4 * Copyright 2008 by Dylan Smith 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 /* 22 * The implementation of tables differs greatly between version 3.0 23 * (in riched20.dll) and version 4.1 (in msftedit.dll) of richedit controls. 24 * Currently Wine is not distinguishing between version 3.0 and version 4.1, 25 * so v4.1 is assumed unless v1.0 is being emulated (i.e. riched32.dll is used). 26 * If this lack of distinction causes a bug in a Windows application, then Wine 27 * will need to start making this distinction. 28 * 29 * Richedit version 1.0 - 3.0: 30 * Tables are implemented in these versions using tabs at the end of cells, 31 * and tab stops to position the cells. The paragraph format flag PFE_TABLE 32 * will indicate that the paragraph is a table row. Note that in this 33 * implementation there is one paragraph per table row. 34 * 35 * Richedit version 4.1: 36 * Tables are implemented such that cells can contain multiple paragraphs, 37 * each with its own paragraph format, and cells may even contain tables 38 * nested within the cell. 39 * 40 * There is also a paragraph at the start of each table row that contains 41 * the rows paragraph format (e.g. to change the row alignment to row), and a 42 * paragraph at the end of the table row with the PFE_TABLEROWDELIMITER flag 43 * set. The paragraphs at the start and end of the table row should always be 44 * empty, but should have a length of 2. 45 * 46 * Wine implements this using display items (ME_DisplayItem) with a type of 47 * diCell. These cell display items store the cell properties, and are 48 * inserted into the editors linked list before each cell, and at the end of 49 * the last cell. The cell display item for a cell comes before the paragraphs 50 * for the cell, but the last cell display item refers to no cell, so it is 51 * just a delimiter. 52 */ 53 54 #include "editor.h" 55 #include "rtf.h" 56 57 WINE_DEFAULT_DEBUG_CHANNEL(richedit_lists); 58 59 static ME_DisplayItem* ME_InsertEndParaFromCursor(ME_TextEditor *editor, 60 int nCursor, 61 const WCHAR *eol_str, int eol_len, 62 int paraFlags) 63 { 64 ME_Style *pStyle = ME_GetInsertStyle(editor, nCursor); 65 ME_DisplayItem *tp; 66 ME_Cursor* cursor = &editor->pCursors[nCursor]; 67 if (cursor->nOffset) 68 ME_SplitRunSimple(editor, cursor); 69 70 tp = ME_SplitParagraph(editor, cursor->pRun, pStyle, eol_str, eol_len, paraFlags); 71 ME_ReleaseStyle(pStyle); 72 cursor->pPara = tp; 73 cursor->pRun = ME_FindItemFwd(tp, diRun); 74 return tp; 75 } 76 77 ME_DisplayItem* ME_InsertTableRowStartFromCursor(ME_TextEditor *editor) 78 { 79 ME_DisplayItem *para; 80 WCHAR cr_lf[] = {'\r', '\n', 0}; 81 para = ME_InsertEndParaFromCursor(editor, 0, cr_lf, 2, MEPF_ROWSTART); 82 return para->member.para.prev_para; 83 } 84 85 ME_DisplayItem* ME_InsertTableRowStartAtParagraph(ME_TextEditor *editor, 86 ME_DisplayItem *para) 87 { 88 ME_DisplayItem *prev_para, *end_para; 89 ME_Cursor savedCursor = editor->pCursors[0]; 90 ME_DisplayItem *startRowPara; 91 editor->pCursors[0].pPara = para; 92 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 93 editor->pCursors[0].nOffset = 0; 94 editor->pCursors[1] = editor->pCursors[0]; 95 startRowPara = ME_InsertTableRowStartFromCursor(editor); 96 savedCursor.pPara = ME_GetParagraph(savedCursor.pRun); 97 editor->pCursors[0] = savedCursor; 98 editor->pCursors[1] = editor->pCursors[0]; 99 100 end_para = editor->pCursors[0].pPara->member.para.next_para; 101 prev_para = startRowPara->member.para.next_para; 102 para = prev_para->member.para.next_para; 103 while (para != end_para) 104 { 105 para->member.para.pCell = prev_para->member.para.pCell; 106 para->member.para.nFlags |= MEPF_CELL; 107 para->member.para.nFlags &= ~(MEPF_ROWSTART|MEPF_ROWEND); 108 para->member.para.fmt.dwMask |= PFM_TABLE|PFM_TABLEROWDELIMITER; 109 para->member.para.fmt.wEffects |= PFE_TABLE; 110 para->member.para.fmt.wEffects &= ~PFE_TABLEROWDELIMITER; 111 prev_para = para; 112 para = para->member.para.next_para; 113 } 114 return startRowPara; 115 } 116 117 /* Inserts a diCell and starts a new paragraph for the next cell. 118 * 119 * Returns the first paragraph of the new cell. */ 120 ME_DisplayItem* ME_InsertTableCellFromCursor(ME_TextEditor *editor) 121 { 122 ME_DisplayItem *para; 123 WCHAR tab = '\t'; 124 para = ME_InsertEndParaFromCursor(editor, 0, &tab, 1, MEPF_CELL); 125 return para; 126 } 127 128 ME_DisplayItem* ME_InsertTableRowEndFromCursor(ME_TextEditor *editor) 129 { 130 ME_DisplayItem *para; 131 WCHAR cr_lf[] = {'\r', '\n', 0}; 132 para = ME_InsertEndParaFromCursor(editor, 0, cr_lf, 2, MEPF_ROWEND); 133 return para->member.para.prev_para; 134 } 135 136 ME_DisplayItem* ME_GetTableRowEnd(ME_DisplayItem *para) 137 { 138 ME_DisplayItem *cell; 139 assert(para); 140 if (para->member.para.nFlags & MEPF_ROWEND) 141 return para; 142 if (para->member.para.nFlags & MEPF_ROWSTART) 143 para = para->member.para.next_para; 144 cell = para->member.para.pCell; 145 assert(cell && cell->type == diCell); 146 while (cell->member.cell.next_cell) 147 cell = cell->member.cell.next_cell; 148 149 para = ME_FindItemFwd(cell, diParagraph); 150 assert(para && para->member.para.nFlags & MEPF_ROWEND); 151 return para; 152 } 153 154 ME_DisplayItem* ME_GetTableRowStart(ME_DisplayItem *para) 155 { 156 ME_DisplayItem *cell; 157 assert(para); 158 if (para->member.para.nFlags & MEPF_ROWSTART) 159 return para; 160 if (para->member.para.nFlags & MEPF_ROWEND) 161 para = para->member.para.prev_para; 162 cell = para->member.para.pCell; 163 assert(cell && cell->type == diCell); 164 while (cell->member.cell.prev_cell) 165 cell = cell->member.cell.prev_cell; 166 167 para = ME_FindItemBack(cell, diParagraph); 168 assert(para && para->member.para.nFlags & MEPF_ROWSTART); 169 return para; 170 } 171 172 ME_DisplayItem* ME_GetOuterParagraph(ME_DisplayItem *para) 173 { 174 if (para->member.para.nFlags & MEPF_ROWEND) 175 para = para->member.para.prev_para; 176 while (para->member.para.pCell) 177 { 178 para = ME_GetTableRowStart(para); 179 if (!para->member.para.pCell) 180 break; 181 para = ME_FindItemBack(para->member.para.pCell, diParagraph); 182 } 183 return para; 184 } 185 186 /* Make a bunch of assertions to make sure tables haven't been corrupted. 187 * 188 * These invariants may not hold true in the middle of streaming in rich text 189 * or during an undo and redo of streaming in rich text. It should be safe to 190 * call this method after an event is processed. 191 */ 192 void ME_CheckTablesForCorruption(ME_TextEditor *editor) 193 { 194 if(TRACE_ON(richedit_lists)) 195 { 196 TRACE("---\n"); 197 ME_DumpDocument(editor->pBuffer); 198 } 199 #ifndef NDEBUG 200 { 201 ME_DisplayItem *p, *pPrev; 202 pPrev = editor->pBuffer->pFirst; 203 p = pPrev->next; 204 if (!editor->bEmulateVersion10) /* v4.1 */ 205 { 206 while (p->type == diParagraph) 207 { 208 assert(p->member.para.fmt.dwMask & PFM_TABLE); 209 assert(p->member.para.fmt.dwMask & PFM_TABLEROWDELIMITER); 210 if (p->member.para.pCell) 211 { 212 assert(p->member.para.nFlags & MEPF_CELL); 213 assert(p->member.para.fmt.wEffects & PFE_TABLE); 214 } 215 if (p->member.para.pCell != pPrev->member.para.pCell) 216 { 217 /* There must be a diCell in between the paragraphs if pCell changes. */ 218 ME_DisplayItem *pCell = ME_FindItemBack(p, diCell); 219 assert(pCell); 220 assert(ME_FindItemBack(p, diRun) == ME_FindItemBack(pCell, diRun)); 221 } 222 if (p->member.para.nFlags & MEPF_ROWEND) 223 { 224 /* ROWEND must come after a cell. */ 225 assert(pPrev->member.para.pCell); 226 assert(p->member.para.pCell 227 == pPrev->member.para.pCell->member.cell.parent_cell); 228 assert(p->member.para.fmt.wEffects & PFE_TABLEROWDELIMITER); 229 } 230 else if (p->member.para.pCell) 231 { 232 assert(!(p->member.para.fmt.wEffects & PFE_TABLEROWDELIMITER)); 233 assert(pPrev->member.para.pCell || 234 pPrev->member.para.nFlags & MEPF_ROWSTART); 235 if (pPrev->member.para.pCell && 236 !(pPrev->member.para.nFlags & MEPF_ROWSTART)) 237 { 238 assert(p->member.para.pCell->member.cell.parent_cell 239 == pPrev->member.para.pCell->member.cell.parent_cell); 240 if (pPrev->member.para.pCell != p->member.para.pCell) 241 assert(pPrev->member.para.pCell 242 == p->member.para.pCell->member.cell.prev_cell); 243 } 244 } 245 else if (!(p->member.para.nFlags & MEPF_ROWSTART)) 246 { 247 assert(!(p->member.para.fmt.wEffects & PFE_TABLEROWDELIMITER)); 248 /* ROWSTART must be followed by a cell. */ 249 assert(!(p->member.para.nFlags & MEPF_CELL)); 250 /* ROWSTART must be followed by a cell. */ 251 assert(!(pPrev->member.para.nFlags & MEPF_ROWSTART)); 252 } 253 pPrev = p; 254 p = p->member.para.next_para; 255 } 256 } else { /* v1.0 - 3.0 */ 257 while (p->type == diParagraph) 258 { 259 assert(!(p->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND|MEPF_CELL))); 260 assert(p->member.para.fmt.dwMask & PFM_TABLE); 261 assert(!(p->member.para.fmt.wEffects & PFE_TABLEROWDELIMITER)); 262 assert(!p->member.para.pCell); 263 p = p->member.para.next_para; 264 } 265 return; 266 } 267 assert(p->type == diTextEnd); 268 assert(!pPrev->member.para.pCell); 269 } 270 #endif 271 } 272 273 BOOL ME_IsInTable(ME_DisplayItem *pItem) 274 { 275 PARAFORMAT2 *pFmt; 276 if (!pItem) 277 return FALSE; 278 if (pItem->type == diRun) 279 pItem = ME_GetParagraph(pItem); 280 if (pItem->type != diParagraph) 281 return FALSE; 282 pFmt = &pItem->member.para.fmt; 283 return pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE; 284 } 285 286 /* Table rows should either be deleted completely or not at all. */ 287 void ME_ProtectPartialTableDeletion(ME_TextEditor *editor, ME_Cursor *c, int *nChars) 288 { 289 int nOfs = ME_GetCursorOfs(c); 290 ME_Cursor c2 = *c; 291 ME_DisplayItem *this_para = c->pPara; 292 ME_DisplayItem *end_para; 293 294 ME_MoveCursorChars(editor, &c2, *nChars, FALSE); 295 end_para = c2.pPara; 296 if (c2.pRun->member.run.nFlags & MERF_ENDPARA) { 297 /* End offset might be in the middle of the end paragraph run. 298 * If this is the case, then we need to use the next paragraph as the last 299 * paragraphs. 300 */ 301 int remaining = nOfs + *nChars - c2.pRun->member.run.nCharOfs 302 - end_para->member.para.nCharOfs; 303 if (remaining) 304 { 305 assert(remaining < c2.pRun->member.run.len); 306 end_para = end_para->member.para.next_para; 307 } 308 } 309 if (!editor->bEmulateVersion10) { /* v4.1 */ 310 if (this_para->member.para.pCell != end_para->member.para.pCell || 311 ((this_para->member.para.nFlags|end_para->member.para.nFlags) 312 & (MEPF_ROWSTART|MEPF_ROWEND))) 313 { 314 while (this_para != end_para) 315 { 316 ME_DisplayItem *next_para = this_para->member.para.next_para; 317 BOOL bTruancateDeletion = FALSE; 318 if (this_para->member.para.nFlags & MEPF_ROWSTART) { 319 /* The following while loop assumes that next_para is MEPF_ROWSTART, 320 * so moving back one paragraph let's it be processed as the start 321 * of the row. */ 322 next_para = this_para; 323 this_para = this_para->member.para.prev_para; 324 } else if (next_para->member.para.pCell != this_para->member.para.pCell 325 || this_para->member.para.nFlags & MEPF_ROWEND) 326 { 327 /* Start of the deletion from after the start of the table row. */ 328 bTruancateDeletion = TRUE; 329 } 330 while (!bTruancateDeletion && 331 next_para->member.para.nFlags & MEPF_ROWSTART) 332 { 333 next_para = ME_GetTableRowEnd(next_para)->member.para.next_para; 334 if (next_para->member.para.nCharOfs > nOfs + *nChars) 335 { 336 /* End of deletion is not past the end of the table row. */ 337 next_para = this_para->member.para.next_para; 338 /* Delete the end paragraph preceding the table row if the 339 * preceding table row will be empty. */ 340 if (this_para->member.para.nCharOfs >= nOfs) 341 { 342 next_para = next_para->member.para.next_para; 343 } 344 bTruancateDeletion = TRUE; 345 } else { 346 this_para = next_para->member.para.prev_para; 347 } 348 } 349 if (bTruancateDeletion) 350 { 351 ME_Run *end_run = &ME_FindItemBack(next_para, diRun)->member.run; 352 int nCharsNew = (next_para->member.para.nCharOfs - nOfs 353 - end_run->len); 354 nCharsNew = max(nCharsNew, 0); 355 assert(nCharsNew <= *nChars); 356 *nChars = nCharsNew; 357 break; 358 } 359 this_para = next_para; 360 } 361 } 362 } else { /* v1.0 - 3.0 */ 363 ME_DisplayItem *pRun; 364 int nCharsToBoundary; 365 366 if ((this_para->member.para.nCharOfs != nOfs || this_para == end_para) && 367 this_para->member.para.fmt.dwMask & PFM_TABLE && 368 this_para->member.para.fmt.wEffects & PFE_TABLE) 369 { 370 pRun = c->pRun; 371 /* Find the next tab or end paragraph to use as a delete boundary */ 372 while (!(pRun->member.run.nFlags & (MERF_TAB|MERF_ENDPARA))) 373 pRun = ME_FindItemFwd(pRun, diRun); 374 nCharsToBoundary = pRun->member.run.nCharOfs 375 - c->pRun->member.run.nCharOfs 376 - c->nOffset; 377 *nChars = min(*nChars, nCharsToBoundary); 378 } else if (end_para->member.para.fmt.dwMask & PFM_TABLE && 379 end_para->member.para.fmt.wEffects & PFE_TABLE) 380 { 381 /* The deletion starts from before the row, so don't join it with 382 * previous non-empty paragraphs. */ 383 ME_DisplayItem *curPara; 384 pRun = NULL; 385 if (nOfs > this_para->member.para.nCharOfs) { 386 pRun = ME_FindItemBack(end_para, diRun); 387 curPara = end_para->member.para.prev_para; 388 } 389 if (!pRun) { 390 pRun = ME_FindItemFwd(end_para, diRun); 391 curPara = end_para; 392 } 393 if (pRun) 394 { 395 nCharsToBoundary = curPara->member.para.nCharOfs 396 + pRun->member.run.nCharOfs 397 - nOfs; 398 if (nCharsToBoundary >= 0) 399 *nChars = min(*nChars, nCharsToBoundary); 400 } 401 } 402 if (*nChars < 0) 403 *nChars = 0; 404 } 405 } 406 407 ME_DisplayItem* ME_AppendTableRow(ME_TextEditor *editor, 408 ME_DisplayItem *table_row) 409 { 410 WCHAR endl = '\r', tab = '\t'; 411 ME_DisplayItem *run; 412 PARAFORMAT2 *pFmt; 413 int i; 414 415 assert(table_row); 416 assert(table_row->type == diParagraph); 417 if (!editor->bEmulateVersion10) { /* v4.1 */ 418 ME_DisplayItem *insertedCell, *para, *cell, *prevTableEnd; 419 cell = ME_FindItemFwd(ME_GetTableRowStart(table_row), diCell); 420 prevTableEnd = ME_GetTableRowEnd(table_row); 421 para = prevTableEnd->member.para.next_para; 422 run = ME_FindItemFwd(para, diRun); 423 editor->pCursors[0].pPara = para; 424 editor->pCursors[0].pRun = run; 425 editor->pCursors[0].nOffset = 0; 426 editor->pCursors[1] = editor->pCursors[0]; 427 para = ME_InsertTableRowStartFromCursor(editor); 428 insertedCell = ME_FindItemFwd(para, diCell); 429 /* Copy cell properties */ 430 insertedCell->member.cell.nRightBoundary = cell->member.cell.nRightBoundary; 431 insertedCell->member.cell.border = cell->member.cell.border; 432 while (cell->member.cell.next_cell) { 433 cell = cell->member.cell.next_cell; 434 para = ME_InsertTableCellFromCursor(editor); 435 insertedCell = ME_FindItemBack(para, diCell); 436 /* Copy cell properties */ 437 insertedCell->member.cell.nRightBoundary = cell->member.cell.nRightBoundary; 438 insertedCell->member.cell.border = cell->member.cell.border; 439 }; 440 para = ME_InsertTableRowEndFromCursor(editor); 441 para->member.para.fmt = prevTableEnd->member.para.fmt; 442 /* return the table row start for the inserted paragraph */ 443 return ME_FindItemFwd(cell, diParagraph)->member.para.next_para; 444 } else { /* v1.0 - 3.0 */ 445 run = ME_FindItemBack(table_row->member.para.next_para, diRun); 446 pFmt = &table_row->member.para.fmt; 447 assert(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE); 448 editor->pCursors[0].pPara = table_row; 449 editor->pCursors[0].pRun = run; 450 editor->pCursors[0].nOffset = 0; 451 editor->pCursors[1] = editor->pCursors[0]; 452 ME_InsertTextFromCursor(editor, 0, &endl, 1, run->member.run.style); 453 run = editor->pCursors[0].pRun; 454 for (i = 0; i < pFmt->cTabCount; i++) { 455 ME_InsertTextFromCursor(editor, 0, &tab, 1, run->member.run.style); 456 } 457 return table_row->member.para.next_para; 458 } 459 } 460 461 /* Selects the next table cell or appends a new table row if at end of table */ 462 static void ME_SelectOrInsertNextCell(ME_TextEditor *editor, 463 ME_DisplayItem *run) 464 { 465 ME_DisplayItem *para = ME_GetParagraph(run); 466 int i; 467 468 assert(run && run->type == diRun); 469 assert(ME_IsInTable(run)); 470 if (!editor->bEmulateVersion10) { /* v4.1 */ 471 ME_DisplayItem *cell; 472 /* Get the initial cell */ 473 if (para->member.para.nFlags & MEPF_ROWSTART) { 474 cell = para->member.para.next_para->member.para.pCell; 475 } else if (para->member.para.nFlags & MEPF_ROWEND) { 476 cell = para->member.para.prev_para->member.para.pCell; 477 } else { 478 cell = para->member.para.pCell; 479 } 480 assert(cell); 481 /* Get the next cell. */ 482 if (cell->member.cell.next_cell && 483 cell->member.cell.next_cell->member.cell.next_cell) 484 { 485 cell = cell->member.cell.next_cell; 486 } else { 487 para = ME_GetTableRowEnd(ME_FindItemFwd(cell, diParagraph)); 488 para = para->member.para.next_para; 489 assert(para); 490 if (para->member.para.nFlags & MEPF_ROWSTART) { 491 cell = para->member.para.next_para->member.para.pCell; 492 } else { 493 /* Insert row */ 494 para = para->member.para.prev_para; 495 para = ME_AppendTableRow(editor, ME_GetTableRowStart(para)); 496 /* Put cursor at the start of the new table row */ 497 para = para->member.para.next_para; 498 editor->pCursors[0].pPara = para; 499 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 500 editor->pCursors[0].nOffset = 0; 501 editor->pCursors[1] = editor->pCursors[0]; 502 ME_WrapMarkedParagraphs(editor); 503 return; 504 } 505 } 506 /* Select cell */ 507 editor->pCursors[1].pRun = ME_FindItemFwd(cell, diRun); 508 editor->pCursors[1].pPara = ME_GetParagraph(editor->pCursors[1].pRun); 509 editor->pCursors[1].nOffset = 0; 510 assert(editor->pCursors[0].pRun); 511 cell = cell->member.cell.next_cell; 512 editor->pCursors[0].pRun = ME_FindItemBack(cell, diRun); 513 editor->pCursors[0].pPara = ME_GetParagraph(editor->pCursors[0].pRun); 514 editor->pCursors[0].nOffset = 0; 515 assert(editor->pCursors[1].pRun); 516 } else { /* v1.0 - 3.0 */ 517 if (run->member.run.nFlags & MERF_ENDPARA && 518 ME_IsInTable(ME_FindItemFwd(run, diParagraphOrEnd))) 519 { 520 run = ME_FindItemFwd(run, diRun); 521 assert(run); 522 } 523 for (i = 0; i < 2; i++) 524 { 525 while (!(run->member.run.nFlags & MERF_TAB)) 526 { 527 run = ME_FindItemFwd(run, diRunOrParagraphOrEnd); 528 if (run->type != diRun) 529 { 530 para = run; 531 if (ME_IsInTable(para)) 532 { 533 run = ME_FindItemFwd(para, diRun); 534 assert(run); 535 editor->pCursors[0].pPara = para; 536 editor->pCursors[0].pRun = run; 537 editor->pCursors[0].nOffset = 0; 538 i = 1; 539 } else { 540 /* Insert table row */ 541 para = ME_AppendTableRow(editor, para->member.para.prev_para); 542 /* Put cursor at the start of the new table row */ 543 editor->pCursors[0].pPara = para; 544 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 545 editor->pCursors[0].nOffset = 0; 546 editor->pCursors[1] = editor->pCursors[0]; 547 ME_WrapMarkedParagraphs(editor); 548 return; 549 } 550 } 551 } 552 if (i == 0) 553 run = ME_FindItemFwd(run, diRun); 554 editor->pCursors[i].pRun = run; 555 editor->pCursors[i].pPara = ME_GetParagraph(run); 556 editor->pCursors[i].nOffset = 0; 557 } 558 } 559 } 560 561 562 void ME_TabPressedInTable(ME_TextEditor *editor, BOOL bSelectedRow) 563 { 564 /* FIXME: Shift tab should move to the previous cell. */ 565 ME_Cursor fromCursor, toCursor; 566 ME_InvalidateSelection(editor); 567 { 568 int from, to; 569 from = ME_GetCursorOfs(&editor->pCursors[0]); 570 to = ME_GetCursorOfs(&editor->pCursors[1]); 571 if (from <= to) 572 { 573 fromCursor = editor->pCursors[0]; 574 toCursor = editor->pCursors[1]; 575 } else { 576 fromCursor = editor->pCursors[1]; 577 toCursor = editor->pCursors[0]; 578 } 579 } 580 if (!editor->bEmulateVersion10) /* v4.1 */ 581 { 582 if (!ME_IsInTable(toCursor.pRun)) 583 { 584 editor->pCursors[0] = toCursor; 585 editor->pCursors[1] = toCursor; 586 } else { 587 ME_SelectOrInsertNextCell(editor, toCursor.pRun); 588 } 589 } else { /* v1.0 - 3.0 */ 590 if (!ME_IsInTable(fromCursor.pRun)) { 591 editor->pCursors[0] = fromCursor; 592 editor->pCursors[1] = fromCursor; 593 /* FIXME: For some reason the caret is shown at the start of the 594 * previous paragraph in v1.0 to v3.0, and bCaretAtEnd only works 595 * within the paragraph for wrapped lines. */ 596 if (ME_FindItemBack(fromCursor.pRun, diRun)) 597 editor->bCaretAtEnd = TRUE; 598 } else if ((bSelectedRow || !ME_IsInTable(toCursor.pRun))) { 599 ME_SelectOrInsertNextCell(editor, fromCursor.pRun); 600 } else { 601 if (ME_IsSelection(editor) && !toCursor.nOffset) 602 { 603 ME_DisplayItem *run; 604 run = ME_FindItemBack(toCursor.pRun, diRunOrParagraphOrEnd); 605 if (run->type == diRun && run->member.run.nFlags & MERF_TAB) 606 ME_SelectOrInsertNextCell(editor, run); 607 else 608 ME_SelectOrInsertNextCell(editor, toCursor.pRun); 609 } else { 610 ME_SelectOrInsertNextCell(editor, toCursor.pRun); 611 } 612 } 613 } 614 ME_InvalidateSelection(editor); 615 ME_Repaint(editor); 616 ITextHost_TxShowCaret(editor->texthost, FALSE); 617 ME_ShowCaret(editor); 618 ME_SendSelChange(editor); 619 } 620 621 /* Make sure the cursor is not in the hidden table row start paragraph 622 * without a selection. */ 623 void ME_MoveCursorFromTableRowStartParagraph(ME_TextEditor *editor) 624 { 625 ME_DisplayItem *para = editor->pCursors[0].pPara; 626 if (para == editor->pCursors[1].pPara && 627 para->member.para.nFlags & MEPF_ROWSTART) { 628 /* The cursors should not be at the hidden start row paragraph without 629 * a selection, so the cursor is moved into the first cell. */ 630 para = para->member.para.next_para; 631 editor->pCursors[0].pPara = para; 632 editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); 633 editor->pCursors[0].nOffset = 0; 634 editor->pCursors[1] = editor->pCursors[0]; 635 } 636 } 637 638 struct RTFTable *ME_MakeTableDef(ME_TextEditor *editor) 639 { 640 RTFTable *tableDef = heap_alloc_zero(sizeof(*tableDef)); 641 642 if (!editor->bEmulateVersion10) /* v4.1 */ 643 tableDef->gapH = 10; 644 return tableDef; 645 } 646 647 void ME_InitTableDef(ME_TextEditor *editor, struct RTFTable *tableDef) 648 { 649 ZeroMemory(tableDef->cells, sizeof(tableDef->cells)); 650 ZeroMemory(tableDef->border, sizeof(tableDef->border)); 651 tableDef->numCellsDefined = 0; 652 tableDef->leftEdge = 0; 653 if (!editor->bEmulateVersion10) /* v4.1 */ 654 tableDef->gapH = 10; 655 else /* v1.0 - 3.0 */ 656 tableDef->gapH = 0; 657 } 658