File indexing completed on 2024-04-21 04:05:25

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("themes/") + 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("themes/") + 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"