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.
11 #include "mainwindow.h"
12 #include <boost/scoped_ptr.hpp>
14 #include <kstandardaction.h>
15 #include <kactioncollection.h>
19 #include <kfiledialog.h>
20 #include <kio/netaccess.h>
21 #include <kmessagebox.h>
23 #include <QKeySequence>
24 #include <QStackedWidget>
25 #include <QDockWidget>
26 #include <QCloseEvent>
27 #include <QTextStream>
30 #include "actioncollection.h"
31 #include "chesstable.h"
37 #include "controllers/editgame.h"
38 #include "controllers/editposition.h"
39 #include "engineinfo.h"
40 #include "movelist_table.h"
41 #include "icsconnection.h"
43 #include "mastersettings.h"
46 #include "pgnparser.h"
47 #include "pref_highlight.h"
48 #include "pref_preferences.h"
49 #include "tabwidget.h"
52 using namespace boost
;
54 MainWindow::~MainWindow() {
59 MainWindow::MainWindow(const QString
& variant
)
61 setObjectName("tagua_main");
62 m_main
= new TabWidget(this);
63 m_main
->setTabBarHidden(true);
64 setCentralWidget(m_main
);
66 m_movelist_stack
= new QStackedWidget
;
68 connect(m_main
, SIGNAL(currentChanged(int)),
69 this, SLOT(changeTab(int)));
70 connect(m_main
, SIGNAL(closeTab()),
71 this, SLOT(closeTab()));
73 movelist_dock
= new QDockWidget(this);
74 movelist_dock
->setWidget(m_movelist_stack
);
75 movelist_dock
->setWindowTitle(i18n("Move list"));
76 movelist_dock
->setObjectName("move_list");
77 addDockWidget(Qt::LeftDockWidgetArea
, movelist_dock
, Qt::Vertical
);
78 movelist_dock
->show();
80 ChessTable
* board
= new ChessTable
;
84 console_dock
= new QDockWidget(this);
85 console
= new Console(0, i18n("FICS Connection"));
86 console_dock
->setWidget(console
);
87 console_dock
->setFocusProxy(console
);
88 console_dock
->setWindowTitle(i18n("Console"));
89 console_dock
->setObjectName("console");
90 addDockWidget(Qt::BottomDockWidgetArea
, console_dock
, Qt::Horizontal
);
91 console_dock
->setWindowFlags(console_dock
->windowFlags() & ~Qt::WindowStaysOnTopHint
);
94 connect(board
, SIGNAL(error(ErrorCode
)), this, SLOT(displayErrorMessage(ErrorCode
)));
95 //BROKEN connect(board->clock(), SIGNAL(labelClicked(int)), &ui(), SLOT(setTurn(int)));
97 // start in edit game mode
98 newGame(variant
, AbstractPosition::Ptr());
103 updateVariantActions();
106 ChessTable
* MainWindow::table() {
107 return qobject_cast
<ChessTable
*>(m_main
->currentWidget());
110 KAction
* MainWindow::installRegularAction(const QString
& name
, const KIcon
& icon
, const QString
& text
, QObject
* obj
, const char* slot
) {
111 KAction
* temp
= new KAction(icon
, text
, this);
112 actionCollection()->addAction(name
, temp
);
113 connect(temp
, SIGNAL(triggered(bool)), obj
, slot
);
117 void MainWindow::setupEngineMenu() {
118 QMenu
* engine_menu
= 0;
119 SettingArray engine_settings
= settings().group("engines").array("engine");
120 foreach (Settings s
, engine_settings
) {
122 // this way the menu is created only if there is at least one engine
123 engine_menu
= menuBar()->addMenu(i18n("E&ngines"));
127 EngineDetails engine_details
;
128 s
["name"] >> engine_details
.name
;
129 s
["path"] >> engine_details
.path
;
130 engine_details
.type
= EngineDetails::typeFromName(s
["type"].value
<QString
>());
132 s
["work-path"] >> engine_details
.workPath
;
134 kDebug() << "creating engine " << engine_details
.name
<< endl
;
135 EngineInfo
* engine
= new EngineInfo(engine_details
, ui());
137 m_engines
.push_back(engine
);
139 QMenu
* menu
= engine_menu
->addMenu(engine_details
.name
);
142 KAction
* play_white
= new KAction(i18n("Play as &white"), this);
143 play_white
->setCheckable(true);
144 connect(play_white
, SIGNAL(triggered()), engine
, SLOT(playAsWhite()));
145 menu
->addAction(play_white
);
148 KAction
* play_black
= new KAction(i18n("Play as &black"), this);
149 play_black
->setCheckable(true);
150 connect(play_black
, SIGNAL(triggered()), engine
, SLOT(playAsBlack()));
151 menu
->addAction(play_black
);
153 #if 0 // disable analysis for the moment
155 KAction
* analyze
= new KAction(i18n("&Analyze"), this);
156 analyze
->setCheckable(true);
157 connect(analyze
, SIGNAL(triggered()), engine
, SLOT(analyze()));
158 menu
->addAction(analyze
);
164 void MainWindow::setupActions() {
165 KStandardAction::openNew(this, SLOT(newGame()), actionCollection());
166 KStandardAction::open(this, SLOT(loadGame()), actionCollection());
167 KStandardAction::quit(this, SLOT(quit()), actionCollection());
169 installRegularAction("back", KIcon("go-previous"), i18n("&Back"), &ui(), SLOT(back()));
170 installRegularAction("forward", KIcon("go-next"), i18n("&Forward"), &ui(), SLOT(forward()));
171 installRegularAction("begin", KIcon("go-first"), i18n("Be&gin"), &ui(), SLOT(gotoFirst()));
172 installRegularAction("end", KIcon("go-last"), i18n("&End"), &ui(), SLOT(gotoLast()));
173 installRegularAction("connect", KIcon("connection-established"), i18n("&Connect"), this, SLOT(icsConnect()));
174 installRegularAction("disconnect", KIcon("connect-no"), i18n("&Disconnect"), this, SLOT(icsDisconnect()));
176 KStandardAction::undo(&ui(), SLOT(undo()), actionCollection());
177 KStandardAction::redo(&ui(), SLOT(redo()), actionCollection());
179 installRegularAction("pgnCopy", KIcon("edit-copy"), i18n("Copy PGN"), this, SLOT(pgnCopy()));
180 installRegularAction("pgnPaste", KIcon("edit-paste"), i18n("Paste PGN"), this, SLOT(pgnPaste()));
181 installRegularAction("editPosition", KIcon("edit"), i18n("&Edit position"), this, SLOT(editPosition()));
182 installRegularAction("clearBoard", KIcon("edit-delete"), i18n("&Clear board"), &ui(), SLOT(clearBoard()));
183 installRegularAction("setStartingPosition", KIcon("contents"), i18n("&Set starting position"),
184 &ui(), SLOT(setStartingPosition()));
185 installRegularAction("copyPosition", KIcon(), i18n("&Copy position"), &ui(), SLOT(copyPosition()));
186 installRegularAction("pastePosition", KIcon(), i18n("&Paste position"), &ui(), SLOT(pastePosition()));
187 installRegularAction("flip", KIcon("rotate"), i18n("&Flip view"), this, SLOT(flipView()));
188 installRegularAction("toggleConsole", KIcon("openterm"), i18n("Toggle &console"), this, SLOT(toggleConsole()));
189 installRegularAction("toggleMoveList", KIcon("view_text"), i18n("Toggle &move list"), this, SLOT(toggleMoveList()));
190 installRegularAction("configure", KIcon("configure"), i18n("&Configure Tagua..."), this, SLOT(preferences()));
193 void MainWindow::updateVariantActions() {
194 ActionCollection
* variant_actions
= m_ui
.variantActions();
195 unplugActionList("variantActions");
196 if (variant_actions
) {
197 plugActionList("variantActions", variant_actions
->actions());
200 WARNING("No variant actions");
204 void MainWindow::readSettings() { }
205 void MainWindow::writeSettings() { }
207 void MainWindow::closeEvent(QCloseEvent
* e
) {
214 void MainWindow::keyPressEvent(QKeyEvent
* event
) {
215 if (event
->key() == Qt::Key_F5
) {
216 ui().createCtrlAction();
220 void MainWindow::keyReleaseEvent(QKeyEvent
* event
) {
221 if (event
->key() == Qt::Key_F5
) {
222 ui().destroyCtrlAction();
226 void MainWindow::changeTab(int index
) {
227 std::cout
<< "changing tab, widget = " << m_main
->currentWidget() << std::endl
;
228 m_ui
.setCurrentTab(m_main
->currentWidget());
229 m_movelist_stack
->setCurrentIndex(index
);
230 updateVariantActions();
233 void MainWindow::closeTab() {
234 if (m_main
->count() > 1) {
235 int old_index
= m_main
->currentIndex();
236 ChessTable
* old_board
= table();
238 int new_index
= old_index
- 1;
240 new_index
= old_index
+ 1;
241 m_main
->setCurrentIndex(new_index
);
243 m_main
->removeTab(old_index
);
244 m_movelist_stack
->removeWidget(m_movelist_stack
->widget(old_index
));
245 m_ui
.removeController(old_board
);
247 if (m_main
->count() <= 1) {
248 m_main
->setTabBarHidden(true);
251 #if 0 // this doesn't work... why?
252 ChessTable
* old_board
= table();
253 m_ui
.removeController(old_board
);
254 m_movelist_stack
->removeWidget(
255 m_movelist_stack
->widget(m_main
->currentIndex()));
256 m_main
->removeTab(m_main
->currentIndex());
260 if (m_main
->count() <= 1) {
261 m_main
->hideTabBar();
264 // update ui controller (just in case...)
265 m_ui
.setCurrentTab(m_main
->currentWidget());
270 void MainWindow::createTab(ChessTable
* board
, const shared_ptr
<Controller
>& controller
,
271 const QString
& caption
, int index
) {
273 index
= m_main
->addTab(board
, caption
);
275 m_main
->insertTab(index
, board
, caption
);
277 m_ui
.addController(board
, controller
);
278 m_ui
.setCurrentTab(board
);
279 m_movelist_stack
->addWidget(board
->moveListTable());
281 m_main
->setCurrentIndex(index
);
282 m_movelist_stack
->setCurrentIndex(index
);
284 if (m_main
->count() > 1) m_main
->setTabBarHidden(false);
286 std::cout
<< "created tab " << board
<< std::endl
;
290 void MainWindow::cleanupGame(const QString
& what
, const QString
& result
) {
297 void MainWindow::cleanupGame() {
303 bool MainWindow::newGame(const QString
& variantName
, AbstractPosition::Ptr startingPos
) {
304 VariantPtr variant
= Variants::instance().get(variantName
);
306 WARNING("no variant " << variantName
<< " found");
307 variant
= Variants::instance().get("chess");
311 ChessTable
* board
= new ChessTable
;
312 shared_ptr
<Controller
> controller(new EditGameController(
313 board
, variant
, startingPos
));
314 createTab(board
, controller
,
315 QString("Editing %1 game").arg(variant
->name().toLower()));
319 ERROR("Could not find the chess variant");
326 void MainWindow::editPosition() {
329 shared_ptr
<Controller
> controller(new EditPositionController(table(), ChessVariant::info()));
330 m_main
->setTabText(m_main
->currentIndex(), "Editing chess position");
331 ui().setController(controller
);
335 void MainWindow::setupGame(const GameInfo
* gameInfo
, const PositionInfo
& style12
) {
336 QString variantCode
= gameInfo
->variant();
337 VariantPtr variant
= Variants::instance().get(variantCode
);
338 shared_ptr
<EditGameController
> controller(new EditGameController(
340 Q_ASSERT(style12
.relation
== PositionInfo::NotMyMove
||
341 style12
.relation
== PositionInfo::MyMove
);
344 int side
= (style12
.relation
== PositionInfo::MyMove
^ style12
.turn
== 0) ? 0 : 1;
346 if (controller
->addICSPlayer(side
, style12
.gameNumber
, m_connection
)) {
347 ui().setController(controller
);
348 table()->setPlayers(gameInfo
->white(), gameInfo
->black());
349 m_main
->setTabText(m_main
->currentIndex(),
350 QString("FICS Game - %1 vs %2").arg(style12
.whitePlayer
)
351 .arg(style12
.blackPlayer
));
355 void MainWindow::setupExaminedGame(const GameInfo
* gameInfo
, const PositionInfo
& style12
) {
356 shared_ptr
<EditGameController
> controller(
357 new EditGameController(table(), Variants::instance().get(gameInfo
->variant())));
358 if (controller
->setExaminationMode(style12
.gameNumber
, m_connection
)) {
359 table()->setPlayers(Player(style12
.whitePlayer
, -1),
360 Player(style12
.blackPlayer
, -1));
361 ui().setController(controller
);
362 m_main
->setTabText(m_main
->currentIndex(),
363 QString("Examining - %1 vs %2").arg(style12
.whitePlayer
)
364 .arg(style12
.blackPlayer
));
369 void MainWindow::setupObservedGame(const GameInfo
* g
, const PositionInfo
& style12
) {
370 std::auto_ptr
<ChessTable
> board(new ChessTable
);
372 shared_ptr
<EditGameController
> controller(new EditGameController(
373 board
.get(), Variants::instance().get(g
->variant())));
374 if (controller
->setObserveMode(style12
.gameNumber
, m_connection
)) {
375 board
->setPlayers(Player(style12
.whitePlayer
, -1),
376 Player(style12
.blackPlayer
, -1));
377 // ui().setController(controller);
378 //std::cout << "adding tab" << std::endl;
379 createTab(board
.get(), controller
,
380 QString("Observing - %1 vs %2").arg(style12
.whitePlayer
)
381 .arg(style12
.blackPlayer
));
386 void MainWindow::setupPGN(const QString
& s
) {
389 std::map
<QString
, QString
>::const_iterator var
= pgn
.m_tags
.find("Variant");
392 if (var
== pgn
.m_tags
.end()) {
393 variant
= Variants::instance().get("chess");
395 else if (!(variant
= Variants::instance().get(var
->second
))) {
396 std::cout
<< " --> MainWindow::setupPGN: Error, no such variant " << var
->second
<< std::endl
;
400 shared_ptr
<EditGameController
> controller(new EditGameController(
402 ui().setController(controller
);
403 controller
->loadPGN(pgn
);
405 // table()->setPlayers(gameInfo->white(), gameInfo->black());
406 // m_main->setTabText(m_main->currentIndex(),
407 // QString("FICS Game - %1 vs %2").arg(style12.whitePlayer)
408 // .arg(style12.blackPlayer));
411 bool MainWindow::openFile(const QString
& filename
) {
412 QFileInfo
info(filename
);
415 KMessageBox::sorry(this, i18n("You have specified a folder"), i18n("Error"));
419 if(!info
.exists() || !info
.isFile()) {
420 KMessageBox::sorry(this, i18n("The specified file does not exist"), i18n("Error"));
424 QFile
file(filename
);
426 if(!file
.open(QIODevice::ReadOnly
)) {
427 KMessageBox::sorry(this, i18n("You do not have read permission to this file."), i18n("Error"));
431 QTextStream
stream(&file
);
433 codec
= QTextCodec::codecForLocale();
434 stream
.setCodec(codec
);
436 setupPGN(stream
.readAll());
437 //ui().pgnPaste(stream.readAll());
441 void MainWindow::loadGame() {
442 KUrl url
= KFileDialog::getOpenUrl(KUrl(), "*.pgn", this, i18n("Open PGN file"));
448 if (KIO::NetAccess::download(url
, tmp_file
, this)) {
450 KIO::NetAccess::removeTempFile(tmp_file
);
453 KMessageBox::error(this, KIO::NetAccess::lastErrorString());
456 void MainWindow::createConnection(const QString
& username
, const QString
& password
,
457 const QString
& host
, quint16 port
,
458 const QString
& timeseal
, const QString
& timeseal_cmd
) {
459 m_connection
= shared_ptr
<ICSConnection
>(new ICSConnection
);
461 connect(m_connection
.get(), SIGNAL(established()), this, SLOT(onEstablishConnection()));
462 connect(m_connection
.get(), SIGNAL(hostLookup()), this, SLOT(onHostLookup()));
463 connect(m_connection
.get(), SIGNAL(hostFound()), this, SLOT(onHostFound()));
465 connect(m_connection
.get(), SIGNAL(receivedLine(QString
, int)), console
, SLOT(displayText(QString
, int)));
466 connect(m_connection
.get(), SIGNAL(receivedText(QString
, int)), console
, SLOT(displayText(QString
, int)));
468 connect(console
, SIGNAL(receivedInput(const QString
&)), m_connection
.get(), SLOT(sendText(const QString
&)));
469 connect(console
, SIGNAL(notify()), this, SLOT(flash()));
471 connect(m_connection
.get(), SIGNAL(loginPrompt()), this, SLOT(sendLogin()));
472 connect(m_connection
.get(), SIGNAL(registeredNickname()), this, SLOT(sendBlankPassword()));
473 connect(m_connection
.get(), SIGNAL(prompt()), this, SLOT(prompt()));
476 connect(m_connection
.get(), SIGNAL(creatingExaminedGame(const GameInfo
*, const PositionInfo
&)),
477 this, SLOT(setupExaminedGame(const GameInfo
*, const PositionInfo
&)));
478 connect(m_connection
.get(), SIGNAL(endingExaminedGame(int)), this, SLOT(cleanupGame()));
480 connect(m_connection
.get(), SIGNAL(creatingObservedGame(const GameInfo
*, const PositionInfo
&)),
481 this, SLOT(setupObservedGame(const GameInfo
*, const PositionInfo
&)));
482 connect(m_connection
.get(), SIGNAL(endingObservedGame(int)), this, SLOT(cleanupGame()));
485 connect(m_connection
.get(), SIGNAL(creatingGame(const GameInfo
*, const PositionInfo
&)),
486 this, SLOT(setupGame(const GameInfo
*, const PositionInfo
&)));
487 connect(m_connection
.get(), SIGNAL(endingGame(const QString
&, const QString
&)),
488 this, SLOT(cleanupGame(const QString
&, const QString
&)));
489 connect(m_connection
.get(), SIGNAL(notification()), this, SLOT(flash()));
491 m_connection
->establish(host
.toAscii().constData(), port
, timeseal
, timeseal_cmd
);
494 // send username / password combination
495 if (!username
.isEmpty()) {
496 m_connection
->sendText(username
);
497 m_connection
->sendText(password
);
500 quickConnectDialog
.reset();
503 void MainWindow::icsConnect() {
504 quickConnectDialog
= shared_ptr
<QConnect
>(new QConnect(this));
505 connect(quickConnectDialog
.get(),
506 SIGNAL(acceptConnection(const QString
&,
513 SLOT(createConnection(const QString
&,
519 quickConnectDialog
->show();
522 void MainWindow::destroyConnection() {
523 m_connection
.reset();
526 void MainWindow::icsDisconnect() {
532 void MainWindow::testConnect() {
533 Settings s_ics
= settings().group("ics");
534 if (s_ics
["username"]) {
535 QString username
= s_ics
["username"].value
<QString
>();
536 QString password
= (s_ics
["password"] | "");
537 QString host
= (s_ics
["icsHost"] | "freechess.org");
538 quint16 port
= (s_ics
["icsPort"] | 5000);
539 createConnection(username
, password
, host
, port
, QString(), QString() );
545 void MainWindow::onEstablishConnection() {
546 // std::cout << "connection established" << std::endl;
549 void MainWindow::onConnectionError(int ) {
550 // std::cout << "connection error (" << code << ")" << std::endl;
553 void MainWindow::onHostLookup() {
554 // std::cout << "hostLookup..." << std::flush;
557 void MainWindow::onHostFound() {
558 // std::cout << "found" << std::endl;
562 void MainWindow::sendLogin() {
563 // std::cout << "sending username" << std::endl;
564 // connection->sendText(connection->username());
567 void MainWindow::sendBlankPassword() {
568 std::cout
<< "sending blank password" << std::endl
;
569 m_connection
->sendText("");
572 void MainWindow::prompt() {
573 if (!m_connection
->initialized()) {
574 m_connection
->startup();
575 m_connection
->setInitialized();
579 void MainWindow::newGame() {
580 AbstractPosition::Ptr pos
= ui().position();
581 scoped_ptr
<NewGame
> dialog(new NewGame(this, pos
));
582 if (dialog
->exec() == QDialog::Accepted
) {
583 if(dialog
->isCustom()) {
584 VariantPtr vi
= Variants::instance().get(dialog
->variant());
585 std::cout
<< "vi[" << dialog
->variant() << "] = " << vi
<< std::endl
;
586 pos
= vi
->createCustomPosition(dialog
->customOptions());
589 else if (!dialog
->playFromCurrent())
590 pos
= AbstractPosition::Ptr();
591 if (!newGame(dialog
->variant(), pos
))
592 QMessageBox::information(this, tr("Error"), tr("Variant not implemented, yet"));
596 void MainWindow::quit() {
600 void MainWindow::flipView() {
604 void MainWindow::toggleConsole() {
605 if (console_dock
->isVisible())
606 console_dock
->hide();
608 console_dock
->show();
609 console_dock
->setFocus(Qt::MouseFocusReason
610 /*Qt::ActiveWindowFocusReason*/ /*Qt::OtherFocusReason*/);
614 void MainWindow::toggleMoveList() {
615 if (movelist_dock
->isVisible())
616 movelist_dock
->hide();
618 movelist_dock
->show();
619 movelist_dock
->setFocus(Qt::OtherFocusReason
);
624 void MainWindow::displayMessage(const QString
& msg
) {
625 Q_UNUSED(msg
); // TODO
626 // statusBar()->message(msg, 2000);
629 void MainWindow::displayErrorMessage(ErrorCode code
) {
632 displayMessage(tr("Illegal move"));
637 void MainWindow::flash() {
638 if( !isAncestorOf(QApplication::focusWidget()) )
643 void MainWindow::prefHighlight() {
644 PrefHighlight dialog
;
645 int result
= dialog
.exec();
646 if (result
== QDialog::Accepted
) {
652 void MainWindow::preferences() {
653 Preferences
dialog(ui().currentVariant());
654 int result
= dialog
.exec();
655 if (result
== QDialog::Accepted
)