1 /****************************************************************************
2 * Copyright (C) 2014 by Jens Nissen jens-chessx@gmx.net *
3 ****************************************************************************/
4
5 #include "board.h"
6 #include "ficsconsole.h"
7 #include "ficsclient.h"
8 #include "partialdate.h"
9 #include "settings.h"
10 #include "tags.h"
11
12 #include "ui_ficsconsole.h"
13
14 #include <QButtonGroup>
15 #include <QCompleter>
16 #include <QMenu>
17 #include <QToolButton>
18 #include <QString>
19 #include <QStringList>
20 #include <QStringListModel>
21
22 using namespace chessx;
23
24 #if defined(_MSC_VER) && defined(_DEBUG)
25 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
26 #define new DEBUG_NEW
27 #endif // _MSC_VER
28
FicsConsole(QWidget * parent,FicsClient * ficsClient)29 FicsConsole::FicsConsole(QWidget *parent, FicsClient* ficsClient) :
30 QWidget(parent),
31 ui(new Ui::FicsConsole),
32 m_ficsClient(ficsClient),
33 gameMode(false),
34 puzzleMode(false),
35 m_bWhiteToMove(true),
36 m_prevTab(TabMessage)
37 {
38 setObjectName("FicsConsole");
39
40 ui->setupUi(this);
41 ui->tabWidget->setCurrentIndex(TabMessage);
42
43 ui->tabWidget->setContextMenuPolicy(Qt::CustomContextMenu);
44
45 connect(ui->tabWidget, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(SlotContextMenu(const QPoint&)));
46 connect(ui->tabWidget, SIGNAL(tabBarClicked(int)), SLOT(SlotTabClicked(int)));
47 connect(ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(SlotTabChanged(int)));
48
49 btgSeek = new QButtonGroup(this);
50 btgSeek->addButton(ui->btLightning);
51 btgSeek->addButton(ui->btBlitz);
52 btgSeek->addButton(ui->btStandard);
53 connect(btgSeek, SIGNAL(buttonClicked(int)), SLOT(SlotSeekTimeChanged(int)));
54
55 connect(ui->textOut, SIGNAL(editingFinished()), SLOT(SendCommand()));
56 connect(m_ficsClient, SIGNAL(receivedMessage(int,QString)), SLOT(HandleMessage(int,QString)), Qt::QueuedConnection);
57 connect(m_ficsClient, SIGNAL(receivedBoard(int, QString)), SLOT(HandleBoard(int, QString)), Qt::QueuedConnection);
58 connect(m_ficsClient, SIGNAL(commandStarted(int)), SLOT(CommandStarted(int)), Qt::QueuedConnection);
59 connect(m_ficsClient, SIGNAL(disconnected()), SLOT(Disconnected()), Qt::QueuedConnection);
60
61 connect(ui->listGames, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(HandleObserveRequest(QListWidgetItem*)));
62 connect(ui->listHistory, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(HandleExamineRequest(QListWidgetItem*)));
63 connect(ui->listRelay, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(HandleRelayRequest(QListWidgetItem*)));
64 connect(ui->listPuzzle, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(HandleTacticsRequest(QListWidgetItem*)));
65 connect(ui->listPlayers, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(HandleHistoryRequest(QTableWidgetItem*)));
66 connect(ui->listSeeks, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(HandleSeekRequest(QListWidgetItem*)));
67
68 connect(ui->listGames, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(CMHandleObserveRequest(const QPoint&)));
69 connect(ui->listHistory, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(CMHandleExamineRequest(const QPoint&)));
70 connect(ui->listRelay, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(CMHandleRelayRequest(const QPoint&)));
71 connect(ui->listPuzzle, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(CMHandleTacticsRequest(const QPoint&)));
72 connect(ui->listPlayers, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(CMHandleHistoryRequest(const QPoint&)));
73 connect(ui->listSeeks, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(CMHandleSeekRequest(const QPoint&)));
74
75 QListWidgetItem* item = new QListWidgetItem(tr("Get Mate"));
76 item->setData(Qt::UserRole, "gm");
77 ui->listPuzzle->addItem(item);
78 item = new QListWidgetItem(tr("Get Study"));
79 item->setData(Qt::UserRole, "gs");
80 ui->listPuzzle->addItem(item);
81 item = new QListWidgetItem(tr("Get Tactics"));
82 item->setData(Qt::UserRole, "gt");
83 ui->listPuzzle->addItem(item);
84
85 QToolButton* button = new QToolButton(this);
86 button->setText(tr("Accept"));
87 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
88 ui->btBoxMessage->addButton(button, QDialogButtonBox::ActionRole);
89 connect(button, SIGNAL(clicked()), SLOT(SlotSendAccept()));
90
91 button = new QToolButton(this);
92 button->setText(tr("Draw"));
93 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
94 ui->btBoxMessage->addButton(button, QDialogButtonBox::ActionRole);
95 connect(button, SIGNAL(clicked()), this, SLOT(SlotSendDraw()));
96
97 button = new QToolButton(this);
98 button->setText(tr("Decline"));
99 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
100 ui->btBoxMessage->addButton(button, QDialogButtonBox::ActionRole);
101 connect(button, SIGNAL(clicked()), this, SLOT(SlotSendDecline()));
102
103 button = new QToolButton(this);
104 button->setText(tr("Abort"));
105 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
106 ui->btBoxMessage->addButton(button, QDialogButtonBox::ActionRole);
107 connect(button, SIGNAL(clicked()), this, SLOT(SlotSendAbort()));
108
109 button = new QToolButton(this);
110 button->setText(tr("Resign"));
111 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
112 ui->btBoxMessage->addButton(button, QDialogButtonBox::ActionRole);
113 connect(button, SIGNAL(clicked()), this, SLOT(SlotSendResign()));
114
115 button = new QToolButton(this);
116 button->setText(tr("Rematch"));
117 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
118 ui->btBoxMessage->addButton(button, QDialogButtonBox::ActionRole);
119 connect(button, SIGNAL(clicked()), SLOT(SlotSendRematch()));
120
121 button = new QToolButton(this);
122 button->setText(tr("Hint"));
123 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
124 ui->btBoxPuzzle->addButton(button, QDialogButtonBox::ActionRole);
125 connect(button, SIGNAL(clicked()), this, SLOT(SlotSendHint()));
126
127 button = new QToolButton(this);
128 button->setText(tr("Cancel"));
129 button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
130 ui->btBoxPuzzle->addButton(button, QDialogButtonBox::ActionRole);
131 connect(button, SIGNAL(clicked()), SLOT(SlotSendUnexamine()));
132
133 connect(ui->btSeek, SIGNAL(clicked()), SLOT(SlotSendSeek()));
134
135 connect(ui->sayMessage, SIGNAL(returnPressed()), SLOT(SlotSayMessage()));
136 connect(ui->btSay, SIGNAL(clicked()), SLOT(SlotSayMessage()));
137
138 connect(ui->btAddNoPlay, SIGNAL(clicked()), SLOT(SlotAddNoPlay()));
139 connect(ui->editNoPlay, SIGNAL(returnPressed()), SLOT(SlotAddNoPlay()));
140 connect(ui->editNoPlay, SIGNAL(textChanged(QString)), SLOT(SlotNoPlayChanged(QString)));
141
142 ui->btAddNoPlay->setDisabled(ui->editNoPlay->text().isEmpty());
143
144 QStringList words;
145 words << "Thanks for the game";
146 QCompleter* completer = new QCompleter(words, this);
147 completer->setCaseSensitivity(Qt::CaseInsensitive);
148 ui->sayMessage->setCompleter(completer);
149
150 ui->seekTime->setValue(AppSettings->getValue("/FICS/minutes").toInt());
151 ui->seekIncrement->setValue(AppSettings->getValue("/FICS/increment").toInt());
152 ui->eloMin->setValue(AppSettings->getValue("/FICS/eloLow").toInt());
153 ui->eloMax->setValue(AppSettings->getValue("/FICS/eloHigh").toInt());
154 }
155
~FicsConsole()156 FicsConsole::~FicsConsole()
157 {
158 delete ui;
159 }
160
161
canUsePremove() const162 bool FicsConsole::canUsePremove() const
163 {
164 return (gameMode && !puzzleMode);
165 }
166
Terminate()167 void FicsConsole::Terminate()
168 {
169 if (m_ficsClient)
170 {
171 disconnect(m_ficsClient, SIGNAL(receivedMessage(int,QString)));
172 disconnect(m_ficsClient, SIGNAL(receivedBoard(int, QString)));
173 disconnect(m_ficsClient, SIGNAL(commandStarted(int)));
174 disconnect(m_ficsClient, SIGNAL(disconnected()));
175 m_ficsClient->exitSession();
176 }
177 }
178
SendCommand()179 void FicsConsole::SendCommand()
180 {
181 if (!m_ficsClient) return;
182 QString line = ui->textOut->text();
183 if (m_ficsClient && !line.isEmpty())
184 {
185 m_ficsClient->sendCommand(line);
186 ui->textOut->clear();
187 }
188 }
189
HandleBoard(int cmd,QString s)190 void FicsConsole::HandleBoard(int cmd, QString s)
191 {
192 QStringList l = s.split(' ');
193 m_bWhiteToMove = (l[C64_COLOR_TO_MOVE]=="W");
194
195 bool handleBoard = true;
196 if (gameMode && !puzzleMode)
197 {
198 Char64Relation relation = static_cast<Char64Relation>(l[C64_GAME_RELATION].toInt());
199 if (m_lastRelation == relation)
200 {
201 handleBoard = false;
202 }
203 else
204 {
205 m_lastRelation = relation;
206 }
207 }
208
209 if (handleBoard)
210 {
211 emit ReceivedBoard(cmd, s);
212 }
213
214 if (cmd == FicsClient::BLKCMD_EXAMINE || cmd == FicsClient::BLKCMD_FORWARD)
215 {
216 m_ficsClient->sendCommand("forward");
217 }
218 if (handleBoard && gameMode)
219 {
220 emit RequestStoredMove();
221 }
222
223 if (gameMode && !puzzleMode)
224 {
225 QString timeWhite = l[C64_REMAINDER_TIME_WHITE];
226 QString timeBlack = l[C64_REMAINDER_TIME_BLACK];
227 QTime tw(0,0,0,0);
228 tw = tw.addSecs(timeWhite.toInt());
229 QTime tb(0,0,0,0);
230 tb = tb.addSecs(timeBlack.toInt());
231 emit FicsShowTime(White, tw.toString("h:mm:ss"));
232 emit FicsShowTime(Black, tb.toString("h:mm:ss"));
233 emit SignalStartTime(m_bWhiteToMove);
234 }
235 }
236
HandleExamineRequest(QListWidgetItem * item)237 void FicsConsole::HandleExamineRequest(QListWidgetItem* item)
238 {
239 if (!m_ficsClient) return;
240
241 SlotSendUnexamine();
242 m_ficsClient->sendCommand("unobserve");
243 puzzleMode = false;
244
245 QString s = item->text();
246
247 int n = s.section(':',0,0).toInt();
248 if (n)
249 {
250 emit RequestNewGame();
251 if (m_lastHistoryPlayer.isEmpty())
252 {
253 m_lastHistoryPlayer = AppSettings->getValue("/FICS/userName").toString();
254 }
255 QString request = "examine " + m_lastHistoryPlayer + " " + QString::number(n);
256 m_ficsClient->sendCommand(request);
257 }
258 }
259
HandleRelayRequest(QListWidgetItem * item)260 void FicsConsole::HandleRelayRequest(QListWidgetItem* item)
261 {
262 if (!m_ficsClient) return;
263 SlotSendUnexamine();
264 m_ficsClient->sendCommand("unobserve");
265 puzzleMode = false;
266
267 QString s = item->text();
268 int n = s.section(' ',0,0).toInt();
269 if (n)
270 {
271 emit RequestNewGame();
272 QString request = "observe " + QString::number(n);
273 m_ficsClient->sendCommand(request);
274 }
275 }
276
HandleTacticsRequest(QListWidgetItem * item)277 void FicsConsole::HandleTacticsRequest(QListWidgetItem* item)
278 {
279 if (!m_ficsClient) return;
280 SlotSendUnexamine();
281 m_ficsClient->sendCommand("unobserve");
282
283 QString cmd = item->data(Qt::UserRole).toString();
284
285 QString request = "tell puzzlebot " + cmd;
286 ui->listPuzzlebotMessages->clear();
287 ui->listPuzzlebotMessages->addItem("Requesting puzzle...");
288 puzzleMode = true;
289 emit RequestNewGame();
290 emit RequestAddTag(TagNameDate, PartialDate::today().asString());
291 if (AppSettings->getValue("/Board/noHints").toBool())
292 {
293 emit RequestGameMode(true);
294 }
295 m_ficsClient->sendCommand(request);
296 }
297
HandleHistoryRequest(QTableWidgetItem * item)298 void FicsConsole::HandleHistoryRequest(QTableWidgetItem* item)
299 {
300 if (!m_ficsClient) return;
301 if (!item->column()) return;
302
303 QString player = item->text();
304 player.remove(QRegExp("\\([^\\)]*\\)"));
305 if (!player.isEmpty())
306 {
307 m_lastHistoryPlayer = player;
308 QString request = "history " + player;
309 m_ficsClient->sendCommand(request);
310
311 QString request2 = "finger " + player;
312 m_ficsClient->sendCommand(request2);
313 }
314 }
315
HandleSeekRequest(QListWidgetItem * item)316 void FicsConsole::HandleSeekRequest(QListWidgetItem* item)
317 {
318 if (!m_ficsClient) return;
319
320 QString s = item->text();
321 QRegExp play("\\\"(play [^\\\"]*)");
322 if (play.indexIn(s) >= 0)
323 {
324 QString seek = play.cap(1);
325 SlotSendUnexamine();
326 m_ficsClient->sendCommand("unobserve");
327 m_ficsClient->sendCommand(seek);
328 }
329 }
330
SendMove(QString m)331 void FicsConsole::SendMove(QString m)
332 {
333 if (gameMode || puzzleMode)
334 {
335 m_ficsClient->sendCommand(m);
336 }
337 }
338
SlotSeekTimeChanged(int)339 void FicsConsole::SlotSeekTimeChanged(int)
340 {
341 ui->listSeeks->clear();
342 m_ficsClient->sendCommand("sought");
343 }
344
DecrementTime(QString s) const345 QString FicsConsole::DecrementTime(QString s) const
346 {
347 QString result;
348 QTime t = QTime::fromString(s,"h:m:ss");
349
350 if (t.isValid())
351 {
352 t = t.addSecs(-1);
353 result = t.toString("h:mm:ss");
354 }
355 else
356 {
357 t = QTime::fromString(s,"m:ss");
358 if (t.isValid())
359 {
360 if (abs(t.secsTo(QTime(0,0,0,0)))>1)
361 {
362 t = t.addSecs(-1);
363 }
364 else
365 {
366 t = QTime(0,0,0,0);
367 }
368 result = t.toString("m:ss");
369 }
370 }
371 return result;
372 }
373
SlotSendAccept()374 void FicsConsole::SlotSendAccept()
375 {
376 m_ficsClient->sendCommand("accept");
377 }
378
SlotSendDraw()379 void FicsConsole::SlotSendDraw()
380 {
381 m_ficsClient->sendCommand("draw");
382 }
383
SlotSendDecline()384 void FicsConsole::SlotSendDecline()
385 {
386 m_ficsClient->sendCommand("decline");
387 }
388
SlotSendAbort()389 void FicsConsole::SlotSendAbort()
390 {
391 m_ficsClient->sendCommand("abort");
392 }
393
SlotSendResign()394 void FicsConsole::SlotSendResign()
395 {
396 m_ficsClient->sendCommand("resign");
397 }
398
SlotSendHint()399 void FicsConsole::SlotSendHint()
400 {
401 m_ficsClient->sendCommand("tell puzzlebot hint");
402 }
403
SlotSendUnexamine()404 void FicsConsole::SlotSendUnexamine()
405 {
406 if (gameMode)
407 {
408 emit RequestGameMode(false);
409 }
410 m_ficsClient->sendCommand("unexamine");
411 }
412
SlotSendRematch()413 void FicsConsole::SlotSendRematch()
414 {
415 if (!gameMode)
416 {
417 m_ficsClient->sendCommand("rematch");
418 }
419 }
420
SlotSendSeek()421 void FicsConsole::SlotSendSeek()
422 {
423 int t = ui->seekTime->value();
424 int inc = ui->seekIncrement->value();
425 int from = ui->eloMin->value();
426 int to = ui->eloMax->value();
427 QString color;
428 if (ui->cbColor->currentIndex() == 0)
429 {
430 color = " w";
431 }
432 else if (ui->cbColor->currentIndex() == 1)
433 {
434 color = " b";
435 }
436 if ((t || inc) && (from<=to))
437 {
438 QString seek = QString("seek %1 %2 %3%4 %5-%6 ")
439 .arg(t)
440 .arg(inc)
441 .arg(ui->cbRated->currentIndex() ? "unrated" : "rated", color)
442 .arg(from)
443 .arg(to);
444 QListWidgetItem* item = new QListWidgetItem(seek);
445 item->setForeground(QBrush(Qt::gray));
446 ui->listSeeks->addItem(item);
447 m_ficsClient->sendCommand(seek);
448 }
449 }
450
SlotSayMessage()451 void FicsConsole::SlotSayMessage()
452 {
453 QString msg = ui->sayMessage->text();
454 m_ficsClient->sendCommand(QString("say %1").arg(msg));
455 ui->sayMessage->clear();
456 ui->textIn->appendHtml(QString("<i>%1</i>").arg(msg));
457 UpdateSayCompleter(msg);
458 }
459
UpdateSayCompleter(QString msg)460 void FicsConsole::UpdateSayCompleter(QString msg)
461 {
462 QCompleter* completer = ui->sayMessage->completer();
463 QStringListModel* model = qobject_cast<QStringListModel*>(completer->model());
464 QStringList words = model->stringList();
465 if (!words.contains(msg))
466 {
467 words.append(msg);
468 model->setStringList(words);
469 ui->sayMessage->setCompleter(completer);
470 }
471 }
472
playerColor() const473 Color FicsConsole::playerColor() const
474 {
475 if (gameMode)
476 {
477 return m_bPlayerIsBlack ? Black : White;
478 }
479 return NoColor;
480 }
481
SlotAddNoPlay()482 void FicsConsole::SlotAddNoPlay()
483 {
484 QString msg = ui->editNoPlay->text();
485 m_ficsClient->sendCommand(QString("+noplay %1").arg(msg));
486 ui->editNoPlay->clear();
487 ui->listNoPlay->clear();
488 m_ficsClient->sendCommand("=noplay");
489 }
490
SlotNoPlayChanged(const QString & s)491 void FicsConsole::SlotNoPlayChanged(const QString& s)
492 {
493 ui->btAddNoPlay->setDisabled(s.isEmpty());
494 }
495
SlotContextMenu(const QPoint & pos)496 void FicsConsole::SlotContextMenu(const QPoint &pos)
497 {
498 QWidget* w = ui->tabWidget->childAt(pos);
499 QListView* v = nullptr;
500 if (w)
501 {
502 v = qobject_cast<QListView*>(w->parentWidget());
503 }
504
505 if (!v)
506 {
507 QMenu headerMenu;
508 QAction* closeFics = headerMenu.addAction(tr("Disconnect"));
509 closeFics->setDisabled(gameMode);
510 QAction* selectedItem = headerMenu.exec(mapToGlobal(pos));
511 if (!gameMode && (selectedItem == closeFics))
512 {
513 emit RequestCloseFICS();
514 }
515 }
516 }
517
SlotGameModeChanged(bool newMode)518 void FicsConsole::SlotGameModeChanged(bool newMode)
519 {
520 gameMode = newMode;
521 }
522
HandleObserveRequest(QListWidgetItem * item)523 void FicsConsole::HandleObserveRequest(QListWidgetItem* item)
524 {
525 if (!m_ficsClient) return;
526 SlotSendUnexamine();
527 m_ficsClient->sendCommand("unobserve");
528 puzzleMode = false;
529
530 QString s = item->text();
531 int n = s.section(' ',0,0).toInt(); // The observer id is right at the start of the string
532 if (n)
533 {
534 emit RequestNewGame();
535 QString request = "observe " + QString::number(n);
536 m_ficsClient->sendCommand(request);
537 }
538 }
539
CommandStarted(int cmd)540 void FicsConsole::CommandStarted(int cmd)
541 {
542 switch (cmd)
543 {
544 case FicsClient::BLKCMD_HISTORY:
545 ui->listHistory->clear();
546 ui->tabWidget->setCurrentIndex(TabHistory);
547 break;
548 case FicsClient::BLKCMD_FINGER:
549 ui->listFinger->clear();
550 ui->tabWidget->setCurrentIndex(TabHistory);
551 break;
552 case FicsClient::BLKCMD_GAMES:
553 ui->listGames->clear();
554 break;
555 case FicsClient::BLKCMD_XTELL:
556 ui->listRelay->clear();
557 break;
558 case FicsClient::BLKCMD_WHO:
559 break;
560 }
561 }
562
SetPlayerListItemsFromLine(QString s)563 void FicsConsole::SetPlayerListItemsFromLine(QString s)
564 {
565 if (s.contains("players displayed"))
566 return;
567
568 QRegExp sep("(\\s+|\\^|~|:|&|#|\\.)");
569 QStringList l = s.split(sep);
570 ui->listPlayers->setSortingEnabled(false);
571 for (int i=0; i<l.count()-1; i+=2)
572 {
573 QString rating = l[i];
574 QString user = l[i+1];
575 if (!rating.startsWith("-"))
576 {
577 if (rating.length()==3)
578 {
579 rating.prepend(" ");
580 }
581 }
582 int n = ui->listPlayers->rowCount();
583 ui->listPlayers->setRowCount(n+1);
584
585 QTableWidgetItem* item = new QTableWidgetItem(rating);
586 item->setFlags(item->flags() ^ Qt::ItemIsEditable);
587 ui->listPlayers->setItem(n,0,item);
588
589 item = new QTableWidgetItem(user);
590 item->setFlags(item->flags() ^ Qt::ItemIsEditable);
591 ui->listPlayers->setItem(n,1,item);
592
593 ui->listPlayers->scrollToBottom();
594 }
595 ui->listPlayers->setSortingEnabled(true);
596 }
597
Disconnected()598 void FicsConsole::Disconnected()
599 {
600 ui->textIn->appendPlainText(tr("Disconnected"));
601 ui->textIn->ensureCursorVisible();
602 ui->tabWidget->setCurrentIndex(TabMessage);
603 m_lastHistoryPlayer.clear();
604 ui->listNoPlay->clear();
605 ui->listSeeks->clear();
606 ui->listGames->clear();
607 ui->listPlayers->clearContents();
608 ui->listPuzzlebotMessages->clear();
609 if (gameMode)
610 {
611 emit RequestGameMode(false);
612 }
613 emit FicsShowTimer(false);
614 gameMode = false;
615 puzzleMode = false;
616 }
617
SlotTabChanged(int tab)618 void FicsConsole::SlotTabChanged(int tab)
619 {
620 if (!m_ficsClient) return;
621
622 switch (m_prevTab)
623 {
624 case TabSeeks:
625 m_ficsClient->sendCommand("set seek 0");
626 break;
627 default:
628 break;
629 }
630
631 m_prevTab = tab;
632 }
633
SlotTabClicked(int tab)634 void FicsConsole::SlotTabClicked(int tab)
635 {
636 if (!m_ficsClient) return;
637
638 switch (tab)
639 {
640 case TabMessage:
641 break;
642 case TabHistory:
643 if (m_lastHistoryPlayer.isEmpty())
644 {
645 // Query own games unless something else is already displayed
646 m_ficsClient->sendCommand("history");
647 }
648 break;
649 case TabGames:
650 m_ficsClient->sendCommand("games /blus");
651 break;
652 case TabRelay:
653 ui->listRelay->clear();
654 ui->listRelay->addItem(tr("Retrieving relayed games..."));
655 m_ficsClient->sendCommand("xtell relay listgames");
656 break;
657 case TabPlayers:
658 ui->listPlayers->clearContents();
659 ui->listPlayers->setRowCount(0);
660 m_ficsClient->sendCommand("who");
661 break;
662 case TabSeeks:
663 ui->listSeeks->clear();
664 m_ficsClient->sendCommand("sought");
665 m_ficsClient->sendCommand("set seek 1");
666 if (ui->listNoPlay->count()==0)
667 {
668 m_ficsClient->sendCommand("=noplay");
669 }
670 break;
671 }
672 }
673
HandleMessage(int blockCmd,QString s)674 void FicsConsole::HandleMessage(int blockCmd,QString s)
675 {
676 if (!s.isEmpty())
677 {
678 // qDebug() << "Cmd " << blockCmd << ": " << s;
679 switch (blockCmd)
680 {
681 case FicsClient::BLKCMD_TIME:
682 {
683 if (s.startsWith("Game"))
684 {
685 QStringList l = s.split(" ",QString::SkipEmptyParts);
686 if (l.size()>=6)
687 {
688 QString nameWhite = l[2];
689 QString nameBlack = l[4];
690 emit RequestAddTag(TagNameWhite, nameWhite);
691 emit RequestAddTag(TagNameBlack, nameBlack);
692 emit RequestAddTag(TagNameDate, PartialDate::today().asString());
693 int ratingWhite = l[3].remove("(").remove(")").toInt();
694 int ratingBlack = l[5].remove("(").remove(")").toInt();
695 if (ratingWhite) emit RequestAddTag(TagNameWhiteElo, QString::number(ratingWhite));
696 if (ratingBlack) emit RequestAddTag(TagNameBlackElo, QString::number(ratingBlack));
697 }
698 }
699 else
700 {
701 QRegExp wReg("White Clock :([^\\.]*)");
702 if (wReg.indexIn(s) >= 0)
703 {
704 QString w = wReg.cap(1).trimmed();
705 emit FicsShowTime(White, w);
706 }
707 QRegExp bReg("Black Clock :([^\\.]*)");
708 if (bReg.indexIn(s) >= 0)
709 {
710 QString b = bReg.cap(1).trimmed();
711 emit FicsShowTime(Black, b);
712 }
713 }
714 break;
715 }
716 case FicsClient::BLKCMD_HISTORY:
717 {
718 QRegExp typeOfGame("\\[([^\\]]+)\\]");
719 int pos = typeOfGame.indexIn(s);
720 if(pos >= 0)
721 {
722 QString segt = typeOfGame.cap(1).trimmed();
723 if (segt.startsWith('b') || segt.startsWith('l') || segt.startsWith('s') || segt.startsWith('u'))
724 {
725 ui->listHistory->addItem(s);
726 ui->listHistory->scrollToBottom();
727 }
728 }
729 }
730 break;
731 case FicsClient::BLKCMD_FINGER:
732 {
733 ui->listFinger->addItem(s);
734 }
735 break;
736 case FicsClient::BLKCMD_GAMES:
737 ui->listGames->addItem(s);
738 ui->listGames->scrollToBottom();
739 break;
740 case FicsClient::BLKCMD_XTELL:
741 if (!s.startsWith("(") && s.length() > 1)
742 {
743 QStringList l = s.split(" ", QString::SkipEmptyParts);
744 if (l.length() == 5)
745 {
746 // todo: Convert List to table widget
747 ui->listRelay->addItem(s);
748 ui->listRelay->scrollToBottom();
749 }
750 else if (s.startsWith("There are"))
751 {
752 ui->listRelay->addItem(s);
753 ui->listRelay->scrollToBottom();
754 }
755 else if (s.contains("tell relay next"))
756 {
757 m_ficsClient->sendCommand("xtell relay next");
758 }
759 }
760 break;
761 case FicsClient::BLKCMD_WHO:
762 {
763 SetPlayerListItemsFromLine(s);
764 break;
765 }
766 case FicsClient::BLKCMD_SEEK:
767 {
768 QString seek = btgSeek->checkedButton()->objectName().remove(0,2).toLower();
769 if (s.contains(seek))
770 {
771 if (ui->disableComputer->isChecked())
772 {
773 if (s.contains("(C)")) break;
774 }
775 if (ui->ratedOnly->isChecked())
776 {
777 if (s.contains("unrated")) break;
778 }
779 ui->listSeeks->addItem(s);
780 ui->listSeeks->scrollToBottom();
781 }
782 }
783 break;
784 case FicsClient::BLKCMD_SOUGHT:
785 {
786 QStringList l = s.split(" ",QString::SkipEmptyParts);
787 if (l.size() >= 8)
788 {
789 QString sought = btgSeek->checkedButton()->objectName().remove(0,2).toLower();
790 if (l[6].contains(sought))
791 {
792 if (ui->disableComputer->isChecked())
793 {
794 if (l[2].contains("(C)")) break;
795 }
796 if (ui->ratedOnly->isChecked())
797 {
798 if (l[5].contains("unrated")) break;
799 }
800 QString spec;
801 for (int i=5;i<l.size();++i)
802 {
803 spec.append(l[i]);
804 spec.append(" ");
805 }
806 QString seek = QString("%1 (%2) sought %3 %4 %5(\"play %6\" to respond)").
807 arg(l[2], l[1], l[3], l[4], spec, l[0]);
808 ui->listSeeks->addItem(seek);
809 ui->listSeeks->scrollToBottom();
810 }
811 }
812 }
813 break;
814 case FicsClient::BLKCMD_INTERNAL_MATCH_START:
815 {
816 gameMode = true;
817 m_ficsClient->sendCommand("time");
818 m_lastRelation = C64_REL_ISOLATED; // Anything invalid in this context
819 emit FicsShowTimer(true);
820 m_bWhiteToMove = true;
821 emit RequestNewGame();
822 emit RequestGameMode(gameMode);
823 ui->textIn->appendPlainText(s);
824 ui->textIn->ensureCursorVisible();
825 ui->tabWidget->setCurrentIndex(TabMessage);
826 QStringList l = s.split(' ');
827 if (l.length() > 2)
828 {
829 QString w = l[2].remove('(');
830 m_bPlayerIsBlack = w != m_ficsClient->getGuestName();
831 emit SignalPlayerIsBlack(m_bPlayerIsBlack, true);
832 }
833 }
834 break;
835 case FicsClient::BLKCMD_INTERNAL_MATCH_END:
836 {
837 gameMode = false;
838 ui->textIn->appendPlainText(s);
839 ui->textIn->ensureCursorVisible();
840 emit SignalGameResult(s);
841 emit RequestSaveGame();
842 emit RequestGameMode(gameMode);
843 emit FicsShowTimer(false);
844 }
845 break;
846 case FicsClient::BLKCMD_INTERNAL_GAME_END:
847 {
848 ui->textIn->appendPlainText(s);
849 ui->textIn->ensureCursorVisible();
850 emit SignalGameResult(s);
851 if (!puzzleMode)
852 {
853 // Puzzlemode is saved a little later
854 emit RequestSaveGame();
855 }
856 break;
857 }
858 case FicsClient::BLKCMD_SHOWLIST:
859 if (!s.contains("--"))
860 {
861 QStringList l = s.split(" ", QString::SkipEmptyParts);
862 foreach(QString name, l)
863 {
864 ui->listNoPlay->addItem(name);
865 }
866 }
867 break;
868 case FicsClient::BLKCMD_SAY:
869 s.remove(QRegExp("[^:]*:"));
870 ui->textIn->appendHtml(QString("<b>%1</b>").arg(s));
871 ui->tabWidget->setCurrentIndex(TabMessage);
872 break;
873 case FicsClient::BLKCMD_INTERNAL_SESSION_STARTED:
874 emit raiseRequest();
875 ui->textIn->appendPlainText(s);
876 ui->textIn->ensureCursorVisible();
877 if (m_ficsClient->loggedInAsGuest())
878 {
879 ui->cbRated->setCurrentIndex(1);
880 ui->cbRated->setEnabled(false);
881 }
882 else
883 {
884 ui->cbRated->setEnabled(true);
885 }
886 break;
887 case FicsClient::BLKCMD_NULL:
888 ui->textIn->appendPlainText(s);
889 ui->textIn->ensureCursorVisible();
890 break;
891 case FicsClient::BLKCMD_INTERNAL_PUZZLEBOT:
892 if (s.contains("kibitzes"))
893 {
894 emit FicsShowTimer(true);
895 s.remove(QRegExp("puzzlebot[^:]*kibitzes:"));
896 s = s.trimmed();
897 if (!s.contains("tell puzzlebot"))
898 {
899 ui->listPuzzlebotMessages->addItem(s.trimmed());
900 ui->listPuzzlebotMessages->scrollToBottom();
901 ui->tabWidget->setCurrentIndex(TabPuzzle);
902 }
903 if (s.contains("Timeout reached"))
904 {
905 emit SignalGameResult("*");
906 emit RequestSaveGame();
907 if (gameMode)
908 {
909 emit RequestGameMode(false);
910 }
911 emit FicsShowTimer(false);
912 puzzleMode = false;
913 SlotSendUnexamine();
914 }
915 else if (s.contains("solved") && !s.contains("almost"))
916 {
917 emit RequestSaveGame();
918 if (gameMode)
919 {
920 emit RequestGameMode(false);
921 }
922 emit FicsShowTimer(false);
923 puzzleMode = false;
924 SlotSendUnexamine();
925 }
926 else
927 {
928 if (s.contains("problem number"))
929 {
930 QString event = s;
931 event.remove("This is ");
932 event.remove('[');
933 event.remove(']');
934 emit RequestAddTag(TagNameEvent, event);
935 }
936 else if (s.startsWith("Black moves"))
937 {
938 m_bPlayerIsBlack = true;
939 emit FicsShowTime(Black, "0:00");
940 emit FicsShowTime(White, "0:00");
941 emit SignalStartTime(false);
942 emit SignalPlayerIsBlack(true,false);
943 }
944 else if (s.startsWith("White moves"))
945 {
946 m_bPlayerIsBlack = false;
947 emit FicsShowTime(Black, "0:00");
948 emit FicsShowTime(White, "0:00");
949 emit SignalStartTime(true);
950 emit SignalPlayerIsBlack(false,false);
951 }
952 }
953 }
954 else if (s.contains("puzzlebot backs up 1 move"))
955 {
956 emit RequestRemoveLastMove();
957 }
958 else if (!s.contains("tell puzzlebot"))
959 {
960 puzzleMode = true;
961 }
962 break;
963 case FicsClient::BLKCMD_FORWARD:
964 if (!s.contains("goes"))
965 {
966 ui->textIn->appendPlainText(s);
967 ui->textIn->ensureCursorVisible();
968 ui->tabWidget->setCurrentIndex(TabMessage);
969 }
970 break;
971 case FicsClient::BLKCMD_BACKWARD:
972 case FicsClient::BLKCMD_MEXAMINE:
973 case FicsClient::BLKCMD_GAME_MOVE:
974 case FicsClient::BLKCMD_TELL:
975 case FicsClient::BLKCMD_OBSERVE:
976 case FicsClient::BLKCMD_EXAMINE:
977 case FicsClient::BLKCMD_UNOBSERVE:
978 case FicsClient::BLKCMD_UNEXAMINE:
979 case FicsClient::BLKCMD_SET:
980 break;
981 default:
982 ui->textIn->appendPlainText(s);
983 ui->textIn->ensureCursorVisible();
984 ui->tabWidget->setCurrentIndex(TabMessage);
985 break;
986 }
987 }
988 }
989
CMHandleObserveRequest(const QPoint & pos)990 void FicsConsole::CMHandleObserveRequest(const QPoint& pos)
991 {
992 QMenu menu;
993 menu.addAction(tr("Observe"));
994 if (menu.exec(ui->listGames->mapToGlobal(pos)))
995 {
996 QListWidgetItem* item = ui->listGames->itemAt(pos);
997 if (item)
998 {
999 HandleObserveRequest(item);
1000 }
1001 }
1002 }
CMHandleExamineRequest(const QPoint & pos)1003 void FicsConsole::CMHandleExamineRequest(const QPoint& pos)
1004 {
1005 QMenu menu;
1006 menu.addAction(tr("Examine"));
1007 if (menu.exec(ui->listHistory->mapToGlobal(pos)))
1008 {
1009 QListWidgetItem* item = ui->listHistory->itemAt(pos);
1010 if (item)
1011 {
1012 HandleExamineRequest(item);
1013 }
1014 }
1015 }
CMHandleRelayRequest(const QPoint & pos)1016 void FicsConsole::CMHandleRelayRequest(const QPoint& pos)
1017 {
1018 QMenu menu;
1019 menu.addAction(tr("Relay"));
1020 if (menu.exec(ui->listRelay->mapToGlobal(pos)))
1021 {
1022 QListWidgetItem* item = ui->listRelay->itemAt(pos);
1023 if (item)
1024 {
1025 HandleRelayRequest(item);
1026 }
1027 }
1028 }
CMHandleTacticsRequest(const QPoint & pos)1029 void FicsConsole::CMHandleTacticsRequest(const QPoint& pos)
1030 {
1031 QMenu menu;
1032 menu.addAction(tr("Puzzle"));
1033 if (menu.exec(ui->listPuzzle->mapToGlobal(pos)))
1034 {
1035 QListWidgetItem* item = ui->listPuzzle->itemAt(pos);
1036 if (item)
1037 {
1038 HandleTacticsRequest(item);
1039 }
1040 }
1041 }
CMHandleHistoryRequest(const QPoint & pos)1042 void FicsConsole::CMHandleHistoryRequest(const QPoint& pos)
1043 {
1044 QMenu menu;
1045 menu.addAction(tr("History"));
1046 if (menu.exec(ui->listPlayers->mapToGlobal(pos)))
1047 {
1048 QTableWidgetItem* item = ui->listPlayers->itemAt(pos);
1049 if (item)
1050 {
1051 HandleHistoryRequest(item);
1052 }
1053 }
1054 }
CMHandleSeekRequest(const QPoint & pos)1055 void FicsConsole::CMHandleSeekRequest(const QPoint& pos)
1056 {
1057 QMenu menu;
1058 menu.addAction(tr("Accept"));
1059 if (menu.exec(ui->listSeeks->mapToGlobal(pos)))
1060 {
1061 QListWidgetItem* item = ui->listSeeks->itemAt(pos);
1062 if (item)
1063 {
1064 HandleSeekRequest(item);
1065 }
1066 }
1067 }
1068
saveConfig()1069 void FicsConsole::saveConfig()
1070 {
1071 AppSettings->setValue("/FicsConsole/NoPlaySplit", ui->noPlaySplitter->saveState());
1072 AppSettings->setValue("/FicsConsole/SeekSplit", ui->seekSplitter->saveState());
1073 AppSettings->setValue("/FICS/minutes", ui->seekTime->value());
1074 AppSettings->setValue("/FICS/increment", ui->seekIncrement->value());
1075 AppSettings->setValue("/FICS/eloLow", ui->eloMin->value());
1076 AppSettings->setValue("/FICS/eloHigh", ui->eloMax->value());
1077 }
1078
slotReconfigure()1079 void FicsConsole::slotReconfigure()
1080 {
1081 ui->noPlaySplitter->restoreState(AppSettings->value("/FicsConsole/NoPlaySplit").toByteArray());
1082 ui->seekSplitter->restoreState(AppSettings->value("/FicsConsole/SeekSplit").toByteArray());
1083
1084 bool on = AppSettings->getValue("/FICS/commandline").toBool();
1085 ui->line->setVisible(on);
1086 ui->textOut->setEnabled(on);
1087 ui->textOut->setVisible(on);
1088 }
1089