Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenrun.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
1.7.6.1
|