mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2025-12-21 06:42:41 -08:00
Compare commits
17 Commits
2024-12-19
...
2024-12-21
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07ee271478 | ||
|
|
4823cce622 | ||
|
|
23099f7e8b | ||
|
|
5bdbd51fa8 | ||
|
|
a0e5871c6e | ||
|
|
3cf0904651 | ||
|
|
2bd06ff0fd | ||
|
|
6ea333d0f1 | ||
|
|
91d2485940 | ||
|
|
0d99b2bcf4 | ||
|
|
a54a424f84 | ||
|
|
3514699f5b | ||
|
|
d196988cab | ||
|
|
03aff83135 | ||
|
|
17e6bfaca6 | ||
|
|
90281262be | ||
|
|
5bbc118920 |
@@ -81,9 +81,9 @@ if(NOT DEFINED GIT_TAG_RELEASENAME)
|
||||
set(GIT_TAG_RELEASENAME "Rings of the Wild")
|
||||
endif()
|
||||
|
||||
# Use c++17 for all targets
|
||||
# Use c++20 for all targets
|
||||
set(CMAKE_CXX_STANDARD
|
||||
17
|
||||
20
|
||||
CACHE STRING "C++ ISO Standard"
|
||||
)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
@@ -140,12 +140,14 @@ endif()
|
||||
|
||||
# Define proper compilation flags
|
||||
if(MSVC)
|
||||
# Visual Studio: Disable Warning C4251, C++17 compatibility, Multi-threaded Builds, Warn Detection, Unwind Semantics
|
||||
set(CMAKE_CXX_FLAGS "/wd4251 /Zc:__cplusplus /std:c++17 /permissive- /W4 /MP /EHsc")
|
||||
# Visual Studio: Disable Warning C4251, C++20 compatibility, Multi-threaded Builds, Warn Detection, Unwind Semantics
|
||||
set(CMAKE_CXX_FLAGS "/wd4251 /Zc:__cplusplus /std:c++20 /permissive- /W4 /MP /EHsc")
|
||||
# Visual Studio: Maximum Optimization, Multi-threaded DLL
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD")
|
||||
# Visual Studio: No Optimization, Multi-threaded Debug DLL, Debug Symbols
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Od /MDd /Zi")
|
||||
|
||||
add_compile_definitions(_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING)
|
||||
elseif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
# linux/gcc, bsd/gcc, windows/mingw
|
||||
include(CheckCXXCompilerFlag)
|
||||
@@ -158,7 +160,7 @@ elseif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++20")
|
||||
endif()
|
||||
|
||||
set(ADDITIONAL_DEBUG_FLAGS
|
||||
|
||||
@@ -235,6 +235,13 @@ ${If} $PortableMode = 0
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
|
||||
; Enable Windows User-Mode Dumps
|
||||
; https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\cockatrice.exe" "DumpFolder" "%LOCALAPPDATA%\CrashDumps\Cockatrice"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\cockatrice.exe" "DumpCount" "5"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\cockatrice.exe" "DumpType" "2"
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayIcon" "$INSTDIR\cockatrice.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayName" "Cockatrice"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayVersion" "@CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@"
|
||||
|
||||
@@ -126,6 +126,7 @@ set(cockatrice_SOURCES
|
||||
src/settings/settings_manager.cpp
|
||||
src/settings/cache_settings.cpp
|
||||
src/settings/shortcuts_settings.cpp
|
||||
src/settings/shortcut_treeview.cpp
|
||||
src/client/sound_engine.cpp
|
||||
src/client/network/spoiler_background_updater.cpp
|
||||
src/game/zones/stack_zone.cpp
|
||||
|
||||
@@ -52,7 +52,7 @@ AbstractClient::AbstractClient(QObject *parent)
|
||||
FeatureSet features;
|
||||
features.initalizeFeatureList(clientFeatures);
|
||||
|
||||
connect(this, SIGNAL(sigQueuePendingCommand(PendingCommand *)), this, SLOT(queuePendingCommand(PendingCommand *)));
|
||||
connect(this, &AbstractClient::sigQueuePendingCommand, this, &AbstractClient::queuePendingCommand);
|
||||
}
|
||||
|
||||
AbstractClient::~AbstractClient()
|
||||
|
||||
@@ -38,7 +38,7 @@ void ReleaseChannel::checkForUpdates()
|
||||
QString releaseChannelUrl = getReleaseChannelUrl();
|
||||
qDebug() << "Searching for updates on the channel: " << releaseChannelUrl;
|
||||
response = netMan->get(QNetworkRequest(releaseChannelUrl));
|
||||
connect(response, SIGNAL(finished()), this, SLOT(releaseListFinished()));
|
||||
connect(response, &QNetworkReply::finished, this, &ReleaseChannel::releaseListFinished);
|
||||
}
|
||||
|
||||
// Different release channel checking functions for different operating systems
|
||||
@@ -158,7 +158,7 @@ void StableReleaseChannel::releaseListFinished()
|
||||
QString url = QString(STABLETAG_URL) + tagName;
|
||||
qDebug() << "Searching for commit hash corresponding to stable channel tag: " << tagName;
|
||||
response = netMan->get(QNetworkRequest(url));
|
||||
connect(response, SIGNAL(finished()), this, SLOT(tagListFinished()));
|
||||
connect(response, &QNetworkReply::finished, this, &StableReleaseChannel::tagListFinished);
|
||||
}
|
||||
|
||||
void StableReleaseChannel::tagListFinished()
|
||||
@@ -260,7 +260,7 @@ void BetaReleaseChannel::releaseListFinished()
|
||||
|
||||
qDebug() << "Searching for a corresponding file on the beta channel: " << betaBuildDownloadUrl;
|
||||
response = netMan->get(QNetworkRequest(betaBuildDownloadUrl));
|
||||
connect(response, SIGNAL(finished()), this, SLOT(fileListFinished()));
|
||||
connect(response, &QNetworkReply::finished, this, &BetaReleaseChannel::fileListFinished);
|
||||
}
|
||||
|
||||
void BetaReleaseChannel::fileListFinished()
|
||||
|
||||
@@ -10,7 +10,7 @@ ReplayTimelineWidget::ReplayTimelineWidget(QWidget *parent)
|
||||
currentEvent(0)
|
||||
{
|
||||
replayTimer = new QTimer(this);
|
||||
connect(replayTimer, SIGNAL(timeout()), this, SLOT(replayTimerTimeout()));
|
||||
connect(replayTimer, &QTimer::timeout, this, &ReplayTimelineWidget::replayTimerTimeout);
|
||||
|
||||
rewindBufferingTimer = new QTimer(this);
|
||||
rewindBufferingTimer->setSingleShot(true);
|
||||
|
||||
@@ -45,10 +45,10 @@ void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
|
||||
|
||||
if (saveResults) {
|
||||
// This will write out to the file (used for spoiler.xml)
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSpoilersFile()));
|
||||
connect(reply, &QNetworkReply::finished, this, &SpoilerBackgroundUpdater::actDownloadFinishedSpoilersFile);
|
||||
} else {
|
||||
// This will check the status (used to see if we're in spoiler season or not)
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(actCheckIfSpoilerSeasonEnabled()));
|
||||
connect(reply, &QNetworkReply::finished, this, &SpoilerBackgroundUpdater::actCheckIfSpoilerSeasonEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
SoundEngine::SoundEngine(QObject *parent) : QObject(parent), player(nullptr)
|
||||
{
|
||||
ensureThemeDirectoryExists();
|
||||
connect(&SettingsCache::instance(), SIGNAL(soundThemeChanged()), this, SLOT(themeChangedSlot()));
|
||||
connect(&SettingsCache::instance(), SIGNAL(soundEnabledChanged()), this, SLOT(soundEnabledChanged()));
|
||||
connect(&SettingsCache::instance(), &SettingsCache::soundThemeChanged, this, &SoundEngine::themeChangedSlot);
|
||||
connect(&SettingsCache::instance(), &SettingsCache::soundEnabledChanged, this, &SoundEngine::soundEnabledChanged);
|
||||
|
||||
soundEnabledChanged();
|
||||
themeChangedSlot();
|
||||
|
||||
@@ -30,29 +30,22 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor,
|
||||
userInfoBox = new UserInfoBox(client, true);
|
||||
userInfoBox->updateInfo(userInfo);
|
||||
|
||||
connect(allUsersList, SIGNAL(openMessageDialog(const QString &, bool)), this,
|
||||
SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
connect(buddyList, SIGNAL(openMessageDialog(const QString &, bool)), this,
|
||||
SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
connect(ignoreList, SIGNAL(openMessageDialog(const QString &, bool)), this,
|
||||
SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
connect(allUsersList, &UserList::openMessageDialog, this, &TabUserLists::openMessageDialog);
|
||||
connect(buddyList, &UserList::openMessageDialog, this, &TabUserLists::openMessageDialog);
|
||||
connect(ignoreList, &UserList::openMessageDialog, this, &TabUserLists::openMessageDialog);
|
||||
|
||||
connect(client, SIGNAL(userJoinedEventReceived(const Event_UserJoined &)), this,
|
||||
SLOT(processUserJoinedEvent(const Event_UserJoined &)));
|
||||
connect(client, SIGNAL(userLeftEventReceived(const Event_UserLeft &)), this,
|
||||
SLOT(processUserLeftEvent(const Event_UserLeft &)));
|
||||
connect(client, SIGNAL(buddyListReceived(const QList<ServerInfo_User> &)), this,
|
||||
SLOT(buddyListReceived(const QList<ServerInfo_User> &)));
|
||||
connect(client, SIGNAL(ignoreListReceived(const QList<ServerInfo_User> &)), this,
|
||||
SLOT(ignoreListReceived(const QList<ServerInfo_User> &)));
|
||||
connect(client, SIGNAL(addToListEventReceived(const Event_AddToList &)), this,
|
||||
SLOT(processAddToListEvent(const Event_AddToList &)));
|
||||
connect(client, SIGNAL(removeFromListEventReceived(const Event_RemoveFromList &)), this,
|
||||
SLOT(processRemoveFromListEvent(const Event_RemoveFromList &)));
|
||||
connect(client, &AbstractClient::userJoinedEventReceived, this, &TabUserLists::processUserJoinedEvent);
|
||||
connect(client, &AbstractClient::userLeftEventReceived, this, &TabUserLists::processUserLeftEvent);
|
||||
connect(client, &AbstractClient::buddyListReceived, this, &TabUserLists::buddyListReceived);
|
||||
connect(client, &AbstractClient::ignoreListReceived, this, &TabUserLists::ignoreListReceived);
|
||||
connect(client, &AbstractClient::addToListEventReceived, this, &TabUserLists::processAddToListEvent);
|
||||
connect(client, &AbstractClient::removeFromListEventReceived, this, &TabUserLists::processRemoveFromListEvent);
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(Command_ListUsers());
|
||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
|
||||
SLOT(processListUsersResponse(const Response &)));
|
||||
connect(pend,
|
||||
static_cast<void (PendingCommand::*)(const Response &, const CommandContainer &, const QVariant &)>(
|
||||
&PendingCommand::finished),
|
||||
this, &TabUserLists::processListUsersResponse);
|
||||
client->sendCommand(pend);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
@@ -63,9 +56,9 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor,
|
||||
addBuddyEdit = new LineEditUnfocusable;
|
||||
addBuddyEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
addBuddyEdit->setPlaceholderText(tr("Add to Buddy List"));
|
||||
connect(addBuddyEdit, SIGNAL(returnPressed()), this, SLOT(addToBuddyList()));
|
||||
connect(addBuddyEdit, &LineEditUnfocusable::returnPressed, this, &TabUserLists::addToBuddyList);
|
||||
QPushButton *addBuddyButton = new QPushButton("Add");
|
||||
connect(addBuddyButton, SIGNAL(clicked()), this, SLOT(addToBuddyList()));
|
||||
connect(addBuddyButton, &QPushButton::clicked, this, &TabUserLists::addToBuddyList);
|
||||
addToBuddyList->addWidget(addBuddyEdit);
|
||||
addToBuddyList->addWidget(addBuddyButton);
|
||||
|
||||
@@ -73,9 +66,9 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor,
|
||||
addIgnoreEdit = new LineEditUnfocusable;
|
||||
addIgnoreEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
addIgnoreEdit->setPlaceholderText(tr("Add to Ignore List"));
|
||||
connect(addIgnoreEdit, SIGNAL(returnPressed()), this, SLOT(addToIgnoreList()));
|
||||
connect(addIgnoreEdit, &LineEditUnfocusable::returnPressed, this, &TabUserLists::addToIgnoreList);
|
||||
QPushButton *addIgnoreButton = new QPushButton("Add");
|
||||
connect(addIgnoreButton, SIGNAL(clicked()), this, SLOT(addToIgnoreList()));
|
||||
connect(addIgnoreButton, &QPushButton::clicked, this, &TabUserLists::addToIgnoreList);
|
||||
addToIgnoreList->addWidget(addIgnoreEdit);
|
||||
addToIgnoreList->addWidget(addIgnoreButton);
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ ShutdownDialog::ShutdownDialog(QWidget *parent) : QDialog(parent)
|
||||
minutesEdit->setMaximum(999);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &ShutdownDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &ShutdownDialog::reject);
|
||||
|
||||
QGridLayout *mainLayout = new QGridLayout;
|
||||
mainLayout->addWidget(reasonLabel, 0, 0);
|
||||
@@ -57,11 +57,11 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool
|
||||
: Tab(_tabSupervisor, parent), locked(true), client(_client), fullAdmin(_fullAdmin)
|
||||
{
|
||||
updateServerMessageButton = new QPushButton;
|
||||
connect(updateServerMessageButton, SIGNAL(clicked()), this, SLOT(actUpdateServerMessage()));
|
||||
connect(updateServerMessageButton, &QPushButton::clicked, this, &TabAdmin::actUpdateServerMessage);
|
||||
shutdownServerButton = new QPushButton;
|
||||
connect(shutdownServerButton, SIGNAL(clicked()), this, SLOT(actShutdownServer()));
|
||||
connect(shutdownServerButton, &QPushButton::clicked, this, &TabAdmin::actShutdownServer);
|
||||
reloadConfigButton = new QPushButton;
|
||||
connect(reloadConfigButton, SIGNAL(clicked()), this, SLOT(actReloadConfig()));
|
||||
connect(reloadConfigButton, &QPushButton::clicked, this, &TabAdmin::actReloadConfig);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(updateServerMessageButton);
|
||||
@@ -74,10 +74,10 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool
|
||||
adminGroupBox->setEnabled(false);
|
||||
|
||||
unlockButton = new QPushButton;
|
||||
connect(unlockButton, SIGNAL(clicked()), this, SLOT(actUnlock()));
|
||||
connect(unlockButton, &QPushButton::clicked, this, &TabAdmin::actUnlock);
|
||||
lockButton = new QPushButton;
|
||||
lockButton->setEnabled(false);
|
||||
connect(lockButton, SIGNAL(clicked()), this, SLOT(actLock()));
|
||||
connect(lockButton, &QPushButton::clicked, this, &TabAdmin::actLock);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(adminGroupBox);
|
||||
|
||||
@@ -50,22 +50,6 @@
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
void SearchLineEdit::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
// List of key events that must be handled by the card list instead of the search box
|
||||
static const QVector<Qt::Key> forwardToTreeView = {Qt::Key_Up, Qt::Key_Down, Qt::Key_PageDown, Qt::Key_PageUp};
|
||||
// forward only if the search text is empty
|
||||
static const QVector<Qt::Key> forwardWhenEmpty = {Qt::Key_Home, Qt::Key_End};
|
||||
Qt::Key key = static_cast<Qt::Key>(event->key());
|
||||
if (treeView) {
|
||||
if (forwardToTreeView.contains(key))
|
||||
QCoreApplication::sendEvent(treeView, event);
|
||||
if (text().isEmpty() && forwardWhenEmpty.contains(key))
|
||||
QCoreApplication::sendEvent(treeView, event);
|
||||
}
|
||||
LineEditUnfocusable::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void TabDeckEditor::createDeckDock()
|
||||
{
|
||||
deckModel = new DeckListModel(this);
|
||||
@@ -1535,6 +1519,6 @@ void TabDeckEditor::showSearchSyntaxHelp()
|
||||
browser->document()->setDefaultStyleSheet(sheet);
|
||||
|
||||
browser->setHtml(text);
|
||||
connect(browser, &QTextBrowser::anchorClicked, [=](const QUrl &link) { searchEdit->setText(link.fragment()); });
|
||||
connect(browser, &QTextBrowser::anchorClicked, [this](const QUrl &link) { searchEdit->setText(link.fragment()); });
|
||||
browser->show();
|
||||
}
|
||||
|
||||
@@ -29,24 +29,6 @@ class QVBoxLayout;
|
||||
class QPushButton;
|
||||
class QDockWidget;
|
||||
|
||||
class SearchLineEdit : public LineEditUnfocusable
|
||||
{
|
||||
private:
|
||||
QTreeView *treeView;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
public:
|
||||
SearchLineEdit() : LineEditUnfocusable(), treeView(nullptr)
|
||||
{
|
||||
}
|
||||
void setTreeView(QTreeView *_treeView)
|
||||
{
|
||||
treeView = _treeView;
|
||||
}
|
||||
};
|
||||
|
||||
class TabDeckEditor : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -1742,22 +1742,22 @@ void TabGame::createReplayDock()
|
||||
aReplaySkipForward = new QAction(timelineWidget);
|
||||
timelineWidget->addAction(aReplaySkipForward);
|
||||
connect(aReplaySkipForward, &QAction::triggered, this,
|
||||
[=]() { timelineWidget->skipByAmount(ReplayTimelineWidget::SMALL_SKIP_MS); });
|
||||
[this] { timelineWidget->skipByAmount(ReplayTimelineWidget::SMALL_SKIP_MS); });
|
||||
|
||||
aReplaySkipBackward = new QAction(timelineWidget);
|
||||
timelineWidget->addAction(aReplaySkipBackward);
|
||||
connect(aReplaySkipBackward, &QAction::triggered, this,
|
||||
[=]() { timelineWidget->skipByAmount(-ReplayTimelineWidget::SMALL_SKIP_MS); });
|
||||
[this] { timelineWidget->skipByAmount(-ReplayTimelineWidget::SMALL_SKIP_MS); });
|
||||
|
||||
aReplaySkipForwardBig = new QAction(timelineWidget);
|
||||
timelineWidget->addAction(aReplaySkipForwardBig);
|
||||
connect(aReplaySkipForwardBig, &QAction::triggered, this,
|
||||
[=]() { timelineWidget->skipByAmount(ReplayTimelineWidget::BIG_SKIP_MS); });
|
||||
[this] { timelineWidget->skipByAmount(ReplayTimelineWidget::BIG_SKIP_MS); });
|
||||
|
||||
aReplaySkipBackwardBig = new QAction(timelineWidget);
|
||||
timelineWidget->addAction(aReplaySkipBackwardBig);
|
||||
connect(aReplaySkipBackwardBig, &QAction::triggered, this,
|
||||
[=]() { timelineWidget->skipByAmount(-ReplayTimelineWidget::BIG_SKIP_MS); });
|
||||
[this] { timelineWidget->skipByAmount(-ReplayTimelineWidget::BIG_SKIP_MS); });
|
||||
|
||||
// buttons
|
||||
replayPlayButton = new QToolButton;
|
||||
|
||||
@@ -14,7 +14,7 @@ TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *par
|
||||
: QObject(parent), cardDatabase(_cardDatabase)
|
||||
{
|
||||
manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *)));
|
||||
connect(manager, &QNetworkAccessManager::finished, this, &TappedOutInterface::queryFinished);
|
||||
}
|
||||
|
||||
void TappedOutInterface::queryFinished(QNetworkReply *reply)
|
||||
|
||||
@@ -7,23 +7,23 @@
|
||||
class TearOffMenu : public QMenu
|
||||
{
|
||||
public:
|
||||
TearOffMenu(const QString &title, QWidget *parent = nullptr) : QMenu(title, parent)
|
||||
explicit TearOffMenu(const QString &title, QWidget *parent = nullptr) : QMenu(title, parent)
|
||||
{
|
||||
connect(&SettingsCache::instance(), &SettingsCache::useTearOffMenusChanged, this,
|
||||
[=](bool state) { setTearOffEnabled(state); });
|
||||
[this](const bool state) { setTearOffEnabled(state); });
|
||||
setTearOffEnabled(SettingsCache::instance().getUseTearOffMenus());
|
||||
}
|
||||
|
||||
TearOffMenu(QWidget *parent = nullptr) : QMenu(parent)
|
||||
explicit TearOffMenu(QWidget *parent = nullptr) : QMenu(parent)
|
||||
{
|
||||
connect(&SettingsCache::instance(), &SettingsCache::useTearOffMenusChanged, this,
|
||||
[=](bool state) { setTearOffEnabled(state); });
|
||||
[this](const bool state) { setTearOffEnabled(state); });
|
||||
setTearOffEnabled(SettingsCache::instance().getUseTearOffMenus());
|
||||
}
|
||||
|
||||
TearOffMenu *addTearOffMenu(const QString &title)
|
||||
{
|
||||
TearOffMenu *menu = new TearOffMenu(title, this);
|
||||
auto *menu = new TearOffMenu(title, this);
|
||||
addMenu(menu);
|
||||
return menu;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "card_info_picture_widget.h"
|
||||
|
||||
#include "../../../../game/cards/card_item.h"
|
||||
#include "../../../../settings/cache_settings.h"
|
||||
#include "../../picture_loader.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
@@ -141,6 +142,7 @@ void CardInfoPictureWidget::loadPixmap()
|
||||
void CardInfoPictureWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QWidget::paintEvent(event);
|
||||
|
||||
if (width() == 0 || height() == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -149,15 +151,31 @@ void CardInfoPictureWidget::paintEvent(QPaintEvent *event)
|
||||
loadPixmap();
|
||||
}
|
||||
|
||||
const QSize scaledSize = resizedPixmap.size().scaled(size(), Qt::KeepAspectRatio);
|
||||
const QPoint topLeft{(width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2};
|
||||
QPixmap transformedPixmap = resizedPixmap; // Default pixmap
|
||||
if (SettingsCache::instance().getAutoRotateSidewaysLayoutCards()) {
|
||||
if (info && info->getLandscapeOrientation()) {
|
||||
// Rotate pixmap 90 degrees to the left
|
||||
QTransform transform;
|
||||
transform.rotate(90);
|
||||
transformedPixmap = resizedPixmap.transformed(transform, Qt::SmoothTransformation);
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust scaling after rotation
|
||||
const QSize availableSize = size(); // Size of the widget
|
||||
const QSize scaledSize = transformedPixmap.size().scaled(availableSize, Qt::KeepAspectRatio);
|
||||
|
||||
const QRect targetRect{(availableSize.width() - scaledSize.width()) / 2,
|
||||
(availableSize.height() - scaledSize.height()) / 2, scaledSize.width(), scaledSize.height()};
|
||||
|
||||
const qreal radius = 0.05 * scaledSize.width();
|
||||
|
||||
// Draw the pixmap with rounded corners
|
||||
QStylePainter painter(this);
|
||||
QPainterPath shape;
|
||||
shape.addRoundedRect(QRect(topLeft, scaledSize), radius, radius);
|
||||
shape.addRoundedRect(targetRect, radius, radius);
|
||||
painter.setClipPath(shape);
|
||||
painter.drawItemPixmap(QRect(topLeft, scaledSize), Qt::AlignCenter, resizedPixmap);
|
||||
painter.drawPixmap(targetRect, transformedPixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -175,7 +175,7 @@ void PrintingSelector::getAllSetsForCurrentCard()
|
||||
// Defer widget creation
|
||||
currentIndex = 0;
|
||||
|
||||
connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=]() mutable {
|
||||
connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable {
|
||||
for (int i = 0; i < BATCH_SIZE && currentIndex < setsToUse.size(); ++i, ++currentIndex) {
|
||||
auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckModel, deckView,
|
||||
cardSizeWidget->getSlider(), selectedCard,
|
||||
|
||||
@@ -15,9 +15,9 @@ void UpdateDownloader::beginDownload(QUrl downloadUrl)
|
||||
originalUrl = downloadUrl;
|
||||
|
||||
response = netMan->get(QNetworkRequest(downloadUrl));
|
||||
connect(response, SIGNAL(finished()), this, SLOT(fileFinished()));
|
||||
connect(response, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64)));
|
||||
connect(this, SIGNAL(stopDownload()), response, SLOT(abort()));
|
||||
connect(response, &QNetworkReply::finished, this, &UpdateDownloader::fileFinished);
|
||||
connect(response, &QNetworkReply::downloadProgress, this, &UpdateDownloader::downloadProgress);
|
||||
connect(this, &UpdateDownloader::stopDownload, response, &QNetworkReply::abort);
|
||||
}
|
||||
|
||||
void UpdateDownloader::downloadError(QNetworkReply::NetworkError)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <QKeyEvent>
|
||||
#include <QLineEdit>
|
||||
#include <QObject>
|
||||
#include <QTreeView>
|
||||
#include <QWidget>
|
||||
|
||||
LineEditUnfocusable::LineEditUnfocusable(QWidget *parent) : QLineEdit(parent)
|
||||
@@ -69,3 +70,19 @@ bool LineEditUnfocusable::eventFilter(QObject *watched, QEvent *event)
|
||||
|
||||
return QLineEdit::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void SearchLineEdit::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
// List of key events that must be handled by the card list instead of the search box
|
||||
static const QVector<Qt::Key> forwardToTreeView = {Qt::Key_Up, Qt::Key_Down, Qt::Key_PageDown, Qt::Key_PageUp};
|
||||
// forward only if the search text is empty
|
||||
static const QVector<Qt::Key> forwardWhenEmpty = {Qt::Key_Home, Qt::Key_End};
|
||||
Qt::Key key = static_cast<Qt::Key>(event->key());
|
||||
if (treeView) {
|
||||
if (forwardToTreeView.contains(key))
|
||||
QCoreApplication::sendEvent(treeView, event);
|
||||
if (text().isEmpty() && forwardWhenEmpty.contains(key))
|
||||
QCoreApplication::sendEvent(treeView, event);
|
||||
}
|
||||
LineEditUnfocusable::keyPressEvent(event);
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
class QTreeView;
|
||||
class QKeyEvent;
|
||||
class QWidget;
|
||||
class QString;
|
||||
@@ -25,4 +26,22 @@ protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
};
|
||||
|
||||
class SearchLineEdit : public LineEditUnfocusable
|
||||
{
|
||||
private:
|
||||
QTreeView *treeView;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
public:
|
||||
SearchLineEdit() : LineEditUnfocusable(), treeView(nullptr)
|
||||
{
|
||||
}
|
||||
void setTreeView(QTreeView *_treeView)
|
||||
{
|
||||
treeView = _treeView;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -95,12 +95,26 @@ void DlgCreateGame::sharedCtor()
|
||||
spectatorsGroupBox = new QGroupBox(tr("Spectators"));
|
||||
spectatorsGroupBox->setLayout(spectatorsLayout);
|
||||
|
||||
startingLifeTotalLabel = new QLabel(tr("Starting life total:"));
|
||||
startingLifeTotalEdit = new QSpinBox();
|
||||
startingLifeTotalEdit->setMinimum(1);
|
||||
startingLifeTotalEdit->setMaximum(99999); ///< Arbitrary but we can raise this when people start complaining.
|
||||
startingLifeTotalEdit->setValue(20);
|
||||
startingLifeTotalLabel->setBuddy(startingLifeTotalEdit);
|
||||
|
||||
QGridLayout *gameSetupOptionsLayout = new QGridLayout;
|
||||
gameSetupOptionsLayout->addWidget(startingLifeTotalLabel, 0, 0);
|
||||
gameSetupOptionsLayout->addWidget(startingLifeTotalEdit, 0, 1);
|
||||
gameSetupOptionsGroupBox = new QGroupBox(tr("Game setup options"));
|
||||
gameSetupOptionsGroupBox->setLayout(gameSetupOptionsLayout);
|
||||
|
||||
QGridLayout *grid = new QGridLayout;
|
||||
grid->addWidget(generalGroupBox, 0, 0);
|
||||
grid->addWidget(joinRestrictionsGroupBox, 0, 1);
|
||||
grid->addWidget(gameTypeGroupBox, 1, 0);
|
||||
grid->addWidget(spectatorsGroupBox, 1, 1, Qt::AlignTop);
|
||||
grid->addWidget(rememberGameSettings, 2, 0);
|
||||
grid->addWidget(gameSetupOptionsGroupBox, 2, 0);
|
||||
grid->addWidget(rememberGameSettings, 3, 0);
|
||||
|
||||
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
@@ -134,6 +148,7 @@ DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap<int, QString> &_gameType
|
||||
spectatorsCanTalkCheckBox->setChecked(SettingsCache::instance().getSpectatorsCanTalk());
|
||||
spectatorsSeeEverythingCheckBox->setChecked(SettingsCache::instance().getSpectatorsCanSeeEverything());
|
||||
createGameAsSpectatorCheckBox->setChecked(SettingsCache::instance().getCreateGameAsSpectator());
|
||||
startingLifeTotalEdit->setValue(SettingsCache::instance().getDefaultStartingLifeTotal());
|
||||
|
||||
if (!rememberGameSettings->isChecked()) {
|
||||
actReset();
|
||||
@@ -208,6 +223,8 @@ void DlgCreateGame::actReset()
|
||||
spectatorsSeeEverythingCheckBox->setChecked(false);
|
||||
createGameAsSpectatorCheckBox->setChecked(false);
|
||||
|
||||
startingLifeTotalEdit->setValue(20);
|
||||
|
||||
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
|
||||
while (gameTypeCheckBoxIterator.hasNext()) {
|
||||
gameTypeCheckBoxIterator.next();
|
||||
@@ -234,6 +251,7 @@ void DlgCreateGame::actOK()
|
||||
cmd.set_spectators_see_everything(spectatorsSeeEverythingCheckBox->isChecked());
|
||||
cmd.set_join_as_judge(QApplication::keyboardModifiers() & Qt::ShiftModifier);
|
||||
cmd.set_join_as_spectator(createGameAsSpectatorCheckBox->isChecked());
|
||||
cmd.set_starting_life_total(startingLifeTotalEdit->value());
|
||||
|
||||
QString _gameTypes = QString();
|
||||
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
|
||||
@@ -256,6 +274,7 @@ void DlgCreateGame::actOK()
|
||||
SettingsCache::instance().setSpectatorsCanTalk(spectatorsCanTalkCheckBox->isChecked());
|
||||
SettingsCache::instance().setSpectatorsCanSeeEverything(spectatorsSeeEverythingCheckBox->isChecked());
|
||||
SettingsCache::instance().setCreateGameAsSpectator(createGameAsSpectatorCheckBox->isChecked());
|
||||
SettingsCache::instance().setDefaultStartingLifeTotal(startingLifeTotalEdit->value());
|
||||
SettingsCache::instance().setGameTypes(_gameTypes);
|
||||
}
|
||||
PendingCommand *pend = room->prepareRoomCommand(cmd);
|
||||
|
||||
@@ -35,10 +35,10 @@ private:
|
||||
QMap<int, QString> gameTypes;
|
||||
QMap<int, QRadioButton *> gameTypeCheckBoxes;
|
||||
|
||||
QGroupBox *generalGroupBox, *spectatorsGroupBox;
|
||||
QLabel *descriptionLabel, *passwordLabel, *maxPlayersLabel;
|
||||
QGroupBox *generalGroupBox, *spectatorsGroupBox, *gameSetupOptionsGroupBox;
|
||||
QLabel *descriptionLabel, *passwordLabel, *maxPlayersLabel, *startingLifeTotalLabel;
|
||||
QLineEdit *descriptionEdit, *passwordEdit;
|
||||
QSpinBox *maxPlayersEdit;
|
||||
QSpinBox *maxPlayersEdit, *startingLifeTotalEdit;
|
||||
QCheckBox *onlyBuddiesCheckBox, *onlyRegisteredCheckBox;
|
||||
QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox,
|
||||
*spectatorsSeeEverythingCheckBox, *createGameAsSpectatorCheckBox;
|
||||
|
||||
@@ -31,7 +31,11 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
|
||||
nameLabel = new QLabel(tr("&Name:"));
|
||||
nameEdit = new QLineEdit(tr("Token"));
|
||||
nameEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
nameEdit->selectAll();
|
||||
QTimer::singleShot(100, this, [=, this]() {
|
||||
nameEdit->selectAll();
|
||||
nameEdit->setFocus();
|
||||
});
|
||||
|
||||
connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &)));
|
||||
nameLabel->setBuddy(nameEdit);
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
#include "../client/sound_engine.h"
|
||||
#include "../client/ui/picture_loader.h"
|
||||
#include "../client/ui/theme_manager.h"
|
||||
#include "../deck/custom_line_edit.h"
|
||||
#include "../game/cards/card_database.h"
|
||||
#include "../game/cards/card_database_manager.h"
|
||||
#include "../main.h"
|
||||
#include "../settings/cache_settings.h"
|
||||
#include "../settings/shortcut_treeview.h"
|
||||
#include "../utility/sequence_edit.h"
|
||||
|
||||
#include <QAction>
|
||||
@@ -37,8 +39,6 @@
|
||||
#include <QStackedWidget>
|
||||
#include <QToolBar>
|
||||
#include <QTranslator>
|
||||
#include <QTreeWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
#define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs"
|
||||
#define WIKI_CUSTOM_SHORTCUTS "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Keyboard-Shortcuts"
|
||||
@@ -338,6 +338,10 @@ AppearanceSettingsPage::AppearanceSettingsPage()
|
||||
displayCardNamesCheckBox.setChecked(settings.getDisplayCardNames());
|
||||
connect(&displayCardNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setDisplayCardNames);
|
||||
|
||||
autoRotateSidewaysLayoutCardsCheckBox.setChecked(settings.getAutoRotateSidewaysLayoutCards());
|
||||
connect(&autoRotateSidewaysLayoutCardsCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings,
|
||||
&SettingsCache::setAutoRotateSidewaysLayoutCards);
|
||||
|
||||
overrideAllCardArtWithPersonalPreferenceCheckBox.setChecked(settings.getOverrideAllCardArtWithPersonalPreference());
|
||||
connect(&overrideAllCardArtWithPersonalPreferenceCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings,
|
||||
&SettingsCache::setOverrideAllCardArtWithPersonalPreference);
|
||||
@@ -361,13 +365,14 @@ AppearanceSettingsPage::AppearanceSettingsPage()
|
||||
|
||||
auto *cardsGrid = new QGridLayout;
|
||||
cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2);
|
||||
cardsGrid->addWidget(&cardScalingCheckBox, 1, 0, 1, 2);
|
||||
cardsGrid->addWidget(&overrideAllCardArtWithPersonalPreferenceCheckBox, 2, 0, 1, 2);
|
||||
cardsGrid->addWidget(&bumpSetsWithCardsInDeckToTopCheckBox, 3, 0, 1, 2);
|
||||
cardsGrid->addWidget(&verticalCardOverlapPercentLabel, 4, 0, 1, 1);
|
||||
cardsGrid->addWidget(&verticalCardOverlapPercentBox, 4, 1, 1, 1);
|
||||
cardsGrid->addWidget(&cardViewInitialRowsMaxLabel, 5, 0);
|
||||
cardsGrid->addWidget(&cardViewInitialRowsMaxBox, 5, 1);
|
||||
cardsGrid->addWidget(&autoRotateSidewaysLayoutCardsCheckBox, 1, 0, 1, 2);
|
||||
cardsGrid->addWidget(&cardScalingCheckBox, 2, 0, 1, 2);
|
||||
cardsGrid->addWidget(&overrideAllCardArtWithPersonalPreferenceCheckBox, 3, 0, 1, 2);
|
||||
cardsGrid->addWidget(&bumpSetsWithCardsInDeckToTopCheckBox, 4, 0, 1, 2);
|
||||
cardsGrid->addWidget(&verticalCardOverlapPercentLabel, 5, 0, 1, 1);
|
||||
cardsGrid->addWidget(&verticalCardOverlapPercentBox, 5, 1, 1, 1);
|
||||
cardsGrid->addWidget(&cardViewInitialRowsMaxLabel, 6, 0);
|
||||
cardsGrid->addWidget(&cardViewInitialRowsMaxBox, 6, 1);
|
||||
|
||||
cardsGroupBox = new QGroupBox;
|
||||
cardsGroupBox->setLayout(cardsGrid);
|
||||
@@ -462,6 +467,7 @@ void AppearanceSettingsPage::retranslateUi()
|
||||
|
||||
cardsGroupBox->setTitle(tr("Card rendering"));
|
||||
displayCardNamesCheckBox.setText(tr("Display card names on cards having a picture"));
|
||||
autoRotateSidewaysLayoutCardsCheckBox.setText(tr("Auto-Rotate cards with sideways layout"));
|
||||
overrideAllCardArtWithPersonalPreferenceCheckBox.setText(
|
||||
tr("Override all card art with personal set preference (Pre-ProviderID change behavior) [Requires Client "
|
||||
"restart]"));
|
||||
@@ -1251,13 +1257,24 @@ void SoundSettingsPage::retranslateUi()
|
||||
|
||||
ShortcutSettingsPage::ShortcutSettingsPage()
|
||||
{
|
||||
// search bar
|
||||
searchEdit = new SearchLineEdit;
|
||||
searchEdit->setObjectName("searchEdit");
|
||||
searchEdit->setPlaceholderText(tr("Search by shortcut name"));
|
||||
searchEdit->setClearButtonEnabled(true);
|
||||
|
||||
setFocusProxy(searchEdit);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
// table
|
||||
shortcutsTable = new QTreeWidget();
|
||||
shortcutsTable->setColumnCount(2);
|
||||
shortcutsTable = new ShortcutTreeView(this);
|
||||
|
||||
shortcutsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
shortcutsTable->setUniformRowHeights(true);
|
||||
shortcutsTable->setAlternatingRowColors(true);
|
||||
shortcutsTable->header()->resizeSection(0, shortcutsTable->width() / 3 * 2);
|
||||
shortcutsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
shortcutsTable->setColumnWidth(0, width() / 3 * 2);
|
||||
searchEdit->setTreeView(shortcutsTable);
|
||||
|
||||
connect(searchEdit, &SearchLineEdit::textChanged, shortcutsTable, &ShortcutTreeView::updateSearchString);
|
||||
|
||||
// edit widget
|
||||
currentActionGroupLabel = new QLabel(this);
|
||||
@@ -1297,6 +1314,7 @@ ShortcutSettingsPage::ShortcutSettingsPage()
|
||||
_buttonsLayout->addWidget(btnClearAll);
|
||||
|
||||
auto *_mainLayout = new QVBoxLayout;
|
||||
_mainLayout->addWidget(searchEdit);
|
||||
_mainLayout->addWidget(shortcutsTable);
|
||||
_mainLayout->addWidget(editShortcutGroupBox);
|
||||
_mainLayout->addLayout(_buttonsLayout);
|
||||
@@ -1305,21 +1323,17 @@ ShortcutSettingsPage::ShortcutSettingsPage()
|
||||
|
||||
connect(btnResetAll, SIGNAL(clicked()), this, SLOT(resetShortcuts()));
|
||||
connect(btnClearAll, SIGNAL(clicked()), this, SLOT(clearShortcuts()));
|
||||
connect(shortcutsTable, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this,
|
||||
SLOT(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
|
||||
connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
|
||||
|
||||
createShortcuts();
|
||||
connect(shortcutsTable, &ShortcutTreeView::currentItemChanged, this, &ShortcutSettingsPage::currentItemChanged);
|
||||
}
|
||||
|
||||
void ShortcutSettingsPage::currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem * /*previous */)
|
||||
void ShortcutSettingsPage::currentItemChanged(const QString &key)
|
||||
{
|
||||
if (current == nullptr) {
|
||||
if (key.isEmpty()) {
|
||||
currentActionGroupName->setText("");
|
||||
currentActionName->setText("");
|
||||
editTextBox->setShortcutName("");
|
||||
} else {
|
||||
QString key = current->data(2, Qt::DisplayRole).toString();
|
||||
QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName();
|
||||
QString action = SettingsCache::instance().shortcuts().getShortcut(key).getName();
|
||||
currentActionGroupName->setText(group);
|
||||
@@ -1336,59 +1350,6 @@ void ShortcutSettingsPage::resetShortcuts()
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutSettingsPage::createShortcuts()
|
||||
{
|
||||
QHash<QString, QTreeWidgetItem *> parentItems;
|
||||
QTreeWidgetItem *curParent = nullptr;
|
||||
for (const auto &key : SettingsCache::instance().shortcuts().getAllShortcutKeys()) {
|
||||
QString name = SettingsCache::instance().shortcuts().getShortcut(key).getName();
|
||||
QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName();
|
||||
QString shortcut = SettingsCache::instance().shortcuts().getShortcutString(key);
|
||||
|
||||
if (parentItems.contains(group)) {
|
||||
curParent = parentItems.value(group);
|
||||
} else {
|
||||
curParent = new QTreeWidgetItem((QTreeWidget *)nullptr, QStringList({group, "", ""}));
|
||||
static QFont font = curParent->font(0);
|
||||
font.setBold(true);
|
||||
curParent->setFont(0, font);
|
||||
parentItems.insert(group, curParent);
|
||||
}
|
||||
|
||||
new QTreeWidgetItem(curParent, QStringList({name, shortcut, key}));
|
||||
}
|
||||
shortcutsTable->clear();
|
||||
shortcutsTable->insertTopLevelItems(0, parentItems.values());
|
||||
shortcutsTable->setCurrentItem(nullptr);
|
||||
shortcutsTable->expandAll();
|
||||
shortcutsTable->sortItems(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
void ShortcutSettingsPage::refreshShortcuts()
|
||||
{
|
||||
QTreeWidgetItem *curParent = nullptr;
|
||||
QTreeWidgetItem *curChild = nullptr;
|
||||
for (int i = 0; i < shortcutsTable->topLevelItemCount(); ++i) {
|
||||
curParent = shortcutsTable->topLevelItem(i);
|
||||
for (int j = 0; j < curParent->childCount(); ++j) {
|
||||
curChild = curParent->child(j);
|
||||
QString key = curChild->data(2, Qt::DisplayRole).toString();
|
||||
QString name = SettingsCache::instance().shortcuts().getShortcut(key).getName();
|
||||
QString shortcut = SettingsCache::instance().shortcuts().getShortcutString(key);
|
||||
curChild->setText(0, name);
|
||||
curChild->setText(1, shortcut);
|
||||
|
||||
if (j == 0) {
|
||||
// the first child also updates the parent's group name
|
||||
QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName();
|
||||
curParent->setText(0, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
shortcutsTable->sortItems(0, Qt::AscendingOrder);
|
||||
currentItemChanged(shortcutsTable->currentItem(), nullptr);
|
||||
}
|
||||
|
||||
void ShortcutSettingsPage::clearShortcuts()
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Clear all default shortcuts"),
|
||||
@@ -1399,8 +1360,7 @@ void ShortcutSettingsPage::clearShortcuts()
|
||||
|
||||
void ShortcutSettingsPage::retranslateUi()
|
||||
{
|
||||
shortcutsTable->setHeaderLabels(QStringList() << tr("Action") << tr("Shortcut"));
|
||||
refreshShortcuts();
|
||||
shortcutsTable->retranslateUi();
|
||||
|
||||
currentActionGroupLabel->setText(tr("Section:"));
|
||||
currentActionLabel->setText(tr("Action:"));
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
|
||||
class ShortcutTreeView;
|
||||
class SearchLineEdit;
|
||||
class QTreeView;
|
||||
class QStandardItemModel;
|
||||
class CardDatabase;
|
||||
class QCloseEvent;
|
||||
class QGridLayout;
|
||||
@@ -21,8 +25,6 @@ class QListWidgetItem;
|
||||
class QRadioButton;
|
||||
class QSlider;
|
||||
class QStackedWidget;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QVBoxLayout;
|
||||
class SequenceEdit;
|
||||
|
||||
@@ -93,6 +95,7 @@ private:
|
||||
QLabel maxFontSizeForCardsLabel;
|
||||
QCheckBox showShortcutsCheckBox;
|
||||
QCheckBox displayCardNamesCheckBox;
|
||||
QCheckBox autoRotateSidewaysLayoutCardsCheckBox;
|
||||
QCheckBox overrideAllCardArtWithPersonalPreferenceCheckBox;
|
||||
QCheckBox bumpSetsWithCardsInDeckToTopCheckBox;
|
||||
QCheckBox cardScalingCheckBox;
|
||||
@@ -267,7 +270,8 @@ public:
|
||||
void retranslateUi() override;
|
||||
|
||||
private:
|
||||
QTreeWidget *shortcutsTable;
|
||||
SearchLineEdit *searchEdit;
|
||||
ShortcutTreeView *shortcutsTable;
|
||||
QVBoxLayout *mainLayout;
|
||||
QHBoxLayout *buttonsLayout;
|
||||
QGroupBox *editShortcutGroupBox;
|
||||
@@ -284,10 +288,8 @@ private:
|
||||
|
||||
private slots:
|
||||
void resetShortcuts();
|
||||
void refreshShortcuts();
|
||||
void createShortcuts();
|
||||
void clearShortcuts();
|
||||
void currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
|
||||
void currentItemChanged(const QString &key);
|
||||
};
|
||||
|
||||
class DlgSettings : public QDialog
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#include <QtMath>
|
||||
|
||||
ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color)
|
||||
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), color(_color),
|
||||
fullColor(true)
|
||||
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false),
|
||||
color(_color), fullColor(true)
|
||||
{
|
||||
qDebug() << "ArrowItem constructor: startItem=" << static_cast<QGraphicsItem *>(startItem);
|
||||
setZValue(2000000005);
|
||||
@@ -167,7 +167,7 @@ void ArrowDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
// This ensures that if a mouse move event happens after a call to delArrow(),
|
||||
// the event will be discarded as it would create some stray pointers.
|
||||
if (!startItem)
|
||||
if (targetLocked || !startItem)
|
||||
return;
|
||||
|
||||
QPointF endPos = event->scenePos();
|
||||
@@ -268,7 +268,7 @@ void ArrowAttachItem::addChildArrow(ArrowAttachItem *childArrow)
|
||||
|
||||
void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (!startItem)
|
||||
if (targetLocked || !startItem)
|
||||
return;
|
||||
|
||||
QPointF endPos = event->scenePos();
|
||||
@@ -309,8 +309,8 @@ void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
|
||||
void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard)
|
||||
{
|
||||
// do nothing if target is already attached to another card
|
||||
if (targetCard->getAttachedTo()) {
|
||||
// do nothing if target is already attached to another card or is not in play
|
||||
if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != "table") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -337,6 +337,12 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
if (!startItem)
|
||||
return;
|
||||
|
||||
// Attaching could move startItem under the current cursor position, causing all children to retarget to it right
|
||||
// before they are processed. Prevent that.
|
||||
for (ArrowAttachItem *child : childArrows) {
|
||||
child->setTargetLocked(true);
|
||||
}
|
||||
|
||||
if (targetItem && (targetItem != startItem)) {
|
||||
auto startCard = qgraphicsitem_cast<CardItem *>(startItem);
|
||||
auto targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
|
||||
|
||||
@@ -21,6 +21,7 @@ protected:
|
||||
Player *player;
|
||||
int id;
|
||||
ArrowTarget *startItem, *targetItem;
|
||||
bool targetLocked;
|
||||
QColor color;
|
||||
bool fullColor;
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent *event);
|
||||
@@ -64,6 +65,10 @@ public:
|
||||
{
|
||||
return targetItem;
|
||||
}
|
||||
void setTargetLocked(bool _targetLocked)
|
||||
{
|
||||
targetLocked = _targetLocked;
|
||||
}
|
||||
void delArrow();
|
||||
};
|
||||
|
||||
|
||||
@@ -252,6 +252,12 @@ CardInfo::~CardInfo()
|
||||
PictureLoader::clearPixmapCache(smartThis);
|
||||
}
|
||||
|
||||
CardInfoPtr CardInfo::newInstance(const QString &_name)
|
||||
{
|
||||
return newInstance(_name, QString(), false, QVariantHash(), QList<CardRelation *>(), QList<CardRelation *>(),
|
||||
CardInfoPerSetMap(), false, false, 0, false);
|
||||
}
|
||||
|
||||
CardInfoPtr CardInfo::newInstance(const QString &_name,
|
||||
const QString &_text,
|
||||
bool _isToken,
|
||||
|
||||
@@ -202,17 +202,17 @@ private:
|
||||
bool upsideDownArt;
|
||||
|
||||
public:
|
||||
explicit CardInfo(const QString &_name = QString(),
|
||||
const QString &_text = QString(),
|
||||
bool _isToken = false,
|
||||
QVariantHash _properties = QVariantHash(),
|
||||
const QList<CardRelation *> &_relatedCards = QList<CardRelation *>(),
|
||||
const QList<CardRelation *> &_reverseRelatedCards = QList<CardRelation *>(),
|
||||
CardInfoPerSetMap _sets = CardInfoPerSetMap(),
|
||||
bool _cipt = false,
|
||||
bool _landscapeOrientation = false,
|
||||
int _tableRow = 0,
|
||||
bool _upsideDownArt = false);
|
||||
explicit CardInfo(const QString &_name,
|
||||
const QString &_text,
|
||||
bool _isToken,
|
||||
QVariantHash _properties,
|
||||
const QList<CardRelation *> &_relatedCards,
|
||||
const QList<CardRelation *> &_reverseRelatedCards,
|
||||
CardInfoPerSetMap _sets,
|
||||
bool _cipt,
|
||||
bool _landscapeOrientation,
|
||||
int _tableRow,
|
||||
bool _upsideDownArt);
|
||||
CardInfo(const CardInfo &other)
|
||||
: QObject(other.parent()), name(other.name), simpleName(other.simpleName), pixmapCacheKey(other.pixmapCacheKey),
|
||||
text(other.text), isToken(other.isToken), properties(other.properties), relatedCards(other.relatedCards),
|
||||
@@ -223,17 +223,19 @@ public:
|
||||
}
|
||||
~CardInfo() override;
|
||||
|
||||
static CardInfoPtr newInstance(const QString &_name = QString(),
|
||||
const QString &_text = QString(),
|
||||
bool _isToken = false,
|
||||
QVariantHash _properties = QVariantHash(),
|
||||
const QList<CardRelation *> &_relatedCards = QList<CardRelation *>(),
|
||||
const QList<CardRelation *> &_reverseRelatedCards = QList<CardRelation *>(),
|
||||
CardInfoPerSetMap _sets = CardInfoPerSetMap(),
|
||||
bool _cipt = false,
|
||||
bool _landscapeOrientation = false,
|
||||
int _tableRow = 0,
|
||||
bool _upsideDownArt = false);
|
||||
static CardInfoPtr newInstance(const QString &_name);
|
||||
|
||||
static CardInfoPtr newInstance(const QString &_name,
|
||||
const QString &_text,
|
||||
bool _isToken,
|
||||
QVariantHash _properties,
|
||||
const QList<CardRelation *> &_relatedCards,
|
||||
const QList<CardRelation *> &_reverseRelatedCards,
|
||||
CardInfoPerSetMap _sets,
|
||||
bool _cipt,
|
||||
bool _landscapeOrientation,
|
||||
int _tableRow,
|
||||
bool _upsideDownArt);
|
||||
|
||||
CardInfoPtr clone() const
|
||||
{
|
||||
|
||||
@@ -140,6 +140,7 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
||||
auto _sets = CardInfoPerSetMap();
|
||||
int tableRow = 0;
|
||||
bool cipt = false;
|
||||
bool landscapeOrientation = false;
|
||||
bool isToken = false;
|
||||
bool upsideDown = false;
|
||||
|
||||
@@ -165,6 +166,8 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
||||
tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt();
|
||||
} else if (xmlName == "cipt") {
|
||||
cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
|
||||
} else if (xmlName == "landscapeOrientation") {
|
||||
landscapeOrientation = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
|
||||
} else if (xmlName == "upsidedown") {
|
||||
upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
|
||||
// sets
|
||||
@@ -233,8 +236,9 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
||||
}
|
||||
}
|
||||
|
||||
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
|
||||
reverseRelatedCards, _sets, cipt, tableRow, upsideDown);
|
||||
CardInfoPtr newCard =
|
||||
CardInfo::newInstance(name, text, isToken, properties, relatedCards, reverseRelatedCards, _sets, cipt,
|
||||
landscapeOrientation, tableRow, upsideDown);
|
||||
emit addCard(newCard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ OracleQuery <- 'o' [:] RegexString
|
||||
CMCQuery <- ('cmc'/'mv') ws? NumericExpression
|
||||
PowerQuery <- [Pp] 'ow' 'er'? ws? NumericExpression
|
||||
ToughnessQuery <- [Tt] 'ou' 'ghness'? ws? NumericExpression
|
||||
RarityQuery <- [rR] ':' RegexString
|
||||
|
||||
RarityQuery <- [rR] ':' Rarity
|
||||
Rarity <- [Cc] 'ommon'? / [Uu] 'ncommon'? / [Rr] 'are'? / [Mm] 'ythic'? / [Ss] 'pecial'? / [a-zA-Z] [a-z]*
|
||||
|
||||
FormatQuery <- 'f' ':' Format / Legality ':' Format
|
||||
Format <- [a-zA-Z] [a-z]*
|
||||
@@ -42,10 +44,11 @@ ColorQuery <- [cC] 'olor'? <[iI]?> <[:!]> ColorEx*
|
||||
|
||||
FieldQuery <- String [:] RegexString / String ws? NumericExpression
|
||||
|
||||
NonDoubleQuoteUnlessEscaped <- !["]. / '\"'.
|
||||
NonSingleQuoteUnlessEscaped <- ![']. / "\'".
|
||||
NonDoubleQuoteUnlessEscaped <- '\\\"'. / !["].
|
||||
NonSingleQuoteUnlessEscaped <- "\\\'". / !['].
|
||||
UnescapedStringListPart <- !['":<>=! ].
|
||||
String <- UnescapedStringListPart+ / ["] <NonDoubleQuoteUnlessEscaped*> ["] / ['] <NonSingleQuoteUnlessEscaped*> [']
|
||||
SingleApostropheString <- (UnescapedStringListPart+ ws*)* ['] (UnescapedStringListPart+ ws*)*
|
||||
String <- UnescapedStringListPart+ / ["] <NonDoubleQuoteUnlessEscaped*> ["] / ['] <NonSingleQuoteUnlessEscaped*> ['] / SingleApostropheString
|
||||
StringValue <- String / [(] StringList [)]
|
||||
StringList <- StringListString (ws? [,] ws? StringListString)*
|
||||
StringListString <- UnescapedStringListPart+
|
||||
@@ -58,78 +61,92 @@ CompactStringSet <- StringListString ([,+] StringListString)+
|
||||
NumericExpression <- NumericOperator ws? NumericValue
|
||||
NumericOperator <- [=:] / <[><!][=]?>
|
||||
NumericValue <- [0-9]+
|
||||
|
||||
)");
|
||||
|
||||
std::once_flag init;
|
||||
|
||||
static void setupParserRules()
|
||||
{
|
||||
auto passthru = [](const peg::SemanticValues &sv) -> Filter { return !sv.empty() ? sv[0].get<Filter>() : nullptr; };
|
||||
auto passthru = [](const peg::SemanticValues &sv) -> Filter {
|
||||
return !sv.empty() ? std::any_cast<Filter>(sv[0]) : nullptr;
|
||||
};
|
||||
|
||||
search["Start"] = passthru;
|
||||
search["QueryPartList"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
return [=](CardData x) {
|
||||
for (int i = 0; i < static_cast<int>(sv.size()); ++i) {
|
||||
if (!sv[i].get<Filter>()(x))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return [=](const CardData &x) {
|
||||
auto matchesFilter = [&x](const std::any &query) { return std::any_cast<Filter>(query)(x); };
|
||||
return std::all_of(sv.begin(), sv.end(), matchesFilter);
|
||||
};
|
||||
};
|
||||
search["ComplexQueryPart"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
return [=](CardData x) {
|
||||
for (int i = 0; i < static_cast<int>(sv.size()); ++i) {
|
||||
if (sv[i].get<Filter>()(x))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return [=](const CardData &x) {
|
||||
auto matchesFilter = [&x](const std::any &query) { return std::any_cast<Filter>(query)(x); };
|
||||
return std::any_of(sv.begin(), sv.end(), matchesFilter);
|
||||
};
|
||||
};
|
||||
search["SomewhatComplexQueryPart"] = passthru;
|
||||
search["QueryPart"] = passthru;
|
||||
search["NotQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
Filter dependent = sv[0].get<Filter>();
|
||||
return [=](CardData x) -> bool { return !dependent(x); };
|
||||
const auto dependent = std::any_cast<Filter>(sv[0]);
|
||||
return [=](const CardData &x) -> bool { return !dependent(x); };
|
||||
};
|
||||
search["TypeQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
StringMatcher matcher = sv[0].get<StringMatcher>();
|
||||
return [=](CardData x) -> bool { return matcher(x->getCardType()); };
|
||||
const auto matcher = std::any_cast<StringMatcher>(sv[0]);
|
||||
return [=](const CardData &x) -> bool { return matcher(x->getCardType()); };
|
||||
};
|
||||
search["SetQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
StringMatcher matcher = sv[0].get<StringMatcher>();
|
||||
return [=](CardData x) -> bool {
|
||||
for (const auto &set : x->getSets().keys()) {
|
||||
if (matcher(set))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
};
|
||||
search["RarityQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
StringMatcher matcher = sv[0].get<StringMatcher>();
|
||||
auto matcher = std::any_cast<StringMatcher>(sv[0]);
|
||||
return [=](const CardData &x) -> bool {
|
||||
for (const auto &cardInfoPerSetList : x->getSets().values()) {
|
||||
for (const auto &set : cardInfoPerSetList) {
|
||||
if (matcher(set.getProperty("rarity")))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
QList<QString> sets = x->getSets().keys();
|
||||
|
||||
auto matchesSet = [&matcher](const QString &set) { return matcher(set); };
|
||||
return std::any_of(sets.begin(), sets.end(), matchesSet);
|
||||
};
|
||||
};
|
||||
search["FormatQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
if (sv.choice() == 0) {
|
||||
QString format = sv[0].get<QString>();
|
||||
return [=](CardData x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == "legal"; };
|
||||
} else {
|
||||
QString format = sv[1].get<QString>();
|
||||
QString legality = sv[0].get<QString>();
|
||||
return [=](CardData x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == legality; };
|
||||
search["Rarity"] = [](const peg::SemanticValues &sv) -> QString {
|
||||
switch (tolower(std::string(sv.sv())[0])) {
|
||||
case 'c':
|
||||
return "common";
|
||||
case 'u':
|
||||
return "uncommon";
|
||||
case 'r':
|
||||
return "rare";
|
||||
case 'm':
|
||||
return "mythic";
|
||||
case 's':
|
||||
return "special";
|
||||
default:
|
||||
return QString::fromStdString(std::string(sv.sv()));
|
||||
}
|
||||
};
|
||||
search["RarityQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
const auto rarity = std::any_cast<QString>(sv[0]);
|
||||
return [=](const CardData &x) -> bool {
|
||||
QList<CardInfoPerSet> infos;
|
||||
for (const auto &setsValue : x->getSets().values()) {
|
||||
for (const auto &cardInfoPerSet : setsValue) {
|
||||
infos.append(cardInfoPerSet);
|
||||
}
|
||||
}
|
||||
|
||||
auto matchesRarity = [&rarity](const CardInfoPerSet &info) { return rarity == info.getProperty("rarity"); };
|
||||
return std::any_of(infos.begin(), infos.end(), matchesRarity);
|
||||
};
|
||||
};
|
||||
|
||||
search["FormatQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
if (sv.choice() == 0) {
|
||||
const auto format = std::any_cast<QString>(sv[0]);
|
||||
return
|
||||
[=](const CardData &x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == "legal"; };
|
||||
}
|
||||
|
||||
const auto format = std::any_cast<QString>(sv[1]);
|
||||
const auto legality = std::any_cast<QString>(sv[0]);
|
||||
return [=](const CardData &x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == legality; };
|
||||
};
|
||||
search["Legality"] = [](const peg::SemanticValues &sv) -> QString {
|
||||
switch (tolower(sv.str()[0])) {
|
||||
switch (tolower(std::string(sv.sv())[0])) {
|
||||
case 'l':
|
||||
return "legal";
|
||||
case 'b':
|
||||
@@ -142,8 +159,8 @@ static void setupParserRules()
|
||||
};
|
||||
|
||||
search["Format"] = [](const peg::SemanticValues &sv) -> QString {
|
||||
if (sv.length() == 1) {
|
||||
switch (tolower(sv.str()[0])) {
|
||||
if (sv.size() == 1) {
|
||||
switch (tolower(std::string(sv.sv())[0])) {
|
||||
case 'm':
|
||||
return "modern";
|
||||
case 's':
|
||||
@@ -159,193 +176,191 @@ static void setupParserRules()
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return QString::fromStdString(sv.str()).toLower();
|
||||
}
|
||||
|
||||
return QString::fromStdString(std::string(sv.sv())).toLower();
|
||||
};
|
||||
search["StringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher {
|
||||
if (sv.choice() == 0) {
|
||||
auto target = sv[0].get<QString>();
|
||||
const auto target = std::any_cast<QString>(sv[0]);
|
||||
return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); };
|
||||
} else {
|
||||
auto target = sv[0].get<QStringList>();
|
||||
return [=](const QString &s) {
|
||||
for (const QString &str : target) {
|
||||
if (s.split(" ").contains(str, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
const auto target = std::any_cast<QStringList>(sv[0]);
|
||||
return [=](const QString &s) {
|
||||
auto containsString = [&s](const QString &str) { return s.split(" ").contains(str, Qt::CaseInsensitive); };
|
||||
return std::any_of(target.begin(), target.end(), containsString);
|
||||
};
|
||||
};
|
||||
|
||||
search["String"] = [](const peg::SemanticValues &sv) -> QString {
|
||||
if (sv.choice() == 0) {
|
||||
return QString::fromStdString(sv.str());
|
||||
} else {
|
||||
return QString::fromStdString(sv.token(0));
|
||||
return QString::fromStdString(std::string(sv.sv()));
|
||||
}
|
||||
|
||||
return QString::fromStdString(std::string(sv.token(0)));
|
||||
};
|
||||
search["FlexStringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher {
|
||||
if (sv.choice() != 1) {
|
||||
auto target = sv[0].get<QStringList>();
|
||||
const auto target = std::any_cast<QStringList>(sv[0]);
|
||||
return [=](const QString &s) {
|
||||
for (const QString &str : target) {
|
||||
if (s.split(" ").contains(str, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
auto containsString = [&s](const QString &str) {
|
||||
return s.split(" ").contains(str, Qt::CaseInsensitive);
|
||||
};
|
||||
return std::any_of(target.begin(), target.end(), containsString);
|
||||
};
|
||||
} else {
|
||||
auto target = sv[0].get<QString>();
|
||||
return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); };
|
||||
}
|
||||
|
||||
const auto target = std::any_cast<QString>(sv[0]);
|
||||
return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); };
|
||||
};
|
||||
search["CompactStringSet"] = search["StringList"] = [](const peg::SemanticValues &sv) -> QStringList {
|
||||
search["CompactStringSet"] = [](const peg::SemanticValues &sv) -> QStringList {
|
||||
QStringList result;
|
||||
for (int i = 0; i < static_cast<int>(sv.size()); ++i) {
|
||||
result.append(sv[i].get<QString>());
|
||||
for (const auto &i : sv) {
|
||||
result.append(std::any_cast<QString>(i));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
search["StringList"] = [](const peg::SemanticValues &sv) -> QStringList {
|
||||
QStringList result;
|
||||
for (const auto &i : sv) {
|
||||
result.append(std::any_cast<QString>(i));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
search["StringListString"] = [](const peg::SemanticValues &sv) -> QString {
|
||||
return QString::fromStdString(sv.str());
|
||||
return QString::fromStdString(std::string(sv.sv()));
|
||||
};
|
||||
|
||||
search["NumericExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher {
|
||||
auto arg = sv[1].get<int>();
|
||||
auto op = sv[0].get<QString>();
|
||||
const auto arg = std::any_cast<int>(sv[1]);
|
||||
const auto op = std::any_cast<QString>(sv[0]);
|
||||
|
||||
if (op == ">")
|
||||
return [=](int s) { return s > arg; };
|
||||
return [=](const int s) { return s > arg; };
|
||||
if (op == ">=")
|
||||
return [=](int s) { return s >= arg; };
|
||||
return [=](const int s) { return s >= arg; };
|
||||
if (op == "<")
|
||||
return [=](int s) { return s < arg; };
|
||||
return [=](const int s) { return s < arg; };
|
||||
if (op == "<=")
|
||||
return [=](int s) { return s <= arg; };
|
||||
return [=](const int s) { return s <= arg; };
|
||||
if (op == "=")
|
||||
return [=](int s) { return s == arg; };
|
||||
return [=](const int s) { return s == arg; };
|
||||
if (op == ":")
|
||||
return [=](int s) { return s == arg; };
|
||||
return [=](const int s) { return s == arg; };
|
||||
if (op == "!=")
|
||||
return [=](int s) { return s != arg; };
|
||||
return [=](const int s) { return s != arg; };
|
||||
return [](int) { return false; };
|
||||
};
|
||||
|
||||
search["NumericValue"] = [](const peg::SemanticValues &sv) -> int {
|
||||
return QString::fromStdString(sv.str()).toInt();
|
||||
return QString::fromStdString(std::string(sv.sv())).toInt();
|
||||
};
|
||||
|
||||
search["NumericOperator"] = [](const peg::SemanticValues &sv) -> QString {
|
||||
return QString::fromStdString(sv.str());
|
||||
return QString::fromStdString(std::string(sv.sv()));
|
||||
};
|
||||
|
||||
search["RegexString"] = [](const peg::SemanticValues &sv) -> StringMatcher {
|
||||
auto target = sv[0].get<QString>();
|
||||
auto target = std::any_cast<QString>(sv[0]);
|
||||
return [=](const QString &s) {
|
||||
auto sanitizedTarget = QString(target);
|
||||
sanitizedTarget.replace("\\\"", "\"");
|
||||
sanitizedTarget.replace("\\'", "'");
|
||||
return s.QString::contains(sanitizedTarget, Qt::CaseInsensitive);
|
||||
return s.contains(sanitizedTarget, Qt::CaseInsensitive);
|
||||
};
|
||||
};
|
||||
|
||||
search["OracleQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
StringMatcher matcher = sv[0].get<StringMatcher>();
|
||||
return [=](CardData x) { return matcher(x->getText()); };
|
||||
const auto matcher = std::any_cast<StringMatcher>(sv[0]);
|
||||
return [=](const CardData &x) { return matcher(x->getText()); };
|
||||
};
|
||||
|
||||
search["ColorQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
QString parts;
|
||||
for (int i = 0; i < static_cast<int>(sv.size()); ++i) {
|
||||
parts += sv[i].get<char>();
|
||||
for (const auto &i : sv) {
|
||||
parts += std::any_cast<char>(i);
|
||||
}
|
||||
bool idenity = sv.tokens[0].first[0] != 'i';
|
||||
if (sv.tokens[1].first[0] == ':') {
|
||||
return [=](CardData x) {
|
||||
QString match = idenity ? x->getColors() : x->getProperty("coloridentity");
|
||||
const bool identity = sv.tokens[0][0] != 'i';
|
||||
if (sv.tokens[1][0] == ':') {
|
||||
return [=](const CardData &x) {
|
||||
QString match = identity ? x->getColors() : x->getProperty("coloridentity");
|
||||
if (parts.contains("m") && match.length() < 2) {
|
||||
return false;
|
||||
} else if (parts == "m") {
|
||||
}
|
||||
if (parts == "m") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parts.contains("c") && match.length() == 0)
|
||||
return true;
|
||||
|
||||
for (const auto &i : match) {
|
||||
if (parts.contains(i))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
} else {
|
||||
return [=](CardData x) {
|
||||
QString match = idenity ? x->getColors() : x->getProperty("colorIdentity");
|
||||
if (parts.contains("m") && match.length() < 2)
|
||||
return false;
|
||||
|
||||
if (parts.contains("c") && match.length() != 0)
|
||||
return false;
|
||||
|
||||
for (const auto &part : parts) {
|
||||
if (!match.contains(part))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &i : match) {
|
||||
if (!parts.contains(i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
auto containsColor = [&parts](const QString &s) { return parts.contains(s); };
|
||||
return std::any_of(match.begin(), match.end(), containsColor);
|
||||
};
|
||||
}
|
||||
|
||||
return [=](const CardData &x) {
|
||||
QString match = identity ? x->getColors() : x->getProperty("colorIdentity");
|
||||
if (parts.contains("m") && match.length() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parts.contains("c") && match.length() != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &part : parts) {
|
||||
if (!match.contains(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto containsColor = [&parts](const QString &s) { return parts.contains(s); };
|
||||
return std::all_of(match.begin(), match.end(), containsColor);
|
||||
};
|
||||
};
|
||||
|
||||
search["CMCQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
NumberMatcher matcher = sv[0].get<NumberMatcher>();
|
||||
return [=](CardData x) -> bool { return matcher(x->getProperty("cmc").toInt()); };
|
||||
const auto matcher = std::any_cast<NumberMatcher>(sv[0]);
|
||||
return [=](const CardData &x) -> bool { return matcher(x->getProperty("cmc").toInt()); };
|
||||
};
|
||||
search["PowerQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
NumberMatcher matcher = sv[0].get<NumberMatcher>();
|
||||
return [=](CardData x) -> bool { return matcher(x->getPowTough().split("/")[0].toInt()); };
|
||||
const auto matcher = std::any_cast<NumberMatcher>(sv[0]);
|
||||
return [=](const CardData &x) -> bool { return matcher(x->getPowTough().split("/")[0].toInt()); };
|
||||
};
|
||||
search["ToughnessQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
NumberMatcher matcher = sv[0].get<NumberMatcher>();
|
||||
return [=](CardData x) -> bool {
|
||||
const auto matcher = std::any_cast<NumberMatcher>(sv[0]);
|
||||
return [=](const CardData &x) -> bool {
|
||||
auto parts = x->getPowTough().split("/");
|
||||
return matcher(parts.length() == 2 ? parts[1].toInt() : 0);
|
||||
};
|
||||
};
|
||||
search["FieldQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
QString field = sv[0].get<QString>();
|
||||
const auto field = std::any_cast<QString>(sv[0]);
|
||||
if (sv.choice() == 0) {
|
||||
StringMatcher matcher = sv[1].get<StringMatcher>();
|
||||
return [=](CardData x) -> bool { return x->hasProperty(field) ? matcher(x->getProperty(field)) : false; };
|
||||
} else {
|
||||
NumberMatcher matcher = sv[1].get<NumberMatcher>();
|
||||
return [=](CardData x) -> bool {
|
||||
return x->hasProperty(field) ? matcher(x->getProperty(field).toInt()) : false;
|
||||
};
|
||||
const auto matcher = std::any_cast<StringMatcher>(sv[1]);
|
||||
return [=](const CardData &x) -> bool { return x->hasProperty(field) && matcher(x->getProperty(field)); };
|
||||
}
|
||||
|
||||
const auto matcher = std::any_cast<NumberMatcher>(sv[1]);
|
||||
return
|
||||
[=](const CardData &x) -> bool { return x->hasProperty(field) && matcher(x->getProperty(field).toInt()); };
|
||||
};
|
||||
search["GenericQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
StringMatcher matcher = sv[0].get<StringMatcher>();
|
||||
return [=](CardData x) { return matcher(x->getName()); };
|
||||
const auto matcher = std::any_cast<StringMatcher>(sv[0]);
|
||||
return [=](const CardData &x) { return matcher(x->getName()); };
|
||||
};
|
||||
|
||||
search["Color"] = [](const peg::SemanticValues &sv) -> char { return "WUBRGU"[sv.choice()]; };
|
||||
search["ColorEx"] = [](const peg::SemanticValues &sv) -> char {
|
||||
return sv.choice() == 0 ? sv[0].get<char>() : *sv.c_str();
|
||||
return sv.choice() == 0 ? std::any_cast<char>(sv[0]) : *std::string(sv.sv()).c_str();
|
||||
};
|
||||
}
|
||||
|
||||
FilterString::FilterString()
|
||||
{
|
||||
result = [](CardData) -> bool { return false; };
|
||||
result = [](const CardData &) -> bool { return false; };
|
||||
_error = "Not initialized";
|
||||
}
|
||||
|
||||
@@ -358,16 +373,16 @@ FilterString::FilterString(const QString &expr)
|
||||
_error = QString();
|
||||
|
||||
if (ba.isEmpty()) {
|
||||
result = [](CardData) -> bool { return true; };
|
||||
result = [](const CardData &) -> bool { return true; };
|
||||
return;
|
||||
}
|
||||
|
||||
search.log = [&](size_t /*ln*/, size_t col, const std::string &msg) {
|
||||
search.set_logger([&](size_t /*ln*/, size_t col, const std::string &msg) {
|
||||
_error = QString("Error at position %1: %2").arg(col).arg(QString::fromStdString(msg));
|
||||
};
|
||||
});
|
||||
|
||||
if (!search.parse(ba.data(), result)) {
|
||||
qDebug().nospace() << "FilterString error for " << expr << "; " << qPrintable(_error);
|
||||
result = [](CardData) -> bool { return false; };
|
||||
result = [](const CardData &) -> bool { return false; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,6 +516,10 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
|
||||
|
||||
aSelectAll = new QAction(this);
|
||||
connect(aSelectAll, SIGNAL(triggered()), this, SLOT(actSelectAll()));
|
||||
aSelectRow = new QAction(this);
|
||||
connect(aSelectRow, SIGNAL(triggered()), this, SLOT(actSelectRow()));
|
||||
aSelectColumn = new QAction(this);
|
||||
connect(aSelectColumn, SIGNAL(triggered()), this, SLOT(actSelectColumn()));
|
||||
|
||||
aPlay = new QAction(this);
|
||||
connect(aPlay, SIGNAL(triggered()), this, SLOT(actPlay()));
|
||||
@@ -841,6 +845,8 @@ void Player::retranslateUi()
|
||||
}
|
||||
|
||||
aSelectAll->setText(tr("&Select All"));
|
||||
aSelectRow->setText(tr("S&elect Row"));
|
||||
aSelectColumn->setText(tr("S&elect Column"));
|
||||
|
||||
aPlay->setText(tr("&Play"));
|
||||
aHide->setText(tr("&Hide"));
|
||||
@@ -930,6 +936,8 @@ void Player::setShortcutsActive()
|
||||
aMoveToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveToExile"));
|
||||
|
||||
aSelectAll->setShortcuts(shortcuts.getShortcut("Player/aSelectAll"));
|
||||
aSelectRow->setShortcuts(shortcuts.getShortcut("Player/aSelectRow"));
|
||||
aSelectColumn->setShortcuts(shortcuts.getShortcut("Player/aSelectColumn"));
|
||||
|
||||
QList<QKeySequence> addCCShortCuts;
|
||||
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCRed"));
|
||||
@@ -1005,6 +1013,10 @@ void Player::setShortcutsActive()
|
||||
game->addAction(aClone);
|
||||
game->addAction(aDrawArrow);
|
||||
game->addAction(aSelectAll);
|
||||
|
||||
// unattach action is only active in card menu if the active card is attached.
|
||||
// make unattach shortcut always active so that it consistently works when multiple cards are selected.
|
||||
game->addAction(aUnattach);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1565,6 +1577,27 @@ void Player::actMoveBottomCardToTop()
|
||||
sendGameCommand(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all cards in the given zone.
|
||||
*
|
||||
* @param zone The zone to select from
|
||||
* @param filter A predicate to filter which cards are selected. Defaults to always returning true.
|
||||
*/
|
||||
static void selectCardsInZone(
|
||||
const CardZone *zone,
|
||||
std::function<bool(const CardItem *)> filter = [](const CardItem *) { return true; })
|
||||
{
|
||||
if (!zone) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &cardItem : zone->getCards()) {
|
||||
if (cardItem && filter(cardItem)) {
|
||||
cardItem->setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Player::actSelectAll()
|
||||
{
|
||||
const CardItem *card = game->getActiveCard();
|
||||
@@ -1572,13 +1605,31 @@ void Player::actSelectAll()
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *zone = card->getZone()) {
|
||||
for (auto &cardItem : zone->getCards()) {
|
||||
if (cardItem) {
|
||||
cardItem->setSelected(true);
|
||||
}
|
||||
}
|
||||
selectCardsInZone(card->getZone());
|
||||
}
|
||||
|
||||
void Player::actSelectRow()
|
||||
{
|
||||
const CardItem *card = game->getActiveCard();
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto isSameRow = [card](const CardItem *cardItem) {
|
||||
return qAbs(card->scenePos().y() - cardItem->scenePos().y()) < 50;
|
||||
};
|
||||
selectCardsInZone(card->getZone(), isSameRow);
|
||||
}
|
||||
|
||||
void Player::actSelectColumn()
|
||||
{
|
||||
const CardItem *card = game->getActiveCard();
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto isSameColumn = [card](const CardItem *cardItem) { return cardItem->x() == card->x(); };
|
||||
selectCardsInZone(card->getZone(), isSameColumn);
|
||||
}
|
||||
|
||||
void Player::actDrawBottomCard()
|
||||
@@ -3461,13 +3512,14 @@ void Player::actAttach()
|
||||
|
||||
void Player::actUnattach()
|
||||
{
|
||||
if (!game->getActiveCard()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QList<const ::google::protobuf::Message *> commandList;
|
||||
for (QGraphicsItem *item : scene()->selectedItems()) {
|
||||
auto *card = static_cast<CardItem *>(item);
|
||||
|
||||
if (!card->getAttachedTo()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *cmd = new Command_AttachCard;
|
||||
cmd->set_start_zone(card->getZone()->getName().toStdString());
|
||||
cmd->set_card_id(card->getId());
|
||||
@@ -3656,6 +3708,7 @@ void Player::updateCardMenu(const CardItem *card)
|
||||
cardMenu->addAction(aClone);
|
||||
cardMenu->addSeparator();
|
||||
cardMenu->addAction(aSelectAll);
|
||||
cardMenu->addAction(aSelectColumn);
|
||||
addRelatedCardView(card, cardMenu);
|
||||
} else if (writeableCard) {
|
||||
if (moveMenu->isEmpty()) {
|
||||
@@ -3714,6 +3767,7 @@ void Player::updateCardMenu(const CardItem *card)
|
||||
cardMenu->addMenu(moveMenu);
|
||||
cardMenu->addSeparator();
|
||||
cardMenu->addAction(aSelectAll);
|
||||
cardMenu->addAction(aSelectRow);
|
||||
|
||||
for (int i = 0; i < aAddCounter.size(); ++i) {
|
||||
cardMenu->addSeparator();
|
||||
@@ -3747,6 +3801,7 @@ void Player::updateCardMenu(const CardItem *card)
|
||||
cardMenu->addMenu(moveMenu);
|
||||
cardMenu->addSeparator();
|
||||
cardMenu->addAction(aSelectAll);
|
||||
cardMenu->addAction(aSelectColumn);
|
||||
|
||||
cardMenu->addSeparator();
|
||||
cardMenu->addAction(aAttach);
|
||||
@@ -3776,6 +3831,9 @@ void Player::updateCardMenu(const CardItem *card)
|
||||
|
||||
cardMenu->addSeparator();
|
||||
cardMenu->addAction(aSelectAll);
|
||||
if (card->getZone()->getIsView()) {
|
||||
cardMenu->addAction(aSelectColumn);
|
||||
}
|
||||
|
||||
addRelatedCardView(card, cardMenu);
|
||||
}
|
||||
|
||||
@@ -186,6 +186,8 @@ public slots:
|
||||
void actMoveBottomCardToTop();
|
||||
|
||||
void actSelectAll();
|
||||
void actSelectRow();
|
||||
void actSelectColumn();
|
||||
|
||||
void actViewLibrary();
|
||||
void actViewHand();
|
||||
@@ -266,7 +268,7 @@ private:
|
||||
QAction *aPlay, *aPlayFacedown, *aHide, *aTap, *aDoesntUntap, *aAttach, *aUnattach, *aDrawArrow, *aSetPT, *aResetPT,
|
||||
*aIncP, *aDecP, *aIncT, *aDecT, *aIncPT, *aDecPT, *aFlowP, *aFlowT, *aSetAnnotation, *aFlip, *aPeek, *aClone,
|
||||
*aMoveToTopLibrary, *aMoveToBottomLibrary, *aMoveToHand, *aMoveToGraveyard, *aMoveToExile,
|
||||
*aMoveToXfromTopOfLibrary, *aSelectAll;
|
||||
*aMoveToXfromTopOfLibrary, *aSelectAll, *aSelectRow, *aSelectColumn;
|
||||
|
||||
bool movingCardsUntil;
|
||||
QTimer *moveTopCardTimer;
|
||||
|
||||
@@ -138,7 +138,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
|
||||
// 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, [=] {
|
||||
connect(lastResizeBeforeVisibleTimer, &QTimer::timeout, this, [=, this] {
|
||||
resizeToZoneContents();
|
||||
disconnect(lastResizeBeforeVisibleTimer);
|
||||
lastResizeBeforeVisibleTimer->deleteLater();
|
||||
|
||||
@@ -61,6 +61,34 @@ static void CockatriceLogger(QtMsgType type, const QMessageLogContext &ctx, cons
|
||||
Logger::getInstance().log(type, ctx, message);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// clang-format off
|
||||
#include <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <tchar.h>
|
||||
#pragma comment(lib, "DbgHelp.lib") // Link the DbgHelp library
|
||||
// clang-format on
|
||||
|
||||
LONG WINAPI CockatriceUnhandledExceptionFilter(EXCEPTION_POINTERS *pExceptionPointers)
|
||||
{
|
||||
HANDLE hDumpFile =
|
||||
CreateFile(_T("cockatrice.crash.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hDumpFile != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
|
||||
dumpInfo.ExceptionPointers = pExceptionPointers;
|
||||
dumpInfo.ThreadId = GetCurrentThreadId();
|
||||
dumpInfo.ClientPointers = TRUE;
|
||||
|
||||
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithFullMemory, &dumpInfo,
|
||||
NULL, NULL);
|
||||
CloseHandle(hDumpFile);
|
||||
}
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
#endif
|
||||
|
||||
void installNewTranslator()
|
||||
{
|
||||
QString lang = SettingsCache::instance().getLang();
|
||||
@@ -93,10 +121,10 @@ void installNewTranslator()
|
||||
QString const generateClientID()
|
||||
{
|
||||
QString macList;
|
||||
foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) {
|
||||
if (interface.hardwareAddress() != "")
|
||||
if (interface.hardwareAddress() != "00:00:00:00:00:00:00:E0")
|
||||
macList += interface.hardwareAddress() + ".";
|
||||
foreach (QNetworkInterface networkInterface, QNetworkInterface::allInterfaces()) {
|
||||
if (networkInterface.hardwareAddress() != "")
|
||||
if (networkInterface.hardwareAddress() != "00:00:00:00:00:00:00:E0")
|
||||
macList += networkInterface.hardwareAddress() + ".";
|
||||
}
|
||||
QString strClientID = QCryptographicHash::hash(macList.toUtf8(), QCryptographicHash::Sha1).toHex().right(15);
|
||||
return strClientID;
|
||||
@@ -104,9 +132,13 @@ QString const generateClientID()
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
SetUnhandledExceptionFilter(CockatriceUnhandledExceptionFilter);
|
||||
#endif
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
|
||||
QObject::connect(&app, &QApplication::lastWindowClosed, &app, &QApplication::quit);
|
||||
|
||||
qInstallMessageHandler(CockatriceLogger);
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
@@ -253,7 +253,7 @@ void UserInfoBox::actPassword()
|
||||
connect(pend,
|
||||
// we need qoverload here in order to select the right version of this function
|
||||
QOverload<const Response &, const CommandContainer &, const QVariant &>::of(&PendingCommand::finished),
|
||||
this, [=](const Response &response, const CommandContainer &, const QVariant &) {
|
||||
this, [=, this](const Response &response, const CommandContainer &, const QVariant &) {
|
||||
if (response.response_code() == Response::RespOk) {
|
||||
changePassword(oldPassword, newPassword);
|
||||
} else {
|
||||
|
||||
@@ -258,6 +258,8 @@ SettingsCache::SettingsCache()
|
||||
invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool();
|
||||
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 4).toInt();
|
||||
tapAnimation = settings->value("cards/tapanimation", true).toBool();
|
||||
autoRotateSidewaysLayoutCards = settings->value("cards/autorotatesidewayslayoutcards", true).toBool();
|
||||
|
||||
openDeckInNewTab = settings->value("editor/openDeckInNewTab", false).toBool();
|
||||
rewindBufferingMs = settings->value("replay/rewindBufferingMs", 200).toInt();
|
||||
chatMention = settings->value("chat/mention", true).toBool();
|
||||
@@ -301,6 +303,7 @@ SettingsCache::SettingsCache()
|
||||
spectatorsCanTalk = settings->value("game/spectatorscantalk", false).toBool();
|
||||
spectatorsCanSeeEverything = settings->value("game/spectatorscanseeeverything", false).toBool();
|
||||
createGameAsSpectator = settings->value("game/creategameasspectator", false).toBool();
|
||||
defaultStartingLifeTotal = settings->value("game/defaultstartinglifetotal", 20).toInt();
|
||||
rememberGameSettings = settings->value("game/remembergamesettings", true).toBool();
|
||||
clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString();
|
||||
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
|
||||
@@ -628,6 +631,12 @@ void SettingsCache::setTapAnimation(QT_STATE_CHANGED_T _tapAnimation)
|
||||
settings->setValue("cards/tapanimation", tapAnimation);
|
||||
}
|
||||
|
||||
void SettingsCache::setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T _autoRotateSidewaysLayoutCards)
|
||||
{
|
||||
autoRotateSidewaysLayoutCards = static_cast<bool>(_autoRotateSidewaysLayoutCards);
|
||||
settings->setValue("cards/autorotatesidewayslayoutcards", autoRotateSidewaysLayoutCards);
|
||||
}
|
||||
|
||||
void SettingsCache::setOpenDeckInNewTab(QT_STATE_CHANGED_T _openDeckInNewTab)
|
||||
{
|
||||
openDeckInNewTab = static_cast<bool>(_openDeckInNewTab);
|
||||
@@ -1089,6 +1098,12 @@ void SettingsCache::setCreateGameAsSpectator(const bool _createGameAsSpectator)
|
||||
settings->setValue("game/creategameasspectator", createGameAsSpectator);
|
||||
}
|
||||
|
||||
void SettingsCache::setDefaultStartingLifeTotal(const int _defaultStartingLifeTotal)
|
||||
{
|
||||
defaultStartingLifeTotal = _defaultStartingLifeTotal;
|
||||
settings->setValue("game/defaultstartinglifetotal", defaultStartingLifeTotal);
|
||||
};
|
||||
|
||||
void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings)
|
||||
{
|
||||
rememberGameSettings = _rememberGameSettings;
|
||||
|
||||
@@ -120,6 +120,7 @@ private:
|
||||
bool invertVerticalCoordinate;
|
||||
int minPlayersForMultiColumnLayout;
|
||||
bool tapAnimation;
|
||||
bool autoRotateSidewaysLayoutCards;
|
||||
bool openDeckInNewTab;
|
||||
int rewindBufferingMs;
|
||||
bool chatMention;
|
||||
@@ -163,6 +164,7 @@ private:
|
||||
bool spectatorsCanTalk;
|
||||
bool spectatorsCanSeeEverything;
|
||||
bool createGameAsSpectator;
|
||||
int defaultStartingLifeTotal;
|
||||
int keepalive;
|
||||
int timeout;
|
||||
void translateLegacySettings();
|
||||
@@ -368,6 +370,10 @@ public:
|
||||
{
|
||||
return tapAnimation;
|
||||
}
|
||||
bool getAutoRotateSidewaysLayoutCards() const
|
||||
{
|
||||
return autoRotateSidewaysLayoutCards;
|
||||
}
|
||||
bool getOpenDeckInNewTab() const
|
||||
{
|
||||
return openDeckInNewTab;
|
||||
@@ -515,6 +521,10 @@ public:
|
||||
{
|
||||
return spectatorsCanSeeEverything;
|
||||
}
|
||||
int getDefaultStartingLifeTotal() const
|
||||
{
|
||||
return defaultStartingLifeTotal;
|
||||
}
|
||||
bool getCreateGameAsSpectator() const
|
||||
{
|
||||
return createGameAsSpectator;
|
||||
@@ -644,6 +654,7 @@ public slots:
|
||||
void setInvertVerticalCoordinate(QT_STATE_CHANGED_T _invertVerticalCoordinate);
|
||||
void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout);
|
||||
void setTapAnimation(QT_STATE_CHANGED_T _tapAnimation);
|
||||
void setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T _autoRotateSidewaysLayoutCards);
|
||||
void setOpenDeckInNewTab(QT_STATE_CHANGED_T _openDeckInNewTab);
|
||||
void setRewindBufferingMs(int _rewindBufferingMs);
|
||||
void setChatMention(QT_STATE_CHANGED_T _chatMention);
|
||||
@@ -679,6 +690,7 @@ public slots:
|
||||
void setSpectatorsCanTalk(const bool _spectatorsCanTalk);
|
||||
void setSpectatorsCanSeeEverything(const bool _spectatorsCanSeeEverything);
|
||||
void setCreateGameAsSpectator(const bool _createGameAsSpectator);
|
||||
void setDefaultStartingLifeTotal(const int _defaultStartingLifeTotal);
|
||||
void setRememberGameSettings(const bool _rememberGameSettings);
|
||||
void setNotifyAboutUpdate(QT_STATE_CHANGED_T _notifyaboutupdate);
|
||||
void setNotifyAboutNewVersion(QT_STATE_CHANGED_T _notifyaboutnewversion);
|
||||
|
||||
132
cockatrice/src/settings/shortcut_treeview.cpp
Normal file
132
cockatrice/src/settings/shortcut_treeview.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "shortcut_treeview.h"
|
||||
|
||||
#include "cache_settings.h"
|
||||
#include "shortcuts_settings.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
ShortcutTreeView::ShortcutTreeView(QWidget *parent) : QTreeView(parent)
|
||||
{
|
||||
// model
|
||||
shortcutsModel = new QStandardItemModel(this);
|
||||
shortcutsModel->setColumnCount(3);
|
||||
populateShortcutsModel();
|
||||
|
||||
// filter proxy
|
||||
proxyModel = new QSortFilterProxyModel(this);
|
||||
proxyModel->setRecursiveFilteringEnabled(true);
|
||||
proxyModel->setSourceModel(shortcutsModel);
|
||||
proxyModel->setDynamicSortFilter(true);
|
||||
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
proxyModel->setFilterKeyColumn(0);
|
||||
|
||||
QTreeView::setModel(proxyModel);
|
||||
|
||||
// treeview
|
||||
hideColumn(2);
|
||||
|
||||
setUniformRowHeights(true);
|
||||
setAlternatingRowColors(true);
|
||||
|
||||
setSortingEnabled(true);
|
||||
proxyModel->sort(0, Qt::AscendingOrder);
|
||||
|
||||
header()->setSectionResizeMode(QHeaderView::Interactive);
|
||||
header()->setSortIndicator(0, Qt::AscendingOrder);
|
||||
setSelectionMode(SingleSelection);
|
||||
setSelectionBehavior(SelectRows);
|
||||
|
||||
expandAll();
|
||||
|
||||
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
||||
&ShortcutTreeView::refreshShortcuts);
|
||||
}
|
||||
|
||||
void ShortcutTreeView::populateShortcutsModel()
|
||||
{
|
||||
QHash<QString, QStandardItem *> parentItems;
|
||||
QStandardItem *curParent = nullptr;
|
||||
for (const auto &key : SettingsCache::instance().shortcuts().getAllShortcutKeys()) {
|
||||
QString name = SettingsCache::instance().shortcuts().getShortcut(key).getName();
|
||||
QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName();
|
||||
QString shortcut = SettingsCache::instance().shortcuts().getShortcutString(key);
|
||||
|
||||
if (parentItems.contains(group)) {
|
||||
curParent = parentItems.value(group);
|
||||
} else {
|
||||
curParent = new QStandardItem(group);
|
||||
static QFont font = curParent->font();
|
||||
font.setBold(true);
|
||||
curParent->setFont(font);
|
||||
parentItems.insert(group, curParent);
|
||||
}
|
||||
|
||||
QList<QStandardItem *> list = {};
|
||||
list << new QStandardItem(name) << new QStandardItem(shortcut) << new QStandardItem(key);
|
||||
curParent->appendRow(list);
|
||||
}
|
||||
|
||||
for (const auto &parent : parentItems) {
|
||||
shortcutsModel->appendRow(parent);
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutTreeView::retranslateUi()
|
||||
{
|
||||
shortcutsModel->setHeaderData(0, Qt::Horizontal, tr("Action"));
|
||||
shortcutsModel->setHeaderData(1, Qt::Horizontal, tr("Shortcut"));
|
||||
refreshShortcuts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops over the model and reloads all rows
|
||||
*/
|
||||
static void loopOverModel(QAbstractItemModel *model, const QModelIndex &parent = QModelIndex())
|
||||
{
|
||||
for (int r = 0; r < model->rowCount(parent); ++r) {
|
||||
const auto friendlyNameIndex = model->index(r, 0, parent);
|
||||
|
||||
if (model->hasChildren(friendlyNameIndex)) {
|
||||
const auto childIndex = model->index(0, 2, friendlyNameIndex);
|
||||
const auto key = model->data(childIndex).toString();
|
||||
const auto shortcutGroupName = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName();
|
||||
model->setData(friendlyNameIndex, shortcutGroupName);
|
||||
|
||||
loopOverModel(model, friendlyNameIndex);
|
||||
} else {
|
||||
const auto shortcutSequenceIndex = model->index(r, 1, parent);
|
||||
const auto keyIndex = model->index(r, 2, parent);
|
||||
const auto key = model->data(keyIndex).toString();
|
||||
|
||||
const auto shortcutKey = SettingsCache::instance().shortcuts().getShortcut(key).getName();
|
||||
const auto shortcutSequence = SettingsCache::instance().shortcuts().getShortcutString(key);
|
||||
model->setData(friendlyNameIndex, shortcutKey);
|
||||
model->setData(shortcutSequenceIndex, shortcutSequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutTreeView::refreshShortcuts()
|
||||
{
|
||||
loopOverModel(shortcutsModel);
|
||||
}
|
||||
|
||||
void ShortcutTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex & /* previous */)
|
||||
{
|
||||
QTreeView::scrollTo(current, QAbstractItemView::EnsureVisible);
|
||||
if (current.parent().isValid()) {
|
||||
auto shortcutName = model()->data(model()->index(current.row(), 2, current.parent())).toString();
|
||||
emit currentItemChanged(shortcutName);
|
||||
} else {
|
||||
// emit empty string if the selection is a category header
|
||||
emit currentItemChanged("");
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutTreeView::updateSearchString(const QString &searchString)
|
||||
{
|
||||
proxyModel->setFilterFixedString(searchString);
|
||||
expandAll();
|
||||
}
|
||||
34
cockatrice/src/settings/shortcut_treeview.h
Normal file
34
cockatrice/src/settings/shortcut_treeview.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef SHORTCUT_TREEVIEW_H
|
||||
#define SHORTCUT_TREEVIEW_H
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QStandardItemModel>
|
||||
#include <QTreeView>
|
||||
|
||||
class QSortFilterProxyModel;
|
||||
class ShortcutTreeView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ShortcutTreeView(QWidget *parent);
|
||||
void retranslateUi();
|
||||
|
||||
signals:
|
||||
void currentItemChanged(const QString &shortcut);
|
||||
|
||||
public slots:
|
||||
void updateSearchString(const QString &searchString);
|
||||
|
||||
private:
|
||||
QStandardItemModel *shortcutsModel;
|
||||
QSortFilterProxyModel *proxyModel;
|
||||
void populateShortcutsModel();
|
||||
|
||||
private slots:
|
||||
void refreshShortcuts();
|
||||
|
||||
protected:
|
||||
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override;
|
||||
};
|
||||
|
||||
#endif // SHORTCUT_TREEVIEW_H
|
||||
@@ -114,6 +114,17 @@ QString ShortcutsSettings::getShortcutString(const QString &name) const
|
||||
return stringifySequence(getShortcut(name));
|
||||
}
|
||||
|
||||
QString ShortcutsSettings::getShortcutFriendlyName(const QString &shortcutName) const
|
||||
{
|
||||
for (auto it = defaultShortCuts.cbegin(); it != defaultShortCuts.cend(); ++it) {
|
||||
if (shortcutName == it.key()) {
|
||||
return it.value().getName();
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QString ShortcutsSettings::stringifySequence(const QList<QKeySequence> &Sequence) const
|
||||
{
|
||||
QStringList stringSequence;
|
||||
@@ -150,9 +161,9 @@ void ShortcutsSettings::setShortcuts(const QString &name, const QKeySequence &Se
|
||||
setShortcuts(name, QList<QKeySequence>{Sequence});
|
||||
}
|
||||
|
||||
void ShortcutsSettings::setShortcuts(const QString &name, const QString &Sequences)
|
||||
void ShortcutsSettings::setShortcuts(const QString &name, const QString &sequences)
|
||||
{
|
||||
setShortcuts(name, parseSequenceString(Sequences));
|
||||
setShortcuts(name, parseSequenceString(sequences));
|
||||
}
|
||||
|
||||
void ShortcutsSettings::resetAllShortcuts()
|
||||
@@ -177,33 +188,45 @@ void ShortcutsSettings::clearAllShortcuts()
|
||||
emit shortCutChanged();
|
||||
}
|
||||
|
||||
bool ShortcutsSettings::isKeyAllowed(const QString &name, const QString &Sequences) const
|
||||
bool ShortcutsSettings::isKeyAllowed(const QString &name, const QString &sequences) const
|
||||
{
|
||||
// if the shortcut is not to be used in deck-editor then it doesn't matter
|
||||
if (name.startsWith("Player") || name.startsWith("Replays")) {
|
||||
return true;
|
||||
}
|
||||
QString checkSequence = Sequences.split(sep).last();
|
||||
QString checkSequence = sequences.split(sep).last();
|
||||
QStringList forbiddenKeys{"Del", "Backspace", "Down", "Up", "Left", "Right",
|
||||
"Return", "Enter", "Menu", "Ctrl+Alt+-", "Ctrl+Alt+=", "Ctrl+Alt+[",
|
||||
"Ctrl+Alt+]", "Tab", "Space", "Shift+S", "Shift+Left", "Shift+Right"};
|
||||
return !forbiddenKeys.contains(checkSequence);
|
||||
}
|
||||
|
||||
bool ShortcutsSettings::isValid(const QString &name, const QString &Sequences) const
|
||||
/**
|
||||
* Checks that the shortcut doesn't overlap with an existing shortcut
|
||||
*
|
||||
* @param name The name of the shortcut
|
||||
* @param sequences The shortcut key sequence
|
||||
* @return Whether the shortcut is valid.
|
||||
*/
|
||||
bool ShortcutsSettings::isValid(const QString &name, const QString &sequences) const
|
||||
{
|
||||
QString checkSequence = Sequences.split(sep).last();
|
||||
return findOverlaps(name, sequences).isEmpty();
|
||||
}
|
||||
|
||||
QStringList ShortcutsSettings::findOverlaps(const QString &name, const QString &sequences) const
|
||||
{
|
||||
QString checkSequence = sequences.split(sep).last();
|
||||
QString checkKey = name.left(name.indexOf("/"));
|
||||
|
||||
QList<QString> allKeys = shortCuts.keys();
|
||||
for (const auto &key : allKeys) {
|
||||
QStringList overlaps;
|
||||
for (const auto &key : shortCuts.keys()) {
|
||||
if (key.startsWith(checkKey) || key.startsWith("MainWindow") || checkKey.startsWith("MainWindow")) {
|
||||
QString storedSequence = stringifySequence(shortCuts.value(key));
|
||||
QStringList stringSequences = storedSequence.split(sep);
|
||||
if (stringSequences.contains(checkSequence)) {
|
||||
return false;
|
||||
if (storedSequence.split(sep).contains(checkSequence)) {
|
||||
overlaps.append(getShortcutFriendlyName(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return overlaps;
|
||||
}
|
||||
@@ -2,9 +2,7 @@
|
||||
#define SHORTCUTSSETTINGS_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QHash>
|
||||
#include <QKeySequence>
|
||||
#include <QObject>
|
||||
#include <QSettings>
|
||||
|
||||
class ShortcutGroup
|
||||
@@ -80,18 +78,18 @@ public:
|
||||
class ShortcutKey : public QList<QKeySequence>
|
||||
{
|
||||
public:
|
||||
ShortcutKey(const QString &_name = QString(),
|
||||
QList<QKeySequence> _sequence = QList<QKeySequence>(),
|
||||
ShortcutGroup::Groups _group = ShortcutGroup::Main_Window);
|
||||
void setSequence(QList<QKeySequence> _sequence)
|
||||
explicit ShortcutKey(const QString &_name = QString(),
|
||||
QList _sequence = QList(),
|
||||
ShortcutGroup::Groups _group = ShortcutGroup::Main_Window);
|
||||
void setSequence(const QList &_sequence)
|
||||
{
|
||||
QList<QKeySequence>::operator=(_sequence);
|
||||
QList::operator=(_sequence);
|
||||
};
|
||||
const QString getName() const
|
||||
QString getName() const
|
||||
{
|
||||
return QApplication::translate("shortcutsTab", name.toUtf8().data());
|
||||
};
|
||||
const QString getGroupName() const
|
||||
QString getGroupName() const
|
||||
{
|
||||
return ShortcutGroup::getGroupName(group);
|
||||
};
|
||||
@@ -105,13 +103,14 @@ class ShortcutsSettings : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShortcutsSettings(const QString &settingsFilePath, QObject *parent = nullptr);
|
||||
explicit ShortcutsSettings(const QString &settingsFilePath, QObject *parent = nullptr);
|
||||
|
||||
ShortcutKey getDefaultShortcut(const QString &name) const;
|
||||
ShortcutKey getShortcut(const QString &name) const;
|
||||
QKeySequence getSingleShortcut(const QString &name) const;
|
||||
QString getDefaultShortcutString(const QString &name) const;
|
||||
QString getShortcutString(const QString &name) const;
|
||||
QString getShortcutFriendlyName(const QString &shortcutName) const;
|
||||
QList<QString> getAllShortcutKeys() const
|
||||
{
|
||||
return shortCuts.keys();
|
||||
@@ -119,10 +118,11 @@ public:
|
||||
|
||||
void setShortcuts(const QString &name, const QList<QKeySequence> &Sequence);
|
||||
void setShortcuts(const QString &name, const QKeySequence &Sequence);
|
||||
void setShortcuts(const QString &name, const QString &Sequences);
|
||||
void setShortcuts(const QString &name, const QString &sequences);
|
||||
|
||||
bool isKeyAllowed(const QString &name, const QString &Sequences) const;
|
||||
bool isValid(const QString &name, const QString &Sequences) const;
|
||||
bool isKeyAllowed(const QString &name, const QString &sequences) const;
|
||||
bool isValid(const QString &name, const QString &sequences) const;
|
||||
QStringList findOverlaps(const QString &name, const QString &sequences) const;
|
||||
|
||||
void resetAllShortcuts();
|
||||
void clearAllShortcuts();
|
||||
@@ -456,6 +456,12 @@ private:
|
||||
{"Player/aSelectAll", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Select All Cards in Zone"),
|
||||
parseSequenceString("Ctrl+A"),
|
||||
ShortcutGroup::Playing_Area)},
|
||||
{"Player/aSelectRow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Select All Cards in Row"),
|
||||
parseSequenceString("Ctrl+Shift+X"),
|
||||
ShortcutGroup::Playing_Area)},
|
||||
{"Player/aSelectColumn", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Select All Cards in Column"),
|
||||
parseSequenceString("Ctrl+Shift+C"),
|
||||
ShortcutGroup::Playing_Area)},
|
||||
{"Player/aMoveToBottomLibrary", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom of Library"),
|
||||
parseSequenceString("Ctrl+B"),
|
||||
ShortcutGroup::Move_selected)},
|
||||
|
||||
@@ -154,26 +154,49 @@ int SequenceEdit::translateModifiers(Qt::KeyboardModifiers state, const QString
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*Validates that shortcut is valid (is a valid shortcut key sequence and doesn't conflict with any other shortcuts).
|
||||
*Displays warning messages if it's not valid.
|
||||
*
|
||||
* @param sequence The shortcut key sequence
|
||||
* @return True if the sequence isn't already self-contained
|
||||
*/
|
||||
bool SequenceEdit::validateShortcut(const QKeySequence &sequence)
|
||||
{
|
||||
if (sequence.isEmpty() || !valid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &shortcutsSettings = SettingsCache::instance().shortcuts();
|
||||
const QString sequenceString = sequence.toString();
|
||||
|
||||
if (!shortcutsSettings.isKeyAllowed(shortcutName, sequenceString)) {
|
||||
QToolTip::showText(lineEdit->mapToGlobal(QPoint()), tr("Invalid key"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!shortcutsSettings.isValid(shortcutName, sequenceString)) {
|
||||
auto overlaps = shortcutsSettings.findOverlaps(shortcutName, sequenceString);
|
||||
QToolTip::showText(lineEdit->mapToGlobal(QPoint()),
|
||||
tr("Shortcut already in use by:") + " " + overlaps.join(", "));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!lineEdit->text().isEmpty()) {
|
||||
if (lineEdit->text().contains(sequenceString)) {
|
||||
return false;
|
||||
}
|
||||
lineEdit->setText(lineEdit->text() + ";");
|
||||
}
|
||||
lineEdit->setText(lineEdit->text() + sequenceString);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SequenceEdit::finishShortcut()
|
||||
{
|
||||
QKeySequence sequence(keys);
|
||||
if (!sequence.isEmpty() && valid) {
|
||||
QString sequenceString = sequence.toString();
|
||||
if (SettingsCache::instance().shortcuts().isKeyAllowed(shortcutName, sequenceString)) {
|
||||
if (SettingsCache::instance().shortcuts().isValid(shortcutName, sequenceString)) {
|
||||
if (!lineEdit->text().isEmpty()) {
|
||||
if (lineEdit->text().contains(sequenceString)) {
|
||||
return;
|
||||
}
|
||||
lineEdit->setText(lineEdit->text() + ";");
|
||||
}
|
||||
lineEdit->setText(lineEdit->text() + sequenceString);
|
||||
} else {
|
||||
QToolTip::showText(lineEdit->mapToGlobal(QPoint()), tr("Shortcut already in use"));
|
||||
}
|
||||
} else {
|
||||
QToolTip::showText(lineEdit->mapToGlobal(QPoint()), tr("Invalid key"));
|
||||
}
|
||||
if (!validateShortcut(QKeySequence(keys))) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentKey = 0;
|
||||
|
||||
@@ -36,6 +36,7 @@ private:
|
||||
|
||||
void processKey(QKeyEvent *e);
|
||||
int translateModifiers(Qt::KeyboardModifiers state, const QString &text);
|
||||
bool validateShortcut(const QKeySequence &sequence);
|
||||
void finishShortcut();
|
||||
void updateSettings();
|
||||
};
|
||||
|
||||
@@ -51,9 +51,9 @@ double Expression::eval(const peg::Ast &ast)
|
||||
{
|
||||
const auto &nodes = ast.nodes;
|
||||
if (ast.name == "NUMBER") {
|
||||
return stod(ast.token);
|
||||
return stod(std::string(ast.token));
|
||||
} else if (ast.name == "FUNCTION") {
|
||||
QString name = QString::fromStdString(nodes[0]->token);
|
||||
QString name = QString::fromStdString(std::string(nodes[0]->token));
|
||||
if (!fns.contains(name))
|
||||
return 0;
|
||||
return fns[name](eval(*nodes[1]));
|
||||
|
||||
5688
common/lib/peglib.h
5688
common/lib/peglib.h
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,7 @@ message Command_CreateGame {
|
||||
repeated uint32 game_type_ids = 10;
|
||||
optional bool join_as_judge = 11;
|
||||
optional bool join_as_spectator = 12;
|
||||
optional uint32 starting_life_total = 13;
|
||||
}
|
||||
|
||||
message Command_JoinGame {
|
||||
|
||||
@@ -61,15 +61,16 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo,
|
||||
bool _spectatorsNeedPassword,
|
||||
bool _spectatorsCanTalk,
|
||||
bool _spectatorsSeeEverything,
|
||||
int _startingLifeTotal,
|
||||
Server_Room *_room)
|
||||
: QObject(), room(_room), nextPlayerId(0), hostId(0), creatorInfo(new ServerInfo_User(_creatorInfo)),
|
||||
gameStarted(false), gameClosed(false), gameId(_gameId), password(_password), maxPlayers(_maxPlayers),
|
||||
gameTypes(_gameTypes), activePlayer(-1), activePhase(-1), onlyBuddies(_onlyBuddies),
|
||||
onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed),
|
||||
spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk),
|
||||
spectatorsSeeEverything(_spectatorsSeeEverything), inactivityCounter(0), startTimeOfThisGame(0),
|
||||
secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()),
|
||||
pingClock(nullptr),
|
||||
spectatorsSeeEverything(_spectatorsSeeEverything), startingLifeTotal(_startingLifeTotal), inactivityCounter(0),
|
||||
startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false),
|
||||
startTime(QDateTime::currentDateTime()), pingClock(nullptr),
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
gameMutex()
|
||||
#else
|
||||
|
||||
@@ -66,6 +66,7 @@ private:
|
||||
bool spectatorsNeedPassword;
|
||||
bool spectatorsCanTalk;
|
||||
bool spectatorsSeeEverything;
|
||||
int startingLifeTotal;
|
||||
int inactivityCounter;
|
||||
int startTimeOfThisGame, secondsElapsed;
|
||||
bool firstGameStarted;
|
||||
@@ -105,6 +106,7 @@ public:
|
||||
bool _spectatorsNeedPassword,
|
||||
bool _spectatorsCanTalk,
|
||||
bool _spectatorsSeeEverything,
|
||||
int startingLifeTotal,
|
||||
Server_Room *parent);
|
||||
~Server_Game();
|
||||
Server_Room *getRoom() const
|
||||
@@ -162,6 +164,10 @@ public:
|
||||
{
|
||||
return spectatorsSeeEverything;
|
||||
}
|
||||
int getStartingLifeTotal() const
|
||||
{
|
||||
return startingLifeTotal;
|
||||
}
|
||||
Response::ResponseCode
|
||||
checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions, bool asJudge);
|
||||
bool containsUser(const QString &userName) const;
|
||||
|
||||
@@ -178,7 +178,7 @@ void Server_Player::setupZones()
|
||||
addZone(new Server_CardZone(this, "grave", false, ServerInfo_Zone::PublicZone));
|
||||
addZone(new Server_CardZone(this, "rfg", false, ServerInfo_Zone::PublicZone));
|
||||
|
||||
addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, 20));
|
||||
addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, game->getStartingLifeTotal()));
|
||||
addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0));
|
||||
addCounter(new Server_Counter(2, "u", makeColor(150, 150, 255), 20, 0));
|
||||
addCounter(new Server_Counter(3, "b", makeColor(150, 150, 150), 20, 0));
|
||||
@@ -450,9 +450,13 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges,
|
||||
if (!card) {
|
||||
return Response::RespNameNotFound;
|
||||
}
|
||||
if (card->getParentCard()) {
|
||||
|
||||
// do not allow attached cards to move around on the table
|
||||
if (card->getParentCard() && targetzone->getName() == "table") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// do not allow cards with attachments to stack with other cards
|
||||
if (!card->getAttachedCards().isEmpty() && !targetzone->isColumnEmpty(xCoord, yCoord)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -815,13 +815,14 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room
|
||||
}
|
||||
|
||||
QString description = nameFromStdString(cmd.description());
|
||||
int startingLifeTotal = cmd.has_starting_life_total() ? cmd.starting_life_total() : 20;
|
||||
|
||||
// When server doesn't permit registered users to exist, do not honor only-reg setting
|
||||
bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers());
|
||||
Server_Game *game = new Server_Game(
|
||||
copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes,
|
||||
cmd.only_buddies(), onlyRegisteredUsers, cmd.spectators_allowed(), cmd.spectators_need_password(),
|
||||
cmd.spectators_can_talk(), cmd.spectators_see_everything(), room);
|
||||
cmd.spectators_can_talk(), cmd.spectators_see_everything(), startingLifeTotal, room);
|
||||
|
||||
game->addPlayer(this, rc, asSpectator, asJudge, false);
|
||||
room->addGame(game);
|
||||
|
||||
@@ -196,6 +196,9 @@ void SettingsCache::setMinPlayersForMultiColumnLayout(int /* _minPlayersForMulti
|
||||
void SettingsCache::setTapAnimation(QT_STATE_CHANGED_T /* _tapAnimation */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T /* _autoRotateSidewaysLayoutCards */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setOpenDeckInNewTab(QT_STATE_CHANGED_T /* _openDeckInNewTab */)
|
||||
{
|
||||
}
|
||||
@@ -300,6 +303,9 @@ void SettingsCache::setSpectatorsCanSeeEverything(const bool /* _spectatorsCanSe
|
||||
void SettingsCache::setCreateGameAsSpectator(const bool /* _createGameAsSpectator */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setDefaultStartingLifeTotal(const int /* _startingLifeTotal */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -200,6 +200,9 @@ void SettingsCache::setMinPlayersForMultiColumnLayout(int /* _minPlayersForMulti
|
||||
void SettingsCache::setTapAnimation(QT_STATE_CHANGED_T /* _tapAnimation */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T /* _autoRotateSidewaysLayoutCards */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setOpenDeckInNewTab(QT_STATE_CHANGED_T /* _openDeckInNewTab */)
|
||||
{
|
||||
}
|
||||
@@ -304,6 +307,9 @@ void SettingsCache::setSpectatorsCanSeeEverything(const bool /* _spectatorsCanSe
|
||||
void SettingsCache::setCreateGameAsSpectator(const bool /* _createGameAsSpectator */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setDefaultStartingLifeTotal(const int /* _startingLifeTotal */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */)
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user