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"