Commit 262ebe3b authored by ctrlaltca's avatar ctrlaltca
Browse files

Merge pull request #1039 from ctrlaltca/update_oracle

Run oracle from inside cockatrice and improve sets handling; 
parents 9c20811a 44800df8
...@@ -38,6 +38,11 @@ ...@@ -38,6 +38,11 @@
<file>resources/remove_row.svg</file> <file>resources/remove_row.svg</file>
<file>resources/arrow_left_green.svg</file> <file>resources/arrow_left_green.svg</file>
<file>resources/arrow_right_green.svg</file> <file>resources/arrow_right_green.svg</file>
<file>resources/arrow_top_green.svg</file>
<file>resources/arrow_up_green.svg</file>
<file>resources/arrow_down_green.svg</file>
<file>resources/arrow_bottom_green.svg</file>
<file>resources/icon_ready_start.svg</file> <file>resources/icon_ready_start.svg</file>
<file>resources/icon_not_ready_start.svg</file> <file>resources/icon_not_ready_start.svg</file>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QDebug> #include <QDebug>
#include <QImageReader> #include <QImageReader>
#include <QMessageBox>
const int CardDatabase::versionNeeded = 3; const int CardDatabase::versionNeeded = 3;
const char* CardDatabase::TOKENS_SETNAME = "TK"; const char* CardDatabase::TOKENS_SETNAME = "TK";
...@@ -1028,6 +1029,8 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens) ...@@ -1028,6 +1029,8 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens)
allSets.append(setsIterator.next().value()); allSets.append(setsIterator.next().value());
allSets.sortByKey(); allSets.sortByKey();
if(!tokens)
checkUnknownSets();
emit cardListChanged(); emit cardListChanged();
} }
...@@ -1098,3 +1101,51 @@ void CardDatabase::picsPathChanged() ...@@ -1098,3 +1101,51 @@ void CardDatabase::picsPathChanged()
pictureLoader->setPicsPath(settingsCache->getPicsPath()); pictureLoader->setPicsPath(settingsCache->getPicsPath());
clearPixmapCache(); clearPixmapCache();
} }
void CardDatabase::checkUnknownSets()
{
SetList sets = getSetList();
// no set is enabled. Probably this is the first time running trice
if(!sets.getEnabledSetsNum())
{
sets.guessSortKeys();
sets.sortByKey();
sets.enableAll();
detectedFirstRun = true;
return;
}
int numUnknownSets = sets.getUnknownSetsNum();
// no unkown sets.
if(!numUnknownSets)
return;
QMessageBox msgbox(QMessageBox::Question, tr("New sets found"), tr("%1 new set(s) have been found in the card database. Do you want to enable them?").arg(numUnknownSets), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
switch(msgbox.exec())
{
case QMessageBox::No:
sets.markAllAsKnown();
break;
case QMessageBox::Yes:
sets.enableAllUnknown();
break;
default:
break;
}
return;
}
bool CardDatabase::hasDetectedFirstRun()
{
if(detectedFirstRun)
{
detectedFirstRun=false;
return true;
}
return false;
}
\ No newline at end of file
...@@ -232,12 +232,14 @@ protected: ...@@ -232,12 +232,14 @@ protected:
QThread *pictureLoaderThread; QThread *pictureLoaderThread;
PictureLoader *pictureLoader; PictureLoader *pictureLoader;
LoadStatus loadStatus; LoadStatus loadStatus;
bool detectedFirstRun;
private: private:
static const int versionNeeded; static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml, bool tokens); void loadCardsFromXml(QXmlStreamReader &xml, bool tokens);
void loadSetsFromXml(QXmlStreamReader &xml); void loadSetsFromXml(QXmlStreamReader &xml);
CardInfo *getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound); CardInfo *getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound);
void checkUnknownSets();
public: public:
static const char* TOKENS_SETNAME; static const char* TOKENS_SETNAME;
...@@ -265,6 +267,7 @@ public: ...@@ -265,6 +267,7 @@ public:
bool getLoadSuccess() const { return loadStatus == Ok; } bool getLoadSuccess() const { return loadStatus == Ok; }
void cacheCardPixmaps(const QStringList &cardNames); void cacheCardPixmaps(const QStringList &cardNames);
void loadImage(CardInfo *card); void loadImage(CardInfo *card);
bool hasDetectedFirstRun();
public slots: public slots:
void clearPixmapCache(); void clearPixmapCache();
LoadStatus loadCardDatabase(const QString &path, bool tokens = false); LoadStatus loadCardDatabase(const QString &path, bool tokens = false);
......
...@@ -293,8 +293,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) ...@@ -293,8 +293,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
resize(950, 700); resize(950, 700);
connect(this, SIGNAL(setListChanged()), db, SIGNAL(cardListChanged())); QTimer::singleShot(0, this, SLOT(checkFirstRunDetected()));
QTimer::singleShot(0, this, SLOT(checkUnknownSets()));
} }
TabDeckEditor::~TabDeckEditor() TabDeckEditor::~TabDeckEditor()
...@@ -786,39 +785,11 @@ void TabDeckEditor::filterRemove(QAction *action) { ...@@ -786,39 +785,11 @@ void TabDeckEditor::filterRemove(QAction *action) {
filterModel->removeRow(idx.row(), idx.parent()); filterModel->removeRow(idx.row(), idx.parent());
} }
void TabDeckEditor::checkUnknownSets() void TabDeckEditor::checkFirstRunDetected()
{ {
SetList sets = db->getSetList(); if(db->hasDetectedFirstRun())
// no set is enabled. Probably this is the first time running trice
if(!sets.getEnabledSetsNum())
{ {
sets.guessSortKeys(); QMessageBox::information(this, tr("Welcome"), tr("Hi! Its seems like it's the first time you run this version of Cockatrice.\nAll the sets in the card database have been enabled.\nRead more about changing the set order or disabling specific sets in the the \"Edit Sets\" window."));
sets.sortByKey();
sets.enableAll();
db->emitCardListChanged();
actEditSets(); actEditSets();
return;
}
int numUnknownSets = sets.getUnknownSetsNum();
// no unkown sets.
if(!numUnknownSets)
return;
int ret = QMessageBox::question(this, tr("New sets found"), tr("%1 new set(s) have been found in the card database. Do you want to enable them?").arg(numUnknownSets), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
switch(ret)
{
case QMessageBox::No:
sets.markAllAsKnown();
break;
case QMessageBox::Yes:
sets.enableAllUnknown();
db->emitCardListChanged();
break;
default:
break;
} }
} }
\ No newline at end of file
...@@ -113,10 +113,9 @@ public: ...@@ -113,10 +113,9 @@ public:
bool confirmClose(); bool confirmClose();
public slots: public slots:
void closeRequest(); void closeRequest();
void checkUnknownSets(); void checkFirstRunDetected();
signals: signals:
void deckEditorClosing(TabDeckEditor *tab); void deckEditorClosing(TabDeckEditor *tab);
void setListChanged();
}; };
#endif #endif
...@@ -325,6 +325,7 @@ void MainWindow::retranslateUi() ...@@ -325,6 +325,7 @@ void MainWindow::retranslateUi()
#endif #endif
aAbout->setText(tr("&About Cockatrice")); aAbout->setText(tr("&About Cockatrice"));
helpMenu->setTitle(tr("&Help")); helpMenu->setTitle(tr("&Help"));
aCheckCardUpdates->setText(tr("Check card updates..."));
tabSupervisor->retranslateUi(); tabSupervisor->retranslateUi();
} }
...@@ -353,6 +354,9 @@ void MainWindow::createActions() ...@@ -353,6 +354,9 @@ void MainWindow::createActions()
aAbout = new QAction(this); aAbout = new QAction(this);
connect(aAbout, SIGNAL(triggered()), this, SLOT(actAbout())); connect(aAbout, SIGNAL(triggered()), this, SLOT(actAbout()));
aCheckCardUpdates = new QAction(this);
connect(aCheckCardUpdates, SIGNAL(triggered()), this, SLOT(actCheckCardUpdates()));
#if defined(__APPLE__) /* For OSX */ #if defined(__APPLE__) /* For OSX */
aSettings->setMenuRole(QAction::PreferencesRole); aSettings->setMenuRole(QAction::PreferencesRole);
aExit->setMenuRole(QAction::QuitRole); aExit->setMenuRole(QAction::QuitRole);
...@@ -383,6 +387,7 @@ void MainWindow::createMenus() ...@@ -383,6 +387,7 @@ void MainWindow::createMenus()
cockatriceMenu->addAction(aFullScreen); cockatriceMenu->addAction(aFullScreen);
cockatriceMenu->addSeparator(); cockatriceMenu->addSeparator();
cockatriceMenu->addAction(aSettings); cockatriceMenu->addAction(aSettings);
cockatriceMenu->addAction(aCheckCardUpdates);
cockatriceMenu->addSeparator(); cockatriceMenu->addSeparator();
cockatriceMenu->addAction(aExit); cockatriceMenu->addAction(aExit);
...@@ -391,7 +396,7 @@ void MainWindow::createMenus() ...@@ -391,7 +396,7 @@ void MainWindow::createMenus()
} }
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), localServer(0), bHasActivated(false) : QMainWindow(parent), localServer(0), bHasActivated(false), cardUpdateProcess(0)
{ {
connect(settingsCache, SIGNAL(pixmapCacheSizeChanged(int)), this, SLOT(pixmapCacheSizeChanged(int))); connect(settingsCache, SIGNAL(pixmapCacheSizeChanged(int)), this, SLOT(pixmapCacheSizeChanged(int)));
pixmapCacheSizeChanged(settingsCache->getPixmapCacheSize()); pixmapCacheSizeChanged(settingsCache->getPixmapCacheSize());
...@@ -522,3 +527,93 @@ void MainWindow::pixmapCacheSizeChanged(int newSizeInMBs) ...@@ -522,3 +527,93 @@ void MainWindow::pixmapCacheSizeChanged(int newSizeInMBs)
void MainWindow::maximize() { void MainWindow::maximize() {
showNormal(); showNormal();
} }
/* CARD UPDATER */
void MainWindow::actCheckCardUpdates()
{
if(cardUpdateProcess)
{
QMessageBox::information(this, tr("Information"), tr("A card update is already ongoing."));
return;
}
cardUpdateProcess = new QProcess(this);
connect(cardUpdateProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(cardUpdateError(QProcess::ProcessError)));
connect(cardUpdateProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(cardUpdateFinished(int, QProcess::ExitStatus)));
// full "run the update" command; leave empty if not present
QString updaterCmd;
QString binaryName;
QDir dir = QDir(QApplication::applicationDirPath());
#if defined(Q_OS_MAC)
binaryName = getCardUpdaterBinaryName();
// exit from the application bundle
dir.cdUp();
dir.cdUp();
dir.cdUp();
dir.cd(binaryName + ".app");
dir.cd("Contents");
dir.cd("MacOS");
#elif defined(Q_OS_WIN)
binaryName = getCardUpdaterBinaryName() + ".exe";
#else
binaryName = getCardUpdaterBinaryName();
#endif
if(dir.exists(binaryName))
updaterCmd = dir.absoluteFilePath(binaryName);
if(updaterCmd.isEmpty())
{
QMessageBox::warning(this, tr("Error"), tr("Unable to run the card updater: ") + dir.absoluteFilePath(binaryName));
return;
}
cardUpdateProcess->start(updaterCmd);
}
void MainWindow::cardUpdateError(QProcess::ProcessError err)
{
QString error;
switch(err)
{
case QProcess::FailedToStart:
error = tr("failed to start.");
break;
case QProcess::Crashed:
error = tr("crashed.");
break;
case QProcess::Timedout:
error = tr("timed out.");
break;
case QProcess::WriteError:
error = tr("write error.");
break;
case QProcess::ReadError:
error = tr("read error.");
break;
case QProcess::UnknownError:
default:
error = tr("unknown error.");
break;
}
cardUpdateProcess->deleteLater();
cardUpdateProcess = 0;
QMessageBox::warning(this, tr("Error"), tr("The card updater exited with an error: %1").arg(error));
}
void MainWindow::cardUpdateFinished(int, QProcess::ExitStatus)
{
cardUpdateProcess->deleteLater();
cardUpdateProcess = 0;
QMessageBox::information(this, tr("Information"), tr("Card update completed successfully. Will now reload card database."));
// this will force a database reload
settingsCache->setCardDatabasePath(settingsCache->getCardDatabasePath());
}
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QProcess>
#include "abstractclient.h" #include "abstractclient.h"
#include "pb/response.pb.h" #include "pb/response.pb.h"
...@@ -62,6 +63,10 @@ private slots: ...@@ -62,6 +63,10 @@ private slots:
void iconActivated(QSystemTrayIcon::ActivationReason reason); void iconActivated(QSystemTrayIcon::ActivationReason reason);
void maximize(); void maximize();
void actCheckCardUpdates();
void cardUpdateError(QProcess::ProcessError err);
void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus);
private: private:
static const QString appName; static const QString appName;
void setClientStatusTitle(); void setClientStatusTitle();
...@@ -71,11 +76,13 @@ private: ...@@ -71,11 +76,13 @@ private:
void createTrayIcon(); void createTrayIcon();
void createTrayActions(); void createTrayActions();
// TODO: add a preference item to choose updater name for other games
inline QString getCardUpdaterBinaryName() { return "oracle"; };
QList<QMenu *> tabMenus; QList<QMenu *> tabMenus;
QMenu *cockatriceMenu, *helpMenu; QMenu *cockatriceMenu, *helpMenu;
QAction *aConnect, *aDisconnect, *aSinglePlayer, *aWatchReplay, *aDeckEditor, *aFullScreen, *aSettings, *aExit, QAction *aConnect, *aDisconnect, *aSinglePlayer, *aWatchReplay, *aDeckEditor, *aFullScreen, *aSettings, *aExit,
*aAbout; *aAbout, *aCheckCardUpdates;
TabSupervisor *tabSupervisor; TabSupervisor *tabSupervisor;
QMenu *trayIconMenu; QMenu *trayIconMenu;
...@@ -89,6 +96,7 @@ private: ...@@ -89,6 +96,7 @@ private:
bool bHasActivated; bool bHasActivated;
QMessageBox *serverShutdownMessageBox; QMessageBox *serverShutdownMessageBox;
QProcess * cardUpdateProcess;
public: public:
MainWindow(QWidget *parent = 0); MainWindow(QWidget *parent = 0);
~MainWindow(); ~MainWindow();
......
...@@ -10,10 +10,48 @@ ...@@ -10,10 +10,48 @@
#include <QGroupBox> #include <QGroupBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QToolBar>
#include <QAction>
#include <QLabel>
WndSets::WndSets(QWidget *parent) WndSets::WndSets(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
{ {
// left toolbar
QToolBar *setsEditToolBar = new QToolBar;
setsEditToolBar->setOrientation(Qt::Vertical);
setsEditToolBar->setIconSize(QSize(24, 24));
setsEditToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
aTop = new QAction(QString(), this);
aTop->setIcon(QIcon(":/resources/arrow_top_green.svg"));
aTop->setToolTip(tr("Move selected set to top"));
aTop->setEnabled(false);
connect(aTop, SIGNAL(triggered()), this, SLOT(actTop()));
setsEditToolBar->addAction(aTop);
aUp = new QAction(QString(), this);
aUp->setIcon(QIcon(":/resources/arrow_up_green.svg"));
aUp->setToolTip(tr("Move selected set up"));
aUp->setEnabled(false);
connect(aUp, SIGNAL(triggered()), this, SLOT(actUp()));
setsEditToolBar->addAction(aUp);
aDown = new QAction(QString(), this);
aDown->setIcon(QIcon(":/resources/arrow_down_green.svg"));
aDown->setToolTip(tr("Move selected set down"));
aDown->setEnabled(false);
connect(aDown, SIGNAL(triggered()), this, SLOT(actDown()));
setsEditToolBar->addAction(aDown);
aBottom = new QAction(QString(), this);
aBottom->setIcon(QIcon(":/resources/arrow_bottom_green.svg"));
aBottom->setToolTip(tr("Move selected set to bottom"));
aBottom->setEnabled(false);
connect(aBottom, SIGNAL(triggered()), this, SLOT(actBottom()));
setsEditToolBar->addAction(aBottom);
// view
model = new SetsModel(db, this); model = new SetsModel(db, this);
view = new QTreeView; view = new QTreeView;
view->setModel(model); view->setModel(model);
...@@ -43,59 +81,32 @@ WndSets::WndSets(QWidget *parent) ...@@ -43,59 +81,32 @@ WndSets::WndSets(QWidget *parent)
view->setColumnHidden(SetsModel::IsKnownCol, true); view->setColumnHidden(SetsModel::IsKnownCol, true);
view->setRootIsDecorated(false); view->setRootIsDecorated(false);
enableButton = new QPushButton(tr("Enable set")); connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
connect(enableButton, SIGNAL(clicked()), this, SLOT(actEnable())); this, SLOT(actToggleButtons(const QItemSelection &, const QItemSelection &)));
disableButton = new QPushButton(tr("Disable set"));
connect(disableButton, SIGNAL(clicked()), this, SLOT(actDisable())); // bottom buttons
enableAllButton = new QPushButton(tr("Enable all sets")); enableAllButton = new QPushButton(tr("Enable all sets"));
connect(enableAllButton, SIGNAL(clicked()), this, SLOT(actEnableAll())); connect(enableAllButton, SIGNAL(clicked()), this, SLOT(actEnableAll()));
disableAllButton = new QPushButton(tr("Disable all sets")); disableAllButton = new QPushButton(tr("Disable all sets"));
connect(disableAllButton, SIGNAL(clicked()), this, SLOT(actDisableAll())); connect(disableAllButton, SIGNAL(clicked()), this, SLOT(actDisableAll()));
upButton = new QPushButton(tr("Move selected set up"));
connect(upButton, SIGNAL(clicked()), this, SLOT(actUp()));
downButton = new QPushButton(tr("Move selected set down"));
connect(downButton, SIGNAL(clicked()), this, SLOT(actDown()));
topButton = new QPushButton(tr("Move selected set to top"));
connect(topButton, SIGNAL(clicked()), this, SLOT(actTop()));
bottomButton = new QPushButton(tr("Move selected set to bottom"));
connect(bottomButton, SIGNAL(clicked()), this, SLOT(actBottom()));
enableButton->setDisabled(true);
disableButton->setDisabled(true);
upButton->setDisabled(true);
downButton->setDisabled(true);
topButton->setDisabled(true);
bottomButton->setDisabled(true);
connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
this, SLOT(actToggleButtons(const QItemSelection &, const QItemSelection &)));
QGroupBox *toggleFrame = new QGroupBox(tr("Enable sets")); QLabel *labNotes = new QLabel;
QVBoxLayout *toggleVBox = new QVBoxLayout; labNotes->setText("Enable the sets that you want to have available in the deck editor.\nMove sets around to change their order, or click on a column header to sort sets on that field.\nSets order decides the source that will be used when loading images for a specific card.\nDisabled sets will still be used for loading images.");
toggleVBox->addWidget(enableButton);
toggleVBox->addWidget(disableButton);
toggleVBox->addWidget(enableAllButton);
toggleVBox->addWidget(disableAllButton);
toggleFrame->setLayout(toggleVBox);
QGroupBox *sortFrame = new QGroupBox(tr("Sort sets"));
QVBoxLayout *sortVBox = new QVBoxLayout;
sortVBox->addWidget(upButton);
sortVBox->addWidget(downButton);
sortVBox->addWidget(topButton);
sortVBox->addWidget(bottomButton);
sortFrame->setLayout(sortVBox);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actSave())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(actSave()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(actRestore())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(actRestore()));
QGridLayout *mainLayout = new QGridLayout; QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(view, 0, 0, 1, 2); mainLayout->addWidget(setsEditToolBar, 0, 0, 1, 1);
mainLayout->addWidget(toggleFrame, 1, 0, 1, 1); mainLayout->addWidget(view, 0, 1, 1, 2);
mainLayout->addWidget(sortFrame, 1, 1, 1, 1); mainLayout->addWidget(enableAllButton, 1, 1, 1, 1);
mainLayout->addWidget(buttonBox, 2, 0, 1, 2); mainLayout->addWidget(disableAllButton, 1, 2, 1, 1);
mainLayout->addWidget(labNotes, 2, 1, 1, 2);
mainLayout->addWidget(buttonBox, 3, 1, 1, 2);
mainLayout->setColumnStretch(1, 1);
mainLayout->setColumnStretch(2, 1);
QWidget *centralWidget = new QWidget; QWidget *centralWidget = new QWidget;
centralWidget->setLayout(mainLayout); centralWidget->setLayout(mainLayout);
...@@ -125,12 +136,10 @@ void WndSets::actRestore() ...@@ -125,12 +136,10 @@ void WndSets::actRestore()
void WndSets::actToggleButtons(const QItemSelection & selected, const QItemSelection &) void WndSets::actToggleButtons(const QItemSelection & selected, const QItemSelection &)
{ {
bool disabled = selected.empty(); bool disabled = selected.empty();
upButton->setDisabled(disabled); aTop->setDisabled(disabled);
downButton->setDisabled(disabled); aUp->setDisabled(disabled);
topButton->setDisabled(disabled); aDown->setDisabled(disabled);
bottomButton->setDisabled(disabled); aBottom->setDisabled(disabled);
enableButton->setDisabled(disabled);
disableButton->setDisabled(disabled);
} }
void WndSets::selectRow(int row) void WndSets::selectRow(int row)
......
...@@ -15,8 +15,8 @@ class WndSets : public QMainWindow { ...@@ -15,8 +15,8 @@ class WndSets : public QMainWindow {
private: private:
SetsModel *model; SetsModel *model;
QTreeView *view; QTreeView *view;
QPushButton *enableButton, *disableButton, *enableAllButton, *disableAllButton, QPushButton *enableAllButton, *disableAllButton;
*upButton, *downButton, *bottomButton, *topButton; QAction *aUp, *aDown, *aBottom, *aTop;
public: public:
WndSets(QWidget *parent = 0); WndSets(QWidget *parent = 0);
~WndSets(); ~WndSets();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment