ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

wrap.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 = &para->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, &para->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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.