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