Continued refactoring.
[tagua.git] / src / mainwindow.cpp
blob7d4850a9bdfbcf880d35a50a96c6b92d613f753f
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 <QKeySequence>
23 #include <QStackedWidget>
24 #include <QDockWidget>
25 #include <QCloseEvent>
26 #include <QTextStream>
27 #include <QTextCodec>
28 #include <ktabwidget.h>
29 #include "chesstable.h"
30 #include "console.h"
31 #include "clock.h"
32 #include "newgame.h"
33 #include "variants/variants.h"
34 #include "variants/xchess/piecetype.h"
35 #include "variants/chess.h"
36 #include "gameinfo.h"
37 #include "controllers/editgame.h"
38 #include "controllers/editposition.h"
39 #include "movelist_table.h"
40 #include "icsconnection.h"
41 #include "qconnect.h"
42 #include "global.h"
43 #include "flash.h"
44 #include "pgnparser.h"
45 #include "pref_highlight.h"
46 #include "pref_clock.h"
47 #include "pref_preferences.h"
49 using namespace Qt;
50 using namespace boost;
52 MainWindow::~MainWindow() {
53 delete console;
54 qApp->quit();
57 MainWindow::MainWindow()
58 : KXmlGuiWindow(0) {
59 setObjectName("kboard_main");
60 m_main = new KTabWidget(this);
61 setCentralWidget(m_main);
63 m_movelist_stack = new QStackedWidget;
65 connect(m_main, SIGNAL(currentChanged(int)),
66 this, SLOT(changeTab(int)));
67 connect(m_main, SIGNAL(closeTab()),
68 this, SLOT(closeTab()));
70 movelist_dock = new QDockWidget(this);
71 movelist_dock->setWidget(m_movelist_stack);
72 movelist_dock->setWindowTitle(i18n("Move list"));
73 movelist_dock->setObjectName("move_list");
74 addDockWidget(Qt::LeftDockWidgetArea, movelist_dock, Qt::Vertical);
75 movelist_dock->show();
77 ChessTable* board = new ChessTable;
79 board->setFocus();
81 console_dock = new QDockWidget(this);
82 console = new Console(0, i18n("FICS Connection"));
83 console_dock->setWidget(console);
84 console_dock->setFocusProxy(console);
85 console_dock->setWindowTitle(i18n("Console"));
86 console_dock->setObjectName("console");
87 addDockWidget(Qt::BottomDockWidgetArea, console_dock, Qt::Horizontal);
88 console_dock->setWindowFlags(console_dock->windowFlags() & ~Qt::WindowStaysOnTopHint);
89 console_dock->show();
91 connect(board, SIGNAL(error(ErrorCode)), this, SLOT(displayErrorMessage(ErrorCode)));
92 connect(board->clock(), SIGNAL(labelClicked(int)), &ui(), SLOT(setTurn(int)));
94 // start in edit game mode
95 newGame("chess", AbstractPosition::Ptr());
97 setupActions();
98 setupGUI();
99 updatePromotionType();
102 ChessTable* MainWindow::table() {
103 return qobject_cast<ChessTable*>(m_main->currentWidget());
106 KAction* MainWindow::addPromotionAction(const QString& name, const QString& text, const char* uiSlot) {
107 KAction* temp = new KAction(KIcon(name), text, this);
108 temp->setCheckable(true);
109 connect(temp, SIGNAL(triggered(bool)), &ui(), uiSlot);
110 m_promote_group->addAction(temp);
111 actionCollection()->addAction(name, temp);
112 return temp;
115 KAction* MainWindow::installRegularAction(const QString& name, const KIcon& icon, const QString& text, QObject* obj, const char* slot) {
116 KAction* temp = new KAction(icon, text, this);
117 actionCollection()->addAction(name, temp);
118 connect(temp, SIGNAL(triggered(bool)), obj, slot);
119 return temp;
122 void MainWindow::setupActions() {
123 // (void) new KAction(i18n("&New game..."), CTRL+Key_N, this, SLOT(newGame()), actionCollection(), "new");
124 KAction* temp;
126 KStandardAction::openNew(this, SLOT(newGame()), actionCollection());
127 KStandardAction::open(this, SLOT(loadGame()), actionCollection());
128 KStandardAction::quit(this, SLOT(quit()), actionCollection());
130 m_promote_group = new QActionGroup(this);
132 m_promote_queen = addPromotionAction("promoteQueen", i18n("Promote to &queen"), SLOT(promoteToQueen()));
133 m_promote_rook = addPromotionAction("promoteRook", i18n("Promote to &Rook"), SLOT(promoteToRook()));
134 m_promote_bishop = addPromotionAction("promoteBishop", i18n("Promote to &Bishop"), SLOT(promoteToBishop()));
135 m_promote_knight = addPromotionAction("promoteKnight", i18n("Promote to K&night"), SLOT(promoteToKnight()));
137 m_do_promotion = installRegularAction("doPromotion", KIcon("favorites"),
138 i18n("Do &promotion"), &ui(),
139 SLOT(setDoPromotion(bool)));
140 m_do_promotion->setCheckable(true);
142 installRegularAction("back", KIcon("back"), i18n("&Back"), &ui(), SLOT(back()));
143 installRegularAction("forward", KIcon("forward"), i18n("&Forward"), &ui(), SLOT(forward()));
144 installRegularAction("begin", KIcon("start"), i18n("Be&gin"), &ui(), SLOT(gotoFirst()));
145 installRegularAction("end", KIcon("finish"), i18n("&End"), &ui(), SLOT(gotoLast()));
146 installRegularAction("connect", KIcon("connect_creating"), i18n("&Connect"), this, SLOT(icsConnect()));
147 installRegularAction("disconnect", KIcon("connect_no"), i18n("&Disconnect"), this, SLOT(icsDisconnect()));
149 KStandardAction::undo(&ui(), SLOT(undo()), actionCollection());
150 KStandardAction::redo(&ui(), SLOT(redo()), actionCollection());
152 installRegularAction("pgnCopy", KIcon("copy"), i18n("Copy PGN"), this, SLOT(pgnCopy()));
153 installRegularAction("pgnPaste", KIcon("paste"), i18n("Paste PGN"), this, SLOT(pgnPaste()));
154 installRegularAction("editPosition", KIcon("edit"), i18n("&Edit position"), this, SLOT(editPosition()));
155 installRegularAction("clearBoard", KIcon("editdelete"), i18n("&Clear board"), &ui(), SLOT(clearBoard()));
156 installRegularAction("setStartingPosition", KIcon("contents"), i18n("&Set starting position"),
157 &ui(), SLOT(setStartingPosition()));
158 installRegularAction("copyPosition", KIcon(), i18n("&Copy position"), &ui(), SLOT(copyPosition()));
159 installRegularAction("pastePosition", KIcon(), i18n("&Paste position"), &ui(), SLOT(pastePosition()));
160 installRegularAction("flip", KIcon("rotate"), i18n("&Flip view"), this, SLOT(flipView()));
161 installRegularAction("toggleConsole", KIcon("openterm"), i18n("Toggle &console"), this, SLOT(toggleConsole()));
162 installRegularAction("toggleMoveList", KIcon("view_text"), i18n("Toggle &move list"), this, SLOT(toggleMoveList()));
163 installRegularAction("configure", KIcon("configure"), i18n("&Configure KBoard..."), this, SLOT(preferences()));
166 void MainWindow::updatePromotionType() {
167 // TODO: I'm removing this code because it causes a crash
168 // and I don't want to investigate further, since the promotion
169 // stuff needs to be changed completely.
170 #if 0
171 int ptype = m_ui.promotionType();
172 if (ptype == 0)
173 m_promote_group->setEnabled(false);
174 else {
175 m_promote_group->setEnabled(true);
176 switch(ptype) {
177 case QUEEN:
178 m_promote_queen->setChecked(true);
179 break;
180 case ROOK:
181 m_promote_rook->setChecked(true);
182 break;
183 case BISHOP:
184 m_promote_bishop->setChecked(true);
185 break;
186 case KNIGHT:
187 m_promote_knight->setChecked(true);
188 break;
189 default:
190 m_promote_group->setEnabled(false);
194 std::cout << "do promotion: " << m_ui.doPromotion() << std::endl;
195 m_do_promotion->setChecked(m_ui.doPromotion());
196 #endif
199 void MainWindow::readSettings() { }
200 void MainWindow::writeSettings() { }
202 void MainWindow::closeEvent(QCloseEvent* e) {
203 e->accept();
204 writeSettings();
205 delete this;
209 void MainWindow::keyPressEvent(QKeyEvent* event) {
210 if (event->key() == Qt::Key_F5) {
211 ui().createCtrlAction();
215 void MainWindow::keyReleaseEvent(QKeyEvent* event) {
216 if (event->key() == Qt::Key_F5) {
217 ui().destroyCtrlAction();
221 void MainWindow::changeTab(int index) {
222 m_ui.setCurrentTab(m_main->currentWidget());
223 m_movelist_stack->setCurrentIndex(index);
224 updatePromotionType();
227 void MainWindow::closeTab() {
228 if (m_main->count() > 1) {
229 ChessTable* old_board = table();
230 m_ui.removeController(old_board);
231 m_movelist_stack->removeWidget(
232 m_movelist_stack->widget(m_main->currentIndex()));
233 m_main->removeTab(m_main->currentIndex());
235 delete old_board;
237 if (m_main->count() <= 1) {
238 m_main->setTabBarHidden(true);
241 // update ui controller (just in case...)
242 m_ui.setCurrentTab(m_main->currentWidget());
246 void MainWindow::createTab(ChessTable* board, const shared_ptr<Controller>& controller,
247 const QString& caption, int index) {
248 if (index == -1)
249 index = m_main->addTab(board, caption);
250 else
251 m_main->insertTab(index, board, caption);
253 m_main->setCurrentIndex(index);
254 m_ui.addController(board, controller);
255 m_ui.setCurrentTab(board);
256 m_movelist_stack->addWidget(board->moveListTable());
257 m_movelist_stack->setCurrentIndex(index);
258 if (m_main->count() > 1) m_main->setTabBarHidden(false);
262 void MainWindow::cleanupGame(const QString& what, const QString& result) {
263 Q_UNUSED(what);
264 Q_UNUSED(result);
266 cleanupGame();
269 void MainWindow::cleanupGame() {
270 ui().detach();
271 ui().end();
275 bool MainWindow::newGame(const QString& variantName, AbstractPosition::Ptr startingPos) {
276 VariantInfo* variant = Variant::variant(variantName);
277 if (variant) {
278 ChessTable* board = new ChessTable;
279 shared_ptr<Controller> controller(new EditGameController(
280 board, variant, startingPos));
281 createTab(board, controller,
282 QString("Editing %1 game").arg(variantName.toLower()));
283 return true;
285 else return false;
289 void MainWindow::editPosition() {
290 //BROKEN
291 #if 0
292 shared_ptr<Controller> controller(new EditPositionController(table(), ChessVariant::info()));
293 m_main->setTabText(m_main->currentIndex(), "Editing chess position");
294 ui().setController(controller);
295 #endif
298 void MainWindow::setupGame(const GameInfo* gameInfo, const PositionInfo& style12) {
299 QString variantCode = gameInfo->variant();
300 VariantInfo* variant = Variant::variant(variantCode);
301 shared_ptr<EditGameController> controller(new EditGameController(
302 table(), variant));
303 Q_ASSERT(style12.relation == PositionInfo::NotMyMove ||
304 style12.relation == PositionInfo::MyMove);
306 // set opponent side
307 int side = (style12.relation == PositionInfo::MyMove ^ style12.turn == 0) ? 0 : 1;
309 if (controller->addICSPlayer(side, style12.gameNumber, m_connection)) {
310 ui().setController(controller);
311 table()->setPlayers(gameInfo->white(), gameInfo->black());
312 m_main->setTabText(m_main->currentIndex(),
313 QString("FICS Game - %1 vs %2").arg(style12.whitePlayer)
314 .arg(style12.blackPlayer));
318 void MainWindow::setupExaminedGame(const GameInfo* /*gameInfo*/, const PositionInfo& style12) {
319 shared_ptr<EditGameController> controller(new EditGameController(
320 table(), ChessVariant::info()));
321 if (controller->setExaminationMode(style12.gameNumber, m_connection)) {
322 table()->setPlayers(Player(style12.whitePlayer, -1),
323 Player(style12.blackPlayer, -1));
324 ui().setController(controller);
325 m_main->setTabText(m_main->currentIndex(),
326 QString("Examining - %1 vs %2").arg(style12.whitePlayer)
327 .arg(style12.blackPlayer));
332 void MainWindow::setupObservedGame(const GameInfo* g, const PositionInfo& style12) {
333 std::auto_ptr<ChessTable> board(new ChessTable);
335 shared_ptr<EditGameController> controller(new EditGameController(
336 board.get(), Variant::variant(g->variant())));
337 if (controller->setObserveMode(style12.gameNumber, m_connection)) {
338 board->setPlayers(Player(style12.whitePlayer, -1),
339 Player(style12.blackPlayer, -1));
340 // ui().setController(controller);
341 //std::cout << "adding tab" << std::endl;
342 createTab(board.get(), controller,
343 QString("Observing - %1 vs %2").arg(style12.whitePlayer)
344 .arg(style12.blackPlayer));
345 board.release();
349 void MainWindow::setupPGN(const QString& s) {
350 PGN pgn(s);
352 std::map<QString, QString>::const_iterator var = pgn.m_tags.find("Variant");
353 VariantInfo *variant;
355 if(var == pgn.m_tags.end())
356 variant = Variant::variant("Chess");
357 else if(!(variant = Variant::variant(var->second))) {
358 std::cout << " --> MainWindow::setupPGN: Error, no such variant " << var->second << std::endl;
359 return;
362 shared_ptr<EditGameController> controller(new EditGameController(
363 table(), variant));
364 ui().setController(controller);
365 controller->loadPGN(pgn);
367 // table()->setPlayers(gameInfo->white(), gameInfo->black());
368 // m_main->setTabText(m_main->currentIndex(),
369 // QString("FICS Game - %1 vs %2").arg(style12.whitePlayer)
370 // .arg(style12.blackPlayer));
373 bool MainWindow::openFile(const QString& filename) {
374 QFileInfo info(filename);
376 if(info.isDir()) {
377 KMessageBox::sorry(this, i18n("You have specified a folder"), i18n("Error"));
378 return false;
381 if(!info.exists() || !info.isFile()) {
382 KMessageBox::sorry(this, i18n("The specified file does not exist"), i18n("Error"));
383 return false;
386 QFile file(filename);
388 if(!file.open(QIODevice::ReadOnly)) {
389 KMessageBox::sorry(this, i18n("You do not have read permission to this file."), i18n("Error"));
390 return false;
393 QTextStream stream(&file);
394 QTextCodec *codec;
395 codec = QTextCodec::codecForLocale();
396 stream.setCodec(codec);
398 setupPGN(stream.readAll());
399 //ui().pgnPaste(stream.readAll());
400 return true;
403 void MainWindow::loadGame() {
404 KUrl url = KFileDialog::getOpenUrl(KUrl(), "*.pgn", this, i18n("Open PGN file"));
406 if(url.isEmpty())
407 return;
409 QString tmp_file;
410 if (KIO::NetAccess::download(url, tmp_file, this)) {
411 openFile(tmp_file);
412 KIO::NetAccess::removeTempFile(tmp_file);
414 else
415 KMessageBox::error(this, KIO::NetAccess::lastErrorString());
418 void MainWindow::createConnection(const QString& username, const QString& password,
419 const QString& host, quint16 port,
420 const QString& timeseal, const QString& timeseal_cmd) {
421 m_connection = shared_ptr<ICSConnection>(new ICSConnection);
423 connect(m_connection.get(), SIGNAL(established()), this, SLOT(onEstablishConnection()));
424 connect(m_connection.get(), SIGNAL(hostLookup()), this, SLOT(onHostLookup()));
425 connect(m_connection.get(), SIGNAL(hostFound()), this, SLOT(onHostFound()));
427 connect(m_connection.get(), SIGNAL(receivedLine(QString, int)), console, SLOT(displayText(QString, int)));
428 connect(m_connection.get(), SIGNAL(receivedText(QString, int)), console, SLOT(displayText(QString, int)));
430 connect(console, SIGNAL(receivedInput(const QString&)), m_connection.get(), SLOT(sendText(const QString&)));
431 connect(console, SIGNAL(notify()), this, SLOT(flash()));
433 connect(m_connection.get(), SIGNAL(loginPrompt()), this, SLOT(sendLogin()));
434 connect(m_connection.get(), SIGNAL(registeredNickname()), this, SLOT(sendBlankPassword()));
435 connect(m_connection.get(), SIGNAL(prompt()), this, SLOT(prompt()));
438 connect(m_connection.get(), SIGNAL(creatingExaminedGame(const GameInfo*, const PositionInfo&)),
439 this, SLOT(setupExaminedGame(const GameInfo*, const PositionInfo&)));
440 connect(m_connection.get(), SIGNAL(endingExaminedGame(int)), this, SLOT(cleanupGame()));
442 connect(m_connection.get(), SIGNAL(creatingObservedGame(const GameInfo*, const PositionInfo&)),
443 this, SLOT(setupObservedGame(const GameInfo*, const PositionInfo&)));
444 connect(m_connection.get(), SIGNAL(endingObservedGame(int)), this, SLOT(cleanupGame()));
447 connect(m_connection.get(), SIGNAL(creatingGame(const GameInfo*, const PositionInfo&)),
448 this, SLOT(setupGame(const GameInfo*, const PositionInfo&)));
449 connect(m_connection.get(), SIGNAL(endingGame(const QString&, const QString&)),
450 this, SLOT(cleanupGame(const QString&, const QString&)));
451 connect(m_connection.get(), SIGNAL(notification()), this, SLOT(flash()));
453 m_connection->establish(host.toAscii().constData(), port, timeseal, timeseal_cmd);
454 console->show();
456 // send username / password combination
457 if (!username.isEmpty()) {
458 m_connection->sendText(username);
459 m_connection->sendText(password);
462 quickConnectDialog.reset();
465 void MainWindow::icsConnect() {
466 quickConnectDialog = shared_ptr<QConnect>(new QConnect(this));
467 connect(quickConnectDialog.get(),
468 SIGNAL(acceptConnection(const QString&,
469 const QString&,
470 const QString&,
471 quint16,
472 const QString&,
473 const QString&)),
474 this,
475 SLOT(createConnection(const QString&,
476 const QString&,
477 const QString&,
478 quint16,
479 const QString&,
480 const QString&)));
481 quickConnectDialog->show();
484 void MainWindow::destroyConnection() {
485 m_connection.reset();
488 void MainWindow::icsDisconnect() {
489 destroyConnection();
490 console->hide();
491 console->clear();
494 void MainWindow::testConnect() {
495 Settings s_ics = settings.group("ics");
496 if (s_ics["username"]) {
497 QString username = s_ics["username"].value<QString>();
498 QString password = (s_ics["password"] | "");
499 QString host = (s_ics["icsHost"] | "freechess.org");
500 quint16 port = (s_ics["icsPort"] | 5000);
501 createConnection(username, password, host, port, QString(), QString() );
503 else icsConnect();
507 void MainWindow::onEstablishConnection() {
508 // std::cout << "connection established" << std::endl;
511 void MainWindow::onConnectionError(int ) {
512 // std::cout << "connection error (" << code << ")" << std::endl;
515 void MainWindow::onHostLookup() {
516 // std::cout << "hostLookup..." << std::flush;
519 void MainWindow::onHostFound() {
520 // std::cout << "found" << std::endl;
524 void MainWindow::sendLogin() {
525 // std::cout << "sending username" << std::endl;
526 // connection->sendText(connection->username());
529 void MainWindow::sendBlankPassword() {
530 std::cout << "sending blank password" << std::endl;
531 m_connection->sendText("");
534 void MainWindow::prompt() {
535 if (!m_connection->initialized()) {
536 m_connection->startup();
537 m_connection->setInitialized();
541 void MainWindow::newGame() {
542 AbstractPosition::Ptr pos = ui().position();
543 scoped_ptr<NewGame> dialog(new NewGame(this, pos));
544 if (dialog->exec() == QDialog::Accepted) {
545 if(dialog->isCustom()) {
546 VariantInfo *vi = Variant::variant(dialog->variant());
547 std::cout << "vi[" << dialog->variant() << "] = " << vi << std::endl;
548 pos = vi->createCustomPosition(dialog->customOptions());
549 pos->setup();
551 else if (!dialog->playFromCurrent())
552 pos = AbstractPosition::Ptr();
553 if (!newGame(dialog->variant(), pos))
554 QMessageBox::information(this, tr("Error"), tr("Variant not implemented, yet"));
558 void MainWindow::quit() {
559 delete this;
562 void MainWindow::flipView() {
563 table()->flip();
566 void MainWindow::toggleConsole() {
567 if (console_dock->isVisible())
568 console_dock->hide();
569 else {
570 console_dock->show();
571 console_dock->setFocus(Qt::MouseFocusReason
572 /*Qt::ActiveWindowFocusReason*/ /*Qt::OtherFocusReason*/);
576 void MainWindow::toggleMoveList() {
577 if (movelist_dock->isVisible())
578 movelist_dock->hide();
579 else {
580 movelist_dock->show();
581 movelist_dock->setFocus(Qt::OtherFocusReason);
586 void MainWindow::displayMessage(const QString& msg) {
587 Q_UNUSED(msg); // TODO
588 // statusBar()->message(msg, 2000);
591 void MainWindow::displayErrorMessage(ErrorCode code) {
592 switch(code) {
593 case InvalidMove:
594 displayMessage(tr("Illegal move"));
595 break;
599 void MainWindow::flash() {
600 if( !isAncestorOf(QApplication::focusWidget()) )
601 Flash::flash(this);
604 #if 0
605 void MainWindow::prefHighlight() {
606 PrefHighlight dialog;
607 int result = dialog.exec();
608 if (result == QDialog::Accepted) {
609 dialog.apply();
612 #endif
614 void MainWindow::prefClock() {
615 PrefClock dialog;
616 int result = dialog.exec();
617 if (result == QDialog::Accepted) {
618 dialog.apply();
619 table()->clock()->reload();
623 void MainWindow::preferences() {
624 Preferences dialog;
625 int result = dialog.exec();
626 if (result == QDialog::Accepted)
627 dialog.apply();
632 #include "mainwindow.moc"