Merge branch 'master' into pr/5097

This commit is contained in:
tooomm 2024-12-01 14:38:21 +01:00
commit dc61357a31
40 changed files with 886 additions and 404 deletions

View file

@ -4,13 +4,13 @@
git push -d origin --REPLACE-WITH-BETA-LIST--
-->
<!-- This list of binaries should be updated every time the ci is changed to
<!-- This list of binaries should be updated every time the CI is changed to
include different targets -->
<pre>
<b>Pre-compiled binaries we serve:</b>
- <kbd>Windows 10+</kbd>
- <kbd>Windows 7+</kbd>
- <kbd>macOS 13+</kbd> ("Ventura") / Apple M
- <kbd>macOS 14+</kbd> ("Sonoma") / Apple M
- <kbd>macOS 13+</kbd> ("Ventura") / Intel
- <kbd>Ubuntu 24.04 LTS</kbd> ("Noble Numbat")
- <kbd>Ubuntu 22.04 LTS</kbd> ("Jammy Jellyfish")
@ -19,8 +19,9 @@ include different targets -->
- <kbd>Debian 11</kbd> ("Bullseye")
- <kbd>Fedora 41</kbd>
- <kbd>Fedora 40</kbd>
<kbd>We are also packaged in Arch Linux's official "extra" repository, courtesy of @FFY00</kbd></i>
<kbd>General Linux support is available via a flatpak package (Flathub)</kbd></i>
<i>We are also packaged in <kbd>Arch Linux</kbd>'s official "extra" repository, courtesy of @FFY00</i>
<i>General Linux support is available via a <kbd>flatpak</kbd> package (Flathub)</i>
</pre>
@ -28,22 +29,24 @@ include different targets -->
We're pleased to announce the newest official release: <kbd>--REPLACE-WITH-RELEASE-TITLE--</kbd>
We hope you enjoy the changes made and we have listed all changes, with their corresponding tickets, since the last version of Cockatrice was released for your convenience.
We hope you enjoy the changes made! All improvements with their corresponding tickets since the last version of Cockatrice are listed in the changelog below.
If you ever encounter a bug, have a suggestion or idea, or feel a need for a developer to look into something, please feel free to [open a ticket](https://github.com/Cockatrice/Cockatrice/issues). ([How to create a GitHub Ticket for Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))
If you ever encounter a bug, have a suggestion or idea, or feel a need for a developer to look into something, please feel free to [open a ticket](https://github.com/Cockatrice/Cockatrice/issues). ([How to create a Ticket for Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))
For any information relating to Cockatrice, please take a look at our official site: **https://cockatrice.github.io**
For basic information related to the app and getting started, please take a look at our official site: **https://cockatrice.github.io**
If you'd like to help contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#get-involved-). We're always available to answer questions you may have on how the program works and how you can provide a meaningful contribution.
If you'd like to help and contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#get-involved-).
We're always available to answer questions you may have on how the program works and how you can provide a meaningful contribution.
## Upgrading Cockatrice
<!-- this optional section puts a warning banner for problems with updating
> ⚠️ **With this release, we no longer provide a ready-to-install binary for:**
> [!IMPORTANT]
> **With this release, we no longer provide a ready-to-install binary for:**
> --DEPRECATED-OS-HERE--
-->
- Run the internal software updater: <kbd>Help → Check for Client Updates</kbd>
Run the internal software updater: <kbd>Help → Check for Client Updates</kbd>
Don't forget to update your card database right after! (<kbd>Help → Check for Card Updates...</kbd>)
@ -60,14 +63,14 @@ Remove empty headers when done.
-->
<!-- Highlights of the release -->
### ⚠️ Important:
### 🔖 Highlights:
### ✨ New Features:
### 🐛 Fixed Bugs / Resolved issues:
### 🐛 Fixed Bugs / Resolved Issues:
<!-- Complete list of changes (foldable) -->
<details>
<summary>
📘 <b>Show all changes</b> (--REPLACE-WITH-COMMIT-COUNT-- commits)
<b>Show all changes</b> (--REPLACE-WITH-COMMIT-COUNT-- commits)
</summary>
### User Interface
@ -88,5 +91,6 @@ Remove empty headers when done.
## Special Thanks
<!-- Personalise this a bit! -->
We continue to find it amazing that so many people contribute their time, knowledge, code, testing and more to the project. We'd like to thank the entire Cockatrice community for their efforts.
It's amazing that so many people contribute their time, knowledge, code, testing and more to the project.
We'd like to thank the entire Cockatrice community for their efforts! 🙏
<!-- We'd like to especially recognize @ZeldaZach, --ADD-CONTRIBUTORS-HERE-- for their help in preparing so many amazing new features for the user base. -->

View file

@ -212,7 +212,7 @@ jobs:
- target: 14
soc: Apple
os: macos-14
xcode: "14.3.1"
xcode: "15.4"
type: Release
core_count: 3
make_package: 1
@ -220,7 +220,7 @@ jobs:
- target: 14
soc: Apple
os: macos-14
xcode: "14.3.1"
xcode: "15.4"
type: Debug
core_count: 3

View file

@ -19,7 +19,7 @@ function(get_commit_id)
PARENT_SCOPE
)
set(PROJECT_VERSION_LABEL
"custom(${GIT_COM_ID})"
"custom-${GIT_COM_ID}"
PARENT_SCOPE
)
endfunction()

View file

@ -162,6 +162,10 @@ void ReplayTimelineWidget::processNewEvents(PlaybackMode playbackMode)
if (playbackMode == BACKWARD_SKIP || currentTime - replayTimeline[currentEvent] > BIG_SKIP_MS)
options |= Player::EventProcessingOption::SKIP_REVEAL_WINDOW;
// backwards skip => always skip tap animation
if (playbackMode == BACKWARD_SKIP)
options |= Player::EventProcessingOption::SKIP_TAP_ANIMATION;
emit processNextEvent(options);
++currentEvent;
}

View file

@ -748,9 +748,7 @@ bool TabDeckEditor::confirmClose()
{
if (modified) {
tabSupervisor->setCurrentWidget(this);
QMessageBox::StandardButton ret = QMessageBox::warning(
this, tr("Are you sure?"), tr("The decklist has been modified.\nDo you want to save the changes?"),
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
int ret = createSaveConfirmationWindow()->exec();
if (ret == QMessageBox::Save)
return actSaveDeck();
else if (ret == QMessageBox::Cancel)
@ -767,13 +765,16 @@ void TabDeckEditor::closeRequest()
void TabDeckEditor::actNewDeck()
{
if (SettingsCache::instance().getOpenDeckInNewTab()) {
emit openDeckEditor(nullptr);
auto deckOpenLocation = confirmOpen(false);
if (deckOpenLocation == CANCELLED) {
return;
}
if (!confirmClose())
if (deckOpenLocation == NEW_TAB) {
emit openDeckEditor(nullptr);
return;
}
deckModel->cleanList();
nameEdit->setText(QString());
@ -785,10 +786,11 @@ void TabDeckEditor::actNewDeck()
void TabDeckEditor::actLoadDeck()
{
bool openInNewTab = SettingsCache::instance().getOpenDeckInNewTab() && !isBlankNewDeck();
auto deckOpenLocation = confirmOpen();
if (!openInNewTab && !confirmClose())
if (deckOpenLocation == CANCELLED) {
return;
}
QFileDialog dialog(this, tr("Load deck"));
dialog.setDirectory(SettingsCache::instance().getDeckPath());
@ -801,7 +803,7 @@ void TabDeckEditor::actLoadDeck()
auto *l = new DeckLoader;
if (l->loadFromFile(fileName, fmt)) {
if (openInNewTab) {
if (deckOpenLocation == NEW_TAB) {
emit openDeckEditor(l);
} else {
setSaveStatus(false);
@ -878,16 +880,17 @@ bool TabDeckEditor::actSaveDeckAs()
void TabDeckEditor::actLoadDeckFromClipboard()
{
bool openInNewTab = SettingsCache::instance().getOpenDeckInNewTab() && !isBlankNewDeck();
auto deckOpenLocation = confirmOpen();
if (!openInNewTab && !confirmClose())
if (deckOpenLocation == CANCELLED) {
return;
}
DlgLoadDeckFromClipboard dlg(this);
if (!dlg.exec())
return;
if (openInNewTab) {
if (deckOpenLocation == NEW_TAB) {
emit openDeckEditor(dlg.getDeckList());
} else {
setDeck(dlg.getDeckList());
@ -987,6 +990,70 @@ void TabDeckEditor::recursiveExpand(const QModelIndex &index)
deckView->expand(index);
}
/**
* @brief Displays the save confirmation dialogue that is shown before loading a deck, if required. Takes into
* account the `openDeckInNewTab` settting.
*
* @param openInSameTabIfBlank Open the deck in the same tab instead of a new tab if the current tab is completely
* blank. Only relevant when the `openDeckInNewTab` setting is enabled.
*
* @returns An enum that indicates if and where to load the deck
*/
TabDeckEditor::DeckOpenLocation TabDeckEditor::confirmOpen(const bool openInSameTabIfBlank)
{
// handle `openDeckInNewTab` setting
if (SettingsCache::instance().getOpenDeckInNewTab()) {
if (openInSameTabIfBlank && isBlankNewDeck()) {
return SAME_TAB;
} else {
return NEW_TAB;
}
}
// early return if deck is unmodified
if (!modified) {
return SAME_TAB;
}
// do the save confirmation dialogue
tabSupervisor->setCurrentWidget(this);
QMessageBox *msgBox = createSaveConfirmationWindow();
QPushButton *newTabButton = msgBox->addButton(tr("Open in new tab"), QMessageBox::ApplyRole);
int ret = msgBox->exec();
// `exec()` returns an opaque value if a non-standard button was clicked.
// Directly check if newTabButton was clicked before switching over the standard buttons.
if (msgBox->clickedButton() == newTabButton) {
return NEW_TAB;
}
switch (ret) {
case QMessageBox::Save:
return actSaveDeck() ? SAME_TAB : CANCELLED;
case QMessageBox::Discard:
return SAME_TAB;
default:
return CANCELLED;
}
}
/**
* @brief Creates the base save confirmation dialogue box.
*
* @returns A QMessageBox that can be further modified
*/
QMessageBox *TabDeckEditor::createSaveConfirmationWindow()
{
QMessageBox *msgBox = new QMessageBox(this);
msgBox->setIcon(QMessageBox::Warning);
msgBox->setWindowTitle(tr("Are you sure?"));
msgBox->setText(tr("The decklist has been modified.\nDo you want to save the changes?"));
msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
return msgBox;
}
/**
* @brief Returns true if this tab is a blank newly opened tab, as if it was just created with the `New Deck` action.
*/

View file

@ -22,6 +22,7 @@ class Response;
class FilterTreeModel;
class FilterBuilder;
class QGroupBox;
class QMessageBox;
class QHBoxLayout;
class QVBoxLayout;
class QPushButton;
@ -100,6 +101,19 @@ private slots:
void showSearchSyntaxHelp();
private:
/**
* @brief Which tab to open the new deck in
*/
enum DeckOpenLocation
{
CANCELLED,
SAME_TAB,
NEW_TAB
};
DeckOpenLocation confirmOpen(const bool openInSameTabIfBlank = true);
QMessageBox *createSaveConfirmationWindow();
bool isBlankNewDeck() const;
CardInfoPtr currentCardInfo() const;
void addCardHelper(QString zoneName);

View file

@ -648,8 +648,7 @@ void TabGame::replayRewind()
messageLog->clearChat();
// reset phase markers
currentPhase = -1;
phasesToolbar->reset();
setActivePhase(-1);
}
void TabGame::incrementGameTime()

View file

@ -86,23 +86,6 @@ void PhaseButton::updateAnimation()
update();
}
/**
* @brief Immediately resets the button to the inactive state, without going through the animation.
*/
void PhaseButton::reset()
{
activeAnimationTimer->stop();
active = false;
if (highlightable) {
activeAnimationCounter = 0;
} else {
activeAnimationCounter = 9;
}
update();
}
void PhaseButton::mousePressEvent(QGraphicsSceneMouseEvent * /*event*/)
{
emit clicked();
@ -268,16 +251,6 @@ void PhasesToolbar::phaseButtonClicked()
emit sendGameCommand(cmd, -1);
}
/**
* @brief Immediately resets the toolbar to its initial state, with all phases inactive.
*/
void PhasesToolbar::reset()
{
for (auto &i : buttonList) {
i->reset();
}
}
void PhasesToolbar::actNextTurn()
{
emit sendGameCommand(Command_NextTurn(), -1);

View file

@ -45,7 +45,6 @@ public:
{
return active;
}
void reset();
void triggerDoubleClickAction();
signals:
void clicked();
@ -86,7 +85,6 @@ public:
public slots:
void setActivePhase(int phase);
void triggerPhaseAction(int phase);
void reset();
private slots:
void phaseButtonClicked();
void actNextTurn();

View file

@ -1,17 +1,14 @@
#include "picture_loader.h"
#include "../../game/cards/card_database.h"
#include "../../game/cards/card_database_manager.h"
#include "../../settings/cache_settings.h"
#include "theme_manager.h"
#include <QApplication>
#include <QBuffer>
#include <QCryptographicHash>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QImageReader>
#include <QMovie>
#include <QNetworkAccessManager>
@ -23,7 +20,6 @@
#include <QRegularExpression>
#include <QScreen>
#include <QSet>
#include <QSvgRenderer>
#include <QThread>
#include <QUrl>
#include <algorithm>
@ -136,6 +132,8 @@ PictureLoaderWorker::PictureLoaderWorker()
#endif
auto cache = new QNetworkDiskCache(this);
cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath());
cache->setMaximumCacheSize(1024L * 1024L *
static_cast<qint64>(SettingsCache::instance().getNetworkCacheSizeInMB()));
// Note: the settings is in MB, but QNetworkDiskCache uses bytes
connect(&SettingsCache::instance(), &SettingsCache::networkCacheSizeChanged, cache,
[cache](int newSizeInMB) { cache->setMaximumCacheSize(1024L * 1024L * static_cast<qint64>(newSizeInMB)); });
@ -145,6 +143,13 @@ PictureLoaderWorker::PictureLoaderWorker()
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *)));
cacheFilePath = SettingsCache::instance().getRedirectCachePath() + REDIRECT_CACHE_FILENAME;
loadRedirectCache();
cleanStaleEntries();
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this,
&PictureLoaderWorker::saveRedirectCache);
pictureLoaderThread = new QThread;
pictureLoaderThread->start(QThread::LowPriority);
moveToThread(pictureLoaderThread);
@ -447,11 +452,104 @@ bool PictureLoaderWorker::imageIsBlackListed(const QByteArray &picData)
QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url)
{
// Check if the redirect is cached
QUrl cachedRedirect = getCachedRedirect(url);
if (!cachedRedirect.isEmpty()) {
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Using cached redirect for "
<< url.toDisplayString() << " to " << cachedRedirect.toDisplayString();
return makeRequest(cachedRedirect); // Use the cached redirect
}
QNetworkRequest req(url);
if (!picDownload) {
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
}
return networkManager->get(req);
QNetworkReply *reply = networkManager->get(req);
connect(reply, &QNetworkReply::finished, this, [this, reply, url]() {
QVariant redirectTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (redirectTarget.isValid()) {
QUrl redirectUrl = redirectTarget.toUrl();
if (redirectUrl.isRelative()) {
redirectUrl = url.resolved(redirectUrl);
}
cacheRedirect(url, redirectUrl);
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Caching redirect from "
<< url.toDisplayString() << " to " << redirectUrl.toDisplayString();
}
reply->deleteLater();
});
return reply;
}
void PictureLoaderWorker::cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl)
{
redirectCache[originalUrl] = qMakePair(redirectUrl, QDateTime::currentDateTimeUtc());
saveRedirectCache();
}
QUrl PictureLoaderWorker::getCachedRedirect(const QUrl &originalUrl) const
{
if (redirectCache.contains(originalUrl)) {
return redirectCache[originalUrl].first;
}
return {};
}
void PictureLoaderWorker::loadRedirectCache()
{
QSettings settings(cacheFilePath, QSettings::IniFormat);
redirectCache.clear();
int size = settings.beginReadArray(REDIRECT_HEADER_NAME);
for (int i = 0; i < size; ++i) {
settings.setArrayIndex(i);
QUrl originalUrl = settings.value(REDIRECT_ORIGINAL_URL).toUrl();
QUrl redirectUrl = settings.value(REDIRECT_URL).toUrl();
QDateTime timestamp = settings.value(REDIRECT_TIMESTAMP).toDateTime();
if (originalUrl.isValid() && redirectUrl.isValid()) {
redirectCache[originalUrl] = qMakePair(redirectUrl, timestamp);
}
}
settings.endArray();
}
void PictureLoaderWorker::saveRedirectCache() const
{
QSettings settings(cacheFilePath, QSettings::IniFormat);
settings.beginWriteArray(REDIRECT_HEADER_NAME, static_cast<int>(redirectCache.size()));
int index = 0;
for (auto it = redirectCache.cbegin(); it != redirectCache.cend(); ++it) {
settings.setArrayIndex(index++);
settings.setValue(REDIRECT_ORIGINAL_URL, it.key());
settings.setValue(REDIRECT_URL, it.value().first);
settings.setValue(REDIRECT_TIMESTAMP, it.value().second);
}
settings.endArray();
}
void PictureLoaderWorker::cleanStaleEntries()
{
QDateTime now = QDateTime::currentDateTimeUtc();
auto it = redirectCache.begin();
while (it != redirectCache.end()) {
if (it.value().second.addDays(SettingsCache::instance().getRedirectCacheTtl()) < now) {
it = redirectCache.erase(it); // Remove stale entry
} else {
++it;
}
}
}
void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)

View file

@ -11,6 +11,12 @@ class QNetworkAccessManager;
class QNetworkReply;
class QThread;
#define REDIRECT_HEADER_NAME "redirects"
#define REDIRECT_ORIGINAL_URL "original"
#define REDIRECT_URL "redirect"
#define REDIRECT_TIMESTAMP "timestamp"
#define REDIRECT_CACHE_FILENAME "cache.ini"
class PictureToLoad
{
private:
@ -83,6 +89,9 @@ private:
QList<PictureToLoad> loadQueue;
QMutex mutex;
QNetworkAccessManager *networkManager;
QHash<QUrl, QPair<QUrl, QDateTime>> redirectCache; // Stores redirect and timestamp
QString cacheFilePath; // Path to persistent storage
static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable
QList<PictureToLoad> cardsToDownload;
PictureToLoad cardBeingLoaded;
PictureToLoad cardBeingDownloaded;
@ -91,6 +100,12 @@ private:
bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName);
bool imageIsBlackListed(const QByteArray &);
QNetworkReply *makeRequest(const QUrl &url);
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
QUrl getCachedRedirect(const QUrl &originalUrl) const;
void loadRedirectCache();
void saveRedirectCache() const;
void cleanStaleEntries();
private slots:
void picDownloadFinished(QNetworkReply *reply);
void picDownloadFailed();

View file

@ -666,6 +666,7 @@ void MainWindow::retranslateUi()
aOpenCustomFolder->setText(tr("Open custom image folder"));
aOpenCustomsetsFolder->setText(tr("Open custom sets folder"));
aAddCustomSet->setText(tr("Add custom sets/cards"));
aReloadCardDatabase->setText(tr("Reload card database"));
helpMenu->setTitle(tr("&Help"));
aAbout->setText(tr("&About Cockatrice"));
@ -714,6 +715,8 @@ void MainWindow::createActions()
connect(aOpenCustomsetsFolder, SIGNAL(triggered()), this, SLOT(actOpenCustomsetsFolder()));
aAddCustomSet = new QAction(QString(), this);
connect(aAddCustomSet, SIGNAL(triggered()), this, SLOT(actAddCustomSet()));
aReloadCardDatabase = new QAction(QString(), this);
connect(aReloadCardDatabase, SIGNAL(triggered()), this, SLOT(actReloadCardDatabase()));
aAbout = new QAction(this);
connect(aAbout, SIGNAL(triggered()), this, SLOT(actAbout()));
@ -788,6 +791,8 @@ void MainWindow::createMenus()
dbMenu->addAction(aOpenCustomFolder);
dbMenu->addAction(aOpenCustomsetsFolder);
dbMenu->addAction(aAddCustomSet);
dbMenu->addSeparator();
dbMenu->addAction(aReloadCardDatabase);
helpMenu = menuBar()->addMenu(QString());
helpMenu->addAction(aAbout);
@ -878,6 +883,7 @@ void MainWindow::startupConfigCheck()
// no config found, 99% new clean install
qDebug() << "Startup: old client version empty, assuming first start after clean install";
alertForcedOracleRun(VERSION_STRING, false);
SettingsCache::instance().downloads().resetToDefaultURLs(); // populate the download urls
SettingsCache::instance().setClientVersion(VERSION_STRING);
} else if (SettingsCache::instance().getClientVersion() != VERSION_STRING) {
// config found, from another (presumably older) version
@ -1324,6 +1330,11 @@ int MainWindow::getNextCustomSetPrefix(QDir dataDir)
return maxIndex + 1;
}
void MainWindow::actReloadCardDatabase()
{
const auto reloadOk1 = QtConcurrent::run([] { CardDatabaseManager::getInstance()->loadCardDatabases(); });
}
void MainWindow::actManageSets()
{
wndSets = new WndSets(this);

View file

@ -98,6 +98,7 @@ private slots:
void actOpenCustomFolder();
void actOpenCustomsetsFolder();
void actAddCustomSet();
void actReloadCardDatabase();
void actManageSets();
void actEditTokens();
@ -125,7 +126,7 @@ private:
QMenu *cockatriceMenu, *dbMenu, *helpMenu, *trayIconMenu;
QAction *aConnect, *aDisconnect, *aSinglePlayer, *aWatchReplay, *aDeckEditor, *aFullScreen, *aSettings, *aExit,
*aAbout, *aTips, *aCheckCardUpdates, *aRegister, *aForgotPassword, *aUpdate, *aViewLog, *aManageSets,
*aEditTokens, *aOpenCustomFolder, *aOpenCustomsetsFolder, *aAddCustomSet, *aShow;
*aEditTokens, *aOpenCustomFolder, *aOpenCustomsetsFolder, *aAddCustomSet, *aReloadCardDatabase, *aShow;
TabSupervisor *tabSupervisor;
WndSets *wndSets;
RemoteClient *client;

View file

@ -80,8 +80,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
autoConnectCheckBox->setEnabled(false);
}
connect(savePasswordCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this,
SLOT(passwordSaved(QT_STATE_CHANGED_T)));
connect(savePasswordCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &DlgConnect::passwordSaved);
serverIssuesLabel =
new QLabel(tr("If you have any trouble connecting or registering then contact the server staff for help!"));

View file

@ -81,8 +81,7 @@ void DlgCreateGame::sharedCtor()
spectatorsAllowedCheckBox = new QCheckBox(tr("&Spectators can watch"));
spectatorsAllowedCheckBox->setChecked(true);
connect(spectatorsAllowedCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this,
SLOT(spectatorsAllowedChanged(QT_STATE_CHANGED_T)));
connect(spectatorsAllowedCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &DlgCreateGame::spectatorsAllowedChanged);
spectatorsNeedPasswordCheckBox = new QCheckBox(tr("Spectators &need a password to watch"));
spectatorsCanTalkCheckBox = new QCheckBox(tr("Spectators can &chat"));
spectatorsSeeEverythingCheckBox = new QCheckBox(tr("Spectators can see &hands"));

View file

@ -276,7 +276,7 @@ int DlgFilterGames::getMaxPlayersFilterMax() const
return maxPlayersFilterMaxSpinBox->value();
}
const QTime &DlgFilterGames::getMaxGameAge() const
QTime DlgFilterGames::getMaxGameAge() const
{
int index = maxGameAgeComboBox->currentIndex();
if (index < 0 || index >= gameAgeMap.size()) { // index is out of bounds

View file

@ -67,7 +67,7 @@ public:
int getMaxPlayersFilterMin() const;
int getMaxPlayersFilterMax() const;
void setMaxPlayersFilter(int _maxPlayersFilterMin, int _maxPlayersFilterMax);
const QTime &getMaxGameAge() const;
QTime getMaxGameAge() const;
const QMap<QTime, QString> gameAgeMap;
bool getShowOnlyIfSpectatorsCanWatch() const;
bool getShowSpectatorPasswordProtected() const;

View file

@ -74,10 +74,9 @@ GeneralSettingsPage::GeneralSettingsPage()
connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
connect(&updateReleaseChannelBox, SIGNAL(currentIndexChanged(int)), &settings, SLOT(setUpdateReleaseChannel(int)));
connect(&updateNotificationCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings,
SLOT(setNotifyAboutUpdate(QT_STATE_CHANGED_T)));
connect(&newVersionOracleCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings,
SLOT(setNotifyAboutNewVersion(QT_STATE_CHANGED_T)));
connect(&updateNotificationCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setNotifyAboutUpdate);
connect(&newVersionOracleCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings,
&SettingsCache::setNotifyAboutNewVersion);
connect(&showTipsOnStartup, SIGNAL(clicked(bool)), &settings, SLOT(setShowTipsOnStartup(bool)));
auto *personalGrid = new QGridLayout;
@ -324,12 +323,10 @@ AppearanceSettingsPage::AppearanceSettingsPage()
themeGroupBox->setLayout(themeGrid);
displayCardNamesCheckBox.setChecked(settings.getDisplayCardNames());
connect(&displayCardNamesCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings,
SLOT(setDisplayCardNames(QT_STATE_CHANGED_T)));
connect(&displayCardNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setDisplayCardNames);
cardScalingCheckBox.setChecked(settings.getScaleCards());
connect(&cardScalingCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings,
SLOT(setCardScaling(QT_STATE_CHANGED_T)));
connect(&cardScalingCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setCardScaling);
verticalCardOverlapPercentBox.setValue(settings.getStackCardOverlapPercent());
verticalCardOverlapPercentBox.setRange(0, 80);
@ -346,12 +343,10 @@ AppearanceSettingsPage::AppearanceSettingsPage()
cardsGroupBox->setLayout(cardsGrid);
horizontalHandCheckBox.setChecked(settings.getHorizontalHand());
connect(&horizontalHandCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings,
SLOT(setHorizontalHand(QT_STATE_CHANGED_T)));
connect(&horizontalHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setHorizontalHand);
leftJustifiedHandCheckBox.setChecked(settings.getLeftJustified());
connect(&leftJustifiedHandCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings,
SLOT(setLeftJustified(QT_STATE_CHANGED_T)));
connect(&leftJustifiedHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setLeftJustified);
auto *handGrid = new QGridLayout;
handGrid->addWidget(&horizontalHandCheckBox, 0, 0, 1, 2);
@ -361,8 +356,8 @@ AppearanceSettingsPage::AppearanceSettingsPage()
handGroupBox->setLayout(handGrid);
invertVerticalCoordinateCheckBox.setChecked(settings.getInvertVerticalCoordinate());
connect(&invertVerticalCoordinateCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings,
SLOT(setInvertVerticalCoordinate(QT_STATE_CHANGED_T)));
connect(&invertVerticalCoordinateCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings,
&SettingsCache::setInvertVerticalCoordinate);
minPlayersForMultiColumnLayoutEdit.setMinimum(2);
minPlayersForMultiColumnLayoutEdit.setValue(settings.getMinPlayersForMultiColumnLayout());
@ -442,37 +437,37 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
{
// general settings and notification settings
notificationsEnabledCheckBox.setChecked(SettingsCache::instance().getNotificationsEnabled());
connect(&notificationsEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setNotificationsEnabled(QT_STATE_CHANGED_T)));
connect(&notificationsEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this,
SLOT(setNotificationEnabled(QT_STATE_CHANGED_T)));
connect(&notificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setNotificationsEnabled);
connect(&notificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&UserInterfaceSettingsPage::setNotificationEnabled);
specNotificationsEnabledCheckBox.setChecked(SettingsCache::instance().getSpectatorNotificationsEnabled());
specNotificationsEnabledCheckBox.setEnabled(SettingsCache::instance().getNotificationsEnabled());
connect(&specNotificationsEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setSpectatorNotificationsEnabled(QT_STATE_CHANGED_T)));
connect(&specNotificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setSpectatorNotificationsEnabled);
buddyConnectNotificationsEnabledCheckBox.setChecked(
SettingsCache::instance().getBuddyConnectNotificationsEnabled());
buddyConnectNotificationsEnabledCheckBox.setEnabled(SettingsCache::instance().getNotificationsEnabled());
connect(&buddyConnectNotificationsEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)),
&SettingsCache::instance(), SLOT(setBuddyConnectNotificationsEnabled(QT_STATE_CHANGED_T)));
connect(&buddyConnectNotificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setBuddyConnectNotificationsEnabled);
doubleClickToPlayCheckBox.setChecked(SettingsCache::instance().getDoubleClickToPlay());
connect(&doubleClickToPlayCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setDoubleClickToPlay(QT_STATE_CHANGED_T)));
connect(&doubleClickToPlayCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setDoubleClickToPlay);
playToStackCheckBox.setChecked(SettingsCache::instance().getPlayToStack());
connect(&playToStackCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setPlayToStack(QT_STATE_CHANGED_T)));
connect(&playToStackCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setPlayToStack);
annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens());
connect(&annotateTokensCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setAnnotateTokens(QT_STATE_CHANGED_T)));
connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setAnnotateTokens);
useTearOffMenusCheckBox.setChecked(SettingsCache::instance().getUseTearOffMenus());
connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
[](QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); });
[](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); });
auto *generalGrid = new QGridLayout;
generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0);
@ -493,8 +488,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
// animation settings
tapAnimationCheckBox.setChecked(SettingsCache::instance().getTapAnimation());
connect(&tapAnimationCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setTapAnimation(QT_STATE_CHANGED_T)));
connect(&tapAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setTapAnimation);
auto *animationGrid = new QGridLayout;
animationGrid->addWidget(&tapAnimationCheckBox, 0, 0);
@ -504,8 +499,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
// deck editor settings
openDeckInNewTabCheckBox.setChecked(SettingsCache::instance().getOpenDeckInNewTab());
connect(&openDeckInNewTabCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setOpenDeckInNewTab(QT_STATE_CHANGED_T)));
connect(&openDeckInNewTabCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setOpenDeckInNewTab);
auto *deckEditorGrid = new QGridLayout;
deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0);
@ -524,7 +519,7 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
setLayout(mainLayout);
}
void UserInterfaceSettingsPage::setNotificationEnabled(int i)
void UserInterfaceSettingsPage::setNotificationEnabled(QT_STATE_CHANGED_T i)
{
specNotificationsEnabledCheckBox.setEnabled(i != 0);
buddyConnectNotificationsEnabledCheckBox.setEnabled(i != 0);
@ -554,8 +549,8 @@ void UserInterfaceSettingsPage::retranslateUi()
DeckEditorSettingsPage::DeckEditorSettingsPage()
{
picDownloadCheckBox.setChecked(SettingsCache::instance().getPicDownload());
connect(&picDownloadCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setPicDownload(QT_STATE_CHANGED_T)));
connect(&picDownloadCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setPicDownload);
urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse);
urlLinkLabel.setOpenExternalLinks(true);
@ -588,8 +583,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
connect(urlList->model(), SIGNAL(rowsMoved(const QModelIndex, int, int, const QModelIndex, int)), this,
SLOT(urlListChanged(const QModelIndex, int, int, const QModelIndex, int)));
for (int i = 0; i < SettingsCache::instance().downloads().getCount(); i++)
urlList->addItem(SettingsCache::instance().downloads().getDownloadUrlAt(i));
urlList->addItems(SettingsCache::instance().downloads().getAllURLs());
auto aAdd = new QAction(this);
aAdd->setIcon(QPixmap("theme:icons/increment"));
@ -604,16 +598,16 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
aRemove->setToolTip(tr("Remove URL"));
connect(aRemove, SIGNAL(triggered()), this, SLOT(actRemoveURL()));
auto *messageToolBar = new QToolBar;
messageToolBar->setOrientation(Qt::Vertical);
messageToolBar->addAction(aAdd);
messageToolBar->addAction(aRemove);
messageToolBar->addAction(aEdit);
messageToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
auto *urlToolBar = new QToolBar;
urlToolBar->setOrientation(Qt::Vertical);
urlToolBar->addAction(aAdd);
urlToolBar->addAction(aRemove);
urlToolBar->addAction(aEdit);
urlToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
auto *messageListLayout = new QHBoxLayout;
messageListLayout->addWidget(messageToolBar);
messageListLayout->addWidget(urlList);
auto *urlListLayout = new QHBoxLayout;
urlListLayout->addWidget(urlToolBar);
urlListLayout->addWidget(urlList);
// pixmap cache
pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN);
@ -629,11 +623,22 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
networkCacheEdit.setValue(SettingsCache::instance().getNetworkCacheSizeInMB());
networkCacheEdit.setSuffix(" MB");
networkRedirectCacheTtlEdit.setMinimum(NETWORK_REDIRECT_CACHE_TTL_MIN);
networkRedirectCacheTtlEdit.setMaximum(NETWORK_REDIRECT_CACHE_TTL_MAX);
networkRedirectCacheTtlEdit.setSingleStep(1);
networkRedirectCacheTtlEdit.setValue(SettingsCache::instance().getRedirectCacheTtl());
networkRedirectCacheTtlEdit.setSuffix(" " + tr("Day(s)"));
auto networkCacheLayout = new QHBoxLayout;
networkCacheLayout->addStretch();
networkCacheLayout->addWidget(&networkCacheLabel);
networkCacheLayout->addWidget(&networkCacheEdit);
auto networkRedirectCacheLayout = new QHBoxLayout;
networkRedirectCacheLayout->addStretch();
networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlLabel);
networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlEdit);
auto pixmapCacheLayout = new QHBoxLayout;
pixmapCacheLayout->addStretch();
pixmapCacheLayout->addWidget(&pixmapCacheLabel);
@ -642,11 +647,12 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
// Top Layout
lpGeneralGrid->addWidget(&picDownloadCheckBox, 0, 0);
lpGeneralGrid->addWidget(&resetDownloadURLs, 0, 1);
lpGeneralGrid->addLayout(messageListLayout, 1, 0, 1, 2);
lpGeneralGrid->addLayout(urlListLayout, 1, 0, 1, 2);
lpGeneralGrid->addLayout(networkCacheLayout, 2, 0, 1, 2);
lpGeneralGrid->addLayout(pixmapCacheLayout, 3, 0, 1, 2);
lpGeneralGrid->addWidget(&urlLinkLabel, 4, 0);
lpGeneralGrid->addWidget(&clearDownloadedPicsButton, 4, 1);
lpGeneralGrid->addLayout(networkRedirectCacheLayout, 4, 0, 1, 2);
lpGeneralGrid->addWidget(&urlLinkLabel, 5, 0);
lpGeneralGrid->addWidget(&clearDownloadedPicsButton, 5, 1);
// Spoiler Layout
lpSpoilerGrid->addWidget(&mcDownloadSpoilersCheckBox, 0, 0);
@ -657,13 +663,15 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
lpSpoilerGrid->addWidget(updateNowButton, 2, 1);
lpSpoilerGrid->addWidget(&infoOnSpoilersLabel, 3, 0, 1, 3, Qt::AlignTop);
// On a change to the check box, hide/unhide the other fields
// On a change to the checkbox, hide/un-hide the other fields
connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), &SettingsCache::instance(),
SLOT(setDownloadSpoilerStatus(bool)));
connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), this, SLOT(setSpoilersEnabled(bool)));
connect(&pixmapCacheEdit, SIGNAL(valueChanged(int)), &SettingsCache::instance(), SLOT(setPixmapCacheSize(int)));
connect(&networkCacheEdit, SIGNAL(valueChanged(int)), &SettingsCache::instance(),
SLOT(setNetworkCacheSizeInMB(int)));
connect(&networkRedirectCacheTtlEdit, SIGNAL(valueChanged(int)), &SettingsCache::instance(),
SLOT(setNetworkRedirectCacheTtl(int)));
mpGeneralGroupBox = new QGroupBox;
mpGeneralGroupBox->setLayout(lpGeneralGrid);
@ -680,7 +688,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
void DeckEditorSettingsPage::resetDownloadedURLsButtonClicked()
{
SettingsCache::instance().downloads().clear();
SettingsCache::instance().downloads().resetToDefaultURLs();
urlList->clear();
urlList->addItems(SettingsCache::instance().downloads().getAllURLs());
QMessageBox::information(this, tr("Success"), tr("Download URLs have been reset."));
@ -760,11 +768,13 @@ void DeckEditorSettingsPage::actEditURL()
void DeckEditorSettingsPage::storeSettings()
{
qInfo() << "URL Priority Reset";
SettingsCache::instance().downloads().clear();
QStringList downloadUrls;
for (int i = 0; i < urlList->count(); i++) {
qInfo() << "Priority" << i << ":" << urlList->item(i)->text();
SettingsCache::instance().downloads().setDownloadUrlAt(i, urlList->item(i)->text());
downloadUrls << urlList->item(i)->text();
}
SettingsCache::instance().downloads().setDownloadUrls(downloadUrls);
}
void DeckEditorSettingsPage::urlListChanged(const QModelIndex &, int, int, const QModelIndex &, int)
@ -844,39 +854,40 @@ void DeckEditorSettingsPage::retranslateUi()
urlLinkLabel.setText(QString("<a href='%1'>%2</a>").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to add a custom URL")));
clearDownloadedPicsButton.setText(tr("Delete Downloaded Images"));
resetDownloadURLs.setText(tr("Reset Download URLs"));
networkCacheLabel.setText(tr("Downloaded images directory size:"));
networkCacheLabel.setText(tr("Network Cache Size:"));
networkCacheEdit.setToolTip(tr("On-disk cache for downloaded pictures"));
pixmapCacheLabel.setText(tr("Picture cache size:"));
networkRedirectCacheTtlLabel.setText(tr("Redirect Cache TTL:"));
networkRedirectCacheTtlEdit.setToolTip(tr("How long cached redirects for urls are valid for."));
pixmapCacheLabel.setText(tr("Picture Cache Size:"));
pixmapCacheEdit.setToolTip(tr("In-memory cache for pictures not currently on screen"));
}
MessagesSettingsPage::MessagesSettingsPage()
{
chatMentionCheckBox.setChecked(SettingsCache::instance().getChatMention());
connect(&chatMentionCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setChatMention(QT_STATE_CHANGED_T)));
connect(&chatMentionCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setChatMention);
chatMentionCompleterCheckbox.setChecked(SettingsCache::instance().getChatMentionCompleter());
connect(&chatMentionCompleterCheckbox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setChatMentionCompleter(QT_STATE_CHANGED_T)));
connect(&chatMentionCompleterCheckbox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setChatMentionCompleter);
explainMessagesLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse);
explainMessagesLabel.setOpenExternalLinks(true);
ignoreUnregUsersMainChat.setChecked(SettingsCache::instance().getIgnoreUnregisteredUsers());
ignoreUnregUserMessages.setChecked(SettingsCache::instance().getIgnoreUnregisteredUserMessages());
connect(&ignoreUnregUsersMainChat, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setIgnoreUnregisteredUsers(QT_STATE_CHANGED_T)));
connect(&ignoreUnregUserMessages, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T)));
connect(&ignoreUnregUsersMainChat, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setIgnoreUnregisteredUsers);
connect(&ignoreUnregUserMessages, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setIgnoreUnregisteredUserMessages);
invertMentionForeground.setChecked(SettingsCache::instance().getChatMentionForeground());
connect(&invertMentionForeground, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this,
SLOT(updateTextColor(QT_STATE_CHANGED_T)));
connect(&invertMentionForeground, &QCheckBox::QT_STATE_CHANGED, this, &MessagesSettingsPage::updateTextColor);
invertHighlightForeground.setChecked(SettingsCache::instance().getChatHighlightForeground());
connect(&invertHighlightForeground, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this,
SLOT(updateTextHighlightColor(QT_STATE_CHANGED_T)));
connect(&invertHighlightForeground, &QCheckBox::QT_STATE_CHANGED, this,
&MessagesSettingsPage::updateTextHighlightColor);
mentionColor = new QLineEdit();
mentionColor->setText(SettingsCache::instance().getChatMentionColor());
@ -884,16 +895,15 @@ MessagesSettingsPage::MessagesSettingsPage()
connect(mentionColor, SIGNAL(textChanged(QString)), this, SLOT(updateColor(QString)));
messagePopups.setChecked(SettingsCache::instance().getShowMessagePopup());
connect(&messagePopups, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setShowMessagePopups(QT_STATE_CHANGED_T)));
connect(&messagePopups, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setShowMessagePopups);
mentionPopups.setChecked(SettingsCache::instance().getShowMentionPopup());
connect(&mentionPopups, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setShowMentionPopups(QT_STATE_CHANGED_T)));
connect(&mentionPopups, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setShowMentionPopups);
roomHistory.setChecked(SettingsCache::instance().getRoomHistory());
connect(&roomHistory, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setRoomHistory(QT_STATE_CHANGED_T)));
connect(&roomHistory, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), &SettingsCache::setRoomHistory);
customAlertString = new QLineEdit();
customAlertString->setPlaceholderText(tr("Word1 Word2 Word3"));
@ -1095,8 +1105,8 @@ void MessagesSettingsPage::retranslateUi()
SoundSettingsPage::SoundSettingsPage()
{
soundEnabledCheckBox.setChecked(SettingsCache::instance().getSoundEnabled());
connect(&soundEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(),
SLOT(setSoundEnabled(QT_STATE_CHANGED_T)));
connect(&soundEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setSoundEnabled);
QString themeName = SettingsCache::instance().getSoundThemeName();

View file

@ -113,7 +113,7 @@ class UserInterfaceSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
private slots:
void setNotificationEnabled(int);
void setNotificationEnabled(QT_STATE_CHANGED_T);
private:
QCheckBox notificationsEnabledCheckBox;
@ -174,6 +174,8 @@ private:
QPushButton *updateNowButton;
QLabel networkCacheLabel;
QSpinBox networkCacheEdit;
QLabel networkRedirectCacheTtlLabel;
QSpinBox networkRedirectCacheTtlEdit;
QSpinBox pixmapCacheEdit;
QLabel pixmapCacheLabel;
};

View file

@ -13,6 +13,9 @@ enum GraphicsItemType
typeOther = QGraphicsItem::UserType + 6
};
/**
* Parent class of all objects that appear in a game.
*/
class AbstractGraphicsItem : public QObject, public QGraphicsItem
{
Q_OBJECT

View file

@ -3,61 +3,152 @@
#include "card_database.h"
#include "card_item.h"
#include <QDebug>
#include <algorithm>
CardList::CardList(bool _contentsKnown) : QList<CardItem *>(), contentsKnown(_contentsKnown)
{
}
CardItem *CardList::findCard(const int id, const bool remove, int *position)
/**
* @brief Finds the CardItem with the given id in the list.
* If contentsKnown is false, then this just returns the first element of the list.
*
* @param cardId The id of the card to find.
*
* @returns A pointer to the CardItem, or a nullptr if not found.
*/
CardItem *CardList::findCard(const int cardId) const
{
if (!contentsKnown) {
if (empty())
return 0;
CardItem *temp = at(0);
if (remove)
removeAt(0);
if (position)
*position = id;
return temp;
} else
for (int i = 0; i < size(); i++) {
CardItem *temp = at(i);
if (temp->getId() == id) {
if (remove)
removeAt(i);
if (position)
*position = i;
return temp;
if (!contentsKnown && !empty()) {
return at(0);
} else {
for (auto *cardItem : *this) {
if (cardItem->getId() == cardId) {
return cardItem;
}
}
return 0;
}
return nullptr;
}
class CardList::compareFunctor
/**
* @brief sorts the list by using string comparison on properties extracted from the CardItem
* The cards are compared using each property in order.
* If two cards have the same value for a property, then the next property in the list is used.
*
* @param option the option to compare the cards by, in order of usage.
*/
void CardList::sortBy(const QList<SortOption> &option)
{
private:
int flags;
public:
explicit compareFunctor(int _flags) : flags(_flags)
{
// early return if we know we won't be sorting
if (option.isEmpty()) {
return;
}
inline bool operator()(CardItem *a, CardItem *b) const
{
if (flags & SortByType) {
QString t1 = a->getInfo() ? a->getInfo()->getMainCardType() : QString();
QString t2 = b->getInfo() ? b->getInfo()->getMainCardType() : QString();
if ((t1 == t2) && (flags & SortByName))
return a->getName() < b->getName();
return t1 < t2;
} else
return a->getName() < b->getName();
}
};
void CardList::sort(int flags)
{
compareFunctor cf(flags);
std::sort(begin(), end(), cf);
auto comparator = [&option](CardItem *a, CardItem *b) {
for (auto prop : option) {
auto extractor = getExtractorFor(prop);
QString t1 = extractor(a);
QString t2 = extractor(b);
if (t1 != t2) {
return t1 < t2;
}
}
return false;
};
std::sort(begin(), end(), comparator);
}
/**
* Creates a String for the card such that when sorting cards using that string, it will result in the
* following sort order:
* - Unrecognized colors
* - Land cards
* - Colorless cards
* - Monocolor cards, in wubrg order
* - Monocolor cards of any custom colors
* - 2C cards (no internal order)
* - 3C cards (no internal order)
* - 4C cards (no internal order)
* - 5C cards (no internal order)
*
* @param c The card info
* @param appendAtEnd For multicolor cards, whether to also append the entire color string at the end.
*/
static QString getColorSortString(CardInfoPtr c, bool appendAtEnd)
{
QString colors = c->getColors();
switch (colors.size()) {
case 0: {
if (c->getCardType().contains("Land")) {
return "a_land";
} else {
return "b_colorless";
}
}
case 1:
// force wubrg order
switch (colors.at(0).toLatin1()) {
case 'W':
return "c_W";
case 'U':
return "d_U";
case 'B':
return "e_B";
case 'R':
return "f_R";
case 'G':
return "g_G";
default:
// handle any custom colors
return QString("h_%1").arg(colors.at(0));
}
default:
return QString("i%1_%2").arg(colors.size()).arg(appendAtEnd ? colors : "");
}
}
/**
* @brief returns the function that extracts the given property from the CardItem.
*/
std::function<QString(CardItem *)> CardList::getExtractorFor(SortOption option)
{
switch (option) {
case NoSort:
return [](CardItem *) { return ""; };
case SortByMainType:
return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getMainCardType() : ""; };
case SortByManaValue:
// getCmc returns the int as a string. We pad with 0's so that string comp also works on it
return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getCmc().rightJustified(4, '0') : ""; };
case SortByColorGrouping:
return [](CardItem *c) { return c->getInfo() ? getColorSortString(c->getInfo(), false) : ""; };
case SortByName:
return [](CardItem *c) { return c->getName(); };
case SortByType:
return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getCardType() : ""; };
case SortByManaCost:
return [](CardItem *c) {
auto info = c->getInfo();
if (!info)
return QString("");
// calculation copied from CardDatabaseModel.
// we pad the cmc and also append the mana cost to the end so same cmc cards still have a sort order
return QString("%1%2").arg(info->getCmc(), 4, QChar('0')).arg(info->getManaCost());
};
case SortByColors:
return [](CardItem *c) { return c->getInfo() ? getColorSortString(c->getInfo(), true) : ""; };
case SortByPt:
// do the same padding trick as above
return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getPowTough().rightJustified(10, '0') : ""; };
case SortBySet:
return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getSetsNames() : ""; };
}
// this line should never be reached
qDebug() << "cardlist.cpp: Could not find extractor for SortOption" << option;
return [](CardItem *) { return ""; };
}

View file

@ -7,25 +7,39 @@ class CardItem;
class CardList : public QList<CardItem *>
{
private:
class compareFunctor;
protected:
bool contentsKnown;
public:
enum SortFlags
enum SortOption
{
SortByName = 1,
SortByType = 2
NoSort,
// Options that are used by groupBy
// Should partition all cards into a reasonable number of buckets
SortByMainType,
SortByManaValue,
SortByColorGrouping,
// Options that are used by sortBy
// We don't care about buckets; we want as many distinct values as possible.
SortByName,
SortByType,
SortByManaCost,
SortByColors,
SortByPt,
SortBySet
};
CardList(bool _contentsKnown);
CardItem *findCard(const int id, const bool remove, int *position = NULL);
CardItem *findCard(const int cardId) const;
bool getContentsKnown() const
{
return contentsKnown;
}
void sort(int flags = SortByName);
void sortBy(const QList<SortOption> &options);
static std::function<QString(CardItem *)> getExtractorFor(SortOption option);
};
#endif

View file

@ -1893,7 +1893,8 @@ void Player::setCardAttrHelper(const GameEventContext &context,
CardItem *card,
CardAttribute attribute,
const QString &avalue,
bool allCards)
bool allCards,
EventProcessingOptions options)
{
if (card == nullptr) {
return;
@ -1907,7 +1908,8 @@ void Player::setCardAttrHelper(const GameEventContext &context,
if (!allCards) {
emit logSetTapped(this, card, tapped);
}
card->setTapped(tapped, !moveCardContext);
bool canAnimate = !options.testFlag(SKIP_TAP_ANIMATION) && !moveCardContext;
card->setTapped(tapped, canAnimate);
}
break;
}
@ -2049,7 +2051,9 @@ void Player::eventCreateToken(const Event_CreateToken &event)
zone->addCard(card, true, event.x(), event.y());
}
void Player::eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context)
void Player::eventSetCardAttr(const Event_SetCardAttr &event,
const GameEventContext &context,
EventProcessingOptions options)
{
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0);
if (!zone) {
@ -2059,8 +2063,8 @@ void Player::eventSetCardAttr(const Event_SetCardAttr &event, const GameEventCon
if (!event.has_card_id()) {
const CardList &cards = zone->getCards();
for (int i = 0; i < cards.size(); ++i) {
setCardAttrHelper(context, cards.at(i), event.attribute(), QString::fromStdString(event.attr_value()),
true);
setCardAttrHelper(context, cards.at(i), event.attribute(), QString::fromStdString(event.attr_value()), true,
options);
}
if (event.attribute() == AttrTapped) {
emit logSetTapped(this, nullptr, event.attr_value() == "1");
@ -2071,7 +2075,7 @@ void Player::eventSetCardAttr(const Event_SetCardAttr &event, const GameEventCon
qWarning() << "Player::eventSetCardAttr: card id=" << event.card_id() << "not found";
return;
}
setCardAttrHelper(context, card, event.attribute(), QString::fromStdString(event.attr_value()), false);
setCardAttrHelper(context, card, event.attribute(), QString::fromStdString(event.attr_value()), false, options);
}
}
@ -2444,7 +2448,7 @@ void Player::processGameEvent(GameEvent::GameEventType type,
eventCreateToken(event.GetExtension(Event_CreateToken::ext));
break;
case GameEvent::SET_CARD_ATTR:
eventSetCardAttr(event.GetExtension(Event_SetCardAttr::ext), context);
eventSetCardAttr(event.GetExtension(Event_SetCardAttr::ext), context, options);
break;
case GameEvent::SET_CARD_COUNTER:
eventSetCardCounter(event.GetExtension(Event_SetCardCounter::ext));

View file

@ -68,6 +68,9 @@ class ZoneViewZone;
const int MAX_TOKENS_PER_DIALOG = 99;
/**
* The entire graphical area belonging to a single player.
*/
class PlayerArea : public QObject, public QGraphicsItem
{
Q_OBJECT
@ -229,7 +232,8 @@ private slots:
public:
enum EventProcessingOption
{
SKIP_REVEAL_WINDOW = 0x0001
SKIP_REVEAL_WINDOW = 0x0001,
SKIP_TAP_ANIMATION = 0x0002
};
Q_DECLARE_FLAGS(EventProcessingOptions, EventProcessingOption)
@ -301,7 +305,8 @@ private:
CardItem *card,
CardAttribute attribute,
const QString &avalue,
bool allCards);
bool allCards,
EventProcessingOptions options);
void addRelatedCardActions(const CardItem *card, QMenu *cardMenu);
void addRelatedCardView(const CardItem *card, QMenu *cardMenu);
void createCard(const CardItem *sourceCard,
@ -328,7 +333,8 @@ private:
void eventCreateArrow(const Event_CreateArrow &event);
void eventDeleteArrow(const Event_DeleteArrow &event);
void eventCreateToken(const Event_CreateToken &event);
void eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context);
void
eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context, EventProcessingOptions options);
void eventSetCardCounter(const Event_SetCardCounter &event);
void eventCreateCounter(const Event_CreateCounter &event);
void eventSetCounter(const Event_SetCounter &event);

View file

@ -11,6 +11,14 @@
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
/**
* @param _p the player that the zone belongs to
* @param _name internal name of the zone
* @param _isShufflable whether it makes sense to shuffle this zone by default after viewing it
* @param _contentsKnown whether the cards in the zone are known to the client
* @param parent the parent graphics object.
* @param _isView whether this zone is a view of another zone. Modifications to a view should modify the original
*/
CardZone::CardZone(Player *_p,
const QString &_name,
bool _hasCardAttr,
@ -142,7 +150,7 @@ void CardZone::addCard(CardItem *card, bool reorganize, int x, int y)
CardItem *CardZone::getCard(int cardId, const QString &cardName)
{
CardItem *c = cards.findCard(cardId, false);
CardItem *c = cards.findCard(cardId);
if (!c) {
qDebug() << "CardZone::getCard: card id=" << cardId << "not found";
return 0;

View file

@ -14,6 +14,12 @@ class QAction;
class QPainter;
class CardDragItem;
/**
* A zone in the game that can contain cards.
* This class contains methods to get and modify the cards that are contained inside this zone.
*
* The cards are stored as a list of `CardItem*`.
*/
class CardZone : public AbstractGraphicsItem
{
Q_OBJECT

View file

@ -3,6 +3,10 @@
#include "card_zone.h"
/**
* A CardZone where the cards are in a single pile instead of being laid out.
* Usually only top card is accessible by clicking.
*/
class PileZone : public CardZone
{
Q_OBJECT

View file

@ -3,6 +3,9 @@
#include "card_zone.h"
/**
* A CardZone where the cards are laid out, with each card directly interactable by clicking.
*/
class SelectZone : public CardZone
{
Q_OBJECT

View file

@ -16,6 +16,13 @@
#include <QPainter>
#include <QtMath>
/**
* @param _p the player that the cards are revealed to.
* @param _origZone the zone the cards were revealed from.
* @param _revealZone if false, the cards will be face down.
* @param _writeableRevealZone whether the player can interact with the revealed cards.
* @param parent the parent QGraphicsWidget containing the reveal zone
*/
ZoneViewZone::ZoneViewZone(Player *_p,
CardZone *_origZone,
int _numberCards,
@ -24,7 +31,7 @@ ZoneViewZone::ZoneViewZone(Player *_p,
QGraphicsItem *parent)
: SelectZone(_p, _origZone->getName(), false, false, true, parent, true), bRect(QRectF()), minRows(0),
numberCards(_numberCards), origZone(_origZone), revealZone(_revealZone),
writeableRevealZone(_writeableRevealZone), sortByName(false), sortByType(false)
writeableRevealZone(_writeableRevealZone), groupBy(CardList::NoSort), sortBy(CardList::NoSort)
{
if (!(revealZone && !writeableRevealZone)) {
origZone->getViews().append(this);
@ -105,85 +112,127 @@ void ZoneViewZone::reorganizeCards()
for (int i = 0; i < cardCount; ++i)
cards[i]->setId(i);
int cols = qFloor(qSqrt((double)cardCount / 2));
if (cols > 7)
cols = 7;
int rows = qCeil((double)cardCount / cols);
if (rows < 1)
rows = 1;
if (minRows == 0)
minRows = rows;
else if (rows < minRows) {
rows = minRows;
cols = qCeil((double)cardCount / minRows);
}
if (cols < 2)
cols = 2;
qDebug() << "reorganizeCards: rows=" << rows << "cols=" << cols;
CardList cardsToDisplay(cards);
if (sortByName || sortByType)
cardsToDisplay.sort((sortByName ? CardList::SortByName : 0) | (sortByType ? CardList::SortByType : 0));
int typeColumn = 0;
int longestRow = 0;
if (pileView && sortByType) { // we need sort by type enabled for the feature to work
int typeRow = 0;
QString lastCardType;
for (int i = 0; i < cardCount; i++) {
CardItem *c = cardsToDisplay.at(i);
QString cardType = c->getInfo() ? c->getInfo()->getMainCardType() : "";
// sort cards
QList<CardList::SortOption> sortOptions;
if (groupBy != CardList::NoSort) {
sortOptions << groupBy;
}
if (i) { // if not the first card
if (cardType == lastCardType)
typeRow++; // add below current card
else { // if no match then move card to next column
typeColumn++;
typeRow = 0;
}
}
if (sortBy != CardList::NoSort) {
sortOptions << sortBy;
lastCardType = cardType;
qreal x = 7 + (typeColumn * CARD_WIDTH);
qreal y = typeRow * CARD_HEIGHT / 3;
c->setPos(x + 5, y + 5);
c->setRealZValue(i);
longestRow = qMax(typeRow, longestRow);
}
} else {
for (int i = 0; i < cardCount; i++) {
CardItem *c = cardsToDisplay.at(i);
qreal x = 7 + ((i / rows) * CARD_WIDTH);
qreal y = (i % rows) * CARD_HEIGHT / 3;
c->setPos(x + 5, y + 5);
c->setRealZValue(i);
// implicitly sort by name at the end so that cards with the same name appear together
if (sortBy != CardList::SortByName) {
sortOptions << CardList::SortByName;
}
}
cardsToDisplay.sortBy(sortOptions);
// position cards
GridSize gridSize;
if (pileView) {
gridSize = positionCardsForDisplay(cardsToDisplay, groupBy);
} else {
gridSize = positionCardsForDisplay(cardsToDisplay);
}
// determine bounding rect
qreal aleft = 0;
qreal atop = 0;
qreal awidth = (pileView && sortByType) ? qMax(typeColumn + 1, 3) * CARD_WIDTH + (CARD_WIDTH / 2)
: qMax(cols, 1) * CARD_WIDTH + (CARD_WIDTH / 2);
qreal aheight = (pileView && sortByType) ? (longestRow * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3
: (rows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3;
qreal awidth = gridSize.cols * CARD_WIDTH + (CARD_WIDTH / 2) + HORIZONTAL_PADDING;
qreal aheight = (gridSize.rows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3;
optimumRect = QRectF(aleft, atop, awidth, aheight);
updateGeometry();
emit optimumRectChanged();
}
void ZoneViewZone::setSortByName(int _sortByName)
/**
* @brief Sets the position of each card to the proper position for the view
*
* @param cards The cards to reposition. Will modify the cards in the list.
* @param pileOption Property used to group cards for the piles. Expects `cards` to be sorted by that property. Pass in
* NoSort to not make piles.
*
* @returns The number of rows and columns to display
*/
ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, CardList::SortOption pileOption)
{
sortByName = _sortByName;
int cardCount = cards.size();
if (pileOption != CardList::NoSort) {
int row = 0;
int col = 0;
int longestRow = 0;
QString lastColumnProp;
const auto extractor = CardList::getExtractorFor(pileOption);
for (int i = 0; i < cardCount; i++) {
CardItem *c = cards.at(i);
QString columnProp = extractor(c);
if (i) { // if not the first card
if (columnProp == lastColumnProp)
row++; // add below current card
else { // if no match then move card to next column
col++;
row = 0;
}
}
lastColumnProp = columnProp;
qreal x = col * CARD_WIDTH;
qreal y = row * CARD_HEIGHT / 3;
c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
c->setRealZValue(i);
longestRow = qMax(row, longestRow);
}
// +1 because the row/col variables used in the calculations are 0-indexed but
// GridSize expects the actual row/col count
return GridSize{longestRow + 1, qMax(col + 1, 3)};
} else {
int cols = qBound(1, qFloor(qSqrt((double)cardCount / 2)), 7);
int rows = qMax(qCeil((double)cardCount / cols), 1);
if (minRows == 0) {
minRows = rows;
} else if (rows < minRows) {
rows = minRows;
cols = qCeil((double)cardCount / minRows);
}
if (cols < 2)
cols = 2;
qDebug() << "reorganizeCards: rows=" << rows << "cols=" << cols;
for (int i = 0; i < cardCount; i++) {
CardItem *c = cards.at(i);
qreal x = (i / rows) * CARD_WIDTH;
qreal y = (i % rows) * CARD_HEIGHT / 3;
c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
c->setRealZValue(i);
}
return GridSize{rows, qMax(cols, 1)};
}
}
void ZoneViewZone::setGroupBy(CardList::SortOption _groupBy)
{
groupBy = _groupBy;
reorganizeCards();
}
void ZoneViewZone::setSortByType(int _sortByType)
void ZoneViewZone::setSortBy(CardList::SortOption _sortBy)
{
sortByType = _sortByType;
if (!sortByType)
pileView = false;
sortBy = _sortBy;
reorganizeCards();
}

View file

@ -10,19 +10,39 @@ class Response;
class ServerInfo_Card;
class QGraphicsSceneWheelEvent;
/**
* A CardZone that is a view into another CardZone.
* If this zone is writable, then modifications to this zone will be reflected in the underlying zone.
*
* Most interactions with StackZones are handled through a zone view.
* For example, viewing the deck/graveyard/exile is handled through a view.
*
* Handles both limited reveals (eg. look at top X cards) and full zone reveals (eg. searching the deck).
*/
class ZoneViewZone : public SelectZone, public QGraphicsLayoutItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsLayoutItem)
private:
static constexpr int HORIZONTAL_PADDING = 12;
static constexpr int VERTICAL_PADDING = 5;
QRectF bRect, optimumRect;
int minRows, numberCards;
void handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone *startZone, const QPoint &dropPoint);
CardZone *origZone;
bool revealZone, writeableRevealZone;
bool sortByName, sortByType;
CardList::SortOption groupBy, sortBy;
bool pileView;
struct GridSize
{
int rows;
int cols;
};
GridSize positionCardsForDisplay(CardList &cards, CardList::SortOption pileOption = CardList::NoSort);
public:
ZoneViewZone(Player *_p,
CardZone *_origZone,
@ -55,8 +75,8 @@ public:
}
void setWriteableRevealZone(bool _writeableRevealZone);
public slots:
void setSortByName(int _sortByName);
void setSortByType(int _sortByType);
void setGroupBy(CardList::SortOption _groupBy);
void setSortBy(CardList::SortOption _sortBy);
void setPileView(int _pileView);
private slots:
void zoneDumpReceived(const Response &r);

View file

@ -17,6 +17,15 @@
#include <QStyleOption>
#include <QStyleOptionTitleBar>
/**
* @param _player the player the cards were revealed to.
* @param _origZone the zone the cards were revealed from.
* @param numberCards num of cards to reveal from the zone. Ex: scry the top 3 cards.
* Pass in a negative number to reveal the entire zone.
* -1 specifically will give the option to shuffle the zone upon closing the window.
* @param _revealZone if false, the cards will be face down.
* @param _writeableRevealZone whether the player can interact with the revealed cards.
*/
ZoneViewWidget::ZoneViewWidget(Player *_player,
CardZone *_origZone,
int numberCards,
@ -31,22 +40,27 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
setFlag(ItemIgnoresTransformations);
QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical);
QGraphicsLinearLayout *hPilebox = 0;
// If the number is < 0, then it means that we can give the option to make the area sorted
if (numberCards < 0) {
hPilebox = new QGraphicsLinearLayout(Qt::Horizontal);
QGraphicsLinearLayout *hFilterbox = new QGraphicsLinearLayout(Qt::Horizontal);
// top row
QGraphicsLinearLayout *hTopRow = new QGraphicsLinearLayout(Qt::Horizontal);
QGraphicsProxyWidget *sortByNameProxy = new QGraphicsProxyWidget;
sortByNameProxy->setWidget(&sortByNameCheckBox);
hFilterbox->addItem(sortByNameProxy);
// groupBy options
QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget;
groupBySelectorProxy->setWidget(&groupBySelector);
groupBySelectorProxy->setZValue(2000000008);
hTopRow->addItem(groupBySelectorProxy);
QGraphicsProxyWidget *sortByTypeProxy = new QGraphicsProxyWidget;
sortByTypeProxy->setWidget(&sortByTypeCheckBox);
hFilterbox->addItem(sortByTypeProxy);
// sortBy options
QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget;
sortBySelectorProxy->setWidget(&sortBySelector);
sortBySelectorProxy->setZValue(2000000007);
hTopRow->addItem(sortBySelectorProxy);
vbox->addItem(hFilterbox);
vbox->addItem(hTopRow);
// line
QGraphicsProxyWidget *lineProxy = new QGraphicsProxyWidget;
QFrame *line = new QFrame;
line->setFrameShape(QFrame::HLine);
@ -54,19 +68,24 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
lineProxy->setWidget(line);
vbox->addItem(lineProxy);
// bottom row
QGraphicsLinearLayout *hBottomRow = new QGraphicsLinearLayout(Qt::Horizontal);
// pile view options
QGraphicsProxyWidget *pileViewProxy = new QGraphicsProxyWidget;
pileViewProxy->setWidget(&pileViewCheckBox);
hPilebox->addItem(pileViewProxy);
}
hBottomRow->addItem(pileViewProxy);
if (_origZone->getIsShufflable() && (numberCards == -1)) {
shuffleCheckBox.setChecked(true);
QGraphicsProxyWidget *shuffleProxy = new QGraphicsProxyWidget;
shuffleProxy->setWidget(&shuffleCheckBox);
hPilebox->addItem(shuffleProxy);
}
// shuffle options
if (_origZone->getIsShufflable() && numberCards == -1) {
shuffleCheckBox.setChecked(true);
QGraphicsProxyWidget *shuffleProxy = new QGraphicsProxyWidget;
shuffleProxy->setWidget(&shuffleCheckBox);
hBottomRow->addItem(shuffleProxy);
}
vbox->addItem(hPilebox);
vbox->addItem(hBottomRow);
}
extraHeight = vbox->sizeHint(Qt::PreferredSize).height();
resize(150, 150);
@ -93,42 +112,71 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
connect(zone, SIGNAL(wheelEventReceived(QGraphicsSceneWheelEvent *)), scrollBarProxy,
SLOT(recieveWheelEvent(QGraphicsSceneWheelEvent *)));
// numberCard is the num of cards we want to reveal from an area. Ex: scry the top 3 cards.
// If the number is < 0 then it means that we can make the area sorted and we dont care about the order.
retranslateUi();
// only wire up sort options after creating ZoneViewZone, since it segfaults otherwise.
if (numberCards < 0) {
connect(&sortByNameCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this,
SLOT(processSortByName(QT_STATE_CHANGED_T)));
connect(&sortByTypeCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this,
SLOT(processSortByType(QT_STATE_CHANGED_T)));
connect(&pileViewCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this,
SLOT(processSetPileView(QT_STATE_CHANGED_T)));
sortByNameCheckBox.setChecked(SettingsCache::instance().getZoneViewSortByName());
sortByTypeCheckBox.setChecked(SettingsCache::instance().getZoneViewSortByType());
connect(&groupBySelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ZoneViewWidget::processGroupBy);
connect(&sortBySelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ZoneViewWidget::processSortBy);
connect(&pileViewCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSetPileView);
groupBySelector.setCurrentIndex(SettingsCache::instance().getZoneViewGroupByIndex());
sortBySelector.setCurrentIndex(SettingsCache::instance().getZoneViewSortByIndex());
pileViewCheckBox.setChecked(SettingsCache::instance().getZoneViewPileView());
if (!SettingsCache::instance().getZoneViewSortByType())
if (CardList::NoSort == static_cast<CardList::SortOption>(groupBySelector.currentData().toInt())) {
pileViewCheckBox.setEnabled(false);
}
}
retranslateUi();
setLayout(vbox);
connect(zone, SIGNAL(optimumRectChanged()), this, SLOT(resizeToZoneContents()));
connect(zone, SIGNAL(beingDeleted()), this, SLOT(zoneDeleted()));
zone->initializeCards(cardList);
// QLabel sizes aren't taken into account until the widget is rendered.
// Force refresh after 1ms to fix glitchy rendering with long QLabels.
auto *lastResizeBeforeVisibleTimer = new QTimer(this);
connect(lastResizeBeforeVisibleTimer, &QTimer::timeout, this, [=] {
resizeToZoneContents();
disconnect(lastResizeBeforeVisibleTimer);
lastResizeBeforeVisibleTimer->deleteLater();
});
lastResizeBeforeVisibleTimer->setSingleShot(true);
lastResizeBeforeVisibleTimer->start(1);
}
void ZoneViewWidget::processSortByType(QT_STATE_CHANGED_T value)
void ZoneViewWidget::processGroupBy(int index)
{
pileViewCheckBox.setEnabled(value);
SettingsCache::instance().setZoneViewSortByType(value);
zone->setPileView(pileViewCheckBox.isChecked());
zone->setSortByType(value);
auto option = static_cast<CardList::SortOption>(groupBySelector.itemData(index).toInt());
SettingsCache::instance().setZoneViewGroupByIndex(index);
zone->setGroupBy(option);
// disable pile view checkbox if we're not grouping by anything
pileViewCheckBox.setEnabled(option != CardList::NoSort);
// reset sortBy if it has the same value as groupBy
if (option != CardList::NoSort &&
option == static_cast<CardList::SortOption>(sortBySelector.currentData().toInt())) {
sortBySelector.setCurrentIndex(1); // set to SortByName
}
}
void ZoneViewWidget::processSortByName(QT_STATE_CHANGED_T value)
void ZoneViewWidget::processSortBy(int index)
{
SettingsCache::instance().setZoneViewSortByName(value);
zone->setSortByName(value);
auto option = static_cast<CardList::SortOption>(sortBySelector.itemData(index).toInt());
// set to SortByName instead if it has the same value as groupBy
if (option != CardList::NoSort &&
option == static_cast<CardList::SortOption>(groupBySelector.currentData().toInt())) {
sortBySelector.setCurrentIndex(1); // set to SortByName
return;
}
SettingsCache::instance().setZoneViewSortByIndex(index);
zone->setSortBy(option);
}
void ZoneViewWidget::processSetPileView(QT_STATE_CHANGED_T value)
@ -140,8 +188,30 @@ void ZoneViewWidget::processSetPileView(QT_STATE_CHANGED_T value)
void ZoneViewWidget::retranslateUi()
{
setWindowTitle(zone->getTranslatedName(false, CaseNominative));
sortByNameCheckBox.setText(tr("sort by name"));
sortByTypeCheckBox.setText(tr("sort by type"));
{ // We can't change the strings after they're put into the QComboBox, so this is our workaround
int oldIndex = groupBySelector.currentIndex();
groupBySelector.clear();
groupBySelector.addItem(tr("Group by ---"), CardList::NoSort);
groupBySelector.addItem(tr("Group by Type"), CardList::SortByMainType);
groupBySelector.addItem(tr("Group by Mana Value"), CardList::SortByManaValue);
groupBySelector.addItem(tr("Group by Color"), CardList::SortByColorGrouping);
groupBySelector.setCurrentIndex(oldIndex);
}
{
int oldIndex = sortBySelector.currentIndex();
sortBySelector.clear();
sortBySelector.addItem(tr("Sort by ---"), CardList::NoSort);
sortBySelector.addItem(tr("Sort by Name"), CardList::SortByName);
sortBySelector.addItem(tr("Sort by Type"), CardList::SortByType);
sortBySelector.addItem(tr("Sort by Mana Cost"), CardList::SortByManaCost);
sortBySelector.addItem(tr("Sort by Colors"), CardList::SortByColors);
sortBySelector.addItem(tr("Sort by P/T"), CardList::SortByPt);
sortBySelector.addItem(tr("Sort by Set"), CardList::SortBySet);
sortBySelector.setCurrentIndex(oldIndex);
}
shuffleCheckBox.setText(tr("shuffle when closing"));
pileViewCheckBox.setText(tr("pile view"));
}

View file

@ -4,6 +4,7 @@
#include "../../utility/macros.h"
#include <QCheckBox>
#include <QComboBox>
#include <QGraphicsProxyWidget>
#include <QGraphicsWidget>
@ -14,7 +15,6 @@ class ZoneViewZone;
class Player;
class CardDatabase;
class QScrollBar;
class QCheckBox;
class GameScene;
class ServerInfo_Card;
class QGraphicsSceneMouseEvent;
@ -31,6 +31,12 @@ public slots:
}
};
/**
* A QGraphicsWidget that holds a ZoneViewZone.
*
* Some zone views allow sorting.
* This widget will display the sort options when relevant, and forward the values of the options to the ZoneViewZone.
*/
class ZoneViewWidget : public QGraphicsWidget
{
Q_OBJECT
@ -41,8 +47,8 @@ private:
QPushButton *closeButton;
QScrollBar *scrollBar;
ScrollableGraphicsProxyWidget *scrollBarProxy;
QCheckBox sortByNameCheckBox;
QCheckBox sortByTypeCheckBox;
QComboBox groupBySelector;
QComboBox sortBySelector;
QCheckBox shuffleCheckBox;
QCheckBox pileViewCheckBox;
@ -52,8 +58,8 @@ private:
signals:
void closePressed(ZoneViewWidget *zv);
private slots:
void processSortByType(QT_STATE_CHANGED_T value);
void processSortByName(QT_STATE_CHANGED_T value);
void processGroupBy(int value);
void processSortBy(int value);
void processSetPileView(QT_STATE_CHANGED_T value);
void resizeToZoneContents();
void handleScrollBarChange(int value);

View file

@ -221,6 +221,7 @@ SettingsCache::SettingsCache()
pixmapCacheSize = PIXMAPCACHE_SIZE_DEFAULT;
networkCacheSize = settings->value("personal/networkCacheSize", NETWORK_CACHE_SIZE_DEFAULT).toInt();
redirectCacheTtl = settings->value("personal/redirectCacheTtl", NETWORK_REDIRECT_CACHE_TTL_DEFAULT).toInt();
picDownload = settings->value("personal/picturedownload", true).toBool();
@ -251,8 +252,8 @@ SettingsCache::SettingsCache()
chatMentionColor = settings->value("chat/mentioncolor", "A6120D").toString();
chatHighlightColor = settings->value("chat/highlightcolor", "A6120D").toString();
zoneViewSortByName = settings->value("zoneview/sortbyname", true).toBool();
zoneViewSortByType = settings->value("zoneview/sortbytype", true).toBool();
zoneViewGroupByIndex = settings->value("zoneview/groupby", 1).toInt();
zoneViewSortByIndex = settings->value("zoneview/sortby", 1).toInt();
zoneViewPileView = settings->value("zoneview/pileview", true).toBool();
soundEnabled = settings->value("sound/enabled", false).toBool();
@ -581,16 +582,16 @@ void SettingsCache::setChatHighlightColor(const QString &_chatHighlightColor)
settings->setValue("chat/highlightcolor", chatHighlightColor);
}
void SettingsCache::setZoneViewSortByName(QT_STATE_CHANGED_T _zoneViewSortByName)
void SettingsCache::setZoneViewGroupByIndex(int _zoneViewGroupByIndex)
{
zoneViewSortByName = static_cast<bool>(_zoneViewSortByName);
settings->setValue("zoneview/sortbyname", zoneViewSortByName);
zoneViewGroupByIndex = _zoneViewGroupByIndex;
settings->setValue("zoneview/groupby", zoneViewGroupByIndex);
}
void SettingsCache::setZoneViewSortByType(QT_STATE_CHANGED_T _zoneViewSortByType)
void SettingsCache::setZoneViewSortByIndex(int _zoneViewSortByIndex)
{
zoneViewSortByType = static_cast<bool>(_zoneViewSortByType);
settings->setValue("zoneview/sortbytype", zoneViewSortByType);
zoneViewSortByIndex = _zoneViewSortByIndex;
settings->setValue("zoneview/sortby", zoneViewSortByIndex);
}
void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T _zoneViewPileView)
@ -657,6 +658,13 @@ void SettingsCache::setNetworkCacheSizeInMB(const int _networkCacheSize)
emit networkCacheSizeChanged(networkCacheSize);
}
void SettingsCache::setNetworkRedirectCacheTtl(const int _redirectCacheTtl)
{
redirectCacheTtl = _redirectCacheTtl;
settings->setValue("personal/redirectCacheSize", redirectCacheTtl);
emit redirectCacheTtlChanged(redirectCacheTtl);
}
void SettingsCache::setClientID(const QString &_clientID)
{
clientID = _clientID;
@ -1030,6 +1038,7 @@ void SettingsCache::loadPaths()
replaysPath = getSafeConfigPath("paths/replays", dataPath + "/replays/");
themesPath = getSafeConfigPath("paths/themes", dataPath + "/themes/");
picsPath = getSafeConfigPath("paths/pics", dataPath + "/pics/");
redirectCachePath = getSafeConfigPath("paths/redirects", getCachePath() + "/redirects/");
// this has never been exposed as an user-configurable setting
if (picsPath.endsWith("/")) {
customPicsPath = getSafeConfigPath("paths/custompics", picsPath + "CUSTOM/");

View file

@ -16,16 +16,21 @@
class ReleaseChannel;
// size should be a multiple of 64
#define PIXMAPCACHE_SIZE_DEFAULT 2047
// In MB (Increments of 64)
#define PIXMAPCACHE_SIZE_DEFAULT 2048
#define PIXMAPCACHE_SIZE_MIN 64
#define PIXMAPCACHE_SIZE_MAX 2047
#define PIXMAPCACHE_SIZE_MAX 4096
// In MB
constexpr int NETWORK_CACHE_SIZE_DEFAULT = 1024 * 4; // 4 GB
constexpr int NETWORK_CACHE_SIZE_MIN = 1; // 1 MB
constexpr int NETWORK_CACHE_SIZE_MAX = 1024 * 1024; // 1 TB
// In Days
#define NETWORK_REDIRECT_CACHE_TTL_DEFAULT 30
#define NETWORK_REDIRECT_CACHE_TTL_MIN 1
#define NETWORK_REDIRECT_CACHE_TTL_MAX 90
#define DEFAULT_LANG_NAME "English"
#define CLIENT_INFO_NOT_SET "notset"
@ -53,6 +58,7 @@ signals:
void ignoreUnregisteredUserMessagesChanged();
void pixmapCacheSizeChanged(int newSizeInMBs);
void networkCacheSizeChanged(int newSizeInMBs);
void redirectCacheTtlChanged(int newTtl);
void masterVolumeChanged(int value);
void chatMentionCompleterChanged();
void downloadSpoilerTimeIndexChanged();
@ -73,8 +79,8 @@ private:
QByteArray tokenDialogGeometry;
QByteArray setsDialogGeometry;
QString lang;
QString deckPath, replaysPath, picsPath, customPicsPath, cardDatabasePath, customCardDatabasePath, themesPath,
spoilerDatabasePath, tokenDatabasePath, themeName;
QString deckPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath,
customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName;
bool notifyAboutUpdates;
bool notifyAboutNewVersion;
bool showTipsOnStartup;
@ -103,7 +109,8 @@ private:
QString chatHighlightColor;
bool chatMentionForeground;
bool chatHighlightForeground;
bool zoneViewSortByName, zoneViewSortByType, zoneViewPileView;
int zoneViewSortByIndex, zoneViewGroupByIndex;
bool zoneViewPileView;
bool soundEnabled;
QString soundThemeName;
bool ignoreUnregisteredUsers;
@ -116,6 +123,7 @@ private:
bool useTearOffMenus;
int pixmapCacheSize;
int networkCacheSize;
int redirectCacheTtl;
bool scaleCards;
int verticalCardOverlapPercent;
bool showMessagePopups;
@ -183,6 +191,10 @@ public:
{
return picsPath;
}
QString getRedirectCachePath() const
{
return redirectCachePath;
}
QString getCustomPicsPath() const
{
return customPicsPath;
@ -316,13 +328,19 @@ public:
{
return chatHighlightForeground;
}
bool getZoneViewSortByName() const
/**
* Currently selected index for the `Group by X` QComboBox
*/
int getZoneViewGroupByIndex() const
{
return zoneViewSortByName;
return zoneViewGroupByIndex;
}
bool getZoneViewSortByType() const
/**
* Currently selected index for the `Sort by X` QComboBox
*/
int getZoneViewSortByIndex() const
{
return zoneViewSortByType;
return zoneViewSortByIndex;
}
/**
Returns if the view should be sorted into pile view.
@ -356,6 +374,10 @@ public:
{
return networkCacheSize;
}
int getRedirectCacheTtl() const
{
return redirectCacheTtl;
}
bool getScaleCards() const
{
return scaleCards;
@ -548,8 +570,8 @@ public slots:
void setChatMentionCompleter(QT_STATE_CHANGED_T _chatMentionCompleter);
void setChatMentionForeground(QT_STATE_CHANGED_T _chatMentionForeground);
void setChatHighlightForeground(QT_STATE_CHANGED_T _chatHighlightForeground);
void setZoneViewSortByName(QT_STATE_CHANGED_T _zoneViewSortByName);
void setZoneViewSortByType(QT_STATE_CHANGED_T _zoneViewSortByType);
void setZoneViewGroupByIndex(const int _zoneViewGroupByIndex);
void setZoneViewSortByIndex(const int _zoneViewSortByIndex);
void setZoneViewPileView(QT_STATE_CHANGED_T _zoneViewPileView);
void setSoundEnabled(QT_STATE_CHANGED_T _soundEnabled);
void setSoundThemeName(const QString &_soundThemeName);
@ -557,6 +579,7 @@ public slots:
void setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T _ignoreUnregisteredUserMessages);
void setPixmapCacheSize(const int _pixmapCacheSize);
void setNetworkCacheSizeInMB(const int _networkCacheSize);
void setNetworkRedirectCacheTtl(const int _redirectCacheTtl);
void setCardScaling(const QT_STATE_CHANGED_T _scaleCards);
void setStackCardOverlapPercent(const int _verticalCardOverlapPercent);
void setShowMessagePopups(const QT_STATE_CHANGED_T _showMessagePopups);

View file

@ -2,56 +2,28 @@
#include "settings_manager.h"
const QStringList DownloadSettings::DEFAULT_DOWNLOAD_URLS = {
"https://api.scryfall.com/cards/!set:uuid!?format=image&face=!prop:side!",
"https://api.scryfall.com/cards/multiverse/!set:muid!?format=image",
"https://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!set:muid!&type=card",
"https://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card"};
DownloadSettings::DownloadSettings(const QString &settingPath, QObject *parent = nullptr)
: SettingsManager(settingPath + "downloads.ini", parent)
{
downloadURLs = getValue("urls", "downloads").value<QStringList>();
}
void DownloadSettings::setDownloadUrlAt(int index, const QString &url)
void DownloadSettings::setDownloadUrls(const QStringList &downloadURLs)
{
downloadURLs.insert(index, url);
setValue(QVariant::fromValue(downloadURLs), "urls", "downloads");
}
/**
* If reset or first run, this method contains the default URLs we will populate
*/
QStringList DownloadSettings::getAllURLs()
{
// First run, these will be empty
if (downloadURLs.count() == 0) {
populateDefaultURLs();
}
return downloadURLs;
return getValue("urls", "downloads").toStringList();
}
void DownloadSettings::populateDefaultURLs()
void DownloadSettings::resetToDefaultURLs()
{
downloadURLs.clear();
downloadURLs.append("https://api.scryfall.com/cards/!set:uuid!?format=image&face=!prop:side!");
downloadURLs.append("https://api.scryfall.com/cards/multiverse/!set:muid!?format=image");
downloadURLs.append("https://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!set:muid!&type=card");
downloadURLs.append("https://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card");
setValue(QVariant::fromValue(downloadURLs), "urls", "downloads");
setValue(QVariant::fromValue(DEFAULT_DOWNLOAD_URLS), "urls", "downloads");
}
QString DownloadSettings::getDownloadUrlAt(int index)
{
if (0 <= index && index < downloadURLs.size()) {
return downloadURLs[index];
}
return "";
}
int DownloadSettings::getCount()
{
return downloadURLs.size();
}
void DownloadSettings::clear()
{
downloadURLs.clear();
}

View file

@ -10,20 +10,14 @@ class DownloadSettings : public SettingsManager
Q_OBJECT
friend class SettingsCache;
static const QStringList DEFAULT_DOWNLOAD_URLS;
public:
explicit DownloadSettings(const QString &, QObject *);
QStringList getAllURLs();
QString getDownloadUrlAt(int);
void setDownloadUrlAt(int, const QString &);
int getCount();
void clear();
private:
QStringList downloadURLs;
private:
void populateDefaultURLs();
void setDownloadUrls(const QStringList &downloadURLs);
void resetToDefaultURLs();
};
#endif // COCKATRICE_DOWNLOADSETTINGS_H

View file

@ -45,7 +45,7 @@
<message>
<location filename="src/dialogs/dlg_settings.cpp" line="416"/>
<source>Open themes folder</source>
<translation>Open thema folder</translation>
<translation>Open themafolder</translation>
</message>
<message>
<location filename="src/dialogs/dlg_settings.cpp" line="418"/>
@ -55,7 +55,7 @@
<message>
<location filename="src/dialogs/dlg_settings.cpp" line="419"/>
<source>Display card names on cards having a picture</source>
<translation>Kaartnamen altijd weergeven</translation>
<translation>Kaartnamen altijd weergeven op kaarten met een afbeelding</translation>
</message>
<message>
<location filename="src/dialogs/dlg_settings.cpp" line="420"/>
@ -7063,7 +7063,7 @@ Gelieve af te zien van deelname aan deze activiteit of er kunnen verdere acties
<message>
<location filename="src/settings/shortcuts_settings.h" line="73"/>
<source>Replays</source>
<translation type="unfinished"/>
<translation>Replays</translation>
</message>
<message>
<location filename="src/settings/shortcuts_settings.h" line="143"/>
@ -7749,12 +7749,12 @@ Gelieve af te zien van deelname aan deze activiteit of er kunnen verdere acties
<message>
<location filename="src/settings/shortcuts_settings.h" line="593"/>
<source>Play/Pause</source>
<translation type="unfinished"/>
<translation>Start/Stop</translation>
</message>
<message>
<location filename="src/settings/shortcuts_settings.h" line="596"/>
<source>Toggle Fast Forward</source>
<translation type="unfinished"/>
<translation>Versnelling Schakelen</translation>
</message>
</context>
</TS>

View file

@ -184,10 +184,10 @@ void SettingsCache::setChatMentionColor(const QString & /* _chatMentionColor */)
void SettingsCache::setChatHighlightColor(const QString & /* _chatHighlightColor */)
{
}
void SettingsCache::setZoneViewSortByName(QT_STATE_CHANGED_T /* _zoneViewSortByName */)
void SettingsCache::setZoneViewGroupByIndex(int /* _zoneViewGroupByIndex */)
{
}
void SettingsCache::setZoneViewSortByType(QT_STATE_CHANGED_T /* _zoneViewSortByType */)
void SettingsCache::setZoneViewSortByIndex(int /* _zoneViewSortByIndex */)
{
}
void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T /* _zoneViewPileView */)
@ -220,6 +220,9 @@ void SettingsCache::setPixmapCacheSize(const int /* _pixmapCacheSize */)
void SettingsCache::setNetworkCacheSizeInMB(const int /* _networkCacheSize */)
{
}
void SettingsCache::setNetworkRedirectCacheTtl(const int /* _redirectCacheTtl */)
{
}
void SettingsCache::setClientID(const QString & /* _clientID */)
{
}

View file

@ -188,10 +188,10 @@ void SettingsCache::setChatMentionColor(const QString & /* _chatMentionColor */)
void SettingsCache::setChatHighlightColor(const QString & /* _chatHighlightColor */)
{
}
void SettingsCache::setZoneViewSortByName(QT_STATE_CHANGED_T /* _zoneViewSortByName */)
void SettingsCache::setZoneViewGroupByIndex(int /* _zoneViewGroupByIndex */)
{
}
void SettingsCache::setZoneViewSortByType(QT_STATE_CHANGED_T /* _zoneViewSortByType */)
void SettingsCache::setZoneViewSortByIndex(int /* _zoneViewSortByIndex */)
{
}
void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T /* _zoneViewPileView */)
@ -224,6 +224,9 @@ void SettingsCache::setPixmapCacheSize(const int /* _pixmapCacheSize */)
void SettingsCache::setNetworkCacheSizeInMB(const int /* _networkCacheSize */)
{
}
void SettingsCache::setNetworkRedirectCacheTtl(const int /* _redirectCacheTtl */)
{
}
void SettingsCache::setClientID(const QString & /* _clientID */)
{
}

View file

@ -7006,9 +7006,9 @@
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@ -25082,9 +25082,9 @@
}
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",