00001
00002 #include "mythfontproperties.h"
00003
00004 #include <QCoreApplication>
00005 #include <QDomDocument>
00006 #include <QFontInfo>
00007 #include <QRect>
00008
00009 #include "mythlogging.h"
00010 #include "mythdb.h"
00011
00012 #include "mythuihelper.h"
00013 #include "mythmainwindow.h"
00014 #include "xmlparsebase.h"
00015
00016 #define LOC QString("MythFontProperties: ")
00017
00018 MythFontProperties::MythFontProperties() :
00019 m_brush(QColor(Qt::white)), m_hasShadow(false), m_shadowAlpha(255),
00020 m_hasOutline(false), m_outlineAlpha(255), m_bFreeze(false), m_stretch(100)
00021 {
00022 CalcHash();
00023 }
00024
00025 void MythFontProperties::SetFace(const QFont &face)
00026 {
00027 m_face = face;
00028 CalcHash();
00029 }
00030
00031 void MythFontProperties::SetColor(const QColor &color)
00032 {
00033 m_brush.setColor(color);
00034 CalcHash();
00035 }
00036
00037 void MythFontProperties::SetShadow(bool on, const QPoint &offset,
00038 const QColor &color, int alpha)
00039 {
00040 m_hasShadow = on;
00041 m_shadowOffset = offset;
00042 m_shadowColor = color;
00043 m_shadowAlpha = alpha;
00044 CalcHash();
00045 }
00046
00047 void MythFontProperties::SetOutline(bool on, const QColor &color,
00048 int size, int alpha)
00049 {
00050 m_hasOutline = on;
00051 m_outlineColor = color;
00052 m_outlineSize = size;
00053 m_outlineAlpha = alpha;
00054 CalcHash();
00055 }
00056
00057 void MythFontProperties::GetShadow(QPoint &offset, QColor &color, int &alpha) const
00058 {
00059 offset = m_shadowOffset;
00060 color = m_shadowColor;
00061 alpha = m_shadowAlpha;
00062 }
00063
00064 void MythFontProperties::GetOutline(QColor &color, int &size, int &alpha) const
00065 {
00066 color = m_outlineColor;
00067 size = m_outlineSize;
00068 alpha = m_outlineAlpha;
00069 }
00070
00071 void MythFontProperties::GetOffset(QPoint &offset) const
00072 {
00073 offset = m_drawingOffset;
00074 }
00075
00076 void MythFontProperties::CalcHash(void)
00077 {
00078 if (m_bFreeze)
00079 return;
00080
00081 m_hash = QString("%1%2%3%4").arg(m_face.toString())
00082 .arg(m_brush.color().name())
00083 .arg(m_hasShadow)
00084 .arg(m_hasOutline);
00085
00086 if (m_hasShadow)
00087 m_hash += QString("%1%2%3%4").arg(m_shadowOffset.x())
00088 .arg(m_shadowOffset.y()).arg(m_shadowColor.name())
00089 .arg(m_shadowAlpha);
00090
00091 if (m_hasOutline)
00092 m_hash += QString("%1%2%3").arg(m_outlineColor.name())
00093 .arg(m_outlineSize).arg(m_outlineAlpha);
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104 m_drawingOffset = QPoint(0, 0);
00105
00106 if (m_hasOutline)
00107 {
00108 m_drawingOffset = QPoint(m_outlineSize, m_outlineSize);
00109 }
00110
00111 if (m_hasShadow && !m_hasOutline)
00112 {
00113 if (m_shadowOffset.x() < 0)
00114 m_drawingOffset.setX(-m_shadowOffset.x());
00115 if (m_shadowOffset.y() < 0)
00116 m_drawingOffset.setY(-m_shadowOffset.y());
00117 }
00118 if (m_hasShadow && m_hasOutline)
00119 {
00120 if (m_shadowOffset.x() < 0 && m_shadowOffset.x() < -m_outlineSize)
00121 m_drawingOffset.setX(-m_shadowOffset.x());
00122 if (m_shadowOffset.y() < 0 && m_shadowOffset.y() < -m_outlineSize)
00123 m_drawingOffset.setY(-m_shadowOffset.y());
00124 }
00125 }
00126
00127 void MythFontProperties::Rescale(int height)
00128 {
00129 m_face.setPixelSize(m_relativeSize * height);
00130 }
00131
00132 void MythFontProperties::Rescale(void)
00133 {
00134 QRect rect = GetMythMainWindow()->GetUIScreenRect();
00135 Rescale(rect.height());
00136 }
00137
00138 void MythFontProperties::AdjustStretch(int stretch)
00139 {
00140 int newStretch = (int)(((float)m_stretch * ((float)stretch / 100.0f)) + 0.5f);
00141
00142 if (newStretch <= 0)
00143 newStretch = 1;
00144
00145 m_face.setStretch(newStretch);
00146 }
00147
00148 void MythFontProperties::SetPixelSize(float size)
00149 {
00150 QSize baseSize = GetMythUI()->GetBaseSize();
00151 m_relativeSize = size / (float)(baseSize.height());
00152 m_face.setPixelSize(GetMythMainWindow()->NormY((int)(size + 0.5f)));
00153 }
00154
00155 void MythFontProperties::SetPointSize(uint points)
00156 {
00157 float pixels = (float)points / 72.0 * 100.0;
00158 SetPixelSize(pixels);
00159 }
00160
00161 void MythFontProperties::Freeze(void)
00162 {
00163 m_bFreeze = true;
00164 }
00165
00166 void MythFontProperties::Unfreeze(void)
00167 {
00168 m_bFreeze = false;
00169 CalcHash();
00170 }
00171
00172 MythFontProperties *MythFontProperties::ParseFromXml(
00173 const QString &filename,
00174 const QDomElement &element,
00175 MythUIType *parent,
00176 bool addToGlobal,
00177 bool showWarnings)
00178 {
00179
00180
00181 bool fromBase = false;
00182 MythFontProperties *newFont = new MythFontProperties();
00183 newFont->Freeze();
00184
00185 if (element.tagName() == "font")
00186 LOG(VB_GENERAL, LOG_WARNING, LOC +
00187 QString("File %1: Use of 'font' is deprecated in favour of "
00188 "'fontdef'") .arg(filename));
00189
00190 QString name = element.attribute("name", "");
00191 if (name.isEmpty())
00192 {
00193 VERBOSE_XML(VB_GENERAL, LOG_ERR,
00194 filename, element, "Font requires a name");
00195 delete newFont;
00196 return NULL;
00197 }
00198
00199 QString base = element.attribute("from", "");
00200
00201 if (!base.isEmpty())
00202 {
00203 MythFontProperties *tmp = NULL;
00204
00205 if (parent)
00206 tmp = parent->GetFont(base);
00207
00208 if (!tmp)
00209 tmp = GetGlobalFontMap()->GetFont(base);
00210
00211 if (!tmp)
00212 {
00213 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
00214 QString("Specified base font '%1' does not exist.").arg(base));
00215
00216 delete newFont;
00217 return NULL;
00218 }
00219
00220 *newFont = *tmp;
00221 fromBase = true;
00222 }
00223
00224 int size, pixelsize;
00225 size = pixelsize = -1;
00226
00227 QString face = element.attribute("face", "");
00228 if (face.isEmpty())
00229 {
00230 if (!fromBase)
00231 {
00232 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
00233 "Font needs a face");
00234 delete newFont;
00235 return NULL;
00236 }
00237 }
00238 else
00239 {
00240 newFont->m_face.setFamily(face);
00241 }
00242
00243 if (addToGlobal && GetGlobalFontMap()->Contains(name))
00244 {
00245 MythFontProperties *tmp = GetGlobalFontMap()->GetFont(name);
00246 if (showWarnings)
00247 {
00248 VERBOSE_XML(VB_GENERAL, LOG_WARNING, filename, element,
00249 QString("Attempting to define '%1'\n\t\t\t"
00250 "with face '%2', but it already "
00251 "exists with face '%3'")
00252 .arg(name).arg(QFontInfo(newFont->m_face).family())
00253 .arg((tmp) ? QFontInfo(tmp->m_face).family() : "ERROR"));
00254 }
00255 delete newFont;
00256 return NULL;
00257 }
00258
00259 QString hint = element.attribute("stylehint", "");
00260 if (!hint.isEmpty())
00261 {
00262 newFont->m_face.setStyleHint((QFont::StyleHint)hint.toInt());
00263 }
00264
00265 for (QDomNode child = element.firstChild(); !child.isNull();
00266 child = child.nextSibling())
00267 {
00268 QDomElement info = child.toElement();
00269 if (!info.isNull())
00270 {
00271 if (info.tagName() == "size")
00272 {
00273 size = getFirstText(info).toInt();
00274 }
00275 else if (info.tagName() == "pixelsize")
00276 {
00277 pixelsize = getFirstText(info).toInt();
00278 }
00279 else if (info.tagName() == "color")
00280 {
00281 newFont->m_brush = QBrush(QColor(getFirstText(info)));
00282 }
00283 else if (info.tagName() == "gradient")
00284 {
00285 newFont->m_brush = parseGradient(info);
00286 }
00287 else if (info.tagName() == "shadowcolor")
00288 {
00289 newFont->m_shadowColor = QColor(getFirstText(info));
00290 }
00291 else if (info.tagName() == "shadowoffset")
00292 {
00293 newFont->m_hasShadow = true;
00294 newFont->m_shadowOffset = parsePoint(info);
00295 }
00296 else if (info.tagName() == "shadowalpha")
00297 {
00298 newFont->m_shadowAlpha = getFirstText(info).toInt();
00299 }
00300 else if (info.tagName() == "outlinecolor")
00301 {
00302 newFont->m_outlineColor = QColor(getFirstText(info));
00303 }
00304 else if (info.tagName() == "outlinesize")
00305 {
00306 newFont->m_hasOutline = true;
00307 newFont->m_outlineSize = getFirstText(info).toInt();
00308 }
00309 else if (info.tagName() == "outlinealpha")
00310 {
00311 newFont->m_outlineAlpha = getFirstText(info).toInt();
00312 }
00313 else if (info.tagName() == "italics")
00314 {
00315 newFont->m_face.setItalic(parseBool(info));
00316 }
00317 else if (info.tagName() == "letterspacing")
00318 {
00319 newFont->m_face.setLetterSpacing(QFont::AbsoluteSpacing,
00320 getFirstText(info).toInt());
00321 }
00322 else if (info.tagName() == "wordspacing")
00323 {
00324 newFont->m_face.setWordSpacing(getFirstText(info).toInt());
00325 }
00326 else if (info.tagName() == "decoration")
00327 {
00328 QString dec = getFirstText(info).toLower();
00329 QStringList values = dec.split(',');
00330
00331 QStringList::Iterator it;
00332 for ( it = values.begin(); it != values.end(); ++it )
00333 {
00334 if (*it == "underline")
00335 newFont->m_face.setUnderline(true);
00336 else if (*it == "overline")
00337 newFont->m_face.setOverline(true);
00338 else if (*it == "strikeout")
00339 newFont->m_face.setStrikeOut(true);
00340 }
00341 }
00342 else if (info.tagName() == "weight")
00343 {
00344 QString weight = getFirstText(info).toLower();
00345
00346 if (weight == "ultralight" ||
00347 weight == "1")
00348 newFont->m_face.setWeight(1);
00349 else if (weight == "light" ||
00350 weight == "2")
00351 newFont->m_face.setWeight(QFont::Light);
00352 else if (weight == "normal" ||
00353 weight == "3")
00354 newFont->m_face.setWeight(QFont::Normal);
00355 else if (weight == "demibold" ||
00356 weight == "4")
00357 newFont->m_face.setWeight(QFont::DemiBold);
00358 else if (weight == "bold" ||
00359 weight == "5")
00360 newFont->m_face.setWeight(QFont::Bold);
00361 else if (weight == "black" ||
00362 weight == "6")
00363 newFont->m_face.setWeight(QFont::Black);
00364 else if (weight == "ultrablack" ||
00365 weight == "7")
00366 newFont->m_face.setWeight(99);
00367 else
00368 newFont->m_face.setWeight(QFont::Normal);
00369 }
00370 else if (info.tagName() == "stretch")
00371 {
00372 QString stretch = getFirstText(info).toLower();
00373
00374 if (stretch == "ultracondensed" ||
00375 stretch == "1")
00376 newFont->m_stretch = QFont::UltraCondensed;
00377 else if (stretch == "extracondensed" ||
00378 stretch == "2")
00379 newFont->m_stretch = QFont::ExtraCondensed;
00380 else if (stretch == "condensed" ||
00381 stretch == "3")
00382 newFont->m_stretch = QFont::Condensed;
00383 else if (stretch == "semicondensed" ||
00384 stretch == "4")
00385 newFont->m_stretch = QFont::SemiCondensed;
00386 else if (stretch == "unstretched" ||
00387 stretch == "5")
00388 newFont->m_stretch = QFont::Unstretched;
00389 else if (stretch == "semiexpanded" ||
00390 stretch == "6")
00391 newFont->m_stretch = QFont::SemiExpanded;
00392 else if (stretch == "expanded" ||
00393 stretch == "7")
00394 newFont->m_stretch = QFont::Expanded;
00395 else if (stretch == "extraexpanded" ||
00396 stretch == "8")
00397 newFont->m_stretch = QFont::ExtraExpanded;
00398 else if (stretch == "ultraexpanded" ||
00399 stretch == "9")
00400 newFont->m_stretch = QFont::UltraExpanded;
00401 else
00402 newFont->m_stretch = QFont::Unstretched;
00403
00404 newFont->m_face.setStretch(newFont->m_stretch);
00405 }
00406 else
00407 {
00408 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, info,
00409 QString("Unknown tag in font '%1'").arg(name));
00410 delete newFont;
00411 return NULL;
00412 }
00413 }
00414 }
00415
00416 if (size <= 0 && pixelsize <= 0 && !fromBase)
00417 {
00418 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
00419 "Font size must be greater than 0.");
00420 delete newFont;
00421 return NULL;
00422 }
00423 else if (pixelsize > 0)
00424 {
00425 newFont->SetPixelSize(pixelsize);
00426 }
00427 else if (size > 0)
00428 {
00429 newFont->SetPointSize(size);
00430 }
00431
00432 newFont->Unfreeze();
00433
00434 QFontInfo fi(newFont->m_face);
00435 if (newFont->m_face.family() != fi.family())
00436 {
00437 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
00438 QString("Failed to load '%1', got '%2' instead")
00439 .arg(newFont->m_face.family()).arg(fi.family()));
00440 }
00441 else
00442 {
00443 VERBOSE_XML(VB_GUI, LOG_DEBUG, filename, element,
00444 QString("loaded '%1'").arg(fi.family()));
00445 }
00446
00447 if (addToGlobal)
00448 GetGlobalFontMap()->AddFont(name, newFont);
00449
00450 return newFont;
00451 }
00452
00453 static FontMap *gFontMap = NULL;
00454
00455
00456 QMap<QString, fontProp> globalFontMap;
00457
00458 MythFontProperties *FontMap::GetFont(const QString &text)
00459 {
00460 if (text.isEmpty())
00461 return NULL;
00462
00463 if (m_FontMap.contains(text))
00464 return &(m_FontMap[text]);
00465 return NULL;
00466 }
00467
00468 bool FontMap::AddFont(const QString &text, MythFontProperties *font)
00469 {
00470 if (!font || text.isEmpty())
00471 return false;
00472
00473 if (m_FontMap.contains(text))
00474 {
00475 LOG(VB_GENERAL, LOG_ERR, LOC +
00476 QString("Already have a font: %1").arg(text));
00477 return false;
00478 }
00479
00480 m_FontMap[text] = *font;
00481
00482 {
00483
00484 fontProp oldf;
00485
00486 oldf.face = font->m_face;
00487 oldf.color = font->m_brush.color();
00488 if (font->m_hasShadow)
00489 {
00490 oldf.dropColor = font->m_shadowColor;
00491 oldf.shadowOffset = font->m_shadowOffset;
00492 }
00493
00494 globalFontMap[text] = oldf;
00495 }
00496
00497 return true;
00498 }
00499
00500 bool FontMap::Contains(const QString &text)
00501 {
00502 return m_FontMap.contains(text);
00503 }
00504
00505 void FontMap::Clear(void)
00506 {
00507 m_FontMap.clear();
00508
00509
00510 globalFontMap.clear();
00511 }
00512
00513 void FontMap::Rescale(int height)
00514 {
00515 if (height <= 0)
00516 {
00517 QRect rect = GetMythMainWindow()->GetUIScreenRect();
00518 height = rect.height();
00519 }
00520
00521 QMap<QString, MythFontProperties>::iterator it;
00522 for (it = m_FontMap.begin(); it != m_FontMap.end(); ++it)
00523 {
00524 (*it).Rescale(height);
00525 }
00526 }
00527
00528 FontMap *FontMap::GetGlobalFontMap(void)
00529 {
00530 if (!gFontMap)
00531 gFontMap = new FontMap();
00532 return gFontMap;
00533 }
00534
00535 FontMap *GetGlobalFontMap(void)
00536 {
00537 return FontMap::GetGlobalFontMap();
00538 }
00539