]> vaikene.ee Git - evaf/blob - src/apps/ScosTime/gui.cpp
The ScosTime application now more user friendly.
[evaf] / src / apps / ScosTime / gui.cpp
1 /**
2 * @file ScosTime/gui.cpp
3 * @brief GUI for the ScosTime application
4 * @author Enar Vaikene
5 *
6 * Copyright (c) 2012 Enar Vaikene
7 *
8 * This file is part of the eVaf C++ cross-platform application development framework.
9 *
10 * This file can be used under the terms of the GNU General Public License
11 * version 3.0 as published by the Free Software Foundation and appearing in
12 * the file LICENSE included in the packaging of this file. Please review the
13 * the following information to ensure the GNU General Public License version
14 * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
15 *
16 * Alternatively, this file may be used in accordance with the Commercial License
17 * Agreement provided with the Software.
18 */
19
20 #include "gui.h"
21 #include "version.h"
22
23 #include <Common/Globals>
24 #include <Common/iLogger>
25 #include <Common/iRegistry>
26 #include <SdiWindow/iSdiWindow>
27 #include <Gui/Panel>
28
29 #include <QtGui>
30
31
32 VER_EXPORT_VERSION_INFO()
33 Q_EXPORT_PLUGIN2(VER_MODULE_NAME_STR, eVaf::ScosTime::Module)
34
35
36 //-------------------------------------------------------------------
37
38 using namespace eVaf;
39 using namespace eVaf::ScosTime;
40
41
42 //-------------------------------------------------------------------
43
44 Internal::DateTime::DateTime()
45 : mType(Invalid)
46 , mEpoch(QDateTime(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC))
47 , mRxIso(new QRegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2}(\\.\\d{3})?)?$"))
48 , mRxAsd(new QRegExp("^\\d{4}\\.\\d{3}\\.\\d{2}\\.\\d{2}(\\.\\d{2}(\\.\\d{3})?)?$"))
49 , mRxCUC(new QRegExp("^[0-9a-f]{8}([0-9a-f]{4})?$", Qt::CaseInsensitive))
50 {}
51
52 Internal::DateTime::~DateTime()
53 {
54 delete mRxCUC;
55 delete mRxAsd;
56 delete mRxIso;
57 }
58
59 Internal::DateTime::Type Internal::DateTime::setDateTime(QString const & s, QDateTime const & epoch)
60 {
61 mEpoch = epoch;
62 return setDateTime(s);
63 }
64
65 Internal::DateTime::Type Internal::DateTime::setDateTime(QString const & s)
66 {
67 // Detect the type of the date/time string
68 mType = getDateTimeType(s);
69 if (mType == Invalid)
70 {
71 return mType;
72 }
73
74 // Convert the string to a date/time value
75 mDateTime = strToDateTime(s, mType);
76 if (!mDateTime.isValid())
77 {
78 mType = Invalid;
79 }
80
81
82 return mType;
83 }
84
85 void Internal::DateTime::setEpoch(QDateTime const & epoch)
86 {
87 if (!epoch.isValid())
88 {
89 return;
90 }
91
92 if (epoch != mEpoch)
93 {
94 if (mDateTime.isValid())
95 {
96 qint64 diff = mEpoch.msecsTo(epoch);
97 mDateTime = mDateTime.addMSecs(diff);
98 }
99 mEpoch = epoch;
100 }
101 }
102
103 Internal::DateTime::Type Internal::DateTime::getDateTimeType(QString const & s) const
104 {
105 if (mRxIso->exactMatch(s))
106 {
107 return ISO;
108 }
109 else if (mRxAsd->exactMatch(s))
110 {
111 return ASD;
112 }
113 else if (mRxCUC->exactMatch(s))
114 {
115 return CUC;
116 }
117 return Invalid;
118 }
119
120 QDateTime Internal::DateTime::strToDateTime(QString const & s, Type type) const
121 {
122 switch (type)
123 {
124 case ASD:
125 {
126 // Using the yyyy.ddd.hh.mm.ss[.zzz] format
127 QStringList tok = s.split(QChar('.'));
128 if (tok.size() < 4)
129 {
130 return QDateTime();
131 }
132
133 bool ok = false;
134 int year = tok.at(0).toInt(&ok);
135 if (!ok)
136 {
137 return QDateTime();
138 }
139 int days = tok.at(1).toInt(&ok);
140 if (!ok)
141 {
142 return QDateTime();
143 }
144 int hours = tok.at(2).toInt(&ok);
145 if (!ok)
146 {
147 return QDateTime();
148 }
149 int minutes = tok.at(3).toInt(&ok);
150 if (!ok)
151 {
152 return QDateTime();
153 }
154 int secs = 0;
155 int msecs = 0;
156 if (tok.size() > 4)
157 {
158 secs = tok.at(4).toInt(&ok);
159 if (!ok)
160 {
161 return QDateTime();
162 }
163 if (tok.size() > 5)
164 {
165 msecs = tok.at(5).toInt(&ok);
166 if (!ok)
167 {
168 return QDateTime();
169 }
170 }
171 }
172
173 QDate dt(year, 1, 1);
174 dt = dt.addDays(days - 1);
175
176 return QDateTime(dt, QTime(hours, minutes, secs, msecs), Qt::UTC);
177 break;
178 }
179 case ISO:
180 {
181 // Using the ISO format yyyy-MM-ddThh:mm:ss[.zzz]
182 QString tmp = s;
183 if (tmp.length() < 19)
184 {
185 tmp += ":00";
186 }
187 QDateTime dt = QDateTime::fromString(tmp, Qt::ISODate);
188 dt.setTimeSpec(Qt::UTC);
189 return dt;
190 break;
191 }
192 case CUC:
193 {
194 // Get the CUC coarse and fine values
195 bool ok = false;
196 int coarse = s.left(8).toLong(&ok, 16);
197 if (!ok)
198 {
199 return QDateTime();
200 }
201 int fine = 0;
202 if (s.size() == 12)
203 {
204 fine = s.mid(8, 4).toLong(&ok, 16);
205 if (!ok)
206 {
207 return QDateTime();
208 }
209 }
210
211 // Get the date/time value
212 QDateTime tm = mEpoch.addSecs(coarse);
213 tm = tm.addMSecs(rint((double(fine) / 58.0 * 885.0) / 1000.0));
214 return tm;
215
216 break;
217 }
218 default:
219 {
220 return QDateTime();
221 }
222 }
223 }
224
225 QString Internal::DateTime::asISOstring() const
226 {
227 if (mDateTime.isValid())
228 {
229 return mDateTime.toString("yyyy-MM-ddThh:mm:ss.zzz");
230 }
231 return QString();
232 }
233
234 QString Internal::DateTime::asASDstring() const
235 {
236 if (mDateTime.isValid())
237 {
238 return QString("%1.%2.%3.%4.%5.%6")
239 .arg(mDateTime.date().year(), 4, 10, QChar('0'))
240 .arg(mDateTime.date().dayOfYear(), 3, 10, QChar('0'))
241 .arg(mDateTime.time().hour(), 2, 10, QChar('0'))
242 .arg(mDateTime.time().minute(), 2, 10, QChar('0'))
243 .arg(mDateTime.time().second(), 2, 10, QChar('0'))
244 .arg(mDateTime.time().msec(), 3, 10, QChar('0'));
245 }
246 return QString();
247 }
248
249 QString Internal::DateTime::asCUChexString() const
250 {
251 if (!mDateTime.isValid())
252 {
253 return QString();
254 }
255
256 // Convert to CUC coarse and fine values
257 qint64 msecs = mEpoch.msecsTo(mDateTime);
258 quint32 coarse = quint32(msecs / 1000);
259 quint32 fine = quint32(rint(1000.0 * double(msecs % 1000) * 58.0 / 885.0));
260
261 // Set the CUC hex string
262 return QString("%1%2").arg(coarse, 8, 16, QChar('0')).arg(fine, 4, 16, QChar('0'));
263 }
264
265
266 //-------------------------------------------------------------------
267
268 Module::Module()
269 : Plugins::iPlugin()
270 , mReady(false)
271 , mLastDateTimeType(Internal::DateTime::Invalid)
272 {
273 setObjectName(QString("%1.%2").arg(VER_MODULE_NAME_STR).arg(__FUNCTION__));
274
275 EVAF_INFO("%s created", qPrintable(objectName()));
276 }
277
278 Module::~Module()
279 {
280 EVAF_INFO("%s destroyed", qPrintable(objectName()));
281 }
282
283 bool Module::init(QString const & args)
284 {
285 Q_UNUSED(args);
286
287 // Get the main window interface and fill it with the widgets
288 SdiWindow::iSdiWindow * win = evafQueryInterface<SdiWindow::iSdiWindow>("iSdiWindow");
289 EVAF_TEST_X(win, "No iSdiWindow interface");
290
291 Gui::Panel * panel = new Gui::Panel;
292 win->addPanel("PswGen", panel);
293
294 QVBoxLayout * v = new QVBoxLayout;
295 panel->setLayout(v);
296
297 QGridLayout * g = new QGridLayout;
298 v->addLayout(g);
299 g->setColumnStretch(1, 2);
300
301 QLabel * l = new QLabel(tr("&Epoch:", VER_MODULE_NAME_STR));
302 l->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
303 g->addWidget(l, 0, 0);
304
305 wEpoch = new QComboBox;
306 l->setBuddy(wEpoch);
307 wEpoch->setEditable(true);
308 {
309 QRegExp rx("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?$");
310 wEpoch->setValidator(new QRegExpValidator(rx, wEpoch));
311 }
312 wEpoch->addItems(QStringList() << "1970-01-01T00:00:00.000" << "1999-08-22T00:00:00.000");
313 connect(wEpoch, SIGNAL(editTextChanged(QString)), this, SLOT(epochChanged(QString)));
314 g->addWidget(wEpoch, 0, 1);
315 panel->setFocusProxy(wEpoch);
316
317 l = new QLabel(tr("&Date/time:", VER_MODULE_NAME_STR));
318 l->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
319 g->addWidget(l, 1, 0);
320
321 wDateTime = new QLineEdit;
322 l->setBuddy(wDateTime);
323 {
324 QRegExp rx("^(\\d{4}\\.\\d{3}\\.\\d{2}\\.\\d{2}\\.\\d{2}(\\.\\d{3})?)|(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(.\\d{3})?)$");
325 wDateTime->setValidator(new QRegExpValidator(rx, wDateTime));
326 }
327 connect(wDateTime, SIGNAL(textEdited(QString)), this, SLOT(dateTimeEdited(QString)));
328 g->addWidget(wDateTime, 1, 1);
329
330 QPushButton * btn = new QPushButton();
331 btn->setDisabled(true);
332 g->addWidget(btn, 1, 2);
333 connect(btn, SIGNAL(clicked()), this, SLOT(dateTimeClicked()));
334 wConvertDateTime = btn;
335
336 l = new QLabel(tr("&CUC:", VER_MODULE_NAME_STR));
337 l->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
338 g->addWidget(l, 2, 0);
339
340 wCucHex = new QLineEdit;
341 l->setBuddy(wCucHex);
342 wCucHex->setMaxLength(12);
343 {
344 QRegExp rx("^[0-9a-f]{8}([0-9a-f]{4})?$", Qt::CaseInsensitive);
345 wCucHex->setValidator(new QRegExpValidator(rx, wCucHex));
346 }
347 connect(wCucHex, SIGNAL(textEdited(QString)), this, SLOT(cucHexEdited(QString)));
348 g->addWidget(wCucHex, 2, 1);
349
350 v->addStretch();
351
352 QHBoxLayout * h = new QHBoxLayout;
353 h->addStretch();
354 v->addLayout(h);
355
356 QAction * a = new QAction(panel);
357 a->setShortcut(Qt::Key_Escape);
358 connect(a, SIGNAL(triggered()), qApp, SLOT(quit()));
359 panel->addAction(a);
360
361 mReady = true;
362
363 EVAF_INFO("%s initialized", qPrintable(objectName()));
364
365 return true;
366 }
367
368 void Module::done()
369 {
370 mReady = false;
371
372 EVAF_INFO("%s finalized", qPrintable(objectName()));
373 }
374
375 void Module::dateTimeClicked()
376 {
377 if (!mDateTime.isValid())
378 {
379 return;
380 }
381
382 // Convert to another type
383 switch (mLastDateTimeType)
384 {
385 case Internal::DateTime::ISO:
386 {
387 mLastDateTimeType = Internal::DateTime::ASD;
388 wDateTime->setText(mDateTime.asASDstring());
389 wConvertDateTime->setText(tr("&to ISO", VER_MODULE_NAME_STR));
390 break;
391 }
392 case Internal::DateTime::ASD:
393 {
394 mLastDateTimeType = Internal::DateTime::ISO;
395 wDateTime->setText(mDateTime.asISOstring());
396 wConvertDateTime->setText(tr("&to ASD", VER_MODULE_NAME_STR));
397 break;
398 }
399 default:
400 break;
401 }
402 }
403
404 void Module::dateTimeEdited(QString const & s)
405 {
406 wConvertDateTime->setDisabled(true);
407 wConvertDateTime->setText(QString());
408 mLastDateTimeType = Internal::DateTime::Invalid;
409
410 if (s.isEmpty())
411 {
412 return;
413 }
414
415 mDateTime.setDateTime(s);
416 if (!mDateTime.isValid())
417 {
418 return;
419 }
420
421 mLastDateTimeType = mDateTime.type();
422
423 // Set the CUC hex string from this date/time string
424 wCucHex->setText(mDateTime.asCUChexString());
425
426 // Enable the button that converts between ISO and ASD date/time strings
427 if (mDateTime.type() == Internal::DateTime::ISO)
428 {
429 wConvertDateTime->setEnabled(true);
430 wConvertDateTime->setText(tr("&to ASD", VER_MODULE_NAME_STR));
431 }
432 else if (mDateTime.type() == Internal::DateTime::ISO)
433 {
434 wConvertDateTime->setEnabled(true);
435 wConvertDateTime->setText(tr("&to ISO", VER_MODULE_NAME_STR));
436 }
437 }
438
439 void Module::cucHexEdited(QString const & s)
440 {
441 if (s.isEmpty() || s.size() < 8)
442 {
443 return;
444 }
445
446 mDateTime.setDateTime(s);
447 if (!mDateTime.isValid())
448 {
449 return;
450 }
451
452 if (mLastDateTimeType == Internal::DateTime::Invalid)
453 {
454 mLastDateTimeType = Internal::DateTime::ASD;
455 }
456
457 // Set the date/time string in the last used format
458 if (mLastDateTimeType == Internal::DateTime::ASD)
459 {
460 wDateTime->setText(mDateTime.asASDstring());
461 wConvertDateTime->setEnabled(true);
462 wConvertDateTime->setText(tr("&to ISO", VER_MODULE_NAME_STR));
463 }
464 else if (mLastDateTimeType == Internal::DateTime::ISO)
465 {
466 wDateTime->setText(mDateTime.asISOstring());
467 wConvertDateTime->setEnabled(true);
468 wConvertDateTime->setText(tr("&to ASD", VER_MODULE_NAME_STR));
469 }
470 }
471
472 void Module::epochChanged(QString const & s)
473 {
474 if (s.isEmpty())
475 return;
476 QRegExp rx("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?$");
477 if (!rx.exactMatch(s))
478 return;
479
480 QDateTime dt = QDateTime::fromString(s, Qt::ISODate);
481 dt.setTimeSpec(Qt::UTC);
482 mDateTime.setEpoch(dt);
483
484 // If there is a valid entry, do the conversion
485 switch (mDateTime.type())
486 {
487 case Internal::DateTime::ISO:
488 case Internal::DateTime::ASD:
489 {
490 wCucHex->setText(mDateTime.asCUChexString());
491 break;
492 }
493 case Internal::DateTime::CUC:
494 {
495 if (mLastDateTimeType == Internal::DateTime::ASD)
496 {
497 wDateTime->setText(mDateTime.asASDstring());
498 }
499 else if (mLastDateTimeType == Internal::DateTime::ISO)
500 {
501 wDateTime->setText(mDateTime.asISOstring());
502 }
503 break;
504 }
505 default:
506 break;
507 }
508 }