From 6b8ebe54e9c37892fc59cab8d006b1242198c1be Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 17 May 2015 23:30:14 +0200 Subject: [PATCH] Theme manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the first implementation of a theme manager. It’s a rebased and revisited version of #792. --- CMakeLists.txt | 2 +- cockatrice/CMakeLists.txt | 1 + cockatrice/cockatrice.qrc | 2 +- cockatrice/src/carddatabase.cpp | 15 +- cockatrice/src/decklistmodel.cpp | 2 +- cockatrice/src/deckview.cpp | 17 +- cockatrice/src/deckview.h | 1 - cockatrice/src/dlg_edit_tokens.cpp | 4 +- cockatrice/src/dlg_settings.cpp | 190 ++++-------------- cockatrice/src/dlg_settings.h | 31 +-- cockatrice/src/gameselector.cpp | 4 +- cockatrice/src/handcounter.cpp | 2 +- cockatrice/src/handzone.cpp | 15 +- cockatrice/src/handzone.h | 3 +- cockatrice/src/main.cpp | 7 +- cockatrice/src/pixmapgenerator.cpp | 14 +- cockatrice/src/player.cpp | 16 +- cockatrice/src/player.h | 3 +- cockatrice/src/playerlistwidget.cpp | 12 +- .../src/remotereplaylist_treewidget.cpp | 2 +- cockatrice/src/settingscache.cpp | 42 +--- cockatrice/src/settingscache.h | 21 +- cockatrice/src/stackzone.cpp | 15 +- cockatrice/src/stackzone.h | 3 +- cockatrice/src/tab_deck_editor.cpp | 18 +- cockatrice/src/tab_deck_storage.cpp | 12 +- cockatrice/src/tab_game.cpp | 6 +- cockatrice/src/tab_replays.cpp | 12 +- cockatrice/src/tab_room.cpp | 2 +- cockatrice/src/tab_supervisor.cpp | 2 +- cockatrice/src/tablezone.cpp | 18 +- cockatrice/src/tablezone.h | 9 +- cockatrice/src/thememanager.cpp | 144 +++++++++++++ cockatrice/src/thememanager.h | 42 ++++ cockatrice/src/window_main.cpp | 2 +- cockatrice/src/window_sets.cpp | 8 +- cockatrice/src/zoneviewwidget.cpp | 2 +- oracle/CMakeLists.txt | 1 + oracle/src/main.cpp | 3 + themes/CMakeLists.txt | 22 ++ themes/Default/version.txt | 1 + themes/Fabric/version.txt | 1 + .../Fabric/zones/handzone.png | Bin .../Fabric/zones/playerzone.png | Bin .../Fabric/zones/stackzone.png | Bin .../Fabric/zones/tablezone.png | Bin themes/Leather/version.txt | 1 + .../Leather/zones/handzone.png | Bin .../Leather/zones/playerzone.png | Bin .../Leather/zones/stackzone.png | Bin .../Leather/zones/tablezone.png | Bin themes/Plasma/version.txt | 1 + .../Plasma/zones/handzone.png | Bin .../Plasma/zones/playerzone.png | Bin .../Plasma/zones/stackzone.png | Bin .../Plasma/zones/tablezone.png | Bin themes/VelvetMarble/version.txt | 1 + .../VelvetMarble/zones/handzone.jpg | Bin .../VelvetMarble/zones/playerzone.jpg | Bin .../VelvetMarble/zones/stackzone.jpg | Bin .../VelvetMarble/zones/tablezone.jpg | Bin zonebg/CMakeLists.txt | 16 -- zonebg/VelvetMarble_VerticalHand_Hand.jpg | Bin 24893 -> 0 bytes zonebg/VelvetMarble_VerticalHand_Player.jpg | Bin 37636 -> 0 bytes 64 files changed, 360 insertions(+), 388 deletions(-) create mode 100644 cockatrice/src/thememanager.cpp create mode 100644 cockatrice/src/thememanager.h create mode 100644 themes/CMakeLists.txt create mode 100644 themes/Default/version.txt create mode 100644 themes/Fabric/version.txt rename zonebg/fabric_green.png => themes/Fabric/zones/handzone.png (100%) rename zonebg/fabric_gray.png => themes/Fabric/zones/playerzone.png (100%) rename zonebg/fabric_red.png => themes/Fabric/zones/stackzone.png (100%) rename zonebg/fabric_blue.png => themes/Fabric/zones/tablezone.png (100%) create mode 100644 themes/Leather/version.txt rename zonebg/leather_green.png => themes/Leather/zones/handzone.png (100%) rename zonebg/leather_gray.png => themes/Leather/zones/playerzone.png (100%) rename zonebg/leather_red.png => themes/Leather/zones/stackzone.png (100%) rename zonebg/leather_blue.png => themes/Leather/zones/tablezone.png (100%) create mode 100644 themes/Plasma/version.txt rename zonebg/plasma_green.png => themes/Plasma/zones/handzone.png (100%) rename zonebg/plasma_gray.png => themes/Plasma/zones/playerzone.png (100%) rename zonebg/plasma_red.png => themes/Plasma/zones/stackzone.png (100%) rename zonebg/plasma_blue.png => themes/Plasma/zones/tablezone.png (100%) create mode 100644 themes/VelvetMarble/version.txt rename zonebg/VelvetMarble_HorizontalHand_Hand.jpg => themes/VelvetMarble/zones/handzone.jpg (100%) rename zonebg/VelvetMarble_Table.jpg => themes/VelvetMarble/zones/playerzone.jpg (100%) rename zonebg/VelvetMarble_Stack.jpg => themes/VelvetMarble/zones/stackzone.jpg (100%) rename zonebg/VelvetMarble_HorizontalHand_Player.jpg => themes/VelvetMarble/zones/tablezone.jpg (100%) delete mode 100644 zonebg/CMakeLists.txt delete mode 100644 zonebg/VelvetMarble_VerticalHand_Hand.jpg delete mode 100644 zonebg/VelvetMarble_VerticalHand_Player.jpg diff --git a/CMakeLists.txt b/CMakeLists.txt index c7ac1eb9..c4d239fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,7 +221,7 @@ option(WITH_CLIENT "build cockatrice" ON) if(WITH_CLIENT) add_subdirectory(cockatrice) add_subdirectory(sounds) - add_subdirectory(zonebg) + add_subdirectory(themes) SET(CPACK_INSTALL_CMAKE_PROJECTS "cockatrice;cockatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 99f2e62e..108bd885 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -84,6 +84,7 @@ SET(cockatrice_SOURCES src/playerlistwidget.cpp src/pixmapgenerator.cpp src/settingscache.cpp + src/thememanager.cpp src/localserver.cpp src/localserverinterface.cpp src/localclient.cpp diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index ebf5f44e..4fecaa62 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -1,6 +1,6 @@ - resources/back.svg + resources/back.svg resources/lock.svg resources/icon_delete.svg resources/icon_tab_changed.svg diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 26d8b219..d2e3a2f7 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -1,5 +1,6 @@ #include "carddatabase.h" #include "settingscache.h" +#include "thememanager.h" #include #include #include @@ -570,7 +571,7 @@ void CardInfo::loadPixmap(QPixmap &pixmap) pixmap = QPixmap(); if (getName().isEmpty()) { - pixmap.load(settingsCache->getCardBackPicturePath()); + pixmap = themeManager->getCardBackPixmap(); return; } @@ -594,15 +595,15 @@ void CardInfo::getPixmap(QSize size, QPixmap &pixmap) QPixmap bigPixmap; loadPixmap(bigPixmap); if (bigPixmap.isNull()) { - if (!getName().isEmpty()) { - pixmap = QPixmap(); // null - return; - } else { - QSvgRenderer svg(QString(":/back.svg")); + if (getName().isEmpty()) { + QSvgRenderer svg(QString("theme:back.svg")); bigPixmap = QPixmap(svg.defaultSize()); bigPixmap.fill(Qt::transparent); QPainter painter(&bigPixmap); svg.render(&painter); + } else { + pixmap = QPixmap(); // null + return; } } @@ -724,7 +725,7 @@ CardDatabase::CardDatabase(QObject *parent) noCard = new CardInfo(this); QPixmap tmp; noCard->loadPixmap(tmp); // cache pixmap for card back - connect(settingsCache, SIGNAL(cardBackPicturePathChanged()), noCard, SLOT(updatePixmapCache())); + connect(themeManager, SIGNAL(themeChanged()), noCard, SLOT(updatePixmapCache())); } CardDatabase::~CardDatabase() diff --git a/cockatrice/src/decklistmodel.cpp b/cockatrice/src/decklistmodel.cpp index 039db364..f08fe992 100644 --- a/cockatrice/src/decklistmodel.cpp +++ b/cockatrice/src/decklistmodel.cpp @@ -463,7 +463,7 @@ void DeckListModel::printDeckList(QPrinter *printer) cursor.insertBlock(headerBlockFormat, headerCharFormat); for (int i = 0; i < root->size(); i++) { - cursor.insertHtml("
"); + cursor.insertHtml("
"); //cursor.insertHtml("
"); cursor.insertBlock(headerBlockFormat, headerCharFormat); diff --git a/cockatrice/src/deckview.cpp b/cockatrice/src/deckview.cpp index 836aa533..0f439c17 100644 --- a/cockatrice/src/deckview.cpp +++ b/cockatrice/src/deckview.cpp @@ -5,6 +5,7 @@ #include "decklist.h" #include "carddatabase.h" #include "settingscache.h" +#include "thememanager.h" #include "main.h" DeckViewCardDragItem::DeckViewCardDragItem(DeckViewCard *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag) @@ -128,10 +129,6 @@ void DeckViewCard::hoverEnterEvent(QGraphicsSceneHoverEvent *event) DeckViewCardContainer::DeckViewCardContainer(const QString &_name) : QGraphicsItem(), name(_name), width(0), height(0) { - QString bgPath = settingsCache->getTableBgPath(); - if (!bgPath.isEmpty()) - bgPixmap.load(bgPath); - setCacheMode(DeviceCoordinateCache); } @@ -144,17 +141,7 @@ void DeckViewCardContainer::paint(QPainter *painter, const QStyleOptionGraphicsI { qreal totalTextWidth = getCardTypeTextWidth(); - if (bgPixmap.isNull()) { - QLinearGradient grad1(0, 0, 1, 0); - grad1.setCoordinateMode(QGradient::ObjectBoundingMode); - grad1.setColorAt(0, QColor(30, 30, 30)); - grad1.setColorAt(1, QColor(80, 80, 80)); - painter->fillRect(QRectF(0, 0, width, height), QBrush(grad1)); - - painter->fillRect(boundingRect(), QColor(0, 0, 0, 80)); - } - else - painter->fillRect(boundingRect(), QBrush(bgPixmap)); + painter->fillRect(boundingRect(), themeManager->getTableBgBrush()); painter->setPen(QColor(255, 255, 255, 100)); painter->drawLine(QPointF(0, separatorY), QPointF(width, separatorY)); diff --git a/cockatrice/src/deckview.h b/cockatrice/src/deckview.h index 059afa2a..91246eb2 100644 --- a/cockatrice/src/deckview.h +++ b/cockatrice/src/deckview.h @@ -52,7 +52,6 @@ private: QMultiMap cardsByType; QList > currentRowsAndCols; qreal width, height; - QPixmap bgPixmap; int getCardTypeTextWidth() const; public: enum { Type = typeDeckViewCardContainer }; diff --git a/cockatrice/src/dlg_edit_tokens.cpp b/cockatrice/src/dlg_edit_tokens.cpp index 4f2b6372..4f32890b 100644 --- a/cockatrice/src/dlg_edit_tokens.cpp +++ b/cockatrice/src/dlg_edit_tokens.cpp @@ -83,10 +83,10 @@ DlgEditTokens::DlgEditTokens(CardDatabaseModel *_cardDatabaseModel, QWidget *par connect(chooseTokenView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SLOT(tokenSelectionChanged(QModelIndex, QModelIndex))); QAction *aAddToken = new QAction(tr("Add token"), this); - aAddToken->setIcon(QIcon(":/resources/increment.svg")); + aAddToken->setIcon(QIcon("theme:increment.svg")); connect(aAddToken, SIGNAL(triggered()), this, SLOT(actAddToken())); QAction *aRemoveToken = new QAction(tr("Remove token"), this); - aRemoveToken->setIcon(QIcon(":/resources/decrement.svg")); + aRemoveToken->setIcon(QIcon("theme:decrement.svg")); connect(aRemoveToken, SIGNAL(triggered()), this, SLOT(actRemoveToken())); QToolBar *databaseToolBar = new QToolBar; diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 254c1e78..006301bc 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -26,6 +26,7 @@ #include "dlg_settings.h" #include "main.h" #include "settingscache.h" +#include "thememanager.h" #include "priceupdater.h" #include "soundengine.h" @@ -234,67 +235,23 @@ void GeneralSettingsPage::retranslateUi() AppearanceSettingsPage::AppearanceSettingsPage() { - QIcon deleteIcon(":/resources/icon_delete.svg"); - - handBgEdit = new QLineEdit(settingsCache->getHandBgPath()); - handBgEdit->setReadOnly(true); - QPushButton *handBgClearButton = new QPushButton(deleteIcon, QString()); - connect(handBgClearButton, SIGNAL(clicked()), this, SLOT(handBgClearButtonClicked())); - QPushButton *handBgButton = new QPushButton("..."); - connect(handBgButton, SIGNAL(clicked()), this, SLOT(handBgButtonClicked())); - - stackBgEdit = new QLineEdit(settingsCache->getStackBgPath()); - stackBgEdit->setReadOnly(true); - QPushButton *stackBgClearButton = new QPushButton(deleteIcon, QString()); - connect(stackBgClearButton, SIGNAL(clicked()), this, SLOT(stackBgClearButtonClicked())); - QPushButton *stackBgButton = new QPushButton("..."); - connect(stackBgButton, SIGNAL(clicked()), this, SLOT(stackBgButtonClicked())); + QString themeName = settingsCache->getThemeName(); - tableBgEdit = new QLineEdit(settingsCache->getTableBgPath()); - tableBgEdit->setReadOnly(true); - QPushButton *tableBgClearButton = new QPushButton(deleteIcon, QString()); - connect(tableBgClearButton, SIGNAL(clicked()), this, SLOT(tableBgClearButtonClicked())); - QPushButton *tableBgButton = new QPushButton("..."); - connect(tableBgButton, SIGNAL(clicked()), this, SLOT(tableBgButtonClicked())); - - playerAreaBgEdit = new QLineEdit(settingsCache->getPlayerBgPath()); - playerAreaBgEdit->setReadOnly(true); - QPushButton *playerAreaBgClearButton = new QPushButton(deleteIcon, QString()); - connect(playerAreaBgClearButton, SIGNAL(clicked()), this, SLOT(playerAreaBgClearButtonClicked())); - QPushButton *playerAreaBgButton = new QPushButton("..."); - connect(playerAreaBgButton, SIGNAL(clicked()), this, SLOT(playerAreaBgButtonClicked())); - - cardBackPicturePathEdit = new QLineEdit(settingsCache->getCardBackPicturePath()); - cardBackPicturePathEdit->setReadOnly(true); - QPushButton *cardBackPicturePathClearButton = new QPushButton(deleteIcon, QString()); - connect(cardBackPicturePathClearButton, SIGNAL(clicked()), this, SLOT(cardBackPicturePathClearButtonClicked())); - QPushButton *cardBackPicturePathButton = new QPushButton("..."); - connect(cardBackPicturePathButton, SIGNAL(clicked()), this, SLOT(cardBackPicturePathButtonClicked())); - - QGridLayout *zoneBgGrid = new QGridLayout; - zoneBgGrid->addWidget(&handBgLabel, 0, 0); - zoneBgGrid->addWidget(handBgEdit, 0, 1); - zoneBgGrid->addWidget(handBgClearButton, 0, 2); - zoneBgGrid->addWidget(handBgButton, 0, 3); - zoneBgGrid->addWidget(&stackBgLabel, 1, 0); - zoneBgGrid->addWidget(stackBgEdit, 1, 1); - zoneBgGrid->addWidget(stackBgClearButton, 1, 2); - zoneBgGrid->addWidget(stackBgButton, 1, 3); - zoneBgGrid->addWidget(&tableBgLabel, 2, 0); - zoneBgGrid->addWidget(tableBgEdit, 2, 1); - zoneBgGrid->addWidget(tableBgClearButton, 2, 2); - zoneBgGrid->addWidget(tableBgButton, 2, 3); - zoneBgGrid->addWidget(&playerAreaBgLabel, 3, 0); - zoneBgGrid->addWidget(playerAreaBgEdit, 3, 1); - zoneBgGrid->addWidget(playerAreaBgClearButton, 3, 2); - zoneBgGrid->addWidget(playerAreaBgButton, 3, 3); - zoneBgGrid->addWidget(&cardBackPicturePathLabel, 4, 0); - zoneBgGrid->addWidget(cardBackPicturePathEdit, 4, 1); - zoneBgGrid->addWidget(cardBackPicturePathClearButton, 4, 2); - zoneBgGrid->addWidget(cardBackPicturePathButton, 4, 3); + QStringList themeDirs = themeManager->getAvailableThemes().keys(); + for (int i = 0; i < themeDirs.size(); i++) { + themeBox.addItem(themeDirs[i]); + if (themeDirs[i] == themeName) + themeBox.setCurrentIndex(i); + } - zoneBgGroupBox = new QGroupBox; - zoneBgGroupBox->setLayout(zoneBgGrid); + connect(&themeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(themeBoxChanged(int))); + + QGridLayout *themeGrid = new QGridLayout; + themeGrid->addWidget(&themeLabel, 0, 0); + themeGrid->addWidget(&themeBox, 0, 1); + + themeGroupBox = new QGroupBox; + themeGroupBox->setLayout(themeGrid); displayCardNamesCheckBox.setChecked(settingsCache->getDisplayCardNames()); connect(&displayCardNamesCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setDisplayCardNames(int))); @@ -339,7 +296,7 @@ AppearanceSettingsPage::AppearanceSettingsPage() tableGroupBox->setLayout(tableGrid); QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addWidget(zoneBgGroupBox); + mainLayout->addWidget(themeGroupBox); mainLayout->addWidget(cardsGroupBox); mainLayout->addWidget(handGroupBox); mainLayout->addWidget(tableGroupBox); @@ -347,14 +304,17 @@ AppearanceSettingsPage::AppearanceSettingsPage() setLayout(mainLayout); } +void AppearanceSettingsPage::themeBoxChanged(int index) +{ + QStringList themeDirs = themeManager->getAvailableThemes().keys(); + if(index >= 0 && index < themeDirs.count()) + settingsCache->setThemeName(themeDirs.at(index)); +} + void AppearanceSettingsPage::retranslateUi() { - zoneBgGroupBox->setTitle(tr("Zone background pictures")); - handBgLabel.setText(tr("Hand background:")); - stackBgLabel.setText(tr("Stack background:")); - tableBgLabel.setText(tr("Table background:")); - playerAreaBgLabel.setText(tr("Player info background:")); - cardBackPicturePathLabel.setText(tr("Card back:")); + themeGroupBox->setTitle(tr("Theme settings")); + themeLabel.setText(tr("Current theme:")); cardsGroupBox->setTitle(tr("Card rendering")); displayCardNamesCheckBox.setText(tr("Display card names on cards having a picture")); @@ -369,86 +329,6 @@ void AppearanceSettingsPage::retranslateUi() minPlayersForMultiColumnLayoutLabel.setText(tr("Minimum player count for multi-column layout:")); } -void AppearanceSettingsPage::handBgClearButtonClicked() -{ - handBgEdit->setText(QString()); - settingsCache->setHandBgPath(QString()); -} - -void AppearanceSettingsPage::handBgButtonClicked() -{ - QString path = QFileDialog::getOpenFileName(this, tr("Choose path")); - if (path.isEmpty()) - return; - - handBgEdit->setText(path); - settingsCache->setHandBgPath(path); -} - -void AppearanceSettingsPage::stackBgClearButtonClicked() -{ - stackBgEdit->setText(QString()); - settingsCache->setStackBgPath(QString()); -} - -void AppearanceSettingsPage::stackBgButtonClicked() -{ - QString path = QFileDialog::getOpenFileName(this, tr("Choose path")); - if (path.isEmpty()) - return; - - stackBgEdit->setText(path); - settingsCache->setStackBgPath(path); -} - -void AppearanceSettingsPage::tableBgClearButtonClicked() -{ - tableBgEdit->setText(QString()); - settingsCache->setTableBgPath(QString()); -} - -void AppearanceSettingsPage::tableBgButtonClicked() -{ - QString path = QFileDialog::getOpenFileName(this, tr("Choose path")); - if (path.isEmpty()) - return; - - tableBgEdit->setText(path); - settingsCache->setTableBgPath(path); -} - -void AppearanceSettingsPage::playerAreaBgClearButtonClicked() -{ - playerAreaBgEdit->setText(QString()); - settingsCache->setPlayerBgPath(QString()); -} - -void AppearanceSettingsPage::playerAreaBgButtonClicked() -{ - QString path = QFileDialog::getOpenFileName(this, tr("Choose path")); - if (path.isEmpty()) - return; - - playerAreaBgEdit->setText(path); - settingsCache->setPlayerBgPath(path); -} - -void AppearanceSettingsPage::cardBackPicturePathClearButtonClicked() -{ - cardBackPicturePathEdit->setText(QString()); - settingsCache->setCardBackPicturePath(QString()); -} - -void AppearanceSettingsPage::cardBackPicturePathButtonClicked() -{ - QString path = QFileDialog::getOpenFileName(this, tr("Choose path")); - if (path.isEmpty()) - return; - - cardBackPicturePathEdit->setText(path); - settingsCache->setCardBackPicturePath(path); -} - UserInterfaceSettingsPage::UserInterfaceSettingsPage() { notificationsEnabledCheckBox.setChecked(settingsCache->getNotificationsEnabled()); @@ -588,10 +468,10 @@ MessagesSettingsPage::MessagesSettingsPage() messageList->addItem(settings.value(QString("msg%1").arg(i)).toString()); aAdd = new QAction(this); - aAdd->setIcon(QIcon(":/resources/increment.svg")); + aAdd->setIcon(QIcon("theme:increment.svg")); connect(aAdd, SIGNAL(triggered()), this, SLOT(actAdd())); aRemove = new QAction(this); - aRemove->setIcon(QIcon(":/resources/decrement.svg")); + aRemove->setIcon(QIcon("theme:decrement.svg")); connect(aRemove, SIGNAL(triggered()), this, SLOT(actRemove())); QToolBar *messageToolBar = new QToolBar; @@ -679,7 +559,7 @@ void MessagesSettingsPage::retranslateUi() SoundSettingsPage::SoundSettingsPage() { - QIcon deleteIcon(":/resources/icon_delete.svg"); + QIcon deleteIcon("theme:icon_delete.svg"); soundEnabledCheckBox.setChecked(settingsCache->getSoundEnabled()); connect(&soundEnabledCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setSoundEnabled(int))); @@ -815,32 +695,32 @@ void DlgSettings::createIcons() generalButton = new QListWidgetItem(contentsWidget); generalButton->setTextAlignment(Qt::AlignHCenter); generalButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - generalButton->setIcon(QIcon(":/resources/icon_config_general.svg")); + generalButton->setIcon(QIcon("theme:icon_config_general.svg")); appearanceButton = new QListWidgetItem(contentsWidget); appearanceButton->setTextAlignment(Qt::AlignHCenter); appearanceButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - appearanceButton->setIcon(QIcon(":/resources/icon_config_appearance.svg")); + appearanceButton->setIcon(QIcon("theme:icon_config_appearance.svg")); userInterfaceButton = new QListWidgetItem(contentsWidget); userInterfaceButton->setTextAlignment(Qt::AlignHCenter); userInterfaceButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - userInterfaceButton->setIcon(QIcon(":/resources/icon_config_interface.svg")); + userInterfaceButton->setIcon(QIcon("theme:icon_config_interface.svg")); deckEditorButton = new QListWidgetItem(contentsWidget); deckEditorButton->setTextAlignment(Qt::AlignHCenter); deckEditorButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - deckEditorButton->setIcon(QIcon(":/resources/icon_config_deckeditor.svg")); + deckEditorButton->setIcon(QIcon("theme:icon_config_deckeditor.svg")); messagesButton = new QListWidgetItem(contentsWidget); messagesButton->setTextAlignment(Qt::AlignHCenter); messagesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - messagesButton->setIcon(QIcon(":/resources/icon_config_messages.svg")); + messagesButton->setIcon(QIcon("theme:icon_config_messages.svg")); soundButton = new QListWidgetItem(contentsWidget); soundButton->setTextAlignment(Qt::AlignHCenter); soundButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - soundButton->setIcon(QIcon(":/resources/icon_config_sound.svg")); + soundButton->setIcon(QIcon("theme:icon_config_sound.svg")); connect(contentsWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(changePage(QListWidgetItem *, QListWidgetItem *))); } diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index c949ed6d..c4222066 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -71,40 +71,17 @@ private: class AppearanceSettingsPage : public AbstractSettingsPage { Q_OBJECT private slots: - void handBgClearButtonClicked(); - void handBgButtonClicked(); - void stackBgClearButtonClicked(); - void stackBgButtonClicked(); - void tableBgClearButtonClicked(); - void tableBgButtonClicked(); - void playerAreaBgClearButtonClicked(); - void playerAreaBgButtonClicked(); - void cardBackPicturePathClearButtonClicked(); - void cardBackPicturePathButtonClicked(); -signals: - void handBgChanged(const QString &path); - void stackBgChanged(const QString &path); - void tableBgChanged(const QString &path); - void playerAreaBgChanged(const QString &path); - void cardBackPicturePathChanged(const QString &path); + void themeBoxChanged(int index); private: - QLabel handBgLabel; - QLabel stackBgLabel; - QLabel tableBgLabel; - QLabel playerAreaBgLabel; - QLabel cardBackPicturePathLabel; + QLabel themeLabel; + QComboBox themeBox; QLabel minPlayersForMultiColumnLayoutLabel; - QLineEdit *handBgEdit; - QLineEdit *stackBgEdit; - QLineEdit *tableBgEdit; - QLineEdit *playerAreaBgEdit; - QLineEdit *cardBackPicturePathEdit; QCheckBox displayCardNamesCheckBox; QCheckBox cardScalingCheckBox; QCheckBox horizontalHandCheckBox; QCheckBox leftJustifiedHandCheckBox; QCheckBox invertVerticalCoordinateCheckBox; - QGroupBox *zoneBgGroupBox; + QGroupBox *themeGroupBox; QGroupBox *cardsGroupBox; QGroupBox *handGroupBox; QGroupBox *tableGroupBox; diff --git a/cockatrice/src/gameselector.cpp b/cockatrice/src/gameselector.cpp index 43471165..a3454856 100644 --- a/cockatrice/src/gameselector.cpp +++ b/cockatrice/src/gameselector.cpp @@ -60,10 +60,10 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup gameListView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); #endif filterButton = new QPushButton; - filterButton->setIcon(QIcon(":/resources/icon_search_black.svg")); + filterButton->setIcon(QIcon("theme:icon_search_black.svg")); connect(filterButton, SIGNAL(clicked()), this, SLOT(actSetFilter())); clearFilterButton = new QPushButton; - clearFilterButton->setIcon(QIcon(":/resources/icon_clearsearch.svg")); + clearFilterButton->setIcon(QIcon("theme:icon_clearsearch.svg")); clearFilterButton->setEnabled(true); connect(clearFilterButton, SIGNAL(clicked()), this, SLOT(actClearFilter())); diff --git a/cockatrice/src/handcounter.cpp b/cockatrice/src/handcounter.cpp index 4aa33d69..3b7ba1d9 100644 --- a/cockatrice/src/handcounter.cpp +++ b/cockatrice/src/handcounter.cpp @@ -36,7 +36,7 @@ void HandCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op #else if (!QPixmapCache::find("handCounter" + QString::number(translatedSize.width()), cachedPixmap)) { #endif - QSvgRenderer svg(QString(":/resources/hand.svg")); + QSvgRenderer svg(QString("theme:hand.svg")); cachedPixmap = QPixmap(translatedSize); cachedPixmap.fill(Qt::transparent); QPainter painter(&cachedPixmap); diff --git a/cockatrice/src/handzone.cpp b/cockatrice/src/handzone.cpp index 80e1194c..0db67f13 100644 --- a/cockatrice/src/handzone.cpp +++ b/cockatrice/src/handzone.cpp @@ -1,6 +1,7 @@ #include #include "handzone.h" #include "settingscache.h" +#include "thememanager.h" #include "player.h" #include "carddragitem.h" #include "carditem.h" @@ -10,16 +11,13 @@ HandZone::HandZone(Player *_p, bool _contentsKnown, int _zoneHeight, QGraphicsItem *parent) : SelectZone(_p, "hand", false, false, _contentsKnown, parent), zoneHeight(_zoneHeight) { - connect(settingsCache, SIGNAL(handBgPathChanged()), this, SLOT(updateBgPixmap())); - updateBgPixmap(); + connect(themeManager, SIGNAL(themeChanged()), this, SLOT(updateBg())); + updateBg(); setCacheMode(DeviceCoordinateCache); } -void HandZone::updateBgPixmap() +void HandZone::updateBg() { - QString bgPath = settingsCache->getHandBgPath(); - if (!bgPath.isEmpty()) - bgPixmap.load(bgPath); update(); } @@ -77,10 +75,7 @@ QRectF HandZone::boundingRect() const void HandZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - if (bgPixmap.isNull()) - painter->fillRect(boundingRect(), QColor(30, 30, 30)); - else - painter->fillRect(boundingRect(), QBrush(bgPixmap)); + painter->fillRect(boundingRect(), themeManager->getHandBgBrush()); } void HandZone::reorganizeCards() diff --git a/cockatrice/src/handzone.h b/cockatrice/src/handzone.h index 66245c50..47fb5075 100644 --- a/cockatrice/src/handzone.h +++ b/cockatrice/src/handzone.h @@ -7,9 +7,8 @@ class HandZone : public SelectZone { Q_OBJECT private: qreal width, zoneHeight; - QPixmap bgPixmap; private slots: - void updateBgPixmap(); + void updateBg(); public slots: void updateOrientation(); public: diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index dc0473ab..f7d941c9 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -38,6 +38,7 @@ #include "dlg_settings.h" #include "carddatabase.h" #include "settingscache.h" +#include "thememanager.h" #include "pixmapgenerator.h" #include "rng_sfmt.h" #include "soundengine.h" @@ -50,7 +51,7 @@ SettingsCache *settingsCache; RNG_Abstract *rng; SoundEngine *soundEngine; QSystemTrayIcon *trayIcon; - +ThemeManager *themeManager; const QString translationPrefix = "cockatrice"; #ifdef TRANSLATION_PATH @@ -132,6 +133,7 @@ int main(int argc, char *argv[]) rng = new RNG_SFMT; settingsCache = new SettingsCache; + themeManager = new ThemeManager; db = new CardDatabase; qtTranslator = new QTranslator; @@ -195,13 +197,14 @@ int main(int argc, char *argv[]) if (settingsValid()) { qDebug("main(): starting main program"); + soundEngine = new SoundEngine; qDebug("main(): SoundEngine constructor finished"); MainWindow ui; qDebug("main(): MainWindow constructor finished"); - QIcon icon(":/resources/appicon.svg"); + QIcon icon("theme:appicon.svg"); ui.setWindowIcon(icon); ui.show(); diff --git a/cockatrice/src/pixmapgenerator.cpp b/cockatrice/src/pixmapgenerator.cpp index 0ff25f57..8bb64ed9 100644 --- a/cockatrice/src/pixmapgenerator.cpp +++ b/cockatrice/src/pixmapgenerator.cpp @@ -16,7 +16,7 @@ QPixmap PhasePixmapGenerator::generatePixmap(int height, QString name) if (pmCache.contains(key)) return pmCache.value(key); - QSvgRenderer svg(QString(":/resources/phases/icon_phase_" + name + ".svg")); + QSvgRenderer svg(QString("theme:phases/icon_phase_" + name + ".svg")); QPixmap pixmap(height, height); pixmap.fill(Qt::transparent); @@ -36,13 +36,13 @@ QPixmap CounterPixmapGenerator::generatePixmap(int height, QString name, bool hi if (pmCache.contains(key)) return pmCache.value(key); - QSvgRenderer svg(QString(":/resources/counters/" + name + ".svg")); + QSvgRenderer svg(QString("theme:counters/" + name + ".svg")); if (!svg.isValid()) { name = "general"; if (highlight) name.append("_highlight"); - svg.load(QString(":/resources/counters/" + name + ".svg")); + svg.load(QString("theme:counters/" + name + ".svg")); } int width = (int) round(height * (double) svg.defaultSize().width() / (double) svg.defaultSize().height()); @@ -98,7 +98,7 @@ QPixmap GenderPixmapGenerator::generatePixmap(int height, int _gender) default: genderStr = "unknown"; }; - QSvgRenderer svg(QString(":/resources/genders/" + genderStr + ".svg")); + QSvgRenderer svg(QString("theme:genders/" + genderStr + ".svg")); int width = (int) round(height * (double) svg.defaultSize().width() / (double) svg.defaultSize().height()); QPixmap pixmap(width, height); pixmap.fill(Qt::transparent); @@ -119,7 +119,7 @@ QPixmap CountryPixmapGenerator::generatePixmap(int height, const QString &countr if (pmCache.contains(key)) return pmCache.value(key); - QSvgRenderer svg(QString(":/resources/countries/" + countryCode + ".svg")); + QSvgRenderer svg(QString("theme:countries/" + countryCode + ".svg")); int width = (int) round(height * (double) svg.defaultSize().width() / (double) svg.defaultSize().height()); QPixmap pixmap(width, height); pixmap.fill(Qt::transparent); @@ -154,7 +154,7 @@ QPixmap UserLevelPixmapGenerator::generatePixmap(int height, UserLevelFlags user if (isBuddy) levelString.append("_buddy"); - QSvgRenderer svg(QString(":/resources/userlevels/" + levelString + ".svg")); + QSvgRenderer svg(QString("theme:userlevels/" + levelString + ".svg")); int width = (int) round(height * (double) svg.defaultSize().width() / (double) svg.defaultSize().height()); QPixmap pixmap(width, height); pixmap.fill(Qt::transparent); @@ -175,7 +175,7 @@ QPixmap LockPixmapGenerator::generatePixmap(int height) if (pmCache.contains(key)) return pmCache.value(key); - QSvgRenderer svg(QString(":/resources/lock.svg")); + QSvgRenderer svg(QString("theme:lock.svg")); int width = (int) round(height * (double) svg.defaultSize().width() / (double) svg.defaultSize().height()); QPixmap pixmap(width, height); pixmap.fill(Qt::transparent); diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 30c0e6a5..667fbab9 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -15,6 +15,7 @@ #include "tab_game.h" #include "gamescene.h" #include "settingscache.h" +#include "thememanager.h" #include "dlg_create_token.h" #include "carddatabase.h" #include "color.h" @@ -69,25 +70,18 @@ PlayerArea::PlayerArea(QGraphicsItem *parentItem) : QObject(), QGraphicsItem(parentItem) { setCacheMode(DeviceCoordinateCache); - connect(settingsCache, SIGNAL(playerBgPathChanged()), this, SLOT(updateBgPixmap())); - updateBgPixmap(); + connect(themeManager, SIGNAL(themeChanged()), this, SLOT(updateBg())); + updateBg(); } -void PlayerArea::updateBgPixmap() +void PlayerArea::updateBg() { - QString bgPath = settingsCache->getPlayerBgPath(); - if (bgPath.isEmpty()) - bgPixmapBrush = QBrush(QColor(200, 200, 200)); - else { - qDebug() << "loading" << bgPath; - bgPixmapBrush = QBrush(QPixmap(bgPath)); - } update(); } void PlayerArea::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - painter->fillRect(bRect, bgPixmapBrush); + painter->fillRect(bRect, themeManager->getPlayerBgBrush()); } void PlayerArea::setSize(qreal width, qreal height) diff --git a/cockatrice/src/player.h b/cockatrice/src/player.h index 1fed2b10..0925d039 100644 --- a/cockatrice/src/player.h +++ b/cockatrice/src/player.h @@ -60,10 +60,9 @@ class PlayerArea : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES(QGraphicsItem) private: - QBrush bgPixmapBrush; QRectF bRect; private slots: - void updateBgPixmap(); + void updateBg(); public: enum { Type = typeOther }; int type() const { return Type; } diff --git a/cockatrice/src/playerlistwidget.cpp b/cockatrice/src/playerlistwidget.cpp index e18bab98..354ed669 100644 --- a/cockatrice/src/playerlistwidget.cpp +++ b/cockatrice/src/playerlistwidget.cpp @@ -51,12 +51,12 @@ bool PlayerListTWI::operator<(const QTreeWidgetItem &other) const PlayerListWidget::PlayerListWidget(TabSupervisor *_tabSupervisor, AbstractClient *_client, TabGame *_game, QWidget *parent) : QTreeWidget(parent), tabSupervisor(_tabSupervisor), client(_client), game(_game), gameStarted(false) { - readyIcon = QIcon(":/resources/icon_ready_start.svg"); - notReadyIcon = QIcon(":/resources/icon_not_ready_start.svg"); - concededIcon = QIcon(":/resources/icon_conceded.svg"); - playerIcon = QIcon(":/resources/icon_player.svg"); - spectatorIcon = QIcon(":/resources/icon_spectator.svg"); - lockIcon = QIcon(":/resources/lock.svg"); + readyIcon = QIcon("theme:icon_ready_start.svg"); + notReadyIcon = QIcon("theme:icon_not_ready_start.svg"); + concededIcon = QIcon("theme:icon_conceded.svg"); + playerIcon = QIcon("theme:icon_player.svg"); + spectatorIcon = QIcon("theme:icon_spectator.svg"); + lockIcon = QIcon("theme:lock.svg"); if (tabSupervisor) { itemDelegate = new PlayerListItemDelegate(this); diff --git a/cockatrice/src/remotereplaylist_treewidget.cpp b/cockatrice/src/remotereplaylist_treewidget.cpp index 5357596f..47da5f91 100644 --- a/cockatrice/src/remotereplaylist_treewidget.cpp +++ b/cockatrice/src/remotereplaylist_treewidget.cpp @@ -35,7 +35,7 @@ RemoteReplayList_TreeModel::RemoteReplayList_TreeModel(AbstractClient *_client, QFileIconProvider fip; dirIcon = fip.icon(QFileIconProvider::Folder); fileIcon = fip.icon(QFileIconProvider::File); - lockIcon = QIcon(":/resources/lock.svg"); + lockIcon = QIcon("theme:lock.svg"); refreshTree(); } diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 16ef669b..11d4590c 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -13,11 +13,7 @@ SettingsCache::SettingsCache() cardDatabasePath = settings->value("paths/carddatabase").toString(); tokenDatabasePath = settings->value("paths/tokendatabase").toString(); - handBgPath = settings->value("zonebg/hand").toString(); - stackBgPath = settings->value("zonebg/stack").toString(); - tableBgPath = settings->value("zonebg/table").toString(); - playerBgPath = settings->value("zonebg/playerarea").toString(); - cardBackPicturePath = settings->value("paths/cardbackpicture").toString(); + themeName = settings->value("theme/name").toString(); // we only want to reset the cache once, then its up to the user bool updateCache = settings->value("revert/pixmapCacheSize", false).toBool(); @@ -155,39 +151,11 @@ void SettingsCache::setTokenDatabasePath(const QString &_tokenDatabasePath) emit tokenDatabasePathChanged(); } -void SettingsCache::setHandBgPath(const QString &_handBgPath) +void SettingsCache::setThemeName(const QString &_themeName) { - handBgPath = _handBgPath; - settings->setValue("zonebg/hand", handBgPath); - emit handBgPathChanged(); -} - -void SettingsCache::setStackBgPath(const QString &_stackBgPath) -{ - stackBgPath = _stackBgPath; - settings->setValue("zonebg/stack", stackBgPath); - emit stackBgPathChanged(); -} - -void SettingsCache::setTableBgPath(const QString &_tableBgPath) -{ - tableBgPath = _tableBgPath; - settings->setValue("zonebg/table", tableBgPath); - emit tableBgPathChanged(); -} - -void SettingsCache::setPlayerBgPath(const QString &_playerBgPath) -{ - playerBgPath = _playerBgPath; - settings->setValue("zonebg/playerarea", playerBgPath); - emit playerBgPathChanged(); -} - -void SettingsCache::setCardBackPicturePath(const QString &_cardBackPicturePath) -{ - cardBackPicturePath = _cardBackPicturePath; - settings->setValue("paths/cardbackpicture", cardBackPicturePath); - emit cardBackPicturePathChanged(); + themeName = _themeName; + settings->setValue("theme/name", themeName); + emit themeChanged(); } void SettingsCache::setPicDownload(int _picDownload) diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 6e7dc76e..cfc81113 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -21,11 +21,7 @@ signals: void picsPathChanged(); void cardDatabasePathChanged(); void tokenDatabasePathChanged(); - void handBgPathChanged(); - void stackBgPathChanged(); - void tableBgPathChanged(); - void playerBgPathChanged(); - void cardBackPicturePathChanged(); + void themeChanged(); void picDownloadChanged(); void picDownloadHqChanged(); void displayCardNamesChanged(); @@ -45,8 +41,7 @@ private: QByteArray mainWindowGeometry; QString lang; - QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath; - QString handBgPath, stackBgPath, tableBgPath, playerBgPath, cardBackPicturePath; + QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath, themeName; bool picDownload; bool picDownloadHq; bool notificationsEnabled; @@ -91,11 +86,7 @@ public: QString getPicsPath() const { return picsPath; } QString getCardDatabasePath() const { return cardDatabasePath; } QString getTokenDatabasePath() const { return tokenDatabasePath; } - QString getHandBgPath() const { return handBgPath; } - QString getStackBgPath() const { return stackBgPath; } - QString getTableBgPath() const { return tableBgPath; } - QString getPlayerBgPath() const { return playerBgPath; } - QString getCardBackPicturePath() const { return cardBackPicturePath; } + QString getThemeName() const { return themeName; } QString getChatMentionColor() const { return chatMentionColor; } bool getPicDownload() const { return picDownload; } bool getPicDownloadHq() const { return picDownloadHq; } @@ -146,11 +137,7 @@ public slots: void setPicsPath(const QString &_picsPath); void setCardDatabasePath(const QString &_cardDatabasePath); void setTokenDatabasePath(const QString &_tokenDatabasePath); - void setHandBgPath(const QString &_handBgPath); - void setStackBgPath(const QString &_stackBgPath); - void setTableBgPath(const QString &_tableBgPath); - void setPlayerBgPath(const QString &_playerBgPath); - void setCardBackPicturePath(const QString &_cardBackPicturePath); + void setThemeName(const QString &_themeName); void setChatMentionColor(const QString &_chatMentionColor); void setPicDownload(int _picDownload); void setPicDownloadHq(int _picDownloadHq); diff --git a/cockatrice/src/stackzone.cpp b/cockatrice/src/stackzone.cpp index a60c4037..4b4b6b52 100644 --- a/cockatrice/src/stackzone.cpp +++ b/cockatrice/src/stackzone.cpp @@ -3,6 +3,7 @@ #include "arrowitem.h" #include "stackzone.h" #include "settingscache.h" +#include "thememanager.h" #include "player.h" #include "carddragitem.h" #include "carditem.h" @@ -12,16 +13,13 @@ StackZone::StackZone(Player *_p, int _zoneHeight, QGraphicsItem *parent) : SelectZone(_p, "stack", false, false, true, parent), zoneHeight(_zoneHeight) { - connect(settingsCache, SIGNAL(stackBgPathChanged()), this, SLOT(updateBgPixmap())); - updateBgPixmap(); + connect(themeManager, SIGNAL(themeChanged()), this, SLOT(updateBg())); + updateBg(); setCacheMode(DeviceCoordinateCache); } -void StackZone::updateBgPixmap() +void StackZone::updateBg() { - QString bgPath = settingsCache->getStackBgPath(); - if (!bgPath.isEmpty()) - bgPixmap.load(bgPath); update(); } @@ -48,10 +46,7 @@ QRectF StackZone::boundingRect() const void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - if (bgPixmap.isNull()) - painter->fillRect(boundingRect(), QColor(113, 43, 43)); - else - painter->fillRect(boundingRect(), QBrush(bgPixmap)); + painter->fillRect(boundingRect(), themeManager->getStackBgBrush()); } void StackZone::handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &/*dropPoint*/) diff --git a/cockatrice/src/stackzone.h b/cockatrice/src/stackzone.h index 948d3bcb..05fb2d24 100644 --- a/cockatrice/src/stackzone.h +++ b/cockatrice/src/stackzone.h @@ -7,9 +7,8 @@ class StackZone : public SelectZone { Q_OBJECT private: qreal zoneHeight; - QPixmap bgPixmap; private slots: - void updateBgPixmap(); + void updateBg(); public: StackZone(Player *_p, int _zoneHeight, QGraphicsItem *parent = 0); void handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index df84220e..e6ddc382 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -51,16 +51,16 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) : Tab(_tabSupervisor, parent), modified(false) { aClearFilterAll = new QAction(QString(), this); - aClearFilterAll->setIcon(QIcon(":/resources/icon_clearsearch.svg")); + aClearFilterAll->setIcon(QIcon("theme:icon_clearsearch.svg")); connect(aClearFilterAll, SIGNAL(triggered()), this, SLOT(actClearFilterAll())); aClearFilterOne = new QAction(QString(), this); - aClearFilterOne->setIcon(QIcon(":/resources/decrement.svg")); + aClearFilterOne->setIcon(QIcon("theme:decrement.svg")); connect(aClearFilterOne, SIGNAL(triggered()), this, SLOT(actClearFilterOne())); searchEdit = new SearchLineEdit; #if QT_VERSION >= 0x050300 - searchEdit->addAction(QIcon(":/resources/icon_search_black.svg"), QLineEdit::LeadingPosition); + searchEdit->addAction(QIcon("theme:icon_search_black.svg"), QLineEdit::LeadingPosition); #endif searchEdit->setObjectName("searchEdit"); @@ -193,7 +193,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) /* Update price aUpdatePrices = new QAction(QString(), this); - aUpdatePrices->setIcon(QIcon(":/resources/icon_update.png")); + aUpdatePrices->setIcon(QIcon("theme:icon_update.png")); connect(aUpdatePrices, SIGNAL(triggered()), this, SLOT(actUpdatePrices())); if (!settingsCache->getPriceTagFeature()) aUpdatePrices->setVisible(false); @@ -282,19 +282,19 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) addTabMenu(dbMenu); aAddCard = new QAction(QString(), this); - aAddCard->setIcon(QIcon(":/resources/arrow_right_green.svg")); + aAddCard->setIcon(QIcon("theme:arrow_right_green.svg")); connect(aAddCard, SIGNAL(triggered()), this, SLOT(actAddCard())); aAddCardToSideboard = new QAction(QString(), this); - aAddCardToSideboard->setIcon(QIcon(":/resources/add_to_sideboard.svg")); + aAddCardToSideboard->setIcon(QIcon("theme:add_to_sideboard.svg")); connect(aAddCardToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard())); aRemoveCard = new QAction(QString(), this); - aRemoveCard->setIcon(QIcon(":/resources/remove_row.svg")); + aRemoveCard->setIcon(QIcon("theme:remove_row.svg")); connect(aRemoveCard, SIGNAL(triggered()), this, SLOT(actRemoveCard())); aIncrement = new QAction(QString(), this); - aIncrement->setIcon(QIcon(":/resources/increment.svg")); + aIncrement->setIcon(QIcon("theme:increment.svg")); connect(aIncrement, SIGNAL(triggered()), this, SLOT(actIncrement())); aDecrement = new QAction(QString(), this); - aDecrement->setIcon(QIcon(":/resources/decrement.svg")); + aDecrement->setIcon(QIcon("theme:decrement.svg")); connect(aDecrement, SIGNAL(triggered()), this, SLOT(actDecrement())); deckEditToolBar->addAction(aAddCard); diff --git a/cockatrice/src/tab_deck_storage.cpp b/cockatrice/src/tab_deck_storage.cpp index 9898c9ee..c4e23eaf 100644 --- a/cockatrice/src/tab_deck_storage.cpp +++ b/cockatrice/src/tab_deck_storage.cpp @@ -80,25 +80,25 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_c hbox->addWidget(rightGroupBox); aOpenLocalDeck = new QAction(this); - aOpenLocalDeck->setIcon(QIcon(":/resources/pencil.svg")); + aOpenLocalDeck->setIcon(QIcon("theme:pencil.svg")); connect(aOpenLocalDeck, SIGNAL(triggered()), this, SLOT(actOpenLocalDeck())); aUpload = new QAction(this); - aUpload->setIcon(QIcon(":/resources/arrow_right_green.svg")); + aUpload->setIcon(QIcon("theme:arrow_right_green.svg")); connect(aUpload, SIGNAL(triggered()), this, SLOT(actUpload())); aDeleteLocalDeck = new QAction(this); - aDeleteLocalDeck->setIcon(QIcon(":/resources/remove_row.svg")); + aDeleteLocalDeck->setIcon(QIcon("theme:remove_row.svg")); connect(aDeleteLocalDeck, SIGNAL(triggered()), this, SLOT(actDeleteLocalDeck())); aOpenRemoteDeck = new QAction(this); - aOpenRemoteDeck->setIcon(QIcon(":/resources/pencil.svg")); + aOpenRemoteDeck->setIcon(QIcon("theme:pencil.svg")); connect(aOpenRemoteDeck, SIGNAL(triggered()), this, SLOT(actOpenRemoteDeck())); aDownload = new QAction(this); - aDownload->setIcon(QIcon(":/resources/arrow_left_green.svg")); + aDownload->setIcon(QIcon("theme:arrow_left_green.svg")); connect(aDownload, SIGNAL(triggered()), this, SLOT(actDownload())); aNewFolder = new QAction(this); aNewFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder)); connect(aNewFolder, SIGNAL(triggered()), this, SLOT(actNewFolder())); aDeleteRemoteDeck = new QAction(this); - aDeleteRemoteDeck->setIcon(QIcon(":/resources/remove_row.svg")); + aDeleteRemoteDeck->setIcon(QIcon("theme:remove_row.svg")); connect(aDeleteRemoteDeck, SIGNAL(triggered()), this, SLOT(actDeleteRemoteDeck())); leftToolBar->addAction(aOpenLocalDeck); diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 0d91d7ea..4c52066e 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -305,17 +305,17 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay) replayStartButton = new QToolButton; replayStartButton->setIconSize(QSize(32, 32)); - replayStartButton->setIcon(QIcon(":/resources/replay_start.svg")); + replayStartButton->setIcon(QIcon("theme:replay_start.svg")); connect(replayStartButton, SIGNAL(clicked()), this, SLOT(replayStartButtonClicked())); replayPauseButton = new QToolButton; replayPauseButton->setIconSize(QSize(32, 32)); replayPauseButton->setEnabled(false); - replayPauseButton->setIcon(QIcon(":/resources/replay_pause.svg")); + replayPauseButton->setIcon(QIcon("theme:replay_pause.svg")); connect(replayPauseButton, SIGNAL(clicked()), this, SLOT(replayPauseButtonClicked())); replayFastForwardButton = new QToolButton; replayFastForwardButton->setIconSize(QSize(32, 32)); replayFastForwardButton->setEnabled(false); - replayFastForwardButton->setIcon(QIcon(":/resources/replay_fastforward.svg")); + replayFastForwardButton->setIcon(QIcon("theme:replay_fastforward.svg")); replayFastForwardButton->setCheckable(true); connect(replayFastForwardButton, SIGNAL(toggled(bool)), this, SLOT(replayFastForwardButtonToggled(bool))); diff --git a/cockatrice/src/tab_replays.cpp b/cockatrice/src/tab_replays.cpp index 9f1b2a12..8e42bbd3 100644 --- a/cockatrice/src/tab_replays.cpp +++ b/cockatrice/src/tab_replays.cpp @@ -78,22 +78,22 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client) hbox->addWidget(rightGroupBox); aOpenLocalReplay = new QAction(this); - aOpenLocalReplay->setIcon(QIcon(":/resources/icon_view.svg")); + aOpenLocalReplay->setIcon(QIcon("theme:icon_view.svg")); connect(aOpenLocalReplay, SIGNAL(triggered()), this, SLOT(actOpenLocalReplay())); aDeleteLocalReplay = new QAction(this); - aDeleteLocalReplay->setIcon(QIcon(":/resources/remove_row.svg")); + aDeleteLocalReplay->setIcon(QIcon("theme:remove_row.svg")); connect(aDeleteLocalReplay, SIGNAL(triggered()), this, SLOT(actDeleteLocalReplay())); aOpenRemoteReplay = new QAction(this); - aOpenRemoteReplay->setIcon(QIcon(":/resources/icon_view.svg")); + aOpenRemoteReplay->setIcon(QIcon("theme:icon_view.svg")); connect(aOpenRemoteReplay, SIGNAL(triggered()), this, SLOT(actOpenRemoteReplay())); aDownload = new QAction(this); - aDownload->setIcon(QIcon(":/resources/arrow_left_green.svg")); + aDownload->setIcon(QIcon("theme:arrow_left_green.svg")); connect(aDownload, SIGNAL(triggered()), this, SLOT(actDownload())); aKeep = new QAction(this); - aKeep->setIcon(QIcon(":/resources/lock.svg")); + aKeep->setIcon(QIcon("theme:lock.svg")); connect(aKeep, SIGNAL(triggered()), this, SLOT(actKeepRemoteReplay())); aDeleteRemoteReplay = new QAction(this); - aDeleteRemoteReplay->setIcon(QIcon(":/resources/remove_row.svg")); + aDeleteRemoteReplay->setIcon(QIcon("theme:remove_row.svg")); connect(aDeleteRemoteReplay, SIGNAL(triggered()), this, SLOT(actDeleteRemoteReplay())); leftToolBar->addAction(aOpenLocalReplay); diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index 545901ce..ea5d7613 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -68,7 +68,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI connect(aOpenChatSettings, SIGNAL(triggered()), this, SLOT(actOpenChatSettings())); QToolButton *chatSettingsButton = new QToolButton; - chatSettingsButton->setIcon(QIcon(":/resources/icon_settings.svg")); + chatSettingsButton->setIcon(QIcon("theme:icon_settings.svg")); chatSettingsButton->setMenu(chatSettingsMenu); chatSettingsButton->setPopupMode(QToolButton::InstantPopup); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index 867f84b3..1f2f0524 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -79,7 +79,7 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/) TabSupervisor::TabSupervisor(AbstractClient *_client, QWidget *parent) : QTabWidget(parent), userInfo(0), client(_client), tabServer(0), tabUserLists(0), tabDeckStorage(0), tabReplays(0), tabAdmin(0) { - tabChangedIcon = new QIcon(":/resources/icon_tab_changed.svg"); + tabChangedIcon = new QIcon("theme:icon_tab_changed.svg"); setElideMode(Qt::ElideRight); setMovable(true); setIconSize(QSize(15, 15)); diff --git a/cockatrice/src/tablezone.cpp b/cockatrice/src/tablezone.cpp index de53507f..0d486870 100644 --- a/cockatrice/src/tablezone.cpp +++ b/cockatrice/src/tablezone.cpp @@ -8,6 +8,7 @@ #include "tablezone.h" #include "player.h" #include "settingscache.h" +#include "thememanager.h" #include "arrowitem.h" #include "carddragitem.h" #include "carddatabase.h" @@ -16,20 +17,18 @@ #include "pb/command_move_card.pb.h" #include "pb/command_set_card_attr.pb.h" - const QColor TableZone::BACKGROUND_COLOR = QColor(100, 100, 100); const QColor TableZone::FADE_MASK = QColor(0, 0, 0, 80); const QColor TableZone::GRADIENT_COLOR = QColor(255, 255, 255, 150); const QColor TableZone::GRADIENT_COLORLESS = QColor(255, 255, 255, 0); - TableZone::TableZone(Player *_p, QGraphicsItem *parent) : SelectZone(_p, "table", true, false, true, parent), active(false) { - connect(settingsCache, SIGNAL(tableBgPathChanged()), this, SLOT(updateBgPixmap())); + connect(themeManager, SIGNAL(themeChanged()), this, SLOT(updateBg())); connect(settingsCache, SIGNAL(invertVerticalCoordinateChanged()), this, SLOT(reorganizeCards())); - updateBgPixmap(); + updateBg(); height = 2 * BOX_LINE_WIDTH + 3 * (CARD_HEIGHT + 20) + 2 * PADDING_Y; width = MIN_WIDTH + 2 * MARGIN_X + 2 * BOX_LINE_WIDTH; @@ -44,11 +43,8 @@ TableZone::TableZone(Player *_p, QGraphicsItem *parent) } -void TableZone::updateBgPixmap() +void TableZone::updateBg() { - QString bgPath = settingsCache->getTableBgPath(); - if (!bgPath.isEmpty()) - backgroundPixelMap.load(bgPath); update(); } @@ -67,11 +63,7 @@ bool TableZone::isInverted() const void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - // if no custom background is provided then use the default color - if (backgroundPixelMap.isNull()) - painter->fillRect(boundingRect(), BACKGROUND_COLOR); - else - painter->fillRect(boundingRect(), QBrush(backgroundPixelMap)); + painter->fillRect(boundingRect(), themeManager->getTableBgBrush()); if (active) { paintZoneOutline(painter); diff --git a/cockatrice/src/tablezone.h b/cockatrice/src/tablezone.h index 9b362efa..6eeae7f1 100644 --- a/cockatrice/src/tablezone.h +++ b/cockatrice/src/tablezone.h @@ -27,7 +27,7 @@ private: static const int MIN_WIDTH = 15 * CARD_WIDTH / 2; /* - Default background color, inactive mask and boarder gradient + Default inactive mask and border gradient */ static const QColor BACKGROUND_COLOR; static const QColor FADE_MASK; @@ -42,11 +42,6 @@ private: int height; int currentMinimumWidth; - /* - Holds any custom background image for the TableZone - */ - QPixmap backgroundPixelMap; - /* If this TableZone is currently active */ @@ -58,7 +53,7 @@ private slots: /** Loads in any found custom background and updates */ - void updateBgPixmap(); + void updateBg(); public slots: /** diff --git a/cockatrice/src/thememanager.cpp b/cockatrice/src/thememanager.cpp new file mode 100644 index 00000000..0e2bb996 --- /dev/null +++ b/cockatrice/src/thememanager.cpp @@ -0,0 +1,144 @@ +#include "thememanager.h" +#include "settingscache.h" +#include +#include +#include +#include +#if QT_VERSION < 0x050000 + #include +#else + #include +#endif + +#define DEFAULT_THEME_NAME "Default" +#define STYLE_CSS_NAME "style.css" +#define HANDZONE_BG_NAME "handzone" +#define PLAYERZONE_BG_NAME "playerzone" +#define STACKZONE_BG_NAME "stackzone" +#define TABLEZONE_BG_NAME "tablezone" +#define CARD_BACK_NAME "cardback" + +ThemeManager::ThemeManager(QObject *parent) + :QObject(parent) +{ + ensureThemeDirectoryExists(); + connect(settingsCache, SIGNAL(themeChanged()), this, SLOT(themeChangedSlot())); + themeChangedSlot(); +} + +void ThemeManager::ensureThemeDirectoryExists() +{ + if(settingsCache->getThemeName().isEmpty() || + !getAvailableThemes().contains(settingsCache->getThemeName())) + { + qDebug() << "Theme name not set, setting default value"; + settingsCache->setThemeName(DEFAULT_THEME_NAME); + } +} + +QStringMap & ThemeManager::getAvailableThemes() +{ + QDir dir; + availableThemes.clear(); + + // load themes from user profile dir + dir = +#if QT_VERSION < 0x050000 + QDesktopServices::storageLocation(QDesktopServices::DataLocation) + +#else + QStandardPaths::standardLocations(QStandardPaths::DataLocation).first() + +#endif + "/themes"; + + foreach(QString themeName, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) + { + if(!availableThemes.contains(themeName)) + availableThemes.insert(themeName, dir.absoluteFilePath(themeName)); + } + + // load themes from cockatrice system dir +#ifdef Q_OS_MAC + dir = qApp->applicationDirPath() + "/../Resources/themes"; +#elif defined(Q_OS_WIN) + dir = qApp->applicationDirPath() + "/themes"; +#else // linux + dir = qApp->applicationDirPath() + "/../share/cockatrice/themes"; +#endif + foreach(QString themeName, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) + { + if(!availableThemes.contains(themeName)) + availableThemes.insert(themeName, dir.absoluteFilePath(themeName)); + } + + return availableThemes; +} + +QBrush ThemeManager::loadBrush(QDir dir, QString fileName, QColor fallbackColor) +{ + QBrush brush; + QPixmap tmp; + QStringList exts; + exts << ".png" << ".jpg" << ".jpeg" << ".gif" << ".bmp"; + + brush.setColor(fallbackColor); + brush.setStyle(Qt::SolidPattern); + + foreach (const QString &ext, exts) { + if (dir.exists(fileName + ext)) { + tmp.load(dir.absoluteFilePath(fileName + ext)); + if(!tmp.isNull()) + brush.setTexture(tmp); + break; + } + } + + return brush; +} + +QPixmap ThemeManager::loadPixmap(QDir dir, QString fileName) +{ + QPixmap pix; + QStringList exts; + exts << ".png" << ".jpg" << ".jpeg" << ".gif" << ".bmp"; + + foreach (const QString &ext, exts) { + if (dir.exists(fileName + ext)) { + pix.load(dir.absoluteFilePath(fileName + ext)); + break; + } + } + + return pix; +} + +void ThemeManager::themeChangedSlot() +{ + QString themeName = settingsCache->getThemeName(); + qDebug() << "Theme changed:" << themeName; + + QDir dir = getAvailableThemes().value(themeName); + + // css + if(dir.exists(STYLE_CSS_NAME)) + + qApp->setStyleSheet("file:///" + dir.absoluteFilePath(STYLE_CSS_NAME)); + else + qApp->setStyleSheet(""); + + // card background + cardBackPixmap = loadPixmap(dir, CARD_BACK_NAME); + + // zones bg + dir.cd("zones"); + handBgBrush = loadBrush(dir, HANDZONE_BG_NAME, QColor(80, 100, 50)); + tableBgBrush = loadBrush(dir, TABLEZONE_BG_NAME, QColor(70, 50, 100)); + playerBgBrush = loadBrush(dir, PLAYERZONE_BG_NAME, QColor(200, 200, 200)); + stackBgBrush = loadBrush(dir, STACKZONE_BG_NAME, QColor(113, 43, 43)); + + // resources + QStringList resources; + resources << dir.absolutePath() << ":/resources"; + QDir::setSearchPaths("theme", resources); + + emit themeChanged(); +} \ No newline at end of file diff --git a/cockatrice/src/thememanager.h b/cockatrice/src/thememanager.h new file mode 100644 index 00000000..19cb19c5 --- /dev/null +++ b/cockatrice/src/thememanager.h @@ -0,0 +1,42 @@ +#ifndef THEMEMANAGER_H +#define THEMEMANAGER_H + +#include +#include +#include +#include +#include +#include + +typedef QMap QStringMap; + +class QApplication; + +class ThemeManager : public QObject { + Q_OBJECT +public: + ThemeManager(QObject *parent = 0); +private: + QBrush handBgBrush, stackBgBrush, tableBgBrush, playerBgBrush; + QPixmap cardBackPixmap; + QStringMap availableThemes; +protected: + void ensureThemeDirectoryExists(); + QBrush loadBrush(QDir dir, QString fileName, QColor fallbackColor); + QPixmap loadPixmap(QDir dir, QString fileName); +public: + QBrush &getHandBgBrush() { return handBgBrush; } + QBrush &getStackBgBrush() { return stackBgBrush; } + QBrush &getTableBgBrush() { return tableBgBrush; } + QBrush &getPlayerBgBrush() { return playerBgBrush; } + QPixmap &getCardBackPixmap() { return cardBackPixmap; } + QStringMap &getAvailableThemes(); +protected slots: + void themeChangedSlot(); +signals: + void themeChanged(); +}; + +extern ThemeManager * themeManager; + +#endif diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 3a0d4044..d0720d9a 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -561,7 +561,7 @@ void MainWindow::createTrayIcon() { trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayIconMenu); - trayIcon->setIcon(QIcon(":/resources/appicon.svg")); + trayIcon->setIcon(QIcon("theme:appicon.svg")); trayIcon->show(); connect(trayIcon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),this, diff --git a/cockatrice/src/window_sets.cpp b/cockatrice/src/window_sets.cpp index b2c9ce3c..e8cb852a 100644 --- a/cockatrice/src/window_sets.cpp +++ b/cockatrice/src/window_sets.cpp @@ -24,28 +24,28 @@ WndSets::WndSets(QWidget *parent) setsEditToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); aTop = new QAction(QString(), this); - aTop->setIcon(QIcon(":/resources/arrow_top_green.svg")); + aTop->setIcon(QIcon("theme:arrow_top_green.svg")); aTop->setToolTip(tr("Move selected set to the top")); aTop->setEnabled(false); connect(aTop, SIGNAL(triggered()), this, SLOT(actTop())); setsEditToolBar->addAction(aTop); aUp = new QAction(QString(), this); - aUp->setIcon(QIcon(":/resources/arrow_up_green.svg")); + aUp->setIcon(QIcon("theme:arrow_up_green.svg")); aUp->setToolTip(tr("Move selected set up")); aUp->setEnabled(false); connect(aUp, SIGNAL(triggered()), this, SLOT(actUp())); setsEditToolBar->addAction(aUp); aDown = new QAction(QString(), this); - aDown->setIcon(QIcon(":/resources/arrow_down_green.svg")); + aDown->setIcon(QIcon("theme:arrow_down_green.svg")); aDown->setToolTip(tr("Move selected set down")); aDown->setEnabled(false); connect(aDown, SIGNAL(triggered()), this, SLOT(actDown())); setsEditToolBar->addAction(aDown); aBottom = new QAction(QString(), this); - aBottom->setIcon(QIcon(":/resources/arrow_bottom_green.svg")); + aBottom->setIcon(QIcon("theme:arrow_bottom_green.svg")); aBottom->setToolTip(tr("Move selected set to the bottom")); aBottom->setEnabled(false); connect(aBottom, SIGNAL(triggered()), this, SLOT(actBottom())); diff --git a/cockatrice/src/zoneviewwidget.cpp b/cockatrice/src/zoneviewwidget.cpp index ccb36af1..fd27bae7 100644 --- a/cockatrice/src/zoneviewwidget.cpp +++ b/cockatrice/src/zoneviewwidget.cpp @@ -250,5 +250,5 @@ void ZoneViewWidget::zoneDeleted() void ZoneViewWidget::initStyleOption(QStyleOption *option) const { QStyleOptionTitleBar *titleBar = qstyleoption_cast(option); if (titleBar) - titleBar->icon = QIcon(":/resources/appicon.svg"); + titleBar->icon = QIcon("theme:appicon.svg"); } diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 5e554f38..413928ea 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -13,6 +13,7 @@ SET(oracle_SOURCES src/oracleimporter.cpp ../cockatrice/src/carddatabase.cpp ../cockatrice/src/settingscache.cpp + ../cockatrice/src/thememanager.cpp ../cockatrice/src/qt-json/json.cpp ) diff --git a/oracle/src/main.cpp b/oracle/src/main.cpp index 9b462dd8..c946aa4b 100644 --- a/oracle/src/main.cpp +++ b/oracle/src/main.cpp @@ -7,9 +7,11 @@ #include "main.h" #include "oraclewizard.h" #include "settingscache.h" +#include "thememanager.h" QTranslator *translator, *qtTranslator; SettingsCache *settingsCache; +ThemeManager *themeManager; const QString translationPrefix = "oracle"; #ifdef TRANSLATION_PATH @@ -51,6 +53,7 @@ int main(int argc, char *argv[]) } settingsCache = new SettingsCache; + themeManager = new ThemeManager; qtTranslator = new QTranslator; translator = new QTranslator; diff --git a/themes/CMakeLists.txt b/themes/CMakeLists.txt new file mode 100644 index 00000000..dc3b9b50 --- /dev/null +++ b/themes/CMakeLists.txt @@ -0,0 +1,22 @@ +# CMakeLists for themes directory +# +# add themes subfolders + +SET(defthemes + Default + Fabric + Leather + Plasma + VelvetMarble +) + +if(UNIX) + if(APPLE) + INSTALL(DIRECTORY ${defthemes} DESTINATION cockatrice.app/Contents/Resources/themes/) + else() + # Assume linux + INSTALL(DIRECTORY ${defthemes} DESTINATION share/cockatrice/themes/) + endif() +elseif(WIN32) + INSTALL(DIRECTORY ${defthemes} DESTINATION themes/) +endif() diff --git a/themes/Default/version.txt b/themes/Default/version.txt new file mode 100644 index 00000000..c2270834 --- /dev/null +++ b/themes/Default/version.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/themes/Fabric/version.txt b/themes/Fabric/version.txt new file mode 100644 index 00000000..c2270834 --- /dev/null +++ b/themes/Fabric/version.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/zonebg/fabric_green.png b/themes/Fabric/zones/handzone.png similarity index 100% rename from zonebg/fabric_green.png rename to themes/Fabric/zones/handzone.png diff --git a/zonebg/fabric_gray.png b/themes/Fabric/zones/playerzone.png similarity index 100% rename from zonebg/fabric_gray.png rename to themes/Fabric/zones/playerzone.png diff --git a/zonebg/fabric_red.png b/themes/Fabric/zones/stackzone.png similarity index 100% rename from zonebg/fabric_red.png rename to themes/Fabric/zones/stackzone.png diff --git a/zonebg/fabric_blue.png b/themes/Fabric/zones/tablezone.png similarity index 100% rename from zonebg/fabric_blue.png rename to themes/Fabric/zones/tablezone.png diff --git a/themes/Leather/version.txt b/themes/Leather/version.txt new file mode 100644 index 00000000..c2270834 --- /dev/null +++ b/themes/Leather/version.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/zonebg/leather_green.png b/themes/Leather/zones/handzone.png similarity index 100% rename from zonebg/leather_green.png rename to themes/Leather/zones/handzone.png diff --git a/zonebg/leather_gray.png b/themes/Leather/zones/playerzone.png similarity index 100% rename from zonebg/leather_gray.png rename to themes/Leather/zones/playerzone.png diff --git a/zonebg/leather_red.png b/themes/Leather/zones/stackzone.png similarity index 100% rename from zonebg/leather_red.png rename to themes/Leather/zones/stackzone.png diff --git a/zonebg/leather_blue.png b/themes/Leather/zones/tablezone.png similarity index 100% rename from zonebg/leather_blue.png rename to themes/Leather/zones/tablezone.png diff --git a/themes/Plasma/version.txt b/themes/Plasma/version.txt new file mode 100644 index 00000000..c2270834 --- /dev/null +++ b/themes/Plasma/version.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/zonebg/plasma_green.png b/themes/Plasma/zones/handzone.png similarity index 100% rename from zonebg/plasma_green.png rename to themes/Plasma/zones/handzone.png diff --git a/zonebg/plasma_gray.png b/themes/Plasma/zones/playerzone.png similarity index 100% rename from zonebg/plasma_gray.png rename to themes/Plasma/zones/playerzone.png diff --git a/zonebg/plasma_red.png b/themes/Plasma/zones/stackzone.png similarity index 100% rename from zonebg/plasma_red.png rename to themes/Plasma/zones/stackzone.png diff --git a/zonebg/plasma_blue.png b/themes/Plasma/zones/tablezone.png similarity index 100% rename from zonebg/plasma_blue.png rename to themes/Plasma/zones/tablezone.png diff --git a/themes/VelvetMarble/version.txt b/themes/VelvetMarble/version.txt new file mode 100644 index 00000000..c2270834 --- /dev/null +++ b/themes/VelvetMarble/version.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/zonebg/VelvetMarble_HorizontalHand_Hand.jpg b/themes/VelvetMarble/zones/handzone.jpg similarity index 100% rename from zonebg/VelvetMarble_HorizontalHand_Hand.jpg rename to themes/VelvetMarble/zones/handzone.jpg diff --git a/zonebg/VelvetMarble_Table.jpg b/themes/VelvetMarble/zones/playerzone.jpg similarity index 100% rename from zonebg/VelvetMarble_Table.jpg rename to themes/VelvetMarble/zones/playerzone.jpg diff --git a/zonebg/VelvetMarble_Stack.jpg b/themes/VelvetMarble/zones/stackzone.jpg similarity index 100% rename from zonebg/VelvetMarble_Stack.jpg rename to themes/VelvetMarble/zones/stackzone.jpg diff --git a/zonebg/VelvetMarble_HorizontalHand_Player.jpg b/themes/VelvetMarble/zones/tablezone.jpg similarity index 100% rename from zonebg/VelvetMarble_HorizontalHand_Player.jpg rename to themes/VelvetMarble/zones/tablezone.jpg diff --git a/zonebg/CMakeLists.txt b/zonebg/CMakeLists.txt deleted file mode 100644 index b250a44b..00000000 --- a/zonebg/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# CMakeLists for zonebg/ directory -# -# Installs default "zone background" files - -FILE(GLOB zonebg "${CMAKE_CURRENT_SOURCE_DIR}/*.png" "${CMAKE_CURRENT_SOURCE_DIR}/*.jpg") - -if(UNIX) - if(APPLE) - INSTALL(FILES ${zonebg} DESTINATION cockatrice.app/Contents/Resources/zonebg/) - else() - # Assume linux - INSTALL(FILES ${zonebg} DESTINATION share/cockatrice/zonebg/) - endif() -elseif(WIN32) - INSTALL(FILES ${zonebg} DESTINATION zonebg/) -endif() \ No newline at end of file diff --git a/zonebg/VelvetMarble_VerticalHand_Hand.jpg b/zonebg/VelvetMarble_VerticalHand_Hand.jpg deleted file mode 100644 index 83e8915ad4c539b38a4a3001be5a3021340f41a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24893 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

~KtYPH`wD$eHy0aodm9^5 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 a)&QtkGlSg{Lpm5ga!bp!f`9<^_5T1;9yv__