Index: Evidence/Edd/Edd.cc
===================================================================
--- Evidence/Edd/Edd.cc	(revision 138)
+++ Evidence/Edd/Edd.cc	(revision 139)
@@ -4,4 +4,7 @@
 
 Qt-based graphical user interface for the Evidence contron system
+
+Edd_Indicator changes its background colour in case it display
+a DIM status service
 
 December 2009, Oliver Grimm
@@ -21,8 +24,7 @@
 
 // Constructor
-Edd_Indicator::Edd_Indicator(char *DIMService, QWidget *P): QLineEdit(P) {
-
-  ServiceName = new char [strlen(DIMService)+1];
-  strcpy(ServiceName, DIMService);
+Edd_Indicator::Edd_Indicator(QString DIMService, QWidget *P): QLineEdit(P) {
+
+  ServiceName = qstrdup(DIMService.toAscii().data());
   
   // Widget properties
@@ -31,6 +33,10 @@
   connect(this, SIGNAL(YEP(QString)), this, SLOT(setText(QString)));
 
+  // Context menu
+  Menu = new QMenu(this);
+  Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
+
   // DIM client
-  Data = new DimStampedInfo(DIMService, INT_MAX, (char *) NO_LINK, this);
+  Data = new DimStampedInfo(ServiceName, INT_MAX, (char *) NO_LINK, this);
 }
 
@@ -44,20 +50,32 @@
 void Edd_Indicator::infoHandler() {
 
+  QPalette Pal = palette();  
+  QString S;
+
   // 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));
+    emit(YEP(QString("n/a")));
+    setStatusTip(QString("%1:  unavailable").arg(ServiceName));
     Pal.setColor(backgroundRole(), Qt::red);
     setPalette(Pal);
-	return;
+    return;
   }
   Pal.setColor(backgroundRole(), Qt::white);
-  setPalette(Pal);
 
   // Translate data into ASCII
   char *Text = EvidenceServer::ToString(getInfo());
-  QString S;
-
+
+  // If this is a status indicator, adapt background colour
+  if (getInfo()->getSize() == (int) strlen(Text)+3) {
+    switch (*((char *) getInfo()->getData() + strlen(Text) + 2)) {
+      case 0:  Pal.setColor(backgroundRole(), Qt::white); break;
+      case 1:  Pal.setColor(backgroundRole(), Qt::cyan); break;
+      case 2:  Pal.setColor(backgroundRole(), Qt::red); break;
+      case 3:  Pal.setColor(backgroundRole(), Qt::red); break;
+      default: break;
+    }
+  }
+  setPalette(Pal);
+  
   if (Text != NULL) {
     QTextStream(&S) << Text;
@@ -81,8 +99,18 @@
 void Edd_Indicator::mouseReleaseEvent(QMouseEvent *Event) {
 
-  if (Event->button()==Qt::LeftButton && contentsRect().contains(Event->pos())) {
-    Edd_Plot *Graph = new Edd_Plot(ServiceName, NULL);
-    Graph->show();
-  }
+  if (Event->button()!=Qt::LeftButton || !contentsRect().contains(Event->pos())) return;
+
+  // Check if last history plot still open, then raise
+  foreach (QWidget *Widget, QApplication::allWidgets()) {
+    if (Widget == LastPlot) {
+      Widget->activateWindow();
+      Widget->raise();
+      return;
+    }
+  }
+
+  // If not, open new plot
+  LastPlot = new Edd_Plot(ServiceName);
+  LastPlot->show();
 }
 
@@ -106,4 +134,17 @@
 }
 
+//
+// Opening context menu
+//
+void Edd_Indicator::contextMenuEvent(QContextMenuEvent *Event) {
+
+  Menu->exec(Event->globalPos());
+}
+
+// Copy service name
+void Edd_Indicator::MenuCopyService() {
+  
+  QApplication::clipboard()->setText(QString(ServiceName));
+}
 
 //////////////////////////////////
@@ -114,8 +155,9 @@
 // Constructor
 //
-Edd_Plot::Edd_Plot(char *DIMService, QWidget *P): QwtPlot(P) {
+Edd_Plot::Edd_Plot(QString DIMService, QWidget *P): QwtPlot(P) {
 
   setAcceptDrops(true);
-
+  setAttribute(Qt::WA_DeleteOnClose);
+  
   // Graph properties
   QwtText XAxisTitle("Time (RJD-55000)");
@@ -144,12 +186,17 @@
   NormAction = Menu->addAction("Normalize", this, SLOT(UpdatePlot()));
   NormAction->setCheckable(true);
+  StyleAction = Menu->addAction("Draw dots", this, SLOT(UpdatePlot()));
+  StyleAction->setCheckable(true);
   Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
   Menu->addAction("Single trace", this, SLOT(MenuSingleTrace()));
   Menu->addSeparator();
+  Menu->addAction("Save as ASCII", this, SLOT(MenuSaveASCII()));
   Menu->addAction("Save plot", this, SLOT(MenuSave()));
   Menu->addAction("Print plot", this, SLOT(MenuPrint()));
+  Menu->addSeparator();
+  Menu->addAction("Paste service", this, SLOT(MenuPasteService()));
 
   // DIM client
-  AddService(DIMService);
+  if (!DIMService.isEmpty()) AddService(DIMService);
 }
 
@@ -161,4 +208,5 @@
   for (int i=0; i<Items.size(); i++) {
     delete Items[i].Data;
+    delete Items[i].LiveData;
     delete Items[i].Signal;
     delete[] Items[i].x;
@@ -171,12 +219,18 @@
 // Add history service to plot
 //
-void Edd_Plot::AddService(char *DIMService) {
-
-  // Generate name of DIM service
-  QString Name(DIMService);
-  Name.append(".hist");
-
-  // Lock this thread (because infoHandler() might be called before list insertion)
+void Edd_Plot::AddService(QString Name) {
+
+  QString HistName = Name+".hist";
+  
+  // Lock before accessing Items list
   QMutexLocker Locker(&Mutex);
+
+  // Check if already subsribed to service
+  for (int i=0; i<Items.size(); i++) {
+    if (HistName == Items[i].Data->getName()) {
+      QMessageBox::warning(this, "Edd Message",HistName+" already present",QMessageBox::Ok);
+      return;
+    }
+  }  
 
   // Generate new curve and subscribe to service
@@ -184,9 +238,11 @@
   New.Signal = new QwtPlotCurve;
   New.Signal->attach(this);
-  New.Signal->setTitle(Name);
+  New.Signal->setTitle(HistName);
   New.Signal->setPen(QColor(LineColors[Items.size()%(sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
   New.x = NULL;
   New.y = NULL;
-  New.Data = new DimStampedInfo(Name.toAscii(), NO_LINK, this); 
+  New.Count = 0;
+  New.Data = new DimStampedInfo(HistName.toAscii(), NO_LINK, this); 
+  New.LiveData = new DimStampedInfo(Name.toAscii(), NO_LINK, this); 
 
   Items.append(New);
@@ -204,47 +260,69 @@
   }
 
-  // Lock this thread (see AddService())
+  // Lock before accessing Items list
   QMutexLocker Locker(&Mutex);
 
   // Determine which plot item this call belongs to
   int ItemNo;
-  for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Items[ItemNo].Data == getInfo()) break;
-  if (ItemNo == Items.size()) return;  // safety check
-  
-  EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) getInfo()->getData();
-  int Count=0, DataPoints = getInfo()->getSize()/sizeof(struct EvidenceHistoryItem);
-
-  delete[] Items[ItemNo].x;
-  delete[] Items[ItemNo].y;
-  Items[ItemNo].x = new double [DataPoints];
-  Items[ItemNo].y = new double [DataPoints];
-
-  // Find oldest item
-  int Oldest;
-  for (Oldest=1; Oldest<DataPoints; Oldest++) {
-    if (Curr[Oldest].Seconds < Curr[Oldest-1].Seconds) break;
-  }
-
-  // Plot data starting with oldest value
-  for (int i=0; i<DataPoints; i++) {
-    Items[ItemNo].x[Count] = (double) Curr[(i+Oldest)%DataPoints].Seconds;
-    Items[ItemNo].y[Count] = Curr[(i+Oldest)%DataPoints].Value;
-    if (Items[ItemNo].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 < Items[ItemNo].y[i]) Largest = Items[ItemNo].y[i];
-    if (Smallest > Items[ItemNo].y[i]) Smallest = Items[ItemNo].y[i];
-  }
-  Items[ItemNo].Smallest = Smallest;
-  Items[ItemNo].Largest = Largest;
-  Items[ItemNo].Count = Count;
-  
-  // Update status tip
-  QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp()); 
-  setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(getInfo()->getName(), Time.toString()).arg(getInfo()->getFormat()));
-
+  for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Items[ItemNo].Data == getInfo()) {
+    // This is a history service  
+    EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) getInfo()->getData();
+    int Count=0, DataPoints = getInfo()->getSize()/sizeof(struct EvidenceHistoryItem);
+
+    delete[] Items[ItemNo].x;
+    delete[] Items[ItemNo].y;
+    Items[ItemNo].x = new int [DataPoints];
+    Items[ItemNo].y = new double [DataPoints];
+
+    // Find oldest item
+    int Oldest;
+    for (Oldest=1; Oldest<DataPoints; Oldest++) {
+      if (Curr[Oldest].Seconds < Curr[Oldest-1].Seconds) break;
+    }
+
+    // Plot data starting with oldest value
+    for (int i=0; i<DataPoints; i++) {
+      Items[ItemNo].x[Count] = Curr[(i+Oldest)%DataPoints].Seconds;
+      Items[ItemNo].y[Count] = Curr[(i+Oldest)%DataPoints].Value;
+      if (Items[ItemNo].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 < Items[ItemNo].y[i]) Largest = Items[ItemNo].y[i];
+      if (Smallest > Items[ItemNo].y[i]) Smallest = Items[ItemNo].y[i];
+    }
+    Items[ItemNo].Smallest = Smallest;
+    Items[ItemNo].Largest = Largest;
+    Items[ItemNo].Count = Count;
+
+    // Clear local history
+    Items[ItemNo].Live.clear();
+
+    // Update status tip
+    QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp()); 
+    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(getInfo()->getName(), Time.toString()).arg(getInfo()->getFormat()));
+
+  } else if (Items[ItemNo].LiveData == getInfo()) {
+    // This is a live service 
+
+    // Limit size of list
+    if (Items[ItemNo].Live.size() > 1000) Items[ItemNo].Live.removeFirst();
+
+    // Append data
+    struct EvidenceHistoryItem Data;
+    Data.Seconds = getInfo()->getTimestamp();
+    Data.Value = atof(EvidenceServer::ToString(getInfo()));            
+    Items[ItemNo].Live.append(Data);
+    
+    // Update largest and smallest value    
+    if (Data.Value > Items[ItemNo].Largest) Items[ItemNo].Largest = Data.Value;
+    if (Data.Value < Items[ItemNo].Smallest) Items[ItemNo].Smallest = Data.Value;    
+  }
+
+  Locker.unlock();
+
+  // Do not call replot() directly from this thread!
   emit(YEP());
 }
@@ -255,6 +333,4 @@
 //
 void Edd_Plot::UpdatePlot() {
-
-  QMutexLocker Locker(&Mutex);
 
   if (!YLogAction->isChecked()) {
@@ -263,15 +339,31 @@
   else setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine);
 
+  // Lock before accessing Items list
+  QMutexLocker Locker(&Mutex);
+
   for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
-  
-    double *x = new double [Items[ItemNo].Count];
-    double *y = new double [Items[ItemNo].Count];
-
+
+    if (Items[ItemNo].Count == 0) continue;
+
+    if (StyleAction->isChecked()) Items[ItemNo].Signal->setStyle(QwtPlotCurve::Dots);
+    else Items[ItemNo].Signal->setStyle(QwtPlotCurve::Lines);
+
+    int DataPoints = Items[ItemNo].Count + Items[ItemNo].Live.size();      
+    double *x = new double [DataPoints];
+    double *y = new double [DataPoints];
+    
     // Adapt time scale and normalize y scale if requested
-    for (int i=0; i<Items[ItemNo].Count; i++) {
-      x[i] = Items[ItemNo].x[i]/86400 + 40587.5 - 55000;
-      y[i] = Items[ItemNo].y[i];
+    for (int i=0; i<DataPoints; i++) {
+      if (i < Items[ItemNo].Count) {
+        x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55000;
+        y[i] = Items[ItemNo].y[i];
+      }
+      else {
+        x[i]= Items[ItemNo].Live[i-Items[ItemNo].Count].Seconds / 86400.0 + 40587.5 - 55000;
+        y[i] = Items[ItemNo].Live[i-Items[ItemNo].Count].Value;
+      }
+
       if (NormAction->isChecked()) {
-	if (Items[ItemNo].Smallest != Items[ItemNo].Largest) y[i] = (Items[ItemNo].y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
+	if (Items[ItemNo].Smallest != Items[ItemNo].Largest) y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
 	else y[i] = 1;
       }  
@@ -279,5 +371,5 @@
 
     // Plot datas
-    Items[ItemNo].Signal->setData(x, y, Items[ItemNo].Count);
+    Items[ItemNo].Signal->setData(x, y, DataPoints);
     Items[ItemNo].Signal->show();
     Zoomer->setZoomBase(Items[ItemNo].Signal->boundingRect());
@@ -315,5 +407,5 @@
     
 //
-// Context menu and its functions
+// Opening context menu
 //
 void Edd_Plot::contextMenuEvent(QContextMenuEvent *Event) {
@@ -325,13 +417,16 @@
 
   Zoomer->zoom(0);
-  emit YEP();
-}
-
+  UpdatePlot();
+}
+
+// Remove all items except last
 void Edd_Plot::MenuSingleTrace() {
 
+  // Lock before accessing Items list
   QMutexLocker Locker(&Mutex);
 
   while (Items.size() > 1) {  
     delete Items.last().Data;
+    delete Items.last().LiveData;
     delete Items.last().Signal;
     delete[] Items.last().x;
@@ -339,7 +434,35 @@
     Items.takeLast();
   }
-  emit(YEP());
-}
-
+  
+  Locker.unlock();
+  UpdatePlot();
+}
+
+// Save data of plot as test 
+void Edd_Plot::MenuSaveASCII() {
+  QString Filename = QFileDialog::getSaveFileName(this,
+     "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)");
+  if (Filename.length() <= 0) return;
+  
+  QFile File(Filename);
+  if (!File.open(QFile::WriteOnly | QIODevice::Text | QFile::Truncate)) {
+    QMessageBox::warning(this, "Edd Message","Could not open file for writing.",QMessageBox::Ok);
+    return;
+  }
+  
+  // Lock before accessing Items list
+  QMutexLocker Locker(&Mutex);
+  QTextStream Stream(&File);
+
+   // Write x and y data for all signals to file
+  for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
+    Stream << QString("# ")+Items[ItemNo].Data->getName() << endl;
+    for (int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
+      Stream << (int) Items[ItemNo].x[i] << " " << Items[ItemNo].Signal->y(i) << endl;
+    }
+  }
+}
+
+// Print plot
 void Edd_Plot::MenuPrint() {
 
@@ -354,4 +477,5 @@
 }
 
+// Save plot as image
 void Edd_Plot::MenuSave() {
 
@@ -361,5 +485,5 @@
     QPixmap Pixmap = QPixmap::grabWidget(this);
     if(!Pixmap.save(Filename)) {
-      QMessageBox::warning(this, "ddd Message","Could not write image file.",QMessageBox::Ok);
+      QMessageBox::warning(this, "Edd Message","Could not write image file.",QMessageBox::Ok);
       remove(Filename.toAscii().data());
     }
@@ -367,20 +491,30 @@
 }
 
-//
-// Main GUI
+// Add new service by pasting name
+void Edd_Plot::MenuPasteService() {
+
+  AddService(QApplication::clipboard()->text().toAscii().data());
+}
+
+
+//
+// Main GUI (all widgets have ultimately Central as parent)
 //
 GUI::GUI() {
+
+  Edd_Indicator *Value;
+  Edd_Plot *Graph;
+  QString Text;
   
   // Set features of main window
   Central = new QWidget(this);
   setCentralWidget(Central);
-  setStatusBar (new QStatusBar(this));
+  setStatusBar(new QStatusBar(this));
   
    // TextBox for value
-  Value = new Edd_Indicator((char *) "SQM/NSB", Central);
-  Graph = new Edd_Plot((char *) "SQM/NSB", Central);
-
-  Value1 = new Edd_Indicator((char *) "BIAS/VOLT/ID00/00-000", Central);
-  //Graph1 = new Edd_Plot((char *) "BIAS/VOLT/ID00/00-000", Central);
+  //Value = new Edd_Indicator((char *) "SQM/NSB", Central);
+  //Graph = new Edd_Plot((char *) "SQM/NSB", Central);
+  //Graph->AddService("BIAS/VOLT/ID00/00-000");
+  
 
   // Clock (updated every second)
@@ -397,42 +531,53 @@
 
   MainWidget = new QWidget();
+  MainLayout = new QGridLayout(MainWidget);
 
   // Layout of all widgets
-  MainLayout = new QGridLayout(MainWidget);
-  MainLayout->addWidget(Value, 0, 0, 1, 1);
+  //MainLayout->addWidget(Value, 0, 0, 1, 1);
   //MainLayout->addWidget(Clock, 0, 1, 1, 1);
-  MainLayout->addWidget(Graph, 1, 0, 1, 2);
+  //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);
 
+  // Bias voltage page
   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 Edd_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 Edd_Indicator(Item, NULL);
-      BiasLayout->addWidget(Value, i%10, 2+i/10, 1, 1);      
-      free(Item);
-    }
-  }
-
+  Graph = new Edd_Plot();
+  for (int i=0; i<18; i++) {
+    Text = Text.sprintf("BIAS/VOLT/ID00/00-%.3d",i);
+    Value = new Edd_Indicator(Text);
+    BiasLayout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
+    Graph->AddService(Text);
+
+    Text = Text.sprintf("BIAS/VOLT/ID00/01-%.3d",i);
+    Value = new Edd_Indicator(Text);
+    BiasLayout->addWidget(Value, i%9+1, 2+i/9, 1, 1);
+    Graph->AddService(Text);
+  }
+  BiasLayout->addWidget(Graph, 0, 4, 12, 3);
+  Value = new Edd_Indicator("BIAS/Status");
+  Value->setMaximumWidth(200);
+  BiasLayout->addWidget(Value, 0, 0, 1, 3);      
+
+  // Environment page
   EnvironmentWidget = new QWidget();
   EnvironmentLayout = new QGridLayout(EnvironmentWidget);
+  Value = new Edd_Indicator("ARDUINO/Status");
+  Value->setMaximumWidth(200);
+  EnvironmentLayout->addWidget(Value, 0, 0, 1, 3);      
+
+  Graph = new Edd_Plot();
   for (int i=0; i<10; i++) {
-    char *Item;
-    if (asprintf(&Item, "ARDUINO/VAL%.2d", i) != -1) {
-      Value = new Edd_Indicator(Item, NULL);
-      EnvironmentLayout->addWidget(Value, i%5, i/5, 1, 1);      
-      free(Item);
-    }
-  }
+    Text = Text.sprintf("ARDUINO/VAL%.2d", i);
+    Value = new Edd_Indicator(Text);
+    EnvironmentLayout->addWidget(Value, i%5+1, i/5, 1, 1);
+    Graph->AddService(Text);
+  }
+  EnvironmentLayout->addWidget(Graph, 0, 3, 7, 4);      
+
+  Value = new Edd_Indicator("SQM/NSB");
+  EnvironmentLayout->addWidget(Value, 6, 0, 1, 1);      
     
   // Tab widget
@@ -444,4 +589,6 @@
   // Menu bar
   QMenu* Menu = menuBar()->addMenu("&Menu");
+  Menu->addAction("New history plot", this, SLOT(MenuNewHistory()));
+  Menu->addSeparator();
   Menu->addAction("About", this, SLOT(MenuAbout()));
   Menu->addSeparator();
@@ -455,12 +602,39 @@
 
 void GUI::MenuAbout() {
+  QString Rev(SVN_REVISION);
+  Rev.remove(0,1).chop(1);
+  
   QMessageBox::about(this, "About Edd","Evidence Data Display\n\n"
     "Written by Oliver Grimm, IPP, ETH Zurich\n"
-    "EMail: oliver.grimm@phys.ethz.ch\n"
-    "This version compiled "__DATE__".\n"
-    "subversion revision "SVN_REVISION".\n\n"
+    "This version compiled "__DATE__" ("+Rev+")\n\n"
     "Graphical user interface implemented with Qt.\n"
-    "Evidence control system based on DIM (http://dim.web.cern.ch).");
-     printf("Revision: $Revision$\n");
+    "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n"
+    "Comments to oliver.grimm@phys.ethz.ch.");
+}
+
+// Open request for new history plot
+void GUI::MenuNewHistory() {
+
+  QStringList List;
+  char *Name, *Format;
+  int Type;
+  bool OK;
+
+  // Find all services that are not history services and sort
+  getServices("*");
+  while ((Type = getNextService(Name, Format)) != 0) {
+    if (Type==DimSERVICE && strstr(Name, ".hist")==NULL) List.append(Name);
+  }
+  List.sort();
+
+  // Open dialog and open history window
+  QString Result = QInputDialog::getItem(this, "Edd Request",
+    "Enter DIM service name", List, 0, true, &OK);
+  if (OK && !Result.isEmpty()) {
+    Result = Result.trimmed();
+    if (Result.endsWith(".hist")) Result.chop(5);
+    Edd_Plot *Plot = new Edd_Plot(Result);
+    Plot->show();
+  }
 }
 
@@ -483,5 +657,5 @@
   
   GUI MainWindow;
-  MainWindow.setGeometry(100, 100, 800, 500);
+  MainWindow.setGeometry(100, 100, 800, 650);
   MainWindow.setWindowTitle("Edd - Evidence Data Display");
   MainWindow.show();
Index: Evidence/Edd/Edd.h
===================================================================
--- Evidence/Edd/Edd.h	(revision 138)
+++ Evidence/Edd/Edd.h	(revision 139)
@@ -32,6 +32,8 @@
     DimStampedInfo *Data;
 
+    QMenu *Menu;
     QPoint dragStart;
-	
+    QwtPlot *LastPlot;
+    	
     void infoHandler();
     void mousePressEvent(QMouseEvent *); 
@@ -40,6 +42,10 @@
 	
   public:
-    Edd_Indicator(char*, QWidget* = NULL);
+    Edd_Indicator(QString, QWidget * = NULL);
     ~Edd_Indicator();
+
+  private slots:
+    void contextMenuEvent(QContextMenuEvent *);    
+    void MenuCopyService();
 
   signals:
@@ -53,10 +59,12 @@
     struct PlotItem {
       DimInfo *Data;
+      DimInfo *LiveData;
       QwtPlotCurve *Signal;
-      double *x;
+      int *x;
       double *y;
       int Count;
       double Smallest;
       double Largest;
+      QList<struct EvidenceHistoryItem> Live;
     };
 
@@ -67,4 +75,5 @@
     QAction *YLogAction;
     QAction *NormAction;
+    QAction *StyleAction;
     
     QwtPlotPanner *Panner;
@@ -73,5 +82,4 @@
     QwtLegend *Legend;
     
-    void AddService(char *);
     void infoHandler();    
     void dragEnterEvent(QDragEnterEvent *);
@@ -79,6 +87,7 @@
 
   public:
-    Edd_Plot(char *, QWidget * = NULL);
+    Edd_Plot(QString = QString(), QWidget * = NULL);
     ~Edd_Plot();
+    void AddService(QString);
 
   private slots:
@@ -88,7 +97,9 @@
     void MenuZoomOut();
     void MenuSingleTrace();        
+    void MenuSaveASCII();
     void MenuSave();
     void MenuPrint();
-
+    void MenuPasteService();
+ 
  signals:
      void YEP();
@@ -97,9 +108,7 @@
 
 // Main window class
-class GUI: public QMainWindow {
+class GUI: public QMainWindow, public DimBrowser {
     Q_OBJECT
 
-    Edd_Indicator *Value, *Value1;
-    Edd_Plot *Graph, *Graph1;
     QwtAnalogClock *Clock;
 	
@@ -117,4 +126,5 @@
   private slots:
     void MenuAbout();
+    void MenuNewHistory();
 };
 
