Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenwrap.c
Go to the documentation of this file.
00001 /* 00002 * RichEdit - Paragraph wrapping. Don't try to understand it. You've been 00003 * warned ! 00004 * 00005 * Copyright 2004 by Krzysztof Foltman 00006 * 00007 * This library is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * This library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00020 */ 00021 00022 00023 #include "editor.h" 00024 00025 WINE_DEFAULT_DEBUG_CHANNEL(richedit); 00026 00027 /* 00028 * Unsolved problems: 00029 * 00030 * - center and right align in WordPad omits all spaces at the start, we don't 00031 * - objects/images are not handled yet 00032 * - no tabs 00033 */ 00034 00035 static ME_DisplayItem *ME_MakeRow(int height, int baseline, int width) 00036 { 00037 ME_DisplayItem *item = ME_MakeDI(diStartRow); 00038 00039 item->member.row.nHeight = height; 00040 item->member.row.nBaseline = baseline; 00041 item->member.row.nWidth = width; 00042 return item; 00043 } 00044 00045 static void ME_BeginRow(ME_WrapContext *wc) 00046 { 00047 PARAFORMAT2 *pFmt; 00048 ME_DisplayItem *para = wc->pPara; 00049 00050 pFmt = para->member.para.pFmt; 00051 wc->pRowStart = NULL; 00052 wc->bOverflown = FALSE; 00053 wc->pLastSplittableRun = NULL; 00054 wc->bWordWrap = wc->context->editor->bWordWrap; 00055 if (para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) { 00056 wc->nAvailWidth = 0; 00057 wc->bWordWrap = FALSE; 00058 if (para->member.para.nFlags & MEPF_ROWEND) 00059 { 00060 ME_Cell *cell = &ME_FindItemBack(para, diCell)->member.cell; 00061 cell->nWidth = 0; 00062 } 00063 } else if (para->member.para.pCell) { 00064 ME_Cell *cell = ¶->member.para.pCell->member.cell; 00065 int width; 00066 00067 width = cell->nRightBoundary; 00068 if (cell->prev_cell) 00069 width -= cell->prev_cell->member.cell.nRightBoundary; 00070 if (!cell->prev_cell) 00071 { 00072 int rowIndent = ME_GetTableRowEnd(para)->member.para.pFmt->dxStartIndent; 00073 width -= rowIndent; 00074 } 00075 cell->nWidth = max(ME_twips2pointsX(wc->context, width), 0); 00076 00077 wc->nAvailWidth = cell->nWidth 00078 - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin; 00079 wc->bWordWrap = TRUE; 00080 } else { 00081 wc->nAvailWidth = wc->context->nAvailWidth 00082 - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin; 00083 } 00084 wc->pt.x = wc->context->pt.x; 00085 if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ 00086 pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) 00087 /* Shift the text down because of the border. */ 00088 wc->pt.y++; 00089 } 00090 00091 static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd) 00092 { 00093 ME_DisplayItem *p, *row, *para; 00094 BOOL bSkippingSpaces = TRUE; 00095 int ascent = 0, descent = 0, width=0, shift = 0, align = 0; 00096 PARAFORMAT2 *pFmt; 00097 /* wrap text */ 00098 para = wc->pPara; 00099 pFmt = para->member.para.pFmt; 00100 00101 for (p = pEnd->prev; p!=wc->pRowStart->prev; p = p->prev) 00102 { 00103 /* ENDPARA run shouldn't affect row height, except if it's the only run in the paragraph */ 00104 if (p->type==diRun && ((p==wc->pRowStart) || !(p->member.run.nFlags & MERF_ENDPARA))) { /* FIXME add more run types */ 00105 if (p->member.run.nAscent>ascent) 00106 ascent = p->member.run.nAscent; 00107 if (p->member.run.nDescent>descent) 00108 descent = p->member.run.nDescent; 00109 if (bSkippingSpaces) 00110 { 00111 /* Exclude space characters from run width. 00112 * Other whitespace or delimiters are not treated this way. */ 00113 SIZE sz; 00114 int len = p->member.run.strText->nLen; 00115 WCHAR *text = p->member.run.strText->szData + len - 1; 00116 00117 assert (len); 00118 if (~p->member.run.nFlags & MERF_GRAPHICS) 00119 while (len && *(text--) == ' ') 00120 len--; 00121 if (len) 00122 { 00123 if (len == p->member.run.strText->nLen) 00124 { 00125 width += p->member.run.nWidth; 00126 } else { 00127 sz = ME_GetRunSize(wc->context, ¶->member.para, 00128 &p->member.run, len, p->member.run.pt.x); 00129 width += sz.cx; 00130 } 00131 } 00132 bSkippingSpaces = !len; 00133 } else if (!(p->member.run.nFlags & MERF_ENDPARA)) 00134 width += p->member.run.nWidth; 00135 } 00136 } 00137 00138 para->member.para.nWidth = max(para->member.para.nWidth, width); 00139 row = ME_MakeRow(ascent+descent, ascent, width); 00140 if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ 00141 pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) 00142 { 00143 /* The text was shifted down in ME_BeginRow so move the wrap context 00144 * back to where it should be. */ 00145 wc->pt.y--; 00146 /* The height of the row is increased by the borders. */ 00147 row->member.row.nHeight += 2; 00148 } 00149 row->member.row.pt = wc->pt; 00150 row->member.row.nLMargin = (!wc->nRow ? wc->nFirstMargin : wc->nLeftMargin); 00151 row->member.row.nRMargin = wc->nRightMargin; 00152 assert(para->member.para.pFmt->dwMask & PFM_ALIGNMENT); 00153 align = para->member.para.pFmt->wAlignment; 00154 if (align == PFA_CENTER) 00155 shift = max((wc->nAvailWidth-width)/2, 0); 00156 if (align == PFA_RIGHT) 00157 shift = max(wc->nAvailWidth-width, 0); 00158 for (p = wc->pRowStart; p!=pEnd; p = p->next) 00159 { 00160 if (p->type==diRun) { /* FIXME add more run types */ 00161 p->member.run.pt.x += row->member.row.nLMargin+shift; 00162 } 00163 } 00164 ME_InsertBefore(wc->pRowStart, row); 00165 wc->nRow++; 00166 wc->pt.y += row->member.row.nHeight; 00167 ME_BeginRow(wc); 00168 } 00169 00170 static void ME_WrapEndParagraph(ME_WrapContext *wc, ME_DisplayItem *p) 00171 { 00172 ME_DisplayItem *para = wc->pPara; 00173 PARAFORMAT2 *pFmt = para->member.para.pFmt; 00174 if (wc->pRowStart) 00175 ME_InsertRowStart(wc, p); 00176 if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ 00177 pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) 00178 { 00179 /* ME_BeginRow was called an extra time for the paragraph, and it shifts the 00180 * text down by one pixel for the border, so fix up the wrap context. */ 00181 wc->pt.y--; 00182 } 00183 00184 /* 00185 p = para->next; 00186 while(p) { 00187 if (p->type == diParagraph || p->type == diTextEnd) 00188 return; 00189 if (p->type == diRun) 00190 { 00191 ME_Run *run = &p->member.run; 00192 TRACE("%s - (%d, %d)\n", debugstr_w(run->strText->szData), run->pt.x, run->pt.y); 00193 } 00194 p = p->next; 00195 } 00196 */ 00197 } 00198 00199 static void ME_WrapSizeRun(ME_WrapContext *wc, ME_DisplayItem *p) 00200 { 00201 /* FIXME compose style (out of character and paragraph styles) here */ 00202 00203 ME_UpdateRunFlags(wc->context->editor, &p->member.run); 00204 00205 ME_CalcRunExtent(wc->context, &wc->pPara->member.para, 00206 wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, &p->member.run); 00207 } 00208 00209 static ME_DisplayItem *ME_MaximizeSplit(ME_WrapContext *wc, ME_DisplayItem *p, int i) 00210 { 00211 ME_DisplayItem *pp, *piter = p; 00212 int j; 00213 if (!i) 00214 return NULL; 00215 j = ME_ReverseFindNonWhitespaceV(p->member.run.strText, i); 00216 if (j>0) { 00217 pp = ME_SplitRun(wc, piter, j); 00218 wc->pt.x += piter->member.run.nWidth; 00219 return pp; 00220 } 00221 else 00222 { 00223 pp = piter; 00224 /* omit all spaces before split point */ 00225 while(piter != wc->pRowStart) 00226 { 00227 piter = ME_FindItemBack(piter, diRun); 00228 if (piter->member.run.nFlags & MERF_WHITESPACE) 00229 { 00230 pp = piter; 00231 continue; 00232 } 00233 if (piter->member.run.nFlags & MERF_ENDWHITE) 00234 { 00235 i = ME_ReverseFindNonWhitespaceV(piter->member.run.strText, 00236 piter->member.run.strText->nLen); 00237 pp = ME_SplitRun(wc, piter, i); 00238 wc->pt = pp->member.run.pt; 00239 return pp; 00240 } 00241 /* this run is the end of spaces, so the run edge is a good point to split */ 00242 wc->pt = pp->member.run.pt; 00243 wc->bOverflown = TRUE; 00244 TRACE("Split point is: %s|%s\n", debugstr_w(piter->member.run.strText->szData), debugstr_w(pp->member.run.strText->szData)); 00245 return pp; 00246 } 00247 wc->pt = piter->member.run.pt; 00248 return piter; 00249 } 00250 } 00251 00252 static ME_DisplayItem *ME_SplitByBacktracking(ME_WrapContext *wc, ME_DisplayItem *p, int loc) 00253 { 00254 ME_DisplayItem *piter = p, *pp; 00255 int i, idesp, len; 00256 ME_Run *run = &p->member.run; 00257 00258 idesp = i = ME_CharFromPoint(wc->context, loc, run); 00259 len = run->strText->nLen; 00260 assert(len>0); 00261 assert(i<len); 00262 if (i) { 00263 /* don't split words */ 00264 i = ME_ReverseFindWhitespaceV(run->strText, i); 00265 pp = ME_MaximizeSplit(wc, p, i); 00266 if (pp) 00267 return pp; 00268 } 00269 TRACE("Must backtrack to split at: %s\n", debugstr_w(p->member.run.strText->szData)); 00270 if (wc->pLastSplittableRun) 00271 { 00272 if (wc->pLastSplittableRun->member.run.nFlags & (MERF_GRAPHICS|MERF_TAB)) 00273 { 00274 wc->pt = wc->pLastSplittableRun->member.run.pt; 00275 return wc->pLastSplittableRun; 00276 } 00277 else if (wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE) 00278 { 00279 /* the following two lines are just to check if we forgot to call UpdateRunFlags earlier, 00280 they serve no other purpose */ 00281 ME_UpdateRunFlags(wc->context->editor, run); 00282 assert((wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE)); 00283 00284 piter = wc->pLastSplittableRun; 00285 run = &piter->member.run; 00286 len = run->strText->nLen; 00287 /* don't split words */ 00288 i = ME_ReverseFindWhitespaceV(run->strText, len); 00289 if (i == len) 00290 i = ME_ReverseFindNonWhitespaceV(run->strText, len); 00291 if (i) { 00292 ME_DisplayItem *piter2 = ME_SplitRun(wc, piter, i); 00293 wc->pt = piter2->member.run.pt; 00294 return piter2; 00295 } 00296 /* splittable = must have whitespaces */ 00297 assert(0 == "Splittable, but no whitespaces"); 00298 } 00299 else 00300 { 00301 /* restart from the first run beginning with spaces */ 00302 wc->pt = wc->pLastSplittableRun->member.run.pt; 00303 return wc->pLastSplittableRun; 00304 } 00305 } 00306 TRACE("Backtracking failed, trying desperate: %s\n", debugstr_w(p->member.run.strText->szData)); 00307 /* OK, no better idea, so assume we MAY split words if we can split at all*/ 00308 if (idesp) 00309 return ME_SplitRun(wc, piter, idesp); 00310 else 00311 if (wc->pRowStart && piter != wc->pRowStart) 00312 { 00313 /* don't need to break current run, because it's possible to split 00314 before this run */ 00315 wc->bOverflown = TRUE; 00316 return piter; 00317 } 00318 else 00319 { 00320 /* split point inside first character - no choice but split after that char */ 00321 if (len != 1) { 00322 /* the run is more than 1 char, so we may split */ 00323 return ME_SplitRun(wc, piter, 1); 00324 } 00325 /* the run is one char, can't split it */ 00326 return piter; 00327 } 00328 } 00329 00330 static ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p) 00331 { 00332 ME_DisplayItem *pp; 00333 ME_Run *run; 00334 int len; 00335 00336 assert(p->type == diRun); 00337 if (!wc->pRowStart) 00338 wc->pRowStart = p; 00339 run = &p->member.run; 00340 run->pt.x = wc->pt.x; 00341 run->pt.y = wc->pt.y; 00342 ME_WrapSizeRun(wc, p); 00343 len = run->strText->nLen; 00344 00345 if (wc->bOverflown) /* just skipping final whitespaces */ 00346 { 00347 /* End paragraph run can't overflow to the next line by itself. */ 00348 if (run->nFlags & MERF_ENDPARA) 00349 return p->next; 00350 00351 if (run->nFlags & MERF_WHITESPACE) { 00352 wc->pt.x += run->nWidth; 00353 /* skip runs consisting of only whitespaces */ 00354 return p->next; 00355 } 00356 00357 if (run->nFlags & MERF_STARTWHITE) { 00358 /* try to split the run at the first non-white char */ 00359 int black; 00360 black = ME_FindNonWhitespaceV(run->strText, 0); 00361 if (black) { 00362 wc->bOverflown = FALSE; 00363 pp = ME_SplitRun(wc, p, black); 00364 ME_CalcRunExtent(wc->context, &wc->pPara->member.para, 00365 wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, 00366 &pp->member.run); 00367 ME_InsertRowStart(wc, pp); 00368 return pp; 00369 } 00370 } 00371 /* black run: the row goes from pRowStart to the previous run */ 00372 ME_InsertRowStart(wc, p); 00373 return p; 00374 } 00375 /* simply end the current row and move on to next one */ 00376 if (run->nFlags & MERF_ENDROW) 00377 { 00378 p = p->next; 00379 ME_InsertRowStart(wc, p); 00380 return p; 00381 } 00382 00383 /* will current run fit? */ 00384 if (wc->bWordWrap && 00385 wc->pt.x + run->nWidth - wc->context->pt.x > wc->nAvailWidth) 00386 { 00387 int loc = wc->context->pt.x + wc->nAvailWidth - wc->pt.x; 00388 /* total white run ? */ 00389 if (run->nFlags & MERF_WHITESPACE) { 00390 /* let the overflow logic handle it */ 00391 wc->bOverflown = TRUE; 00392 return p; 00393 } 00394 /* TAB: we can split before */ 00395 if (run->nFlags & MERF_TAB) { 00396 wc->bOverflown = TRUE; 00397 if (wc->pRowStart == p) 00398 /* Don't split before the start of the run, or we will get an 00399 * endless loop. */ 00400 return p->next; 00401 else 00402 return p; 00403 } 00404 /* graphics: we can split before, if run's width is smaller than row's width */ 00405 if ((run->nFlags & MERF_GRAPHICS) && run->nWidth <= wc->nAvailWidth) { 00406 wc->bOverflown = TRUE; 00407 return p; 00408 } 00409 /* can we separate out the last spaces ? (to use overflow logic later) */ 00410 if (run->nFlags & MERF_ENDWHITE) 00411 { 00412 /* we aren't sure if it's *really* necessary, it's a good start however */ 00413 int black = ME_ReverseFindNonWhitespaceV(run->strText, len); 00414 ME_SplitRun(wc, p, black); 00415 /* handle both parts again */ 00416 return p; 00417 } 00418 /* determine the split point by backtracking */ 00419 pp = ME_SplitByBacktracking(wc, p, loc); 00420 if (pp == wc->pRowStart) 00421 { 00422 if (run->nFlags & MERF_STARTWHITE) 00423 { 00424 /* We had only spaces so far, so we must be on the first line of the 00425 * paragraph (or the first line after MERF_ENDROW forced the line 00426 * break within the paragraph), since no other lines of the paragraph 00427 * start with spaces. */ 00428 00429 /* The lines will only contain spaces, and the rest of the run will 00430 * overflow onto the next line. */ 00431 wc->bOverflown = TRUE; 00432 return p; 00433 } 00434 /* Couldn't split the first run, possible because we have a large font 00435 * with a single character that caused an overflow. 00436 */ 00437 wc->pt.x += run->nWidth; 00438 return p->next; 00439 } 00440 if (p != pp) /* found a suitable split point */ 00441 { 00442 wc->bOverflown = TRUE; 00443 return pp; 00444 } 00445 /* we detected that it's best to split on start of this run */ 00446 if (wc->bOverflown) 00447 return pp; 00448 ERR("failure!\n"); 00449 /* not found anything - writing over margins is the only option left */ 00450 } 00451 if ((run->nFlags & (MERF_SPLITTABLE | MERF_STARTWHITE)) 00452 || ((run->nFlags & (MERF_GRAPHICS|MERF_TAB)) && (p != wc->pRowStart))) 00453 { 00454 wc->pLastSplittableRun = p; 00455 } 00456 wc->pt.x += run->nWidth; 00457 return p->next; 00458 } 00459 00460 static int ME_GetParaLineSpace(ME_Context* c, ME_Paragraph* para) 00461 { 00462 int sp = 0, ls = 0; 00463 if (!(para->pFmt->dwMask & PFM_LINESPACING)) return 0; 00464 00465 /* FIXME: how to compute simply the line space in ls ??? */ 00466 /* FIXME: does line spacing include the line itself ??? */ 00467 switch (para->pFmt->bLineSpacingRule) 00468 { 00469 case 0: sp = ls; break; 00470 case 1: sp = (3 * ls) / 2; break; 00471 case 2: sp = 2 * ls; break; 00472 case 3: sp = ME_twips2pointsY(c, para->pFmt->dyLineSpacing); if (sp < ls) sp = ls; break; 00473 case 4: sp = ME_twips2pointsY(c, para->pFmt->dyLineSpacing); break; 00474 case 5: sp = para->pFmt->dyLineSpacing / 20; break; 00475 default: FIXME("Unsupported spacing rule value %d\n", para->pFmt->bLineSpacingRule); 00476 } 00477 if (c->editor->nZoomNumerator == 0) 00478 return sp; 00479 else 00480 return sp * c->editor->nZoomNumerator / c->editor->nZoomDenominator; 00481 } 00482 00483 static void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp) { 00484 ME_DisplayItem *p; 00485 00486 tp->member.para.nWidth = 0; 00487 /* remove row start items as they will be reinserted by the 00488 * paragraph wrapper anyway */ 00489 tp->member.para.nRows = 0; 00490 for (p = tp->next; p != tp->member.para.next_para; p = p->next) { 00491 if (p->type == diStartRow) { 00492 ME_DisplayItem *pRow = p; 00493 p = p->prev; 00494 ME_Remove(pRow); 00495 ME_DestroyDisplayItem(pRow); 00496 } 00497 } 00498 /* join runs that can be joined */ 00499 for (p = tp->next; p != tp->member.para.next_para; p = p->next) { 00500 assert(p->type != diStartRow); /* should have been deleted above */ 00501 if (p->type == diRun) { 00502 while (p->next->type == diRun && /* FIXME */ 00503 ME_CanJoinRuns(&p->member.run, &p->next->member.run)) { 00504 ME_JoinRuns(c->editor, p); 00505 } 00506 } 00507 } 00508 } 00509 00510 static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) { 00511 ME_DisplayItem *p; 00512 ME_WrapContext wc; 00513 int border = 0; 00514 int linespace = 0; 00515 PARAFORMAT2 *pFmt; 00516 00517 assert(tp->type == diParagraph); 00518 if (!(tp->member.para.nFlags & MEPF_REWRAP)) { 00519 return; 00520 } 00521 ME_PrepareParagraphForWrapping(c, tp); 00522 pFmt = tp->member.para.pFmt; 00523 00524 wc.context = c; 00525 wc.pPara = tp; 00526 /* wc.para_style = tp->member.para.style; */ 00527 wc.style = NULL; 00528 if (tp->member.para.nFlags & MEPF_ROWEND) { 00529 wc.nFirstMargin = wc.nLeftMargin = wc.nRightMargin = 0; 00530 } else { 00531 int dxStartIndent = pFmt->dxStartIndent; 00532 if (tp->member.para.pCell) { 00533 dxStartIndent += ME_GetTableRowEnd(tp)->member.para.pFmt->dxOffset; 00534 } 00535 wc.nFirstMargin = ME_twips2pointsX(c, dxStartIndent); 00536 wc.nLeftMargin = wc.nFirstMargin + ME_twips2pointsX(c, pFmt->dxOffset); 00537 wc.nRightMargin = ME_twips2pointsX(c, pFmt->dxRightIndent); 00538 } 00539 if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ 00540 pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) 00541 { 00542 wc.nFirstMargin += ME_twips2pointsX(c, pFmt->dxOffset * 2); 00543 } 00544 wc.nRow = 0; 00545 wc.pt.y = 0; 00546 if (pFmt->dwMask & PFM_SPACEBEFORE) 00547 wc.pt.y += ME_twips2pointsY(c, pFmt->dySpaceBefore); 00548 if (!(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) && 00549 pFmt->dwMask & PFM_BORDER) 00550 { 00551 border = ME_GetParaBorderWidth(c, tp->member.para.pFmt->wBorders); 00552 if (pFmt->wBorders & 1) { 00553 wc.nFirstMargin += border; 00554 wc.nLeftMargin += border; 00555 } 00556 if (pFmt->wBorders & 2) 00557 wc.nRightMargin -= border; 00558 if (pFmt->wBorders & 4) 00559 wc.pt.y += border; 00560 } 00561 00562 linespace = ME_GetParaLineSpace(c, &tp->member.para); 00563 00564 ME_BeginRow(&wc); 00565 for (p = tp->next; p!=tp->member.para.next_para; ) { 00566 assert(p->type != diStartRow); 00567 if (p->type == diRun) { 00568 p = ME_WrapHandleRun(&wc, p); 00569 } 00570 else p = p->next; 00571 if (wc.nRow && p == wc.pRowStart) 00572 wc.pt.y += linespace; 00573 } 00574 ME_WrapEndParagraph(&wc, p); 00575 if (!(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) && 00576 (pFmt->dwMask & PFM_BORDER) && (pFmt->wBorders & 8)) 00577 wc.pt.y += border; 00578 if (tp->member.para.pFmt->dwMask & PFM_SPACEAFTER) 00579 wc.pt.y += ME_twips2pointsY(c, pFmt->dySpaceAfter); 00580 00581 tp->member.para.nFlags &= ~MEPF_REWRAP; 00582 tp->member.para.nHeight = wc.pt.y; 00583 tp->member.para.nRows = wc.nRow; 00584 } 00585 00586 static void ME_MarkRepaintEnd(ME_DisplayItem *para, 00587 ME_DisplayItem **repaint_start, 00588 ME_DisplayItem **repaint_end) 00589 { 00590 if (!*repaint_start) 00591 *repaint_start = para; 00592 *repaint_end = para->member.para.next_para; 00593 para->member.para.nFlags |= MEPF_REPAINT; 00594 } 00595 00596 BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) 00597 { 00598 ME_DisplayItem *item; 00599 ME_Context c; 00600 int totalWidth = 0; 00601 ME_DisplayItem *repaint_start = NULL, *repaint_end = NULL; 00602 00603 ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost)); 00604 c.pt.x = 0; 00605 item = editor->pBuffer->pFirst->next; 00606 while(item != editor->pBuffer->pLast) { 00607 BOOL bRedraw = FALSE; 00608 00609 assert(item->type == diParagraph); 00610 if ((item->member.para.nFlags & MEPF_REWRAP) 00611 || (item->member.para.pt.y != c.pt.y)) 00612 bRedraw = TRUE; 00613 item->member.para.pt = c.pt; 00614 00615 ME_WrapTextParagraph(&c, item); 00616 00617 if (bRedraw) 00618 ME_MarkRepaintEnd(item, &repaint_start, &repaint_end); 00619 00620 if (item->member.para.nFlags & MEPF_ROWSTART) 00621 { 00622 ME_DisplayItem *cell = ME_FindItemFwd(item, diCell); 00623 ME_DisplayItem *endRowPara; 00624 int borderWidth = 0; 00625 cell->member.cell.pt = c.pt; 00626 /* Offset the text by the largest top border width. */ 00627 while (cell->member.cell.next_cell) { 00628 borderWidth = max(borderWidth, cell->member.cell.border.top.width); 00629 cell = cell->member.cell.next_cell; 00630 } 00631 endRowPara = ME_FindItemFwd(cell, diParagraph); 00632 assert(endRowPara->member.para.nFlags & MEPF_ROWEND); 00633 if (borderWidth > 0) 00634 { 00635 borderWidth = max(ME_twips2pointsY(&c, borderWidth), 1); 00636 while (cell) { 00637 cell->member.cell.yTextOffset = borderWidth; 00638 cell = cell->member.cell.prev_cell; 00639 } 00640 c.pt.y += borderWidth; 00641 } 00642 if (endRowPara->member.para.pFmt->dxStartIndent > 0) 00643 { 00644 int dxStartIndent = endRowPara->member.para.pFmt->dxStartIndent; 00645 cell = ME_FindItemFwd(item, diCell); 00646 cell->member.cell.pt.x += ME_twips2pointsX(&c, dxStartIndent); 00647 c.pt.x = cell->member.cell.pt.x; 00648 } 00649 } 00650 else if (item->member.para.nFlags & MEPF_ROWEND) 00651 { 00652 /* Set all the cells to the height of the largest cell */ 00653 ME_DisplayItem *startRowPara; 00654 int prevHeight, nHeight, bottomBorder = 0; 00655 ME_DisplayItem *cell = ME_FindItemBack(item, diCell); 00656 item->member.para.nWidth = cell->member.cell.pt.x + cell->member.cell.nWidth; 00657 if (!(item->member.para.next_para->member.para.nFlags & MEPF_ROWSTART)) 00658 { 00659 /* Last row, the bottom border is added to the height. */ 00660 cell = cell->member.cell.prev_cell; 00661 while (cell) 00662 { 00663 bottomBorder = max(bottomBorder, cell->member.cell.border.bottom.width); 00664 cell = cell->member.cell.prev_cell; 00665 } 00666 bottomBorder = ME_twips2pointsY(&c, bottomBorder); 00667 cell = ME_FindItemBack(item, diCell); 00668 } 00669 prevHeight = cell->member.cell.nHeight; 00670 nHeight = cell->member.cell.prev_cell->member.cell.nHeight + bottomBorder; 00671 cell->member.cell.nHeight = nHeight; 00672 item->member.para.nHeight = nHeight; 00673 cell = cell->member.cell.prev_cell; 00674 cell->member.cell.nHeight = nHeight; 00675 while (cell->member.cell.prev_cell) 00676 { 00677 cell = cell->member.cell.prev_cell; 00678 cell->member.cell.nHeight = nHeight; 00679 } 00680 /* Also set the height of the start row paragraph */ 00681 startRowPara = ME_FindItemBack(cell, diParagraph); 00682 startRowPara->member.para.nHeight = nHeight; 00683 c.pt.x = startRowPara->member.para.pt.x; 00684 c.pt.y = cell->member.cell.pt.y + nHeight; 00685 if (prevHeight < nHeight) 00686 { 00687 /* The height of the cells has grown, so invalidate the bottom of 00688 * the cells. */ 00689 ME_MarkRepaintEnd(item, &repaint_start, &repaint_end); 00690 cell = ME_FindItemBack(item, diCell); 00691 while (cell) { 00692 ME_MarkRepaintEnd(ME_FindItemBack(cell, diParagraph), &repaint_start, &repaint_end); 00693 cell = cell->member.cell.prev_cell; 00694 } 00695 } 00696 } 00697 else if (item->member.para.pCell && 00698 item->member.para.pCell != item->member.para.next_para->member.para.pCell) 00699 { 00700 /* The next paragraph is in the next cell in the table row. */ 00701 ME_Cell *cell = &item->member.para.pCell->member.cell; 00702 cell->nHeight = c.pt.y + item->member.para.nHeight - cell->pt.y; 00703 00704 /* Propagate the largest height to the end so that it can be easily 00705 * sent back to all the cells at the end of the row. */ 00706 if (cell->prev_cell) 00707 cell->nHeight = max(cell->nHeight, cell->prev_cell->member.cell.nHeight); 00708 00709 c.pt.x = cell->pt.x + cell->nWidth; 00710 c.pt.y = cell->pt.y; 00711 cell->next_cell->member.cell.pt = c.pt; 00712 if (!(item->member.para.next_para->member.para.nFlags & MEPF_ROWEND)) 00713 c.pt.y += cell->yTextOffset; 00714 } 00715 else 00716 { 00717 if (item->member.para.pCell) { 00718 /* Next paragraph in the same cell. */ 00719 c.pt.x = item->member.para.pCell->member.cell.pt.x; 00720 } else { 00721 /* Normal paragraph */ 00722 c.pt.x = 0; 00723 } 00724 c.pt.y += item->member.para.nHeight; 00725 } 00726 00727 totalWidth = max(totalWidth, item->member.para.nWidth); 00728 item = item->member.para.next_para; 00729 } 00730 editor->sizeWindow.cx = c.rcView.right-c.rcView.left; 00731 editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top; 00732 00733 editor->nTotalLength = c.pt.y; 00734 editor->nTotalWidth = totalWidth; 00735 editor->pBuffer->pLast->member.para.pt.x = 0; 00736 editor->pBuffer->pLast->member.para.pt.y = c.pt.y; 00737 00738 ME_DestroyContext(&c); 00739 00740 if (repaint_start || editor->nTotalLength < editor->nLastTotalLength) 00741 { 00742 if (!repaint_start) repaint_start = editor->pBuffer->pFirst; 00743 ME_InvalidateMarkedParagraphs(editor, repaint_start, repaint_end); 00744 } 00745 return !!repaint_start; 00746 } 00747 00748 void ME_InvalidateMarkedParagraphs(ME_TextEditor *editor, 00749 ME_DisplayItem *start_para, 00750 ME_DisplayItem *end_para) 00751 { 00752 ME_Context c; 00753 RECT rc; 00754 int ofs; 00755 ME_DisplayItem *item; 00756 00757 ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost)); 00758 rc = c.rcView; 00759 ofs = editor->vert_si.nPos; 00760 00761 item = start_para; 00762 while(item && item != end_para) { 00763 if (item->member.para.nFlags & MEPF_REPAINT) { 00764 rc.top = c.rcView.top + item->member.para.pt.y - ofs; 00765 rc.bottom = max(rc.top + item->member.para.nHeight, c.rcView.bottom); 00766 ITextHost_TxInvalidateRect(editor->texthost, &rc, TRUE); 00767 item->member.para.nFlags &= ~MEPF_REPAINT; 00768 } 00769 item = item->member.para.next_para; 00770 } 00771 if (editor->nTotalLength < editor->nLastTotalLength) 00772 { 00773 rc.top = c.rcView.top + editor->nTotalLength - ofs; 00774 rc.bottom = c.rcView.top + editor->nLastTotalLength - ofs; 00775 ITextHost_TxInvalidateRect(editor->texthost, &rc, TRUE); 00776 } 00777 ME_DestroyContext(&c); 00778 } 00779 00780 00781 void 00782 ME_SendRequestResize(ME_TextEditor *editor, BOOL force) 00783 { 00784 if (editor->nEventMask & ENM_REQUESTRESIZE) 00785 { 00786 RECT rc; 00787 00788 ITextHost_TxGetClientRect(editor->texthost, &rc); 00789 00790 if (force || rc.bottom != editor->nTotalLength) 00791 { 00792 REQRESIZE info; 00793 00794 info.nmhdr.hwndFrom = NULL; 00795 info.nmhdr.idFrom = 0; 00796 info.nmhdr.code = EN_REQUESTRESIZE; 00797 info.rc = rc; 00798 info.rc.right = editor->nTotalWidth; 00799 info.rc.bottom = editor->nTotalLength; 00800 00801 editor->nEventMask &= ~ENM_REQUESTRESIZE; 00802 ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info); 00803 editor->nEventMask |= ENM_REQUESTRESIZE; 00804 } 00805 } 00806 } Generated on Sun May 27 2012 04:26:00 for ReactOS by
1.7.6.1
|