Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenpaint.c
Go to the documentation of this file.
00001 /* 00002 * RichEdit - painting functions 00003 * 00004 * Copyright 2004 by Krzysztof Foltman 00005 * Copyright 2005 by Phil Krylov 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 #include "editor.h" 00023 00024 WINE_DEFAULT_DEBUG_CHANNEL(richedit); 00025 00026 static void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph); 00027 00028 void ME_PaintContent(ME_TextEditor *editor, HDC hDC, const RECT *rcUpdate) 00029 { 00030 ME_DisplayItem *item; 00031 ME_Context c; 00032 int ys, ye; 00033 HRGN oldRgn; 00034 00035 oldRgn = CreateRectRgn(0, 0, 0, 0); 00036 if (!GetClipRgn(hDC, oldRgn)) 00037 { 00038 DeleteObject(oldRgn); 00039 oldRgn = NULL; 00040 } 00041 IntersectClipRect(hDC, rcUpdate->left, rcUpdate->top, 00042 rcUpdate->right, rcUpdate->bottom); 00043 00044 editor->nSequence++; 00045 ME_InitContext(&c, editor, hDC); 00046 SetBkMode(hDC, TRANSPARENT); 00047 ME_MoveCaret(editor); 00048 item = editor->pBuffer->pFirst->next; 00049 /* This context point is an offset for the paragraph positions stored 00050 * during wrapping. It shouldn't be modified during painting. */ 00051 c.pt.x = c.rcView.left - editor->horz_si.nPos; 00052 c.pt.y = c.rcView.top - editor->vert_si.nPos; 00053 while(item != editor->pBuffer->pLast) 00054 { 00055 assert(item->type == diParagraph); 00056 00057 ys = c.pt.y + item->member.para.pt.y; 00058 if (item->member.para.pCell 00059 != item->member.para.next_para->member.para.pCell) 00060 { 00061 ME_Cell *cell = NULL; 00062 cell = &ME_FindItemBack(item->member.para.next_para, diCell)->member.cell; 00063 ye = c.pt.y + cell->pt.y + cell->nHeight; 00064 } else { 00065 ye = ys + item->member.para.nHeight; 00066 } 00067 if (item->member.para.pCell && !(item->member.para.nFlags & MEPF_ROWEND) && 00068 item->member.para.pCell != item->member.para.prev_para->member.para.pCell) 00069 { 00070 /* the border shifts the text down */ 00071 ys -= item->member.para.pCell->member.cell.yTextOffset; 00072 } 00073 00074 /* Draw the paragraph if any of the paragraph is in the update region. */ 00075 if (ys < rcUpdate->bottom && ye > rcUpdate->top) 00076 ME_DrawParagraph(&c, item); 00077 item = item->member.para.next_para; 00078 } 00079 if (c.pt.y + editor->nTotalLength < c.rcView.bottom) 00080 { 00081 /* Fill space after the end of the text. */ 00082 RECT rc; 00083 rc.top = c.pt.y + editor->nTotalLength; 00084 rc.left = c.rcView.left; 00085 rc.bottom = c.rcView.bottom; 00086 rc.right = c.rcView.right; 00087 00088 IntersectRect(&rc, &rc, rcUpdate); 00089 00090 if (!IsRectEmpty(&rc)) 00091 FillRect(hDC, &rc, c.editor->hbrBackground); 00092 } 00093 if (editor->nTotalLength != editor->nLastTotalLength || 00094 editor->nTotalWidth != editor->nLastTotalWidth) 00095 ME_SendRequestResize(editor, FALSE); 00096 editor->nLastTotalLength = editor->nTotalLength; 00097 editor->nLastTotalWidth = editor->nTotalWidth; 00098 00099 SelectClipRgn(hDC, oldRgn); 00100 if (oldRgn) 00101 DeleteObject(oldRgn); 00102 00103 c.hDC = NULL; 00104 ME_DestroyContext(&c); 00105 } 00106 00107 void ME_Repaint(ME_TextEditor *editor) 00108 { 00109 if (ME_WrapMarkedParagraphs(editor)) 00110 { 00111 ME_UpdateScrollBar(editor); 00112 FIXME("ME_Repaint had to call ME_WrapMarkedParagraphs\n"); 00113 } 00114 ITextHost_TxViewChange(editor->texthost, TRUE); 00115 } 00116 00117 void ME_UpdateRepaint(ME_TextEditor *editor, BOOL update_now) 00118 { 00119 /* Should be called whenever the contents of the control have changed */ 00120 BOOL wrappedParagraphs; 00121 00122 wrappedParagraphs = ME_WrapMarkedParagraphs(editor); 00123 if (wrappedParagraphs) 00124 ME_UpdateScrollBar(editor); 00125 00126 /* Ensure that the cursor is visible */ 00127 ME_EnsureVisible(editor, &editor->pCursors[0]); 00128 00129 ITextHost_TxViewChange(editor->texthost, update_now); 00130 00131 ME_SendSelChange(editor); 00132 00133 /* send EN_CHANGE if the event mask asks for it */ 00134 if(editor->nEventMask & ENM_CHANGE) 00135 { 00136 editor->nEventMask &= ~ENM_CHANGE; 00137 ME_SendOldNotify(editor, EN_CHANGE); 00138 editor->nEventMask |= ENM_CHANGE; 00139 } 00140 } 00141 00142 void 00143 ME_RewrapRepaint(ME_TextEditor *editor) 00144 { 00145 /* RewrapRepaint should be called whenever the control has changed in 00146 * looks, but not content. Like resizing. */ 00147 00148 ME_MarkAllForWrapping(editor); 00149 ME_WrapMarkedParagraphs(editor); 00150 ME_UpdateScrollBar(editor); 00151 ME_Repaint(editor); 00152 } 00153 00154 int ME_twips2pointsX(const ME_Context *c, int x) 00155 { 00156 if (c->editor->nZoomNumerator == 0) 00157 return x * c->dpi.cx / 1440; 00158 else 00159 return x * c->dpi.cx * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator; 00160 } 00161 00162 int ME_twips2pointsY(const ME_Context *c, int y) 00163 { 00164 if (c->editor->nZoomNumerator == 0) 00165 return y * c->dpi.cy / 1440; 00166 else 00167 return y * c->dpi.cy * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator; 00168 } 00169 00170 static void ME_HighlightSpace(ME_Context *c, int x, int y, LPCWSTR szText, 00171 int nChars, ME_Style *s, int width, 00172 int nSelFrom, int nSelTo, int ymin, int cy) 00173 { 00174 HDC hDC = c->hDC; 00175 HGDIOBJ hOldFont = NULL; 00176 SIZE sz; 00177 int selWidth; 00178 /* Only highlight if there is a selection in the run and when 00179 * EM_HIDESELECTION is not being used to hide the selection. */ 00180 if (nSelFrom >= nChars || nSelTo < 0 || nSelFrom >= nSelTo 00181 || c->editor->bHideSelection) 00182 return; 00183 hOldFont = ME_SelectStyleFont(c, s); 00184 if (width <= 0) 00185 { 00186 GetTextExtentPoint32W(hDC, szText, nChars, &sz); 00187 width = sz.cx; 00188 } 00189 if (nSelFrom < 0) nSelFrom = 0; 00190 if (nSelTo > nChars) nSelTo = nChars; 00191 GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz); 00192 x += sz.cx; 00193 if (nSelTo != nChars) 00194 { 00195 GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz); 00196 selWidth = sz.cx; 00197 } else { 00198 selWidth = width - sz.cx; 00199 } 00200 ME_UnselectStyleFont(c, s, hOldFont); 00201 00202 if (c->editor->bEmulateVersion10) 00203 PatBlt(hDC, x, ymin, selWidth, cy, DSTINVERT); 00204 else 00205 { 00206 RECT rect; 00207 HBRUSH hBrush; 00208 rect.left = x; 00209 rect.top = ymin; 00210 rect.right = x + selWidth; 00211 rect.bottom = ymin + cy; 00212 hBrush = CreateSolidBrush(ITextHost_TxGetSysColor(c->editor->texthost, 00213 COLOR_HIGHLIGHT)); 00214 FillRect(hDC, &rect, hBrush); 00215 DeleteObject(hBrush); 00216 } 00217 } 00218 00219 static void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText, 00220 int nChars, ME_Style *s, int width, 00221 int nSelFrom, int nSelTo, int ymin, int cy) 00222 { 00223 HDC hDC = c->hDC; 00224 HGDIOBJ hOldFont; 00225 COLORREF rgbOld; 00226 int yOffset = 0, yTwipsOffset = 0; 00227 SIZE sz; 00228 COLORREF rgb; 00229 HPEN hPen = NULL, hOldPen = NULL; 00230 BOOL bHighlightedText = (nSelFrom < nChars && nSelTo >= 0 00231 && nSelFrom < nSelTo && !c->editor->bHideSelection); 00232 int xSelStart = x, xSelEnd = x; 00233 int *lpDx = NULL; 00234 /* lpDx is only needed for tabs to make sure the underline done automatically 00235 * by the font extends to the end of the tab. Tabs are always stored as 00236 * a single character run, so we can handle this case separately, since 00237 * otherwise lpDx would need to specify the lengths of each character. */ 00238 if (width && nChars == 1) 00239 lpDx = &width; /* Make sure underline for tab extends across tab space */ 00240 00241 hOldFont = ME_SelectStyleFont(c, s); 00242 if ((s->fmt.dwMask & s->fmt.dwEffects) & CFM_OFFSET) { 00243 yTwipsOffset = s->fmt.yOffset; 00244 } 00245 if ((s->fmt.dwMask & s->fmt.dwEffects) & (CFM_SUPERSCRIPT | CFM_SUBSCRIPT)) { 00246 if (s->fmt.dwEffects & CFE_SUPERSCRIPT) yTwipsOffset = s->fmt.yHeight/3; 00247 if (s->fmt.dwEffects & CFE_SUBSCRIPT) yTwipsOffset = -s->fmt.yHeight/12; 00248 } 00249 if (yTwipsOffset) 00250 yOffset = ME_twips2pointsY(c, yTwipsOffset); 00251 00252 if ((s->fmt.dwMask & CFM_LINK) && (s->fmt.dwEffects & CFE_LINK)) 00253 rgb = RGB(0,0,255); 00254 else if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR)) 00255 rgb = ITextHost_TxGetSysColor(c->editor->texthost, COLOR_WINDOWTEXT); 00256 else 00257 rgb = s->fmt.crTextColor; 00258 00259 /* Determine the area that is selected in the run. */ 00260 GetTextExtentPoint32W(hDC, szText, nChars, &sz); 00261 /* Treat width as an optional parameter. We can get the width from the 00262 * text extent of the string if it isn't specified. */ 00263 if (!width) width = sz.cx; 00264 if (bHighlightedText) 00265 { 00266 if (nSelFrom <= 0) 00267 { 00268 nSelFrom = 0; 00269 } 00270 else 00271 { 00272 GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz); 00273 xSelStart = x + sz.cx; 00274 } 00275 if (nSelTo >= nChars) 00276 { 00277 nSelTo = nChars; 00278 xSelEnd = x + width; 00279 } 00280 else 00281 { 00282 GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz); 00283 xSelEnd = xSelStart + sz.cx; 00284 } 00285 } 00286 00287 /* Choose the pen type for underlining the text. */ 00288 if (s->fmt.dwMask & CFM_UNDERLINETYPE) 00289 { 00290 switch (s->fmt.bUnderlineType) 00291 { 00292 case CFU_UNDERLINE: 00293 case CFU_UNDERLINEWORD: /* native seems to map it to simple underline (MSDN) */ 00294 case CFU_UNDERLINEDOUBLE: /* native seems to map it to simple underline (MSDN) */ 00295 hPen = CreatePen(PS_SOLID, 1, rgb); 00296 break; 00297 case CFU_UNDERLINEDOTTED: 00298 hPen = CreatePen(PS_DOT, 1, rgb); 00299 break; 00300 default: 00301 FIXME("Unknown underline type (%u)\n", s->fmt.bUnderlineType); 00302 /* fall through */ 00303 case CFU_CF1UNDERLINE: /* this type is supported in the font, do nothing */ 00304 case CFU_UNDERLINENONE: 00305 hPen = NULL; 00306 break; 00307 } 00308 if (hPen) 00309 { 00310 hOldPen = SelectObject(hDC, hPen); 00311 } 00312 } 00313 00314 rgbOld = SetTextColor(hDC, rgb); 00315 if (bHighlightedText && !c->editor->bEmulateVersion10) 00316 { 00317 COLORREF rgbBackOld; 00318 RECT dim; 00319 /* FIXME: should use textmetrics info for Descent info */ 00320 if (hPen) 00321 MoveToEx(hDC, x, y - yOffset + 1, NULL); 00322 if (xSelStart > x) 00323 { 00324 ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nSelFrom, NULL); 00325 if (hPen) 00326 LineTo(hDC, xSelStart, y - yOffset + 1); 00327 } 00328 dim.top = ymin; 00329 dim.bottom = ymin + cy; 00330 dim.left = xSelStart; 00331 dim.right = xSelEnd; 00332 SetTextColor(hDC, ITextHost_TxGetSysColor(c->editor->texthost, 00333 COLOR_HIGHLIGHTTEXT)); 00334 rgbBackOld = SetBkColor(hDC, ITextHost_TxGetSysColor(c->editor->texthost, 00335 COLOR_HIGHLIGHT)); 00336 ExtTextOutW(hDC, xSelStart, y-yOffset, ETO_OPAQUE, &dim, 00337 szText+nSelFrom, nSelTo-nSelFrom, lpDx); 00338 if (hPen) 00339 LineTo(hDC, xSelEnd, y - yOffset + 1); 00340 SetBkColor(hDC, rgbBackOld); 00341 if (xSelEnd < x + width) 00342 { 00343 SetTextColor(hDC, rgb); 00344 ExtTextOutW(hDC, xSelEnd, y-yOffset, 0, NULL, szText+nSelTo, 00345 nChars-nSelTo, NULL); 00346 if (hPen) 00347 LineTo(hDC, x + width, y - yOffset + 1); 00348 } 00349 } 00350 else 00351 { 00352 ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nChars, lpDx); 00353 00354 /* FIXME: should use textmetrics info for Descent info */ 00355 if (hPen) 00356 { 00357 MoveToEx(hDC, x, y - yOffset + 1, NULL); 00358 LineTo(hDC, x + width, y - yOffset + 1); 00359 } 00360 00361 if (bHighlightedText) /* v1.0 inverts the selection */ 00362 { 00363 PatBlt(hDC, xSelStart, ymin, xSelEnd-xSelStart, cy, DSTINVERT); 00364 } 00365 } 00366 00367 if (hPen) 00368 { 00369 SelectObject(hDC, hOldPen); 00370 DeleteObject(hPen); 00371 } 00372 SetTextColor(hDC, rgbOld); 00373 ME_UnselectStyleFont(c, s, hOldFont); 00374 } 00375 00376 static void ME_DebugWrite(HDC hDC, const POINT *pt, LPCWSTR szText) { 00377 int align = SetTextAlign(hDC, TA_LEFT|TA_TOP); 00378 HGDIOBJ hFont = SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT)); 00379 COLORREF color = SetTextColor(hDC, RGB(128,128,128)); 00380 TextOutW(hDC, pt->x, pt->y, szText, lstrlenW(szText)); 00381 SelectObject(hDC, hFont); 00382 SetTextAlign(hDC, align); 00383 SetTextColor(hDC, color); 00384 } 00385 00386 static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Paragraph *para) 00387 { 00388 ME_Run *run = &rundi->member.run; 00389 ME_DisplayItem *start; 00390 int runofs = run->nCharOfs+para->nCharOfs; 00391 int nSelFrom, nSelTo; 00392 const WCHAR wszSpace[] = {' ', 0}; 00393 00394 if (run->nFlags & MERF_HIDDEN) 00395 return; 00396 00397 start = ME_FindItemBack(rundi, diStartRow); 00398 ME_GetSelectionOfs(c->editor, &nSelFrom, &nSelTo); 00399 00400 /* Draw selected end-of-paragraph mark */ 00401 if (run->nFlags & MERF_ENDPARA) 00402 { 00403 if (runofs >= nSelFrom && runofs < nSelTo) 00404 { 00405 ME_HighlightSpace(c, x, y, wszSpace, 1, run->style, 0, 0, 1, 00406 c->pt.y + para->pt.y + start->member.row.pt.y, 00407 start->member.row.nHeight); 00408 } 00409 return; 00410 } 00411 00412 if (run->nFlags & (MERF_TAB | MERF_ENDCELL)) 00413 { 00414 /* wszSpace is used instead of the tab character because otherwise 00415 * an unwanted symbol can be inserted instead. */ 00416 ME_DrawTextWithStyle(c, x, y, wszSpace, 1, run->style, run->nWidth, 00417 nSelFrom-runofs, nSelTo-runofs, 00418 c->pt.y + para->pt.y + start->member.row.pt.y, 00419 start->member.row.nHeight); 00420 return; 00421 } 00422 00423 if (run->nFlags & MERF_GRAPHICS) 00424 ME_DrawOLE(c, x, y, run, para, (runofs >= nSelFrom) && (runofs < nSelTo)); 00425 else 00426 { 00427 if (c->editor->cPasswordMask) 00428 { 00429 ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask, run->strText->nLen); 00430 ME_DrawTextWithStyle(c, x, y, 00431 szMasked->szData, szMasked->nLen, run->style, run->nWidth, 00432 nSelFrom-runofs,nSelTo-runofs, 00433 c->pt.y + para->pt.y + start->member.row.pt.y, 00434 start->member.row.nHeight); 00435 ME_DestroyString(szMasked); 00436 } 00437 else 00438 ME_DrawTextWithStyle(c, x, y, 00439 run->strText->szData, run->strText->nLen, run->style, run->nWidth, 00440 nSelFrom-runofs,nSelTo-runofs, 00441 c->pt.y + para->pt.y + start->member.row.pt.y, 00442 start->member.row.nHeight); 00443 } 00444 } 00445 00446 /* The documented widths are in points (72 dpi), but converting them to 00447 * 96 dpi (standard display resolution) avoids dealing with fractions. */ 00448 static const struct {unsigned width : 8, pen_style : 4, dble : 1;} border_details[] = { 00449 /* none */ {0, PS_SOLID, FALSE}, 00450 /* 3/4 */ {1, PS_SOLID, FALSE}, 00451 /* 1 1/2 */ {2, PS_SOLID, FALSE}, 00452 /* 2 1/4 */ {3, PS_SOLID, FALSE}, 00453 /* 3 */ {4, PS_SOLID, FALSE}, 00454 /* 4 1/2 */ {6, PS_SOLID, FALSE}, 00455 /* 6 */ {8, PS_SOLID, FALSE}, 00456 /* 3/4 double */ {1, PS_SOLID, TRUE}, 00457 /* 1 1/2 double */ {2, PS_SOLID, TRUE}, 00458 /* 2 1/4 double */ {3, PS_SOLID, TRUE}, 00459 /* 3/4 gray */ {1, PS_DOT /* FIXME */, FALSE}, 00460 /* 1 1/2 dashed */ {2, PS_DASH, FALSE}, 00461 }; 00462 00463 static const COLORREF pen_colors[16] = { 00464 /* Black */ RGB(0x00, 0x00, 0x00), /* Blue */ RGB(0x00, 0x00, 0xFF), 00465 /* Cyan */ RGB(0x00, 0xFF, 0xFF), /* Green */ RGB(0x00, 0xFF, 0x00), 00466 /* Magenta */ RGB(0xFF, 0x00, 0xFF), /* Red */ RGB(0xFF, 0x00, 0x00), 00467 /* Yellow */ RGB(0xFF, 0xFF, 0x00), /* White */ RGB(0xFF, 0xFF, 0xFF), 00468 /* Dark blue */ RGB(0x00, 0x00, 0x80), /* Dark cyan */ RGB(0x00, 0x80, 0x80), 00469 /* Dark green */ RGB(0x00, 0x80, 0x80), /* Dark magenta */ RGB(0x80, 0x00, 0x80), 00470 /* Dark red */ RGB(0x80, 0x00, 0x00), /* Dark yellow */ RGB(0x80, 0x80, 0x00), 00471 /* Dark gray */ RGB(0x80, 0x80, 0x80), /* Light gray */ RGB(0xc0, 0xc0, 0xc0), 00472 }; 00473 00474 static int ME_GetBorderPenWidth(const ME_Context* c, int idx) 00475 { 00476 int width = border_details[idx].width; 00477 00478 if (c->dpi.cx != 96) 00479 width = MulDiv(width, c->dpi.cx, 96); 00480 00481 if (c->editor->nZoomNumerator != 0) 00482 width = MulDiv(width, c->editor->nZoomNumerator, c->editor->nZoomDenominator); 00483 00484 return width; 00485 } 00486 00487 int ME_GetParaBorderWidth(const ME_Context* c, int flags) 00488 { 00489 int idx = (flags >> 8) & 0xF; 00490 int width; 00491 00492 if (idx >= sizeof(border_details) / sizeof(border_details[0])) 00493 { 00494 FIXME("Unsupported border value %d\n", idx); 00495 return 0; 00496 } 00497 width = ME_GetBorderPenWidth(c, idx); 00498 if (border_details[idx].dble) width = width * 2 + 1; 00499 return width; 00500 } 00501 00502 static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT* bounds) 00503 { 00504 int idx, border_width, top_border, bottom_border; 00505 RECT rc; 00506 BOOL hasParaBorder; 00507 00508 SetRectEmpty(bounds); 00509 if (!(para->pFmt->dwMask & (PFM_BORDER | PFM_SPACEBEFORE | PFM_SPACEAFTER))) return; 00510 00511 border_width = top_border = bottom_border = 0; 00512 idx = (para->pFmt->wBorders >> 8) & 0xF; 00513 hasParaBorder = (!(c->editor->bEmulateVersion10 && 00514 para->pFmt->dwMask & PFM_TABLE && 00515 para->pFmt->wEffects & PFE_TABLE) && 00516 (para->pFmt->dwMask & PFM_BORDER) && 00517 idx != 0 && 00518 (para->pFmt->wBorders & 0xF)); 00519 if (hasParaBorder) 00520 { 00521 /* FIXME: wBorders is not stored as MSDN says in v1.0 - 4.1 of richedit 00522 * controls. It actually stores the paragraph or row border style. Although 00523 * the value isn't used for drawing, it is used for streaming out rich text. 00524 * 00525 * wBorders stores the border style for each side (top, left, bottom, right) 00526 * using nibble (4 bits) to store each border style. The rich text format 00527 * control words, and their associated value are the following: 00528 * \brdrdash 0 00529 * \brdrdashsm 1 00530 * \brdrdb 2 00531 * \brdrdot 3 00532 * \brdrhair 4 00533 * \brdrs 5 00534 * \brdrth 6 00535 * \brdrtriple 7 00536 * 00537 * The order of the sides stored actually differs from v1.0 to 3.0 and v4.1. 00538 * The mask corresponding to each side for the version are the following: 00539 * mask v1.0-3.0 v4.1 00540 * 0x000F top left 00541 * 0x00F0 left top 00542 * 0x0F00 bottom right 00543 * 0xF000 right bottom 00544 */ 00545 if (para->pFmt->wBorders & 0x00B0) 00546 FIXME("Unsupported border flags %x\n", para->pFmt->wBorders); 00547 border_width = ME_GetParaBorderWidth(c, para->pFmt->wBorders); 00548 if (para->pFmt->wBorders & 4) top_border = border_width; 00549 if (para->pFmt->wBorders & 8) bottom_border = border_width; 00550 } 00551 00552 if (para->pFmt->dwMask & PFM_SPACEBEFORE) 00553 { 00554 rc.left = c->rcView.left; 00555 rc.right = c->rcView.right; 00556 rc.top = y; 00557 bounds->top = ME_twips2pointsY(c, para->pFmt->dySpaceBefore); 00558 rc.bottom = y + bounds->top + top_border; 00559 FillRect(c->hDC, &rc, c->editor->hbrBackground); 00560 } 00561 00562 if (para->pFmt->dwMask & PFM_SPACEAFTER) 00563 { 00564 rc.left = c->rcView.left; 00565 rc.right = c->rcView.right; 00566 rc.bottom = y + para->nHeight; 00567 bounds->bottom = ME_twips2pointsY(c, para->pFmt->dySpaceAfter); 00568 rc.top = rc.bottom - bounds->bottom - bottom_border; 00569 FillRect(c->hDC, &rc, c->editor->hbrBackground); 00570 } 00571 00572 /* Native richedit doesn't support paragraph borders in v1.0 - 4.1, 00573 * but might support it in later versions. */ 00574 if (hasParaBorder) { 00575 int pen_width, rightEdge; 00576 COLORREF pencr; 00577 HPEN pen = NULL, oldpen = NULL; 00578 POINT pt; 00579 00580 if (para->pFmt->wBorders & 64) /* autocolor */ 00581 pencr = ITextHost_TxGetSysColor(c->editor->texthost, 00582 COLOR_WINDOWTEXT); 00583 else 00584 pencr = pen_colors[(para->pFmt->wBorders >> 12) & 0xF]; 00585 00586 rightEdge = c->pt.x + max(c->editor->sizeWindow.cx, 00587 c->editor->nTotalWidth); 00588 00589 pen_width = ME_GetBorderPenWidth(c, idx); 00590 pen = CreatePen(border_details[idx].pen_style, pen_width, pencr); 00591 oldpen = SelectObject(c->hDC, pen); 00592 MoveToEx(c->hDC, 0, 0, &pt); 00593 00594 /* before & after spaces are not included in border */ 00595 00596 /* helper to draw the double lines in case of corner */ 00597 #define DD(x) ((para->pFmt->wBorders & (x)) ? (pen_width + 1) : 0) 00598 00599 if (para->pFmt->wBorders & 1) 00600 { 00601 MoveToEx(c->hDC, c->pt.x, y + bounds->top, NULL); 00602 LineTo(c->hDC, c->pt.x, y + para->nHeight - bounds->bottom); 00603 if (border_details[idx].dble) { 00604 rc.left = c->pt.x + 1; 00605 rc.right = rc.left + border_width; 00606 rc.top = y + bounds->top; 00607 rc.bottom = y + para->nHeight - bounds->bottom; 00608 FillRect(c->hDC, &rc, c->editor->hbrBackground); 00609 MoveToEx(c->hDC, c->pt.x + pen_width + 1, y + bounds->top + DD(4), NULL); 00610 LineTo(c->hDC, c->pt.x + pen_width + 1, y + para->nHeight - bounds->bottom - DD(8)); 00611 } 00612 bounds->left += border_width; 00613 } 00614 if (para->pFmt->wBorders & 2) 00615 { 00616 MoveToEx(c->hDC, rightEdge - 1, y + bounds->top, NULL); 00617 LineTo(c->hDC, rightEdge - 1, y + para->nHeight - bounds->bottom); 00618 if (border_details[idx].dble) { 00619 rc.left = rightEdge - pen_width - 1; 00620 rc.right = rc.left + pen_width; 00621 rc.top = y + bounds->top; 00622 rc.bottom = y + para->nHeight - bounds->bottom; 00623 FillRect(c->hDC, &rc, c->editor->hbrBackground); 00624 MoveToEx(c->hDC, rightEdge - 1 - pen_width - 1, y + bounds->top + DD(4), NULL); 00625 LineTo(c->hDC, rightEdge - 1 - pen_width - 1, y + para->nHeight - bounds->bottom - DD(8)); 00626 } 00627 bounds->right += border_width; 00628 } 00629 if (para->pFmt->wBorders & 4) 00630 { 00631 MoveToEx(c->hDC, c->pt.x, y + bounds->top, NULL); 00632 LineTo(c->hDC, rightEdge, y + bounds->top); 00633 if (border_details[idx].dble) { 00634 MoveToEx(c->hDC, c->pt.x + DD(1), y + bounds->top + pen_width + 1, NULL); 00635 LineTo(c->hDC, rightEdge - DD(2), y + bounds->top + pen_width + 1); 00636 } 00637 bounds->top += border_width; 00638 } 00639 if (para->pFmt->wBorders & 8) 00640 { 00641 MoveToEx(c->hDC, c->pt.x, y + para->nHeight - bounds->bottom - 1, NULL); 00642 LineTo(c->hDC, rightEdge, y + para->nHeight - bounds->bottom - 1); 00643 if (border_details[idx].dble) { 00644 MoveToEx(c->hDC, c->pt.x + DD(1), y + para->nHeight - bounds->bottom - 1 - pen_width - 1, NULL); 00645 LineTo(c->hDC, rightEdge - DD(2), y + para->nHeight - bounds->bottom - 1 - pen_width - 1); 00646 } 00647 bounds->bottom += border_width; 00648 } 00649 #undef DD 00650 00651 MoveToEx(c->hDC, pt.x, pt.y, NULL); 00652 SelectObject(c->hDC, oldpen); 00653 DeleteObject(pen); 00654 } 00655 } 00656 00657 static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph) 00658 { 00659 ME_Paragraph *para = ¶graph->member.para; 00660 if (!c->editor->bEmulateVersion10) /* v4.1 */ 00661 { 00662 if (para->pCell) 00663 { 00664 RECT rc; 00665 ME_Cell *cell = ¶->pCell->member.cell; 00666 ME_DisplayItem *paraAfterRow; 00667 HPEN pen, oldPen; 00668 LOGBRUSH logBrush; 00669 HBRUSH brush; 00670 COLORREF color; 00671 POINT oldPt; 00672 int width; 00673 BOOL atTop = (para->pCell != para->prev_para->member.para.pCell); 00674 BOOL atBottom = (para->pCell != para->next_para->member.para.pCell); 00675 int top = c->pt.y + (atTop ? cell->pt.y : para->pt.y); 00676 int bottom = (atBottom ? 00677 c->pt.y + cell->pt.y + cell->nHeight : 00678 top + para->nHeight + (atTop ? cell->yTextOffset : 0)); 00679 rc.left = c->pt.x + cell->pt.x; 00680 rc.right = rc.left + cell->nWidth; 00681 if (atTop) { 00682 /* Erase gap before text if not all borders are the same height. */ 00683 width = max(ME_twips2pointsY(c, cell->border.top.width), 1); 00684 rc.top = top + width; 00685 width = cell->yTextOffset - width; 00686 rc.bottom = rc.top + width; 00687 if (width) { 00688 FillRect(c->hDC, &rc, c->editor->hbrBackground); 00689 } 00690 } 00691 /* Draw cell borders. 00692 * The order borders are draw in is left, top, bottom, right in order 00693 * to be consistent with native richedit. This is noticeable from the 00694 * overlap of borders of different colours. */ 00695 if (!(para->nFlags & MEPF_ROWEND)) { 00696 rc.top = top; 00697 rc.bottom = bottom; 00698 if (cell->border.left.width > 0) 00699 { 00700 color = cell->border.left.colorRef; 00701 width = max(ME_twips2pointsX(c, cell->border.left.width), 1); 00702 } else { 00703 color = RGB(192,192,192); 00704 width = 1; 00705 } 00706 logBrush.lbStyle = BS_SOLID; 00707 logBrush.lbColor = color; 00708 logBrush.lbHatch = 0; 00709 pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER, 00710 width, &logBrush, 0, NULL); 00711 oldPen = SelectObject(c->hDC, pen); 00712 MoveToEx(c->hDC, rc.left, rc.top, &oldPt); 00713 LineTo(c->hDC, rc.left, rc.bottom); 00714 SelectObject(c->hDC, oldPen); 00715 DeleteObject(pen); 00716 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL); 00717 } 00718 00719 if (atTop) { 00720 if (cell->border.top.width > 0) 00721 { 00722 brush = CreateSolidBrush(cell->border.top.colorRef); 00723 width = max(ME_twips2pointsY(c, cell->border.top.width), 1); 00724 } else { 00725 brush = GetStockObject(LTGRAY_BRUSH); 00726 width = 1; 00727 } 00728 rc.top = top; 00729 rc.bottom = rc.top + width; 00730 FillRect(c->hDC, &rc, brush); 00731 if (cell->border.top.width > 0) 00732 DeleteObject(brush); 00733 } 00734 00735 /* Draw the bottom border if at the last paragraph in the cell, and when 00736 * in the last row of the table. */ 00737 if (atBottom) { 00738 int oldLeft = rc.left; 00739 width = max(ME_twips2pointsY(c, cell->border.bottom.width), 1); 00740 paraAfterRow = ME_GetTableRowEnd(paragraph)->member.para.next_para; 00741 if (paraAfterRow->member.para.nFlags & MEPF_ROWSTART) { 00742 ME_DisplayItem *nextEndCell; 00743 nextEndCell = ME_FindItemBack(ME_GetTableRowEnd(paraAfterRow), diCell); 00744 assert(nextEndCell && !nextEndCell->member.cell.next_cell); 00745 rc.left = c->pt.x + nextEndCell->member.cell.pt.x; 00746 /* FIXME: Native draws FROM the bottom of the table rather than 00747 * TO the bottom of the table in this case, but just doing so here 00748 * will cause the next row to erase the border. */ 00749 /* 00750 rc.top = bottom; 00751 rc.bottom = rc.top + width; 00752 */ 00753 } 00754 if (rc.left < rc.right) { 00755 if (cell->border.bottom.width > 0) { 00756 brush = CreateSolidBrush(cell->border.bottom.colorRef); 00757 } else { 00758 brush = GetStockObject(LTGRAY_BRUSH); 00759 } 00760 rc.bottom = bottom; 00761 rc.top = rc.bottom - width; 00762 FillRect(c->hDC, &rc, brush); 00763 if (cell->border.bottom.width > 0) 00764 DeleteObject(brush); 00765 } 00766 rc.left = oldLeft; 00767 } 00768 00769 /* Right border only drawn if at the end of the table row. */ 00770 if (!cell->next_cell->member.cell.next_cell && 00771 !(para->nFlags & MEPF_ROWSTART)) 00772 { 00773 rc.top = top; 00774 rc.bottom = bottom; 00775 if (cell->border.right.width > 0) { 00776 color = cell->border.right.colorRef; 00777 width = max(ME_twips2pointsX(c, cell->border.right.width), 1); 00778 } else { 00779 color = RGB(192,192,192); 00780 width = 1; 00781 } 00782 logBrush.lbStyle = BS_SOLID; 00783 logBrush.lbColor = color; 00784 logBrush.lbHatch = 0; 00785 pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER, 00786 width, &logBrush, 0, NULL); 00787 oldPen = SelectObject(c->hDC, pen); 00788 MoveToEx(c->hDC, rc.right - 1, rc.top, &oldPt); 00789 LineTo(c->hDC, rc.right - 1, rc.bottom); 00790 SelectObject(c->hDC, oldPen); 00791 DeleteObject(pen); 00792 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL); 00793 } 00794 } 00795 } else { /* v1.0 - 3.0 */ 00796 /* Draw simple table border */ 00797 if (para->pFmt->dwMask & PFM_TABLE && para->pFmt->wEffects & PFE_TABLE) { 00798 HPEN pen = NULL, oldpen = NULL; 00799 int i, firstX, startX, endX, rowY, rowBottom, nHeight; 00800 POINT oldPt; 00801 PARAFORMAT2 *pNextFmt; 00802 00803 pen = CreatePen(PS_SOLID, 0, para->border.top.colorRef); 00804 oldpen = SelectObject(c->hDC, pen); 00805 00806 /* Find the start relative to the text */ 00807 firstX = c->pt.x + ME_FindItemFwd(paragraph, diRun)->member.run.pt.x; 00808 /* Go back by the horizontal gap, which is stored in dxOffset */ 00809 firstX -= ME_twips2pointsX(c, para->pFmt->dxOffset); 00810 /* The left edge, stored in dxStartIndent affected just the first edge */ 00811 startX = firstX - ME_twips2pointsX(c, para->pFmt->dxStartIndent); 00812 rowY = c->pt.y + para->pt.y; 00813 if (para->pFmt->dwMask & PFM_SPACEBEFORE) 00814 rowY += ME_twips2pointsY(c, para->pFmt->dySpaceBefore); 00815 nHeight = ME_FindItemFwd(paragraph, diStartRow)->member.row.nHeight; 00816 rowBottom = rowY + nHeight; 00817 00818 /* Draw horizontal lines */ 00819 MoveToEx(c->hDC, firstX, rowY, &oldPt); 00820 i = para->pFmt->cTabCount - 1; 00821 endX = startX + ME_twips2pointsX(c, para->pFmt->rgxTabs[i] & 0x00ffffff) + 1; 00822 LineTo(c->hDC, endX, rowY); 00823 pNextFmt = para->next_para->member.para.pFmt; 00824 /* The bottom of the row only needs to be drawn if the next row is 00825 * not a table. */ 00826 if (!(pNextFmt && pNextFmt->dwMask & PFM_TABLE && pNextFmt->wEffects && 00827 para->nRows == 1)) 00828 { 00829 /* Decrement rowBottom to draw the bottom line within the row, and 00830 * to not draw over this line when drawing the vertical lines. */ 00831 rowBottom--; 00832 MoveToEx(c->hDC, firstX, rowBottom, NULL); 00833 LineTo(c->hDC, endX, rowBottom); 00834 } 00835 00836 /* Draw vertical lines */ 00837 MoveToEx(c->hDC, firstX, rowY, NULL); 00838 LineTo(c->hDC, firstX, rowBottom); 00839 for (i = 0; i < para->pFmt->cTabCount; i++) 00840 { 00841 int rightBoundary = para->pFmt->rgxTabs[i] & 0x00ffffff; 00842 endX = startX + ME_twips2pointsX(c, rightBoundary); 00843 MoveToEx(c->hDC, endX, rowY, NULL); 00844 LineTo(c->hDC, endX, rowBottom); 00845 } 00846 00847 MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL); 00848 SelectObject(c->hDC, oldpen); 00849 DeleteObject(pen); 00850 } 00851 } 00852 } 00853 00854 static void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) 00855 { 00856 int align = SetTextAlign(c->hDC, TA_BASELINE); 00857 ME_DisplayItem *p; 00858 ME_Run *run; 00859 ME_Paragraph *para = NULL; 00860 RECT rc, bounds; 00861 int y; 00862 int height = 0, baseline = 0, no=0; 00863 BOOL visible = FALSE; 00864 00865 rc.left = c->pt.x; 00866 rc.right = c->rcView.right; 00867 00868 assert(paragraph); 00869 para = ¶graph->member.para; 00870 y = c->pt.y + para->pt.y; 00871 if (para->pCell) 00872 { 00873 ME_Cell *cell = ¶->pCell->member.cell; 00874 rc.left = c->pt.x + cell->pt.x; 00875 rc.right = rc.left + cell->nWidth; 00876 } 00877 if (para->nFlags & MEPF_ROWSTART) { 00878 ME_Cell *cell = ¶->next_para->member.para.pCell->member.cell; 00879 rc.right = c->pt.x + cell->pt.x; 00880 } else if (para->nFlags & MEPF_ROWEND) { 00881 ME_Cell *cell = ¶->prev_para->member.para.pCell->member.cell; 00882 rc.left = c->pt.x + cell->pt.x + cell->nWidth; 00883 } 00884 ME_DrawParaDecoration(c, para, y, &bounds); 00885 y += bounds.top; 00886 if (bounds.left || bounds.right) { 00887 rc.left = max(rc.left, c->pt.x + bounds.left); 00888 rc.right = min(rc.right, c->pt.x - bounds.right 00889 + max(c->editor->sizeWindow.cx, 00890 c->editor->nTotalWidth)); 00891 } 00892 00893 for (p = paragraph->next; p != para->next_para; p = p->next) 00894 { 00895 switch(p->type) { 00896 case diParagraph: 00897 assert(FALSE); 00898 break; 00899 case diStartRow: 00900 y += height; 00901 rc.top = y; 00902 if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) { 00903 rc.bottom = y + para->nHeight; 00904 } else { 00905 rc.bottom = y + p->member.row.nHeight; 00906 } 00907 visible = RectVisible(c->hDC, &rc); 00908 if (visible) { 00909 FillRect(c->hDC, &rc, c->editor->hbrBackground); 00910 } 00911 if (bounds.right) 00912 { 00913 /* If scrolled to the right past the end of the text, then 00914 * there may be space to the right of the paragraph border. */ 00915 RECT rcAfterBrdr = rc; 00916 rcAfterBrdr.left = rc.right + bounds.right; 00917 rcAfterBrdr.right = c->rcView.right; 00918 if (RectVisible(c->hDC, &rcAfterBrdr)) 00919 FillRect(c->hDC, &rcAfterBrdr, c->editor->hbrBackground); 00920 } 00921 if (me_debug) 00922 { 00923 const WCHAR wszRowDebug[] = {'r','o','w','[','%','d',']',0}; 00924 WCHAR buf[128]; 00925 POINT pt = c->pt; 00926 wsprintfW(buf, wszRowDebug, no); 00927 pt.y = 12+y; 00928 ME_DebugWrite(c->hDC, &pt, buf); 00929 } 00930 00931 height = p->member.row.nHeight; 00932 baseline = p->member.row.nBaseline; 00933 break; 00934 case diRun: 00935 assert(para); 00936 run = &p->member.run; 00937 if (visible && me_debug) { 00938 RECT rc; 00939 rc.left = c->pt.x + run->pt.x; 00940 rc.right = rc.left + run->nWidth; 00941 rc.top = c->pt.y + para->pt.y + run->pt.y; 00942 rc.bottom = rc.top + height; 00943 TRACE("rc = (%d, %d, %d, %d)\n", rc.left, rc.top, rc.right, rc.bottom); 00944 FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT)); 00945 } 00946 if (visible) 00947 ME_DrawRun(c, c->pt.x + run->pt.x, 00948 c->pt.y + para->pt.y + run->pt.y + baseline, p, para); 00949 if (me_debug) 00950 { 00951 /* I'm using %ls, hope wsprintfW is not going to use wrong (4-byte) WCHAR version */ 00952 const WCHAR wszRunDebug[] = {'[','%','d',':','%','x',']',' ','%','l','s',0}; 00953 WCHAR buf[2560]; 00954 POINT pt; 00955 pt.x = c->pt.x + run->pt.x; 00956 pt.y = c->pt.y + para->pt.y + run->pt.y; 00957 wsprintfW(buf, wszRunDebug, no, p->member.run.nFlags, p->member.run.strText->szData); 00958 ME_DebugWrite(c->hDC, &pt, buf); 00959 } 00960 break; 00961 case diCell: 00962 /* Clear any space at the bottom of the cell after the text. */ 00963 if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) 00964 break; 00965 y += height; 00966 rc.top = c->pt.y + para->pt.y + para->nHeight; 00967 rc.bottom = c->pt.y + p->member.cell.pt.y + p->member.cell.nHeight; 00968 if (RectVisible(c->hDC, &rc)) 00969 { 00970 FillRect(c->hDC, &rc, c->editor->hbrBackground); 00971 } 00972 break; 00973 default: 00974 break; 00975 } 00976 no++; 00977 } 00978 00979 ME_DrawTableBorders(c, paragraph); 00980 00981 SetTextAlign(c->hDC, align); 00982 } 00983 00984 void ME_ScrollAbs(ME_TextEditor *editor, int x, int y) 00985 { 00986 BOOL bScrollBarIsVisible, bScrollBarWillBeVisible; 00987 int scrollX = 0, scrollY = 0; 00988 00989 if (editor->horz_si.nPos != x) { 00990 x = min(x, editor->horz_si.nMax); 00991 x = max(x, editor->horz_si.nMin); 00992 scrollX = editor->horz_si.nPos - x; 00993 editor->horz_si.nPos = x; 00994 if (editor->horz_si.nMax > 0xFFFF) /* scale to 16-bit value */ 00995 x = MulDiv(x, 0xFFFF, editor->horz_si.nMax); 00996 ITextHost_TxSetScrollPos(editor->texthost, SB_HORZ, x, TRUE); 00997 } 00998 00999 if (editor->vert_si.nPos != y) { 01000 y = min(y, editor->vert_si.nMax - (int)editor->vert_si.nPage); 01001 y = max(y, editor->vert_si.nMin); 01002 scrollY = editor->vert_si.nPos - y; 01003 editor->vert_si.nPos = y; 01004 if (editor->vert_si.nMax > 0xFFFF) /* scale to 16-bit value */ 01005 y = MulDiv(y, 0xFFFF, editor->vert_si.nMax); 01006 ITextHost_TxSetScrollPos(editor->texthost, SB_VERT, y, TRUE); 01007 } 01008 01009 if (abs(scrollX) > editor->sizeWindow.cx || 01010 abs(scrollY) > editor->sizeWindow.cy) 01011 ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE); 01012 else 01013 ITextHost_TxScrollWindowEx(editor->texthost, scrollX, scrollY, 01014 &editor->rcFormat, &editor->rcFormat, 01015 NULL, NULL, SW_INVALIDATE); 01016 ME_Repaint(editor); 01017 01018 if (editor->hWnd) 01019 { 01020 LONG winStyle = GetWindowLongW(editor->hWnd, GWL_STYLE); 01021 if (editor->styleFlags & WS_HSCROLL) 01022 { 01023 bScrollBarIsVisible = (winStyle & WS_HSCROLL) != 0; 01024 bScrollBarWillBeVisible = (editor->nTotalWidth > editor->sizeWindow.cx 01025 && (editor->styleFlags & WS_HSCROLL)) 01026 || (editor->styleFlags & ES_DISABLENOSCROLL); 01027 if (bScrollBarIsVisible != bScrollBarWillBeVisible) 01028 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, 01029 bScrollBarWillBeVisible); 01030 } 01031 01032 if (editor->styleFlags & WS_VSCROLL) 01033 { 01034 bScrollBarIsVisible = (winStyle & WS_VSCROLL) != 0; 01035 bScrollBarWillBeVisible = (editor->nTotalLength > editor->sizeWindow.cy 01036 && (editor->styleFlags & WS_VSCROLL) 01037 && (editor->styleFlags & ES_MULTILINE)) 01038 || (editor->styleFlags & ES_DISABLENOSCROLL); 01039 if (bScrollBarIsVisible != bScrollBarWillBeVisible) 01040 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, 01041 bScrollBarWillBeVisible); 01042 } 01043 } 01044 ME_UpdateScrollBar(editor); 01045 } 01046 01047 void ME_HScrollAbs(ME_TextEditor *editor, int x) 01048 { 01049 ME_ScrollAbs(editor, x, editor->vert_si.nPos); 01050 } 01051 01052 void ME_VScrollAbs(ME_TextEditor *editor, int y) 01053 { 01054 ME_ScrollAbs(editor, editor->horz_si.nPos, y); 01055 } 01056 01057 void ME_ScrollUp(ME_TextEditor *editor, int cy) 01058 { 01059 ME_VScrollAbs(editor, editor->vert_si.nPos - cy); 01060 } 01061 01062 void ME_ScrollDown(ME_TextEditor *editor, int cy) 01063 { 01064 ME_VScrollAbs(editor, editor->vert_si.nPos + cy); 01065 } 01066 01067 void ME_ScrollLeft(ME_TextEditor *editor, int cx) 01068 { 01069 ME_HScrollAbs(editor, editor->horz_si.nPos - cx); 01070 } 01071 01072 void ME_ScrollRight(ME_TextEditor *editor, int cx) 01073 { 01074 ME_HScrollAbs(editor, editor->horz_si.nPos + cx); 01075 } 01076 01077 /* Calculates the visibility after a call to SetScrollRange or 01078 * SetScrollInfo with SIF_RANGE. */ 01079 static BOOL ME_PostSetScrollRangeVisibility(SCROLLINFO *si) 01080 { 01081 if (si->fMask & SIF_DISABLENOSCROLL) 01082 return TRUE; 01083 01084 /* This must match the check in SetScrollInfo to determine whether 01085 * to show or hide the scrollbars. */ 01086 return si->nMin < si->nMax - max(si->nPage - 1, 0); 01087 } 01088 01089 void ME_UpdateScrollBar(ME_TextEditor *editor) 01090 { 01091 /* Note that this is the only function that should ever call 01092 * SetScrollInfo with SIF_PAGE or SIF_RANGE. */ 01093 01094 SCROLLINFO si; 01095 BOOL bScrollBarWasVisible, bScrollBarWillBeVisible; 01096 01097 if (ME_WrapMarkedParagraphs(editor)) 01098 FIXME("ME_UpdateScrollBar had to call ME_WrapMarkedParagraphs\n"); 01099 01100 si.cbSize = sizeof(si); 01101 si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; 01102 si.nMin = 0; 01103 if (editor->styleFlags & ES_DISABLENOSCROLL) 01104 si.fMask |= SIF_DISABLENOSCROLL; 01105 01106 /* Update horizontal scrollbar */ 01107 bScrollBarWasVisible = editor->horz_si.nMax > editor->horz_si.nPage; 01108 bScrollBarWillBeVisible = editor->nTotalWidth > editor->sizeWindow.cx; 01109 if (editor->horz_si.nPos && !bScrollBarWillBeVisible) 01110 { 01111 ME_HScrollAbs(editor, 0); 01112 /* ME_HScrollAbs will call this function, 01113 * so nothing else needs to be done here. */ 01114 return; 01115 } 01116 01117 si.nMax = editor->nTotalWidth; 01118 si.nPos = editor->horz_si.nPos; 01119 si.nPage = editor->sizeWindow.cx; 01120 01121 if (si.nMax != editor->horz_si.nMax || 01122 si.nPage != editor->horz_si.nPage) 01123 { 01124 TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage); 01125 editor->horz_si.nMax = si.nMax; 01126 editor->horz_si.nPage = si.nPage; 01127 if ((bScrollBarWillBeVisible || bScrollBarWasVisible) && 01128 editor->styleFlags & WS_HSCROLL) 01129 { 01130 if (si.nMax > 0xFFFF) 01131 { 01132 /* Native scales the scrollbar info to 16-bit external values. */ 01133 si.nPos = MulDiv(si.nPos, 0xFFFF, si.nMax); 01134 si.nMax = 0xFFFF; 01135 } 01136 if (editor->hWnd) { 01137 SetScrollInfo(editor->hWnd, SB_HORZ, &si, TRUE); 01138 } else { 01139 ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, si.nMin, si.nMax, FALSE); 01140 ITextHost_TxSetScrollPos(editor->texthost, SB_HORZ, si.nPos, TRUE); 01141 } 01142 /* SetScrollInfo or SetScrollRange change scrollbar visibility. */ 01143 bScrollBarWasVisible = ME_PostSetScrollRangeVisibility(&si); 01144 } 01145 } 01146 01147 if (editor->styleFlags & WS_HSCROLL) 01148 { 01149 if (si.fMask & SIF_DISABLENOSCROLL) { 01150 bScrollBarWillBeVisible = TRUE; 01151 } else if (!(editor->styleFlags & WS_HSCROLL)) { 01152 bScrollBarWillBeVisible = FALSE; 01153 } 01154 01155 if (bScrollBarWasVisible != bScrollBarWillBeVisible) 01156 ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, bScrollBarWillBeVisible); 01157 } 01158 01159 /* Update vertical scrollbar */ 01160 bScrollBarWasVisible = editor->vert_si.nMax > editor->vert_si.nPage; 01161 bScrollBarWillBeVisible = editor->nTotalLength > editor->sizeWindow.cy && 01162 (editor->styleFlags & ES_MULTILINE); 01163 01164 if (editor->vert_si.nPos && !bScrollBarWillBeVisible) 01165 { 01166 ME_VScrollAbs(editor, 0); 01167 /* ME_VScrollAbs will call this function, 01168 * so nothing else needs to be done here. */ 01169 return; 01170 } 01171 01172 si.nMax = editor->nTotalLength; 01173 si.nPos = editor->vert_si.nPos; 01174 si.nPage = editor->sizeWindow.cy; 01175 01176 if (si.nMax != editor->vert_si.nMax || 01177 si.nPage != editor->vert_si.nPage) 01178 { 01179 TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage); 01180 editor->vert_si.nMax = si.nMax; 01181 editor->vert_si.nPage = si.nPage; 01182 if ((bScrollBarWillBeVisible || bScrollBarWasVisible) && 01183 editor->styleFlags & WS_VSCROLL) 01184 { 01185 if (si.nMax > 0xFFFF) 01186 { 01187 /* Native scales the scrollbar info to 16-bit external values. */ 01188 si.nPos = MulDiv(si.nPos, 0xFFFF, si.nMax); 01189 si.nMax = 0xFFFF; 01190 } 01191 if (editor->hWnd) { 01192 SetScrollInfo(editor->hWnd, SB_VERT, &si, TRUE); 01193 } else { 01194 ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, si.nMin, si.nMax, FALSE); 01195 ITextHost_TxSetScrollPos(editor->texthost, SB_VERT, si.nPos, TRUE); 01196 } 01197 /* SetScrollInfo or SetScrollRange change scrollbar visibility. */ 01198 bScrollBarWasVisible = ME_PostSetScrollRangeVisibility(&si); 01199 } 01200 } 01201 01202 if (editor->styleFlags & WS_VSCROLL) 01203 { 01204 if (si.fMask & SIF_DISABLENOSCROLL) { 01205 bScrollBarWillBeVisible = TRUE; 01206 } else if (!(editor->styleFlags & WS_VSCROLL)) { 01207 bScrollBarWillBeVisible = FALSE; 01208 } 01209 01210 if (bScrollBarWasVisible != bScrollBarWillBeVisible) 01211 ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, 01212 bScrollBarWillBeVisible); 01213 } 01214 } 01215 01216 void ME_EnsureVisible(ME_TextEditor *editor, ME_Cursor *pCursor) 01217 { 01218 ME_Run *pRun = &pCursor->pRun->member.run; 01219 ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diStartRow); 01220 ME_DisplayItem *pPara = pCursor->pPara; 01221 int x, y, yheight; 01222 01223 assert(pRow); 01224 assert(pPara); 01225 01226 if (editor->styleFlags & ES_AUTOHSCROLL) 01227 { 01228 x = pRun->pt.x + ME_PointFromChar(editor, pRun, pCursor->nOffset); 01229 if (x > editor->horz_si.nPos + editor->sizeWindow.cx) 01230 x = x + 1 - editor->sizeWindow.cx; 01231 else if (x > editor->horz_si.nPos) 01232 x = editor->horz_si.nPos; 01233 01234 if (~editor->styleFlags & ES_AUTOVSCROLL) 01235 { 01236 ME_HScrollAbs(editor, x); 01237 return; 01238 } 01239 } else { 01240 if (~editor->styleFlags & ES_AUTOVSCROLL) 01241 return; 01242 x = editor->horz_si.nPos; 01243 } 01244 01245 y = pPara->member.para.pt.y + pRow->member.row.pt.y; 01246 yheight = pRow->member.row.nHeight; 01247 01248 if (y < editor->vert_si.nPos) 01249 ME_ScrollAbs(editor, x, y); 01250 else if (y + yheight > editor->vert_si.nPos + editor->sizeWindow.cy) 01251 ME_ScrollAbs(editor, x, y + yheight - editor->sizeWindow.cy); 01252 else if (x != editor->horz_si.nPos) 01253 ME_ScrollAbs(editor, x, editor->vert_si.nPos); 01254 } 01255 01256 01257 void 01258 ME_InvalidateSelection(ME_TextEditor *editor) 01259 { 01260 ME_DisplayItem *sel_start, *sel_end; 01261 ME_DisplayItem *repaint_start = NULL, *repaint_end = NULL; 01262 int nStart, nEnd; 01263 int len = ME_GetTextLength(editor); 01264 01265 ME_GetSelectionOfs(editor, &nStart, &nEnd); 01266 /* if both old and new selection are 0-char (= caret only), then 01267 there's no (inverted) area to be repainted, neither old nor new */ 01268 if (nStart == nEnd && editor->nLastSelStart == editor->nLastSelEnd) 01269 return; 01270 ME_WrapMarkedParagraphs(editor); 01271 ME_GetSelectionParas(editor, &sel_start, &sel_end); 01272 assert(sel_start->type == diParagraph); 01273 assert(sel_end->type == diParagraph); 01274 /* last selection markers aren't always updated, which means 01275 * they can point past the end of the document */ 01276 if (editor->nLastSelStart > len || editor->nLastSelEnd > len) { 01277 repaint_start = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph); 01278 repaint_end = editor->pBuffer->pLast; 01279 ME_MarkForPainting(editor, repaint_start, repaint_end); 01280 } else { 01281 /* if the start part of selection is being expanded or contracted... */ 01282 if (nStart < editor->nLastSelStart) { 01283 repaint_start = sel_start; 01284 repaint_end = editor->pLastSelStartPara->member.para.next_para; 01285 } else if (nStart > editor->nLastSelStart) { 01286 repaint_start = editor->pLastSelStartPara; 01287 repaint_end = sel_start->member.para.next_para; 01288 } 01289 ME_MarkForPainting(editor, repaint_start, repaint_end); 01290 01291 /* if the end part of selection is being contracted or expanded... */ 01292 if (nEnd < editor->nLastSelEnd) { 01293 if (!repaint_start) repaint_start = sel_end; 01294 repaint_end = editor->pLastSelEndPara->member.para.next_para; 01295 ME_MarkForPainting(editor, sel_end, repaint_end); 01296 } else if (nEnd > editor->nLastSelEnd) { 01297 if (!repaint_start) repaint_start = editor->pLastSelEndPara; 01298 repaint_end = sel_end->member.para.next_para; 01299 ME_MarkForPainting(editor, editor->pLastSelEndPara, repaint_end); 01300 } 01301 } 01302 01303 ME_InvalidateMarkedParagraphs(editor, repaint_start, repaint_end); 01304 /* remember the last invalidated position */ 01305 ME_GetSelectionOfs(editor, &editor->nLastSelStart, &editor->nLastSelEnd); 01306 ME_GetSelectionParas(editor, &editor->pLastSelStartPara, &editor->pLastSelEndPara); 01307 assert(editor->pLastSelStartPara->type == diParagraph); 01308 assert(editor->pLastSelEndPara->type == diParagraph); 01309 } 01310 01311 BOOL 01312 ME_SetZoom(ME_TextEditor *editor, int numerator, int denominator) 01313 { 01314 /* TODO: Zoom images and objects */ 01315 01316 if (numerator == 0 && denominator == 0) 01317 { 01318 editor->nZoomNumerator = editor->nZoomDenominator = 0; 01319 return TRUE; 01320 } 01321 if (numerator <= 0 || denominator <= 0) 01322 return FALSE; 01323 if (numerator * 64 <= denominator || numerator / denominator >= 64) 01324 return FALSE; 01325 01326 editor->nZoomNumerator = numerator; 01327 editor->nZoomDenominator = denominator; 01328 01329 ME_RewrapRepaint(editor); 01330 return TRUE; 01331 } Generated on Sun May 27 2012 04:25:57 for ReactOS by
1.7.6.1
|