Renamed AnimationSettings -> AnimationManager.
[tagua/yd.git] / src / mainwindow.cpp
blobffd2e83d83c1a79e79711b24113757af803e833e
1 /*
2 Copyright (c) 2006-2007 Paolo Capriotti <p.capriotti@gmail.com>
3 (c) 2006-2007 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>
14 #include <QKeySequence>
15 #include <QStackedWidget>
16 #include <QDockWidget>
17 #include <QCloseEvent>
18 #include <QTextStream>
19 #include <QTextCodec>
21 #include <KAction>
22 #include <KStandardAction>
23 #include <KActionCollection>
24 #include <KIcon>
25 #include <KLocale>
26 #include <KFileDialog>
27 #include <kio/netaccess.h>
28 #include <KMessageBox>
29 #include <KMenuBar>
30 #include <KTemporaryFile>
32 #include <core/behaviour.h>
33 #include <core/color.h>
34 #include <core/state.h>
35 #include "variant.h"
37 #include "actioncollection.h"
38 #include "chesstable.h"
39 #include "console.h"
40 #include "clock.h"
41 #include "newgame.h"
42 #include "variants.h"
43 #include "gameinfo.h"
44 #include "controllers/editgame.h"
45 #include "controllers/editposition.h"
46 #include "engineinfo.h"
47 #include "movelist_table.h"
48 #include "icsconnection.h"
49 #include "qconnect.h"
50 #include "mastersettings.h"
51 #include "flash.h"
52 #include "foreach.h"
53 #include "pgnparser.h"
54 #include "pref_highlight.h"
55 #include "pref_preferences.h"
56 #include "tabwidget.h"
58 using namespace Qt;
59 using namespace boost;
61 MainWindow::~MainWindow() {
62 delete console;
63 qApp->quit();
66 MainWindow::MainWindow(const QString& variant)
67 : KXmlGuiWindow(0)
68 , m_ui(actionCollection()) {
69 setObjectName("tagua_main");
70 m_main = new TabWidget(this);
71 m_main->setTabBarHidden(true);
72 setCentralWidget(m_main);
74 m_movelist_stack = new QStackedWidget;
76 connect(m_main, SIGNAL(currentChanged(int)),
77 this, SLOT(changeTab(int)));
78 connect(m_main, SIGNAL(closeTab()),
79 this, SLOT(closeTab()));
81 movelist_dock = new QDockWidget(this);
82 movelist_dock->setWidget(m_movelist_stack);
83 movelist_dock->setWindowTitle(i18n("Move list"));
84 movelist_dock->setObjectName("move_list");
85 addDockWidget(Qt::LeftDockWidgetArea, movelist_dock, Qt::Vertical);
86 movelist_dock->show();
88 Variant* v = Variants::self().create(variant);
89 ChessTable* board = new ChessTable(v);
91 board->setFocus();
93 console_dock = new QDockWidget(this);
94 console = new Console(0, i18n("FICS Connection"));
95 console_dock->setWidget(console);
96 console_dock->setFocusProxy(console);
97 console_dock->setWindowTitle(i18n("Console"));
98 console_dock->setObjectName("console");
99 addDockWidget(Qt::BottomDockWidgetArea, console_dock, Qt::Horizontal);
100 console_dock->setWindowFlags(console_dock->windowFlags() & ~Qt::WindowStaysOnTopHint);
101 console_dock->show();
103 settings().onChange(this, "settingsChanged");
105 connect(board, SIGNAL(error(ErrorCode)), this, SLOT(displayErrorMessage(ErrorCode)));
107 setupActions();
108 setupGUI();
109 setupEngineMenu();
111 // start in edit game mode
112 newGame(v->name(), StatePtr(), true);
113 updateVariantActions();
116 ChessTable* MainWindow::table() {
117 return qobject_cast<ChessTable*>(m_main->currentWidget());
120 KAction* MainWindow::installRegularAction(const QString& name, const KIcon& icon, const QString& text, QObject* obj, const char* slot) {
121 KAction* temp = new KAction(icon, text, this);
122 actionCollection()->addAction(name, temp);
123 connect(temp, SIGNAL(triggered(bool)), obj, slot);
124 return temp;
127 void MainWindow::setupEngineMenu() {
128 QMenu* engine_menu = 0;
129 SettingArray engine_settings = settings().group("engines").array("engine");
130 foreach (Settings s, engine_settings) {
131 if (!engine_menu) {
132 // this way the menu is created only if there is at least one engine
133 engine_menu = menuBar()->addMenu(i18n("E&ngines"));
136 QString name;
137 EngineDetails engine_details;
138 engine_details.load(s);
140 EngineInfo* engine = new EngineInfo(engine_details, ui());
142 m_engines.push_back(engine);
144 QMenu* menu = engine_menu->addMenu(engine_details.name);
147 KAction* play_white = new KAction(i18n("Play as &white"), this);
148 play_white->setCheckable(true);
149 connect(play_white, SIGNAL(triggered()), engine, SLOT(playAsWhite()));
150 menu->addAction(play_white);
153 KAction* play_black = new KAction(i18n("Play as &black"), this);
154 play_black->setCheckable(true);
155 connect(play_black, SIGNAL(triggered()), engine, SLOT(playAsBlack()));
156 menu->addAction(play_black);
158 #if 0 // disable analysis for the moment
160 KAction* analyze = new KAction(i18n("&Analyze"), this);
161 analyze->setCheckable(true);
162 connect(analyze, SIGNAL(triggered()), engine, SLOT(analyze()));
163 menu->addAction(analyze);
165 #endif
169 void MainWindow::setupActions() {
170 KAction* tmp;
172 KStandardAction::openNew(this, SLOT(newGame()), actionCollection());
173 KStandardAction::open(this, SLOT(loadGame()), actionCollection());
174 KStandardAction::save(this, SLOT(saveGame()), actionCollection());
175 KStandardAction::saveAs(this, SLOT(saveGameAs()), actionCollection());
176 KStandardAction::quit(this, SLOT(quit()), actionCollection());
177 KStandardAction::preferences(this, SLOT(preferences()), actionCollection());
179 installRegularAction("back", KIcon("go-previous"), i18n("&Back"), &ui(), SLOT(back()));
180 installRegularAction("forward", KIcon("go-next"), i18n("&Forward"), &ui(), SLOT(forward()));
181 installRegularAction("begin", KIcon("go-first"), i18n("Be&gin"), &ui(), SLOT(gotoFirst()));
182 installRegularAction("end", KIcon("go-last"), i18n("&End"), &ui(), SLOT(gotoLast()));
183 installRegularAction("connect", KIcon("connection-established"), i18n("&Connect"), this, SLOT(icsConnect()));
184 installRegularAction("disconnect", KIcon("connect-no"), i18n("&Disconnect"), this, SLOT(icsDisconnect()));
186 KStandardAction::undo(&ui(), SLOT(undo()), actionCollection());
187 KStandardAction::redo(&ui(), SLOT(redo()), actionCollection());
189 // installRegularAction("pgnCopy", KIcon("edit-copy"), i18n("Copy PGN"), this, SLOT(pgnCopy()));
190 // installRegularAction("pgnPaste", KIcon("edit-paste"), i18n("Paste PGN"), this, SLOT(pgnPaste()));
191 installRegularAction("editPosition", KIcon("edit"), i18n("&Edit position"), this, SLOT(editPosition()));
192 installRegularAction("clearBoard", KIcon("edit-delete"), i18n("&Clear board"), &ui(), SLOT(clearBoard()));
193 installRegularAction("setStartingPosition", KIcon("contents"), i18n("&Set starting position"),
194 &ui(), SLOT(setStartingPosition()));
195 // installRegularAction("copyPosition", KIcon(), i18n("&Copy position"), &ui(), SLOT(copyPosition()));
196 // installRegularAction("pastePosition", KIcon(), i18n("&Paste position"), &ui(), SLOT(pastePosition()));
197 tmp = installRegularAction("flip", KIcon("rotate"), i18n("&Flip view"), this, SLOT(flipView()));
198 tmp->setShortcut(Qt::CTRL + Qt::Key_F);
199 installRegularAction("toggleConsole", KIcon("openterm"), i18n("Toggle &console"), this, SLOT(toggleConsole()));
200 installRegularAction("toggleMoveList", KIcon("view_text"), i18n("Toggle &move list"), this, SLOT(toggleMoveList()));
203 void MainWindow::updateVariantActions() {
204 ActionCollection* variant_actions = m_ui.variantActions();
205 unplugActionList("variantActions");
206 if (variant_actions) {
207 plugActionList("variantActions", variant_actions->actions());
209 else {
210 WARNING("No variant actions");
214 void MainWindow::readSettings() { }
215 void MainWindow::writeSettings() { }
217 void MainWindow::closeEvent(QCloseEvent* e) {
218 e->accept();
219 writeSettings();
220 delete this;
224 void MainWindow::keyPressEvent(QKeyEvent* event) {
225 if (event->key() == Qt::Key_F5) {
226 ui().createCtrlAction();
230 void MainWindow::keyReleaseEvent(QKeyEvent* event) {
231 if (event->key() == Qt::Key_F5) {
232 ui().destroyCtrlAction();
236 void MainWindow::changeTab(int index) {
237 m_ui.setCurrentTab(m_main->currentWidget());
238 m_movelist_stack->setCurrentIndex(index);
239 updateVariantActions();
242 void MainWindow::closeTab() {
243 if (m_main->count() > 1) {
244 int old_index = m_main->currentIndex();
245 ChessTable* old_board = table();
247 int new_index = old_index - 1;
248 if (new_index < 0)
249 new_index = old_index + 1;
250 m_main->setCurrentIndex(new_index);
252 m_main->removeTab(old_index);
253 m_movelist_stack->removeWidget(m_movelist_stack->widget(old_index));
254 m_ui.removeController(old_board);
256 if (m_main->count() <= 1) {
257 m_main->setTabBarHidden(true);
260 #if 0 // this doesn't work... why?
261 ChessTable* old_board = table();
262 m_ui.removeController(old_board);
263 m_movelist_stack->removeWidget(
264 m_movelist_stack->widget(m_main->currentIndex()));
265 m_main->removeTab(m_main->currentIndex());
267 delete old_board;
269 if (m_main->count() <= 1) {
270 m_main->hideTabBar();
273 // update ui controller (just in case...)
274 m_ui.setCurrentTab(m_main->currentWidget());
275 #endif
279 void MainWindow::createTab(ChessTable* board, const shared_ptr<Controller>& controller,
280 const QString& caption, int index) {
281 if (index == -1)
282 index = m_main->addTab(board, caption);
283 else
284 m_main->insertTab(index, board, caption);
286 m_ui.addController(board, controller);
287 // m_ui.setCurrentTab(board);
288 m_movelist_stack->addWidget(board->moveListTable());
290 m_main->setCurrentIndex(index);
291 m_movelist_stack->setCurrentIndex(index);
293 if (m_main->count() > 1) m_main->setTabBarHidden(false);
297 void MainWindow::cleanupGame(const QString& what, const QString& result) {
298 Q_UNUSED(what);
299 Q_UNUSED(result);
301 cleanupGame();
304 void MainWindow::cleanupGame() {
305 ui().detach();
306 ui().end();
309 bool MainWindow::newGame(const QString& variantName,
310 const StatePtr& startingPos,
311 bool newTab) {
312 Variant* variant = Variants::self().create(variantName);
313 if (!variant) {
314 WARNING("no variant " << variantName << " found");
315 variant = Variants::self().create("chess");
318 if (variant) {
319 ChessTable* board = newTab ? new ChessTable(variant) : table();
320 QString text = QString("Editing %1 game").arg(variant->name().toLower());
322 shared_ptr<Controller> controller(
323 new EditGameController(board, startingPos));
324 if (newTab) {
325 createTab(board, controller, text);
327 else {
328 unplugActionList("variantActions");
329 ui().setController(controller);
330 table()->setPlayers(Player(), Player());
331 m_main->setTabText(m_main->currentIndex(), text);
333 return true;
335 else {
336 ERROR("Could not find the chess variant");
337 exit(1);
338 return false;
343 void MainWindow::editPosition() {
344 //BROKEN
345 #if 0
346 shared_ptr<Controller> controller(new EditPositionController(table(), ChessVariant::info()));
347 m_main->setTabText(m_main->currentIndex(), "Editing chess position");
348 ui().setController(controller);
349 #endif
352 void MainWindow::setupGame(const GameInfo* gameInfo, const PositionInfo& style12) {
353 QString variantCode = gameInfo->variant();
354 Variant* variant = Variants::self().create(variantCode);
355 shared_ptr<EditGameController> controller(
356 new EditGameController(table()));
357 Q_ASSERT(style12.relation == PositionInfo::NotMyMove ||
358 style12.relation == PositionInfo::MyMove);
360 // set opponent side
361 const IColor* turn = style12.position->turn();
362 const IBehaviour* behaviour = style12.position->behaviour();
363 if (!behaviour) return;
364 const IColor* side = style12.relation == PositionInfo::MyMove ?
365 behaviour->opponent(turn) : turn;
367 if (controller->addICSPlayer(side, style12.gameNumber, m_connection)) {
368 ui().setController(controller);
369 table()->setPlayers(gameInfo->white(), gameInfo->black());
370 m_main->setTabText(m_main->currentIndex(),
371 QString("FICS Game - %1 vs %2").arg(style12.whitePlayer)
372 .arg(style12.blackPlayer));
376 void MainWindow::setupExaminedGame(const GameInfo* gameInfo, const PositionInfo& style12) {
377 table()->setVariant(
378 Variants::self().create(gameInfo->variant()));
379 shared_ptr<EditGameController> controller(
380 new EditGameController(table()));
381 if (controller->setExaminationMode(style12.gameNumber, m_connection)) {
382 table()->setPlayers(Player(style12.whitePlayer, -1),
383 Player(style12.blackPlayer, -1));
384 ui().setController(controller);
385 m_main->setTabText(m_main->currentIndex(),
386 QString("Examining - %1 vs %2").arg(style12.whitePlayer)
387 .arg(style12.blackPlayer));
392 void MainWindow::setupObservedGame(const GameInfo* g, const PositionInfo& style12) {
393 Variant* variant = Variants::self().create(g->variant());
394 std::auto_ptr<ChessTable> board(new ChessTable(variant));
396 shared_ptr<EditGameController> controller(
397 new EditGameController(board.get()));
398 if (controller->setObserveMode(style12.gameNumber, m_connection)) {
399 board->setPlayers(Player(style12.whitePlayer, -1),
400 Player(style12.blackPlayer, -1));
401 // ui().setController(controller);
402 //std::cout << "adding tab" << std::endl;
403 createTab(board.get(), controller,
404 QString("Observing - %1 vs %2").arg(style12.whitePlayer)
405 .arg(style12.blackPlayer));
406 board.release();
410 void MainWindow::setupPGN(const QString& s) {
411 PGN pgn(s);
413 std::map<QString, QString>::const_iterator var = pgn.m_tags.find("Variant");
414 Variant* variant;
416 if (var == pgn.m_tags.end()) {
417 variant = Variants::self().create("chess");
419 else if (!(variant = Variants::self().create(var->second))) {
420 std::cout << " --> MainWindow::setupPGN: Error, no such variant " << var->second << std::endl;
421 return;
424 shared_ptr<EditGameController> controller(
425 new EditGameController(table()));
426 ui().setController(controller);
427 controller->loadPGN(pgn);
429 // table()->setPlayers(gameInfo->white(), gameInfo->black());
430 // m_main->setTabText(m_main->currentIndex(),
431 // QString("FICS Game - %1 vs %2").arg(style12.whitePlayer)
432 // .arg(style12.blackPlayer));
435 bool MainWindow::openFile(const QString& filename) {
436 QFileInfo info(filename);
438 if(info.isDir()) {
439 KMessageBox::sorry(this, i18n("You have specified a folder"), i18n("Error"));
440 return false;
443 if(!info.exists() || !info.isFile()) {
444 KMessageBox::sorry(this, i18n("The specified file does not exist"), i18n("Error"));
445 return false;
448 QFile file(filename);
450 if(!file.open(QIODevice::ReadOnly)) {
451 KMessageBox::sorry(this, i18n("You do not have read permission to this file."), i18n("Error"));
452 return false;
455 QTextStream stream(&file);
456 QTextCodec *codec;
457 codec = QTextCodec::codecForLocale();
458 stream.setCodec(codec);
460 setupPGN(stream.readAll());
461 //ui().pgnPaste(stream.readAll());
462 return true;
465 void MainWindow::loadGame() {
466 KUrl url = KFileDialog::getOpenUrl(KUrl(), "*.pgn", this, i18n("Open PGN file"));
468 if(url.isEmpty())
469 return;
471 QString tmp_file;
472 if (KIO::NetAccess::download(url, tmp_file, this)) {
473 openFile(tmp_file);
474 KIO::NetAccess::removeTempFile(tmp_file);
476 else
477 KMessageBox::error(this, KIO::NetAccess::lastErrorString());
480 void MainWindow::saveGame() {
481 if (m_url.isEmpty())
482 saveGameAs();
483 else
484 m_url = saveGame(m_url);
487 void MainWindow::saveGameAs() {
488 m_url = saveGame(KFileDialog::getOpenUrl(KUrl(), "*.pgn", this, i18n("Save PGN file")));
491 KUrl MainWindow::saveGame(const KUrl& url) {
492 if (url.isEmpty())
493 return KUrl();
495 if (!url.isLocalFile()) {
496 // save in a temporary file
497 KTemporaryFile tmp_file;
498 tmp_file.open();
499 saveFile(tmp_file);
500 if (!KIO::NetAccess::upload(tmp_file.fileName(), url, this))
501 return KUrl();
503 else {
504 QFile file(url.path());
505 if (!file.open(QIODevice::WriteOnly))
506 return KUrl();
507 saveFile(file);
510 return url;
513 void MainWindow::saveFile(QFile& file) {
514 QTextStream stream(&file);
515 QTextCodec *codec;
516 codec = QTextCodec::codecForLocale();
517 stream.setCodec(codec);
518 stream << ui().currentPGN() << "\n";
521 void MainWindow::createConnection(const QString& username, const QString& password,
522 const QString& host, quint16 port,
523 const QString& timeseal, const QString& timeseal_cmd) {
524 m_connection = shared_ptr<ICSConnection>(new ICSConnection);
526 connect(m_connection.get(), SIGNAL(established()), this, SLOT(onEstablishConnection()));
527 connect(m_connection.get(), SIGNAL(hostLookup()), this, SLOT(onHostLookup()));
528 connect(m_connection.get(), SIGNAL(hostFound()), this, SLOT(onHostFound()));
530 connect(m_connection.get(), SIGNAL(receivedLine(QString, int)), console, SLOT(displayText(QString, int)));
531 connect(m_connection.get(), SIGNAL(receivedText(QString, int)), console, SLOT(displayText(QString, int)));
533 connect(console, SIGNAL(receivedInput(const QString&)), m_connection.get(), SLOT(sendText(const QString&)));
534 connect(console, SIGNAL(notify()), this, SLOT(flash()));
536 connect(m_connection.get(), SIGNAL(loginPrompt()), this, SLOT(sendLogin()));
537 connect(m_connection.get(), SIGNAL(registeredNickname()), this, SLOT(sendBlankPassword()));
538 connect(m_connection.get(), SIGNAL(prompt()), this, SLOT(prompt()));
541 connect(m_connection.get(), SIGNAL(creatingExaminedGame(const GameInfo*, const PositionInfo&)),
542 this, SLOT(setupExaminedGame(const GameInfo*, const PositionInfo&)));
543 connect(m_connection.get(), SIGNAL(endingExaminedGame(int)), this, SLOT(cleanupGame()));
545 connect(m_connection.get(), SIGNAL(creatingObservedGame(const GameInfo*, const PositionInfo&)),
546 this, SLOT(setupObservedGame(const GameInfo*, const PositionInfo&)));
547 connect(m_connection.get(), SIGNAL(endingObservedGame(int)), this, SLOT(cleanupGame()));
550 connect(m_connection.get(), SIGNAL(creatingGame(const GameInfo*, const PositionInfo&)),
551 this, SLOT(setupGame(const GameInfo*, const PositionInfo&)));
552 connect(m_connection.get(), SIGNAL(endingGame(const QString&, const QString&)),
553 this, SLOT(cleanupGame(const QString&, const QString&)));
554 connect(m_connection.get(), SIGNAL(notification()), this, SLOT(flash()));
556 m_connection->establish(host.toAscii().constData(), port, timeseal, timeseal_cmd);
557 console->show();
559 // send username / password combination
560 if (!username.isEmpty()) {
561 m_connection->sendText(username);
562 m_connection->sendText(password);
565 quickConnectDialog.reset();
568 void MainWindow::icsConnect() {
569 quickConnectDialog = shared_ptr<QConnect>(new QConnect(this));
570 connect(quickConnectDialog.get(),
571 SIGNAL(acceptConnection(const QString&,
572 const QString&,
573 const QString&,
574 quint16,
575 const QString&,
576 const QString&)),
577 this,
578 SLOT(createConnection(const QString&,
579 const QString&,
580 const QString&,
581 quint16,
582 const QString&,
583 const QString&)));
584 quickConnectDialog->show();
587 void MainWindow::destroyConnection() {
588 m_connection.reset();
591 void MainWindow::icsDisconnect() {
592 destroyConnection();
593 console->hide();
594 console->clear();
597 void MainWindow::testConnect() {
598 Settings s_ics = settings().group("ics");
599 if (s_ics["username"]) {
600 QString username = s_ics["username"].value<QString>();
601 QString password = (s_ics["password"] | "");
602 QString host = (s_ics["icsHost"] | "freechess.org");
603 quint16 port = (s_ics["icsPort"] | 5000);
604 createConnection(username, password, host, port, QString(), QString() );
606 else icsConnect();
610 void MainWindow::onEstablishConnection() {
611 // std::cout << "connection established" << std::endl;
614 void MainWindow::onConnectionError(int ) {
615 // std::cout << "connection error (" << code << ")" << std::endl;
618 void MainWindow::onHostLookup() {
619 // std::cout << "hostLookup..." << std::flush;
622 void MainWindow::onHostFound() {
623 // std::cout << "found" << std::endl;
627 void MainWindow::sendLogin() {
628 // std::cout << "sending username" << std::endl;
629 // connection->sendText(connection->username());
632 void MainWindow::sendBlankPassword() {
633 m_connection->sendText("");
636 void MainWindow::prompt() {
637 if (!m_connection->initialized()) {
638 m_connection->startup();
639 m_connection->setInitialized();
643 void MainWindow::newGame() {
644 StatePtr pos = ui().position();
645 scoped_ptr<NewGame> dialog(new NewGame(this));
646 if (dialog->exec() == QDialog::Accepted) {
647 if (!newGame(dialog->variant(), StatePtr(), dialog->newTab()))
648 QMessageBox::information(this, i18n("Error"), i18n("Error creating game"));
652 void MainWindow::quit() {
653 delete this;
656 void MainWindow::flipView() {
657 table()->flip();
660 void MainWindow::toggleConsole() {
661 if (console_dock->isVisible())
662 console_dock->hide();
663 else {
664 console_dock->show();
665 console_dock->setFocus(Qt::MouseFocusReason
666 /*Qt::ActiveWindowFocusReason*/ /*Qt::OtherFocusReason*/);
670 void MainWindow::toggleMoveList() {
671 if (movelist_dock->isVisible())
672 movelist_dock->hide();
673 else {
674 movelist_dock->show();
675 movelist_dock->setFocus(Qt::OtherFocusReason);
680 void MainWindow::displayMessage(const QString& msg) {
681 Q_UNUSED(msg); // TODO
682 // statusBar()->message(msg, 2000);
685 void MainWindow::displayErrorMessage(ErrorCode code) {
686 switch(code) {
687 case InvalidMove:
688 displayMessage(i18n("Illegal move"));
689 break;
693 void MainWindow::flash() {
694 if( !isAncestorOf(QApplication::focusWidget()) )
695 Flash::flash(this);
698 #if 0
699 void MainWindow::prefHighlight() {
700 PrefHighlight dialog;
701 int result = dialog.exec();
702 if (result == QDialog::Accepted) {
703 dialog.apply();
706 #endif
708 void MainWindow::preferences() {
709 Preferences dialog(ui().currentVariant());
710 int result = dialog.exec();
711 if (result == QDialog::Accepted)
712 dialog.apply();
715 void MainWindow::settingsChanged() {
716 ui().reloadSettings();