Index: Evidence/Edd/Edd.cc
===================================================================
--- Evidence/Edd/Edd.cc	(revision 204)
+++ Evidence/Edd/Edd.cc	(revision 208)
@@ -37,4 +37,20 @@
 }
 
+// Set status tip (returns true if service was available)
+bool SetStatus(QWidget *W, QString Name, int Time, QString Format, int Index) {
+
+  QString Status;
+
+  if (Index != -1) Name = Name + "(" + QString::number(Index) + ")";
+
+  if (Time == -1) Status = QString("%1:  unavailable").arg(Name);
+  else Status = QString("%1:  Last update %2   Format '%3'").arg(Name).arg(QDateTime::fromTime_t(Time).toString()).arg(Format); 
+
+  W->setStatusTip(Status);
+  if (W->parent() == NULL) W->setToolTip(Status);
+  
+  return(Time != -1);
+}
+
 
 //////////////////////////////////////////
@@ -45,5 +61,5 @@
 EddLineDisplay::EddLineDisplay(QString Name, int Index, QWidget *P):
 	QLineEdit(P), ServiceName(Name), Index(Index) {
- 
+
  LastPlot = NULL;
  
@@ -52,4 +68,5 @@
   setMaximumWidth(100);
   ShowAsTime = false;
+  setFrame(false);
   
   // Connect to DIM handler
@@ -82,7 +99,6 @@
 
   // Check if service available
-  if (Time == -1) {
+  if (!SetStatus(this, Name, Time, Format, Index)) {
     setText("n/a");
-	setStatusTip(QString("%1:  unavailable").arg(ServiceName));
     Pal.setColor(QPalette::Base, Qt::lightGray);
   }
@@ -102,7 +118,4 @@
 	else setText(QDateTime::fromTime_t(Text.toInt()).toString());
 	setCursorPosition(0);
-
-    // Update status tip
-    setStatusTip(QString("%1 (%4):  Last update %2   Format '%3'").arg(ServiceName).arg( QDateTime::fromTime_t(Time).toString()).arg(Format).arg(Index));
   }
   
@@ -183,26 +196,185 @@
 //////////////////////////////////
 
-//
 // Constructor
-//
-EddPlot::EddPlot(QString DIMService, int Index, QWidget *P):
-	QwtPlot(P) {
-
-  Mutex = new QMutex(QMutex::Recursive);
-  
+EddPlot::EddPlot(QString DIMService, int Index, QWidget *P): EddBasePlot(P) {
+
   // Widget properties
   setAcceptDrops(true);
+  setAxisScaleDraw(QwtPlot::xBottom, new EddTimeScale);
+	
+  // Update time range on plot when axis change	(update() results in paintEvent())
+  connect(axisWidget(QwtPlot::xBottom), SIGNAL(scaleDivChanged()), SLOT(update()));
+
+  legend()->setItemMode(QwtLegend::ClickableItem);
+  connect(this, SIGNAL(legendClicked (QwtPlotItem *)), SLOT(LegendClicked(QwtPlotItem *)));
+  
+  // Connect to DIM handler
+  if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
+    printf("Failed connection for %s\n", DIMService.toAscii().data());
+  }
+
+  // Additonal context menu items
+  QAction* Action = Menu->addAction("Paste service", this, SLOT(MenuPasteService()));
+  Menu->removeAction(Action);
+  Menu->insertAction(Menu->actions().value(1), Action);
+  
+  // Maximum number if points in curve (will be increased in Update())
+  SizeLimit = 0;
+
+  // DIM client
+  if (!DIMService.isEmpty()) AddService(DIMService, Index);
+}
+
+// Destructor (items with parent widget are automatically deleted)
+EddPlot::~EddPlot() {
+
+  while (!List.isEmpty()) DeleteCurve(List.last().Signal);
+}
+
+// Add history service to plot
+void EddPlot::AddService(QString Name, int Index) {
+
+  // Check if curve already present on plot
+  for (int i=0; i<List.size(); i++) {
+    if (Name == List[i].Name && Index == List[i].Index) {
+      QMessageBox::warning(this, "Edd Message",Name+" ("+QString::number(Index)+") already present",QMessageBox::Ok);
+      return;
+    }
+  }  
+  
+  // Generate new curve and subscribe to service
+  struct ItemDetails N;
+
+  N.Name = Name;
+  N.Signal = NewCurve(Name+"("+QString::number(Index)+")");
+  N.Index = Index;
+  List.append(N);
+
+  Handler->Subscribe(Name);
+}
+
+// Update widget (must happen in GUI thread)
+void EddPlot::Update(QString Name, int Time, QByteArray, QString Format, QString Text) {
+
+  // Determine which plot item this call belongs to
+  int ItemNo;
+  for (ItemNo=0; ItemNo<List.size(); ItemNo++) if (List[ItemNo].Name == Name) {
+
+	// Service available?
+	if (!SetStatus(this, Name, Time, Format)) return;
+  
+	// If size limit reached, clear buffer
+    if (List[ItemNo].Signal->dataSize() > SizeLimit) List[ItemNo].Signal->setData(QPolygonF());
+
+	// If buffer empty, request new history buffer
+    if (List[ItemNo].Signal->dataSize() == 0) {
+	  int Time, Size;
+	  void *Data;
+
+	  class EvidenceHistory *HistClass = Handler->GetHistory(List[ItemNo].Name);
+	  
+	  if (HistClass->GetHistory()) {
+		double Number=0;
+		while (HistClass->Next(Time, Size, Data)) {
+		  switch (Format[0].toUpper().toAscii()) {
+    		case 'I':
+			case 'L':  Number = *((int *) Data + List[ItemNo].Index);   break;
+    		case 'S':  Number = *((short *) Data + List[ItemNo].Index);   break;
+    		case 'F':  Number = *((float *) Data + List[ItemNo].Index);   break;
+    		case 'D':  Number = *((double *) Data + List[ItemNo].Index);   break;
+    		case 'X':  Number = *((long long *) Data + List[ItemNo].Index);   break;
+    		default: break;
+		  }
+		  AddPoint(ItemNo, Time, Number);
+		}
+
+		// Local buffer at least twice as large as longest history
+		if (SizeLimit < 2*List[ItemNo].Signal->dataSize()) SizeLimit = 2*List[ItemNo].Signal->dataSize();
+	  }
+	}
+
+    // Append data
+	QString Txt = Text;
+	Txt = Txt.section(' ', List[ItemNo].Index, List[ItemNo].Index);
+    AddPoint(ItemNo, Time, atof(Txt.toAscii().data()));	
+  }
+
+  UpdatePlot();
+}
+
+
+// Add text indicating time range to plot
+void EddPlot::paintEvent(QPaintEvent *) {
+
+  QString Text;
+  QFont Font;
+  QPainter Painter(this);
+  
+  Text = QDateTime::fromTime_t((int) axisScaleDiv(QwtPlot::xBottom)->lowerBound()).toString("d-MMM-yyyy hh:mm:ss") + " to " + QDateTime::fromTime_t((int) axisScaleDiv(QwtPlot::xBottom)->upperBound()).toString("d-MMM-yyyy hh:mm:ss");
+
+  Font.setPointSize(6);  
+  Painter.setFont(Font);
+  Painter.drawText(0, height(), Text);
+}
+
+// Drag and drop methods
+void EddPlot::dragEnterEvent(QDragEnterEvent *Event) {
+    
+  if (Event->mimeData()->hasFormat("Edd/Service")) Event->acceptProposedAction();
+}
+
+void EddPlot::dropEvent(QDropEvent *Event) {
+
+  QByteArray D(Event->mimeData()->data("Edd/Service"));
+  AddService(D.left(D.lastIndexOf(' ')), D.right(D.size()-D.lastIndexOf(' ')).toInt());
+}
+
+void EddPlot::LegendClicked(QwtPlotItem *Item) {
+
+  QString D(Item->title().text());
+  D.replace('(',' ').chop(1);
+  
+  QDrag *Drag = new QDrag(this);
+  QMimeData *MimeData = new QMimeData;
+  QByteArray Data;
+  MimeData->setData("Edd/Service", Data.append(D));
+  Drag->setMimeData(MimeData);
+  Drag->exec();
+}
+
+// Add new service by pasting name
+void EddPlot::MenuPasteService() {
+
+  const QMimeData *D = QApplication::clipboard()->mimeData();
+  if (!D->hasFormat("Edd/Service")) return;
+  
+  QByteArray E(D->data("Edd/Service"));
+  AddService(E.left(E.lastIndexOf(' ')), E.right(E.size()-E.lastIndexOf(' ')).toInt());
+}
+
+// Remove list entry
+void EddPlot::DeleteCurve(QwtPlotCurve *Curve) {
+
+  for (int i=0; i<List.size(); i++) if (List[i].Signal == Curve) {
+    Handler->Unsubscribe(List[i].Name);
+    List.removeAt(i);
+  }
+}
+
+
+//////////////////
+// General plot //
+//////////////////
+
+// Constructor (all slots called in GUI thread, therefore no mutex necessary)
+EddBasePlot::EddBasePlot(QWidget *P): QwtPlot(P) {
+
+  // Widget properties
   setAttribute(Qt::WA_DeleteOnClose);
   setAutoReplot(false);
   setCanvasBackground(EddPlotBackgroundColor);
-  setAxisScaleDraw(QwtPlot::xBottom, new EddTimeScale);
   setMargin(15);
 
-  // Update additional information (e.g. time range) on widget when axis change	
-  connect(axisWidget(QwtPlot::xBottom), SIGNAL(scaleDivChanged()), SLOT(update()));
-
-  Picker = new QwtPicker(QwtPicker::CornerToCorner|QwtPicker::RectSelection, QwtPicker::RectRubberBand, QwtPicker::AlwaysOff, this);
-  connect(Picker, SIGNAL(selected(const QwtPolygon &)), SLOT(MouseSelection(const QwtPolygon &)));
-
+  // Plot navigation
   Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
   connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(HandleZoom(const QwtDoubleRect &)));
@@ -216,17 +388,13 @@
   Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
 
+  Picker = new QwtPicker(QwtPicker::CornerToCorner|QwtPicker::RectSelection, QwtPicker::RectRubberBand, QwtPicker::AlwaysOff, this);
+  connect(Picker, SIGNAL(selected(const QwtPolygon &)), SLOT(MouseSelection(const QwtPolygon &)));
+
+  // Grid and legend
   Grid = new QwtPlotGrid;
   Grid->setMajPen(QPen(Qt::gray, 0, Qt::DotLine));
   Grid->attach(this);
-
-  Legend = new QwtLegend();
-  Legend->setItemMode(QwtLegend::ClickableItem);
-  insertLegend(Legend, QwtPlot::TopLegend, 0.3);
-  connect(this, SIGNAL(legendClicked (QwtPlotItem *)), SLOT(LegendClicked(QwtPlotItem *)));
-  
-  // Connect to DIM handler
-  if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
-    printf("Failed connection for %s\n", DIMService.toAscii().data());
-  }
+  
+  insertLegend(new QwtLegend(), QwtPlot::TopLegend, 0.3);
 
   // Context menu
@@ -247,138 +415,16 @@
   Menu->addAction("Save plot", this, SLOT(MenuSave()));
   Menu->addAction("Print plot", this, SLOT(MenuPrint()));
-  Menu->addSeparator();
-  Menu->addAction("Paste service", this, SLOT(MenuPasteService()));
   Menu->addAction("Plot help", this, SLOT(MenuPlotHelp()));
-
-  // DIM client
-  if (!DIMService.isEmpty()) AddService(DIMService, Index);
-}
-
-//
+}
+
 // Destructor (items with parent widget are automatically deleted)
-//
-EddPlot::~EddPlot() {
-
-  for (int i=0; i<Items.size(); i++) {
-    Handler->Unsubscribe(Items[i].Name);
-    delete Items[i].Signal;
-  }
-
+EddBasePlot::~EddBasePlot() {
+
+  for (int i=0; i<Items.size(); i++) delete Items[i].Signal;
   delete Grid;
-  delete Mutex;
-}
-
-//
-// Add history service to plot
-//
-void EddPlot::AddService(QString Name, int Index) {
-
-  // Lock before accessing Items list
-  QMutexLocker Locker(Mutex);
-
-  // Check if already subscribed to service
-  for (int i=0; i<Items.size(); i++) {
-    if (Name == Items[i].Name && Index == Items[i].Index) {
-      QMessageBox::warning(this, "Edd Message",Name+" ("+QString::number(Index)+") already present",QMessageBox::Ok);
-      return;
-    }
-  }  
-  
-  // Generate new curve and subscribe to service
-  struct PlotItem New;
-
-  New.Name = Name;
-  New.Signal = new QwtPlotCurve;
-  New.Signal->attach(this);
-  New.Signal->setTitle(Name+"("+QString::number(Index)+")");
-  New.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
-  New.SizeLimit = 5000;
-  New.Index = Index;
-
-  Items.append(New);
-  Handler->Subscribe(Name);
-}
-
-// Update widget (must happen in GUI thread)
-void EddPlot::Update(QString Name, int Time, QByteArray, QString Format, QString Text) {
-
-  // 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].Name == Name) {
-
-	// Check if service available
-	if (Time == -1) {
-	  setStatusTip(QString("%1:  unavailable").arg(Name));
-	  return;
-	}
-  
-	// If size limit reached, clear buffer
-    if (Items[ItemNo].x.size() > Items[ItemNo].SizeLimit) {
-	  Items[ItemNo].x.clear();
-	  Items[ItemNo].y.clear();
-    }
-
-	// If buffer empty, request new history buffer
-    if (Items[ItemNo].x.isEmpty()) {
-	  int Time, Size;
-	  void *Data;
-
-	  class EvidenceHistory *HistClass = Handler->GetHistory(Items[ItemNo].Name);
-	  
-	  if (HistClass->GetHistory()) {
-     	double Smallest = DBL_MAX, Largest = DBL_MIN;
-		double Number=0;
-		while (HistClass->Next(Time, Size, Data)) {
-		  switch (Format[0].toUpper().toAscii()) {
-    		case 'I':
-			case 'L':  Number = *((int *) Data + Items[ItemNo].Index);   break;
-    		case 'S':  Number = *((short *) Data + Items[ItemNo].Index);   break;
-    		case 'F':  Number = *((float *) Data + Items[ItemNo].Index);   break;
-    		case 'D':  Number = *((double *) Data + Items[ItemNo].Index);   break;
-    		case 'X':  Number = *((long long *) Data + Items[ItemNo].Index);   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
-	QString Txt = Text;
-	Txt = Txt.section(' ', Items[ItemNo].Index, Items[ItemNo].Index);
-    Items[ItemNo].x.append(Time);
-    Items[ItemNo].y.append(atof(Txt.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); 
-    setStatusTip(QString("%1:  Last update %2   Format '%3'").arg(Name).arg(Timex.toString()).arg(Format));
-  }
-
-  UpdatePlot();
-}
-
-//
+}
+
 // Update all curves in plot
-//
-void EddPlot::UpdatePlot() {
+void EddBasePlot::UpdatePlot() {
 
   double Lower = axisScaleDiv(QwtPlot::xBottom)->lowerBound();
@@ -394,7 +440,4 @@
   }
   else setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine);
-
-  // Lock before accessing Items list
-  QMutexLocker Locker(Mutex);
 
   for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
@@ -447,20 +490,43 @@
 }
 
-// Add text indicating time range to plot
-void EddPlot::paintEvent(QPaintEvent *) {
-
-  QString Text;
-  QFont Font;
-  QPainter Painter(this);
-  
-  Text = QDateTime::fromTime_t((int) axisScaleDiv(QwtPlot::xBottom)->lowerBound()).toString("d-MMM-yyyy hh:mm:ss") + " to " + QDateTime::fromTime_t((int) axisScaleDiv(QwtPlot::xBottom)->upperBound()).toString("d-MMM-yyyy hh:mm:ss");
-
-  Font.setPointSize(6);  
-  Painter.setFont(Font);
-  Painter.drawText(0, height(),Text);
+// Append curve to plot
+QwtPlotCurve *EddBasePlot::NewCurve(QwtText Title) {
+
+  struct PlotItem N;
+
+  N.Signal = new QwtPlotCurve;
+  N.Signal->attach(this);
+  N.Signal->setTitle(Title);
+  N.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
+  N.Largest = DBL_MIN;
+  N.Smallest = DBL_MAX;  
+  Items.append(N);
+  
+  return N.Signal;
+}
+
+// Clear curve data
+void EddBasePlot::ClearCurve(unsigned int Item) {
+
+  if (Item >= (unsigned int) Items.size()) return;
+  
+  Items[Item].x.clear();
+  Items[Item].y.clear();
+}
+
+// Append data point
+void EddBasePlot::AddPoint(unsigned int Item, double x, double y) {
+
+  if (Item >= (unsigned int) Items.size()) return;
+  
+  Items[Item].x.append(x);
+  Items[Item].y.append(y);
+
+  if (y > Items[Item].Largest) Items[Item].Largest = y;
+  if (y < Items[Item].Smallest) Items[Item].Smallest = y;    
 }
 
 // Rescale plot in case selection has been made outside the canvas
-void EddPlot::MouseSelection(const QPolygon &P) {
+void EddBasePlot::MouseSelection(const QPolygon &P) {
  
   QwtDoubleInterval xPlot, xMouse, yPlot, yMouse; 
@@ -520,9 +586,6 @@
 } 
 
-
-//
 // Reset graph axes to autoscale when fully unzoomed
-//
-void EddPlot::HandleZoom(const QwtDoubleRect &) {
+void EddBasePlot::HandleZoom(const QwtDoubleRect &) {
 
   if(Zoomer->zoomRectIndex() == 0) {
@@ -531,44 +594,13 @@
   }
 }
-
-//
-// Drag and drop methods
-//
-
-void EddPlot::dragEnterEvent(QDragEnterEvent *Event) {
-    
-  if (Event->mimeData()->hasFormat("Edd/Service")) Event->acceptProposedAction();
-}
-
-void EddPlot::dropEvent(QDropEvent *Event) {
-
-  QByteArray D(Event->mimeData()->data("Edd/Service"));
-  AddService(D.left(D.lastIndexOf(' ')), D.right(D.size()-D.lastIndexOf(' ')).toInt());
-}
     
 // Opening context menu
-void EddPlot::contextMenuEvent(QContextMenuEvent *Event) {
+void EddBasePlot::contextMenuEvent(QContextMenuEvent *Event) {
 
   Menu->exec(Event->globalPos());
 }
 
-// Drag&Drop method
-void EddPlot::LegendClicked(QwtPlotItem *Item) {
-
-
-  QString D(Item->title().text());
-  D.replace('(',' ').chop(1);
-  
-  QDrag *Drag = new QDrag(this);
-  QMimeData *MimeData = new QMimeData;
-  QByteArray Data;
-  MimeData->setData("Edd/Service", Data.append(D));
-  Drag->setMimeData(MimeData);
-  Drag->exec();
-}
-
-
 // Zoom completely out
-void EddPlot::MenuZoomOut() {
+void EddBasePlot::MenuZoomOut() {
 
   Zoomer->zoom(0);
@@ -576,12 +608,9 @@
 }
 
-// Remove all items except last
-void EddPlot::MenuSingleTrace() {
-
-  // Lock before accessing Items list
-  QMutexLocker Locker(Mutex);
+// Remove all items except one
+void EddBasePlot::MenuSingleTrace() {
 
   while (Items.size() > 1) {  
-    Handler->Unsubscribe(Items.last().Name);
+	DeleteCurve(Items.last().Signal);
     delete Items.last().Signal;
     Items.takeLast();
@@ -591,5 +620,5 @@
 
 // Save data of plot as test 
-void EddPlot::MenuSaveASCII() {
+void EddBasePlot::MenuSaveASCII() {
   QString Filename = QFileDialog::getSaveFileName(this,
      "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)");
@@ -601,12 +630,10 @@
     return;
   }
-  
-  // Lock before accessing Items list
-  QMutexLocker Locker(Mutex);
+
+   // Write x and y data for all signals to file
   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].Name + ".hist" << endl;
+    Stream << QString("# ") + Items[ItemNo].Signal->title().text() + ".hist" << endl;
     for (int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
       Stream << (int) Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
@@ -616,5 +643,5 @@
 
 // Print plot
-void EddPlot::MenuPrint() {
+void EddBasePlot::MenuPrint() {
 
   QPrinter *Printer = new QPrinter;
@@ -629,5 +656,5 @@
 
 // Save plot as image
-void EddPlot::MenuSave() {
+void EddBasePlot::MenuSave() {
 
   QString Filename = QFileDialog::getSaveFileName(this,
@@ -642,16 +669,6 @@
 }
 
-// Add new service by pasting name
-void EddPlot::MenuPasteService() {
-
-  const QMimeData *D = QApplication::clipboard()->mimeData();
-  if (!D->hasFormat("Edd/Service")) return;
-  
-  QByteArray E(D->data("Edd/Service"));
-  AddService(E.left(E.lastIndexOf(' ')), E.right(E.size()-E.lastIndexOf(' ')).toInt());
-}
-
 // Help text
-void EddPlot::MenuPlotHelp() {
+void EddBasePlot::MenuPlotHelp() {
 
   QMessageBox::about(this, "Edd - Plot navigation",
@@ -667,12 +684,9 @@
 }
 
-
 //////////////////////////////////////
 // History text box for DIM service //
 //////////////////////////////////////
 
-//
 // Constructor
-//
 EddText::EddText(QString Name, bool Pure, QWidget *P):
 	QTextEdit(P), Name(Name), Pure(Pure) {
@@ -724,6 +738,5 @@
 
   // Check if service available
-  if (Time == -1) {
-	setStatusTip(QString("%1:  unavailable").arg(Name));
+  if (!SetStatus(this, Name, Time, Format)) {
     Pal.setColor(QPalette::Base, Qt::lightGray);
 	setPalette(Pal);
@@ -744,7 +757,4 @@
   }
   else 	if (Format == "C") insertPlainText(Text);
-
-  // Update status tip
-  setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Name).arg(Timex.toString()).arg(Format));
 }
 
@@ -883,5 +893,459 @@
 }
 
-
+//
+//
+// ====== FACT specific part ======
+//
+//
+
+////////////////////////
+// Event oscilloscope //
+////////////////////////
+
+// Constructor
+EventScope::EventScope(QWidget *P): EddBasePlot(P), PixelMap("../../config/PixelMap.txt", false) {
+
+  Name = "Feedback/EventData";
+
+  Tmpfile = tmpfile();
+  if(Tmpfile == NULL) {
+    QMessageBox::warning(this, "Edd Message", "Could not open temporary file.", QMessageBox::Ok);
+  }
+
+  // Open file with RawDataCTX
+  RD = new RawDataCTX(true);
+  ErrCode = CTX_NOTOPEN;
+
+  // Context menu
+  PhysPipeAction = new QAction("Plot physical pipeline", this);
+  PhysPipeAction->setCheckable(true);
+  connect(PhysPipeAction, SIGNAL(triggered()), SLOT(PlotTraces()));
+  Menu->insertAction(StripAction, PhysPipeAction);
+  Menu->removeAction(StripAction);
+
+  // Initial trace
+  AddTrace(0,0,0);
+
+  // Connect to DIM handler
+  if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
+    printf("Failed connection for %s\n", Name.toAscii().data());
+  }
+  Handler->Subscribe(Name);
+}
+
+// Destructor (items with parent widget are automatically deleted)
+EventScope::~EventScope() {
+
+  Handler->Unsubscribe(Name);
+  while (!List.isEmpty()) DeleteCurve(List.last().Signal);
+  delete RD;
+  if (Tmpfile != NULL) fclose(Tmpfile);
+}  
+
+// Add trace
+void EventScope::AddTrace(int Board, int Chip, int Channel) {
+
+  struct ItemDetails N;
+  
+  N.Signal = NewCurve(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel)+ " (" + DRS_to_Pixel(Board, Chip, Channel).c_str() + ")");
+  N.Board = Board;
+  N.Chip = Chip;
+  N.Channel = Channel;
+  N.Trigger = new QwtPlotMarker();
+  N.Trigger->setSymbol(QwtSymbol(QwtSymbol::Diamond, QBrush(N.Signal->pen().color()), N.Signal->pen(), QSize(10,10)));
+  N.Trigger->attach(this);
+
+  if (List.isEmpty()) {
+    QPen Pen = N.Signal->pen();
+	Pen.setWidth(2);
+	N.Signal->setPen(Pen);
+  }
+  List.append(N);
+    
+  PlotTraces();
+}
+
+// Update last trace (to reflect current setting of spin boxes in DAQ page)
+void EventScope::UpdateFirst(int Board, int Chip, int Channel) {
+
+  if (List.isEmpty()) return;
+  
+  List.first().Signal->setTitle(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel) + " (" + DRS_to_Pixel(Board, Chip, Channel).c_str() + ")");
+  List.first().Board = Board;
+  List.first().Chip = Chip;
+  List.first().Channel = Channel;
+    
+  PlotTraces();
+}
+  
+// Update event buffer
+void EventScope::Update(QString Name, int Time, QByteArray Data, QString Format, QString) {
+
+  if (Name != this->Name) return;
+
+  // Check if service available
+  if (!SetStatus(this, Name, Time, Format)) return;
+  if (Data.size() < (int) sizeof(RunHeader)) return;
+  
+  // Open tempory file and write event data to this file
+  QTemporaryFile File;
+  if (!File.open()) {
+	QMessageBox::warning(this, "Edd Message","Could not open temporary file.",QMessageBox::Ok);
+	return;
+  }
+  if (File.write(Data) == -1) {
+	QMessageBox::warning(this, "Edd Message","Could not write data to temporary file.",QMessageBox::Ok);
+	return;	
+  }
+
+  // Prepare temporary file for run header  
+  ftruncate(fileno(Tmpfile), 0);
+  rewind(Tmpfile);
+
+  // Open file with RawDataCTX
+  switch (ErrCode = RD->OpenDataFile(File.fileName().toAscii().data(), Tmpfile)) {
+    case CTX_FOPEN:		QMessageBox::warning(this, "Edd Message","Could not open file.",QMessageBox::Ok);
+						return;
+    case CTX_RHEADER:	QMessageBox::warning(this, "Edd Message","Could not read run header.",QMessageBox::Ok);
+		      			return;
+    case CTX_BSTRUCT:	QMessageBox::warning(this, "Edd Message","Could not read board structures.",QMessageBox::Ok);
+						return;
+	default: break;
+  }
+
+  // Emit signal containing run header
+  rewind(Tmpfile);
+  QTextStream Stream(Tmpfile);
+  emit(RunHeaderChanged(Stream.readAll()));
+
+  // Prepare temporary file for event header  
+  ftruncate(fileno(Tmpfile), 0);
+  rewind(Tmpfile);
+
+  if (RD->ReadEvent(0, Tmpfile) != CTX_OK) {
+    QMessageBox::warning(this, "Edd Warning","Could not read event.",QMessageBox::Ok);
+    return;
+  }
+
+  // Emit signal containing run header
+  rewind(Tmpfile);
+  emit(EventHeaderChanged(Stream.readAll()));
+
+  PlotTraces();
+}
+
+// Update curves
+void EventScope::PlotTraces() {
+
+  double x,y;
+  unsigned int Cell, Trig;
+  
+  // Only process if valid data in RawDataCTX class
+  if (ErrCode != CTX_OK) return;
+  
+  // Set x axis title
+  if (PhysPipeAction->isChecked()) setAxisTitle(QwtPlot::xBottom, "Time from start of pipeline (ns)");
+  else setAxisTitle(QwtPlot::xBottom, "Time from trigger minus one revolution (ns)");
+  
+  // Loop through event data
+  RunHeader *R = RD->RHeader;
+  for (int i=0; i<List.size(); i++) {
+
+	ClearCurve(i);
+
+ 	// Check if current event contains requested trace
+    if (List[i].Board>=R->NBoards || List[i].Chip>=R->NChips || List[i].Channel>=R->NChannels) continue;
+
+	// Set trigger marker visibility
+	List[i].Trigger->setVisible(PhysPipeAction->isChecked());
+	
+	// Determine trigger cell
+	Trig = *((int *) RD->Data + List[i].Board*R->NChips + List[i].Chip);
+
+	for (unsigned int j=0; j<R->Samples; j++) {
+
+	  if (PhysPipeAction->isChecked()) Cell = (j + Trig) % 1024;
+	  else Cell = j;
+
+	  x = j / RD->BStruct[List[i].Board].NomFreq;
+	  y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) +
+	  	List[i].Board*R->NChips*R->NChannels*R->Samples + List[i].Chip*R->NChannels*R->Samples +
+		List[i].Channel*R->Samples + Cell) * RD->BStruct[List[i].Board].ScaleFactor;
+
+	  AddPoint(i, x, y);
+	  
+	  // Set trigger point indicator
+	  if (Trig == j/*+R->Offset*/) List[i].Trigger->setValue(x, y);	
+    }
+  }
+
+  UpdatePlot();
+}
+
+// Remove list entry
+void EventScope::DeleteCurve(QwtPlotCurve *Curve) {
+
+  for (int i=0; i<List.size(); i++) if (List[i].Signal == Curve) {
+	delete List[i].Trigger;
+    List.removeAt(i);
+  }
+}
+
+//
+// Tab page classes
+//
+
+// Environment page
+TP_Environment::TP_Environment() {
+
+  QGridLayout *Layout = new QGridLayout(this);
+
+  // Status display
+  EddLineDisplay *Line = new EddLineDisplay("ARDUINO/Status");
+  Line->setMaximumWidth(200);
+  Layout->addWidget(Line, 0, 0, 1, 3);      
+
+  // Generate plot and data displays
+  EddPlot *Plot = new EddPlot();
+  for (int i=0; i<10; i++) {
+    Line = new EddLineDisplay("ARDUINO/Data", i);
+    Layout->addWidget(Line, i%5+1, i/5, 1, 1);
+    Plot->AddService("ARDUINO/Data", i);
+  }
+  Layout->addWidget(Plot, 0, 3, 6, 4);      
+
+  // Night sky monitor
+  Line = new EddLineDisplay("SQM/NSB");
+  Layout->addWidget(Line, 6, 0, 1, 1);          
+}
+
+// Bias page
+TP_Bias::TP_Bias() {
+
+  QGridLayout *Layout = new QGridLayout(this);
+  EddLineDisplay *Line;
+  
+  EddPlot *Plot = new EddPlot();
+  Plot->setMinimumWidth(400);
+  for (int i=0; i<18; i++) {
+    Line = new EddLineDisplay("Bias/VOLT/ID00", i);
+    Layout->addWidget(Line, i%9+1, 0+i/9, 1, 1);
+    Plot->AddService("Bias/VOLT/ID00", i);
+
+    Line = new EddLineDisplay("Bias/VOLT/ID00", i+32);
+    Layout->addWidget(Line, i%9+1, 2+i/9, 1, 1);
+    Plot->AddService("Bias/VOLT/ID00",i+32);
+  }
+
+  Layout->addWidget(Plot, 0, 4, 12, 3);
+  Line = new EddLineDisplay("Bias/Status");
+  Line->setMaximumWidth(200);
+  Layout->addWidget(Line, 0, 0, 1, 3);      
+
+  EddText *Text = new EddText("Bias/Textout", true);
+  Text->setFixedWidth(400);
+  Layout->addWidget(Text, 10, 0, 4, 4);      
+}
+
+// Feedback page
+TP_Feedback::TP_Feedback() {
+
+  QGridLayout *Layout = new QGridLayout(this);
+  EddLineDisplay *Line;
+
+  EddPlot *Plot = new EddPlot();
+  for (int i=0; i<36; i++) {
+    Line = new EddLineDisplay("Feedback/Average", i);
+    Layout->addWidget(Line, i%9+1, 0+i/9, 1, 1);
+    Plot->AddService("Feedback/Average", i);
+  }
+  Layout->addWidget(Plot, 0, 4, 11, 3);
+
+  //Graph = new EddPlot();
+  //for (int i=0; i<36; i++) {
+    //Text = Text.sprintf("Feedback/Sigma/ID%.2d/%.2d-%.3d",i/16, (i%16)/8, i%8);
+    //Graph->AddService(Text);
+  //}
+  //FeedbackLayout->addWidget(Graph, 10, 0, 10, 3);
+
+  Line = new EddLineDisplay("Feedback/Status");
+  Line->setMaximumWidth(200);
+  Layout->addWidget(Line, 0, 0, 1, 3);      
+  Line = new EddLineDisplay("Feedback/Count");
+  Layout->addWidget(Line, 0, 3, 1, 1);      
+
+  QWidget *Button = new QPushButton("Details");
+  Layout->addWidget(Button, 10, 0, 1, 1);      
+  connect(Button, SIGNAL(pressed()), SLOT(FeedbackDetails()));
+
+}
+
+void TP_Feedback::FeedbackDetails() {
+
+  EddLineDisplay *Line;
+  QWidget *Widget = new QWidget();
+  QGridLayout *Layout = new QGridLayout(Widget);
+  EddPlot *Plot = new EddPlot();
+  for (int i=0; i<36; i++) {
+    Line = new EddLineDisplay("Feedback/Sigma", i);
+    Layout->addWidget(Line, i%9+1, 0+i/9, 1, 1);
+    Plot->AddService("Feedback/Sigma", i);
+  }
+  Layout->addWidget(Plot, 0, 4, 11, 3);
+  
+  Widget->show();
+}
+
+// DAQ page
+TP_DAQ::TP_DAQ() {
+
+  QGridLayout *Layout = new QGridLayout(this);
+
+  // Event scope
+  Scope = new EventScope;
+  Scope->setMinimumWidth(700);
+
+  // Text boxes for run and event header
+  RunHeaderDisplay = new QPlainTextEdit();
+  EventHeaderDisplay = new QPlainTextEdit();
+  RunHeaderDisplay->setReadOnly(true);
+  EventHeaderDisplay->setReadOnly(true);
+
+  // Tab widget
+  QTabWidget *TabWidget = new QTabWidget();
+  TabWidget->addTab(Scope, "&Signals");
+  TabWidget->addTab(RunHeaderDisplay, "&Run Header");
+  TabWidget->addTab(EventHeaderDisplay, "&Event Header");
+  Layout->addWidget(TabWidget, 0, 1, 3, 3);
+
+  connect(Scope, SIGNAL(RunHeaderChanged(QString)), RunHeaderDisplay, SLOT(setPlainText(QString)));
+  connect(Scope, SIGNAL(EventHeaderChanged(QString)), EventHeaderDisplay, SLOT(setPlainText(QString)));
+
+  // Channel number 
+  Channel = new QSpinBox;
+  connect(Channel, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
+  Channel->setToolTip("DRS channel number");
+
+  // Chip number 
+  Chip = new QSpinBox;
+  connect(Chip, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
+  Chip->setToolTip("DRS chip number");
+
+  // Board number 
+  Board = new QSpinBox;
+  connect(Board, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
+  Board->setToolTip("DRS board number");
+
+  // Pixel ID
+  PixelID = new QLineEdit;
+  PixelID->setMaximumWidth(60);
+  connect(PixelID, SIGNAL(returnPressed()), SLOT(TranslatePixelID()));
+  PixelID->setToolTip("Pixel identification");
+  
+  // Layout of pixel addressing widgets
+  QFormLayout *FormLayout = new QFormLayout();
+  FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
+  FormLayout->addRow("Channel", Channel);
+  FormLayout->addRow("Chip", Chip);
+  FormLayout->addRow("Board", Board);
+  FormLayout->addRow("Pixel ID", PixelID);
+  Layout->addLayout(FormLayout, 0, 0);
+  
+  // Add trace permanently
+  QPushButton *Button = new QPushButton("Keep trace");
+  Button->setToolTip("Keep trace in display");
+  Button->setMaximumWidth(80);
+  Layout->addWidget(Button, 2, 0);
+  connect(Button, SIGNAL(clicked()), SLOT(KeepCurrent()));
+}
+
+void TP_DAQ::TranslatePixelID() {
+  
+  if (Scope->Pixel_to_DRSboard(PixelID->text().toStdString()) == 999999999) {
+	QMessageBox::warning(this, "Edd Message","Pixel ID unknown.",QMessageBox::Ok);
+  }
+  else {
+    Board->setValue(Scope->Pixel_to_DRSboard(PixelID->text().toStdString()));
+    Chip->setValue(Scope->Pixel_to_DRSchip(PixelID->text().toStdString()));
+    Channel->setValue(Scope->Pixel_to_DRSchannel(PixelID->text().toStdString()));
+  }
+}
+
+// Update event scope
+void TP_DAQ::KeepCurrent() {
+
+  Scope->AddTrace(Board->value(), Chip->value(), Channel->value());
+}
+
+// Update event scope
+void TP_DAQ::UpdateScope(int) {
+
+  // Update pixel ID
+  PixelID->setText(Scope->DRS_to_Pixel(Board->value(), Chip->value(), Channel->value()).c_str());
+  // Update first trace
+  Scope->UpdateFirst(Board->value(), Chip->value(), Channel->value());
+}
+
+// DAQ page
+TP_Evidence::TP_Evidence() {
+
+  QGridLayout *Layout = new QGridLayout(this);
+  EddLineDisplay *Line;
+  EddText *Text;
+  
+  Line = new EddLineDisplay("Alarm/Status");
+  Line->setMaximumWidth(200);
+  Layout->addWidget(Line, 0, 0, 1, 2);      
+
+  Line = new EddLineDisplay("Alarm/MasterAlarm");
+  Layout->addWidget(Line, 0, 1, 1, 1);
+
+  Text = new EddText("Alarm/Summary", true);
+  Text->Accumulate = false;
+  Text->setMaximumWidth(200);
+  Text->setMaximumHeight(150);
+  Layout->addWidget(Text, 1, 0, 1, 2);
+
+  Line = new EddLineDisplay("DColl/Status");
+  Line->setMaximumWidth(200);
+  Layout->addWidget(Line, 3, 0, 1, 2);      
+
+  Line = new EddLineDisplay("DColl/DataSizekB");
+  Layout->addWidget(Line, 4, 0, 1, 1);
+
+  Line = new EddLineDisplay("DColl/LogSizekB");
+  Layout->addWidget(Line, 4, 1, 1, 1);
+
+  Line = new EddLineDisplay("DColl/CurrentFile");
+  Line->setMaximumWidth(400);
+  Layout->addWidget(Line, 5, 0, 1, 3);
+
+  Line = new EddLineDisplay("Config/Status");
+  Line->setMaximumWidth(200);
+  Layout->addWidget(Line, 6, 0, 1, 2);      
+
+  Line = new EddLineDisplay("Config/ModifyTime");
+  Line->setMaximumWidth(200);
+  Line->ShowAsTime = true;
+  Layout->addWidget(Line, 7, 0, 1, 1);
+
+  QPushButton *Button = new QPushButton();
+  Button->setText("Start DIM browser");
+  connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser()));
+  Layout->addWidget(Button, 7, 1, 1, 1);
+
+  Line = new EddLineDisplay("Edd/Rate_kBMin");
+  Layout->addWidget(Line, 8, 0, 1, 1);
+  Line = new EddLineDisplay("Edd/Total_MB");
+  Layout->addWidget(Line, 8, 1, 1, 1);
+}
+ 
+// Start DIM Browser
+void TP_Evidence::StartDIMBrowser() {
+
+  QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/");
+}
+
+ 
 //--------------------------------------------------------------------
 //*************************** Main GUI *******************************
@@ -891,5 +1355,5 @@
 //
 GUI::GUI() {
- 
+
   Handler = new EddDim();
   
@@ -901,158 +1365,11 @@
   setWindowTitle("Edd - Evidence Data Display");
 
-  EddLineDisplay *Value;
-  EddPlot *Graph;
-  EddText *Textout; 
-  QString Text;
-
-   // TextBox for value
-  
-
-  // 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();
-  MainLayout = new QGridLayout(MainWidget);
-
-  Value = new EddLineDisplay("Alarm/Status");
-  Value->setMaximumWidth(200);
-  MainLayout->addWidget(Value, 0, 0, 1, 2);      
-
-  Value = new EddLineDisplay("Alarm/MasterAlarm");
-  MainLayout->addWidget(Value, 0, 1, 1, 1);
-
-  Textout = new EddText("Alarm/Summary", true);
-  Textout->Accumulate = false;
-  Textout->setMaximumWidth(200);
-  Textout->setMaximumHeight(150);
-  MainLayout->addWidget(Textout, 1, 0, 1, 2);
-
-  Value = new EddLineDisplay("DColl/Status");
-  Value->setMaximumWidth(200);
-  MainLayout->addWidget(Value, 3, 0, 1, 2);      
-
-  Value = new EddLineDisplay("DColl/DataSizekB");
-  MainLayout->addWidget(Value, 4, 0, 1, 1);
-
-  Value = new EddLineDisplay("DColl/LogSizekB");
-  MainLayout->addWidget(Value, 4, 1, 1, 1);
-
-  Value = new EddLineDisplay("DColl/CurrentFile");
-  Value->setMaximumWidth(400);
-  MainLayout->addWidget(Value, 5, 0, 1, 3);
-
-  Value = new EddLineDisplay("Config/Status");
-  Value->setMaximumWidth(200);
-  MainLayout->addWidget(Value, 6, 0, 1, 2);      
-
-  Value = new EddLineDisplay("Config/ModifyTime");
-  Value->setMaximumWidth(200);
-  Value->ShowAsTime = true;
-  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);
-
-  Value = new EddLineDisplay("Edd/Rate_kBMin");
-  MainLayout->addWidget(Value, 8, 0, 1, 1);
-  Value = new EddLineDisplay("Edd/Total_MB");
-  MainLayout->addWidget(Value, 8, 1, 1, 1);
-
-  // Layout of all widgets
-  //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(Clock, 0, 1, 1, 1);
-  //MainLayout->addWidget(Graph1, 2, 0, 1, 2);
-
-  // Feedback page
-  FeedbackWidget = new QWidget();
-  FeedbackLayout = new QGridLayout(FeedbackWidget);
-  Graph = new EddPlot();
-  for (int i=0; i<36; i++) {
-    Value = new EddLineDisplay("Feedback/Average", i);
-    FeedbackLayout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
-    Graph->AddService("Feedback/Average", i);
-  }
-  FeedbackLayout->addWidget(Graph, 0, 4, 11, 3);
-
-  //Graph = new EddPlot();
-  //for (int i=0; i<36; i++) {
-    //Text = Text.sprintf("Feedback/Sigma/ID%.2d/%.2d-%.3d",i/16, (i%16)/8, i%8);
-    //Graph->AddService(Text);
-  //}
-  //FeedbackLayout->addWidget(Graph, 10, 0, 10, 3);
-
-  Value = new EddLineDisplay("Feedback/Status");
-  Value->setMaximumWidth(200);
-  FeedbackLayout->addWidget(Value, 0, 0, 1, 3);      
-  Value = new EddLineDisplay("Feedback/Count");
-  FeedbackLayout->addWidget(Value, 0, 3, 1, 1);      
-
-  QWidget *Value1 = new QPushButton("Details");
-  FeedbackLayout->addWidget(Value1, 10, 0, 1, 1);      
-  connect(Value1, SIGNAL(pressed()), this, SLOT(FeedbackDetails()));
-  
-
-  // Bias voltage page
-  BiasWidget = new QWidget();
-  BiasLayout = new QGridLayout(BiasWidget);
-  Graph = new EddPlot();
-  for (int i=0; i<18; i++) {
-    Value = new EddLineDisplay("Bias/VOLT/ID00", i);
-    BiasLayout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
-    Graph->AddService("Bias/VOLT/ID00", i);
-
-    Value = new EddLineDisplay("Bias/VOLT/ID00", i+32);
-    BiasLayout->addWidget(Value, i%9+1, 2+i/9, 1, 1);
-    Graph->AddService("Bias/VOLT/ID00",i+32);
-  }
-
-  BiasLayout->addWidget(Graph, 0, 4, 12, 3);
-  Value = new EddLineDisplay("Bias/Status");
-  Value->setMaximumWidth(200);
-  BiasLayout->addWidget(Value, 0, 0, 1, 3);      
-
-  Textout = new EddText("Bias/Textout", true);
-  Textout->setFixedWidth(400);
-  BiasLayout->addWidget(Textout, 10, 0, 4, 4);      
-
-  // Environment page
-  EnvironmentWidget = new QWidget();
-  EnvironmentLayout = new QGridLayout(EnvironmentWidget);
-  Value = new EddLineDisplay("ARDUINO/Status");
-  Value->setMaximumWidth(200);
-  EnvironmentLayout->addWidget(Value, 0, 0, 1, 3);      
-
-  Graph = new EddPlot();
-  for (int i=0; i<10; i++) {
-    Value = new EddLineDisplay("ARDUINO/Data", i);
-    EnvironmentLayout->addWidget(Value, i%5+1, i/5, 1, 1);
-    Graph->AddService("ARDUINO/Data", i);
-  }
-  EnvironmentLayout->addWidget(Graph, 0, 3, 6, 4);      
-
-  Value = new EddLineDisplay("SQM/NSB");
-  EnvironmentLayout->addWidget(Value, 6, 0, 1, 1);      
-
-  // Tab widget
+  // Arrangement in tabs
   TabWidget = new QTabWidget(Central);
-  TabWidget->addTab(BiasWidget, "&Bias");
-  TabWidget->addTab(FeedbackWidget, "&Feedback");
-  TabWidget->addTab(EnvironmentWidget, "&Environment");
-  TabWidget->addTab(MainWidget, "Evidence");
+  TabWidget->addTab(new TP_DAQ, "&drsdaq");
+  TabWidget->addTab(new TP_Bias, "&Bias");
+  TabWidget->addTab(new TP_Feedback, "&Feedback");
+  TabWidget->addTab(new TP_Environment, "&Environment");
+  TabWidget->addTab(new TP_Evidence, "Evidence");
 
   // Menu bar
@@ -1112,27 +1429,4 @@
 }
 
-// Feedback details page
-void GUI::FeedbackDetails() {
-
-  QWidget *Value;
-  QWidget *Widget = new QWidget();
-  QGridLayout *Layout = new QGridLayout(Widget);
-  EddPlot *Graph = new EddPlot();
-  for (int i=0; i<36; i++) {
-    Value = new EddLineDisplay("Feedback/Sigma", i);
-    Layout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
-    Graph->AddService("Feedback/Sigma", i);
-  }
-  Layout->addWidget(Graph, 0, 4, 11, 3);
-  
-  Widget->show();
-}
-
-// Start DIM Browser
-void GUI::StartDIMBrowser() {
-
-  QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/");
-}
-
 // Quit application when clicking close button on window
 void GUI::closeEvent(QCloseEvent *) {
Index: Evidence/Edd/Edd.h
===================================================================
--- Evidence/Edd/Edd.h	(revision 204)
+++ Evidence/Edd/Edd.h	(revision 208)
@@ -18,4 +18,5 @@
 #include <qwt_symbol.h>
 #include <qwt_plot_marker.h>
+#include <qwt_data.h>
 
 #include <limits.h>
@@ -24,4 +25,6 @@
 #include "dic.hxx"
 #include "Evidence.h"
+#include "RawDataCTX.h"
+#include "PixelMap.h"
 
 #define SVN_REVISION "$Revision$"
@@ -30,83 +33,28 @@
 
 QWidget *OpenHistory(char *, int);
-
-// Time scale for axis
-class EddTimeScale: public QwtScaleDraw {
-
-  public:
-    EddTimeScale() {}
-
-    virtual QwtText label(double v) const {
-
-	  // Adapt text format to time span 
-	  QString Format;
-	  if (scaleDiv().range() < 60*60) Format = "hh' h\n'mm:ss";
-	  else if (scaleDiv().range() < 24*60*60) Format = "hh:mm";
-	  else if (scaleDiv().range() < 30*24*60*60) Format = "h' h\n'd-MMM";
-	  else Format = "d-MMM'\n'yyyy";
-
-	  // Generate text
-	  QwtText Text = QDateTime::fromTime_t((int) v).toString(Format);
-	  QFont Font = Text.font();
-	  Font.setPointSize(7);
-	  Text.setFont(Font);
-
-      return Text;
-    }
-};
-
-		  
-// General indicator for DIM service
-class EddLineDisplay: public QLineEdit {
-    Q_OBJECT
-
+bool SetStatus(QWidget *, QString, int, QString, int = -1);
+
+// Base class for Edd plot
+// DeleteCurve() is pure virtual and needs to be implemented iin the application class
+class EddBasePlot: public QwtPlot {
+  Q_OBJECT
+
+  protected:    
     QMenu *Menu;
-    QPoint dragStart;
-    QWidget *LastPlot;
-	
-	QString ServiceName;
-	int Index;
-	
-    void mousePressEvent(QMouseEvent *); 
-    void mouseReleaseEvent(QMouseEvent *); 
-    void mouseMoveEvent(QMouseEvent *); 
-	
-  public:
-    EddLineDisplay(QString, int=0, QWidget * = NULL);
-    ~EddLineDisplay();
-
-	bool ShowAsTime;
-	
-  private slots:
-	void Update(QString, int, QByteArray, QString, QString);
-    void contextMenuEvent(QContextMenuEvent *);    
-    void MenuOpenHistory();
-    void MenuCopyService();
-    void MenuCopyData();
-};
-
-// Graph class for history display
-class EddPlot: public QwtPlot {
-    Q_OBJECT
-
+    QAction *StripAction;
+
+  private:
     struct PlotItem {
-      QString Name;
       QwtPlotCurve *Signal;
-      double Smallest;
-      double Largest;
-	  int SizeLimit;
 	  QVector<double> x;
 	  QVector<double> y;
-	  int Index;	  
+      double Smallest;
+      double Largest;	  
     };
-
     QList<struct PlotItem> Items;
-    QMutex *Mutex;
-    
-    QMenu *Menu;
+
     QAction *YLogAction;
     QAction *NormAction;
     QAction *StyleAction;
-    QAction *StripAction;
     
     QwtPlotPanner *Panner;
@@ -115,6 +63,97 @@
 	QwtPlotMagnifier *Magnifier;
     QwtPicker *Picker;
+	QwtDoubleRect BBox;
+	 	
+  public:
+    EddBasePlot(QWidget * = NULL);
+    ~EddBasePlot();
+
+	QwtPlotCurve *NewCurve(QwtText);
+	void ClearCurve(unsigned int);
+	void AddPoint(unsigned int, double, double);
+	virtual void DeleteCurve(QwtPlotCurve *) = 0;
+
+  protected slots:
+    void UpdatePlot();
+
+  private slots:
+    void HandleZoom(const QwtDoubleRect &);
+	void MouseSelection(const QwtPolygon &);
+	void contextMenuEvent(QContextMenuEvent *);
+    void MenuSingleTrace();        
+    void MenuZoomOut();
+    void MenuSaveASCII();
+    void MenuSave();
+    void MenuPrint();
+	void MenuPlotHelp();
+};
+		  
+// General indicator for DIM service
+class EddLineDisplay: public QLineEdit {
+    Q_OBJECT
+
+    QMenu *Menu;
+    QPoint dragStart;
+    QWidget *LastPlot;
+	
+	QString ServiceName;
+	int Index;
+	
+    void mousePressEvent(QMouseEvent *); 
+    void mouseReleaseEvent(QMouseEvent *); 
+    void mouseMoveEvent(QMouseEvent *); 
+	
+  public:
+    EddLineDisplay(QString, int=0, QWidget * = NULL);
+    ~EddLineDisplay();
+
+	bool ShowAsTime;
+	
+  private slots:
+	void Update(QString, int, QByteArray, QString, QString);
+    void contextMenuEvent(QContextMenuEvent *);    
+    void MenuOpenHistory();
+    void MenuCopyService();
+    void MenuCopyData();
+};
+
+// Graph class for history display 
+class EddPlot: public EddBasePlot {
+    Q_OBJECT
+
+	// Time scale for axis
+	class EddTimeScale: public QwtScaleDraw {
+
+	  public:
+		EddTimeScale() {}
+
+		virtual QwtText label(double v) const {
+		  // Adapt text format to time span 
+		  QString Format;
+		  if (scaleDiv().range() < 60*60) Format = "hh' h\n'mm:ss";
+		  else if (scaleDiv().range() < 24*60*60) Format = "hh:mm";
+		  else if (scaleDiv().range() < 30*24*60*60) Format = "h' h\n'd-MMM";
+		  else Format = "d-MMM'\n'yyyy";
+
+		  // Generate text
+		  QwtText Text = QDateTime::fromTime_t((int) v).toString(Format);
+		  QFont Font = Text.font();
+		  Font.setPointSize(7);
+		  Text.setFont(Font);
+
+    	  return Text;
+		}
+	};
+
+    struct ItemDetails {
+      QString Name;
+	  int Index;
+      QwtPlotCurve *Signal;	  
+    };
+    QList<struct ItemDetails> List;
+
+  private:
 	QwtLegend *Legend;
-	QwtDoubleRect BBox;
+	int SizeLimit;
 
     void dragEnterEvent(QDragEnterEvent *);
@@ -126,26 +165,18 @@
     ~EddPlot();
     void AddService(QString, int = 0);
-
-  private slots:
-	void Update(QString, int, QByteArray, QString, QString);
-    void UpdatePlot();
-
-    void HandleZoom(const QwtDoubleRect &);
-	void MouseSelection(const QwtPolygon &);
-	void contextMenuEvent(QContextMenuEvent *);
+	void DeleteCurve(QwtPlotCurve *);
+
+  private slots:
+	void Update(QString, int, QByteArray, QString, QString);
 	void LegendClicked(QwtPlotItem *);
-    void MenuZoomOut();
-    void MenuSingleTrace();        
-    void MenuSaveASCII();
-    void MenuSave();
-    void MenuPrint();
     void MenuPasteService();
-	void MenuPlotHelp();
-};
+};
+
 
 // Text history and output class
 class EddText: public QTextEdit {
-    Q_OBJECT
-
+  Q_OBJECT
+
+  private:
 	QString Name;
 	bool Pure;
@@ -161,8 +192,10 @@
 };
 
+
 // Interface to DIM system
 class EddDim: public QObject, public DimInfo {
-    Q_OBJECT
-
+  Q_OBJECT
+
+  private:
 	struct Item {
 	  QString Name;
@@ -205,14 +238,108 @@
 };
 
+//
+//
+// ====== FACT specific part ======
+//
+//
+
+// Event oscilloscope
+class EventScope: public EddBasePlot, public PixelMap {
+  Q_OBJECT
+
+  private:
+    struct ItemDetails {
+	  unsigned int Board, Chip, Channel;
+      QwtPlotCurve *Signal;
+	  QwtPlotMarker *Trigger;
+    };
+    QList<struct ItemDetails> List;
+
+    QString Name;
+	RawDataCTX *RD;
+	CTX_ErrCode ErrCode;
+	QAction *PhysPipeAction;
+	FILE *Tmpfile;
+
+  public:
+    EventScope(QWidget * = NULL);
+    ~EventScope();
+	
+	void UpdateFirst(int, int, int);
+	void AddTrace(int, int, int);
+
+  private slots:
+	void Update(QString, int, QByteArray, QString, QString);
+	void PlotTraces();
+	void DeleteCurve(QwtPlotCurve *);
+	
+  signals:
+	void RunHeaderChanged(QString);
+	void EventHeaderChanged(QString);
+};
+
+
+// Tab page classes
+class TP_Environment: public QWidget {
+  Q_OBJECT
+
+  public:
+    TP_Environment();
+};
+
+class TP_Bias: public QWidget {
+  Q_OBJECT
+
+  public:
+    TP_Bias();
+};
+
+class TP_Feedback: public QWidget {
+  Q_OBJECT
+
+  private slots:
+	void FeedbackDetails();
+
+  public:
+    TP_Feedback();
+};
+
+class TP_DAQ: public QWidget {
+  Q_OBJECT
+
+  private:
+	EventScope *Scope;
+    QPlainTextEdit *RunHeaderDisplay, *EventHeaderDisplay;
+
+ 	QSpinBox *Channel, *Chip, *Board;
+ 	QLineEdit *PixelID;
+ 	QFormLayout *FormLayout;
+
+  private slots:
+	void TranslatePixelID();
+	void UpdateScope(int);
+	void KeepCurrent();
+
+  public:
+	TP_DAQ();	
+};
+
+class TP_Evidence: public QWidget {
+  Q_OBJECT
+
+  private slots:
+	void StartDIMBrowser();
+
+  public:
+	TP_Evidence();	
+};
+
 
 // Main window class
 class GUI: public QMainWindow, public DimBrowser {
-    Q_OBJECT
-
-    QwtAnalogClock *Clock;
-	
-    QWidget *Central, *MainWidget, *BiasWidget, *FeedbackWidget, *FeedbackDetailsWidget, *EnvironmentWidget;
-    QGridLayout *MainLayout, *BiasLayout, *FeedbackLayout, *FeedbackDetailsLayout, *EnvironmentLayout;
-
+  Q_OBJECT
+
+  private:
+    QWidget *Central;
     QTabWidget *TabWidget;
             
@@ -226,6 +353,4 @@
     void MenuAbout();
     void MenuNewHistory();
-	void StartDIMBrowser();
-	void FeedbackDetails();
 };
 
Index: Evidence/Edd/Edd.pro
===================================================================
--- Evidence/Edd/Edd.pro	(revision 204)
+++ Evidence/Edd/Edd.pro	(revision 208)
@@ -6,8 +6,8 @@
 TARGET = 
 DEPENDPATH += .
-INCLUDEPATH += . .. /usr/local/qwt-5.2.0/include $(DIMDIR)/dim
+INCLUDEPATH += . .. /ihp/local/qwt-5.2.1/include $(DIMDIR)/dim ../../drsdaq ../../pixelmap
 
 # Input
 HEADERS += Edd.h
-SOURCES += Edd.cc ../Evidence.cc
-LIBS += -L/usr/local/qwt-5.2.0/lib -lqwt $(DIMDIR)/linux/libdim.a
+SOURCES += Edd.cc ../Evidence.cc ../../drsdaq/RawDataCTX.cc ../../pixelmap/Pixel.cc ../../pixelmap/PixelMap.cc
+LIBS += -L/ihp/local/qwt-5.2.1/lib -lqwt $(DIMDIR)/linux/libdim.a
