Index: Evidence/Edd/Edd.cc
===================================================================
--- Evidence/Edd/Edd.cc	(revision 154)
+++ Evidence/Edd/Edd.cc	(revision 159)
@@ -9,5 +9,5 @@
 a DIM status service
 
-December 2009, Oliver Grimm
+February 2010, Oliver Grimm
 
 ============================================================ */
@@ -35,5 +35,5 @@
   
   // Connect to DIM handler
-  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QString, QByteArray, QString)), SLOT(Update(DimInfo*, int, QString, QByteArray, QString))) == false) {
+  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QByteArray, QString)), SLOT(Update(DimInfo*, int, QByteArray, QString))) == false) {
     printf("Failed connection for %s\n", DIMService.toAscii().data());
   }
@@ -43,7 +43,8 @@
   Menu->addAction("Open history", this, SLOT(MenuOpenHistory()));
   Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
+  Menu->addAction("Copy data", this, SLOT(MenuCopyData()));
 
   // DIM client
-  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, (char *) NO_LINK, Handler);
+  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, NO_LINK, Handler);
 }
 
@@ -54,7 +55,7 @@
 
 // Update widget
-void Edd_Indicator::Update(DimInfo *Info, int Time, QString Format, QByteArray Data, QString Text) {
-
-  if (Info != this->Data) return;
+void Edd_Indicator::Update(DimInfo *Info, int Time, QByteArray Array, QString Text) {
+
+  if (Info != Data) return;
     
   QPalette Pal = palette();  
@@ -67,15 +68,12 @@
   }
   else {
-    // If this is a status indicator, adapt background colour
-    if (Data.size() == Text.size()+2) {
-      switch (Data[Text.size() + 1]) {
-        case 0:  Pal.setColor(QPalette::Base, Qt::white); break;
-        case 1:  Pal.setColor(QPalette::Base, Qt::yellow); break;
-        case 2:  Pal.setColor(QPalette::Base, Qt::red); break;
-        case 3:  Pal.setColor(QPalette::Base, Qt::red); break;
-        default: break;
-      }
+    // Backgound colour determined by last byte
+    switch (Array[Array.size()]) {
+      case 0:  Pal.setColor(QPalette::Base, Qt::white); break;
+      case 1:  Pal.setColor(QPalette::Base, Qt::yellow); break;
+      case 2:  Pal.setColor(QPalette::Base, Qt::red); break;
+      case 3:  Pal.setColor(QPalette::Base, Qt::red); break;
+      default: break;
     }
-    else Pal.setColor(QPalette::Base, Qt::white);
 	
 	if (!ShowAsTime) setText(Text);
@@ -83,5 +81,5 @@
 	
     // Update status tip
-    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Format));
+    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Info->getFormat()));
   }
   
@@ -134,5 +132,5 @@
 }
 
-// Open history plot
+// Menu: Open history plot
 void Edd_Indicator::MenuOpenHistory() {
   
@@ -141,8 +139,14 @@
 }
 
-// Copy service name
+// Menu: Copy service name
 void Edd_Indicator::MenuCopyService() {
   
   QApplication::clipboard()->setText(QString(Data->getName()));
+}
+
+// Menu: Copy data
+void Edd_Indicator::MenuCopyData() {
+  
+  QApplication::clipboard()->setText(text());
 }
 
@@ -154,5 +158,5 @@
 // Constructor
 //
-Edd_Plot::Edd_Plot(QString DIMService, QWidget *P): QwtPlot(P) {
+Edd_Plot::Edd_Plot(QString DIMService, QWidget *P): QwtPlot(P), EvidenceHistory() {
 
   setAcceptDrops(true);
@@ -180,5 +184,5 @@
   
   // Connect to DIM handler
-  if (connect(Handler, SIGNAL(YEP(DimInfo *, int, QString, QByteArray, QString)), SLOT(Update(DimInfo *, int, QString, QByteArray, QString))) == false) {
+  if (connect(Handler, SIGNAL(YEP(DimInfo *, int, QByteArray, QString)), SLOT(Update(DimInfo *, int, QByteArray, QString))) == false) {
     printf("Failed connection for %s\n", DIMService.toAscii().data());
   }
@@ -211,9 +215,6 @@
 
   for (int i=0; i<Items.size(); i++) {
-    delete Items[i].Data;
     delete Items[i].LiveData;
     delete Items[i].Signal;
-    delete[] Items[i].x;
-    delete[] Items[i].y;
   }  
   delete Grid;
@@ -225,13 +226,11 @@
 void Edd_Plot::AddService(QString Name) {
 
-  QString HistName = Name+".hist";
-
   // Lock before accessing Items list
   QMutexLocker Locker(&Mutex);
 
-  // Check if already subsribed to service
+  // Check if already subscribed 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);
+    if (Name == Items[i].LiveData->getName()) {
+      QMessageBox::warning(this, "Edd Message",Name+".hist already present",QMessageBox::Ok);
       return;
     }
@@ -242,11 +241,8 @@
   New.Signal = new QwtPlotCurve;
   New.Signal->attach(this);
-  New.Signal->setTitle(HistName);
+  New.Signal->setTitle(Name+".hist");
   New.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
-  New.x = NULL;
-  New.y = NULL;
-  New.Count = 0;
-  New.Data = new DimStampedInfo(HistName.toAscii().data(), (char *) NO_LINK, Handler);
-  New.LiveData = new DimStampedInfo(Name.toAscii().data(), (char *) NO_LINK, Handler); 
+  New.SizeLimit = 5000;
+  New.LiveData = new DimStampedInfo(Name.toAscii().data(), NO_LINK, Handler); 
 
   Items.append(New);
@@ -254,5 +250,5 @@
 
 // Update widget (must happen in GUI thread)
-void Edd_Plot::Update(DimInfo *Info, int Time, QString Format, QByteArray Data, QString Text) {
+void Edd_Plot::Update(DimInfo *Info, int Time, QByteArray, QString Text) {
 
   // Check if service available
@@ -266,59 +262,58 @@
   // Determine which plot item this call belongs to
   int ItemNo;
-  for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Info == Items[ItemNo].Data) {
-    // This is a history service  
-    EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) Data.data();
-    int Count=0, DataPoints = Data.size()/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;
+  for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Info == Items[ItemNo].LiveData) {
+  
+	// If size limit reached, clear buffer
+    if (Items[ItemNo].x.size() > Items[ItemNo].SizeLimit) {
+	  Items[ItemNo].x.clear();
+	  Items[ItemNo].y.clear();
     }
 
-    // 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();
+	// If buffer empty, request new history buffer
+    if (Items[ItemNo].x.isEmpty()) {
+	  int Time, Size;
+	  void *Data;
+
+	  if (GetHistory(Items[ItemNo].LiveData->getName())) {
+     	double Smallest = DBL_MAX, Largest = DBL_MIN;
+		double Number=0;
+		while (Next(Time, Size, Data)) {
+		  switch (*(Info->getFormat())) {
+    		case 'I':  Number = *(int *) Data;   break;
+    		case 'S':  Number = *(short *) Data;   break;
+    		case 'F':  Number = *(float *) Data;   break;
+    		case 'D':  Number = *(double *) Data;   break;
+    		case 'X':  Number = *(long long *) Data;   break;
+    		default: break;
+		  }
+
+		  Items[ItemNo].x.append(Time);
+		  Items[ItemNo].y.append(Number);
+		  
+      	  if (Largest < Items[ItemNo].y.last()) Largest = Items[ItemNo].y.last();
+    	  if (Smallest > Items[ItemNo].y.last()) Smallest = Items[ItemNo].y.last();
+		}
+
+    	Items[ItemNo].Smallest = Smallest;
+    	Items[ItemNo].Largest = Largest;
+
+		// Local buffer always at least twice as large as history
+		if (Items[ItemNo].SizeLimit < 2*Items[ItemNo].x.size()) {
+		  Items[ItemNo].SizeLimit = 2*Items[ItemNo].x.size();
+		}		
+	  }
+	}
+
+    // Append data
+    Items[ItemNo].x.append(Time);
+    Items[ItemNo].y.append(atof(Text.toAscii().data()));
+
+    // Update largest and smallest value    
+    if (Items[ItemNo].y.last() > Items[ItemNo].Largest) Items[ItemNo].Largest = Items[ItemNo].y.last();
+    if (Items[ItemNo].y.last() < Items[ItemNo].Smallest) Items[ItemNo].Smallest = Items[ItemNo].y.last();    
 
     // Update status tip
     QDateTime Timex = QDateTime::fromTime_t(Time); 
-    StatusTip = QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), Timex.toString()).arg(Format);
-
-  } else if (Info == Items[ItemNo].LiveData) {
-    // 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 = Time;
-    Data.Value = atof(Text.toAscii().data());            
-    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;    
+    StatusTip = QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), Timex.toString()).arg(Info->getFormat());
   }
 
@@ -352,5 +347,5 @@
     else Items[ItemNo].Signal->setSymbol(Sym1);
 
-    int DataPoints = Items[ItemNo].Count + Items[ItemNo].Live.size();
+    int DataPoints = Items[ItemNo].x.size();
 	
 	if (DataPoints == 0) continue;
@@ -361,20 +356,16 @@
     // Adapt time scale and normalize y scale if requested
     for (int i=0; i<DataPoints; i++) {
-      if (i < Items[ItemNo].Count) {
-        x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55200;
-        y[i] = Items[ItemNo].y[i];
-      }
-      else {
-        x[i]= Items[ItemNo].Live[i-Items[ItemNo].Count].Seconds / 86400.0 + 40587.5 - 55200;
-        y[i] = Items[ItemNo].Live[i-Items[ItemNo].Count].Value;
-      }
+      x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55200;
+      y[i] = Items[ItemNo].y[i];
 
       if (NormAction->isChecked()) {
-	if (Items[ItemNo].Smallest != Items[ItemNo].Largest) y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
-	else y[i] = 1;
+	    if (Items[ItemNo].Smallest != Items[ItemNo].Largest) {
+		  y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
+		}
+	    else y[i] = 1;
       }  
     }
 
-    // Plot datas
+    // Plot data
     Items[ItemNo].Signal->setData(x, y, DataPoints);
     Items[ItemNo].Signal->show();
@@ -443,9 +434,6 @@
 
   while (Items.size() > 1) {  
-    delete Items.last().Data;
     delete Items.last().LiveData;
     delete Items.last().Signal;
-    delete[] Items.last().x;
-    delete[] Items.last().y;
     Items.takeLast();
   }
@@ -473,7 +461,7 @@
    // 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;
+    Stream << QString("# ")+Items[ItemNo].LiveData->getName()+".hist" << endl;
     for (int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
-      Stream << (int) Items[ItemNo].x[i] << " " << Items[ItemNo].Signal->y(i) << endl;
+      Stream << (int) Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
     }
   }
@@ -514,4 +502,69 @@
 
 
+///////////////////////////////////////
+// History text box for DIM service //
+//////////////////////////////////////
+
+//
+// Constructor
+//
+Edd_TextHist::Edd_TextHist(QString DIMService, QWidget *P): QTextEdit(P), EvidenceHistory() {
+  
+  // Widget properties
+  setReadOnly(true);
+  setAttribute(Qt::WA_DeleteOnClose);
+  setAutoFillBackground(true);
+  document()->setMaximumBlockCount(1000);
+  
+  // Connect to DIM handler
+  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QByteArray, QString)), SLOT(Update(DimInfo*, int, QByteArray, QString))) == false) {
+    printf("Failed connection for %s\n", DIMService.toAscii().data());
+  }
+
+  // Get history for this service 
+  int Time, Size;
+  void *Data;
+
+  if (GetHistory(DIMService.toAscii().data())) {
+	while (Next(Time, Size, Data)) {
+	  moveCursor (QTextCursor::Start);
+	  insertPlainText(QString("(")+QDateTime::fromTime_t(Time).toString()+") ");	  
+	  insertPlainText(QString((char *) Data) + "\n");	  
+	}
+  }
+
+  // DIM client
+  Service = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, NO_LINK, Handler);
+}
+
+// Destructor
+Edd_TextHist::~Edd_TextHist() {
+
+  delete Service;
+}
+
+
+// Update widget (must happen in GUI thread)
+void Edd_TextHist::Update(DimInfo *Info, int Time, QByteArray, QString Text) {
+
+  if (Info != this->Service) return;
+
+  // Check if service available
+  if (Time == -1) {
+	setStatusTip(QString("%1:  unavailable").arg(Info->getName()));
+	return;
+  }
+
+  QDateTime Timex = QDateTime::fromTime_t(Time); 
+
+  moveCursor(QTextCursor::Start);
+  insertPlainText(QString("(")+Timex.toString()+QString(") "));	  
+  insertPlainText(Text + "\n");	  
+	
+  // Update status tip
+  StatusTip = QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), Timex.toString()).arg(Info->getFormat());
+}
+
+
 //////////////////
 // Text display //
@@ -528,10 +581,10 @@
   
   // Connect to DIM handler
-  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QString, QByteArray, QString)), SLOT(Update(DimInfo*, int, QString, QByteArray, QString))) == false) {
+  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QByteArray, QString)), SLOT(Update(DimInfo*, int, QByteArray, QString))) == false) {
     printf("Failed connection for %s\n", DIMService.toAscii().data());
   }
 
   // DIM client
-  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, (char *) NO_LINK, Handler);
+  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, NO_LINK, Handler);
 }
 
@@ -543,5 +596,5 @@
 
 // Handling of DIM service update
-void Edd_Textout::Update(DimInfo *Info, int Time, QString Format, QByteArray, QString Text) {
+void Edd_Textout::Update(DimInfo *Info, int Time, QByteArray, QString Text) {
 
   if (Info != this->Data) return;
@@ -561,8 +614,8 @@
 	
     // Add if service contains only a string
-    if (Format == "C") insertPlainText(Text);
+    if (strcmp(Info->getFormat(), "C") == 0) insertPlainText(Text);
 
     // Update status tip
-    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Format));
+    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Info->getFormat()));
   }
   setPalette(Pal);
@@ -652,4 +705,12 @@
   MainLayout->addWidget(Value, 7, 0, 1, 1);
 
+  QPushButton *Button = new QPushButton();
+  Button->setText("Start DIM browser");
+  connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser()));
+  MainLayout->addWidget(Button, 7, 1, 1, 1);
+
+  Edd_TextHist *Bla;
+  Bla = new Edd_TextHist("DColl/CurrentFile");
+  MainLayout->addWidget(Bla, 8, 0, 1, 1);
 
   // Layout of all widgets
@@ -669,4 +730,5 @@
     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);
@@ -686,5 +748,5 @@
   Textout->setFixedWidth(400);
   BiasLayout->addWidget(Textout, 10, 0, 4, 4);      
-  
+
   // Environment page
   EnvironmentWidget = new QWidget();
@@ -772,14 +834,19 @@
 
   // Check if service available
-  if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
-    YEP(getInfo(), -1);
-  }
+  if (!EvidenceServer::ServiceOK(getInfo())) YEP(getInfo(), -1);
   else {
     char *Txt = EvidenceServer::ToString(getInfo());
 
-    YEP(getInfo(), getInfo()->getTimestamp(), getInfo()->getFormat(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), QString(Txt));
-		free(Txt);
-  }
-}
+    YEP(getInfo(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), QString(Txt));
+	free(Txt);
+  }
+}
+
+// Start DIM Browser
+void GUI::StartDIMBrowser() {
+
+  QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/");
+}
+
 
 //---------------------------------------------------------------------
Index: Evidence/Edd/Edd.h
===================================================================
--- Evidence/Edd/Edd.h	(revision 154)
+++ Evidence/Edd/Edd.h	(revision 159)
@@ -46,24 +46,23 @@
 	
   private slots:
-	void Update(DimInfo *, int, QString, QByteArray, QString);
+	void Update(DimInfo *, int, QByteArray, QString);
     void contextMenuEvent(QContextMenuEvent *);    
     void MenuOpenHistory();
     void MenuCopyService();
+    void MenuCopyData();
 };
 
 // Graph class for history display
-class Edd_Plot: public QwtPlot, public DimClient {
+class Edd_Plot: public QwtPlot, public EvidenceHistory {
     Q_OBJECT
 
     struct PlotItem {
-      DimInfo *Data;
       DimInfo *LiveData;
       QwtPlotCurve *Signal;
-      int *x;
-      double *y;
-      int Count;
       double Smallest;
       double Largest;
-      QList<struct EvidenceHistoryItem> Live;
+	  int SizeLimit;
+	  QVector<double> x;
+	  QVector<double> y;	  
     };
 
@@ -93,5 +92,5 @@
   private slots:
     void UpdatePlot();
-	void Update(DimInfo* Info, int, QString, QByteArray, QString);
+	void Update(DimInfo* Info, int, QByteArray, QString);
 
     void HandleZoom(const QwtDoubleRect &);
@@ -106,6 +105,21 @@
 };
 
+// Text history class
+class Edd_TextHist: public QTextEdit, public EvidenceHistory {
+    Q_OBJECT
+
+	QString StatusTip;
+	DimStampedInfo *Service;
+	
+  public:
+    Edd_TextHist(QString, QWidget * = NULL);
+    ~Edd_TextHist();
+
+  private slots:
+	void Update(DimInfo* Info, int, QByteArray, QString);
+};
+
 // Textout indicator for DIM service
-class Edd_Textout: public QTextEdit, public DimClient {
+class Edd_Textout: public QTextEdit, public EvidenceHistory {
     Q_OBJECT
 
@@ -119,5 +133,5 @@
 	
   private slots:
-	void Update(DimInfo* Info, int, QString, QByteArray, QString);
+	void Update(DimInfo* Info, int, QByteArray, QString);
 };
 
@@ -143,7 +157,8 @@
     void MenuAbout();
     void MenuNewHistory();
+	void StartDIMBrowser();
 	
   signals:
-    void YEP(DimInfo *, int, QString = QString(), QByteArray = QByteArray(), QString = QString());
+    void YEP(DimInfo *, int, QByteArray = QByteArray(), QString = QString());
 };
 
