diff options
Diffstat (limited to 'src/pmtime')
30 files changed, 5676 insertions, 0 deletions
diff --git a/src/pmtime/GNUmakefile b/src/pmtime/GNUmakefile new file mode 100644 index 0000000..e35c1a6 --- /dev/null +++ b/src/pmtime/GNUmakefile @@ -0,0 +1,71 @@ +TOPDIR = ../.. +COMMAND = pmtime +PROJECT = $(COMMAND).pro +include $(TOPDIR)/src/include/builddefs + +WRAPPER = $(COMMAND).sh +QRCFILE = $(COMMAND).qrc +RCFILE = $(COMMAND).rc +ICOFILE = $(COMMAND).ico +ICNFILE = $(COMMAND).icns +XMLFILE = $(COMMAND).info +UIFILES = $(shell echo *.ui) +HEADERS = aboutdialog.h console.h pmtime.h pmtimearch.h pmtimelive.h \ + seealsodialog.h showboundsdialog.h timelord.h timezone.h +SOURCES = aboutdialog.cpp console.cpp pmtime.cpp pmtimearch.cpp pmtimelive.cpp \ + seealsodialog.cpp showboundsdialog.cpp timelord.cpp main.cpp +LSRCFILES = $(PROJECT) $(QRCFILE) $(RCFILE) $(UIFILES) $(HEADERS) \ + $(SOURCES) $(WRAPPER).in $(XMLFILE).in +LDIRT = $(COMMAND) $(WRAPPER) $(XMLFILE) images + +default: build-me + +include $(BUILDRULES) + +ifeq "$(ENABLE_QT)" "true" +build-me: images wrappers + $(QTMAKE) + $(LNMAKE) + +ifeq ($(WINDOW),mac) +PKG_MAC_DIR = /Library/PCP/$(COMMAND).app/Contents +wrappers: $(WRAPPER) $(XMLFILE) +else +wrappers: +endif + +$(WRAPPER): $(WRAPPER).in + $(SED) -e '/\# .*/b' -e 's;PKG_MAC_DIR;$(PKG_MAC_DIR);g' < $< > $@ +$(XMLFILE): $(XMLFILE).in + $(SED) -e 's;PACKAGE_VERSION;$(PACKAGE_VERSION);g' < $< > $@ + +install: default +ifneq ($(WINDOW),mac) + $(INSTALL) -m 755 $(BINARY) $(PCP_BIN_DIR)/$(COMMAND) +else + $(INSTALL) -m 755 $(WRAPPER) $(PCP_BIN_DIR)/$(COMMAND) + $(call INSTALL_DIRECTORY_HIERARCHY,$(PKG_MAC_DIR),/Library) + $(INSTALL) -m 644 $(XMLFILE) $(PKG_MAC_DIR)/Info.plist + $(INSTALL) -m 644 $(MACBUILD)/PkgInfo $(PKG_MAC_DIR)/PkgInfo + $(INSTALL) -m 755 -d $(PKG_MAC_DIR)/MacOS + $(call INSTALL_QT_FRAMEWORKS,$(BINARY)) + $(INSTALL) -m 755 $(BINARY) $(PKG_MAC_DIR)/MacOS/$(COMMAND) + rm $(BINARY) + $(INSTALL) -m 755 -d $(PKG_MAC_DIR)/Resources + $(INSTALL) -m 644 $(ICNFILE) $(PKG_MAC_DIR)/Resources/$(ICNFILE) + $(call INSTALL_QT_RESOURCES,$(PKG_MAC_DIR)/Resources) +endif +else +build-me: +install: +endif + +default_pcp: default + +install_pcp: install + +images: $(ICNFILE) + $(LN_S) $(TOPDIR)/images images + +$(ICNFILE): + $(LN_S) $(TOPDIR)/images/$(ICNFILE) $(ICNFILE) diff --git a/src/pmtime/aboutdialog.cpp b/src/pmtime/aboutdialog.cpp new file mode 100644 index 0000000..37de391 --- /dev/null +++ b/src/pmtime/aboutdialog.cpp @@ -0,0 +1,29 @@ +/* + * 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 "aboutdialog.h" +#include <pcp/pmapi.h> + +AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent) +{ + setupUi(this); + QRegExp rx("\\b(VERSION)\\b"); + QString version = versionTextLabel->text(); + version.replace(rx, pmGetConfig("PCP_VERSION")); + versionTextLabel->setText(version); +} + +void AboutDialog::aboutOKButton_clicked() +{ + done(0); +} diff --git a/src/pmtime/aboutdialog.h b/src/pmtime/aboutdialog.h new file mode 100644 index 0000000..17ca543 --- /dev/null +++ b/src/pmtime/aboutdialog.h @@ -0,0 +1,30 @@ +/* + * 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. + */ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include "ui_aboutdialog.h" + +class AboutDialog : public QDialog, public Ui::AboutDialog +{ + Q_OBJECT + +public: + AboutDialog(QWidget* parent); + +public slots: + virtual void aboutOKButton_clicked(); +}; + +#endif // ABOUTDIALOG_H diff --git a/src/pmtime/aboutdialog.ui b/src/pmtime/aboutdialog.ui new file mode 100644 index 0000000..9b10993 --- /dev/null +++ b/src/pmtime/aboutdialog.ui @@ -0,0 +1,229 @@ +<ui version="4.0" > + <class>AboutDialog</class> + <widget class="QDialog" name="AboutDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>330</width> + <height>240</height> + </rect> + </property> + <property name="minimumSize" > + <size> + <width>330</width> + <height>240</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>330</width> + <height>260</height> + </size> + </property> + <property name="windowTitle" > + <string>About</string> + </property> + <property name="windowIcon" > + <iconset resource="pmtime.qrc" >:/images/pmtime.png</iconset> + </property> + <property name="modal" > + <bool>true</bool> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="0" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="aboutPixmapLabel" > + <property name="pixmap" > + <pixmap resource="pmtime.qrc" >:/images/pmtime.png</pixmap> + </property> + <property name="scaledContents" > + <bool>false</bool> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="versionTextLabel" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string><b><i>pmtime</i></b><br> +<b><i>Version VERSION</i></b></string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="authorsTextLabel" > + <property name="midLineWidth" > + <number>0</number> + </property> + <property name="text" > + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Copyright 2012, Red Hat.<br />Copyright 2012, Nathan Scott.<br />Copyright 2006, Ken McDonell.<br />Copyright 2006-2010, Aconex.<br />All rights reserved.<br /><span style=" font-style:italic;"><br />This program is licensed under the<br />GNU General Public License.<br /></span></p></body></html></string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>5</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="aboutOKButton" > + <property name="focusPolicy" > + <enum>Qt::TabFocus</enum> + </property> + <property name="text" > + <string>OK</string> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources> + <include location="pmtime.qrc" /> + </resources> + <connections> + <connection> + <sender>aboutOKButton</sender> + <signal>clicked()</signal> + <receiver>AboutDialog</receiver> + <slot>aboutOKButton_clicked()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/pmtime/console.cpp b/src/pmtime/console.cpp new file mode 100644 index 0000000..cb346b9 --- /dev/null +++ b/src/pmtime/console.cpp @@ -0,0 +1,65 @@ +/* + * 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 "console.h" +#include "pmtime.h" +#include <stdarg.h> +#include <pcp/pmapi.h> +#include <pcp/impl.h> + +Console *console; + +Console::Console() : QDialog() +{ + my.level = 0; + if (pmDebug & DBG_TRACE_APPL0) + my.level |= PmTime::DebugApp; // pmtime apps internals + if (pmDebug & DBG_TRACE_APPL1) + my.level |= PmTime::DebugProtocol; // trace pmtime protocol + setupUi(this); +} + +void Console::post(const char *fmt, ...) +{ + static char buffer[4096]; + va_list ap; + + if (!(my.level & PmTime::DebugApp)) + return; + + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + fputs(buffer, stderr); + fputc('\n', stderr); + text->append(QString(buffer)); +} + +void Console::post(int level, const char *fmt, ...) +{ + static char buffer[4096]; + va_list ap; + + if (!(my.level & level)) + return; + + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + fputs(buffer, stderr); + fputc('\n', stderr); + text->append(QString(buffer)); +} diff --git a/src/pmtime/console.h b/src/pmtime/console.h new file mode 100644 index 0000000..7776616 --- /dev/null +++ b/src/pmtime/console.h @@ -0,0 +1,37 @@ +/* + * 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. + */ +#ifndef CONSOLE_H +#define CONSOLE_H + +#include "ui_console.h" + +class Console : public QDialog, public Ui::Console +{ + Q_OBJECT + +public: + Console(); + virtual void post(const char * p, ...); + virtual void post(int level, const char * p, ...); + +private: + struct { + int level; + } my; +}; + +extern Console *console; + +#endif // CONSOLE_H diff --git a/src/pmtime/console.ui b/src/pmtime/console.ui new file mode 100644 index 0000000..a3ea80f --- /dev/null +++ b/src/pmtime/console.ui @@ -0,0 +1,139 @@ +<ui version="4.0" > + <class>Console</class> + <widget class="QDialog" name="Console" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>493</width> + <height>326</height> + </rect> + </property> + <property name="windowTitle" > + <string>pmtime Console</string> + </property> + <property name="windowIcon" > + <iconset resource="pmtime.qrc" >:/images/pmtime.png</iconset> + </property> + <property name="sizeGripEnabled" > + <bool>true</bool> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="0" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QTextEdit" name="text" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="autoFormatting" > + <set>QTextEdit::AutoNone</set> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="buttonHide" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>85</width> + <height>30</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>85</width> + <height>30</height> + </size> + </property> + <property name="text" > + <string>&Hide</string> + </property> + <property name="shortcut" > + <string>Alt+H</string> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <resources> + <include location="pmtime.qrc" /> + </resources> + <connections> + <connection> + <sender>buttonHide</sender> + <signal>clicked()</signal> + <receiver>Console</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/pmtime/main.cpp b/src/pmtime/main.cpp new file mode 100644 index 0000000..3e85e63 --- /dev/null +++ b/src/pmtime/main.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014, 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 <QtGui/QApplication> +#include <pcp/pmapi.h> +#include <pcp/impl.h> +#include "timelord.h" +#include "pmtime.h" + +static pmOptions opts; +static pmLongOptions longopts[] = { + PMAPI_OPTIONS_HEADER("Options"), + PMOPT_GUIPORT, + PMOPT_VERSION, + PMOPT_HELP, + PMAPI_OPTIONS_END +}; + +static void setupEnvironment(void) +{ + char *value; + QString confirm = pmGetConfig("PCP_BIN_DIR"); + confirm.prepend("PCP_XCONFIRM_PROG="); + confirm.append(QChar(__pmPathSeparator())); + confirm.append("pmquery"); + if ((value = strdup((const char *)confirm.toAscii())) != NULL) + putenv(value); + if (getenv("PCP_STDERR") == NULL && // do not overwrite, for QA + ((value = strdup("PCP_STDERR=DISPLAY")) != NULL)) + putenv(value); + + QCoreApplication::setOrganizationName("PCP"); + QCoreApplication::setApplicationName("pmtime"); +} + +int main(int argc, char **argv) +{ + int autoport = 0; + + QApplication a(argc, argv); + setupEnvironment(); + + opts.short_options = "D:p:V?"; + opts.long_options = longopts; + pmGetOptions(argc, argv, &opts); + if (opts.errors || opts.optind != argc) { + pmUsageMessage(&opts); + exit(1); + } + + if (!opts.guiport) { + char *endnum, *envstr; + + autoport = 1; + if ((envstr = getenv("PMTIME_PORT")) == NULL) { + opts.guiport = PmTime::BasePort; + } else { + opts.guiport = strtol(envstr, &endnum, 0); + if (*endnum != '\0' || opts.guiport < 0) { + pmprintf( + "%s: PMTIME_PORT must be a numeric port number (not %s)\n", + pmProgname, envstr); + pmflush(); + exit(1); + } + } + } + + console = new Console; + TimeLord tl(&a); + do { + if (tl.listen(QHostAddress::LocalHost, opts.guiport)) + break; + opts.guiport++; + } while (autoport && (opts.guiport >= 0)); + + if (!opts.guiport || tl.isListening() == false) { + if (!autoport) + pmprintf("%s: cannot find an available port\n", pmProgname); + else + pmprintf("%s: cannot connect to requested port (%d)\n", + pmProgname, opts.guiport); + pmflush(); + exit(1); + } else if (autoport) { /* write to stdout for client */ + char name[32]; + int c = snprintf(name, sizeof(name), "port=%u\n", opts.guiport); + if (write(fileno(stdout), name, c + 1) < 0) { + if (errno != EPIPE) { + pmprintf("%s: cannot write port for client: %s\n", + pmProgname, strerror(errno)); + pmflush(); + } + exit(1); + } + } + + PmTimeLive hc; + PmTimeArch ac; + tl.setContext(&hc, &ac); + + hc.init(); + if (!pmDebug) hc.disableConsole(); + else hc.popup(1); + + ac.init(); + if (!pmDebug) ac.disableConsole(); + else ac.popup(1); + + a.exec(); + return 0; +} diff --git a/src/pmtime/pmtime.cpp b/src/pmtime/pmtime.cpp new file mode 100644 index 0000000..3c586f4 --- /dev/null +++ b/src/pmtime/pmtime.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014, Red Hat. + * + * 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 "pmtime.h" +#include <QtCore/QUrl> +#include <QtCore/QLibraryInfo> +#include <QtGui/QDesktopServices> +#include <QtGui/QWhatsThis> +#include <QtGui/QMessageBox> +#include <QtGui/QCloseEvent> +#include "console.h" +#include "aboutdialog.h" +#include "seealsodialog.h" +#include <pcp/pmapi.h> +#include <pcp/impl.h> + +PmTime::PmTime() : QMainWindow(NULL) +{ +} + +void PmTime::helpAbout() +{ + AboutDialog about(this); + about.exec(); +} + +void PmTime::helpAboutQt() +{ + QApplication::aboutQt(); +} + +void PmTime::helpSeeAlso() +{ + SeeAlsoDialog about(this); + about.exec(); +} + +void PmTime::whatsThis() +{ + QWhatsThis::enterWhatsThisMode(); +} + +void PmTime::helpManual() +{ + bool ok; + QString documents("file://"); + QString separator = QString(__pmPathSeparator()); + documents.append(pmGetConfig("PCP_HTML_DIR")); + documents.append(separator).append("timecontrol.html"); + ok = QDesktopServices::openUrl(QUrl(documents, QUrl::TolerantMode)); + if (!ok) { + documents.prepend("Failed to open:\n"); + QMessageBox::warning(this, pmProgname, documents); + } +} + +void PmTime::hideWindow() +{ + if (isVisible()) + hide(); + else { + show(); + raise(); + } +} + +void PmTime::popup(bool hello_popetts) +{ + if (!hello_popetts) + hide(); + else { + show(); + raise(); + } +} + +void PmTime::closeEvent(QCloseEvent *ce) +{ + hide(); + ce->ignore(); +} + +void PmTime::showConsole() +{ + console->show(); +} diff --git a/src/pmtime/pmtime.h b/src/pmtime/pmtime.h new file mode 100644 index 0000000..01faee7 --- /dev/null +++ b/src/pmtime/pmtime.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2006-2009, 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. + */ +#ifndef PMTIME_H +#define PMTIME_H + +#include <QtGui/QMainWindow> +#include <qmc_time.h> + +class PmTime : public QMainWindow, public QmcTime +{ + Q_OBJECT + +public: + typedef enum { + DebugApp = 0x1, + DebugProtocol = 0x2, + } DebugOptions; + +public: + PmTime(); + virtual void popup(bool hello_popetts); + +public slots: + virtual void helpManual(); + virtual void helpAbout(); + virtual void helpAboutQt(); + virtual void helpSeeAlso(); + virtual void whatsThis(); + virtual void hideWindow(); + virtual void showConsole(); + +protected: + virtual void closeEvent(QCloseEvent * ce); +}; + +#endif // PMTIME_H diff --git a/src/pmtime/pmtime.info.in b/src/pmtime/pmtime.info.in new file mode 100644 index 0000000..7a0842f --- /dev/null +++ b/src/pmtime/pmtime.info.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>CFBundleIconFile</key> + <string>pmtime.icns</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleGetInfoString</key> + <string>PACKAGE_VERSION</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleExecutable</key> + <string>pmtime</string> + <key>CFBundleIdentifier</key> + <string>com.aconex.pmtime</string> +</dict> +</plist> diff --git a/src/pmtime/pmtime.pro b/src/pmtime/pmtime.pro new file mode 100644 index 0000000..ad3e193 --- /dev/null +++ b/src/pmtime/pmtime.pro @@ -0,0 +1,24 @@ +TEMPLATE = app +LANGUAGE = C++ +HEADERS = console.h pmtime.h pmtimearch.h pmtimelive.h \ + aboutdialog.h seealsodialog.h showboundsdialog.h \ + timelord.h timezone.h +SOURCES = console.cpp pmtime.cpp pmtimearch.cpp pmtimelive.cpp \ + aboutdialog.cpp seealsodialog.cpp showboundsdialog.cpp \ + timelord.cpp main.cpp +FORMS = aboutdialog.ui console.ui pmtimelive.ui pmtimearch.ui \ + seealsodialog.ui showboundsdialog.ui +ICON = pmtime.icns +RC_FILE = pmtime.rc +RESOURCES = pmtime.qrc +CONFIG += qt warn_on +INCLUDEPATH += ../include ../libpcp_qwt/src ../libpcp_qmc/src +release:DESTDIR = build/debug +debug:DESTDIR = build/release +LIBS += -L../libpcp/src +LIBS += -L../libpcp_qwt/src -L../libpcp_qwt/src/$$DESTDIR +LIBS += -L../libpcp_qmc/src -L../libpcp_qmc/src/$$DESTDIR +LIBS += -lpcp_qwt -lpcp_qmc -lpcp +win32:LIBS += -lwsock32 +QT += network +QMAKE_INFO_PLIST = pmtime.info diff --git a/src/pmtime/pmtime.qrc b/src/pmtime/pmtime.qrc new file mode 100644 index 0000000..be262b0 --- /dev/null +++ b/src/pmtime/pmtime.qrc @@ -0,0 +1,24 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/play_off.png</file> + <file>images/play_on.png</file> + <file>images/fastfwd_off.png</file> + <file>images/fastfwd_on.png</file> + <file>images/fastback_off.png</file> + <file>images/fastback_on.png</file> + <file>images/back_off.png</file> + <file>images/back_on.png</file> + <file>images/stepfwd_off.png</file> + <file>images/stepfwd_on.png</file> + <file>images/stepback_off.png</file> + <file>images/stepback_on.png</file> + <file>images/stop_off.png</file> + <file>images/stop_on.png</file> + <file>images/whatsthis.png</file> + <file>images/edit-clear.png</file> + <file>images/internet-web-browser.png</file> + <file>images/pmtime.png</file> + <file>images/aboutpmtime.png</file> + <file>images/aboutpcp.png</file> +</qresource> +</RCC> diff --git a/src/pmtime/pmtime.rc b/src/pmtime/pmtime.rc new file mode 100644 index 0000000..61dd59a --- /dev/null +++ b/src/pmtime/pmtime.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "images/pmtime.ico" diff --git a/src/pmtime/pmtime.sh.in b/src/pmtime/pmtime.sh.in new file mode 100644 index 0000000..350504a --- /dev/null +++ b/src/pmtime/pmtime.sh.in @@ -0,0 +1,2 @@ +#!/bin/sh +exec PKG_MAC_DIR/MacOS/pmtime "$@" 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); +} diff --git a/src/pmtime/pmtimearch.h b/src/pmtime/pmtimearch.h new file mode 100644 index 0000000..3c06043 --- /dev/null +++ b/src/pmtime/pmtimearch.h @@ -0,0 +1,101 @@ +/* + * 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. + */ +#ifndef PMTIMEARCH_H +#define PMTIMEARCH_H + +#include "ui_pmtimearch.h" +#include "pmtime.h" +#include "console.h" +#include "showboundsdialog.h" +#include "timezone.h" + +class PmTimeArch : public PmTime, public Ui::PmTimeArch +{ + Q_OBJECT + +public: + PmTimeArch(); + + virtual void play(); + virtual void back(); + virtual void stop(); + virtual int addDelta(); + virtual int subDelta(); + virtual int timerInterval(); + virtual void displayDeltaText(); + virtual void displayPositionText(); + virtual void displayPositionSlide(); + virtual void setPositionSlideRange(); + virtual void setPositionSlideDelta(); + virtual void addTimezone(const char *string); + virtual void setTime(PmTime::Packet *k, char *tzdata); + virtual void addBound(PmTime::Packet *k, char *tzdata); + +public slots: + virtual void setControl(PmTime::State newstate, PmTime::Mode newmode); + virtual void init(); + virtual void quit(); + virtual void play_clicked(); + virtual void back_clicked(); + virtual void stop_clicked(); + virtual void timerTick(); + virtual void changeDelta(int value); + virtual void disableConsole(); + virtual void changeControl(int value); + virtual void updateTime(); + virtual void pressedPosition(); + virtual void releasedPosition(); + virtual void changedPosition(double value); + virtual void clickShowMsec(); + virtual void clickShowYear(); + virtual void resetSpeed(); + virtual void changeSpeed(double value); + virtual void showBounds(); + virtual void doneBounds(); + virtual void lineEditDelta_changed(const QString & s); + virtual void lineEditCtime_changed(const QString & s); + virtual void lineEditDelta_validate(); + virtual void lineEditCtime_validate(); + virtual void lineEditSpeed_validate(); + virtual void setTimezone(QAction *action); + +signals: + void timePulse(PmTime::Packet *); + void boundsPulse(PmTime::Packet *); + void vcrModePulse(PmTime::Packet *, int); + void tzPulse(PmTime::Packet *, char *, int, char *, int); + +private: + struct { + PmTime::Packet pmtime; + PmTime::DeltaUnits units; + struct timeval absoluteStart; + struct timeval absoluteEnd; + double speed; + + bool first; + bool showMilliseconds; + bool showYear; + + QTimer *timer; + Console *console; + ShowBounds *bounds; + QActionGroup *tzActions; + QList<TimeZone*> tzlist; + } my; +}; + +#endif // PMTIMEARCH_H diff --git a/src/pmtime/pmtimearch.ui b/src/pmtime/pmtimearch.ui new file mode 100644 index 0000000..cddc526 --- /dev/null +++ b/src/pmtime/pmtimearch.ui @@ -0,0 +1,1262 @@ +<ui version="4.0" > + <class>PmTimeArch</class> + <widget class="QMainWindow" name="PmTimeArch" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>220</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>320</width> + <height>220</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>320</width> + <height>220</height> + </size> + </property> + <property name="windowTitle" > + <string>PCP Archive Time Control</string> + </property> + <property name="windowIcon" > + <iconset resource="pmtime.qrc" >:/images/pmtime.png</iconset> + </property> + <property name="windowIconText" > + <string/> + </property> + <property name="toolTip" > + <string/> + </property> + <property name="whatsThis" > + <string><b>Archive Time Control</b><br> +A time server, sending time related information (current time, +metric value fetch interval, timezone) to one or more client +programs (e.g. pmchart, pmval, etc).</string> + </property> + <widget class="QWidget" name="widget" > + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="0" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="intervalLabel" > + <property name="toolTip" > + <string comment="Time interval between performance metric value samples" >Metric sampling interval</string> + </property> + <property name="whatsThis" > + <string><b>Metric sampling interval</b><br> +Numeric values entered into these fields will form the number of +hours, minutes and/or seconds between PCP metric value +samples in Time Control client programs (e.g. pmchart, pmval, etc).</string> + </property> + <property name="text" > + <string>Interval</string> + </property> + <property name="scaledContents" > + <bool>false</bool> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="positionLabel" > + <property name="whatsThis" > + <string><b>Time Position</b><br> +This text field displays the current time position. This value is +sent to clients (e.g. pmchart, pmval) of this Time Control +program to synchronise them in terms of the progression of time +and also with respect to any "play mode" changes requested.</string> + </property> + <property name="text" > + <string>Position</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLineEdit" name="lineEditDelta" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>95</width> + <height>0</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>95</width> + <height>32767</height> + </size> + </property> + <property name="toolTip" > + <string comment="Time interval between performance metric value samples" >Metric sampling interval</string> + </property> + <property name="whatsThis" > + <string><b>Metric sampling interval</b><br> +Numeric values entered into these fields will form the number of +hours, minutes and/or seconds between PCP metric value +samples in Time Control client programs (e.g. pmchart, pmval, etc).</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBoxDelta" > + <property name="whatsThis" > + <string><b>Sampling interval units</b><br> +Displays the units in which the sampling interval is entered. +Metric values can be sampled in days, hours, minutes, seconds +or milliseconds in Time Control client programs (e.g. pmchart, +pmval, etc).</string> + </property> + <property name="currentIndex" > + <number>1</number> + </property> + <item> + <property name="text" > + <string>Milliseconds</string> + </property> + </item> + <item> + <property name="text" > + <string>Seconds</string> + </property> + </item> + <item> + <property name="text" > + <string>Minutes</string> + </property> + </item> + <item> + <property name="text" > + <string>Hours</string> + </property> + </item> + <item> + <property name="text" > + <string>Days</string> + </property> + </item> + <item> + <property name="text" > + <string>Weeks</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLineEdit" name="lineEditCtime" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>226</width> + <height>0</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>226</width> + <height>32767</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Time Position</b><br> +This text field displays the current time position. This value is +sent to clients (e.g. pmchart, pmval) of this Time Control +program to synchronise them in terms of the progression of time +and also with respect to any "play mode" changes requested.</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="QwtSlider" name="sliderPosition" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>290</width> + <height>16</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>290</width> + <height>14</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Time Position</b><br> +This slider modifies the current time position. This value is +sent to clients (e.g. pmchart, pmval) of this Time Control +program to synchronise them in terms of the progression of time +and also with respect to any "play mode" changes requested.</string> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="backgroundStyle" > + <enum>QwtSlider::Groove</enum> + </property> + <property name="handleSize" > + <size> + <width>12</width> + <height>16</height> + </size> + </property> + <property name="borderWidth" > + <number>2</number> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>8</number> + </property> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>4</number> + </property> + <item> + <widget class="QLabel" name="textLabel3" > + <property name="text" > + <string>Archive Control</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBoxSpeed" > + <property name="currentIndex" > + <number>1</number> + </property> + <item> + <property name="text" > + <string>Step</string> + </property> + </item> + <item> + <property name="text" > + <string>Normal</string> + </property> + </item> + <item> + <property name="text" > + <string>Fast</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QToolButton" name="buttonBack" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>54</width> + <height>36</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Back/Step/Rewind</b><br> +Pressing this button will cause time to decrease, as seen by the +Time Control program and all of its clients (e.g. pmchart, pmval, +etc). In Backward mode, time will decrease in decrements of the +current Interval (subject to Speed setting). In Step mode, time +will decrease one Interval and then wait. In Rewind mode, time +will move backward at a very rapid rate in decrements of the current +time Interval.</string> + </property> + <property name="text" > + <string/> + </property> + <property name="iconSize" > + <size> + <width>52</width> + <height>32</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="buttonStop" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>54</width> + <height>36</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Stop</b><br> +Pressing this button will stop the movement of time, as seen by +the Time Control program and all of its clients (e.g. pmchart, +pmval, etc). Unless stopped, time either advances/retreats in +increments of the current Interval.</string> + </property> + <property name="text" > + <string/> + </property> + <property name="iconSize" > + <size> + <width>52</width> + <height>32</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="buttonPlay" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>54</width> + <height>36</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Play/Step/FastFwd</b><br> +Pressing this button will begin the advancement of time, as seen +by the Time Control program and all of its clients (e.g. pmchart, +pmval, etc). In Play mode, time will advance in increments of the +current Interval (subject to Speed setting). In Step mode, time +will advance one Interval and then wait. In Fast Forward mode, time +will move forward at a very rapid rate in increments of the current +time Interval.</string> + </property> + <property name="text" > + <string/> + </property> + <property name="iconSize" > + <size> + <width>52</width> + <height>32</height> + </size> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="textLabelSpeed" > + <property name="whatsThis" > + <string><b>Time Speed</b><br> +The Speed at which Time clients are updated with respect to the current +time Position can be modified here. This only affects normal play/back +modes.</string> + </property> + <property name="text" > + <string>Speed</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEditSpeed" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>45</width> + <height>0</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>45</width> + <height>32767</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Time Speed</b><br> +The Speed at which Time clients are updated with respect to the current +time Position can be modified here. This only affects normal play/back +modes.</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="buttonSpeed" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Speed Reset</b><br> +The Speed at which Time clients are updated with respect to the current +time Position can be reset (to one) here. This affects normal play/back +modes only.</string> + </property> + </widget> + </item> + <item> + <widget class="QwtWheel" name="wheelSpeed" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>155</width> + <height>16</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>155</width> + <height>16</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Speed Wheel</b><br> +The Speed at which Time clients are updated with respect to the current +time Position can be modified by rotating the wheel left (slower) and +right (faster). This only affects normal play/back modes.</string> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="wheelBorderWidth" > + <number>1</number> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="MenuBar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>25</height> + </rect> + </property> + <widget class="QMenu" name="File" > + <property name="title" > + <string>&File</string> + </property> + <addaction name="fileHideAction" /> + </widget> + <widget class="QMenu" name="Help" > + <property name="title" > + <string>&Help</string> + </property> + <addaction name="helpManualAction" /> + <addaction name="separator" /> + <addaction name="helpAboutAction" /> + <addaction name="helpAboutQtAction" /> + <addaction name="helpSeeAlsoAction" /> + <addaction name="separator" /> + <addaction name="helpWhats_ThisAction" /> + </widget> + <widget class="QMenu" name="Options" > + <property name="title" > + <string>&Options</string> + </property> + <widget class="QMenu" name="optionsDetailAction" > + <property name="whatsThis" > + <string><b>Options|Detail</b><br> +Click this menu item to allow extended precision in time +reporting to be enabled. Optionally, milliseconds and year can +be displayed in the current time position, and days and milliseconds +can be displayed in the current time interval.</string> + </property> + <property name="title" > + <string>&Detail</string> + </property> + <addaction name="optionsDetailShow_MillisecondsAction" /> + <addaction name="optionsDetailShow_YearAction" /> + </widget> + <widget class="QMenu" name="optionsTimezoneAction" > + <property name="whatsThis" > + <string><b>Options|Timezone</b><br> +Click on this menu item to display a list of available timezones, +from which to choose. The timezone selected will be made the +new reporting timezone for all client programs (e.g. pmchart, +pmval, etc) and affects the display of timestamps in the clients +and also in the Time Control program itself.</string> + </property> + <property name="title" > + <string>&Timezone</string> + </property> + <property name="icon" > + <iconset resource="pmtime.qrc" >:/images/internet-web-browser.png</iconset> + </property> + </widget> + <addaction name="optionsTimezoneAction" /> + <addaction name="optionsDetailAction" /> + <addaction name="optionsShowBoundsAction" /> + <addaction name="optionsShowConsoleAction" /> + </widget> + <addaction name="File" /> + <addaction name="Options" /> + <addaction name="separator" /> + <addaction name="Help" /> + </widget> + <action name="helpAboutAction" > + <property name="icon" > + <iconset resource="pmtime.qrc" >:/images/pmtime.png</iconset> + </property> + <property name="text" > + <string>&About</string> + </property> + <property name="iconText" > + <string>About pmtime</string> + </property> + <property name="whatsThis" > + <string><b>Help|About</b><br> +Click this menu option to bring up the About <i>pmtime</i> dialog with version, author, and licensing information.</string> + </property> + <property name="shortcut" > + <string/> + </property> + </action> + <action name="fileHideAction" > + <property name="icon" > + <iconset resource="pmtime.qrc" >:/images/edit-clear.png</iconset> + </property> + <property name="text" > + <string>&Hide</string> + </property> + <property name="iconText" > + <string>Hide Archive Time Control window</string> + </property> + <property name="toolTip" > + <string>Hide Archive Time Control window</string> + </property> + <property name="whatsThis" > + <string><b>File|Hide</b><br> +Click on this menu option to dismiss the Time Control window, +for the time being. As the Time Control serves one or +more client programs (which are completely reliant on the Time +Controls), this action does not end the Time Control program. +Rather, it simply removes the window from the visible set.<br> +The client programs will typically provide a mechanism for making +the Time Controls visible.</string> + </property> + <property name="shortcut" > + <string>Ctrl+H</string> + </property> + </action> + <action name="optionsDetailShow_MillisecondsAction" > + <property name="checkable" > + <bool>true</bool> + </property> + <property name="text" > + <string>Show &Milliseconds</string> + </property> + <property name="iconText" > + <string>Show Milliseconds in Time</string> + </property> + <property name="whatsThis" > + <string><b>Options|Detail|Milliseconds</b><br> +Display millisecond precision (or not) in the current time interval and +position.</string> + </property> + <property name="shortcut" > + <string>Ctrl+M</string> + </property> + </action> + <action name="optionsDetailShow_YearAction" > + <property name="checkable" > + <bool>true</bool> + </property> + <property name="text" > + <string>Show &Year</string> + </property> + <property name="iconText" > + <string>Show Year in Position</string> + </property> + <property name="whatsThis" > + <string><b>Options|Detail|Year</b><br> +Display the year (or not) in the current time position.</string> + </property> + <property name="shortcut" > + <string>Ctrl+Y</string> + </property> + </action> + <action name="optionsShowBoundsAction" > + <property name="text" > + <string>&Boundaries</string> + </property> + <property name="iconText" > + <string>Archive Time Boundaries</string> + </property> + <property name="whatsThis" > + <string><b>Option|Show Console</b><br> +Click here to display the Time Boundaries dialog, which allows the +archive time boundaries (as seen by pmtime clients, like pmval and +pmchart) to be set. +Initially these are set to the start and end time of the archive +(or earliest start and latest end, if multiple archives present), +but it can be further restricted as needed here.</string> + </property> + <property name="shortcut" > + <string>Ctrl+B</string> + </property> + </action> + <action name="optionsShowConsoleAction" > + <property name="text" > + <string>&Show Console</string> + </property> + <property name="iconText" > + <string>Show Diagnostic Console</string> + </property> + <property name="whatsThis" > + <string><b>Option|Show Console</b><br> +Click here to display the diagnostic console with whatever debug +information has been sent there. This menu option is available +only when the -D command line option is in use, which allows a +level of runtime verbosity to be configured (default is "none").</string> + </property> + <property name="shortcut" > + <string>Ctrl+C</string> + </property> + </action> + <action name="helpAboutQtAction" > + <property name="text" > + <string>About Qt</string> + </property> + <property name="iconText" > + <string>About Qt</string> + </property> + <property name="whatsThis" > + <string><b>Help|About Qt</b><br> +Click on this menu option to bring up the See Also dialog, with author and website information about Qt - which provides software infrastructure on which this tool was built.</string> + </property> + </action> + <action name="helpSeeAlsoAction" > + <property name="text" > + <string>See Also</string> + </property> + <property name="iconText" > + <string>See Also</string> + </property> + <property name="whatsThis" > + <string><b>Help|See Also</b><br> +Click on this menu option to bring up the See Also dialog, with author and website information about PCP, Qwt, and Qt - all of which provide software infrastructure on which this tool was built.</string> + </property> + </action> + <action name="helpWhats_ThisAction" > + <property name="icon" > + <iconset resource="pmtime.qrc" >:/images/whatsthis.png</iconset> + </property> + <property name="text" > + <string>What's This?</string> + </property> + <property name="iconText" > + <string>"What's This?" context sensitive help</string> + </property> + <property name="whatsThis" > + <string><b>Help|What's This</b><br> +Click this menu option to invoke a small question mark that is attached to the mouse pointer. Click on a feature which you would like more information about. A popup box appears with more information about the feature.</string> + </property> + <property name="shortcut" > + <string>Shift+F1</string> + </property> + </action> + <action name="helpManualAction" > + <property name="text" > + <string>&Manual</string> + </property> + <property name="iconText" > + <string>Manual</string> + </property> + <property name="shortcut" > + <string>F1</string> + </property> + </action> + </widget> + <layoutdefault spacing="6" margin="11" /> + <customwidgets> + <customwidget> + <class>QwtSlider</class> + <extends>QWidget</extends> + <header>qwt_slider.h</header> + </customwidget> + <customwidget> + <class>QwtWheel</class> + <extends>QWidget</extends> + <header>qwt_wheel.h</header> + </customwidget> + </customwidgets> + <includes> + <include location="local" >pmtime.h</include> + <include location="local" >console.h</include> + <include location="local" >timezone.h</include> + <include location="local" >showboundsdialog.h</include> + </includes> + <resources> + <include location="pmtime.qrc" /> + </resources> + <connections> + <connection> + <sender>fileHideAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>hideWindow()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>helpAboutAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>helpAbout()</slot> + </connection> + <connection> + <sender>helpAboutQtAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>helpAboutQt()</slot> + </connection> + <connection> + <sender>helpSeeAlsoAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>helpSeeAlso()</slot> + </connection> + <connection> + <sender>buttonBack</sender> + <signal>clicked()</signal> + <receiver>PmTimeArch</receiver> + <slot>back_clicked()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonStop</sender> + <signal>clicked()</signal> + <receiver>PmTimeArch</receiver> + <slot>stop_clicked()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonPlay</sender> + <signal>clicked()</signal> + <receiver>PmTimeArch</receiver> + <slot>play_clicked()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>optionsDetailShow_MillisecondsAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>clickShowMsec()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>optionsDetailShow_YearAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>clickShowYear()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>comboBoxSpeed</sender> + <signal>activated(int)</signal> + <receiver>PmTimeArch</receiver> + <slot>changeControl(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>sliderPosition</sender> + <signal>sliderPressed()</signal> + <receiver>PmTimeArch</receiver> + <slot>pressedPosition()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>sliderPosition</sender> + <signal>sliderReleased()</signal> + <receiver>PmTimeArch</receiver> + <slot>releasedPosition()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>sliderPosition</sender> + <signal>valueChanged(double)</signal> + <receiver>PmTimeArch</receiver> + <slot>changedPosition(double)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>optionsShowBoundsAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>showBounds()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>optionsShowConsoleAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>showConsole()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>lineEditCtime</sender> + <signal>returnPressed()</signal> + <receiver>PmTimeArch</receiver> + <slot>lineEditCtime_validate()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>lineEditCtime</sender> + <signal>textChanged(QString)</signal> + <receiver>PmTimeArch</receiver> + <slot>lineEditCtime_changed(QString)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>lineEditSpeed</sender> + <signal>returnPressed()</signal> + <receiver>PmTimeArch</receiver> + <slot>lineEditSpeed_validate()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>lineEditSpeed</sender> + <signal>editingFinished()</signal> + <receiver>PmTimeArch</receiver> + <slot>lineEditSpeed_validate()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>helpWhats_ThisAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>whatsThis()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>wheelSpeed</sender> + <signal>valueChanged(double)</signal> + <receiver>PmTimeArch</receiver> + <slot>changeSpeed(double)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonSpeed</sender> + <signal>clicked()</signal> + <receiver>PmTimeArch</receiver> + <slot>resetSpeed()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>comboBoxDelta</sender> + <signal>activated(int)</signal> + <receiver>PmTimeArch</receiver> + <slot>changeDelta(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>lineEditDelta</sender> + <signal>returnPressed()</signal> + <receiver>PmTimeArch</receiver> + <slot>lineEditDelta_validate()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>lineEditDelta</sender> + <signal>textChanged(QString)</signal> + <receiver>PmTimeArch</receiver> + <slot>lineEditDelta_changed(QString)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>helpManualAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeArch</receiver> + <slot>helpManual()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/pmtime/pmtimelive.cpp b/src/pmtime/pmtimelive.cpp new file mode 100644 index 0000000..cc480c3 --- /dev/null +++ b/src/pmtime/pmtimelive.cpp @@ -0,0 +1,324 @@ +/* + * 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 "pmtimelive.h" + +#include <QtCore/QTimer> +#include <QtGui/QValidator> +#include <QtGui/QActionGroup> +#include <pcp/pmapi.h> +#include <pcp/impl.h> +#include "pmtime.h" +#include "aboutdialog.h" +#include "seealsodialog.h" + +PmTimeLive::PmTimeLive() : 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; + QIcon *stop; + QIcon *play; +} IconStateMap; + +static void setup(IconStateMap *map, PmTime::State state, + QIcon *stop, QIcon *play) +{ + map->state = state; + map->stop = stop; + map->play = play; +} + +void PmTimeLive::setControl(PmTime::State state) +{ + static IconStateMap maps[2]; + static int nmaps; + + if (!nmaps) { + nmaps = sizeof(maps) / sizeof(maps[0]); + setup(&maps[0], PmTime::StoppedState, + PmTime::icon(PmTime::StoppedOn), + PmTime::icon(PmTime::ForwardOff)); + setup(&maps[1], PmTime::ForwardState, + PmTime::icon(PmTime::StoppedOff), + PmTime::icon(PmTime::ForwardOn)); + } + + if (my.pmtime.state != state) { + for (int i = 0; i < nmaps; i++) { + if (maps[i].state == state) { + buttonStop->setIcon(*maps[i].stop); + buttonPlay->setIcon(*maps[i].play); + break; + } + } + my.pmtime.state = state; + } +} + +void PmTimeLive::init() +{ + static const char *UTC = "UTC\0Universal Coordinated Time"; + + console->post("Starting Live Time Control..."); + + my.units = PmTime::Seconds; + my.first = true; + my.tzActions = NULL; + + memset(&my.pmtime, 0, sizeof(my.pmtime)); + my.pmtime.source = PmTime::HostSource; + my.pmtime.delta.tv_sec = PmTime::DefaultDelta; + + my.showMilliseconds = false; + optionsDetailShow_MillisecondsAction->setChecked(my.showMilliseconds); + my.showYear = false; + optionsDetailShow_YearAction->setChecked(my.showYear); + + addTimezone(UTC); + displayPosition(); + displayDeltaText(); + setControl(PmTime::ForwardState); + + my.timer = new QTimer(this); + connect(my.timer, SIGNAL(timeout()), SLOT(updateTime())); + my.timer->start(timerInterval()); + lineEditDelta->setAlignment(Qt::AlignRight); + lineEditDelta->setValidator(new QDoubleValidator + (0.001, INT_MAX, 3, lineEditDelta)); +} + +void PmTimeLive::quit() +{ + console->post("live quit!\n"); +} + +int PmTimeLive::timerInterval() +{ + return (int)((my.pmtime.delta.tv_sec * 1000) + + (my.pmtime.delta.tv_usec / 1000)); +} + +void PmTimeLive::play_clicked() +{ + if (lineEditDelta->isModified()) + lineEditDelta_validate(); + if (my.pmtime.state != PmTime::ForwardState) + play(); +} + +void PmTimeLive::play() +{ + console->post("PmTimeLive::play"); + setControl(PmTime::ForwardState); + __pmtimevalNow(&my.pmtime.position); + displayPosition(); + emit vcrModePulse(&my.pmtime, 0); + if (!my.timer->isActive()) + my.timer->start(timerInterval()); +} + +void PmTimeLive::stop_clicked() +{ + if (my.pmtime.state != PmTime::StoppedState) + stop(); +} + +void PmTimeLive::stop() +{ + console->post("PmTimeLive::stop stopped time"); + setControl(PmTime::StoppedState); + my.timer->stop(); + __pmtimevalNow(&my.pmtime.position); + emit vcrModePulse(&my.pmtime, 0); +} + +void PmTimeLive::updateTime() +{ + __pmtimevalNow(&my.pmtime.position); + displayPosition(); + emit timePulse(&my.pmtime); +} + +void PmTimeLive::displayPosition() +{ + 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 PmTimeLive::clickShowMsec() +{ + if (my.showMilliseconds == true) + my.showMilliseconds = false; + else + my.showMilliseconds = true; + optionsDetailShow_MillisecondsAction->setChecked(my.showMilliseconds); + displayPosition(); +} + +void PmTimeLive::clickShowYear() +{ + if (my.showYear == true) + my.showYear = false; + else + my.showYear = true; + optionsDetailShow_YearAction->setChecked(my.showYear); + displayPosition(); +} + +void PmTimeLive::changeDelta(int value) +{ + my.units = (PmTime::DeltaUnits)value; + displayDeltaText(); +} + +void PmTimeLive::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 PmTimeLive::disableConsole() +{ + optionsShowConsoleAction->setVisible(false); +} + +void PmTimeLive::lineEditDelta_changed(const QString &) +{ + if (lineEditDelta->isModified()) + stop_clicked(); +} + +void PmTimeLive::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 PmTimeLive::setTimezone(QAction *action) +{ + for (int i = 0; i < my.tzlist.size(); i++) { + TimeZone *tz = my.tzlist.at(i); + if (tz->action() == action) { + pmUseZone(tz->handle()); + emit tzPulse(&my.pmtime, tz->tz(), strlen(tz->tz()) + 1, + tz->tzlabel(), strlen(tz->tzlabel()) + 1); + setTime(&my.pmtime, NULL); // re-display the time, no messages + console->post("PmTimeLive::setTimezone sent TZ %s(%s) to clients\n", + tz->tz(), tz->tzlabel()); + break; + } + } +} + +void PmTimeLive::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("PmTimeLive::addTimezone added TZ=%s label=%s", tz, label); +} + +void PmTimeLive::setTime(PmTime::Packet *k, char *tzdata) +{ + if (tzdata != NULL) + addTimezone(tzdata); + + if (my.first == true) { + bool reset = my.timer->isActive(); + + my.first = false; + my.pmtime.position = k->position; + my.pmtime.delta = k->delta; + my.timer->stop(); + if (reset) + my.timer->start(timerInterval()); + displayDeltaText(); + displayPosition(); + } +} diff --git a/src/pmtime/pmtimelive.h b/src/pmtime/pmtimelive.h new file mode 100644 index 0000000..2a4835b --- /dev/null +++ b/src/pmtime/pmtimelive.h @@ -0,0 +1,72 @@ +/* + * 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. + */ +#ifndef PMTIMELIVE_H +#define PMTIMELIVE_H + +#include "ui_pmtimelive.h" +#include "pmtime.h" +#include "console.h" +#include "timezone.h" + +class PmTimeLive : public PmTime, public Ui::PmTimeLive +{ + Q_OBJECT + +public: + PmTimeLive(); + + virtual void play(); + virtual void stop(); + virtual int timerInterval(); + virtual void displayPosition(); + virtual void displayDeltaText(); + virtual void addTimezone(const char *string); + virtual void setTime(PmTime::Packet *k, char *tzdata); + +public slots: + virtual void setControl(PmTime::State newstate); + virtual void init(); + virtual void quit(); + virtual void play_clicked(); + virtual void stop_clicked(); + virtual void updateTime(); + virtual void clickShowMsec(); + virtual void clickShowYear(); + virtual void changeDelta(int value); + virtual void disableConsole(); + virtual void lineEditDelta_changed(const QString &s); + virtual void lineEditDelta_validate(); + virtual void setTimezone(QAction * action); + +signals: + void timePulse(PmTime::Packet *); + void vcrModePulse(PmTime::Packet *, int); + void tzPulse(PmTime::Packet *, char *, int, char *, int); + +private: + struct { + PmTime::Packet pmtime; + PmTime::DeltaUnits units; + bool first; + bool showMilliseconds; + bool showYear; + QTimer *timer; + QActionGroup *tzActions; + QList<TimeZone *> tzlist; + } my; +}; + +#endif // PMTIMELIVE_H diff --git a/src/pmtime/pmtimelive.ui b/src/pmtime/pmtimelive.ui new file mode 100644 index 0000000..7a3b882 --- /dev/null +++ b/src/pmtime/pmtimelive.ui @@ -0,0 +1,824 @@ +<ui version="4.0" > + <class>PmTimeLive</class> + <widget class="QMainWindow" name="PmTimeLive" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>310</width> + <height>155</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>310</width> + <height>155</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>310</width> + <height>155</height> + </size> + </property> + <property name="windowTitle" > + <string>PCP Live Time Control</string> + </property> + <property name="windowIcon" > + <iconset resource="pmtime.qrc" >:/images/pmtime.png</iconset> + </property> + <property name="windowIconText" > + <string/> + </property> + <property name="toolTip" > + <string/> + </property> + <property name="whatsThis" > + <string><b>Live Time Control</b><br> +A time server, sending time related information (current time, +metric value fetch interval, timezone) to one or more client +programs (e.g. pmchart, pmval, etc).</string> + </property> + <widget class="QWidget" name="widget" > + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="0" > + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="textLabel1" > + <property name="whatsThis" > + <string><b>Sampling interval</b><br> +Numeric values entered into these fields will form the number of +days, hours, minutes, seconds, and/or milliseconds between PCP +metric value samples in Time Control client programs +(e.g. pmchart, pmval, etc).</string> + </property> + <property name="text" > + <string>Interval</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="textLabel2" > + <property name="whatsThis" > + <string><b>Time Position</b><br> +This text field displays the current time position. This value is +sent to clients (e.g. pmchart, pmval) of this Time Control +program to synchronise them in terms of the progression of time +and also with respect to any "play mode" changes requested.</string> + </property> + <property name="text" > + <string>Time</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="textLabel3" > + <property name="whatsThis" > + <string><b>Live Control</b><br> +Pressing the Stop/Play controls will begin/block the advancement +of time, as seen by the Time Control program and all of its clients +(e.g. pmchart, pmval, etc). Time will advance in increments of thecurrent Interval.</string> + </property> + <property name="text" > + <string>Control</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLineEdit" name="lineEditDelta" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>98</width> + <height>0</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>98</width> + <height>32767</height> + </size> + </property> + <property name="toolTip" > + <string comment="Time interval between performance metric value samples" >Sampling interval</string> + </property> + <property name="whatsThis" > + <string><b>Sampling interval</b><br> +Numeric values entered into these fields will form the number of +hours, minutes and/or seconds between PCP metric value +samples in Time Control client programs (e.g. pmchart, pmval, etc).</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBoxDelta" > + <property name="whatsThis" > + <string><b>Sampling interval units</b><br> +Displays the units in which the sampling interval is entered. +Metric values can be sampled in days, hours, minutes, seconds +or milliseconds in Time Control client programs (e.g. pmchart, +pmval, etc).</string> + </property> + <property name="currentIndex" > + <number>1</number> + </property> + <item> + <property name="text" > + <string>Milliseconds</string> + </property> + </item> + <item> + <property name="text" > + <string>Seconds</string> + </property> + </item> + <item> + <property name="text" > + <string>Minutes</string> + </property> + </item> + <item> + <property name="text" > + <string>Hours</string> + </property> + </item> + <item> + <property name="text" > + <string>Days</string> + </property> + </item> + <item> + <property name="text" > + <string>Weeks</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLineEdit" name="lineEditCtime" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>200</width> + <height>0</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + <property name="acceptDrops" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string comment="This is the current time position as seen by all pmtime clients (e.g. pmchart, pmval, etc)" >Current date and time display</string> + </property> + <property name="whatsThis" > + <string><b>Time Position</b><br> +This text field displays the current time position. This value is +sent to clients (e.g. pmchart, pmval) of this Time Control +program to synchronise them in terms of the progression of time +and also with respect to any "play mode" changes requested.</string> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QToolButton" name="buttonStop" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>54</width> + <height>36</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Stop</b><br> +Pressing this button will stop the advancement of time, as seen +by the Time Control program and all of its clients (e.g. pmchart, +pmval, etc). Unless stopped, time advances in increments of the +current Interval.</string> + </property> + <property name="text" > + <string/> + </property> + <property name="iconSize" > + <size> + <width>52</width> + <height>32</height> + </size> + </property> + <property name="shortcut" > + <string/> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="buttonPlay" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>54</width> + <height>36</height> + </size> + </property> + <property name="whatsThis" > + <string><b>Play</b><br> +Pressing this button will begin the advancement of time, as seen +by the Time Control program and all of its clients (e.g. pmchart, +pmval, etc). Time will advance in increments of the current +Interval.</string> + </property> + <property name="text" > + <string/> + </property> + <property name="iconSize" > + <size> + <width>52</width> + <height>32</height> + </size> + </property> + <property name="shortcut" > + <string/> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" > + <size> + <width>29</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="MenuBar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>310</width> + <height>25</height> + </rect> + </property> + <widget class="QMenu" name="Help" > + <property name="title" > + <string>&Help</string> + </property> + <addaction name="helpManualAction" /> + <addaction name="separator" /> + <addaction name="helpAboutAction" /> + <addaction name="helpAboutQtAction" /> + <addaction name="helpSeeAlsoAction" /> + <addaction name="separator" /> + <addaction name="helpWhats_ThisAction" /> + </widget> + <widget class="QMenu" name="File" > + <property name="title" > + <string>&File</string> + </property> + <addaction name="fileHideAction" /> + </widget> + <widget class="QMenu" name="Options" > + <property name="title" > + <string>&Options</string> + </property> + <widget class="QMenu" name="optionsDetailAction" > + <property name="whatsThis" > + <string><b>Options|Detail</b><br> +Click this menu item to allow extended precision in time +reporting to be enabled. Optionally, milliseconds and year can +be displayed in the current time position, and days and milliseconds +can be displayed in the current time interval.</string> + </property> + <property name="title" > + <string>&Detail</string> + </property> + <addaction name="optionsDetailShow_MillisecondsAction" /> + <addaction name="optionsDetailShow_YearAction" /> + </widget> + <widget class="QMenu" name="optionsTimezoneAction" > + <property name="windowIcon" > + <iconset resource="pmtime.qrc" >:/images/internet-web-browser.png</iconset> + </property> + <property name="whatsThis" > + <string><b>Options|Timezone</b><br> +Click on this menu item to display a list of available timezones, +from which to choose. The timezone selected will be made the +new reporting timezone for all client programs (e.g. pmchart, +pmval, etc) and affects the display of timestamps in the clients +and also in the Time Control program itself.</string> + </property> + <property name="title" > + <string>&Timezone</string> + </property> + <property name="icon" > + <iconset resource="pmtime.qrc" >:/images/internet-web-browser.png</iconset> + </property> + </widget> + <addaction name="optionsTimezoneAction" /> + <addaction name="optionsDetailAction" /> + <addaction name="optionsShowConsoleAction" /> + </widget> + <addaction name="File" /> + <addaction name="Options" /> + <addaction name="separator" /> + <addaction name="Help" /> + </widget> + <action name="helpAboutAction" > + <property name="icon" > + <iconset resource="pmtime.qrc" >:/images/pmtime.png</iconset> + </property> + <property name="text" > + <string>&About</string> + </property> + <property name="iconText" > + <string>About pmtime</string> + </property> + <property name="whatsThis" > + <string><b>Help|About</b><br> +Click this menu option to bring up the About <i>pmtime</i> dialog with version, author, and licensing information.</string> + </property> + <property name="shortcut" > + <string/> + </property> + </action> + <action name="fileHideAction" > + <property name="icon" > + <iconset resource="pmtime.qrc" >:/images/edit-clear.png</iconset> + </property> + <property name="text" > + <string>&Hide</string> + </property> + <property name="iconText" > + <string>Hide Live Time Control window</string> + </property> + <property name="toolTip" > + <string>Hide Live Time Control window</string> + </property> + <property name="whatsThis" > + <string><b>File|Hide</b><br> +Click on this menu option to dismiss the Time Control window, +for the time being. As the Time Control serves one or +more client programs (which are completely reliant on the Time +Controls), this action does not end the Time Control program. +Rather, it simply removes the window from the visible set.<br> +The client programs will typically provide a mechanism for making +the Time Controls visible.</string> + </property> + <property name="shortcut" > + <string>Ctrl+H</string> + </property> + </action> + <action name="optionsDetailShow_MillisecondsAction" > + <property name="checkable" > + <bool>true</bool> + </property> + <property name="text" > + <string>Show &Milliseconds</string> + </property> + <property name="iconText" > + <string>Show Milliseconds in Time</string> + </property> + <property name="whatsThis" > + <string><b>Options|Detail|Milliseconds</b><br> +Display millisecond precision (or not) in the current time interval and +position.</string> + </property> + <property name="shortcut" > + <string>Ctrl+M</string> + </property> + </action> + <action name="optionsDetailShow_YearAction" > + <property name="checkable" > + <bool>true</bool> + </property> + <property name="text" > + <string>Show &Year</string> + </property> + <property name="iconText" > + <string>Show Year in Time</string> + </property> + <property name="whatsThis" > + <string><b>Options|Detail|Year</b><br> +Display the year (or not) in the current time position.</string> + </property> + <property name="shortcut" > + <string>Ctrl+Y</string> + </property> + </action> + <action name="optionsShowConsoleAction" > + <property name="text" > + <string>&Show Console</string> + </property> + <property name="iconText" > + <string>Show Diagnostic Console</string> + </property> + <property name="whatsThis" > + <string><b>Option|Show Console</b><br> +Click here to display the diagnostic console with whatever debug +information has been sent there. This menu option is available +only when the -D command line option is in use, which allows a +level of runtime verbosity to be configured (default is "none").</string> + </property> + <property name="shortcut" > + <string>Ctrl+C</string> + </property> + </action> + <action name="helpAboutQtAction" > + <property name="text" > + <string>About Qt</string> + </property> + <property name="iconText" > + <string>About Qt</string> + </property> + <property name="whatsThis" > + <string><b>Help|About Qt</b><br> +Click on this menu option to bring up the See Also dialog, with author and website information about Qt - which provides software infrastructure on which this tool was built.</string> + </property> + </action> + <action name="helpSeeAlsoAction" > + <property name="text" > + <string>See Also</string> + </property> + <property name="iconText" > + <string>See Also</string> + </property> + <property name="whatsThis" > + <string><b>Help|See Also</b><br> +Click on this menu option to bring up the See Also dialog, with author and website information about PCP, Qwt, and Qt - all of which provide software infrastructure on which this tool was built.</string> + </property> + </action> + <action name="helpWhats_ThisAction" > + <property name="icon" > + <iconset resource="pmtime.qrc" >:/images/whatsthis.png</iconset> + </property> + <property name="text" > + <string>What's This?</string> + </property> + <property name="iconText" > + <string>"What's This?" context sensitive help</string> + </property> + <property name="whatsThis" > + <string><b>Help|What's This</b><br> +Click this menu option to invoke a small question mark that is attached to the mouse pointer. Click on a feature which you would like more information about. A popup box appears with more information about the feature.</string> + </property> + <property name="shortcut" > + <string>Shift+F1</string> + </property> + </action> + <action name="helpManualAction" > + <property name="text" > + <string>&Manual</string> + </property> + <property name="iconText" > + <string>Manual</string> + </property> + <property name="shortcut" > + <string>F1</string> + </property> + </action> + </widget> + <layoutdefault spacing="6" margin="11" /> + <includes> + <include location="local" >pmtime.h</include> + <include location="local" >console.h</include> + <include location="local" >timezone.h</include> + </includes> + <resources> + <include location="pmtime.qrc" /> + </resources> + <connections> + <connection> + <sender>fileHideAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>hideWindow()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>helpAboutAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>helpAbout()</slot> + </connection> + <connection> + <sender>helpAboutQtAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>helpAboutQt()</slot> + </connection> + <connection> + <sender>helpSeeAlsoAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>helpSeeAlso()</slot> + </connection> + <connection> + <sender>buttonPlay</sender> + <signal>clicked()</signal> + <receiver>PmTimeLive</receiver> + <slot>play_clicked()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonStop</sender> + <signal>clicked()</signal> + <receiver>PmTimeLive</receiver> + <slot>stop_clicked()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>optionsDetailShow_MillisecondsAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>clickShowMsec()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>optionsDetailShow_YearAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>clickShowYear()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>optionsShowConsoleAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>showConsole()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>comboBoxDelta</sender> + <signal>activated(int)</signal> + <receiver>PmTimeLive</receiver> + <slot>changeDelta(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>helpWhats_ThisAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>whatsThis()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>lineEditDelta</sender> + <signal>returnPressed()</signal> + <receiver>PmTimeLive</receiver> + <slot>lineEditDelta_validate()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>lineEditDelta</sender> + <signal>textChanged(QString)</signal> + <receiver>PmTimeLive</receiver> + <slot>lineEditDelta_changed(QString)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>helpManualAction</sender> + <signal>triggered()</signal> + <receiver>PmTimeLive</receiver> + <slot>helpManual()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/pmtime/seealsodialog.cpp b/src/pmtime/seealsodialog.cpp new file mode 100644 index 0000000..ae663e8 --- /dev/null +++ b/src/pmtime/seealsodialog.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 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 "seealsodialog.h" + +SeeAlsoDialog::SeeAlsoDialog(QWidget* parent) : QDialog(parent) +{ + setupUi(this); +} + +void SeeAlsoDialog::seeAlsoOKButton_clicked() +{ + done(0); +} diff --git a/src/pmtime/seealsodialog.h b/src/pmtime/seealsodialog.h new file mode 100644 index 0000000..b5465b6 --- /dev/null +++ b/src/pmtime/seealsodialog.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 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. + */ +#ifndef SEEALSODIALOG_H +#define SEEALSODIALOG_H + +#include "ui_seealsodialog.h" + +class SeeAlsoDialog : public QDialog, public Ui::SeeAlsoDialog +{ + Q_OBJECT + +public: + SeeAlsoDialog(QWidget* parent); + +public slots: + virtual void seeAlsoOKButton_clicked(); +}; + +#endif // SEEALSODIALOG_H diff --git a/src/pmtime/seealsodialog.ui b/src/pmtime/seealsodialog.ui new file mode 100644 index 0000000..9b81af8 --- /dev/null +++ b/src/pmtime/seealsodialog.ui @@ -0,0 +1,235 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SeeAlsoDialog</class> + <widget class="QDialog" name="SeeAlsoDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>410</width> + <height>250</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>410</width> + <height>250</height> + </size> + </property> + <property name="windowTitle"> + <string>Acknowledgements</string> + </property> + <property name="windowIcon"> + <iconset resource="pmtime.qrc"> + <normaloff>:/images/pmtime.png</normaloff>:/images/pmtime.png</iconset> + </property> + <layout class="QGridLayout"> + <property name="margin"> + <number>9</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="0" column="0"> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="seeAlsoPCPPixmapLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>76</width> + <height>82</height> + </size> + </property> + <property name="pixmap"> + <pixmap resource="pmtime.qrc">:/images/aboutpcp.png</pixmap> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>51</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="versionPCPTextLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string><b><i>Performance Co-Pilot (PCP)</i></b><br> +Copyright (c) 2010-2012 Red Hat<br> +Copyright (c) 2006-2012 Aconex<br> +Copyright (c) 1995-2007 SGI<br> +... and many, many others!<br> +<i>http://oss.sgi.com/projects/pcp</i></string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="versionQwtTextLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string><b><i>Qt Widgets for Technical Apps (Qwt)</i></b><br> +Copyright (c) 1997 Josef Wilgen<br> +Copyright (c) 2002 Uwe Rathmann<br> +<i>http://qwt.sourceforge.net/</i></string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="seeAlsoOKButton"> + <property name="focusPolicy"> + <enum>Qt::TabFocus</enum> + </property> + <property name="text"> + <string>OK</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources> + <include location="pmtime.qrc"/> + </resources> + <connections> + <connection> + <sender>seeAlsoOKButton</sender> + <signal>clicked()</signal> + <receiver>SeeAlsoDialog</receiver> + <slot>seeAlsoOKButton_clicked()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/pmtime/showboundsdialog.cpp b/src/pmtime/showboundsdialog.cpp new file mode 100644 index 0000000..6d982b3 --- /dev/null +++ b/src/pmtime/showboundsdialog.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 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 "showboundsdialog.h" +#include <QtGui/QMessageBox> +#include <pcp/pmapi.h> +#include <pcp/impl.h> +#include "pmtime.h" + +ShowBounds::ShowBounds(QWidget* parent) : QDialog(parent) +{ + setupUi(this); +} + +void ShowBounds::init( + struct timeval *absoluteStart, struct timeval *currentStart, + struct timeval *absoluteEnd, struct timeval *currentEnd) +{ + my.absoluteStart = absoluteStart; + my.currentStart = currentStart; + my.absoluteEnd = absoluteEnd; + my.currentEnd = currentEnd; + reset(); +} + +void ShowBounds::reset() +{ + my.localAbsoluteStart = PmTime::secondsFromTimeval(my.absoluteStart); + my.localCurrentStart = PmTime::secondsFromTimeval(my.currentStart); + my.localAbsoluteEnd = PmTime::secondsFromTimeval(my.absoluteEnd); + my.localCurrentEnd = PmTime::secondsFromTimeval(my.currentEnd); + +#ifdef DESPERATE + console->post(PmTime::DebugProtocol, "ShowBounds::reset START: " + "end=%u.%u(%.3f) start=%u.%u(%.3f) lend=%u.%u(%.3f) lstart=%u.%u(%.3f)", + my.absoluteEnd->tv_sec, my.absoluteEnd->tv_usec, my.localAbsoluteEnd, + my.absoluteStart->tv_sec, my.absoluteStart->tv_usec, + my.localAbsoluteStart, + my.currentEnd->tv_sec, my.currentEnd->tv_usec, my.localCurrentEnd, + my.currentStart->tv_sec, my.currentStart->tv_usec, + my.localCurrentStart); +#endif + + displayStartSlider(); + displayEndSlider(); + displayStartText(); + displayEndText(); + +#ifdef DESPERATE + console->post(PmTime::DebugProtocol, "ShowBounds::reset ENDED: " + "end=%u.%u(%.3f) start=%u.%u(%.3f) lend=%u.%u(%.3f) lstart=%u.%u(%.3f)", + my.absoluteEnd->tv_sec, my.absoluteEnd->tv_usec, my.localAbsoluteEnd, + my.absoluteStart->tv_sec, my.absoluteStart->tv_usec, + my.localAbsoluteStart, + my.currentEnd->tv_sec, my.currentEnd->tv_usec, my.localCurrentEnd, + my.currentStart->tv_sec, my.currentStart->tv_usec, + my.localCurrentStart); +#endif +} + +void ShowBounds::displayStartSlider() +{ + sliderStart->blockSignals(true); + sliderStart->setRange(my.localAbsoluteStart, my.localAbsoluteEnd); + sliderStart->setValue(my.localCurrentStart); + sliderStart->blockSignals(false); +} + +void ShowBounds::displayEndSlider() +{ + sliderEnd->blockSignals(true); + sliderEnd->setRange(my.localAbsoluteStart, my.localAbsoluteEnd); + sliderEnd->setValue(my.localCurrentEnd); + sliderEnd->blockSignals(false); +} + +void ShowBounds::displayStartText() +{ + time_t clock = (time_t)my.localCurrentStart; + char ctimebuf[32]; + + pmCtime(&clock, ctimebuf); + lineEditStart->setText(tr(ctimebuf).simplified()); + + console->post("ShowBounds::displayStartText clock=%.3f - %s", + my.localCurrentStart, + (const char *)lineEditStart->text().toAscii()); +} + +void ShowBounds::displayEndText() +{ + time_t clock = (time_t)my.localCurrentEnd; + char ctimebuf[32]; + + pmCtime(&clock, ctimebuf); + lineEditEnd->setText(tr(ctimebuf).simplified()); +} + +void ShowBounds::changedStart(double value) +{ + if (value != my.localCurrentStart) { + console->post("ShowBounds::changedStart: %.3f -> %.3f", + my.localCurrentStart, value); + my.localCurrentStart = value; + displayStartSlider(); + displayStartText(); + if (my.localCurrentStart > my.localCurrentEnd) + sliderEnd->setValue(value); + } +} + +void ShowBounds::changedEnd(double value) +{ + if (value != my.localCurrentEnd) { + console->post("ShowBounds::changedEnd %.3f -> %.3f", __func__, + my.localCurrentEnd, value); + my.localCurrentEnd = value; + displayEndSlider(); + displayEndText(); + if (my.localCurrentStart > my.localCurrentEnd) + sliderStart->setValue(value); + } +} + +// +// This routine just verifies that input from the user is correct, +// before allowing the window to be closed. If theres invalid date +// strings in start/end text boxes, we must deny the "OK" press. +// Actual work of updating pmtime is done in flush(). +// +void ShowBounds::accept() +{ + struct timeval current, start, end; + QString error, input; + char *msg; + + console->post("ShowBounds::accept: OK pressed"); + + PmTime::secondsToTimeval(my.localAbsoluteStart, &start); + PmTime::secondsToTimeval(my.localAbsoluteEnd, &end); + + if (lineEditStart->isModified()) { + input = lineEditStart->text().simplified(); + if (input.length() == 0) { + error.sprintf("Start time has not been set.\n"); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + return; + } + if (input[0] != '@') + input.prepend("@"); + if (__pmParseTime(input.toAscii(), &start, &end, ¤t, &msg) < 0) { + error.sprintf("Invalid start date/time:\n\n%s\n", msg); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + free(msg); + return; + } else if (PmTime::timevalCompare(¤t, &start) < 0 || + PmTime::timevalCompare(¤t, &end) > 0) { + error.sprintf("Start time is outside archive boundaries\n"); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + return; + } + my.localCurrentStart = PmTime::secondsFromTimeval(¤t); + console->post("ShowBounds::accept start=%.2f (abs=%.2f-%.2f)", + my.localCurrentStart, my.localAbsoluteStart, + my.localAbsoluteEnd); + } + + if (lineEditEnd->isModified()) { + input = lineEditEnd->text().simplified(); + if (input.length() == 0) { + error.sprintf("End time has not been set.\n"); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + return; + } + if (input[0] != '@') + input.prepend("@"); + if (__pmParseTime(input.toAscii(), &start, &end, ¤t, &msg) < 0) { + error.sprintf("Invalid end date/time:\n%s\n\n", msg); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + free(msg); + return; + } else if (PmTime::timevalCompare(¤t, &start) < 0 || + PmTime::timevalCompare(¤t, &end) > 0) { + error.sprintf("End time is outside the archive boundaries\n"); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + return; + } + my.localCurrentEnd = PmTime::secondsFromTimeval(¤t); + console->post("ShowBounds::accept end=%.2f (abs=%.2f-%.2f)", + my.localCurrentEnd, my.localAbsoluteStart, my.localAbsoluteEnd); + } + + if (my.localCurrentStart > my.localCurrentEnd) { + error.sprintf("Start time must be less than end time.\n"); + QMessageBox::warning(0, tr("Warning"), error, tr("Quit")); + return; + } + + emit boundsChanged(); + done(0); +} + +void ShowBounds::reject() +{ + done(1); +} + +// +// Inform parent pmtime window of accepted changes (start/end), +// via the pointers-to-struct-timevals we were initially given. +// +void ShowBounds::flush() +{ + struct timeval start, end; + + PmTime::secondsToTimeval(my.localCurrentStart, &start); + PmTime::secondsToTimeval(my.localCurrentEnd, &end); + + console->post("ShowBounds::flush updating bounds to %.2f->%.2f", + PmTime::secondsFromTimeval(&start), + PmTime::secondsFromTimeval(&end)); + + if (PmTime::timevalCompare(&start, my.currentStart) != 0) + *my.currentStart = start; + if (PmTime::timevalCompare(&end, my.currentEnd) != 0) + *my.currentEnd = end; +} diff --git a/src/pmtime/showboundsdialog.h b/src/pmtime/showboundsdialog.h new file mode 100644 index 0000000..da080c8 --- /dev/null +++ b/src/pmtime/showboundsdialog.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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. + */ +#ifndef SHOWBOUNDS_H +#define SHOWBOUNDS_H + +#include "ui_showboundsdialog.h" +#include "console.h" + +class ShowBounds : public QDialog, public Ui::ShowBounds +{ + Q_OBJECT + +public: + ShowBounds(QWidget* parent); + + virtual void init(struct timeval *, struct timeval *, + struct timeval *, struct timeval *); + virtual void reset(); + virtual void flush(); + virtual void displayStartText(); + virtual void displayEndText(); + virtual void displayStartSlider(); + virtual void displayEndSlider(); + +public slots: + virtual void changedStart(double value); + virtual void changedEnd(double value); + virtual void accept(); + virtual void reject(); + +signals: + void boundsChanged(); + +private: + struct { + double localCurrentStart; + double localCurrentEnd; + double localAbsoluteStart; + double localAbsoluteEnd; + struct timeval *currentStart; + struct timeval *currentEnd; + struct timeval *absoluteStart; + struct timeval *absoluteEnd; + } my; +}; + +#endif // SHOWBOUNDS_H diff --git a/src/pmtime/showboundsdialog.ui b/src/pmtime/showboundsdialog.ui new file mode 100644 index 0000000..8459921 --- /dev/null +++ b/src/pmtime/showboundsdialog.ui @@ -0,0 +1,303 @@ +<ui version="4.0" > + <class>ShowBounds</class> + <widget class="QDialog" name="ShowBounds" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>324</width> + <height>181</height> + </rect> + </property> + <property name="acceptDrops" > + <bool>false</bool> + </property> + <property name="windowTitle" > + <string>Archive Time Bounds</string> + </property> + <property name="windowIcon" > + <iconset resource="pmtime.qrc" >:/images/pmtime.png</iconset> + </property> + <property name="whatsThis" > + <string>The Archive Bounds dialog displays and allows modifications +to the start and end time of a (set of) PCP data archive(s). +This effectively restricts all pmtime clients (e.g. pmval, pmchart) +to a subset of the available performance data.</string> + </property> + <property name="sizeGripEnabled" > + <bool>false</bool> + </property> + <property name="modal" > + <bool>false</bool> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>7</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="0" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>4</number> + </property> + <item> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="3" column="0" colspan="2" > + <widget class="QwtSlider" name="sliderEnd" > + <property name="whatsThis" > + <string>Modify the archive end time as seen by all<br> +pmtime clients (e.g. pmval, pmchart).<br><br> +This cannot be set before the start time of the<br> +archive with the earliest start time, nor beyond<br> +of the archive with the latest end time.<br> +It also cannot be less than the current start<br> +time.</string> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="backgroundStyle" > + <enum>QwtSlider::Groove</enum> + </property> + <property name="handleSize" > + <size> + <width>12</width> + <height>16</height> + </size> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="endLabel" > + <property name="text" > + <string>End</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="startLabel" > + <property name="text" > + <string>Start</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="lineEditStart" > + <property name="whatsThis" > + <string>Modify the archive start time as seen by all<br> +pmtime clients (e.g. pmval, pmchart).<br><br> +This cannot be set before the start time of the<br> +archive with the earliest start time, nor beyond<br> +of the archive with the latest end time.<br> +It also cannot be greater than the current end<br> +time.</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QwtSlider" name="sliderStart" > + <property name="whatsThis" > + <string>Modify the archive start time as seen by all<br> +pmtime clients (e.g. pmval, pmchart).<br><br> +This cannot be set before the start time of the<br> +archive with the earliest start time, nor beyond<br> +of the archive with the latest end time.<br> +It also cannot be greater than the current end<br> +time.</string> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="backgroundStyle" > + <enum>QwtSlider::Groove</enum> + </property> + <property name="handleSize" > + <size> + <width>12</width> + <height>16</height> + </size> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLineEdit" name="lineEditEnd" > + <property name="whatsThis" > + <string>Modify the archive end time as seen by all<br> +pmtime clients (e.g. pmval, pmchart).<br><br> +This cannot be set before the start time of the<br> +archive with the earliest start time, nor beyond<br> +of the archive with the latest end time.<br> +It also cannot be less than the current start<br> +time.</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="buttonOK" > + <property name="text" > + <string>&OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCancel" > + <property name="text" > + <string>&Cancel</string> + </property> + <property name="shortcut" > + <string/> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <customwidgets> + <customwidget> + <class>QwtSlider</class> + <extends>QWidget</extends> + <header>qwt_slider.h</header> + </customwidget> + </customwidgets> + <includes> + <include location="local" >console.h</include> + </includes> + <resources> + <include location="pmtime.qrc" /> + </resources> + <connections> + <connection> + <sender>buttonOK</sender> + <signal>clicked()</signal> + <receiver>ShowBounds</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>ShowBounds</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>sliderStart</sender> + <signal>valueChanged(double)</signal> + <receiver>ShowBounds</receiver> + <slot>changedStart(double)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>sliderEnd</sender> + <signal>valueChanged(double)</signal> + <receiver>ShowBounds</receiver> + <slot>changedEnd(double)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/pmtime/timelord.cpp b/src/pmtime/timelord.cpp new file mode 100644 index 0000000..64ebcf0 --- /dev/null +++ b/src/pmtime/timelord.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (c) 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 "timelord.h" +#include <stdlib.h> + +TimeClient::TimeClient(QTcpSocket *s, QObject *p) : QObject(p) +{ + my.ac = NULL; + my.hc = NULL; + my.socket = s; + my.state = TimeClient::Disconnected; + my.source = PmTime::NoSource; + memset(&my.acktime, 0, sizeof(my.acktime)); + console->post(PmTime::DebugProtocol, "TimeClient initialised"); + connect(my.socket, SIGNAL(readyRead()), SLOT(readClient())); + connect(my.socket, SIGNAL(disconnected()), SLOT(disconnectClient())); +} + +TimeClient::~TimeClient() +{ + console->post(PmTime::DebugProtocol, "Destroying client %p", this); + my.state = TimeClient::Disconnected; + my.source = PmTime::NoSource; + emit endConnect(this); +} + +static char *stateString(int state) +{ + static char buffer[32]; + + if (state == TimeClient::Disconnected) + strcpy(buffer, "Disconnected State"); + else if (state == TimeClient::ClientConnectSET) + strcpy(buffer, "ClientConnectSET State"); + else if (state == TimeClient::ServerConnectACK) + strcpy(buffer, "ServerConnectACK State"); + else if (state == TimeClient::ServerNeedACK) + strcpy(buffer, "ServerNeedACK State"); + else if (state == TimeClient::ClientReady) + strcpy(buffer, "ClientReady State"); + else + strcpy(buffer, "Unknown State"); + return buffer; +} + +void TimeClient::disconnectClient() +{ + console->post(PmTime::DebugProtocol, "TimeClient::disconnectClient"); + delete this; +} + +bool TimeClient::writeClient(PmTime::Packet *packet, + char *tz, int tzlen, char *label, int llen) +{ + if (packet->source != my.source) + return true; + + switch (my.state) { + case TimeClient::Disconnected: + return true; + case TimeClient::ClientConnectSET: + if (packet->command == PmTime::ACK) { + my.state = TimeClient::ServerConnectACK; + break; + } + return true; + case TimeClient::ServerConnectACK: + break; + case TimeClient::ClientReady: + if (packet->command == PmTime::Step) { + my.state = TimeClient::ServerNeedACK; + my.acktime = packet->position; + } + else { + my.state = TimeClient::ClientReady; + memset(&my.acktime, 0, sizeof(my.acktime)); + } + break; + case TimeClient::ServerNeedACK: + if (packet->command != PmTime::Step) { + my.state = TimeClient::ClientReady; // clear NEED_ACK + memset(&my.acktime, 0, sizeof(my.acktime)); + break; + } + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "SKIP STEP to pos=%u.%u when client %p in NEED_ACK", + packet->position.tv_sec,packet->position.tv_usec, this); + return false; + } + + int len = my.socket->write((const char *)packet, sizeof(PmTime::Packet)); + if (len != sizeof(PmTime::Packet)) { + console->post(PmTime::DebugProtocol, "TimeCient::writeClient " + "wrote %d bytes not %d (%x command)", + len, packet->length, packet->command); + my.state = TimeClient::Disconnected; + endConnect(this); + } else { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes command=%x state=%u", + len, packet->command, my.state); + } + if (tzlen > 0 && len > 0 && + (len = my.socket->write(tz, tzlen)) != tzlen) { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes not %d (timezone)", len, tzlen); + my.state = TimeClient::Disconnected; + endConnect(this); + } else if (tzlen) { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes of timezone successfully", len); + } + if (llen > 0 && len > 0 && + (len = my.socket->write(label, llen)) != llen) { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes not %d (tz label)", len, llen); + my.state = TimeClient::Disconnected; + endConnect(this); + } else if (llen) { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes of tz label successfully", len); + } + return true; +} + +void TimeClient::readClient(void) +{ + PmTime::Packet packet; + char *payload = NULL; + int bad = 0, len, sz; + + console->post(PmTime::DebugProtocol, "Reading data from client %p", this); + + len = my.socket->read((char *)&packet, sizeof(PmTime::Packet)); + if (len < 0) { + console->post(PmTime::DebugProtocol, "Read error on client %p", this); + bad = 1; + } else if (packet.magic != PmTime::Magic) { + console->post(PmTime::DebugProtocol, "Bad magic (%x) from client %p", + packet.magic, this); + bad = 1; + } else if (len != sizeof(PmTime::Packet)) { + console->post(PmTime::DebugProtocol, + "Bad 1st read (want %d, got %d) on client %p", + len, sizeof(PmTime::Packet), this); + bad = 1; + } else if (packet.length > sizeof(PmTime::Packet)) { + sz = packet.length - sizeof(PmTime::Packet); + payload = (char *)malloc(sz); + if (payload == NULL) { + console->post(PmTime::DebugProtocol, + "No memory (%d) for second read on client %p", + sz, len, this); + bad = 1; + } else if ((len = my.socket->read(payload, sz)) != sz) { + console->post(PmTime::DebugProtocol, + "Bad 2nd read (want %d, got %d) on client %p", + sz, len, this); + bad = 1; + } + console->post(PmTime::DebugProtocol, "+%d message from client %p", + sz, this); + } else { + console->post(PmTime::DebugProtocol, "good message from client %p", + this); + } + + if (!bad) { + console->post(PmTime::DebugProtocol, "state %s message %d", + stateString(my.state), packet.command); + switch(my.state) { + case TimeClient::Disconnected: + if (packet.command == PmTime::Set) + console->post(PmTime::DebugProtocol, + "%s got new SET from client %p", + __func__, this); + if (packet.source == PmTime::HostSource) { + my.source = PmTime::HostSource; + my.hc->setTime(&packet, payload); + } else { + my.source = PmTime::ArchiveSource; + my.ac->setTime(&packet, payload); + } + my.state = TimeClient::ClientConnectSET; + packet.command = PmTime::ACK; + packet.length = sizeof(PmTime::Packet); + writeClient(&packet); + return; + + case TimeClient::ClientConnectSET: + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "bad client %p command %d in ConnectSET state", + this, packet.command); + break; + + case TimeClient::ServerConnectACK: + if (packet.command == PmTime::ACK) + my.state = TimeClient::ClientReady; + break; + + case TimeClient::ServerNeedACK: + if (packet.command != PmTime::ACK) + break; + if (packet.position.tv_sec == my.acktime.tv_sec && + packet.position.tv_usec == my.acktime.tv_usec) { + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "good ACK client=%p (%u.%u)", this, + my.acktime.tv_sec, my.acktime.tv_usec); + my.state = TimeClient::ClientReady; + break; + } + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "BAD ACK client=%p (got %u.%u vs %u.%u)", this, + packet.position.tv_sec, packet.position.tv_usec, + my.acktime.tv_sec, my.acktime.tv_usec); + bad = 1; + break; + + case TimeClient::ClientReady: + if (packet.command == PmTime::ACK) { + console->post(PmTime::DebugProtocol, "TimeClient:: readClient " + "unexpected client %p ACK in Ready state", this); + } + break; + } + + switch(packet.command) { + case PmTime::GUIHide: + case PmTime::GUIShow: + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "HIDE/SHOW from client %p", this); + if (my.source == PmTime::HostSource) + my.hc->popup(packet.command == PmTime::GUIShow); + if (my.source == PmTime::ArchiveSource) + my.ac->popup(packet.command == PmTime::GUIShow); + break; + case PmTime::Bounds: + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "BOUNDS from client %p", this); + my.ac->addBound(&packet, payload); + break; + case PmTime::ACK: + break; + default: + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "unknown command %d from client %p", + packet.command, this); + bad = 1; + } + } + + if (bad) + reset(); +} + +void TimeClient::reset() +{ + console->post(PmTime::DebugProtocol, "TimeClient::reset"); +#if 0 + if (my.source == PmTime::HostSource) + my.hc->stop(); + else + my.ac->stop(); +#endif +} + +TimeLord::TimeLord(QApplication *app) +{ + my.ac = NULL; + my.hc = NULL; + connect(this, SIGNAL(lastClientExit()), app, SLOT(quit())); + connect(this, SIGNAL(lastClientExit()), this, SLOT(quit())); + connect(this, SIGNAL(newConnection()), SLOT(newConnection())); + console->post(PmTime::DebugProtocol, "TimeLord initialised"); +} + +void TimeLord::quit() +{ + if (my.ac) + my.ac->quit(); + if (my.hc) + my.hc->quit(); +} + +void TimeLord::setContext(PmTimeLive *live, PmTimeArch *arch) +{ + my.hc = live; + connect(live, SIGNAL(timePulse(PmTime::Packet *)), + SLOT(timePulse(PmTime::Packet *))); + connect(live, SIGNAL(vcrModePulse(PmTime::Packet *, int)), + SLOT(vcrModePulse(PmTime::Packet *, int))); + connect(live, SIGNAL(tzPulse(PmTime::Packet *, char *, int, char *, int)), + SLOT(tzPulse(PmTime::Packet *, char *, int, char *, int))); + my.ac = arch; + connect(arch, SIGNAL(timePulse(PmTime::Packet *)), + SLOT(timePulse(PmTime::Packet *))); + connect(arch, SIGNAL(boundsPulse(PmTime::Packet *)), + SLOT(boundsPulse(PmTime::Packet *))); + connect(arch, SIGNAL(vcrModePulse(PmTime::Packet *, int)), + SLOT(vcrModePulse(PmTime::Packet *, int))); + connect(arch, SIGNAL(tzPulse(PmTime::Packet *, char *, int, char *, int)), + SLOT(tzPulse(PmTime::Packet *, char *, int, char *, int))); +} + +void TimeLord::newConnection(void) +{ + TimeClient *c = new TimeClient(nextPendingConnection(), this); + + console->post(PmTime::DebugProtocol, "Adding new client %p", c); + c->setContext(my.ac, my.hc); + connect(c, SIGNAL(endConnect(TimeClient *)), + SLOT(endConnect(TimeClient *))); + my.clientlist.append(c); +} + +void TimeLord::endConnect(TimeClient *client) +{ + console->post(PmTime::DebugProtocol, "Removing client %p", client); + my.clientlist.removeAll(client); + if (my.clientlist.isEmpty()) { + console->post(PmTime::DebugProtocol, "No clients remain, exiting"); + emit lastClientExit(); + } +} + +void TimeLord::timePulse(PmTime::Packet *packet) +{ + QList<TimeClient*> overrunClients; + +#if DESPERATE + static int sequence; + int localSequence = sequence++; + console->post(PmTime::DebugProtocol, "TimeLord::timePulse %d (%d clients)", + localSequence, my.clientlist.count()); +#endif + + packet->magic = PmTime::Magic; + packet->length = sizeof(PmTime::Packet); + packet->command = PmTime::Step; + for (int i = 0; i < my.clientlist.size(); i++) + if (my.clientlist.at(i)->writeClient(packet) == false) + overrunClients.append(my.clientlist.at(i)); + for (int i = 0; i < overrunClients.size(); i++) + overrunClients.at(i)->reset(); + +#if DESPERATE + console->post(PmTime::DebugProtocol, "TimeLord::timePulse ended %d (%d)", + localSequence, overrunClients.size()); +#endif +} + +void TimeLord::boundsPulse(PmTime::Packet *packet) +{ + console->post(PmTime::DebugProtocol, "TimeLord::boundsPulse (%d clients)", + my.clientlist.count()); + packet->magic = PmTime::Magic; + packet->length = sizeof(PmTime::Packet); + packet->command = PmTime::Bounds; + for (int i = 0; i < my.clientlist.size(); i++) + my.clientlist.at(i)->writeClient(packet); +} + +void TimeLord::vcrModePulse(PmTime::Packet *packet, int drag) +{ + console->post(PmTime::DebugProtocol, "TimeLord::vcrModePulse (%d clients)" + " %d", my.clientlist.count(), drag); + packet->magic = PmTime::Magic; + packet->length = sizeof(PmTime::Packet); + packet->command = drag ? PmTime::VCRModeDrag : PmTime::VCRMode; + for (int i = 0; i < my.clientlist.size(); i++) + my.clientlist.at(i)->writeClient(packet); +} + +void TimeLord::tzPulse(PmTime::Packet *packet, + char *tz, int tzlen, char *l, int llen) +{ + console->post(PmTime::DebugProtocol, "TimeLord::tzPulse (%d clients)" + " - %s/%d/%d", my.clientlist.count(), tz, tzlen, llen); + packet->magic = PmTime::Magic; + packet->length = sizeof(PmTime::Packet) + tzlen + llen; + packet->command = PmTime::TZ; + for (int i = 0; i < my.clientlist.size(); i++) + my.clientlist.at(i)->writeClient(packet, tz, tzlen, l, llen); +} diff --git a/src/pmtime/timelord.h b/src/pmtime/timelord.h new file mode 100644 index 0000000..31fedf9 --- /dev/null +++ b/src/pmtime/timelord.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 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. + */ +#ifndef TIMELORD_H +#define TIMELORD_H + +#include <QtCore/QVariant> +#include <QtCore/QTimer> +#include <QtCore/QList> +#include <QtCore/QTextStream> +#include <QtNetwork/QTcpSocket> +#include <QtNetwork/QTcpServer> +#include <QtGui/QApplication> + +#include "console.h" +#include "pmtimelive.h" +#include "pmtimearch.h" + +// The TimeClient class provides a socket that is connected with a client. +// For every client that connects to the server, the server creates a new +// instance of this class. +// +class TimeClient : public QObject +{ + Q_OBJECT + +public: + // State transitions for a pmtime client. Basic SET/ACK protocol is: + // - client connects and sends initial (global) SET + // - server responds with ACK (beware live mode, with timers SETs here) + // - server now sends SETs with optional ACKs, until first ACK recv'd + // - after first ACK recv'd by server, all subsequent SETs must be ACK'd. + // The other messages can be sent/recv'd any time after the server has + // ACK'd the initial connection. + // + typedef enum { + Disconnected = 1, + ClientConnectSET, + ServerConnectACK, + ServerNeedACK, + ClientReady, + } State; + +public: + TimeClient(QTcpSocket *socket, QObject *parent); + ~TimeClient(); + void reset(); + + void setContext(PmTimeArch *ac, PmTimeLive *hc) { my.ac = ac; my.hc = hc; } + bool writeClient(PmTime::Packet *k, char *tz = NULL, int tzlen = 0, + char *label = NULL, int llen = 0); + +signals: + void endConnect(TimeClient *); + +public slots: + void readClient(); + void disconnectClient(); + +private: + struct { + QTcpSocket *socket; + TimeClient::State state; + PmTime::Source source; + struct timeval acktime; // time position @ last STEP + PmTimeLive *hc; + PmTimeArch *ac; + } my; +}; + +// The TimeLord class is a QTcpServer which randomly travels the space- +// time continuim, servicing and connecting up clients with the server; +// it also hooks up logging of the action to the wide-screen console in +// the Tardis. +// +// For each client that connects it creates a new TimeClient (maintained +// in a QList) - the new instance is responsible for communication with +// that TCP client. +// +class TimeLord : public QTcpServer +{ + Q_OBJECT + +public: + TimeLord(QApplication *app); + void setContext(PmTimeLive *live, PmTimeArch *archive); + +signals: + void lastClientExit(); + +public slots: + void quit(); + void newConnection(); + void endConnect(TimeClient *client); + void timePulse(PmTime::Packet *k); + void boundsPulse(PmTime::Packet *k); + void vcrModePulse(PmTime::Packet *k, int drag); + void tzPulse(PmTime::Packet *k, char *t, int tlen, char *l, int llen); + +private: + struct { + PmTimeLive *hc; + PmTimeArch *ac; + QList<TimeClient*> clientlist; + } my; +}; + +#endif // TIMELORD_H diff --git a/src/pmtime/timezone.h b/src/pmtime/timezone.h new file mode 100644 index 0000000..2b8ea32 --- /dev/null +++ b/src/pmtime/timezone.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 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. + */ +#ifndef TIMEZONE_H +#define TIMEZONE_H + +#include <string.h> +#include <QtGui/QAction> + +class TimeZone +{ +public: + TimeZone(char *name, char *label, QAction *action, int handle) + { + my.name = name; + my.label = label; + my.action = action; + my.handle = handle; + } + + ~TimeZone() + { + if (my.name) free(my.name); + if (my.label) free(my.label); + if (my.action) delete my.action; + } + + char *tz(void) { return my.name; } + char *tzlabel(void) { return my.label; } + int handle(void) { return my.handle; } + QAction *action(void) { return my.action; } + +private: + struct { + char *name; + char *label; + int handle; + QAction *action; + } my; +}; + +#endif // TIMEZONE_H |