diff options
Diffstat (limited to 'src/pmtime/pmtimearch.cpp')
-rw-r--r-- | src/pmtime/pmtimearch.cpp | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/src/pmtime/pmtimearch.cpp b/src/pmtime/pmtimearch.cpp new file mode 100644 index 0000000..cb14f03 --- /dev/null +++ b/src/pmtime/pmtimearch.cpp @@ -0,0 +1,704 @@ +/* + * Copyright (c) 2012, Red Hat. + * Copyright (c) 2006, Ken McDonell. All Rights Reserved. + * Copyright (c) 2006-2007, Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "pmtimearch.h" + +#include <QtCore/QTimer> +#include <QtGui/QValidator> +#include <QtGui/QMessageBox> +#include <pcp/pmapi.h> +#include <pcp/impl.h> +#include "aboutdialog.h" +#include "seealsodialog.h" + +PmTimeArch::PmTimeArch() : PmTime() +{ + setupUi(this); +#ifdef Q_OS_MAC // fixup after relocation of the MenuBar by Qt + QSize size = QSize(width(), height() - MenuBar->height()); + setMinimumSize(size); + setMaximumSize(size); +#endif +} + +typedef struct { + PmTime::State state; + PmTime::Mode mode; + QIcon *back; + QIcon *stop; + QIcon *play; +} IconStateMap; + +static void setup(IconStateMap *map, PmTime::State state, PmTime::Mode mode, + QIcon *back, QIcon *stop, QIcon *play) +{ + map->state = state; + map->mode = mode; + map->back = back; + map->stop = stop; + map->play = play; +} + +void PmTimeArch::setControl(PmTime::State state, PmTime::Mode mode) +{ + static IconStateMap maps[3 * 3]; + static int nmaps; + + if (!nmaps) { + nmaps = sizeof(maps) / sizeof(maps[0]); + setup(&maps[0], PmTime::StoppedState, PmTime::NormalMode, + PmTime::icon(PmTime::BackwardOff), + PmTime::icon(PmTime::StoppedOn), + PmTime::icon(PmTime::ForwardOff)); + setup(&maps[1], PmTime::ForwardState, PmTime::NormalMode, + PmTime::icon(PmTime::BackwardOff), + PmTime::icon(PmTime::StoppedOff), + PmTime::icon(PmTime::ForwardOn)); + setup(&maps[2], PmTime::BackwardState, PmTime::NormalMode, + PmTime::icon(PmTime::BackwardOn), + PmTime::icon(PmTime::StoppedOff), + PmTime::icon(PmTime::ForwardOff)); + setup(&maps[3], PmTime::StoppedState, PmTime::FastMode, + PmTime::icon(PmTime::FastBackwardOff), + PmTime::icon(PmTime::StoppedOn), + PmTime::icon(PmTime::FastForwardOff)); + setup(&maps[4], PmTime::ForwardState, PmTime::FastMode, + PmTime::icon(PmTime::FastBackwardOff), + PmTime::icon(PmTime::StoppedOff), + PmTime::icon(PmTime::FastForwardOn)); + setup(&maps[5], PmTime::BackwardState, PmTime::FastMode, + PmTime::icon(PmTime::FastBackwardOn), + PmTime::icon(PmTime::StoppedOff), + PmTime::icon(PmTime::FastForwardOff)); + setup(&maps[6], PmTime::StoppedState, PmTime::StepMode, + PmTime::icon(PmTime::StepBackwardOff), + PmTime::icon(PmTime::StoppedOn), + PmTime::icon(PmTime::StepForwardOff)); + setup(&maps[7], PmTime::ForwardState, PmTime::StepMode, + PmTime::icon(PmTime::StepBackwardOff), + PmTime::icon(PmTime::StoppedOff), + PmTime::icon(PmTime::StepForwardOn)); + setup(&maps[8], PmTime::BackwardState, PmTime::StepMode, + PmTime::icon(PmTime::StepBackwardOn), + PmTime::icon(PmTime::StoppedOff), + PmTime::icon(PmTime::StepForwardOff)); + } + + if (my.pmtime.state != state || my.pmtime.mode != mode) { + for (int i = 0; i < nmaps; i++) { + if (maps[i].state == state && maps[i].mode == mode) { + buttonBack->setIcon(*maps[i].back); + buttonStop->setIcon(*maps[i].stop); + buttonPlay->setIcon(*maps[i].play); + break; + } + } + my.pmtime.state = state; + my.pmtime.mode = mode; + if (my.pmtime.mode == PmTime::NormalMode) { + buttonSpeed->setEnabled(true); + textLabelSpeed->setEnabled(true); + lineEditSpeed->setEnabled(true); + wheelSpeed->setEnabled(true); + } + else { + buttonSpeed->setEnabled(false); + textLabelSpeed->setEnabled(false); + lineEditSpeed->setEnabled(false); + wheelSpeed->setEnabled(false); + } + } +} + +void PmTimeArch::init() +{ + static char UTC[] = "UTC\0Universal Coordinated Time"; + + console->post(PmTime::DebugApp, "Starting Archive Time Control..."); + + my.units = PmTime::Seconds; + my.first = true; + my.tzActions = NULL; + + memset(&my.absoluteStart, 0, sizeof(struct timeval)); + memset(&my.absoluteEnd, 0, sizeof(struct timeval)); + memset(&my.pmtime, 0, sizeof(my.pmtime)); + my.pmtime.source = PmTime::ArchiveSource; + my.pmtime.delta.tv_sec = PmTime::DefaultDelta; + + my.showMilliseconds = false; + optionsDetailShow_MillisecondsAction->setChecked(my.showMilliseconds); + my.showYear = false; + optionsDetailShow_YearAction->setChecked(my.showYear); + + my.speed = 1.0; + my.timer = new QTimer(this); + my.timer->setInterval(timerInterval()); + my.timer->stop(); + connect(my.timer, SIGNAL(timeout()), SLOT(timerTick())); + + addTimezone(UTC); + displayDeltaText(); + setControl(PmTime::StoppedState, PmTime::NormalMode); + + double delta = PmTime::secondsFromTimeval(&my.pmtime.delta); + changeSpeed(PmTime::defaultSpeed(delta)); + wheelSpeed->setRange( + PmTime::minimumSpeed(delta), PmTime::maximumSpeed(delta), 0.1); + wheelSpeed->setValue(PmTime::defaultSpeed(delta)); + lineEditDelta->setAlignment(Qt::AlignRight); + lineEditDelta->setValidator( + new QDoubleValidator(0.001, INT_MAX, 3, lineEditDelta)); + lineEditSpeed->setAlignment(Qt::AlignRight); + lineEditSpeed->setValidator( + new QDoubleValidator(0.001, INT_MAX, 1, lineEditSpeed)); + + my.bounds = new ShowBounds(this); + my.bounds->init(&my.absoluteStart, &my.pmtime.start, + &my.absoluteEnd, &my.pmtime.end); + connect(my.bounds, SIGNAL(boundsChanged()), this, SLOT(doneBounds())); + + console->post("PmTimeArch::init absS=%p S=%p absE=%p E=%p\n", + &my.absoluteStart, &my.pmtime.start, + &my.absoluteEnd, &my.pmtime.end); +} + +void PmTimeArch::quit() +{ + console->post("arch quit!\n"); +} + +int PmTimeArch::timerInterval() +{ + return (int)(((my.pmtime.delta.tv_sec * 1000) + + (my.pmtime.delta.tv_usec / 1000)) / my.speed); +} + +void PmTimeArch::play_clicked() +{ + if (lineEditCtime->isModified()) + lineEditCtime_validate(); + if (lineEditDelta->isModified()) + lineEditDelta_validate(); + if (my.pmtime.state != PmTime::ForwardState || + my.pmtime.mode == PmTime::StepMode) + play(); +} + +void PmTimeArch::play() +{ + if (addDelta()) { + setControl(PmTime::ForwardState, my.pmtime.mode); + updateTime(); + if (my.pmtime.mode == PmTime::NormalMode) + my.timer->start(timerInterval()); + else if (my.pmtime.mode == PmTime::FastMode) + my.timer->start(PmTime::FastModeDelay); + console->post(PmTime::DebugApp, "PmTimeArch::play moved time forward"); + } else { + console->post(PmTime::DebugApp, "PmTimeArch::play reached archive end"); + emit boundsPulse(&my.pmtime); + stop(); + } +} + +void PmTimeArch::back_clicked() +{ + if (lineEditCtime->isModified()) + lineEditCtime_validate(); + if (lineEditDelta->isModified()) + lineEditDelta_validate(); + if (my.pmtime.state != PmTime::BackwardState || + my.pmtime.mode == PmTime::StepMode) + back(); +} + +void PmTimeArch::back() +{ + if (subDelta()) { + setControl(PmTime::BackwardState, my.pmtime.mode); + updateTime(); + if (my.pmtime.mode == PmTime::NormalMode) + my.timer->start(timerInterval()); + else if (my.pmtime.mode == PmTime::FastMode) + my.timer->start(PmTime::FastModeDelay); + console->post(PmTime::DebugApp, "PmTimeArch::back moved time backward"); + } else { + console->post(PmTime::DebugApp, "PmTimeArch::back reached archive end"); + emit boundsPulse(&my.pmtime); + stop(); + } +} + +void PmTimeArch::stop_clicked() +{ + if (my.pmtime.state != PmTime::StoppedState) + stop(); +} + +void PmTimeArch::stop() +{ + setControl(PmTime::StoppedState, my.pmtime.mode); + my.timer->stop(); + emit vcrModePulse(&my.pmtime, 0); + console->post(PmTime::DebugApp, "PmTimeArch::stop stopped time"); +} + +void PmTimeArch::timerTick() +{ + if (my.pmtime.state == PmTime::ForwardState) + play(); + else if (my.pmtime.state == PmTime::BackwardState) + back(); + else + console->post(PmTime::DebugApp, "PmTimeArch::timerTick: dodgey state?"); +} + +int PmTimeArch::addDelta() +{ + struct timeval current = my.pmtime.position; + +#if DESPERATE + console->post(PmTime::DebugProtocol, + "now=%u.%u end=%u.%u start=%u.%u delta=%u.%u speed=%.3e", + my.pmtime.position.tv_sec, my.pmtime.position.tv_usec, + my.pmtime.end.tv_sec, my.pmtime.end.tv_usec, my.pmtime.start.tv_sec, + my.pmtime.start.tv_usec, my.pmtime.delta.tv_sec, + my.pmtime.delta.tv_usec, speed); +#endif + + PmTime::timevalAdd(¤t, &my.pmtime.delta); + if (PmTime::timevalCompare(¤t, &my.pmtime.end) > 0 || + PmTime::timevalCompare(¤t, &my.pmtime.start) < 0) + return 0; + my.pmtime.position = current; + return 1; +} + +int PmTimeArch::subDelta() +{ + struct timeval current = my.pmtime.position; + + PmTime::timevalSub(¤t, &my.pmtime.delta); + if (PmTime::timevalCompare(¤t, &my.pmtime.end) > 0 || + PmTime::timevalCompare(¤t, &my.pmtime.start) < 0) + return 0; + my.pmtime.position = current; + return 1; +} + +void PmTimeArch::changeDelta(int value) +{ + my.units = (PmTime::DeltaUnits)value; + displayDeltaText(); +} + +void PmTimeArch::changeControl(int value) +{ + setControl(PmTime::StoppedState, (PmTime::Mode)value); +} + +void PmTimeArch::updateTime() +{ + emit timePulse(&my.pmtime); + displayPositionText(); + displayPositionSlide(); +} + +void PmTimeArch::displayDeltaText() +{ + QString text; + double delta = PmTime::secondsFromTimeval(&my.pmtime.delta); + + delta = PmTime::secondsToUnits(delta, my.units); + if ((double)(int)delta == delta) + text.sprintf("%.2f", delta); + else + text.sprintf("%.6f", delta); + lineEditDelta->setText(text); +} + +void PmTimeArch::displayPositionText() +{ + QString text; + char ctimebuf[32], msecbuf[5]; + + pmCtime(&my.pmtime.position.tv_sec, ctimebuf); + text = tr(ctimebuf); + if (my.showYear == false) + text.remove(19, 5); + if (my.showMilliseconds == true) { + sprintf(msecbuf, ".%03u", (uint)my.pmtime.position.tv_usec / 1000); + text.insert(19, msecbuf); + } + lineEditCtime->setText(text.simplified()); +} + +void PmTimeArch::displayPositionSlide(void) +{ + sliderPosition->setValue(PmTime::secondsFromTimeval(&my.pmtime.position)); +} + +void PmTimeArch::setPositionSlideRange(void) +{ + sliderPosition->setRange(PmTime::secondsFromTimeval(&my.pmtime.start), + PmTime::secondsFromTimeval(&my.pmtime.end)); +} + +void PmTimeArch::setPositionSlideDelta(void) +{ + sliderPosition->setStep(PmTime::secondsFromTimeval(&my.pmtime.delta)); +} + +void PmTimeArch::pressedPosition() +{ + emit vcrModePulse(&my.pmtime, 1); +} + +void PmTimeArch::releasedPosition() +{ + emit vcrModePulse(&my.pmtime, 0); +} + +void PmTimeArch::changedPosition(double value) +{ +#if DESPERATE + console->post("PmTimeArch::changedPosition changing pos from %d.%d", + my.pmtime.position.tv_sec, my.pmtime.position.tv_usec); +#endif + + PmTime::secondsToTimeval(value, &my.pmtime.position); + displayPositionText(); + +#if DESPERATE + console->post("PmTimeArch::changedPosition changed pos to %d.%d", + my.pmtime.position.tv_sec, my.pmtime.position.tv_usec); +#endif +} + +void PmTimeArch::clickShowMsec() +{ + if (my.showMilliseconds == true) + my.showMilliseconds = false; + else + my.showMilliseconds = true; + optionsDetailShow_MillisecondsAction->setChecked(my.showMilliseconds); + displayPositionText(); +} + +void PmTimeArch::clickShowYear() +{ + if (my.showYear == true) + my.showYear = false; + else + my.showYear = true; + optionsDetailShow_YearAction->setChecked(my.showYear); + displayPositionText(); +} + +void PmTimeArch::resetSpeed() +{ + double delta = PmTime::secondsFromTimeval(&my.pmtime.delta); + changeSpeed(PmTime::defaultSpeed(delta)); +} + +void PmTimeArch::changeSpeed(double value) +{ + QString text; + int reset = my.timer->isActive(); + double upper, lower, delta = PmTime::secondsFromTimeval(&my.pmtime.delta); + + my.timer->stop(); + + upper = PmTime::maximumSpeed(delta); + lower = PmTime::minimumSpeed(delta); + if (value > upper) + value = upper; + else if (value < lower) + value = lower; + text.sprintf("%.1f", value); + lineEditSpeed->setText(text); + if (wheelSpeed->value() != value) + wheelSpeed->setValue(value); + + my.speed = value; + if (reset) + my.timer->start(timerInterval()); + + console->post("PmTimeArch::changeSpeed changed delta to %d.%d (%.2fs)", + my.pmtime.delta.tv_sec, my.pmtime.delta.tv_usec, value); +} + +void PmTimeArch::showBounds() +{ + my.bounds->reset(); + console->post("PmTimeArch::showBounds: absS=%p S=%p absE=%p E=%p\n", + &my.absoluteStart, &my.pmtime.start, &my.absoluteEnd, &my.pmtime.end); + my.bounds->show(); +} + +void PmTimeArch::doneBounds(void) +{ + int tellclients = 0; + + console->post("PmTimeArch::doneBounds signal received\n"); + + my.bounds->flush(); + if (PmTime::timevalCompare(&my.pmtime.position, &my.pmtime.start) < 0) { + my.pmtime.position = my.pmtime.start; + tellclients = 1; + } + if (PmTime::timevalCompare(&my.pmtime.position, &my.pmtime.end) > 0) { + my.pmtime.position = my.pmtime.end; + tellclients = 1; + } + sliderPosition->blockSignals(true); + setPositionSlideRange(); + sliderPosition->blockSignals(false); + if (tellclients) + emit vcrModePulse(&my.pmtime, 0); +} + +void PmTimeArch::disableConsole() +{ + optionsShowConsoleAction->setVisible(false); +} + +void PmTimeArch::lineEditDelta_changed(const QString &) +{ + if (lineEditDelta->isModified()) + stop_clicked(); +} + +void PmTimeArch::lineEditCtime_changed(const QString &) +{ + if (lineEditCtime->isModified()) + stop_clicked(); +} + +void PmTimeArch::lineEditDelta_validate() +{ + double delta; + bool ok, reset = my.timer->isActive(); + + delta = lineEditDelta->text().toDouble(&ok); + if (!ok || delta <= 0) { + displayDeltaText(); // reset to previous, known-good delta + } else { + my.timer->stop(); + delta = PmTime::unitsToSeconds(delta, my.units); + PmTime::secondsToTimeval(delta, &my.pmtime.delta); + emit vcrModePulse(&my.pmtime, 0); + if (reset) + my.timer->start(timerInterval()); + } +} + +void PmTimeArch::lineEditCtime_validate() +{ + struct timeval current; + QString input, error; + char *msg; + + input = lineEditCtime->text().simplified(); + if (input.length() == 0) { + error.sprintf("Position time has not been set.\n"); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + return; + } + if (input[0] != '@') + input.prepend("@"); + if (__pmParseTime(input.toAscii(), + &my.pmtime.start, &my.pmtime.end, ¤t, &msg) < 0) { + error.sprintf("Invalid position date/time:\n\n%s\n", msg); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + displayPositionText(); // reset to previous, known-good position + free(msg); + } else { + my.pmtime.position = current; + displayPositionText(); + displayPositionSlide(); + emit vcrModePulse(&my.pmtime, 0); + } +} + +void PmTimeArch::lineEditSpeed_validate() +{ + double value, delta = PmTime::secondsFromTimeval(&my.pmtime.delta); + bool ok, reset = my.timer->isActive(); + + value = lineEditSpeed->text().toDouble(&ok); + if (!ok || + value < PmTime::minimumSpeed(delta) || + value > PmTime::maximumSpeed(delta)) { + wheelSpeed->setValue(my.speed); // reset to previous, known-good speed + } else { + my.speed = value; + wheelSpeed->setValue(my.speed); + if (reset) { + my.timer->stop(); + my.timer->start(timerInterval()); + } + } +} + +void PmTimeArch::setTimezone(QAction *action) +{ + for (int i = 0; i < my.tzlist.size(); i++) { + TimeZone *tz = my.tzlist.at(i); + if (tz->action() == action) { + my.first = true; // resetting time completely + pmUseZone(tz->handle()); + emit tzPulse(&my.pmtime, tz->tz(), strlen(tz->tz()) + 1, + tz->tzlabel(), strlen(tz->tzlabel()) + 1); + console->post("PmTimeArch::setTimezone sent TZ %s (%s) to clients", + tz->tz(), tz->tzlabel()); + setTime(&my.pmtime, NULL); // re-display the time, no messages + break; + } + } +} + +void PmTimeArch::addTimezone(const char *string) +{ + TimeZone *tmp, *tzp; + QAction *tzAction; + char *label, *tz; + int handle; + + if ((handle = pmNewZone(string)) < 0) + return; + + if ((tz = strdup(string)) == NULL) + return; + + if ((label = strdup(string + strlen(string) + 1)) == NULL) { + free(tz); + return; + } + + for (int i = 0; i < my.tzlist.size(); i++) { + tmp = my.tzlist.at(i); + if (strcmp(tmp->tzlabel(), label) == 0) { + free(label); + free(tz); + return; + } + } + + tzAction = new QAction(this); + tzAction->setCheckable(true); + tzAction->setToolTip(tz); + tzAction->setText(label); + + tzp = new TimeZone(tz, label, tzAction, handle); + my.tzlist.append(tzp); + + if (!my.tzActions) { + my.tzActions = new QActionGroup(this); + connect(my.tzActions, SIGNAL(selected(QAction *)), + this, SLOT(setTimezone(QAction *))); + } + my.tzActions->addAction(tzAction); + optionsTimezoneAction->addActions(my.tzActions->actions()); + console->post("PmTimeArch::addTimezone added tz=%s label=%s", tz, label); +} + +void PmTimeArch::setTime(PmTime::Packet *k, char *tzdata) +{ +#if DESPERATE + console->post(PmTime::DebugProtocol, "PmTimeArch::setTime START: " + "1st=%d now=%u.%u end=%u.%u start=%u.%u delta=%u.%u", + my.first, my.pmtime.position.tv_sec, my.pmtime.position.tv_usec, + my.pmtime.end.tv_sec, my.pmtime.end.tv_usec, my.pmtime.start.tv_sec, + my.pmtime.start.tv_usec, my.pmtime.delta.tv_sec, + my.pmtime.delta.tv_usec); +#endif + + if (my.first == true) { + my.first = false; + if (tzdata != NULL) + addTimezone(tzdata); + my.absoluteStart = my.pmtime.start = k->start; + my.absoluteEnd = my.pmtime.end = k->end; + my.pmtime.position = k->position; + my.pmtime.delta = k->delta; + sliderPosition->blockSignals(true); + setPositionSlideRange(); + setPositionSlideDelta(); + sliderPosition->blockSignals(false); + displayDeltaText(); + displayPositionText(); + displayPositionSlide(); + my.bounds->reset(); + double delta = PmTime::secondsFromTimeval(&k->delta); + changeSpeed(PmTime::defaultSpeed(delta)); + } else { + addBound(k, tzdata); + } + +#if DESPERATE + console->post(PmTime::DebugProtocol, "PmTimeArch::setTime ENDED: " + "1st=%d now=%u.%u end=%u.%u start=%u.%u delta=%u.%u", + my.first, my.pmtime.position.tv_sec, my.pmtime.position.tv_usec, + my.pmtime.end.tv_sec, my.pmtime.end.tv_usec, my.pmtime.start.tv_sec, + my.pmtime.start.tv_usec, my.pmtime.delta.tv_sec, + my.pmtime.delta.tv_usec); +#endif +} + +void PmTimeArch::addBound(PmTime::Packet *k, char *tzdata) +{ + // Note: pmchart can start pmtime up without an archive + // so, we need to explicitly initialise some fields now + // that one might otherwise have expected to be setup. + + bool needPulse = PmTime::timevalNonZero(&my.pmtime.position); + + console->post(PmTime::DebugProtocol, "PmTimeArch::addBound START: " + "p?=%d now=%u.%u end=%u.%u start=%u.%u", needPulse, + my.pmtime.position.tv_sec, my.pmtime.position.tv_usec, + my.pmtime.end.tv_sec, my.pmtime.end.tv_usec, + my.pmtime.start.tv_sec, my.pmtime.start.tv_usec); + + if (tzdata != NULL) + addTimezone(tzdata); + + if (PmTime::timevalCompare(&k->start, &my.absoluteStart) < 0) + my.absoluteStart = my.pmtime.start = k->start; + if (PmTime::timevalCompare(&k->end, &my.absoluteEnd) > 0) + my.absoluteEnd = my.pmtime.end = k->end; + if (!needPulse) { // first-time archive initialisation + my.pmtime.position = k->position; + my.pmtime.start = k->start; + my.pmtime.end = k->end; + } + + sliderPosition->blockSignals(true); + setPositionSlideRange(); + sliderPosition->blockSignals(false); + displayPositionText(); + displayPositionSlide(); + my.bounds->reset(); + + if (needPulse) + emit vcrModePulse(&my.pmtime, 0); + + console->post(PmTime::DebugProtocol, "PmTimeArch::addBound ENDED: " + "p?=%d now=%u.%u end=%u.%u start=%u.%u", needPulse, + my.pmtime.position.tv_sec, my.pmtime.position.tv_usec, + my.pmtime.end.tv_sec, my.pmtime.end.tv_usec, + my.pmtime.start.tv_sec, my.pmtime.start.tv_usec); +} |