]> git.uio.no Git - u/mrichter/AliRoot.git/blobdiff - EVE/EveDet/AliEveListAnalyser.cxx
Additional fix for bug #71737: DELETE_ARRAY reported by Coverity
[u/mrichter/AliRoot.git] / EVE / EveDet / AliEveListAnalyser.cxx
index c8ab0cf9563cbb822bbec565400ded16558212da..9092e0d1eea24165253ab0e436bb940b00939a2e 100644 (file)
@@ -1,30 +1,33 @@
-// Author: Benjamin Hess   06/11/2009
+// Author: Benjamin Hess   29/01/2010
 
 /*************************************************************************
- * Copyright (C) 2009, Alexandru Bercuci, Benjamin Hess.                 *
+ * Copyright (C) 2009-2010, Alexandru Bercuci, Benjamin Hess.            *
  * All rights reserved.                                                  *
  *************************************************************************/
 
-// TODO: Documentation
 //////////////////////////////////////////////////////////////////////////
 //                                                                      //
-// AliEveListAnalyser                                           //
+// AliEveListAnalyser                                                   //
 //                                                                      //
-// An AliEveListAnalyser is, in principal, a TEveElementList    //
-// with some sophisticated features. You can add macros to this list,   //
-// which then can be applied to the list of tracks (these tracks can be //
-// added to the list in the same way as for the TEveElementList).       //
+// An AliEveListAnalyser is, in principal, a TEveElementList with some  //
+// sophisticated features. You can add macros to this list, which then  //
+// can be applied to the list of analysis objects (these objects can be //
+// added to the list in the same way as for the TEveElementList, but    //
+// also "by clicking" (cf. AliEveListAnaLyserEditor)).                  //
 // In general, please use AddMacro(...) for this purpose.               //
 // Macros that are no longer needed can be removed from the list via    //
-// RemoveSelectedMacros(...).This function takes an iterator of the     //
+// RemoveSelectedMacros(...). This function takes an iterator of the    //
 // list of macros that are to be removed.                               //
-// be removed. An entry looks like:                                     //
+// An entry looks like:                                                 //
 // The data for each macro consists of path, name, type and the command //
 // that will be used to apply the macro. This stuff is stored in a map  //
 // which takes the macro name for the key and the above mentioned data  //
 // in a TGeneralMacroData-object for the value.                         //
 // You can get the macro type via GetMacroType(...).                    //
-// With ApplySTSelectionMacros(...) or ApplyProcessMacros(...)          //
+// To find the type of objects the macro will deal with (corresponds to //
+// "YourObjectType" in the examples below) please use                   //
+// GetMacroObjectType(...).                                             //
+// With ApplySOSelectionMacros(...) or ApplyProcessMacros(...)          //
 // respectively you can apply the macros to the track list via          //
 // iterators (same style like for RemoveSelectedMacros(...)(cf. above)).//
 // Selection macros (de-)select macros according to a selection rule    //
 //                                                                      //
 // Currently, the following macro types are supported:                  //
 // Selection macros:                                                    //
-// Bool_t YourMacro(const YourObjectType*);                              //
-// Bool_t YourMacro(const YourObjectType*, const YourObjectType*);        //
+// Bool_t YourMacro(const YourObjectType*);                             //
+// Bool_t YourMacro(const YourObjectType*, const YourObjectType2*);     //
 //                                                                      //
 // Process macros:                                                      //
-// void YourMacro(const YourObjectType*, Double_t*&, Int_t&);            //
-// void YourMacro(const YourObjectType*, const YourObjectType*,           //
+// void YourMacro(const YourObjectType*, Double_t*&, Int_t&);           //
+// void YourMacro(const YourObjectType*, const YourObjectType2*,        //
 //                Double_t*&, Int_t&);                                  //
-// TH1* YourMacro(const YourObjectType*);                                //
-// TH1* YourMacro(const YourObjectType*, const YourObjectType*);          //
+// TH1* YourMacro(const YourObjectType*);                               //
+// TH1* YourMacro(const YourObjectType*, const YourObjectType2*);       //
 //                                                                      //
 // The macros which take 2 tracks are applied to all track pairs        //
 // (whereby BOTH tracks of the pair have to be selected by the single   //
 // Uncomment to display debugging infos
 //#define AliEveListAnalyser_DEBUG
 
+#include <TEveManager.h>
+#include <TEveSelection.h>
 #include <TFile.h>
 #include <TFunction.h>
-#include <TMethodArg.h>
 #include <TH1.h>
 #include <TList.h>
 #include <TMap.h>
+#include <TMethodArg.h>
+#include <TMethodCall.h>
 #include <TObjString.h>
+#include <TQObject.h>
 #include <TROOT.h>
 #include <TSystem.h>
 #include <TTree.h>
 #include <TTreeStream.h>
-#include <TMethodCall.h>
-
-//TODO - NEW -> Ordering! resp. remove the non-needed files 
-#include <TQObject.h>
-#include <TEveManager.h>
-#include <TGLSelectRecord.h>
-#include <TGLViewer.h>
 
 #include <AliTRDReconstructor.h>
 
@@ -87,7 +87,7 @@
 ClassImp(AliEveListAnalyser)
 
 ///////////////////////////////////////////////////////////
-/////////////   AliEveListAnalyser ////////////////
+/////////////   AliEveListAnalyser ////////////////////////
 ///////////////////////////////////////////////////////////
 AliEveListAnalyser::AliEveListAnalyser(const Text_t* n, const Text_t* t, Bool_t doColor):
   TEveElementList(n, t, doColor),
@@ -166,13 +166,13 @@ Int_t AliEveListAnalyser::AddMacro(const Char_t* path, const Char_t* nameC, Bool
   // Supported macro types:
   // Selection macros:                                                    
   // Bool_t YourMacro(const YourObjectType*)
-  // Bool_t YourMacro(const YourObjectType*, const YourObjectType*)
+  // Bool_t YourMacro(const YourObjectType*, const YourObjectType2*)
   //
   // Process macros:                                                      
   // void YourMacro(const YourObjectType*, Double_t*&, Int_t&)             
-  // void YourMacro(const YourObjectType*, const TObject*, Double_t*&, Int_t&)                                   
+  // void YourMacro(const YourObjectType*, const YourObjectType2*, Double_t*&, Int_t&)                                   
   // TH1* YourMacro(const YourObjectType*)                                 
-  // TH1* YourMacro(const YourObjectType*, const YourObjectType*)                              
+  // TH1* YourMacro(const YourObjectType*, const YourObjectType2*)                              
 
   Char_t pathname[fkMaxMacroPathNameLength];
   memset(pathname, '\0', sizeof(Char_t) * fkMaxMacroPathNameLength);
@@ -202,15 +202,41 @@ Int_t AliEveListAnalyser::AddMacro(const Char_t* path, const Char_t* nameC, Bool
   gROOT->ProcessLineSync(Form(".L %s+%c", pathname, forceReload ? '+' : ' '));
 
   TClass* objectType;
+  TClass* objectType2;
 
-  objectType = GetMacroObjectType(name);
+  objectType = GetMacroObjectType(name, 1);
+  objectType2 = GetMacroObjectType(name, 2);
 
   // We need this line... otherwise, in some cases, there will be problems concerning ACLIC
   gROOT->ProcessLineSync(Form(".L %s", pathname));
 
   if (!objectType)  return UNKNOWN_OBJECT_TYPE_ERROR;
 
-  AliEveListAnalyserMacroType type = GetMacroType(name, objectType->GetName(), kFALSE);
+  // This might be a macro dealing with only 1 object... test this afterwards!
+  Bool_t testSecondObj = kFALSE;
+  if (!objectType2) 
+  {
+    objectType2 = TObject::Class();
+    testSecondObj = kTRUE;
+  }
+  AliEveListAnalyserMacroType type = GetMacroType(name, objectType->GetName(), objectType2->GetName(), kFALSE);
+
+  if (testSecondObj)
+  {
+    switch (type)
+    {
+    case AliEveListAnalyser::kCorrelObjectSelect:
+    case AliEveListAnalyser::kCorrelObjectAnalyse:
+    case AliEveListAnalyser::kCorrelObjectHisto:
+      // There must be a second type -> Error!
+      return UNKNOWN_OBJECT_TYPE_ERROR;
+      break;
+    default:
+      // Ok, single object macro!
+      break;
+    }
+  }
+
 
   // Clean up again
   // A.B. gROOT->Reset();
@@ -222,19 +248,21 @@ Int_t AliEveListAnalyser::AddMacro(const Char_t* path, const Char_t* nameC, Bool
   Int_t returnValue = WARNING;
   if(fMacroList->GetValue(name) == 0) 
   {
-    returnValue = AddMacroFast(path, name, type, objectType) ? SUCCESS : ERROR;
+    returnValue = AddMacroFast(path, name, type, objectType, objectType2) ? SUCCESS : ERROR;
   }
 
   return returnValue;
 }
 
 //______________________________________________________
-Bool_t AliEveListAnalyser::AddMacroFast(const Char_t* path, const Char_t* name, AliEveListAnalyserMacroType type, TClass* objectType)
+Bool_t AliEveListAnalyser::AddMacroFast(const Char_t* path, const Char_t* name, AliEveListAnalyserMacroType type, 
+                                        TClass* objectType, TClass* objectType2)
 {
   // Adds a macro (path/name) to the corresponding list. No checks are performed (file exists, 
   // macro already in list/map, signature correct),  no libraries are created!
   // You can use this function only, if the macro has been added successfully before 
   // (and then maybe was removed). The function is very fast. On success kTRUE is returned, otherwise: kFALSE;
+  // Note: If your macro takes only 1 pointer as a parameter, just use "0x0" for objectType2!
 
   Bool_t success = kFALSE;
 
@@ -246,7 +274,7 @@ Bool_t AliEveListAnalyser::AddMacroFast(const Char_t* path, const Char_t* name,
     case kSingleObjectHisto:
     case kCorrelObjectAnalyse:
     case kCorrelObjectHisto:
-      fMacroList->Add(new TObjString(name), new TGeneralMacroData(name, path, type, objectType));
+      fMacroList->Add(new TObjString(name), new TGeneralMacroData(name, path, type, objectType, objectType2));
 
       // We do not know, where the element has been inserted - deselect this list
       fMacroListSelected = 0;
@@ -255,16 +283,15 @@ Bool_t AliEveListAnalyser::AddMacroFast(const Char_t* path, const Char_t* name,
 
 #ifdef AliEveListAnalyser_DEBUG
       // Successfull add will only be displayed in debug mode
-      printf("AliEveListAnalyser::AddMacroFast: Added macro \"%s/%s\" with object type \"%s\" to the corresponding list\n", path, name,
-             objectType->GetName());
+      printf("AliEveListAnalyser::AddMacroFast: Added macro \"%s/%s\" with object types \"%s\" and \"%s\" to the corresponding list\n", 
+             path, name, objectType->GetName(), objectType2->GetName());
 #endif
 
       break;
 
     default:
       // Error will always be displayed
-      printf("AliEveListAnalyser::AddMacroFast: ERROR: Could not add macro \"%s/%s\" with object type \"%s\" to the corresponding list\n", 
-             path, name, objectType->GetName());
+      printf("AliEveListAnalyser::AddMacroFast: ERROR: Could not add macro \"%s/%s\" with object types \"%s\" and \"%s\" to the corresponding list\n", path, name, objectType->GetName(), objectType2->GetName());
 
       success = kFALSE;
 
@@ -274,69 +301,190 @@ Bool_t AliEveListAnalyser::AddMacroFast(const Char_t* path, const Char_t* name,
   return success;
 }
 
-//TODO - NEW - To be implemented, tested, documented
+
 //______________________________________________________
-void AliEveListAnalyser::AddObjectToList(Int_t pointId)
+Int_t AliEveListAnalyser::AddPrimSelectedObject(TEveElement* el)
 {
-  TEvePointSet* ps = dynamic_cast<TEvePointSet*>((TQObject*) gTQSender);
+  // Adds the TEveElement el to the list. If it is already in the list, it is removed.
+  // If the list is the only parent of the clicked object, the object is moved outside the list in the browser (not deleted!).
+  // If you want to delete the object, just select it there and choose "Destroy" in the menu.
+  // This function is designed to be used together with a signal:
+  // It adds the (primarily) selected objects in the viewer to the list (objects that are already in the list are removed!).
+  // Returns "ERROR" (cf. defines) on error, "WARNING" if the element does not contain any user data and else "SUCCESS" (i.e.
+  // the element has been added successfully or the element is the list itself and therefore ignored, or the element is ignored 
+  // because it has been added via secondary selection).
+  if (!el)
+  {
+    Error("AliEveListAnalyser::AddPrimSelectedObject", "Zero pointer!\n");
+
+    return ERROR;
+  }
+
+  // If the clicked element is the list itself, just do nothing.
+  if (el == this)  return SUCCESS;
+
+  if (!this->HasChild(el))
+  {
+
+    // Ignore objects that do not have any user data, since these cannot be used in the analysis!
+    if (el->GetUserData() == 0x0)
+    {
+      Warning("AddPrimSelectedObject", "Selected object does not contain any \"user data\" and therefore is ignored!");
+
+      return WARNING;
+    }
+
+    // Element clicked that is not in the list (and is not the list itself!) -> Add this element to the list
+    this->AddElement(el);
+    this->SetTitle(Form("Objects %d", this->NumChildren()));
+    gEve->Redraw3D();
+  }
+  else
+  {
+    // Element clicked that is already in the list. Remove it. But: Only take care of objects that have been added
+    // via primary selection (name does not start with "[sec")
+    if (TString(el->GetElementName()).BeginsWith("[sec:"))  return SUCCESS;
+
+
+    // Element is a child of this list. So, if there are only 2 parents, we know them: list + eve selection. In this case,
+    // the element needs to be destroyed. If there are more parents, just remove the element from the list.
+    // Since the elements editor will be opened, the element is not deleted, but "moved" outside the list (in the browser).
+    if (el->NumParents() > 2) 
+    {
+      this->RemoveElement(el);
+    }
+    else
+    {
+      // There must be at least 2 parents!
+      if (el->NumParents() <= 1)  return ERROR;
+
+      TEveElement* listObj = 0x0;
+      listObj = this->FindChild(el->GetElementName());
+      if (!listObj)  return ERROR;
+
+      gEve->AddElement(listObj, 0);
+      // Alternatively: Switch on that the element is NOT destroyed, instead of adding it outside the list. Warning: Memory leaks possible.
+      //listObj->SetDestroyOnZeroRefCnt(kFALSE);
+      this->RemoveElement(listObj);
+      //gEve->RemoveElement(listObj, 0);
+    }
+  } 
+
+  this->SetTitle(Form("Objects %d", this->NumChildren()));
+  gEve->Redraw3D();
+
+  return SUCCESS;
+}
+
+
+/*
+//______________________________________________________
+void AliEveListAnalyser::AddPrimSelectedObjects()
+{
+  // Adds the (primarily) selected objects in the viewer to the list (objects that are already in the list are ignored).
+  // Hold the CTRL-key for multiple selection.
+
+  TEveSelection* eveSel = gEve->GetSelection();
+  if (!eveSel)
+  {
+    Error("AliEveListAnalyser::AddPrimSelectedObjects", "Failed to get the selection!\n");
+    return;
+  }
   
+  TEveElement* elem = 0x0;
+  Bool_t changedSomething = kFALSE;
+
+  for (TEveElement::List_i iter = eveSel->BeginChildren(); iter != eveSel->EndChildren(); ++iter)
+  {
+    if(!(elem = dynamic_cast<TEveElement*>(*iter))) continue;
+
+    if (!this->HasChild(elem) && elem != this)
+    {
+      // Element clicked that is not in the list (and is not the list itself!) -> Add this element to list
+      this->AddElement(elem);
+      this->SetTitle(Form("Objects %d", this->NumChildren()));
+      changedSomething = kTRUE;
+    }
+  }
+
+  if (changedSomething) gEve->Redraw3D();
+}
+*/
+
+//______________________________________________________
+void AliEveListAnalyser::AddSecSelectedSingleObjectToList(Int_t pointId)
+{
+  // This function adds the selected object (secondary selection in the viewer) to the list
+  // of analysis objects. If the object is already in the list, it will be removed from it.
+  // This function is used to add single objects of a TEvePointset, e.g. single clusters.
+
+  TEvePointSet* ps = dynamic_cast<TEvePointSet*>((TQObject*) gTQSender);
   if (!ps)
   {
-    Error("AliEveListAnalyser::AddObjectToList", "Zero pointer!\n");
+    Error("AliEveListAnalyser::AddSecSelectedSingleObjectToList", "Zero pointer!\n");
     return;
   }
 
   // Check, if object is already there. If so, remove it!
   
-  // 1st possibility: Object of the list clicked
+  // 1st possibility: Object of the list clicked. But: Only take care of objects that have been added
+  // via secondary selection (name starts with "[sec"). Note: HasChild will also return kTRUE, if e.g.
+  // the whole TEvePointSet of clusters is in the last (but maybe another point of it has been clicked!)
+
   if (this->HasChild(ps))
   {
-    this->RemoveElement(ps);
-    return;
+    if (TString(ps->GetName()).BeginsWith("[sec:"))
+    {
+      // I don't know why, but you get a crash, if you try this->RemoveElement(ps) (in some cases).
+      // So, use this way instead.
+      TEveElement* listObj = this->FindChild(ps->GetName());
+      if (listObj)
+      {
+        listObj->SetUserData(0x0);
+        this->RemoveElement(listObj);
+        this->SetTitle(Form("Objects %d", this->NumChildren()));
+      }
+
+      return;
+    }
   }
-    
+
   TObject* obj = ps->GetPointId(pointId);
   if (obj)
   {
     // 2nd possibility: Same object clicked again
     TEveElement* listObj = 0x0;
-    listObj = this->FindChild(Form("[viewer:%d] %s%d", obj->GetUniqueID(), obj->GetName(), pointId));
+    listObj = this->FindChild(Form("[sec:%d] %s%d", obj->GetUniqueID(), obj->GetName(), pointId));
     if (listObj)
     {
-      this->RemoveElement(listObj);  
+      listObj->SetUserData(0x0);
+      this->RemoveElement(listObj); 
+      this->SetTitle(Form("Objects %d", this->NumChildren()));
       return;
     }
 
     // Object clicked that is not in the list -> Add this object to list
-    TEvePointSet* newPS = new TEvePointSet(Form("[viewer:%d] %s%d", obj->GetUniqueID(), obj->GetName(), pointId));
+    TEvePointSet* newPS = new TEvePointSet(Form("[sec:%d] %s%d", obj->GetUniqueID(), obj->GetName(), pointId));
     Double_t x = 0, y = 0, z = 0;
     ps->GetPoint(pointId, x, y, z);
     newPS->SetPoint(0, x, y, z);
     newPS->SetUserData(obj);
+    // Choose yellow for the added points and inherit style and size for the marker
     newPS->SetMarkerColor(5);
-    newPS->SetMarkerStyle(2);
-    newPS->SetMarkerSize(2.0);
+    newPS->SetMarkerStyle(ps->GetMarkerStyle());
+    newPS->SetMarkerSize(ps->GetMarkerSize());
+    // Own points -> Will be cleared, if this object is removed
+    newPS->SetOwnIds(kTRUE);
 
-    AddElement(newPS);
+    this->AddElement(newPS);
+    this->SetTitle(Form("Objects %d", this->NumChildren()));
     gEve->Redraw3D();
   }
   else
   {
-    Error("AliEveListAnalyser::AddObjectToList", "Selected object is NULL and therefore ignored!");
+    Error("AliEveListAnalyser::AddSecSelectedSingleObjectToList", "Selected object is NULL and therefore ignored!");
   }
-
-/*
-  TGLSelectRecord rec = gEve->GetDefaultGLViewer()->GetSelRec();
-
-  printf("Objects (%d):\n", rec.GetN());
-  for (int i = 0; i < rec.GetN(); i++)
-  {
-    //printf("%s\n", ((TObject*)rec.GetItem(i))->IsA()->GetName());
-  }
-*/
-  
-  //printf("Type: %s\npointID: %s\n\n", ps->IsA()->GetName(), pointId);
-  //printf("Type objectsender: %s\nType sender: %s\n", ((TQObject*)gTQSender)->IsA()->GetName(), ((TQObjSender*)gTQSender)->IsA()->GetName());
 }
 
 //______________________________________________________
@@ -395,28 +543,32 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
 
   TGeneralMacroData* macro = 0;
 
-  Char_t** procCmds = 0;
+  TString* procCmds = 0;
   AliEveListAnalyserMacroType* mProcType = 0;
   if (procIterator->GetEntries() > 0) {
-    procCmds = new Char_t*[procIterator->GetEntries()];
+    procCmds = new TString[procIterator->GetEntries()];
     mProcType = new AliEveListAnalyserMacroType[procIterator->GetEntries()];
   }
   
   TClass** mProcObjectType = 0;
+  TClass** mProcObjectType2 = 0;
   if (procIterator->GetEntries() > 0) {
     mProcObjectType = new TClass*[procIterator->GetEntries()];
+    mProcObjectType2 = new TClass*[procIterator->GetEntries()];
   }
 
-  Char_t** selCmds  = 0;
+  TString* selCmds  = 0;
   AliEveListAnalyserMacroType* mSelType = 0;
   if (selIterator->GetEntries() > 0) {
-    selCmds = new Char_t*[selIterator->GetEntries()];
+    selCmds = new TString[selIterator->GetEntries()];
     mSelType = new AliEveListAnalyserMacroType[selIterator->GetEntries()];
   }
 
   TClass** mSelObjectType = 0;
+  TClass** mSelObjectType2 = 0;
   if (selIterator->GetEntries() > 0) {
     mSelObjectType = new TClass*[selIterator->GetEntries()];
+    mSelObjectType2 = new TClass*[selIterator->GetEntries()];
   }
   
   Bool_t selectedByCorrSelMacro = kFALSE;
@@ -427,12 +579,10 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
 
   TEveElement* object1 = 0;
   TEveElement* object2 = 0;
+  TH1* returnedHist = 0x0;
 
   // Collect the commands for each process macro and add them to "data-from-list"
   for (Int_t i = 0; i < procIterator->GetEntries(); i++){
-    procCmds[i] = new Char_t[(fkMaxMacroPathNameLength + fkMaxApplyCommandLength)];
-    memset(procCmds[i], '\0', sizeof(Char_t) * (fkMaxMacroNameLength + fkMaxApplyCommandLength));
-
     macro = (TGeneralMacroData*)fMacroList->GetValue(procIterator->At(i)->GetTitle());
 
     if (!macro){
@@ -446,8 +596,9 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
     printf("AliEveListAnalyser: Checking process macro: %s\n", macro->GetName());
 #endif 
            
-    // Find the object type of the macro
+    // Find the object types of the macro
     mProcObjectType[i] = macro->GetObjectType();
+    mProcObjectType2[i] = macro->GetObjectType2();
 
     // Find the type of the process macro
     macroType = macro->GetType();
@@ -455,14 +606,14 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
       mProcType[i] = macroType;
       numHistoMacros++;
       // Create the command 
-      sprintf(procCmds[i], macro->GetCmd());
+      procCmds[i] = macro->GetCmd();
 
       // Add to "data-from-list" -> Mark as a histo macro with the substring "(histo macro)"
       fDataFromMacroList->Add(new TObjString(Form("%s (histo macro)", macro->GetName())));
     } else if (macroType == kSingleObjectAnalyse || macroType == kCorrelObjectAnalyse) {
       mProcType[i] = macroType;
       // Create the command 
-      sprintf(procCmds[i], macro->GetCmd());
+      procCmds[i] = macro->GetCmd();
 
       // Add to "data-from-list"
       fDataFromMacroList->Add(new TObjString(macro->GetName()));
@@ -476,9 +627,6 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
 
   // Collect the commands for each selection macro and add them to "data-from-list"
   for (Int_t i = 0; i < selIterator->GetEntries(); i++){
-    selCmds[i] = new Char_t[(fkMaxMacroPathNameLength + fkMaxApplyCommandLength)];
-    memset(selCmds[i], '\0', sizeof(Char_t) * (fkMaxMacroNameLength + fkMaxApplyCommandLength));
-
     macro = (TGeneralMacroData*)fMacroList->GetValue(selIterator->At(i)->GetTitle());
 
     if (!macro){
@@ -492,15 +640,16 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
     printf("AliEveListAnalyser: Checking selection macro: %s\n", macro->GetName());
 #endif
 
-    // Find the object type of the macro
+    // Find the object types of the macro
     mSelObjectType[i] = macro->GetObjectType();
+    mSelObjectType2[i] = macro->GetObjectType2();
        
     // Find the type of the process macro
     macroType = macro->GetType();
 
     // Single Object select macro
     if (macroType == kSingleObjectSelect) {
-      // Has already been processed by ApplySTSelectionMacros(...)
+      // Has already been processed by ApplySOSelectionMacros(...)
       mSelType[i] = macroType;         
     }
     // Correlated Objects select macro
@@ -508,7 +657,7 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
       mSelType[i] = macroType;  
  
       // Create the command
-      sprintf(selCmds[i], macro->GetCmd());
+      selCmds[i] = macro->GetCmd();
     } else {
       Error("Apply process macros", 
         Form("Macro list corrupted: Macro \"%s/%s.C\" is not registered as a selection macro!", 
@@ -521,10 +670,12 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
   if (numHistoMacros > 0)  histos = new TH1*[numHistoMacros];
   for (Int_t i = 0; i < numHistoMacros; i++)  histos[i] = 0x0;
 
+  Bool_t secondBeforeFirstObject = kTRUE;
+  
 
-  //////////////////////////////////
-  // WALK THROUGH THE LIST OF OBJECTS
-  //////////////////////////////////     
+  //////////////////////////////////////
+  // WALK THROUGH THE LIST OF OBJECTS //
+  //////////////////////////////////////     
   for (TEveElement::List_i iter = this->BeginChildren(); iter != this->EndChildren(); ++iter){
     if(!(object1 = dynamic_cast<TEveElement*>(*iter))) continue;
 
@@ -532,7 +683,7 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
     if (!object1->GetRnrState())  continue;
     
     // Cast to the "real" object behind
-    gROOT->ProcessLineSync(Form("TEveElement *automaticEveElement = (TEveElement*)0x%xl;", object1));
+    gROOT->ProcessLineSync(Form("TEveElement *automaticEveElement = (TEveElement*)%p;", (void*)object1));
     gROOT->ProcessLineSync("TObject* automaticObject_1 = (TObject*)automaticEveElement->GetUserData();");
 
     // Collect data for each macro
@@ -550,29 +701,55 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
         
       // Single object histo
       if (mProcType[i] == kSingleObjectHisto){
-        histos[histoIndex++] = (TH1*)gROOT->ProcessLineSync(procCmds[i]);
+        returnedHist = (TH1*)gROOT->ProcessLineSync(procCmds[i]);
+        if (returnedHist)
+        {
+          if (!histos[histoIndex])  histos[histoIndex] = returnedHist;
+          else  
+          {
+            histos[histoIndex]->Add((const TH1*)returnedHist);
+            delete returnedHist;
+            returnedHist = 0;
+          }
+        }
+        histoIndex++;
        // Correlated Objects histo
       } else if (mProcType[i] == kCorrelObjectHisto) {
-        // Loop over all pairs behind the current one - together with the other loop this will be a loop
-        // over all pairs. We have a pair of objects, if and only if both objects of the pair are selected (Rnr-state)
+        // To get all pairs, do the second loop over all objects.
+        // But: If a macro takes 2 pointers of the same type, we must take care that one gets the same pair, when we exchange the objects
+        // (this is not true, if we have different types - even if they inherit from the same classes!).
+        // Thus: If the latter case occurs, we ignore an object pair, if the second object is BEFORE the first object in the list.
+        // Since then the pair has already been taken into account.
+        // Furthermore, we have a pair of objects, if and only if both objects of the pair are selected (Rnr-state)
         // and are not equal.
         // The correlated objects process macro will be applied to all pairs that will be additionally selected by
         // all correlated objects selection macros.
-        TEveElement::List_i iter2 = iter;
-        iter2++;
-        for ( ; iter2 != this->EndChildren(); ++iter2)
+
+        secondBeforeFirstObject = kTRUE;
+        for (TEveElement::List_i iter2 = this->BeginChildren(); iter2 != this->EndChildren(); ++iter2)
         {
+          // If the objects are the same, it is not a pair -> continue. From now on: 2nd object BEHIND the 1st object in the list!
+          if (iter == iter2)
+          {
+            secondBeforeFirstObject = kFALSE;
+            continue;
+          }
           if(!(object2 = dynamic_cast<TEveElement*>(*iter2))) continue;
 
           // Skip objects that have not been selected
           if (!object2->GetRnrState())  continue;
 
           // Same check of the macro object type as before
-          if (((TObject*)object2->GetUserData())->IsA() != mProcObjectType[i] && 
-              !((TObject*)object2->GetUserData())->InheritsFrom(mProcObjectType[i]))  continue;
+          if (((TObject*)object2->GetUserData())->IsA() != mProcObjectType2[i] && 
+              !((TObject*)object2->GetUserData())->InheritsFrom(mProcObjectType2[i]))  continue;
+          // Do not process object pairs twice
+          if (secondBeforeFirstObject)
+          {
+            if (mProcObjectType[i] == mProcObjectType2[i]) continue;
+          }
       
           // Cast to the "real" object behind
-          gROOT->ProcessLineSync(Form("TEveElement *automaticEveElement = (TEveElement*)0x%xl;", object2));
+          gROOT->ProcessLineSync(Form("TEveElement *automaticEveElement = (TEveElement*)%p;", (void*)object2));
           gROOT->ProcessLineSync("TObject* automaticObject_2 = (TObject*)automaticEveElement->GetUserData();");
 
           // Select object by default (so it will be processed, if there are no correlated objects selection macros!)
@@ -583,8 +760,8 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
           // Note: Again, via selCmds[i], the automatic objects are casted to the correct type!
           if (((TObject*)object1->GetUserData())->IsA() != mSelObjectType[j] && 
               !((TObject*)object1->GetUserData())->InheritsFrom(mSelObjectType[j]))  continue;
-          if (((TObject*)object2->GetUserData())->IsA() != mSelObjectType[j] && 
-              !((TObject*)object2->GetUserData())->InheritsFrom(mSelObjectType[j]))  continue;
+          if (((TObject*)object2->GetUserData())->IsA() != mSelObjectType2[j] && 
+              !((TObject*)object2->GetUserData())->InheritsFrom(mSelObjectType2[j]))  continue;
 
               selectedByCorrSelMacro = (Bool_t)gROOT->ProcessLineSync(selCmds[j]);
               if (!selectedByCorrSelMacro)  break;
@@ -593,8 +770,19 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
 
           // If the pair has not been selected by the correlated objects selection macros, skip it!
           if (!selectedByCorrSelMacro) continue;
-          
-          histos[histoIndex] = (TH1*)gROOT->ProcessLineSync(procCmds[i]);
+
+          returnedHist = (TH1*)gROOT->ProcessLineSync(procCmds[i]);
+          if (returnedHist)
+          {
+            if (!histos[histoIndex])  histos[histoIndex] = returnedHist;
+            else  
+            {
+              histos[histoIndex]->Add((const TH1*)returnedHist);
+
+              delete returnedHist;
+              returnedHist = 0;
+            }
+          }
         }
         histoIndex++;
       }
@@ -620,26 +808,41 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
       }
       // Correlated objects analyse
       else if (mProcType[i] == kCorrelObjectAnalyse){
-        // Loop over all pairs behind the current one - together with the other loop this will be a loop
-        // over all pairs. We have a pair of objects, if and only if both objects of the pair are selected (Rnr-state)
+        // To get all pairs, do the second loop over all objects.
+        // But: If a macro takes 2 pointers of the same type, we must take care that one gets the same pair, when we exchange the objects
+        // (this is not true, if we have different types - even if they inherit from the same classes!).
+        // Thus: If the latter case occurs, we ignore an object pair, if the second object is BEFORE the first object in the list.
+        // Since then the pair has already been taken into account.
+        // Furthermore, we have a pair of objects, if and only if both objects of the pair are selected (Rnr-state)
         // and are not equal.
         // The correlated objects process macro will be applied to all pairs that will be additionally selected by
         // all correlated objects selection macros.
-        TEveElement::List_i iter2 = iter;
-        iter2++;
 
-        for ( ; iter2 != this->EndChildren(); ++iter2) {
+        secondBeforeFirstObject = kTRUE;
+        for (TEveElement::List_i iter2 = this->BeginChildren(); iter2 != this->EndChildren(); ++iter2)
+        {
+          // If the objects are the same, it is not a pair -> continue. From now on: 2nd object BEHIND the 1st object in the list!
+          if (iter == iter2)
+          {
+            secondBeforeFirstObject = kFALSE;
+            continue;
+          }
           if(!(object2 = dynamic_cast<TEveElement*>(*iter2))) continue;
  
           // Skip objects that have not been selected
           if (!object2->GetRnrState())  continue;
 
           // Same check of the macro object type as before
-          if (((TObject*)object2->GetUserData())->IsA() != mProcObjectType[i] && 
-              !((TObject*)object2->GetUserData())->InheritsFrom(mProcObjectType[i]))  continue;
+          if (((TObject*)object2->GetUserData())->IsA() != mProcObjectType2[i] && 
+              !((TObject*)object2->GetUserData())->InheritsFrom(mProcObjectType2[i]))  continue;
+          // Do not process object pairs twice
+          if (secondBeforeFirstObject)
+          {
+            if (mProcObjectType[i] == mProcObjectType2[i]) continue;
+          }
     
           // Cast to the "real" object behind
-          gROOT->ProcessLineSync(Form("TEveElement *automaticEveElement = (TEveElement*)0x%xl;", object2));
+          gROOT->ProcessLineSync(Form("TEveElement *automaticEveElement = (TEveElement*)%p;", (void*)object2));
           gROOT->ProcessLineSync("TObject* automaticObject_2 = (TObject*)automaticEveElement->GetUserData();");
 
           // Select object by default (so it will be processed, if there are no correlated objects selection macros!)
@@ -647,11 +850,11 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
           for (Int_t j = 0; j < selIterator->GetEntries(); j++) {
             if (mSelType[j] == kCorrelObjectSelect) {
               // Check, whether the macro can deal with both objects. If not, skip it.
-              // Note: Again, via selCmds[i], the automatic objects are casted to the correct type!
+              // Note: Again, via selCmds[i], the automatic objects are casted to the correct type! 
               if (((TObject*)object1->GetUserData())->IsA() != mSelObjectType[j] && 
                   !((TObject*)object1->GetUserData())->InheritsFrom(mSelObjectType[j]))  continue;
-              if (((TObject*)object2->GetUserData())->IsA() != mSelObjectType[j] && 
-                  !((TObject*)object2->GetUserData())->InheritsFrom(mSelObjectType[j]))  continue;
+              if (((TObject*)object2->GetUserData())->IsA() != mSelObjectType2[j] && 
+                  !((TObject*)object2->GetUserData())->InheritsFrom(mSelObjectType2[j]))  continue;
 
               selectedByCorrSelMacro = (Bool_t)gROOT->ProcessLineSync(selCmds[j]);
               if (!selectedByCorrSelMacro)  break;
@@ -698,16 +901,20 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
 
   if (procCmds != 0)  delete [] procCmds;
   procCmds = 0;
-  if (mProcObjectType != 0) delete mProcObjectType;
+  if (mProcObjectType != 0) delete [] mProcObjectType;
   mProcObjectType = 0;
-  if (mProcType != 0)  delete mProcType;
+  if (mProcObjectType2 != 0) delete [] mProcObjectType2;
+  mProcObjectType2 = 0;
+  if (mProcType != 0)  delete [] mProcType;
   mProcType = 0;
 
   if (selCmds != 0)  delete [] selCmds;
   selCmds = 0;
-  if (mSelObjectType != 0)  delete mSelObjectType;
+  if (mSelObjectType != 0)  delete [] mSelObjectType;
   mSelObjectType = 0;
-  if (mSelType != 0)  delete mSelType;
+  if (mSelObjectType2 != 0)  delete [] mSelObjectType2;
+  mSelObjectType2 = 0;
+  if (mSelType != 0)  delete [] mSelType;
   mSelType = 0;
 
   if (histos != 0)  delete [] histos;
@@ -725,12 +932,12 @@ Bool_t AliEveListAnalyser::ApplyProcessMacros(const TList* selIterator, const TL
 }
 
 //______________________________________________________
-void AliEveListAnalyser::ApplySTSelectionMacros(const TList* iterator)
+void AliEveListAnalyser::ApplySOSelectionMacros(const TList* iterator)
 {
   // Uses the iterator (for the selected selection macros) to apply the selected macros to the data.
   // The rnr-states of the objects are set according to the result of the macro calls (kTRUE, if all
   // macros return kTRUE for this object, otherwise: kFALSE).
-  // "ST" stands for "single object". This means that only single object selection macros are applied.
+  // "SO" stands for "single object". This means that only single object selection macros are applied.
   // Correlated objects selection macros will be used inside the call of ApplyProcessMacros(...)!
 
   TGeneralMacroData* macro = 0;
@@ -784,7 +991,7 @@ void AliEveListAnalyser::ApplySTSelectionMacros(const TList* iterator)
             !((TObject*)object1->GetUserData())->InheritsFrom(macro->GetObjectType()))  continue;
 
         // Cast to the "real" object behind
-        gROOT->ProcessLineSync(Form("TEveElement *automaticEveElement = (TEveElement*)0x%xl;", object1));
+        gROOT->ProcessLineSync(Form("TEveElement *automaticEveElement = (TEveElement*)%p;", (void*)object1));
         gROOT->ProcessLineSync("TObject* automaticObject_1 = (TObject*)automaticEveElement->GetUserData();");
 
         // GetCmd() will cast the automatic objects to the correct type for each macro!
@@ -808,9 +1015,42 @@ void AliEveListAnalyser::ApplySTSelectionMacros(const TList* iterator)
 }
 
 //______________________________________________________
-AliEveListAnalyser::AliEveListAnalyserMacroType AliEveListAnalyser::GetMacroType(const Char_t* name, const Char_t* objectType, Bool_t UseList) const
+TClass* AliEveListAnalyser::GetMacroObjectType(const Char_t* name, Int_t argNum) const
+{
+  // Returns the type of object (of argument argNum) the macro with name "name" is dealing with; 
+  // e.g. if you have the signature:
+  // void MyMacro(const AliTRDtrackV1* track, Double_t* &results, Int_t& nResults)
+  // the call 'GetMacroObjectType("MyMacro")' yields the AliTRDtrackV1-class.
+  // If the macro is not found (or there is an error), 0x0 is returned.
+
+  if (argNum - 1 < 0) return 0x0;
+
+  TFunction* f = gROOT->GetGlobalFunction(name, 0 , kTRUE);
+  TMethodArg* m = 0;
+  TList* list = 0;
+
+  if (f)
+  {
+    list = f->GetListOfMethodArgs();
+    
+    if (!list->IsEmpty())
+    {
+      m = (TMethodArg*)list->At(argNum - 1);
+
+      if (m)  return TClass::GetClass(m->GetTypeName());
+    }
+  }  
+
+  // Error
+  return 0x0;
+}
+
+//______________________________________________________
+AliEveListAnalyser::AliEveListAnalyserMacroType AliEveListAnalyser::GetMacroType(const Char_t* name, const Char_t* objectType, 
+                                                                                 const Char_t* objectType2, Bool_t UseList) const
 {
-  // Returns the type of the corresponding macro, that accepts pointers of the class "objectType" as a parametre. 
+  // Returns the type of the corresponding macro, that accepts pointers of the classes "objectType" (first pointer) and
+  // objectType2" (second pointer) as parametres. 
   // If "UseList" is kTRUE, the type will be looked up in the internal list (very fast). But if this list
   // does not exist, you have to use kFALSE for this parameter. Then the type will be determined by the
   // prototype! NOTE: It is assumed that the macro has been compiled! If not, the return value is not
@@ -820,6 +1060,7 @@ AliEveListAnalyser::AliEveListAnalyserMacroType AliEveListAnalyser::GetMacroType
   AliEveListAnalyserMacroType type = kUnknown;
 
   TString* typeStr = 0;
+  TString* typeStr2 = 0;
   
   if (objectType != 0) 
   {
@@ -831,6 +1072,16 @@ AliEveListAnalyser::AliEveListAnalyserMacroType AliEveListAnalyser::GetMacroType
   {
     typeStr = new TString("TObject");
   }
+  if (objectType2 != 0) 
+  {
+    typeStr2 = new TString(objectType2);
+    // Remove white-spaces
+    typeStr2->ReplaceAll(" ", "");
+  }
+  else
+  {
+    typeStr2 = new TString("TObject");
+  }
 
   TString* mangled1Str = new TString();
   TString* mangled2Str = new TString();
@@ -845,17 +1096,17 @@ AliEveListAnalyser::AliEveListAnalyserMacroType AliEveListAnalyser::GetMacroType
   // We want "const 'OBJECTTYPE'*, Double_t*&, Int_t&"
   mangled2Str->Append("const ").Append(*typeStr).Append("*, Double_t*&, Int_t&");
 
-  // We want "const 'OBJECTTYPE'*, const 'OBJECTTYPE'*"
-  mangled3Str->Append("const ").Append(*typeStr).Append("*, const ").Append(*typeStr).Append("*");
+  // We want "const 'OBJECTTYPE'*, const 'OBJECTTYPE2'*"
+  mangled3Str->Append("const ").Append(*typeStr).Append("*, const ").Append(*typeStr2).Append("*");
 
-  // We want "const 'OBJECTTYPE'*, const 'OBJECTTYPE'*, Double_t*&, Int_t&"
-  mangled4Str->Append("const ").Append(*typeStr).Append("*, const ").Append(*typeStr).Append("*, Double_t*&, Int_t&");
+  // We want "const 'OBJECTTYPE'*, const 'OBJECTTYPE2'*, Double_t*&, Int_t&"
+  mangled4Str->Append("const ").Append(*typeStr).Append("*, const ").Append(*typeStr2).Append("*, Double_t*&, Int_t&");
 
   // We want "oPconstsP'OBJECTTYPE'mUsP"
   mangledArg1Str->Append("oPconstsP").Append(*typeStr).Append("mUsP");
 
-  // We want "cOconstsP'OBJECTTYPE'mUsP"
-  mangledArg2Str->Append("cOconstsP").Append(*typeStr).Append("mUsP");  
+  // We want "cOconstsP'OBJECTTYPE2'mUsP"
+  mangledArg2Str->Append("cOconstsP").Append(*typeStr2).Append("mUsP");  
   
   // Re-do the check of the macro type
   if (!UseList){
@@ -997,34 +1248,52 @@ AliEveListAnalyser::AliEveListAnalyserMacroType AliEveListAnalyser::GetMacroType
     delete typeStr;
     typeStr = 0;
   }
+  if (typeStr2 != 0)
+  {
+    typeStr2->Clear();
+    delete typeStr2;
+    typeStr2 = 0;
+  }
 
 
   return type;
 }
 
-// TODO: DOCUMENTATION
+
+/*
 //______________________________________________________
-TClass* AliEveListAnalyser::GetMacroObjectType(const Char_t* name) const
+void AliEveListAnalyser::RemovePrimSelectedObjects()
 {
-  TFunction* f = gROOT->GetGlobalFunction(name, 0 , kTRUE);
-  TMethodArg* m = 0;
-  TList* list = 0;
+  // Removes the (primarily) selected objects in the viewer from the list (objects that are already in the list are ignored).
+  // Hold the CTRL-key for multiple selection.
 
-  if (f)
+  TEveSelection* eveSel = gEve->GetSelection();
+
+  if (!eveSel)
   {
-    list = f->GetListOfMethodArgs();
-    
-    if (!list->IsEmpty())
-    {
-      m = (TMethodArg*)list->At(0);
+    Error("AliEveListAnalyser::RemovePrimSelectedObjects", "Failed to get the selection!\n");
+    return;
+  }
+  
+  TEveElement* elem = 0x0;
+  Bool_t changedSomething = kFALSE;
 
-      if (m)  return TClass::GetClass(m->GetTypeName());
+  for (TEveElement::List_i iter = eveSel->BeginChildren(); iter != eveSel->EndChildren(); ++iter)
+  {
+    if(!(elem = dynamic_cast<TEveElement*>(*iter))) continue;
+
+    // Check, if element is already there. If so, remove it!
+    if (this->HasChild(elem) && elem != this)
+    {
+      this->RemoveElement(elem);
+      this->SetTitle(Form("Objects %d", this->NumChildren()));
+      changedSomething = kTRUE;
     }
-  }  
+  }
 
-  // Error
-  return 0x0;
+  if (changedSomething) gEve->Redraw3D();
 }
+*/
 
 //______________________________________________________
 void AliEveListAnalyser::RemoveSelectedMacros(const TList* iterator) 
@@ -1073,23 +1342,28 @@ void AliEveListAnalyser::RemoveSelectedMacros(const TList* iterator)
 //______________________________________________________
 void AliEveListAnalyser::ResetObjectList()
 {
-  // Remove all objects from the list.
+  // Removes all objects from the list.
 
   RemoveElements();
+  this->SetTitle(Form("Objects %d", this->NumChildren()));
 }
 
 //______________________________________________________
 Bool_t AliEveListAnalyser::StartAddingObjects()
 { 
-  // Start adding objects for the analysis. Returns kTRUE on success.
+  // Starts adding objects for the analysis. Returns kTRUE on success.
 
   if (fConnected == kFALSE)
   {
-    fConnected = TQObject::Connect("TEvePointSet", "PointSelected(Int_t)", "AliEveListAnalyser", this, "AddObjectToList(Int_t)");
-    //fConnected = TQObject::Connect("TEvePointSet", "Message(char*)", "AliEveListAnalyser", this, "AddObjectToList(char*)");
+    fConnected = TQObject::Connect("TEvePointSet", "PointSelected(Int_t)", "AliEveListAnalyser", this, "AddSecSelectedSingleObjectToList(Int_t)");
+    if (fConnected)  fConnected = TQObject::Connect(gEve->GetSelection(), "SelectionAdded(TEveElement*)", "AliEveListAnalyser", this, "AddPrimSelectedObject(TEveElement*)");
+
     if (fConnected) return kTRUE;
     
     Error("AliEveListAnalyser::StartAddingObjects", "Connection failed!");
+    
+    // Connection of 2nd signal failed, but first connection succeeded -> Disconnect 1st signal.
+    TQObject::Disconnect("TEvePointSet", "PointSelected(Int_t)", this, "AddObjectToList(Int_t)");
   }
 
   return kFALSE;
@@ -1098,19 +1372,20 @@ Bool_t AliEveListAnalyser::StartAddingObjects()
 //______________________________________________________
 Bool_t AliEveListAnalyser::StopAddingObjects()
 {
-  // Stop adding objects for the analysis. Returns kTRUE on success.
+  // Stops adding objects for the analysis. Returns kTRUE on success.
 
   if (fConnected)
   {
-    //if (TQObject::Disconnect("TEvePointSet", "AddObjectToList(Char_t*)"))  fConnected = kFALSE;
-    if (TQObject::Disconnect("TEvePointSet", "PointSelected(Int_t)", this, "AddObjectToList(Int_t)"))
-    { 
-      fConnected = kFALSE;
-      return kTRUE;
-    }
+    Bool_t dis1 = kFALSE, dis2 = kFALSE;
+    dis1 = TQObject::Disconnect("TEvePointSet", "PointSelected(Int_t)", this, "AddSecSelectedSingleObjectToList(Int_t)");
+    dis2 = TQObject::Disconnect(gEve->GetSelection(), "SelectionAdded(TEveElement*)", this, "AddPrimSelectedObject(TEveElement*)");
+
+    if (dis1 || dis2) fConnected = kFALSE;
+    if (dis1 && dis2) return kTRUE;
     else
     {
       Error("AliEveListAnalyser::StopAddingObjects", "Disconnection failed!");
+
       return kFALSE;
     }
   }