mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-12 00:54:53 -07:00
[Move refactor] Move tabs to interface/widgets (#6235)
* Move tabs to interface/widgets. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
d9c65d4ae0
commit
b8983f27ab
134 changed files with 111 additions and 112 deletions
|
|
@ -0,0 +1,611 @@
|
|||
#include "abstract_tab_deck_editor.h"
|
||||
|
||||
#include "../client/network/interfaces/deck_stats_interface.h"
|
||||
#include "../client/network/interfaces/tapped_out_interface.h"
|
||||
#include "../dialogs/dlg_load_deck.h"
|
||||
#include "../dialogs/dlg_load_deck_from_clipboard.h"
|
||||
#include "../dialogs/dlg_load_deck_from_website.h"
|
||||
#include "../interface/card_picture_loader/card_picture_loader.h"
|
||||
#include "../interface/pixel_map_generator.h"
|
||||
#include "../interface/widgets/cards/card_info_frame_widget.h"
|
||||
#include "tab_supervisor.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QCloseEvent>
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QHeaderView>
|
||||
#include <QLineEdit>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
#include <QPrintPreviewDialog>
|
||||
#include <QPrinter>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSplitter>
|
||||
#include <QTextStream>
|
||||
#include <QTreeView>
|
||||
#include <QUrl>
|
||||
#include <libcockatrice/card/card_database/card_database_manager.h>
|
||||
#include <libcockatrice/card/card_database/model/card_database_model.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_upload.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/cache_settings.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
|
||||
AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||
{
|
||||
setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
|
||||
|
||||
databaseDisplayDockWidget = new DeckEditorDatabaseDisplayWidget(this);
|
||||
deckDockWidget = new DeckEditorDeckDockWidget(this);
|
||||
cardInfoDockWidget = new DeckEditorCardInfoDockWidget(this);
|
||||
filterDockWidget = new DeckEditorFilterDockWidget(this);
|
||||
printingSelectorDockWidget = new DeckEditorPrintingSelectorDockWidget(this);
|
||||
connect(&SettingsCache::instance(), &SettingsCache::overrideAllCardArtWithPersonalPreferenceChanged, this, [this] {
|
||||
printingSelectorDockWidget->setHidden(SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference());
|
||||
});
|
||||
|
||||
connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged);
|
||||
connect(deckDockWidget, &DeckEditorDeckDockWidget::deckModified, this, &AbstractTabDeckEditor::onDeckModified);
|
||||
connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard);
|
||||
connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard);
|
||||
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this,
|
||||
&AbstractTabDeckEditor::updateCard);
|
||||
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::addCardToMainDeck, this,
|
||||
&AbstractTabDeckEditor::actAddCard);
|
||||
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::addCardToSideboard, this,
|
||||
&AbstractTabDeckEditor::actAddCardToSideboard);
|
||||
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromMainDeck, this,
|
||||
&AbstractTabDeckEditor::actDecrementCard);
|
||||
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromSideboard, this,
|
||||
&AbstractTabDeckEditor::actDecrementCardFromSideboard);
|
||||
|
||||
connect(filterDockWidget, &DeckEditorFilterDockWidget::clearAllDatabaseFilters, databaseDisplayDockWidget,
|
||||
&DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters);
|
||||
|
||||
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
||||
&AbstractTabDeckEditor::refreshShortcuts);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::updateCard(const ExactCard &card)
|
||||
{
|
||||
cardInfoDockWidget->updateCard(card);
|
||||
printingSelectorDockWidget->printingSelector->setCard(card.getCardPtr(), DECK_ZONE_MAIN);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::onDeckChanged()
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::onDeckModified()
|
||||
{
|
||||
setModified(!isBlankNewDeck());
|
||||
deckMenu->setSaveStatus(!isBlankNewDeck());
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, QString zoneName)
|
||||
{
|
||||
if (!card)
|
||||
return;
|
||||
if (card.getInfo().getIsToken())
|
||||
zoneName = DECK_ZONE_TOKENS;
|
||||
|
||||
QModelIndex newCardIndex = deckDockWidget->deckModel->addCard(card, zoneName);
|
||||
// recursiveExpand(newCardIndex);
|
||||
deckDockWidget->deckView->clearSelection();
|
||||
deckDockWidget->deckView->setCurrentIndex(newCardIndex);
|
||||
setModified(true);
|
||||
databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length());
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actAddCard(const ExactCard &card)
|
||||
{
|
||||
if (QApplication::keyboardModifiers() & Qt::ControlModifier)
|
||||
actAddCardToSideboard(card);
|
||||
else
|
||||
addCardHelper(card, DECK_ZONE_MAIN);
|
||||
deckMenu->setSaveStatus(true);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actAddCardToSideboard(const ExactCard &card)
|
||||
{
|
||||
addCardHelper(card, DECK_ZONE_SIDE);
|
||||
deckMenu->setSaveStatus(true);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actDecrementCard(const ExactCard &card)
|
||||
{
|
||||
emit decrementCard(card, DECK_ZONE_MAIN);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actDecrementCardFromSideboard(const ExactCard &card)
|
||||
{
|
||||
emit decrementCard(card, DECK_ZONE_SIDE);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actSwapCard(const ExactCard &card, const QString &zoneName)
|
||||
{
|
||||
QString providerId = card.getPrinting().getUuid();
|
||||
QString collectorNumber = card.getPrinting().getProperty("num");
|
||||
|
||||
QModelIndex foundCard = deckDockWidget->deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber);
|
||||
if (!foundCard.isValid()) {
|
||||
foundCard = deckDockWidget->deckModel->findCard(card.getName(), zoneName);
|
||||
}
|
||||
|
||||
deckDockWidget->swapCard(foundCard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the deck in this tab.
|
||||
* @param deck The deck. Takes ownership of the object
|
||||
*/
|
||||
void AbstractTabDeckEditor::openDeck(DeckLoader *deck)
|
||||
{
|
||||
setDeck(deck);
|
||||
|
||||
if (!deck->getLastFileName().isEmpty()) {
|
||||
SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(deck->getLastFileName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the currently active deck for this tab
|
||||
* @param _deck The deck. Takes ownership of the object
|
||||
*/
|
||||
void AbstractTabDeckEditor::setDeck(DeckLoader *_deck)
|
||||
{
|
||||
deckDockWidget->setDeck(_deck);
|
||||
CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList()->getCardRefList()));
|
||||
setModified(false);
|
||||
|
||||
// If they load a deck, make the deck list appear
|
||||
aDeckDockVisible->setChecked(true);
|
||||
deckDockWidget->setVisible(aDeckDockVisible->isChecked());
|
||||
}
|
||||
|
||||
DeckLoader *AbstractTabDeckEditor::getDeckList() const
|
||||
{
|
||||
return deckDockWidget->getDeckList();
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::setModified(bool _modified)
|
||||
{
|
||||
modified = _modified;
|
||||
emit tabTextChanged(this, getTabText());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if this tab is a blank newly opened tab, as if it was just created with the `New Deck` action.
|
||||
*/
|
||||
bool AbstractTabDeckEditor::isBlankNewDeck() const
|
||||
{
|
||||
DeckLoader *deck = getDeckList();
|
||||
return !modified && deck->isBlankDeck() && deck->hasNotBeenLoaded();
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actNewDeck()
|
||||
{
|
||||
auto deckOpenLocation = confirmOpen(false);
|
||||
|
||||
if (deckOpenLocation == CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (deckOpenLocation == NEW_TAB) {
|
||||
emit openDeckEditor(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
cleanDeckAndResetModified();
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::cleanDeckAndResetModified()
|
||||
{
|
||||
deckMenu->setSaveStatus(false);
|
||||
deckDockWidget->cleanDeck();
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Displays the save confirmation dialogue that is shown before loading a deck, if required. Takes into
|
||||
* account the `openDeckInNewTab` settting.
|
||||
*
|
||||
* @param openInSameTabIfBlank Open the deck in the same tab instead of a new tab if the current tab is completely
|
||||
* blank. Only relevant when the `openDeckInNewTab` setting is enabled.
|
||||
*
|
||||
* @returns An enum that indicates if and where to load the deck
|
||||
*/
|
||||
AbstractTabDeckEditor::DeckOpenLocation AbstractTabDeckEditor::confirmOpen(const bool openInSameTabIfBlank)
|
||||
{
|
||||
// handle `openDeckInNewTab` setting
|
||||
if (SettingsCache::instance().getOpenDeckInNewTab()) {
|
||||
if (openInSameTabIfBlank && isBlankNewDeck()) {
|
||||
return SAME_TAB;
|
||||
} else {
|
||||
return NEW_TAB;
|
||||
}
|
||||
}
|
||||
|
||||
// early return if deck is unmodified
|
||||
if (!modified) {
|
||||
return SAME_TAB;
|
||||
}
|
||||
|
||||
// do the save confirmation dialogue
|
||||
tabSupervisor->setCurrentWidget(this);
|
||||
|
||||
QMessageBox *msgBox = createSaveConfirmationWindow();
|
||||
QPushButton *newTabButton = msgBox->addButton(tr("Open in new tab"), QMessageBox::ApplyRole);
|
||||
|
||||
int ret = msgBox->exec();
|
||||
|
||||
// `exec()` returns an opaque value if a non-standard button was clicked.
|
||||
// Directly check if newTabButton was clicked before switching over the standard buttons.
|
||||
if (msgBox->clickedButton() == newTabButton) {
|
||||
return NEW_TAB;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case QMessageBox::Save:
|
||||
return actSaveDeck() ? SAME_TAB : CANCELLED;
|
||||
case QMessageBox::Discard:
|
||||
return SAME_TAB;
|
||||
default:
|
||||
return CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates the base save confirmation dialogue box.
|
||||
*
|
||||
* @returns A QMessageBox that can be further modified
|
||||
*/
|
||||
QMessageBox *AbstractTabDeckEditor::createSaveConfirmationWindow()
|
||||
{
|
||||
QMessageBox *msgBox = new QMessageBox(this);
|
||||
msgBox->setIcon(QMessageBox::Warning);
|
||||
msgBox->setWindowTitle(tr("Are you sure?"));
|
||||
msgBox->setText(tr("The decklist has been modified.\nDo you want to save the changes?"));
|
||||
msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
|
||||
return msgBox;
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actLoadDeck()
|
||||
{
|
||||
auto deckOpenLocation = confirmOpen();
|
||||
|
||||
if (deckOpenLocation == CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
DlgLoadDeck dialog(this);
|
||||
if (!dialog.exec())
|
||||
return;
|
||||
|
||||
QString fileName = dialog.selectedFiles().at(0);
|
||||
openDeckFromFile(fileName, deckOpenLocation);
|
||||
deckDockWidget->updateBannerCardComboBox();
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actOpenRecent(const QString &fileName)
|
||||
{
|
||||
auto deckOpenLocation = confirmOpen();
|
||||
|
||||
if (deckOpenLocation == CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
openDeckFromFile(fileName, deckOpenLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually opens the deck from file
|
||||
* @param fileName The path of the deck to open
|
||||
* @param deckOpenLocation Which tab to open the deck
|
||||
*/
|
||||
void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation)
|
||||
{
|
||||
DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName);
|
||||
|
||||
auto *l = new DeckLoader;
|
||||
if (l->loadFromFile(fileName, fmt, true)) {
|
||||
if (deckOpenLocation == NEW_TAB) {
|
||||
emit openDeckEditor(l);
|
||||
l->deleteLater();
|
||||
} else {
|
||||
deckMenu->setSaveStatus(false);
|
||||
openDeck(l);
|
||||
}
|
||||
} else {
|
||||
l->deleteLater();
|
||||
QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(fileName));
|
||||
}
|
||||
deckMenu->setSaveStatus(true);
|
||||
}
|
||||
|
||||
bool AbstractTabDeckEditor::actSaveDeck()
|
||||
{
|
||||
DeckLoader *const deck = getDeckList();
|
||||
if (deck->getLastRemoteDeckId() != -1) {
|
||||
QString deckString = deck->writeToString_Native();
|
||||
if (deckString.length() > MAX_FILE_LENGTH) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Could not save remote deck"));
|
||||
return false;
|
||||
}
|
||||
|
||||
Command_DeckUpload cmd;
|
||||
cmd.set_deck_id(static_cast<google::protobuf::uint32>(deck->getLastRemoteDeckId()));
|
||||
cmd.set_deck_list(deckString.toStdString());
|
||||
|
||||
PendingCommand *pend = AbstractClient::prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &AbstractTabDeckEditor::saveDeckRemoteFinished);
|
||||
tabSupervisor->getClient()->sendCommand(pend);
|
||||
|
||||
return true;
|
||||
} else if (deck->getLastFileName().isEmpty())
|
||||
return actSaveDeckAs();
|
||||
else if (deck->saveToFile(deck->getLastFileName(), deck->getLastFileFormat())) {
|
||||
setModified(false);
|
||||
return true;
|
||||
}
|
||||
QMessageBox::critical(
|
||||
this, tr("Error"),
|
||||
tr("The deck could not be saved.\nPlease check that the directory is writable and try again."));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractTabDeckEditor::actSaveDeckAs()
|
||||
{
|
||||
QFileDialog dialog(this, tr("Save deck"));
|
||||
dialog.setDirectory(SettingsCache::instance().getDeckPath());
|
||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
dialog.setDefaultSuffix("cod");
|
||||
dialog.setNameFilters(DeckLoader::FILE_NAME_FILTERS);
|
||||
dialog.selectFile(getDeckList()->getName().trimmed());
|
||||
|
||||
if (!dialog.exec())
|
||||
return false;
|
||||
|
||||
QString fileName = dialog.selectedFiles().at(0);
|
||||
DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName);
|
||||
|
||||
if (!getDeckList()->saveToFile(fileName, fmt)) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Error"),
|
||||
tr("The deck could not be saved.\nPlease check that the directory is writable and try again."));
|
||||
return false;
|
||||
}
|
||||
setModified(false);
|
||||
|
||||
SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::saveDeckRemoteFinished(const Response &response)
|
||||
{
|
||||
if (response.response_code() != Response::RespOk)
|
||||
QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved."));
|
||||
else
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actLoadDeckFromClipboard()
|
||||
{
|
||||
auto deckOpenLocation = confirmOpen();
|
||||
|
||||
if (deckOpenLocation == CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
DlgLoadDeckFromClipboard dlg(this);
|
||||
if (!dlg.exec())
|
||||
return;
|
||||
|
||||
if (deckOpenLocation == NEW_TAB) {
|
||||
emit openDeckEditor(dlg.getDeckList());
|
||||
} else {
|
||||
setDeck(dlg.getDeckList());
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
deckMenu->setSaveStatus(true);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::editDeckInClipboard(bool annotated)
|
||||
{
|
||||
DlgEditDeckInClipboard dlg(*getDeckList(), annotated, this);
|
||||
if (!dlg.exec())
|
||||
return;
|
||||
|
||||
setDeck(dlg.getDeckList());
|
||||
setModified(true);
|
||||
|
||||
deckMenu->setSaveStatus(true);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actEditDeckInClipboard()
|
||||
{
|
||||
editDeckInClipboard(true);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actEditDeckInClipboardRaw()
|
||||
{
|
||||
editDeckInClipboard(false);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actSaveDeckToClipboard()
|
||||
{
|
||||
getDeckList()->saveToClipboard(true, true);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actSaveDeckToClipboardNoSetInfo()
|
||||
{
|
||||
getDeckList()->saveToClipboard(true, false);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actSaveDeckToClipboardRaw()
|
||||
{
|
||||
getDeckList()->saveToClipboard(false, true);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo()
|
||||
{
|
||||
getDeckList()->saveToClipboard(false, false);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actPrintDeck()
|
||||
{
|
||||
auto *dlg = new QPrintPreviewDialog(this);
|
||||
connect(dlg, &QPrintPreviewDialog::paintRequested, deckDockWidget->deckModel, &DeckListModel::printDeckList);
|
||||
dlg->exec();
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actLoadDeckFromWebsite()
|
||||
{
|
||||
auto deckOpenLocation = confirmOpen();
|
||||
|
||||
if (deckOpenLocation == CANCELLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
DlgLoadDeckFromWebsite dlg(this);
|
||||
if (!dlg.exec())
|
||||
return;
|
||||
|
||||
if (deckOpenLocation == NEW_TAB) {
|
||||
emit openDeckEditor(dlg.getDeck());
|
||||
} else {
|
||||
setDeck(dlg.getDeck());
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
deckMenu->setSaveStatus(true);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website)
|
||||
{
|
||||
// check if deck is not null
|
||||
if (DeckLoader *const deck = getDeckList()) {
|
||||
// Get the decklist url string from the deck loader class.
|
||||
QString decklistUrlString = deck->exportDeckToDecklist(website);
|
||||
// Check to make sure the string isn't empty.
|
||||
if (QString::compare(decklistUrlString, "", Qt::CaseInsensitive) == 0) {
|
||||
// Show an error if the deck is empty, and return.
|
||||
QMessageBox::critical(this, tr("Error"), tr("There are no cards in your deck to be exported"));
|
||||
return;
|
||||
}
|
||||
// Encode the string recieved from the model to make sure all characters are encoded.
|
||||
// first we put it into a qurl object
|
||||
QUrl decklistUrl = QUrl(decklistUrlString);
|
||||
// we get the correctly encoded url.
|
||||
decklistUrlString = decklistUrl.toEncoded();
|
||||
// We open the url in the user's default browser
|
||||
QDesktopServices::openUrl(decklistUrlString);
|
||||
} else {
|
||||
// if there's no deck loader object, return an error
|
||||
QMessageBox::critical(this, tr("Error"), tr("No deck was selected to be exported."));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the deck to www.decklist.org (the old website)
|
||||
*/
|
||||
void AbstractTabDeckEditor::actExportDeckDecklist()
|
||||
{
|
||||
exportToDecklistWebsite(DeckLoader::DecklistOrg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the deck to www.decklist.xyz (the new website)
|
||||
*/
|
||||
void AbstractTabDeckEditor::actExportDeckDecklistXyz()
|
||||
{
|
||||
exportToDecklistWebsite(DeckLoader::DecklistXyz);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actAnalyzeDeckDeckstats()
|
||||
{
|
||||
auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(),
|
||||
this); // it deletes itself when done
|
||||
interface->analyzeDeck(getDeckList());
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::actAnalyzeDeckTappedout()
|
||||
{
|
||||
auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(),
|
||||
this); // it deletes itself when done
|
||||
interface->analyzeDeck(getDeckList());
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::filterTreeChanged(FilterTree *filterTree)
|
||||
{
|
||||
databaseDisplayDockWidget->setFilterTree(filterTree);
|
||||
}
|
||||
|
||||
void AbstractTabDeckEditor::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
emit deckEditorClosing(this);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
// Method uses to sync docks state with menu items state
|
||||
bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::Close) {
|
||||
if (o == cardInfoDockWidget) {
|
||||
aCardInfoDockVisible->setChecked(false);
|
||||
aCardInfoDockFloating->setEnabled(false);
|
||||
} else if (o == deckDockWidget) {
|
||||
aDeckDockVisible->setChecked(false);
|
||||
aDeckDockFloating->setEnabled(false);
|
||||
} else if (o == filterDockWidget) {
|
||||
aFilterDockVisible->setChecked(false);
|
||||
aFilterDockFloating->setEnabled(false);
|
||||
} else if (o == printingSelectorDockWidget) {
|
||||
aPrintingSelectorDockVisible->setChecked(false);
|
||||
aPrintingSelectorDockFloating->setEnabled(false);
|
||||
}
|
||||
}
|
||||
if (o == this && e->type() == QEvent::Hide) {
|
||||
LayoutsSettings &layouts = SettingsCache::instance().layouts();
|
||||
layouts.setDeckEditorLayoutState(saveState());
|
||||
layouts.setDeckEditorGeometry(saveGeometry());
|
||||
layouts.setDeckEditorCardSize(cardInfoDockWidget->size());
|
||||
layouts.setDeckEditorFilterSize(filterDockWidget->size());
|
||||
layouts.setDeckEditorDeckSize(deckDockWidget->size());
|
||||
layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractTabDeckEditor::confirmClose()
|
||||
{
|
||||
if (modified) {
|
||||
tabSupervisor->setCurrentWidget(this);
|
||||
int ret = createSaveConfirmationWindow()->exec();
|
||||
if (ret == QMessageBox::Save)
|
||||
return actSaveDeck();
|
||||
else if (ret == QMessageBox::Cancel)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractTabDeckEditor::closeRequest()
|
||||
{
|
||||
if (!confirmClose()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return close();
|
||||
}
|
||||
160
cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h
Normal file
160
cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
#ifndef TAB_GENERIC_DECK_EDITOR_H
|
||||
#define TAB_GENERIC_DECK_EDITOR_H
|
||||
|
||||
#include "../interface/widgets/deck_editor/deck_editor_card_info_dock_widget.h"
|
||||
#include "../interface/widgets/deck_editor/deck_editor_database_display_widget.h"
|
||||
#include "../interface/widgets/deck_editor/deck_editor_deck_dock_widget.h"
|
||||
#include "../interface/widgets/deck_editor/deck_editor_filter_dock_widget.h"
|
||||
#include "../interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h"
|
||||
#include "../interface/widgets/menus/deck_editor_menu.h"
|
||||
#include "../interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
|
||||
#include "tab.h"
|
||||
|
||||
class CardDatabaseModel;
|
||||
class CardDatabaseDisplayModel;
|
||||
|
||||
class CardInfoFrameWidget;
|
||||
class DeckLoader;
|
||||
class DeckEditorMenu;
|
||||
class DeckEditorCardInfoDockWidget;
|
||||
class DeckEditorDatabaseDisplayWidget;
|
||||
class DeckEditorDeckDockWidget;
|
||||
class DeckEditorFilterDockWidget;
|
||||
class DeckEditorPrintingSelectorDockWidget;
|
||||
class DeckPreviewDeckTagsDisplayWidget;
|
||||
class Response;
|
||||
class FilterTree;
|
||||
class FilterTreeModel;
|
||||
class FilterBuilder;
|
||||
|
||||
class QTreeView;
|
||||
class QTextEdit;
|
||||
class QLabel;
|
||||
class QComboBox;
|
||||
class QGroupBox;
|
||||
class QMessageBox;
|
||||
class QHBoxLayout;
|
||||
class QVBoxLayout;
|
||||
class QPushButton;
|
||||
class QDockWidget;
|
||||
class QMenu;
|
||||
class QAction;
|
||||
|
||||
class AbstractTabDeckEditor : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class DeckEditorMenu;
|
||||
|
||||
public:
|
||||
explicit AbstractTabDeckEditor(TabSupervisor *_tabSupervisor);
|
||||
|
||||
// UI and Navigation
|
||||
virtual void createMenus() = 0;
|
||||
[[nodiscard]] virtual QString getTabText() const override = 0;
|
||||
bool confirmClose();
|
||||
virtual void retranslateUi() override = 0;
|
||||
|
||||
// Deck Management
|
||||
void openDeck(DeckLoader *deck);
|
||||
DeckLoader *getDeckList() const;
|
||||
void setModified(bool _windowModified);
|
||||
|
||||
// UI Elements
|
||||
DeckEditorMenu *deckMenu;
|
||||
DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget;
|
||||
DeckEditorCardInfoDockWidget *cardInfoDockWidget;
|
||||
DeckEditorDeckDockWidget *deckDockWidget;
|
||||
DeckEditorFilterDockWidget *filterDockWidget;
|
||||
DeckEditorPrintingSelectorDockWidget *printingSelectorDockWidget;
|
||||
|
||||
public slots:
|
||||
virtual void onDeckChanged();
|
||||
virtual void onDeckModified();
|
||||
void updateCard(const ExactCard &card);
|
||||
void actAddCard(const ExactCard &card);
|
||||
void actAddCardToSideboard(const ExactCard &card);
|
||||
void actDecrementCard(const ExactCard &card);
|
||||
void actDecrementCardFromSideboard(const ExactCard &card);
|
||||
void actOpenRecent(const QString &fileName);
|
||||
void filterTreeChanged(FilterTree *filterTree);
|
||||
bool closeRequest() override;
|
||||
virtual void showPrintingSelector() = 0;
|
||||
virtual void dockTopLevelChanged(bool topLevel) = 0;
|
||||
|
||||
signals:
|
||||
void openDeckEditor(const DeckLoader *deckLoader);
|
||||
void deckEditorClosing(AbstractTabDeckEditor *tab);
|
||||
void decrementCard(const ExactCard &card, QString zoneName);
|
||||
|
||||
protected slots:
|
||||
// Deck Operations
|
||||
virtual void actNewDeck();
|
||||
void cleanDeckAndResetModified();
|
||||
virtual void actLoadDeck();
|
||||
bool actSaveDeck();
|
||||
virtual bool actSaveDeckAs();
|
||||
virtual void actLoadDeckFromClipboard();
|
||||
void actEditDeckInClipboard();
|
||||
void actEditDeckInClipboardRaw();
|
||||
void actSaveDeckToClipboard();
|
||||
void actSaveDeckToClipboardNoSetInfo();
|
||||
void actSaveDeckToClipboardRaw();
|
||||
void actSaveDeckToClipboardRawNoSetInfo();
|
||||
void actPrintDeck();
|
||||
void actLoadDeckFromWebsite();
|
||||
void actExportDeckDecklist();
|
||||
void actExportDeckDecklistXyz();
|
||||
void actAnalyzeDeckDeckstats();
|
||||
void actAnalyzeDeckTappedout();
|
||||
|
||||
// Remote Save
|
||||
void saveDeckRemoteFinished(const Response &r);
|
||||
|
||||
// UI Layout Management
|
||||
virtual void loadLayout() = 0;
|
||||
virtual void restartLayout() = 0;
|
||||
virtual void freeDocksSize() = 0;
|
||||
virtual void refreshShortcuts() = 0;
|
||||
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
virtual void dockVisibleTriggered() = 0;
|
||||
virtual void dockFloatingTriggered() = 0;
|
||||
|
||||
private:
|
||||
virtual void setDeck(DeckLoader *_deck);
|
||||
void editDeckInClipboard(bool annotated);
|
||||
void exportToDecklistWebsite(DeckLoader::DecklistWebsite website);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Enum for selecting deck open location
|
||||
*/
|
||||
enum DeckOpenLocation
|
||||
{
|
||||
CANCELLED,
|
||||
SAME_TAB,
|
||||
NEW_TAB
|
||||
};
|
||||
|
||||
DeckOpenLocation confirmOpen(bool openInSameTabIfBlank = true);
|
||||
QMessageBox *createSaveConfirmationWindow();
|
||||
bool isBlankNewDeck() const;
|
||||
|
||||
// Helper functions for card actions
|
||||
void addCardHelper(const ExactCard &card, QString zoneName);
|
||||
void actSwapCard(const ExactCard &card, const QString &zoneName);
|
||||
virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation);
|
||||
|
||||
// UI Menu Elements
|
||||
QMenu *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *printingSelectorDockMenu;
|
||||
|
||||
QAction *aResetLayout;
|
||||
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating;
|
||||
QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
|
||||
|
||||
bool modified = false;
|
||||
};
|
||||
|
||||
#endif // TAB_GENERIC_DECK_EDITOR_H
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#include "edhrec_api_response_archidekt_links.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
void EdhrecApiResponseArchidektLink::fromJson(const QJsonObject &json)
|
||||
{
|
||||
c = json.value("c").toString();
|
||||
f = json.value("f").toInt(0);
|
||||
q = json.value("q").toInt(0);
|
||||
u = json.value("u").toString();
|
||||
}
|
||||
|
||||
void EdhrecApiResponseArchidektLink::debugPrint() const
|
||||
{
|
||||
qDebug() << " C:" << c;
|
||||
qDebug() << " F:" << f;
|
||||
qDebug() << " Q:" << q;
|
||||
qDebug() << " U:" << u;
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseArchidektLinks::fromJson(const QJsonArray &json)
|
||||
{
|
||||
entries.clear();
|
||||
for (const QJsonValue &value : json) {
|
||||
if (value.isObject()) {
|
||||
QJsonObject entryJson = value.toObject();
|
||||
EdhrecApiResponseArchidektLink entry;
|
||||
entry.fromJson(entryJson);
|
||||
entries.append(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseArchidektLinks::debugPrint() const
|
||||
{
|
||||
qDebug() << "Archidekt Entries:";
|
||||
for (const auto &entry : entries) {
|
||||
entry.debugPrint();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef ARCHIDEKTENTRY_H
|
||||
#define ARCHIDEKTENTRY_H
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
/**
|
||||
* @class EdhrecApiResponseArchidektLink
|
||||
* @ingroup ApiResponses
|
||||
* @brief Represents a single Archidekt entry
|
||||
*/
|
||||
class EdhrecApiResponseArchidektLink
|
||||
{
|
||||
public:
|
||||
QString c;
|
||||
int f = 0;
|
||||
int q = 0;
|
||||
QString u;
|
||||
|
||||
void fromJson(const QJsonObject &json);
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class EdhrecCommanderApiResponseArchidektLinks
|
||||
* @ingroup ApiResponses
|
||||
* @brief Represents the Archidekt section as a list of entries
|
||||
*/
|
||||
class EdhrecCommanderApiResponseArchidektLinks
|
||||
{
|
||||
public:
|
||||
QVector<EdhrecApiResponseArchidektLink> entries;
|
||||
|
||||
void fromJson(const QJsonArray &json);
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
#endif // ARCHIDEKTENTRY_H
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#include "edhrec_average_deck_api_response.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
|
||||
void EdhrecAverageDeckApiResponse::fromJson(const QJsonObject &json)
|
||||
{
|
||||
// Parse the collapsed DeckStatistics
|
||||
deckStats.fromJson(json);
|
||||
|
||||
// Parse Archidekt section
|
||||
QJsonArray archidektJson = json.value("archidekt").toArray();
|
||||
archidekt.fromJson(archidektJson);
|
||||
|
||||
// Parse other fields
|
||||
similar = json.value("similar").toObject();
|
||||
header = json.value("header").toString();
|
||||
panels = json.value("panels").toObject();
|
||||
description = json.value("description").toString();
|
||||
QJsonObject containerJson = json.value("container").toObject();
|
||||
container.fromJson(containerJson);
|
||||
QJsonArray cardsJson = json.value("deck").toArray();
|
||||
deck.fromJson(cardsJson);
|
||||
}
|
||||
|
||||
void EdhrecAverageDeckApiResponse::debugPrint() const
|
||||
{
|
||||
qDebug() << "Deck Statistics:";
|
||||
qDebug() << " Creature:" << deckStats.creature;
|
||||
qDebug() << " Instant:" << deckStats.instant;
|
||||
qDebug() << " Sorcery:" << deckStats.sorcery;
|
||||
qDebug() << " Artifact:" << deckStats.artifact;
|
||||
qDebug() << " Enchantment:" << deckStats.enchantment;
|
||||
qDebug() << " Battle:" << deckStats.battle;
|
||||
qDebug() << " Planeswalker:" << deckStats.planeswalker;
|
||||
qDebug() << " Land:" << deckStats.land;
|
||||
qDebug() << " Basic:" << deckStats.basic;
|
||||
qDebug() << " Nonbasic:" << deckStats.nonbasic;
|
||||
|
||||
archidekt.debugPrint();
|
||||
|
||||
qDebug() << "Similar:" << similar;
|
||||
qDebug() << "Header:" << header;
|
||||
qDebug() << "Panels:" << panels;
|
||||
qDebug() << "Description:" << description;
|
||||
container.debugPrint();
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef EDHREC_AVERAGE_DECK_API_RESPONSE_H
|
||||
#define EDHREC_AVERAGE_DECK_API_RESPONSE_H
|
||||
|
||||
#include "../archidekt_links/edhrec_api_response_archidekt_links.h"
|
||||
#include "../cards/edhrec_api_response_card_container.h"
|
||||
#include "../commander/edhrec_commander_api_response_average_deck_statistics.h"
|
||||
#include "edhrec_deck_api_response.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* @class EdhrecAverageDeckApiResponse
|
||||
* @ingroup ApiResponses
|
||||
* @brief Represents the main structure of the JSON
|
||||
*/
|
||||
class EdhrecAverageDeckApiResponse
|
||||
{
|
||||
public:
|
||||
EdhrecCommanderApiResponseAverageDeckStatistics deckStats;
|
||||
EdhrecCommanderApiResponseArchidektLinks archidekt;
|
||||
QJsonObject similar;
|
||||
QString header;
|
||||
QJsonObject panels;
|
||||
QString description;
|
||||
EdhrecApiResponseCardContainer container;
|
||||
EdhrecDeckApiResponse deck;
|
||||
|
||||
void fromJson(const QJsonObject &json);
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
#endif // EDHREC_AVERAGE_DECK_API_RESPONSE_H
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#include "edhrec_deck_api_response.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QMainWindow>
|
||||
#include <libcockatrice/deck_list/deck_loader.h>
|
||||
|
||||
void EdhrecDeckApiResponse::fromJson(const QJsonArray &json)
|
||||
{
|
||||
QString deckList;
|
||||
for (const QJsonValue &cardlistValue : json) {
|
||||
deckList += cardlistValue.toString() + "\n";
|
||||
}
|
||||
|
||||
deckLoader = new DeckLoader();
|
||||
|
||||
QTextStream stream(&deckList);
|
||||
deckLoader->loadFromStream_Plain(stream, true);
|
||||
}
|
||||
|
||||
void EdhrecDeckApiResponse::debugPrint() const
|
||||
{
|
||||
qDebug() << "Breadcrumb:";
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file edhrec_deck_api_response.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_DECK_API_RESPONSE_H
|
||||
#define EDHREC_DECK_API_RESPONSE_H
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <libcockatrice/deck_list/deck_loader.h>
|
||||
|
||||
class EdhrecDeckApiResponse
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
EdhrecDeckApiResponse() = default;
|
||||
|
||||
// Parse deck-related data from JSON
|
||||
void fromJson(const QJsonArray &json);
|
||||
|
||||
// Debug method for logging
|
||||
void debugPrint() const;
|
||||
|
||||
DeckLoader *deckLoader;
|
||||
};
|
||||
|
||||
#endif // EDHREC_DECK_API_RESPONSE_H
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#include "edhrec_api_response_card_prices.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
void CardPrices::fromJson(const QJsonObject &json)
|
||||
{
|
||||
// Parse prices from various sources
|
||||
cardhoarder = json.value("cardhoarder").toObject();
|
||||
cardkingdom = json.value("cardkingdom").toObject();
|
||||
cardmarket = json.value("cardmarket").toObject();
|
||||
face2face = json.value("face2face").toObject();
|
||||
manapool = json.value("manapool").toObject();
|
||||
mtgstocks = json.value("mtgstocks").toObject();
|
||||
scg = json.value("scg").toObject();
|
||||
tcgl = json.value("tcgl").toObject();
|
||||
tcgplayer = json.value("tcgplayer").toObject();
|
||||
}
|
||||
|
||||
void CardPrices::debugPrint() const
|
||||
{
|
||||
qInfo() << "Card Prices:";
|
||||
qInfo() << "Cardhoarder:" << cardhoarder;
|
||||
qInfo() << "Cardkingdom:" << cardkingdom;
|
||||
qInfo() << "Cardmarket:" << cardmarket;
|
||||
qInfo() << "Face2Face:" << face2face;
|
||||
qInfo() << "Manapool:" << manapool;
|
||||
qInfo() << "Mtgstocks:" << mtgstocks;
|
||||
qInfo() << "SCG:" << scg;
|
||||
qInfo() << "TCGL:" << tcgl;
|
||||
qInfo() << "Tcgplayer:" << tcgplayer;
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_prices.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_PRICES_H
|
||||
#define EDHREC_COMMANDER_API_RESPONSE_CARD_PRICES_H
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
class CardPrices
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
CardPrices() = default;
|
||||
|
||||
// Parse prices from JSON
|
||||
void fromJson(const QJsonObject &json);
|
||||
void debugPrint() const;
|
||||
|
||||
// Getter methods for card prices
|
||||
const QJsonObject &getCardhoarder() const
|
||||
{
|
||||
return cardhoarder;
|
||||
}
|
||||
const QJsonObject &getCardkingdom() const
|
||||
{
|
||||
return cardkingdom;
|
||||
}
|
||||
const QJsonObject &getCardmarket() const
|
||||
{
|
||||
return cardmarket;
|
||||
}
|
||||
const QJsonObject &getFace2face() const
|
||||
{
|
||||
return face2face;
|
||||
}
|
||||
const QJsonObject &getManapool() const
|
||||
{
|
||||
return manapool;
|
||||
}
|
||||
const QJsonObject &getMtgstocks() const
|
||||
{
|
||||
return mtgstocks;
|
||||
}
|
||||
const QJsonObject &getScg() const
|
||||
{
|
||||
return scg;
|
||||
}
|
||||
const QJsonObject &getTcgl() const
|
||||
{
|
||||
return tcgl;
|
||||
}
|
||||
const QJsonObject &getTcgplayer() const
|
||||
{
|
||||
return tcgplayer;
|
||||
}
|
||||
|
||||
private:
|
||||
QJsonObject cardhoarder;
|
||||
QJsonObject cardkingdom;
|
||||
QJsonObject cardmarket;
|
||||
QJsonObject face2face;
|
||||
QJsonObject manapool;
|
||||
QJsonObject mtgstocks;
|
||||
QJsonObject scg;
|
||||
QJsonObject tcgl;
|
||||
QJsonObject tcgplayer;
|
||||
};
|
||||
|
||||
#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_PRICES_H
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
#include "edhrec_api_response_card_container.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
void EdhrecApiResponseCardContainer::fromJson(const QJsonObject &json)
|
||||
{
|
||||
// Parse breadcrumb
|
||||
QJsonArray breadcrumbArray = json.value("breadcrumb").toArray();
|
||||
for (const QJsonValue &breadcrumbValue : breadcrumbArray) {
|
||||
breadcrumb.push_back(breadcrumbValue.toObject());
|
||||
}
|
||||
|
||||
description = json.value("description").toString();
|
||||
QJsonObject jsonDict = json.value("json_dict").toObject();
|
||||
card.fromJson(jsonDict.value("card").toObject());
|
||||
QJsonArray cardlistsArray = jsonDict.value("cardlists").toArray();
|
||||
|
||||
for (const QJsonValue &cardlistValue : cardlistsArray) {
|
||||
QJsonObject cardlistObj = cardlistValue.toObject();
|
||||
QJsonArray cardviewsArray = cardlistObj.value("cardviews").toArray();
|
||||
EdhrecApiResponseCardList cardView;
|
||||
cardView.fromJson(cardlistValue.toObject());
|
||||
cardlists.push_back(cardView);
|
||||
}
|
||||
|
||||
keywords = json.value("keywords").toString();
|
||||
title = json.value("title").toString();
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardContainer::debugPrint() const
|
||||
{
|
||||
qDebug() << "Breadcrumb:";
|
||||
for (const auto &breadcrumbEntry : breadcrumb) {
|
||||
qDebug() << breadcrumbEntry;
|
||||
}
|
||||
|
||||
qDebug() << "Description:" << description;
|
||||
card.debugPrint();
|
||||
|
||||
qDebug() << "Cardlists:";
|
||||
for (const auto &cardlist : cardlists) {
|
||||
cardlist.debugPrint();
|
||||
}
|
||||
|
||||
qDebug() << "Keywords:" << keywords;
|
||||
qDebug() << "Title:" << title;
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_container.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef CONTAINER_ENTRY_H
|
||||
#define CONTAINER_ENTRY_H
|
||||
|
||||
#include "edhrec_api_response_card_list.h"
|
||||
#include "edhrec_commander_api_response_commander_details.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
class EdhrecApiResponseCardContainer
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
EdhrecApiResponseCardContainer() = default;
|
||||
|
||||
// Parse deck-related data from JSON
|
||||
void fromJson(const QJsonObject &json);
|
||||
|
||||
// Debug method for logging
|
||||
void debugPrint() const;
|
||||
|
||||
// Getter methods for deck container
|
||||
const QString &getDescription() const
|
||||
{
|
||||
return description;
|
||||
}
|
||||
const QVector<QJsonObject> &getBreadcrumb() const
|
||||
{
|
||||
return breadcrumb;
|
||||
}
|
||||
const EdhrecCommanderApiResponseCommanderDetails &getCommanderDetails() const
|
||||
{
|
||||
return card;
|
||||
}
|
||||
const QVector<EdhrecApiResponseCardList> &getCardlists() const
|
||||
{
|
||||
return cardlists;
|
||||
}
|
||||
const QString &getKeywords() const
|
||||
{
|
||||
return keywords;
|
||||
}
|
||||
const QString &getTitle() const
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
private:
|
||||
QString description;
|
||||
QVector<QJsonObject> breadcrumb;
|
||||
EdhrecCommanderApiResponseCommanderDetails card;
|
||||
QVector<EdhrecApiResponseCardList> cardlists;
|
||||
QString keywords;
|
||||
QString title;
|
||||
};
|
||||
|
||||
#endif // CONTAINER_ENTRY_H
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#include "edhrec_api_response_card_details.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
EdhrecApiResponseCardDetails::EdhrecApiResponseCardDetails()
|
||||
: synergy(0.0), inclusion(0), numDecks(0), potentialDecks(0)
|
||||
{
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardDetails::fromJson(const QJsonObject &json)
|
||||
{
|
||||
// Parse the fields from the JSON object
|
||||
name = json.value("name").toString();
|
||||
sanitized = json.value("sanitized").toString();
|
||||
sanitizedWo = json.value("sanitized_wo").toString();
|
||||
url = json.value("url").toString();
|
||||
synergy = json.value("synergy").toDouble(0.0);
|
||||
inclusion = json.value("inclusion").toInt(0);
|
||||
label = json.value("label").toString();
|
||||
numDecks = json.value("num_decks").toInt(0);
|
||||
potentialDecks = json.value("potential_decks").toInt(0);
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardDetails::debugPrint() const
|
||||
{
|
||||
// Print out all the fields for debugging
|
||||
qDebug() << "Name:" << name;
|
||||
qDebug() << "Sanitized:" << sanitized;
|
||||
qDebug() << "Sanitized Wo:" << sanitizedWo;
|
||||
qDebug() << "URL:" << url;
|
||||
qDebug() << "Synergy:" << synergy;
|
||||
qDebug() << "Inclusion:" << inclusion;
|
||||
qDebug() << "Label:" << label;
|
||||
qDebug() << "Num Decks:" << numDecks;
|
||||
qDebug() << "Potential Decks:" << potentialDecks;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_details.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef CARD_VIEW_H
|
||||
#define CARD_VIEW_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
class EdhrecApiResponseCardDetails
|
||||
{
|
||||
public:
|
||||
QString name;
|
||||
QString sanitized;
|
||||
QString sanitizedWo;
|
||||
QString url;
|
||||
double synergy;
|
||||
int inclusion;
|
||||
QString label;
|
||||
int numDecks;
|
||||
int potentialDecks;
|
||||
|
||||
EdhrecApiResponseCardDetails();
|
||||
|
||||
// Method to populate the object from a JSON object
|
||||
void fromJson(const QJsonObject &json);
|
||||
|
||||
// Debug method to print out the data
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
#endif // CARD_VIEW_H
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#include "edhrec_api_response_card_list.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
EdhrecApiResponseCardList::EdhrecApiResponseCardList()
|
||||
{
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardList::fromJson(const QJsonObject &json)
|
||||
{
|
||||
// Parse the header from the JSON object
|
||||
header = json.value("header").toString();
|
||||
|
||||
// Parse the cardviews array and populate cardViews
|
||||
QJsonArray cardviewsArray = json.value("cardviews").toArray();
|
||||
for (const QJsonValue &value : cardviewsArray) {
|
||||
QJsonObject cardviewObj = value.toObject();
|
||||
EdhrecApiResponseCardDetails cardView;
|
||||
cardView.fromJson(cardviewObj);
|
||||
cardViews.append(cardView);
|
||||
}
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardList::debugPrint() const
|
||||
{
|
||||
// Print out the header
|
||||
qDebug() << "Header:" << header;
|
||||
|
||||
// Print out all the CardView objects
|
||||
for (const EdhrecApiResponseCardDetails &cardView : cardViews) {
|
||||
cardView.debugPrint();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_list.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef CARD_LIST_H
|
||||
#define CARD_LIST_H
|
||||
|
||||
#include "edhrec_api_response_card_details.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
class EdhrecApiResponseCardList
|
||||
{
|
||||
public:
|
||||
QString header;
|
||||
QList<EdhrecApiResponseCardDetails> cardViews;
|
||||
|
||||
// Default constructor
|
||||
EdhrecApiResponseCardList();
|
||||
|
||||
// Method to populate the object from a JSON object
|
||||
void fromJson(const QJsonObject &json);
|
||||
|
||||
// Debug method to print out the data
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
#endif // CARD_LIST_H
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
#include "edhrec_commander_api_response_commander_details.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
void EdhrecCommanderApiResponseCommanderDetails::fromJson(const QJsonObject &json)
|
||||
{
|
||||
// Parse card-related data
|
||||
aetherhubUri = json.value("aetherhub_uri").toString();
|
||||
archidektUri = json.value("archidekt_uri").toString();
|
||||
cmc = json.value("cmc").toInt(0);
|
||||
colorIdentity = json.value("color_identity").toArray();
|
||||
combos = json.value("combos").toBool(false);
|
||||
deckstatsUri = json.value("deckstats_uri").toString();
|
||||
|
||||
// Parse image URIs
|
||||
QJsonArray imageUrisArray = json.value("image_uris").toArray();
|
||||
for (const QJsonValue &imageValue : imageUrisArray) {
|
||||
QJsonObject imageObject = imageValue.toObject();
|
||||
imageUris.push_back(imageObject.value("normal").toString());
|
||||
imageUris.push_back(imageObject.value("art_crop").toString());
|
||||
}
|
||||
|
||||
inclusion = json.value("inclusion").toInt(0);
|
||||
isCommander = json.value("is_commander").toBool(false);
|
||||
label = json.value("label").toString();
|
||||
layout = json.value("layout").toString();
|
||||
legalCommander = json.value("legal_commander").toBool(false);
|
||||
moxfieldUri = json.value("moxfield_uri").toString();
|
||||
mtggoldfishUri = json.value("mtggoldfish_uri").toString();
|
||||
name = json.value("name").toString();
|
||||
names = json.value("names").toArray();
|
||||
numDecks = json.value("num_decks").toInt(0);
|
||||
potentialDecks = json.value("potential_decks").toInt(0);
|
||||
precon = json.value("precon").toString();
|
||||
|
||||
// Parse prices
|
||||
prices.fromJson(json.value("prices").toObject());
|
||||
|
||||
primaryType = json.value("primary_type").toString();
|
||||
rarity = json.value("rarity").toString();
|
||||
salt = json.value("salt").toDouble(0.0);
|
||||
sanitized = json.value("sanitized").toString();
|
||||
sanitizedWo = json.value("sanitized_wo").toString();
|
||||
scryfallUri = json.value("scryfall_uri").toString();
|
||||
spellbookUri = json.value("spellbook_uri").toString();
|
||||
type = json.value("type").toString();
|
||||
url = json.value("url").toString();
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseCommanderDetails::debugPrint() const
|
||||
{
|
||||
qDebug() << "Card Data:";
|
||||
qDebug() << "Aetherhub URI:" << aetherhubUri;
|
||||
qDebug() << "Archidekt URI:" << archidektUri;
|
||||
qDebug() << "CMC:" << cmc;
|
||||
qDebug() << "Color Identity:" << colorIdentity;
|
||||
qDebug() << "Combos:" << combos;
|
||||
qDebug() << "Deckstats URI:" << deckstatsUri;
|
||||
|
||||
qDebug() << "Image URIs:";
|
||||
for (const auto &uri : imageUris) {
|
||||
qDebug() << uri;
|
||||
}
|
||||
|
||||
qDebug() << "Inclusion:" << inclusion;
|
||||
qDebug() << "Is Commander:" << isCommander;
|
||||
qDebug() << "Label:" << label;
|
||||
qDebug() << "Layout:" << layout;
|
||||
qDebug() << "Legal Commander:" << legalCommander;
|
||||
qDebug() << "Moxfield URI:" << moxfieldUri;
|
||||
qDebug() << "MTGGoldfish URI:" << mtggoldfishUri;
|
||||
qDebug() << "Name:" << name;
|
||||
qDebug() << "Names:" << names;
|
||||
qDebug() << "Number of Decks:" << numDecks;
|
||||
qDebug() << "Potential Decks:" << potentialDecks;
|
||||
qDebug() << "Precon:" << precon;
|
||||
|
||||
// Print the prices using the debugPrint method from CardPrices
|
||||
prices.debugPrint();
|
||||
|
||||
qDebug() << "Primary Type:" << primaryType;
|
||||
qDebug() << "Rarity:" << rarity;
|
||||
qDebug() << "Salt:" << salt;
|
||||
qDebug() << "Sanitized:" << sanitized;
|
||||
qDebug() << "Sanitized WO:" << sanitizedWo;
|
||||
qDebug() << "Scryfall URI:" << scryfallUri;
|
||||
qDebug() << "Spellbook URI:" << spellbookUri;
|
||||
qDebug() << "Type:" << type;
|
||||
qDebug() << "URL:" << url;
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* @file edhrec_commander_api_response_commander_details.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_H
|
||||
#define EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_H
|
||||
|
||||
#include "../card_prices/edhrec_api_response_card_prices.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
class EdhrecCommanderApiResponseCommanderDetails
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
EdhrecCommanderApiResponseCommanderDetails() = default;
|
||||
|
||||
// Parse card-related data from JSON
|
||||
void fromJson(const QJsonObject &json);
|
||||
|
||||
// Debug method for logging
|
||||
void debugPrint() const;
|
||||
|
||||
// Getters for the card data
|
||||
const QString &getAetherhubUri() const
|
||||
{
|
||||
return aetherhubUri;
|
||||
}
|
||||
const QString &getArchidektUri() const
|
||||
{
|
||||
return archidektUri;
|
||||
}
|
||||
int getCmc() const
|
||||
{
|
||||
return cmc;
|
||||
}
|
||||
const QJsonArray &getColorIdentity() const
|
||||
{
|
||||
return colorIdentity;
|
||||
}
|
||||
bool isCombos() const
|
||||
{
|
||||
return combos;
|
||||
}
|
||||
const QString &getDeckstatsUri() const
|
||||
{
|
||||
return deckstatsUri;
|
||||
}
|
||||
const QVector<QString> &getImageUris() const
|
||||
{
|
||||
return imageUris;
|
||||
}
|
||||
int getInclusion() const
|
||||
{
|
||||
return inclusion;
|
||||
}
|
||||
bool getIsCommander() const
|
||||
{
|
||||
return isCommander;
|
||||
}
|
||||
const QString &getLabel() const
|
||||
{
|
||||
return label;
|
||||
}
|
||||
const QString &getLayout() const
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
bool getLegalCommander() const
|
||||
{
|
||||
return legalCommander;
|
||||
}
|
||||
const QString &getMoxfieldUri() const
|
||||
{
|
||||
return moxfieldUri;
|
||||
}
|
||||
const QString &getMtggoldfishUri() const
|
||||
{
|
||||
return mtggoldfishUri;
|
||||
}
|
||||
const QString &getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
const QJsonArray &getNames() const
|
||||
{
|
||||
return names;
|
||||
}
|
||||
int getNumDecks() const
|
||||
{
|
||||
return numDecks;
|
||||
}
|
||||
int getPotentialDecks() const
|
||||
{
|
||||
return potentialDecks;
|
||||
}
|
||||
const QString &getPrecon() const
|
||||
{
|
||||
return precon;
|
||||
}
|
||||
const CardPrices &getPrices() const
|
||||
{
|
||||
return prices;
|
||||
}
|
||||
const QString &getPrimaryType() const
|
||||
{
|
||||
return primaryType;
|
||||
}
|
||||
const QString &getRarity() const
|
||||
{
|
||||
return rarity;
|
||||
}
|
||||
double getSalt() const
|
||||
{
|
||||
return salt;
|
||||
}
|
||||
const QString &getSanitized() const
|
||||
{
|
||||
return sanitized;
|
||||
}
|
||||
const QString &getSanitizedWo() const
|
||||
{
|
||||
return sanitizedWo;
|
||||
}
|
||||
const QString &getScryfallUri() const
|
||||
{
|
||||
return scryfallUri;
|
||||
}
|
||||
const QString &getSpellbookUri() const
|
||||
{
|
||||
return spellbookUri;
|
||||
}
|
||||
const QString &getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
const QString &getUrl() const
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
private:
|
||||
QString aetherhubUri;
|
||||
QString archidektUri;
|
||||
int cmc = 0;
|
||||
QJsonArray colorIdentity;
|
||||
bool combos = false;
|
||||
QString deckstatsUri;
|
||||
QVector<QString> imageUris;
|
||||
int inclusion = 0;
|
||||
bool isCommander = false;
|
||||
QString label;
|
||||
QString layout;
|
||||
bool legalCommander = false;
|
||||
QString moxfieldUri;
|
||||
QString mtggoldfishUri;
|
||||
QString name;
|
||||
QJsonArray names;
|
||||
int numDecks = 0;
|
||||
int potentialDecks = 0;
|
||||
QString precon;
|
||||
CardPrices prices;
|
||||
QString primaryType;
|
||||
QString rarity;
|
||||
double salt = 0.0;
|
||||
QString sanitized;
|
||||
QString sanitizedWo;
|
||||
QString scryfallUri;
|
||||
QString spellbookUri;
|
||||
QString type;
|
||||
QString url;
|
||||
};
|
||||
|
||||
#endif // EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_H
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#include "edhrec_commander_api_response.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
|
||||
void EdhrecCommanderApiResponse::fromJson(const QJsonObject &json)
|
||||
{
|
||||
// Parse the collapsed DeckStatistics
|
||||
deckStats.fromJson(json);
|
||||
|
||||
// Parse Archidekt section
|
||||
QJsonArray archidektJson = json.value("archidekt").toArray();
|
||||
archidekt.fromJson(archidektJson);
|
||||
|
||||
// Parse other fields
|
||||
similar = json.value("similar").toObject();
|
||||
header = json.value("header").toString();
|
||||
panels = json.value("panels").toObject();
|
||||
description = json.value("description").toString();
|
||||
QJsonObject containerJson = json.value("container").toObject();
|
||||
container.fromJson(containerJson);
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponse::debugPrint() const
|
||||
{
|
||||
qDebug() << "Deck Statistics:";
|
||||
qDebug() << " Creature:" << deckStats.creature;
|
||||
qDebug() << " Instant:" << deckStats.instant;
|
||||
qDebug() << " Sorcery:" << deckStats.sorcery;
|
||||
qDebug() << " Artifact:" << deckStats.artifact;
|
||||
qDebug() << " Enchantment:" << deckStats.enchantment;
|
||||
qDebug() << " Battle:" << deckStats.battle;
|
||||
qDebug() << " Planeswalker:" << deckStats.planeswalker;
|
||||
qDebug() << " Land:" << deckStats.land;
|
||||
qDebug() << " Basic:" << deckStats.basic;
|
||||
qDebug() << " Nonbasic:" << deckStats.nonbasic;
|
||||
|
||||
archidekt.debugPrint();
|
||||
|
||||
qDebug() << "Similar:" << similar;
|
||||
qDebug() << "Header:" << header;
|
||||
qDebug() << "Panels:" << panels;
|
||||
qDebug() << "Description:" << description;
|
||||
container.debugPrint();
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef DECKDATA_H
|
||||
#define DECKDATA_H
|
||||
|
||||
#include "../archidekt_links/edhrec_api_response_archidekt_links.h"
|
||||
#include "../cards/edhrec_api_response_card_container.h"
|
||||
#include "edhrec_commander_api_response_average_deck_statistics.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* @class EdhrecCommanderApiResponse
|
||||
* @ingroup ApiResponses
|
||||
* @brief Represents the main structure of the JSON
|
||||
*/
|
||||
class EdhrecCommanderApiResponse
|
||||
{
|
||||
public:
|
||||
EdhrecCommanderApiResponseAverageDeckStatistics deckStats;
|
||||
EdhrecCommanderApiResponseArchidektLinks archidekt;
|
||||
QJsonObject similar;
|
||||
QString header;
|
||||
QJsonObject panels;
|
||||
QString description;
|
||||
EdhrecApiResponseCardContainer container;
|
||||
|
||||
void fromJson(const QJsonObject &json);
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
#endif // DECKDATA_H
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#include "edhrec_commander_api_response_average_deck_statistics.h"
|
||||
|
||||
void EdhrecCommanderApiResponseAverageDeckStatistics::fromJson(const QJsonObject &json)
|
||||
{
|
||||
creature = json.value("creature").toInt(0);
|
||||
instant = json.value("instant").toInt(0);
|
||||
sorcery = json.value("sorcery").toInt(0);
|
||||
artifact = json.value("artifact").toInt(0);
|
||||
enchantment = json.value("enchantment").toInt(0);
|
||||
battle = json.value("battle").toInt(0);
|
||||
planeswalker = json.value("planeswalker").toInt(0);
|
||||
land = json.value("land").toInt(0);
|
||||
basic = json.value("basic").toInt(0);
|
||||
nonbasic = json.value("nonbasic").toInt(0);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef AVERAGE_DECK_STATISTICS_H
|
||||
#define AVERAGE_DECK_STATISTICS_H
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
/**
|
||||
* @struct EdhrecCommanderApiResponseAverageDeckStatistics
|
||||
* @ingroup ApiResponses
|
||||
* @brief Represents the typical deck statistics (collapsed section)
|
||||
*/
|
||||
struct EdhrecCommanderApiResponseAverageDeckStatistics
|
||||
{
|
||||
int creature = 0;
|
||||
int instant = 0;
|
||||
int sorcery = 0;
|
||||
int artifact = 0;
|
||||
int enchantment = 0;
|
||||
int battle = 0;
|
||||
int planeswalker = 0;
|
||||
int land = 0;
|
||||
int basic = 0;
|
||||
int nonbasic = 0;
|
||||
|
||||
void fromJson(const QJsonObject &json);
|
||||
};
|
||||
#endif // AVERAGE_DECK_STATISTICS_H
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#include "edhrec_top_cards_api_response.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
|
||||
void EdhrecTopCardsApiResponse::fromJson(const QJsonObject &json)
|
||||
{
|
||||
header = json.value("header").toString();
|
||||
description = json.value("description").toString();
|
||||
QJsonObject containerJson = json.value("container").toObject();
|
||||
container.fromJson(containerJson);
|
||||
}
|
||||
|
||||
void EdhrecTopCardsApiResponse::debugPrint() const
|
||||
{
|
||||
qDebug() << "Header:" << header;
|
||||
qDebug() << "Description:" << description;
|
||||
container.debugPrint();
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @file edhrec_top_cards_api_response.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_TOP_CARDS_API_RESPONSE_H
|
||||
#define EDHREC_TOP_CARDS_API_RESPONSE_H
|
||||
|
||||
#include "../cards/edhrec_api_response_card_container.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
|
||||
class EdhrecTopCardsApiResponse
|
||||
{
|
||||
public:
|
||||
QString header;
|
||||
QString description;
|
||||
EdhrecApiResponseCardContainer container;
|
||||
|
||||
void fromJson(const QJsonObject &json);
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
#endif // EDHREC_TOP_CARDS_API_RESPONSE_H
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#include "edhrec_top_commanders_api_response.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
|
||||
void EdhrecTopCommandersApiResponse::fromJson(const QJsonObject &json)
|
||||
{
|
||||
header = json.value("header").toString();
|
||||
description = json.value("description").toString();
|
||||
QJsonObject containerJson = json.value("container").toObject();
|
||||
container.fromJson(containerJson);
|
||||
}
|
||||
|
||||
void EdhrecTopCommandersApiResponse::debugPrint() const
|
||||
{
|
||||
qDebug() << "Header:" << header;
|
||||
qDebug() << "Description:" << description;
|
||||
container.debugPrint();
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @file edhrec_top_commanders_api_response.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_TOP_COMMANDERS_API_RESPONSE_H
|
||||
#define EDHREC_TOP_COMMANDERS_API_RESPONSE_H
|
||||
|
||||
#include "../cards/edhrec_api_response_card_container.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
|
||||
class EdhrecTopCommandersApiResponse
|
||||
{
|
||||
public:
|
||||
QString header;
|
||||
QString description;
|
||||
EdhrecApiResponseCardContainer container;
|
||||
|
||||
void fromJson(const QJsonObject &json);
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
#endif // EDHREC_TOP_COMMANDERS_API_RESPONSE_H
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#include "edhrec_top_tags_api_response.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
|
||||
void EdhrecTopTagsApiResponse::fromJson(const QJsonObject &json)
|
||||
{
|
||||
header = json.value("header").toString();
|
||||
description = json.value("description").toString();
|
||||
QJsonObject containerJson = json.value("container").toObject();
|
||||
container.fromJson(containerJson);
|
||||
}
|
||||
|
||||
void EdhrecTopTagsApiResponse::debugPrint() const
|
||||
{
|
||||
qDebug() << "Header:" << header;
|
||||
qDebug() << "Description:" << description;
|
||||
container.debugPrint();
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @file edhrec_top_tags_api_response.h
|
||||
* @ingroup ApiResponses
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_TOP_TAGS_API_RESPONSE_H
|
||||
#define EDHREC_TOP_TAGS_API_RESPONSE_H
|
||||
|
||||
#include "../cards/edhrec_api_response_card_container.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
|
||||
class EdhrecTopTagsApiResponse
|
||||
{
|
||||
public:
|
||||
QString header;
|
||||
QString description;
|
||||
EdhrecApiResponseCardContainer container;
|
||||
|
||||
void fromJson(const QJsonObject &json);
|
||||
void debugPrint() const;
|
||||
};
|
||||
|
||||
#endif // EDHREC_TOP_TAGS_API_RESPONSE_H
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#include "edhrec_api_response_card_prices_display_widget.h"
|
||||
|
||||
EdhrecApiResponseCardPricesDisplayWidget::EdhrecApiResponseCardPricesDisplayWidget(QWidget *parent,
|
||||
const CardPrices &_cardPrices)
|
||||
: QWidget(parent), cardPrices(_cardPrices)
|
||||
{
|
||||
layout = new QGridLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
cardHoarderLabel = new QLabel(this);
|
||||
cardHoarderPrice = new QLabel(QString::number(cardPrices.getCardhoarder().value("price").toDouble()), this);
|
||||
cardKingdomLabel = new QLabel(this);
|
||||
cardKingdomPrice = new QLabel(QString::number(cardPrices.getCardkingdom().value("price").toDouble()), this);
|
||||
cardMarketLabel = new QLabel(this);
|
||||
cardMarketPrice = new QLabel(QString::number(cardPrices.getCardmarket().value("price").toDouble()), this);
|
||||
face2faceLabel = new QLabel(this);
|
||||
face2facePrice = new QLabel(QString::number(cardPrices.getFace2face().value("price").toDouble()), this);
|
||||
manaPoolLabel = new QLabel(this);
|
||||
manaPoolPrice = new QLabel(QString::number(cardPrices.getManapool().value("price").toDouble()), this);
|
||||
mtgStocksLabel = new QLabel(this);
|
||||
mtgStocksPrice = new QLabel(QString::number(cardPrices.getMtgstocks().value("price").toDouble()), this);
|
||||
scgLabel = new QLabel(this);
|
||||
scgPrice = new QLabel(QString::number(cardPrices.getScg().value("price").toDouble()), this);
|
||||
tcglLabel = new QLabel(this);
|
||||
tcglPrice = new QLabel(QString::number(cardPrices.getTcgl().value("price").toDouble()), this);
|
||||
tcgplayerLabel = new QLabel(this);
|
||||
tcgplayerPrice = new QLabel(QString::number(cardPrices.getTcgplayer().value("price").toDouble()), this);
|
||||
|
||||
layout->addWidget(cardHoarderLabel, 0, 0);
|
||||
layout->addWidget(cardHoarderPrice, 0, 1);
|
||||
layout->addWidget(cardKingdomLabel, 0, 2);
|
||||
layout->addWidget(cardKingdomPrice, 0, 3);
|
||||
|
||||
layout->addWidget(cardMarketLabel, 1, 0);
|
||||
layout->addWidget(cardMarketPrice, 1, 1);
|
||||
layout->addWidget(face2faceLabel, 1, 2);
|
||||
layout->addWidget(face2facePrice, 1, 3);
|
||||
|
||||
layout->addWidget(manaPoolLabel, 2, 0);
|
||||
layout->addWidget(manaPoolPrice, 2, 1);
|
||||
layout->addWidget(mtgStocksLabel, 2, 2);
|
||||
layout->addWidget(mtgStocksPrice, 2, 3);
|
||||
|
||||
layout->addWidget(scgLabel, 3, 0);
|
||||
layout->addWidget(scgPrice, 3, 1);
|
||||
layout->addWidget(tcglLabel, 3, 2);
|
||||
layout->addWidget(tcglPrice, 3, 3);
|
||||
|
||||
layout->addWidget(tcgplayerLabel, 4, 0);
|
||||
layout->addWidget(tcgplayerPrice, 4, 1);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardPricesDisplayWidget::retranslateUi()
|
||||
{
|
||||
cardHoarderLabel->setText(tr("Card Hoarder"));
|
||||
cardKingdomLabel->setText(tr("Card Kingdom"));
|
||||
cardMarketLabel->setText(tr("Card Market"));
|
||||
face2faceLabel->setText(tr("Face 2-Face"));
|
||||
manaPoolLabel->setText(tr("Mana Pool"));
|
||||
mtgStocksLabel->setText(tr("MTG Stocks"));
|
||||
scgLabel->setText(tr("Scg"));
|
||||
tcglLabel->setText(tr("Tcgl"));
|
||||
tcgplayerLabel->setText(tr("Tcgplayer"));
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_prices_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_API_RESPONSE_CARD_PRICES_DISPLAY_WIDGET_H
|
||||
#define EDHREC_API_RESPONSE_CARD_PRICES_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../api_response/card_prices/edhrec_api_response_card_prices.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecApiResponseCardPricesDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EdhrecApiResponseCardPricesDisplayWidget(QWidget *parent, const CardPrices &cardPrices);
|
||||
|
||||
public slots:
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
CardPrices cardPrices;
|
||||
QGridLayout *layout;
|
||||
QLabel *cardHoarderLabel;
|
||||
QLabel *cardHoarderPrice;
|
||||
QLabel *cardKingdomLabel;
|
||||
QLabel *cardKingdomPrice;
|
||||
QLabel *cardMarketLabel;
|
||||
QLabel *cardMarketPrice;
|
||||
QLabel *face2faceLabel;
|
||||
QLabel *face2facePrice;
|
||||
QLabel *manaPoolLabel;
|
||||
QLabel *manaPoolPrice;
|
||||
QLabel *mtgStocksLabel;
|
||||
QLabel *mtgStocksPrice;
|
||||
QLabel *scgLabel;
|
||||
QLabel *scgPrice;
|
||||
QLabel *tcglLabel;
|
||||
QLabel *tcglPrice;
|
||||
QLabel *tcgplayerLabel;
|
||||
QLabel *tcgplayerPrice;
|
||||
};
|
||||
|
||||
#endif // EDHREC_API_RESPONSE_CARD_PRICES_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#include "edhrec_api_response_card_details_display_widget.h"
|
||||
|
||||
#include "../../tab_edhrec_main.h"
|
||||
|
||||
#include <libcockatrice/card/card_database/card_database_manager.h>
|
||||
|
||||
EdhrecApiResponseCardDetailsDisplayWidget::EdhrecApiResponseCardDetailsDisplayWidget(
|
||||
QWidget *parent,
|
||||
const EdhrecApiResponseCardDetails &_toDisplay)
|
||||
: QWidget(parent), toDisplay(_toDisplay)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
cardPictureWidget = new CardInfoPictureWidget(this);
|
||||
cardPictureWidget->setCard(CardDatabaseManager::query()->guessCard({toDisplay.sanitized}));
|
||||
|
||||
nameLabel = new QLabel(this);
|
||||
nameLabel->setText(toDisplay.name);
|
||||
nameLabel->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
inclusionDisplayWidget = new EdhrecApiResponseCardInclusionDisplayWidget(this, toDisplay);
|
||||
|
||||
synergyDisplayWidget = new EdhrecApiResponseCardSynergyDisplayWidget(this, toDisplay);
|
||||
|
||||
layout->addWidget(nameLabel);
|
||||
layout->addWidget(cardPictureWidget);
|
||||
layout->addWidget(inclusionDisplayWidget);
|
||||
layout->addWidget(synergyDisplayWidget);
|
||||
|
||||
QWidget *currentParent = parentWidget();
|
||||
TabEdhRecMain *parentTab = nullptr;
|
||||
|
||||
while (currentParent) {
|
||||
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
|
||||
break;
|
||||
}
|
||||
currentParent = currentParent->parentWidget();
|
||||
}
|
||||
|
||||
if (parentTab) {
|
||||
cardPictureWidget->setScaleFactor(parentTab->getCardSizeSlider()->getSlider()->value());
|
||||
connect(cardPictureWidget, &CardInfoPictureWidget::cardClicked, this,
|
||||
&EdhrecApiResponseCardDetailsDisplayWidget::actRequestPageNavigation);
|
||||
connect(parentTab->getCardSizeSlider()->getSlider(), &QSlider::valueChanged, cardPictureWidget,
|
||||
&CardInfoPictureWidget::setScaleFactor);
|
||||
connect(this, &EdhrecApiResponseCardDetailsDisplayWidget::requestUrl, parentTab,
|
||||
&TabEdhRecMain::actNavigatePage);
|
||||
}
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardDetailsDisplayWidget::actRequestPageNavigation()
|
||||
{
|
||||
emit requestUrl(toDisplay.url);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_details_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H
|
||||
#define EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../../../../cards/card_info_picture_widget.h"
|
||||
#include "../../api_response/cards/edhrec_api_response_card_details.h"
|
||||
#include "edhrec_api_response_card_inclusion_display_widget.h"
|
||||
#include "edhrec_api_response_card_synergy_display_widget.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecApiResponseCardDetailsDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EdhrecApiResponseCardDetailsDisplayWidget(QWidget *parent, const EdhrecApiResponseCardDetails &_toDisplay);
|
||||
public slots:
|
||||
void actRequestPageNavigation();
|
||||
signals:
|
||||
void requestUrl(QString url);
|
||||
|
||||
private:
|
||||
EdhrecApiResponseCardDetails toDisplay;
|
||||
QVBoxLayout *layout;
|
||||
CardInfoPictureWidget *cardPictureWidget;
|
||||
QLabel *nameLabel;
|
||||
EdhrecApiResponseCardInclusionDisplayWidget *inclusionDisplayWidget;
|
||||
EdhrecApiResponseCardSynergyDisplayWidget *synergyDisplayWidget;
|
||||
};
|
||||
|
||||
#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_DETAILS_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#include "edhrec_api_response_card_inclusion_display_widget.h"
|
||||
|
||||
EdhrecApiResponseCardInclusionDisplayWidget::EdhrecApiResponseCardInclusionDisplayWidget(
|
||||
QWidget *parent,
|
||||
const EdhrecApiResponseCardDetails &_toDisplay)
|
||||
: QWidget(parent), toDisplay(_toDisplay)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
commanderLabel = new QLabel(this);
|
||||
commanderLabel->setAlignment(Qt::AlignCenter);
|
||||
amountLabel = new QLabel(this);
|
||||
amountLabel->setAlignment(Qt::AlignCenter);
|
||||
inclusionLabel = new QLabel(this);
|
||||
inclusionLabel->setAlignment(Qt::AlignCenter);
|
||||
percentBarWidget = new PercentBarWidget(this, toDisplay.inclusion / (toDisplay.potentialDecks / 100.0));
|
||||
|
||||
if (toDisplay.inclusion != 0 && toDisplay.potentialDecks != 0) {
|
||||
layout->addWidget(amountLabel);
|
||||
layout->addWidget(inclusionLabel);
|
||||
layout->addWidget(percentBarWidget);
|
||||
commanderLabel->hide();
|
||||
} else {
|
||||
amountLabel->hide();
|
||||
inclusionLabel->hide();
|
||||
percentBarWidget->hide();
|
||||
layout->addWidget(commanderLabel);
|
||||
}
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardInclusionDisplayWidget::retranslateUi()
|
||||
{
|
||||
commanderLabel->setText(toDisplay.label);
|
||||
amountLabel->setText(tr("In %1 decks").arg(QString::number(toDisplay.inclusion)));
|
||||
inclusionLabel->setText(tr("%1% of %2 decks")
|
||||
.arg(QString::number(toDisplay.inclusion / (toDisplay.potentialDecks / 100.0), 'f', 2),
|
||||
QString::number(toDisplay.potentialDecks)));
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_inclusion_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H
|
||||
#define EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../../../../general/display/percent_bar_widget.h"
|
||||
#include "../../api_response/cards/edhrec_api_response_card_details.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecApiResponseCardInclusionDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EdhrecApiResponseCardInclusionDisplayWidget(QWidget *parent, const EdhrecApiResponseCardDetails &_toDisplay);
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
EdhrecApiResponseCardDetails toDisplay;
|
||||
QLabel *commanderLabel;
|
||||
QLabel *amountLabel;
|
||||
QLabel *inclusionLabel;
|
||||
PercentBarWidget *percentBarWidget;
|
||||
};
|
||||
|
||||
#endif // EDHREC_API_RESPONSE_CARD_INCLUSION_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#include "edhrec_api_response_card_list_display_widget.h"
|
||||
|
||||
#include "../../../../../general/display/banner_widget.h"
|
||||
#include "edhrec_api_response_card_details_display_widget.h"
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
EdhrecApiResponseCardListDisplayWidget::EdhrecApiResponseCardListDisplayWidget(QWidget *parent,
|
||||
EdhrecApiResponseCardList toDisplay)
|
||||
: QWidget(parent)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
header = new BannerWidget(this, toDisplay.header);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
|
||||
header->setBuddy(flowWidget);
|
||||
|
||||
foreach (EdhrecApiResponseCardDetails card_detail, toDisplay.cardViews) {
|
||||
auto widget = new EdhrecApiResponseCardDetailsDisplayWidget(flowWidget, card_detail);
|
||||
flowWidget->addWidget(widget);
|
||||
}
|
||||
|
||||
layout->addWidget(header);
|
||||
layout->addWidget(flowWidget);
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardListDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
qDebug() << event->size();
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_list_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_COMMANDER_API_RESPONSE_CARD_LIST_DISPLAY_WIDGET_H
|
||||
#define EDHREC_COMMANDER_API_RESPONSE_CARD_LIST_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../../../../general/display/banner_widget.h"
|
||||
#include "../../../../../general/layout_containers/flow_widget.h"
|
||||
#include "../../api_response/cards/edhrec_api_response_card_list.h"
|
||||
|
||||
#include <QResizeEvent>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecApiResponseCardListDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EdhrecApiResponseCardListDisplayWidget(QWidget *parent, EdhrecApiResponseCardList toDisplay);
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
[[nodiscard]] QString getBannerText() const
|
||||
{
|
||||
return header->getText();
|
||||
};
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
BannerWidget *header;
|
||||
FlowWidget *flowWidget;
|
||||
};
|
||||
|
||||
#endif // EDHREC_COMMANDER_API_RESPONSE_CARD_LIST_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#include "edhrec_api_response_card_synergy_display_widget.h"
|
||||
|
||||
EdhrecApiResponseCardSynergyDisplayWidget::EdhrecApiResponseCardSynergyDisplayWidget(
|
||||
QWidget *parent,
|
||||
const EdhrecApiResponseCardDetails &_toDisplay)
|
||||
: QWidget(parent), toDisplay(_toDisplay)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
label = new QLabel(this);
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
percentBarWidget = new PercentBarWidget(this, toDisplay.synergy * 100.0);
|
||||
|
||||
if (toDisplay.synergy != 0) {
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(percentBarWidget);
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void EdhrecApiResponseCardSynergyDisplayWidget::retranslateUi()
|
||||
{
|
||||
label->setText(tr("%1% Synergy").arg(QString::number(toDisplay.synergy * 100.0, 'f', 1)));
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @file edhrec_api_response_card_synergy_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H
|
||||
#define EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../../../../general/display/percent_bar_widget.h"
|
||||
#include "../../api_response/cards/edhrec_api_response_card_details.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecApiResponseCardSynergyDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EdhrecApiResponseCardSynergyDisplayWidget(QWidget *parent, const EdhrecApiResponseCardDetails &_toDisplay);
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
EdhrecApiResponseCardDetails toDisplay;
|
||||
QLabel *label;
|
||||
PercentBarWidget *percentBarWidget;
|
||||
};
|
||||
|
||||
#endif // EDHREC_API_RESPONSE_CARD_SYNERGY_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#include "edhrec_api_response_commander_details_display_widget.h"
|
||||
|
||||
#include "../../../../../cards/card_info_picture_widget.h"
|
||||
#include "../../tab_edhrec_main.h"
|
||||
#include "../card_prices/edhrec_api_response_card_prices_display_widget.h"
|
||||
|
||||
#include <libcockatrice/card/card_database/card_database_manager.h>
|
||||
|
||||
EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget(
|
||||
QWidget *parent,
|
||||
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
|
||||
QString baseUrl)
|
||||
: QWidget(parent), commanderDetails(_commanderDetails)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
commanderPicture = new CardInfoPictureWidget(this);
|
||||
commanderPicture->setCard(CardDatabaseManager::query()->getCard({commanderDetails.getName()}));
|
||||
|
||||
QWidget *currentParent = parentWidget();
|
||||
TabEdhRecMain *parentTab = nullptr;
|
||||
|
||||
while (currentParent) {
|
||||
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
|
||||
break;
|
||||
}
|
||||
currentParent = currentParent->parentWidget();
|
||||
}
|
||||
|
||||
if (parentTab) {
|
||||
connect(parentTab->getCardSizeSlider()->getSlider(), &QSlider::valueChanged, commanderPicture,
|
||||
&CardInfoPictureWidget::setScaleFactor);
|
||||
commanderPicture->setScaleFactor(parentTab->getCardSizeSlider()->getSlider()->value());
|
||||
}
|
||||
|
||||
commanderDetails.debugPrint();
|
||||
|
||||
label = new QLabel(this);
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
salt = new QLabel(this);
|
||||
salt->setAlignment(Qt::AlignCenter);
|
||||
|
||||
cardPricesDisplayWidget = new EdhrecApiResponseCardPricesDisplayWidget(this, commanderDetails.getPrices());
|
||||
|
||||
navigationWidget = new EdhrecCommanderApiResponseNavigationWidget(this, commanderDetails, baseUrl);
|
||||
|
||||
layout->addWidget(commanderPicture);
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(salt);
|
||||
layout->addWidget(cardPricesDisplayWidget);
|
||||
layout->addWidget(navigationWidget);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void EdhrecCommanderResponseCommanderDetailsDisplayWidget::retranslateUi()
|
||||
{
|
||||
label->setText(commanderDetails.getLabel());
|
||||
salt->setText(tr("Salt: ") + QString::number(commanderDetails.getSalt()));
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* @file edhrec_api_response_commander_details_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H
|
||||
#define EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../../../../cards/card_info_picture_widget.h"
|
||||
#include "../../api_response/cards/edhrec_commander_api_response_commander_details.h"
|
||||
#include "../card_prices/edhrec_api_response_card_prices_display_widget.h"
|
||||
#include "edhrec_commander_api_response_navigation_widget.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecCommanderApiResponseNavigationWidget;
|
||||
class EdhrecCommanderResponseCommanderDetailsDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EdhrecCommanderResponseCommanderDetailsDisplayWidget(
|
||||
QWidget *parent,
|
||||
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
|
||||
QString baseUrl);
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
EdhrecCommanderApiResponseCommanderDetails commanderDetails;
|
||||
QVBoxLayout *layout;
|
||||
CardInfoPictureWidget *commanderPicture;
|
||||
QLabel *label;
|
||||
QLabel *salt;
|
||||
EdhrecApiResponseCardPricesDisplayWidget *cardPricesDisplayWidget;
|
||||
EdhrecCommanderApiResponseNavigationWidget *navigationWidget;
|
||||
};
|
||||
|
||||
#endif // EDHREC_COMMANDER_API_RESPONSE_COMMANDER_DETAILS_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#include "edhrec_commander_api_response_display_widget.h"
|
||||
|
||||
#include "../../../../../cards/card_info_picture_widget.h"
|
||||
#include "../../api_response/commander/edhrec_commander_api_response.h"
|
||||
#include "../cards/edhrec_api_response_card_list_display_widget.h"
|
||||
#include "edhrec_api_response_commander_details_display_widget.h"
|
||||
|
||||
#include <QListView>
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QSplitter>
|
||||
#include <QStringListModel>
|
||||
|
||||
EdhrecCommanderApiResponseDisplayWidget::EdhrecCommanderApiResponseDisplayWidget(QWidget *parent,
|
||||
EdhrecCommanderApiResponse response,
|
||||
QString baseUrl)
|
||||
: QWidget(parent)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
cardDisplayLayout = new QVBoxLayout(this);
|
||||
|
||||
// Create a QSplitter to hold the ListView and ScrollArea holding CardListdisplayWidgets side by side
|
||||
auto splitter = new QSplitter(this);
|
||||
splitter->setOrientation(Qt::Horizontal);
|
||||
|
||||
auto listView = new QListView(splitter);
|
||||
listView->setMinimumWidth(50);
|
||||
listView->setMaximumWidth(150);
|
||||
auto listModel = new QStringListModel(this);
|
||||
QStringList widgetNames;
|
||||
|
||||
// Add commander details
|
||||
auto commanderPicture = new EdhrecCommanderResponseCommanderDetailsDisplayWidget(
|
||||
this, response.container.getCommanderDetails(), baseUrl);
|
||||
cardDisplayLayout->addWidget(commanderPicture);
|
||||
widgetNames.append("Commander Details");
|
||||
|
||||
// Add card list widgets
|
||||
auto edhrec_commander_api_response_card_lists = response.container.getCardlists();
|
||||
for (const EdhrecApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
|
||||
auto cardListDisplayWidget = new EdhrecApiResponseCardListDisplayWidget(this, card_list);
|
||||
cardDisplayLayout->addWidget(cardListDisplayWidget);
|
||||
widgetNames.append(cardListDisplayWidget->getBannerText());
|
||||
}
|
||||
|
||||
// Create a QScrollArea to hold the card display widgets
|
||||
scrollArea = new QScrollArea(splitter);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
// Set the cardDisplayLayout inside the scroll area
|
||||
auto scrollWidget = new QWidget(scrollArea);
|
||||
scrollWidget->setLayout(cardDisplayLayout);
|
||||
connect(splitter, &QSplitter::splitterMoved, this, &EdhrecCommanderApiResponseDisplayWidget::onSplitterChange);
|
||||
scrollArea->setWidget(scrollWidget);
|
||||
|
||||
// Configure the list view
|
||||
listModel->setStringList(widgetNames);
|
||||
listView->setModel(listModel);
|
||||
listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
|
||||
// Connect the list view to ensure the corresponding widget is visible
|
||||
connect(listView, &QListView::clicked, this, [this](const QModelIndex &index) {
|
||||
int widgetIndex = index.row();
|
||||
qDebug() << "clicked: " << widgetIndex;
|
||||
auto targetWidget = cardDisplayLayout->itemAt(widgetIndex)->widget();
|
||||
if (targetWidget) {
|
||||
qDebug() << "Found targetWidget" << targetWidget;
|
||||
// Attempt to cast the parent to QScrollArea
|
||||
auto scrollArea = qobject_cast<QScrollArea *>(this->scrollArea); // Use the scroll area instance
|
||||
if (scrollArea) {
|
||||
qDebug() << "ScrollArea" << scrollArea;
|
||||
scrollArea->ensureWidgetVisible(targetWidget);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add splitter to the main layout
|
||||
splitter->addWidget(listView);
|
||||
splitter->addWidget(scrollArea);
|
||||
|
||||
layout->addWidget(splitter);
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseDisplayWidget::onSplitterChange()
|
||||
{
|
||||
scrollArea->widget()->resize(scrollArea->size());
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
qDebug() << event->size();
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
if (scrollArea && scrollArea->widget()) {
|
||||
scrollArea->widget()->resize(event->size());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* @file edhrec_commander_api_response_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
#define EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../api_response/commander/edhrec_commander_api_response.h"
|
||||
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecCommanderApiResponseDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EdhrecCommanderApiResponseDisplayWidget(QWidget *parent,
|
||||
EdhrecCommanderApiResponse response,
|
||||
QString baseUrl);
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
public slots:
|
||||
void onSplitterChange();
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QVBoxLayout *cardDisplayLayout;
|
||||
QScrollArea *scrollArea;
|
||||
};
|
||||
|
||||
#endif // EDHREC_COMMANDER_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
#include "edhrec_commander_api_response_navigation_widget.h"
|
||||
|
||||
#include "../../tab_edhrec_main.h"
|
||||
|
||||
EdhrecCommanderApiResponseNavigationWidget::EdhrecCommanderApiResponseNavigationWidget(
|
||||
QWidget *parent,
|
||||
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
|
||||
QString baseUrl)
|
||||
: QWidget(parent), commanderDetails(_commanderDetails)
|
||||
{
|
||||
layout = new QGridLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
gameChangerLabel = new QLabel(this);
|
||||
budgetLabel = new QLabel(this);
|
||||
|
||||
comboPushButton = new QPushButton(this);
|
||||
averageDeckPushButton = new QPushButton(this);
|
||||
|
||||
layout->addWidget(comboPushButton, 0, 0, 1, 1);
|
||||
layout->addWidget(averageDeckPushButton, 0, 1, 1, 1);
|
||||
|
||||
layout->addWidget(gameChangerLabel, 1, 0, 1, 2);
|
||||
|
||||
for (int i = 0; i < gameChangerOptions.length(); i++) {
|
||||
QString option = gameChangerOptions.at(i);
|
||||
QString label = option.isEmpty() ? "All" : option.at(0).toUpper() + option.mid(1);
|
||||
QPushButton *optionButton = new QPushButton(label, this);
|
||||
gameChangerButtons[option] = optionButton;
|
||||
layout->addWidget(optionButton, 2, i);
|
||||
connect(optionButton, &QPushButton::clicked, this, [=, this]() {
|
||||
selectedGameChanger = option;
|
||||
updateOptionButtonSelection(gameChangerButtons, option);
|
||||
actRequestCommanderNavigation();
|
||||
});
|
||||
}
|
||||
|
||||
layout->addWidget(budgetLabel, 3, 0, 1, 2);
|
||||
|
||||
for (int i = 0; i < budgetOptions.length(); i++) {
|
||||
QString option = budgetOptions.at(i);
|
||||
QString label = option.isEmpty() ? "Any" : option.at(0).toUpper() + option.mid(1);
|
||||
QPushButton *btn = new QPushButton(label, this);
|
||||
budgetButtons[option] = btn;
|
||||
layout->addWidget(btn, 4, i);
|
||||
connect(btn, &QPushButton::clicked, this, [=, this]() {
|
||||
selectedBudget = option;
|
||||
updateOptionButtonSelection(budgetButtons, option);
|
||||
actRequestCommanderNavigation();
|
||||
});
|
||||
}
|
||||
|
||||
updateOptionButtonSelection(gameChangerButtons, "");
|
||||
updateOptionButtonSelection(budgetButtons, "");
|
||||
|
||||
QWidget *currentParent = parentWidget();
|
||||
TabEdhRecMain *parentTab = nullptr;
|
||||
|
||||
while (currentParent) {
|
||||
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
|
||||
break;
|
||||
}
|
||||
currentParent = currentParent->parentWidget();
|
||||
}
|
||||
|
||||
if (parentTab) {
|
||||
connect(comboPushButton, &QPushButton::clicked, this,
|
||||
&EdhrecCommanderApiResponseNavigationWidget::actRequestComboNavigation);
|
||||
connect(averageDeckPushButton, &QPushButton::clicked, this,
|
||||
&EdhrecCommanderApiResponseNavigationWidget::actRequestAverageDeckNavigation);
|
||||
connect(this, &EdhrecCommanderApiResponseNavigationWidget::requestUrl, parentTab,
|
||||
&TabEdhRecMain::actNavigatePage);
|
||||
}
|
||||
|
||||
retranslateUi();
|
||||
applyOptionsFromUrl(baseUrl);
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseNavigationWidget::retranslateUi()
|
||||
{
|
||||
comboPushButton->setText(tr("Combos"));
|
||||
averageDeckPushButton->setText(tr("Average Deck"));
|
||||
gameChangerLabel->setText(tr("Game Changers"));
|
||||
budgetLabel->setText(tr("Budget"));
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseNavigationWidget::applyOptionsFromUrl(const QString &url)
|
||||
{
|
||||
QString cleanedUrl = url;
|
||||
|
||||
// Remove base and file extension
|
||||
if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) {
|
||||
cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length());
|
||||
}
|
||||
if (cleanedUrl.endsWith(".json")) {
|
||||
cleanedUrl.chop(5);
|
||||
}
|
||||
|
||||
// Expecting something like: "commanders/the-ur-dragon/core/expensive"
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts);
|
||||
#else
|
||||
QStringList parts = cleanedUrl.split('/', QString::SkipEmptyParts);
|
||||
#endif
|
||||
|
||||
if (parts.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString commanderName = parts[1];
|
||||
QString gameChangerOpt, budgetOpt;
|
||||
|
||||
// Define valid sets
|
||||
QSet<QString> validGameChangers = {"core", "upgraded", "optimized"};
|
||||
QSet<QString> validBudgets = {"budget", "expensive"};
|
||||
|
||||
// Check remaining parts after commander
|
||||
for (int i = 2; i < parts.size(); ++i) {
|
||||
QString part = parts[i].toLower();
|
||||
if (validGameChangers.contains(part)) {
|
||||
gameChangerOpt = part;
|
||||
} else if (validBudgets.contains(part)) {
|
||||
budgetOpt = part;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate and apply
|
||||
if (!gameChangerButtons.contains(gameChangerOpt)) {
|
||||
gameChangerOpt.clear();
|
||||
}
|
||||
if (!budgetButtons.contains(budgetOpt)) {
|
||||
budgetOpt.clear();
|
||||
}
|
||||
|
||||
selectedGameChanger = gameChangerOpt;
|
||||
selectedBudget = budgetOpt;
|
||||
|
||||
updateOptionButtonSelection(gameChangerButtons, selectedGameChanger);
|
||||
updateOptionButtonSelection(budgetButtons, selectedBudget);
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseNavigationWidget::updateOptionButtonSelection(QMap<QString, QPushButton *> &buttons,
|
||||
const QString &selectedKey)
|
||||
{
|
||||
for (auto it = buttons.begin(); it != buttons.end(); ++it) {
|
||||
it.value()->setStyleSheet(it.key() == selectedKey ? "background-color: lightblue; font-weight: bold;" : "");
|
||||
}
|
||||
}
|
||||
|
||||
QString EdhrecCommanderApiResponseNavigationWidget::addNavigationOptionsToUrl(QString baseUrl)
|
||||
{
|
||||
if (!selectedGameChanger.isEmpty()) {
|
||||
baseUrl += "/" + selectedGameChanger;
|
||||
}
|
||||
if (!selectedBudget.isEmpty()) {
|
||||
baseUrl += "/" + selectedBudget;
|
||||
}
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseNavigationWidget::actRequestCommanderNavigation()
|
||||
{
|
||||
emit requestUrl(addNavigationOptionsToUrl("/commanders/" + commanderDetails.getSanitized()));
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseNavigationWidget::actRequestComboNavigation()
|
||||
{
|
||||
emit requestUrl("/combos/" + commanderDetails.getSanitized());
|
||||
}
|
||||
|
||||
void EdhrecCommanderApiResponseNavigationWidget::actRequestAverageDeckNavigation()
|
||||
{
|
||||
emit requestUrl(addNavigationOptionsToUrl("/average-decks/" + commanderDetails.getSanitized()));
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* @file edhrec_commander_api_response_navigation_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H
|
||||
#define EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H
|
||||
|
||||
#include "edhrec_api_response_commander_details_display_widget.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecCommanderApiResponseNavigationWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EdhrecCommanderApiResponseNavigationWidget(
|
||||
QWidget *parent,
|
||||
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
|
||||
QString baseUrl);
|
||||
void retranslateUi();
|
||||
void applyOptionsFromUrl(const QString &url);
|
||||
|
||||
public slots:
|
||||
void actRequestCommanderNavigation();
|
||||
void actRequestComboNavigation();
|
||||
void actRequestAverageDeckNavigation();
|
||||
signals:
|
||||
void requestUrl(QString url);
|
||||
|
||||
private:
|
||||
QGridLayout *layout;
|
||||
|
||||
QLabel *gameChangerLabel;
|
||||
QLabel *budgetLabel;
|
||||
|
||||
QStringList gameChangerOptions = {"", "core", "upgraded", "optimized"};
|
||||
QStringList budgetOptions = {"", "budget", "expensive"};
|
||||
|
||||
QString selectedGameChanger;
|
||||
QString selectedBudget;
|
||||
|
||||
QMap<QString, QPushButton *> gameChangerButtons;
|
||||
QMap<QString, QPushButton *> budgetButtons;
|
||||
|
||||
QPushButton *comboPushButton;
|
||||
QPushButton *averageDeckPushButton;
|
||||
|
||||
EdhrecCommanderApiResponseCommanderDetails commanderDetails;
|
||||
|
||||
void updateOptionButtonSelection(QMap<QString, QPushButton *> &buttons, const QString &selectedKey);
|
||||
QString addNavigationOptionsToUrl(QString baseUrl);
|
||||
QString buildComboUrl() const;
|
||||
};
|
||||
|
||||
#endif // EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#include "edhrec_top_cards_api_response_display_widget.h"
|
||||
|
||||
#include "../../api_response/top_cards/edhrec_top_cards_api_response.h"
|
||||
#include "../cards/edhrec_api_response_card_list_display_widget.h"
|
||||
|
||||
EdhrecTopCardsApiResponseDisplayWidget::EdhrecTopCardsApiResponseDisplayWidget(QWidget *parent,
|
||||
EdhrecTopCardsApiResponse response)
|
||||
: QWidget(parent)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
cardDisplayLayout = new QVBoxLayout(this);
|
||||
|
||||
// Add card list widgets
|
||||
auto edhrec_commander_api_response_card_lists = response.container.getCardlists();
|
||||
for (const EdhrecApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
|
||||
auto cardListDisplayWidget = new EdhrecApiResponseCardListDisplayWidget(this, card_list);
|
||||
cardDisplayLayout->addWidget(cardListDisplayWidget);
|
||||
}
|
||||
|
||||
// Create a QScrollArea to hold the card display widgets
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
// Set the cardDisplayLayout inside the scroll area
|
||||
auto scrollWidget = new QWidget(scrollArea);
|
||||
scrollWidget->setLayout(cardDisplayLayout);
|
||||
scrollArea->setWidget(scrollWidget);
|
||||
|
||||
layout->addWidget(scrollArea);
|
||||
}
|
||||
|
||||
void EdhrecTopCardsApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
if (scrollArea && scrollArea->widget()) {
|
||||
scrollArea->widget()->resize(event->size());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @file edhrec_top_cards_api_response_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_TOP_CARDS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
#define EDHREC_TOP_CARDS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../api_response/top_cards/edhrec_top_cards_api_response.h"
|
||||
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecTopCardsApiResponseDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EdhrecTopCardsApiResponseDisplayWidget(QWidget *parent, EdhrecTopCardsApiResponse response);
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QVBoxLayout *cardDisplayLayout;
|
||||
QScrollArea *scrollArea;
|
||||
};
|
||||
|
||||
#endif // EDHREC_TOP_CARDS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#include "edhrec_top_commanders_api_response_display_widget.h"
|
||||
|
||||
#include "../../api_response/top_commanders/edhrec_top_commanders_api_response.h"
|
||||
#include "../cards/edhrec_api_response_card_list_display_widget.h"
|
||||
|
||||
EdhrecTopCommandersApiResponseDisplayWidget::EdhrecTopCommandersApiResponseDisplayWidget(
|
||||
QWidget *parent,
|
||||
EdhrecTopCommandersApiResponse response)
|
||||
: QWidget(parent)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
cardDisplayLayout = new QVBoxLayout(this);
|
||||
|
||||
// Add card list widgets
|
||||
auto edhrec_commander_api_response_card_lists = response.container.getCardlists();
|
||||
for (const EdhrecApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
|
||||
auto cardListDisplayWidget = new EdhrecApiResponseCardListDisplayWidget(this, card_list);
|
||||
cardDisplayLayout->addWidget(cardListDisplayWidget);
|
||||
}
|
||||
|
||||
// Create a QScrollArea to hold the card display widgets
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
// Set the cardDisplayLayout inside the scroll area
|
||||
auto scrollWidget = new QWidget(scrollArea);
|
||||
scrollWidget->setLayout(cardDisplayLayout);
|
||||
scrollArea->setWidget(scrollWidget);
|
||||
|
||||
layout->addWidget(scrollArea);
|
||||
}
|
||||
|
||||
void EdhrecTopCommandersApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
qDebug() << event->size();
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
if (scrollArea && scrollArea->widget()) {
|
||||
scrollArea->widget()->resize(event->size());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @file edhrec_top_commanders_api_response_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_TOP_COMMANDERS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
#define EDHREC_TOP_COMMANDERS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../api_response/top_commanders/edhrec_top_commanders_api_response.h"
|
||||
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecTopCommandersApiResponseDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EdhrecTopCommandersApiResponseDisplayWidget(QWidget *parent, EdhrecTopCommandersApiResponse response);
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QVBoxLayout *cardDisplayLayout;
|
||||
QScrollArea *scrollArea;
|
||||
};
|
||||
|
||||
#endif // EDHREC_TOP_COMMANDERS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#include "edhrec_top_tags_api_response_display_widget.h"
|
||||
|
||||
#include "../../api_response/top_tags/edhrec_top_tags_api_response.h"
|
||||
#include "../cards/edhrec_api_response_card_list_display_widget.h"
|
||||
|
||||
EdhrecTopTagsApiResponseDisplayWidget::EdhrecTopTagsApiResponseDisplayWidget(QWidget *parent,
|
||||
EdhrecTopTagsApiResponse response)
|
||||
: QWidget(parent)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
cardDisplayLayout = new QVBoxLayout(this);
|
||||
|
||||
// Add card list widgets
|
||||
auto edhrec_commander_api_response_card_lists = response.container.getCardlists();
|
||||
for (const EdhrecApiResponseCardList &card_list : edhrec_commander_api_response_card_lists) {
|
||||
auto cardListDisplayWidget = new EdhrecApiResponseCardListDisplayWidget(this, card_list);
|
||||
cardDisplayLayout->addWidget(cardListDisplayWidget);
|
||||
}
|
||||
|
||||
// Create a QScrollArea to hold the card display widgets
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
// Set the cardDisplayLayout inside the scroll area
|
||||
auto scrollWidget = new QWidget(scrollArea);
|
||||
scrollWidget->setLayout(cardDisplayLayout);
|
||||
scrollArea->setWidget(scrollWidget);
|
||||
|
||||
layout->addWidget(scrollArea);
|
||||
}
|
||||
|
||||
void EdhrecTopTagsApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
if (scrollArea && scrollArea->widget()) {
|
||||
scrollArea->widget()->resize(event->size());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @file edhrec_top_tags_api_response_display_widget.h
|
||||
* @ingroup ApiResponseDisplayWidgets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef EDHREC_TOP_TAGS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
#define EDHREC_TOP_TAGS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../api_response/top_tags/edhrec_top_tags_api_response.h"
|
||||
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class EdhrecTopTagsApiResponseDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EdhrecTopTagsApiResponseDisplayWidget(QWidget *parent, EdhrecTopTagsApiResponse response);
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QVBoxLayout *cardDisplayLayout;
|
||||
QScrollArea *scrollArea;
|
||||
};
|
||||
|
||||
#endif // EDHREC_TOP_TAGS_API_RESPONSE_DISPLAY_WIDGET_H
|
||||
112
cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
Normal file
112
cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#include "tab_edhrec.h"
|
||||
|
||||
#include "api_response/commander/edhrec_commander_api_response.h"
|
||||
#include "display/commander/edhrec_commander_api_response_display_widget.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QRegularExpression>
|
||||
#include <QResizeEvent>
|
||||
|
||||
TabEdhRec::TabEdhRec(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||
{
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
networkManager->setTransferTimeout(); // Use Qt's default timeout
|
||||
#endif
|
||||
|
||||
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
|
||||
connect(networkManager, &QNetworkAccessManager::finished, this, &TabEdhRec::processApiJson);
|
||||
}
|
||||
|
||||
void TabEdhRec::retranslateUi()
|
||||
{
|
||||
}
|
||||
|
||||
void TabEdhRec::setCard(CardInfoPtr _cardToQuery, bool isCommander)
|
||||
{
|
||||
cardToQuery = _cardToQuery;
|
||||
|
||||
if (!cardToQuery) {
|
||||
qDebug() << "Invalid card information provided.";
|
||||
return;
|
||||
}
|
||||
|
||||
QString cardName = cardToQuery->getName();
|
||||
QString formattedName = cardName.toLower().replace(" ", "-").remove(QRegularExpression("[^a-z0-9\\-]"));
|
||||
|
||||
QString url;
|
||||
if (isCommander) {
|
||||
url = QString("https://json.edhrec.com/pages/commanders/%1.json").arg(formattedName);
|
||||
} else {
|
||||
url = QString("https://json.edhrec.com/pages/card/%1.json").arg(formattedName);
|
||||
}
|
||||
|
||||
QNetworkRequest request{QUrl(url)};
|
||||
|
||||
networkManager->get(request);
|
||||
}
|
||||
|
||||
void TabEdhRec::processApiJson(QNetworkReply *reply)
|
||||
{
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qDebug() << "Network error occurred:" << reply->errorString();
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
|
||||
|
||||
if (!jsonDoc.isObject()) {
|
||||
qDebug() << "Invalid JSON response received.";
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject jsonObj = jsonDoc.object();
|
||||
|
||||
// qDebug() << jsonObj;
|
||||
|
||||
EdhrecCommanderApiResponse deckData;
|
||||
deckData.fromJson(jsonObj);
|
||||
|
||||
displayWidget = new EdhrecCommanderApiResponseDisplayWidget(this, deckData, reply->url().toString());
|
||||
// flowWidget->addWidget(displayWidget);
|
||||
setCentralWidget(displayWidget);
|
||||
|
||||
reply->deleteLater();
|
||||
update();
|
||||
}
|
||||
|
||||
void TabEdhRec::prettyPrintJson(const QJsonValue &value, int indentLevel)
|
||||
{
|
||||
const QString indent(indentLevel * 2, ' '); // Adjust spacing as needed for pretty printing
|
||||
|
||||
if (value.isObject()) {
|
||||
QJsonObject obj = value.toObject();
|
||||
for (auto it = obj.begin(); it != obj.end(); ++it) {
|
||||
qDebug().noquote() << indent + it.key() + ":";
|
||||
prettyPrintJson(it.value(), indentLevel + 1);
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
QJsonArray array = value.toArray();
|
||||
for (int i = 0; i < array.size(); ++i) {
|
||||
qDebug().noquote() << indent + QString("[%1]:").arg(i);
|
||||
prettyPrintJson(array[i], indentLevel + 1);
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
qDebug().noquote() << indent + "\"" + value.toString() + "\"";
|
||||
} else if (value.isDouble()) {
|
||||
qDebug().noquote() << indent + QString::number(value.toDouble());
|
||||
} else if (value.isBool()) {
|
||||
qDebug().noquote() << indent + (value.toBool() ? "true" : "false");
|
||||
} else if (value.isNull()) {
|
||||
qDebug().noquote() << indent + "null";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* @file tab_edhrec.h
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_EDHREC_H
|
||||
#define TAB_EDHREC_H
|
||||
|
||||
#include "../../tab.h"
|
||||
#include "display/commander/edhrec_commander_api_response_display_widget.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <libcockatrice/card/card_info.h>
|
||||
|
||||
class TabEdhRec : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TabEdhRec(TabSupervisor *_tabSupervisor);
|
||||
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
auto cardName = cardToQuery.isNull() ? QString() : cardToQuery->getName();
|
||||
return tr("EDHREC: ") + cardName;
|
||||
}
|
||||
|
||||
QNetworkAccessManager *networkManager;
|
||||
|
||||
public slots:
|
||||
void processApiJson(QNetworkReply *reply);
|
||||
void prettyPrintJson(const QJsonValue &value, int indentLevel);
|
||||
void setCard(CardInfoPtr _cardToQuery, bool isCommander = false);
|
||||
|
||||
private:
|
||||
CardInfoPtr cardToQuery;
|
||||
EdhrecCommanderApiResponseDisplayWidget *displayWidget;
|
||||
};
|
||||
|
||||
#endif // TAB_EDHREC_H
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
#include "tab_edhrec_main.h"
|
||||
|
||||
#include "../../tab_supervisor.h"
|
||||
#include "api_response/average_deck/edhrec_average_deck_api_response.h"
|
||||
#include "api_response/commander/edhrec_commander_api_response.h"
|
||||
#include "api_response/top_cards/edhrec_top_cards_api_response.h"
|
||||
#include "api_response/top_commanders/edhrec_top_commanders_api_response.h"
|
||||
#include "api_response/top_tags/edhrec_top_tags_api_response.h"
|
||||
#include "display/commander/edhrec_commander_api_response_display_widget.h"
|
||||
#include "display/top_cards/edhrec_top_cards_api_response_display_widget.h"
|
||||
#include "display/top_commander/edhrec_top_commanders_api_response_display_widget.h"
|
||||
#include "display/top_tags/edhrec_top_tags_api_response_display_widget.h"
|
||||
|
||||
#include <QCompleter>
|
||||
#include <QDebug>
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QResizeEvent>
|
||||
#include <libcockatrice/card/card_database/card_database_manager.h>
|
||||
#include <libcockatrice/card/card_database/model/card/card_completer_proxy_model.h>
|
||||
#include <libcockatrice/card/card_database/model/card/card_search_model.h>
|
||||
|
||||
static bool canBeCommander(const CardInfoPtr &cardInfo)
|
||||
{
|
||||
return ((cardInfo->getCardType().contains("Legendary", Qt::CaseInsensitive) &&
|
||||
cardInfo->getCardType().contains("Creature", Qt::CaseInsensitive))) ||
|
||||
cardInfo->getText().contains("can be your commander", Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
TabEdhRecMain::TabEdhRecMain(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||
{
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
networkManager->setTransferTimeout(); // Use Qt's default timeout
|
||||
#endif
|
||||
|
||||
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
|
||||
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *)));
|
||||
|
||||
container = new QWidget(this);
|
||||
mainLayout = new QVBoxLayout(container);
|
||||
container->setLayout(mainLayout);
|
||||
|
||||
navigationContainer = new QWidget(container);
|
||||
navigationContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
navigationLayout = new QHBoxLayout(navigationContainer);
|
||||
navigationLayout->setSpacing(5);
|
||||
navigationContainer->setLayout(navigationLayout);
|
||||
|
||||
cardsPushButton = new QPushButton(navigationContainer);
|
||||
connect(cardsPushButton, &QPushButton::clicked, this, &TabEdhRecMain::getTopCards);
|
||||
topCommandersPushButton = new QPushButton(navigationContainer);
|
||||
connect(topCommandersPushButton, &QPushButton::clicked, this, &TabEdhRecMain::getTopCommanders);
|
||||
tagsPushButton = new QPushButton(navigationContainer);
|
||||
connect(tagsPushButton, &QPushButton::clicked, this, &TabEdhRecMain::getTopTags);
|
||||
|
||||
searchBar = new QLineEdit(this);
|
||||
auto cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
|
||||
auto displayModel = new CardDatabaseDisplayModel(this);
|
||||
displayModel->setSourceModel(cardDatabaseModel);
|
||||
CardSearchModel *searchModel = new CardSearchModel(displayModel, this);
|
||||
|
||||
CardCompleterProxyModel *proxyModel = new CardCompleterProxyModel(this);
|
||||
proxyModel->setSourceModel(searchModel);
|
||||
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
proxyModel->setFilterRole(Qt::DisplayRole);
|
||||
|
||||
QCompleter *completer = new QCompleter(proxyModel, this);
|
||||
completer->setCompletionRole(Qt::DisplayRole);
|
||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setFilterMode(Qt::MatchContains);
|
||||
completer->setMaxVisibleItems(10);
|
||||
searchBar->setCompleter(completer);
|
||||
|
||||
// Update suggestions dynamically
|
||||
connect(searchBar, &QLineEdit::textChanged, searchModel, &CardSearchModel::updateSearchResults);
|
||||
connect(searchBar, &QLineEdit::textChanged, this, [=](const QString &text) {
|
||||
// Ensure substring matching
|
||||
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
|
||||
proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
completer->complete(); // Force the dropdown to appear
|
||||
}
|
||||
});
|
||||
|
||||
searchPushButton = new QPushButton(navigationContainer);
|
||||
connect(searchPushButton, &QPushButton::clicked, this, [=, this]() { doSearch(); });
|
||||
|
||||
settingsButton = new SettingsButtonWidget(this);
|
||||
|
||||
cardSizeSlider = new CardSizeWidget(this);
|
||||
|
||||
settingsButton->addSettingsWidget(cardSizeSlider);
|
||||
|
||||
navigationLayout->addWidget(cardsPushButton);
|
||||
navigationLayout->addWidget(topCommandersPushButton);
|
||||
navigationLayout->addWidget(tagsPushButton);
|
||||
navigationLayout->addWidget(searchBar);
|
||||
navigationLayout->addWidget(searchPushButton);
|
||||
navigationLayout->addWidget(settingsButton);
|
||||
|
||||
currentPageDisplay = new QWidget(container);
|
||||
currentPageLayout = new QVBoxLayout(currentPageDisplay);
|
||||
currentPageDisplay->setLayout(currentPageLayout);
|
||||
|
||||
mainLayout->addWidget(navigationContainer);
|
||||
mainLayout->addWidget(currentPageDisplay);
|
||||
|
||||
// Ensure navigation stays at the top and currentPageDisplay takes remaining space
|
||||
mainLayout->setStretch(0, 0); // navigationContainer gets minimum space
|
||||
mainLayout->setStretch(1, 1); // currentPageDisplay expands as much as possible
|
||||
|
||||
setCentralWidget(container);
|
||||
|
||||
TabEdhRecMain::retranslateUi();
|
||||
|
||||
getTopCards();
|
||||
}
|
||||
|
||||
void TabEdhRecMain::retranslateUi()
|
||||
{
|
||||
cardsPushButton->setText(tr("&Cards"));
|
||||
topCommandersPushButton->setText(tr("Top Commanders"));
|
||||
tagsPushButton->setText(tr("Tags"));
|
||||
searchBar->setPlaceholderText(tr("Search for a card ..."));
|
||||
searchPushButton->setText(tr("Search"));
|
||||
}
|
||||
|
||||
void TabEdhRecMain::doSearch()
|
||||
{
|
||||
CardInfoPtr searchedCard = CardDatabaseManager::query()->getCardInfo(searchBar->text());
|
||||
if (!searchedCard) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCard(searchedCard, canBeCommander(searchedCard));
|
||||
}
|
||||
|
||||
void TabEdhRecMain::setCard(CardInfoPtr _cardToQuery, bool isCommander)
|
||||
{
|
||||
cardToQuery = _cardToQuery;
|
||||
|
||||
if (!cardToQuery) {
|
||||
qDebug() << "Invalid card information provided.";
|
||||
return;
|
||||
}
|
||||
|
||||
QString cardName = cardToQuery->getName();
|
||||
QString formattedName = cardName.toLower().replace(" ", "-").remove(QRegularExpression("[^a-z0-9\\-]"));
|
||||
|
||||
QString url;
|
||||
if (isCommander) {
|
||||
url = QString("https://json.edhrec.com/pages/commanders/%1.json").arg(formattedName);
|
||||
} else {
|
||||
url = QString("https://json.edhrec.com/pages/cards/%1.json").arg(formattedName);
|
||||
}
|
||||
|
||||
QNetworkRequest request{QUrl(url)};
|
||||
|
||||
networkManager->get(request);
|
||||
}
|
||||
|
||||
void TabEdhRecMain::actNavigatePage(QString url)
|
||||
{
|
||||
QNetworkRequest request{QUrl("https://json.edhrec.com/pages" + url + ".json")};
|
||||
|
||||
networkManager->get(request);
|
||||
}
|
||||
|
||||
void TabEdhRecMain::getTopCards()
|
||||
{
|
||||
QNetworkRequest request{QUrl("https://json.edhrec.com/pages/top/year.json")};
|
||||
|
||||
networkManager->get(request);
|
||||
}
|
||||
|
||||
void TabEdhRecMain::getTopCommanders()
|
||||
{
|
||||
QNetworkRequest request{QUrl("https://json.edhrec.com/pages/commanders/year.json")};
|
||||
|
||||
networkManager->get(request);
|
||||
}
|
||||
|
||||
void TabEdhRecMain::getTopTags()
|
||||
{
|
||||
QNetworkRequest request{QUrl("https://json.edhrec.com/pages/tags.json")};
|
||||
|
||||
networkManager->get(request);
|
||||
}
|
||||
|
||||
void TabEdhRecMain::processApiJson(QNetworkReply *reply)
|
||||
{
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qDebug() << "Network error occurred:" << reply->errorString();
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
|
||||
|
||||
if (!jsonDoc.isObject()) {
|
||||
qDebug() << "Invalid JSON response received.";
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject jsonObj = jsonDoc.object();
|
||||
|
||||
// Get the actual URL from the reply
|
||||
QString responseUrl = reply->url().toString();
|
||||
|
||||
// Check if the response URL matches a commander request
|
||||
if (responseUrl.startsWith("https://json.edhrec.com/pages/commanders/year.json")) {
|
||||
processTopCommandersResponse(jsonObj);
|
||||
} else if (responseUrl.startsWith("https://json.edhrec.com/pages/commanders/")) {
|
||||
qInfo() << "Received top kek";
|
||||
processCommanderResponse(jsonObj, responseUrl);
|
||||
} else if (responseUrl.startsWith("https://json.edhrec.com/pages/cards/")) {
|
||||
processCommanderResponse(jsonObj);
|
||||
} else if (responseUrl.startsWith("https://json.edhrec.com/pages/tags/")) {
|
||||
processCommanderResponse(jsonObj);
|
||||
} else if (responseUrl.startsWith("https://json.edhrec.com/pages/tags.json")) {
|
||||
processTopTagsResponse(jsonObj);
|
||||
} else if (responseUrl.startsWith("https://json.edhrec.com/pages/top/year.json")) {
|
||||
processTopCardsResponse(jsonObj);
|
||||
} else if (responseUrl.startsWith("https://json.edhrec.com/pages/combos/")) {
|
||||
qInfo() << "Received combos";
|
||||
processCommanderResponse(jsonObj);
|
||||
} else if (responseUrl.startsWith("https://json.edhrec.com/pages/average-decks/")) {
|
||||
processAverageDeckResponse(jsonObj);
|
||||
} else {
|
||||
prettyPrintJson(jsonObj, 4);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void TabEdhRecMain::processTopCardsResponse(QJsonObject reply)
|
||||
{
|
||||
EdhrecTopCardsApiResponse deckData;
|
||||
deckData.fromJson(reply);
|
||||
|
||||
// **Remove previous page display to prevent stacking**
|
||||
if (currentPageDisplay) {
|
||||
mainLayout->removeWidget(currentPageDisplay);
|
||||
delete currentPageDisplay;
|
||||
currentPageDisplay = nullptr;
|
||||
}
|
||||
|
||||
// **Create new currentPageDisplay**
|
||||
currentPageDisplay = new QWidget(container);
|
||||
currentPageLayout = new QVBoxLayout(currentPageDisplay);
|
||||
currentPageDisplay->setLayout(currentPageLayout);
|
||||
|
||||
auto display = new EdhrecTopCardsApiResponseDisplayWidget(currentPageDisplay, deckData);
|
||||
currentPageLayout->addWidget(display);
|
||||
|
||||
mainLayout->addWidget(currentPageDisplay);
|
||||
|
||||
// **Ensure layout stays correct**
|
||||
mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
|
||||
mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
|
||||
}
|
||||
|
||||
void TabEdhRecMain::processTopTagsResponse(QJsonObject reply)
|
||||
{
|
||||
EdhrecTopTagsApiResponse deckData;
|
||||
deckData.fromJson(reply);
|
||||
|
||||
// **Remove previous page display to prevent stacking**
|
||||
if (currentPageDisplay) {
|
||||
mainLayout->removeWidget(currentPageDisplay);
|
||||
delete currentPageDisplay;
|
||||
currentPageDisplay = nullptr;
|
||||
}
|
||||
|
||||
// **Create new currentPageDisplay**
|
||||
currentPageDisplay = new QWidget(container);
|
||||
currentPageLayout = new QVBoxLayout(currentPageDisplay);
|
||||
currentPageDisplay->setLayout(currentPageLayout);
|
||||
|
||||
auto display = new EdhrecTopTagsApiResponseDisplayWidget(currentPageDisplay, deckData);
|
||||
currentPageLayout->addWidget(display);
|
||||
|
||||
mainLayout->addWidget(currentPageDisplay);
|
||||
|
||||
// **Ensure layout stays correct**
|
||||
mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
|
||||
mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
|
||||
}
|
||||
|
||||
void TabEdhRecMain::processTopCommandersResponse(QJsonObject reply)
|
||||
{
|
||||
EdhrecTopCommandersApiResponse deckData;
|
||||
deckData.fromJson(reply);
|
||||
|
||||
// **Remove previous page display to prevent stacking**
|
||||
if (currentPageDisplay) {
|
||||
mainLayout->removeWidget(currentPageDisplay);
|
||||
delete currentPageDisplay;
|
||||
currentPageDisplay = nullptr;
|
||||
}
|
||||
|
||||
// **Create new currentPageDisplay**
|
||||
currentPageDisplay = new QWidget(container);
|
||||
currentPageLayout = new QVBoxLayout(currentPageDisplay);
|
||||
currentPageDisplay->setLayout(currentPageLayout);
|
||||
|
||||
auto display = new EdhrecTopCommandersApiResponseDisplayWidget(currentPageDisplay, deckData);
|
||||
currentPageLayout->addWidget(display);
|
||||
|
||||
mainLayout->addWidget(currentPageDisplay);
|
||||
|
||||
// **Ensure layout stays correct**
|
||||
mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
|
||||
mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
|
||||
}
|
||||
|
||||
void TabEdhRecMain::processCommanderResponse(QJsonObject reply, QString responseUrl)
|
||||
{
|
||||
EdhrecCommanderApiResponse deckData;
|
||||
deckData.fromJson(reply);
|
||||
|
||||
// **Remove previous page display to prevent stacking**
|
||||
if (currentPageDisplay) {
|
||||
mainLayout->removeWidget(currentPageDisplay);
|
||||
delete currentPageDisplay;
|
||||
currentPageDisplay = nullptr;
|
||||
}
|
||||
|
||||
// **Create new currentPageDisplay**
|
||||
currentPageDisplay = new QWidget(container);
|
||||
currentPageLayout = new QVBoxLayout(currentPageDisplay);
|
||||
currentPageDisplay->setLayout(currentPageLayout);
|
||||
|
||||
auto display = new EdhrecCommanderApiResponseDisplayWidget(currentPageDisplay, deckData, responseUrl);
|
||||
currentPageLayout->addWidget(display);
|
||||
|
||||
mainLayout->addWidget(currentPageDisplay);
|
||||
|
||||
// **Ensure layout stays correct**
|
||||
mainLayout->setStretch(0, 0); // Keep navigationContainer at the top
|
||||
mainLayout->setStretch(1, 1); // Make sure currentPageDisplay takes remaining space
|
||||
}
|
||||
|
||||
void TabEdhRecMain::processAverageDeckResponse(QJsonObject reply)
|
||||
{
|
||||
EdhrecAverageDeckApiResponse deckData;
|
||||
deckData.fromJson(reply);
|
||||
tabSupervisor->openDeckInNewTab(deckData.deck.deckLoader);
|
||||
}
|
||||
|
||||
void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel)
|
||||
{
|
||||
const QString indent(indentLevel * 2, ' '); // Adjust spacing as needed for pretty printing
|
||||
|
||||
if (value.isObject()) {
|
||||
QJsonObject obj = value.toObject();
|
||||
for (auto it = obj.begin(); it != obj.end(); ++it) {
|
||||
qDebug().noquote() << indent + it.key() + ":";
|
||||
prettyPrintJson(it.value(), indentLevel + 1);
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
QJsonArray array = value.toArray();
|
||||
for (int i = 0; i < array.size(); ++i) {
|
||||
qDebug().noquote() << indent + QString("[%1]:").arg(i);
|
||||
prettyPrintJson(array[i], indentLevel + 1);
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
qDebug().noquote() << indent + "\"" + value.toString() + "\"";
|
||||
} else if (value.isDouble()) {
|
||||
qDebug().noquote() << indent + QString::number(value.toDouble());
|
||||
} else if (value.isBool()) {
|
||||
qDebug().noquote() << indent + (value.toBool() ? "true" : "false");
|
||||
} else if (value.isNull()) {
|
||||
qDebug().noquote() << indent + "null";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @file tab_edhrec_main.h
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_EDHREC_MAIN_H
|
||||
#define TAB_EDHREC_MAIN_H
|
||||
|
||||
#include "../../interface/widgets/cards/card_size_widget.h"
|
||||
#include "../../interface/widgets/general/layout_containers/flow_widget.h"
|
||||
#include "../../interface/widgets/quick_settings/settings_button_widget.h"
|
||||
#include "../../tab.h"
|
||||
#include "display/commander/edhrec_commander_api_response_display_widget.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QPushButton>
|
||||
#include <libcockatrice/card/card_database/card_database.h>
|
||||
|
||||
class TabEdhRecMain : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TabEdhRecMain(TabSupervisor *_tabSupervisor);
|
||||
|
||||
void retranslateUi() override;
|
||||
void doSearch();
|
||||
QString getTabText() const override
|
||||
{
|
||||
auto cardName = cardToQuery.isNull() ? QString() : cardToQuery->getName();
|
||||
return tr("EDHRec: ") + cardName;
|
||||
}
|
||||
|
||||
CardSizeWidget *getCardSizeSlider()
|
||||
{
|
||||
return cardSizeSlider;
|
||||
}
|
||||
|
||||
QNetworkAccessManager *networkManager;
|
||||
|
||||
public slots:
|
||||
void processApiJson(QNetworkReply *reply);
|
||||
void processCommanderResponse(QJsonObject reply, QString responseUrl = "");
|
||||
void processTopCardsResponse(QJsonObject reply);
|
||||
void processTopTagsResponse(QJsonObject reply);
|
||||
void processTopCommandersResponse(QJsonObject reply);
|
||||
void processAverageDeckResponse(QJsonObject reply);
|
||||
void prettyPrintJson(const QJsonValue &value, int indentLevel);
|
||||
void setCard(CardInfoPtr _cardToQuery, bool isCommander = false);
|
||||
void actNavigatePage(QString url);
|
||||
void getTopCards();
|
||||
void getTopCommanders();
|
||||
void getTopTags();
|
||||
|
||||
private:
|
||||
QWidget *container;
|
||||
QWidget *navigationContainer;
|
||||
QWidget *currentPageDisplay;
|
||||
QVBoxLayout *mainLayout;
|
||||
QHBoxLayout *navigationLayout;
|
||||
QVBoxLayout *currentPageLayout;
|
||||
QPushButton *cardsPushButton;
|
||||
QPushButton *topCommandersPushButton;
|
||||
QPushButton *tagsPushButton;
|
||||
QLineEdit *searchBar;
|
||||
QPushButton *searchPushButton;
|
||||
SettingsButtonWidget *settingsButton;
|
||||
CardSizeWidget *cardSizeSlider;
|
||||
CardInfoPtr cardToQuery;
|
||||
EdhrecCommanderApiResponseDisplayWidget *displayWidget;
|
||||
};
|
||||
|
||||
#endif // TAB_EDHREC_MAIN_H
|
||||
49
cockatrice/src/interface/widgets/tabs/tab.cpp
Normal file
49
cockatrice/src/interface/widgets/tabs/tab.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include "tab.h"
|
||||
|
||||
#include "../interface/widgets/cards/card_info_display_widget.h"
|
||||
#include "tab_supervisor.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QDebug>
|
||||
#include <QScreen>
|
||||
|
||||
Tab::Tab(TabSupervisor *_tabSupervisor)
|
||||
: QMainWindow(_tabSupervisor), tabSupervisor(_tabSupervisor), contentsChanged(false), infoPopup(0)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
void Tab::showCardInfoPopup(const QPoint &pos, const CardRef &cardRef)
|
||||
{
|
||||
if (infoPopup) {
|
||||
infoPopup->deleteLater();
|
||||
}
|
||||
currentCard = cardRef;
|
||||
infoPopup = new CardInfoDisplayWidget(currentCard, nullptr,
|
||||
Qt::Widget | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint |
|
||||
Qt::WindowStaysOnTopHint);
|
||||
infoPopup->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
auto screenRect = qApp->primaryScreen()->geometry();
|
||||
infoPopup->move(qMax(screenRect.left(), qMin(pos.x() - infoPopup->width() / 2,
|
||||
screenRect.left() + screenRect.width() - infoPopup->width())),
|
||||
qMax(screenRect.top(), qMin(pos.y() - infoPopup->height() / 2,
|
||||
screenRect.top() + screenRect.height() - infoPopup->height())));
|
||||
infoPopup->show();
|
||||
}
|
||||
|
||||
void Tab::deleteCardInfoPopup(const QString &cardName)
|
||||
{
|
||||
if (infoPopup) {
|
||||
if (currentCard.name == cardName || cardName == "_") {
|
||||
infoPopup->deleteLater();
|
||||
infoPopup = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Tab::closeRequest()
|
||||
{
|
||||
return close();
|
||||
}
|
||||
75
cockatrice/src/interface/widgets/tabs/tab.h
Normal file
75
cockatrice/src/interface/widgets/tabs/tab.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @file tab.h
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_H
|
||||
#define TAB_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
|
||||
class QMenu;
|
||||
class TabSupervisor;
|
||||
class CardInfoDisplayWidget;
|
||||
|
||||
class Tab : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void userEvent(bool globalEvent = true);
|
||||
void tabTextChanged(Tab *tab, const QString &newTabText);
|
||||
|
||||
protected:
|
||||
TabSupervisor *tabSupervisor;
|
||||
void addTabMenu(QMenu *menu)
|
||||
{
|
||||
tabMenus.append(menu);
|
||||
}
|
||||
protected slots:
|
||||
void showCardInfoPopup(const QPoint &pos, const CardRef &cardRef);
|
||||
void deleteCardInfoPopup(const QString &cardName);
|
||||
|
||||
private:
|
||||
CardRef currentCard;
|
||||
bool contentsChanged;
|
||||
CardInfoDisplayWidget *infoPopup;
|
||||
QList<QMenu *> tabMenus;
|
||||
|
||||
public:
|
||||
explicit Tab(TabSupervisor *_tabSupervisor);
|
||||
const QList<QMenu *> &getTabMenus() const
|
||||
{
|
||||
return tabMenus;
|
||||
}
|
||||
TabSupervisor *getTabSupervisor() const
|
||||
{
|
||||
return tabSupervisor;
|
||||
}
|
||||
bool getContentsChanged() const
|
||||
{
|
||||
return contentsChanged;
|
||||
}
|
||||
void setContentsChanged(bool _contentsChanged)
|
||||
{
|
||||
contentsChanged = _contentsChanged;
|
||||
}
|
||||
virtual QString getTabText() const = 0;
|
||||
virtual void retranslateUi() = 0;
|
||||
|
||||
/**
|
||||
* Nicely asks to close the tab.
|
||||
* Override this method to do checks or ask for confirmation before closing the tab.
|
||||
* If you need to force close the tab, just call close() instead.
|
||||
*
|
||||
* @return True if the tab is successfully closed.
|
||||
*/
|
||||
virtual bool closeRequest();
|
||||
|
||||
virtual void tabActivated()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
239
cockatrice/src/interface/widgets/tabs/tab_account.cpp
Normal file
239
cockatrice/src/interface/widgets/tabs/tab_account.cpp
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
#include "tab_account.h"
|
||||
|
||||
#include "../client/sound_engine.h"
|
||||
#include "../interface/widgets/server/user/user_info_box.h"
|
||||
#include "../interface/widgets/server/user/user_list_manager.h"
|
||||
#include "../interface/widgets/server/user/user_list_widget.h"
|
||||
#include "../interface/widgets/utility/custom_line_edit.h"
|
||||
#include "tab_supervisor.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/event_add_to_list.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_remove_from_list.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_user_joined.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_user_left.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_list_users.pb.h>
|
||||
#include <libcockatrice/protocol/pb/session_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
|
||||
TabAccount::TabAccount(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User &userInfo)
|
||||
: Tab(_tabSupervisor), client(_client)
|
||||
{
|
||||
allUsersList = new UserListWidget(_tabSupervisor, client, UserListWidget::AllUsersList);
|
||||
buddyList = new UserListWidget(_tabSupervisor, client, UserListWidget::BuddyList);
|
||||
ignoreList = new UserListWidget(_tabSupervisor, client, UserListWidget::IgnoreList);
|
||||
userInfoBox = new UserInfoBox(client, true);
|
||||
userInfoBox->updateInfo(userInfo);
|
||||
|
||||
connect(allUsersList, &UserListWidget::openMessageDialog, this, &TabAccount::openMessageDialog);
|
||||
connect(buddyList, &UserListWidget::openMessageDialog, this, &TabAccount::openMessageDialog);
|
||||
connect(ignoreList, &UserListWidget::openMessageDialog, this, &TabAccount::openMessageDialog);
|
||||
|
||||
connect(client, &AbstractClient::userJoinedEventReceived, this, &TabAccount::processUserJoinedEvent);
|
||||
connect(client, &AbstractClient::userLeftEventReceived, this, &TabAccount::processUserLeftEvent);
|
||||
connect(client, &AbstractClient::buddyListReceived, this, &TabAccount::buddyListReceived);
|
||||
connect(client, &AbstractClient::ignoreListReceived, this, &TabAccount::ignoreListReceived);
|
||||
connect(client, &AbstractClient::addToListEventReceived, this, &TabAccount::processAddToListEvent);
|
||||
connect(client, &AbstractClient::removeFromListEventReceived, this, &TabAccount::processRemoveFromListEvent);
|
||||
|
||||
// Attempt to populate the tab with the cache
|
||||
buddyListReceived(tabSupervisor->getUserListManager()->getBuddyList().values());
|
||||
ignoreListReceived(tabSupervisor->getUserListManager()->getIgnoreList().values());
|
||||
|
||||
PendingCommand *pend = AbstractClient::prepareSessionCommand(Command_ListUsers());
|
||||
connect(pend, &PendingCommand::finished, this, &TabAccount::processListUsersResponse);
|
||||
client->sendCommand(pend);
|
||||
|
||||
auto *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(userInfoBox);
|
||||
vbox->addWidget(allUsersList);
|
||||
|
||||
auto *addToBuddyList = new QHBoxLayout;
|
||||
addBuddyEdit = new LineEditUnfocusable;
|
||||
addBuddyEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
addBuddyEdit->setPlaceholderText(tr("Add to Buddy List"));
|
||||
connect(addBuddyEdit, &LineEditUnfocusable::returnPressed, this, &TabAccount::addToBuddyList);
|
||||
auto *addBuddyButton = new QPushButton("Add");
|
||||
connect(addBuddyButton, &QPushButton::clicked, this, &TabAccount::addToBuddyList);
|
||||
addToBuddyList->addWidget(addBuddyEdit);
|
||||
addToBuddyList->addWidget(addBuddyButton);
|
||||
|
||||
auto *addToIgnoreList = new QHBoxLayout;
|
||||
addIgnoreEdit = new LineEditUnfocusable;
|
||||
addIgnoreEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
addIgnoreEdit->setPlaceholderText(tr("Add to Ignore List"));
|
||||
connect(addIgnoreEdit, &LineEditUnfocusable::returnPressed, this, &TabAccount::addToIgnoreList);
|
||||
auto *addIgnoreButton = new QPushButton("Add");
|
||||
connect(addIgnoreButton, &QPushButton::clicked, this, &TabAccount::addToIgnoreList);
|
||||
addToIgnoreList->addWidget(addIgnoreEdit);
|
||||
addToIgnoreList->addWidget(addIgnoreButton);
|
||||
|
||||
auto *buddyPanel = new QVBoxLayout;
|
||||
buddyPanel->addWidget(buddyList);
|
||||
buddyPanel->addLayout(addToBuddyList);
|
||||
|
||||
auto *ignorePanel = new QVBoxLayout;
|
||||
ignorePanel->addWidget(ignoreList);
|
||||
ignorePanel->addLayout(addToIgnoreList);
|
||||
|
||||
auto *mainLayout = new QHBoxLayout;
|
||||
mainLayout->addLayout(buddyPanel);
|
||||
mainLayout->addLayout(ignorePanel);
|
||||
mainLayout->addLayout(vbox);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
auto *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(mainLayout);
|
||||
setCentralWidget(mainWidget);
|
||||
}
|
||||
|
||||
void TabAccount::addToBuddyList()
|
||||
{
|
||||
const QString &userName = addBuddyEdit->text();
|
||||
if (userName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string listName = "buddy";
|
||||
addToList(listName, userName);
|
||||
addBuddyEdit->clear();
|
||||
}
|
||||
|
||||
void TabAccount::addToIgnoreList()
|
||||
{
|
||||
const QString &userName = addIgnoreEdit->text();
|
||||
if (userName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string listName = "ignore";
|
||||
addToList(listName, userName);
|
||||
addIgnoreEdit->clear();
|
||||
}
|
||||
|
||||
void TabAccount::addToList(const std::string &listName, const QString &userName)
|
||||
{
|
||||
Command_AddToList cmd;
|
||||
cmd.set_list(listName);
|
||||
cmd.set_user_name(userName.toStdString());
|
||||
|
||||
client->sendCommand(AbstractClient::prepareSessionCommand(cmd));
|
||||
}
|
||||
|
||||
void TabAccount::retranslateUi()
|
||||
{
|
||||
allUsersList->retranslateUi();
|
||||
buddyList->retranslateUi();
|
||||
ignoreList->retranslateUi();
|
||||
userInfoBox->retranslateUi();
|
||||
}
|
||||
|
||||
void TabAccount::processListUsersResponse(const Response &response)
|
||||
{
|
||||
const Response_ListUsers &resp = response.GetExtension(Response_ListUsers::ext);
|
||||
for (int i = 0; i < resp.user_list_size(); ++i) {
|
||||
const ServerInfo_User &info = resp.user_list(i);
|
||||
const QString &userName = QString::fromStdString(info.name());
|
||||
allUsersList->processUserInfo(info, true);
|
||||
ignoreList->setUserOnline(userName, true);
|
||||
buddyList->setUserOnline(userName, true);
|
||||
}
|
||||
|
||||
allUsersList->sortItems();
|
||||
ignoreList->sortItems();
|
||||
buddyList->sortItems();
|
||||
}
|
||||
|
||||
void TabAccount::processUserJoinedEvent(const Event_UserJoined &event)
|
||||
{
|
||||
const ServerInfo_User &info = event.user_info();
|
||||
const QString &userName = QString::fromStdString(info.name());
|
||||
|
||||
allUsersList->processUserInfo(info, true);
|
||||
ignoreList->setUserOnline(userName, true);
|
||||
buddyList->setUserOnline(userName, true);
|
||||
|
||||
allUsersList->sortItems();
|
||||
ignoreList->sortItems();
|
||||
buddyList->sortItems();
|
||||
|
||||
if (buddyList->getUsers().keys().contains(userName)) {
|
||||
soundEngine->playSound("buddy_join");
|
||||
}
|
||||
|
||||
emit userJoined(info);
|
||||
}
|
||||
|
||||
void TabAccount::processUserLeftEvent(const Event_UserLeft &event)
|
||||
{
|
||||
const 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);
|
||||
ignoreList->sortItems();
|
||||
buddyList->sortItems();
|
||||
|
||||
emit userLeft(userName);
|
||||
}
|
||||
}
|
||||
|
||||
void TabAccount::buddyListReceived(const QList<ServerInfo_User> &_buddyList)
|
||||
{
|
||||
for (const auto &user : _buddyList) {
|
||||
buddyList->processUserInfo(user, false);
|
||||
}
|
||||
buddyList->sortItems();
|
||||
}
|
||||
|
||||
void TabAccount::ignoreListReceived(const QList<ServerInfo_User> &_ignoreList)
|
||||
{
|
||||
for (const auto &user : _ignoreList) {
|
||||
ignoreList->processUserInfo(user, false);
|
||||
}
|
||||
ignoreList->sortItems();
|
||||
}
|
||||
|
||||
void TabAccount::processAddToListEvent(const Event_AddToList &event)
|
||||
{
|
||||
const ServerInfo_User &info = event.user_info();
|
||||
const bool online = allUsersList->getUsers().contains(QString::fromStdString(info.name()));
|
||||
const QString &list = QString::fromStdString(event.list_name());
|
||||
|
||||
UserListWidget *userList;
|
||||
if (list == "buddy") {
|
||||
userList = buddyList;
|
||||
} else if (list == "ignore") {
|
||||
userList = ignoreList;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
userList->processUserInfo(info, online);
|
||||
userList->sortItems();
|
||||
}
|
||||
|
||||
void TabAccount::processRemoveFromListEvent(const Event_RemoveFromList &event)
|
||||
{
|
||||
const auto &list = QString::fromStdString(event.list_name());
|
||||
const auto &user = QString::fromStdString(event.user_name());
|
||||
|
||||
UserListWidget *userList;
|
||||
if (list == "buddy") {
|
||||
userList = buddyList;
|
||||
} else if (list == "ignore") {
|
||||
userList = ignoreList;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
userList->deleteUser(user);
|
||||
}
|
||||
64
cockatrice/src/interface/widgets/tabs/tab_account.h
Normal file
64
cockatrice/src/interface/widgets/tabs/tab_account.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* @file tab_account.h
|
||||
* @ingroup AccountTabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_ACCOUNT_H
|
||||
#define TAB_ACCOUNT_H
|
||||
|
||||
#include "tab.h"
|
||||
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||
|
||||
class AbstractClient;
|
||||
class Event_AddToList;
|
||||
class Event_ListRooms;
|
||||
class Event_RemoveFromList;
|
||||
class Event_UserJoined;
|
||||
class Event_UserLeft;
|
||||
class LineEditUnfocusable;
|
||||
class Response;
|
||||
class ServerInfo_User;
|
||||
class UserInfoBox;
|
||||
class UserListWidget;
|
||||
|
||||
class TabAccount : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void openMessageDialog(const QString &userName, bool focus);
|
||||
void userLeft(const QString &userName);
|
||||
void userJoined(const ServerInfo_User &userInfo);
|
||||
|
||||
private slots:
|
||||
void processListUsersResponse(const Response &response);
|
||||
void processUserJoinedEvent(const Event_UserJoined &event);
|
||||
void processUserLeftEvent(const Event_UserLeft &event);
|
||||
void buddyListReceived(const QList<ServerInfo_User> &_buddyList);
|
||||
void ignoreListReceived(const QList<ServerInfo_User> &_ignoreList);
|
||||
void processAddToListEvent(const Event_AddToList &event);
|
||||
void processRemoveFromListEvent(const Event_RemoveFromList &event);
|
||||
void addToIgnoreList();
|
||||
void addToBuddyList();
|
||||
|
||||
private:
|
||||
AbstractClient *client;
|
||||
UserListWidget *allUsersList;
|
||||
UserListWidget *buddyList;
|
||||
UserListWidget *ignoreList;
|
||||
UserInfoBox *userInfoBox;
|
||||
LineEditUnfocusable *addBuddyEdit;
|
||||
LineEditUnfocusable *addIgnoreEdit;
|
||||
void addToList(const std::string &listName, const QString &userName);
|
||||
|
||||
public:
|
||||
explicit TabAccount(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User &userInfo);
|
||||
void retranslateUi() override;
|
||||
[[nodiscard]] QString getTabText() const override
|
||||
{
|
||||
return tr("Account");
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
263
cockatrice/src/interface/widgets/tabs/tab_admin.cpp
Normal file
263
cockatrice/src/interface/widgets/tabs/tab_admin.cpp
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#include "tab_admin.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/admin_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_replay_added.pb.h>
|
||||
#include <libcockatrice/protocol/pb/moderator_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
|
||||
ShutdownDialog::ShutdownDialog(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
QLabel *reasonLabel = new QLabel(tr("&Reason for shutdown:"));
|
||||
reasonEdit = new QLineEdit;
|
||||
reasonEdit->setMaxLength(MAX_TEXT_LENGTH);
|
||||
reasonLabel->setBuddy(reasonEdit);
|
||||
QLabel *minutesLabel = new QLabel(tr("&Time until shutdown (minutes):"));
|
||||
minutesEdit = new QSpinBox;
|
||||
minutesLabel->setBuddy(minutesEdit);
|
||||
minutesEdit->setMinimum(0);
|
||||
minutesEdit->setValue(5);
|
||||
minutesEdit->setMaximum(999);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &ShutdownDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &ShutdownDialog::reject);
|
||||
|
||||
QGridLayout *mainLayout = new QGridLayout;
|
||||
mainLayout->addWidget(reasonLabel, 0, 0);
|
||||
mainLayout->addWidget(reasonEdit, 0, 1);
|
||||
mainLayout->addWidget(minutesLabel, 1, 0);
|
||||
mainLayout->addWidget(minutesEdit, 1, 1);
|
||||
mainLayout->addWidget(buttonBox, 2, 0, 1, 2);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(tr("Shut down server"));
|
||||
}
|
||||
|
||||
QString ShutdownDialog::getReason() const
|
||||
{
|
||||
return reasonEdit->text();
|
||||
}
|
||||
|
||||
int ShutdownDialog::getMinutes() const
|
||||
{
|
||||
return minutesEdit->value();
|
||||
}
|
||||
|
||||
TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool _fullAdmin)
|
||||
: Tab(_tabSupervisor), locked(true), client(_client), fullAdmin(_fullAdmin)
|
||||
{
|
||||
updateServerMessageButton = new QPushButton;
|
||||
connect(updateServerMessageButton, &QPushButton::clicked, this, &TabAdmin::actUpdateServerMessage);
|
||||
shutdownServerButton = new QPushButton;
|
||||
connect(shutdownServerButton, &QPushButton::clicked, this, &TabAdmin::actShutdownServer);
|
||||
reloadConfigButton = new QPushButton;
|
||||
connect(reloadConfigButton, &QPushButton::clicked, this, &TabAdmin::actReloadConfig);
|
||||
|
||||
grantReplayAccessButton = new QPushButton;
|
||||
grantReplayAccessButton->setEnabled(false);
|
||||
connect(grantReplayAccessButton, &QPushButton::clicked, this, &TabAdmin::actGrantReplayAccess);
|
||||
replayIdToGrant = new QLineEdit;
|
||||
replayIdToGrant->setMaximumWidth(500);
|
||||
replayIdToGrant->setValidator(new QIntValidator(0, INT_MAX, this));
|
||||
connect(replayIdToGrant, &QLineEdit::textChanged, this,
|
||||
[=, this]() { grantReplayAccessButton->setEnabled(!replayIdToGrant->text().isEmpty()); });
|
||||
auto *grandReplayAccessLayout = new QGridLayout(this);
|
||||
grandReplayAccessLayout->addWidget(replayIdToGrant, 0, 0);
|
||||
grandReplayAccessLayout->addWidget(grantReplayAccessButton, 0, 1);
|
||||
|
||||
activateUserButton = new QPushButton;
|
||||
activateUserButton->setEnabled(false);
|
||||
connect(activateUserButton, &QPushButton::clicked, this, &TabAdmin::actForceActivateUser);
|
||||
userToActivate = new QLineEdit;
|
||||
userToActivate->setMaximumWidth(500);
|
||||
connect(userToActivate, &QLineEdit::textChanged, this,
|
||||
[=, this]() { activateUserButton->setEnabled(!userToActivate->text().isEmpty()); });
|
||||
auto *activateUserLayout = new QGridLayout(this);
|
||||
activateUserLayout->addWidget(userToActivate, 0, 0);
|
||||
activateUserLayout->addWidget(activateUserButton, 0, 1);
|
||||
|
||||
auto *adminVBox = new QVBoxLayout;
|
||||
adminVBox->addWidget(updateServerMessageButton);
|
||||
adminVBox->addWidget(shutdownServerButton);
|
||||
adminVBox->addWidget(reloadConfigButton);
|
||||
|
||||
adminGroupBox = new QGroupBox;
|
||||
adminGroupBox->setLayout(adminVBox);
|
||||
adminGroupBox->setEnabled(false);
|
||||
|
||||
auto *moderatorVBox = new QVBoxLayout;
|
||||
moderatorVBox->addLayout(grandReplayAccessLayout);
|
||||
moderatorVBox->addLayout(activateUserLayout);
|
||||
|
||||
moderatorGroupBox = new QGroupBox;
|
||||
moderatorGroupBox->setLayout(moderatorVBox);
|
||||
moderatorGroupBox->setEnabled(false);
|
||||
|
||||
unlockButton = new QPushButton;
|
||||
connect(unlockButton, &QPushButton::clicked, this, &TabAdmin::actUnlock);
|
||||
lockButton = new QPushButton;
|
||||
lockButton->setEnabled(false);
|
||||
connect(lockButton, &QPushButton::clicked, this, &TabAdmin::actLock);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(adminGroupBox);
|
||||
mainLayout->addWidget(moderatorGroupBox);
|
||||
mainLayout->addStretch();
|
||||
mainLayout->addWidget(unlockButton);
|
||||
mainLayout->addWidget(lockButton);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(mainLayout);
|
||||
setCentralWidget(mainWidget);
|
||||
|
||||
actUnlock();
|
||||
}
|
||||
|
||||
void TabAdmin::retranslateUi()
|
||||
{
|
||||
updateServerMessageButton->setText(tr("Update server &message"));
|
||||
shutdownServerButton->setText(tr("&Shut down server"));
|
||||
reloadConfigButton->setText(tr("&Reload configuration"));
|
||||
adminGroupBox->setTitle(tr("Server administration functions"));
|
||||
moderatorGroupBox->setTitle(tr("Server moderator functions"));
|
||||
|
||||
replayIdToGrant->setPlaceholderText(tr("Replay ID"));
|
||||
grantReplayAccessButton->setText(tr("Grant Replay Access"));
|
||||
|
||||
userToActivate->setPlaceholderText(tr("Username to Activate"));
|
||||
activateUserButton->setText(tr("Force Activate User"));
|
||||
|
||||
unlockButton->setText(tr("&Unlock functions"));
|
||||
lockButton->setText(tr("&Lock functions"));
|
||||
}
|
||||
|
||||
void TabAdmin::actUpdateServerMessage()
|
||||
{
|
||||
client->sendCommand(client->prepareAdminCommand(Command_UpdateServerMessage()));
|
||||
}
|
||||
|
||||
void TabAdmin::actShutdownServer()
|
||||
{
|
||||
ShutdownDialog dlg;
|
||||
if (dlg.exec()) {
|
||||
Command_ShutdownServer cmd;
|
||||
cmd.set_reason(dlg.getReason().toStdString());
|
||||
cmd.set_minutes(dlg.getMinutes());
|
||||
|
||||
client->sendCommand(AbstractClient::prepareAdminCommand(cmd));
|
||||
}
|
||||
}
|
||||
|
||||
void TabAdmin::actReloadConfig()
|
||||
{
|
||||
Command_ReloadConfig cmd;
|
||||
client->sendCommand(client->prepareAdminCommand(cmd));
|
||||
}
|
||||
|
||||
void TabAdmin::actGrantReplayAccess()
|
||||
{
|
||||
if (!replayIdToGrant) {
|
||||
return;
|
||||
}
|
||||
|
||||
Command_GrantReplayAccess cmd;
|
||||
cmd.set_replay_id(replayIdToGrant->text().toUInt());
|
||||
cmd.set_moderator_name(client->getUserName().toStdString());
|
||||
|
||||
auto *pend = client->prepareModeratorCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabAdmin::grantReplayAccessProcessResponse);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabAdmin::actForceActivateUser()
|
||||
{
|
||||
if (!userToActivate) {
|
||||
return;
|
||||
}
|
||||
|
||||
Command_ForceActivateUser cmd;
|
||||
cmd.set_username_to_activate(userToActivate->text().trimmed().toStdString());
|
||||
cmd.set_moderator_name(client->getUserName().toStdString());
|
||||
|
||||
auto *pend = client->prepareModeratorCommand(cmd);
|
||||
connect(pend,
|
||||
QOverload<const Response &, const CommandContainer &, const QVariant &>::of(&PendingCommand::finished),
|
||||
this, &TabAdmin::activateUserProcessResponse);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabAdmin::grantReplayAccessProcessResponse(const Response &response)
|
||||
{
|
||||
auto *event = new Event_ReplayAdded();
|
||||
|
||||
switch (response.response_code()) {
|
||||
case Response::RespOk:
|
||||
client->replayAddedEventReceived(*event);
|
||||
QMessageBox::information(this, tr("Success"), tr("Replay access granted"));
|
||||
break;
|
||||
case Response::RespContextError:
|
||||
QMessageBox::critical(this, tr("Error"), tr("Unable to grant replay access. Replay ID invalid"));
|
||||
break;
|
||||
default:
|
||||
QMessageBox::critical(this, tr("Error"), tr("Unable to grant replay access. Internal error"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TabAdmin::activateUserProcessResponse(const Response &response)
|
||||
{
|
||||
switch (response.response_code()) {
|
||||
case Response::RespActivationAccepted:
|
||||
QMessageBox::information(this, tr("Success"), tr("User successfully activated"));
|
||||
break;
|
||||
case Response::RespNameNotFound:
|
||||
QMessageBox::critical(this, tr("Error"), tr("Unable to activate user. Username invalid"));
|
||||
break;
|
||||
case Response::RespActivationFailed:
|
||||
QMessageBox::critical(this, tr("Error"), tr("Unable to activate user. User already active"));
|
||||
break;
|
||||
default:
|
||||
QMessageBox::critical(this, tr("Error"), tr("Unable to activate user. Internal error"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TabAdmin::actUnlock()
|
||||
{
|
||||
if (fullAdmin) {
|
||||
adminGroupBox->setEnabled(true);
|
||||
}
|
||||
|
||||
moderatorGroupBox->setEnabled(true);
|
||||
lockButton->setEnabled(true);
|
||||
unlockButton->setEnabled(false);
|
||||
locked = false;
|
||||
|
||||
emit adminLockChanged(false);
|
||||
}
|
||||
|
||||
void TabAdmin::actLock()
|
||||
{
|
||||
if (fullAdmin) {
|
||||
adminGroupBox->setEnabled(false);
|
||||
}
|
||||
|
||||
moderatorGroupBox->setEnabled(false);
|
||||
lockButton->setEnabled(false);
|
||||
unlockButton->setEnabled(true);
|
||||
locked = true;
|
||||
|
||||
emit adminLockChanged(true);
|
||||
}
|
||||
75
cockatrice/src/interface/widgets/tabs/tab_admin.h
Normal file
75
cockatrice/src/interface/widgets/tabs/tab_admin.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @file tab_admin.h
|
||||
* @ingroup ServerTabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_ADMIN_H
|
||||
#define TAB_ADMIN_H
|
||||
|
||||
#include "tab.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <libcockatrice/protocol/pb/commands.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||
|
||||
class AbstractClient;
|
||||
|
||||
class QGroupBox;
|
||||
class QPushButton;
|
||||
class QSpinBox;
|
||||
class QLineEdit;
|
||||
|
||||
class ShutdownDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QLineEdit *reasonEdit;
|
||||
QSpinBox *minutesEdit;
|
||||
|
||||
public:
|
||||
explicit ShutdownDialog(QWidget *parent = nullptr);
|
||||
QString getReason() const;
|
||||
int getMinutes() const;
|
||||
};
|
||||
|
||||
class TabAdmin : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
bool locked;
|
||||
AbstractClient *client;
|
||||
bool fullAdmin;
|
||||
QPushButton *updateServerMessageButton, *shutdownServerButton, *reloadConfigButton, *grantReplayAccessButton,
|
||||
*activateUserButton;
|
||||
QGroupBox *adminGroupBox, *moderatorGroupBox;
|
||||
QPushButton *unlockButton, *lockButton;
|
||||
QLineEdit *replayIdToGrant, *userToActivate;
|
||||
signals:
|
||||
void adminLockChanged(bool lock);
|
||||
private slots:
|
||||
void actUpdateServerMessage();
|
||||
void actShutdownServer();
|
||||
void actReloadConfig();
|
||||
void actGrantReplayAccess();
|
||||
void actForceActivateUser();
|
||||
void grantReplayAccessProcessResponse(const Response &response);
|
||||
void activateUserProcessResponse(const Response &response);
|
||||
|
||||
void actUnlock();
|
||||
void actLock();
|
||||
|
||||
public:
|
||||
TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool _fullAdmin);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Administration");
|
||||
}
|
||||
bool getLocked() const
|
||||
{
|
||||
return locked;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
374
cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp
Normal file
374
cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
#include "tab_deck_editor.h"
|
||||
|
||||
#include "../client/network/interfaces/tapped_out_interface.h"
|
||||
#include "../dialogs/dlg_load_deck.h"
|
||||
#include "../dialogs/dlg_load_deck_from_clipboard.h"
|
||||
#include "../filters/filter_builder.h"
|
||||
#include "../filters/filter_tree_model.h"
|
||||
#include "../interface/pixel_map_generator.h"
|
||||
#include "../interface/widgets/cards/card_info_frame_widget.h"
|
||||
#include "../interface/widgets/deck_editor/deck_editor_filter_dock_widget.h"
|
||||
#include "../interface/widgets/menus/deck_editor_menu.h"
|
||||
#include "tab_supervisor.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDockWidget>
|
||||
#include <QFileDialog>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
#include <QPrintPreviewDialog>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QSplitter>
|
||||
#include <QTextEdit>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/card_database/card_database_manager.h>
|
||||
#include <libcockatrice/card/card_database/model/card_database_model.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/cache_settings.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
|
||||
TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor) : AbstractTabDeckEditor(_tabSupervisor)
|
||||
{
|
||||
setObjectName("TabDeckEditor");
|
||||
|
||||
TabDeckEditor::createMenus();
|
||||
|
||||
installEventFilter(this);
|
||||
|
||||
TabDeckEditor::retranslateUi();
|
||||
TabDeckEditor::refreshShortcuts();
|
||||
|
||||
TabDeckEditor::loadLayout();
|
||||
}
|
||||
|
||||
void TabDeckEditor::createMenus()
|
||||
{
|
||||
deckMenu = new DeckEditorMenu(this);
|
||||
addTabMenu(deckMenu);
|
||||
|
||||
viewMenu = new QMenu(this);
|
||||
|
||||
cardInfoDockMenu = viewMenu->addMenu(QString());
|
||||
deckDockMenu = viewMenu->addMenu(QString());
|
||||
filterDockMenu = viewMenu->addMenu(QString());
|
||||
printingSelectorDockMenu = viewMenu->addMenu(QString());
|
||||
|
||||
aCardInfoDockVisible = cardInfoDockMenu->addAction(QString());
|
||||
aCardInfoDockVisible->setCheckable(true);
|
||||
connect(aCardInfoDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
|
||||
aCardInfoDockFloating = cardInfoDockMenu->addAction(QString());
|
||||
aCardInfoDockFloating->setCheckable(true);
|
||||
connect(aCardInfoDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
|
||||
|
||||
aDeckDockVisible = deckDockMenu->addAction(QString());
|
||||
aDeckDockVisible->setCheckable(true);
|
||||
connect(aDeckDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
|
||||
aDeckDockFloating = deckDockMenu->addAction(QString());
|
||||
aDeckDockFloating->setCheckable(true);
|
||||
connect(aDeckDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
|
||||
|
||||
aFilterDockVisible = filterDockMenu->addAction(QString());
|
||||
aFilterDockVisible->setCheckable(true);
|
||||
connect(aFilterDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
|
||||
aFilterDockFloating = filterDockMenu->addAction(QString());
|
||||
aFilterDockFloating->setCheckable(true);
|
||||
connect(aFilterDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
|
||||
|
||||
aPrintingSelectorDockVisible = printingSelectorDockMenu->addAction(QString());
|
||||
aPrintingSelectorDockVisible->setCheckable(true);
|
||||
connect(aPrintingSelectorDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
|
||||
aPrintingSelectorDockFloating = printingSelectorDockMenu->addAction(QString());
|
||||
aPrintingSelectorDockFloating->setCheckable(true);
|
||||
connect(aPrintingSelectorDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
|
||||
|
||||
if (SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {
|
||||
printingSelectorDockMenu->setEnabled(false);
|
||||
}
|
||||
|
||||
connect(&SettingsCache::instance(), &SettingsCache::overrideAllCardArtWithPersonalPreferenceChanged, this,
|
||||
[this](bool enabled) { printingSelectorDockMenu->setEnabled(!enabled); });
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
aResetLayout = viewMenu->addAction(QString());
|
||||
connect(aResetLayout, &QAction::triggered, this, &TabDeckEditor::restartLayout);
|
||||
viewMenu->addAction(aResetLayout);
|
||||
|
||||
deckMenu->setSaveStatus(false);
|
||||
|
||||
addTabMenu(viewMenu);
|
||||
}
|
||||
|
||||
QString TabDeckEditor::getTabText() const
|
||||
{
|
||||
QString result = tr("Deck: %1").arg(deckDockWidget->getSimpleDeckName());
|
||||
if (modified)
|
||||
result.prepend("* ");
|
||||
return result;
|
||||
}
|
||||
|
||||
void TabDeckEditor::retranslateUi()
|
||||
{
|
||||
deckMenu->retranslateUi();
|
||||
|
||||
cardInfoDockWidget->retranslateUi();
|
||||
deckDockWidget->retranslateUi();
|
||||
filterDockWidget->retranslateUi();
|
||||
printingSelectorDockWidget->retranslateUi();
|
||||
|
||||
viewMenu->setTitle(tr("&View"));
|
||||
cardInfoDockMenu->setTitle(tr("Card Info"));
|
||||
deckDockMenu->setTitle(tr("Deck"));
|
||||
filterDockMenu->setTitle(tr("Filters"));
|
||||
printingSelectorDockMenu->setTitle(tr("Printing"));
|
||||
|
||||
aCardInfoDockVisible->setText(tr("Visible"));
|
||||
aCardInfoDockFloating->setText(tr("Floating"));
|
||||
|
||||
aDeckDockVisible->setText(tr("Visible"));
|
||||
aDeckDockFloating->setText(tr("Floating"));
|
||||
|
||||
aFilterDockVisible->setText(tr("Visible"));
|
||||
aFilterDockFloating->setText(tr("Floating"));
|
||||
|
||||
aPrintingSelectorDockVisible->setText(tr("Visible"));
|
||||
aPrintingSelectorDockFloating->setText(tr("Floating"));
|
||||
|
||||
aResetLayout->setText(tr("Reset layout"));
|
||||
}
|
||||
|
||||
void TabDeckEditor::refreshShortcuts()
|
||||
{
|
||||
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
|
||||
aResetLayout->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aResetLayout"));
|
||||
}
|
||||
|
||||
void TabDeckEditor::showPrintingSelector()
|
||||
{
|
||||
printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr(),
|
||||
DECK_ZONE_MAIN);
|
||||
printingSelectorDockWidget->printingSelector->updateDisplay();
|
||||
aPrintingSelectorDockVisible->setChecked(true);
|
||||
printingSelectorDockWidget->setVisible(true);
|
||||
}
|
||||
|
||||
void TabDeckEditor::loadLayout()
|
||||
{
|
||||
LayoutsSettings &layouts = SettingsCache::instance().layouts();
|
||||
|
||||
setCentralWidget(databaseDisplayDockWidget);
|
||||
|
||||
auto &layoutState = layouts.getDeckEditorLayoutState();
|
||||
if (layoutState.isNull()) {
|
||||
restartLayout();
|
||||
} else {
|
||||
restoreState(layoutState);
|
||||
restoreGeometry(layouts.getDeckEditorGeometry());
|
||||
}
|
||||
|
||||
if (SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {
|
||||
if (!printingSelectorDockWidget->isHidden()) {
|
||||
printingSelectorDockWidget->setHidden(true);
|
||||
aPrintingSelectorDockVisible->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
aCardInfoDockVisible->setChecked(!cardInfoDockWidget->isHidden());
|
||||
aFilterDockVisible->setChecked(!filterDockWidget->isHidden());
|
||||
aDeckDockVisible->setChecked(!deckDockWidget->isHidden());
|
||||
aPrintingSelectorDockVisible->setChecked(!printingSelectorDockWidget->isHidden());
|
||||
|
||||
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
|
||||
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
|
||||
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
|
||||
aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
|
||||
|
||||
aCardInfoDockFloating->setChecked(cardInfoDockWidget->isFloating());
|
||||
aFilterDockFloating->setChecked(filterDockWidget->isFloating());
|
||||
aDeckDockFloating->setChecked(deckDockWidget->isFloating());
|
||||
aPrintingSelectorDockFloating->setChecked(printingSelectorDockWidget->isFloating());
|
||||
|
||||
cardInfoDockWidget->setMinimumSize(layouts.getDeckEditorCardSize());
|
||||
cardInfoDockWidget->setMaximumSize(layouts.getDeckEditorCardSize());
|
||||
|
||||
filterDockWidget->setMinimumSize(layouts.getDeckEditorFilterSize());
|
||||
filterDockWidget->setMaximumSize(layouts.getDeckEditorFilterSize());
|
||||
|
||||
deckDockWidget->setMinimumSize(layouts.getDeckEditorDeckSize());
|
||||
deckDockWidget->setMaximumSize(layouts.getDeckEditorDeckSize());
|
||||
|
||||
printingSelectorDockWidget->setMinimumSize(layouts.getDeckEditorPrintingSelectorSize());
|
||||
printingSelectorDockWidget->setMaximumSize(layouts.getDeckEditorPrintingSelectorSize());
|
||||
|
||||
QTimer::singleShot(100, this, &TabDeckEditor::freeDocksSize);
|
||||
}
|
||||
|
||||
void TabDeckEditor::restartLayout()
|
||||
{
|
||||
|
||||
aCardInfoDockVisible->setChecked(true);
|
||||
aDeckDockVisible->setChecked(true);
|
||||
aFilterDockVisible->setChecked(true);
|
||||
aPrintingSelectorDockVisible->setChecked(!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference());
|
||||
|
||||
aCardInfoDockFloating->setChecked(false);
|
||||
aDeckDockFloating->setChecked(false);
|
||||
aFilterDockFloating->setChecked(false);
|
||||
aPrintingSelectorDockFloating->setChecked(false);
|
||||
|
||||
setCentralWidget(databaseDisplayDockWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, deckDockWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, cardInfoDockWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, filterDockWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, printingSelectorDockWidget);
|
||||
|
||||
deckDockWidget->setFloating(false);
|
||||
cardInfoDockWidget->setFloating(false);
|
||||
filterDockWidget->setFloating(false);
|
||||
printingSelectorDockWidget->setFloating(false);
|
||||
|
||||
deckDockWidget->setVisible(true);
|
||||
cardInfoDockWidget->setVisible(true);
|
||||
filterDockWidget->setVisible(true);
|
||||
printingSelectorDockWidget->setVisible(!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference());
|
||||
|
||||
splitDockWidget(cardInfoDockWidget, printingSelectorDockWidget, Qt::Horizontal);
|
||||
splitDockWidget(printingSelectorDockWidget, deckDockWidget, Qt::Horizontal);
|
||||
splitDockWidget(cardInfoDockWidget, printingSelectorDockWidget, Qt::Horizontal);
|
||||
splitDockWidget(cardInfoDockWidget, filterDockWidget, Qt::Vertical);
|
||||
|
||||
QTimer::singleShot(100, this, &TabDeckEditor::freeDocksSize);
|
||||
}
|
||||
|
||||
void TabDeckEditor::freeDocksSize()
|
||||
{
|
||||
deckDockWidget->setMinimumSize(100, 100);
|
||||
deckDockWidget->setMaximumSize(5000, 5000);
|
||||
|
||||
cardInfoDockWidget->setMinimumSize(100, 100);
|
||||
cardInfoDockWidget->setMaximumSize(5000, 5000);
|
||||
|
||||
filterDockWidget->setMinimumSize(100, 100);
|
||||
filterDockWidget->setMaximumSize(5000, 5000);
|
||||
|
||||
printingSelectorDockWidget->setMinimumSize(100, 100);
|
||||
printingSelectorDockWidget->setMaximumSize(5000, 5000);
|
||||
}
|
||||
|
||||
void TabDeckEditor::dockVisibleTriggered()
|
||||
{
|
||||
QObject *o = sender();
|
||||
if (o == aCardInfoDockVisible) {
|
||||
cardInfoDockWidget->setHidden(!aCardInfoDockVisible->isChecked());
|
||||
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aDeckDockVisible) {
|
||||
deckDockWidget->setHidden(!aDeckDockVisible->isChecked());
|
||||
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aFilterDockVisible) {
|
||||
filterDockWidget->setHidden(!aFilterDockVisible->isChecked());
|
||||
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aPrintingSelectorDockVisible) {
|
||||
printingSelectorDockWidget->setHidden(!aPrintingSelectorDockVisible->isChecked());
|
||||
aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::dockFloatingTriggered()
|
||||
{
|
||||
QObject *o = sender();
|
||||
if (o == aCardInfoDockFloating) {
|
||||
cardInfoDockWidget->setFloating(aCardInfoDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aDeckDockFloating) {
|
||||
deckDockWidget->setFloating(aDeckDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aFilterDockFloating) {
|
||||
filterDockWidget->setFloating(aFilterDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aPrintingSelectorDockFloating) {
|
||||
printingSelectorDockWidget->setFloating(aPrintingSelectorDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::dockTopLevelChanged(bool topLevel)
|
||||
{
|
||||
QObject *o = sender();
|
||||
if (o == cardInfoDockWidget) {
|
||||
aCardInfoDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == deckDockWidget) {
|
||||
aDeckDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == filterDockWidget) {
|
||||
aFilterDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == printingSelectorDockWidget) {
|
||||
aPrintingSelectorDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Method uses to sync docks state with menu items state
|
||||
bool TabDeckEditor::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::Close) {
|
||||
if (o == cardInfoDockWidget) {
|
||||
aCardInfoDockVisible->setChecked(false);
|
||||
aCardInfoDockFloating->setEnabled(false);
|
||||
} else if (o == deckDockWidget) {
|
||||
aDeckDockVisible->setChecked(false);
|
||||
aDeckDockFloating->setEnabled(false);
|
||||
} else if (o == filterDockWidget) {
|
||||
aFilterDockVisible->setChecked(false);
|
||||
aFilterDockFloating->setEnabled(false);
|
||||
} else if (o == printingSelectorDockWidget) {
|
||||
aPrintingSelectorDockVisible->setChecked(false);
|
||||
aPrintingSelectorDockFloating->setEnabled(false);
|
||||
}
|
||||
}
|
||||
if (o == this && e->type() == QEvent::Hide) {
|
||||
LayoutsSettings &layouts = SettingsCache::instance().layouts();
|
||||
layouts.setDeckEditorLayoutState(saveState());
|
||||
layouts.setDeckEditorGeometry(saveGeometry());
|
||||
layouts.setDeckEditorCardSize(cardInfoDockWidget->size());
|
||||
layouts.setDeckEditorFilterSize(filterDockWidget->size());
|
||||
layouts.setDeckEditorDeckSize(deckDockWidget->size());
|
||||
layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
42
cockatrice/src/interface/widgets/tabs/tab_deck_editor.h
Normal file
42
cockatrice/src/interface/widgets/tabs/tab_deck_editor.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef WINDOW_DECKEDITOR_H
|
||||
#define WINDOW_DECKEDITOR_H
|
||||
|
||||
#include "../interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
|
||||
#include "abstract_tab_deck_editor.h"
|
||||
|
||||
#include <libcockatrice/card/card_info.h>
|
||||
#include <libcockatrice/utility/key_signals.h>
|
||||
|
||||
class CardDatabaseModel;
|
||||
class CardDatabaseDisplayModel;
|
||||
class DeckListModel;
|
||||
|
||||
class QLabel;
|
||||
class DeckLoader;
|
||||
|
||||
class TabDeckEditor : public AbstractTabDeckEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
protected slots:
|
||||
void loadLayout() override;
|
||||
void restartLayout() override;
|
||||
void freeDocksSize() override;
|
||||
void refreshShortcuts() override;
|
||||
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
void dockVisibleTriggered() override;
|
||||
void dockFloatingTriggered() override;
|
||||
void dockTopLevelChanged(bool topLevel) override;
|
||||
|
||||
public:
|
||||
explicit TabDeckEditor(TabSupervisor *_tabSupervisor);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override;
|
||||
void createMenus() override;
|
||||
|
||||
public slots:
|
||||
void showPrintingSelector() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
604
cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp
Normal file
604
cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp
Normal file
|
|
@ -0,0 +1,604 @@
|
|||
#include "tab_deck_storage.h"
|
||||
|
||||
#include "../interface/widgets/server/remote/remote_decklist_tree_widget.h"
|
||||
#include "../interface/widgets/utility/get_text_with_max.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileSystemModel>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QToolBar>
|
||||
#include <QTreeView>
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/deck_loader.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_del.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_del_dir.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_download.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_new_dir.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_upload.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_upload.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/cache_settings.h>
|
||||
|
||||
TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
|
||||
AbstractClient *_client,
|
||||
const ServerInfo_User *currentUserInfo)
|
||||
: Tab(_tabSupervisor), client(_client)
|
||||
{
|
||||
localDirModel = new QFileSystemModel(this);
|
||||
localDirModel->setRootPath(SettingsCache::instance().getDeckPath());
|
||||
localDirModel->sort(0, Qt::AscendingOrder);
|
||||
|
||||
localDirView = new QTreeView;
|
||||
localDirView->setModel(localDirModel);
|
||||
localDirView->setColumnHidden(1, true);
|
||||
localDirView->setRootIndex(localDirModel->index(localDirModel->rootPath(), 0));
|
||||
localDirView->setSortingEnabled(true);
|
||||
localDirView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
localDirView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
localDirView->header()->setSortIndicator(0, Qt::AscendingOrder);
|
||||
|
||||
connect(localDirView, &QTreeView::doubleClicked, this, &TabDeckStorage::actLocalDoubleClick);
|
||||
|
||||
// Left side layout
|
||||
/* put an invisible dummy QToolBar in the leftmost column so that the main toolbar is centered.
|
||||
* Really ugly workaround, but I couldn't figure out the proper way to make it centered */
|
||||
QToolBar *dummyToolBar = new QToolBar(this);
|
||||
QSizePolicy sizePolicy = dummyToolBar->sizePolicy();
|
||||
sizePolicy.setRetainSizeWhenHidden(true);
|
||||
dummyToolBar->setSizePolicy(sizePolicy);
|
||||
dummyToolBar->setVisible(false);
|
||||
|
||||
leftToolBar = new QToolBar(this);
|
||||
leftToolBar->setOrientation(Qt::Horizontal);
|
||||
leftToolBar->setIconSize(QSize(32, 32));
|
||||
|
||||
QToolBar *leftRightmostToolBar = new QToolBar(this);
|
||||
leftRightmostToolBar->setOrientation(Qt::Horizontal);
|
||||
leftRightmostToolBar->setIconSize(QSize(32, 32));
|
||||
|
||||
QGridLayout *leftToolBarLayout = new QGridLayout;
|
||||
leftToolBarLayout->addWidget(dummyToolBar, 0, 0, Qt::AlignLeft);
|
||||
leftToolBarLayout->addWidget(leftToolBar, 0, 1, Qt::AlignHCenter);
|
||||
leftToolBarLayout->addWidget(leftRightmostToolBar, 0, 2, Qt::AlignRight);
|
||||
|
||||
QVBoxLayout *leftVbox = new QVBoxLayout;
|
||||
leftVbox->addWidget(localDirView);
|
||||
leftVbox->addLayout(leftToolBarLayout);
|
||||
leftGroupBox = new QGroupBox;
|
||||
leftGroupBox->setLayout(leftVbox);
|
||||
|
||||
// Right side layout
|
||||
rightToolBar = new QToolBar;
|
||||
rightToolBar->setOrientation(Qt::Horizontal);
|
||||
rightToolBar->setIconSize(QSize(32, 32));
|
||||
QHBoxLayout *rightToolBarLayout = new QHBoxLayout;
|
||||
rightToolBarLayout->addStretch();
|
||||
rightToolBarLayout->addWidget(rightToolBar);
|
||||
rightToolBarLayout->addStretch();
|
||||
|
||||
serverDirView = new RemoteDeckList_TreeWidget(client);
|
||||
|
||||
connect(serverDirView, &QTreeView::doubleClicked, this, &TabDeckStorage::actRemoteDoubleClick);
|
||||
|
||||
QVBoxLayout *rightVbox = new QVBoxLayout;
|
||||
rightVbox->addWidget(serverDirView);
|
||||
rightVbox->addLayout(rightToolBarLayout);
|
||||
rightGroupBox = new QGroupBox;
|
||||
rightGroupBox->setLayout(rightVbox);
|
||||
|
||||
// combine layouts
|
||||
QHBoxLayout *hbox = new QHBoxLayout;
|
||||
hbox->addWidget(leftGroupBox);
|
||||
hbox->addWidget(rightGroupBox);
|
||||
|
||||
// Left side actions
|
||||
aOpenLocalDeck = new QAction(this);
|
||||
aOpenLocalDeck->setIcon(QPixmap("theme:icons/pencil"));
|
||||
connect(aOpenLocalDeck, &QAction::triggered, this, &TabDeckStorage::actOpenLocalDeck);
|
||||
aRenameLocal = new QAction(this);
|
||||
aRenameLocal->setIcon(QPixmap("theme:icons/rename"));
|
||||
connect(aRenameLocal, &QAction::triggered, this, &TabDeckStorage::actRenameLocal);
|
||||
aUpload = new QAction(this);
|
||||
aUpload->setIcon(QPixmap("theme:icons/arrow_right_green"));
|
||||
connect(aUpload, &QAction::triggered, this, &TabDeckStorage::actUpload);
|
||||
aNewLocalFolder = new QAction(this);
|
||||
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
||||
connect(aNewLocalFolder, &QAction::triggered, this, &TabDeckStorage::actNewLocalFolder);
|
||||
aDeleteLocalDeck = new QAction(this);
|
||||
aDeleteLocalDeck->setIcon(QPixmap("theme:icons/remove_row"));
|
||||
connect(aDeleteLocalDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteLocalDeck);
|
||||
|
||||
aOpenDecksFolder = new QAction(this);
|
||||
aOpenDecksFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_DirOpenIcon));
|
||||
connect(aOpenDecksFolder, &QAction::triggered, this, &TabDeckStorage::actOpenDecksFolder);
|
||||
|
||||
// Right side actions
|
||||
aOpenRemoteDeck = new QAction(this);
|
||||
aOpenRemoteDeck->setIcon(QPixmap("theme:icons/pencil"));
|
||||
connect(aOpenRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actOpenRemoteDeck);
|
||||
aDownload = new QAction(this);
|
||||
aDownload->setIcon(QPixmap("theme:icons/arrow_left_green"));
|
||||
connect(aDownload, &QAction::triggered, this, &TabDeckStorage::actDownload);
|
||||
aNewFolder = new QAction(this);
|
||||
aNewFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
||||
connect(aNewFolder, &QAction::triggered, this, &TabDeckStorage::actNewFolder);
|
||||
aDeleteRemoteDeck = new QAction(this);
|
||||
aDeleteRemoteDeck->setIcon(QPixmap("theme:icons/remove_row"));
|
||||
connect(aDeleteRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteRemoteDeck);
|
||||
|
||||
// Add actions to toolbars
|
||||
leftToolBar->addAction(aOpenLocalDeck);
|
||||
leftToolBar->addAction(aRenameLocal);
|
||||
leftToolBar->addAction(aUpload);
|
||||
leftToolBar->addAction(aNewLocalFolder);
|
||||
leftToolBar->addAction(aDeleteLocalDeck);
|
||||
|
||||
leftRightmostToolBar->addAction(aOpenDecksFolder);
|
||||
|
||||
rightToolBar->addAction(aOpenRemoteDeck);
|
||||
rightToolBar->addAction(aDownload);
|
||||
rightToolBar->addAction(aNewFolder);
|
||||
rightToolBar->addAction(aDeleteRemoteDeck);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(hbox);
|
||||
setCentralWidget(mainWidget);
|
||||
|
||||
connect(client, &AbstractClient::userInfoChanged, this, &TabDeckStorage::handleConnected);
|
||||
connect(client, &AbstractClient::statusChanged, this, &TabDeckStorage::handleConnectionChanged);
|
||||
setRemoteEnabled(currentUserInfo && currentUserInfo->user_level() & ServerInfo_User::IsRegistered);
|
||||
}
|
||||
|
||||
void TabDeckStorage::retranslateUi()
|
||||
{
|
||||
leftGroupBox->setTitle(tr("Local file system"));
|
||||
rightGroupBox->setTitle(tr("Server deck storage"));
|
||||
|
||||
aOpenLocalDeck->setText(tr("Open in deck editor"));
|
||||
aRenameLocal->setText(tr("Rename deck or folder"));
|
||||
aUpload->setText(tr("Upload deck"));
|
||||
aOpenRemoteDeck->setText(tr("Open in deck editor"));
|
||||
aDownload->setText(tr("Download deck"));
|
||||
aNewLocalFolder->setText(tr("New folder"));
|
||||
aNewFolder->setText(tr("New folder"));
|
||||
aDeleteLocalDeck->setText(tr("Delete"));
|
||||
aDeleteRemoteDeck->setText(tr("Delete"));
|
||||
aOpenDecksFolder->setText(tr("Open decks folder"));
|
||||
}
|
||||
|
||||
QString TabDeckStorage::getTargetPath() const
|
||||
{
|
||||
RemoteDeckList_TreeModel::Node *curRight = serverDirView->getCurrentItem();
|
||||
if (curRight == nullptr)
|
||||
return {};
|
||||
auto *dir = dynamic_cast<RemoteDeckList_TreeModel::DirectoryNode *>(curRight);
|
||||
if (dir == nullptr) {
|
||||
dir = dynamic_cast<RemoteDeckList_TreeModel::DirectoryNode *>(curRight->getParent());
|
||||
if (dir != nullptr) {
|
||||
return dir->getPath();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return dir->getPath();
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::handleConnected(const ServerInfo_User &userInfo)
|
||||
{
|
||||
setRemoteEnabled(userInfo.user_level() & ServerInfo_User::IsRegistered);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is only responsible for handling the disconnect. The connect is already handled elsewhere
|
||||
*/
|
||||
void TabDeckStorage::handleConnectionChanged(ClientStatus status)
|
||||
{
|
||||
if (status == StatusDisconnected) {
|
||||
setRemoteEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::setRemoteEnabled(bool enabled)
|
||||
{
|
||||
aUpload->setEnabled(enabled);
|
||||
aOpenRemoteDeck->setEnabled(enabled);
|
||||
aDownload->setEnabled(enabled);
|
||||
aNewFolder->setEnabled(enabled);
|
||||
aDeleteRemoteDeck->setEnabled(enabled);
|
||||
|
||||
if (enabled) {
|
||||
serverDirView->refreshTree();
|
||||
} else {
|
||||
serverDirView->clearTree();
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::actLocalDoubleClick(const QModelIndex &curLeft)
|
||||
{
|
||||
if (!localDirModel->isDir(curLeft)) {
|
||||
actOpenLocalDeck();
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::actOpenLocalDeck()
|
||||
{
|
||||
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
||||
for (const auto &curLeft : curLefts) {
|
||||
if (localDirModel->isDir(curLeft))
|
||||
continue;
|
||||
QString filePath = localDirModel->filePath(curLeft);
|
||||
|
||||
DeckLoader deckLoader;
|
||||
if (!deckLoader.loadFromFile(filePath, DeckLoader::CockatriceFormat, true))
|
||||
continue;
|
||||
|
||||
emit openDeckEditor(&deckLoader);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::actRenameLocal()
|
||||
{
|
||||
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
||||
for (const auto &curLeft : curLefts) {
|
||||
const QFileInfo info = localDirModel->fileInfo(curLeft);
|
||||
|
||||
const QString oldName = info.baseName();
|
||||
const QString title = info.isDir() ? tr("Rename local folder") : tr("Rename local file");
|
||||
|
||||
bool ok;
|
||||
QString newName = QInputDialog::getText(this, title, tr("New name:"), QLineEdit::Normal, oldName, &ok);
|
||||
if (!ok) { // terminate all remaining selections if user cancels
|
||||
return;
|
||||
}
|
||||
if (newName.isEmpty() || oldName == newName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString newFileName = newName;
|
||||
if (!info.suffix().isEmpty()) {
|
||||
newFileName += "." + info.suffix();
|
||||
}
|
||||
const QString newFilePath = QFileInfo(info.dir(), newFileName).filePath();
|
||||
|
||||
if (!QFile::rename(info.filePath(), newFilePath)) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Rename failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::actUpload()
|
||||
{
|
||||
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
||||
if (curLefts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString targetPath = getTargetPath();
|
||||
if (targetPath.length() > MAX_NAME_LENGTH) {
|
||||
qCritical() << "target path to upload to is too long" << targetPath;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &curLeft : curLefts) {
|
||||
if (localDirModel->isDir(curLeft)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString filePath = localDirModel->filePath(curLeft);
|
||||
uploadDeck(filePath, targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPath)
|
||||
{
|
||||
QFile deckFile(filePath);
|
||||
QFileInfo deckFileInfo(deckFile);
|
||||
|
||||
DeckLoader deck;
|
||||
if (!deck.loadFromFile(filePath, DeckLoader::CockatriceFormat)) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Invalid deck file"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (deck.getName().isEmpty()) {
|
||||
bool ok;
|
||||
QString deckName =
|
||||
getTextWithMax(this, tr("Enter deck name"), tr("This decklist does not have a name.\nPlease enter a name:"),
|
||||
QLineEdit::Normal, deckFileInfo.completeBaseName(), &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
if (deckName.isEmpty())
|
||||
deckName = tr("Unnamed deck");
|
||||
deck.setName(deckName);
|
||||
} else {
|
||||
deck.setName(deck.getName().left(MAX_NAME_LENGTH));
|
||||
}
|
||||
|
||||
QString deckString = deck.writeToString_Native();
|
||||
if (deckString.length() > MAX_FILE_LENGTH) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Invalid deck file"));
|
||||
return;
|
||||
}
|
||||
|
||||
Command_DeckUpload cmd;
|
||||
cmd.set_path(targetPath.toStdString());
|
||||
cmd.set_deck_list(deckString.toStdString());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabDeckStorage::uploadFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabDeckStorage::uploadFinished(const Response &r, const CommandContainer &commandContainer)
|
||||
{
|
||||
if (r.response_code() != Response::RespOk) {
|
||||
qCritical() << "failed to upload deck:" << r.response_code();
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to upload deck to server"));
|
||||
return;
|
||||
}
|
||||
|
||||
const Response_DeckUpload &resp = r.GetExtension(Response_DeckUpload::ext);
|
||||
const Command_DeckUpload &cmd = commandContainer.session_command(0).GetExtension(Command_DeckUpload::ext);
|
||||
|
||||
serverDirView->addFileToTree(resp.new_file(), serverDirView->getNodeByPath(QString::fromStdString(cmd.path())));
|
||||
}
|
||||
|
||||
void TabDeckStorage::actNewLocalFolder()
|
||||
{
|
||||
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
|
||||
|
||||
QModelIndex dirIndex;
|
||||
if (curLeft.isValid() && !localDirModel->isDir(curLeft)) {
|
||||
dirIndex = curLeft.parent();
|
||||
} else {
|
||||
dirIndex = curLeft;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
QString folderName =
|
||||
QInputDialog::getText(this, tr("New folder"), tr("Name of new folder:"), QLineEdit::Normal, "", &ok);
|
||||
if (!ok || folderName.isEmpty())
|
||||
return;
|
||||
|
||||
localDirModel->mkdir(dirIndex, folderName);
|
||||
}
|
||||
|
||||
void TabDeckStorage::actDeleteLocalDeck()
|
||||
{
|
||||
const QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
||||
|
||||
if (curLefts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QMessageBox::warning(this, tr("Delete local file"), tr("Are you sure you want to delete the selected files?"),
|
||||
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
for (const auto &curLeft : curLefts) {
|
||||
if (curLeft.isValid()) {
|
||||
localDirModel->remove(curLeft);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::actOpenDecksFolder()
|
||||
{
|
||||
QString dir = localDirModel->rootPath();
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
|
||||
}
|
||||
|
||||
void TabDeckStorage::actRemoteDoubleClick(const QModelIndex &curRight)
|
||||
{
|
||||
if (dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(serverDirView->getNode(curRight))) {
|
||||
actOpenRemoteDeck();
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::actOpenRemoteDeck()
|
||||
{
|
||||
for (const auto &curRight : serverDirView->getCurrentSelection()) {
|
||||
RemoteDeckList_TreeModel::FileNode *node = dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(curRight);
|
||||
if (!node)
|
||||
continue;
|
||||
|
||||
Command_DeckDownload cmd;
|
||||
cmd.set_deck_id(node->getId());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabDeckStorage::openRemoteDeckFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::openRemoteDeckFinished(const Response &r, const CommandContainer &commandContainer)
|
||||
{
|
||||
if (r.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
|
||||
const Command_DeckDownload &cmd = commandContainer.session_command(0).GetExtension(Command_DeckDownload::ext);
|
||||
|
||||
DeckLoader loader;
|
||||
if (!loader.loadFromRemote(QString::fromStdString(resp.deck()), cmd.deck_id()))
|
||||
return;
|
||||
|
||||
emit openDeckEditor(&loader);
|
||||
}
|
||||
|
||||
void TabDeckStorage::actDownload()
|
||||
{
|
||||
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
|
||||
while (!localDirModel->isDir(curLeft)) {
|
||||
curLeft = curLeft.parent();
|
||||
}
|
||||
|
||||
for (const auto curRight : serverDirView->selectionModel()->selectedRows()) {
|
||||
downloadNodeAtIndex(curLeft, curRight);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::downloadNodeAtIndex(const QModelIndex &curLeft, const QModelIndex &curRight)
|
||||
{
|
||||
auto node = serverDirView->getNode(curRight);
|
||||
if (const auto dirNode = dynamic_cast<RemoteDeckList_TreeModel::DirectoryNode *>(node)) {
|
||||
// node at index is a folder
|
||||
const QString name = dirNode->getName();
|
||||
|
||||
const auto dirIndex = curLeft.isValid() ? curLeft : localDirModel->index(localDirModel->rootPath());
|
||||
const auto newDirIndex = localDirModel->mkdir(dirIndex, name);
|
||||
|
||||
int rows = serverDirView->model()->rowCount(curRight);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
const auto childIndex = serverDirView->model()->index(i, 0, curRight);
|
||||
downloadNodeAtIndex(newDirIndex, childIndex);
|
||||
}
|
||||
} else if (const auto fileNode = dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(node)) {
|
||||
// node at index is a deck
|
||||
const QString dirPath = curLeft.isValid() ? localDirModel->filePath(curLeft) : localDirModel->rootPath();
|
||||
const QString filePath = dirPath + QString("/deck_%1.cod").arg(fileNode->getId());
|
||||
|
||||
Command_DeckDownload cmd;
|
||||
cmd.set_deck_id(fileNode->getId());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
pend->setExtraData(filePath);
|
||||
connect(pend, &PendingCommand::finished, this, &TabDeckStorage::downloadFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
// node at index is invalid
|
||||
}
|
||||
|
||||
void TabDeckStorage::downloadFinished(const Response &r,
|
||||
const CommandContainer & /*commandContainer*/,
|
||||
const QVariant &extraData)
|
||||
{
|
||||
if (r.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
|
||||
QString filePath = extraData.toString();
|
||||
|
||||
DeckLoader deck(QString::fromStdString(resp.deck()));
|
||||
deck.saveToFile(filePath, DeckLoader::CockatriceFormat);
|
||||
}
|
||||
|
||||
void TabDeckStorage::actNewFolder()
|
||||
{
|
||||
QString targetPath = getTargetPath();
|
||||
int max_length = MAX_NAME_LENGTH - targetPath.length() - 1; // generated length would be path + / + name
|
||||
|
||||
if (max_length < 1) // can't create path that's short enough
|
||||
return;
|
||||
|
||||
QString folderName = getTextWithMax(this, tr("New folder"), tr("Name of new folder:"), max_length);
|
||||
if (folderName.isEmpty())
|
||||
return;
|
||||
|
||||
// '/' isn't a valid filename character on *nix so we're choosing to replace it with a different arbitrary
|
||||
// character.
|
||||
std::string folder = folderName.toStdString();
|
||||
std::replace(folder.begin(), folder.end(), '/', '-');
|
||||
|
||||
Command_DeckNewDir cmd;
|
||||
cmd.set_path(targetPath.toStdString());
|
||||
cmd.set_dir_name(folder);
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabDeckStorage::newFolderFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabDeckStorage::newFolderFinished(const Response &response, const CommandContainer &commandContainer)
|
||||
{
|
||||
if (response.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Command_DeckNewDir &cmd = commandContainer.session_command(0).GetExtension(Command_DeckNewDir::ext);
|
||||
serverDirView->addFolderToTree(QString::fromStdString(cmd.dir_name()),
|
||||
serverDirView->getNodeByPath(QString::fromStdString(cmd.path())));
|
||||
}
|
||||
|
||||
void TabDeckStorage::actDeleteRemoteDeck()
|
||||
{
|
||||
auto curRights = serverDirView->getCurrentSelection();
|
||||
|
||||
if (curRights.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QMessageBox::warning(this, tr("Delete remote decks"), tr("Are you sure you want to delete the selected decks?"),
|
||||
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &curRight : curRights) {
|
||||
deleteRemoteDeck(curRight);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckStorage::deleteRemoteDeck(const RemoteDeckList_TreeModel::Node *curRight)
|
||||
{
|
||||
if (!curRight) {
|
||||
return;
|
||||
}
|
||||
|
||||
PendingCommand *pend;
|
||||
if (const auto *dir = dynamic_cast<const RemoteDeckList_TreeModel::DirectoryNode *>(curRight)) {
|
||||
QString targetPath = dir->getPath();
|
||||
if (targetPath.isEmpty())
|
||||
return;
|
||||
if (targetPath.length() > MAX_NAME_LENGTH) {
|
||||
qCritical() << "target path to delete is too long" << targetPath;
|
||||
return;
|
||||
}
|
||||
Command_DeckDelDir cmd;
|
||||
cmd.set_path(targetPath.toStdString());
|
||||
pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabDeckStorage::deleteFolderFinished);
|
||||
} else {
|
||||
const auto *deckNode = dynamic_cast<const RemoteDeckList_TreeModel::FileNode *>(curRight);
|
||||
Command_DeckDel cmd;
|
||||
cmd.set_deck_id(deckNode->getId());
|
||||
pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabDeckStorage::deleteDeckFinished);
|
||||
}
|
||||
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabDeckStorage::deleteDeckFinished(const Response &response, const CommandContainer &commandContainer)
|
||||
{
|
||||
if (response.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Command_DeckDel &cmd = commandContainer.session_command(0).GetExtension(Command_DeckDel::ext);
|
||||
RemoteDeckList_TreeModel::Node *toDelete = serverDirView->getNodeById(cmd.deck_id());
|
||||
if (toDelete)
|
||||
serverDirView->removeNode(toDelete);
|
||||
}
|
||||
|
||||
void TabDeckStorage::deleteFolderFinished(const Response &response, const CommandContainer &commandContainer)
|
||||
{
|
||||
if (response.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Command_DeckDelDir &cmd = commandContainer.session_command(0).GetExtension(Command_DeckDelDir::ext);
|
||||
RemoteDeckList_TreeModel::Node *toDelete = serverDirView->getNodeByPath(QString::fromStdString(cmd.path()));
|
||||
if (toDelete)
|
||||
serverDirView->removeNode(toDelete);
|
||||
}
|
||||
93
cockatrice/src/interface/widgets/tabs/tab_deck_storage.h
Normal file
93
cockatrice/src/interface/widgets/tabs/tab_deck_storage.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* @file tab_deck_storage.h
|
||||
* @ingroup DeckStorageWidgets
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_DECK_STORAGE_H
|
||||
#define TAB_DECK_STORAGE_H
|
||||
|
||||
#include "../interface/widgets/server/remote/remote_decklist_tree_widget.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
|
||||
class ServerInfo_User;
|
||||
class AbstractClient;
|
||||
class QTreeView;
|
||||
class QFileSystemModel;
|
||||
class QToolBar;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QGroupBox;
|
||||
class CommandContainer;
|
||||
class Response;
|
||||
class DeckLoader;
|
||||
|
||||
class TabDeckStorage : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
AbstractClient *client;
|
||||
QTreeView *localDirView;
|
||||
QFileSystemModel *localDirModel;
|
||||
QToolBar *leftToolBar, *rightToolBar;
|
||||
RemoteDeckList_TreeWidget *serverDirView;
|
||||
QGroupBox *leftGroupBox, *rightGroupBox;
|
||||
|
||||
QAction *aOpenLocalDeck, *aRenameLocal, *aUpload, *aNewLocalFolder, *aDeleteLocalDeck;
|
||||
QAction *aOpenDecksFolder;
|
||||
QAction *aOpenRemoteDeck, *aDownload, *aNewFolder, *aDeleteRemoteDeck;
|
||||
QString getTargetPath() const;
|
||||
|
||||
void setRemoteEnabled(bool enabled);
|
||||
|
||||
void uploadDeck(const QString &filePath, const QString &targetPath);
|
||||
void deleteRemoteDeck(const RemoteDeckList_TreeModel::Node *node);
|
||||
|
||||
void downloadNodeAtIndex(const QModelIndex &curLeft, const QModelIndex &curRight);
|
||||
|
||||
private slots:
|
||||
void handleConnected(const ServerInfo_User &userInfo);
|
||||
void handleConnectionChanged(ClientStatus status);
|
||||
|
||||
void actLocalDoubleClick(const QModelIndex &curLeft);
|
||||
void actOpenLocalDeck();
|
||||
|
||||
void actRenameLocal();
|
||||
|
||||
void actUpload();
|
||||
void uploadFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void actNewLocalFolder();
|
||||
void actDeleteLocalDeck();
|
||||
|
||||
void actOpenDecksFolder();
|
||||
|
||||
void actRemoteDoubleClick(const QModelIndex &curRight);
|
||||
void actOpenRemoteDeck();
|
||||
void openRemoteDeckFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void actDownload();
|
||||
void downloadFinished(const Response &r, const CommandContainer &commandContainer, const QVariant &extraData);
|
||||
|
||||
void actNewFolder();
|
||||
void newFolderFinished(const Response &response, const CommandContainer &commandContainer);
|
||||
|
||||
void actDeleteRemoteDeck();
|
||||
void deleteFolderFinished(const Response &response, const CommandContainer &commandContainer);
|
||||
void deleteDeckFinished(const Response &response, const CommandContainer &commandContainer);
|
||||
|
||||
public:
|
||||
TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User *currentUserInfo);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Deck Storage");
|
||||
}
|
||||
signals:
|
||||
void openDeckEditor(const DeckLoader *deckLoader);
|
||||
};
|
||||
|
||||
#endif
|
||||
1508
cockatrice/src/interface/widgets/tabs/tab_game.cpp
Normal file
1508
cockatrice/src/interface/widgets/tabs/tab_game.cpp
Normal file
File diff suppressed because it is too large
Load diff
205
cockatrice/src/interface/widgets/tabs/tab_game.h
Normal file
205
cockatrice/src/interface/widgets/tabs/tab_game.h
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/**
|
||||
* @file tab_game.h
|
||||
* @ingroup Tabs
|
||||
* @ingroup GameWidgets
|
||||
* @ingroup Lobby
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_GAME_H
|
||||
#define TAB_GAME_H
|
||||
|
||||
#include "../game/abstract_game.h"
|
||||
#include "../game/log/message_log_widget.h"
|
||||
#include "../game/player/player.h"
|
||||
#include "../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "../interface/widgets/replay/replay_manager.h"
|
||||
#include "../interface/widgets/visual_deck_storage/visual_deck_storage_widget.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <QCompleter>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMap>
|
||||
#include <libcockatrice/protocol/pb/event_leave.pb.h>
|
||||
|
||||
class ServerInfo_PlayerProperties;
|
||||
class TabbedDeckViewContainer;
|
||||
inline Q_LOGGING_CATEGORY(TabGameLog, "tab_game");
|
||||
|
||||
class UserListProxy;
|
||||
class DeckViewContainer;
|
||||
class AbstractClient;
|
||||
class CardDatabase;
|
||||
class GameView;
|
||||
class GameScene;
|
||||
class ReplayManager;
|
||||
class CardInfoFrameWidget;
|
||||
class QTimer;
|
||||
class QSplitter;
|
||||
class QLabel;
|
||||
class QToolButton;
|
||||
class QMenu;
|
||||
class ZoneViewLayout;
|
||||
class ZoneViewWidget;
|
||||
class PhasesToolbar;
|
||||
class PlayerListWidget;
|
||||
class ReplayTimelineWidget;
|
||||
class CardZone;
|
||||
class AbstractCardItem;
|
||||
class CardItem;
|
||||
class DeckLoader;
|
||||
class QVBoxLayout;
|
||||
class QHBoxLayout;
|
||||
class GameReplay;
|
||||
class LineEditCompleter;
|
||||
class QDockWidget;
|
||||
class QStackedWidget;
|
||||
|
||||
class TabGame : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
AbstractGame *game;
|
||||
const UserListProxy *userListProxy;
|
||||
ReplayManager *replayManager;
|
||||
QStringList gameTypes;
|
||||
QCompleter *completer;
|
||||
QStringList autocompleteUserList;
|
||||
QStackedWidget *mainWidget;
|
||||
|
||||
CardInfoFrameWidget *cardInfoFrameWidget;
|
||||
PlayerListWidget *playerListWidget;
|
||||
QLabel *timeElapsedLabel;
|
||||
MessageLogWidget *messageLog;
|
||||
QLabel *sayLabel;
|
||||
LineEditCompleter *sayEdit;
|
||||
PhasesToolbar *phasesToolbar;
|
||||
GameScene *scene;
|
||||
GameView *gameView;
|
||||
QMap<int, TabbedDeckViewContainer *> deckViewContainers;
|
||||
QVBoxLayout *deckViewContainerLayout;
|
||||
QWidget *gamePlayAreaWidget, *deckViewContainerWidget;
|
||||
QDockWidget *cardInfoDock, *messageLayoutDock, *playerListDock, *replayDock;
|
||||
QAction *playersSeparator;
|
||||
QMenu *gameMenu, *viewMenu, *cardInfoDockMenu, *messageLayoutDockMenu, *playerListDockMenu, *replayDockMenu;
|
||||
TearOffMenu *phasesMenu;
|
||||
QAction *aGameInfo, *aConcede, *aLeaveGame, *aCloseReplay, *aNextPhase, *aNextPhaseAction, *aNextTurn,
|
||||
*aReverseTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
|
||||
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aMessageLayoutDockVisible, *aMessageLayoutDockFloating,
|
||||
*aPlayerListDockVisible, *aPlayerListDockFloating, *aReplayDockVisible, *aReplayDockFloating;
|
||||
QAction *aFocusChat;
|
||||
QList<QAction *> phaseActions;
|
||||
QAction *aCardMenu;
|
||||
|
||||
Player *addPlayer(Player *newPlayer);
|
||||
void addLocalPlayer(Player *newPlayer, int playerId);
|
||||
void processRemotePlayerDeckSelect(QString deckList, int playerId, QString playerName);
|
||||
void processMultipleRemotePlayerDeckSelect(QVector<QPair<int, QPair<QString, QString>>> playerIdDeckMap);
|
||||
void processLocalPlayerDeckSelect(Player *localPlayer, int playerId, ServerInfo_Player playerInfo);
|
||||
void loadDeckForLocalPlayer(Player *localPlayer, int playerId, ServerInfo_Player playerInfo);
|
||||
void processLocalPlayerReady(int playerId, ServerInfo_Player playerInfo);
|
||||
void createZoneForPlayer(Player *newPlayer, int playerId);
|
||||
|
||||
void startGame(bool resuming);
|
||||
void stopGame();
|
||||
void closeGame();
|
||||
bool leaveGame();
|
||||
|
||||
Player *setActivePlayer(int id);
|
||||
void setActivePhase(int phase);
|
||||
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(GameReplay *replay);
|
||||
signals:
|
||||
void gameClosing(TabGame *tab);
|
||||
void containerProcessingStarted(const GameEventContext &context);
|
||||
void containerProcessingDone();
|
||||
void openMessageDialog(const QString &userName, bool focus);
|
||||
void openDeckEditor(const DeckLoader *deck);
|
||||
void notIdle();
|
||||
|
||||
void phaseChanged(int phase);
|
||||
void gameLeft();
|
||||
void chatMessageSent(QString chatMessage);
|
||||
void turnAdvanced();
|
||||
void arrowDeletionRequested(int arrowId);
|
||||
|
||||
private slots:
|
||||
void adminLockChanged(bool lock);
|
||||
void newCardAdded(AbstractCardItem *card);
|
||||
void setCardMenu(QMenu *menu);
|
||||
|
||||
void actGameInfo();
|
||||
void actConcede();
|
||||
void actRemoveLocalArrows();
|
||||
void actRotateViewCW();
|
||||
void actRotateViewCCW();
|
||||
void actSay();
|
||||
void actPhaseAction();
|
||||
void actNextPhase();
|
||||
void actNextPhaseAction();
|
||||
|
||||
void addMentionTag(const QString &value);
|
||||
void linkCardToChat(const QString &cardName);
|
||||
|
||||
void refreshShortcuts();
|
||||
|
||||
void loadLayout();
|
||||
void actCompleterChanged();
|
||||
void notifyPlayerJoin(QString playerName);
|
||||
void notifyPlayerKicked();
|
||||
void processPlayerLeave(Player *leavingPlayer);
|
||||
void actResetLayout();
|
||||
void freeDocksSize();
|
||||
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
void dockVisibleTriggered();
|
||||
void dockFloatingTriggered();
|
||||
void dockTopLevelChanged(bool topLevel);
|
||||
|
||||
protected slots:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
public:
|
||||
TabGame(TabSupervisor *_tabSupervisor,
|
||||
QList<AbstractClient *> &_clients,
|
||||
const Event_GameJoined &event,
|
||||
const QMap<int, QString> &_roomGameTypes);
|
||||
void connectToGameState();
|
||||
void connectToPlayerManager();
|
||||
void connectToGameEventHandler();
|
||||
void connectMessageLogToGameEventHandler();
|
||||
void connectPlayerListToGameEventHandler();
|
||||
TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay);
|
||||
~TabGame() override;
|
||||
void retranslateUi() override;
|
||||
void updatePlayerListDockTitle();
|
||||
bool closeRequest() override;
|
||||
|
||||
QString getTabText() const override;
|
||||
|
||||
AbstractGame *getGame() const
|
||||
{
|
||||
return game;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void viewCardInfo(const CardRef &cardRef = {}) const;
|
||||
void resetChatAndPhase();
|
||||
void updateTimeElapsedLabel(QString newTime);
|
||||
void addPlayerToAutoCompleteList(QString playerName);
|
||||
void removePlayerFromAutoCompleteList(QString playerName);
|
||||
void removeSpectator(int spectatorId, ServerInfo_User spectator);
|
||||
void processLocalPlayerSideboardLocked(int playerId, bool sideboardLocked);
|
||||
void processLocalPlayerReadyStateChanged(int playerId, bool ready);
|
||||
void emitUserEvent();
|
||||
};
|
||||
|
||||
#endif
|
||||
14
cockatrice/src/interface/widgets/tabs/tab_home.cpp
Normal file
14
cockatrice/src/interface/widgets/tabs/tab_home.cpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#include "tab_home.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QPushButton>
|
||||
|
||||
TabHome::TabHome(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client)
|
||||
{
|
||||
homeWidget = new HomeWidget(this, tabSupervisor);
|
||||
setCentralWidget(homeWidget);
|
||||
}
|
||||
|
||||
void TabHome::retranslateUi()
|
||||
{
|
||||
}
|
||||
35
cockatrice/src/interface/widgets/tabs/tab_home.h
Normal file
35
cockatrice/src/interface/widgets/tabs/tab_home.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* @file tab_home.h
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_HOME_H
|
||||
#define TAB_HOME_H
|
||||
|
||||
#include "../interface/widgets/general/home_widget.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <qgroupbox.h>
|
||||
|
||||
class AbstractClient;
|
||||
|
||||
class TabHome : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
AbstractClient *client;
|
||||
HomeWidget *homeWidget;
|
||||
|
||||
public:
|
||||
TabHome(TabSupervisor *_tabSupervisor, AbstractClient *_client);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Home");
|
||||
}
|
||||
};
|
||||
|
||||
#endif // TAB_HOME_H
|
||||
348
cockatrice/src/interface/widgets/tabs/tab_logs.cpp
Normal file
348
cockatrice/src/interface/widgets/tabs/tab_logs.cpp
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
#include "tab_logs.h"
|
||||
|
||||
#include "../dialogs/dlg_manage_sets.h"
|
||||
#include "../interface/widgets/utility/custom_line_edit.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QSpinBox>
|
||||
#include <QTabWidget>
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/moderator_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_viewlog_history.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
|
||||
TabLog::TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client)
|
||||
{
|
||||
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();
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
TabLog::~TabLog()
|
||||
{
|
||||
}
|
||||
|
||||
void TabLog::retranslateUi()
|
||||
{
|
||||
}
|
||||
|
||||
void TabLog::getClicked()
|
||||
{
|
||||
if (findUsername->text().isEmpty() && findIPAddress->text().isEmpty() && findGameName->text().isEmpty() &&
|
||||
findGameID->text().isEmpty() && findMessage->text().isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("You must select at least one filter."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lastHour->isChecked() && !today->isChecked() && !pastDays->isChecked()) {
|
||||
pastDays->setChecked(true);
|
||||
pastXDays->setValue(20);
|
||||
}
|
||||
|
||||
if (pastDays->isChecked() && pastXDays->value() == 0) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("You have to select a valid number of days to locate."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mainRoom->isChecked() && !gameRoom->isChecked() && !privateChat->isChecked()) {
|
||||
mainRoom->setChecked(true);
|
||||
gameRoom->setChecked(true);
|
||||
privateChat->setChecked(true);
|
||||
}
|
||||
|
||||
if (maximumResults->value() == 0)
|
||||
maximumResults->setValue(1000);
|
||||
|
||||
int dateRange = 0;
|
||||
if (lastHour->isChecked())
|
||||
dateRange = 1;
|
||||
|
||||
if (today->isChecked())
|
||||
dateRange = 24;
|
||||
|
||||
if (pastDays->isChecked())
|
||||
dateRange = pastXDays->value() * 24;
|
||||
|
||||
Command_ViewLogHistory cmd;
|
||||
cmd.set_user_name(findUsername->text().toStdString());
|
||||
cmd.set_ip_address(findIPAddress->text().toStdString());
|
||||
cmd.set_game_name(findGameName->text().toStdString());
|
||||
cmd.set_game_id(findGameID->text().toStdString());
|
||||
cmd.set_message(findMessage->text().toStdString());
|
||||
if (mainRoom->isChecked()) {
|
||||
cmd.add_log_location("room");
|
||||
};
|
||||
if (gameRoom->isChecked()) {
|
||||
cmd.add_log_location("game");
|
||||
};
|
||||
if (privateChat->isChecked()) {
|
||||
cmd.add_log_location("chat");
|
||||
};
|
||||
cmd.set_date_range(dateRange);
|
||||
cmd.set_maximum_results(maximumResults->value());
|
||||
PendingCommand *pend = client->prepareModeratorCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabLog::viewLogHistory_processResponse);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabLog::clearClicked()
|
||||
{
|
||||
findUsername->clear();
|
||||
findIPAddress->clear();
|
||||
findGameName->clear();
|
||||
findGameID->clear();
|
||||
findMessage->clear();
|
||||
pastXDays->clear();
|
||||
maximumResults->clear();
|
||||
mainRoom->setChecked(false);
|
||||
gameRoom->setChecked(false);
|
||||
privateChat->setChecked(false);
|
||||
pastDays->setAutoExclusive(false);
|
||||
pastDays->setChecked(false);
|
||||
today->setAutoExclusive(false);
|
||||
today->setChecked(false);
|
||||
lastHour->setAutoExclusive(false);
|
||||
lastHour->setChecked(false);
|
||||
pastDays->setAutoExclusive(true);
|
||||
today->setAutoExclusive(true);
|
||||
lastHour->setAutoExclusive(true);
|
||||
}
|
||||
|
||||
void TabLog::createDock()
|
||||
{
|
||||
labelFindUserName = new QLabel(tr("Username: "));
|
||||
findUsername = new LineEditUnfocusable("");
|
||||
findUsername->setMaxLength(MAX_NAME_LENGTH);
|
||||
findUsername->setAlignment(Qt::AlignCenter);
|
||||
labelFindIPAddress = new QLabel(tr("IP Address: "));
|
||||
findIPAddress = new LineEditUnfocusable("");
|
||||
findIPAddress->setMaxLength(MAX_NAME_LENGTH);
|
||||
findIPAddress->setAlignment(Qt::AlignCenter);
|
||||
labelFindGameName = new QLabel(tr("Game Name: "));
|
||||
findGameName = new LineEditUnfocusable("");
|
||||
findGameName->setMaxLength(MAX_NAME_LENGTH);
|
||||
findGameName->setAlignment(Qt::AlignCenter);
|
||||
labelFindGameID = new QLabel(tr("GameID: "));
|
||||
findGameID = new LineEditUnfocusable("");
|
||||
findGameID->setMaxLength(MAX_NAME_LENGTH);
|
||||
findGameID->setAlignment(Qt::AlignCenter);
|
||||
labelMessage = new QLabel(tr("Message: "));
|
||||
findMessage = new LineEditUnfocusable("");
|
||||
findMessage->setMaxLength(MAX_TEXT_LENGTH);
|
||||
findMessage->setAlignment(Qt::AlignCenter);
|
||||
|
||||
mainRoom = new QCheckBox(tr("Main Room"));
|
||||
gameRoom = new QCheckBox(tr("Game Room"));
|
||||
privateChat = new QCheckBox(tr("Private Chat"));
|
||||
|
||||
pastDays = new QRadioButton(tr("Past X Days: "));
|
||||
today = new QRadioButton(tr("Today"));
|
||||
lastHour = new QRadioButton(tr("Last Hour"));
|
||||
pastXDays = new QSpinBox;
|
||||
pastXDays->setMaximum(20);
|
||||
|
||||
labelMaximum = new QLabel(tr("Maximum Results: "));
|
||||
maximumResults = new QSpinBox;
|
||||
maximumResults->setMaximum(1000);
|
||||
|
||||
labelDescription = new QLabel(tr(
|
||||
"At least one filter is required.\nThe more information you put in, the more specific your results will be."));
|
||||
|
||||
getButton = new QPushButton(tr("Get User Logs"));
|
||||
getButton->setAutoDefault(true);
|
||||
connect(getButton, &QPushButton::clicked, this, &TabLog::getClicked);
|
||||
|
||||
clearButton = new QPushButton(tr("Clear Filters"));
|
||||
clearButton->setAutoDefault(true);
|
||||
connect(clearButton, &QPushButton::clicked, this, &TabLog::clearClicked);
|
||||
|
||||
criteriaGrid = new QGridLayout;
|
||||
criteriaGrid->addWidget(labelFindUserName, 0, 0);
|
||||
criteriaGrid->addWidget(findUsername, 0, 1);
|
||||
criteriaGrid->addWidget(labelFindIPAddress, 1, 0);
|
||||
criteriaGrid->addWidget(findIPAddress, 1, 1);
|
||||
criteriaGrid->addWidget(labelFindGameName, 2, 0);
|
||||
criteriaGrid->addWidget(findGameName, 2, 1);
|
||||
criteriaGrid->addWidget(labelFindGameID, 3, 0);
|
||||
criteriaGrid->addWidget(findGameID, 3, 1);
|
||||
criteriaGrid->addWidget(labelMessage, 4, 0);
|
||||
criteriaGrid->addWidget(findMessage, 4, 1);
|
||||
|
||||
criteriaGroupBox = new QGroupBox(tr("Filters"));
|
||||
criteriaGroupBox->setLayout(criteriaGrid);
|
||||
criteriaGroupBox->setMaximumSize(500, 300);
|
||||
criteriaGroupBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
locationGrid = new QGridLayout;
|
||||
locationGrid->addWidget(mainRoom, 0, 0);
|
||||
locationGrid->addWidget(gameRoom, 0, 1);
|
||||
locationGrid->addWidget(privateChat, 0, 2);
|
||||
|
||||
locationGroupBox = new QGroupBox(tr("Log Locations"));
|
||||
locationGroupBox->setLayout(locationGrid);
|
||||
|
||||
rangeGrid = new QGridLayout;
|
||||
rangeGrid->addWidget(pastDays, 0, 0);
|
||||
rangeGrid->addWidget(pastXDays, 0, 1);
|
||||
rangeGrid->addWidget(today, 0, 2);
|
||||
rangeGrid->addWidget(lastHour, 0, 3);
|
||||
|
||||
rangeGroupBox = new QGroupBox(tr("Date Range"));
|
||||
rangeGroupBox->setLayout(rangeGrid);
|
||||
|
||||
maxResultsGrid = new QGridLayout;
|
||||
maxResultsGrid->addWidget(labelMaximum, 0, 0);
|
||||
maxResultsGrid->addWidget(maximumResults, 0, 1);
|
||||
|
||||
maxResultsGroupBox = new QGroupBox(tr("Maximum Results"));
|
||||
maxResultsGroupBox->setLayout(maxResultsGrid);
|
||||
|
||||
descriptionGrid = new QGridLayout;
|
||||
descriptionGrid->addWidget(labelDescription, 0, 0);
|
||||
|
||||
descriptionGroupBox = new QGroupBox(tr(""));
|
||||
descriptionGroupBox->setLayout(descriptionGrid);
|
||||
|
||||
buttonGrid = new QGridLayout;
|
||||
buttonGrid->addWidget(getButton, 0, 0);
|
||||
buttonGrid->addWidget(clearButton, 0, 1);
|
||||
|
||||
buttonGroupBox = new QGroupBox(tr(""));
|
||||
buttonGroupBox->setLayout(buttonGrid);
|
||||
|
||||
mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addWidget(criteriaGroupBox);
|
||||
mainLayout->addWidget(locationGroupBox);
|
||||
mainLayout->addWidget(rangeGroupBox);
|
||||
mainLayout->addWidget(maxResultsGroupBox);
|
||||
mainLayout->addWidget(descriptionGroupBox);
|
||||
mainLayout->addWidget(buttonGroupBox);
|
||||
mainLayout->setAlignment(Qt::AlignCenter);
|
||||
|
||||
searchDockContents = new QWidget(this);
|
||||
searchDockContents->setLayout(mainLayout);
|
||||
|
||||
searchDock = new QDockWidget(this);
|
||||
searchDock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
|
||||
searchDock->setWidget(searchDockContents);
|
||||
}
|
||||
|
||||
void TabLog::viewLogHistory_processResponse(const Response &resp)
|
||||
{
|
||||
const Response_ViewLogHistory &response = resp.GetExtension(Response_ViewLogHistory::ext);
|
||||
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);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, searchDock);
|
||||
searchDock->setVisible(true);
|
||||
}
|
||||
71
cockatrice/src/interface/widgets/tabs/tab_logs.h
Normal file
71
cockatrice/src/interface/widgets/tabs/tab_logs.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* @file tab_logs.h
|
||||
* @ingroup ServerTabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_LOG_H
|
||||
#define TAB_LOG_H
|
||||
|
||||
#include "tab.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class AbstractClient;
|
||||
class LineEditUnfocusable;
|
||||
|
||||
class QGroupBox;
|
||||
class QPushButton;
|
||||
class QSpinBox;
|
||||
class QCheckBox;
|
||||
class QRadioButton;
|
||||
class QLabel;
|
||||
class QDockWidget;
|
||||
class QWidget;
|
||||
class QGridLayout;
|
||||
class QVBoxLayout;
|
||||
class QTableWidget;
|
||||
class CommandContainer;
|
||||
class Response;
|
||||
class AbstractClient;
|
||||
|
||||
class TabLog : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
AbstractClient *client;
|
||||
QLabel *labelFindUserName, *labelFindIPAddress, *labelFindGameName, *labelFindGameID, *labelMessage, *labelMaximum,
|
||||
*labelDescription;
|
||||
LineEditUnfocusable *findUsername, *findIPAddress, *findGameName, *findGameID, *findMessage;
|
||||
QCheckBox *mainRoom, *gameRoom, *privateChat;
|
||||
QRadioButton *pastDays, *today, *lastHour;
|
||||
QSpinBox *maximumResults, *pastXDays;
|
||||
QDockWidget *searchDock;
|
||||
QWidget *searchDockContents;
|
||||
QPushButton *getButton, *clearButton;
|
||||
QGridLayout *criteriaGrid, *locationGrid, *rangeGrid, *maxResultsGrid, *descriptionGrid, *buttonGrid;
|
||||
QGroupBox *criteriaGroupBox, *locationGroupBox, *rangeGroupBox, *maxResultsGroupBox, *descriptionGroupBox,
|
||||
*buttonGroupBox;
|
||||
QVBoxLayout *mainLayout;
|
||||
QTableWidget *roomTable, *gameTable, *chatTable;
|
||||
|
||||
void createDock();
|
||||
signals:
|
||||
|
||||
private slots:
|
||||
void getClicked();
|
||||
void clearClicked();
|
||||
void viewLogHistory_processResponse(const Response &resp);
|
||||
void restartLayout();
|
||||
|
||||
public:
|
||||
TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client);
|
||||
~TabLog() override;
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Logs");
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
170
cockatrice/src/interface/widgets/tabs/tab_message.cpp
Normal file
170
cockatrice/src/interface/widgets/tabs/tab_message.cpp
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#include "tab_message.h"
|
||||
|
||||
#include "../client/sound_engine.h"
|
||||
#include "../interface/widgets/server/chat_view/chat_view.h"
|
||||
#include "../interface/widgets/server/user/user_list_manager.h"
|
||||
#include "../interface/widgets/utility/custom_line_edit.h"
|
||||
#include "../main.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QMenu>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/event_user_message.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||
#include <libcockatrice/protocol/pb/session_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/cache_settings.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
|
||||
TabMessage::TabMessage(TabSupervisor *_tabSupervisor,
|
||||
AbstractClient *_client,
|
||||
const ServerInfo_User &_ownUserInfo,
|
||||
const ServerInfo_User &_otherUserInfo)
|
||||
: Tab(_tabSupervisor), client(_client), ownUserInfo(new ServerInfo_User(_ownUserInfo)),
|
||||
otherUserInfo(new ServerInfo_User(_otherUserInfo)), userOnline(true)
|
||||
{
|
||||
chatView = new ChatView(tabSupervisor, 0, true);
|
||||
connect(chatView, &ChatView::showCardInfoPopup, this, &TabMessage::showCardInfoPopup);
|
||||
connect(chatView, &ChatView::deleteCardInfoPopup, this, &TabMessage::deleteCardInfoPopup);
|
||||
connect(chatView, &ChatView::addMentionTag, this, &TabMessage::addMentionTag);
|
||||
sayEdit = new LineEditUnfocusable;
|
||||
sayEdit->setMaxLength(MAX_TEXT_LENGTH);
|
||||
connect(sayEdit, &LineEditUnfocusable::returnPressed, this, &TabMessage::sendMessage);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(chatView);
|
||||
vbox->addWidget(sayEdit);
|
||||
|
||||
aLeave = new QAction(this);
|
||||
connect(aLeave, &QAction::triggered, this, &TabMessage::closeRequest);
|
||||
|
||||
messageMenu = new QMenu(this);
|
||||
messageMenu->addAction(aLeave);
|
||||
addTabMenu(messageMenu);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(vbox);
|
||||
setCentralWidget(mainWidget);
|
||||
}
|
||||
|
||||
TabMessage::~TabMessage()
|
||||
{
|
||||
delete ownUserInfo;
|
||||
delete otherUserInfo;
|
||||
}
|
||||
|
||||
void TabMessage::addMentionTag(QString mentionTag)
|
||||
{
|
||||
sayEdit->insert(mentionTag + " ");
|
||||
sayEdit->setFocus();
|
||||
}
|
||||
|
||||
void TabMessage::retranslateUi()
|
||||
{
|
||||
messageMenu->setTitle(tr("Private &chat"));
|
||||
aLeave->setText(tr("&Leave"));
|
||||
}
|
||||
|
||||
void TabMessage::tabActivated()
|
||||
{
|
||||
if (!sayEdit->hasFocus())
|
||||
sayEdit->setFocus();
|
||||
}
|
||||
|
||||
QString TabMessage::getUserName() const
|
||||
{
|
||||
return QString::fromStdString(otherUserInfo->name());
|
||||
}
|
||||
|
||||
QString TabMessage::getTabText() const
|
||||
{
|
||||
return tr("%1 - Private chat").arg(QString::fromStdString(otherUserInfo->name()));
|
||||
}
|
||||
|
||||
void TabMessage::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
emit talkClosing(this);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void TabMessage::sendMessage()
|
||||
{
|
||||
if (sayEdit->text().isEmpty() || !userOnline)
|
||||
return;
|
||||
|
||||
Command_Message cmd;
|
||||
cmd.set_user_name(otherUserInfo->name());
|
||||
cmd.set_message(sayEdit->text().toStdString());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabMessage::messageSent);
|
||||
client->sendCommand(pend);
|
||||
|
||||
sayEdit->clear();
|
||||
}
|
||||
|
||||
void TabMessage::messageSent(const Response &response)
|
||||
{
|
||||
if (response.response_code() == Response::RespInIgnoreList)
|
||||
chatView->appendMessage(tr(
|
||||
"This user is ignoring you, they cannot see your messages in main chat and you cannot join their games."));
|
||||
}
|
||||
|
||||
void TabMessage::processUserMessageEvent(const Event_UserMessage &event)
|
||||
{
|
||||
auto userInfo = event.sender_name() == otherUserInfo->name() ? otherUserInfo : ownUserInfo;
|
||||
|
||||
chatView->appendMessage(QString::fromStdString(event.message()), {}, *userInfo, true);
|
||||
if (tabSupervisor->currentIndex() != tabSupervisor->indexOf(this))
|
||||
soundEngine->playSound("private_message");
|
||||
if (SettingsCache::instance().getShowMessagePopup() && shouldShowSystemPopup(event))
|
||||
showSystemPopup(event);
|
||||
if (QString::fromStdString(event.sender_name()).toLower().simplified() == "servatrice")
|
||||
sayEdit->setDisabled(true);
|
||||
|
||||
emit userEvent();
|
||||
}
|
||||
|
||||
bool TabMessage::shouldShowSystemPopup(const Event_UserMessage &event)
|
||||
{
|
||||
return (QApplication::activeWindow() == 0 || QApplication::focusWidget() == 0 ||
|
||||
(event.sender_name() == otherUserInfo->name() &&
|
||||
tabSupervisor->currentIndex() != tabSupervisor->indexOf(this)));
|
||||
}
|
||||
|
||||
void TabMessage::showSystemPopup(const Event_UserMessage &event)
|
||||
{
|
||||
if (trayIcon) {
|
||||
disconnect(trayIcon, &QSystemTrayIcon::messageClicked, 0, 0);
|
||||
trayIcon->showMessage(tr("Private message from") + " " + otherUserInfo->name().c_str(),
|
||||
event.message().c_str());
|
||||
connect(trayIcon, &QSystemTrayIcon::messageClicked, this, &TabMessage::messageClicked);
|
||||
} else {
|
||||
qCWarning(TabMessageLog) << "Error: trayIcon is NULL. TabMessage::showSystemPopup failed";
|
||||
}
|
||||
}
|
||||
|
||||
void TabMessage::messageClicked()
|
||||
{
|
||||
tabSupervisor->setCurrentIndex(tabSupervisor->indexOf(this));
|
||||
activateWindow();
|
||||
emit maximizeClient();
|
||||
}
|
||||
|
||||
void TabMessage::processUserLeft()
|
||||
{
|
||||
chatView->appendMessage(tr("%1 has left the server.").arg(QString::fromStdString(otherUserInfo->name())));
|
||||
userOnline = false;
|
||||
}
|
||||
|
||||
void TabMessage::processUserJoined(const ServerInfo_User &_userInfo)
|
||||
{
|
||||
chatView->appendMessage(tr("%1 has joined the server.").arg(QString::fromStdString(otherUserInfo->name())));
|
||||
userOnline = true;
|
||||
*otherUserInfo = _userInfo;
|
||||
}
|
||||
70
cockatrice/src/interface/widgets/tabs/tab_message.h
Normal file
70
cockatrice/src/interface/widgets/tabs/tab_message.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* @file tab_message.h
|
||||
* @ingroup MessagingTabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_MESSAGE_H
|
||||
#define TAB_MESSAGE_H
|
||||
|
||||
#include "tab.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(TabMessageLog, "tab_message");
|
||||
|
||||
class AbstractClient;
|
||||
class ChatView;
|
||||
class LineEditUnfocusable;
|
||||
class Event_UserMessage;
|
||||
class Response;
|
||||
class ServerInfo_User;
|
||||
|
||||
class TabMessage : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
AbstractClient *client;
|
||||
QMenu *messageMenu;
|
||||
ServerInfo_User *ownUserInfo;
|
||||
ServerInfo_User *otherUserInfo;
|
||||
bool userOnline;
|
||||
|
||||
ChatView *chatView;
|
||||
LineEditUnfocusable *sayEdit;
|
||||
|
||||
QAction *aLeave;
|
||||
signals:
|
||||
void talkClosing(TabMessage *tab);
|
||||
void maximizeClient();
|
||||
private slots:
|
||||
void sendMessage();
|
||||
void messageSent(const Response &response);
|
||||
void addMentionTag(QString mentionTag);
|
||||
void messageClicked();
|
||||
|
||||
protected slots:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
public:
|
||||
TabMessage(TabSupervisor *_tabSupervisor,
|
||||
AbstractClient *_client,
|
||||
const ServerInfo_User &_ownUserInfo,
|
||||
const ServerInfo_User &_otherUserInfo);
|
||||
~TabMessage() override;
|
||||
void retranslateUi() override;
|
||||
void tabActivated() override;
|
||||
QString getUserName() const;
|
||||
QString getTabText() const override;
|
||||
|
||||
void processUserMessageEvent(const Event_UserMessage &event);
|
||||
|
||||
void processUserLeft();
|
||||
void processUserJoined(const ServerInfo_User &_userInfo);
|
||||
|
||||
private:
|
||||
bool shouldShowSystemPopup(const Event_UserMessage &event);
|
||||
void showSystemPopup(const Event_UserMessage &event);
|
||||
};
|
||||
|
||||
#endif
|
||||
619
cockatrice/src/interface/widgets/tabs/tab_replays.cpp
Normal file
619
cockatrice/src/interface/widgets/tabs/tab_replays.cpp
Normal file
|
|
@ -0,0 +1,619 @@
|
|||
#include "tab_replays.h"
|
||||
|
||||
#include "../interface/widgets/server/remote/remote_replay_list_tree_widget.h"
|
||||
#include "tab_game.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileSystemModel>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QToolBar>
|
||||
#include <QTreeView>
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/command_replay_delete_match.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_replay_download.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_replay_get_code.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_replay_modify_match.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_replay_submit_code.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_replay_added.pb.h>
|
||||
#include <libcockatrice/protocol/pb/game_replay.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_replay_download.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_replay_get_code.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/cache_settings.h>
|
||||
|
||||
TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User *currentUserInfo)
|
||||
: Tab(_tabSupervisor), client(_client)
|
||||
{
|
||||
leftGroupBox = createLeftLayout();
|
||||
rightGroupBox = createRightLayout();
|
||||
|
||||
// combine layouts
|
||||
QHBoxLayout *hbox = new QHBoxLayout;
|
||||
hbox->addWidget(leftGroupBox);
|
||||
hbox->addWidget(rightGroupBox);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(hbox);
|
||||
setCentralWidget(mainWidget);
|
||||
|
||||
connect(client, &AbstractClient::replayAddedEventReceived, this, &TabReplays::replayAddedEventReceived);
|
||||
|
||||
connect(client, &AbstractClient::userInfoChanged, this, &TabReplays::handleConnected);
|
||||
connect(client, &AbstractClient::statusChanged, this, &TabReplays::handleConnectionChanged);
|
||||
setRemoteEnabled(currentUserInfo && currentUserInfo->user_level() & ServerInfo_User::IsRegistered);
|
||||
}
|
||||
|
||||
QGroupBox *TabReplays::createLeftLayout()
|
||||
{
|
||||
localDirModel = new QFileSystemModel(this);
|
||||
localDirModel->setRootPath(SettingsCache::instance().getReplaysPath());
|
||||
localDirModel->sort(0, Qt::AscendingOrder);
|
||||
|
||||
localDirView = new QTreeView;
|
||||
localDirView->setModel(localDirModel);
|
||||
localDirView->setColumnHidden(1, true);
|
||||
localDirView->setRootIndex(localDirModel->index(localDirModel->rootPath(), 0));
|
||||
localDirView->setSortingEnabled(true);
|
||||
localDirView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
localDirView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
localDirView->header()->setSortIndicator(0, Qt::AscendingOrder);
|
||||
|
||||
// Left side layout
|
||||
/* put an invisible dummy QToolBar in the leftmost column so that the main toolbar is centered.
|
||||
* Really ugly workaround, but I couldn't figure out the proper way to make it centered */
|
||||
QToolBar *dummyToolBar = new QToolBar(this);
|
||||
QSizePolicy sizePolicy = dummyToolBar->sizePolicy();
|
||||
sizePolicy.setRetainSizeWhenHidden(true);
|
||||
dummyToolBar->setSizePolicy(sizePolicy);
|
||||
dummyToolBar->setVisible(false);
|
||||
|
||||
QToolBar *toolBar = new QToolBar(this);
|
||||
toolBar->setOrientation(Qt::Horizontal);
|
||||
toolBar->setIconSize(QSize(32, 32));
|
||||
|
||||
QToolBar *rightmostToolBar = new QToolBar(this);
|
||||
rightmostToolBar->setOrientation(Qt::Horizontal);
|
||||
rightmostToolBar->setIconSize(QSize(32, 32));
|
||||
|
||||
QGridLayout *toolBarLayout = new QGridLayout;
|
||||
toolBarLayout->addWidget(dummyToolBar, 0, 0, Qt::AlignLeft);
|
||||
toolBarLayout->addWidget(toolBar, 0, 1, Qt::AlignHCenter);
|
||||
toolBarLayout->addWidget(rightmostToolBar, 0, 2, Qt::AlignRight);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(localDirView);
|
||||
vbox->addLayout(toolBarLayout);
|
||||
QGroupBox *groupBox = new QGroupBox;
|
||||
groupBox->setLayout(vbox);
|
||||
|
||||
// Left side actions
|
||||
aOpenLocalReplay = new QAction(this);
|
||||
aOpenLocalReplay->setIcon(QPixmap("theme:icons/view"));
|
||||
connect(aOpenLocalReplay, &QAction::triggered, this, &TabReplays::actOpenLocalReplay);
|
||||
connect(localDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenLocalReplay);
|
||||
aRenameLocal = new QAction(this);
|
||||
aRenameLocal->setIcon(QPixmap("theme:icons/rename"));
|
||||
connect(aRenameLocal, &QAction::triggered, this, &TabReplays::actRenameLocal);
|
||||
aNewLocalFolder = new QAction(this);
|
||||
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
||||
connect(aNewLocalFolder, &QAction::triggered, this, &TabReplays::actNewLocalFolder);
|
||||
aDeleteLocalReplay = new QAction(this);
|
||||
aDeleteLocalReplay->setIcon(QPixmap("theme:icons/remove_row"));
|
||||
connect(aDeleteLocalReplay, &QAction::triggered, this, &TabReplays::actDeleteLocalReplay);
|
||||
|
||||
aOpenReplaysFolder = new QAction(this);
|
||||
aOpenReplaysFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_DirOpenIcon));
|
||||
connect(aOpenReplaysFolder, &QAction::triggered, this, &TabReplays::actOpenReplaysFolder);
|
||||
|
||||
// Add actions to toolbars
|
||||
toolBar->addAction(aOpenLocalReplay);
|
||||
toolBar->addAction(aRenameLocal);
|
||||
toolBar->addAction(aNewLocalFolder);
|
||||
toolBar->addAction(aDeleteLocalReplay);
|
||||
|
||||
rightmostToolBar->addAction(aOpenReplaysFolder);
|
||||
|
||||
return groupBox;
|
||||
}
|
||||
|
||||
QGroupBox *TabReplays::createRightLayout()
|
||||
{
|
||||
serverDirView = new RemoteReplayList_TreeWidget(client);
|
||||
|
||||
// Right side layout
|
||||
/* put an invisible dummy QToolBar in the leftmost column so that the main toolbar is centered.
|
||||
* Really ugly workaround, but I couldn't figure out the proper way to make it centered */
|
||||
QToolBar *dummyToolBar = new QToolBar(this);
|
||||
QSizePolicy sizePolicy = dummyToolBar->sizePolicy();
|
||||
sizePolicy.setRetainSizeWhenHidden(true);
|
||||
dummyToolBar->setSizePolicy(sizePolicy);
|
||||
dummyToolBar->setVisible(false);
|
||||
|
||||
QToolBar *toolBar = new QToolBar(this);
|
||||
toolBar->setOrientation(Qt::Horizontal);
|
||||
toolBar->setIconSize(QSize(32, 32));
|
||||
|
||||
QToolBar *rightmostToolBar = new QToolBar(this);
|
||||
rightmostToolBar->setOrientation(Qt::Horizontal);
|
||||
rightmostToolBar->setIconSize(QSize(32, 32));
|
||||
|
||||
QGridLayout *toolBarLayout = new QGridLayout;
|
||||
toolBarLayout->addWidget(dummyToolBar, 0, 0, Qt::AlignLeft);
|
||||
toolBarLayout->addWidget(toolBar, 0, 1, Qt::AlignHCenter);
|
||||
toolBarLayout->addWidget(rightmostToolBar, 0, 2, Qt::AlignRight);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(serverDirView);
|
||||
vbox->addLayout(toolBarLayout);
|
||||
QGroupBox *groupBox = new QGroupBox;
|
||||
groupBox->setLayout(vbox);
|
||||
|
||||
// Right side actions
|
||||
aOpenRemoteReplay = new QAction(this);
|
||||
aOpenRemoteReplay->setIcon(QPixmap("theme:icons/view"));
|
||||
connect(aOpenRemoteReplay, &QAction::triggered, this, &TabReplays::actOpenRemoteReplay);
|
||||
connect(serverDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenRemoteReplay);
|
||||
aDownload = new QAction(this);
|
||||
aDownload->setIcon(QPixmap("theme:icons/arrow_left_green"));
|
||||
connect(aDownload, &QAction::triggered, this, &TabReplays::actDownload);
|
||||
aKeep = new QAction(this);
|
||||
aKeep->setIcon(QPixmap("theme:icons/lock"));
|
||||
connect(aKeep, &QAction::triggered, this, &TabReplays::actKeepRemoteReplay);
|
||||
aDeleteRemoteReplay = new QAction(this);
|
||||
aDeleteRemoteReplay->setIcon(QPixmap("theme:icons/remove_row"));
|
||||
connect(aDeleteRemoteReplay, &QAction::triggered, this, &TabReplays::actDeleteRemoteReplay);
|
||||
aGetReplayCode = new QAction(this);
|
||||
aGetReplayCode->setIcon(QPixmap("theme:icons/share"));
|
||||
connect(aGetReplayCode, &QAction::triggered, this, &TabReplays::actGetReplayCode);
|
||||
|
||||
aSubmitReplayCode = new QAction(this);
|
||||
aSubmitReplayCode->setIcon(QPixmap("theme:icons/search"));
|
||||
connect(aSubmitReplayCode, &QAction::triggered, this, &TabReplays::actSubmitReplayCode);
|
||||
|
||||
// Add actions to toolbars
|
||||
toolBar->addAction(aOpenRemoteReplay);
|
||||
toolBar->addAction(aDownload);
|
||||
toolBar->addAction(aKeep);
|
||||
toolBar->addAction(aDeleteRemoteReplay);
|
||||
toolBar->addAction(aGetReplayCode);
|
||||
|
||||
rightmostToolBar->addAction(aSubmitReplayCode);
|
||||
|
||||
return groupBox;
|
||||
}
|
||||
|
||||
void TabReplays::retranslateUi()
|
||||
{
|
||||
leftGroupBox->setTitle(tr("Local file system"));
|
||||
rightGroupBox->setTitle(tr("Server replay storage"));
|
||||
|
||||
aOpenLocalReplay->setText(tr("Watch replay"));
|
||||
aRenameLocal->setText(tr("Rename"));
|
||||
aNewLocalFolder->setText(tr("New folder"));
|
||||
aDeleteLocalReplay->setText(tr("Delete"));
|
||||
aOpenReplaysFolder->setText(tr("Open replays folder"));
|
||||
aOpenRemoteReplay->setText(tr("Watch replay"));
|
||||
aDownload->setText(tr("Download replay"));
|
||||
aKeep->setText(tr("Toggle expiration lock"));
|
||||
aDeleteRemoteReplay->setText(tr("Delete"));
|
||||
aGetReplayCode->setText(tr("Get replay share code"));
|
||||
|
||||
aSubmitReplayCode->setText(tr("Look up replay by share code"));
|
||||
}
|
||||
|
||||
void TabReplays::handleConnected(const ServerInfo_User &userInfo)
|
||||
{
|
||||
setRemoteEnabled(userInfo.user_level() & ServerInfo_User::IsRegistered);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is only responsible for handling the disconnect. The connect is already handled elsewhere
|
||||
*/
|
||||
void TabReplays::handleConnectionChanged(ClientStatus status)
|
||||
{
|
||||
if (status == StatusDisconnected) {
|
||||
setRemoteEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::setRemoteEnabled(bool enabled)
|
||||
{
|
||||
aOpenRemoteReplay->setEnabled(enabled);
|
||||
aDownload->setEnabled(enabled);
|
||||
aKeep->setEnabled(enabled);
|
||||
aDeleteRemoteReplay->setEnabled(enabled);
|
||||
aGetReplayCode->setEnabled(enabled);
|
||||
aSubmitReplayCode->setEnabled(enabled);
|
||||
|
||||
if (enabled) {
|
||||
serverDirView->refreshTree();
|
||||
} else {
|
||||
serverDirView->clearTree();
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::actLocalDoubleClick(const QModelIndex &curLeft)
|
||||
{
|
||||
if (!localDirModel->isDir(curLeft)) {
|
||||
actOpenLocalReplay();
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::actOpenLocalReplay()
|
||||
{
|
||||
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
||||
for (const auto &curLeft : curLefts) {
|
||||
if (localDirModel->isDir(curLeft))
|
||||
continue;
|
||||
QString filePath = localDirModel->filePath(curLeft);
|
||||
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
continue;
|
||||
QByteArray _data = f.readAll();
|
||||
f.close();
|
||||
|
||||
GameReplay *replay = new GameReplay;
|
||||
replay->ParseFromArray(_data.data(), _data.size());
|
||||
|
||||
emit openReplay(replay);
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::actRenameLocal()
|
||||
{
|
||||
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
||||
for (const auto &curLeft : curLefts) {
|
||||
const QFileInfo info = localDirModel->fileInfo(curLeft);
|
||||
|
||||
const QString oldName = info.baseName();
|
||||
const QString title = info.isDir() ? tr("Rename local folder") : tr("Rename local file");
|
||||
|
||||
bool ok;
|
||||
QString newName = QInputDialog::getText(this, title, tr("New name:"), QLineEdit::Normal, oldName, &ok);
|
||||
if (!ok) { // terminate all remaining selections if user cancels
|
||||
return;
|
||||
}
|
||||
if (newName.isEmpty() || oldName == newName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString newFileName = newName;
|
||||
if (!info.suffix().isEmpty()) {
|
||||
newFileName += "." + info.suffix();
|
||||
}
|
||||
const QString newFilePath = QFileInfo(info.dir(), newFileName).filePath();
|
||||
|
||||
if (!QFile::rename(info.filePath(), newFilePath)) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Rename failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::actNewLocalFolder()
|
||||
{
|
||||
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
|
||||
|
||||
QModelIndex dirIndex;
|
||||
if (curLeft.isValid() && !localDirModel->isDir(curLeft)) {
|
||||
dirIndex = curLeft.parent();
|
||||
} else {
|
||||
dirIndex = curLeft;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
QString folderName =
|
||||
QInputDialog::getText(this, tr("New folder"), tr("Name of new folder:"), QLineEdit::Normal, "", &ok);
|
||||
if (!ok || folderName.isEmpty())
|
||||
return;
|
||||
|
||||
localDirModel->mkdir(dirIndex, folderName);
|
||||
}
|
||||
|
||||
void TabReplays::actDeleteLocalReplay()
|
||||
{
|
||||
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
|
||||
|
||||
if (curLefts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QMessageBox::warning(this, tr("Delete local file"), tr("Are you sure you want to delete the selected files?"),
|
||||
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &curLeft : curLefts) {
|
||||
if (curLeft.isValid()) {
|
||||
localDirModel->remove(curLeft);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::actOpenReplaysFolder()
|
||||
{
|
||||
QString dir = localDirModel->rootPath();
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
|
||||
}
|
||||
|
||||
void TabReplays::actRemoteDoubleClick(const QModelIndex &curRight)
|
||||
{
|
||||
if (serverDirView->getReplay(curRight)) {
|
||||
actOpenRemoteReplay();
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::actOpenRemoteReplay()
|
||||
{
|
||||
auto const curRights = serverDirView->getSelectedReplays();
|
||||
|
||||
for (const auto curRight : curRights) {
|
||||
if (!curRight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Command_ReplayDownload cmd;
|
||||
cmd.set_replay_id(curRight->replay_id());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabReplays::openRemoteReplayFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::openRemoteReplayFinished(const Response &r)
|
||||
{
|
||||
if (r.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext);
|
||||
GameReplay *replay = new GameReplay;
|
||||
replay->ParseFromString(resp.replay_data());
|
||||
|
||||
emit openReplay(replay);
|
||||
}
|
||||
|
||||
void TabReplays::actDownload()
|
||||
{
|
||||
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
|
||||
while (!localDirModel->isDir(curLeft)) {
|
||||
curLeft = curLeft.parent();
|
||||
}
|
||||
|
||||
for (const auto curRight : serverDirView->selectionModel()->selectedRows()) {
|
||||
downloadNodeAtIndex(curLeft, curRight);
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::downloadNodeAtIndex(const QModelIndex &curLeft, const QModelIndex &curRight)
|
||||
{
|
||||
if (const auto replayMatch = serverDirView->getReplayMatch(curRight)) {
|
||||
// node at index is a folder
|
||||
const QString name =
|
||||
QString::number(replayMatch->game_id()) + "_" + QString::fromStdString(replayMatch->game_name());
|
||||
|
||||
const auto dirIndex = curLeft.isValid() ? curLeft : localDirModel->index(localDirModel->rootPath());
|
||||
const auto newDirIndex = localDirModel->mkdir(dirIndex, name);
|
||||
|
||||
int rows = serverDirView->model()->rowCount(curRight);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
const auto childIndex = serverDirView->model()->index(i, 0, curRight);
|
||||
downloadNodeAtIndex(newDirIndex, childIndex);
|
||||
}
|
||||
} else if (const auto replay = serverDirView->getReplay(curRight)) {
|
||||
// node at index is a replay
|
||||
const QString dirPath = curLeft.isValid() ? localDirModel->filePath(curLeft) : localDirModel->rootPath();
|
||||
const QString filePath = dirPath + QString("/replay_%1.cor").arg(replay->replay_id());
|
||||
|
||||
Command_ReplayDownload cmd;
|
||||
cmd.set_replay_id(replay->replay_id());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
pend->setExtraData(filePath);
|
||||
connect(pend, &PendingCommand::finished, this, &TabReplays::downloadFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
// node at index was invalid
|
||||
}
|
||||
|
||||
void TabReplays::downloadFinished(const Response &r,
|
||||
const CommandContainer & /* commandContainer */,
|
||||
const QVariant &extraData)
|
||||
{
|
||||
if (r.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext);
|
||||
QString filePath = extraData.toString();
|
||||
|
||||
const std::string &_data = resp.replay_data();
|
||||
QFile f(filePath);
|
||||
f.open(QIODevice::WriteOnly);
|
||||
f.write((const char *)_data.data(), _data.size());
|
||||
f.close();
|
||||
}
|
||||
|
||||
void TabReplays::actKeepRemoteReplay()
|
||||
{
|
||||
const auto curRights = serverDirView->getSelectedReplayMatches();
|
||||
|
||||
if (curRights.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto curRight : curRights) {
|
||||
Command_ReplayModifyMatch cmd;
|
||||
cmd.set_game_id(curRight->game_id());
|
||||
cmd.set_do_not_hide(!curRight->do_not_hide());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabReplays::keepRemoteReplayFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::keepRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer)
|
||||
{
|
||||
if (r.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Command_ReplayModifyMatch &cmd =
|
||||
commandContainer.session_command(0).GetExtension(Command_ReplayModifyMatch::ext);
|
||||
|
||||
ServerInfo_ReplayMatch temp;
|
||||
temp.set_do_not_hide(cmd.do_not_hide());
|
||||
|
||||
serverDirView->updateMatchInfo(cmd.game_id(), temp);
|
||||
}
|
||||
|
||||
void TabReplays::actDeleteRemoteReplay()
|
||||
{
|
||||
const auto curRights = serverDirView->getSelectedReplayMatches();
|
||||
|
||||
if (curRights.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QMessageBox::warning(this, tr("Delete remote replay"),
|
||||
tr("Are you sure you want to delete the selected replays?"),
|
||||
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto curRight : curRights) {
|
||||
Command_ReplayDeleteMatch cmd;
|
||||
cmd.set_game_id(curRight->game_id());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabReplays::deleteRemoteReplayFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::deleteRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer)
|
||||
{
|
||||
if (r.response_code() != Response::RespOk)
|
||||
return;
|
||||
|
||||
const Command_ReplayDeleteMatch &cmd =
|
||||
commandContainer.session_command(0).GetExtension(Command_ReplayDeleteMatch::ext);
|
||||
serverDirView->removeMatchInfo(cmd.game_id());
|
||||
}
|
||||
|
||||
void TabReplays::actGetReplayCode()
|
||||
{
|
||||
const auto curRights = serverDirView->getSelectedReplayMatches();
|
||||
if (curRights.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto curRight : curRights) {
|
||||
Command_ReplayGetCode cmd;
|
||||
cmd.set_game_id(curRight->game_id());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabReplays::getReplayCodeFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::getReplayCodeFinished(const Response &r, const CommandContainer & /*commandContainer*/)
|
||||
{
|
||||
if (r.response_code() == Response::RespFunctionNotAllowed) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setText(tr("Failed to get code"));
|
||||
msgBox.setInformativeText(
|
||||
tr("Either this server does not support replay sharing, or does not permit replay sharing for you."));
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
if (r.response_code() != Response::RespOk) {
|
||||
QMessageBox::warning(this, tr("Failed"), tr("Could not get replay code"));
|
||||
return;
|
||||
}
|
||||
|
||||
const Response_ReplayGetCode &resp = r.GetExtension(Response_ReplayGetCode::ext);
|
||||
QString code = QString::fromStdString(resp.replay_code());
|
||||
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(tr("Replay Share Code"));
|
||||
msgBox.setInformativeText(
|
||||
tr("Others can use this code to add the replay to their list of remote replays:\n%1").arg(code));
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
QPushButton *copyToClipboardButton = msgBox.addButton(tr("Copy to clipboard"), QMessageBox::ActionRole);
|
||||
connect(copyToClipboardButton, &QPushButton::clicked, this, [code] { QApplication::clipboard()->setText(code); });
|
||||
msgBox.setDefaultButton(copyToClipboardButton);
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
void TabReplays::actSubmitReplayCode()
|
||||
{
|
||||
bool ok;
|
||||
QString code = QInputDialog::getText(this, tr("Look up replay by share code"), tr("Replay share code"),
|
||||
QLineEdit::Normal, "", &ok);
|
||||
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
Command_ReplaySubmitCode cmd;
|
||||
cmd.set_replay_code(code.toStdString());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabReplays::submitReplayCodeFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabReplays::submitReplayCodeFinished(const Response &r, const CommandContainer & /*commandContainer*/)
|
||||
{
|
||||
switch (r.response_code()) {
|
||||
case Response::RespOk: {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Information);
|
||||
msgBox.setText(tr("Replay code found"));
|
||||
msgBox.setInformativeText(tr("Replay was added, or you already had access to it."));
|
||||
msgBox.exec();
|
||||
break;
|
||||
}
|
||||
case Response::RespNameNotFound:
|
||||
QMessageBox::warning(this, tr("Failed"), tr("Replay code not found"));
|
||||
break;
|
||||
case Response::RespFunctionNotAllowed: {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setText(tr("Failed to submit code"));
|
||||
msgBox.setInformativeText(
|
||||
tr("Either this server does not support replay sharing, or does not permit replay sharing for you."));
|
||||
msgBox.exec();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
QMessageBox::warning(this, tr("Failed"), tr("Unexpected error"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::replayAddedEventReceived(const Event_ReplayAdded &event)
|
||||
{
|
||||
if (event.has_match_info()) {
|
||||
// 99.9% of events will have match info (Normal Workflow)
|
||||
serverDirView->addMatchInfo(event.match_info());
|
||||
} else {
|
||||
// When a Moderator force adds a replay or a user submits a replay code, we need to refresh their view
|
||||
serverDirView->refreshTree();
|
||||
}
|
||||
}
|
||||
93
cockatrice/src/interface/widgets/tabs/tab_replays.h
Normal file
93
cockatrice/src/interface/widgets/tabs/tab_replays.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* @file tab_replays.h
|
||||
* @ingroup Replays
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_REPLAYS_H
|
||||
#define TAB_REPLAYS_H
|
||||
|
||||
#include "tab.h"
|
||||
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
|
||||
class ServerInfo_User;
|
||||
class Response;
|
||||
class AbstractClient;
|
||||
class QTreeView;
|
||||
class QFileSystemModel;
|
||||
class QToolBar;
|
||||
class QGroupBox;
|
||||
class RemoteReplayList_TreeWidget;
|
||||
class GameReplay;
|
||||
class Event_ReplayAdded;
|
||||
class CommandContainer;
|
||||
|
||||
class TabReplays : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
AbstractClient *client;
|
||||
QTreeView *localDirView;
|
||||
QFileSystemModel *localDirModel;
|
||||
RemoteReplayList_TreeWidget *serverDirView;
|
||||
QGroupBox *leftGroupBox, *rightGroupBox;
|
||||
|
||||
QAction *aOpenLocalReplay, *aRenameLocal, *aNewLocalFolder, *aDeleteLocalReplay;
|
||||
QAction *aOpenReplaysFolder;
|
||||
QAction *aOpenRemoteReplay, *aDownload, *aKeep, *aDeleteRemoteReplay, *aGetReplayCode;
|
||||
QAction *aSubmitReplayCode;
|
||||
|
||||
QGroupBox *createLeftLayout();
|
||||
QGroupBox *createRightLayout();
|
||||
|
||||
void setRemoteEnabled(bool enabled);
|
||||
|
||||
void downloadNodeAtIndex(const QModelIndex &curLeft, const QModelIndex &curRight);
|
||||
|
||||
private slots:
|
||||
void handleConnected(const ServerInfo_User &userInfo);
|
||||
void handleConnectionChanged(ClientStatus status);
|
||||
|
||||
void actLocalDoubleClick(const QModelIndex &curLeft);
|
||||
void actRenameLocal();
|
||||
void actOpenLocalReplay();
|
||||
void actNewLocalFolder();
|
||||
void actDeleteLocalReplay();
|
||||
|
||||
void actOpenReplaysFolder();
|
||||
|
||||
void actRemoteDoubleClick(const QModelIndex &curLeft);
|
||||
void actOpenRemoteReplay();
|
||||
void openRemoteReplayFinished(const Response &r);
|
||||
|
||||
void actDownload();
|
||||
void downloadFinished(const Response &r, const CommandContainer &commandContainer, const QVariant &extraData);
|
||||
|
||||
void actKeepRemoteReplay();
|
||||
void keepRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void actDeleteRemoteReplay();
|
||||
void deleteRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void actGetReplayCode();
|
||||
void getReplayCodeFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void actSubmitReplayCode();
|
||||
void submitReplayCodeFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void replayAddedEventReceived(const Event_ReplayAdded &event);
|
||||
signals:
|
||||
void openReplay(GameReplay *replay);
|
||||
|
||||
public:
|
||||
TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User *currentUserInfo);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Game Replays");
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
338
cockatrice/src/interface/widgets/tabs/tab_room.cpp
Normal file
338
cockatrice/src/interface/widgets/tabs/tab_room.cpp
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
#include "tab_room.h"
|
||||
|
||||
#include "../dialogs/dlg_settings.h"
|
||||
#include "../interface/widgets/server/chat_view/chat_view.h"
|
||||
#include "../interface/widgets/server/game_selector.h"
|
||||
#include "../interface/widgets/server/user/user_list_manager.h"
|
||||
#include "../interface/widgets/server/user/user_list_widget.h"
|
||||
#include "../main.h"
|
||||
#include "tab_account.h"
|
||||
#include "tab_supervisor.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCompleter>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QSplitter>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/get_pb_extension.h>
|
||||
#include <libcockatrice/protocol/pb/event_join_room.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_leave_room.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_list_games.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_remove_messages.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_room_say.pb.h>
|
||||
#include <libcockatrice/protocol/pb/room_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_room.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/cache_settings.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
|
||||
TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
|
||||
AbstractClient *_client,
|
||||
ServerInfo_User *_ownUser,
|
||||
const ServerInfo_Room &info)
|
||||
: Tab(_tabSupervisor), client(_client), roomId(info.room_id()), roomName(QString::fromStdString(info.name())),
|
||||
ownUser(_ownUser), userListProxy(_tabSupervisor->getUserListManager())
|
||||
{
|
||||
const int gameTypeListSize = info.gametype_list_size();
|
||||
for (int i = 0; i < gameTypeListSize; ++i)
|
||||
gameTypes.insert(info.gametype_list(i).game_type_id(),
|
||||
QString::fromStdString(info.gametype_list(i).description()));
|
||||
|
||||
QMap<int, GameTypeMap> tempMap;
|
||||
tempMap.insert(info.room_id(), gameTypes);
|
||||
gameSelector = new GameSelector(client, tabSupervisor, this, QMap<int, QString>(), tempMap, true, true);
|
||||
userList = new UserListWidget(tabSupervisor, client, UserListWidget::RoomList);
|
||||
connect(userList, SIGNAL(openMessageDialog(const QString &, bool)), this,
|
||||
SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
|
||||
chatView = new ChatView(tabSupervisor, nullptr, true, this);
|
||||
connect(chatView, &ChatView::showMentionPopup, this, &TabRoom::actShowMentionPopup);
|
||||
connect(chatView, &ChatView::messageClickedSignal, this, &TabRoom::focusTab);
|
||||
connect(chatView, &ChatView::openMessageDialog, this, &TabRoom::openMessageDialog);
|
||||
connect(chatView, &ChatView::showCardInfoPopup, this, &TabRoom::showCardInfoPopup);
|
||||
connect(chatView, &ChatView::deleteCardInfoPopup, this, &TabRoom::deleteCardInfoPopup);
|
||||
connect(chatView, &ChatView::addMentionTag, this, &TabRoom::addMentionTag);
|
||||
connect(&SettingsCache::instance(), &SettingsCache::chatMentionCompleterChanged, this,
|
||||
&TabRoom::actCompleterChanged);
|
||||
sayLabel = new QLabel;
|
||||
sayEdit = new LineEditCompleter;
|
||||
sayEdit->setMaxLength(MAX_TEXT_LENGTH);
|
||||
sayLabel->setBuddy(sayEdit);
|
||||
connect(sayEdit, &LineEditCompleter::returnPressed, this, &TabRoom::sendMessage);
|
||||
|
||||
QMenu *chatSettingsMenu = new QMenu(this);
|
||||
|
||||
aClearChat = chatSettingsMenu->addAction(QString());
|
||||
connect(aClearChat, &QAction::triggered, this, &TabRoom::actClearChat);
|
||||
|
||||
chatSettingsMenu->addSeparator();
|
||||
|
||||
aOpenChatSettings = chatSettingsMenu->addAction(QString());
|
||||
connect(aOpenChatSettings, &QAction::triggered, this, &TabRoom::actOpenChatSettings);
|
||||
|
||||
QToolButton *chatSettingsButton = new QToolButton;
|
||||
chatSettingsButton->setIcon(QPixmap("theme:icons/settings"));
|
||||
chatSettingsButton->setMenu(chatSettingsMenu);
|
||||
chatSettingsButton->setPopupMode(QToolButton::InstantPopup);
|
||||
|
||||
QHBoxLayout *sayHbox = new QHBoxLayout;
|
||||
sayHbox->addWidget(sayLabel);
|
||||
sayHbox->addWidget(sayEdit);
|
||||
sayHbox->addWidget(chatSettingsButton);
|
||||
|
||||
QVBoxLayout *chatVbox = new QVBoxLayout;
|
||||
chatVbox->addWidget(chatView);
|
||||
chatVbox->addLayout(sayHbox);
|
||||
|
||||
chatGroupBox = new QGroupBox;
|
||||
chatGroupBox->setLayout(chatVbox);
|
||||
|
||||
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
||||
splitter->addWidget(gameSelector);
|
||||
splitter->addWidget(chatGroupBox);
|
||||
|
||||
QHBoxLayout *hbox = new QHBoxLayout;
|
||||
hbox->addWidget(splitter, 3);
|
||||
hbox->addWidget(userList, 1);
|
||||
|
||||
aLeaveRoom = new QAction(this);
|
||||
connect(aLeaveRoom, &QAction::triggered, this, &TabRoom::closeRequest);
|
||||
|
||||
roomMenu = new QMenu(this);
|
||||
roomMenu->addAction(aLeaveRoom);
|
||||
addTabMenu(roomMenu);
|
||||
|
||||
const int userListSize = info.user_list_size();
|
||||
for (int i = 0; i < userListSize; ++i) {
|
||||
userList->processUserInfo(info.user_list(i), true);
|
||||
autocompleteUserList.append("@" + QString::fromStdString(info.user_list(i).name()));
|
||||
}
|
||||
userList->sortItems();
|
||||
|
||||
const int gameListSize = info.game_list_size();
|
||||
for (int i = 0; i < gameListSize; ++i)
|
||||
gameSelector->processGameInfo(info.game_list(i));
|
||||
|
||||
completer = new QCompleter(autocompleteUserList, sayEdit);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setMaxVisibleItems(5);
|
||||
completer->setFilterMode(Qt::MatchStartsWith);
|
||||
|
||||
sayEdit->setCompleter(completer);
|
||||
actCompleterChanged();
|
||||
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
||||
&TabRoom::refreshShortcuts);
|
||||
refreshShortcuts();
|
||||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(hbox);
|
||||
setCentralWidget(mainWidget);
|
||||
}
|
||||
|
||||
void TabRoom::retranslateUi()
|
||||
{
|
||||
gameSelector->retranslateUi();
|
||||
chatView->retranslateUi();
|
||||
userList->retranslateUi();
|
||||
sayLabel->setText(tr("&Say:"));
|
||||
chatGroupBox->setTitle(tr("Chat"));
|
||||
roomMenu->setTitle(tr("&Room"));
|
||||
aLeaveRoom->setText(tr("&Leave room"));
|
||||
aClearChat->setText(tr("&Clear chat"));
|
||||
aOpenChatSettings->setText(tr("Chat Settings..."));
|
||||
}
|
||||
|
||||
void TabRoom::focusTab()
|
||||
{
|
||||
activateWindow();
|
||||
tabSupervisor->setCurrentIndex(tabSupervisor->indexOf(this));
|
||||
emit maximizeClient();
|
||||
}
|
||||
|
||||
void TabRoom::actShowMentionPopup(const QString &sender)
|
||||
{
|
||||
this->actShowPopup(sender + tr(" mentioned you."));
|
||||
}
|
||||
|
||||
void TabRoom::actShowPopup(const QString &message)
|
||||
{
|
||||
if (trayIcon && (tabSupervisor->currentIndex() != tabSupervisor->indexOf(this) ||
|
||||
QApplication::activeWindow() == nullptr || QApplication::focusWidget() == nullptr)) {
|
||||
disconnect(trayIcon, &QSystemTrayIcon::messageClicked, nullptr, nullptr);
|
||||
trayIcon->showMessage(message, tr("Click to view"));
|
||||
connect(trayIcon, &QSystemTrayIcon::messageClicked, chatView, &ChatView::messageClickedSignal);
|
||||
}
|
||||
}
|
||||
|
||||
void TabRoom::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
sendRoomCommand(prepareRoomCommand(Command_LeaveRoom()));
|
||||
emit roomClosing(this);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void TabRoom::tabActivated()
|
||||
{
|
||||
if (!sayEdit->hasFocus())
|
||||
sayEdit->setFocus();
|
||||
}
|
||||
|
||||
QString TabRoom::sanitizeHtml(QString dirty) const
|
||||
{
|
||||
return dirty.replace("&", "&").replace("<", "<").replace(">", ">");
|
||||
}
|
||||
|
||||
void TabRoom::sendMessage()
|
||||
{
|
||||
if (sayEdit->text().isEmpty()) {
|
||||
return;
|
||||
} else if (completer->popup()->isVisible()) {
|
||||
completer->popup()->hide();
|
||||
return;
|
||||
} else {
|
||||
Command_RoomSay cmd;
|
||||
cmd.set_message(sayEdit->text().toStdString());
|
||||
|
||||
PendingCommand *pend = prepareRoomCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabRoom::sayFinished);
|
||||
sendRoomCommand(pend);
|
||||
sayEdit->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TabRoom::sayFinished(const Response &response)
|
||||
{
|
||||
if (response.response_code() == Response::RespChatFlood)
|
||||
chatView->appendMessage(tr("You are flooding the chat. Please wait a couple of seconds."));
|
||||
}
|
||||
|
||||
void TabRoom::actClearChat()
|
||||
{
|
||||
chatView->clearChat();
|
||||
}
|
||||
|
||||
void TabRoom::actOpenChatSettings()
|
||||
{
|
||||
DlgSettings settings(this);
|
||||
settings.setTab(4);
|
||||
settings.exec();
|
||||
}
|
||||
|
||||
void TabRoom::actCompleterChanged()
|
||||
{
|
||||
SettingsCache::instance().getChatMentionCompleter() ? completer->setCompletionRole(2)
|
||||
: completer->setCompletionRole(1);
|
||||
}
|
||||
|
||||
void TabRoom::processRoomEvent(const RoomEvent &event)
|
||||
{
|
||||
switch (static_cast<RoomEvent::RoomEventType>(getPbExtension(event))) {
|
||||
case RoomEvent::LIST_GAMES:
|
||||
processListGamesEvent(event.GetExtension(Event_ListGames::ext));
|
||||
break;
|
||||
case RoomEvent::JOIN_ROOM:
|
||||
processJoinRoomEvent(event.GetExtension(Event_JoinRoom::ext));
|
||||
break;
|
||||
case RoomEvent::LEAVE_ROOM:
|
||||
processLeaveRoomEvent(event.GetExtension(Event_LeaveRoom::ext));
|
||||
break;
|
||||
case RoomEvent::ROOM_SAY:
|
||||
processRoomSayEvent(event.GetExtension(Event_RoomSay::ext));
|
||||
break;
|
||||
case RoomEvent::REMOVE_MESSAGES:
|
||||
processRemoveMessagesEvent(event.GetExtension(Event_RemoveMessages::ext));
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
void TabRoom::processListGamesEvent(const Event_ListGames &event)
|
||||
{
|
||||
const int gameListSize = event.game_list_size();
|
||||
for (int i = 0; i < gameListSize; ++i)
|
||||
gameSelector->processGameInfo(event.game_list(i));
|
||||
}
|
||||
|
||||
void TabRoom::processJoinRoomEvent(const Event_JoinRoom &event)
|
||||
{
|
||||
userList->processUserInfo(event.user_info(), true);
|
||||
userList->sortItems();
|
||||
if (!autocompleteUserList.contains("@" + QString::fromStdString(event.user_info().name()))) {
|
||||
autocompleteUserList << "@" + QString::fromStdString(event.user_info().name());
|
||||
sayEdit->setCompletionList(autocompleteUserList);
|
||||
}
|
||||
}
|
||||
|
||||
void TabRoom::processLeaveRoomEvent(const Event_LeaveRoom &event)
|
||||
{
|
||||
userList->deleteUser(QString::fromStdString(event.name()));
|
||||
autocompleteUserList.removeOne("@" + QString::fromStdString(event.name()));
|
||||
sayEdit->setCompletionList(autocompleteUserList);
|
||||
}
|
||||
|
||||
void TabRoom::processRoomSayEvent(const Event_RoomSay &event)
|
||||
{
|
||||
QString senderName = QString::fromStdString(event.name());
|
||||
QString message = QString::fromStdString(event.message());
|
||||
|
||||
if (userListProxy->isUserIgnored(senderName))
|
||||
return;
|
||||
|
||||
UserListTWI *twi = userList->getUsers().value(senderName);
|
||||
ServerInfo_User userInfo = {};
|
||||
if (twi) {
|
||||
userInfo = twi->getUserInfo();
|
||||
if (SettingsCache::instance().getIgnoreUnregisteredUsers() &&
|
||||
!UserLevelFlags(userInfo.user_level()).testFlag(ServerInfo_User::IsRegistered))
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.message_type() == Event_RoomSay::ChatHistory && !SettingsCache::instance().getRoomHistory())
|
||||
return;
|
||||
|
||||
if (event.message_type() == Event_RoomSay::ChatHistory)
|
||||
message =
|
||||
"[" +
|
||||
QString(QDateTime::fromMSecsSinceEpoch(event.time_of()).toLocalTime().toString("d MMM yyyy HH:mm:ss")) +
|
||||
"] " + message;
|
||||
|
||||
chatView->appendMessage(message, event.message_type(), userInfo, true);
|
||||
emit userEvent(false);
|
||||
}
|
||||
|
||||
void TabRoom::processRemoveMessagesEvent(const Event_RemoveMessages &event)
|
||||
{
|
||||
QString userName = QString::fromStdString(event.name());
|
||||
int amount = event.amount();
|
||||
chatView->redactMessages(userName, amount);
|
||||
}
|
||||
|
||||
void TabRoom::refreshShortcuts()
|
||||
{
|
||||
aClearChat->setShortcuts(SettingsCache::instance().shortcuts().getShortcut("tab_room/aClearChat"));
|
||||
}
|
||||
|
||||
void TabRoom::addMentionTag(QString mentionTag)
|
||||
{
|
||||
sayEdit->insert(mentionTag + " ");
|
||||
sayEdit->setFocus();
|
||||
}
|
||||
|
||||
PendingCommand *TabRoom::prepareRoomCommand(const ::google::protobuf::Message &cmd)
|
||||
{
|
||||
return client->prepareRoomCommand(cmd, roomId);
|
||||
}
|
||||
|
||||
void TabRoom::sendRoomCommand(PendingCommand *pend)
|
||||
{
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
134
cockatrice/src/interface/widgets/tabs/tab_room.h
Normal file
134
cockatrice/src/interface/widgets/tabs/tab_room.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/**
|
||||
* @file tab_room.h
|
||||
* @ingroup RoomTabs
|
||||
* @ingroup Lobby
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_ROOM_H
|
||||
#define TAB_ROOM_H
|
||||
|
||||
#include "../interface/widgets/utility/line_edit_completer.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <QFocusEvent>
|
||||
#include <QGroupBox>
|
||||
#include <QKeyEvent>
|
||||
#include <QMap>
|
||||
|
||||
class UserListProxy;
|
||||
class UserListManager;
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
}
|
||||
} // namespace google
|
||||
class AbstractClient;
|
||||
class UserListWidget;
|
||||
class QLabel;
|
||||
class ChatView;
|
||||
class QPushButton;
|
||||
class QTextTable;
|
||||
class QCompleter;
|
||||
class RoomEvent;
|
||||
class ServerInfo_Room;
|
||||
class ServerInfo_Game;
|
||||
class Event_ListGames;
|
||||
class Event_JoinRoom;
|
||||
class Event_LeaveRoom;
|
||||
class Event_RoomSay;
|
||||
class Event_RemoveMessages;
|
||||
class GameSelector;
|
||||
class Response;
|
||||
class PendingCommand;
|
||||
class ServerInfo_User;
|
||||
class LineEditCompleter;
|
||||
|
||||
class TabRoom : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
AbstractClient *client;
|
||||
int roomId;
|
||||
QString roomName;
|
||||
ServerInfo_User *ownUser;
|
||||
QMap<int, QString> gameTypes;
|
||||
|
||||
GameSelector *gameSelector;
|
||||
UserListWidget *userList;
|
||||
const UserListProxy *userListProxy;
|
||||
ChatView *chatView;
|
||||
QLabel *sayLabel;
|
||||
LineEditCompleter *sayEdit;
|
||||
QGroupBox *chatGroupBox;
|
||||
|
||||
QMenu *roomMenu;
|
||||
QAction *aLeaveRoom;
|
||||
QAction *aOpenChatSettings;
|
||||
QAction *aClearChat;
|
||||
QString sanitizeHtml(QString dirty) const;
|
||||
|
||||
QStringList autocompleteUserList;
|
||||
QCompleter *completer;
|
||||
signals:
|
||||
void roomClosing(TabRoom *tab);
|
||||
void openMessageDialog(const QString &userName, bool focus);
|
||||
void maximizeClient();
|
||||
void notIdle();
|
||||
private slots:
|
||||
void sendMessage();
|
||||
void sayFinished(const Response &response);
|
||||
void actClearChat();
|
||||
void actOpenChatSettings();
|
||||
void addMentionTag(QString mentionTag);
|
||||
void focusTab();
|
||||
void actShowMentionPopup(const QString &sender);
|
||||
void actShowPopup(const QString &message);
|
||||
void actCompleterChanged();
|
||||
|
||||
void processListGamesEvent(const Event_ListGames &event);
|
||||
void processJoinRoomEvent(const Event_JoinRoom &event);
|
||||
void processLeaveRoomEvent(const Event_LeaveRoom &event);
|
||||
void processRoomSayEvent(const Event_RoomSay &event);
|
||||
void processRemoveMessagesEvent(const Event_RemoveMessages &event);
|
||||
void refreshShortcuts();
|
||||
|
||||
protected slots:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
public:
|
||||
TabRoom(TabSupervisor *_tabSupervisor,
|
||||
AbstractClient *_client,
|
||||
ServerInfo_User *_ownUser,
|
||||
const ServerInfo_Room &info);
|
||||
void retranslateUi() override;
|
||||
void tabActivated() override;
|
||||
void processRoomEvent(const RoomEvent &event);
|
||||
int getRoomId() const
|
||||
{
|
||||
return roomId;
|
||||
}
|
||||
const QMap<int, QString> &getGameTypes() const
|
||||
{
|
||||
return gameTypes;
|
||||
}
|
||||
QString getChannelName() const
|
||||
{
|
||||
return roomName;
|
||||
}
|
||||
QString getTabText() const override
|
||||
{
|
||||
return roomName;
|
||||
}
|
||||
const ServerInfo_User *getUserInfo() const
|
||||
{
|
||||
return ownUser;
|
||||
}
|
||||
|
||||
PendingCommand *prepareRoomCommand(const ::google::protobuf::Message &cmd);
|
||||
void sendRoomCommand(PendingCommand *pend);
|
||||
};
|
||||
|
||||
#endif
|
||||
227
cockatrice/src/interface/widgets/tabs/tab_server.cpp
Normal file
227
cockatrice/src/interface/widgets/tabs/tab_server.cpp
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
#include "tab_server.h"
|
||||
|
||||
#include "../interface/widgets/server/user/user_list_widget.h"
|
||||
#include "tab_supervisor.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDebug>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QInputDialog>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/event_list_rooms.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_server_message.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_join_room.pb.h>
|
||||
#include <libcockatrice/protocol/pb/session_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
|
||||
RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent) : QGroupBox(parent), client(_client)
|
||||
{
|
||||
roomList = new QTreeWidget;
|
||||
roomList->setRootIsDecorated(false);
|
||||
roomList->setColumnCount(5);
|
||||
roomList->header()->setStretchLastSection(false);
|
||||
roomList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
roomList->header()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
roomList->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
|
||||
roomList->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
|
||||
|
||||
joinButton = new QPushButton;
|
||||
connect(joinButton, &QPushButton::clicked, this, &RoomSelector::joinClicked);
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->addWidget(joinButton);
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(roomList);
|
||||
vbox->addLayout(buttonLayout);
|
||||
|
||||
retranslateUi();
|
||||
setLayout(vbox);
|
||||
|
||||
connect(client, &AbstractClient::listRoomsEventReceived, this, &RoomSelector::processListRoomsEvent);
|
||||
connect(roomList, &QTreeWidget::activated, this, &RoomSelector::joinClicked);
|
||||
client->sendCommand(client->prepareSessionCommand(Command_ListRooms()));
|
||||
}
|
||||
|
||||
void RoomSelector::retranslateUi()
|
||||
{
|
||||
setTitle(tr("Rooms"));
|
||||
joinButton->setText(tr("Joi&n"));
|
||||
|
||||
QTreeWidgetItem *header = roomList->headerItem();
|
||||
header->setText(0, tr("Room"));
|
||||
header->setText(1, tr("Description"));
|
||||
header->setText(2, tr("Permissions"));
|
||||
header->setText(3, tr("Players"));
|
||||
header->setText(4, tr("Games"));
|
||||
header->setTextAlignment(2, Qt::AlignRight);
|
||||
header->setTextAlignment(3, Qt::AlignRight);
|
||||
header->setTextAlignment(4, Qt::AlignRight);
|
||||
}
|
||||
|
||||
void RoomSelector::processListRoomsEvent(const Event_ListRooms &event)
|
||||
{
|
||||
const int roomListSize = event.room_list_size();
|
||||
for (int i = 0; i < roomListSize; ++i) {
|
||||
const ServerInfo_Room &room = event.room_list(i);
|
||||
|
||||
for (int j = 0; j < roomList->topLevelItemCount(); ++j) {
|
||||
QTreeWidgetItem *twi = roomList->topLevelItem(j);
|
||||
if (twi->data(0, Qt::UserRole).toInt() == room.room_id()) {
|
||||
if (room.has_name())
|
||||
twi->setData(0, Qt::DisplayRole, QString::fromStdString(room.name()));
|
||||
if (room.has_description())
|
||||
twi->setData(1, Qt::DisplayRole, QString::fromStdString(room.description()));
|
||||
if (room.has_permissionlevel())
|
||||
twi->setData(2, Qt::DisplayRole, getRoomPermissionDisplay(room));
|
||||
if (room.has_player_count())
|
||||
twi->setData(3, Qt::DisplayRole, room.player_count());
|
||||
if (room.has_game_count())
|
||||
twi->setData(4, Qt::DisplayRole, room.game_count());
|
||||
return;
|
||||
}
|
||||
}
|
||||
QTreeWidgetItem *twi = new QTreeWidgetItem;
|
||||
twi->setData(0, Qt::UserRole, room.room_id());
|
||||
if (room.has_name())
|
||||
twi->setData(0, Qt::DisplayRole, QString::fromStdString(room.name()));
|
||||
if (room.has_description())
|
||||
twi->setData(1, Qt::DisplayRole, QString::fromStdString(room.description()));
|
||||
if (room.has_permissionlevel())
|
||||
twi->setData(2, Qt::DisplayRole, getRoomPermissionDisplay(room));
|
||||
twi->setData(3, Qt::DisplayRole, room.player_count());
|
||||
twi->setData(4, Qt::DisplayRole, room.game_count());
|
||||
twi->setTextAlignment(2, Qt::AlignRight);
|
||||
twi->setTextAlignment(3, Qt::AlignRight);
|
||||
twi->setTextAlignment(4, Qt::AlignRight);
|
||||
|
||||
roomList->addTopLevelItem(twi);
|
||||
if (room.has_auto_join())
|
||||
if (room.auto_join())
|
||||
emit joinRoomRequest(room.room_id(), false);
|
||||
}
|
||||
}
|
||||
|
||||
QString RoomSelector::getRoomPermissionDisplay(const ServerInfo_Room &room)
|
||||
{
|
||||
/*
|
||||
* A server room can have a permission level and a privilege level. How ever we want to display only the necessary
|
||||
* information on the server tab needed to inform users of required permissions to enter a room. If the room has a
|
||||
* privilege level the server tab will display the privilege level in the "permissions" column in the row however if
|
||||
* the room contains a permissions level for the room the permissions level defined for the room will be displayed.
|
||||
*/
|
||||
|
||||
QString roomPermissionDisplay = QString::fromStdString(room.privilegelevel()).toLower();
|
||||
if (QString::fromStdString(room.permissionlevel()).toLower() != "none")
|
||||
roomPermissionDisplay = QString::fromStdString(room.permissionlevel()).toLower();
|
||||
if (roomPermissionDisplay == "") // catch all for misconfigured .ini room definitions
|
||||
roomPermissionDisplay = "none";
|
||||
|
||||
return roomPermissionDisplay;
|
||||
}
|
||||
|
||||
void RoomSelector::joinClicked()
|
||||
{
|
||||
QTreeWidgetItem *twi = roomList->currentItem();
|
||||
if (!twi)
|
||||
return;
|
||||
|
||||
int id = twi->data(0, Qt::UserRole).toInt();
|
||||
|
||||
emit joinRoomRequest(id, true);
|
||||
}
|
||||
|
||||
TabServer::TabServer(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client)
|
||||
{
|
||||
roomSelector = new RoomSelector(client);
|
||||
serverInfoBox = new QTextBrowser;
|
||||
serverInfoBox->setOpenExternalLinks(true);
|
||||
|
||||
connect(roomSelector, &RoomSelector::joinRoomRequest, this, &TabServer::joinRoom);
|
||||
|
||||
connect(client, &AbstractClient::serverMessageEventReceived, this, &TabServer::processServerMessageEvent);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(roomSelector);
|
||||
vbox->addWidget(serverInfoBox);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(vbox);
|
||||
setCentralWidget(mainWidget);
|
||||
}
|
||||
|
||||
void TabServer::retranslateUi()
|
||||
{
|
||||
roomSelector->retranslateUi();
|
||||
}
|
||||
|
||||
void TabServer::processServerMessageEvent(const Event_ServerMessage &event)
|
||||
{
|
||||
serverInfoBox->setHtml(QString::fromStdString(event.message()));
|
||||
if (shouldEmitUpdate) {
|
||||
// prevent the initial server message from taking attention from ping icon
|
||||
emit userEvent();
|
||||
} else {
|
||||
shouldEmitUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TabServer::joinRoom(int id, bool setCurrent)
|
||||
{
|
||||
TabRoom *room = tabSupervisor->getRoomTabs().value(id);
|
||||
if (!room) {
|
||||
Command_JoinRoom cmd;
|
||||
cmd.set_room_id(id);
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
pend->setExtraData(setCurrent);
|
||||
connect(pend, &PendingCommand::finished, this, &TabServer::joinRoomFinished);
|
||||
|
||||
client->sendCommand(pend);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (setCurrent)
|
||||
tabSupervisor->setCurrentWidget((QWidget *)room);
|
||||
}
|
||||
|
||||
void TabServer::joinRoomFinished(const Response &r,
|
||||
const CommandContainer & /*commandContainer*/,
|
||||
const QVariant &extraData)
|
||||
{
|
||||
switch (r.response_code()) {
|
||||
case Response::RespOk:
|
||||
break;
|
||||
case Response::RespNameNotFound:
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Failed to join the server room: it doesn't exist on the server."));
|
||||
return;
|
||||
case Response::RespContextError:
|
||||
QMessageBox::critical(
|
||||
this, tr("Error"),
|
||||
tr("The server thinks you are in the server room but your client is unable to display it. "
|
||||
"Try restarting your client."));
|
||||
return;
|
||||
case Response::RespUserLevelTooLow:
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("You do not have the required permission to join this server room."));
|
||||
return;
|
||||
default:
|
||||
QMessageBox::critical(
|
||||
this, tr("Error"),
|
||||
tr("Failed to join the server room due to an unknown error: %1.").arg(r.response_code()));
|
||||
return;
|
||||
}
|
||||
|
||||
const Response_JoinRoom &resp = r.GetExtension(Response_JoinRoom::ext);
|
||||
emit roomJoined(resp.room_info(), extraData.toBool());
|
||||
}
|
||||
72
cockatrice/src/interface/widgets/tabs/tab_server.h
Normal file
72
cockatrice/src/interface/widgets/tabs/tab_server.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* @file tab_server.h
|
||||
* @ingroup ServerTabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_SERVER_H
|
||||
#define TAB_SERVER_H
|
||||
|
||||
#include "tab.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QTextBrowser>
|
||||
#include <QTreeWidget>
|
||||
|
||||
class AbstractClient;
|
||||
class QTextEdit;
|
||||
class QLabel;
|
||||
class UserListWidget;
|
||||
class QPushButton;
|
||||
|
||||
class Event_ListRooms;
|
||||
class Event_ServerMessage;
|
||||
class Response;
|
||||
class ServerInfo_Room;
|
||||
class CommandContainer;
|
||||
|
||||
class RoomSelector : public QGroupBox
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QTreeWidget *roomList;
|
||||
QPushButton *joinButton;
|
||||
AbstractClient *client;
|
||||
QString getRoomPermissionDisplay(const ServerInfo_Room &room);
|
||||
private slots:
|
||||
void processListRoomsEvent(const Event_ListRooms &event);
|
||||
void joinClicked();
|
||||
signals:
|
||||
void joinRoomRequest(int, bool setCurrent);
|
||||
|
||||
public:
|
||||
explicit RoomSelector(AbstractClient *_client, QWidget *parent = nullptr);
|
||||
void retranslateUi();
|
||||
};
|
||||
|
||||
class TabServer : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void roomJoined(const ServerInfo_Room &info, bool setCurrent);
|
||||
private slots:
|
||||
void processServerMessageEvent(const Event_ServerMessage &event);
|
||||
void joinRoom(int id, bool setCurrent);
|
||||
void joinRoomFinished(const Response &resp, const CommandContainer &commandContainer, const QVariant &extraData);
|
||||
|
||||
private:
|
||||
AbstractClient *client;
|
||||
RoomSelector *roomSelector;
|
||||
QTextBrowser *serverInfoBox;
|
||||
bool shouldEmitUpdate = false;
|
||||
|
||||
public:
|
||||
TabServer(TabSupervisor *_tabSupervisor, AbstractClient *_client);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Server");
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
1121
cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp
Normal file
1121
cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp
Normal file
File diff suppressed because it is too large
Load diff
223
cockatrice/src/interface/widgets/tabs/tab_supervisor.h
Normal file
223
cockatrice/src/interface/widgets/tabs/tab_supervisor.h
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/**
|
||||
* @file tab_supervisor.h
|
||||
* @ingroup Core
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_SUPERVISOR_H
|
||||
#define TAB_SUPERVISOR_H
|
||||
|
||||
#include "../interface/widgets/server/user/user_list_proxy.h"
|
||||
#include "abstract_tab_deck_editor.h"
|
||||
#include "api/edhrec/tab_edhrec.h"
|
||||
#include "api/edhrec/tab_edhrec_main.h"
|
||||
#include "tab_visual_database_display.h"
|
||||
#include "visual_deck_editor/tab_deck_editor_visual.h"
|
||||
#include "visual_deck_editor/tab_deck_editor_visual_tab_widget.h"
|
||||
#include "visual_deck_storage/tab_deck_storage_visual.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QCommonStyle>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMap>
|
||||
#include <QProxyStyle>
|
||||
#include <QTabWidget>
|
||||
#include <libcockatrice/deck_list/deck_loader.h>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(TabSupervisorLog, "tab_supervisor");
|
||||
|
||||
class UserListManager;
|
||||
class QMenu;
|
||||
class AbstractClient;
|
||||
class Tab;
|
||||
class TabServer;
|
||||
class TabRoom;
|
||||
class TabHome;
|
||||
class TabGame;
|
||||
class TabDeckStorage;
|
||||
class TabReplays;
|
||||
class TabAdmin;
|
||||
class TabMessage;
|
||||
class TabAccount;
|
||||
class TabDeckEditor;
|
||||
class TabLog;
|
||||
class RoomEvent;
|
||||
class GameEventContainer;
|
||||
class Event_GameJoined;
|
||||
class Event_UserMessage;
|
||||
class Event_NotifyUser;
|
||||
class ServerInfo_Room;
|
||||
class ServerInfo_User;
|
||||
class GameReplay;
|
||||
class DeckList;
|
||||
|
||||
class MacOSTabFixStyle : public QProxyStyle
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QRect subElementRect(SubElement, const QStyleOption *, const QWidget *) const override;
|
||||
};
|
||||
|
||||
class CloseButton : public QAbstractButton
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CloseButton(QWidget *parent = nullptr);
|
||||
QSize sizeHint() const override;
|
||||
inline QSize minimumSizeHint() const override
|
||||
{
|
||||
return sizeHint();
|
||||
}
|
||||
|
||||
protected:
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
#else
|
||||
void enterEvent(QEvent *event) override;
|
||||
#endif
|
||||
void leaveEvent(QEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class TabSupervisor : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum DeckEditorType
|
||||
{
|
||||
ClassicDeckEditor,
|
||||
VisualDeckEditor
|
||||
};
|
||||
|
||||
private:
|
||||
ServerInfo_User *userInfo;
|
||||
AbstractClient *client;
|
||||
UserListManager *userListManager;
|
||||
QList<AbstractClient *> localClients;
|
||||
QMenu *tabsMenu;
|
||||
TabHome *tabHome;
|
||||
TabDeckStorageVisual *tabVisualDeckStorage;
|
||||
TabServer *tabServer;
|
||||
TabAccount *tabAccount;
|
||||
TabDeckStorage *tabDeckStorage;
|
||||
TabReplays *tabReplays;
|
||||
TabAdmin *tabAdmin;
|
||||
TabLog *tabLog;
|
||||
QMap<int, TabRoom *> roomTabs;
|
||||
QMap<int, TabGame *> gameTabs;
|
||||
QList<TabGame *> replayTabs;
|
||||
QMap<QString, TabMessage *> messageTabs;
|
||||
QList<AbstractTabDeckEditor *> deckEditorTabs;
|
||||
bool isLocalGame;
|
||||
|
||||
QAction *aTabHome, *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabVisualDeckStorage,
|
||||
*aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin, *aTabLog;
|
||||
|
||||
int myAddTab(Tab *tab, QAction *manager = nullptr);
|
||||
void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager);
|
||||
static QString sanitizeTabName(QString dirty);
|
||||
static QString sanitizeHtml(QString dirty);
|
||||
void resetTabsMenu();
|
||||
|
||||
public:
|
||||
explicit TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *parent = nullptr);
|
||||
~TabSupervisor() override;
|
||||
void retranslateUi();
|
||||
void initStartupTabs();
|
||||
void start(const ServerInfo_User &userInfo);
|
||||
void startLocal(const QList<AbstractClient *> &_clients);
|
||||
void stop();
|
||||
bool getIsLocalGame() const
|
||||
{
|
||||
return isLocalGame;
|
||||
}
|
||||
int getGameCount() const
|
||||
{
|
||||
return gameTabs.size();
|
||||
}
|
||||
TabAccount *getTabAccount() const
|
||||
{
|
||||
return tabAccount;
|
||||
}
|
||||
ServerInfo_User *getUserInfo() const
|
||||
{
|
||||
return userInfo;
|
||||
}
|
||||
AbstractClient *getClient() const;
|
||||
const UserListManager *getUserListManager() const
|
||||
{
|
||||
return userListManager;
|
||||
}
|
||||
const QMap<int, TabRoom *> &getRoomTabs() const
|
||||
{
|
||||
return roomTabs;
|
||||
}
|
||||
QList<AbstractTabDeckEditor *> getDeckEditorTabs() const
|
||||
{
|
||||
return deckEditorTabs;
|
||||
}
|
||||
bool getAdminLocked() const;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
bool switchToGameTabIfAlreadyExists(const int gameId);
|
||||
static void actShowPopup(const QString &message);
|
||||
signals:
|
||||
void setMenu(const QList<QMenu *> &newMenuList = QList<QMenu *>());
|
||||
void localGameEnded();
|
||||
void adminLockChanged(bool lock);
|
||||
void showWindowIfHidden();
|
||||
|
||||
public slots:
|
||||
void openDeckInNewTab(const DeckLoader *deckToOpen);
|
||||
TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen);
|
||||
TabDeckEditorVisual *addVisualDeckEditorTab(const DeckLoader *deckToOpen);
|
||||
TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();
|
||||
TabEdhRecMain *addEdhrecMainTab();
|
||||
TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false);
|
||||
void openReplay(GameReplay *replay);
|
||||
void switchToFirstAvailableNetworkTab();
|
||||
void maximizeMainWindow();
|
||||
void actTabVisualDeckStorage(bool checked);
|
||||
void actTabReplays(bool checked);
|
||||
private slots:
|
||||
void refreshShortcuts();
|
||||
|
||||
void actTabHome(bool checked);
|
||||
void actTabServer(bool checked);
|
||||
void actTabAccount(bool checked);
|
||||
void actTabDeckStorage(bool checked);
|
||||
void actTabAdmin(bool checked);
|
||||
void actTabLog(bool checked);
|
||||
|
||||
void openTabVisualDeckStorage();
|
||||
void openTabHome();
|
||||
void openTabServer();
|
||||
void openTabAccount();
|
||||
void openTabDeckStorage();
|
||||
void openTabReplays();
|
||||
void openTabAdmin();
|
||||
void openTabLog();
|
||||
|
||||
void updateCurrent(int index);
|
||||
void updatePingTime(int value, int max);
|
||||
void gameJoined(const Event_GameJoined &event);
|
||||
void localGameJoined(const Event_GameJoined &event);
|
||||
void gameLeft(TabGame *tab);
|
||||
void addRoomTab(const ServerInfo_Room &info, bool setCurrent);
|
||||
void roomLeft(TabRoom *tab);
|
||||
TabMessage *addMessageTab(const QString &userName, bool focus);
|
||||
void replayLeft(TabGame *tab);
|
||||
void processUserLeft(const QString &userName);
|
||||
void processUserJoined(const ServerInfo_User &userInfo);
|
||||
void talkLeft(TabMessage *tab);
|
||||
void deckEditorClosed(AbstractTabDeckEditor *tab);
|
||||
void tabUserEvent(bool globalEvent);
|
||||
void updateTabText(Tab *tab, const QString &newTabText);
|
||||
void processRoomEvent(const RoomEvent &event);
|
||||
void processGameEventContainer(const GameEventContainer &cont);
|
||||
void processUserMessageEvent(const Event_UserMessage &event);
|
||||
void processNotifyUserEvent(const Event_NotifyUser &event);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#include "tab_visual_database_display.h"
|
||||
|
||||
#include "tab_deck_editor.h"
|
||||
|
||||
TabVisualDatabaseDisplay::TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||
{
|
||||
deckEditor = new TabDeckEditor(_tabSupervisor);
|
||||
deckEditor->setHidden(true);
|
||||
visualDatabaseDisplayWidget =
|
||||
new VisualDatabaseDisplayWidget(this, deckEditor, deckEditor->databaseDisplayDockWidget->databaseModel,
|
||||
deckEditor->databaseDisplayDockWidget->databaseDisplayModel);
|
||||
|
||||
setCentralWidget(visualDatabaseDisplayWidget);
|
||||
|
||||
TabVisualDatabaseDisplay::retranslateUi();
|
||||
}
|
||||
|
||||
void TabVisualDatabaseDisplay::retranslateUi()
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file tab_visual_database_display.h
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_VISUAL_DATABASE_DISPLAY_H
|
||||
#define TAB_VISUAL_DATABASE_DISPLAY_H
|
||||
|
||||
#include "../interface/widgets/visual_database_display/visual_database_display_widget.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class TabVisualDatabaseDisplay : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
TabDeckEditor *deckEditor;
|
||||
VisualDatabaseDisplayWidget *visualDatabaseDisplayWidget;
|
||||
|
||||
public:
|
||||
TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Visual Database Display");
|
||||
}
|
||||
};
|
||||
|
||||
#endif // TAB_VISUAL_DATABASE_DISPLAY_H
|
||||
|
|
@ -0,0 +1,451 @@
|
|||
#include "tab_deck_editor_visual.h"
|
||||
|
||||
#include "../../client/network/interfaces/deck_stats_interface.h"
|
||||
#include "../../filters/filter_builder.h"
|
||||
#include "../../interface/pixel_map_generator.h"
|
||||
#include "../../interface/widgets/cards/card_info_frame_widget.h"
|
||||
#include "../../interface/widgets/deck_analytics/deck_analytics_widget.h"
|
||||
#include "../../interface/widgets/visual_deck_editor/visual_deck_editor_widget.h"
|
||||
#include "../tab_deck_editor.h"
|
||||
#include "../tab_supervisor.h"
|
||||
#include "tab_deck_editor_visual_tab_widget.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QCompleter>
|
||||
#include <QDir>
|
||||
#include <QDockWidget>
|
||||
#include <QFileDialog>
|
||||
#include <QHeaderView>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QPrintPreviewDialog>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QSplitter>
|
||||
#include <QTextStream>
|
||||
#include <QTimer>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/card_database/model/card_database_model.h>
|
||||
#include <libcockatrice/deck_list/deck_list_model.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_upload.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/cache_settings.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
|
||||
TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : AbstractTabDeckEditor(_tabSupervisor)
|
||||
{
|
||||
setObjectName("TabDeckEditorVisual");
|
||||
|
||||
createCentralFrame();
|
||||
|
||||
TabDeckEditorVisual::createMenus();
|
||||
|
||||
installEventFilter(this);
|
||||
|
||||
TabDeckEditorVisual::retranslateUi();
|
||||
connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
|
||||
TabDeckEditorVisual::refreshShortcuts();
|
||||
|
||||
TabDeckEditorVisual::loadLayout();
|
||||
databaseDisplayDockWidget->setHidden(true);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::createCentralFrame()
|
||||
{
|
||||
centralWidget = new QWidget(this);
|
||||
centralWidget->setObjectName("centralWidget");
|
||||
|
||||
centralFrame = new QVBoxLayout;
|
||||
centralWidget->setLayout(centralFrame);
|
||||
|
||||
tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckDockWidget->deckModel,
|
||||
databaseDisplayDockWidget->databaseModel,
|
||||
databaseDisplayDockWidget->databaseDisplayModel);
|
||||
connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChanged, this,
|
||||
&TabDeckEditorVisual::changeModelIndexAndCardInfo);
|
||||
connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChangedDatabaseDisplay, this,
|
||||
&AbstractTabDeckEditor::updateCard);
|
||||
connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClicked, this,
|
||||
&TabDeckEditorVisual::processMainboardCardClick);
|
||||
|
||||
connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClickedDatabaseDisplay, this,
|
||||
&TabDeckEditorVisual::processCardClickDatabaseDisplay);
|
||||
centralFrame->addWidget(tabContainer);
|
||||
|
||||
setCentralWidget(centralWidget);
|
||||
setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::onDeckChanged()
|
||||
{
|
||||
AbstractTabDeckEditor::onDeckModified();
|
||||
tabContainer->visualDeckView->constructZoneWidgetsFromDeckListModel();
|
||||
tabContainer->deckAnalytics->refreshDisplays(deckDockWidget->deckModel);
|
||||
tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::createMenus()
|
||||
{
|
||||
deckMenu = new DeckEditorMenu(this);
|
||||
addTabMenu(deckMenu);
|
||||
|
||||
viewMenu = new QMenu(this);
|
||||
|
||||
cardInfoDockMenu = viewMenu->addMenu(QString());
|
||||
deckDockMenu = viewMenu->addMenu(QString());
|
||||
filterDockMenu = viewMenu->addMenu(QString());
|
||||
printingSelectorDockMenu = 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()));
|
||||
|
||||
aPrintingSelectorDockVisible = printingSelectorDockMenu->addAction(QString());
|
||||
aPrintingSelectorDockVisible->setCheckable(true);
|
||||
connect(aPrintingSelectorDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
|
||||
aPrintingSelectorDockFloating = printingSelectorDockMenu->addAction(QString());
|
||||
aPrintingSelectorDockFloating->setCheckable(true);
|
||||
connect(aPrintingSelectorDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
|
||||
|
||||
if (SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {
|
||||
printingSelectorDockMenu->setEnabled(false);
|
||||
}
|
||||
|
||||
connect(&SettingsCache::instance(), &SettingsCache::overrideAllCardArtWithPersonalPreferenceChanged, this,
|
||||
[this](bool enabled) { printingSelectorDockMenu->setEnabled(!enabled); });
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
aResetLayout = viewMenu->addAction(QString());
|
||||
connect(aResetLayout, SIGNAL(triggered()), this, SLOT(restartLayout()));
|
||||
viewMenu->addAction(aResetLayout);
|
||||
|
||||
deckMenu->setSaveStatus(false);
|
||||
|
||||
addTabMenu(viewMenu);
|
||||
}
|
||||
|
||||
QString TabDeckEditorVisual::getTabText() const
|
||||
{
|
||||
QString result = tr("Visual Deck: %1").arg(deckDockWidget->getSimpleDeckName());
|
||||
if (modified)
|
||||
result.prepend("* ");
|
||||
return result;
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::changeModelIndexAndCardInfo(const ExactCard &activeCard)
|
||||
{
|
||||
updateCard(activeCard);
|
||||
changeModelIndexToCard(activeCard);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::changeModelIndexToCard(const ExactCard &activeCard)
|
||||
{
|
||||
QString cardName = activeCard.getName();
|
||||
QModelIndex index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_MAIN);
|
||||
if (!index.isValid()) {
|
||||
index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_SIDE);
|
||||
}
|
||||
deckDockWidget->deckView->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
|
||||
CardInfoPictureWithTextOverlayWidget *instance,
|
||||
QString zoneName)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
actSwapCard(instance->getCard(), zoneName);
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
actDecrementCard(instance->getCard());
|
||||
} else if (event->button() == Qt::MiddleButton) {
|
||||
deckDockWidget->actRemoveCard();
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::processCardClickDatabaseDisplay(QMouseEvent *event,
|
||||
CardInfoPictureWithTextOverlayWidget *instance)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
actAddCard(instance->getCard());
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
actDecrementCard(instance->getCard());
|
||||
} else if (event->button() == Qt::MiddleButton) {
|
||||
deckDockWidget->actRemoveCard();
|
||||
}
|
||||
}
|
||||
|
||||
bool TabDeckEditorVisual::actSaveDeckAs()
|
||||
{
|
||||
// We have to disable the quick-add search bar or else it'll steal focus after dialog creation.
|
||||
tabContainer->visualDeckView->searchBar->setEnabled(false);
|
||||
auto result = AbstractTabDeckEditor::actSaveDeckAs();
|
||||
tabContainer->visualDeckView->searchBar->setEnabled(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::showPrintingSelector()
|
||||
{
|
||||
printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr(),
|
||||
DECK_ZONE_MAIN);
|
||||
printingSelectorDockWidget->printingSelector->updateDisplay();
|
||||
aPrintingSelectorDockVisible->setChecked(true);
|
||||
printingSelectorDockWidget->setVisible(true);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::freeDocksSize()
|
||||
{
|
||||
deckDockWidget->setMinimumSize(100, 100);
|
||||
deckDockWidget->setMaximumSize(5000, 5000);
|
||||
|
||||
cardInfoDockWidget->setMinimumSize(100, 100);
|
||||
cardInfoDockWidget->setMaximumSize(5000, 5000);
|
||||
|
||||
filterDockWidget->setMinimumSize(100, 100);
|
||||
filterDockWidget->setMaximumSize(5000, 5000);
|
||||
|
||||
printingSelectorDockWidget->setMinimumSize(100, 100);
|
||||
printingSelectorDockWidget->setMaximumSize(5000, 5000);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::refreshShortcuts()
|
||||
{
|
||||
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
|
||||
aResetLayout->setShortcuts(shortcuts.getShortcut("TabDeckEditorVisual/aResetLayout"));
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::loadLayout()
|
||||
{
|
||||
LayoutsSettings &layouts = SettingsCache::instance().layouts();
|
||||
auto &layoutState = layouts.getDeckEditorLayoutState();
|
||||
if (layoutState.isNull()) {
|
||||
restartLayout();
|
||||
} else {
|
||||
restoreState(layoutState);
|
||||
restoreGeometry(layouts.getDeckEditorGeometry());
|
||||
}
|
||||
|
||||
if (SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {
|
||||
if (!printingSelectorDockWidget->isHidden()) {
|
||||
printingSelectorDockWidget->setHidden(true);
|
||||
aPrintingSelectorDockVisible->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
aCardInfoDockVisible->setChecked(!cardInfoDockWidget->isHidden());
|
||||
aFilterDockVisible->setChecked(!filterDockWidget->isHidden());
|
||||
aDeckDockVisible->setChecked(!deckDockWidget->isHidden());
|
||||
aPrintingSelectorDockVisible->setChecked(!printingSelectorDockWidget->isHidden());
|
||||
|
||||
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
|
||||
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
|
||||
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
|
||||
aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
|
||||
|
||||
aCardInfoDockFloating->setChecked(cardInfoDockWidget->isFloating());
|
||||
aFilterDockFloating->setChecked(filterDockWidget->isFloating());
|
||||
aDeckDockFloating->setChecked(deckDockWidget->isFloating());
|
||||
aPrintingSelectorDockFloating->setChecked(printingSelectorDockWidget->isFloating());
|
||||
|
||||
cardInfoDockWidget->setMinimumSize(layouts.getDeckEditorCardSize());
|
||||
cardInfoDockWidget->setMaximumSize(layouts.getDeckEditorCardSize());
|
||||
|
||||
filterDockWidget->setMinimumSize(layouts.getDeckEditorFilterSize());
|
||||
filterDockWidget->setMaximumSize(layouts.getDeckEditorFilterSize());
|
||||
|
||||
deckDockWidget->setMinimumSize(layouts.getDeckEditorDeckSize());
|
||||
deckDockWidget->setMaximumSize(layouts.getDeckEditorDeckSize());
|
||||
|
||||
printingSelectorDockWidget->setMinimumSize(layouts.getDeckEditorPrintingSelectorSize());
|
||||
printingSelectorDockWidget->setMaximumSize(layouts.getDeckEditorPrintingSelectorSize());
|
||||
|
||||
QTimer::singleShot(100, this, &TabDeckEditorVisual::freeDocksSize);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::restartLayout()
|
||||
{
|
||||
aCardInfoDockVisible->setChecked(true);
|
||||
aDeckDockVisible->setChecked(true);
|
||||
aFilterDockVisible->setChecked(false);
|
||||
aPrintingSelectorDockVisible->setChecked(!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference());
|
||||
|
||||
aCardInfoDockFloating->setChecked(false);
|
||||
aDeckDockFloating->setChecked(false);
|
||||
aFilterDockFloating->setChecked(false);
|
||||
aPrintingSelectorDockFloating->setChecked(false);
|
||||
|
||||
setCentralWidget(centralWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, deckDockWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, cardInfoDockWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, filterDockWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, printingSelectorDockWidget);
|
||||
|
||||
deckDockWidget->setVisible(true);
|
||||
cardInfoDockWidget->setVisible(true);
|
||||
filterDockWidget->setVisible(false);
|
||||
printingSelectorDockWidget->setVisible(!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference());
|
||||
|
||||
deckDockWidget->setFloating(false);
|
||||
cardInfoDockWidget->setFloating(false);
|
||||
filterDockWidget->setFloating(false);
|
||||
printingSelectorDockWidget->setFloating(false);
|
||||
|
||||
splitDockWidget(cardInfoDockWidget, printingSelectorDockWidget, Qt::Vertical);
|
||||
splitDockWidget(cardInfoDockWidget, deckDockWidget, Qt::Horizontal);
|
||||
splitDockWidget(cardInfoDockWidget, filterDockWidget, Qt::Horizontal);
|
||||
|
||||
QTimer::singleShot(100, this, SLOT(freeDocksSize()));
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::retranslateUi()
|
||||
{
|
||||
deckMenu->setTitle(tr("&Visual Deck Editor"));
|
||||
|
||||
cardInfoDockWidget->setWindowTitle(tr("Card Info"));
|
||||
deckDockWidget->setWindowTitle(tr("Deck"));
|
||||
filterDockWidget->setWindowTitle(tr("Filters"));
|
||||
|
||||
viewMenu->setTitle(tr("&View"));
|
||||
cardInfoDockMenu->setTitle(tr("Card Info"));
|
||||
deckDockMenu->setTitle(tr("Deck"));
|
||||
filterDockMenu->setTitle(tr("Filters"));
|
||||
printingSelectorDockMenu->setTitle(tr("Printing"));
|
||||
|
||||
aCardInfoDockVisible->setText(tr("Visible"));
|
||||
aCardInfoDockFloating->setText(tr("Floating"));
|
||||
|
||||
aDeckDockVisible->setText(tr("Visible"));
|
||||
aDeckDockFloating->setText(tr("Floating"));
|
||||
|
||||
aFilterDockVisible->setText(tr("Visible"));
|
||||
aFilterDockFloating->setText(tr("Floating"));
|
||||
|
||||
aPrintingSelectorDockVisible->setText(tr("Visible"));
|
||||
aPrintingSelectorDockFloating->setText(tr("Floating"));
|
||||
|
||||
aResetLayout->setText(tr("Reset layout"));
|
||||
}
|
||||
|
||||
// Method uses to sync docks state with menu items state
|
||||
bool TabDeckEditorVisual::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::Close) {
|
||||
if (o == cardInfoDockWidget) {
|
||||
aCardInfoDockVisible->setChecked(false);
|
||||
aCardInfoDockFloating->setEnabled(false);
|
||||
} else if (o == deckDockWidget) {
|
||||
aDeckDockVisible->setChecked(false);
|
||||
aDeckDockFloating->setEnabled(false);
|
||||
} else if (o == filterDockWidget) {
|
||||
aFilterDockVisible->setChecked(false);
|
||||
aFilterDockFloating->setEnabled(false);
|
||||
} else if (o == printingSelectorDockWidget) {
|
||||
aPrintingSelectorDockVisible->setChecked(false);
|
||||
aPrintingSelectorDockFloating->setEnabled(false);
|
||||
}
|
||||
}
|
||||
if (o == this && e->type() == QEvent::Hide) {
|
||||
LayoutsSettings &layouts = SettingsCache::instance().layouts();
|
||||
layouts.setDeckEditorLayoutState(saveState());
|
||||
layouts.setDeckEditorGeometry(saveGeometry());
|
||||
layouts.setDeckEditorCardSize(cardInfoDockWidget->size());
|
||||
layouts.setDeckEditorFilterSize(filterDockWidget->size());
|
||||
layouts.setDeckEditorDeckSize(deckDockWidget->size());
|
||||
layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::dockVisibleTriggered()
|
||||
{
|
||||
QObject *o = sender();
|
||||
if (o == aCardInfoDockVisible) {
|
||||
cardInfoDockWidget->setHidden(!aCardInfoDockVisible->isChecked());
|
||||
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aDeckDockVisible) {
|
||||
deckDockWidget->setHidden(!aDeckDockVisible->isChecked());
|
||||
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aFilterDockVisible) {
|
||||
filterDockWidget->setHidden(!aFilterDockVisible->isChecked());
|
||||
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aPrintingSelectorDockVisible) {
|
||||
printingSelectorDockWidget->setHidden(!aPrintingSelectorDockVisible->isChecked());
|
||||
aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::dockFloatingTriggered()
|
||||
{
|
||||
QObject *o = sender();
|
||||
if (o == aCardInfoDockFloating) {
|
||||
cardInfoDockWidget->setFloating(aCardInfoDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aDeckDockFloating) {
|
||||
deckDockWidget->setFloating(aDeckDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aFilterDockFloating) {
|
||||
filterDockWidget->setFloating(aFilterDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == aPrintingSelectorDockFloating) {
|
||||
printingSelectorDockWidget->setFloating(aPrintingSelectorDockFloating->isChecked());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditorVisual::dockTopLevelChanged(bool topLevel)
|
||||
{
|
||||
QObject *o = sender();
|
||||
if (o == cardInfoDockWidget) {
|
||||
aCardInfoDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == deckDockWidget) {
|
||||
aDeckDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == filterDockWidget) {
|
||||
aFilterDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == printingSelectorDockWidget) {
|
||||
aPrintingSelectorDockFloating->setChecked(topLevel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef WINDOW_DECKEDITORVISUAL_H
|
||||
#define WINDOW_DECKEDITORVISUAL_H
|
||||
|
||||
#include "../tab.h"
|
||||
#include "tab_deck_editor_visual_tab_widget.h"
|
||||
|
||||
class TabDeckEditorVisual : public AbstractTabDeckEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
protected slots:
|
||||
void loadLayout() override;
|
||||
void restartLayout() override;
|
||||
void freeDocksSize() override;
|
||||
void refreshShortcuts() override;
|
||||
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
void dockVisibleTriggered() override;
|
||||
void dockFloatingTriggered() override;
|
||||
void dockTopLevelChanged(bool topLevel) override;
|
||||
|
||||
protected:
|
||||
TabDeckEditorVisualTabWidget *tabContainer;
|
||||
|
||||
QVBoxLayout *centralFrame;
|
||||
QVBoxLayout *searchAndDatabaseFrame;
|
||||
QHBoxLayout *searchLayout;
|
||||
QDockWidget *searchAndDatabaseDock;
|
||||
QWidget *centralWidget;
|
||||
|
||||
public:
|
||||
explicit TabDeckEditorVisual(TabSupervisor *_tabSupervisor);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override;
|
||||
void changeModelIndexAndCardInfo(const ExactCard &activeCard);
|
||||
void changeModelIndexToCard(const ExactCard &activeCard);
|
||||
void createDeckAnalyticsDock();
|
||||
void createMenus() override;
|
||||
void createSearchAndDatabaseFrame();
|
||||
void createCentralFrame();
|
||||
|
||||
public slots:
|
||||
void onDeckChanged() override;
|
||||
void showPrintingSelector() override;
|
||||
void
|
||||
processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
|
||||
void processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
|
||||
bool actSaveDeckAs() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
#include "tab_deck_editor_visual_tab_widget.h"
|
||||
|
||||
#include "../../interface/widgets/visual_database_display/visual_database_display_widget.h"
|
||||
#include "../abstract_tab_deck_editor.h"
|
||||
|
||||
TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent,
|
||||
AbstractTabDeckEditor *_deckEditor,
|
||||
DeckListModel *_deckModel,
|
||||
CardDatabaseModel *_cardDatabaseModel,
|
||||
CardDatabaseDisplayModel *_cardDatabaseDisplayModel)
|
||||
: QTabWidget(parent), deckEditor(_deckEditor), deckModel(_deckModel), cardDatabaseModel(_cardDatabaseModel),
|
||||
cardDatabaseDisplayModel(_cardDatabaseDisplayModel)
|
||||
{
|
||||
this->setTabsClosable(true); // Enable tab closing
|
||||
connect(this, &QTabWidget::tabCloseRequested, this, &TabDeckEditorVisualTabWidget::handleTabClose);
|
||||
|
||||
// Set up the layout and add tab widget
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
visualDeckView = new VisualDeckEditorWidget(this, deckModel);
|
||||
visualDeckView->setObjectName("visualDeckView");
|
||||
connect(visualDeckView, &VisualDeckEditorWidget::activeCardChanged, this,
|
||||
&TabDeckEditorVisualTabWidget::onCardChanged);
|
||||
connect(visualDeckView, &VisualDeckEditorWidget::cardClicked, this,
|
||||
&TabDeckEditorVisualTabWidget::onCardClickedDeckEditor);
|
||||
connect(visualDeckView, &VisualDeckEditorWidget::cardAdditionRequested, deckEditor,
|
||||
&AbstractTabDeckEditor::actAddCard);
|
||||
|
||||
visualDatabaseDisplay =
|
||||
new VisualDatabaseDisplayWidget(this, deckEditor, _cardDatabaseModel, _cardDatabaseDisplayModel);
|
||||
visualDatabaseDisplay->setObjectName("visualDatabaseView");
|
||||
connect(visualDatabaseDisplay, &VisualDatabaseDisplayWidget::cardHoveredDatabaseDisplay, this,
|
||||
&TabDeckEditorVisualTabWidget::onCardChangedDatabaseDisplay);
|
||||
connect(visualDatabaseDisplay, &VisualDatabaseDisplayWidget::cardClickedDatabaseDisplay, this,
|
||||
&TabDeckEditorVisualTabWidget::onCardClickedDatabaseDisplay);
|
||||
|
||||
deckAnalytics = new DeckAnalyticsWidget(this, deckModel);
|
||||
deckAnalytics->setObjectName("deckAnalytics");
|
||||
|
||||
sampleHandWidget = new VisualDeckEditorSampleHandWidget(this, deckModel);
|
||||
|
||||
this->addNewTab(visualDeckView, tr("Visual Deck View"));
|
||||
this->addNewTab(visualDatabaseDisplay, tr("Visual Database Display"));
|
||||
this->addNewTab(deckAnalytics, tr("Deck Analytics"));
|
||||
this->addNewTab(sampleHandWidget, tr("Sample Hand"));
|
||||
}
|
||||
|
||||
void TabDeckEditorVisualTabWidget::onCardChanged(const ExactCard &activeCard)
|
||||
{
|
||||
emit cardChanged(activeCard);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisualTabWidget::onCardChangedDatabaseDisplay(const ExactCard &activeCard)
|
||||
{
|
||||
emit cardChangedDatabaseDisplay(activeCard);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisualTabWidget::onCardClickedDeckEditor(QMouseEvent *event,
|
||||
CardInfoPictureWithTextOverlayWidget *instance,
|
||||
QString zoneName)
|
||||
{
|
||||
emit cardClicked(event, instance, zoneName);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisualTabWidget::onCardClickedDatabaseDisplay(QMouseEvent *event,
|
||||
CardInfoPictureWithTextOverlayWidget *instance)
|
||||
{
|
||||
emit cardClickedDatabaseDisplay(event, instance);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisualTabWidget::addNewTab(QWidget *widget, const QString &title)
|
||||
{
|
||||
// Add new tab to the tab widget
|
||||
this->addTab(widget, title);
|
||||
}
|
||||
|
||||
void TabDeckEditorVisualTabWidget::removeCurrentTab()
|
||||
{
|
||||
// Remove the currently selected tab
|
||||
int currentIndex = this->currentIndex();
|
||||
if (currentIndex != -1) {
|
||||
this->removeTab(currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditorVisualTabWidget::setTabTitle(int index, const QString &title)
|
||||
{
|
||||
// Set the title of the tab at the given index
|
||||
if (index >= 0 && index < this->count()) {
|
||||
this->setTabText(index, title);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *TabDeckEditorVisualTabWidget::getCurrentTab() const
|
||||
{
|
||||
// Return the currently selected tab widget
|
||||
return this->currentWidget();
|
||||
}
|
||||
|
||||
int TabDeckEditorVisualTabWidget::getTabCount() const
|
||||
{
|
||||
// Return the number of tabs
|
||||
return this->count();
|
||||
}
|
||||
|
||||
void TabDeckEditorVisualTabWidget::handleTabClose(int index)
|
||||
{
|
||||
// Handle closing of the tab at the given index
|
||||
QWidget *tab = this->widget(index);
|
||||
this->removeTab(index);
|
||||
delete tab; // Delete the tab's widget to free memory
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
|
||||
#define TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
|
||||
|
||||
#include "../../interface/widgets/deck_analytics/deck_analytics_widget.h"
|
||||
#include "../../interface/widgets/printing_selector/printing_selector.h"
|
||||
#include "../../interface/widgets/visual_database_display/visual_database_display_widget.h"
|
||||
#include "../../interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.h"
|
||||
#include "../../interface/widgets/visual_deck_editor/visual_deck_editor_widget.h"
|
||||
#include "../abstract_tab_deck_editor.h"
|
||||
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class TabDeckEditorVisualTabWidget : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TabDeckEditorVisualTabWidget(QWidget *parent,
|
||||
AbstractTabDeckEditor *_deckEditor,
|
||||
DeckListModel *_deckModel,
|
||||
CardDatabaseModel *_cardDatabaseModel,
|
||||
CardDatabaseDisplayModel *_cardDatabaseDisplayModel);
|
||||
|
||||
// Utility functions
|
||||
void addNewTab(QWidget *widget, const QString &title);
|
||||
void removeCurrentTab();
|
||||
void setTabTitle(int index, const QString &title);
|
||||
QWidget *getCurrentTab() const;
|
||||
int getTabCount() const;
|
||||
|
||||
VisualDeckEditorWidget *visualDeckView;
|
||||
DeckAnalyticsWidget *deckAnalytics;
|
||||
VisualDatabaseDisplayWidget *visualDatabaseDisplay;
|
||||
PrintingSelector *printingSelector;
|
||||
VisualDeckEditorSampleHandWidget *sampleHandWidget;
|
||||
|
||||
public slots:
|
||||
void onCardChanged(const ExactCard &activeCard);
|
||||
void onCardChangedDatabaseDisplay(const ExactCard &activeCard);
|
||||
void onCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
|
||||
void onCardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
|
||||
|
||||
signals:
|
||||
void cardChanged(const ExactCard &activeCard);
|
||||
void cardChangedDatabaseDisplay(const ExactCard &activeCard);
|
||||
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
|
||||
void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout; // Layout for the tab widget and other controls
|
||||
AbstractTabDeckEditor *deckEditor;
|
||||
DeckListModel *deckModel;
|
||||
CardDatabaseModel *cardDatabaseModel;
|
||||
CardDatabaseDisplayModel *cardDatabaseDisplayModel;
|
||||
|
||||
private slots:
|
||||
void handleTabClose(int index); // Slot for closing a tab
|
||||
};
|
||||
|
||||
#endif // TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#include "tab_deck_storage_visual.h"
|
||||
|
||||
#include "../../interface/widgets/cards/deck_preview_card_picture_widget.h"
|
||||
#include "../../interface/widgets/visual_deck_storage/visual_deck_storage_widget.h"
|
||||
#include "../tab_supervisor.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QMouseEvent>
|
||||
#include <libcockatrice/card/card_database/model/card_database_model.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_del.pb.h>
|
||||
|
||||
TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
|
||||
: Tab(_tabSupervisor), visualDeckStorageWidget(new VisualDeckStorageWidget(this))
|
||||
{
|
||||
connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::openDeckInNewTab);
|
||||
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::deckLoadRequested, this,
|
||||
&TabDeckStorageVisual::actOpenLocalDeck);
|
||||
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this,
|
||||
&TabDeckStorageVisual::openDeckEditor);
|
||||
|
||||
auto *widget = new QWidget(this);
|
||||
auto *layout = new QVBoxLayout(widget);
|
||||
widget->setLayout(layout);
|
||||
this->setCentralWidget(widget);
|
||||
layout->addWidget(visualDeckStorageWidget);
|
||||
}
|
||||
|
||||
void TabDeckStorageVisual::actOpenLocalDeck(const QString &filePath)
|
||||
{
|
||||
DeckLoader deckLoader;
|
||||
if (!deckLoader.loadFromFile(filePath, DeckLoader::getFormatFromName(filePath), true)) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(filePath));
|
||||
return;
|
||||
}
|
||||
|
||||
emit openDeckEditor(&deckLoader);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @file tab_deck_storage_visual.h
|
||||
* @ingroup Tabs
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef TAB_DECK_STORAGE_VISUAL_H
|
||||
#define TAB_DECK_STORAGE_VISUAL_H
|
||||
|
||||
#include "../tab.h"
|
||||
|
||||
class AbstractClient;
|
||||
class CommandContainer;
|
||||
class DeckLoader;
|
||||
class DeckPreviewWidget;
|
||||
class QFileSystemModel;
|
||||
class QGroupBox;
|
||||
class QToolBar;
|
||||
class QTreeView;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class Response;
|
||||
class VisualDeckStorageWidget;
|
||||
|
||||
class TabDeckStorageVisual final : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TabDeckStorageVisual(TabSupervisor *_tabSupervisor);
|
||||
void retranslateUi() override{};
|
||||
[[nodiscard]] QString getTabText() const override
|
||||
{
|
||||
return tr("Visual Deck Storage");
|
||||
}
|
||||
|
||||
public slots:
|
||||
void actOpenLocalDeck(const QString &filePath);
|
||||
|
||||
signals:
|
||||
void openDeckEditor(const DeckLoader *deckLoader);
|
||||
|
||||
private:
|
||||
VisualDeckStorageWidget *visualDeckStorageWidget;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue