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