File indexing completed on 2023-09-24 08:13:17

0001 // Copyright (c) 2002-2004 Rob Kaper <cap@capsi.com>
0002 //
0003 // This program is free software; you can redistribute it and/or
0004 // modify it under the terms of the GNU General Public License
0005 // version 2 as published by the Free Software Foundation.
0006 //
0007 // This program 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 // General Public License for more details.
0011 //
0012 // You should have received a copy of the GNU General Public License
0013 // along with this program; see the file COPYING.  If not, write to
0014 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0015 // Boston, MA 02110-1301, USA.
0016 
0017 #include <QLineEdit>
0018 #include <qmenu.h>
0019 #include <QVBoxLayout>
0020 #include <QGridLayout>
0021 #include <QCloseEvent>
0022 #include <QApplication>
0023 #include <QScrollArea>
0024 #include <QTextDocument>
0025 #include <QCommandLineParser>
0026 #include <QLocale>
0027 #include <QStatusBar>
0028 
0029 #include <kaboutapplicationdialog.h>
0030 #include <kactioncollection.h>
0031 #include <klocalizedstring.h>
0032 #include <kmessagebox.h>
0033 #include <knotifyconfigwidget.h>
0034 #include <knotification.h>
0035 #include <kstandardgameaction.h>
0036 #include <kstandardaction.h>
0037 #include <kiconloader.h>
0038 #include <kaboutdata.h>
0039 
0040 #include <atlantic_core.h>
0041 #include <auction.h>
0042 #include <estate.h>
0043 #include <player.h>
0044 #include <trade.h>
0045 #include "atlantik.h"
0046 
0047 #include <atlantik_network.h>
0048 
0049 #include <board.h>
0050 #include <trade_widget.h>
0051 #include <portfolioview.h>
0052 
0053 #include "eventlogwidget.h"
0054 #include "version.h"
0055 #include "selectserver_widget.h"
0056 #include "selectgame_widget.h"
0057 #include "selectconfiguration_widget.h"
0058 #include "configdlg.h"
0059 #include "clickablelabel.h"
0060 
0061 #include <settings.h>
0062 #include <atlantik_debug.h>
0063 
0064 LogTextEdit::LogTextEdit(QWidget *parent)
0065     : QTextEdit(parent)
0066 {
0067     m_clear = KStandardAction::clear( this, SLOT( clear() ), nullptr );
0068     m_selectAll = KStandardAction::selectAll( this, SLOT( selectAll() ), nullptr );
0069     m_copy = KStandardAction::copy( this, SLOT( copy() ), nullptr );
0070     connect(this, SIGNAL(copyAvailable(bool)), m_copy, SLOT(setEnabled(bool)));
0071     m_copy->setEnabled(false);
0072 }
0073 
0074 LogTextEdit::~LogTextEdit()
0075 {
0076     delete m_clear;
0077     delete m_selectAll;
0078     delete m_copy;
0079 }
0080 
0081 void LogTextEdit::contextMenuEvent(QContextMenuEvent *event)
0082 {
0083     QMenu menu(this);
0084     menu.addAction(m_clear);
0085     menu.addSeparator();
0086     menu.addAction(m_copy);
0087     menu.addAction(m_selectAll);
0088     menu.exec(event->globalPos());
0089 }
0090 
0091 Atlantik::Atlantik(QCommandLineParser *parser)
0092     : KXmlGuiWindow()
0093     , m_cliParser(parser)
0094     , m_atlantikNetwork(nullptr)
0095     , m_board(nullptr)
0096     , m_selectServer(nullptr)
0097     , m_selectGame(nullptr)
0098     , m_selectConfiguration(nullptr)
0099     , m_runningGame(false)
0100     , m_reconnecting(false)
0101     , m_tokenTheme(TokenTheme::defaultTheme())
0102 {
0103     // Read application configuration
0104     readConfig();
0105 
0106     // Toolbar: Game
0107         m_showEventLog = actionCollection()->addAction(QStringLiteral("showeventlog"));
0108         m_showEventLog->setText(i18n("Show Event &Log..."));
0109         connect(m_showEventLog, SIGNAL(triggered(bool)), this, SLOT(showEventLog()));
0110     m_reconnect = actionCollection()->addAction(QStringLiteral("reconnect"));
0111     m_reconnect->setText(i18n("&Reconnect (After Crash)"));
0112     connect(m_reconnect, SIGNAL(triggered()), this, SLOT(slotReconnect()));
0113     m_reconnect->setEnabled(false);
0114     m_leaveGame = actionCollection()->addAction(QStringLiteral("leave_game"));
0115     m_leaveGame->setText(i18n("&Leave Game"));
0116     connect(m_leaveGame, SIGNAL(triggered()), this, SLOT(slotLeaveGame()));
0117     m_leaveGame->setEnabled(false);
0118     (void) KStandardGameAction::quit(qApp, SLOT(closeAllWindows()), actionCollection());
0119 
0120     // Toolbar: Settings
0121     (void) KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection());
0122     (void) KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection());
0123 
0124     // Game and network core
0125     m_atlanticCore = new AtlanticCore(this);
0126         m_atlanticCore->setObjectName(QStringLiteral("atlanticCore"));
0127     connect(m_atlanticCore, SIGNAL(createGUI(Player *)), this, SLOT(newPlayer(Player *)));
0128     connect(m_atlanticCore, SIGNAL(removeGUI(Player *)), this, SLOT(removeGUI(Player *)));
0129     connect(m_atlanticCore, SIGNAL(createGUI(Trade *)), this, SLOT(newTrade(Trade *)));
0130     connect(m_atlanticCore, SIGNAL(removeGUI(Trade *)), this, SLOT(removeGUI(Trade *)));
0131 
0132     m_eventLog = new EventLog(this);
0133 
0134     initNetworkObject();
0135 
0136     // Menu,toolbar: Move
0137     m_roll = KStandardGameAction::roll(this, SIGNAL(rollDice()), actionCollection());
0138     m_roll->setEnabled(false);
0139 
0140     m_buyEstate = actionCollection()->addAction(QStringLiteral("atlantik_buy_estate"));
0141         m_buyEstate->setText(i18n("&Buy"));
0142     actionCollection()->setDefaultShortcut(m_buyEstate, QKeySequence(Qt::CTRL + Qt::Key_B));
0143     m_buyEstate->setIcon(KDE::icon(QStringLiteral("atlantik_buy_estate")));
0144     connect (m_buyEstate,SIGNAL(triggered()), this, SIGNAL(buyEstate()));
0145     m_buyEstate->setEnabled(false);
0146 
0147     m_auctionEstate = actionCollection()->addAction(QStringLiteral("auction"));
0148         m_auctionEstate->setText(i18n("&Auction"));
0149     actionCollection()->setDefaultShortcut(m_auctionEstate, QKeySequence(Qt::CTRL + Qt::Key_A));
0150     m_auctionEstate->setIcon(KDE::icon(QStringLiteral("auction")));
0151     connect(m_auctionEstate,SIGNAL(triggered()),this, SIGNAL(auctionEstate()));
0152     m_auctionEstate->setEnabled(false);
0153 
0154 
0155     m_endTurn = KStandardGameAction::endTurn(this, SIGNAL(endTurn()), actionCollection());
0156     m_endTurn->setEnabled(false);
0157 
0158     m_jailCard = actionCollection()->addAction(QStringLiteral("move_jailcard"));
0159         m_jailCard->setText(i18n("Use Card to Leave Jail"));
0160     connect(m_jailCard, SIGNAL(triggered()),this, SIGNAL(jailCard()));
0161     m_jailCard->setEnabled(false);
0162 
0163     m_jailPay = actionCollection()->addAction(QStringLiteral("jail_pay"));
0164         m_jailPay->setText(i18n("&Pay to Leave Jail"));
0165     actionCollection()->setDefaultShortcut(m_jailPay, QKeySequence(Qt::CTRL + Qt::Key_P));
0166     m_jailPay->setIcon(KDE::icon(QStringLiteral("jail_pay")));
0167     connect(m_jailPay, SIGNAL(triggered()),this, SIGNAL(jailPay()));
0168     m_jailPay->setEnabled(false);
0169 
0170     m_jailRoll = actionCollection()->addAction(QStringLiteral("move_jailroll"));
0171         m_jailRoll->setText(i18n("Roll to Leave &Jail"));
0172     actionCollection()->setDefaultShortcut(m_jailRoll, QKeySequence(Qt::CTRL + Qt::Key_J));
0173     connect(m_jailRoll, SIGNAL(triggered()), this, SIGNAL(jailRoll()));
0174     m_jailRoll->setEnabled(false);
0175 
0176     // Mix code and XML into GUI
0177     setupGUI();
0178     m_sbVersion = new ClickableLabel();
0179     m_sbVersion->setText(QString::fromLatin1("Atlantik " ATLANTIK_VERSION_STRING));
0180     connect(m_sbVersion, SIGNAL(clicked()), this, SLOT(showAboutDialog()));
0181     statusBar()->addWidget(m_sbVersion, 0);
0182     m_sbStatus = new ClickableLabel();
0183     connect(m_sbStatus, SIGNAL(clicked()), this, SLOT(showEventLog()));
0184     statusBar()->addWidget(m_sbStatus, 1);
0185 
0186     // Main widget, containing all others
0187     m_mainWidget = new QWidget(this);
0188         m_mainWidget->setObjectName(QStringLiteral("main"));
0189     m_mainWidget->show();
0190     m_mainLayout = new QGridLayout(m_mainWidget);
0191     setCentralWidget(m_mainWidget);
0192 
0193     // Vertical view area for portfolios.
0194     m_portfolioScroll = new QScrollArea(m_mainWidget);
0195     m_portfolioScroll->setObjectName(QStringLiteral("pfScroll"));
0196     m_mainLayout->addWidget( m_portfolioScroll, 0, 0 );
0197     m_portfolioScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0198     m_portfolioScroll->setWidgetResizable(true);
0199     m_portfolioScroll->setFixedHeight( 200 );
0200     m_portfolioScroll->hide();
0201 
0202     m_portfolioWidget = new QWidget();
0203         m_portfolioWidget->setObjectName(QStringLiteral("pfWidget"));
0204     m_portfolioScroll->setWidget(m_portfolioWidget);
0205     m_portfolioWidget->show();
0206 
0207     m_portfolioLayout = new QVBoxLayout(m_portfolioWidget);
0208     m_portfolioLayout->setContentsMargins(0, 0, 0, 0);
0209 
0210     // Nice label
0211 //  m_portfolioLabel = new QLabel(i18n("Players"), m_portfolioWidget, "pfLabel");
0212 //  m_portfolioLayout->addWidget(m_portfolioLabel);
0213 //  m_portfolioLabel->show();
0214 
0215     // Text view for chat and status messages from server.
0216     m_serverMsgs = new LogTextEdit(m_mainWidget);
0217     m_serverMsgs->setObjectName(QLatin1String("serverMsgs"));
0218     m_serverMsgs->setReadOnly(true);
0219     m_serverMsgs->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0220     m_serverMsgs->setMinimumWidth(200);
0221     m_serverMsgs->setUndoRedoEnabled(false);
0222     m_mainLayout->addWidget(m_serverMsgs, 1, 0);
0223 
0224     // LineEdit to enter commands and chat messages.
0225     m_input = new QLineEdit(m_mainWidget);
0226     m_input->setObjectName(QStringLiteral("input"));
0227     m_mainLayout->addWidget(m_input, 2, 0);
0228 
0229     m_serverMsgs->setFocusProxy(m_input);
0230 
0231     connect(m_input, SIGNAL(returnPressed()), this, SLOT(slotSendMsg()));
0232 
0233     // Set stretching where we want it.
0234     m_mainLayout->setRowStretch(1, 1); // make m_board+m_serverMsgs stretch vertically, not the rest
0235     m_mainLayout->setColumnStretch(1, 1); // make m_board stretch horizontally, not the rest
0236 
0237     // Check command-line args to see if we need to connect or show the metaserver window
0238     QString host = m_cliParser ? m_cliParser->value(QStringLiteral("host")) : QString();
0239     QString port = m_cliParser ? m_cliParser->value(QStringLiteral("port")) : QString();
0240     if (!host.isEmpty() && !port.isEmpty())
0241         m_atlantikNetwork->serverConnect(host, port.toInt());
0242     else
0243         showSelectServer();
0244 
0245     // Connection cookie
0246     m_reconnectCookie.reset(ConnectionCookie::read());
0247     m_reconnect->setEnabled(m_reconnectCookie != nullptr);
0248 }
0249 
0250 void Atlantik::readConfig()
0251 {
0252     // Read configuration settings
0253 
0254     // General configuration
0255     m_config.chatTimestamps = Settings::chatTimeStamps();
0256 
0257     // Personalization configuration
0258     m_config.playerName = Settings::playerName();
0259     m_config.playerImage = Settings::playerImage();
0260 
0261     // Board configuration
0262     m_config.indicateUnowned = Settings::indicateUnowned();
0263     m_config.highlightUnowned = Settings::highlightUnowned();
0264     m_config.darkenMortgaged = Settings::darkenMortgaged();
0265     m_config.animateTokens = Settings::animateToken();
0266     m_config.quartzEffects = Settings::quartzEffects();
0267 
0268     // Meta server configuation
0269     m_config.connectOnStart = Settings::connectOnStart();
0270     m_config.hideDevelopmentServers = Settings::hideDevelopmentServers();
0271 
0272     // Portfolio colors
0273     m_config.activeColor = Settings::activeBackground();
0274     m_config.inactiveColor = Settings::inactiveBlend();
0275 }
0276 
0277 void Atlantik::newPlayer(Player *player)
0278 {
0279     initBoard();
0280     m_board->addToken(player);
0281     addPortfolioView(player);
0282 
0283     // Player::changed() is not connected until later this method, so
0284     // we'd better force an update.
0285     playerChanged(player);
0286 
0287     connect(player, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *)));
0288     connect(player, SIGNAL(gainedTurn()), this, SLOT(gainedTurn()));
0289     connect(player, SIGNAL(changed(Player *)), m_board, SLOT(playerChanged(Player *)));
0290 
0291     if (!player->isSelf())
0292         KNotification::event(QStringLiteral("newplayer"), i18n("New player joined."));
0293 }
0294 
0295 void Atlantik::newEstate(Estate *estate)
0296 {
0297     initBoard();
0298     m_board->addEstateView(estate, m_config.indicateUnowned, m_config.highlightUnowned, m_config.darkenMortgaged, m_config.quartzEffects);
0299 }
0300 
0301 void Atlantik::newTrade(Trade *trade)
0302 {
0303     TradeDisplay *tradeDisplay = new TradeDisplay(trade, m_atlanticCore);
0304         tradeDisplay->setObjectName(QStringLiteral("tradeDisplay"));
0305     m_tradeGUIMap.insert(trade, tradeDisplay);
0306     tradeDisplay->show();
0307 }
0308 
0309 void Atlantik::newAuction(Auction *auction)
0310 {
0311     initBoard();
0312     m_board->addAuctionWidget(auction);
0313 }
0314 
0315 void Atlantik::removeGUI(Player *player)
0316 {
0317     // Find and remove portfolioview
0318     PortfolioView *portfolioView = findPortfolioView(player);
0319     if (portfolioView)
0320     {
0321         m_portfolioViews.removeOne(portfolioView);
0322         delete portfolioView;
0323     }
0324 
0325     if (m_board)
0326         m_board->removeToken(player);
0327 }
0328 
0329 void Atlantik::removeGUI(Trade *trade)
0330 {
0331     delete m_tradeGUIMap.take(trade);
0332 }
0333 
0334 void Atlantik::showSelectServer()
0335 {
0336     if (m_selectServer)
0337         return;
0338 
0339     m_selectServer = new SelectServer(m_config.hideDevelopmentServers, m_mainWidget );
0340         m_selectServer->setObjectName(QStringLiteral("selectServer"));
0341     m_mainLayout->addWidget(m_selectServer, 0, 1, 3, 1);
0342     m_selectServer->show();
0343 
0344     if (m_selectGame)
0345     {
0346         delete m_selectGame;
0347         m_selectGame = nullptr;
0348     }
0349 
0350     m_atlanticCore->reset(true);
0351     initNetworkObject();
0352 
0353     m_reconnect->setEnabled(false);
0354     m_reconnecting = false;
0355     m_leaveGame->setEnabled(false);
0356 
0357     connect(m_selectServer, SIGNAL(serverConnect(QString, int)), m_atlantikNetwork, SLOT(serverConnect(QString, int)));
0358     connect(m_selectServer, SIGNAL(msgStatus(const QString &)), this, SLOT(slotMsgStatus(const QString &)));
0359 
0360     if (m_config.connectOnStart)
0361         m_selectServer->reloadServerList();
0362 }
0363 
0364 void Atlantik::showSelectGame()
0365 {
0366     if (m_selectGame)
0367         return;
0368 
0369     m_selectGame = new SelectGame(m_atlanticCore, m_mainWidget);
0370     m_atlanticCore->emitGames();
0371     m_leaveGame->setEnabled(false);
0372 
0373     m_mainLayout->addWidget(m_selectGame, 0, 1, 3, 1);
0374     m_selectGame->show();
0375 
0376     // Reset core and GUI
0377     if (m_board)
0378     {
0379         m_board->hide();
0380         m_board->reset();
0381 //      delete m_board;
0382 //      m_board = nullptr;
0383 
0384         // m_portfolioViews.clear();
0385         m_atlanticCore->reset();
0386     }
0387 
0388     if (m_selectServer)
0389     {
0390         delete m_selectServer;
0391         m_selectServer = nullptr;
0392     }
0393     if (m_selectConfiguration)
0394     {
0395         delete m_selectConfiguration;
0396         m_selectConfiguration = nullptr;
0397     }
0398 
0399     connect(m_selectGame, SIGNAL(joinGame(int)), m_atlantikNetwork, SLOT(joinGame(int)));
0400     connect(m_selectGame, SIGNAL(watchGame(int)), m_atlantikNetwork, SLOT(watchGame(int)));
0401     connect(m_selectGame, SIGNAL(newGame(const QString &)), m_atlantikNetwork, SLOT(newGame(const QString &)));
0402     connect(m_selectGame, SIGNAL(leaveServer()), this, SLOT(showSelectServer()));
0403     connect(m_selectGame, SIGNAL(msgStatus(const QString &)), this, SLOT(slotMsgStatus(const QString &)));
0404 }
0405 
0406 void Atlantik::showSelectConfiguration()
0407 {
0408     if (m_selectConfiguration)
0409         return;
0410 
0411     if (m_selectGame)
0412     {
0413         delete m_selectGame;
0414         m_selectGame = nullptr;
0415     }
0416 
0417     m_selectConfiguration = new SelectConfiguration(m_atlanticCore, m_mainWidget );
0418         m_selectConfiguration->setObjectName(QStringLiteral("selectConfiguration"));
0419     m_mainLayout->addWidget(m_selectConfiguration, 0, 1, 3, 1);
0420     m_selectConfiguration->show();
0421     m_leaveGame->setEnabled(true);
0422 
0423     connect(m_atlantikNetwork, SIGNAL(gameOption(QString, QString, QString, QString, QString)), m_selectConfiguration, SLOT(gameOption(QString, QString, QString, QString, QString)));
0424     connect(m_atlantikNetwork, SIGNAL(gameInit()), m_selectConfiguration, SLOT(initGame()));
0425     connect(m_selectConfiguration, SIGNAL(startGame()), m_atlantikNetwork, SLOT(startGame()));
0426     connect(m_selectConfiguration, SIGNAL(leaveGame()), m_atlantikNetwork, SLOT(leaveGame()));
0427     connect(m_selectConfiguration, SIGNAL(changeOption(int, const QString &)), m_atlantikNetwork, SLOT(changeOption(int, const QString &)));
0428     connect(m_selectConfiguration, SIGNAL(buttonCommand(QString)), m_atlantikNetwork, SLOT(writeData(QString)));
0429     connect(m_selectConfiguration, SIGNAL(iconSelected(const QString &)), m_atlantikNetwork, SLOT(setImage(const QString &)));
0430     connect(m_selectConfiguration, SIGNAL(statusMessage(const QString &)), this, SLOT(slotMsgStatus(const QString &)));
0431 }
0432 
0433 void Atlantik::initBoard()
0434 {
0435     if (m_board)
0436         return;
0437 
0438     m_board = new AtlantikBoard(m_atlanticCore, 40, AtlantikBoard::Play, m_mainWidget);
0439         m_board->setObjectName(QStringLiteral("board"));
0440     m_board->setViewProperties(m_config.indicateUnowned, m_config.highlightUnowned, m_config.darkenMortgaged, m_config.quartzEffects, m_config.animateTokens);
0441     m_board->setTokenTheme(m_tokenTheme);
0442 
0443     connect(m_atlantikNetwork, SIGNAL(displayDetails(QString, bool, bool, Estate *)), m_board, SLOT(insertDetails(QString, bool, bool, Estate *)));
0444     connect(m_atlantikNetwork, SIGNAL(displayText(QString, bool, bool)), m_board, SLOT(insertText(QString, bool, bool)));
0445     connect(m_atlantikNetwork, SIGNAL(addCommandButton(QString, QString, bool)), m_board, SLOT(displayButton(QString, QString, bool)));
0446     connect(m_atlantikNetwork, SIGNAL(addCloseButton()), m_board, SLOT(addCloseButton()));
0447     connect(m_board, SIGNAL(tokenConfirmation(Estate *)), m_atlantikNetwork, SLOT(tokenConfirmation(Estate *)));
0448     connect(m_board, SIGNAL(buttonCommand(QString)), m_atlantikNetwork, SLOT(writeData(QString)));
0449 }
0450 
0451 void Atlantik::showBoard()
0452 {
0453     if (m_selectGame)
0454     {
0455         delete m_selectGame;
0456         m_selectGame = nullptr;
0457     }
0458 
0459     if (m_selectConfiguration)
0460     {
0461         delete m_selectConfiguration;
0462         m_selectConfiguration = nullptr;
0463     }
0464 
0465     if (!m_board)
0466         initBoard();
0467 
0468     m_runningGame = true;
0469     m_leaveGame->setEnabled(true);
0470 
0471     m_mainLayout->addWidget(m_board, 0, 1, 3, 1);
0472     m_board->displayDefault();
0473     m_board->show();
0474 
0475     foreach (PortfolioView *portfolioView, m_portfolioViews)
0476         if (!portfolioView->player()->isSpectator())
0477             portfolioView->buildPortfolio();
0478 }
0479 
0480 void Atlantik::freezeBoard()
0481 {
0482     if (!m_board)
0483         showBoard();
0484 
0485     m_runningGame = false;
0486     m_leaveGame->setEnabled(false);
0487     // TODO: m_board->freeze();
0488 }
0489 
0490 void Atlantik::slotNetworkConnected()
0491 {
0492 }
0493 
0494 
0495 
0496 void Atlantik::slotNetworkError(QAbstractSocket::SocketError errnum)
0497 {
0498     switch (errnum) {
0499     case QAbstractSocket::ConnectionRefusedError:
0500         appendMsg(i18n("Error connecting: connection refused by host."), ErrorMsg);
0501         break;
0502     case QAbstractSocket::HostNotFoundError:
0503         appendMsg(i18n("Error connecting: host not found."), ErrorMsg);
0504         break;
0505     default:
0506         appendMsg(i18n("Error connecting: error %1.", QString::number(errnum)), ErrorMsg);
0507         break;
0508     }
0509 
0510     // Re-init network object
0511     initNetworkObject();
0512 }
0513 
0514 void Atlantik::slotNetworkDisconnected()
0515 {
0516 //  switch( status )
0517 //  {
0518 //  case KBufferedIO::involuntary:
0519 //      slotMsgStatus( i18n("Connection with server %1:%2 lost.").arg(m_atlantikNetwork->host()).arg(m_atlantikNetwork->port()), QString("connect_no") );
0520 //      showSelectServer();
0521 //      break;
0522 //  default:
0523 //      if ( !m_atlantikNetwork->host().isEmpty() )
0524 //          slotMsgStatus( i18n("Disconnected from %1:%2.").arg(m_atlantikNetwork->host()).arg(m_atlantikNetwork->port()), QString("connect_no") );
0525 //      break;
0526 //  }
0527 }
0528 
0529 void Atlantik::slotConfigure()
0530 {
0531     if (KConfigDialog::showDialog(QStringLiteral("configdialog")))
0532         return;
0533 
0534     ConfigDialog *dialog = new ConfigDialog(m_tokenTheme, this);
0535     connect(dialog, SIGNAL(settingsChanged(QString)), this, SLOT(slotUpdateConfig()));
0536 
0537     dialog->show();
0538 }
0539 
0540 void Atlantik::showEventLog()
0541 {
0542     bool created = false;
0543     if (!m_eventLogWidget)
0544     {
0545         m_eventLogWidget = new EventLogWidget(m_eventLog, this);
0546         created = true;
0547     }
0548     m_eventLogWidget->show();
0549     if (created)
0550         m_eventLogWidget->restoreSettings();
0551 }
0552 
0553 void Atlantik::configureNotifications()
0554 {
0555     KNotifyConfigWidget::configure(this);
0556 }
0557 
0558 void Atlantik::slotUpdateConfig()
0559 {
0560     bool optBool, viewConfigChanged = false;
0561     QString optStr;
0562 
0563     optBool = Settings::chatTimeStamps();
0564     if (m_config.chatTimestamps != optBool)
0565     {
0566         m_config.chatTimestamps = optBool;
0567     }
0568 
0569     optStr = Settings::playerName();
0570     if (m_config.playerName != optStr)
0571     {
0572         m_config.playerName = optStr;
0573         if (m_atlantikNetwork->isConnected())
0574             m_atlantikNetwork->setName(optStr);
0575     }
0576 
0577     optStr = Settings::playerImage();
0578     if (m_config.playerImage != optStr)
0579     {
0580         m_config.playerImage = optStr;
0581         if (m_atlantikNetwork->isConnected())
0582             m_atlantikNetwork->setImage(optStr);
0583     }
0584 
0585     optBool = Settings::indicateUnowned();
0586     if (m_config.indicateUnowned != optBool)
0587     {
0588         m_config.indicateUnowned = optBool;
0589         viewConfigChanged = true;
0590     }
0591 
0592     optBool = Settings::highlightUnowned();
0593     if (m_config.highlightUnowned != optBool)
0594     {
0595         m_config.highlightUnowned = optBool;
0596         viewConfigChanged = true;
0597     }
0598 
0599     optBool = Settings::darkenMortgaged();
0600     if (m_config.darkenMortgaged != optBool)
0601     {
0602         m_config.darkenMortgaged = optBool;
0603         viewConfigChanged = true;
0604     }
0605 
0606     optBool = Settings::animateToken();
0607     if (m_config.animateTokens != optBool)
0608     {
0609         m_config.animateTokens = optBool;
0610         viewConfigChanged = true;
0611     }
0612 
0613     optBool = Settings::quartzEffects();
0614     if (m_config.quartzEffects != optBool)
0615     {
0616         m_config.quartzEffects = optBool;
0617         viewConfigChanged = true;
0618     }
0619 
0620     optBool = Settings::connectOnStart();
0621     if (m_config.connectOnStart != optBool)
0622     {
0623         m_config.connectOnStart = optBool;
0624     }
0625 
0626     optBool = Settings::hideDevelopmentServers();
0627     if (m_config.hideDevelopmentServers != optBool)
0628     {
0629         m_config.hideDevelopmentServers = optBool;
0630         if (m_selectServer)
0631             m_selectServer->setHideDevelopmentServers(optBool);
0632     }
0633 
0634     if (viewConfigChanged && m_board)
0635         m_board->setViewProperties(m_config.indicateUnowned, m_config.highlightUnowned, m_config.darkenMortgaged, m_config.quartzEffects, m_config.animateTokens);
0636 }
0637 
0638 void Atlantik::slotSendMsg()
0639 {
0640     if (m_atlantikNetwork->isConnected())
0641         m_atlantikNetwork->cmdChat(m_input->text());
0642     m_input->setText(QString());
0643 }
0644 
0645 void Atlantik::slotMsgInfo(const QString &msg)
0646 {
0647     appendMsg(msg, InfoMsg);
0648 }
0649 
0650 void Atlantik::slotMsgError(const QString &msg)
0651 {
0652     appendMsg(msg, ErrorMsg);
0653 }
0654 
0655 void Atlantik::slotMsgStatus(const QString &message, EventType type)
0656 {
0657     m_sbStatus->setText(message);
0658     m_eventLog->addEvent(message, type);
0659 }
0660 
0661 static bool commandForMe(const QList<QStringView> &parts, const QString &playerName)
0662 {
0663     if (parts.size() <= 1)
0664         return true;
0665 
0666     foreach (const QStringView &p, parts)
0667     {
0668         if (p == playerName)
0669             return true;
0670     }
0671 
0672     return false;
0673 }
0674 
0675 void Atlantik::slotMsgChat(const QString &player, const QString &msg)
0676 {
0677     QString res;
0678     if (msg == QLatin1String("/me"))
0679         res = QStringLiteral("* %1").arg(player);
0680     else if (msg.startsWith(QLatin1String("/me ")))
0681         res = QStringLiteral("* %1 %2").arg(player, msg.mid(4));
0682     else if (msg.startsWith(QLatin1String("[ACTION] ")))
0683         res = QStringLiteral("* %1 %2").arg(player, msg.mid(9));
0684     else
0685         res = QStringLiteral("<%1> %2").arg(player, msg);
0686     appendMsg(res, ChatMsg);
0687     Player *playerSelf = m_atlanticCore->playerSelf();
0688     if (!isActiveWindow() && (!playerSelf || playerSelf->name() != player))
0689         KNotification::event(QStringLiteral("chat"), QStringLiteral("%1: %2").arg(player, msg.toHtmlEscaped()));
0690     if (m_atlantikNetwork->isConnected() && playerSelf && msg.startsWith(QLatin1Char('!')))
0691     {
0692         const QList<QStringView> parts = QStringView(msg).split(QLatin1Char(' '), Qt::SkipEmptyParts);
0693         Q_ASSERT(!parts.isEmpty());
0694         if (commandForMe(parts, playerSelf->name()))
0695         {
0696             const QStringView cmd = parts.first();
0697             if (cmd == QLatin1String("!date"))
0698                 m_atlantikNetwork->cmdChat(QLocale::c().toString(QDateTime::currentDateTime(), QLocale::ShortFormat));
0699             else if (cmd == QLatin1String("!ping"))
0700                 m_atlantikNetwork->cmdChat(QStringLiteral("pong"));
0701             else if (cmd == QLatin1String("!version"))
0702                 m_atlantikNetwork->cmdChat(QString::fromLatin1("Atlantik " ATLANTIK_VERSION_STRING));
0703         }
0704     }
0705 }
0706 
0707 void Atlantik::appendMsg(const QString &msg, MsgType type)
0708 {
0709     const QString escaped = msg.toHtmlEscaped();
0710     QString ts;
0711     QString res;
0712 
0713     if (m_config.chatTimestamps)
0714     {
0715         const QString timeString = QLocale::system().toString(QTime::currentTime(), QLocale::ShortFormat);
0716         ts = QStringLiteral("[%1] ").arg(timeString);
0717     }
0718 
0719     switch (type)
0720     {
0721     case ErrorMsg:
0722         res = QStringLiteral("<font color=\"%1\">%2[%3] %4</font>").arg(QStringLiteral("#ff0000"), ts, i18nc("error message", "Error"), escaped);
0723         break;
0724     case InfoMsg:
0725         res = QStringLiteral("<font color=\"%1\">%2[%3] %4</font>").arg(QStringLiteral("#91640a"), ts, i18nc("informative message", "Info"), escaped);
0726         break;
0727     case ChatMsg:
0728         res = QStringLiteral("<font color=\"%1\">%2</font>%3").arg(QStringLiteral("#709070"), ts, escaped);
0729         break;
0730     }
0731     res += QStringLiteral("<br/>\n");
0732 
0733     m_serverMsgs->insertHtml(res);
0734 }
0735 
0736 void Atlantik::playerChanged(Player *player)
0737 {
0738     PortfolioView *portfolioView = findPortfolioView(player);
0739     if (!portfolioView)
0740         portfolioView = addPortfolioView(player);
0741 
0742     Player *playerSelf = m_atlanticCore->playerSelf();
0743     if (player == playerSelf)
0744     {
0745         // We changed ourselves..
0746         foreach (PortfolioView *portfolioView, m_portfolioViews)
0747             {
0748                 // Clear all portfolios if we're not in game
0749                 if ( !player->game() )
0750                     portfolioView->clearPortfolio();
0751 
0752                 // Show players in our game, hide the rest
0753                 Player *pTmp = portfolioView->player();
0754                 if (pTmp->game() == playerSelf->game())
0755                     portfolioView->show();
0756                 else
0757                     portfolioView->hide();
0758             }
0759         if (!player->game())
0760             showSelectGame();
0761         else
0762         {
0763             if ( !m_board || m_board->isHidden() )
0764                 showSelectConfiguration();
0765         }
0766 
0767         m_roll->setEnabled(player->canRoll());
0768         m_buyEstate->setEnabled(player->canBuy());
0769         m_auctionEstate->setEnabled(player->canAuction());
0770 
0771         // TODO: Should be more finetuned, but monopd doesn't send can_endturn can_payjail can_jailroll yet
0772         m_endTurn->setEnabled(player->hasTurn() && !(player->canRoll() || player->canBuy() || player->inJail()));
0773         m_jailCard->setEnabled(player->canUseCard());
0774         m_jailPay->setEnabled(player->hasTurn() && player->inJail());
0775         m_jailRoll->setEnabled(player->hasTurn() && player->inJail());
0776     }
0777     else
0778     {
0779         // Another player changed, check if we need to show or hide
0780         // his/her portfolioView.
0781         if (playerSelf)
0782         {
0783             if (player->game() == playerSelf->game())
0784                 portfolioView->show();
0785             else
0786                 portfolioView->hide();
0787         }
0788         else if ( !player->game() )
0789             portfolioView->hide();
0790     }
0791 }
0792 
0793 void Atlantik::gainedTurn()
0794 {
0795     KNotification::event(QStringLiteral("gainedturn"), i18n("It is your turn now."), QPixmap()
0796 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0797     ,this
0798 #endif
0799     );
0800 }
0801 
0802 void Atlantik::initNetworkObject()
0803 {
0804     if (m_atlantikNetwork)
0805     {
0806         m_atlantikNetwork->reset();
0807         return;
0808     }
0809 
0810     m_atlantikNetwork = new AtlantikNetwork(m_atlanticCore);
0811     connect(m_atlantikNetwork, SIGNAL(msgInfo(QString)), this, SLOT(slotMsgInfo(QString)));
0812     connect(m_atlantikNetwork, SIGNAL(msgError(QString)), this, SLOT(slotMsgError(QString)));
0813     connect(m_atlantikNetwork, SIGNAL(msgStatus(const QString &, EventType)), this, SLOT(slotMsgStatus(const QString &, EventType)));
0814     connect(m_atlantikNetwork, SIGNAL(msgChat(QString, QString)), this, SLOT(slotMsgChat(QString, QString)));
0815 
0816     connect(m_atlantikNetwork, SIGNAL(connectionSuccess()), this, SLOT(slotNetworkConnected()));
0817     connect(m_atlantikNetwork, SIGNAL(connectionFailed(QAbstractSocket::SocketError)), this, SLOT(slotNetworkError(QAbstractSocket::SocketError)));
0818     connect(m_atlantikNetwork, SIGNAL(disconnected()), this, SLOT(slotNetworkDisconnected()));
0819 
0820     connect(m_atlantikNetwork, SIGNAL(receivedHandshake()), this, SLOT(sendHandshake()));
0821 
0822     connect(m_atlantikNetwork, SIGNAL(gameConfig()), this, SLOT(showSelectConfiguration()));
0823     connect(m_atlantikNetwork, SIGNAL(gameInit()), this, SLOT(initBoard()));
0824     connect(m_atlantikNetwork, SIGNAL(gameRun()), this, SLOT(showBoard()));
0825     connect(m_atlantikNetwork, SIGNAL(gameEnd()), this, SLOT(freezeBoard()));
0826 
0827     connect(m_atlantikNetwork, SIGNAL(newEstate(Estate *)), this, SLOT(newEstate(Estate *)));
0828     connect(m_atlantikNetwork, SIGNAL(newAuction(Auction *)), this, SLOT(newAuction(Auction *)));
0829 
0830     connect(m_atlantikNetwork, SIGNAL(clientCookie(QString)), this, SLOT(clientCookie(QString)));
0831     connect(m_atlantikNetwork, SIGNAL(networkEvent(const QString &, EventType)), m_eventLog, SLOT(addEvent(const QString &, EventType)));
0832 
0833     connect(this, SIGNAL(rollDice()), m_atlantikNetwork, SLOT(rollDice()));
0834     connect(this, SIGNAL(buyEstate()), m_atlantikNetwork, SLOT(buyEstate()));
0835     connect(this, SIGNAL(auctionEstate()), m_atlantikNetwork, SLOT(auctionEstate()));
0836     connect(this, SIGNAL(endTurn()), m_atlantikNetwork, SLOT(endTurn()));
0837     connect(this, SIGNAL(jailCard()), m_atlantikNetwork, SLOT(jailCard()));
0838     connect(this, SIGNAL(jailPay()), m_atlantikNetwork, SLOT(jailPay()));
0839     connect(this, SIGNAL(jailRoll()), m_atlantikNetwork, SLOT(jailRoll()));
0840     connect(this, SIGNAL(reconnect(QString)), m_atlantikNetwork, SLOT(reconnect(QString)));
0841 }
0842 
0843 void Atlantik::clientCookie(const QString &cookie)
0844 {
0845     ConnectionCookie *newCookie = nullptr;
0846 
0847     if (!cookie.isEmpty() && m_atlantikNetwork)
0848         newCookie = new ConnectionCookie(m_atlantikNetwork->host(), m_atlantikNetwork->port(), cookie);
0849 
0850     m_cookie.reset(newCookie);
0851     m_reconnect->setEnabled(false);
0852 }
0853 
0854 void Atlantik::sendHandshake()
0855 {
0856     if (m_reconnecting)
0857     {
0858         m_reconnecting = false;
0859         Q_EMIT reconnect(m_reconnectCookie->cookie());
0860         m_reconnectCookie.reset();
0861         return;
0862     }
0863 
0864     m_atlantikNetwork->setName(m_config.playerName);
0865     m_atlantikNetwork->setImage(m_config.playerImage);
0866 
0867     // Check command-line args to see if we need to auto-join
0868     QString game = m_cliParser ? m_cliParser->value(QStringLiteral("game")) : QString();
0869     qCDebug(ATLANTIK_LOG) << "received Handshake; joining game:" << game.toInt();
0870     if (!game.isEmpty())
0871         m_atlantikNetwork->joinGame(game.toInt());
0872 }
0873 
0874 void Atlantik::showAboutDialog()
0875 {
0876     KAboutApplicationDialog dialog(KAboutData::applicationData(), this);
0877     dialog.exec();
0878 }
0879 
0880 PortfolioView *Atlantik::addPortfolioView(Player *player)
0881 {
0882     PortfolioView *portfolioView = new PortfolioView(m_atlanticCore, player, m_config.activeColor, m_config.inactiveColor, m_portfolioWidget);
0883     portfolioView->setTokenTheme(m_tokenTheme);
0884     m_portfolioViews.append(portfolioView);
0885     if ( m_portfolioViews.count() > 0 && m_portfolioScroll->isHidden() )
0886         m_portfolioScroll->show();
0887 
0888     connect(player, SIGNAL(changed(Player *)), portfolioView, SLOT(playerChanged()));
0889     connect(portfolioView, SIGNAL(newTrade(Player *)), m_atlantikNetwork, SLOT(newTrade(Player *)));
0890     connect(portfolioView, SIGNAL(kickPlayer(Player *)), m_atlantikNetwork, SLOT(kickPlayer(Player *)));
0891     connect(portfolioView, SIGNAL(estateClicked(Estate *)), m_board, SLOT(prependEstateDetails(Estate *)));
0892 
0893     m_portfolioLayout->addWidget(portfolioView);
0894     portfolioView->show();
0895 
0896     return portfolioView;
0897 }
0898 
0899 PortfolioView *Atlantik::findPortfolioView(Player *player)
0900 {
0901     foreach (PortfolioView *portfolioView, m_portfolioViews)
0902         if (player == portfolioView->player())
0903             return portfolioView;
0904 
0905     return nullptr;
0906 }
0907 
0908 void Atlantik::closeEvent(QCloseEvent *e)
0909 {
0910     Game *gameSelf = m_atlanticCore->gameSelf();
0911     Player *playerSelf = m_atlanticCore->playerSelf();
0912 
0913     int result = KMessageBox::Continue;
0914     if ( gameSelf && !playerSelf->isBankrupt() && m_runningGame && !playerSelf->isSpectator() )
0915         result = KMessageBox::warningContinueCancel( this, i18n("You are currently part of an active game. Are you sure you want to close Atlantik? If you do, you forfeit the game."), i18n("Close & Forfeit?"), KGuiItem(i18n("Close && Forfeit")) );
0916 
0917     if ( result == KMessageBox::Continue )
0918     {
0919         if ( m_atlantikNetwork && m_atlantikNetwork->isConnected() )
0920         {
0921             m_atlantikNetwork->leaveGame();
0922             m_atlantikNetwork->disconnect();
0923         }
0924 
0925         KXmlGuiWindow::closeEvent(e);
0926     }
0927     else
0928         e->ignore();
0929 }
0930 
0931 void Atlantik::slotReconnect()
0932 {
0933     if (!m_reconnectCookie)
0934         return;
0935 
0936     m_atlantikNetwork->serverConnect(m_reconnectCookie->host(), m_reconnectCookie->port());
0937     m_reconnecting = true;
0938 }
0939 
0940 void Atlantik::slotLeaveGame()
0941 {
0942     Game *gameSelf = m_atlanticCore->gameSelf();
0943     Player *playerSelf = m_atlanticCore->playerSelf();
0944 
0945     if (!gameSelf)
0946         return;
0947 
0948     if (!m_runningGame || playerSelf->isBankrupt() || playerSelf->isSpectator() || (KMessageBox::warningContinueCancel(this, i18n("You are currently part of an active game. Are you sure you want to leave it? If you do, you forfeit the game."), i18n("Leave & Forfeit?"), KGuiItem(i18n("Leave && Forfeit"))) == KMessageBox::Continue))
0949         m_atlantikNetwork->leaveGame();
0950 }
0951 
0952 #include "moc_atlantik.cpp"