File indexing completed on 2024-04-14 14:32:02

0001 // Copyright (c) 2002-2003 Rob Kaper <cap@capsi.com>
0002 //
0003 // This library is free software; you can redistribute it and/or
0004 // modify it under the terms of the GNU Lesser General Public
0005 // License version 2.1 as published by the Free Software Foundation.
0006 //
0007 // This library is distributed in the hope that it will be useful,
0008 // but WITHOUT ANY WARRANTY; without even the implied warranty of
0009 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0010 // Lesser General Public License for more details.
0011 //
0012 // You should have received a copy of the GNU Lesser General Public License
0013 // along with this library; see the file COPYING.LIB.  If not, write to
0014 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0015 // Boston, MA 02110-1301, USA.
0016 
0017 #include <QGroupBox>
0018 #include <QLabel>
0019 #include <QSpinBox>
0020 #include <QCloseEvent>
0021 #include <QHBoxLayout>
0022 #include <QVBoxLayout>
0023 #include <QTreeWidget>
0024 #include <QHeaderView>
0025 #include <QMenu>
0026 #include <QPushButton>
0027 #include <QApplication>
0028 #include <QStyle>
0029 #include <QComboBox>
0030 
0031 #include <klocalizedstring.h>
0032 #include <kiconloader.h>
0033 
0034 #include <atlantic_core.h>
0035 #include <player.h>
0036 #include <estate.h>
0037 #include <trade.h>
0038 #include <portfolioestate.h>
0039 #include <card.h>
0040 
0041 #include "trade_widget.h"
0042 
0043 TradeDisplay::TradeDisplay(Trade *trade, AtlanticCore *atlanticCore, QWidget *parent)
0044     : QDialog(parent, Qt::WindowContextHelpButtonHint)
0045     , m_atlanticCore(atlanticCore)
0046     , m_trade(trade)
0047     , m_contextTradeItem(nullptr)
0048 {
0049     setWindowTitle(i18nc("@title:window", "Trade %1", trade->tradeId()));
0050 
0051     QVBoxLayout *listCompBox = new QVBoxLayout(this);
0052 
0053     m_updateComponentBox = new QGroupBox(i18n("Add Component"), this);
0054     listCompBox->addWidget(m_updateComponentBox);
0055 
0056     QHBoxLayout *updateComponentBoxLayout = new QHBoxLayout(m_updateComponentBox);
0057 
0058     m_editTypeCombo = new QComboBox(m_updateComponentBox);
0059     m_editTypeCombo->addItem(i18n("Estate"));
0060     m_editTypeCombo->addItem(i18n("Money"));
0061     m_editTypeCombo->addItem(i18n("Card"));
0062     updateComponentBoxLayout->addWidget(m_editTypeCombo);
0063 
0064     connect(m_editTypeCombo, SIGNAL(activated(int)), this, SLOT(setTypeCombo(int)));
0065 
0066     m_estateCombo = new QComboBox(m_updateComponentBox);
0067     updateComponentBoxLayout->addWidget(m_estateCombo);
0068     foreach (Estate *estate, m_atlanticCore->estates())
0069     {
0070         if (estate->isOwned())
0071         {
0072             m_estateCombo->addItem( PortfolioEstate::drawPixmap(estate), estate->name() );
0073             m_estateMap[m_estateCombo->count() - 1] = estate;
0074             m_estateRevMap[estate] = m_estateCombo->count() - 1;
0075         }
0076     }
0077 
0078     connect(m_estateCombo, SIGNAL(activated(int)), this, SLOT(setEstateCombo(int)));
0079 
0080     m_moneyBox = new QSpinBox(m_updateComponentBox);
0081     m_moneyBox->setRange(0, 10000);
0082     m_moneyBox->setSingleStep(1);
0083     updateComponentBoxLayout->addWidget(m_moneyBox);
0084 
0085     m_cardCombo = new QComboBox(m_updateComponentBox);
0086     updateComponentBoxLayout->addWidget(m_cardCombo);
0087     foreach (Card *card, m_atlanticCore->cards())
0088     {
0089         if (card->isOwned())
0090         {
0091             m_cardCombo->addItem(card->title());
0092             m_cardMap[m_cardCombo->count() - 1] = card;
0093             m_cardRevMap[card] = m_cardCombo->count() - 1;
0094         }
0095     }
0096 
0097     connect(m_cardCombo, SIGNAL(activated(int)), this, SLOT(setCardCombo(int)));
0098 
0099     Player *pSelf = m_atlanticCore->playerSelf();
0100 
0101     m_fromLabel = new QLabel(m_updateComponentBox);
0102     m_fromLabel->setText(i18n("From"));
0103     updateComponentBoxLayout->addWidget(m_fromLabel);
0104     m_playerFromCombo = new QComboBox(m_updateComponentBox);
0105     updateComponentBoxLayout->addWidget(m_playerFromCombo);
0106 
0107     m_toLabel = new QLabel(m_updateComponentBox);
0108     m_toLabel->setText(i18n("To"));
0109     updateComponentBoxLayout->addWidget(m_toLabel);
0110     m_playerTargetCombo = new QComboBox(m_updateComponentBox);
0111     updateComponentBoxLayout->addWidget(m_playerTargetCombo);
0112 
0113     foreach (Player *player, m_atlanticCore->players())
0114     {
0115         if (player->game() == pSelf->game())
0116         {
0117             m_playerFromCombo->addItem(player->name());
0118             m_playerFromMap[m_playerFromCombo->count() - 1] = player;
0119             m_playerFromRevMap[player] = m_playerFromCombo->count() - 1;
0120 
0121             m_playerTargetCombo->addItem(player->name());
0122             m_playerTargetMap[m_playerTargetCombo->count() - 1] = player;
0123             m_playerTargetRevMap[player] = m_playerTargetCombo->count() - 1;
0124 
0125             connect(player, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *)));
0126         }
0127     }
0128 
0129     m_updateButton = new QPushButton(i18n("Update"), m_updateComponentBox);
0130     m_updateButton->setEnabled(false);
0131     updateComponentBoxLayout->addWidget(m_updateButton);
0132 
0133     connect(m_updateButton, SIGNAL(clicked()), this, SLOT(updateComponent()));
0134 
0135     QHBoxLayout *centralLayout = new QHBoxLayout();
0136     listCompBox->addLayout(centralLayout);
0137 
0138     m_participantsList = new QTreeWidget(this);
0139     m_participantsList->setRootIsDecorated(false);
0140     m_participantsList->header()->setSectionResizeMode(QHeaderView::Stretch);
0141     m_participantsList->setHeaderLabels(QStringList() << i18n("Participants"));
0142     QList<QTreeWidgetItem *> items;
0143     foreach (Player *player, m_trade->participants())
0144     {
0145         QTreeWidgetItem *item = new QTreeWidgetItem();
0146         item->setText(0, player->name());
0147         m_playerListMap.insert(player, item);
0148         items << item;
0149     }
0150     m_participantsList->addTopLevelItems(items);
0151     m_participantsList->setMaximumWidth(175); // FIXME grr
0152     centralLayout->addWidget(m_participantsList);
0153 
0154     m_componentList = new QTreeWidget(this);
0155         m_componentList->setObjectName(QStringLiteral("componentList"));
0156     m_componentList->setContextMenuPolicy(Qt::CustomContextMenu);
0157     m_componentList->setRootIsDecorated(false);
0158     m_componentList->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
0159     centralLayout->addWidget(m_componentList, 1);
0160 
0161     QStringList headers;
0162     headers << i18n("Player");
0163     headers << i18n("Gives");
0164     headers << i18n("Player");
0165     headers << i18n("Item");
0166     m_componentList->setHeaderLabels(headers);
0167 
0168     connect(m_componentList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenu(QPoint)));
0169     connect(m_componentList, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(setCombos(QTreeWidgetItem*)));
0170 
0171     QHBoxLayout *actionBox = new QHBoxLayout();
0172     actionBox->setContentsMargins(0, 0, 0, 0);
0173     listCompBox->addItem(actionBox);
0174 
0175     actionBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
0176 
0177     m_rejectButton = new QPushButton(KDE::icon(QStringLiteral("dialog-cancel")), i18n("Reject"), this);
0178     actionBox->addWidget(m_rejectButton);
0179 
0180     connect(m_rejectButton, SIGNAL(clicked()), this, SLOT(reject()));
0181 
0182     m_acceptButton = new QPushButton(KDE::icon(QStringLiteral("dialog-ok")), i18n("Accept"), this);
0183 //  m_acceptButton->setEnabled(false);
0184     actionBox->addWidget(m_acceptButton);
0185 
0186     connect(m_acceptButton, SIGNAL(clicked()), this, SLOT(accept()));
0187 
0188     m_status = new QLabel(this);
0189     listCompBox->addWidget(m_status);
0190     m_status->setText( i18n( "%1 out of %2 players accept current trade proposal.", m_trade->count( true ), m_trade->count( false ) ) );
0191 
0192     connect(m_trade, SIGNAL(itemAdded(TradeItem *)), this, SLOT(tradeItemAdded(TradeItem *)));
0193     connect(m_trade, SIGNAL(itemRemoved(TradeItem *)), this, SLOT(tradeItemRemoved(TradeItem *)));
0194     connect(m_trade, SIGNAL(changed(Trade *)), this, SLOT(tradeChanged()));
0195     connect(m_trade, SIGNAL(rejected(Player *)), this, SLOT(tradeRejected(Player *)));
0196     connect(m_trade, SIGNAL(playerAdded(Player *)), this, SLOT(slotPlayerAdded(Player *)));
0197     connect(m_trade, SIGNAL(playerRemoved(Player *)), this, SLOT(slotPlayerRemoved(Player *)));
0198     connect(m_trade, SIGNAL(acceptChanged(Player *, bool)), this, SLOT(slotAcceptChanged(Player *, bool)));
0199     connect(this, SIGNAL(updateEstate(Trade *, Estate *, Player *)), m_trade, SIGNAL(updateEstate(Trade *, Estate *, Player *)));
0200     connect(this, SIGNAL(updateMoney(Trade *, unsigned int, Player *, Player *)), m_trade, SIGNAL(updateMoney(Trade *, unsigned int, Player *, Player *)));
0201     connect(this, SIGNAL(updateCard(Trade *, Card *, Player *)), m_trade, SIGNAL(updateCard(Trade *, Card *, Player *)));
0202     connect(this, SIGNAL(reject(Trade *)), m_trade, SIGNAL(reject(Trade *)));
0203     connect(this, SIGNAL(accept(Trade *)), m_trade, SIGNAL(accept(Trade *)));
0204 
0205     setTypeCombo(m_editTypeCombo->currentIndex());
0206     setEstateCombo(m_estateCombo->currentIndex());
0207 }
0208 
0209 void TradeDisplay::closeEvent(QCloseEvent *e)
0210 {
0211     // Don't send network event when trade is already rejected
0212     if (m_trade->isRejected())
0213         m_atlanticCore->removeTrade(m_trade);
0214     else
0215         Q_EMIT reject(m_trade);
0216 
0217     e->accept();
0218 }
0219 
0220 void TradeDisplay::tradeItemAdded(TradeItem *tradeItem)
0221 {
0222     QTreeWidgetItem *item = new QTreeWidgetItem();
0223     item->setText(0, tradeItem->from() ? tradeItem->from()->name() : QStringLiteral("?"));
0224     item->setText(1, i18nc("gives is transitive ;)", "gives"));
0225     item->setText(2, tradeItem->to() ? tradeItem->to()->name() : QStringLiteral("?"));
0226     item->setText(3, tradeItem->text());
0227     connect(tradeItem, SIGNAL(changed(TradeItem *)), this, SLOT(tradeItemChanged(TradeItem *)));
0228 
0229     const QIcon userIcon = KDE::icon(QStringLiteral("user-identity"));
0230     item->setIcon(0, userIcon);
0231     item->setIcon(2, userIcon);
0232 
0233     if (TradeEstate *tradeEstate = dynamic_cast<TradeEstate*>(tradeItem))
0234         item->setIcon(3, PortfolioEstate::drawPixmap(tradeEstate->estate()));
0235 //  else if (TradeMoney *tradeMoney = dynamic_cast<TradeMoney*>(tradeItem))
0236 //      item->setIcon(3, PortfolioEstate::pixMap(tradeEstate->estate()));
0237 
0238     m_componentList->addTopLevelItem(item);
0239 
0240     m_componentMap[tradeItem] = item;
0241     m_componentRevMap[item] = tradeItem;
0242 }
0243 
0244 void TradeDisplay::tradeItemRemoved(TradeItem *t)
0245 {
0246     QTreeWidgetItem *item = m_componentMap.take(t);
0247     m_componentRevMap.remove(item);
0248     delete item;
0249 }
0250 
0251 void TradeDisplay::tradeItemChanged(TradeItem *t)
0252 {
0253     QTreeWidgetItem *item = m_componentMap.value(t, nullptr);
0254     if (item)
0255     {
0256         const QIcon userIcon = KDE::icon(QStringLiteral("user-identity"));
0257         item->setText(0, t->from() ? t->from()->name() : QStringLiteral("?"));
0258         item->setIcon(0, userIcon);
0259         item->setText(2, t->to() ? t->to()->name() : QStringLiteral("?"));
0260         item->setIcon(2, userIcon);
0261         item->setText(3, t->text());
0262     }
0263 }
0264 
0265 void TradeDisplay::tradeChanged()
0266 {
0267     // TODO: add notification whether playerSelf has accepted or not and
0268     // enable/disable accept button based on that
0269     m_status->setText( i18n( "%1 out of %2 players accept current trade proposal.", m_trade->count( true ), m_trade->count( false ) ) );
0270 }
0271 
0272 void TradeDisplay::playerChanged(Player *player)
0273 {
0274     m_playerFromCombo->setItemText(m_playerFromRevMap[player], player->name());
0275     m_playerTargetCombo->setItemText(m_playerTargetRevMap[player], player->name());
0276 
0277     foreach (TradeItem *item, m_componentRevMap)
0278         tradeItemChanged(item);
0279 }
0280 
0281 void TradeDisplay::tradeRejected(Player *player)
0282 {
0283     if (player)
0284     {
0285         m_status->setText(i18n("Trade proposal was rejected by %1.", player->name()));
0286         QTreeWidgetItem *item = m_playerListMap.value(player, nullptr);
0287         if (item)
0288             item->setIcon(0, KDE::icon(QStringLiteral("dialog-cancel")));
0289     } else
0290         m_status->setText(i18n("Trade proposal was rejected."));
0291 
0292     // Disable GUI elements
0293     m_updateButton->setEnabled(false);
0294     m_componentList->setEnabled(false);
0295     m_rejectButton->setEnabled(false);
0296     m_acceptButton->setEnabled(false);
0297 
0298     // TODO: add/enable close button
0299 }
0300 
0301 void TradeDisplay::slotPlayerAdded(Player *player)
0302 {
0303     QTreeWidgetItem *item = m_playerListMap.value(player, nullptr);
0304     if (item)
0305         return;
0306 
0307     item = new QTreeWidgetItem();
0308     item->setText(0, player->name());
0309     m_playerListMap.insert(player, item);
0310     m_participantsList->addTopLevelItem(item);
0311     tradeChanged();
0312 }
0313 
0314 void TradeDisplay::slotPlayerRemoved(Player *player)
0315 {
0316     QTreeWidgetItem *item = m_playerListMap.take(player);
0317     if (!item)
0318         return;
0319 
0320     delete item;
0321     tradeChanged();
0322 }
0323 
0324 void TradeDisplay::slotAcceptChanged(Player *player, bool accept)
0325 {
0326     slotPlayerAdded(player);
0327     QTreeWidgetItem *item = m_playerListMap.value(player, nullptr);
0328     Q_ASSERT(item);  // slotPlayerAdded made sure there's an item for player
0329     item->setIcon(0, accept ? KDE::icon(QStringLiteral("dialog-ok")) : QIcon());
0330 }
0331 
0332 void TradeDisplay::setTypeCombo(int index)
0333 {
0334     switch (index)
0335     {
0336     case 0:
0337         // Editing estate component
0338 
0339         m_estateCombo->show();
0340         m_estateCombo->setMaximumWidth(9999);
0341 
0342         m_moneyBox->hide();
0343         m_moneyBox->setMaximumWidth(0);
0344 
0345         m_cardCombo->hide();
0346         m_cardCombo->setMaximumWidth(0);
0347 
0348         setEstateCombo(m_estateCombo->currentIndex()); // also updates playerfromCombo
0349         m_playerFromCombo->setEnabled(false);
0350 
0351         m_updateButton->setEnabled( m_estateCombo->count() > 0 );
0352 
0353         break;
0354 
0355     case 1:
0356         // Editing money component
0357 
0358         m_estateCombo->hide();
0359         m_estateCombo->setMaximumWidth(0);
0360 
0361         m_moneyBox->show();
0362         m_moneyBox->setMaximumWidth(9999);
0363 
0364         m_cardCombo->hide();
0365         m_cardCombo->setMaximumWidth(0);
0366 
0367         m_playerFromCombo->setEnabled(true);
0368 
0369         m_updateButton->setEnabled(true);
0370 
0371         break;
0372 
0373     case 2:
0374         // Editing card component
0375 
0376         m_estateCombo->hide();
0377         m_estateCombo->setMaximumWidth(0);
0378 
0379         m_moneyBox->hide();
0380         m_moneyBox->setMaximumWidth(0);
0381 
0382         m_cardCombo->show();
0383         m_cardCombo->setMaximumWidth(9999);
0384 
0385         setCardCombo(m_cardCombo->currentIndex()); // also updates playerFromCombo
0386         m_playerFromCombo->setEnabled(false);
0387 
0388         m_updateButton->setEnabled(m_cardCombo->count() > 0);
0389 
0390         break;
0391     }
0392 }
0393 
0394 void TradeDisplay::setEstateCombo(int index)
0395 {
0396     if (m_estateCombo->currentIndex() != index)
0397         m_estateCombo->setCurrentIndex(index);
0398 
0399     if (Estate *estate = m_estateMap.value(index, nullptr))
0400         m_playerFromCombo->setCurrentIndex( m_playerFromRevMap[estate->owner()] );
0401 }
0402 
0403 void TradeDisplay::setCardCombo(int index)
0404 {
0405     if (m_cardCombo->currentIndex() != index)
0406         m_cardCombo->setCurrentIndex(index);
0407 
0408     if (Card *card = m_cardMap.value(index, nullptr))
0409         m_playerFromCombo->setCurrentIndex(m_playerFromRevMap[card->owner()]);
0410 }
0411 
0412 void TradeDisplay::setCombos(QTreeWidgetItem *i)
0413 {
0414     TradeItem *item = m_componentRevMap[i];
0415     if (TradeEstate *tradeEstate = dynamic_cast<TradeEstate*>(item))
0416     {
0417         setTypeCombo(0);
0418         setEstateCombo( m_estateRevMap[tradeEstate->estate()] ); // also updates playerFromCombo
0419         m_playerTargetCombo->setCurrentIndex( m_playerTargetRevMap[tradeEstate->to()] );
0420     }
0421     else if (TradeMoney *tradeMoney = dynamic_cast<TradeMoney*>(item))
0422     {
0423         setTypeCombo(1);
0424         m_moneyBox->setValue( tradeMoney->money() );
0425         m_playerFromCombo->setCurrentIndex(  m_playerFromRevMap[tradeMoney->from()] );
0426         m_playerTargetCombo->setCurrentIndex(  m_playerTargetRevMap[tradeMoney->to()] );
0427     }
0428     else if (TradeCard *tradeCard = dynamic_cast<TradeCard*>(item))
0429     {
0430         setTypeCombo(2);
0431         setCardCombo(m_cardRevMap[tradeCard->card()]); // also updates playerFromCombo
0432         m_playerTargetCombo->setCurrentIndex(m_playerTargetRevMap[tradeCard->to()]);
0433     }
0434 }
0435 
0436 void TradeDisplay::updateComponent()
0437 {
0438     Estate *estate;
0439     Player *pFrom, *pTarget;
0440     Card *card;
0441 
0442     switch (m_editTypeCombo->currentIndex())
0443     {
0444     case 0:
0445         // Updating estate component
0446         estate = m_estateMap[m_estateCombo->currentIndex()];
0447         pTarget = m_playerTargetMap[m_playerTargetCombo->currentIndex()];
0448 
0449         if (estate && pTarget)
0450             Q_EMIT updateEstate(m_trade, estate, pTarget);
0451 
0452         break;
0453 
0454     case 1:
0455         // Updating money component
0456         pFrom = m_playerFromMap[m_playerFromCombo->currentIndex()];
0457         pTarget = m_playerTargetMap[m_playerTargetCombo->currentIndex()];
0458 
0459         if (pFrom && pTarget)
0460             Q_EMIT updateMoney(m_trade, m_moneyBox->value(), pFrom, pTarget);
0461 
0462         break;
0463 
0464     case 2:
0465         // Updating card component
0466         card = m_cardMap[m_cardCombo->currentIndex()];
0467         pTarget = m_playerTargetMap[m_playerTargetCombo->currentIndex()];
0468 
0469         if (card && pTarget)
0470             Q_EMIT updateCard(m_trade, card, pTarget);
0471 
0472         break;
0473     }
0474 }
0475 
0476 void TradeDisplay::reject()
0477 {
0478     Q_EMIT reject(m_trade);
0479 }
0480 
0481 void TradeDisplay::accept()
0482 {
0483     Q_EMIT accept(m_trade);
0484 }
0485 
0486 void TradeDisplay::contextMenu(const QPoint &pos)
0487 {
0488     QTreeWidgetItem* item = m_componentList->itemAt(pos);
0489     if (!item)
0490         return;
0491 
0492     m_contextTradeItem = m_componentRevMap[item];
0493 
0494     QMenu *rmbMenu = new QMenu(this);
0495     QAction *act = rmbMenu->addAction(i18n("Remove From Trade"));
0496     connect(act, SIGNAL(triggered()), this, SLOT(contextMenuClickedRemove()));
0497 
0498     rmbMenu->exec(m_componentList->viewport()->mapToGlobal(pos));
0499 }
0500 
0501 void TradeDisplay::contextMenuClickedRemove()
0502 {
0503     if (!m_contextTradeItem)
0504         return;
0505 
0506     if (TradeEstate *tradeEstate = dynamic_cast<TradeEstate*>(m_contextTradeItem))
0507         Q_EMIT updateEstate(m_trade, tradeEstate->estate(), nullptr);
0508     else if (TradeMoney *tradeMoney = dynamic_cast<TradeMoney*>(m_contextTradeItem))
0509         Q_EMIT updateMoney(m_trade, 0, tradeMoney->from(), tradeMoney->to());
0510     else if (TradeCard *tradeCard = dynamic_cast<TradeCard*>(m_contextTradeItem))
0511         Q_EMIT updateCard(m_trade, tradeCard->card(), nullptr);
0512 
0513     m_contextTradeItem = nullptr;
0514 }
0515 
0516 #include "moc_trade_widget.cpp"