File indexing completed on 2024-04-28 07:55:00
0001 /* 0002 This file is part of the KDE games lskat program 0003 SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 // General includes 0009 // #include <typeinfo> 0010 0011 // Own include 0012 #include "thememanager.h" 0013 0014 // Qt includes 0015 #include <QApplication> 0016 #include <QPixmap> 0017 #include <QPainter> 0018 #include <QRectF> 0019 0020 // KF includes 0021 #include <KConfigGroup> 0022 0023 // Local includes 0024 #include "lskat_debug.h" 0025 #include "lskatglobal.h" 0026 #include "deck.h" 0027 #include "fromlibkdegames/cardcache.h" 0028 0029 // SVG id for accessing the backside of a card 0030 #define CARDBACK_SVGID "back" 0031 0032 //convenience methods for converting to KCardInfo enums 0033 KCardInfo convertToKCardInfo(CardDeck::Suite suite, CardDeck::CardType card) 0034 { 0035 KCardInfo::Suit s; 0036 KCardInfo::Card c; 0037 switch(card) 0038 { 0039 case CardDeck::Ace: 0040 c = KCardInfo::Ace; 0041 break; 0042 case CardDeck::Eight: 0043 c = KCardInfo::Eight; 0044 break; 0045 case CardDeck::King: 0046 c = KCardInfo::King; 0047 break; 0048 case CardDeck::Nine: 0049 c = KCardInfo::Nine; 0050 break; 0051 case CardDeck::Ten: 0052 c = KCardInfo::Ten; 0053 break; 0054 case CardDeck::Jack: 0055 c = KCardInfo::Jack; 0056 break; 0057 case CardDeck::Seven: 0058 c = KCardInfo::Seven; 0059 break; 0060 case CardDeck::Queen: 0061 c = KCardInfo::Queen; 0062 break; 0063 } 0064 switch(suite) 0065 { 0066 case CardDeck::Heart: 0067 s = KCardInfo::Heart; 0068 break; 0069 case CardDeck::Club: 0070 s = KCardInfo::Club; 0071 break; 0072 case CardDeck::Diamond: 0073 s = KCardInfo::Diamond; 0074 break; 0075 case CardDeck::Spade: 0076 s = KCardInfo::Spade; 0077 break; 0078 default: 0079 s = KCardInfo::None; 0080 break; 0081 } 0082 return KCardInfo(s, c); 0083 } 0084 0085 // Constructor for the theme manager 0086 ThemeManager::ThemeManager(const QString &cardTheme, 0087 const QString &themefile, QObject *parent, int initialSize) 0088 : QObject(parent), mRenderer(nullptr), mConfig(nullptr), mCardTheme(cardTheme), 0089 mScale(initialSize), mAspectRatio(1.0), mCardAspectRatio(1.0) 0090 { 0091 mCardCache = new KCardCache(); 0092 0093 // updateTheme(themefile); 0094 updateCardTheme(themefile, cardTheme); 0095 } 0096 0097 ThemeManager::~ThemeManager() 0098 { 0099 delete mConfig; 0100 delete mCardCache; 0101 } 0102 0103 // Register an object with the manager 0104 void ThemeManager::registerTheme(Themable *ob) 0105 { 0106 mObjects[ob] = 1; 0107 } 0108 0109 // Unregister an object from the manager 0110 void ThemeManager::unregisterTheme(Themable *ob) 0111 { 0112 mObjects.remove(ob); 0113 } 0114 0115 // Check whether the theme is alright 0116 int ThemeManager::checkTheme() 0117 { 0118 // Check theme 0119 if (mRenderer == nullptr) return 1; 0120 return 0; // OK 0121 } 0122 0123 // Force an refresh of the theme object given 0124 void ThemeManager::updateTheme(Themable *ob) 0125 { 0126 ob->changeTheme(); 0127 } 0128 0129 // Update card deck and card set 0130 void ThemeManager::updateCardTheme(const QString &cardTheme) 0131 { 0132 updateCardTheme(mThemeFile, cardTheme); 0133 } 0134 0135 // Update card deck and card set 0136 void ThemeManager::updateCardTheme(const QString &themefile, const QString &cardTheme) 0137 { 0138 if (global_debug > 1) 0139 { 0140 qCDebug(LSKAT_LOG) << "ThemeManager Pixmap cards: "; 0141 qCDebug(LSKAT_LOG) << " Card theme =" << cardTheme; 0142 } 0143 0144 // Cards 0145 mCardTheme = cardTheme; 0146 mCardCache->setDeckName(mCardTheme); 0147 0148 mCardCache->setSize(QSize()); 0149 0150 updateTheme(themefile); 0151 } 0152 0153 // Update the theme file and refresh all registered objects. Used 0154 // to really change the theme. 0155 void ThemeManager::updateTheme(const QString &themefile) 0156 { 0157 // Empty cache 0158 mPixmapCache.clear(); 0159 mThemeFile = themefile; 0160 0161 // Process dirs 0162 QString rcfile = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("grafix/") + themefile); 0163 if (global_debug > 0) qCDebug(LSKAT_LOG) << "ThemeManager LOAD with theme " << rcfile; 0164 0165 // Read config and SVG file for theme 0166 delete mConfig; 0167 mConfig = new KConfig(rcfile, KConfig::NoGlobals); 0168 QString svgfile = config(QStringLiteral("general")).readEntry("svgfile"); 0169 svgfile = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("grafix/") + svgfile); 0170 if (global_debug > 0) qCDebug(LSKAT_LOG) << "Reading SVG master file =" << svgfile; 0171 0172 mAspectRatio = config(QStringLiteral("general")).readEntry("aspect-ratio", 1.0); 0173 mCardAspectRatio = config(QStringLiteral("general")).readEntry("card-aspect-ratio", 1.0); 0174 if (global_debug > 0) qCDebug(LSKAT_LOG) << "Aspect ration =" << mAspectRatio << "Cards aspect=" << mCardAspectRatio; 0175 0176 delete mRenderer; 0177 mRenderer = new QSvgRenderer(this); 0178 bool result = mRenderer->load(svgfile); 0179 if (!result) 0180 { 0181 mRenderer = nullptr; 0182 qCCritical(LSKAT_LOG) << "Cannot open file" << svgfile; 0183 } 0184 0185 // Notify all theme objects of a change 0186 QHashIterator<Themable *, int> it(mObjects); 0187 while (it.hasNext()) 0188 { 0189 it.next(); 0190 Themable *ob = it.key(); 0191 ob->changeTheme(); 0192 } 0193 } 0194 0195 // Rescale the theme. Call all registered objects so that they can refresh. 0196 void ThemeManager::rescale(int scale, QPoint offset) 0197 { 0198 if (global_debug > 1) 0199 { 0200 if (scale == mScale) 0201 qCDebug(LSKAT_LOG) << "No scale change to" << scale << "If this happens too often it is BAD"; 0202 } 0203 //if (scale == mScale) return; 0204 mScale = scale; 0205 mOffset = offset; 0206 if (global_debug > 1) qCDebug(LSKAT_LOG) << "THEMEMANAGER:: Rescale to " << scale << " offset to " << offset; 0207 0208 QHashIterator<Themable *, int> it(mObjects); 0209 while (it.hasNext()) 0210 { 0211 it.next(); 0212 Themable *ob = it.key(); 0213 ob->changeTheme(); 0214 } 0215 } 0216 0217 // Retrieve the theme's scale 0218 double ThemeManager::getScale() 0219 { 0220 return (double)mScale; 0221 } 0222 0223 // Retrieve the theme offset 0224 QPoint ThemeManager::getOffset() 0225 { 0226 return mOffset; 0227 } 0228 0229 // Retrieve the current theme configuration file. 0230 KConfigGroup ThemeManager::config(const QString &id) 0231 { 0232 KConfigGroup grp = mConfig->group(id); 0233 return grp; 0234 } 0235 0236 // Get the pixmap for a card , given suite, type and the desired card width in pixel 0237 const QPixmap ThemeManager::getCard(int suite, int cardtype, double width) 0238 { 0239 KCardInfo info = convertToKCardInfo(CardDeck::Suite(suite), CardDeck::CardType(cardtype)); 0240 0241 const qreal dpr = qApp->devicePixelRatio(); 0242 const int deviceWidth = width * dpr; 0243 const QSize deviceSize(deviceWidth, int(deviceWidth / mCardAspectRatio)); 0244 0245 if (deviceSize != mCardCache->size()) { 0246 mCardCache->setSize(deviceSize); 0247 QMetaObject::invokeMethod(this, "loadCardsInBackground", Qt::QueuedConnection); 0248 } 0249 0250 QPixmap pix = mCardCache->frontside(info); 0251 pix.setDevicePixelRatio(dpr); 0252 return pix; 0253 } 0254 0255 // Get the pixmap for a card back, given the desired card width in pixel 0256 const QPixmap ThemeManager::getCardback(double width) 0257 { 0258 const qreal dpr = qApp->devicePixelRatio(); 0259 const int deviceWidth = width * dpr; 0260 const QSize deviceSize(deviceWidth, int(deviceWidth / mCardAspectRatio)); 0261 0262 if (deviceSize != mCardCache->size()) { 0263 mCardCache->setSize(deviceSize); 0264 QMetaObject::invokeMethod(this, "loadCardsInBackground", Qt::QueuedConnection); 0265 } 0266 0267 QPixmap pix = mCardCache->backside(); 0268 pix.setDevicePixelRatio(dpr); 0269 return pix; 0270 } 0271 0272 // Get a pixmap when its size is given (this can distort the image) 0273 const QPixmap ThemeManager::getPixmap(QSvgRenderer *renderer, const QString &svgid, const QSize &size) 0274 { 0275 QPixmap pixmap; 0276 if (size.width() < 1 || size.height() < 1) 0277 { 0278 if (global_debug > 1) 0279 qCDebug(LSKAT_LOG) << "ThemeManager::getPixmap Cannot create svgid ID " << svgid << " with zero size" << size; 0280 return pixmap; 0281 } 0282 0283 const auto dpr = qApp->devicePixelRatio(); 0284 const QSize deviceSize = size * dpr; 0285 0286 // Cached pixmap? 0287 if (mPixmapCache.contains(svgid)) 0288 { 0289 pixmap = mPixmapCache[svgid]; 0290 if (pixmap.size() == deviceSize) 0291 { 0292 return pixmap; 0293 } 0294 } 0295 0296 // Create new image 0297 pixmap = QPixmap(deviceSize); 0298 pixmap.fill(Qt::transparent); 0299 QPainter p(&pixmap); 0300 renderer->render(&p, svgid); 0301 p.end(); 0302 pixmap.setDevicePixelRatio(dpr); 0303 if (pixmap.isNull()) 0304 qCCritical(LSKAT_LOG) << "ThemeManager::getPixmap Cannot load svgid ID " << svgid; 0305 0306 // Cache image 0307 mPixmapCache[svgid] = pixmap; 0308 0309 return pixmap; 0310 } 0311 0312 // Get a pixmap when its size is given (this can distort the image) 0313 const QPixmap ThemeManager::getPixmap(const QString &svgid, const QSize &size) 0314 { 0315 return getPixmap(mRenderer, svgid, size); 0316 } 0317 0318 // Get a pixmap when only width is given (this keeps the aspect ratio) 0319 const QPixmap ThemeManager::getPixmap(const QString &svgid, double width) 0320 { 0321 QRectF rect = mRenderer->boundsOnElement(svgid); 0322 double factor = width / rect.width(); 0323 QSize size = QSize(int(width), int(rect.height() * factor)); 0324 return getPixmap(svgid, size); 0325 } 0326 0327 // Get a pixmap with original properties and a scale factor given with respect to 0328 // another SVG item. 0329 const QPixmap ThemeManager::getPixmap(const QString &svgid, const QString &svgref, double refwidth) 0330 { 0331 QRectF refrect = mRenderer->boundsOnElement(svgref); 0332 QRectF rect = mRenderer->boundsOnElement(svgid); 0333 double factor = refwidth / refrect.width(); 0334 QSize size = QSize(int(rect.width() * factor), int(rect.height() * factor)); 0335 return getPixmap(svgid, size); 0336 } 0337 0338 void ThemeManager::loadCardsInBackground() 0339 { 0340 mCardCache->loadTheme(KCardCache::LoadFrontSide|KCardCache::Load32Cards); 0341 } 0342 0343 // ========================== Themable interface =============================== 0344 0345 // Constructs a themable interface 0346 Themable::Themable() 0347 { 0348 mScale = 1.0; 0349 mThemeManager = nullptr; 0350 } 0351 0352 // Constructs a themable interface given its id and the master theme manager. 0353 // This automatically registers the object with the manager. 0354 Themable::Themable(const QString &id, ThemeManager *thememanager) 0355 { 0356 mScale = 1.0; 0357 mId = id; 0358 mThemeManager = thememanager; 0359 if (!thememanager) return; 0360 thememanager->registerTheme(this); 0361 } 0362 0363 // Destructs the themable object 0364 Themable::~Themable() 0365 { 0366 if (mThemeManager) mThemeManager->unregisterTheme(this); 0367 } 0368 0369 #include "moc_thememanager.cpp"