source: trunk/FACT++/gui/FactGui.h@ 10406

Last change on this file since 10406 was 10397, checked in by tbretz, 14 years ago
Some cosmetics.
File size: 18.4 KB
Line 
1#ifndef FACT_FactGui
2#define FACT_FactGui
3
4#include "MainWindow.h"
5
6#include <iomanip>
7
8#include <boost/bind.hpp>
9
10#include <QtGui/QStandardItemModel>
11
12#include "CheckBoxDelegate.h"
13
14#include "src/Converter.h"
15#include "src/HeadersFTM.h"
16#include "src/DimNetwork.h"
17
18using namespace std;
19
20class FactGui : public MainWindow, public DimNetwork
21{
22private:
23 class FunctionEvent : public QEvent
24 {
25 public:
26 boost::function<void(const QEvent &)> fFunction;
27
28 FunctionEvent(const boost::function<void(const QEvent &)> &f)
29 : QEvent((QEvent::Type)QEvent::registerEventType()),
30 fFunction(f) { }
31
32 bool Exec() { fFunction(*this); return true; }
33 };
34
35 DimInfo fDimDNS;
36 map<string, DimInfo*> fServices;
37
38 // ======================================================================
39
40 QStandardItem *AddServiceItem(const std::string &server, const std::string &service, bool iscmd)
41 {
42 QListView *servers = iscmd ? fDimCmdServers : fDimSvcServers;
43 QListView *services = iscmd ? fDimCmdCommands : fDimSvcServices;
44 QListView *description = iscmd ? fDimCmdDescription : fDimSvcDescription;
45
46 QStandardItemModel *m = dynamic_cast<QStandardItemModel*>(servers->model());
47 if (!m)
48 {
49 m = new QStandardItemModel(this);
50 servers->setModel(m);
51 services->setModel(m);
52 description->setModel(m);
53 }
54
55 QList<QStandardItem*> l = m->findItems(server.c_str());
56
57 if (l.size()>1)
58 {
59 cout << "hae" << endl;
60 return 0;
61 }
62
63 QStandardItem *col = l.size()==0 ? NULL : l[0];
64
65 if (!col)
66 {
67 col = new QStandardItem(server.c_str());
68 m->appendRow(col);
69
70 if (!services->rootIndex().isValid())
71 {
72 services->setRootIndex(col->index());
73 servers->setCurrentIndex(col->index());
74 }
75 }
76
77 QStandardItem *item = 0;
78 for (int i=0; i<col->rowCount(); i++)
79 {
80 QStandardItem *coli = col->child(i);
81 if (coli->text().toStdString()==service)
82 return coli;
83 }
84
85 item = new QStandardItem(service.c_str());
86 col->appendRow(item);
87
88 if (!description->rootIndex().isValid())
89 {
90 description->setRootIndex(item->index());
91 services->setCurrentIndex(item->index());
92 }
93
94 if (!iscmd)
95 item->setCheckable(true);
96
97 return item;
98 }
99
100 void AddDescription(QStandardItem *item, const vector<Description> &vec)
101 {
102 if (!item)
103 return;
104 if (vec.size()==0)
105 return;
106
107 item->setToolTip(vec[0].comment.c_str());
108
109 const string str = Description::GetHtmlDescription(vec);
110
111 QStandardItem *desc = new QStandardItem(str.c_str());
112 desc->setSelectable(false);
113 item->setChild(0, 0, desc);
114 }
115
116 // ======================================================================
117
118 void handleAddServer(const std::string &server)
119 {
120 const State s = GetState(server, GetCurrentState(server));
121 handleStateChanged(Time(), server, s);
122 }
123
124 void handleRemoveServer(const string &server)
125 {
126 handleStateOffline(server);
127 handleRemoveAllServices(server);
128 }
129
130 void handleRemoveAllServers()
131 {
132 QStandardItemModel *m = 0;
133 if ((m=dynamic_cast<QStandardItemModel*>(fDimCmdServers->model())))
134 m->removeRows(0, m->rowCount());
135
136 if ((m = dynamic_cast<QStandardItemModel*>(fDimSvcServers->model())))
137 m->removeRows(0, m->rowCount());
138 }
139
140 void handleAddService(const std::string &server, const std::string &service, const std::string &/*fmt*/, bool iscmd)
141 {
142 QStandardItem *item = AddServiceItem(server, service, iscmd);
143 const vector<Description> v = GetDescription(server, service);
144 AddDescription(item, v);
145 }
146
147 void handleRemoveService(const std::string &server, const std::string &service, bool iscmd)
148 {
149 QListView *servers = iscmd ? fDimCmdServers : fDimSvcServers;
150
151 QStandardItemModel *m = dynamic_cast<QStandardItemModel*>(servers->model());
152 if (!m)
153 return;
154
155 QList<QStandardItem*> l = m->findItems(server.c_str());
156 if (l.size()!=1)
157 return;
158
159 for (int i=0; i<l[0]->rowCount(); i++)
160 {
161 QStandardItem *row = l[0]->child(i);
162 if (row->text().toStdString()==service)
163 {
164 l[0]->removeRow(row->index().row());
165 return;
166 }
167 }
168 }
169
170 void handleRemoveAllServices(const std::string &server)
171 {
172 QStandardItemModel *m = 0;
173 if ((m=dynamic_cast<QStandardItemModel*>(fDimCmdServers->model())))
174 {
175 QList<QStandardItem*> l = m->findItems(server.c_str());
176 if (l.size()==1)
177 m->removeRow(l[0]->index().row());
178 }
179
180 if ((m = dynamic_cast<QStandardItemModel*>(fDimSvcServers->model())))
181 {
182 QList<QStandardItem*> l = m->findItems(server.c_str());
183 if (l.size()==1)
184 m->removeRow(l[0]->index().row());
185 }
186 }
187
188 void handleAddDescription(const std::string &server, const std::string &service, const vector<Description> &vec)
189 {
190 const bool iscmd = IsCommand(server, service)==true;
191
192 QStandardItem *item = AddServiceItem(server, service, iscmd);
193 AddDescription(item, vec);
194 }
195
196 void handleStateChanged(const Time &/*time*/, const std::string &server,
197 const State &s)
198 {
199 // FIXME: Prefix tooltip with time
200 if (server=="FTM_CONTROL")
201 {
202 fStatusFTMLabel->setText(s.name.c_str());
203 fStatusFTMLabel->setToolTip(s.comment.c_str());
204
205 if (s.index<FTM::kDisconnected) // No Dim connection
206 {
207 fStatusFTMLed_1->setEnabled(false);
208 fStatusFTMLed_2->setEnabled(false);
209 fStatusFTMLed_3->setEnabled(false);
210 }
211 if (s.index==FTM::kDisconnected) // Dim connection / FTM disconnected
212 {
213 fStatusFTMLed_1->setEnabled(true);
214 fStatusFTMLed_2->setEnabled(false);
215 fStatusFTMLed_3->setEnabled(false);
216 }
217 if (s.index==FTM::kConnected) // Dim connection / FTM connected
218 {
219 fStatusFTMLed_1->setEnabled(false);
220 fStatusFTMLed_2->setEnabled(true);
221 fStatusFTMLed_3->setEnabled(false);
222 }
223 }
224
225 if (server=="FAD_CONTROL")
226 {
227 fStatusFADLabel->setText(s.name.c_str());
228 fStatusFADLabel->setToolTip(s.comment.c_str());
229
230 if (s.index<FTM::kDisconnected) // No Dim connection
231 {
232 fStatusFADLed_1->setEnabled(false);
233 fStatusFADLed_2->setEnabled(false);
234 fStatusFADLed_3->setEnabled(false);
235 }
236 if (s.index==FTM::kDisconnected) // Dim connection / FTM disconnected
237 {
238 fStatusFADLed_1->setEnabled(true);
239 fStatusFADLed_2->setEnabled(false);
240 fStatusFADLed_3->setEnabled(false);
241 }
242 if (s.index==FTM::kConnected) // Dim connection / FTM connected
243 {
244 fStatusFADLed_1->setEnabled(false);
245 fStatusFADLed_2->setEnabled(true);
246 fStatusFADLed_3->setEnabled(false);
247 }
248 }
249
250 if (server=="DATA_LOGGER")
251 {
252 fStatusLoggerLabel->setText(s.name.c_str());
253 fStatusLoggerLabel->setToolTip(s.comment.c_str());
254
255 if (s.index<-1 || s.index>=0x100) // Error
256 {
257 fStatusLoggerLed_1->setEnabled(false);
258 fStatusLoggerLed_2->setEnabled(false);
259 fStatusLoggerLed_3->setEnabled(false);
260 }
261 if (s.index<=30) // Waiting
262 {
263 fStatusLoggerLed_1->setEnabled(false);
264 fStatusLoggerLed_2->setEnabled(true);
265 fStatusLoggerLed_3->setEnabled(false);
266 }
267 if (s.index==40) // Logging
268 {
269 fStatusLoggerLed_1->setEnabled(false);
270 fStatusLoggerLed_2->setEnabled(false);
271 fStatusLoggerLed_3->setEnabled(true);
272 }
273 }
274
275 if (server=="CHAT")
276 {
277 fStatusChatLabel->setText(s.name.c_str());
278
279 if (s.index==FTM::kConnected) // Dim connection / FTM connected
280 fStatusChatLed_3->setEnabled(true);
281 else
282 fStatusChatLed_3->setEnabled(false);
283 }
284 }
285
286 void handleStateOffline(const string &server)
287 {
288 handleStateChanged(Time(), server, State(-2, "Offline", "No connection via DIM."));
289 }
290
291 void handleWrite(const Time &time, const string &text, int qos)
292 {
293 stringstream out;
294
295 if (text.substr(0, 6)=="CHAT: ")
296 {
297 out << "<font size='-1' color='navy'>[<B>";
298 out << Time::fmt("%H:%M:%S") << time << "</B>]</FONT> ";
299 out << text.substr(6);
300 fChatText->append(out.str().c_str());
301 return;
302 }
303
304
305 out << "<font color='";
306
307 switch (qos)
308 {
309 case kMessage: out << "black"; break;
310 case kInfo: out << "green"; break;
311 case kWarn: out << "#FF6600"; break;
312 case kError: out << "maroon"; break;
313 case kFatal: out << "maroon"; break;
314 case kDebug: out << "navy"; break;
315 default: out << "navy"; break;
316 }
317 out << "'>" << time.GetAsStr() << " - " << text << "</font>";
318
319 fLogText->append(out.str().c_str());
320
321 if (qos>=kWarn)
322 fTextEdit->append(out.str().c_str());
323 }
324
325 void handleDimService(const string &txt)
326 {
327 fDimSvcText->append(txt.c_str());
328 }
329
330 void handleDimDNS(int version)
331 {
332 ostringstream str;
333 str << "DIM V" << version/100 << 'r' << version%100;
334
335 fStatusDNSLed_3->setEnabled(version!=0);
336 fStatusDNSLabel->setText(version==0?"Offline":str.str().c_str());
337 fStatusDNSLabel->setToolTip(version==0?"No connection to DIM DNS.":"Connection to DIM DNS established.");
338 }
339
340 // ======================================================================
341
342 void AddServer(const std::string &s)
343 {
344 DimNetwork::AddServer(s);
345
346 QApplication::postEvent(this,
347 new FunctionEvent(boost::bind(&FactGui::handleAddServer, this, s)));
348 }
349
350 void RemoveServer(const std::string &s)
351 {
352 UnsubscribeServer(s);
353
354 DimNetwork::RemoveServer(s);
355
356 QApplication::postEvent(this,
357 new FunctionEvent(boost::bind(&FactGui::handleRemoveServer, this, s)));
358 }
359
360 void RemoveAllServers()
361 {
362 UnsubscribeAllServers();
363
364 vector<string> v = GetServerList();
365 for (vector<string>::iterator i=v.begin(); i<v.end(); i++)
366 QApplication::postEvent(this,
367 new FunctionEvent(boost::bind(&FactGui::handleStateOffline, this, *i)));
368
369 DimNetwork::RemoveAllServers();
370
371 QApplication::postEvent(this,
372 new FunctionEvent(boost::bind(&FactGui::handleRemoveAllServers, this)));
373 }
374
375 void AddService(const std::string &server, const std::string &service, const std::string &fmt, bool iscmd)
376 {
377 QApplication::postEvent(this,
378 new FunctionEvent(boost::bind(&FactGui::handleAddService, this, server, service, fmt, iscmd)));
379 }
380
381 void RemoveService(const std::string &server, const std::string &service, bool iscmd)
382 {
383 if (fServices.find(server+'/'+service)!=fServices.end())
384 UnsubscribeService(server+'/'+service);
385
386 QApplication::postEvent(this,
387 new FunctionEvent(boost::bind(&FactGui::handleRemoveService, this, server, service, iscmd)));
388 }
389
390 void RemoveAllServices(const std::string &server)
391 {
392 UnsubscribeServer(server);
393
394 QApplication::postEvent(this,
395 new FunctionEvent(boost::bind(&FactGui::handleRemoveAllServices, this, server)));
396 }
397
398 void AddDescription(const std::string &server, const std::string &service, const vector<Description> &vec)
399 {
400 QApplication::postEvent(this,
401 new FunctionEvent(boost::bind(&FactGui::handleAddDescription, this, server, service, vec)));
402 }
403
404
405 void IndicateStateChange(const Time &time, const std::string &server)
406 {
407 const State s = GetState(server, GetCurrentState(server));
408
409 QApplication::postEvent(this,
410 new FunctionEvent(boost::bind(&FactGui::handleStateChanged, this, time, server, s)));
411 }
412
413 int Write(const Time &time, const string &txt, int qos)
414 {
415 QApplication::postEvent(this,
416 new FunctionEvent(boost::bind(&FactGui::handleWrite, this, time, txt, qos)));
417
418 return 0;
419 }
420
421 void infoHandler(DimInfo &info)
422 {
423 const string fmt = string("DIS_DNS/SERVER_INFO")==info.getName() ? "C" : info.getFormat();
424
425 stringstream dummy;
426 const Converter conv(dummy, fmt, false);
427
428 const Time tm(info.getTimestamp(), info.getTimestampMillisecs());
429
430 stringstream out;
431 out << Time::fmt("%H:%M:%S.%f") << tm << " <B>" << info.getName() << "</B> - ";
432
433 bool iserr = false;
434 if (!conv)
435 {
436 out << "Compilation of format string '" << fmt << "' failed!";
437 iserr = true;
438 }
439 else
440 {
441 try
442 {
443 const string dat = conv.GetString(info.getData(), info.getSize());
444 out << dat;
445 }
446 catch (const runtime_error &e)
447 {
448 out << "Conversion to string failed!<pre>" << e.what() << "</pre>";
449 iserr = true;
450 }
451 }
452
453 // srand(hash<string>()(string(info.getName())));
454 // int bg = rand()&0xffffff;
455
456 int bg = hash<string>()(string(info.getName()));
457
458 int r = bg&0x1f0000;
459 int g = bg&0x001f00;
460 int b = bg&0x00001f;
461
462 bg = ~(b|g|r)&0xffffff;
463
464 if (iserr)
465 bg = 0xffffff;
466
467 stringstream bgcol;
468 bgcol << hex << setfill('0') << setw(6) << bg;
469
470 const string col = iserr ? "red" : "black";
471 const string str = "<table width='100%' bgcolor=#"+bgcol.str()+"><tr><td><font color='"+col+"'>"+out.str()+"</font></td></tr></table>";
472
473 QApplication::postEvent(this,
474 new FunctionEvent(boost::bind(&FactGui::handleDimService, this, str)));
475 }
476
477 void infoHandler()
478 {
479 if (getInfo()==&fDimDNS)
480 {
481 QApplication::postEvent(this,
482 new FunctionEvent(boost::bind(&FactGui::handleDimDNS, this, getInfo()->getInt())));
483 return;
484 }
485
486 for (map<string,DimInfo*>::iterator i=fServices.begin(); i!=fServices.end(); i++)
487 if (i->second==getInfo())
488 {
489 infoHandler(*i->second);
490 return;
491 }
492
493 DimNetwork::infoHandler();
494 }
495
496 // ======================================================================
497
498
499 void SubscribeService(const string &service)
500 {
501 if (fServices.find(service)!=fServices.end())
502 {
503 cout << "ERROR - We are already subscribed to " << service << endl;
504 return;
505 }
506
507 fServices[service] = new DimStampedInfo(service.c_str(), (void*)NULL, 0, this);
508 }
509
510 void UnsubscribeService(const string &service)
511 {
512 const map<string,DimInfo*>::iterator i=fServices.find(service);
513
514 if (i==fServices.end())
515 {
516 cout << "ERROR - We are not subscribed to " << service << endl;
517 return;
518 }
519
520 delete i->second;
521
522 fServices.erase(i);
523 }
524
525 void UnsubscribeServer(const string &server)
526 {
527 for (map<string,DimInfo*>::iterator i=fServices.begin();
528 i!=fServices.end(); i++)
529 if (i->first.substr(0, server.length()+1)==server+'/')
530 {
531 delete i->second;
532 fServices.erase(i);
533 }
534 }
535
536 void UnsubscribeAllServers()
537 {
538 for (map<string,DimInfo*>::iterator i=fServices.begin();
539 i!=fServices.end(); i++)
540 delete i->second;
541
542 fServices.clear();
543 }
544
545 // ======================================================================
546
547 bool event(QEvent *evt)
548 {
549 if (dynamic_cast<FunctionEvent*>(evt))
550 return static_cast<FunctionEvent*>(evt)->Exec();
551
552 if (dynamic_cast<CheckBoxEvent*>(evt))
553 {
554 const QStandardItem &item = static_cast<CheckBoxEvent*>(evt)->item;
555 const QStandardItem *par = item.parent();
556 if (par)
557 {
558 const QString server = par->text();
559 const QString service = item.text();
560
561 const string s = (server+'/'+service).toStdString();
562
563 if (item.checkState()==Qt::Checked)
564 SubscribeService(s);
565 else
566 UnsubscribeService(s);
567 }
568 }
569
570 return MainWindow::event(evt); // unrecognized
571 }
572
573 void on_fDimCmdSend_clicked(bool)
574 {
575 const QString server = fDimCmdServers->currentIndex().data().toString();
576 const QString command = fDimCmdCommands->currentIndex().data().toString();
577 const QString arguments = fDimCmdLineEdit->displayText();
578
579 // FIXME: Sending a command exactly when the info Handler changes
580 // the list it might lead to confusion.
581 try
582 {
583 SendDimCommand(server.toStdString(), command.toStdString()+" "+arguments.toStdString());
584 fTextEdit->append("<font color='green'>Command '"+server+'/'+command+"' successfully emitted.</font>");
585 fDimCmdLineEdit->clear();
586 }
587 catch (const runtime_error &e)
588 {
589 stringstream txt;
590 txt << e.what();
591
592 string buffer;
593 while (getline(txt, buffer, '\n'))
594 fTextEdit->append(("<font color='red'><pre>"+buffer+"</pre></font>").c_str());
595 }
596 }
597
598public:
599 FactGui() : fDimDNS("DIS_DNS/VERSION_NUMBER", 1, int(0), this)
600 {
601 DimClient::sendCommand("CHAT/MSG", "GUI online.");
602 // + MessageDimRX
603 }
604 ~FactGui()
605 {
606 UnsubscribeAllServers();
607 }
608};
609
610#endif
Note: See TracBrowser for help on using the repository browser.