// @(#)root/base:$Name:  $:$Id: TQObject.cxx,v 1.39 2004/06/22 08:39:45 rdm Exp $
// Author: Valeriy Onuchin & Fons Rademakers   15/10/2000

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// This is the ROOT implementation of the Qt object communication       //
// mechanism (see also http://www.troll.no/qt/metaobjects.html)         //
//                                                                      //
// Signals and slots are used for communication between objects.        //
// When an object has changed in some way that might be interesting     //
// for the outside world, it emits a signal to tell whoever is          //
// listening. All slots that are connected to this signal will be       //
// activated (called). It is even possible to connect a signal          //
// directly to another signal (this will emit the second signal         //
// immediately whenever the first is emitted.) There is no limitation   //
// on the number of slots that can be connected to a signal.            //
// The slots will be activated in the order they were connected         //
// to the signal. This mechanism allows objects to be easily reused,    //
// because the object that emits a signal does not need to know         //
// to which objects the signals are connected.                          //
// Together, signals and slots make up a powerfull component            //
// programming mechanism.                                               //
//                                                                      //
// This implementation is provided by                                   //
// Valeriy Onuchin (onuchin@sirius.ihep.su).                            //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//***************************** Signals *****************************
//___________________________________________________________________
//
//             Destroyed()
//
// Signal emitted when object is destroyed.
// This signal could be connected to some garbage-collector object.
//
//___________________________________________________________________
//
//             ChangedBy(const char *method_name)
//
// This signal is emitted when some important data members of
// the object were changed. method_name parameter can be used
// as an identifier of the modifier method.
//
//___________________________________________________________________
//
//             Message(const char *msg)
//
// General purpose message signal
//
/////////////////////////////////////////////////////////////////////

#include "TQObject.h"
#include "TQConnection.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TMethod.h"
#include "TBaseClass.h"
#include "TDataType.h"
#include "TInterpreter.h"
#include "TClass.h"
#include "TError.h"
#include "G__ci.h"
#include "Api.h"
#include "Riostream.h"
#include "RQ_OBJECT.h"
#include "TVirtualMutex.h"

#ifdef HAVE_CONFIG
#include "config.h"
#endif


void *gTQSender; // A pointer to the object that sent the last signal.
                 // Getting access to the sender might be practical
                 // when many signals are connected to a single slot.


ClassImpQ(TQObject)
ClassImpQ(TQObjSender)
ClassImpQ(TQClass)

/////////////////// internal use static functions ///////////////////
//______________________________________________________________________________
static char *ResolveTypes(const char *method)
{
   // Resolve any typedefs in the method signature. For example:
   // func(Float_t,Int_t) becomes func(float,int).
   // The returned string must be deleted by the user.

   if (!method || !*method) return 0;

   char *str = new char[strlen(method)+1];
   if (str) strcpy(str, method);

   TString res;

   char *s = strtok(str, "(");
   res = s;
   res += "(";

   Bool_t first = kTRUE;
   while ((s = strtok(0, ",)"))) {
      char *s1, s2 = 0;
      if ((s1 = strchr(s, '*'))) {
         *s1 = 0;
         s2  = '*';
      }
      if (!s1 && (s1 = strchr(s, '&'))) {
         *s1 = 0;
         s2  = '&';
      }
      TDataType *dt = gROOT->GetType(s);
      if (s1) *s1 = s2;
      if (!first) res += ",";
      if (dt) {
         res += dt->GetFullTypeName();
         if (s1) res += s1;
      } else
         res += s;
      first = kFALSE;
   }

   res += ")";

   delete [] str;
   str = new char[res.Length()+1];
   strcpy(str, res.Data());

   return str;
}

//______________________________________________________________________________
static char *CompressName(const char *method_name)
{
   // Removes "const" words and blanks from full (with prototype)
   // method name.
   //
   //  Example: CompressName(" Draw(const char *, const char *,
   //                               Option_t * , Int_t , Int_t)")
   //
   // Returns the string "Draw(char*,char*,char*,int,int)"
   // The returned string must be deleted by the user.

   if (!method_name || !*method_name) return 0;

   char *str = new char[strlen(method_name)+1];
   if (str) strcpy(str, method_name);

   char *tmp = str;

   // substitute "const" with white spaces
   while ((tmp = strstr(tmp,"const"))) {
      for (int i = 0; i < 5; i++) *(tmp+i) = ' ';
   }

   tmp = str;
   char *s;
   s = str;

   Bool_t quote = kFALSE;
   while (*tmp) {
      if (*tmp == '\"')
         quote = quote ? kFALSE : kTRUE;
      if (*tmp != ' ' || quote)
         *s++ = *tmp;
      tmp++;
   }
   *s = '\0';

   s = ResolveTypes(str);

   delete [] str;

   return s;
}

//______________________________________________________________________________
static TMethod *GetMethodWithPrototype(TClass *cl, const char *method,
                                       const char *proto, Int_t &nargs)
{
   // Almost the same as TClass::GetMethodWithPrototype().

   nargs = 0;

   if (!gInterpreter) return 0;
   R__LOCKGUARD(gCINTMutex);

   Long_t faddr = 0;
   if (!cl->IsLoaded()) {
      // interpreted class
      G__MethodInfo meth;
      long offset;
      if (cl->GetClassInfo())
         meth = cl->GetClassInfo()->GetMethod(method, proto, &offset);
      if (meth.IsValid()) {
         nargs = meth.NArg();
         return (TMethod *) -1;
      }
      return 0;
   } else {
      faddr = (Long_t)gInterpreter->GetInterfaceMethodWithPrototype(cl, method,
                                                                    proto);
      if (!faddr) return 0;
   }

   TMethod *m;
   TIter next_method(cl->GetListOfMethods());

   // Look for a method in this class
   while ((m = (TMethod *) next_method())) {
      if (faddr == (Long_t)m->InterfaceMethod()) {
         nargs = m->GetNargs();
         return m;
      }
   }

   TIter next_base(cl->GetListOfBases());
   TBaseClass *base;

   // loop over all base classes
   while ((base = (TBaseClass *)next_base())) {
      TClass *c;
      if ((c = base->GetClassPointer())) {
         if ((m = GetMethodWithPrototype(c, method, proto, nargs))) return m;
      }
   }
   return 0;
}

//______________________________________________________________________________
static TMethod *GetMethod(TClass *cl, const char *method, const char *params)
{
   // Almost the same as TClass::GetMethod().

   if (!gInterpreter) return 0;
   R__LOCKGUARD(gCINTMutex);

   Long_t faddr = 0;
   if (!cl->IsLoaded()) {
      // interpreted class
      G__CallFunc  func;
      long         offset;
      func.SetFunc(cl->GetClassInfo(), method, params, &offset);
      if (func.IsValid())
         return (TMethod *) -1;
      return 0;
   } else {
      faddr = (Long_t)gInterpreter->GetInterfaceMethod(cl, method, params);
      if (!faddr) return 0;
   }

   TMethod *m;
   TIter next_method(cl->GetListOfMethods());

   // Look for a method in this class
   while ((m = (TMethod *) next_method())) {
      if (faddr == (Long_t)m->InterfaceMethod()) return m;
   }

   TIter next_base(cl->GetListOfBases());
   TBaseClass *base;

   // loop over all base classes
   while ((base = (TBaseClass *)next_base())) {
      TClass *c;
      if ((c = base->GetClassPointer())) {
         if ((m = GetMethod(c,method,params))) return m;
      }
   }
   return 0;
}

//______________________________________________________________________________
Int_t TQObject::CheckConnectArgs(TQObject *sender,
                                 TClass *sender_class, const char *signal,
                                 TClass *receiver_class, const char *slot)
{
   // Checking of consitency of sender/receiver methods/arguments.
   // Returns -1 on error, otherwise number or arguments of signal function.
   // Static method.

   char *signal_method = new char[strlen(signal)+1];
   if (signal_method) strcpy(signal_method, signal);

   char *signal_proto;
   char *tmp;

   if ((signal_proto = strchr(signal_method,'('))) {
      // substitute first '(' symbol with '\0'
      *signal_proto++ = '\0';
      // substitute last ')' symbol with '\0'
      if ((tmp = strrchr(signal_proto,')'))) *tmp = '\0';
   }

   if (!signal_proto) signal_proto = (char*)""; // avoid zero strings

   // if delegation object TQObjSender is used get the real sender class
   if (sender && sender_class == TQObjSender::Class()) {
      sender_class = gROOT->GetClass(sender->GetSenderClassName());
      if (!sender_class) {
         ::Error("TQObject::CheckConnectArgs", "for signal/slot consistency\n"
                 "checking need to specify class name as argument to "
                 "RQ_OBJECT macro");
         delete [] signal_method;
         return -1;
      }
   }

   Int_t nargs;
   TMethod *signalMethod = GetMethodWithPrototype(sender_class,
                                                  signal_method,
                                                  signal_proto,
                                                  nargs);
   if (!signalMethod) {
      ::Error("TQObject::CheckConnectArgs",  "signal %s::%s(%s) does not exist",
              sender_class->GetName(), signal_method, signal_proto);
      delete [] signal_method;
      return -1;
   }
   Int_t nsigargs = nargs;

#if defined(CHECK_COMMENT_STRING)
   const char *comment = 0;
   if (signalMethod != (TMethod *) -1)   // -1 in case of interpreted class
      comment = signalMethod->GetCommentString();

   if (!comment || !strlen(comment) || strstr(comment,"*SIGNAL")){
      ::Error("TQObject::CheckConnectArgs",
              "signal %s::%s(%s), to declare signal use comment //*SIGNAL*",
              sender_class->GetName(), signal_method, signal_proto);
      delete [] signal_method;
      return -1;
   }
#endif

   // cleaning
   delete [] signal_method;

   char *slot_method = new char[strlen(slot)+1];
   if (slot_method) strcpy(slot_method, slot);

   char *slot_proto;
   char *slot_params = 0;

   if ((slot_proto = strchr(slot_method,'('))) {

      // substitute first '(' symbol with '\0'
      *slot_proto++ = '\0';

      // substitute last ')' symbol with '\0'
      if ((tmp = strrchr(slot_proto,')'))) *tmp = '\0';
   }

   if (!slot_proto) slot_proto = (char*)"";     // avoid zero strings
   if ((slot_params = strchr(slot_proto,'='))) *slot_params = ' ';

   TFunction *slotMethod = 0;
   if (!receiver_class) {
      // case of slot_method is compiled/intrepreted function
      slotMethod = (TFunction*)gROOT->GetListOfGlobalFunctions(kTRUE)->
                                           FindObject(slot_method);
   } else {
      slotMethod  = !slot_params ?
                          GetMethodWithPrototype(receiver_class,
                                                 slot_method,
                                                 slot_proto,
                                                 nargs) :
                          GetMethod(receiver_class,
                                    slot_method, slot_params);
   }

   if (!slotMethod) {
      if (!slot_params) {
         ::Error("TQObject::CheckConnectArgs", "slot %s(%s) does not exist",
                 receiver_class ? Form("%s::%s", receiver_class->GetName(),
                 slot_method) : slot_method, slot_proto);
      } else {
         ::Error("TQObject::CheckConnectArgs", "slot %s(%s) does not exist",
                 receiver_class ? Form("%s::%s", receiver_class->GetName(),
                 slot_method) : slot_method, slot_params);
      }
      delete [] slot_method;
      return -1;
   }

#if defined(CHECK_ARGS_NUMBER)
   if (slotMethod != (TMethod *) -1 && slotMethod->GetNargsOpt() >= 0 &&
       nsigargs < (slotMethod->GetNargs() - slotMethod->GetNargsOpt())) {
      ::Error("TQObject::CheckConnectArgs",
              "inconsistency in numbers of arguments");
      delete [] slot_method;
      return -1;
   }
#endif

   // cleaning
   delete [] slot_method;

   return nsigargs;
}

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//    TQConnectionList is the named list of connections,                      //
//    see also TQConnection class.                                            //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
class TQConnectionList : public TList {

private:
   Int_t   fSignalArgs;    // number of arguments in signal function

public:
   TQConnectionList(const char *name, Int_t nsigargs) : TList()
      { fName = name; fSignalArgs = nsigargs; }
   virtual ~TQConnectionList();

   Bool_t Disconnect(void *receiver=0, const char *slot_name=0);
   Int_t  GetNargs() const { return fSignalArgs; }
   void   ls(Option_t *option = "") const;
   void   Print(Option_t *option = "") const;
};

//______________________________________________________________________________
TQConnectionList::~TQConnectionList()
{
   // Destructor.

   TIter next(this);
   TQConnection *connection;

   while ((connection = (TQConnection*)next())) {
      // remove this from feed back reference list
      connection->Remove(this);
      if (connection->IsEmpty()) delete connection;
   }
   Clear("nodelete");
}

//______________________________________________________________________________
Bool_t TQConnectionList::Disconnect(void *receiver, const char *slot_name)
{
   // Remove connection from the list. For more info see
   // TQObject::Disconnect()

   TQConnection *connection = 0;
   Bool_t return_value = kFALSE;

   TObjLink *lnk = FirstLink();
   TObjLink *savlnk; // savlnk is used when link is deleted

   while (lnk) {
      connection = (TQConnection*)lnk->GetObject();
      const char *name = connection->GetName();
      void *obj = connection->GetReceiver();

      if (!slot_name || !strlen(slot_name)
                     || !strcmp(name,slot_name)) {

         if (!receiver || (receiver == obj)) {
            return_value = kTRUE;
            savlnk = lnk->Next();   // keep next link ..
            Remove(lnk);
            lnk = savlnk;           // current link == saved ...
            connection->Remove(this);      // remove back reference
            if (connection->IsEmpty()) SafeDelete(connection);
            continue;               // .. continue from saved link
         }
      }
      lnk = lnk->Next();
   }
   return return_value;
}

//______________________________________________________________________________
void TQConnectionList::ls(Option_t *option) const
{
   // List signal name and list all connections in this signal list.

   cout <<  "TQConnectionList:" << "\t" << GetName() << endl;
   ((TQConnectionList*)this)->ForEach(TQConnection,Print)(option);
}

//______________________________________________________________________________
void TQConnectionList::Print(Option_t *) const
{
   // Print signal name.

   cout << "TQConnectionList:" << "\t" << GetName() << endl;
}


//______________________________________________________________________________
TQObject::TQObject()
{
   // TQObject Constructor.
   // Comment:
   //  - In order to minimize memory allocation fListOfSignals and
   //    fListOfConnections are allocated only if it is neccesary
   //  - When fListOfSignals/fListOfConnections are empty they will
   //    be deleted

   fListOfSignals     = 0;
   fListOfConnections = 0;
}

//______________________________________________________________________________
TQObject::~TQObject()
{
   // TQObject Destructor.
   //    - delete all connections and signal list

   if (!gROOT) return;

   Destroyed();   // emit "Destroyed()" signal

   if (fListOfSignals) {
      fListOfSignals->Delete();
      SafeDelete(fListOfSignals);   // delete list of signals
   }

   // loop over all connections and remove references to this object
   if (fListOfConnections) {
      TIter next_connection(fListOfConnections);
      TQConnection *connection;

      while ((connection = (TQConnection*)next_connection())) {
         TIter next_list(connection);
         TQConnectionList *list;
         while ((list = (TQConnectionList*)next_list())) {
            list->Remove(connection);
            if (list->IsEmpty()) SafeDelete(list);
         }
      }
      SafeDelete(fListOfConnections);
   }
}

//______________________________________________________________________________
TList *TQObject::GetListOfClassSignals() const
{
   // Returns pointer to list of signals of this class.

   TQClass *qcl = 0;

   qcl = dynamic_cast<TQClass*>(IsA());

   return qcl ? qcl->fListOfSignals : 0; //!!
}

//______________________________________________________________________________
void TQObject::HighPriority(const char *signal_name, const char *slot_name)
{
   // 1. If slot_name = 0 => makes signal defined by the signal_name
   //    to be the first in the fListOfSignals, this decreases
   //    the time for lookup.
   // 2. If slot_name != 0 => makes slot defined by the slot_name
   //    to be executed first when signal_name is emitted.

   TQConnectionList *clist = 0;
   TIter    next_list(fListOfSignals);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal_name,clist->GetName()))
         break;
   }

   if (!clist)  return;      // not found
   if (!slot_name)  {        // update list of signal lists
      fListOfSignals->Remove(clist);   // remove and add first
      fListOfSignals->AddFirst(clist);
      return;
   } else {                   // slot_name != 0 , update signal list
      TQConnection *con = 0;
      TIter next_con(clist);
      while ((con = (TQConnection*)next_con())) {
         if (!strcmp(slot_name,con->GetName()))
            break;
      }

      if (!con) return;       // not found
      clist->Remove(con);     // remove and add as first
      clist->AddFirst(con);
   }
}

//______________________________________________________________________________
void TQObject::LowPriority(const char *signal_name, const char *slot_name)
{
   // 1. If slot_name = 0 => makes signal defined by the signal_name
   //    to be the last in the fListOfSignals, this increase the time
   //    for lookup.
   // 2. If slot_name != 0 => makes slot defined by the slot_name
   //    to  be executed last when signal_name is emitted.

   TQConnectionList *clist = 0;
   TIter    next_list(fListOfSignals);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal_name,clist->GetName()))
         break;
   }

   if (!clist)   return;
   if (!slot_name)  {
      fListOfSignals->Remove(clist);   // remove and add first
      fListOfSignals->AddLast(clist);
      return;
   } else  {                  // slot_name != 0 , update signal list
      TQConnection *con = 0;
      TIter next_con(clist);
      while ((con = (TQConnection*)next_con())) {
         if (!strcmp(slot_name,con->GetName()))  break;
      }

      if (!con) return;
      clist->Remove(con);     // remove and add as last
      clist->AddLast(con);
   }
}

//______________________________________________________________________________
Bool_t TQObject::HasConnection(const char *signal_name) const
{
   // Return true if there is any object connected to this signal.
   // Only checks for object signals.

   if (!fListOfSignals)
      return kFALSE;

   register TQConnectionList *clist  = 0;
   char *signal = CompressName(signal_name);

   // check object signals
   TIter next_list(fListOfSignals);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal, clist->GetName())) {
         delete [] signal;
         return kTRUE;
      }
   }
   delete [] signal;
   return kFALSE;
}

//______________________________________________________________________________
Int_t TQObject::NumberOfSignals() const
{
   // Return number of signals for this object.

   if (fListOfSignals)
      return fListOfSignals->GetSize();
   return 0;
}

//______________________________________________________________________________
Int_t TQObject::NumberOfConnections() const
{
   // Return number of connections for this object.

   if (fListOfConnections)
      return fListOfConnections->GetSize();
   return 0;
}

//______________________________________________________________________________
void TQObject::Emit(const char *signal_name)
{
   // Acitvate signal without args
   // Example:
   //          theButton->Emit("Clicked()");

   TList *slist = GetListOfClassSignals();

   if (!slist && !fListOfSignals)
      return;

   register TQConnectionList *clist  = 0;
   register TQConnection *connection = 0;

   char *signal = CompressName(signal_name);

   // execute class signals
   if (slist) {
      TIter nextcl_list(slist);
      while ((clist = (TQConnectionList*)nextcl_list())) {
         if (!strcmp(signal,clist->GetName())) break;
      }

      if (clist) {
         TIter nextcl(clist);
         while ((connection = (TQConnection*)nextcl())) {
            gTQSender = GetSender();
            connection->ExecuteMethod();
         }
      }
   }
   if (!fListOfSignals) {
      delete [] signal;
      return;
   }

   // execute object signals
   TIter next_list(fListOfSignals);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal,clist->GetName())) break;
   }
   if (!clist) {
      delete [] signal;
      return;
   }

   TIter next(clist);
   while (fListOfSignals && (connection = (TQConnection*)next())) {
      gTQSender = GetSender();
      connection->ExecuteMethod();
   }
   delete [] signal;
}

//______________________________________________________________________________
void TQObject::Emit(const char *signal_name, Long_t param)
{
   // Activate signal with single parameter
   // Example:
   //          theButton->Emit("Clicked(int)",id)

   TList *slist = GetListOfClassSignals();

   if (!slist && !fListOfSignals)
      return;

   register TQConnectionList *clist  = 0;
   register TQConnection *connection = 0;

   char *signal = CompressName(signal_name);

   // execute class signals
   if (slist) {
      TIter nextcl_list(slist);
      while ((clist = (TQConnectionList*)nextcl_list())) {
         if (!strcmp(signal,clist->GetName())) break;
      }

      if (clist) {
         TIter nextcl(clist);
         while ((connection = (TQConnection*)nextcl())) {
            gTQSender = GetSender();
            connection->ExecuteMethod(param);
         }
      }
   }
   if (!fListOfSignals) {
      delete [] signal;
      return;
   }

   // execute object signals
   TIter next_list(fListOfSignals);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal,clist->GetName())) break;
   }
   if (!clist) {
      delete [] signal;
      return;
   }

   TIter next(clist);
   while (fListOfSignals && (connection = (TQConnection*)next())) {
      gTQSender = GetSender();
      connection->ExecuteMethod(param);
   }
   delete [] signal;
}

//______________________________________________________________________________
void TQObject::Emit(const char *signal_name, Double_t param)
{
   // Activate signal with single parameter.

   TList *slist = GetListOfClassSignals();

   if (!slist && !fListOfSignals)
      return;

   register TQConnectionList *clist  = 0;
   register TQConnection *connection = 0;

   char *signal = CompressName(signal_name);

   // execute class signals
   if (slist) {
      TIter nextcl_list(slist);
      while ((clist = (TQConnectionList*)nextcl_list())) {
         if (!strcmp(signal,clist->GetName())) break;
      }

      if (clist) {
         TIter nextcl(clist);
         while ((connection = (TQConnection*)nextcl())) {
            gTQSender = GetSender();
            connection->ExecuteMethod(param);
         }
      }
   }
   if (!fListOfSignals) {
      delete [] signal;
      return;
   }

   // execute object signals
   TIter next_list(fListOfSignals);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal,clist->GetName())) break;
   }

   if (!clist) {
      delete [] signal;
      return;
   }

   TIter next(clist);
   while (fListOfSignals && (connection = (TQConnection*)next())) {
      gTQSender = GetSender();
      connection->ExecuteMethod(param);
   }
   delete [] signal;
}

//______________________________________________________________________________
void TQObject::Emit(const char *signal_name, const char *params)
{
   // Activate signal with parameter text string.
   // Example:
   //          myObject->Emit("Error(char*)","Fatal error");

   TList *slist = GetListOfClassSignals();

   if (!slist && !fListOfSignals)
      return;

   register TQConnectionList *clist  = 0;
   register TQConnection *connection = 0;

   char *signal = CompressName(signal_name);

   // execute class signals
   if (slist) {
      TIter nextcl_list(slist);
      while ((clist = (TQConnectionList*)nextcl_list())) {
         if (!strcmp(signal,clist->GetName())) break;
      }

      if (clist) {
         TIter nextcl(clist);
         while ((connection = (TQConnection*)nextcl())) {
            gTQSender = GetSender();
            connection->ExecuteMethod(params);
         }
      }
   }
   if (!fListOfSignals) {
      delete [] signal;
      return;
   }

   // execute object signals
   TIter next_list(fListOfSignals);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal,clist->GetName())) break;
   }
   if (!clist) {
      delete [] signal;
      return;
   }

   TIter next(clist);
   while (fListOfSignals && (connection = (TQConnection*)next())) {
      gTQSender = GetSender();
      connection->ExecuteMethod(params);
   }
   delete [] signal;
}

//______________________________________________________________________________
void TQObject::Emit(const char *signal_name, Long_t *paramArr)
{
   // Emit a signal with a varying number of arguments,
   // paramArr is an array of the parameters.
   // Note: any parameter should be converted to long type.
   // Example:
   //    TQObject *processor; // data processor
   //    TH1F     *hist;      // filled with processor results
   //
   //    processor->Connect("Evaluated(Float_t,Float_t)",
   //                       "TH1F",hist,"Fill12(Axis_t,Axis_t)");
   //
   //    Long_t args[2];
   //    args[0] = (Long_t)processor->GetValue(1);
   //    args[1] = (Long_t)processor->GetValue(2);
   //
   //    processor->Emit("Evaluated(Float_t,Float_t)",args);

   TList *slist = GetListOfClassSignals();

   if (!slist && !fListOfSignals)
      return;

   register TQConnectionList *clist  = 0;
   register TQConnection *connection = 0;

   char *signal = CompressName(signal_name);

   // execute class signals
   if (slist) {
      TIter nextcl_list(slist);
      while ((clist = (TQConnectionList*)nextcl_list())) {
         if (!strcmp(signal,clist->GetName())) break;
      }

      if (clist) {
         TIter nextcl(clist);
         while ((connection = (TQConnection*)nextcl())) {
            gTQSender = GetSender();
            connection->ExecuteMethod(paramArr, clist->GetNargs());
         }
      }
   }
   if (!fListOfSignals) {
      delete [] signal;
      return;
   }

   // execute object signals
   TIter next_list(fListOfSignals);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal,clist->GetName())) break;
   }
   if (!clist) {
      delete [] signal;
      return;
   }

   TIter next(clist);
   while (fListOfSignals && (connection = (TQConnection*)next())) {
      gTQSender = GetSender();
      connection->ExecuteMethod(paramArr, clist->GetNargs());
   }
   delete [] signal;
}

//______________________________________________________________________________
void  TQObject::Emit(const char *signal, Long64_t param)
{
   // Emit a signal with a Long64_t argument.

   Long_t args[1];
   args[0] = (Long_t) (&param);

   Emit(signal, args);
}

//______________________________________________________________________________
void  TQObject::Emit(const char *signal, ULong64_t param)
{
   // Emit a signal with a ULong64_t argument.

   Long_t args[1];
   args[0] = (Long_t) (&param);

   Emit(signal, args);
}

//______________________________________________________________________________
Bool_t TQObject::ConnectToClass(TQObject *sender,
                                const char *signal,
                                TClass *cl,
                                void *receiver,
                                const char *slot)
{
   // Create connection between sender and receiver.
   // Receiver class needs to have a dictionary.

   // sender should be TQObject
   if (!sender->IsA()->InheritsFrom(TQObject::Class()))
      return kFALSE;

   // remove "const" and strip blanks
   char *signal_name = CompressName(signal);
   char *slot_name   = CompressName(slot);

   // check consitency of signal/slot methods/args
   Int_t nsigargs;
   if ((nsigargs = CheckConnectArgs(sender, sender->IsA(), signal_name, cl, slot_name)) == -1)
      return kFALSE;

   if (!sender->fListOfSignals)
      sender->fListOfSignals = new TList();

   TQConnectionList *clist=0;
   TIter next_list(sender->fListOfSignals);

   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal_name,clist->GetName())) break;
   }

   if (!clist) {
      clist = new TQConnectionList(signal_name, nsigargs);
      sender->fListOfSignals->Add(clist);
   }

   TIter next(clist);
   TQConnection *connection = 0;

   while ((connection = (TQConnection*)next())) {
      if (!strcmp(slot_name,connection->GetName()) &&
          (receiver == connection->GetReceiver())) break;
   }

   if (!connection)
      connection = new TQConnection(cl, receiver, slot_name);

   // check to prevent multiple entries
   if (!clist->FindObject(connection)) {
      clist->Add(connection);
      if (!connection->FindObject(clist)) connection->Add(clist);
      sender->Connected(signal_name);
   }

   // cleaning
   if (signal_name) { delete [] signal_name; signal_name = 0; }
   if (slot_name) { delete [] slot_name; slot_name = 0; }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TQObject::ConnectToClass(const char *class_name,
                                const char *signal,
                                TClass *cl,
                                void *receiver,
                                const char *slot)
{
   // This method allows to make connection from any object
   // of the same class to the receiver object.
   // Receiver class needs to have a dictionary.

   TClass *sender = gROOT->GetClass(class_name);

   // sender class should be TQObject (i.e. TQClass)
   if (!sender || !sender->IsA()->InheritsFrom(TQObject::Class()))
      return kFALSE;

   TList *slist = ((TQClass*)sender)->fListOfSignals;
   char *signal_name = CompressName(signal);
   char *slot_name   = CompressName(slot);

   // check consitency of signal/slot methods/args
   Int_t nsigargs;
   if ((nsigargs = CheckConnectArgs(0, sender, signal_name, cl, slot_name)) == -1)
      return kFALSE;

   TQConnectionList *clist = 0;

   if (!slist)
      ((TQClass*)sender)->fListOfSignals = slist = new TList();

   TIter next_list(slist);
   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal_name,clist->GetName())) break;
   }

   if (!clist) {
      clist = new TQConnectionList(signal_name, nsigargs);
      slist->Add(clist);
   }

   TQConnection *connection = 0;
   TIter next(clist);

   while ((connection = (TQConnection*)next())) {
      if (!strcmp(slot_name,connection->GetName()) &&
          (receiver == connection->GetReceiver())) break;
   }

   if (!connection)
      connection = new TQConnection(cl, receiver, slot_name);

   // check to prevent multiple entries
   if (!clist->FindObject(connection)) {
      clist->Add(connection);
      if (!connection->FindObject(clist)) connection->Add(clist);
      ((TQClass*)sender)->Connected(signal_name);
   }

   // cleaning
   if (signal_name)  { delete [] signal_name; signal_name = 0; }
   if (slot_name) { delete [] slot_name; slot_name = 0; }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TQObject::Connect(TQObject *sender,
                         const char *signal,
                         const char *cl,
                         void *receiver,
                         const char *slot)
{
   // Create connection between sender and receiver.
   // Signal and slot string must have a form:
   //    "Draw(char*, Option_t* ,Int_t)"
   // All blanks and "const" words will be removed,
   //
   // cl != 0 - class name, it can be class with or
   //           without dictionary, e.g interpreted class.
   // Example:
   //       TGButton *myButton;
   //       TH2F     *myHist;
   //
   //       TQObject::Connect(myButton,"Clicked()",
   //                         "TH2F", myHist,"Draw(Option_t*)");
   //
   // cl == 0 - corresponds to function (interpereted or global)
   //           the name of the function is defined by the slot string,
   //           parameter receiver should be 0.
   // Example:
   //       TGButton *myButton;
   //       TH2F     *myHist;
   //
   //       TQObject::Connect(myButton,"Clicked()",
   //                         0, 0,"hsimple()");
   //
   // Warning:
   //  If receiver is class not derived from TQObject and going to be
   //  deleted, disconnect all connections to this receiver.
   //  In case of class derived from TQObject it is done automatically.

   if (cl) {
      TClass *rcv_cl = gROOT->GetClass(cl);
      if (rcv_cl) return ConnectToClass(sender, signal, rcv_cl, receiver, slot);
   }

   // the following is the case of receiver class without dictionary
   // e.g. interpreted class or function.

   // sender should be TQObject
   if (!sender->IsA()->InheritsFrom(TQObject::Class()))
      return kFALSE;

   // remove "const" and strip blanks
   char *signal_name = CompressName(signal);
   char *slot_name   = CompressName(slot);

   // check consitency of signal/slot methods/args
   Int_t nsigargs;
   if ((nsigargs = CheckConnectArgs(sender, sender->IsA(), signal_name, 0, slot_name)) == -1)
      return kFALSE;

   if (!sender->fListOfSignals) sender->fListOfSignals = new TList();

   TQConnectionList *clist = 0;
   TIter next_list(sender->fListOfSignals);

   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal_name,clist->GetName())) break;
   }

   if (!clist) {
      clist = new TQConnectionList(signal_name, nsigargs);
      sender->fListOfSignals->Add(clist);
   }

   TQConnection *connection = 0;
   TIter next(clist);

   while ((connection = (TQConnection*)next())) {
      if (!strcmp(slot_name,connection->GetName()) &&
          (receiver == connection->GetReceiver())) break;
   }

   if (!connection)
      connection = new TQConnection(cl, receiver, slot_name);

   // check to prevent multiple entries
   if (!clist->FindObject(connection)) {
      clist->Add(connection);
      if (!connection->FindObject(clist)) connection->Add(clist);
      sender->Connected(signal_name);
   }

   // cleaning
   if (signal_name) { delete [] signal_name; signal_name = 0; }
   if (slot_name) { delete [] slot_name; slot_name = 0; }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TQObject::Connect(const char *class_name,
                         const char *signal,
                         const char *cl,
                         void *receiver,
                         const char *slot)
{
   // This method allows to make a connection from any object
   // of the same class to a single slot.
   // Signal and slot string must have a form:
   //    "Draw(char*, Option_t* ,Int_t)"
   // All blanks and "const" words will be removed,
   //
   // cl != 0 - class name, it can be class with or
   //           without dictionary, e.g interpreted class.
   // Example:
   //       TGButton *myButton;
   //       TH2F     *myHist;
   //
   //       TQObject::Connect("TGButton", "Clicked()",
   //                         "TH2F", myHist, "Draw(Option_t*)");
   //
   // cl == 0 - corresponds to function (interpereted or global)
   //           the name of the function is defined by the slot string,
   //           parameter receiver should be 0.
   // Example:
   //       TGButton *myButton;
   //       TH2F     *myHist;
   //
   //       TQObject::Connect("TGButton", "Clicked()",
   //                         0, 0, "hsimple()");
   //
   // Warning:
   //  If receiver class not derived from TQObject and going to be
   //  deleted, disconnect all connections to this receiver.
   //  In case of class derived from TQObject it is done automatically.

   if (cl) {
      TClass *rcv_cl = gROOT->GetClass(cl);
      if (rcv_cl) return ConnectToClass(class_name, signal, rcv_cl, receiver,
                                        slot);
   }

   // the following is case of receiver class without dictionary
   // e.g. interpreted class or function.

   TClass *sender = gROOT->GetClass(class_name);

   // sender class should be TQObject (i.e. TQClass)
   if (!sender || !sender->IsA()->InheritsFrom(TQObject::Class()))
      return kFALSE;

   TList *slist = ((TQClass*)sender)->fListOfSignals;

   char *signal_name = CompressName(signal);
   char *slot_name   = CompressName(slot);

   // check consitency of signal/slot methods/args
   Int_t nsigargs;
   if ((nsigargs = CheckConnectArgs(0, sender, signal_name, 0, slot_name)) == -1)
      return kFALSE;

   TQConnectionList *clist = 0;

   if (!slist) {
      slist = ((TQClass*)sender)->fListOfSignals = new TList();
   }

   TIter next_list(slist);

   while ((clist = (TQConnectionList*)next_list()))  {
      if (!strcmp(signal_name,clist->GetName())) break;
   }

   if (!clist) {
      clist = new TQConnectionList(signal_name, nsigargs);
      slist->Add(clist);
   }

   TQConnection *connection = 0;
   TIter next(clist);

   while ((connection = (TQConnection*)next())) {
      if (!strcmp(slot_name,connection->GetName()) &&
          (receiver == connection->GetReceiver())) break;
   }

   if (!connection)
      connection = new TQConnection(cl, receiver, slot_name);

   // check to prevent multiple entries
   if (!clist->FindObject(connection)) {
      clist->Add(connection);
      if (!connection->FindObject(clist)) connection->Add(clist);
      ((TQClass*)sender)->Connected(signal_name);
   }

   // cleaning
   if (signal_name)  { delete [] signal_name; signal_name = 0; }
   if (slot_name) { delete [] slot_name; slot_name = 0; }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TQObject::Connect(const char *signal,
                         const char *receiver_class,
                         void *receiver,
                         const char *slot)
{
   // Non-static method is used to connect from the signal
   // of this object to the receiver slot.
   //
   // Warning! No check on consistency of sender/receiver
   // classes/methods.
   //
   // This method makes possible to have connection/signals from
   // interpreted class. See also RQ_OBJECT.h.

   // remove "const" and strip blanks
   char *signal_name = CompressName(signal);
   char *slot_name   = CompressName(slot);

   // check consitency of signal/slot methods/args
   TClass *cl = 0;
   if (receiver_class)
      cl = gROOT->GetClass(receiver_class);
   Int_t nsigargs;
   if ((nsigargs = CheckConnectArgs(this, IsA(), signal_name, cl, slot_name)) == -1)
      return kFALSE;

   if (!fListOfSignals) fListOfSignals = new TList();

   TQConnectionList *clist = 0;
   TIter next_list(fListOfSignals);

   while ((clist = (TQConnectionList*)next_list())) {
      if (!strcmp(signal_name,clist->GetName())) break;
   }

   if (!clist) {
      clist = new TQConnectionList(signal_name, nsigargs);
      fListOfSignals->Add(clist);
   }

   TIter next(clist);
   TQConnection *connection = 0;

   while ((connection = (TQConnection*)next())) {
      if (!strcmp(slot_name,connection->GetName()) &&
          (receiver == connection->GetReceiver())) break;
   }

   if (!connection)
      connection = new TQConnection(receiver_class, receiver, slot_name);

   // check to prevent multiple entries
   if (!clist->FindObject(connection)) {
      clist->Add(connection);
      if (!connection->FindObject(clist)) connection->Add(clist);
      Connected(signal_name);
   }

   // cleaning
   if (signal_name) { delete [] signal_name; signal_name = 0; }
   if (slot_name) { delete [] slot_name; slot_name = 0; }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TQObject::Disconnect(TQObject *sender,
                            const char *signal,
                            void *receiver,
                            const char *slot)
{
   // Disconnects signal in object sender from slot_method in
   // object receiver. For objects derived from TQObject signal-slot
   // connection is removed when either of the objects involved
   // are destroyed.
   //
   // Disconnect() is typically used in three ways, as the following
   // examples shows:
   //
   //  - Disconnect everything connected to an object's signals:
   //       Disconnect(myObject);
   //  - Disconnect everything connected to a signal:
   //       Disconnect(myObject, "mySignal()");
   //  - Disconnect a specific receiver:
   //       Disconnect(myObject, 0, myReceiver, 0);
   //
   // 0 may be used as a wildcard in three of the four arguments,
   // meaning "any signal", "any receiving object" or
   // "any slot in the receiving object", respectively.
   //
   // The sender has no default and may never be 0
   // (you cannot disconnect signals from more than one object).
   //
   // If signal is 0, it disconnects receiver and slot_method
   // from any signal. If not, only the specified signal is
   // disconnected.
   //
   // If  receiver is 0, it disconnects anything connected to signal.
   // If not, slots in objects other than receiver are not
   // disconnected
   //
   // If slot_method is 0, it disconnects anything that is connected
   // to receiver.  If not, only slots named slot_method will be
   // disconnected, and all other slots are left alone.
   // The slot_method must be 0 if receiver is left out, so you
   // cannot disconnect a specifically-named slot on all objects.

   Bool_t return_value = kFALSE;
   Bool_t next_return  = kFALSE;

   if (!sender->GetListOfSignals()) return kFALSE;

   char *signal_name = CompressName(signal);
   char *slot_name   = CompressName(slot);

   TQConnectionList *slist = 0;
   TIter next_signal(sender->GetListOfSignals());

   while ((slist = (TQConnectionList*)next_signal()))   {
      if (!signal_name) {                // disconnect all signals
         next_return = slist->Disconnect(receiver,slot_name);
         return_value = return_value || next_return;

         if (slist->IsEmpty()) {
            sender->GetListOfSignals()->Remove(slist);
            SafeDelete(slist);            // delete empty list
         }
      } else if (signal && !strcmp(signal_name,slist->GetName())) {
         next_return = slist->Disconnect(receiver,slot_name);
         return_value = return_value || next_return;

         if (slist->IsEmpty()) {
            sender->GetListOfSignals()->Remove(slist);
            SafeDelete(slist);            // delete empty list
            break;
         }
      }
   }

   if (sender->GetListOfSignals() && sender->GetListOfSignals()->IsEmpty()) {
      SafeDelete(sender->fListOfSignals);
   }

   // cleaning
   if (signal_name) { delete [] signal_name; signal_name = 0; }
   if (slot_name) { delete [] slot_name; slot_name = 0; }

   return return_value;
}

//______________________________________________________________________________
Bool_t TQObject::Disconnect(const char *class_name,
                            const char *signal,
                            void *receiver,
                            const char *slot)
{
   // Disconnects "class signal". The class is defined by class_name.
   // See also Connect(class_name,signal,receiver,slot).

   TClass *sender = gROOT->GetClass(class_name);

   // sender should be TQClass (which derives from TQObject)
   if (!sender->IsA()->InheritsFrom(TQObject::Class()))
      return kFALSE;

   TQClass *qcl = (TQClass*)sender;   // cast TClass to TQClass
   return Disconnect(qcl, signal, receiver, slot);
}

//______________________________________________________________________________
Bool_t TQObject::Disconnect(const char *signal,
                            void *receiver,
                            const char *slot)
{
   // Disconnects signal of this object from slot of receiver.
   // Equivalent to Disconnect(this, signal, receiver, slot)

   return Disconnect(this, signal, receiver, slot);
}

//______________________________________________________________________________
void TQObject::Streamer(TBuffer &R__b)
{
   // Stream an object of class TQObject.

   if (R__b.IsReading()) {
      // nothing to read
   } else {
      // nothing to write
   }
}

//______________________________________________________________________________
void TQObject::LoadRQ_OBJECT()
{
   // Load RQ_OBJECT.h which contains the #define RQ_OBJECT needed to
   // let interpreted classes connect to signals of compiled classes.

#if !defined(R__ACC) && !defined(__DECCXX_VER)

   G__load_text(RQ_OBJECT_STRING);

#else

   // Work around for aCC Error 131: "base/src/TQObject.cxx", line 1467 # The
   // string literal created with the '#' operator is too long.
   // Idem for cxx on Alpha/TRUE64
   char rqh[128];
# ifdef ROOTINCDIR
   sprintf(rqh, "%s/RQ_OBJECT.h", ROOTINCDIR);
# else
   sprintf(rqh, "%s/include/RQ_OBJECT.h", gSystem->Getenv("ROOTSYS"));
# endif
   if (!gSystem->AccessPathName(rqh, kReadPermission))
      G__loadfile(rqh);
   else
      Warning("TQObject::LoadRQ_OBJECT", "%s not found", rqh);

#endif
}

// Global function which simplifies making connection in interpreted
// ROOT session
//
//  ConnectCINT      - connects to interpreter(CINT) command

//______________________________________________________________________________
Bool_t ConnectCINT(TQObject *sender, const char *signal, const char *slot)
{
   TString str = "ProcessLine(=";
   str += '"';
   str += slot;
   str += '"';
   str += ")";
   return TQObject::Connect(sender, signal, "TInterpreter",
                            gInterpreter, str.Data());
}
