diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 70de75a5..c90a00d7 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -26,6 +26,7 @@ SET(cockatrice_SOURCES src/handzone.cpp src/handcounter.cpp src/carddatabase.cpp + src/keysignals.cpp src/gameview.cpp src/gameselector.cpp src/decklistmodel.cpp @@ -33,6 +34,13 @@ SET(cockatrice_SOURCES src/dlg_load_deck_from_clipboard.cpp src/dlg_load_remote_deck.cpp src/cardinfowidget.cpp + src/cardframe.cpp + src/cardinfopicture.cpp + src/cardinfotext.cpp + src/filterbuilder.cpp + src/cardfilter.cpp + src/filtertreemodel.cpp + src/filtertree.cpp src/messagelogwidget.cpp src/zoneviewzone.cpp src/zoneviewwidget.cpp @@ -103,6 +111,7 @@ SET(cockatrice_HEADERS src/handzone.h src/handcounter.h src/carddatabase.h + src/keysignals.h src/gameview.h src/gameselector.h src/decklistmodel.h @@ -110,6 +119,13 @@ SET(cockatrice_HEADERS src/dlg_load_deck_from_clipboard.h src/dlg_load_remote_deck.h src/cardinfowidget.h + src/cardframe.h + src/cardinfopicture.h + src/cardinfotext.h + src/filterbuilder.h + src/cardfilter.h + src/filtertreemodel.h + src/filtertree.h src/messagelogwidget.h src/zoneviewzone.h src/zoneviewwidget.h diff --git a/cockatrice/src/carddatabasemodel.cpp b/cockatrice/src/carddatabasemodel.cpp index 08051764..e62664be 100644 --- a/cockatrice/src/carddatabasemodel.cpp +++ b/cockatrice/src/carddatabasemodel.cpp @@ -1,4 +1,5 @@ #include "carddatabasemodel.h" +#include "filtertree.h" CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, QObject *parent) : QAbstractListModel(parent), db(_db) @@ -109,6 +110,7 @@ CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent) : QSortFilterProxyModel(parent), isToken(ShowAll) { + filterTree = NULL; setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -116,7 +118,6 @@ CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent) bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const { CardInfo const *info = static_cast(sourceModel())->getCard(sourceRow); - if (((isToken == ShowTrue) && !info->getIsToken()) || ((isToken == ShowFalse) && info->getIsToken())) return false; @@ -144,6 +145,9 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex if (!cardTypes.contains(info->getMainCardType())) return false; + if (filterTree != NULL) + return filterTree->acceptsCard(info); + return true; } @@ -153,5 +157,22 @@ void CardDatabaseDisplayModel::clearSearch() cardText.clear(); cardTypes.clear(); cardColors.clear(); + if (filterTree != NULL) + filterTree->clear(); invalidateFilter(); } + +void CardDatabaseDisplayModel::setFilterTree(FilterTree *filterTree) +{ + if (this->filterTree != NULL) + disconnect(this->filterTree, 0, this, 0); + + this->filterTree = filterTree; + connect(this->filterTree, SIGNAL(changed()), this, SLOT(filterTreeChanged())); + invalidate(); +} + +void CardDatabaseDisplayModel::filterTreeChanged() +{ + invalidate(); +} diff --git a/cockatrice/src/carddatabasemodel.h b/cockatrice/src/carddatabasemodel.h index 51c9467b..eb0f5bc2 100644 --- a/cockatrice/src/carddatabasemodel.h +++ b/cockatrice/src/carddatabasemodel.h @@ -7,6 +7,8 @@ #include #include "carddatabase.h" +class FilterTree; + class CardDatabaseModel : public QAbstractListModel { Q_OBJECT public: @@ -36,8 +38,10 @@ private: FilterBool isToken; QString cardNameBeginning, cardName, cardText; QSet cardNameSet, cardTypes, cardColors; + FilterTree *filterTree; public: CardDatabaseDisplayModel(QObject *parent = 0); + void setFilterTree(FilterTree *filterTree); void setIsToken(FilterBool _isToken) { isToken = _isToken; invalidate(); } void setCardNameBeginning(const QString &_beginning) { cardNameBeginning = _beginning; invalidate(); } void setCardName(const QString &_cardName) { cardName = _cardName; invalidate(); } @@ -48,6 +52,8 @@ public: void clearSearch(); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; +private slots: + void filterTreeChanged(); }; #endif diff --git a/cockatrice/src/cardfilter.cpp b/cockatrice/src/cardfilter.cpp new file mode 100644 index 00000000..fb59adcf --- /dev/null +++ b/cockatrice/src/cardfilter.cpp @@ -0,0 +1,37 @@ +#include "cardfilter.h" + +const char *CardFilter::typeName(Type t) +{ + switch (t) { + case TypeAnd: + return "and"; + case TypeOr: + return "or"; + case TypeAndNot: + return "and not"; + case TypeOrNot: + return "or not"; + default: + return ""; + } +} + +const char *CardFilter::attrName(Attr a) +{ + switch (a) { + case AttrName: + return "name"; + case AttrType: + return "type"; + case AttrColor: + return "color"; + case AttrText: + return "text"; + case AttrSet: + return "set"; + case AttrManaCost: + return "mana cost"; + default: + return ""; + } +} diff --git a/cockatrice/src/cardfilter.h b/cockatrice/src/cardfilter.h new file mode 100644 index 00000000..47a85ea6 --- /dev/null +++ b/cockatrice/src/cardfilter.h @@ -0,0 +1,44 @@ +#ifndef CARDFILTER_H +#define CARDFILTER_H + +#include + +class CardFilter { +public: + enum Type { + TypeAnd = 0, + TypeOr, + TypeAndNot, + TypeOrNot, + TypeEnd + }; + + /* if you add an atribute here you also need to + * add its string representation in attrName */ + enum Attr { + AttrName = 0, + AttrType, + AttrColor, + AttrText, + AttrSet, + AttrManaCost, + AttrEnd + }; + +private: + enum Type t; + enum Attr a; + QString trm; + +public: + CardFilter(QString term, Type type, Attr attr) : trm(term), t(type), a(attr) {}; + + Type type() const { return t; } + const QString &term() const { return trm; } + Attr attr() const { return a; } + + static const char *typeName(Type t); + static const char *attrName(Attr a); +}; + +#endif diff --git a/cockatrice/src/cardframe.cpp b/cockatrice/src/cardframe.cpp new file mode 100644 index 00000000..2ec2b42c --- /dev/null +++ b/cockatrice/src/cardframe.cpp @@ -0,0 +1,59 @@ +#include "cardframe.h" + +#include "carditem.h" +#include "carddatabase.h" +#include "main.h" +#include "cardinfopicture.h" +#include "cardinfotext.h" + +CardFrame::CardFrame(int width, int height, + const QString &cardName, QWidget *parent) + : QStackedWidget(parent) + , info(0) + , cardTextOnly(false) +{ + setFrameStyle(QFrame::Panel | QFrame::Raised); + setMaximumWidth(width); + setMinimumWidth(width); + setMaximumHeight(height); + setMinimumHeight(height); + pic = new CardInfoPicture(width); + addWidget(pic); + text = new CardInfoText(); + addWidget(text); + connect(pic, SIGNAL(hasPictureChanged()), this, SLOT(hasPictureChanged())); + setCard(db->getCard(cardName)); +} + +void CardFrame::setCard(CardInfo *card) +{ + if (info) + disconnect(info, 0, this, 0); + info = card; + connect(info, SIGNAL(destroyed()), this, SLOT(clear())); + text->setCard(info); + pic->setCard(info); +} + +void CardFrame::setCard(const QString &cardName) +{ + setCard(db->getCard(cardName)); +} + +void CardFrame::setCard(AbstractCardItem *card) +{ + setCard(card->getInfo()); +} + +void CardFrame::clear() +{ + setCard(db->getCard()); +} + +void CardFrame::hasPictureChanged() +{ + if (pic->hasPicture() && !cardTextOnly) + setCurrentWidget(pic); + else + setCurrentWidget(text); +} diff --git a/cockatrice/src/cardframe.h b/cockatrice/src/cardframe.h new file mode 100644 index 00000000..d75ce45a --- /dev/null +++ b/cockatrice/src/cardframe.h @@ -0,0 +1,36 @@ +#ifndef CARDFRAME_H +#define CARDFRAME_H + +#include + +class AbstractCardItem; +class CardInfo; +class CardInfoPicture; +class CardInfoText; + +class CardFrame : public QStackedWidget { + Q_OBJECT + +private: + CardInfo *info; + CardInfoPicture *pic; + CardInfoText *text; + bool cardTextOnly; + +public: + CardFrame(int width, int height, const QString &cardName = QString(), + QWidget *parent = 0); + void setCardTextOnly(bool status) { cardTextOnly = status; hasPictureChanged(); } + +public slots: + void setCard(CardInfo *card); + void setCard(const QString &cardName); + void setCard(AbstractCardItem *card); + void clear(); + +private slots: + void hasPictureChanged(); + void toggleCardTextOnly() { setCardTextOnly(!cardTextOnly); } +}; + +#endif diff --git a/cockatrice/src/cardinfopicture.cpp b/cockatrice/src/cardinfopicture.cpp new file mode 100644 index 00000000..8b560958 --- /dev/null +++ b/cockatrice/src/cardinfopicture.cpp @@ -0,0 +1,53 @@ +#include "cardinfopicture.h" + +#include +#include "carditem.h" +#include "carddatabase.h" +#include "main.h" + +CardInfoPicture::CardInfoPicture(int maximumWidth, QWidget *parent) + : QLabel(parent) + , info(0) + , noPicture(true) +{ + setMaximumWidth(maximumWidth); +} + +void CardInfoPicture::setNoPicture(bool status) +{ + if (noPicture != status) { + noPicture = status; + emit hasPictureChanged(); + } +} + +void CardInfoPicture::setCard(CardInfo *card) +{ + if (info) + disconnect(info, 0, this, 0); + info = card; + connect(info, SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap())); + + updatePixmap(); +} + +void CardInfoPicture::updatePixmap() +{ + qreal aspectRatio = (qreal) CARD_HEIGHT / (qreal) CARD_WIDTH; + qreal pixmapWidth = this->width(); + + if (pixmapWidth == 0) { + setNoPicture(true); + return; + } + + QPixmap *resizedPixmap = info->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio)); + if (resizedPixmap) { + setNoPicture(false); + this->setPixmap(*resizedPixmap); + } + else { + setNoPicture(true); + this->setPixmap(*(db->getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio)))); + } +} diff --git a/cockatrice/src/cardinfopicture.h b/cockatrice/src/cardinfopicture.h new file mode 100644 index 00000000..2773f99f --- /dev/null +++ b/cockatrice/src/cardinfopicture.h @@ -0,0 +1,34 @@ +#ifndef CARDINFOPICTURE_H +#define CARDINFOPICTURE_H + +#include + +class AbstractCardItem; +class CardInfo; + +class CardInfoPicture : public QLabel { + Q_OBJECT + +signals: + void hasPictureChanged(); + +private: + CardInfo *info; + bool noPicture; + +public: + CardInfoPicture(int maximumWidth, QWidget *parent = 0); + bool hasPicture() const { return !noPicture; } + +private: + void setNoPicture(bool status); + +public slots: + void setCard(CardInfo *card); + +private slots: + void updatePixmap(); + +}; + +#endif diff --git a/cockatrice/src/cardinfotext.cpp b/cockatrice/src/cardinfotext.cpp new file mode 100644 index 00000000..21336a83 --- /dev/null +++ b/cockatrice/src/cardinfotext.cpp @@ -0,0 +1,67 @@ +#include "cardinfotext.h" + +#include +#include +#include +#include "carditem.h" +#include "carddatabase.h" +#include "main.h" + +CardInfoText::CardInfoText(QWidget *parent) + : QFrame(parent) + , info(0) +{ + nameLabel1 = new QLabel; + nameLabel2 = new QLabel; + nameLabel2->setWordWrap(true); + manacostLabel1 = new QLabel; + manacostLabel2 = new QLabel; + manacostLabel2->setWordWrap(true); + cardtypeLabel1 = new QLabel; + cardtypeLabel2 = new QLabel; + cardtypeLabel2->setWordWrap(true); + powtoughLabel1 = new QLabel; + powtoughLabel2 = new QLabel; + loyaltyLabel1 = new QLabel; + loyaltyLabel2 = new QLabel; + + textLabel = new QTextEdit(); + textLabel->setReadOnly(true); + + QGridLayout *grid = new QGridLayout(this); + int row = 0; + grid->addWidget(nameLabel1, row, 0); + grid->addWidget(nameLabel2, row++, 1); + grid->addWidget(manacostLabel1, row, 0); + grid->addWidget(manacostLabel2, row++, 1); + grid->addWidget(cardtypeLabel1, row, 0); + grid->addWidget(cardtypeLabel2, row++, 1); + grid->addWidget(powtoughLabel1, row, 0); + grid->addWidget(powtoughLabel2, row++, 1); + grid->addWidget(loyaltyLabel1, row, 0); + grid->addWidget(loyaltyLabel2, row++, 1); + grid->addWidget(textLabel, row, 0, -1, 2); + grid->setRowStretch(row, 1); + grid->setColumnStretch(1, 1); + + retranslateUi(); +} + +void CardInfoText::setCard(CardInfo *card) +{ + nameLabel2->setText(card->getName()); + manacostLabel2->setText(card->getManaCost()); + cardtypeLabel2->setText(card->getCardType()); + powtoughLabel2->setText(card->getPowTough()); + loyaltyLabel2->setText(card->getLoyalty() > 0 ? QString::number(card->getLoyalty()) : QString()); + textLabel->setText(card->getText()); +} + +void CardInfoText::retranslateUi() +{ + nameLabel1->setText(tr("Name:")); + manacostLabel1->setText(tr("Mana cost:")); + cardtypeLabel1->setText(tr("Card type:")); + powtoughLabel1->setText(tr("P / T:")); + loyaltyLabel1->setText(tr("Loyalty:")); +} diff --git a/cockatrice/src/cardinfotext.h b/cockatrice/src/cardinfotext.h new file mode 100644 index 00000000..c0f59480 --- /dev/null +++ b/cockatrice/src/cardinfotext.h @@ -0,0 +1,31 @@ +#ifndef CARDINFOTEXT_H +#define CARDINFOTEXT_H + +#include + +class QLabel; +class QTextEdit; +class CardInfo; + +class CardInfoText : public QFrame { + Q_OBJECT + +private: + QLabel *nameLabel1, *nameLabel2; + QLabel *manacostLabel1, *manacostLabel2; + QLabel *cardtypeLabel1, *cardtypeLabel2; + QLabel *powtoughLabel1, *powtoughLabel2; + QLabel *loyaltyLabel1, *loyaltyLabel2; + QTextEdit *textLabel; + + CardInfo *info; + +public: + CardInfoText(QWidget *parent = 0); + void retranslateUi(); + +public slots: + void setCard(CardInfo *card); +}; + +#endif diff --git a/cockatrice/src/decklistmodel.cpp b/cockatrice/src/decklistmodel.cpp index 3e55ec15..e8518046 100644 --- a/cockatrice/src/decklistmodel.cpp +++ b/cockatrice/src/decklistmodel.cpp @@ -169,8 +169,7 @@ Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const return 0; Qt::ItemFlags result = Qt::ItemIsEnabled; - if (getNode(index)) - result |= Qt::ItemIsSelectable; + result |= Qt::ItemIsSelectable; return result; } @@ -236,6 +235,38 @@ InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerD return newNode; } +DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, const QString &zoneName) const +{ + InnerDecklistNode *zoneNode, *typeNode; + CardInfo *info; + QString cardType; + + zoneNode = dynamic_cast(root->findChild(zoneName)); + if(!zoneNode) + return NULL; + + info = db->getCard(cardName); + if(!info) + return NULL; + + cardType = info->getMainCardType(); + typeNode = dynamic_cast(zoneNode->findChild(cardType)); + if(!typeNode) + return NULL; + + return dynamic_cast(typeNode->findChild(cardName)); +} + +QModelIndex DeckListModel::findCard(const QString &cardName, const QString &zoneName) const +{ + DecklistModelCardNode *cardNode; + + cardNode = findCardNode(cardName, zoneName); + if(!cardNode) + return QModelIndex(); + return nodeToIndex(cardNode); +} + QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneName) { InnerDecklistNode *zoneNode = createNodeIfNeeded(zoneName, root); diff --git a/cockatrice/src/decklistmodel.h b/cockatrice/src/decklistmodel.h index 2e2c570a..e2a85c56 100644 --- a/cockatrice/src/decklistmodel.h +++ b/cockatrice/src/decklistmodel.h @@ -45,6 +45,7 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value, int role); bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + QModelIndex findCard(const QString &cardName, const QString &zoneName) const; QModelIndex addCard(const QString &cardName, const QString &zoneName); void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); void cleanList(); @@ -56,6 +57,7 @@ private: InnerDecklistNode *root; InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent); QModelIndex nodeToIndex(AbstractDecklistNode *node) const; + DecklistModelCardNode *findCardNode(const QString &cardName, const QString &zoneName) const; void emitRecursiveUpdates(const QModelIndex &index); void sortHelper(InnerDecklistNode *node, Qt::SortOrder order); diff --git a/cockatrice/src/filterbuilder.cpp b/cockatrice/src/filterbuilder.cpp new file mode 100644 index 00000000..f594a5de --- /dev/null +++ b/cockatrice/src/filterbuilder.cpp @@ -0,0 +1,84 @@ +#include "filterbuilder.h" + +#include +#include +#include +#include + +#include "cardfilter.h" + +FilterBuilder::FilterBuilder(QWidget *parent) + : QFrame(parent) +{ + int i; + + QVBoxLayout *layout = new QVBoxLayout; + QHBoxLayout *addFilter = new QHBoxLayout; + + filterCombo = new QComboBox; + for (i = 0; i < CardFilter::AttrEnd; i++) + filterCombo->addItem( + tr(CardFilter::attrName(static_cast(i))), + QVariant(i) + ); + + typeCombo = new QComboBox; + for (i = 0; i < CardFilter::TypeEnd; i++) + typeCombo->addItem( + tr(CardFilter::typeName(static_cast(i))), + QVariant(i) + ); + + QPushButton *ok = new QPushButton("+"); + ok->setMaximumSize(20, 20); + + addFilter->addWidget(ok); + addFilter->addWidget(typeCombo); + addFilter->addWidget(filterCombo, Qt::AlignLeft); + + edit = new QLineEdit; + edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + layout->addLayout(addFilter); + layout->addWidget(edit); + + setFrameStyle(QFrame::Panel | QFrame::Raised); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + connect(edit, SIGNAL(returnPressed()), this, SLOT(emit_add())); + connect(ok, SIGNAL(released()), this, SLOT(emit_add())); + connect(filterCombo, SIGNAL(currentIndexChanged(int)), edit, SLOT(clear())); + fltr = NULL; +} + +FilterBuilder::~FilterBuilder() +{ + destroyFilter(); +} + +void FilterBuilder::destroyFilter() +{ + if (fltr) + delete fltr; +} + +static int comboCurrentIntData(const QComboBox *combo) +{ + return combo->itemData(combo->currentIndex()).toInt(); +} + +void FilterBuilder::emit_add() +{ + QString txt; + + txt = edit->text(); + if (txt.length() < 1) + return; + + destroyFilter(); + fltr = new CardFilter(txt, + static_cast(comboCurrentIntData(typeCombo)), + static_cast(comboCurrentIntData(filterCombo))); + emit add(fltr); + edit->clear(); +} diff --git a/cockatrice/src/filterbuilder.h b/cockatrice/src/filterbuilder.h new file mode 100644 index 00000000..6438e383 --- /dev/null +++ b/cockatrice/src/filterbuilder.h @@ -0,0 +1,35 @@ +#ifndef FILTERBUILDER_H +#define FILTERBUILDER_H + +#include + +class QCheckBox; +class QComboBox; +class QLineEdit; +class CardFilter; + +class FilterBuilder : public QFrame { + Q_OBJECT + +private: + QComboBox *typeCombo; + QComboBox *filterCombo; + QLineEdit *edit; + CardFilter *fltr; + + void destroyFilter(); + +public: + FilterBuilder(QWidget *parent = 0); + ~FilterBuilder(); + +signals: + void add(const CardFilter *f); + +public slots: +private slots: + void emit_add(); +protected: +}; + +#endif diff --git a/cockatrice/src/filtertree.cpp b/cockatrice/src/filtertree.cpp new file mode 100644 index 00000000..4c28028b --- /dev/null +++ b/cockatrice/src/filtertree.cpp @@ -0,0 +1,339 @@ +#include "filtertree.h" +#include "cardfilter.h" +#include "carddatabase.h" + +#include + +template +FilterTreeNode *FilterTreeBranch::nodeAt(int i) const +{ + return ((childNodes.size() > i)? childNodes.at(i) : NULL); +} + +template +void FilterTreeBranch::deleteAt(int i) +{ + preRemoveChild(this, i); + delete childNodes.takeAt(i); + postRemoveChild(this, i); + nodeChanged(); +} + +template +int FilterTreeBranch::childIndex(const FilterTreeNode *node) const +{ + FilterTreeNode *unconst; + T downcasted; + + /* to do the dynamic cast to T we will lose const'ness, but we can + * trust QList::indexOf */ + unconst = (FilterTreeNode *) node; + downcasted = dynamic_cast(unconst); + if (downcasted == NULL) + return -1; + return childNodes.indexOf(downcasted); +} + +template +FilterTreeBranch::~FilterTreeBranch() +{ + while (!childNodes.isEmpty()) + delete childNodes.takeFirst(); +} + +const FilterItemList *LogicMap::findTypeList(CardFilter::Type type) const +{ + QList::const_iterator i; + + for (i = childNodes.constBegin(); i != childNodes.constEnd(); i++) + if ((*i)->type == type) + return *i; + + return NULL; +} + +FilterItemList *LogicMap::typeList(CardFilter::Type type) +{ + QList::iterator i; + int count; + + count = 0; + for (i = childNodes.begin(); i != childNodes.end(); i++) { + if ((*i)->type == type) + break; + count++; + } + if (i == childNodes.end()) { + preInsertChild(this, count); + i = childNodes.insert(i, new FilterItemList(type, this)); + postInsertChild(this, count); + nodeChanged(); + } + + return *i; +} + +FilterTreeNode *LogicMap::parent() const +{ + return p; +} + +int FilterItemList::termIndex(const QString &term) const +{ + int i; + + for (i = 0; i < childNodes.count(); i++) + if ((childNodes.at(i))->term == term) + return i; + + return -1; +} + +FilterTreeNode *FilterItemList::termNode(const QString &term) +{ + int i, count; + FilterItem *fi; + + i = termIndex(term); + if (i < 0) { + fi = new FilterItem(term, this); + count = childNodes.count(); + preInsertChild(this, count); + childNodes.append(fi); + postInsertChild(this, count); + nodeChanged(); + return fi; + } + + return childNodes.at(i); +} + +bool FilterItemList::testTypeAnd(const CardInfo *info, CardFilter::Attr attr) const +{ + QList::const_iterator i; + + for (i = childNodes.constBegin(); i != childNodes.constEnd(); i++) { + if (!(*i)->isEnabled()) + continue; + if (!(*i)->acceptCardAttr(info, attr)) + return false; + } + + return true; +} + +bool FilterItemList::testTypeAndNot(const CardInfo *info, CardFilter::Attr attr) const +{ + // if any one in the list is true, return false + return !testTypeOr(info, attr); +} + +bool FilterItemList::testTypeOr(const CardInfo *info, CardFilter::Attr attr) const +{ + QList::const_iterator i; + + for (i = childNodes.constBegin(); i != childNodes.constEnd(); i++) { + if (!(*i)->isEnabled()) + continue; + if ((*i)->acceptCardAttr(info, attr)) + return true; + } + + return false; +} + +bool FilterItemList::testTypeOrNot(const CardInfo *info, CardFilter::Attr attr) const +{ + // if any one in the list is false, return true + return !testTypeAnd(info, attr); +} + +bool FilterItem::acceptName(const CardInfo *info) const +{ + return info->getName().contains(term, Qt::CaseInsensitive); +} + +bool FilterItem::acceptType(const CardInfo *info) const +{ + return info->getCardType().contains(term, Qt::CaseInsensitive); +} + +bool FilterItem::acceptColor(const CardInfo *info) const +{ + QStringList::const_iterator i; + bool status; + + status = false; + for (i = info->getColors().constBegin(); i != info->getColors().constEnd(); i++) + if ((*i).contains(term, Qt::CaseInsensitive)) { + status = true; + break; + } + + return status; +} + +bool FilterItem::acceptText(const CardInfo *info) const +{ + return info->getText().contains(term, Qt::CaseInsensitive); +} + +bool FilterItem::acceptSet(const CardInfo *info) const +{ + SetList::const_iterator i; + bool status; + + status = false; + for (i = info->getSets().constBegin(); i != info->getSets().constEnd(); i++) + if ((*i)->getShortName() == term + || (*i)->getLongName().contains(term, Qt::CaseInsensitive)) { + status = true; + break; + } + + return status; +} + +bool FilterItem::acceptManaCost(const CardInfo *info) const +{ + return (info->getManaCost() == term); +} + +bool FilterItem::acceptCardAttr(const CardInfo *info, CardFilter::Attr attr) const +{ + bool status; + + switch (attr) { + case CardFilter::AttrName: + status = acceptName(info); + break; + case CardFilter::AttrType: + status = acceptType(info); + break; + case CardFilter::AttrColor: + status = acceptColor(info); + break; + case CardFilter::AttrText: + status = acceptText(info); + break; + case CardFilter::AttrSet: + status = acceptSet(info); + break; + case CardFilter::AttrManaCost: + status = acceptManaCost(info); + break; + default: + status = true; /* ignore this attribute */ + } + + return status; +} + +/* need to define these here to make QT happy, otherwise + * moc doesnt find some of the FilterTreeBranch symbols. + */ +FilterTree::FilterTree() {} +FilterTree::~FilterTree() {} + +LogicMap *FilterTree::attrLogicMap(CardFilter::Attr attr) +{ + QList::iterator i; + int count; + + count = 0; + for (i = childNodes.begin(); i != childNodes.end(); i++) { + if ((*i)->attr == attr) + break; + count++; + } + + if (i == childNodes.end()) { + preInsertChild(this, count); + i = childNodes.insert(i, new LogicMap(attr, this)); + postInsertChild(this, count); + nodeChanged(); + } + + return *i; +} + +FilterItemList *FilterTree::attrTypeList(CardFilter::Attr attr, + CardFilter::Type type) +{ + return attrLogicMap(attr)->typeList(type); +} + +int FilterTree::findTermIndex(CardFilter::Attr attr, CardFilter::Type type, + const QString &term) +{ + attrTypeList(attr, type)->termIndex(term); +} + +int FilterTree::findTermIndex(const CardFilter *f) +{ + return findTermIndex(f->attr(), f->type(), f->term()); +} + +FilterTreeNode *FilterTree::termNode(CardFilter::Attr attr, CardFilter::Type type, + const QString &term) +{ + return attrTypeList(attr, type)->termNode(term); +} + +FilterTreeNode *FilterTree::termNode(const CardFilter *f) +{ + return termNode(f->attr(), f->type(), f->term()); +} + +FilterTreeNode *FilterTree::attrTypeNode(CardFilter::Attr attr, + CardFilter::Type type) +{ + return attrTypeList(attr, type); +} + +bool FilterTree::testAttr(const CardInfo *info, const LogicMap *lm) const +{ + const FilterItemList *fil; + bool status; + + status = true; + + fil = lm->findTypeList(CardFilter::TypeAnd); + if (fil != NULL && fil->isEnabled() && !fil->testTypeAnd(info, lm->attr)) + return false; + + fil = lm->findTypeList(CardFilter::TypeAndNot); + if (fil != NULL && fil->isEnabled() && !fil->testTypeAndNot(info, lm->attr)) + return false; + + fil = lm->findTypeList(CardFilter::TypeOr); + if (fil != NULL && fil->isEnabled()) { + status = false; + // if this is true we can return because it is OR'd with the OrNot list + if (fil->testTypeOr(info, lm->attr)) + return true; + } + + fil = lm->findTypeList(CardFilter::TypeOrNot); + if (fil != NULL && fil->isEnabled() && fil->testTypeOrNot(info, lm->attr)) + return true; + + return status; +} + +bool FilterTree::acceptsCard(const CardInfo *info) const +{ + QList::const_iterator i; + + for (i = childNodes.constBegin(); i != childNodes.constEnd(); i++) + if ((*i)->isEnabled() && !testAttr(info, *i)) + return false; + + return true; +} + +void FilterTree::clear() +{ + while(childCount() > 0) + deleteAt(0); +} diff --git a/cockatrice/src/filtertree.h b/cockatrice/src/filtertree.h new file mode 100644 index 00000000..9a5fc531 --- /dev/null +++ b/cockatrice/src/filtertree.h @@ -0,0 +1,163 @@ +#ifndef FILTERTREE_H +#define FILTERTREE_H + +#include +#include +#include + +#include "cardfilter.h" + +class CardInfo; + +class FilterTreeNode { +private: + bool enabled; +public: + FilterTreeNode() : enabled(true) {} + virtual bool isEnabled() const { return enabled; } + virtual void enable() { enabled = true; nodeChanged(); } + virtual void disable() { enabled = false; nodeChanged(); } + virtual FilterTreeNode *parent() const { return NULL; } + virtual FilterTreeNode *nodeAt(int i) const { return NULL; } + virtual void deleteAt(int i) {} + virtual int childCount() const { return 0; } + virtual int childIndex(const FilterTreeNode *node) const { return -1; } + virtual int index() const { return (parent() != NULL)? parent()->childIndex(this) : -1; } + virtual QString text() const { return QString(textCStr()); } + virtual bool isLeaf() const { return false; } + virtual const char *textCStr() const { return ""; } + virtual void nodeChanged() const { + if (parent() != NULL) parent()->nodeChanged(); + } + virtual void preInsertChild(const FilterTreeNode *p, int i) const { + if (parent() != NULL) parent()->preInsertChild(p, i); + } + virtual void postInsertChild(const FilterTreeNode *p, int i) const { + if (parent() != NULL) parent()->postInsertChild(p, i); + } + virtual void preRemoveChild(const FilterTreeNode *p, int i) const { + if (parent() != NULL) parent()->preRemoveChild(p, i); + } + virtual void postRemoveChild(const FilterTreeNode *p, int i) const { + if (parent() != NULL) parent()->postRemoveChild(p, i); + } +}; + +template +class FilterTreeBranch : public FilterTreeNode { +protected: + QList childNodes; +public: + ~FilterTreeBranch(); + FilterTreeNode *nodeAt(int i) const; + void deleteAt(int i); + int childCount() const { return childNodes.size(); } + int childIndex(const FilterTreeNode *node) const; +}; + +class FilterItemList; +class FilterTree; +class LogicMap : public FilterTreeBranch { + +private: + FilterTree *const p; + +public: + const CardFilter::Attr attr; + + LogicMap(CardFilter::Attr a, FilterTree *parent) + : attr(a), p(parent) {} + const FilterItemList *findTypeList(CardFilter::Type type) const; + FilterItemList *typeList(CardFilter::Type type); + FilterTreeNode *parent() const; + const char* textCStr() const { return CardFilter::attrName(attr); } +}; + +class FilterItem; +class FilterItemList : public FilterTreeBranch { +private: + LogicMap *const p; +public: + const CardFilter::Type type; + + FilterItemList(CardFilter::Type t, LogicMap *parent) + : type(t), p(parent) {} + CardFilter::Attr attr() const { return p->attr; } + FilterTreeNode *parent() const { return p; } + int termIndex(const QString &term) const; + FilterTreeNode *termNode(const QString &term); + const char *textCStr() const { return CardFilter::typeName(type); } + + bool testTypeAnd(const CardInfo *info, CardFilter::Attr attr) const; + bool testTypeAndNot(const CardInfo *info, CardFilter::Attr attr) const; + bool testTypeOr(const CardInfo *info, CardFilter::Attr attr) const; + bool testTypeOrNot(const CardInfo *info, CardFilter::Attr attr) const; +}; + +class FilterItem : public FilterTreeNode { +private: + FilterItemList *const p; +public: + const QString term; + + FilterItem(QString trm, FilterItemList *parent) + : p(parent), term(trm) {} + + CardFilter::Attr attr() const { return p->attr(); } + CardFilter::Type type() const { return p->type; } + FilterTreeNode *parent() const { return p; } + QString text() const { return term; } + const char *textCStr() const { return term.toStdString().c_str(); } + bool isLeaf() const { return true; } + + bool acceptName(const CardInfo *info) const; + bool acceptType(const CardInfo *info) const; + bool acceptColor(const CardInfo *info) const; + bool acceptText(const CardInfo *info) const; + bool acceptSet(const CardInfo *info) const; + bool acceptManaCost(const CardInfo *info) const; + bool acceptCardAttr(const CardInfo *info, CardFilter::Attr attr) const; +}; + +class FilterTree : public QObject, public FilterTreeBranch { + Q_OBJECT + +signals: + void preInsertRow(const FilterTreeNode *parent, int i) const; + void postInsertRow(const FilterTreeNode *parent, int i) const; + void preRemoveRow(const FilterTreeNode *parent, int i) const; + void postRemoveRow(const FilterTreeNode *parent, int i) const; + void changed() const; + +private: + LogicMap *attrLogicMap(CardFilter::Attr attr); + FilterItemList *attrTypeList(CardFilter::Attr attr, + CardFilter::Type type); + + bool testAttr(const CardInfo *info, const LogicMap *lm) const; + + void nodeChanged() const { emit changed(); } + void preInsertChild(const FilterTreeNode *p, int i) const { emit preInsertRow(p, i); } + void postInsertChild(const FilterTreeNode *p, int i) const { emit postInsertRow(p, i); } + void preRemoveChild(const FilterTreeNode *p, int i) const { emit preRemoveRow(p, i); } + void postRemoveChild(const FilterTreeNode *p, int i) const { emit postRemoveRow(p, i); } + +public: + FilterTree(); + ~FilterTree(); + int findTermIndex(CardFilter::Attr attr, CardFilter::Type type, + const QString &term); + int findTermIndex(const CardFilter *f); + FilterTreeNode *termNode(CardFilter::Attr attr, CardFilter::Type type, + const QString &term); + FilterTreeNode *termNode(const CardFilter *f); + FilterTreeNode *attrTypeNode(CardFilter::Attr attr, + CardFilter::Type type); + const char *textCStr() { return "root"; } + int index() const { return 0; } + + bool acceptsCard(const CardInfo *info) const; + void clear(); +}; + +#endif diff --git a/cockatrice/src/filtertreemodel.cpp b/cockatrice/src/filtertreemodel.cpp new file mode 100644 index 00000000..5fed7766 --- /dev/null +++ b/cockatrice/src/filtertreemodel.cpp @@ -0,0 +1,272 @@ +#include +#include "filtertreemodel.h" +#include "filtertree.h" +#include "cardfilter.h" + +FilterTreeModel::FilterTreeModel(QObject *parent) + : QAbstractItemModel(parent) +{ + fTree = new FilterTree; + connect(fTree, + SIGNAL(preInsertRow(const FilterTreeNode *, int)), + this, SLOT(proxyBeginInsertRow(const FilterTreeNode *, int))); + connect(fTree, + SIGNAL(postInsertRow(const FilterTreeNode *, int)), + this, SLOT(proxyEndInsertRow(const FilterTreeNode *, int))); + connect(fTree, + SIGNAL(preRemoveRow(const FilterTreeNode *, int)), + this, SLOT(proxyBeginRemoveRow(const FilterTreeNode *, int))); + connect(fTree, + SIGNAL(postRemoveRow(const FilterTreeNode *, int)), + this, SLOT(proxyEndRemoveRow(const FilterTreeNode *, int))); +} + +FilterTreeModel::~FilterTreeModel() +{ + delete fTree; +} + +void FilterTreeModel::proxyBeginInsertRow(const FilterTreeNode *node, int i) +{ + int idx; + + idx = node->index(); + if (idx >= 0) + beginInsertRows(createIndex(idx, 0, (void *) node), i, i); +} + +void FilterTreeModel::proxyEndInsertRow(const FilterTreeNode *node, int) +{ + int idx; + + idx = node->index(); + if (idx >= 0) + endInsertRows(); +} + +void FilterTreeModel::proxyBeginRemoveRow(const FilterTreeNode *node, int i) +{ + int idx; + + idx = node->index(); + if (idx >= 0) + beginRemoveRows(createIndex(idx, 0, (void *) node), i, i); +} + +void FilterTreeModel::proxyEndRemoveRow(const FilterTreeNode *node, int) +{ + int idx; + + idx = node->index(); + if (idx >= 0) + endRemoveRows(); +} + +FilterTreeNode *FilterTreeModel::indexToNode(const QModelIndex &idx) const +{ + void *ip; + FilterTreeNode *node; + + if (!idx.isValid()) + return fTree; + + ip = idx.internalPointer(); + if (ip == NULL) + return fTree; + + node = static_cast(ip); + return node; +} + +void FilterTreeModel::addFilter(const CardFilter *f) +{ + emit layoutAboutToBeChanged(); + fTree->termNode(f); + emit layoutChanged(); +} + +int FilterTreeModel::rowCount(const QModelIndex &parent) const +{ + const FilterTreeNode *node; + int result; + + if (parent.column() > 0) + return 0; + + node = indexToNode(parent); + if (node) + result = node->childCount(); + else + result = 0; + + return result; +} + +int FilterTreeModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; +} + +QVariant FilterTreeModel::data(const QModelIndex &index, int role) const +{ + const FilterTreeNode *node; + + if (!index.isValid()) + return QVariant(); + if (index.column() >= columnCount()) + return QVariant(); + + node = indexToNode(index); + if (node == NULL) + return QVariant(); + + switch (role) { + case Qt::FontRole: + if (!node->isLeaf()) { + QFont f; + f.setBold(true); + return f; + } + break; + case Qt::DisplayRole: + case Qt::EditRole: + case Qt::ToolTipRole: + case Qt::StatusTipRole: + case Qt::WhatsThisRole: + if(!node->isLeaf()) + return tr(node->textCStr()); + else + return node->text(); + case Qt::CheckStateRole: + if (node->isEnabled()) + return Qt::Checked; + else + return Qt::Unchecked; + default: + return QVariant(); + } + + return QVariant(); +} + +bool FilterTreeModel::setData(const QModelIndex &index, + const QVariant &value, int role) +{ + FilterTreeNode *node; + + if (!index.isValid()) + return false; + if (index.column() >= columnCount()) + return false; + if (role != Qt::CheckStateRole ) + return false; + + node = indexToNode(index); + if (node == NULL || node == fTree) + return false; + + Qt::CheckState state = static_cast(value.toInt()); + if (state == Qt::Checked) + node->enable(); + else + node->disable(); + + emit dataChanged(index, index); + return true; +} + +Qt::ItemFlags FilterTreeModel::flags(const QModelIndex &index) const +{ + const FilterTreeNode *node; + Qt::ItemFlags result; + + if (!index.isValid()) + return 0; + + node = indexToNode(index); + if (node == NULL) + return 0; + + result = Qt::ItemIsEnabled; + if (node == fTree) + return result; + + result |= Qt::ItemIsSelectable; + result |= Qt::ItemIsUserCheckable; + + return result; +} + +QModelIndex FilterTreeModel::nodeIndex(const FilterTreeNode *node, int row, int column) const +{ + FilterTreeNode *child; + + if (column > 0 || row >= node->childCount()) + return QModelIndex(); + + child = node->nodeAt(row); + return createIndex(row, column, child); +} + +QModelIndex FilterTreeModel::index(int row, int column, + const QModelIndex &parent) const +{ + const FilterTreeNode *node; + + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + node = indexToNode(parent); + if (node == NULL) + return QModelIndex(); + + return nodeIndex(node, row, column); +} + +QModelIndex FilterTreeModel::parent(const QModelIndex &ind) const +{ + const FilterTreeNode *node; + FilterTreeNode *parent; + int row; + QModelIndex idx; + + if (!ind.isValid()) + return QModelIndex(); + + node = indexToNode(ind); + if (node == NULL || node == fTree) + return QModelIndex(); + + parent = node->parent(); + if (parent) { + row = parent->index(); + if (row < 0) + return QModelIndex(); + idx = createIndex(row, 0, parent); + return idx; + } + + return QModelIndex(); +} + +bool FilterTreeModel::removeRows(int row, int count, const QModelIndex & parent) +{ + FilterTreeNode *node; + int i, last; + + last = row+count-1; + if (!parent.isValid() || count < 1 || row < 0) + return false; + + node = indexToNode(parent); + if (node == NULL || last >= node->childCount()) + return false; + + for (i = 0; i < count; i++) + node->deleteAt(row); + + if (node != fTree && node->childCount() < 1) + return removeRow(parent.row(), parent.parent()); + + return true; +} diff --git a/cockatrice/src/filtertreemodel.h b/cockatrice/src/filtertreemodel.h new file mode 100644 index 00000000..ac90db57 --- /dev/null +++ b/cockatrice/src/filtertreemodel.h @@ -0,0 +1,44 @@ +#ifndef FILTERTREEMODEL_H +#define FILTERTREEMODEL_H + +#include + +class FilterTree; +class CardFilter; +class FilterTreeNode; + +class FilterTreeModel : public QAbstractItemModel { + Q_OBJECT +private: + FilterTree *fTree; + +public slots: + void addFilter(const CardFilter *f); + +private slots: + void proxyBeginInsertRow(const FilterTreeNode *, int); + void proxyEndInsertRow(const FilterTreeNode *, int); + void proxyBeginRemoveRow(const FilterTreeNode *, int); + void proxyEndRemoveRow(const FilterTreeNode *, int); + +private: + FilterTreeNode *indexToNode(const QModelIndex &idx) const; + QModelIndex nodeIndex(const FilterTreeNode *node, int row, int column) const; + +public: + FilterTreeModel(QObject *parent = 0); + ~FilterTreeModel(); + FilterTree *filterTree() const { return fTree; } + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) const; + QModelIndex parent(const QModelIndex &ind) const; + QModelIndex index(int row, int column, + const QModelIndex &parent) const; + bool removeRows(int row, int count, const QModelIndex & parent); +}; + +#endif diff --git a/cockatrice/src/keysignals.cpp b/cockatrice/src/keysignals.cpp new file mode 100644 index 00000000..5db58cb5 --- /dev/null +++ b/cockatrice/src/keysignals.cpp @@ -0,0 +1,65 @@ +#include "keysignals.h" +#include + +bool KeySignals::eventFilter(QObject * /*object*/, QEvent *event) { + QKeyEvent *kevent; + + if(event->type() != QEvent::KeyPress) + return false; + + kevent = static_cast(event); + switch(kevent->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + if (kevent->modifiers().testFlag(Qt::AltModifier) + && kevent->modifiers().testFlag(Qt::ControlModifier) ) + emit onCtrlAltEnter(); + else if (kevent->modifiers() & Qt::ControlModifier) + emit onCtrlEnter(); + else + emit onEnter(); + + break; + case Qt::Key_Right: + emit onRight(); + + break; + case Qt::Key_Left: + emit onLeft(); + + break; + case Qt::Key_Delete: + case Qt::Key_Backspace: + emit onDelete(); + + break; + case Qt::Key_Minus: + if (kevent->modifiers().testFlag(Qt::AltModifier) + && kevent->modifiers().testFlag(Qt::ControlModifier) ) + emit onCtrlAltMinus(); + + break; + case Qt::Key_Equal: + if (kevent->modifiers().testFlag(Qt::AltModifier) + && kevent->modifiers().testFlag(Qt::ControlModifier) ) + emit onCtrlAltEqual(); + + break; + case Qt::Key_BracketLeft: + if (kevent->modifiers().testFlag(Qt::AltModifier) + && kevent->modifiers().testFlag(Qt::ControlModifier) ) + emit onCtrlAltLBracket(); + + break; + case Qt::Key_BracketRight: + if (kevent->modifiers().testFlag(Qt::AltModifier) + && kevent->modifiers().testFlag(Qt::ControlModifier) ) + emit onCtrlAltRBracket(); + + break; + default: + return false; + } + + return false; +} diff --git a/cockatrice/src/keysignals.h b/cockatrice/src/keysignals.h new file mode 100644 index 00000000..23a07b77 --- /dev/null +++ b/cockatrice/src/keysignals.h @@ -0,0 +1,21 @@ +#include +#include + +class KeySignals : public QObject { + Q_OBJECT + +signals: + void onEnter(); + void onCtrlEnter(); + void onCtrlAltEnter(); + void onLeft(); + void onRight(); + void onDelete(); + void onCtrlAltMinus(); + void onCtrlAltEqual(); + void onCtrlAltLBracket(); + void onCtrlAltRBracket(); + +protected: + virtual bool eventFilter(QObject *, QEvent *event); +}; diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 80709a84..91c8819e 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -22,7 +22,6 @@ #include "carddatabasemodel.h" #include "decklistmodel.h" #include "cardinfowidget.h" -#include "dlg_cardsearch.h" #include "dlg_load_deck_from_clipboard.h" #include "dlg_edit_tokens.h" #include "main.h" @@ -34,6 +33,9 @@ #include "pending_command.h" #include "pb/response.pb.h" #include "pb/command_deck_upload.pb.h" +#include "filtertreemodel.h" +#include "cardframe.h" +#include "filterbuilder.h" void SearchLineEdit::keyPressEvent(QKeyEvent *event) { @@ -45,9 +47,6 @@ void SearchLineEdit::keyPressEvent(QKeyEvent *event) TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) : Tab(_tabSupervisor, parent), modified(false) { - aSearch = new QAction(QString(), this); - aSearch->setIcon(QIcon(":/resources/icon_search.svg")); - connect(aSearch, SIGNAL(triggered()), this, SLOT(actSearch())); aClearSearch = new QAction(QString(), this); aClearSearch->setIcon(QIcon(":/resources/icon_clearsearch.svg")); connect(aClearSearch, SIGNAL(triggered()), this, SLOT(actClearSearch())); @@ -55,25 +54,36 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) searchLabel = new QLabel(); searchEdit = new SearchLineEdit; searchLabel->setBuddy(searchEdit); + setFocusProxy(searchEdit); + setFocusPolicy(Qt::ClickFocus); + + searchEdit->installEventFilter(&searchKeySignals); connect(searchEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &))); - connect(searchEdit, SIGNAL(returnPressed()), this, SLOT(actAddCard())); - QToolButton *searchButton = new QToolButton; - searchButton->setDefaultAction(aSearch); - QToolButton *clearSearchButton = new QToolButton; - clearSearchButton->setDefaultAction(aClearSearch); + connect(&searchKeySignals, SIGNAL(onEnter()), this, SLOT(actAddCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actAddCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltRBracket()), this, SLOT(actAddCardToSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrementCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard())); + + QToolBar *deckEditToolBar = new QToolBar; + deckEditToolBar->setOrientation(Qt::Horizontal); + deckEditToolBar->setIconSize(QSize(24, 24)); QHBoxLayout *searchLayout = new QHBoxLayout; + searchLayout->addWidget(deckEditToolBar); searchLayout->addWidget(searchLabel); searchLayout->addWidget(searchEdit); - searchLayout->addWidget(searchButton); - searchLayout->addWidget(clearSearchButton); databaseModel = new CardDatabaseModel(db, this); databaseDisplayModel = new CardDatabaseDisplayModel(this); databaseDisplayModel->setSourceModel(databaseModel); databaseDisplayModel->setFilterKeyColumn(0); databaseDisplayModel->sort(0, Qt::AscendingOrder); + databaseView = new QTreeView(); + databaseView->setFocusProxy(searchEdit); databaseView->setModel(databaseDisplayModel); databaseView->setUniformRowHeights(true); databaseView->setRootIsDecorated(false); @@ -89,21 +99,33 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) leftFrame->addLayout(searchLayout); leftFrame->addWidget(databaseView); - cardInfo = new CardInfoWidget(CardInfoWidget::ModeDeckEditor); - cardInfo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + cardInfo = new CardFrame(250, 356); + aCardTextOnly = new QAction(QString(), this); + aCardTextOnly->setCheckable(true); + connect(aCardTextOnly, SIGNAL(triggered()), cardInfo, SLOT(toggleCardTextOnly())); - QToolBar *verticalToolBar = new QToolBar; - verticalToolBar->setOrientation(Qt::Vertical); - verticalToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - verticalToolBar->setIconSize(QSize(24, 24)); - QHBoxLayout *verticalToolBarLayout = new QHBoxLayout; - verticalToolBarLayout->addStretch(); - verticalToolBarLayout->addWidget(verticalToolBar); - verticalToolBarLayout->addStretch(); + filterModel = new FilterTreeModel(); + databaseDisplayModel->setFilterTree(filterModel->filterTree()); + filterView = new QTreeView; + filterView->setModel(filterModel); + filterView->setMaximumWidth(250); + filterView->setUniformRowHeights(true); + filterView->setHeaderHidden(true); + filterView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(filterModel, SIGNAL(layoutChanged()), filterView, SLOT(expandAll())); + connect(filterView, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(filterViewCustomContextMenu(const QPoint &))); + FilterBuilder *filterBuilder = new FilterBuilder; + filterBuilder->setMaximumWidth(250); + connect(filterBuilder, SIGNAL(add(const CardFilter *)), filterModel, SLOT(addFilter(const CardFilter *))); + + QVBoxLayout *filter = new QVBoxLayout; + filter->addWidget(filterBuilder, 0, Qt::AlignTop); + filter->addWidget(filterView, 10); QVBoxLayout *middleFrame = new QVBoxLayout; - middleFrame->addWidget(cardInfo, 10); - middleFrame->addLayout(verticalToolBarLayout); + middleFrame->addWidget(cardInfo, 0, Qt::AlignTop); + middleFrame->addLayout(filter, 10); deckModel = new DeckListModel(this); connect(deckModel, SIGNAL(deckHashChanged()), this, SLOT(updateHash())); @@ -111,7 +133,14 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) deckView->setModel(deckModel); deckView->setUniformRowHeights(true); deckView->header()->setResizeMode(QHeaderView::ResizeToContents); + deckView->installEventFilter(&deckViewKeySignals); connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateCardInfoRight(const QModelIndex &, const QModelIndex &))); + connect(&deckViewKeySignals, SIGNAL(onEnter()), this, SLOT(actIncrement())); + connect(&deckViewKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actIncrement())); + connect(&deckViewKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrement())); + connect(&deckViewKeySignals, SIGNAL(onRight()), this, SLOT(actIncrement())); + connect(&deckViewKeySignals, SIGNAL(onLeft()), this, SLOT(actDecrement())); + connect(&deckViewKeySignals, SIGNAL(onDelete()), this, SLOT(actRemoveCard())); nameLabel = new QLabel(); nameEdit = new QLineEdit; @@ -131,36 +160,36 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) grid->addWidget(commentsLabel, 1, 0); grid->addWidget(commentsEdit, 1, 1); - + grid->addWidget(hashLabel1, 2, 0); grid->addWidget(hashLabel, 2, 1); - // Update price - aUpdatePrices = new QAction(QString(), this); - aUpdatePrices->setIcon(QIcon(":/resources/icon_update.png")); - connect(aUpdatePrices, SIGNAL(triggered()), this, SLOT(actUpdatePrices())); + // Update price + aUpdatePrices = new QAction(QString(), this); + aUpdatePrices->setIcon(QIcon(":/resources/icon_update.png")); + connect(aUpdatePrices, SIGNAL(triggered()), this, SLOT(actUpdatePrices())); if (!settingsCache->getPriceTagFeature()) aUpdatePrices->setVisible(false); - - QToolBar *deckToolBar = new QToolBar; - deckToolBar->setOrientation(Qt::Vertical); - deckToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - deckToolBar->setIconSize(QSize(24, 24)); - deckToolBar->addAction(aUpdatePrices); - QHBoxLayout *deckToolbarLayout = new QHBoxLayout; - deckToolbarLayout->addStretch(); - deckToolbarLayout->addWidget(deckToolBar); - deckToolbarLayout->addStretch(); + + QToolBar *deckToolBar = new QToolBar; + deckToolBar->setOrientation(Qt::Vertical); + deckToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + deckToolBar->setIconSize(QSize(24, 24)); + deckToolBar->addAction(aUpdatePrices); + QHBoxLayout *deckToolbarLayout = new QHBoxLayout; + deckToolbarLayout->addStretch(); + deckToolbarLayout->addWidget(deckToolBar); + deckToolbarLayout->addStretch(); QVBoxLayout *rightFrame = new QVBoxLayout; rightFrame->addLayout(grid); rightFrame->addWidget(deckView, 10); - rightFrame->addLayout(deckToolbarLayout); + rightFrame->addLayout(deckToolbarLayout); QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addLayout(leftFrame, 10); mainLayout->addLayout(middleFrame); - mainLayout->addLayout(rightFrame, 10); + mainLayout->addLayout(rightFrame); setLayout(mainLayout); aNewDeck = new QAction(QString(), this); @@ -214,34 +243,32 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) dbMenu->addAction(aEditSets); dbMenu->addAction(aEditTokens); dbMenu->addSeparator(); - dbMenu->addAction(aSearch); dbMenu->addAction(aClearSearch); + dbMenu->addAction(aCardTextOnly); addTabMenu(dbMenu); aAddCard = new QAction(QString(), this); aAddCard->setIcon(QIcon(":/resources/arrow_right_green.svg")); connect(aAddCard, SIGNAL(triggered()), this, SLOT(actAddCard())); aAddCardToSideboard = new QAction(QString(), this); - aAddCardToSideboard->setIcon(QIcon(":/resources/add_to_sideboard.svg")); + aAddCardToSideboard->setIcon(QIcon(":/resources/add_to_sideboard.svg")); connect(aAddCardToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard())); aRemoveCard = new QAction(QString(), this); - aRemoveCard->setIcon(QIcon(":/resources/remove_row.svg")); + aRemoveCard->setIcon(QIcon(":/resources/remove_row.svg")); connect(aRemoveCard, SIGNAL(triggered()), this, SLOT(actRemoveCard())); aIncrement = new QAction(QString(), this); - aIncrement->setIcon(QIcon(":/resources/increment.svg")); + aIncrement->setIcon(QIcon(":/resources/increment.svg")); connect(aIncrement, SIGNAL(triggered()), this, SLOT(actIncrement())); aDecrement = new QAction(QString(), this); - aDecrement->setIcon(QIcon(":/resources/decrement.svg")); + aDecrement->setIcon(QIcon(":/resources/decrement.svg")); connect(aDecrement, SIGNAL(triggered()), this, SLOT(actDecrement())); - verticalToolBar->addAction(aAddCard); - verticalToolBar->addAction(aAddCardToSideboard); - verticalToolBar->addAction(aRemoveCard); - verticalToolBar->addAction(aIncrement); - verticalToolBar->addAction(aDecrement); - verticalToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - dlgCardSearch = new DlgCardSearch(this); + deckEditToolBar->addAction(aAddCard); + deckEditToolBar->addAction(aAddCardToSideboard); + deckEditToolBar->addAction(aRemoveCard); + deckEditToolBar->addAction(aIncrement); + deckEditToolBar->addAction(aDecrement); + deckEditToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); retranslateUi(); @@ -255,7 +282,7 @@ TabDeckEditor::~TabDeckEditor() void TabDeckEditor::retranslateUi() { - aSearch->setText(tr("&Search...")); + aCardTextOnly->setText(tr("Show card text only")); aClearSearch->setText(tr("&Clear search")); searchLabel->setText(tr("&Search for:")); @@ -264,11 +291,11 @@ void TabDeckEditor::retranslateUi() hashLabel1->setText(tr("Hash:")); aUpdatePrices->setText(tr("&Update prices")); - aUpdatePrices->setShortcut(tr("Ctrl+U")); - - aNewDeck->setText(tr("&New deck")); - aLoadDeck->setText(tr("&Load deck...")); - aSaveDeck->setText(tr("&Save deck")); + aUpdatePrices->setShortcut(tr("Ctrl+U")); + + aNewDeck->setText(tr("&New deck")); + aLoadDeck->setText(tr("&Load deck...")); + aSaveDeck->setText(tr("&Save deck")); aSaveDeckAs->setText(tr("Save deck &as...")); aLoadDeckFromClipboard->setText(tr("Load deck from cl&ipboard...")); aSaveDeckToClipboard->setText(tr("Save deck to clip&board")); @@ -278,9 +305,8 @@ void TabDeckEditor::retranslateUi() aClose->setShortcut(tr("Ctrl+Q")); aAddCard->setText(tr("Add card to &maindeck")); - aAddCard->setShortcuts(QList() << QKeySequence(tr("Return")) << QKeySequence(tr("Enter"))); aAddCardToSideboard->setText(tr("Add card to &sideboard")); - aAddCardToSideboard->setShortcuts(QList() << QKeySequence(tr("Ctrl+Return")) << QKeySequence(tr("Ctrl+Enter"))); + aRemoveCard->setText(tr("&Remove row")); aRemoveCard->setShortcut(tr("Del")); aIncrement->setText(tr("&Increment number")); @@ -497,17 +523,6 @@ void TabDeckEditor::actEditTokens() db->saveToFile(settingsCache->getTokenDatabasePath(), true); } -void TabDeckEditor::actSearch() -{ - if (dlgCardSearch->exec()) { - searchEdit->clear(); - databaseDisplayModel->setCardName(dlgCardSearch->getCardName()); - databaseDisplayModel->setCardText(dlgCardSearch->getCardText()); - databaseDisplayModel->setCardTypes(dlgCardSearch->getCardTypes()); - databaseDisplayModel->setCardColors(dlgCardSearch->getCardColors()); - } -} - void TabDeckEditor::actClearSearch() { databaseDisplayModel->clearSearch(); @@ -520,21 +535,29 @@ void TabDeckEditor::recursiveExpand(const QModelIndex &index) deckView->expand(index); } -void TabDeckEditor::addCardHelper(QString zoneName) +CardInfo *TabDeckEditor::currentCardInfo() const { const QModelIndex currentIndex = databaseView->selectionModel()->currentIndex(); if (!currentIndex.isValid()) - return; + return NULL; const QString cardName = currentIndex.sibling(currentIndex.row(), 0).data().toString(); - CardInfo *info = db->getCard(cardName); + return db->getCard(cardName); +} + +void TabDeckEditor::addCardHelper(QString zoneName) +{ + const CardInfo *info; + + info = currentCardInfo(); + if(!info) + return; if (info->getIsToken()) zoneName = "tokens"; - - QModelIndex newCardIndex = deckModel->addCard(cardName, zoneName); + + QModelIndex newCardIndex = deckModel->addCard(info->getName(), zoneName); recursiveExpand(newCardIndex); deckView->setCurrentIndex(newCardIndex); - setModified(true); } @@ -557,31 +580,57 @@ void TabDeckEditor::actRemoveCard() setModified(true); } +void TabDeckEditor::offsetCountAtIndex(const QModelIndex &idx, int offset) +{ + if (!idx.isValid() || offset == 0) + return; + + const QModelIndex numberIndex = idx.sibling(idx.row(), 0); + const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); + const int new_count = count + offset; + deckView->setCurrentIndex(numberIndex); + if (new_count <= 0) + deckModel->removeRow(idx.row(), idx.parent()); + else + deckModel->setData(numberIndex, new_count, Qt::EditRole); + setModified(true); +} + +void TabDeckEditor::decrementCardHelper(QString zoneName) +{ + const CardInfo *info; + QModelIndex idx; + + info = currentCardInfo(); + if(!info) + return; + if (info->getIsToken()) + zoneName = "tokens"; + + idx = deckModel->findCard(info->getName(), zoneName); + offsetCountAtIndex(idx, -1); +} + +void TabDeckEditor::actDecrementCard() +{ + decrementCardHelper("main"); +} + +void TabDeckEditor::actDecrementCardFromSideboard() +{ + decrementCardHelper("side"); +} + void TabDeckEditor::actIncrement() { const QModelIndex ¤tIndex = deckView->selectionModel()->currentIndex(); - if (!currentIndex.isValid()) - return; - const QModelIndex numberIndex = currentIndex.sibling(currentIndex.row(), 0); - const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); - deckView->setCurrentIndex(numberIndex); - deckModel->setData(numberIndex, count + 1, Qt::EditRole); - setModified(true); + offsetCountAtIndex(currentIndex, 1); } void TabDeckEditor::actDecrement() { const QModelIndex ¤tIndex = deckView->selectionModel()->currentIndex(); - if (!currentIndex.isValid()) - return; - const QModelIndex numberIndex = currentIndex.sibling(currentIndex.row(), 0); - const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); - deckView->setCurrentIndex(numberIndex); - if (count == 1) - deckModel->removeRow(currentIndex.row(), currentIndex.parent()); - else - deckModel->setData(numberIndex, count - 1, Qt::EditRole); - setModified(true); + offsetCountAtIndex(currentIndex, -1); } void TabDeckEditor::actUpdatePrices() @@ -620,3 +669,31 @@ void TabDeckEditor::setModified(bool _modified) modified = _modified; emit tabTextChanged(this, getTabText()); } + +void TabDeckEditor::filterViewCustomContextMenu(const QPoint &point) { + QMenu menu; + QAction *action; + QModelIndex idx; + + idx = filterView->indexAt(point); + if (!idx.isValid()) + return; + + action = menu.addAction(QString("delete")); + action->setData(point); + connect(&menu, SIGNAL(triggered(QAction *)), + this, SLOT(filterRemove(QAction *))); + menu.exec(filterView->mapToGlobal(point)); +} + +void TabDeckEditor::filterRemove(QAction *action) { + QPoint point; + QModelIndex idx; + + point = action->data().toPoint(); + idx = filterView->indexAt(point); + if (!idx.isValid()) + return; + + filterModel->removeRow(idx.row(), idx.parent()); +} diff --git a/cockatrice/src/tab_deck_editor.h b/cockatrice/src/tab_deck_editor.h index e482d929..3acf1d15 100644 --- a/cockatrice/src/tab_deck_editor.h +++ b/cockatrice/src/tab_deck_editor.h @@ -4,18 +4,20 @@ #include "tab.h" #include #include +#include "keysignals.h" class CardDatabaseModel; class CardDatabaseDisplayModel; class DeckListModel; class QTreeView; class QTableView; -class CardInfoWidget; +class CardFrame; class QTextEdit; -class DlgCardSearch; class QLabel; class DeckLoader; class Response; +class FilterTreeModel; +class CardInfo; class SearchLineEdit : public QLineEdit { private: @@ -49,7 +51,6 @@ private slots: void actEditSets(); void actEditTokens(); - void actSearch(); void actClearSearch(); void actAddCard(); @@ -57,12 +58,20 @@ private slots: void actRemoveCard(); void actIncrement(); void actDecrement(); - void actUpdatePrices(); + void actDecrementCard(); + void actDecrementCardFromSideboard(); - void finishedUpdatingPrices(); + void actUpdatePrices(); + + void finishedUpdatingPrices(); void saveDeckRemoteFinished(const Response &r); + void filterViewCustomContextMenu(const QPoint &point); + void filterRemove(QAction *action); private: + CardInfo *currentCardInfo() const; void addCardHelper(QString zoneName); + void offsetCountAtIndex(const QModelIndex &idx, int offset); + void decrementCardHelper(QString zoneName); void recursiveExpand(const QModelIndex &index); bool confirmClose(); @@ -70,22 +79,27 @@ private: CardDatabaseDisplayModel *databaseDisplayModel; DeckListModel *deckModel; QTreeView *databaseView; + QTreeView *deckView; - CardInfoWidget *cardInfo; + KeySignals deckViewKeySignals; + CardFrame *cardInfo; QLabel *searchLabel; SearchLineEdit *searchEdit; + KeySignals searchKeySignals; + QLabel *nameLabel; QLineEdit *nameEdit; QLabel *commentsLabel; QTextEdit *commentsEdit; QLabel *hashLabel1; QLabel *hashLabel; - DlgCardSearch *dlgCardSearch; + FilterTreeModel *filterModel; + QTreeView *filterView; QMenu *deckMenu, *dbMenu; QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose; - QAction *aEditSets, *aEditTokens, *aSearch, *aClearSearch; - QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement, *aUpdatePrices; + QAction *aEditSets, *aEditTokens, *aClearSearch, *aCardTextOnly; + QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement, *aUpdatePrices; bool modified; public: