Index: /Evidence/Edd/Edd.cc
===================================================================
--- /Evidence/Edd/Edd.cc	(revision 136)
+++ /Evidence/Edd/Edd.cc	(revision 136)
@@ -0,0 +1,394 @@
+/* ============================================================ 
+
+Edd - Evidence Data Display
+
+Qt-based graphical user interface for the Evidence contron system
+
+December 2009, Oliver Grimm
+
+============================================================ */
+
+#include "Edd.h"
+
+//////////////////////////////////////////
+// Text display for arbitary DIM service//
+//////////////////////////////////////////
+
+// Constructor
+FCS_Indicator::FCS_Indicator(char *DIMService, QWidget *P): QLineEdit(P) {
+
+  ServiceName = new char [strlen(DIMService)+1];
+  strcpy(ServiceName, DIMService);
+  
+  // Widget properties
+  setReadOnly(true);
+  setMaximumWidth(100);
+  connect(this, SIGNAL(YEP(QString)), this, SLOT(setText(QString)));
+
+  // DIM client
+  Data = new DimStampedInfo(DIMService, INT_MAX, (char *) NO_LINK, this);
+}
+
+// Destructor
+FCS_Indicator::~FCS_Indicator() {
+  delete Data;
+  delete[] ServiceName;
+}
+
+// Handling of DIM service update
+void FCS_Indicator::infoHandler() {
+
+  // Check if service available
+  QPalette Pal = palette();  
+  if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
+	emit(YEP(QString("n/a")));
+	setStatusTip(QString("%1:  unavailable").arg(ServiceName));
+    Pal.setColor(backgroundRole(), Qt::red);
+    setPalette(Pal);
+	return;
+  }
+  Pal.setColor(backgroundRole(), Qt::white);
+  setPalette(Pal);
+
+  // Translate data into ASCII
+  char *Text = EvidenceServer::ToString(getInfo());
+  QString S;
+
+  if (Text != NULL) {
+    QTextStream(&S) << Text;
+	free(Text);
+  }
+  else QTextStream(&S) << "Cannot interpret format identifier";
+    
+  if (strlen(getInfo()->getFormat()) > 1) {
+    QTextStream(&S) << " (DIM format string longer)";
+  }
+  
+  // Trigger display update
+  emit(YEP(S));
+  
+  // Update status tip
+  QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp()); 
+  setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(ServiceName, Time.toString()).arg(getInfo()->getFormat()));
+}
+
+// Open plot if mouse release within widget
+void FCS_Indicator::mouseReleaseEvent(QMouseEvent *Event) {
+
+  if (Event->button()==Qt::LeftButton && contentsRect().contains(Event->pos())) {
+    FCS_Plot *Graph = new FCS_Plot(ServiceName, NULL);
+    Graph->show();
+  }
+}
+
+// Handling of mouse press event: Register start position for drag
+void FCS_Indicator::mousePressEvent(QMouseEvent *Event) {
+
+  if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
+}
+
+// Handling of dragging (Drag and MimeData will be deleted by Qt)
+void FCS_Indicator::mouseMoveEvent(QMouseEvent *Event) {
+
+  if ((Event->buttons() & Qt::LeftButton) == 0) return;
+  if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
+
+  QDrag *Drag = new QDrag(this);
+  QMimeData *MimeData = new QMimeData;
+  MimeData->setText(QString(ServiceName));
+  Drag->setMimeData(MimeData);
+  Drag->exec();
+}
+
+
+//////////////////////////////////
+// History plot for DIM service //
+//////////////////////////////////
+
+//
+// Constructor
+//
+FCS_Plot::FCS_Plot(char *DIMService, QWidget *P): QwtPlot(P) {
+
+  // Generate name of DIM history service
+  if (asprintf(&Name, "%s.hist", DIMService) == -1) {
+    QMessageBox::warning(this, "Error","Could not generate service name with asprintf()",QMessageBox::Ok);
+    Name = NULL;
+    return;
+  }
+
+  // Enable drag&drop
+  setAcceptDrops(true);
+
+  // Graph properties
+  setAxisTitle(QwtPlot::xBottom, "Time");
+  setAxisTitle(QwtPlot::yLeft, Name);
+  setAutoReplot(false);
+  setCanvasBackground(QColor(Qt::yellow));
+  
+  Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
+  connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(HandleZoom(const QwtDoubleRect &)));
+  Panner = new QwtPlotPanner(canvas());
+  Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
+  Grid = new QwtPlotGrid;
+  Grid->setMajPen(QPen(Qt::gray, 0, Qt::DotLine));
+  Grid->attach(this);
+  Signal = new QwtPlotCurve;
+  Signal->attach(this);
+
+  // Threads may not call replot directly, but only through this signal
+  connect(this, SIGNAL(YEP()), this, SLOT(replot()));
+
+  // Context menu
+  Menu = new QMenu(this);
+  YLogAction = Menu->addAction("y scale log", this, SLOT(MenuYScale()));
+  YLogAction->setCheckable(true);
+  Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
+  NormAction = Menu->addAction("Normalize");
+  NormAction->setCheckable(true);
+  Menu->addSeparator();
+  Menu->addAction("Save plot", this, SLOT(MenuSave()));
+  Menu->addAction("Print plot", this, SLOT(MenuPrint()));
+
+  // DIM client
+  Data = new DimStampedInfo(Name, NO_LINK, this); 
+}
+
+//
+// Destructor (items with parent widget are automatically deleted)
+//
+FCS_Plot::~FCS_Plot() {
+  free(Name);
+
+  delete Data;	delete Signal;  delete Grid;
+}
+
+//
+// Handle update of DIM service
+//
+void FCS_Plot::infoHandler() {
+
+  // Check if service available
+  if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
+    setStatusTip(QString("%1:  unavailable").arg(Name));
+    return;
+  }
+
+  EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) getInfo()->getData();
+  int Count=0, Items = getInfo()->getSize()/sizeof(struct EvidenceHistoryItem);
+
+  double *x = new double [Items];
+  double *y = new double [Items];
+
+  // Find oldest item
+  int Oldest;
+  for (Oldest=1; Oldest<Items; Oldest++) {
+    if (Curr[Oldest].Seconds < Curr[Oldest-1].Seconds) break;
+  }
+
+  // Plot data starting with oldest value
+  for (int i=0; i<Items; i++) {
+    x[Count] = (double) Curr[(i+Oldest)%Items].Seconds;
+    y[Count] = Curr[(i+Oldest)%Items].Value;
+    if (x[Count] != 0) Count++;
+  }
+  
+  // Find smallest and largest item
+  double Smallest = DBL_MAX, Largest = DBL_MIN;
+  for (int i=0; i<Count; i++) {
+    if (Largest < y[i]) Largest = y[i];
+    if (Smallest > y[i]) Smallest = y[i];
+  }
+  
+  // Adapt time scale and normalize y scale if requested
+  for (int i=Count-1; i>=0; i--) {
+    x[i] = x[i]-x[0];
+    if (NormAction->isChecked()) {
+      if (Smallest != Largest) y[i] = (y[i] - Smallest)/(Largest-Smallest);
+      else y[i] = 1;
+    }  
+  }
+  
+  // Plot datas
+  Signal->setData(x, y, Count);
+  Signal->show();
+  Zoomer->setZoomBase(Signal->boundingRect());
+  emit YEP(); // Direct drawing within thread illegal!
+
+  delete[] x;
+  delete[] y;
+
+  // Update status tip
+  QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp()); 
+  setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Name, Time.toString()).arg(getInfo()->getFormat()));
+}
+
+//
+// Reset graph axes to autoscale when fully unzoomed
+//
+void FCS_Plot::HandleZoom(const QwtDoubleRect &) {
+
+  if(Zoomer->zoomRectIndex() == 0) {
+    setAxisAutoScale(QwtPlot::xBottom);
+    setAxisAutoScale(QwtPlot::yLeft);
+  }
+}
+
+//
+// Drag and drop methode
+//
+
+void FCS_Plot::dragEnterEvent(QDragEnterEvent *Event) {
+    
+  if (Event->mimeData()->hasFormat("text/plain")) Event->acceptProposedAction();
+}
+
+void FCS_Plot::dropEvent(QDropEvent *Event) {
+
+ FCS_Plot *Graph = new FCS_Plot(Event->mimeData()->text().toAscii().data(), NULL);
+ Graph->show();
+}
+    
+//
+// Context menu and its functions
+//
+void FCS_Plot::contextMenuEvent(QContextMenuEvent *Event) {
+
+  Menu->exec(Event->globalPos());
+}
+
+void FCS_Plot::MenuYScale() {
+
+  if (!YLogAction->isChecked()) {
+    setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
+  }
+  else setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine);
+  emit YEP();
+}
+
+void FCS_Plot::MenuZoomOut() {
+
+  Zoomer->zoom(0);
+  emit YEP();
+}
+
+void FCS_Plot::MenuPrint() {
+
+  QPrinter *Printer = new QPrinter;
+  QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
+  if (PrintDialog->exec() == QDialog::Accepted) {
+    QPainter Painter(Printer);
+    QPixmap Pixmap = QPixmap::grabWidget(this);
+    Painter.drawPixmap(0, 0, Pixmap);
+  }
+  delete Printer;	delete PrintDialog;
+}
+
+void FCS_Plot::MenuSave() {
+
+  QString Filename = QFileDialog::getSaveFileName(this,
+     "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
+  if (Filename.length()>0) {
+    QPixmap Pixmap = QPixmap::grabWidget(this);
+    if(!Pixmap.save(Filename)) {
+      QMessageBox::warning(this, "ddd Message","Could not write image file.",QMessageBox::Ok);
+      remove(Filename.toAscii().data());
+    }
+  }
+}
+
+//
+// Main GUI
+//
+GUI::GUI() {
+  
+  // Set features of main window
+  Central = new QWidget(this);
+  setCentralWidget(Central);
+  setStatusBar (new QStatusBar(this));
+  
+   // TextBox for value
+  Value = new FCS_Indicator((char *) "SQM/NSB", Central);
+  Graph = new FCS_Plot((char *) "SQM/NSB", Central);
+
+  Value1 = new FCS_Indicator((char *) "BIAS/VOLT/ID00/00-000", Central);
+  //Graph1 = new FCS_Plot((char *) "BIAS/VOLT/ID00/00-000", Central);
+
+  // Clock (updated every second)
+  //Clock = new QwtAnalogClock(Central);
+  //Clock->scaleDraw()->setPenWidth(3);
+  //Clock->setLineWidth(6);
+  //Clock->setFrameShadow(QwtDial::Sunken);
+  //Clock->setGeometry(0,0,10,10);
+  //Clock->setTime();
+  
+  //QTimer *Timer = new QTimer(Clock);
+  //Timer->connect(Timer, SIGNAL(timeout()), Clock, SLOT(setCurrentTime()));
+  //Timer->start(1000);
+
+  MainWidget = new QWidget();
+
+  // Layout of all widgets
+  MainLayout = new QGridLayout(MainWidget);
+  MainLayout->addWidget(Value, 0, 0, 1, 1);
+  //MainLayout->addWidget(Clock, 0, 1, 1, 1);
+  MainLayout->addWidget(Graph, 1, 0, 1, 2);
+  //MainLayout->setColumnStretch(1, 10);
+  
+  MainLayout->addWidget(Value1, 0, 1, 1, 1);
+  //MainLayout->addWidget(Clock, 0, 1, 1, 1);
+  //MainLayout->addWidget(Graph1, 2, 0, 1, 2);
+
+  BiasWidget = new QWidget();
+  BiasLayout = new QGridLayout(BiasWidget);
+  for (int i=0; i<19; i++) {
+    char *Item;
+    if (asprintf(&Item, "BIAS/VOLT/ID00/00-%.3d", i) != -1) {
+      Value = new FCS_Indicator(Item, NULL);
+      BiasLayout->addWidget(Value, i%10, 0+i/10, 1, 1);      
+      free(Item);
+    }
+    if (asprintf(&Item, "BIAS/VOLT/ID00/01-%.3d", i) != -1) {
+      Value = new FCS_Indicator(Item, NULL);
+      BiasLayout->addWidget(Value, i%10, 2+i/10, 1, 1);      
+      free(Item);
+    }
+  }
+    
+  // Tab widget
+  TabWidget = new QTabWidget(Central);
+  TabWidget->addTab(MainWidget, "&Main");
+  TabWidget->addTab(BiasWidget, "&Bias");
+  //TabWidget->addTab(EventHeaderDisplay, "&Environment");
+}  
+    
+
+GUI::~GUI() {
+  delete Central;
+}
+
+
+//---------------------------------------------------------------------
+//************************ All functions ****************************
+//-------------------------------------------------------------------
+
+// Quit application when clicking close button on window
+void GUI::closeEvent(QCloseEvent *) {
+  qApp->quit();
+}
+
+
+//---------------------------------------------------------------------
+//**************************** Main program ***************************
+//---------------------------------------------------------------------
+
+int main(int argc, char *argv[]) {
+  QApplication app(argc, argv);
+  
+  GUI MainWindow;
+  MainWindow.setGeometry(100, 100, 800, 500);
+  MainWindow.setWindowTitle("Edd - Evidence Data Display");
+  MainWindow.show();
+  
+  return app.exec();
+}
Index: /Evidence/Edd/Edd.h
===================================================================
--- /Evidence/Edd/Edd.h	(revision 136)
+++ /Evidence/Edd/Edd.h	(revision 136)
@@ -0,0 +1,98 @@
+#include <QtGui>
+
+#include <qwt_plot.h>
+#include <qwt_plot_curve.h>
+#include <qwt_plot_grid.h>
+#include <qwt_plot_zoomer.h>
+#include <qwt_plot_panner.h>
+#include <qwt_scale_engine.h>
+#include <qwt_analog_clock.h>
+#include <qwt_scale_widget.h>
+#include <qwt_plot_layout.h>
+
+#include <limits.h>
+#include <float.h>
+
+#include "dic.hxx"
+#include "Evidence.h"
+
+#define NO_LINK "__&DIM&NOLINK&__" // for checking if DIMserver is alive
+
+// General indicator for DIM service
+class FCS_Indicator: public QLineEdit, public DimClient, public DimBrowser {
+    Q_OBJECT
+
+    char *ServiceName;
+    DimStampedInfo *Data;
+
+    QPoint dragStart;
+	
+    void infoHandler();
+    void mousePressEvent(QMouseEvent *); 
+    void mouseReleaseEvent(QMouseEvent *); 
+    void mouseMoveEvent(QMouseEvent *); 
+	
+  public:
+    FCS_Indicator(char*, QWidget* = NULL);
+    ~FCS_Indicator();
+
+  signals:
+    void YEP(QString);
+};
+
+// Graph class for history display
+class FCS_Plot: public QwtPlot, public DimClient {
+    Q_OBJECT
+
+    char *Name;
+    DimInfo *Data;
+    void infoHandler();
+
+    QMenu *Menu;
+    QAction *YLogAction;
+    QAction *NormAction;
+    
+    QwtPlotPanner *Panner;
+    QwtPlotGrid *Grid;
+    QwtPlotCurve *Signal;
+    QwtPlotZoomer *Zoomer;
+
+    void dragEnterEvent(QDragEnterEvent *);
+    void dropEvent(QDropEvent *);
+
+  public:
+    FCS_Plot(char*, QWidget* = NULL);
+    ~FCS_Plot();
+
+  private slots:    
+    void HandleZoom(const QwtDoubleRect &);
+    void contextMenuEvent(QContextMenuEvent *);    
+    void MenuYScale();
+    void MenuZoomOut();
+    void MenuSave();
+    void MenuPrint();
+
+ signals:
+     void YEP();
+
+};
+
+// Main window class
+class GUI: public QMainWindow {
+    Q_OBJECT
+
+    FCS_Indicator *Value, *Value1;
+    FCS_Plot *Graph, *Graph1;
+    QwtAnalogClock *Clock;
+	
+    QWidget *Central, *MainWidget, *BiasWidget;
+    QGridLayout *MainLayout, *BiasLayout;
+
+    QTabWidget *TabWidget;
+            
+    void closeEvent(QCloseEvent *); 
+         
+  public:
+    GUI();
+    ~GUI();
+};
Index: /Evidence/Edd/Edd.pro
===================================================================
--- /Evidence/Edd/Edd.pro	(revision 136)
+++ /Evidence/Edd/Edd.pro	(revision 136)
@@ -0,0 +1,13 @@
+######################################################################
+# Automatically generated by qmake (2.01a) Mon Sep 28 16:32:39 2009
+######################################################################
+
+TEMPLATE = app
+TARGET = 
+DEPENDPATH += .
+INCLUDEPATH += . /usr/local/qwt-5.2.0/include ../DIM/ ..
+
+# Input
+HEADERS += Edd.h
+SOURCES += Edd.cc ../Evidence.cc
+LIBS += -L/usr/local/qwt-5.2.0/lib -lqwt ../DIM/libdim.a
