Refactored TurnTest.
[tagua/yd.git] / src / mainwindow.cpp
blobd7413a9b73406ecab7ddfca789315ef9b0e720aa
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 */
11 #include "mainwindow.h"
12 #include <boost/scoped_ptr.hpp>
13 #include <kaction.h>
14 #include <kstandardaction.h>
15 #include <kactioncollection.h>
16 #include <kicon.h>
17 #include <klocale.h>
18 #include <kurl.h>
19 #include <kfiledialog.h>
20 #include <kio/netaccess.h>
21 #include <kmessagebox.h>
22 #include <kmenubar.h>
23 #include <QKeySequence>
24 #include <QStackedWidget>
25 #include <QDockWidget>
26 #include <QCloseEvent>
27 #include <QTextStream>
28 #include <QTextCodec>
29 #include <ktabwidget.h>
30 #include "chesstable.h"
31 #include "console.h"
32 #include "clock.h"
33 #include "newgame.h"
34 #include "variants/variants.h"
35 #include "variants/xchess/piecetype.h"
36 #include "variants/chess.h"
37 #include "gameinfo.h"
38 #include "controllers/editgame.h"
39 #include "controllers/editposition.h"
40 #include "engineinfo.h"
41 #include "movelist_table.h"
42 #include "icsconnection.h"
43 #include "qconnect.h"
44 #include "mastersettings.h"
45 #include "flash.h"
46 #include "foreach.h"
47 #include "pgnparser.h"
48 #include "pref_highlight.h"
49 #include "pref_preferences.h"
51 using namespace Qt;
52 using namespace boost;
54 MainWindow::~MainWindow() {
55 delete console;
56 qApp->quit();
59 MainWindow::MainWindow()
60 : KXmlGuiWindow(0) {
61 setObjectName("tagua_main");
62 m_main = new KTabWidget(this);
63 setCentralWidget(m_main);
65 m_movelist_stack = new QStackedWidget;
67 connect(m_main, SIGNAL(currentChanged(int)),
68 this, SLOT(changeTab(int)));
69 connect(m_main, SIGNAL(closeTab()),
70 this, SLOT(closeTab()));
72 movelist_dock = new QDockWidget(this);
73 movelist_dock->setWidget(m_movelist_stack);
74 movelist_dock->setWindowTitle(i18n("Move list"));
75 movelist_dock->setObjectName("move_list");
76 addDockWidget(Qt::LeftDockWidgetArea, movelist_dock, Qt::Vertical);
77 movelist_dock->show();
79 ChessTable* board = new ChessTable;
81 board->setFocus();
83 console_dock = new QDockWidget(this);
84 console = new Console(0, i18n("FICS Connection"));
85 console_dock->setWidget(console);
86 console_dock->setFocusProxy(console);
87 console_dock->setWindowTitle(i18n("Console"));
88 console_dock->setObjectName("console");
89 addDockWidget(Qt::BottomDockWidgetArea, console_dock, Qt::Horizontal);
90 console_dock->setWindowFlags(console_dock->windowFlags() & ~Qt::WindowStaysOnTopHint);
91 console_dock->show();
93 connect(board, SIGNAL(error(ErrorCode)), this, SLOT(displayErrorMessage(ErrorCode)));
94 //BROKEN connect(board->clock(), SIGNAL(labelClicked(int)), &ui(), SLOT(setTurn(int)));
96 // start in edit game mode
97 newGame("chess", AbstractPosition::Ptr());
99 setupActions();
100 setupGUI();
101 setupEngineMenu();
102 updatePromotionType();
105 ChessTable* MainWindow::table() {
106 return qobject_cast<ChessTable*>(m_main->currentWidget());
109 KAction* MainWindow::addPromotionAction(const QString& name, const QString& text, const char* uiSlot) {
110 KAction* temp = new KAction(KIcon(name), text, this);
111 temp->setCheckable(true);
112 connect(temp, SIGNAL(triggered(bool)), &ui(), uiSlot);
113 m_promote_group->addAction(temp);
114 actionCollection()->addAction(name, temp);
115 return temp;
118 KAction* MainWindow::installRegularAction(const QString& name, const KIcon& icon, const QString& text, QObject* obj, const char* slot) {
119 KAction* temp = new KAction(icon, text, this);
120 actionCollection()->addAction(name, temp);
121 connect(temp, SIGNAL(triggered(bool)), obj, slot);
122 return temp;
125 void MainWindow::setupEngineMenu() {
126 QMenu* engine_menu = 0;
127 SettingArray engine_settings = settings().group("engines").array("engine");
128 foreach (Settings s, engine_settings) {
129 if (!engine_menu) {
130 // this way the menu is created only if there is at least one engine
131 engine_menu = menuBar()->addMenu(i18n("E&ngines"));
134 QString name;
135 EngineDetails engine_details;
136 s["name"] >> engine_details.name;
137 s["path"] >> engine_details.path;
138 engine_details.type = EngineDetails::typeFromName(s["type"].value<QString>());
139 if (s["work-path"])
140 s["work-path"] >> engine_details.workPath;
142 kDebug() << "creating engine " << engine_details.name << endl;
143 EngineInfo* engine = new EngineInfo(engine_details, ui());
145 m_engines.push_back(engine);
147 QMenu* menu = engine_menu->addMenu(engine_details.name);
150 KAction* play_white = new KAction(i18n("Play as &white"), this);
151 connect(play_white, SIGNAL(triggered()), engine, SLOT(playAsWhite()));
152 menu->addAction(play_white);
155 KAction* play_black = new KAction(i18n("Play as &black"), this);
156 connect(play_black, SIGNAL(triggered()), engine, SLOT(playAsBlack()));
157 menu->addAction(play_black);
160 KAction* analyze = new KAction(i18n("&Analyze"), this);
161 analyze->setCheckable(true);
162 connect(analyze, SIGNAL(triggered()), engine, SLOT(analyze()));
163 menu->addAction(analyze);
168 void MainWindow::setupActions() {
169 KStandardAction::openNew(this, SLOT(newGame()), actionCollection());
170 KStandardAction::open(this, SLOT(loadGame()), actionCollection());
171 KStandardAction::quit(this, SLOT(quit()), actionCollection());
173 m_promote_group = new QActionGroup(this);
175 m_promote_queen = addPromotionAction("promoteQueen", i18n("Promote to &queen"), SLOT(promoteToQueen()));
176 m_promote_rook = addPromotionAction("promoteRook", i18n("Promote to &Rook"), SLOT(promoteToRook()));
177 m_promote_bishop = addPromotionAction("promoteBishop", i18n("Promote to &Bishop"), SLOT(promoteToBishop()));
178 m_promote_knight = addPromotionAction("promoteKnight", i18n("Promote to K&night"), SLOT(promoteToKnight()));
180 m_do_promotion = installRegularAction("doPromotion", KIcon("favorites"), i18n("Do &promotion"), &ui(),SLOT(setDoPromotion(bool)));
181 m_do_promotion->setCheckable(true);
183 installRegularAction("back", KIcon("go-previous"), i18n("&Back"), &ui(), SLOT(back()));
184 installRegularAction("forward", KIcon("go-next"), i18n("&Forward"), &ui(), SLOT(forward()));
185 installRegularAction("begin", KIcon("go-first"), i18n("Be&gin"), &ui(), SLOT(gotoFirst()));
186 installRegularAction("end", KIcon("go-last"), i18n("&End"), &ui(), SLOT(gotoLast()));
187 installRegularAction("connect", KIcon("connection-established"), i18n("&Connect"), this, SLOT(icsConnect()));
188 installRegularAction("disconnect", KIcon("connect-no"), i18n("&Disconnect"), this, SLOT(icsDisconnect()));
190 KStandardAction::undo(&ui(), SLOT(undo()), actionCollection());
191 KStandardAction::redo(&ui(), SLOT(redo()), actionCollection());
193 installRegularAction("pgnCopy", KIcon("edit-copy"), i18n("Copy PGN"), this, SLOT(pgnCopy()));
194 installRegularAction("pgnPaste", KIcon("edit-paste"), i18n("Paste PGN"), this, SLOT(pgnPaste()));
195 installRegularAction("editPosition", KIcon("edit"), i18n("&Edit position"), this, SLOT(editPosition()));
196 installRegularAction("clearBoard", KIcon("edit-delete"), i18n("&Clear board"), &ui(), SLOT(clearBoard()));
197 installRegularAction("setStartingPosition", KIcon("contents"), i18n("&Set starting position"),
198 &ui(), SLOT(setStartingPosition()));
199 installRegularAction("copyPosition", KIcon(), i18n("&Copy position"), &ui(), SLOT(copyPosition()));
200 installRegularAction("pastePosition", KIcon(), i18n("&Paste position"), &ui(), SLOT(pastePosition()));
201 installRegularAction("flip", KIcon("rotate"), i18n("&Flip view"), this, SLOT(flipView()));
202 installRegularAction("toggleConsole", KIcon("openterm"), i18n("Toggle &console"), this, SLOT(toggleConsole()));
203 installRegularAction("toggleMoveList", KIcon("view_text"), i18n("Toggle &move list"), this, SLOT(toggleMoveList()));
204 installRegularAction("configure", KIcon("configure"), i18n("&Configure Tagua..."), this, SLOT(preferences()));
207 void MainWindow::updatePromotionType() {
208 // TODO: I'm removing this code because it causes a crash
209 // and I don't want to investigate further, since the promotion
210 // stuff needs to be changed completely.
211 #if 0
212 int ptype = m_ui.promotionType();
213 if (ptype == 0)
214 m_promote_group->setEnabled(false);
215 else {
216 m_promote_group->setEnabled(true);
217 switch(ptype) {
218 case QUEEN:
219 m_promote_queen->setChecked(true);
220 break;
221 case ROOK:
222 m_promote_rook->setChecked(true);
223 break;
224 case BISHOP:
225 m_promote_bishop->setChecked(true);
226 break;
227 case KNIGHT:
228 m_promote_knight->setChecked(true);
229 break;
230 default:
231 m_promote_group->setEnabled(false);
235 std::cout << "do promotion: " << m_ui.doPromotion() << std::endl;
236 m_do_promotion->setChecked(m_ui.doPromotion());
237 #endif
240 void MainWindow::readSettings() { }
241 void MainWindow::writeSettings() { }
243 void MainWindow::closeEvent(QCloseEvent* e) {
244 e->accept();
245 writeSettings();
246 delete this;
250 void MainWindow::keyPressEvent(QKeyEvent* event) {
251 if (event->key() == Qt::Key_F5) {
252 ui().createCtrlAction();
256 void MainWindow::keyReleaseEvent(QKeyEvent* event) {
257 if (event->key() == Qt::Key_F5) {
258 ui().destroyCtrlAction();
262 void MainWindow::changeTab(int index) {
263 m_ui.setCurrentTab(m_main->currentWidget());
264 m_movelist_stack->setCurrentIndex(index);
265 updatePromotionType();
268 void MainWindow::closeTab() {
269 if (m_main->count() > 1) {
270 ChessTable* old_board = table();
271 m_ui.removeController(old_board);
272 m_movelist_stack->removeWidget(
273 m_movelist_stack->widget(m_main->currentIndex()));
274 m_main->removeTab(m_main->currentIndex());
276 delete old_board;
278 if (m_main->count() <= 1) {
279 m_main->setTabBarHidden(true);
282 // update ui controller (just in case...)
283 m_ui.setCurrentTab(m_main->currentWidget());
287 void MainWindow::createTab(ChessTable* board, const shared_ptr<Controller>& controller,
288 const QString& caption, int index) {
289 if (index == -1)
290 index = m_main->addTab(board, caption);
291 else
292 m_main->insertTab(index, board, caption);
294 m_main->setCurrentIndex(index);
295 m_ui.addController(board, controller);
296 m_ui.setCurrentTab(board);
297 m_movelist_stack->addWidget(board->moveListTable());
298 m_movelist_stack->setCurrentIndex(index);
299 if (m_main->count() > 1) m_main->setTabBarHidden(false);
303 void MainWindow::cleanupGame(const QString& what, const QString& result) {
304 Q_UNUSED(what);
305 Q_UNUSED(result);
307 cleanupGame();
310 void MainWindow::cleanupGame() {
311 ui().detach();
312 ui().end();
316 bool MainWindow::newGame(const QString& variantName, AbstractPosition::Ptr startingPos) {
317 VariantInfo* variant = Variant::variant(variantName);
318 if (variant) {
319 ChessTable* board = new ChessTable;
320 shared_ptr<Controller> controller(new EditGameController(
321 board, variant, startingPos));
322 createTab(board, controller,
323 QString("Editing %1 game").arg(variantName.toLower()));
324 return true;
326 else return false;
330 void MainWindow::editPosition() {
331 //BROKEN
332 #if 0
333 shared_ptr<Controller> controller(new EditPositionController(table(), ChessVariant::info()));
334 m_main->setTabText(m_main->currentIndex(), "Editing chess position");
335 ui().setController(controller);
336 #endif
339 void MainWindow::setupGame(const GameInfo* gameInfo, const PositionInfo& style12) {
340 QString variantCode = gameInfo->variant();
341 VariantInfo* variant = Variant::variant(variantCode);
342 shared_ptr<EditGameController> controller(new EditGameController(
343 table(), variant));
344 Q_ASSERT(style12.relation == PositionInfo::NotMyMove ||
345 style12.relation == PositionInfo::MyMove);
347 // set opponent side
348 int side = (style12.relation == PositionInfo::MyMove ^ style12.turn == 0) ? 0 : 1;
350 if (controller->addICSPlayer(side, style12.gameNumber, m_connection)) {
351 ui().setController(controller);
352 table()->setPlayers(gameInfo->white(), gameInfo->black());
353 m_main->setTabText(m_main->currentIndex(),
354 QString("FICS Game - %1 vs %2").arg(style12.whitePlayer)
355 .arg(style12.blackPlayer));
359 void MainWindow::setupExaminedGame(const GameInfo* /*gameInfo*/, const PositionInfo& style12) {
360 shared_ptr<EditGameController> controller(new EditGameController(
361 table(), ChessVariant::info()));
362 if (controller->setExaminationMode(style12.gameNumber, m_connection)) {
363 table()->setPlayers(Player(style12.whitePlayer, -1),
364 Player(style12.blackPlayer, -1));
365 ui().setController(controller);
366 m_main->setTabText(m_main->currentIndex(),
367 QString("Examining - %1 vs %2").arg(style12.whitePlayer)
368 .arg(style12.blackPlayer));
373 void MainWindow::setupObservedGame(const GameInfo* g, const PositionInfo& style12) {
374 std::auto_ptr<ChessTable> board(new ChessTable);
376 shared_ptr<EditGameController> controller(new EditGameController(
377 board.get(), Variant::variant(g->variant())));
378 if (controller->setObserveMode(style12.gameNumber, m_connection)) {
379 board->setPlayers(Player(style12.whitePlayer, -1),
380 Player(style12.blackPlayer, -1));
381 // ui().setController(controller);
382 //std::cout << "adding tab" << std::endl;
383 createTab(board.get(), controller,
384 QString("Observing - %1 vs %2").arg(style12.whitePlayer)
385 .arg(style12.blackPlayer));
386 board.release();
390 void MainWindow::setupPGN(const QString& s) {
391 PGN pgn(s);
393 std::map<QString, QString>::const_iterator var = pgn.m_tags.find("Variant");
394 VariantInfo *variant;
396 if(var == pgn.m_tags.end())
397 variant = Variant::variant("Chess");
398 else if(!(variant = Variant::variant(var->second))) {
399 std::cout << " --> MainWindow::setupPGN: Error, no such variant " << var->second << std::endl;
400 return;
403 shared_ptr<EditGameController> controller(new EditGameController(
404 table(), variant));
405 ui().setController(controller);
406 controller->loadPGN(pgn);
408 // table()->setPlayers(gameInfo->white(), gameInfo->black());
409 // m_main->setTabText(m_main->currentIndex(),
410 // QString("FICS Game - %1 vs %2").arg(style12.whitePlayer)
411 // .arg(style12.blackPlayer));
414 bool MainWindow::openFile(const QString& filename) {
415 QFileInfo info(filename);
417 if(info.isDir()) {
418 KMessageBox::sorry(this, i18n("You have specified a folder"), i18n("Error"));
419 return false;
422 if(!info.exists() || !info.isFile()) {
423 KMessageBox::sorry(this, i18n("The specified file does not exist"), i18n("Error"));
424 return false;
427 QFile file(filename);
429 if(!file.open(QIODevice::ReadOnly)) {
430 KMessageBox::sorry(this, i18n("You do not have read permission to this file."), i18n("Error"));
431 return false;
434 QTextStream stream(&file);
435 QTextCodec *codec;
436 codec = QTextCodec::codecForLocale();
437 stream.setCodec(codec);
439 setupPGN(stream.readAll());
440 //ui().pgnPaste(stream.readAll());
441 return true;
444 void MainWindow::loadGame() {
445 KUrl url = KFileDialog::getOpenUrl(KUrl(), "*.pgn", this, i18n("Open PGN file"));
447 if(url.isEmpty())
448 return;
450 QString tmp_file;
451 if (KIO::NetAccess::download(url, tmp_file, this)) {
452 openFile(tmp_file);
453 KIO::NetAccess::removeTempFile(tmp_file);
455 else
456 KMessageBox::error(this, KIO::NetAccess::lastErrorString());
459 void MainWindow::createConnection(const QString& username, const QString& password,
460 const QString& host, quint16 port,
461 const QString& timeseal, const QString& timeseal_cmd) {
462 m_connection = shared_ptr<ICSConnection>(new ICSConnection);
464 connect(m_connection.get(), SIGNAL(established()), this, SLOT(onEstablishConnection()));
465 connect(m_connection.get(), SIGNAL(hostLookup()), this, SLOT(onHostLookup()));
466 connect(m_connection.get(), SIGNAL(hostFound()), this, SLOT(onHostFound()));
468 connect(m_connection.get(), SIGNAL(receivedLine(QString, int)), console, SLOT(displayText(QString, int)));
469 connect(m_connection.get(), SIGNAL(receivedText(QString, int)), console, SLOT(displayText(QString, int)));
471 connect(console, SIGNAL(receivedInput(const QString&)), m_connection.get(), SLOT(sendText(const QString&)));
472 connect(console, SIGNAL(notify()), this, SLOT(flash()));
474 connect(m_connection.get(), SIGNAL(loginPrompt()), this, SLOT(sendLogin()));
475 connect(m_connection.get(), SIGNAL(registeredNickname()), this, SLOT(sendBlankPassword()));
476 connect(m_connection.get(), SIGNAL(prompt()), this, SLOT(prompt()));
479 connect(m_connection.get(), SIGNAL(creatingExaminedGame(const GameInfo*, const PositionInfo&)),
480 this, SLOT(setupExaminedGame(const GameInfo*, const PositionInfo&)));
481 connect(m_connection.get(), SIGNAL(endingExaminedGame(int)), this, SLOT(cleanupGame()));
483 connect(m_connection.get(), SIGNAL(creatingObservedGame(const GameInfo*, const PositionInfo&)),
484 this, SLOT(setupObservedGame(const GameInfo*, const PositionInfo&)));
485 connect(m_connection.get(), SIGNAL(endingObservedGame(int)), this, SLOT(cleanupGame()));
488 connect(m_connection.get(), SIGNAL(creatingGame(const GameInfo*, const PositionInfo&)),
489 this, SLOT(setupGame(const GameInfo*, const PositionInfo&)));
490 connect(m_connection.get(), SIGNAL(endingGame(const QString&, const QString&)),
491 this, SLOT(cleanupGame(const QString&, const QString&)));
492 connect(m_connection.get(), SIGNAL(notification()), this, SLOT(flash()));
494 m_connection->establish(host.toAscii().constData(), port, timeseal, timeseal_cmd);
495 console->show();
497 // send username / password combination
498 if (!username.isEmpty()) {
499 m_connection->sendText(username);
500 m_connection->sendText(password);
503 quickConnectDialog.reset();
506 void MainWindow::icsConnect() {
507 quickConnectDialog = shared_ptr<QConnect>(new QConnect(this));
508 connect(quickConnectDialog.get(),
509 SIGNAL(acceptConnection(const QString&,
510 const QString&,
511 const QString&,
512 quint16,
513 const QString&,
514 const QString&)),
515 this,
516 SLOT(createConnection(const QString&,
517 const QString&,
518 const QString&,
519 quint16,
520 const QString&,
521 const QString&)));
522 quickConnectDialog->show();
525 void MainWindow::destroyConnection() {
526 m_connection.reset();
529 void MainWindow::icsDisconnect() {
530 destroyConnection();
531 console->hide();
532 console->clear();
535 void MainWindow::testConnect() {
536 Settings s_ics = settings().group("ics");
537 if (s_ics["username"]) {
538 QString username = s_ics["username"].value<QString>();
539 QString password = (s_ics["password"] | "");
540 QString host = (s_ics["icsHost"] | "freechess.org");
541 quint16 port = (s_ics["icsPort"] | 5000);
542 createConnection(username, password, host, port, QString(), QString() );
544 else icsConnect();
548 void MainWindow::onEstablishConnection() {
549 // std::cout << "connection established" << std::endl;
552 void MainWindow::onConnectionError(int ) {
553 // std::cout << "connection error (" << code << ")" << std::endl;
556 void MainWindow::onHostLookup() {
557 // std::cout << "hostLookup..." << std::flush;
560 void MainWindow::onHostFound() {
561 // std::cout << "found" << std::endl;
565 void MainWindow::sendLogin() {
566 // std::cout << "sending username" << std::endl;
567 // connection->sendText(connection->username());
570 void MainWindow::sendBlankPassword() {
571 std::cout << "sending blank password" << std::endl;
572 m_connection->sendText("");
575 void MainWindow::prompt() {
576 if (!m_connection->initialized()) {
577 m_connection->startup();
578 m_connection->setInitialized();
582 void MainWindow::newGame() {
583 AbstractPosition::Ptr pos = ui().position();
584 scoped_ptr<NewGame> dialog(new NewGame(this, pos));
585 if (dialog->exec() == QDialog::Accepted) {
586 if(dialog->isCustom()) {
587 VariantInfo *vi = Variant::variant(dialog->variant());
588 std::cout << "vi[" << dialog->variant() << "] = " << vi << std::endl;
589 pos = vi->createCustomPosition(dialog->customOptions());
590 pos->setup();
592 else if (!dialog->playFromCurrent())
593 pos = AbstractPosition::Ptr();
594 if (!newGame(dialog->variant(), pos))
595 QMessageBox::information(this, tr("Error"), tr("Variant not implemented, yet"));
599 void MainWindow::quit() {
600 delete this;
603 void MainWindow::flipView() {
604 table()->flip();
607 void MainWindow::toggleConsole() {
608 if (console_dock->isVisible())
609 console_dock->hide();
610 else {
611 console_dock->show();
612 console_dock->setFocus(Qt::MouseFocusReason
613 /*Qt::ActiveWindowFocusReason*/ /*Qt::OtherFocusReason*/);
617 void MainWindow::toggleMoveList() {
618 if (movelist_dock->isVisible())
619 movelist_dock->hide();
620 else {
621 movelist_dock->show();
622 movelist_dock->setFocus(Qt::OtherFocusReason);
627 void MainWindow::displayMessage(const QString& msg) {
628 Q_UNUSED(msg); // TODO
629 // statusBar()->message(msg, 2000);
632 void MainWindow::displayErrorMessage(ErrorCode code) {
633 switch(code) {
634 case InvalidMove:
635 displayMessage(tr("Illegal move"));
636 break;
640 void MainWindow::flash() {
641 if( !isAncestorOf(QApplication::focusWidget()) )
642 Flash::flash(this);
645 #if 0
646 void MainWindow::prefHighlight() {
647 PrefHighlight dialog;
648 int result = dialog.exec();
649 if (result == QDialog::Accepted) {
650 dialog.apply();
653 #endif
655 void MainWindow::preferences() {
656 Preferences dialog(ui().currentVariant());
657 int result = dialog.exec();
658 if (result == QDialog::Accepted)
659 dialog.apply();