1 /* 2 * RichEdit style management functions 3 * 4 * Copyright 2004 by Krzysztof Foltman 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include "editor.h" 22 23 WINE_DEFAULT_DEBUG_CHANNEL(richedit); 24 WINE_DECLARE_DEBUG_CHANNEL(richedit_style); 25 26 static int all_refs = 0; 27 28 /* the following routines assume that: 29 * - char2[AW] extends char[AW] by adding fields at the end of the charA form) 30 * - szFaceName is the last field of char[AW] form, and wWeight the first of 2[AW] 31 * - the difference between A and W form is the szFaceName as Ansi vs Unicode string 32 * - because of alignment, offset of wWeight field in 2[AW] structure *IS NOT* 33 * sizeof(char[AW]) 34 */ 35 36 CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from) 37 { 38 if (from->cbSize == sizeof(CHARFORMATA)) 39 { 40 CHARFORMATA *f = (CHARFORMATA *)from; 41 CopyMemory(to, f, FIELD_OFFSET(CHARFORMATA, szFaceName)); 42 to->cbSize = sizeof(CHARFORMAT2W); 43 if (f->dwMask & CFM_FACE) { 44 MultiByteToWideChar(CP_ACP, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName)/sizeof(WCHAR)); 45 } 46 return to; 47 } 48 if (from->cbSize == sizeof(CHARFORMATW)) 49 { 50 CHARFORMATW *f = (CHARFORMATW *)from; 51 CopyMemory(to, f, sizeof(*f)); 52 /* theoretically, we don't need to zero the remaining memory */ 53 ZeroMemory(&to->wWeight, sizeof(CHARFORMAT2W)-FIELD_OFFSET(CHARFORMAT2W, wWeight)); 54 to->cbSize = sizeof(CHARFORMAT2W); 55 return to; 56 } 57 if (from->cbSize == sizeof(CHARFORMAT2A)) 58 { 59 CHARFORMAT2A *f = (CHARFORMAT2A *)from; 60 /* copy the A structure without face name */ 61 CopyMemory(to, f, FIELD_OFFSET(CHARFORMATA, szFaceName)); 62 /* convert face name */ 63 if (f->dwMask & CFM_FACE) 64 MultiByteToWideChar(CP_ACP, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName)/sizeof(WCHAR)); 65 /* copy the rest of the 2A structure to 2W */ 66 CopyMemory(&to->wWeight, &f->wWeight, sizeof(CHARFORMAT2A)-FIELD_OFFSET(CHARFORMAT2A, wWeight)); 67 to->cbSize = sizeof(CHARFORMAT2W); 68 return to; 69 } 70 71 return (from->cbSize >= sizeof(CHARFORMAT2W)) ? from : NULL; 72 } 73 74 static CHARFORMAT2W *ME_ToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from) 75 { 76 assert(from->cbSize == sizeof(CHARFORMAT2W)); 77 if (to->cbSize == sizeof(CHARFORMATA)) 78 { 79 CHARFORMATA *t = (CHARFORMATA *)to; 80 CopyMemory(t, from, FIELD_OFFSET(CHARFORMATA, szFaceName)); 81 WideCharToMultiByte(CP_ACP, 0, from->szFaceName, -1, t->szFaceName, sizeof(t->szFaceName), NULL, NULL); 82 t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */ 83 return to; 84 } 85 if (to->cbSize == sizeof(CHARFORMATW)) 86 { 87 CHARFORMATW *t = (CHARFORMATW *)to; 88 CopyMemory(t, from, sizeof(*t)); 89 t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */ 90 return to; 91 } 92 if (to->cbSize == sizeof(CHARFORMAT2A)) 93 { 94 CHARFORMAT2A *t = (CHARFORMAT2A *)to; 95 /* copy the A structure without face name */ 96 CopyMemory(t, from, FIELD_OFFSET(CHARFORMATA, szFaceName)); 97 /* convert face name */ 98 WideCharToMultiByte(CP_ACP, 0, from->szFaceName, -1, t->szFaceName, sizeof(t->szFaceName), NULL, NULL); 99 /* copy the rest of the 2A structure to 2W */ 100 CopyMemory(&t->wWeight, &from->wWeight, sizeof(CHARFORMAT2W)-FIELD_OFFSET(CHARFORMAT2W,wWeight)); 101 t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */ 102 return to; 103 } 104 assert(to->cbSize >= sizeof(CHARFORMAT2W)); 105 return from; 106 } 107 108 void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from) 109 { 110 if (ME_ToCFAny(to, from) == from) 111 CopyMemory(to, from, to->cbSize); 112 } 113 114 ME_Style *ME_MakeStyle(CHARFORMAT2W *style) 115 { 116 ME_Style *s = ALLOC_OBJ(ME_Style); 117 118 assert(style->cbSize == sizeof(CHARFORMAT2W)); 119 s->fmt = *style; 120 s->nRefs = 1; 121 s->font_cache = NULL; 122 memset(&s->tm, 0, sizeof(s->tm)); 123 s->tm.tmAscent = -1; 124 s->script_cache = NULL; 125 list_init(&s->entry); 126 all_refs++; 127 TRACE_(richedit_style)("ME_MakeStyle %p, total refs=%d\n", s, all_refs); 128 return s; 129 } 130 131 #define COPY_STYLE_ITEM(mask, member) \ 132 if (mod->dwMask & mask) { \ 133 fmt.dwMask |= mask;\ 134 fmt.member = mod->member;\ 135 } 136 137 #define COPY_STYLE_ITEM_MEMCPY(mask, member) \ 138 if (mod->dwMask & mask) { \ 139 fmt.dwMask |= mask;\ 140 CopyMemory(fmt.member, mod->member, sizeof(mod->member));\ 141 } 142 143 void ME_InitCharFormat2W(CHARFORMAT2W *pFmt) 144 { 145 ZeroMemory(pFmt, sizeof(CHARFORMAT2W)); 146 pFmt->cbSize = sizeof(CHARFORMAT2W); 147 } 148 149 ME_Style *ME_ApplyStyle(ME_TextEditor *editor, ME_Style *sSrc, CHARFORMAT2W *mod) 150 { 151 CHARFORMAT2W fmt = sSrc->fmt; 152 ME_Style *s; 153 154 assert(mod->cbSize == sizeof(CHARFORMAT2W)); 155 COPY_STYLE_ITEM(CFM_ANIMATION, bAnimation); 156 COPY_STYLE_ITEM(CFM_BACKCOLOR, crBackColor); 157 COPY_STYLE_ITEM(CFM_CHARSET, bCharSet); 158 COPY_STYLE_ITEM(CFM_COLOR, crTextColor); 159 COPY_STYLE_ITEM_MEMCPY(CFM_FACE, szFaceName); 160 COPY_STYLE_ITEM(CFM_KERNING, wKerning); 161 COPY_STYLE_ITEM(CFM_LCID, lcid); 162 COPY_STYLE_ITEM(CFM_OFFSET, yOffset); 163 COPY_STYLE_ITEM(CFM_REVAUTHOR, bRevAuthor); 164 if (mod->dwMask & CFM_SIZE) { 165 fmt.dwMask |= CFM_SIZE; 166 fmt.yHeight = min(mod->yHeight, yHeightCharPtsMost * 20); 167 } 168 COPY_STYLE_ITEM(CFM_SPACING, sSpacing); 169 COPY_STYLE_ITEM(CFM_STYLE, sStyle); 170 COPY_STYLE_ITEM(CFM_WEIGHT, wWeight); 171 /* FIXME: this is not documented this way, but that's the more logical */ 172 COPY_STYLE_ITEM(CFM_FACE, bPitchAndFamily); 173 174 fmt.dwEffects &= ~(mod->dwMask); 175 fmt.dwEffects |= mod->dwEffects & mod->dwMask; 176 fmt.dwMask |= mod->dwMask; 177 if (mod->dwMask & CFM_COLOR) 178 { 179 if (mod->dwEffects & CFE_AUTOCOLOR) 180 fmt.dwEffects |= CFE_AUTOCOLOR; 181 else 182 fmt.dwEffects &= ~CFE_AUTOCOLOR; 183 } 184 185 COPY_STYLE_ITEM(CFM_UNDERLINETYPE, bUnderlineType); 186 /* If the CFM_UNDERLINE effect is not specified, set it appropriately */ 187 if ((mod->dwMask & CFM_UNDERLINETYPE) && !(mod->dwMask & CFM_UNDERLINE)) 188 { 189 fmt.dwMask |= CFM_UNDERLINE; 190 if (mod->bUnderlineType == CFU_UNDERLINENONE) 191 fmt.dwEffects &= ~CFE_UNDERLINE; 192 else 193 fmt.dwEffects |= CFE_UNDERLINE; 194 } 195 196 if (mod->dwMask & CFM_BOLD && !(mod->dwMask & CFM_WEIGHT)) 197 { 198 fmt.wWeight = (mod->dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL; 199 } else if (mod->dwMask & CFM_WEIGHT && !(mod->dwMask & CFM_BOLD)) { 200 if (mod->wWeight > FW_NORMAL) 201 fmt.dwEffects |= CFE_BOLD; 202 else 203 fmt.dwEffects &= ~CFE_BOLD; 204 } 205 206 LIST_FOR_EACH_ENTRY(s, &editor->style_list, ME_Style, entry) 207 { 208 if (!memcmp( &s->fmt, &fmt, sizeof(fmt) )) 209 { 210 TRACE_(richedit_style)("found existing style %p\n", s); 211 ME_AddRefStyle( s ); 212 return s; 213 } 214 } 215 216 s = ME_MakeStyle( &fmt ); 217 if (s) 218 list_add_head( &editor->style_list, &s->entry ); 219 TRACE_(richedit_style)("created new style %p\n", s); 220 return s; 221 } 222 223 void ME_CopyCharFormat(CHARFORMAT2W *pDest, const CHARFORMAT2W *pSrc) 224 { 225 /* using this with non-2W structs is forbidden */ 226 assert(pSrc->cbSize == sizeof(CHARFORMAT2W)); 227 assert(pDest->cbSize == sizeof(CHARFORMAT2W)); 228 *pDest = *pSrc; 229 } 230 231 static void ME_DumpStyleEffect(char **p, const char *name, const CHARFORMAT2W *fmt, int mask) 232 { 233 *p += sprintf(*p, "%-22s%s\n", name, (fmt->dwMask & mask) ? ((fmt->dwEffects & mask) ? "YES" : "no") : "N/A"); 234 } 235 236 void ME_DumpStyle(ME_Style *s) 237 { 238 char buf[2048]; 239 ME_DumpStyleToBuf(&s->fmt, buf); 240 TRACE_(richedit_style)("%s\n", buf); 241 } 242 243 void ME_DumpStyleToBuf(CHARFORMAT2W *pFmt, char buf[2048]) 244 { 245 /* FIXME only CHARFORMAT styles implemented */ 246 /* this function sucks, doesn't check for buffer overruns but it's "good enough" as for debug code */ 247 char *p; 248 p = buf; 249 p += sprintf(p, "Font face: "); 250 if (pFmt->dwMask & CFM_FACE) { 251 WCHAR *q = pFmt->szFaceName; 252 while(*q) { 253 *p++ = (*q > 255) ? '?' : *q; 254 q++; 255 } 256 } else 257 p += sprintf(p, "N/A"); 258 259 if (pFmt->dwMask & CFM_SIZE) 260 p += sprintf(p, "\nFont size: %d\n", pFmt->yHeight); 261 else 262 p += sprintf(p, "\nFont size: N/A\n"); 263 264 if (pFmt->dwMask & CFM_OFFSET) 265 p += sprintf(p, "Char offset: %d\n", pFmt->yOffset); 266 else 267 p += sprintf(p, "Char offset: N/A\n"); 268 269 if (pFmt->dwMask & CFM_CHARSET) 270 p += sprintf(p, "Font charset: %d\n", (int)pFmt->bCharSet); 271 else 272 p += sprintf(p, "Font charset: N/A\n"); 273 274 /* I'm assuming CFM_xxx and CFE_xxx are the same values, fortunately it IS true wrt used flags*/ 275 ME_DumpStyleEffect(&p, "Font bold:", pFmt, CFM_BOLD); 276 ME_DumpStyleEffect(&p, "Font italic:", pFmt, CFM_ITALIC); 277 ME_DumpStyleEffect(&p, "Font underline:", pFmt, CFM_UNDERLINE); 278 ME_DumpStyleEffect(&p, "Font strikeout:", pFmt, CFM_STRIKEOUT); 279 ME_DumpStyleEffect(&p, "Hidden text:", pFmt, CFM_HIDDEN); 280 p += sprintf(p, "Text color: "); 281 if (pFmt->dwMask & CFM_COLOR) 282 { 283 if (pFmt->dwEffects & CFE_AUTOCOLOR) 284 p += sprintf(p, "auto\n"); 285 else 286 p += sprintf(p, "%06x\n", (int)pFmt->crTextColor); 287 } 288 else 289 p += sprintf(p, "N/A\n"); 290 ME_DumpStyleEffect(&p, "Text protected:", pFmt, CFM_PROTECTED); 291 } 292 293 294 static void 295 ME_LogFontFromStyle(ME_Context* c, LOGFONTW *lf, const ME_Style *s) 296 { 297 ZeroMemory(lf, sizeof(LOGFONTW)); 298 lstrcpyW(lf->lfFaceName, s->fmt.szFaceName); 299 300 lf->lfHeight = ME_twips2pointsY(c, -s->fmt.yHeight); 301 302 lf->lfWeight = FW_NORMAL; 303 if (s->fmt.dwEffects & s->fmt.dwMask & CFM_BOLD) 304 lf->lfWeight = FW_BOLD; 305 if (s->fmt.dwMask & CFM_WEIGHT) 306 lf->lfWeight = s->fmt.wWeight; 307 if (s->fmt.dwEffects & s->fmt.dwMask & CFM_ITALIC) 308 lf->lfItalic = 1; 309 if ((s->fmt.dwEffects & s->fmt.dwMask & (CFM_UNDERLINE | CFE_LINK)) && 310 s->fmt.bUnderlineType == CFU_CF1UNDERLINE) 311 lf->lfUnderline = 1; 312 if (s->fmt.dwEffects & s->fmt.dwMask & CFM_STRIKEOUT) 313 lf->lfStrikeOut = 1; 314 if (s->fmt.dwEffects & s->fmt.dwMask & (CFM_SUBSCRIPT|CFM_SUPERSCRIPT)) 315 lf->lfHeight = (lf->lfHeight*2)/3; 316 /*lf.lfQuality = PROOF_QUALITY; */ 317 if (s->fmt.dwMask & CFM_FACE) 318 lf->lfPitchAndFamily = s->fmt.bPitchAndFamily; 319 if (s->fmt.dwMask & CFM_CHARSET) 320 lf->lfCharSet = s->fmt.bCharSet; 321 } 322 323 void ME_CharFormatFromLogFont(HDC hDC, const LOGFONTW *lf, CHARFORMAT2W *fmt) 324 { 325 int ry; 326 327 ME_InitCharFormat2W(fmt); 328 ry = GetDeviceCaps(hDC, LOGPIXELSY); 329 lstrcpyW(fmt->szFaceName, lf->lfFaceName); 330 fmt->dwEffects = 0; 331 fmt->dwMask = CFM_WEIGHT|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_UNDERLINETYPE|CFM_STRIKEOUT|CFM_SIZE|CFM_FACE|CFM_CHARSET; 332 fmt->wWeight = lf->lfWeight; 333 fmt->yHeight = -lf->lfHeight*1440/ry; 334 if (lf->lfWeight > FW_NORMAL) fmt->dwEffects |= CFM_BOLD; 335 if (lf->lfItalic) fmt->dwEffects |= CFM_ITALIC; 336 if (lf->lfUnderline) fmt->dwEffects |= CFM_UNDERLINE; 337 fmt->bUnderlineType = CFU_UNDERLINE; 338 if (lf->lfStrikeOut) fmt->dwEffects |= CFM_STRIKEOUT; 339 fmt->bPitchAndFamily = lf->lfPitchAndFamily; 340 fmt->bCharSet = lf->lfCharSet; 341 } 342 343 static BOOL ME_IsFontEqual(const LOGFONTW *p1, const LOGFONTW *p2) 344 { 345 if (memcmp(p1, p2, sizeof(LOGFONTW)-sizeof(p1->lfFaceName))) 346 return FALSE; 347 if (lstrcmpW(p1->lfFaceName, p2->lfFaceName)) 348 return FALSE; 349 return TRUE; 350 } 351 352 HFONT ME_SelectStyleFont(ME_Context *c, ME_Style *s) 353 { 354 HFONT hOldFont; 355 LOGFONTW lf; 356 int i, nEmpty, nAge = 0x7FFFFFFF; 357 ME_FontCacheItem *item; 358 assert(s); 359 360 ME_LogFontFromStyle(c, &lf, s); 361 362 for (i=0; i<HFONT_CACHE_SIZE; i++) 363 c->editor->pFontCache[i].nAge++; 364 for (i=0, nEmpty=-1, nAge=0; i<HFONT_CACHE_SIZE; i++) 365 { 366 item = &c->editor->pFontCache[i]; 367 if (!item->nRefs) 368 { 369 if (item->nAge > nAge) 370 nEmpty = i, nAge = item->nAge; 371 } 372 if (item->hFont && ME_IsFontEqual(&item->lfSpecs, &lf)) 373 break; 374 } 375 if (i < HFONT_CACHE_SIZE) /* found */ 376 { 377 item = &c->editor->pFontCache[i]; 378 TRACE_(richedit_style)("font reused %d\n", i); 379 item->nRefs++; 380 } 381 else 382 { 383 item = &c->editor->pFontCache[nEmpty]; /* this legal even when nEmpty == -1, as we don't dereference it */ 384 385 assert(nEmpty != -1); /* otherwise we leak cache entries or get too many fonts at once*/ 386 if (item->hFont) { 387 TRACE_(richedit_style)("font deleted %d\n", nEmpty); 388 DeleteObject(item->hFont); 389 item->hFont = NULL; 390 } 391 item->hFont = CreateFontIndirectW(&lf); 392 TRACE_(richedit_style)("font created %d\n", nEmpty); 393 item->nRefs = 1; 394 item->lfSpecs = lf; 395 } 396 s->font_cache = item; 397 hOldFont = SelectObject(c->hDC, item->hFont); 398 /* should be cached too, maybe ? */ 399 GetTextMetricsW(c->hDC, &s->tm); 400 return hOldFont; 401 } 402 403 static void release_font_cache(ME_FontCacheItem *item) 404 { 405 if (item->nRefs > 0) 406 { 407 item->nRefs--; 408 item->nAge = 0; 409 } 410 } 411 412 void ME_UnselectStyleFont(ME_Context *c, ME_Style *s, HFONT hOldFont) 413 { 414 SelectObject(c->hDC, hOldFont); 415 release_font_cache(s->font_cache); 416 s->font_cache = NULL; 417 } 418 419 void ME_DestroyStyle(ME_Style *s) 420 { 421 list_remove( &s->entry ); 422 if (s->font_cache) 423 { 424 release_font_cache( s->font_cache ); 425 s->font_cache = NULL; 426 } 427 ScriptFreeCache( &s->script_cache ); 428 FREE_OBJ(s); 429 } 430 431 void ME_AddRefStyle(ME_Style *s) 432 { 433 assert(s->nRefs>0); /* style with 0 references isn't supposed to exist */ 434 s->nRefs++; 435 all_refs++; 436 TRACE_(richedit_style)("ME_AddRefStyle %p, new refs=%d, total refs=%d\n", s, s->nRefs, all_refs); 437 } 438 439 void ME_ReleaseStyle(ME_Style *s) 440 { 441 s->nRefs--; 442 all_refs--; 443 if (s->nRefs==0) 444 TRACE_(richedit_style)("destroy style %p, total refs=%d\n", s, all_refs); 445 else 446 TRACE_(richedit_style)("release style %p, new refs=%d, total refs=%d\n", s, s->nRefs, all_refs); 447 if (!all_refs) TRACE("all style references freed (good!)\n"); 448 assert(s->nRefs>=0); 449 if (!s->nRefs) 450 ME_DestroyStyle(s); 451 } 452 453 ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor) 454 { 455 if (ME_IsSelection(editor)) 456 { 457 ME_Cursor *from, *to; 458 459 ME_GetSelection(editor, &from, &to); 460 ME_AddRefStyle(from->pRun->member.run.style); 461 return from->pRun->member.run.style; 462 } 463 if (editor->pBuffer->pCharStyle) { 464 ME_AddRefStyle(editor->pBuffer->pCharStyle); 465 return editor->pBuffer->pCharStyle; 466 } 467 else 468 { 469 ME_Cursor *pCursor = &editor->pCursors[nCursor]; 470 ME_DisplayItem *pRunItem = pCursor->pRun; 471 ME_DisplayItem *pPrevItem = NULL; 472 if (pCursor->nOffset) { 473 ME_Run *pRun = &pRunItem->member.run; 474 ME_AddRefStyle(pRun->style); 475 return pRun->style; 476 } 477 pPrevItem = ME_FindItemBack(pRunItem, diRunOrParagraph); 478 if (pPrevItem->type == diRun) 479 { 480 ME_AddRefStyle(pPrevItem->member.run.style); 481 return pPrevItem->member.run.style; 482 } 483 else 484 { 485 ME_AddRefStyle(pRunItem->member.run.style); 486 return pRunItem->member.run.style; 487 } 488 } 489 } 490 491 void ME_SaveTempStyle(ME_TextEditor *editor, ME_Style *style) 492 { 493 ME_Style *old_style = editor->pBuffer->pCharStyle; 494 495 if (style) ME_AddRefStyle( style ); 496 editor->pBuffer->pCharStyle = style; 497 if (old_style) ME_ReleaseStyle( old_style ); 498 } 499 500 void ME_ClearTempStyle(ME_TextEditor *editor) 501 { 502 if (!editor->pBuffer->pCharStyle) return; 503 ME_ReleaseStyle(editor->pBuffer->pCharStyle); 504 editor->pBuffer->pCharStyle = NULL; 505 } 506 507 /****************************************************************************** 508 * ME_SetDefaultCharFormat 509 * 510 * Applies a style change to the default character style. 511 * 512 * The default style is special in that it is mutable - runs 513 * in the document that have this style should change if the 514 * default style changes. That means we need to fix up this 515 * style manually. 516 */ 517 void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod) 518 { 519 ME_Style *style, *def = editor->pBuffer->pDefaultStyle; 520 521 assert(mod->cbSize == sizeof(CHARFORMAT2W)); 522 style = ME_ApplyStyle(editor, def, mod); 523 def->fmt = style->fmt; 524 def->tm = style->tm; 525 if (def->font_cache) 526 { 527 release_font_cache( def->font_cache ); 528 def->font_cache = NULL; 529 } 530 ScriptFreeCache( &def->script_cache ); 531 ME_ReleaseStyle( style ); 532 ME_MarkAllForWrapping( editor ); 533 } 534