#include #include #include #include #include #include #include #include #include #include "main.h" #include "decklistmodel.h" #include "carddatabase.h" #include "settingscache.h" #include "deck_loader.h" DeckListModel::DeckListModel(QObject *parent) : QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder) { deckList = new DeckLoader; connect(deckList, SIGNAL(deckLoaded()), this, SLOT(rebuildTree())); connect(deckList, SIGNAL(deckHashChanged()), this, SIGNAL(deckHashChanged())); root = new InnerDecklistNode; } DeckListModel::~DeckListModel() { delete root; delete deckList; } void DeckListModel::rebuildTree() { beginResetModel(); root->clearTree(); InnerDecklistNode *listRoot = deckList->getRoot(); for (int i = 0; i < listRoot->size(); i++) { InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); InnerDecklistNode *node = new InnerDecklistNode(currentZone->getName(), root); for (int j = 0; j < currentZone->size(); j++) { DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); // XXX better sanity checking if (!currentCard) continue; CardInfo *info = db->getCard(currentCard->getName()); QString cardType; if (!info) cardType = "unknown"; else cardType = info->getMainCardType(); InnerDecklistNode *cardTypeNode = dynamic_cast(node->findChild(cardType)); if (!cardTypeNode) cardTypeNode = new InnerDecklistNode(cardType, node); new DecklistModelCardNode(currentCard, cardTypeNode); } } endResetModel(); } int DeckListModel::rowCount(const QModelIndex &parent) const { // debugIndexInfo("rowCount", parent); InnerDecklistNode *node = getNode(parent); if (node) return node->size(); else return 0; } int DeckListModel::columnCount(const QModelIndex &/*parent*/) const { if (settingsCache->getPriceTagFeature()) return 3; else return 2; } QVariant DeckListModel::data(const QModelIndex &index, int role) const { // debugIndexInfo("data", index); if (!index.isValid()) return QVariant(); if (index.column() >= columnCount()) return QVariant(); AbstractDecklistNode *temp = static_cast(index.internalPointer()); DecklistModelCardNode *card = dynamic_cast(temp); if (!card) { InnerDecklistNode *node = dynamic_cast(temp); switch (role) { case Qt::FontRole: { QFont f; f.setBold(true); return f; } case Qt::DisplayRole: case Qt::EditRole: switch (index.column()) { case 0: return node->recursiveCount(true); case 1: return node->getVisibleName(); case 2: return QString().sprintf("$%.2f", node->recursivePrice(true)); default: return QVariant(); } case Qt::BackgroundRole: { int color = 90 + 60 * node->depth(); return QBrush(QColor(color, 255, color)); } case Qt::ForegroundRole: { return QBrush(QColor(0 ,0 ,0)); } default: return QVariant(); } } else { switch (role) { case Qt::DisplayRole: case Qt::EditRole: { switch (index.column()) { case 0: return card->getNumber(); case 1: return card->getName(); case 2: return QString().sprintf("$%.2f", card->getTotalPrice()); default: return QVariant(); } } case Qt::BackgroundRole: { int color = 255 - (index.row() % 2) * 30; return QBrush(QColor(color, color, color)); } case Qt::ForegroundRole: { return QBrush(QColor(0 ,0 ,0)); } default: return QVariant(); } } } QVariant DeckListModel::headerData(int section, Qt::Orientation orientation, int role) const { if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) return QVariant(); if (section >= columnCount()) return QVariant(); switch (section) { case 0: return tr("Number"); case 1: return tr("Card"); case 2: return tr("Price"); default: return QVariant(); } } QModelIndex DeckListModel::index(int row, int column, const QModelIndex &parent) const { // debugIndexInfo("index", parent); if (!hasIndex(row, column, parent)) return QModelIndex(); InnerDecklistNode *parentNode = getNode(parent); if (row >= parentNode->size()) return QModelIndex(); return createIndex(row, column, parentNode->at(row)); } QModelIndex DeckListModel::parent(const QModelIndex &ind) const { if (!ind.isValid()) return QModelIndex(); return nodeToIndex(static_cast(ind.internalPointer())->getParent()); } Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; Qt::ItemFlags result = Qt::ItemIsEnabled; result |= Qt::ItemIsSelectable; return result; } void DeckListModel::emitRecursiveUpdates(const QModelIndex &index) { if (!index.isValid()) return; emit dataChanged(index, index); emitRecursiveUpdates(index.parent()); } bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, int role) { DecklistModelCardNode *node = getNode(index); if (!node || (role != Qt::EditRole)) return false; switch (index.column()) { case 0: node->setNumber(value.toInt()); break; case 1: node->setName(value.toString()); break; case 2: node->setPrice(value.toFloat()); break; default: return false; } emitRecursiveUpdates(index); deckList->updateDeckHash(); return true; } bool DeckListModel::removeRows(int row, int count, const QModelIndex &parent) { InnerDecklistNode *node = getNode(parent); if (!node) return false; if (row + count > node->size()) return false; beginRemoveRows(parent, row, row + count - 1); for (int i = 0; i < count; i++) { AbstractDecklistNode *toDelete = node->takeAt(row); if (DecklistModelCardNode *temp = dynamic_cast(toDelete)) deckList->deleteNode(temp->getDataNode()); delete toDelete; } endRemoveRows(); if (!node->size() && (node != root)) removeRows(parent.row(), 1, parent.parent()); else emitRecursiveUpdates(parent); return true; } InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerDecklistNode *parent) { InnerDecklistNode *newNode = dynamic_cast(parent->findChild(name)); if (!newNode) { beginInsertRows(nodeToIndex(parent), parent->size(), parent->size()); newNode = new InnerDecklistNode(name, parent); endInsertRows(); } 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); CardInfo *info = db->getCard(cardName); QString cardType = info->getMainCardType(); InnerDecklistNode *cardTypeNode = createNodeIfNeeded(cardType, zoneNode); QModelIndex parentIndex = nodeToIndex(cardTypeNode); DecklistModelCardNode *cardNode = dynamic_cast(cardTypeNode->findChild(cardName)); if (!cardNode) { DecklistCardNode *decklistCard = deckList->addCard(cardName, zoneName); beginInsertRows(parentIndex, cardTypeNode->size(), cardTypeNode->size()); cardNode = new DecklistModelCardNode(decklistCard, cardTypeNode); endInsertRows(); } else { cardNode->setNumber(cardNode->getNumber() + 1); deckList->updateDeckHash(); } sort(lastKnownColumn, lastKnownOrder); emitRecursiveUpdates(parentIndex); return nodeToIndex(cardNode); } QModelIndex DeckListModel::nodeToIndex(AbstractDecklistNode *node) const { if (node == root) return QModelIndex(); return createIndex(node->getParent()->indexOf(node), 0, node); } void DeckListModel::sortHelper(InnerDecklistNode *node, Qt::SortOrder order) { // Sort children of node and save the information needed to // update the list of persistent indexes. QVector > sortResult = node->sort(order); QModelIndexList from, to; int columns = columnCount(); for (int i = sortResult.size() - 1; i >= 0; --i) { const int fromRow = sortResult[i].first; const int toRow = sortResult[i].second; AbstractDecklistNode *temp = node->at(toRow); for (int j = 0; j < columns; ++j) { from << createIndex(fromRow, j, temp); to << createIndex(toRow, j, temp); } } changePersistentIndexList(from, to); // Recursion for (int i = node->size() - 1; i >= 0; --i) { InnerDecklistNode *subNode = dynamic_cast(node->at(i)); if (subNode) sortHelper(subNode, order); } } void DeckListModel::sort(int column, Qt::SortOrder order) { lastKnownColumn = column; lastKnownOrder = order; emit layoutAboutToBeChanged(); DeckSortMethod sortMethod; switch(column) { case 0: sortMethod = ByNumber; break; case 1: sortMethod = ByName; break; case 2: sortMethod = ByPrice; break; default: sortMethod = ByName; } root->setSortMethod(sortMethod); sortHelper(root, order); emit layoutChanged(); } void DeckListModel::cleanList() { setDeckList(new DeckLoader); } void DeckListModel::setDeckList(DeckLoader *_deck) { delete deckList; deckList = _deck; connect(deckList, SIGNAL(deckLoaded()), this, SLOT(rebuildTree())); connect(deckList, SIGNAL(deckHashChanged()), this, SIGNAL(deckHashChanged())); rebuildTree(); } void DeckListModel::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node) { const int totalColumns = settingsCache->getPriceTagFeature() ? 3 : 2; if (node->height() == 1) { QTextBlockFormat blockFormat; QTextCharFormat charFormat; charFormat.setFontPointSize(11); charFormat.setFontWeight(QFont::Bold); cursor->insertBlock(blockFormat, charFormat); QString priceStr; if (settingsCache->getPriceTagFeature()) priceStr = QString().sprintf(": $%.2f", node->recursivePrice(true)); cursor->insertText(QString("%1: %2").arg(node->getVisibleName()).arg(node->recursiveCount(true)).append(priceStr)); QTextTableFormat tableFormat; tableFormat.setCellPadding(0); tableFormat.setCellSpacing(0); tableFormat.setBorder(0); QTextTable *table = cursor->insertTable(node->size() + 1, totalColumns, tableFormat); for (int i = 0; i < node->size(); i++) { AbstractDecklistCardNode *card = dynamic_cast(node->at(i)); QTextCharFormat cellCharFormat; cellCharFormat.setFontPointSize(9); QTextTableCell cell = table->cellAt(i, 0); cell.setFormat(cellCharFormat); QTextCursor cellCursor = cell.firstCursorPosition(); cellCursor.insertText(QString("%1 ").arg(card->getNumber())); cell = table->cellAt(i, 1); cell.setFormat(cellCharFormat); cellCursor = cell.firstCursorPosition(); cellCursor.insertText(card->getName()); if (settingsCache->getPriceTagFeature()) { cell = table->cellAt(i, 2); cell.setFormat(cellCharFormat); cellCursor = cell.firstCursorPosition(); cellCursor.insertText(QString().sprintf("$%.2f ", card->getTotalPrice())); } } } else if (node->height() == 2) { QTextBlockFormat blockFormat; QTextCharFormat charFormat; charFormat.setFontPointSize(14); charFormat.setFontWeight(QFont::Bold); cursor->insertBlock(blockFormat, charFormat); QString priceStr; if (settingsCache->getPriceTagFeature()) priceStr = QString().sprintf(": $%.2f", node->recursivePrice(true)); cursor->insertText(QString("%1: %2").arg(node->getVisibleName()).arg(node->recursiveCount(true)).append(priceStr)); QTextTableFormat tableFormat; tableFormat.setCellPadding(10); tableFormat.setCellSpacing(0); tableFormat.setBorder(0); QVector constraints; for (int i = 0; i < totalColumns; i++) constraints << QTextLength(QTextLength::PercentageLength, 100.0 / totalColumns); tableFormat.setColumnWidthConstraints(constraints); QTextTable *table = cursor->insertTable(1, totalColumns, tableFormat); for (int i = 0; i < node->size(); i++) { QTextCursor cellCursor = table->cellAt(0, (i * totalColumns) / node->size()).lastCursorPosition(); printDeckListNode(&cellCursor, dynamic_cast(node->at(i))); } } cursor->movePosition(QTextCursor::End); } void DeckListModel::printDeckList(QPrinter *printer) { QTextDocument doc; QFont font("Serif"); font.setStyleHint(QFont::Serif); doc.setDefaultFont(font); QTextCursor cursor(&doc); QTextBlockFormat headerBlockFormat; QTextCharFormat headerCharFormat; headerCharFormat.setFontPointSize(16); headerCharFormat.setFontWeight(QFont::Bold); cursor.insertBlock(headerBlockFormat, headerCharFormat); cursor.insertText(deckList->getName()); headerCharFormat.setFontPointSize(12); cursor.insertBlock(headerBlockFormat, headerCharFormat); cursor.insertText(deckList->getComments()); cursor.insertBlock(headerBlockFormat, headerCharFormat); for (int i = 0; i < root->size(); i++) { cursor.insertHtml("
"); //cursor.insertHtml("
"); cursor.insertBlock(headerBlockFormat, headerCharFormat); printDeckListNode(&cursor, dynamic_cast(root->at(i))); } doc.print(printer); } void DeckListModel::pricesUpdated() { emit layoutChanged(); }