Merge branch 'master' into mod_notify_onbanwarn

This commit is contained in:
woogerboy21 2016-01-31 18:43:24 -05:00
commit 54ebb6a508
62 changed files with 31523 additions and 21910 deletions

View file

@ -14,10 +14,7 @@
# Cockatrice
Cockatrice is an open-source multiplatform software for playing card games,
such as Magic: The Gathering, over a network. It is fully client-server based
to prevent any kind of cheating, though it supports single-player games without
a network interface as well. Both client and server are written in Qt, supporting both Qt4 and Qt5.<br>
Cockatrice is an open-source multiplatform supported program for playing tabletop card games over a network. The program's server design prevents any kind of client modifications to gain an unfair advantage in a game. The client also has a built in single-player mode where you can brew without being connected to a server. This project is written in C++/Qt with support for both Qt4 and Qt5.<br>
# Downloads

View file

@ -2,8 +2,10 @@ set(VERSION_STRING_CPP "${PROJECT_BINARY_DIR}/version_string.cpp")
set(VERSION_STRING_H "${PROJECT_BINARY_DIR}/version_string.h")
INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR})
set( hstring "extern const char *VERSION_STRING\;\n" )
set( cppstring "const char * VERSION_STRING = \"${PROJECT_VERSION_FRIENDLY}\"\;\n")
set( hstring "extern const char *VERSION_STRING\;
extern const char *VERSION_DATE\;\n" )
set( cppstring "const char *VERSION_STRING = \"${PROJECT_VERSION_FRIENDLY}\"\;
const char *VERSION_DATE = \"${GIT_COMMIT_DATE_FRIENDLY}\"\;\n")
file(WRITE ${PROJECT_BINARY_DIR}/version_string.cpp.txt ${cppstring} )
file(WRITE ${PROJECT_BINARY_DIR}/version_string.h.txt ${hstring} )

View file

@ -16,7 +16,8 @@ SET(cockatrice_SOURCES
src/dlg_edit_tokens.cpp
src/dlg_edit_user.cpp
src/dlg_register.cpp
src/abstractclient.cpp
src/dlg_update.cpp
src/abstractclient.cpp
src/remoteclient.cpp
src/main.cpp
src/window_main.cpp
@ -107,6 +108,8 @@ SET(cockatrice_SOURCES
src/settings/messagesettings.cpp
src/settings/gamefilterssettings.cpp
src/settings/layoutssettings.cpp
src/update_checker.cpp
src/update_downloader.cpp
${VERSION_STRING_CPP}
)

View file

@ -0,0 +1,181 @@
#define HUMAN_DOWNLOAD_URL "https://bintray.com/cockatrice/Cockatrice/Cockatrice/_latestVersion"
#define API_DOWNLOAD_BASE_URL "https://dl.bintray.com/cockatrice/Cockatrice/"
#define DATE_LENGTH 10
#define MAX_DATE_LENGTH 100
#define SHORT_SHA1_HASH_LENGTH 7
#include <QtNetwork>
#include <QProgressDialog>
#include <QDesktopServices>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QLabel>
#include <QProgressBar>
#include <QApplication>
#include "dlg_update.h"
#include "window_main.h"
DlgUpdate::DlgUpdate(QWidget *parent) : QDialog(parent) {
//Handle layout
text = new QLabel(this);
progress = new QProgressBar(this);
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
ok = new QPushButton("Ok", this);
manualDownload = new QPushButton("Update Anyway", this);
enableUpdateButton(false); //Unless we know there's an update available, you can't install
gotoDownload = new QPushButton("Open Download Page", this);
buttonBox->addButton(manualDownload, QDialogButtonBox::ActionRole);
buttonBox->addButton(gotoDownload, QDialogButtonBox::ActionRole);
buttonBox->addButton(ok, QDialogButtonBox::AcceptRole);
connect(gotoDownload, SIGNAL(clicked()), this, SLOT(gotoDownloadPage()));
connect(manualDownload, SIGNAL(clicked()), this, SLOT(downloadUpdate()));
connect(ok, SIGNAL(clicked()), this, SLOT(closeDialog()));
QVBoxLayout *parentLayout = new QVBoxLayout(this);
parentLayout->addWidget(text);
parentLayout->addWidget(progress);
parentLayout->addWidget(buttonBox);
setLayout(parentLayout);
//Check for SSL (this probably isn't necessary)
if (!QSslSocket::supportsSsl()) {
enableUpdateButton(false);
QMessageBox::critical(
this,
tr("Error"),
tr("Cockatrice was not built with SSL support, so cannot download updates! "
"Please visit the download page and update manually."));
}
//Initialize the checker and downloader class
uDownloader = new UpdateDownloader(this);
connect(uDownloader, SIGNAL(downloadSuccessful(QUrl)), this, SLOT(downloadSuccessful(QUrl)));
connect(uDownloader, SIGNAL(progressMade(qint64, qint64)),
this, SLOT(downloadProgressMade(qint64, qint64)));
connect(uDownloader, SIGNAL(error(QString)),
this, SLOT(downloadError(QString)));
uChecker = new UpdateChecker(this);
connect(uChecker, SIGNAL(finishedCheck(bool, bool, QVariantMap * )),
this, SLOT(finishedUpdateCheck(bool, bool, QVariantMap * )));
connect(uChecker, SIGNAL(error(QString)),
this, SLOT(updateCheckError(QString)));
//Check for updates
beginUpdateCheck();
}
void DlgUpdate::closeDialog() {
accept();
}
void DlgUpdate::gotoDownloadPage() {
QUrl openUrl(HUMAN_DOWNLOAD_URL);
QDesktopServices::openUrl(openUrl);
}
void DlgUpdate::downloadUpdate() {
setLabel("Downloading update...");
enableUpdateButton(false);
uDownloader->beginDownload(updateUrl);
}
void DlgUpdate::beginUpdateCheck() {
progress->setMinimum(0);
progress->setMaximum(0);
setLabel("Checking for updates...");
uChecker->check();
}
void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, QVariantMap *build) {
QString commitHash, commitDate;
//Update the UI to say we've finished
progress->setMaximum(100);
setLabel("Finished checking for updates.");
//If there are no available builds, then they can't auto update.
enableUpdateButton(isCompatible);
//If there is an update, save its URL and work out its name
if (isCompatible) {
QString endUrl = (*build)["path"].toString();
updateUrl = API_DOWNLOAD_BASE_URL + endUrl;
commitHash = (*build)["sha1"].toString().left(SHORT_SHA1_HASH_LENGTH);
commitDate = (*build)["created"].toString().remove(DATE_LENGTH, MAX_DATE_LENGTH);
}
//Give the user the appropriate message
if (needToUpdate) {
if (isCompatible) {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "Update Available",
"A new build (commit " + commitHash + ") from " + commitDate +
" is available. Download?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes)
downloadUpdate();
}
else {
QMessageBox::information(this, "Cockatrice Update",
tr("Your version of Cockatrice is out of date, but there are no packages"
" available for your operating system. You may have to use a developer build or build from source"
" yourself. Please visit the download page."));
}
}
else {
//If there's no need to update, tell them that. However we still allow them to run the
//downloader themselves if there's a compatible build
QMessageBox::information(this, tr("Cockatrice Update"), tr("Your version of Cockatrice is up to date."));
}
}
void DlgUpdate::enableUpdateButton(bool enable) {
manualDownload->setEnabled(enable);
}
void DlgUpdate::setLabel(QString newText) {
text->setText(newText);
}
void DlgUpdate::updateCheckError(QString errorString) {
setLabel("Error");
QMessageBox::critical(this, tr("Update Error"), "An error occurred while checking for updates: " + errorString);
}
void DlgUpdate::downloadError(QString errorString) {
setLabel("Error");
enableUpdateButton(true);
QMessageBox::critical(this, tr("Update Error"), "An error occurred while downloading an update: " + errorString);
}
void DlgUpdate::downloadSuccessful(QUrl filepath) {
setLabel("Installing...");
//Try to open the installer. If it opens, quit Cockatrice
if (QDesktopServices::openUrl(filepath))
{
QMetaObject::invokeMethod((MainWindow*) parent(), "close", Qt::QueuedConnection);
close();
} else {
setLabel("Error");
QMessageBox::critical(this, tr("Update Error"), "Unable to open the installer. You might be able to manually update"
" by closing Cockatrice and running the installer at " + filepath.toLocalFile() + ".");
}
}
void DlgUpdate::downloadProgressMade(qint64 bytesRead, qint64 totalBytes) {
progress->setMaximum(totalBytes);
progress->setValue(bytesRead);
}

View file

@ -0,0 +1,37 @@
#ifndef DLG_UPDATE_H
#define DLG_UPDATE_H
#include <QtNetwork>
#include <QProgressDialog>
#include "update_checker.h"
#include "update_downloader.h"
class DlgUpdate : public QDialog {
Q_OBJECT
public:
DlgUpdate(QWidget *parent);
private slots:
void finishedUpdateCheck(bool needToUpdate, bool isCompatible, QVariantMap *build);
void gotoDownloadPage();
void downloadUpdate();
void updateCheckError(QString errorString);
void downloadSuccessful(QUrl filepath);
void downloadProgressMade(qint64 bytesRead, qint64 totalBytes);
void downloadError(QString errorString);
void closeDialog();
private:
QUrl updateUrl;
void enableUpdateButton(bool enable);
void beginUpdateCheck();
void setLabel(QString text);
QLabel *text;
QProgressBar *progress;
QPushButton *manualDownload, *gotoDownload, *ok;
QPushButton *cancel;
UpdateChecker *uChecker;
UpdateDownloader *uDownloader;
};
#endif

View file

@ -88,10 +88,10 @@ CardSet *PictureToLoad::getCurrentSet() const
return 0;
}
QStringList PictureLoader::md5Blacklist = QStringList()
QStringList PictureLoaderWorker::md5Blacklist = QStringList()
<< "db0c48db407a907c16ade38de048a441"; // card back returned by gatherer when card is not found
PictureLoader::PictureLoader()
PictureLoaderWorker::PictureLoaderWorker()
: QObject(0),
downloadRunning(false), loadQueueRunning(false)
{
@ -110,12 +110,12 @@ PictureLoader::PictureLoader()
moveToThread(pictureLoaderThread);
}
PictureLoader::~PictureLoader()
PictureLoaderWorker::~PictureLoaderWorker()
{
pictureLoaderThread->deleteLater();
}
void PictureLoader::processLoadQueue()
void PictureLoaderWorker::processLoadQueue()
{
if (loadQueueRunning)
return;
@ -160,7 +160,7 @@ void PictureLoader::processLoadQueue()
}
}
bool PictureLoader::cardImageExistsOnDisk(QString & setName, QString & correctedCardname)
bool PictureLoaderWorker::cardImageExistsOnDisk(QString & setName, QString & correctedCardname)
{
QImage image;
QImageReader imgReader;
@ -194,7 +194,7 @@ bool PictureLoader::cardImageExistsOnDisk(QString & setName, QString & corrected
return false;
}
QString PictureLoader::getPicUrl()
QString PictureLoaderWorker::getPicUrl()
{
if (!picDownload) return QString("");
@ -242,7 +242,7 @@ QString PictureLoader::getPicUrl()
return picUrl;
}
void PictureLoader::startNextPicDownload()
void PictureLoaderWorker::startNextPicDownload()
{
if (cardsToDownload.isEmpty()) {
cardBeingDownloaded = 0;
@ -267,7 +267,7 @@ void PictureLoader::startNextPicDownload()
}
}
void PictureLoader::picDownloadFailed()
void PictureLoaderWorker::picDownloadFailed()
{
if (cardBeingDownloaded.nextSet())
{
@ -283,13 +283,13 @@ void PictureLoader::picDownloadFailed()
}
}
bool PictureLoader::imageIsBlackListed(const QByteArray &picData)
bool PictureLoaderWorker::imageIsBlackListed(const QByteArray &picData)
{
QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex();
return md5Blacklist.contains(md5sum);
}
void PictureLoader::picDownloadFinished(QNetworkReply *reply)
void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
{
if (reply->error()) {
qDebug() << "Download failed:" << reply->errorString();
@ -349,7 +349,7 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply)
startNextPicDownload();
}
void PictureLoader::enqueueImageLoad(CardInfo *card)
void PictureLoaderWorker::enqueueImageLoad(CardInfo *card)
{
QMutexLocker locker(&mutex);
@ -367,20 +367,31 @@ void PictureLoader::enqueueImageLoad(CardInfo *card)
emit startLoadQueue();
}
void PictureLoader::picDownloadChanged()
void PictureLoaderWorker::picDownloadChanged()
{
QMutexLocker locker(&mutex);
picDownload = settingsCache->getPicDownload();
QPixmapCache::clear();
}
void PictureLoader::picsPathChanged()
void PictureLoaderWorker::picsPathChanged()
{
QMutexLocker locker(&mutex);
picsPath = settingsCache->getPicsPath();
}
QPixmapCache::clear();
PictureLoader::PictureLoader()
: QObject(0)
{
worker = new PictureLoaderWorker;
connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged()));
connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
connect(worker, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &)));
}
PictureLoader::~PictureLoader()
{
worker->deleteLater();
}
void PictureLoader::internalGetCardBackPixmap(QPixmap &pixmap, QSize size)
@ -388,7 +399,7 @@ void PictureLoader::internalGetCardBackPixmap(QPixmap &pixmap, QSize size)
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height());
if(!QPixmapCache::find(backCacheKey, &pixmap))
{
qDebug() << "cache fail for" << backCacheKey;
qDebug() << "cache fail for" << backCacheKey;
pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QPixmapCache::insert(backCacheKey, pixmap);
}
@ -425,7 +436,7 @@ void PictureLoader::getPixmap(QPixmap &pixmap, CardInfo *card, QSize size)
if(card)
{
// add the card to the load queue
getInstance().enqueueImageLoad(card);
getInstance().worker->enqueueImageLoad(card);
}
}
@ -471,6 +482,16 @@ void PictureLoader::cacheCardPixmaps(QList<CardInfo *> cards)
if(QPixmapCache::find(key, &tmp))
continue;
getInstance().enqueueImageLoad(card);
getInstance().worker->enqueueImageLoad(card);
}
}
void PictureLoader::picDownloadChanged()
{
QPixmapCache::clear();
}
void PictureLoader::picsPathChanged()
{
QPixmapCache::clear();
}

View file

@ -28,6 +28,42 @@ public:
bool nextSet();
};
class PictureLoaderWorker : public QObject {
Q_OBJECT
public:
PictureLoaderWorker();
~PictureLoaderWorker();
void enqueueImageLoad(CardInfo *card);
private:
static QStringList md5Blacklist;
QThread *pictureLoaderThread;
QString picsPath;
QList<PictureToLoad> loadQueue;
QMutex mutex;
QNetworkAccessManager *networkManager;
QList<PictureToLoad> cardsToDownload;
PictureToLoad cardBeingLoaded;
PictureToLoad cardBeingDownloaded;
bool picDownload, downloadRunning, loadQueueRunning;
void startNextPicDownload();
QString getPicUrl();
bool cardImageExistsOnDisk(QString & setName, QString & correctedCardname);
bool imageIsBlackListed(const QByteArray &picData);
private slots:
void picDownloadFinished(QNetworkReply *reply);
void picDownloadFailed();
void picDownloadChanged();
void picsPathChanged();
public slots:
void processLoadQueue();
signals:
void startLoadQueue();
void imageLoaded(CardInfo *card, const QImage &image);
};
class PictureLoader : public QObject {
Q_OBJECT
public:
@ -43,24 +79,8 @@ private:
PictureLoader(PictureLoader const&);
void operator=(PictureLoader const&);
static QStringList md5Blacklist;
QThread *pictureLoaderThread;
QString picsPath;
QList<PictureToLoad> loadQueue;
QMutex mutex;
QNetworkAccessManager *networkManager;
QList<PictureToLoad> cardsToDownload;
PictureToLoad cardBeingLoaded;
PictureToLoad cardBeingDownloaded;
bool picDownload, downloadRunning, loadQueueRunning;
void startNextPicDownload();
void imageLoaded(CardInfo *card, const QImage &image);
QString getPicUrl();
bool cardImageExistsOnDisk(QString & setName, QString & correctedCardname);
bool imageIsBlackListed(const QByteArray &picData);
PictureLoaderWorker * worker;
public:
void enqueueImageLoad(CardInfo *card);
static void getPixmap(QPixmap &pixmap, CardInfo *card, QSize size);
static void clearPixmapCache(CardInfo *card);
static void clearPixmapCache();
@ -68,15 +88,9 @@ public:
protected:
static void internalGetCardBackPixmap(QPixmap &pixmap, QSize size);
private slots:
void picDownloadFinished(QNetworkReply *reply);
void picDownloadFailed();
void picDownloadChanged();
void picsPathChanged();
public slots:
void processLoadQueue();
signals:
void startLoadQueue();
void imageLoaded(CardInfo *card, const QImage &image);
};
#endif

View file

@ -57,3 +57,43 @@ void LayoutsSettings::setDeckEditorFilterSize(const QSize &value)
{
setValue(value,"layouts/deckEditor_FilterSize");
}
void LayoutsSettings::setGamePlayAreaGeometry(const QByteArray &value)
{
setValue(value,"layouts/gameplayarea_geometry");
}
void LayoutsSettings::setGamePlayAreaState(const QByteArray &value)
{
setValue(value,"layouts/gameplayarea_state");
}
const QByteArray LayoutsSettings::getGamePlayAreaLayoutState()
{
return getValue("layouts/gameplayarea_state").toByteArray();
}
const QByteArray LayoutsSettings::getGamePlayAreaGeometry()
{
return getValue("layouts/gameplayarea_geometry").toByteArray();
}
void LayoutsSettings::setReplayPlayAreaGeometry(const QByteArray &value)
{
setValue(value,"layouts/replayplayarea_geometry");
}
void LayoutsSettings::setReplayPlayAreaState(const QByteArray &value)
{
setValue(value,"layouts/replayplayarea_state");
}
const QByteArray LayoutsSettings::getReplayPlayAreaLayoutState()
{
return getValue("layouts/replayplayarea_state").toByteArray();
}
const QByteArray LayoutsSettings::getReplayPlayAreaGeometry()
{
return getValue("layouts/replayplayarea_geometry").toByteArray();
}

View file

@ -15,12 +15,20 @@ public:
void setDeckEditorCardSize(const QSize &value);
void setDeckEditorDeckSize(const QSize &value);
void setDeckEditorFilterSize(const QSize &value);
void setGamePlayAreaGeometry(const QByteArray &value);
void setGamePlayAreaState(const QByteArray &value);
void setReplayPlayAreaGeometry(const QByteArray &value);
void setReplayPlayAreaState(const QByteArray &value);
const QByteArray getDeckEditorLayoutState();
const QByteArray getDeckEditorGeometry();
const QSize getDeckEditorCardSize();
const QSize getDeckEditorDeckSize();
const QSize getDeckEditorFilterSize();
const QByteArray getGamePlayAreaLayoutState();
const QByteArray getGamePlayAreaGeometry();
const QByteArray getReplayPlayAreaLayoutState();
const QByteArray getReplayPlayAreaGeometry();
signals:
public slots:

View file

@ -612,4 +612,4 @@ void SettingsCache::setNotifyAboutUpdate(int _notifyaboutupdate)
{
notifyAboutUpdates = _notifyaboutupdate;
settings->setValue("personal/updatenotification", notifyAboutUpdates);
}
}

View file

@ -243,4 +243,5 @@ void ShortcutsSettings::fillDefaultShorcuts()
defaultShortCuts["Player/phase9"] = parseSequenceString("F9");
defaultShortCuts["tab_room/aClearChat"] = parseSequenceString("F12");
defaultShortCuts["DlgLoadDeckFromClipboard/refreshButton"] = parseSequenceString("F5");
defaultShortCuts["Player/aResetLayout"] = parseSequenceString("");
}

View file

@ -170,6 +170,8 @@ void SoundEngine::themeChangedSlot()
<< "player_join" << "player_leave" << "player_disconnect" << "player_reconnect" << "player_concede"
// Spectator
<< "spectator_join" << "spectator_leave"
// Buddy
<< "buddy_join" << "buddy_leave"
// Chat & UI
<< "chat_mention" << "all_mention" << "private_message";

View file

@ -4,8 +4,9 @@
#include <QApplication>
#include <QDebug>
Tab::Tab(TabSupervisor *_tabSupervisor, QWidget *parent)
: QWidget(parent), tabSupervisor(_tabSupervisor), contentsChanged(false), infoPopup(0)
: QMainWindow(parent), tabSupervisor(_tabSupervisor), contentsChanged(false), infoPopup(0)
{
setAttribute(Qt::WA_DeleteOnClose);
}
void Tab::showCardInfoPopup(const QPoint &pos, const QString &cardName)

View file

@ -1,13 +1,13 @@
#ifndef TAB_H
#define TAB_H
#include <QWidget>
#include <QMainWindow>
class QMenu;
class TabSupervisor;
class CardInfoWidget;
class Tab : public QWidget {
class Tab : public QMainWindow {
Q_OBJECT
signals:
void userEvent(bool globalEvent = true);

View file

@ -83,7 +83,10 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool
mainLayout->addWidget(lockButton);
retranslateUi();
setLayout(mainLayout);
QWidget * mainWidget = new QWidget(this);
mainWidget->setLayout(mainLayout);
setCentralWidget(mainWidget);
}
void TabAdmin::retranslateUi()

View file

@ -52,30 +52,6 @@ void SearchLineEdit::keyPressEvent(QKeyEvent *event)
QLineEdit::keyPressEvent(event);
}
void TabDeckEditor::createShowHideDocksButtons()
{
btnFilter = new QPushButton(QPixmap("theme:icons/view"),QString());
btnFilter->setObjectName("btnFilter");
btnFilter->setCheckable(true);
btnFilter->setChecked(true);
btnFilter->setMaximumWidth(30);
searchLayout->addWidget(btnFilter);
btnDeck = new QPushButton(QPixmap("theme:hand"),QString());
btnDeck->setObjectName("btnDeck");
btnDeck->setCheckable(true);
btnDeck->setChecked(true);
btnDeck->setMaximumWidth(30);
searchLayout->addWidget(btnDeck);
btnCard = new QPushButton(QPixmap("theme:cardback"),QString());
btnCard->setObjectName("btnCard");
btnCard->setCheckable(true);
btnCard->setChecked(true);
btnCard->setMaximumWidth(30);
searchLayout->addWidget(btnCard);
}
void TabDeckEditor::createDeckDock()
{
deckModel = new DeckListModel(this);
@ -160,7 +136,7 @@ void TabDeckEditor::createDeckDock()
rightFrame->addWidget(deckView, 10);
rightFrame->addLayout(deckToolbarLayout);
deckDock = new QDockWidget(MainWindow);
deckDock = new QDockWidget(this);
deckDock->setObjectName("deckDock");
deckDock->setMinimumSize(QSize(200, 41));
@ -171,8 +147,8 @@ void TabDeckEditor::createDeckDock()
deckDockContents->setLayout(rightFrame);
deckDock->setWidget(deckDockContents);
connect(btnDeck,SIGNAL(toggled(bool)),deckDock,SLOT(setVisible(bool)));
deckDock->installEventFilter(this);
connect(deckDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabDeckEditor::createCardInfoDock()
@ -183,7 +159,7 @@ void TabDeckEditor::createCardInfoDock()
cardInfoFrame->setObjectName("cardInfoFrame");
cardInfoFrame->addWidget(cardInfo);
cardInfoDock = new QDockWidget(MainWindow);
cardInfoDock = new QDockWidget(this);
cardInfoDock->setObjectName("cardInfoDock");
cardInfoDock->setMinimumSize(QSize(200, 41));
@ -194,8 +170,8 @@ void TabDeckEditor::createCardInfoDock()
cardInfoDockContents->setLayout(cardInfoFrame);
cardInfoDock->setWidget(cardInfoDockContents);
connect(btnCard,SIGNAL(toggled(bool)),cardInfoDock,SLOT(setVisible(bool)));
cardInfoDock->installEventFilter(this);
connect(cardInfoDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabDeckEditor::createFiltersDock()
@ -243,17 +219,17 @@ void TabDeckEditor::createFiltersDock()
filterFrame->setObjectName("filterFrame");
filterFrame->addWidget(filterBox);
filterDock = new QDockWidget(MainWindow);
filterDock = new QDockWidget(this);
filterDock->setObjectName("filterDock");
filterDock->setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable);
QWidget *filterDockContents = new QWidget(MainWindow);
QWidget *filterDockContents = new QWidget(this);
filterDockContents->setObjectName("filterDockContents");
filterDockContents->setLayout(filterFrame);
filterDock->setWidget(filterDockContents);
connect(btnFilter,SIGNAL(toggled(bool)),filterDock,SLOT(setVisible(bool)));
filterDock->installEventFilter(this);
connect(filterDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabDeckEditor::createMenus()
@ -297,9 +273,6 @@ void TabDeckEditor::createMenus()
aEditTokens = new QAction(QString(), this);
connect(aEditTokens, SIGNAL(triggered()), this, SLOT(actEditTokens()));
aResetLayout = new QAction(QString(), this);
connect(aResetLayout,SIGNAL(triggered()),this,SLOT(restartLayout()));
deckMenu = new QMenu(this);
deckMenu->addAction(aNewDeck);
deckMenu->addAction(aLoadDeck);
@ -313,8 +286,6 @@ void TabDeckEditor::createMenus()
deckMenu->addSeparator();
deckMenu->addAction(aAnalyzeDeck);
deckMenu->addSeparator();
deckMenu->addAction(aResetLayout);
deckMenu->addSeparator();
deckMenu->addAction(aClose);
addTabMenu(deckMenu);
@ -338,6 +309,41 @@ void TabDeckEditor::createMenus()
dbMenu->addAction(aOpenCustomsetsFolder);
#endif
addTabMenu(dbMenu);
viewMenu = new QMenu(this);
cardInfoDockMenu = viewMenu->addMenu(QString());
deckDockMenu = viewMenu->addMenu(QString());
filterDockMenu = viewMenu->addMenu(QString());
aCardInfoDockVisible = cardInfoDockMenu->addAction(QString());
aCardInfoDockVisible->setCheckable(true);
connect(aCardInfoDockVisible,SIGNAL(triggered()),this,SLOT(dockVisibleTriggered()));
aCardInfoDockFloating = cardInfoDockMenu->addAction(QString());
aCardInfoDockFloating->setCheckable(true);
connect(aCardInfoDockFloating,SIGNAL(triggered()),this,SLOT(dockFloatingTriggered()));
aDeckDockVisible = deckDockMenu->addAction(QString());
aDeckDockVisible->setCheckable(true);
connect(aDeckDockVisible,SIGNAL(triggered()),this,SLOT(dockVisibleTriggered()));
aDeckDockFloating = deckDockMenu->addAction(QString());
aDeckDockFloating->setCheckable(true);
connect(aDeckDockFloating,SIGNAL(triggered()),this,SLOT(dockFloatingTriggered()));
aFilterDockVisible = filterDockMenu->addAction(QString());
aFilterDockVisible->setCheckable(true);
connect(aFilterDockVisible,SIGNAL(triggered()),this,SLOT(dockVisibleTriggered()));
aFilterDockFloating = filterDockMenu->addAction(QString());
aFilterDockFloating->setCheckable(true);
connect(aFilterDockFloating,SIGNAL(triggered()),this,SLOT(dockFloatingTriggered()));
viewMenu->addSeparator();
aResetLayout = viewMenu->addAction(QString());
connect(aResetLayout,SIGNAL(triggered()),this,SLOT(restartLayout()));
viewMenu->addAction(aResetLayout);
addTabMenu(viewMenu);
}
void TabDeckEditor::createCentralFrame()
@ -414,41 +420,43 @@ void TabDeckEditor::createCentralFrame()
searchLayout->setObjectName("searchLayout");
searchLayout->addWidget(deckEditToolBar);
searchLayout->addWidget(searchEdit);
createShowHideDocksButtons();
centralFrame = new QVBoxLayout;
centralFrame->setObjectName("centralFrame");
centralFrame->addLayout(searchLayout);
centralFrame->addWidget(databaseView);
centralWidget = new QWidget(MainWindow);
centralWidget = new QWidget(this);
centralWidget->setObjectName("centralWidget");
centralWidget->setLayout(centralFrame);
MainWindow->setCentralWidget(centralWidget);
MainWindow->setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks);
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->setObjectName("mainLayout");
mainLayout->addWidget(MainWindow);
setLayout(mainLayout);
setCentralWidget(centralWidget);
setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks);
}
void TabDeckEditor::restartLayout()
{
btnDeck->setChecked(true);
btnFilter->setChecked(true);
btnCard->setChecked(true);
deckDock->setVisible(true);
cardInfoDock->setVisible(true);
filterDock->setVisible(true);
deckDock->setFloating(false);
cardInfoDock->setFloating(false);
filterDock->setFloating(false);
MainWindow->addDockWidget(static_cast<Qt::DockWidgetArea>(2), deckDock);
MainWindow->addDockWidget(static_cast<Qt::DockWidgetArea>(2), cardInfoDock);
MainWindow->addDockWidget(static_cast<Qt::DockWidgetArea>(2), filterDock);
aCardInfoDockVisible->setChecked(true);
aDeckDockVisible->setChecked(true);
aFilterDockVisible->setChecked(true);
MainWindow->splitDockWidget(cardInfoDock, deckDock, Qt::Horizontal);
MainWindow->splitDockWidget(cardInfoDock, filterDock, Qt::Vertical);
aCardInfoDockFloating->setChecked(false);
aDeckDockFloating->setChecked(false);
aFilterDockFloating->setChecked(false);
addDockWidget(static_cast<Qt::DockWidgetArea>(2), deckDock);
addDockWidget(static_cast<Qt::DockWidgetArea>(2), cardInfoDock);
addDockWidget(static_cast<Qt::DockWidgetArea>(2), filterDock);
splitDockWidget(cardInfoDock, deckDock, Qt::Horizontal);
splitDockWidget(cardInfoDock, filterDock, Qt::Vertical);
deckDock->setMinimumWidth(360);
deckDock->setMaximumWidth(360);
@ -496,12 +504,20 @@ void TabDeckEditor::refreshShortcuts()
void TabDeckEditor::loadLayout()
{
MainWindow->restoreState(settingsCache->layouts().getDeckEditorLayoutState());
MainWindow->restoreGeometry(settingsCache->layouts().getDeckEditorGeometry());
restoreState(settingsCache->layouts().getDeckEditorLayoutState());
restoreGeometry(settingsCache->layouts().getDeckEditorGeometry());
btnCard->setChecked(!cardInfoDock->isHidden());
btnFilter->setChecked(!filterDock->isHidden());
btnDeck->setChecked(!deckDock->isHidden());
aCardInfoDockVisible->setChecked(cardInfoDock->isVisible());
aFilterDockVisible->setChecked(filterDock->isVisible());
aDeckDockVisible->setChecked(deckDock->isVisible());
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
aCardInfoDockFloating->setChecked(cardInfoDock->isFloating());
aFilterDockFloating->setChecked(filterDock->isFloating());
aDeckDockFloating->setChecked(deckDock->isFloating());
cardInfoDock->setMinimumSize(settingsCache->layouts().getDeckEditorCardSize());
cardInfoDock->setMaximumSize(settingsCache->layouts().getDeckEditorCardSize());
@ -518,8 +534,7 @@ void TabDeckEditor::loadLayout()
TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
: Tab(_tabSupervisor, parent), modified(false)
{
MainWindow = new QMainWindow;
MainWindow->setObjectName("MainWindow");
setObjectName("TabDeckEditor");
createMenus();
@ -587,13 +602,25 @@ void TabDeckEditor::retranslateUi()
aEditSets->setText(tr("&Edit sets..."));
aEditTokens->setText(tr("Edit &tokens..."));
btnCard->setToolTip(tr("Show/Hide card information"));
btnDeck->setToolTip(tr("Show/Hide deck"));
btnFilter->setToolTip(tr("Show/Hide filters"));
aResetLayout->setText(tr("Reset layout"));
cardInfoDock->setWindowTitle(tr("Card Info"));
deckDock->setWindowTitle(tr("Deck"));
filterDock->setWindowTitle(tr("Filters"));
viewMenu->setTitle(tr("&View"));
cardInfoDockMenu->setTitle(tr("Card Info"));
deckDockMenu->setTitle(tr("Deck"));
filterDockMenu->setTitle(tr("Filters"));
aCardInfoDockVisible->setText(tr("Visible"));
aCardInfoDockFloating->setText(tr("Floating"));
aDeckDockVisible->setText(tr("Visible"));
aDeckDockFloating->setText(tr("Floating"));
aFilterDockVisible->setText(tr("Visible"));
aFilterDockFloating->setText(tr("Floating"));
aResetLayout->setText(tr("Reset layout"));
}
QString TabDeckEditor::getTabText() const
@ -998,28 +1025,6 @@ void TabDeckEditor::setPriceTagFeatureEnabled(int /* enabled */)
deckModel->pricesUpdated();
}
bool TabDeckEditor::eventFilter(QObject * o, QEvent * e)
{
if(e->type() == QEvent::Close)
{
if(o == cardInfoDock)
btnCard->setChecked(false);
else if(o == deckDock)
btnDeck->setChecked(false);
else if(o == filterDock)
btnFilter->setChecked(false);
}
if( o == this && e->type() == QEvent::Hide){
settingsCache->layouts().setDeckEditorLayoutState(MainWindow->saveState());
settingsCache->layouts().setDeckEditorGeometry(MainWindow->saveGeometry());
settingsCache->layouts().setDeckEditorCardSize(cardInfoDock->size());
settingsCache->layouts().setDeckEditorFilterSize(filterDock->size());
settingsCache->layouts().setDeckEditorDeckSize(deckDock->size());
}
return false;
}
/*
void TabDeckEditor::actUpdatePrices()
{
@ -1105,3 +1110,99 @@ void TabDeckEditor::checkFirstRunDetected()
actEditSets();
}
}
// Method uses to sync docks state with menu items state
bool TabDeckEditor::eventFilter(QObject * o, QEvent * e)
{
if(e->type() == QEvent::Close)
{
if(o == cardInfoDock)
{
aCardInfoDockVisible->setChecked(false);
aCardInfoDockFloating->setEnabled(false);
} else if(o == deckDock) {
aDeckDockVisible->setChecked(false);
aDeckDockFloating->setEnabled(false);
} else if(o == filterDock) {
aFilterDockVisible->setChecked(false);
aFilterDockFloating->setEnabled(false);
}
}
if( o == this && e->type() == QEvent::Hide){
settingsCache->layouts().setDeckEditorLayoutState(saveState());
settingsCache->layouts().setDeckEditorGeometry(saveGeometry());
settingsCache->layouts().setDeckEditorCardSize(cardInfoDock->size());
settingsCache->layouts().setDeckEditorFilterSize(filterDock->size());
settingsCache->layouts().setDeckEditorDeckSize(deckDock->size());
}
return false;
}
void TabDeckEditor::dockVisibleTriggered()
{
QObject *o = sender();
if(o == aCardInfoDockVisible)
{
cardInfoDock->setVisible(aCardInfoDockVisible->isChecked());
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
return;
}
if(o == aDeckDockVisible)
{
deckDock->setVisible(aDeckDockVisible->isChecked());
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
return;
}
if(o == aFilterDockVisible)
{
filterDock->setVisible(aFilterDockVisible->isChecked());
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
return;
}
}
void TabDeckEditor::dockFloatingTriggered()
{
QObject *o = sender();
if(o == aCardInfoDockFloating)
{
cardInfoDock->setFloating(aCardInfoDockFloating->isChecked());
return;
}
if(o == aDeckDockFloating)
{
deckDock->setFloating(aDeckDockFloating->isChecked());
return;
}
if(o == aFilterDockFloating)
{
filterDock->setFloating(aFilterDockFloating->isChecked());
return;
}
}
void TabDeckEditor::dockTopLevelChanged(bool topLevel)
{
QObject *o = sender();
if(o == cardInfoDock)
{
aCardInfoDockFloating->setChecked(topLevel);
return;
}
if(o == deckDock)
{
aDeckDockFloating->setChecked(topLevel);
return;
}
if(o == filterDock)
{
aFilterDockFloating->setChecked(topLevel);
return;
}
}

View file

@ -22,7 +22,6 @@ class CardInfo;
class QGroupBox;
class QHBoxLayout;
class QPushButton;
class QMainWindow;
class QDockWidget;
class SearchLineEdit : public QLineEdit {
@ -79,12 +78,15 @@ class TabDeckEditor : public Tab {
void filterRemove(QAction *action);
void setPriceTagFeatureEnabled(int enabled);
bool eventFilter(QObject *o, QEvent *e);
void loadLayout();
void restartLayout();
void freeDocksSize();
void refreshShortcuts();
bool eventFilter(QObject *o, QEvent *e);
void dockVisibleTriggered();
void dockFloatingTriggered();
void dockTopLevelChanged(bool topLevel);
private:
CardInfo *currentCardInfo() const;
void addCardHelper(QString zoneName);
@ -113,19 +115,16 @@ private:
QTreeView *filterView;
QWidget *filterBox;
QMenu *deckMenu, *dbMenu;
QMenu *deckMenu, *dbMenu, *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu;
QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose, *aOpenCustomFolder, *aOpenCustomsetsFolder;
QAction *aEditSets, *aEditTokens, *aClearFilterAll, *aClearFilterOne;
QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement;// *aUpdatePrices;
QAction *aResetLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating, *aFilterDockVisible, *aFilterDockFloating;
bool modified;
QMainWindow *MainWindow;
QVBoxLayout *centralFrame;
QHBoxLayout *searchLayout;
QPushButton *btnFilter;
QPushButton *btnDeck;
QPushButton *btnCard;
QDockWidget *cardInfoDock;
QDockWidget *deckDock;
QDockWidget *filterDock;
@ -138,7 +137,6 @@ public:
void setDeck(DeckLoader *_deckLoader);
void setModified(bool _windowModified);
bool confirmClose();
void createShowHideDocksButtons();
void createDeckDock();
void createCardInfoDock();
void createFiltersDock();

View file

@ -110,7 +110,10 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_c
rightToolBar->addAction(aDeleteRemoteDeck);
retranslateUi();
setLayout(hbox);
QWidget * mainWidget = new QWidget(this);
mainWidget->setLayout(hbox);
setCentralWidget(mainWidget);
}
void TabDeckStorage::retranslateUi()

View file

@ -9,7 +9,9 @@
#include <QToolButton>
#include <QDebug>
#include <QCompleter>
#include <QDockWidget>
#include <QWidget>
#include <QStackedWidget>
#include "dlg_creategame.h"
#include "tab_game.h"
@ -35,6 +37,7 @@
#include "pictureloader.h"
#include "replay_timeline_widget.h"
#include "lineeditcompleter.h"
#include "window_sets.h"
#include <google/protobuf/descriptor.h>
#include "pending_command.h"
@ -93,7 +96,7 @@ void ToggleButton::setState(bool _state)
}
DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent)
: QWidget(parent), playerId(_playerId)
: QWidget(0), parentGame(parent), playerId(_playerId)
{
loadLocalButton = new QPushButton;
loadRemoteButton = new QPushButton;
@ -202,6 +205,9 @@ void TabGame::refreshShortcuts()
if (aCloseReplay) {
aCloseReplay->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aCloseReplay"));
}
if (aResetLayout) {
aResetLayout->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aResetLayout"));
}
}
void DeckViewContainer::loadLocalDeck()
@ -222,20 +228,20 @@ void DeckViewContainer::loadLocalDeck()
Command_DeckSelect cmd;
cmd.set_deck(deck.writeToString_Native().toStdString());
PendingCommand *pend = static_cast<TabGame *>(parent())->prepareGameCommand(cmd);
PendingCommand *pend = parentGame->prepareGameCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(deckSelectFinished(const Response &)));
static_cast<TabGame *>(parent())->sendGameCommand(pend, playerId);
parentGame->sendGameCommand(pend, playerId);
}
void DeckViewContainer::loadRemoteDeck()
{
DlgLoadRemoteDeck dlg(static_cast<TabGame *>(parent())->getClientForPlayer(playerId));
DlgLoadRemoteDeck dlg(parentGame->getClientForPlayer(playerId));
if (dlg.exec()) {
Command_DeckSelect cmd;
cmd.set_deck_id(dlg.getDeckId());
PendingCommand *pend = static_cast<TabGame *>(parent())->prepareGameCommand(cmd);
PendingCommand *pend = parentGame->prepareGameCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(deckSelectFinished(const Response &)));
static_cast<TabGame *>(parent())->sendGameCommand(pend, playerId);
parentGame->sendGameCommand(pend, playerId);
}
}
@ -251,7 +257,7 @@ void DeckViewContainer::readyStart()
{
Command_ReadyStart cmd;
cmd.set_ready(!readyStartButton->getState());
static_cast<TabGame *>(parent())->sendGameCommand(cmd, playerId);
parentGame->sendGameCommand(cmd, playerId);
}
void DeckViewContainer::sideboardLockButtonClicked()
@ -259,7 +265,7 @@ void DeckViewContainer::sideboardLockButtonClicked()
Command_SetSideboardLock cmd;
cmd.set_locked(sideboardLockButton->getState());
static_cast<TabGame *>(parent())->sendGameCommand(cmd, playerId);
parentGame->sendGameCommand(cmd, playerId);
}
void DeckViewContainer::sideboardPlanChanged()
@ -268,7 +274,7 @@ void DeckViewContainer::sideboardPlanChanged()
const QList<MoveCard_ToZone> &newPlan = deckView->getSideboardPlan();
for (int i = 0; i < newPlan.size(); ++i)
cmd.add_move_list()->CopyFrom(newPlan[i]);
static_cast<TabGame *>(parent())->sendGameCommand(cmd, playerId);
parentGame->sendGameCommand(cmd, playerId);
}
void DeckViewContainer::setReadyStart(bool ready)
@ -310,11 +316,10 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay)
sayLabel(0),
sayEdit(0)
{
setAttribute(Qt::WA_DeleteOnClose);
// THIS CTOR IS USED ON REPLAY
gameInfo.CopyFrom(replay->game_info());
gameInfo.set_spectators_omniscient(true);
// Create list: event number -> time [ms]
// Distribute simultaneous events evenly across 1 second.
unsigned int lastEventTimestamp = 0;
@ -323,106 +328,41 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay)
int j = i + 1;
while ((j < eventCount) && (replay->event_list(j).seconds_elapsed() == lastEventTimestamp))
++j;
const int numberEventsThisSecond = j - i;
for (int k = 0; k < numberEventsThisSecond; ++k)
replayTimeline.append(replay->event_list(i + k).seconds_elapsed() * 1000 + (int) ((qreal) k / (qreal) numberEventsThisSecond * 1000));
if (j < eventCount)
lastEventTimestamp = replay->event_list(j).seconds_elapsed();
i += numberEventsThisSecond - 1;
}
phasesToolbar = new PhasesToolbar;
scene = new GameScene(phasesToolbar, this);
gameView = new GameView(scene);
gameView->hide();
cardInfo = new CardFrame();
playerListWidget = new PlayerListWidget(0, 0, this);
playerListWidget->setFocusPolicy(Qt::NoFocus);
messageLog = new MessageLogWidget(tabSupervisor, this);
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfo, SLOT(setCard(QString)));
connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
deckViewContainerLayout = new QVBoxLayout;
createCardInfoDock(true);
createPlayerListDock(true);
createMessageDock(true);
createPlayAreaWidget(true);
createDeckViewContainerWidget(true);
createReplayDock();
QVBoxLayout *messageLogLayout = new QVBoxLayout;
messageLogLayout->setContentsMargins(0, 0, 0, 0);
messageLogLayout->addWidget(messageLog);
QWidget *messageLogLayoutWidget = new QWidget;
messageLogLayoutWidget->setLayout(messageLogLayout);
timelineWidget = new ReplayTimelineWidget;
timelineWidget->setTimeline(replayTimeline);
connect(timelineWidget, SIGNAL(processNextEvent()), this, SLOT(replayNextEvent()));
connect(timelineWidget, SIGNAL(replayFinished()), this, SLOT(replayFinished()));
replayStartButton = new QToolButton;
replayStartButton->setIconSize(QSize(32, 32));
replayStartButton->setIcon(QPixmap("theme:replay/start"));
connect(replayStartButton, SIGNAL(clicked()), this, SLOT(replayStartButtonClicked()));
replayPauseButton = new QToolButton;
replayPauseButton->setIconSize(QSize(32, 32));
replayPauseButton->setEnabled(false);
replayPauseButton->setIcon(QPixmap("theme:replay/pause"));
connect(replayPauseButton, SIGNAL(clicked()), this, SLOT(replayPauseButtonClicked()));
replayFastForwardButton = new QToolButton;
replayFastForwardButton->setIconSize(QSize(32, 32));
replayFastForwardButton->setEnabled(false);
replayFastForwardButton->setIcon(QPixmap("theme:replay/fastforward"));
replayFastForwardButton->setCheckable(true);
connect(replayFastForwardButton, SIGNAL(toggled(bool)), this, SLOT(replayFastForwardButtonToggled(bool)));
splitter = new QSplitter(Qt::Vertical);
splitter->addWidget(cardInfo);
splitter->addWidget(playerListWidget);
splitter->addWidget(messageLogLayoutWidget);
addDockWidget(Qt::RightDockWidgetArea, cardInfoDock);
addDockWidget(Qt::RightDockWidgetArea, playerListDock);
addDockWidget(Qt::RightDockWidgetArea, messageLayoutDock);
addDockWidget(Qt::BottomDockWidgetArea, replayDock);
mainLayout = new QHBoxLayout;
mainLayout->addWidget(gameView, 10);
mainLayout->addLayout(deckViewContainerLayout, 10);
mainLayout->addWidget(splitter);
QHBoxLayout *replayControlLayout = new QHBoxLayout;
replayControlLayout->addWidget(timelineWidget, 10);
replayControlLayout->addWidget(replayStartButton);
replayControlLayout->addWidget(replayPauseButton);
replayControlLayout->addWidget(replayFastForwardButton);
QVBoxLayout *superMainLayout = new QVBoxLayout;
superMainLayout->addLayout(mainLayout);
superMainLayout->addLayout(replayControlLayout);
aNextPhase = 0;
aNextTurn = 0;
aRemoveLocalArrows = 0;
aRotateViewCW = 0;
aRotateViewCCW = 0;
aGameInfo = 0;
aConcede = 0;
aLeaveGame = 0;
aCloseReplay = new QAction(this);
connect(aCloseReplay, SIGNAL(triggered()), this, SLOT(actLeaveGame()));
phasesMenu = 0;
gameMenu = new QMenu(this);
gameMenu->addAction(aCloseReplay);
addTabMenu(gameMenu);
mainWidget = new QStackedWidget(this);
mainWidget->addWidget(deckViewContainerWidget);
mainWidget->addWidget(gamePlayAreaWidget);
setCentralWidget(mainWidget);
createReplayMenuItems();
createViewMenuItems();
retranslateUi();
connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts()));
refreshShortcuts();
setLayout(superMainLayout);
splitter->restoreState(settingsCache->getTabGameSplitterSizes());
splitter->setChildrenCollapsible(false);
messageLog->logReplayStarted(gameInfo.game_id());
QTimer::singleShot(0, this, SLOT(loadLayout()));
}
TabGame::TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, const Event_GameJoined &event, const QMap<int, QString> &_roomGameTypes)
@ -438,139 +378,39 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_client
currentPhase(-1),
activeCard(0),
gameClosed(false),
replay(0)
replay(0),
replayDock(0)
{
// THIS CTOR IS USED ON GAMES
gameInfo.set_started(false);
gameTimer = new QTimer(this);
gameTimer->setInterval(1000);
connect(gameTimer, SIGNAL(timeout()), this, SLOT(incrementGameTime()));
gameTimer->start();
phasesToolbar = new PhasesToolbar;
connect(phasesToolbar, SIGNAL(sendGameCommand(const ::google::protobuf::Message &, int)), this, SLOT(sendGameCommand(const ::google::protobuf::Message &, int)));
scene = new GameScene(phasesToolbar, this);
gameView = new GameView(scene);
gameView->hide();
cardInfo = new CardFrame();
playerListWidget = new PlayerListWidget(tabSupervisor, clients.first(), this);
playerListWidget->setFocusPolicy(Qt::NoFocus);
connect(playerListWidget, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
timeElapsedLabel = new QLabel;
timeElapsedLabel->setAlignment(Qt::AlignCenter);
messageLog = new MessageLogWidget(tabSupervisor, this);
connect(messageLog, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfo, SLOT(setCard(QString)));
connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
connect(messageLog, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString)));
connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged()));
sayLabel = new QLabel;
sayEdit = new LineEditCompleter;
sayLabel->setBuddy(sayEdit);
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addWidget(sayLabel);
hLayout->addWidget(sayEdit);
deckViewContainerLayout = new QVBoxLayout;
createCardInfoDock();
createPlayerListDock();
createMessageDock();
createPlayAreaWidget();
createDeckViewContainerWidget();
QVBoxLayout *messageLogLayout = new QVBoxLayout;
messageLogLayout->setContentsMargins(0, 0, 0, 0);
messageLogLayout->addWidget(timeElapsedLabel);
messageLogLayout->addWidget(messageLog);
messageLogLayout->addLayout(hLayout);
QWidget *messageLogLayoutWidget = new QWidget;
messageLogLayoutWidget->setLayout(messageLogLayout);
splitter = new QSplitter(Qt::Vertical);
splitter->addWidget(cardInfo);
splitter->addWidget(playerListWidget);
splitter->addWidget(messageLogLayoutWidget);
addDockWidget(Qt::RightDockWidgetArea, cardInfoDock);
addDockWidget(Qt::RightDockWidgetArea, playerListDock);
addDockWidget(Qt::RightDockWidgetArea, messageLayoutDock);
mainLayout = new QHBoxLayout;
mainLayout->addWidget(gameView, 10);
mainLayout->addLayout(deckViewContainerLayout, 10);
mainLayout->addWidget(splitter);
if (spectator && !gameInfo.spectators_can_chat() && tabSupervisor->getAdminLocked()) {
sayLabel->hide();
sayEdit->hide();
}
connect(tabSupervisor, SIGNAL(adminLockChanged(bool)), this, SLOT(adminLockChanged(bool)));
connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(actSay()));
mainWidget = new QStackedWidget(this);
mainWidget->addWidget(deckViewContainerWidget);
mainWidget->addWidget(gamePlayAreaWidget);
setCentralWidget(mainWidget);
// Menu actions
aNextPhase = new QAction(this);
connect(aNextPhase, SIGNAL(triggered()), this, SLOT(actNextPhase()));
aNextTurn = new QAction(this);
connect(aNextTurn, SIGNAL(triggered()), this, SLOT(actNextTurn()));
aRemoveLocalArrows = new QAction(this);
connect(aRemoveLocalArrows, SIGNAL(triggered()), this, SLOT(actRemoveLocalArrows()));
aRotateViewCW = new QAction(this);
connect(aRotateViewCW, SIGNAL(triggered()), this, SLOT(actRotateViewCW()));
aRotateViewCCW = new QAction(this);
connect(aRotateViewCCW, SIGNAL(triggered()), this, SLOT(actRotateViewCCW()));
aGameInfo = new QAction(this);
connect(aGameInfo, SIGNAL(triggered()), this, SLOT(actGameInfo()));
aConcede = new QAction(this);
connect(aConcede, SIGNAL(triggered()), this, SLOT(actConcede()));
aLeaveGame = new QAction(this);
connect(aLeaveGame, SIGNAL(triggered()), this, SLOT(actLeaveGame()));
aCloseReplay = 0;
phasesMenu = new QMenu(this);
for (int i = 0; i < phasesToolbar->phaseCount(); ++i) {
QAction *temp = new QAction(QString(), this);
connect(temp, SIGNAL(triggered()), this, SLOT(actPhaseAction()));
phasesMenu->addAction(temp);
phaseActions.append(temp);
}
phasesMenu->addSeparator();
phasesMenu->addAction(aNextPhase);
gameMenu = new QMenu(this);
playersSeparator = gameMenu->addSeparator();
gameMenu->addMenu(phasesMenu);
gameMenu->addAction(aNextTurn);
gameMenu->addSeparator();
gameMenu->addAction(aRemoveLocalArrows);
gameMenu->addAction(aRotateViewCW);
gameMenu->addAction(aRotateViewCCW);
gameMenu->addSeparator();
gameMenu->addAction(aGameInfo);
gameMenu->addAction(aConcede);
gameMenu->addAction(aLeaveGame);
addTabMenu(gameMenu);
createMenuItems();
createViewMenuItems();
retranslateUi();
connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts()));
refreshShortcuts();
setLayout(mainLayout);
splitter->restoreState(settingsCache->getTabGameSplitterSizes());
splitter->setChildrenCollapsible(false);
messageLog->logGameJoined(gameInfo.game_id());
// append game to rooms game list for others to see
for (int i = gameInfo.game_types_size() - 1; i >= 0; i--)
gameTypes.append(roomGameTypes.find(gameInfo.game_types(i)).value());
completer = new QCompleter(autocompleteUserList, sayEdit);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setMaxVisibleItems(5);
#if QT_VERSION >= 0x050000
completer->setFilterMode(Qt::MatchStartsWith);
#endif
sayEdit->setCompleter(completer);
actCompleterChanged();
QTimer::singleShot(0, this, SLOT(loadLayout()));
}
void TabGame::addMentionTag(QString value) {
@ -585,21 +425,34 @@ void TabGame::emitUserEvent() {
TabGame::~TabGame()
{
delete replay;
settingsCache->setTabGameSplitterSizes(splitter->saveState());
if(replay)
{
settingsCache->layouts().setReplayPlayAreaState(saveState());
settingsCache->layouts().setReplayPlayAreaGeometry(saveGeometry());
delete replay;
} else {
settingsCache->layouts().setGamePlayAreaState(saveState());
settingsCache->layouts().setGamePlayAreaGeometry(saveGeometry());
}
QMapIterator<int, Player *> i(players);
while (i.hasNext())
delete i.next().value();
players.clear();
delete deckViewContainerLayout;
emit gameClosing(this);
}
void TabGame::retranslateUi()
{
QString tabText = getTabText() + " - ";
cardInfoDock->setWindowTitle((cardInfoDock->isWindow() ? tabText : QString()) + tr("Card Info"));
playerListDock->setWindowTitle((playerListDock->isWindow() ? tabText : QString()) + tr("Player List"));
messageLayoutDock->setWindowTitle((messageLayoutDock->isWindow() ? tabText : QString()) + tr("Messages"));
if(replayDock)
replayDock->setWindowTitle((replayDock->isWindow() ? tabText : QString()) + tr("Replay Timeline"));
if (phasesMenu) {
for (int i = 0; i < phaseActions.size(); ++i)
phaseActions[i]->setText(phasesToolbar->getLongPhaseName(i));
@ -633,9 +486,33 @@ void TabGame::retranslateUi()
if (aCloseReplay) {
aCloseReplay->setText(tr("C&lose replay"));
}
if (sayLabel)
if (sayLabel){
sayLabel->setText(tr("&Say:"));
}
viewMenu->setTitle(tr("&View"));
cardInfoDockMenu->setTitle(tr("Card Info"));
messageLayoutDockMenu->setTitle(tr("Messages"));
playerListDockMenu->setTitle(tr("Player List"));
aCardInfoDockVisible->setText(tr("Visible"));
aCardInfoDockFloating->setText(tr("Floating"));
aMessageLayoutDockVisible->setText(tr("Visible"));
aMessageLayoutDockFloating->setText(tr("Floating"));
aPlayerListDockVisible->setText(tr("Visible"));
aPlayerListDockFloating->setText(tr("Floating"));
if(replayDock)
{
replayDockMenu->setTitle(tr("Replay Timeline"));
aReplayDockVisible->setText(tr("Visible"));
aReplayDockFloating->setText(tr("Floating"));
}
aResetLayout->setText(tr("Reset layout"));
cardInfo->retranslateUi();
QMapIterator<int, Player *> i(players);
@ -955,8 +832,9 @@ void TabGame::startGame(bool resuming)
i.value()->setReadyStart(false);
i.value()->hide();
}
mainLayout->removeItem(deckViewContainerLayout);
mainWidget->setCurrentWidget(gamePlayAreaWidget);
if (!resuming) {
QMapIterator<int, Player *> playerIterator(players);
while (playerIterator.hasNext())
@ -966,7 +844,6 @@ void TabGame::startGame(bool resuming)
playerListWidget->setGameStarted(true, resuming);
gameInfo.set_started(true);
static_cast<GameScene *>(gameView->scene())->rearrange();
gameView->show();
if(sayEdit && players.size() > 1)
sayEdit->setFocus();
}
@ -981,12 +858,12 @@ void TabGame::stopGame()
i.next();
i.value()->show();
}
mainLayout->insertLayout(1, deckViewContainerLayout, 10);
mainWidget->setCurrentWidget(deckViewContainerWidget);
playerListWidget->setActivePlayer(-1);
playerListWidget->setGameStarted(false, false);
gameInfo.set_started(false);
gameView->hide();
}
void TabGame::closeGame()
@ -1335,3 +1212,478 @@ void TabGame::updateCardMenu(AbstractCardItem *card)
p = players.value(localPlayerId);
p->updateCardMenu(static_cast<CardItem *>(card));
}
void TabGame::createMenuItems()
{
aNextPhase = new QAction(this);
connect(aNextPhase, SIGNAL(triggered()), this, SLOT(actNextPhase()));
aNextTurn = new QAction(this);
connect(aNextTurn, SIGNAL(triggered()), this, SLOT(actNextTurn()));
aRemoveLocalArrows = new QAction(this);
connect(aRemoveLocalArrows, SIGNAL(triggered()), this, SLOT(actRemoveLocalArrows()));
aRotateViewCW = new QAction(this);
connect(aRotateViewCW, SIGNAL(triggered()), this, SLOT(actRotateViewCW()));
aRotateViewCCW = new QAction(this);
connect(aRotateViewCCW, SIGNAL(triggered()), this, SLOT(actRotateViewCCW()));
aGameInfo = new QAction(this);
connect(aGameInfo, SIGNAL(triggered()), this, SLOT(actGameInfo()));
aConcede = new QAction(this);
connect(aConcede, SIGNAL(triggered()), this, SLOT(actConcede()));
aLeaveGame = new QAction(this);
connect(aLeaveGame, SIGNAL(triggered()), this, SLOT(actLeaveGame()));
aCloseReplay = 0;
phasesMenu = new QMenu(this);
for (int i = 0; i < phasesToolbar->phaseCount(); ++i) {
QAction *temp = new QAction(QString(), this);
connect(temp, SIGNAL(triggered()), this, SLOT(actPhaseAction()));
phasesMenu->addAction(temp);
phaseActions.append(temp);
}
phasesMenu->addSeparator();
phasesMenu->addAction(aNextPhase);
gameMenu = new QMenu(this);
playersSeparator = gameMenu->addSeparator();
gameMenu->addMenu(phasesMenu);
gameMenu->addAction(aNextTurn);
gameMenu->addSeparator();
gameMenu->addAction(aRemoveLocalArrows);
gameMenu->addAction(aRotateViewCW);
gameMenu->addAction(aRotateViewCCW);
gameMenu->addSeparator();
gameMenu->addAction(aGameInfo);
gameMenu->addAction(aConcede);
gameMenu->addAction(aLeaveGame);
addTabMenu(gameMenu);
}
void TabGame::createReplayMenuItems()
{
aNextPhase = 0;
aNextTurn = 0;
aRemoveLocalArrows = 0;
aRotateViewCW = 0;
aRotateViewCCW = 0;
aResetLayout = 0;
aGameInfo = 0;
aConcede = 0;
aLeaveGame = 0;
aCloseReplay = new QAction(this);
connect(aCloseReplay, SIGNAL(triggered()), this, SLOT(actLeaveGame()));
phasesMenu = 0;
gameMenu = new QMenu(this);
gameMenu->addAction(aCloseReplay);
addTabMenu(gameMenu);
}
void TabGame::createViewMenuItems()
{
viewMenu = new QMenu(this);
cardInfoDockMenu = viewMenu->addMenu(QString());
messageLayoutDockMenu = viewMenu->addMenu(QString());
playerListDockMenu = viewMenu->addMenu(QString());
aCardInfoDockVisible = cardInfoDockMenu->addAction(QString());
aCardInfoDockVisible->setCheckable(true);
connect(aCardInfoDockVisible,SIGNAL(triggered()),this,SLOT(dockVisibleTriggered()));
aCardInfoDockFloating = cardInfoDockMenu->addAction(QString());
aCardInfoDockFloating->setCheckable(true);
connect(aCardInfoDockFloating,SIGNAL(triggered()),this,SLOT(dockFloatingTriggered()));
aMessageLayoutDockVisible = messageLayoutDockMenu->addAction(QString());
aMessageLayoutDockVisible->setCheckable(true);
connect(aMessageLayoutDockVisible,SIGNAL(triggered()),this,SLOT(dockVisibleTriggered()));
aMessageLayoutDockFloating = messageLayoutDockMenu->addAction(QString());
aMessageLayoutDockFloating->setCheckable(true);
connect(aMessageLayoutDockFloating,SIGNAL(triggered()),this,SLOT(dockFloatingTriggered()));
aPlayerListDockVisible = playerListDockMenu->addAction(QString());
aPlayerListDockVisible->setCheckable(true);
connect(aPlayerListDockVisible,SIGNAL(triggered()),this,SLOT(dockVisibleTriggered()));
aPlayerListDockFloating = playerListDockMenu->addAction(QString());
aPlayerListDockFloating->setCheckable(true);
connect(aPlayerListDockFloating,SIGNAL(triggered()),this,SLOT(dockFloatingTriggered()));
if(replayDock)
{
replayDockMenu = viewMenu->addMenu(QString());
aReplayDockVisible = replayDockMenu->addAction(QString());
aReplayDockVisible->setCheckable(true);
connect(aReplayDockVisible,SIGNAL(triggered()),this,SLOT(dockVisibleTriggered()));
aReplayDockFloating = replayDockMenu->addAction(QString());
aReplayDockFloating->setCheckable(true);
connect(aReplayDockFloating,SIGNAL(triggered()),this,SLOT(dockFloatingTriggered()));
}
viewMenu->addSeparator();
aResetLayout = viewMenu->addAction(QString());
connect(aResetLayout,SIGNAL(triggered()),this,SLOT(actResetLayout()));
viewMenu->addAction(aResetLayout);
addTabMenu(viewMenu);
}
void TabGame::loadLayout()
{
if(replayDock)
{
restoreGeometry(settingsCache->layouts().getReplayPlayAreaGeometry());
restoreState(settingsCache->layouts().getReplayPlayAreaLayoutState());
} else {
restoreGeometry(settingsCache->layouts().getGamePlayAreaGeometry());
restoreState(settingsCache->layouts().getGamePlayAreaLayoutState());
}
aCardInfoDockVisible->setChecked(cardInfoDock->isVisible());
aMessageLayoutDockVisible->setChecked(messageLayoutDock->isVisible());
aPlayerListDockVisible->setChecked(playerListDock->isVisible());
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
aMessageLayoutDockFloating->setEnabled(aMessageLayoutDockVisible->isChecked());
aPlayerListDockFloating->setEnabled(aPlayerListDockVisible->isChecked());
aCardInfoDockFloating->setChecked(cardInfoDock->isFloating());
aMessageLayoutDockFloating->setChecked(messageLayoutDock->isFloating());
aPlayerListDockFloating->setChecked(playerListDock->isFloating());
if(replayDock)
{
aReplayDockVisible->setChecked(replayDock->isVisible());
aReplayDockFloating->setEnabled(aReplayDockVisible->isChecked());
aReplayDockFloating->setChecked(replayDock->isFloating());
}
}
void TabGame::actResetLayout()
{
cardInfoDock->setVisible(true);
playerListDock->setVisible(true);
messageLayoutDock->setVisible(true);
cardInfoDock->setFloating(false);
playerListDock->setFloating(false);
messageLayoutDock->setFloating(false);
aCardInfoDockVisible->setChecked(true);
aPlayerListDockVisible->setChecked(true);
aMessageLayoutDockVisible->setChecked(true);
aCardInfoDockFloating->setChecked(false);
aPlayerListDockFloating->setChecked(false);
aMessageLayoutDockFloating->setChecked(false);
addDockWidget(Qt::RightDockWidgetArea, cardInfoDock);
addDockWidget(Qt::RightDockWidgetArea, playerListDock);
addDockWidget(Qt::RightDockWidgetArea, messageLayoutDock);
if(replayDock)
{
replayDock->setVisible(true);
replayDock->setFloating(false);
addDockWidget(Qt::BottomDockWidgetArea, replayDock);
aReplayDockVisible->setChecked(true);
aReplayDockFloating->setChecked(false);
}
}
void TabGame::createPlayAreaWidget(bool bReplay)
{
phasesToolbar = new PhasesToolbar;
if(!bReplay)
connect(phasesToolbar, SIGNAL(sendGameCommand(const ::google::protobuf::Message &, int)), this, SLOT(sendGameCommand(const ::google::protobuf::Message &, int)));
scene = new GameScene(phasesToolbar, this);
gameView = new GameView(scene);
gamePlayAreaVBox = new QVBoxLayout;
gamePlayAreaVBox->addWidget(gameView);
gamePlayAreaWidget = new QWidget;
gamePlayAreaWidget->setObjectName("gamePlayAreaWidget");
gamePlayAreaWidget->setLayout(gamePlayAreaVBox);
}
void TabGame::createReplayDock()
{
timelineWidget = new ReplayTimelineWidget;
timelineWidget->setTimeline(replayTimeline);
connect(timelineWidget, SIGNAL(processNextEvent()), this, SLOT(replayNextEvent()));
connect(timelineWidget, SIGNAL(replayFinished()), this, SLOT(replayFinished()));
replayStartButton = new QToolButton;
replayStartButton->setIconSize(QSize(32, 32));
replayStartButton->setIcon(QPixmap("theme:replay/start"));
connect(replayStartButton, SIGNAL(clicked()), this, SLOT(replayStartButtonClicked()));
replayPauseButton = new QToolButton;
replayPauseButton->setIconSize(QSize(32, 32));
replayPauseButton->setEnabled(false);
replayPauseButton->setIcon(QPixmap("theme:replay/pause"));
connect(replayPauseButton, SIGNAL(clicked()), this, SLOT(replayPauseButtonClicked()));
replayFastForwardButton = new QToolButton;
replayFastForwardButton->setIconSize(QSize(32, 32));
replayFastForwardButton->setEnabled(false);
replayFastForwardButton->setIcon(QPixmap("theme:replay/fastforward"));
replayFastForwardButton->setCheckable(true);
connect(replayFastForwardButton, SIGNAL(toggled(bool)), this, SLOT(replayFastForwardButtonToggled(bool)));
replayControlLayout = new QHBoxLayout;
replayControlLayout->addWidget(timelineWidget, 10);
replayControlLayout->addWidget(replayStartButton);
replayControlLayout->addWidget(replayPauseButton);
replayControlLayout->addWidget(replayFastForwardButton);
replayControlWidget = new QWidget();
replayControlWidget->setObjectName("replayControlWidget");
replayControlWidget->setLayout(replayControlLayout);
replayDock = new QDockWidget(this);
replayDock->setObjectName("replayDock");
replayDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
replayDock->setWidget(replayControlWidget);
replayDock->setFloating(false);
replayDock->installEventFilter(this);
connect(replayDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabGame::createDeckViewContainerWidget(bool bReplay)
{
Q_UNUSED(bReplay);
deckViewContainerWidget = new QWidget();
deckViewContainerWidget->setObjectName("deckViewContainerWidget");
deckViewContainerLayout = new QVBoxLayout;
deckViewContainerWidget->setLayout(deckViewContainerLayout);
}
void TabGame::createCardInfoDock(bool bReplay)
{
Q_UNUSED(bReplay);
cardInfo = new CardFrame();
cardHInfoLayout = new QHBoxLayout;
cardVInfoLayout = new QVBoxLayout;
cardVInfoLayout->setContentsMargins(0, 0, 0, 0);
cardVInfoLayout->addWidget(cardInfo);
cardVInfoLayout->addLayout(cardHInfoLayout);
cardBoxLayoutWidget = new QWidget;
cardBoxLayoutWidget->setLayout(cardVInfoLayout);
cardInfoDock = new QDockWidget(this);
cardInfoDock->setObjectName("cardInfoDock");
cardInfoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
cardInfoDock->setWidget(cardBoxLayoutWidget);
cardInfoDock->setFloating(false);
cardInfoDock->installEventFilter(this);
connect(cardInfoDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabGame::createPlayerListDock(bool bReplay)
{
if(bReplay)
{
playerListWidget = new PlayerListWidget(0, 0, this);
} else {
playerListWidget = new PlayerListWidget(tabSupervisor, clients.first(), this);
connect(playerListWidget, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
}
playerListWidget->setFocusPolicy(Qt::NoFocus);
playerListDock = new QDockWidget(this);
playerListDock->setObjectName("playerListDock");
playerListDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
playerListDock->setWidget(playerListWidget);
playerListDock->setFloating(false);
playerListDock->installEventFilter(this);
connect(playerListDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabGame::createMessageDock(bool bReplay)
{
messageLog = new MessageLogWidget(tabSupervisor, this);
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfo, SLOT(setCard(QString)));
connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
if(!bReplay)
{
connect(messageLog, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
connect(messageLog, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString)));
connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged()));
timeElapsedLabel = new QLabel;
timeElapsedLabel->setAlignment(Qt::AlignCenter);
gameTimer = new QTimer(this);
gameTimer->setInterval(1000);
connect(gameTimer, SIGNAL(timeout()), this, SLOT(incrementGameTime()));
gameTimer->start();
sayLabel = new QLabel;
sayEdit = new LineEditCompleter;
sayLabel->setBuddy(sayEdit);
completer = new QCompleter(autocompleteUserList, sayEdit);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setMaxVisibleItems(5);
#if QT_VERSION >= 0x050000
completer->setFilterMode(Qt::MatchStartsWith);
#endif
sayEdit->setCompleter(completer);
actCompleterChanged();
if (spectator && !gameInfo.spectators_can_chat() && tabSupervisor->getAdminLocked()) {
sayLabel->hide();
sayEdit->hide();
}
connect(tabSupervisor, SIGNAL(adminLockChanged(bool)), this, SLOT(adminLockChanged(bool)));
connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(actSay()));
sayHLayout = new QHBoxLayout;
sayHLayout->addWidget(sayLabel);
sayHLayout->addWidget(sayEdit);
}
messageLogLayout = new QVBoxLayout;
messageLogLayout->setContentsMargins(0, 0, 0, 0);
if(!bReplay)
messageLogLayout->addWidget(timeElapsedLabel);
messageLogLayout->addWidget(messageLog);
if(!bReplay)
messageLogLayout->addLayout(sayHLayout);
messageLogLayoutWidget = new QWidget;
messageLogLayoutWidget->setLayout(messageLogLayout);
messageLayoutDock = new QDockWidget(this);
messageLayoutDock->setObjectName("messageLayoutDock");
messageLayoutDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
messageLayoutDock->setWidget(messageLogLayoutWidget);
messageLayoutDock->setFloating(false);
messageLayoutDock->installEventFilter(this);
connect(messageLayoutDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
// Method uses to sync docks state with menu items state
bool TabGame::eventFilter(QObject * o, QEvent * e)
{
if(e->type() == QEvent::Close)
{
if(o == cardInfoDock)
{
aCardInfoDockVisible->setChecked(false);
aCardInfoDockFloating->setEnabled(false);
} else if(o == messageLayoutDock) {
aMessageLayoutDockVisible->setChecked(false);
aMessageLayoutDockFloating->setEnabled(false);
} else if(o == playerListDock) {
aPlayerListDockVisible->setChecked(false);
aPlayerListDockFloating->setEnabled(false);
} else if(o == replayDock) {
aReplayDockVisible->setChecked(false);
aReplayDockFloating->setEnabled(false);
}
}
return false;
}
void TabGame::dockVisibleTriggered()
{
QObject *o = sender();
if(o == aCardInfoDockVisible)
{
cardInfoDock->setVisible(aCardInfoDockVisible->isChecked());
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
return;
}
if(o == aMessageLayoutDockVisible)
{
messageLayoutDock->setVisible(aMessageLayoutDockVisible->isChecked());
aMessageLayoutDockFloating->setEnabled(aMessageLayoutDockVisible->isChecked());
return;
}
if(o == aPlayerListDockVisible)
{
playerListDock->setVisible(aPlayerListDockVisible->isChecked());
aPlayerListDockFloating->setEnabled(aPlayerListDockVisible->isChecked());
return;
}
if(o == aReplayDockVisible)
{
replayDock->setVisible(aReplayDockVisible->isChecked());
aReplayDockFloating->setEnabled(aReplayDockVisible->isChecked());
return;
}
}
void TabGame::dockFloatingTriggered()
{
QObject *o = sender();
if(o == aCardInfoDockFloating)
{
cardInfoDock->setFloating(aCardInfoDockFloating->isChecked());
return;
}
if(o == aMessageLayoutDockFloating)
{
messageLayoutDock->setFloating(aMessageLayoutDockFloating->isChecked());
return;
}
if(o == aPlayerListDockFloating)
{
playerListDock->setFloating(aPlayerListDockFloating->isChecked());
return;
}
if(o == aReplayDockFloating)
{
replayDock->setFloating(aReplayDockFloating->isChecked());
return;
}
}
void TabGame::dockTopLevelChanged(bool topLevel)
{
retranslateUi();
QObject *o = sender();
if(o == cardInfoDock)
{
aCardInfoDockFloating->setChecked(topLevel);
return;
}
if(o == messageLayoutDock)
{
aMessageLayoutDockFloating->setChecked(topLevel);
return;
}
if(o == playerListDock)
{
aPlayerListDockFloating->setChecked(topLevel);
return;
}
if(o == replayDock)
{
aReplayDockFloating->setChecked(topLevel);
return;
}
}

View file

@ -56,6 +56,8 @@ class GameReplay;
class ServerInfo_User;
class PendingCommand;
class LineEditCompleter;
class QDockWidget;
class QStackedWidget;
class ToggleButton : public QPushButton {
Q_OBJECT
@ -77,6 +79,7 @@ private:
QPushButton *loadLocalButton, *loadRemoteButton;
ToggleButton *readyStartButton, *sideboardLockButton;
DeckView *deckView;
TabGame *parentGame;
int playerId;
private slots:
void loadLocalDeck();
@ -90,7 +93,7 @@ private slots:
signals:
void newCardAdded(AbstractCardItem *card);
public:
DeckViewContainer(int _playerId, TabGame *parent = 0);
DeckViewContainer(int _playerId, TabGame *parent);
void retranslateUi();
void setButtonsVisible(bool _visible);
void setReadyStart(bool ready);
@ -121,6 +124,7 @@ private:
QStringList gameTypes;
QCompleter *completer;
QStringList autocompleteUserList;
QStackedWidget * mainWidget;
// Replay related members
GameReplay *replay;
@ -128,8 +132,7 @@ private:
QList<int> replayTimeline;
ReplayTimelineWidget *timelineWidget;
QToolButton *replayStartButton, *replayPauseButton, *replayFastForwardButton;
QSplitter *splitter;
CardFrame *cardInfo;
PlayerListWidget *playerListWidget;
QLabel *timeElapsedLabel;
@ -140,13 +143,14 @@ private:
GameScene *scene;
GameView *gameView;
QMap<int, DeckViewContainer *> deckViewContainers;
QVBoxLayout *deckViewContainerLayout;
QHBoxLayout *mainLayout;
ZoneViewLayout *zoneLayout;
QVBoxLayout *cardVInfoLayout, *messageLogLayout, *gamePlayAreaVBox, *deckViewContainerLayout;
QHBoxLayout *cardHInfoLayout, *sayHLayout, *mainHLayout, *replayControlLayout;
QWidget *cardBoxLayoutWidget, *messageLogLayoutWidget, *gamePlayAreaWidget, *deckViewContainerWidget, *replayControlWidget;
QDockWidget *cardInfoDock, *messageLayoutDock, *playerListDock, *replayDock;
QAction *playersSeparator;
QMenu *gameMenu;
QMenu *phasesMenu;
QAction *aGameInfo, *aConcede, *aLeaveGame, *aCloseReplay, *aNextPhase, *aNextTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW;
QMenu *gameMenu, *phasesMenu, *viewMenu, *cardInfoDockMenu, *messageLayoutDockMenu, *playerListDockMenu, *replayDockMenu;
QAction *aGameInfo, *aConcede, *aLeaveGame, *aCloseReplay, *aNextPhase, *aNextTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aMessageLayoutDockVisible, *aMessageLayoutDockFloating, *aPlayerListDockVisible, *aPlayerListDockFloating, *aReplayDockVisible, *aReplayDockFloating;
QList<QAction *> phaseActions;
Player *addPlayer(int playerId, const ServerInfo_User &info);
@ -171,6 +175,15 @@ private:
void eventSetActivePhase(const Event_SetActivePhase &event, int eventPlayerId, const GameEventContext &context);
void eventPing(const Event_Ping &event, int eventPlayerId, const GameEventContext &context);
void emitUserEvent();
void createMenuItems();
void createReplayMenuItems();
void createViewMenuItems();
void createCardInfoDock(bool bReplay=false);
void createPlayerListDock(bool bReplay=false);
void createMessageDock(bool bReplay=false);
void createPlayAreaWidget(bool bReplay=false);
void createDeckViewContainerWidget(bool bReplay=false);
void createReplayDock();
signals:
void gameClosing(TabGame *tab);
void playerAdded(Player *player);
@ -207,7 +220,14 @@ private slots:
void refreshShortcuts();
void loadLayout();
void actCompleterChanged();
void actResetLayout();
bool eventFilter(QObject *o, QEvent *e);
void dockVisibleTriggered();
void dockFloatingTriggered();
void dockTopLevelChanged(bool topLevel);
public:
TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, const Event_GameJoined &event, const QMap<int, QString> &_roomGameTypes);
TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay);

View file

@ -8,6 +8,7 @@
#include <QLineEdit>
#include <QCheckBox>
#include <QRadioButton>
#include <QTabWidget>
#include "tab_logs.h"
#include "abstractclient.h"
#include "window_sets.h"
@ -23,7 +24,27 @@
TabLog::TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client, QWidget *parent)
: Tab(_tabSupervisor, parent), client(_client)
{
MainWindow = new QMainWindow;
roomTable = new QTableWidget();
roomTable->setColumnCount(6);
roomTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
roomTable->setHorizontalHeaderLabels(QString(tr("Time;SenderName;SenderIP;Message;TargetID;TargetName")).split(";"));
gameTable = new QTableWidget();
gameTable->setColumnCount(6);
gameTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
gameTable->setHorizontalHeaderLabels(QString(tr("Time;SenderName;SenderIP;Message;TargetID;TargetName")).split(";"));
chatTable = new QTableWidget();
chatTable->setColumnCount(6);
chatTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
chatTable->setHorizontalHeaderLabels(QString(tr("Time;SenderName;SenderIP;Message;TargetID;TargetName")).split(";"));
QTabWidget *tabManager = new QTabWidget();
tabManager->addTab(roomTable, tr("Room Logs"));
tabManager->addTab(gameTable, tr("Game Logs"));
tabManager->addTab(chatTable, tr("Chat Logs"));
setCentralWidget(tabManager);
createDock();
restartLayout();
clearClicked();
@ -212,7 +233,7 @@ void TabLog::createDock()
buttonGroupBox = new QGroupBox(tr(""));
buttonGroupBox->setLayout(buttonGrid);
mainLayout = new QVBoxLayout(MainWindow);
mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(criteriaGroupBox);
mainLayout->addWidget(locationGroupBox);
mainLayout->addWidget(rangeGroupBox);
@ -221,116 +242,94 @@ void TabLog::createDock()
mainLayout->addWidget(buttonGroupBox);
mainLayout->setAlignment(Qt::AlignCenter);
searchDockContents = new QWidget(MainWindow);
searchDockContents = new QWidget(this);
searchDockContents->setLayout(mainLayout);
searchDock = new QDockWidget(MainWindow);
searchDock = new QDockWidget(this);
searchDock->setFeatures(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable);
searchDock->setWidget(searchDockContents);
QVBoxLayout *mainVLayoutContent = new QVBoxLayout;
QHBoxLayout *mainHLayoutContent = new QHBoxLayout;
mainHLayoutContent->addWidget(MainWindow);
mainHLayoutContent->addLayout(mainVLayoutContent);
setLayout(mainHLayoutContent);
}
void TabLog::viewLogHistory_processResponse(const Response &resp)
{
const Response_ViewLogHistory &response = resp.GetExtension(Response_ViewLogHistory::ext);
if (resp.response_code() == Response::RespOk) {
if (response.log_message_size() > 0) {
int j = 0;
QTableWidget *roomTable = new QTableWidget();
roomTable->setWindowTitle(tr("Room Logs"));
roomTable->setRowCount(response.log_message_size());
roomTable->setColumnCount(6);
roomTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
roomTable->setHorizontalHeaderLabels(QString(tr("Time;SenderName;SenderIP;Message;TargetID;TargetName")).split(";"));
int k = 0;
QTableWidget *gameTable = new QTableWidget();
gameTable->setWindowTitle(tr("Game Logs"));
gameTable->setRowCount(response.log_message_size());
gameTable->setColumnCount(6);
gameTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
gameTable->setHorizontalHeaderLabels(QString(tr("Time;SenderName;SenderIP;Message;TargetID;TargetName")).split(";"));
int l = 0;
QTableWidget *chatTable = new QTableWidget();
chatTable->setWindowTitle(tr("Chat Logs"));
chatTable->setRowCount(response.log_message_size());
chatTable->setColumnCount(6);
chatTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
chatTable->setHorizontalHeaderLabels(QString(tr("Time;SenderName;SenderIP;Message;TargetID;TargetName")).split(";"));
ServerInfo_ChatMessage message; for (int i = 0; i < response.log_message_size(); ++i) {
message = response.log_message(i);
if (QString::fromStdString(message.target_type()) == "room") {
roomTable->setItem(j, 0, new QTableWidgetItem(QString::fromStdString(message.time())));
roomTable->setItem(j, 1, new QTableWidgetItem(QString::fromStdString(message.sender_name())));
roomTable->setItem(j, 2, new QTableWidgetItem(QString::fromStdString(message.sender_ip())));
roomTable->setItem(j, 3, new QTableWidgetItem(QString::fromStdString(message.message())));
roomTable->setItem(j, 4, new QTableWidgetItem(QString::fromStdString(message.target_id())));
roomTable->setItem(j, 5, new QTableWidgetItem(QString::fromStdString(message.target_name())));
++j;
}
if (QString::fromStdString(message.target_type()) == "game") {
gameTable->setItem(k, 0, new QTableWidgetItem(QString::fromStdString(message.time())));
gameTable->setItem(k, 1, new QTableWidgetItem(QString::fromStdString(message.sender_name())));
gameTable->setItem(k, 2, new QTableWidgetItem(QString::fromStdString(message.sender_ip())));
gameTable->setItem(k, 3, new QTableWidgetItem(QString::fromStdString(message.message())));
gameTable->setItem(k, 4, new QTableWidgetItem(QString::fromStdString(message.target_id())));
gameTable->setItem(k, 5, new QTableWidgetItem(QString::fromStdString(message.target_name())));
++k;
}
if (QString::fromStdString(message.target_type()) == "chat") {
chatTable->setItem(l, 0, new QTableWidgetItem(QString::fromStdString(message.time())));
chatTable->setItem(l, 1, new QTableWidgetItem(QString::fromStdString(message.sender_name())));
chatTable->setItem(l, 2, new QTableWidgetItem(QString::fromStdString(message.sender_ip())));
chatTable->setItem(l, 3, new QTableWidgetItem(QString::fromStdString(message.message())));
chatTable->setItem(l, 4, new QTableWidgetItem(QString::fromStdString(message.target_id())));
chatTable->setItem(l, 5, new QTableWidgetItem(QString::fromStdString(message.target_name())));
++l;
}
}
roomTable->setRowCount(j);
roomTable->resizeColumnsToContents();
gameTable->setRowCount(k);
gameTable->resizeColumnsToContents();
chatTable->setRowCount(l);
chatTable->resizeColumnsToContents();
if (mainRoom->isChecked()) {
roomTable->resize(600, 200);
roomTable->show();
}
if (gameRoom->isChecked()) {
gameTable->resize(600, 200);
gameTable->show();
}
if (privateChat->isChecked()) {
chatTable->resize(600, 200);
chatTable->show();
}
} else
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Message History"), tr("There are no messages for the selected filters."));
} else
if (resp.response_code() != Response::RespOk) {
QMessageBox::critical(static_cast<QWidget *>(parent()), tr("Message History"), tr("Failed to collect message history information."));
return;
}
if (response.log_message_size() == 0) {
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Message History"), tr("There are no messages for the selected filters."));
return;
}
int roomCounter = 0, gameCounter = 0, chatCounter = 0;
roomTable->setRowCount(roomCounter);
gameTable->setRowCount(gameCounter);
chatTable->setRowCount(chatCounter);
for (int i = 0; i < response.log_message_size(); ++i)
{
ServerInfo_ChatMessage message = response.log_message(i);
if (QString::fromStdString(message.target_type()) == "room") {
roomTable->insertRow(roomCounter);
roomTable->setItem(roomCounter, 0, new QTableWidgetItem(QString::fromStdString(message.time())));
roomTable->setItem(roomCounter, 1, new QTableWidgetItem(QString::fromStdString(message.sender_name())));
roomTable->setItem(roomCounter, 2, new QTableWidgetItem(QString::fromStdString(message.sender_ip())));
roomTable->setItem(roomCounter, 3, new QTableWidgetItem(QString::fromStdString(message.message())));
roomTable->setItem(roomCounter, 4, new QTableWidgetItem(QString::fromStdString(message.target_id())));
roomTable->setItem(roomCounter, 5, new QTableWidgetItem(QString::fromStdString(message.target_name())));
++roomCounter;
}
if (QString::fromStdString(message.target_type()) == "game") {
gameTable->insertRow(gameCounter);
gameTable->setItem(gameCounter, 0, new QTableWidgetItem(QString::fromStdString(message.time())));
gameTable->setItem(gameCounter, 1, new QTableWidgetItem(QString::fromStdString(message.sender_name())));
gameTable->setItem(gameCounter, 2, new QTableWidgetItem(QString::fromStdString(message.sender_ip())));
gameTable->setItem(gameCounter, 3, new QTableWidgetItem(QString::fromStdString(message.message())));
gameTable->setItem(gameCounter, 4, new QTableWidgetItem(QString::fromStdString(message.target_id())));
gameTable->setItem(gameCounter, 5, new QTableWidgetItem(QString::fromStdString(message.target_name())));
++gameCounter;
}
if (QString::fromStdString(message.target_type()) == "chat") {
chatTable->insertRow(chatCounter);
chatTable->setItem(chatCounter, 0, new QTableWidgetItem(QString::fromStdString(message.time())));
chatTable->setItem(chatCounter, 1, new QTableWidgetItem(QString::fromStdString(message.sender_name())));
chatTable->setItem(chatCounter, 2, new QTableWidgetItem(QString::fromStdString(message.sender_ip())));
chatTable->setItem(chatCounter, 3, new QTableWidgetItem(QString::fromStdString(message.message())));
chatTable->setItem(chatCounter, 4, new QTableWidgetItem(QString::fromStdString(message.target_id())));
chatTable->setItem(chatCounter, 5, new QTableWidgetItem(QString::fromStdString(message.target_name())));
++chatCounter;
}
}
if (roomCounter) {
roomTable->show();
roomTable->resizeColumnsToContents();
} else {
roomTable->hide();
}
if (gameCounter) {
gameTable->resizeColumnsToContents();
gameTable->show();
} else {
gameTable->hide();
}
if (chatCounter) {
chatTable->resizeColumnsToContents();
chatTable->show();
} else {
chatTable->hide();
}
}
void TabLog::restartLayout()
{
searchDock->setFloating(false);
MainWindow->addDockWidget(Qt::TopDockWidgetArea, searchDock);
addDockWidget(Qt::LeftDockWidgetArea, searchDock);
searchDock->setVisible(true);
}

View file

@ -16,10 +16,10 @@ class QLabel;
class QDockWidget;
class QWidget;
class QGridLayout;
class QTableWidget;
class CommandContainer;
class Response;
class AbstractClient;
class QMainWindow;
class TabLog : public Tab {
Q_OBJECT
@ -36,7 +36,7 @@ private:
QGridLayout *criteriaGrid, *locationGrid, *rangeGrid, *maxResultsGrid, *descriptionGrid, *buttonGrid;
QGroupBox *criteriaGroupBox, *locationGroupBox, *rangeGroupBox, *maxResultsGroupBox, *descriptionGroupBox, *buttonGroupBox;
QVBoxLayout *mainLayout;
QMainWindow *MainWindow;
QTableWidget *roomTable, *gameTable, *chatTable;
void createDock();
signals:

View file

@ -39,7 +39,10 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor, AbstractClient *_client, c
addTabMenu(messageMenu);
retranslateUi();
setLayout(vbox);
QWidget * mainWidget = new QWidget(this);
mainWidget->setLayout(vbox);
setCentralWidget(mainWidget);
}
TabMessage::~TabMessage()

View file

@ -106,7 +106,10 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client)
rightToolBar->addAction(aDeleteRemoteReplay);
retranslateUi();
setLayout(hbox);
QWidget * mainWidget = new QWidget(this);
mainWidget->setLayout(hbox);
setCentralWidget(mainWidget);
connect(client, SIGNAL(replayAddedEventReceived(const Event_ReplayAdded &)), this, SLOT(replayAddedEventReceived(const Event_ReplayAdded &)));
}

View file

@ -104,9 +104,6 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI
roomMenu->addAction(aLeaveRoom);
addTabMenu(roomMenu);
retranslateUi();
setLayout(hbox);
const int userListSize = info.user_list_size();
for (int i = 0; i < userListSize; ++i){
userList->processUserInfo(info.user_list(i), true);
@ -130,6 +127,12 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI
actCompleterChanged();
connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts()));
refreshShortcuts();
retranslateUi();
QWidget * mainWidget = new QWidget(this);
mainWidget->setLayout(hbox);
setCentralWidget(mainWidget);
}
TabRoom::~TabRoom()

View file

@ -140,8 +140,12 @@ TabServer::TabServer(TabSupervisor *_tabSupervisor, AbstractClient *_client, QWi
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(roomSelector);
vbox->addWidget(serverInfoBox);
setLayout(vbox);
retranslateUi();
QWidget * mainWidget = new QWidget(this);
mainWidget->setLayout(vbox);
setCentralWidget(mainWidget);
}
void TabServer::retranslateUi()

View file

@ -2,6 +2,7 @@
#include "userlist.h"
#include "userinfobox.h"
#include "abstractclient.h"
#include "soundengine.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
@ -74,7 +75,11 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor, AbstractClient *_clien
mainLayout->addLayout(ignorePanel);
mainLayout->addLayout(vbox);
setLayout(mainLayout);
retranslateUi();
QWidget * mainWidget = new QWidget(this);
mainWidget->setLayout(mainLayout);
setCentralWidget(mainWidget);
}
void TabUserLists::addToBuddyList()
@ -146,12 +151,19 @@ void TabUserLists::processUserJoinedEvent(const Event_UserJoined &event)
ignoreList->sortItems();
buddyList->sortItems();
if (buddyList->getUsers().keys().contains(userName))
soundEngine->playSound("buddy_join");
emit userJoined(info);
}
void TabUserLists::processUserLeftEvent(const Event_UserLeft &event)
{
QString userName = QString::fromStdString(event.name());
if (buddyList->getUsers().keys().contains(userName))
soundEngine->playSound("buddy_leave");
if (allUsersList->deleteUser(userName)) {
ignoreList->setUserOnline(userName, false);
buddyList->setUserOnline(userName, false);

View file

@ -0,0 +1,121 @@
//
// Created by miguel on 28/12/15.
//
#include <algorithm>
#include <QMessageBox>
#include "update_checker.h"
#include "version_string.h"
#include "qt-json/json.h"
#define LATEST_FILES_URL "https://api.bintray.com/packages/cockatrice/Cockatrice/Cockatrice/files"
UpdateChecker::UpdateChecker(QObject *parent) : QObject(parent){
//Parse the commit date. We'll use this to check for new versions
//We know the format because it's based on `git log` which is documented here:
// https://git-scm.com/docs/git-log#_commit_formatting
buildDate = QDate::fromString(VERSION_DATE, "yyyy-MM-dd");
latestFilesUrl = QUrl(LATEST_FILES_URL);
response = NULL;
netMan = new QNetworkAccessManager(this);
build = NULL;
}
UpdateChecker::~UpdateChecker()
{
delete build;
}
void UpdateChecker::check()
{
response = netMan->get(QNetworkRequest(latestFilesUrl));
connect(response, SIGNAL(finished()),
this, SLOT(fileListFinished()));
}
#if defined(Q_OS_OSX)
bool UpdateChecker::downloadMatchesCurrentOS(QVariant build)
{
return build
.toMap()["name"]
.toString()
.contains("osx");
}
#elif defined(Q_OS_WIN)
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
bool UpdateChecker::downloadMatchesCurrentOS(QVariant build)
{
return build
.toMap()["name"]
.toString()
.contains("qt5.exe");
}
#else
bool UpdateChecker::downloadMatchesCurrentOS(QVariant build)
{
return build
.toMap()["name"]
.toString()
.contains("qt4.exe");
}
#endif
#else
bool UpdateChecker::downloadMatchesCurrentOS(QVariant)
{
//If the OS doesn't fit one of the above #defines, then it will never match
return false;
}
#endif
QDate UpdateChecker::dateFromBuild(QVariant build)
{
QString formatString = "yyyy-MM-dd";
QString dateString = build.toMap()["date"].toString();
dateString = dateString.remove(formatString.length(), dateString.length());
return QDate::fromString(dateString, formatString);
}
QDate UpdateChecker::findOldestBuild(QVariantList allBuilds)
{
//Map the build array into an array of dates
std::vector<QDate> dateArray(allBuilds.size());
std::transform(allBuilds.begin(), allBuilds.end(), dateArray.begin(), dateFromBuild);
//Return the first date
return *std::min_element(dateArray.begin(), dateArray.end());
}
QVariantMap *UpdateChecker::findCompatibleBuild(QVariantList allBuilds) {
QVariantList::iterator result = std::find_if(allBuilds.begin(), allBuilds.end(), downloadMatchesCurrentOS);
//If there is no compatible version, return NULL
if (result == allBuilds.end())
return NULL;
else
{
QVariantMap *ret = new QVariantMap;
*ret = (*result).toMap();
return ret;
}
}
void UpdateChecker::fileListFinished() {
try {
QVariantList builds = QtJson::Json::parse(response->readAll()).toList();
build = findCompatibleBuild(builds);
QDate bintrayBuildDate = findOldestBuild(builds);
bool needToUpdate = bintrayBuildDate > buildDate;
bool compatibleVersion = build != NULL;
emit finishedCheck(needToUpdate, compatibleVersion, build);
}
catch (const std::exception &exc){
emit error(exc.what());
}
}

View file

@ -0,0 +1,38 @@
//
// Created by miguel on 28/12/15.
//
#ifndef COCKATRICE_UPDATECHECKER_H
#define COCKATRICE_UPDATECHECKER_H
#include <QObject>
#include <QUrl>
#include <QDate>
#include <QtNetwork>
class UpdateChecker : public QObject {
Q_OBJECT
public:
UpdateChecker(QObject *parent);
~UpdateChecker();
void check();
signals:
void finishedCheck(bool needToUpdate, bool isCompatible, QVariantMap *build);
void error(QString errorString);
private:
static QVariantMap *findCompatibleBuild();
static QDate findOldestBuild(QVariantList allBuilds);
static QDate dateFromBuild(QVariant build);
static QVariantMap *findCompatibleBuild(QVariantList allBuilds);
static bool downloadMatchesCurrentOS(QVariant build);
QVariantMap *build;
QUrl latestFilesUrl;
QDate buildDate;
QNetworkAccessManager *netMan;
QNetworkReply *response;
private slots:
void fileListFinished();
};
#endif //COCKATRICE_UPDATECHECKER_H

View file

@ -0,0 +1,66 @@
#include <QUrl>
#include "update_downloader.h"
UpdateDownloader::UpdateDownloader(QObject *parent) : QObject(parent) {
netMan = new QNetworkAccessManager(this);
}
void UpdateDownloader::beginDownload(QUrl downloadUrl) {
//Save the original URL because we need it for the filename
if (originalUrl.isEmpty())
originalUrl = downloadUrl;
response = netMan->get(QNetworkRequest(downloadUrl));
connect(response, SIGNAL(finished()),
this, SLOT(fileFinished()));
connect(response, SIGNAL(readyRead()),
this, SLOT(fileReadyRead()));
connect(response, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(downloadProgress(qint64, qint64)));
connect(response, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(downloadError(QNetworkReply::NetworkError)));
}
void UpdateDownloader::downloadError(QNetworkReply::NetworkError) {
emit error(response->errorString().toUtf8());
}
void UpdateDownloader::fileFinished() {
//If we finished but there's a redirect, follow it
QVariant redirect = response->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (!redirect.isNull())
{
beginDownload(redirect.toUrl());
return;
}
//Handle any errors we had
if (response->error())
{
emit error(response->errorString());
return;
}
//Work out the file name of the download
QString fileName = QDir::temp().path() + QDir::separator() + originalUrl.toString().section('/', -1);
//Save the build in a temporary directory
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
emit error("Could not open the file for reading.");
return;
}
file.write(response->readAll());
file.close();
//Emit the success signal with a URL to the download file
emit downloadSuccessful(QUrl::fromLocalFile(fileName));
}
void UpdateDownloader::downloadProgress(qint64 bytesRead, qint64 totalBytes) {
emit progressMade(bytesRead, totalBytes);
}

View file

@ -0,0 +1,33 @@
//
// Created by miguel on 28/12/15.
//
#ifndef COCKATRICE_UPDATEDOWNLOADER_H
#define COCKATRICE_UPDATEDOWNLOADER_H
#include <QObject>
#include <QUrl>
#include <QDate>
#include <QtNetwork>
class UpdateDownloader : public QObject {
Q_OBJECT
public:
UpdateDownloader(QObject *parent);
void beginDownload(QUrl url);
signals:
void downloadSuccessful(QUrl filepath);
void progressMade(qint64 bytesRead, qint64 totalBytes);
void error(QString errorString);
private:
QUrl originalUrl;
QNetworkAccessManager *netMan;
QNetworkReply *response;
private slots:
void fileFinished();
void downloadProgress(qint64 bytesRead, qint64 totalBytes);
void downloadError(QNetworkReply::NetworkError);
};
#endif //COCKATRICE_UPDATEDOWNLOADER_H

View file

@ -30,6 +30,7 @@
#include <QDateTime>
#include <QSystemTrayIcon>
#include <QApplication>
#include <QtNetwork>
#if QT_VERSION < 0x050000
#include <QtGui/qtextdocument.h> // for Qt::escape()
@ -40,6 +41,7 @@
#include "dlg_connect.h"
#include "dlg_register.h"
#include "dlg_settings.h"
#include "dlg_update.h"
#include "tab_supervisor.h"
#include "remoteclient.h"
#include "localserver.h"
@ -48,12 +50,14 @@
#include "settingscache.h"
#include "tab_game.h"
#include "version_string.h"
#include "update_checker.h"
#include "pb/game_replay.pb.h"
#include "pb/room_commands.pb.h"
#include "pb/event_connection_closed.pb.h"
#include "pb/event_server_shutdown.pb.h"
#define GITHUB_PAGES_URL "https://cockatrice.github.io"
#define GITHUB_CONTRIBUTORS_URL "https://github.com/Cockatrice/Cockatrice/graphs/contributors?type=c"
#define GITHUB_CONTRIBUTE_URL "https://github.com/Cockatrice/Cockatrice#cockatrice"
#define GITHUB_TRANSLATOR_RECOGNIZE_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translators"
@ -62,6 +66,8 @@
#define GITHUB_TROUBLESHOOTING_URL "https://github.com/Cockatrice/Cockatrice/wiki/Troubleshooting"
#define GITHUB_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Frequently-Asked-Questions"
#define DOWNLOAD_URL "https://dl.bintray.com/cockatrice/Cockatrice/"
const QString MainWindow::appName = "Cockatrice";
void MainWindow::updateTabMenu(const QList<QMenu *> &newMenuList)
@ -273,6 +279,7 @@ void MainWindow::actAbout()
QMessageBox::about(this, tr("About Cockatrice"), QString(
"<font size=\"8\"><b>Cockatrice</b></font><br>"
+ tr("Version %1").arg(VERSION_STRING)
+ "<br><br><b><a href='" + GITHUB_PAGES_URL + "'>" + tr("Cockatrice Webpage") + "</a></b><br>"
+ "<br><br><b>" + tr("Project Manager:") + "</b><br>Gavin Bisesi<br><br>"
+ "<b>" + tr("Past Project Managers:") + "</b><br>Max-Wilhelm Bruker<br>Marcus Schütz<br><br>"
+ "<b>" + tr("Developers:") + "</b><br>"
@ -288,6 +295,12 @@ void MainWindow::actAbout()
));
}
void MainWindow::actUpdate()
{
DlgUpdate dlg(this);
dlg.exec();
}
void MainWindow::serverTimeout()
{
QMessageBox::critical(this, tr("Error"), tr("Server timeout"));
@ -495,6 +508,7 @@ void MainWindow::retranslateUi()
#endif
aAbout->setText(tr("&About Cockatrice"));
aUpdate->setText(tr("&Update Cockatrice"));
helpMenu->setTitle(tr("&Help"));
aCheckCardUpdates->setText(tr("Check for card updates..."));
tabSupervisor->retranslateUi();
@ -525,6 +539,8 @@ void MainWindow::createActions()
aAbout = new QAction(this);
connect(aAbout, SIGNAL(triggered()), this, SLOT(actAbout()));
aUpdate = new QAction(this);
connect(aUpdate, SIGNAL(triggered()), this, SLOT(actUpdate()));
aCheckCardUpdates = new QAction(this);
connect(aCheckCardUpdates, SIGNAL(triggered()), this, SLOT(actCheckCardUpdates()));
@ -566,6 +582,7 @@ void MainWindow::createMenus()
helpMenu = menuBar()->addMenu(QString());
helpMenu->addAction(aAbout);
helpMenu->addAction(aUpdate);
}
MainWindow::MainWindow(QWidget *parent)

View file

@ -25,8 +25,11 @@
#include <QSystemTrayIcon>
#include <QProcess>
#include <QMessageBox>
#include <QtNetwork>
#include "abstractclient.h"
#include "pb/response.pb.h"
#include "update_checker.h"
class TabSupervisor;
class RemoteClient;
@ -66,6 +69,7 @@ private slots:
void actExit();
void actAbout();
void actUpdate();
void iconActivated(QSystemTrayIcon::ActivationReason reason);
@ -90,7 +94,7 @@ private:
QList<QMenu *> tabMenus;
QMenu *cockatriceMenu, *helpMenu;
QAction *aConnect, *aDisconnect, *aSinglePlayer, *aWatchReplay, *aDeckEditor, *aFullScreen, *aSettings, *aExit,
*aAbout, *aCheckCardUpdates, *aRegister;
*aAbout, *aCheckCardUpdates, *aRegister, *aUpdate;
TabSupervisor *tabSupervisor;
QMenu *trayIconMenu;
@ -105,6 +109,7 @@ private:
QMessageBox serverShutdownMessageBox;
QProcess * cardUpdateProcess;
public:
MainWindow(QWidget *parent = 0);
~MainWindow();

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -608,7 +608,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoo
ServerInfo_ChatMessage chatMessage;
for (int i = 0; i < chatHistory.size(); ++i) {
chatMessage = chatHistory.at(i);
qDebug() << QString::fromStdString(chatMessage.message()).simplified();
Event_RoomSay roomChatHistory;
roomChatHistory.set_message(chatMessage.sender_name() + ": " + chatMessage.message());
roomChatHistory.set_message_type(Event_RoomSay::ChatHistory);

View file

@ -16,7 +16,8 @@
<message>
<source>This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice.
You will need to specify a URL or a filename that will be used as a source.</source>
<translation type="unfinished"/>
<translation>Dieser Assistent wird eine Liste aller Editionen, Karten und Spielsteine, die von Cockatrice genutzt werden, importieren.
Sie müssen eine URL oder einen Dateinamen als Quelle angeben.</translation>
</message>
</context>
<context>
@ -91,7 +92,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Do you want to try to download a fresh copy of the uncompressed file instead?</source>
<translation>Möchtest du stattdessen eine neue Kopie der nicht komprimierten Datei herunerladen?</translation>
<translation>Möchtest du stattdessen eine neue Kopie der nicht komprimierten Datei herunterladen?</translation>
</message>
<message>
<source>The file was retrieved successfully, but it does not contain any sets data.</source>
@ -99,19 +100,19 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>Bitte geben Sie eine Quelle für die Liste von Editionen und Karten an. Sie können eine URL Adresse zum Herunterladen angeben oder eine Datei von Ihrem Computer verwenden.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>Download URL:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>Standard-URL wiederherstellen</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>Die eingegebene URL ist nicht gültig.</translation>
</message>
</context>
<context>
@ -138,19 +139,19 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>Bitte geben Sie eine Quelle für die Liste der Spielsteine an. Sie können eine URL Adresse zum Herunterladen angeben oder eine Datei von Ihrem Computer verwenden.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>Download URL:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>Standard-URL wiederherstellen</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>Die eingegebene URL ist nicht gültig.</translation>
</message>
</context>
<context>

View file

@ -16,7 +16,8 @@
<message>
<source>This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice.
You will need to specify a URL or a filename that will be used as a source.</source>
<translation type="unfinished"/>
<translation>Este asistente importará la lista de los sets y cartas que serán usadas por Cockatrice.
Necesitarás especificar la URL o el nombre de archivo que será usado como origen.</translation>
</message>
</context>
<context>
@ -99,58 +100,58 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>Por favor especifica un origen para la lista de sets y cartas. Puedes especificar la URL de donde descargarla o usar un archivo existente de tu ordenador.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>URL de descarga:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>Restablecer URL predeterminada</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>La URL suministrada no es válida.</translation>
</message>
</context>
<context>
<name>LoadTokensPage</name>
<message>
<source>Tokens source selection</source>
<translation type="unfinished"/>
<translation>Selección de origen de tokens</translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"/>
<translation>Error</translation>
</message>
<message>
<source>Downloading (0MB)</source>
<translation type="unfinished"/>
<translation>Descargando (0MB)</translation>
</message>
<message>
<source>Downloading (%1MB)</source>
<translation type="unfinished"/>
<translation>Descargando (%1MB)</translation>
</message>
<message>
<source>Network error: %1.</source>
<translation type="unfinished"/>
<translation>Error de red: %1.</translation>
</message>
<message>
<source>Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>Por favor especifica un origen para la lista de tokens. Puedes especificar la URL de donde descargarla o usar un archivo existente en tu ordenador.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>URL de descarga:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>Restablecer URL predeterminada</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>La URL suministrada no es válida.</translation>
</message>
</context>
<context>
@ -228,40 +229,41 @@ You will need to specify a URL or a filename that will be used as a source.</sou
<name>SaveTokensPage</name>
<message>
<source>Tokens imported</source>
<translation type="unfinished"/>
<translation>Tokens importados</translation>
</message>
<message>
<source>The tokens has been imported. Press &quot;Save&quot; to save the imported tokens to the Cockatrice tokens database.</source>
<translation type="unfinished"/>
<translation>Los tokens han sido importados. Pulsa &quot;Guardar&quot; para guardar los tokens importados en la base de datos de tokens de Cockatrice.</translation>
</message>
<message>
<source>Save to the default path (recommended)</source>
<translation type="unfinished"/>
<translation>Guardar en la ruta por defecto (recomendado)</translation>
</message>
<message>
<source>Save token database</source>
<translation type="unfinished"/>
<translation>Base de datos de tokens:</translation>
</message>
<message>
<source>XML; token database (*.xml)</source>
<translation type="unfinished"/>
<translation>XML; base de datos de tokens (*.xml)</translation>
</message>
<message>
<source>Success</source>
<translation type="unfinished"/>
<translation>Éxito</translation>
</message>
<message>
<source>The token database has been saved successfully to
%1</source>
<translation type="unfinished"/>
<translation>La base de datos de cartas ha sido guardada correctamente en
%1</translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"/>
<translation>Error</translation>
</message>
<message>
<source>The file could not be saved to %1</source>
<translation type="unfinished"/>
<translation>El archivo no ha podido ser guardado en %1</translation>
</message>
</context>
<context>

View file

@ -16,7 +16,8 @@
<message>
<source>This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice.
You will need to specify a URL or a filename that will be used as a source.</source>
<translation type="unfinished"/>
<translation>Cet assistant va importer la liste des éditions, cartes et jetons qui seront utilisés par Cockatrice.
Vous devrez spécifier une URL ou un fichier qui sera utilisé comme source.</translation>
</message>
</context>
<context>
@ -31,7 +32,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Choose file...</source>
<translation>Choisir un fichier...</translation>
<translation>Choisissez un fichier...</translation>
</message>
<message>
<source>Load sets file</source>
@ -79,11 +80,11 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Zip extraction failed: the Zip archive doesn&apos;t contain exactly one file.</source>
<translation>Ce fichier ne peut être enregistré au répertoire</translation>
<translation>Extraction zip échouée: l&apos;archive zip contient plus qu&apos;un fichier.</translation>
</message>
<message>
<source>Zip extraction failed: %1.</source>
<translation>l&apos;extraction du zip à échoué : %1.</translation>
<translation>L&apos;extraction du zip a échoué : %1.</translation>
</message>
<message>
<source>Sorry, this version of Oracle does not support zipped files.</source>
@ -99,30 +100,30 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>Spécifiez une source pour la liste des éditions et des cartes. Vous pouvez spécifier une URL qui sera téléchargée ou utiliser un fichier existant sur votre ordinateur.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>URL de téléchargement:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>Restaurer l&apos;URL par défaut</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>L&apos;URL fournie n&apos;est pas valable</translation>
</message>
</context>
<context>
<name>LoadTokensPage</name>
<message>
<source>Tokens source selection</source>
<translation type="unfinished"/>
<translation>sélection de la source des jetons</translation>
</message>
<message>
<source>Error</source>
<translation>Érreur</translation>
<translation>Erreur</translation>
</message>
<message>
<source>Downloading (0MB)</source>
@ -138,19 +139,19 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>Spécifiez une source pour la liste des jetons. Vous pouvez spécifier une URL qui sera téléchargée ou utiliser un fichier existant sur votre ordinateur.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>URL de téléchargement:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>Restorer l&apos;URL par défaut</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>L&apos;URL fournie n&apos;est pas valide.</translation>
</message>
</context>
<context>
@ -228,11 +229,11 @@ You will need to specify a URL or a filename that will be used as a source.</sou
<name>SaveTokensPage</name>
<message>
<source>Tokens imported</source>
<translation type="unfinished"/>
<translation>Jetons importés</translation>
</message>
<message>
<source>The tokens has been imported. Press &quot;Save&quot; to save the imported tokens to the Cockatrice tokens database.</source>
<translation type="unfinished"/>
<translation>Les jetons ont é importés. Pressez sur &quot;Sauvegarder&quot; pour sauver les jetons importés dans la base de données des jetons Cockatrice.</translation>
</message>
<message>
<source>Save to the default path (recommended)</source>
@ -253,7 +254,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
<message>
<source>The token database has been saved successfully to
%1</source>
<translation type="unfinished"/>
<translation>La base de donnée de jetons a é correctement sauvegardée dans %1</translation>
</message>
<message>
<source>Error</source>
@ -261,7 +262,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>The file could not be saved to %1</source>
<translation type="unfinished"/>
<translation>Le fichier n&apos;a pas pu être sauvegardé dans %1</translation>
</message>
</context>
<context>

View file

@ -16,7 +16,7 @@
<message>
<source>This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice.
You will need to specify a URL or a filename that will be used as a source.</source>
<translation type="unfinished"/>
<translation>Questo wizard importerà la lista dei set e delle carte che verranno usate da Cockatrice.&lt;br/&gt;Dovrai specificare un indirizzo url o il nome di un file che verrà utilizzato come sorgente.</translation>
</message>
</context>
<context>
@ -99,19 +99,19 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>Specifica una sorgente per la lista dei set e delle carte. Puoi specificare un indirizzo url da cui scaricare il file o alternativamente usare un file già presente nel tuo computer.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>Indirizzo download:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>Usa l&apos;indirizzo predefinito</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>L&apos;indirizzo specificato non è valido.</translation>
</message>
</context>
<context>
@ -138,19 +138,19 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>Specifica una sorgente per la lista delle pedine. Puoi specificare un indirizzo url da cui scaricare il file o alternativamente usare un file già presente nel tuo computer.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>Indirizzo download:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>Usa l&apos;indirizzo predefinito</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>L&apos;indirizzo specificato non è valido.</translation>
</message>
</context>
<context>

View file

@ -16,7 +16,8 @@
<message>
<source>This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice.
You will need to specify a URL or a filename that will be used as a source.</source>
<translation type="unfinished"/>
<translation>Cockatriceが使用するカードセットやトークンのリストをインポートします
使URLまたはファイルを指定した後</translation>
</message>
</context>
<context>
@ -99,19 +100,19 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>URLかURLからダウンロードで構いません</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>URL:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>URLを復元</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>URLは無効です</translation>
</message>
</context>
<context>
@ -138,19 +139,19 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation>URLかURLからダウンロードで構いません</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation>URL:</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation>URLを復元</translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation>URLは無効です</translation>
</message>
</context>
<context>

View file

@ -16,14 +16,15 @@
<message>
<source>This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice.
You will need to specify a URL or a filename that will be used as a source.</source>
<translation type="unfinished"/>
<translation> .
.</translation>
</message>
</context>
<context>
<name>LoadSetsPage</name>
<message>
<source>Source selection</source>
<translation> </translation>
<translation> </translation>
</message>
<message>
<source>Local file:</source>
@ -35,15 +36,15 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Load sets file</source>
<translation> </translation>
<translation> </translation>
</message>
<message>
<source>Sets JSON file (*.json *.zip)</source>
<translation> JSON (*.json *.zip)</translation>
<translation> JSON (*.json *.zip)</translation>
</message>
<message>
<source>Sets JSON file (*.json)</source>
<translation> JSON (*.json)</translation>
<translation> JSON (*.json)</translation>
</message>
<message>
<source>Error</source>
@ -55,7 +56,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please choose a file.</source>
<translation> .</translation>
<translation> .</translation>
</message>
<message>
<source>Cannot open file &apos;%1&apos;.</source>
@ -79,7 +80,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Zip extraction failed: the Zip archive doesn&apos;t contain exactly one file.</source>
<translation> 실패 : 압축 .</translation>
<translation> 실패 : 압축 .</translation>
</message>
<message>
<source>Zip extraction failed: %1.</source>
@ -91,27 +92,28 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Do you want to try to download a fresh copy of the uncompressed file instead?</source>
<translation> ?</translation>
<translation> ?</translation>
</message>
<message>
<source>The file was retrieved successfully, but it does not contain any sets data.</source>
<translation> .</translation>
<translation> .</translation>
</message>
<message>
<source>Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation> .
.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation> :</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation> </translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation> .</translation>
</message>
</context>
<context>
@ -138,26 +140,27 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer.</source>
<translation type="unfinished"/>
<translation> .
.</translation>
</message>
<message>
<source>Download URL:</source>
<translation type="unfinished"/>
<translation> :</translation>
</message>
<message>
<source>Restore default URL</source>
<translation type="unfinished"/>
<translation> </translation>
</message>
<message>
<source>The provided URL is not valid.</source>
<translation type="unfinished"/>
<translation> .</translation>
</message>
</context>
<context>
<name>OracleImporter</name>
<message>
<source>Dummy set containing tokens</source>
<translation> </translation>
<translation> </translation>
</message>
</context>
<context>
@ -175,11 +178,11 @@ You will need to specify a URL or a filename that will be used as a source.</sou
<name>SaveSetsPage</name>
<message>
<source>Sets imported</source>
<translation> </translation>
<translation> </translation>
</message>
<message>
<source>The following sets has been imported. Press &quot;Save&quot; to save the imported cards to the Cockatrice database.</source>
<translation> .
<translation> .
&quot;&quot; .</translation>
</message>
<message>
@ -192,7 +195,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>No set has been imported.</source>
<translation> .</translation>
<translation> .</translation>
</message>
<message>
<source>Import finished: %1 cards.</source>

View file

@ -122,7 +122,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Error</source>
<translation type="unfinished"/>
<translation>Fout</translation>
</message>
<message>
<source>Downloading (0MB)</source>
@ -257,7 +257,7 @@ You will need to specify a URL or a filename that will be used as a source.</sou
</message>
<message>
<source>Error</source>
<translation type="unfinished"/>
<translation>Fout</translation>
</message>
<message>
<source>The file could not be saved to %1</source>

Binary file not shown.

Binary file not shown.