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

run.c
Go to the documentation of this file.
00001 /*
00002  * RichEdit - operations on runs (diRun, rectangular pieces of paragraphs).
00003  * Splitting/joining runs. Adjusting offsets after deleting/adding content.
00004  * Character/pixel conversions.
00005  *
00006  * Copyright 2004 by Krzysztof Foltman
00007  * Copyright 2006 by Phil Krylov
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00022  */
00023 
00024 #include "editor.h"
00025 
00026 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
00027 WINE_DECLARE_DEBUG_CHANNEL(richedit_check);
00028 WINE_DECLARE_DEBUG_CHANNEL(richedit_lists);
00029 
00030 /******************************************************************************
00031  * ME_CanJoinRuns
00032  *
00033  * Returns 1 if two runs can be safely merged into one, 0 otherwise.
00034  */ 
00035 int ME_CanJoinRuns(const ME_Run *run1, const ME_Run *run2)
00036 {
00037   if ((run1->nFlags | run2->nFlags) & MERF_NOJOIN)
00038     return 0;
00039   if (run1->style != run2->style)
00040     return 0;
00041   if ((run1->nFlags & MERF_STYLEFLAGS) != (run2->nFlags & MERF_STYLEFLAGS))
00042     return 0;
00043   return 1;
00044 }
00045 
00046 void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift)
00047 {
00048   p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
00049   assert(p);
00050   ME_PropagateCharOffset(p, shift);
00051 }
00052 
00053 /******************************************************************************
00054  * ME_PropagateCharOffsets
00055  *
00056  * Shifts (increases or decreases) character offset (relative to beginning of 
00057  * the document) of the part of the text starting from given place.  
00058  */ 
00059 void ME_PropagateCharOffset(ME_DisplayItem *p, int shift)
00060 {
00061     /* Runs in one paragraph contain character offset relative to their owning
00062      * paragraph. If we start the shifting from the run, we need to shift
00063      * all the relative offsets until the end of the paragraph
00064      */         
00065   if (p->type == diRun) /* propagate in all runs in this para */
00066   {
00067     TRACE("PropagateCharOffset(%s, %d)\n", debugstr_w(p->member.run.strText->szData), shift);
00068     do {
00069       p->member.run.nCharOfs += shift;
00070       assert(p->member.run.nCharOfs >= 0);
00071       p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
00072     } while(p->type == diRun);
00073   }
00074     /* Runs in next paragraphs don't need their offsets updated, because they, 
00075      * again, those offsets are relative to their respective paragraphs.
00076      * Instead of that, we're updating paragraphs' character offsets.     
00077      */         
00078   if (p->type == diParagraph) /* propagate in all next paras */
00079   {
00080     do {
00081       p->member.para.nCharOfs += shift;
00082       assert(p->member.para.nCharOfs >= 0);
00083       p = p->member.para.next_para;
00084     } while(p->type == diParagraph);
00085   }
00086   /* diTextEnd also has character offset in it, which makes finding text length
00087    * easier. But it needs to be up to date first.
00088    */
00089   if (p->type == diTextEnd)
00090   {
00091     p->member.para.nCharOfs += shift;
00092     assert(p->member.para.nCharOfs >= 0);
00093   }
00094 }
00095 
00096 /******************************************************************************
00097  * ME_CheckCharOffsets
00098  * 
00099  * Checks if editor lists' validity and optionally dumps the document structure
00100  */      
00101 void ME_CheckCharOffsets(ME_TextEditor *editor)
00102 {
00103   ME_DisplayItem *p = editor->pBuffer->pFirst;
00104   int ofs = 0, ofsp = 0;
00105   if(TRACE_ON(richedit_lists))
00106   {
00107     TRACE_(richedit_lists)("---\n");
00108     ME_DumpDocument(editor->pBuffer);
00109   }
00110   do {
00111     p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
00112     switch(p->type) {
00113       case diTextEnd:
00114         TRACE_(richedit_check)("tend, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
00115         assert(ofsp+ofs == p->member.para.nCharOfs);
00116         return;
00117       case diParagraph:
00118         TRACE_(richedit_check)("para, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
00119         assert(ofsp+ofs == p->member.para.nCharOfs);
00120         ofsp = p->member.para.nCharOfs;
00121         ofs = 0;
00122         break;
00123       case diRun:
00124         TRACE_(richedit_check)("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = \"%s\", flags=%08x, fx&mask = %08x\n",
00125           p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs,
00126           p->member.run.strText->nLen, debugstr_w(p->member.run.strText->szData),
00127           p->member.run.nFlags,
00128           p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects);
00129         assert(ofs == p->member.run.nCharOfs);
00130         assert(p->member.run.strText->nLen);
00131         ofs += p->member.run.strText->nLen;
00132         break;
00133       case diCell:
00134         TRACE_(richedit_check)("cell\n");
00135         break;
00136       default:
00137         assert(0);
00138     }
00139   } while(1);
00140 }
00141 
00142 /******************************************************************************
00143  * ME_CharOfsFromRunOfs
00144  *
00145  * Converts a character position relative to the start of the run, to a
00146  * character position relative to the start of the document.
00147  * Kind of a "local to global" offset conversion.
00148  */
00149 int ME_CharOfsFromRunOfs(ME_TextEditor *editor, const ME_DisplayItem *pPara,
00150                          const ME_DisplayItem *pRun, int nOfs)
00151 {
00152   assert(pRun && pRun->type == diRun);
00153   assert(pPara && pPara->type == diParagraph);
00154   return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs + nOfs;
00155 }
00156 
00157 /******************************************************************************
00158  * ME_CursorFromCharOfs
00159  *
00160  * Converts a character offset (relative to the start of the document) to
00161  * a cursor structure (which contains a run and a position relative to that
00162  * run).
00163  */
00164 void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor)
00165 {
00166   ME_RunOfsFromCharOfs(editor, nCharOfs, &pCursor->pPara,
00167                        &pCursor->pRun, &pCursor->nOffset);
00168 }
00169 
00170 /******************************************************************************
00171  * ME_RunOfsFromCharOfs
00172  *
00173  * Find a run and relative character offset given an absolute character offset
00174  * (absolute offset being an offset relative to the start of the document).
00175  * Kind of a "global to local" offset conversion.
00176  */
00177 void ME_RunOfsFromCharOfs(ME_TextEditor *editor,
00178                           int nCharOfs,
00179                           ME_DisplayItem **ppPara,
00180                           ME_DisplayItem **ppRun,
00181                           int *pOfs)
00182 {
00183   ME_DisplayItem *item, *next_item;
00184 
00185   nCharOfs = max(nCharOfs, 0);
00186   nCharOfs = min(nCharOfs, ME_GetTextLength(editor));
00187 
00188   /* Find the paragraph at the offset. */
00189   next_item = editor->pBuffer->pFirst->member.para.next_para;
00190   do {
00191     item = next_item;
00192     next_item = item->member.para.next_para;
00193   } while (next_item->member.para.nCharOfs <= nCharOfs);
00194   assert(item->type == diParagraph);
00195   nCharOfs -= item->member.para.nCharOfs;
00196   if (ppPara) *ppPara = item;
00197 
00198   /* Find the run at the offset. */
00199   next_item = ME_FindItemFwd(item, diRun);
00200   do {
00201     item = next_item;
00202     next_item = ME_FindItemFwd(item, diRunOrParagraphOrEnd);
00203   } while (next_item->type == diRun &&
00204            next_item->member.run.nCharOfs <= nCharOfs);
00205   assert(item->type == diRun);
00206   nCharOfs -= item->member.run.nCharOfs;
00207 
00208   if (ppRun) *ppRun = item;
00209   if (pOfs) *pOfs = nCharOfs;
00210 }
00211 
00212 /******************************************************************************
00213  * ME_JoinRuns
00214  * 
00215  * Merges two adjacent runs, the one given as a parameter and the next one.
00216  */    
00217 void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p)
00218 {
00219   ME_DisplayItem *pNext = p->next;
00220   int i;
00221   assert(p->type == diRun && pNext->type == diRun);
00222   assert(p->member.run.nCharOfs != -1);
00223   ME_GetParagraph(p)->member.para.nFlags |= MEPF_REWRAP;
00224 
00225   /* Update all cursors so that they don't contain the soon deleted run */
00226   for (i=0; i<editor->nCursors; i++) {
00227     if (editor->pCursors[i].pRun == pNext) {
00228       editor->pCursors[i].pRun = p;
00229       editor->pCursors[i].nOffset += p->member.run.strText->nLen;
00230     }
00231   }
00232 
00233   ME_AppendString(p->member.run.strText, pNext->member.run.strText);
00234   ME_Remove(pNext);
00235   ME_DestroyDisplayItem(pNext);
00236   ME_UpdateRunFlags(editor, &p->member.run);
00237   if(TRACE_ON(richedit))
00238   {
00239     TRACE("Before check after join\n");
00240     ME_CheckCharOffsets(editor);
00241     TRACE("After check after join\n");
00242   }
00243 }
00244 
00245 /******************************************************************************
00246  * ME_SplitRun
00247  *
00248  * Splits a run into two in a given place. It also updates the screen position
00249  * and size (extent) of the newly generated runs.
00250  */
00251 ME_DisplayItem *ME_SplitRun(ME_WrapContext *wc, ME_DisplayItem *item, int nVChar)
00252 {
00253   ME_TextEditor *editor = wc->context->editor;
00254   ME_Run *run, *run2;
00255   ME_Paragraph *para = &wc->pPara->member.para;
00256   ME_Cursor cursor = {wc->pPara, item, nVChar};
00257 
00258   assert(item->member.run.nCharOfs != -1);
00259   if(TRACE_ON(richedit))
00260   {
00261     TRACE("Before check before split\n");
00262     ME_CheckCharOffsets(editor);
00263     TRACE("After check before split\n");
00264   }
00265 
00266   run = &item->member.run;
00267 
00268   TRACE("Before split: %s(%d, %d)\n", debugstr_w(run->strText->szData),
00269         run->pt.x, run->pt.y);
00270 
00271   ME_SplitRunSimple(editor, &cursor);
00272 
00273   run2 = &cursor.pRun->member.run;
00274 
00275   ME_CalcRunExtent(wc->context, para, wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, run);
00276 
00277   run2->pt.x = run->pt.x+run->nWidth;
00278   run2->pt.y = run->pt.y;
00279 
00280   if(TRACE_ON(richedit))
00281   {
00282     TRACE("Before check after split\n");
00283     ME_CheckCharOffsets(editor);
00284     TRACE("After check after split\n");
00285     TRACE("After split: %s(%d, %d), %s(%d, %d)\n",
00286       debugstr_w(run->strText->szData), run->pt.x, run->pt.y,
00287       debugstr_w(run2->strText->szData), run2->pt.x, run2->pt.y);
00288   }
00289 
00290   return cursor.pRun;
00291 }
00292 
00293 /******************************************************************************
00294  * ME_SplitRunSimple
00295  *
00296  * Does the most basic job of splitting a run into two - it does not
00297  * update the positions and extents.
00298  */
00299 ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_Cursor *cursor)
00300 {
00301   ME_DisplayItem *run = cursor->pRun;
00302   ME_DisplayItem *new_run;
00303   int i;
00304   int nOffset = cursor->nOffset;
00305 
00306   assert(!(run->member.run.nFlags & MERF_NONTEXT));
00307 
00308   new_run = ME_MakeRun(run->member.run.style,
00309                        ME_VSplitString(run->member.run.strText, nOffset),
00310                        run->member.run.nFlags & MERF_SPLITMASK);
00311 
00312   new_run->member.run.nCharOfs = run->member.run.nCharOfs + nOffset;
00313   cursor->pRun = new_run;
00314   cursor->nOffset = 0;
00315 
00316   ME_InsertBefore(run->next, new_run);
00317 
00318   ME_UpdateRunFlags(editor, &run->member.run);
00319   ME_UpdateRunFlags(editor, &new_run->member.run);
00320   for (i = 0; i < editor->nCursors; i++) {
00321     if (editor->pCursors[i].pRun == run &&
00322         editor->pCursors[i].nOffset >= nOffset) {
00323       editor->pCursors[i].pRun = new_run;
00324       editor->pCursors[i].nOffset -= nOffset;
00325     }
00326   }
00327   cursor->pPara->member.para.nFlags |= MEPF_REWRAP;
00328   return run;
00329 }
00330 
00331 /******************************************************************************
00332  * ME_MakeRun
00333  * 
00334  * A helper function to create run structures quickly.
00335  */   
00336 ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags)
00337 {
00338   ME_DisplayItem *item = ME_MakeDI(diRun);
00339   item->member.run.style = s;
00340   item->member.run.ole_obj = NULL;
00341   item->member.run.strText = strData;
00342   item->member.run.nFlags = nFlags;
00343   item->member.run.nCharOfs = -1;
00344   ME_AddRefStyle(s);
00345   return item;
00346 }
00347 
00348 /******************************************************************************
00349  * ME_InsertRunAtCursor
00350  *
00351  * Inserts a new run with given style, flags and content at a given position,
00352  * which is passed as a cursor structure (which consists of a run and 
00353  * a run-relative character offset).
00354  */
00355 ME_DisplayItem *
00356 ME_InsertRunAtCursor(ME_TextEditor *editor, ME_Cursor *cursor, ME_Style *style,
00357                      const WCHAR *str, int len, int flags)
00358 {
00359   ME_DisplayItem *pDI;
00360   ME_UndoItem *pUI;
00361 
00362   if (cursor->nOffset)
00363     ME_SplitRunSimple(editor, cursor);
00364 
00365   pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
00366   if (pUI) {
00367     pUI->nStart = cursor->pPara->member.para.nCharOfs
00368                   + cursor->pRun->member.run.nCharOfs;
00369     pUI->nLen = len;
00370   }
00371 
00372   pDI = ME_MakeRun(style, ME_MakeStringN(str, len), flags);
00373   pDI->member.run.nCharOfs = cursor->pRun->member.run.nCharOfs;
00374   ME_InsertBefore(cursor->pRun, pDI);
00375   TRACE("Shift length:%d\n", len);
00376   ME_PropagateCharOffset(cursor->pRun, len);
00377   cursor->pPara->member.para.nFlags |= MEPF_REWRAP;
00378   return pDI;
00379 }
00380 
00381 /******************************************************************************
00382  * ME_UpdateRunFlags
00383  *
00384  * Determine some of run attributes given its content (style, text content).
00385  * Some flags cannot be determined by this function (MERF_GRAPHICS,
00386  * MERF_ENDPARA)
00387  */
00388 void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run)
00389 {
00390   ME_String *strText = run->strText;
00391   assert(run->nCharOfs >= 0);
00392 
00393   if (RUN_IS_HIDDEN(run) || run->nFlags & MERF_TABLESTART)
00394     run->nFlags |= MERF_HIDDEN;
00395   else
00396     run->nFlags &= ~MERF_HIDDEN;
00397 
00398   if (ME_IsSplitable(strText))
00399     run->nFlags |= MERF_SPLITTABLE;
00400   else
00401     run->nFlags &= ~MERF_SPLITTABLE;
00402 
00403   if (!(run->nFlags & MERF_NOTEXT)) {
00404     if (ME_IsWhitespaces(strText))
00405       run->nFlags |= MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE;
00406     else
00407     {
00408       run->nFlags &= ~MERF_WHITESPACE;
00409 
00410       if (ME_IsWSpace(strText->szData[0]))
00411         run->nFlags |= MERF_STARTWHITE;
00412       else
00413         run->nFlags &= ~MERF_STARTWHITE;
00414 
00415       if (ME_IsWSpace(strText->szData[strText->nLen - 1]))
00416         run->nFlags |= MERF_ENDWHITE;
00417       else
00418         run->nFlags &= ~MERF_ENDWHITE;
00419     }
00420   }
00421   else
00422     run->nFlags &= ~(MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE);
00423 }
00424 
00425 /******************************************************************************
00426  * ME_CharFromPoint
00427  * 
00428  * Returns a character position inside the run given a run-relative
00429  * pixel horizontal position. This version rounds left (ie. if the second
00430  * character is at pixel position 8, then for cx=0..7 it returns 0).  
00431  */     
00432 int ME_CharFromPoint(ME_Context *c, int cx, ME_Run *run)
00433 {
00434   int fit = 0;
00435   HGDIOBJ hOldFont;
00436   SIZE sz;
00437   if (!run->strText->nLen || cx <= 0)
00438     return 0;
00439 
00440   if (run->nFlags & MERF_TAB ||
00441       (run->nFlags & (MERF_ENDCELL|MERF_ENDPARA)) == MERF_ENDCELL)
00442   {
00443     if (cx < run->nWidth/2) 
00444       return 0;
00445     return 1;
00446   }
00447   if (run->nFlags & MERF_GRAPHICS)
00448   {
00449     SIZE sz;
00450     ME_GetOLEObjectSize(c, run, &sz);
00451     if (cx < sz.cx)
00452       return 0;
00453     return 1;
00454   }
00455   hOldFont = ME_SelectStyleFont(c, run->style);
00456   
00457   if (c->editor->cPasswordMask)
00458   {
00459     ME_String *strMasked = ME_MakeStringR(c->editor->cPasswordMask, run->strText->nLen);
00460     GetTextExtentExPointW(c->hDC, strMasked->szData, run->strText->nLen,
00461       cx, &fit, NULL, &sz);
00462     ME_DestroyString(strMasked);
00463   }
00464   else
00465   {
00466     GetTextExtentExPointW(c->hDC, run->strText->szData, run->strText->nLen,
00467       cx, &fit, NULL, &sz);
00468   }
00469   
00470   ME_UnselectStyleFont(c, run->style, hOldFont);
00471 
00472   return fit;
00473 }
00474 
00475 /******************************************************************************
00476  * ME_CharFromPointCursor
00477  *
00478  * Returns a character position inside the run given a run-relative
00479  * pixel horizontal position. This version rounds to the nearest character edge
00480  * (ie. if the second character is at pixel position 8, then for cx=0..3
00481  * it returns 0, and for cx=4..7 it returns 1).
00482  *
00483  * It is used for mouse click handling, for better usability (and compatibility
00484  * with the native control).
00485  */
00486 int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run)
00487 {
00488   ME_String *strRunText;
00489   /* This could point to either the run's real text, or it's masked form in a password control */
00490 
00491   int fit = 0;
00492   ME_Context c;
00493   HGDIOBJ hOldFont;
00494   SIZE sz, sz2, sz3;
00495   if (!run->strText->nLen || cx <= 0)
00496     return 0;
00497 
00498   if (run->nFlags & (MERF_TAB | MERF_ENDCELL))
00499   {
00500     if (cx < run->nWidth/2)
00501       return 0;
00502     return 1;
00503   }
00504   ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
00505   if (run->nFlags & MERF_GRAPHICS)
00506   {
00507     SIZE sz;
00508     ME_GetOLEObjectSize(&c, run, &sz);
00509     ME_DestroyContext(&c);
00510     if (cx < sz.cx/2)
00511       return 0;
00512     return 1;
00513   }
00514 
00515   if (editor->cPasswordMask)
00516     strRunText = ME_MakeStringR(editor->cPasswordMask, run->strText->nLen);
00517   else
00518     strRunText = run->strText;
00519 
00520   hOldFont = ME_SelectStyleFont(&c, run->style);
00521   GetTextExtentExPointW(c.hDC, strRunText->szData, strRunText->nLen,
00522                         cx, &fit, NULL, &sz);
00523   if (fit != strRunText->nLen)
00524   {
00525     GetTextExtentPoint32W(c.hDC, strRunText->szData, fit, &sz2);
00526     GetTextExtentPoint32W(c.hDC, strRunText->szData, fit + 1, &sz3);
00527     if (cx >= (sz2.cx+sz3.cx)/2)
00528       fit = fit + 1;
00529   }
00530 
00531   if (editor->cPasswordMask)
00532     ME_DestroyString(strRunText);
00533 
00534   ME_UnselectStyleFont(&c, run->style, hOldFont);
00535   ME_DestroyContext(&c);
00536   return fit;
00537 }
00538 
00539 /******************************************************************************
00540  * ME_GetTextExtent
00541  *
00542  * Finds a width and a height of the text using a specified style
00543  */
00544 static void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s, SIZE *size)
00545 {
00546   HGDIOBJ hOldFont;
00547   if (c->hDC) {
00548     hOldFont = ME_SelectStyleFont(c, s);
00549     GetTextExtentPoint32W(c->hDC, szText, nChars, size);
00550     ME_UnselectStyleFont(c, s, hOldFont);
00551   } else {
00552     size->cx = 0;
00553     size->cy = 0;
00554   }
00555 }
00556 
00557 /******************************************************************************
00558  * ME_PointFromChar
00559  *
00560  * Returns a run-relative pixel position given a run-relative character
00561  * position (character offset)
00562  */
00563 int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset)
00564 {
00565   SIZE size;
00566   ME_Context c;
00567   ME_String *strRunText;
00568   /* This could point to either the run's real text, or it's masked form in a password control */
00569 
00570   ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
00571   if (pRun->nFlags & MERF_GRAPHICS)
00572   {
00573     if (nOffset)
00574       ME_GetOLEObjectSize(&c, pRun, &size);
00575     ME_DestroyContext(&c);
00576     return nOffset != 0;
00577   } else if (pRun->nFlags & MERF_ENDPARA) {
00578     nOffset = 0;
00579   }
00580 
00581   if (editor->cPasswordMask)
00582     strRunText = ME_MakeStringR(editor->cPasswordMask, pRun->strText->nLen);
00583   else
00584     strRunText = pRun->strText;
00585 
00586   ME_GetTextExtent(&c,  strRunText->szData, nOffset, pRun->style, &size);
00587   ME_DestroyContext(&c);
00588   if (editor->cPasswordMask)
00589     ME_DestroyString(strRunText);
00590   return size.cx;
00591 }
00592 
00593 /******************************************************************************
00594  * ME_GetRunSizeCommon
00595  * 
00596  * Finds width, height, ascent and descent of a run, up to given character
00597  * (nLen).
00598  */
00599 static SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, int nLen,
00600                                 int startx, int *pAscent, int *pDescent)
00601 {
00602   SIZE size;
00603   int nMaxLen = run->strText->nLen;
00604 
00605   if (nLen>nMaxLen)
00606     nLen = nMaxLen;
00607 
00608   /* FIXME the following call also ensures that TEXTMETRIC structure is filled
00609    * this is wasteful for MERF_NONTEXT runs, but that shouldn't matter
00610    * in practice
00611    */
00612   
00613   if (c->editor->cPasswordMask)
00614   {
00615     ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,nLen);
00616     ME_GetTextExtent(c, szMasked->szData, nLen,run->style, &size); 
00617     ME_DestroyString(szMasked);
00618   }
00619   else
00620   {
00621     ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size);
00622   }
00623   *pAscent = run->style->tm.tmAscent;
00624   *pDescent = run->style->tm.tmDescent;
00625   size.cy = *pAscent + *pDescent;
00626 
00627   if (run->nFlags & MERF_TAB)
00628   {
00629     int pos = 0, i = 0, ppos, shift = 0;
00630     PARAFORMAT2 *pFmt = para->pFmt;
00631 
00632     if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
00633         pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
00634       /* The horizontal gap shifts the tab positions to leave the gap. */
00635       shift = pFmt->dxOffset * 2;
00636     do {
00637       if (i < pFmt->cTabCount)
00638       {
00639         /* Only one side of the horizontal gap is needed at the end of
00640          * the table row. */
00641         if (i == pFmt->cTabCount -1)
00642           shift = shift >> 1;
00643         pos = shift + (pFmt->rgxTabs[i]&0x00FFFFFF);
00644         i++;
00645       }
00646       else
00647       {
00648         pos += lDefaultTab - (pos % lDefaultTab);
00649       }
00650       ppos = ME_twips2pointsX(c, pos);
00651       if (ppos > startx + run->pt.x) {
00652         size.cx = ppos - startx - run->pt.x;
00653         break;
00654       }
00655     } while(1);
00656     size.cy = *pAscent + *pDescent;
00657     return size;
00658   }
00659   if (run->nFlags & MERF_GRAPHICS)
00660   {
00661     ME_GetOLEObjectSize(c, run, &size);
00662     if (size.cy > *pAscent)
00663       *pAscent = size.cy;
00664     /* descent is unchanged */
00665     return size;
00666   }
00667   return size;
00668 }
00669 
00670 /******************************************************************************
00671  * ME_GetRunSize
00672  * 
00673  * Finds width and height (but not ascent and descent) of a part of the run
00674  * up to given character.    
00675  */     
00676 SIZE ME_GetRunSize(ME_Context *c, const ME_Paragraph *para,
00677                    ME_Run *run, int nLen, int startx)
00678 {
00679   int asc, desc;
00680   return ME_GetRunSizeCommon(c, para, run, nLen, startx, &asc, &desc);
00681 }
00682 
00683 /******************************************************************************
00684  * ME_CalcRunExtent
00685  * 
00686  * Updates the size of the run (fills width, ascent and descent). The height
00687  * is calculated based on whole row's ascent and descent anyway, so no need
00688  * to use it here.        
00689  */     
00690 void ME_CalcRunExtent(ME_Context *c, const ME_Paragraph *para, int startx, ME_Run *run)
00691 {
00692   if (run->nFlags & MERF_HIDDEN)
00693     run->nWidth = 0;
00694   else
00695   {
00696     int nEnd = run->strText->nLen;
00697     SIZE size = ME_GetRunSizeCommon(c, para, run, nEnd, startx,
00698                                     &run->nAscent, &run->nDescent);
00699     run->nWidth = size.cx;
00700     if (!size.cx)
00701       WARN("size.cx == 0\n");
00702   }
00703 }
00704 
00705 /******************************************************************************
00706  * ME_SetSelectionCharFormat
00707  *
00708  * Applies a style change, either to a current selection, or to insert cursor
00709  * (ie. the style next typed characters will use).
00710  */
00711 void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
00712 {
00713   if (!ME_IsSelection(editor))
00714   {
00715     ME_Style *s;
00716     if (!editor->pBuffer->pCharStyle)
00717       editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0);
00718     s = ME_ApplyStyle(editor->pBuffer->pCharStyle, pFmt);
00719     ME_ReleaseStyle(editor->pBuffer->pCharStyle);
00720     editor->pBuffer->pCharStyle = s;
00721   } else {
00722     ME_Cursor *from, *to;
00723     ME_GetSelection(editor, &from, &to);
00724     ME_SetCharFormat(editor, from, to, pFmt);
00725   }
00726 }
00727 
00728 /******************************************************************************
00729  * ME_SetCharFormat
00730  *
00731  * Applies a style change to the specified part of the text
00732  *
00733  * The start and end cursors specify the part of the text.  These cursors will
00734  * be updated to stay valid, but this function may invalidate other
00735  * non-selection cursors. The end cursor may be NULL to specify all the text
00736  * following the start cursor.
00737  *
00738  * If no text is selected, then nothing is done.
00739  */
00740 void ME_SetCharFormat(ME_TextEditor *editor, ME_Cursor *start, ME_Cursor *end, CHARFORMAT2W *pFmt)
00741 {
00742   ME_DisplayItem *para;
00743   ME_DisplayItem *run;
00744   ME_DisplayItem *end_run = NULL;
00745 
00746   if (end && start->pRun == end->pRun && start->nOffset == end->nOffset)
00747     return;
00748 
00749   if (start->nOffset)
00750   {
00751     /* SplitRunSimple may or may not update the cursors, depending on whether they
00752      * are selection cursors, but we need to make sure they are valid. */
00753     int split_offset = start->nOffset;
00754     ME_DisplayItem *split_run = ME_SplitRunSimple(editor, start);
00755     if (end && end->pRun == split_run)
00756     {
00757       end->pRun = start->pRun;
00758       end->nOffset -= split_offset;
00759     }
00760   }
00761 
00762   if (end && end->nOffset)
00763     ME_SplitRunSimple(editor, end);
00764   end_run = end ? end->pRun : NULL;
00765 
00766   run = start->pRun;
00767   para = start->pPara;
00768   para->member.para.nFlags |= MEPF_REWRAP;
00769 
00770   while(run != end_run)
00771   {
00772     ME_UndoItem *undo = NULL;
00773     ME_Style *new_style = ME_ApplyStyle(run->member.run.style, pFmt);
00774     /* ME_DumpStyle(new_style); */
00775     undo = ME_AddUndoItem(editor, diUndoSetCharFormat, NULL);
00776     if (undo) {
00777       undo->nStart = run->member.run.nCharOfs+para->member.para.nCharOfs;
00778       undo->nLen = run->member.run.strText->nLen;
00779       undo->di.member.ustyle = run->member.run.style;
00780       /* we'd have to addref undo...ustyle and release tmp...style
00781          but they'd cancel each other out so we can do nothing instead */
00782     }
00783     else
00784       ME_ReleaseStyle(run->member.run.style);
00785     run->member.run.style = new_style;
00786     run = ME_FindItemFwd(run, diRunOrParagraph);
00787     if (run && run->type == diParagraph)
00788     {
00789       para = run;
00790       run = ME_FindItemFwd(run, diRun);
00791       if (run != end_run)
00792         para->member.para.nFlags |= MEPF_REWRAP;
00793     }
00794   }
00795 }
00796 
00797 /******************************************************************************
00798  * ME_SetDefaultCharFormat
00799  * 
00800  * Applies a style change to the default character style.
00801  */     
00802 void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod)
00803 {
00804   ME_Style *style;
00805 
00806   assert(mod->cbSize == sizeof(CHARFORMAT2W));
00807   style = ME_ApplyStyle(editor->pBuffer->pDefaultStyle, mod);
00808   editor->pBuffer->pDefaultStyle->fmt = style->fmt;
00809   editor->pBuffer->pDefaultStyle->tm = style->tm;
00810   ME_ReleaseStyle(style);
00811   ME_MarkAllForWrapping(editor);
00812   /*  pcf = editor->pBuffer->pDefaultStyle->fmt; */
00813 }
00814 
00815 static void ME_GetRunCharFormat(ME_TextEditor *editor, ME_DisplayItem *run, CHARFORMAT2W *pFmt)
00816 {
00817   ME_CopyCharFormat(pFmt, &run->member.run.style->fmt);
00818   if ((pFmt->dwMask & CFM_UNDERLINETYPE) && (pFmt->bUnderlineType == CFU_CF1UNDERLINE))
00819   {
00820     pFmt->dwMask |= CFM_UNDERLINE;
00821     pFmt->dwEffects |= CFE_UNDERLINE;
00822   }
00823   if ((pFmt->dwMask & CFM_UNDERLINETYPE) && (pFmt->bUnderlineType == CFU_UNDERLINENONE))
00824   {
00825     pFmt->dwMask |= CFM_UNDERLINE;
00826     pFmt->dwEffects &= ~CFE_UNDERLINE;
00827   }
00828 }
00829 
00830 /******************************************************************************
00831  * ME_GetDefaultCharFormat
00832  * 
00833  * Retrieves the current default character style (the one applied where no
00834  * other style was applied) .
00835  */     
00836 void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
00837 {
00838   ME_CopyCharFormat(pFmt, &editor->pBuffer->pDefaultStyle->fmt);
00839 }
00840 
00841 /******************************************************************************
00842  * ME_GetSelectionCharFormat
00843  *
00844  * If selection exists, it returns all style elements that are set consistently
00845  * in the whole selection. If not, it just returns the current style.
00846  */
00847 void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
00848 {
00849   ME_Cursor *from, *to;
00850   if (!ME_IsSelection(editor) && editor->pBuffer->pCharStyle)
00851   {
00852     ME_CopyCharFormat(pFmt, &editor->pBuffer->pCharStyle->fmt);
00853     return;
00854   }
00855   ME_GetSelection(editor, &from, &to);
00856   ME_GetCharFormat(editor, from, to, pFmt);
00857 }
00858 
00859 /******************************************************************************
00860  * ME_GetCharFormat
00861  *
00862  * Returns the style consisting of those attributes which are consistently set
00863  * in the whole character range.
00864  */
00865 void ME_GetCharFormat(ME_TextEditor *editor, const ME_Cursor *from,
00866                       const ME_Cursor *to, CHARFORMAT2W *pFmt)
00867 {
00868   ME_DisplayItem *run, *run_end;
00869   CHARFORMAT2W tmp;
00870 
00871   run = from->pRun;
00872   /* special case - if selection is empty, take previous char's formatting */
00873   if (from->pRun == to->pRun && from->nOffset == to->nOffset)
00874   {
00875     if (!from->nOffset)
00876     {
00877       ME_DisplayItem *tmp_run = ME_FindItemBack(run, diRunOrParagraph);
00878       if (tmp_run->type == diRun) {
00879         ME_GetRunCharFormat(editor, tmp_run, pFmt);
00880         return;
00881       }
00882     }
00883     ME_GetRunCharFormat(editor, run, pFmt);
00884     return;
00885   }
00886 
00887   run_end = to->pRun;
00888   if (!to->nOffset)
00889     run_end = ME_FindItemBack(run_end, diRun);
00890 
00891   ME_GetRunCharFormat(editor, run, pFmt);
00892 
00893   if (run == run_end) return;
00894 
00895   do {
00896     /* FIXME add more style feature comparisons */
00897     DWORD dwAttribs = CFM_SIZE | CFM_FACE | CFM_COLOR | CFM_UNDERLINETYPE;
00898     DWORD dwEffects = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_PROTECTED | CFM_LINK | CFM_SUPERSCRIPT;
00899 
00900     run = ME_FindItemFwd(run, diRun);
00901 
00902     ZeroMemory(&tmp, sizeof(tmp));
00903     tmp.cbSize = sizeof(tmp);
00904     ME_GetRunCharFormat(editor, run, &tmp);
00905 
00906     assert((tmp.dwMask & dwAttribs) == dwAttribs);
00907     /* reset flags that differ */
00908 
00909     if (pFmt->yHeight != tmp.yHeight)
00910       pFmt->dwMask &= ~CFM_SIZE;
00911     if (pFmt->dwMask & CFM_FACE)
00912     {
00913       if (!(tmp.dwMask & CFM_FACE))
00914         pFmt->dwMask &= ~CFM_FACE;
00915       else if (lstrcmpW(pFmt->szFaceName, tmp.szFaceName) ||
00916           pFmt->bPitchAndFamily != tmp.bPitchAndFamily)
00917         pFmt->dwMask &= ~CFM_FACE;
00918     }
00919     if (pFmt->yHeight != tmp.yHeight)
00920       pFmt->dwMask &= ~CFM_SIZE;
00921     if (pFmt->bUnderlineType != tmp.bUnderlineType)
00922       pFmt->dwMask &= ~CFM_UNDERLINETYPE;
00923     if (pFmt->dwMask & CFM_COLOR)
00924     {
00925       if (!((pFmt->dwEffects&CFE_AUTOCOLOR) & (tmp.dwEffects&CFE_AUTOCOLOR)))
00926       {
00927         if (pFmt->crTextColor != tmp.crTextColor)
00928           pFmt->dwMask &= ~CFM_COLOR;
00929       }
00930     }
00931 
00932     pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & dwEffects);
00933     pFmt->dwEffects = tmp.dwEffects;
00934 
00935   } while(run != run_end);
00936 }

Generated on Sat May 26 2012 04:16:29 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.