File indexing completed on 2023-10-01 08:05:26

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 <QImage>
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, "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, "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     QSize s(int(width), int(width / mCardAspectRatio));
0241 
0242     if (s != mCardCache->size())
0243     {
0244         mCardCache->setSize(s);
0245         QMetaObject::invokeMethod(this, "loadCardsInBackground", Qt::QueuedConnection);
0246     }
0247 
0248     QPixmap pix = mCardCache->frontside(info);
0249     return pix;
0250 }
0251 
0252 // Get the pixmap for a card back, given the desired card width in pixel
0253 const QPixmap ThemeManager::getCardback(double width)
0254 {
0255     QSize s(int(width), int(width / mCardAspectRatio));
0256     if (s != mCardCache->size())
0257     {
0258         mCardCache->setSize(s);
0259         QMetaObject::invokeMethod(this, "loadCardsInBackground", Qt::QueuedConnection);
0260     }
0261 
0262     QPixmap pix = mCardCache->backside();
0263     return pix;
0264 }
0265 
0266 // Get a pixmap when its size is given (this can distort the image)
0267 const QPixmap ThemeManager::getPixmap(QSvgRenderer *renderer, const QString &svgid, const QSize &size)
0268 {
0269     QPixmap pixmap;
0270     if (size.width() < 1 || size.height() < 1)
0271     {
0272         if (global_debug > 1)
0273         qCDebug(LSKAT_LOG) << "ThemeManager::getPixmap Cannot create svgid ID " << svgid << " with zero size" << size;
0274         return pixmap;
0275     }
0276 
0277     // Cached pixmap?
0278     if (mPixmapCache.contains(svgid))
0279     {
0280         pixmap = mPixmapCache[svgid];
0281         if (pixmap.size() == size)
0282         {
0283             return pixmap;
0284         }
0285     }
0286 
0287     // Create new image
0288     QImage image(size, QImage::Format_ARGB32_Premultiplied);
0289     image.fill(0);
0290     QPainter p(&image);
0291     renderer->render(&p, svgid);
0292     p.end();
0293     pixmap = QPixmap::fromImage(image);
0294     if (pixmap.isNull())
0295         qCCritical(LSKAT_LOG) << "ThemeManager::getPixmap Cannot load svgid ID " << svgid;
0296 
0297     // Cache image
0298     mPixmapCache[svgid] = pixmap;
0299 
0300     return pixmap;
0301 }
0302 
0303 // Get a pixmap when its size is given (this can distort the image)
0304 const QPixmap ThemeManager::getPixmap(const QString &svgid, const QSize &size)
0305 {
0306     return getPixmap(mRenderer, svgid, size);
0307 }
0308 
0309 // Get a pixmap when only width is given (this keeps the aspect ratio)
0310 const QPixmap ThemeManager::getPixmap(const QString &svgid, double width)
0311 {
0312     QRectF rect   = mRenderer->boundsOnElement(svgid);
0313     double factor = width / rect.width();
0314     QSize size    = QSize(int(width), int(rect.height() * factor));
0315     return getPixmap(svgid, size);
0316 }
0317 
0318 // Get a pixmap with original properties and a scale factor given with respect to
0319 // another SVG item.
0320 const QPixmap ThemeManager::getPixmap(const QString &svgid, const QString &svgref, double refwidth)
0321 {
0322     QRectF refrect    = mRenderer->boundsOnElement(svgref);
0323     QRectF rect       = mRenderer->boundsOnElement(svgid);
0324     double factor     = refwidth / refrect.width();
0325     QSize size        = QSize(int(rect.width() * factor), int(rect.height() * factor));
0326     return getPixmap(svgid, size);
0327 }
0328 
0329 void ThemeManager::loadCardsInBackground()
0330 {
0331     mCardCache->loadTheme(KCardCache::LoadFrontSide|KCardCache::Load32Cards);
0332 }
0333 
0334 // ========================== Themable interface ===============================
0335 
0336 // Constructs a themable interface
0337 Themable::Themable()
0338 {
0339     mScale        = 1.0;
0340     mThemeManager = nullptr;
0341 }
0342 
0343 // Constructs a themable interface given its id and the master theme manager.
0344 // This automatically registers the object with the manager.
0345 Themable::Themable(const QString &id, ThemeManager *thememanager)
0346 {
0347     mScale        = 1.0;
0348     mId           = id;
0349     mThemeManager = thememanager;
0350     if (!thememanager) return;
0351     thememanager->registerTheme(this);
0352 }
0353 
0354 // Destructs the themable object
0355 Themable::~Themable()
0356 {
0357     if (mThemeManager) mThemeManager->unregisterTheme(this);
0358 }
0359 
0360 #include "moc_thememanager.cpp"