/* $Id: gtk--sigcommon.h,v 1.23.2.2 1999/03/01 19:38:30 terop Exp $ */
#ifndef GTKMM_SIGCOMMON
#define GTKMM_SIGCOMMON

/* People can define GTKMM_SIGNALS_WITHOUT_GTK to use the signal system
   without gtk or other parts of gtk-- */

#ifndef GTKMM_SIGNALS_WITHOUT_GTK
#include <gtk--config.h>
#else
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#endif

#ifndef GTKMM_SIGNALS_WITHOUT_GTK
#include <gtk/gtksignal.h>
#include <gtk/gtkwidget.h>
#include <gtk/gtkobject.h>
#else
#define GtkObject int
#define gint int
#define gtk_signal_disconnect(a,b)
#define GtkWidget int
#endif

/* this nice hack is because proxies need to do something nasty
   when virtual function table has NULL value in it... (why does
   gtk+ need these kind of special meanings for the vtable entries?)

   This hack needs explanation. when GTK_RUN_LAST is used and virtual
   function table entry in gtk+ is 0, the proxy classes force gtk--
   run marshaller after connect stuffs => return value of the connected
   signals gets lost. This hack fixes it by storing the returned value
   to mem area pointed at gtkmm_rv and then it can be retrieved later
   in *_impl()s wrapper generated by gensig.cc. (this hack is hairy
   and odd..., but its a must to get the proxy return values work when
   needed -- only gints can be used in proxies as ret value.)
   This hack affects all *_callback methods called from proxy implementation.
   => SLOTs and concretegtkslots callback methods.
   (see gtk--sigcpp.h, gtk--sigcommon.h, base.gen_h, gensig.cc, gtksignal.c, 
   gtkmarshal.c to understand this hack)
*/
#ifndef GTKMM_SIGNALS_WITHOUT_GTK
#define GTKMM_PROXY_RV_HACK *(rettype*)gtkmm_rv =
extern "C" char *gtkmm_rv;
#else
#define GTKMM_PROXY_RV_HACK
#endif

//
// Connections are stored to two lists - one at the signal and one at
// the receiver.
//
// This object must know how to disconnect it from both lists!
// Thats why we have this alittle odd looking list implementations here.
// 

//// Fix for destruction of Signal before Connection.disconnect().
//// Need to ensure that if a Connection is used its Connection_impl*
//// is set to 0 otherwise we have a dangling pointer and a segfault
//// just waiting to happen.
//// forward declare Connection
class Connection;

// private class
class Connection_impl {
public:
    // virtual destructor is needed here or vc++ does not compile signals
    // correctly.
    virtual ~Connection_impl() { }
    // first list is stored at signals
    Connection_impl *next;
    Connection_impl *prev;
    // 2nd list is stored at Gtk_object
    Connection_impl *snext;
    Connection_impl *sprev;
    // remember Connection (if any) to avoid dangling pointers
    Connection *cnn;

     
#ifndef GTKMM_SIGNALS_WITHOUT_GTK
    gint connid;
    GtkObject *gtkobj;
    void setgtkconnection(GtkObject *gtko, gint cid) 
    { connid=cid; 
      if(gtkobj)
         gtk_object_unref(gtkobj);
      gtkobj=gtko; 
      if(gtkobj)
         gtk_object_ref(gtkobj);
    }
#endif
    
    Connection_impl() : next(0),prev(0),snext(0),sprev(0),cnn(0)

#ifndef GTKMM_SIGNALS_WITHOUT_GTK
      ,connid(0)
      ,gtkobj(0)
#endif
 {
    }

    void disconnect() {
	if (next) { 
	    next->prev=prev;
	    prev->next=next;
	    next=prev=0;
	}
	if (snext) {
	    snext->sprev=sprev;
	    sprev->snext=snext;
 	    snext=sprev=0;
	}
#ifndef GTKMM_SIGNALS_WITHOUT_GTK
	if (gtkobj) {
	    if(!GTK_OBJECT_DESTROYED (gtkobj))
		gtk_signal_disconnect(gtkobj,connid);
	    gtk_object_unref(gtkobj);
            gtkobj=0;
	}
#endif
    }
};

#ifdef GTKMM_CXX_HAVE_MUTABLE
#define GTKMM_CONST const
#define GTKMM_MUTABLE mutable
#else
#define GTKMM_CONST
#define GTKMM_MUTABLE
#endif

// This is an object given to user -- user can store it, mess with it
// and call disconnect() for it. -- or user can just dump this object
// and the disconnect is made automatically either at Gtk_Object's
// destructor or Signal's destructor. (actually G_List/G_SList) 

class ConnImpl_List;
class G_SList;

// private class
class Connection {
  friend class ConnImpl_List;
  friend class G_SList;
  GTKMM_MUTABLE Connection_impl *c;
public:
  // can assign this to another object, but then the original loses
  // its ability to disconnect the signal -- This is for making it
  // possible to pass Connection to the user.
  void bind()
    {
     if (c)
       {
        if (c->cnn)
          {c->cnn->c=0;}
        c->cnn=this;
       } 
    }
  void unbind()
    {
     if (c)
       {c->cnn=0;}
    }
  void operator=(const Connection &obj) 
    {
     c=obj.c; 
     bind();
    }
  Connection(const Connection &conn) 
    {
     c=conn.c; 
     bind();
    }
  Connection(Connection_impl *c_) : c(c_) 
    { 
     bind();
    }
  Connection() : c(0) { }
  ~Connection()
    {
     unbind();
    } 
  void disconnect() GTKMM_CONST 
    {
     if (c) 
       {c->disconnect(); 
        delete c; 
        c=0; 
       } 
    }

#ifndef GTKMM_SIGNALS_WITHOUT_GTK
  // access to low level gdk signal stuffs. Try to avoid use of this.
  gint GetSignalID() const { return c->connid; }
#endif

};

// This is a circular list for Connection_impl-items

// private class
class ConnImpl_List {
    Connection_impl enditem; // one extra element for marking start and end
public:
    Connection_impl * begin() { return enditem.next; }
    Connection_impl * end() { return &enditem; }
    void insert(Connection_impl *item) {
	item->next=enditem.next;
	item->prev=&enditem;
	enditem.next->prev=item;
	enditem.next=item;
    }
    // Connection_impl -object handles disconnection from the list.
    
    ConnImpl_List() {
	enditem.next=&enditem; enditem.prev=&enditem; 
    }
    ~ConnImpl_List() { 
	while(begin()!=end()) {
	    Connection_impl *c=begin();
            if (c->cnn) 
              c->cnn->c=0;
            c->disconnect(); // remove from the lists.
            delete c; // delete the object.
 	}
    }
};

// This is a circular list for Connection_impl-items -- uses snext/sprev

// private class
class G_SList {
  Connection_impl enditem; // one extra element for marking start and end
public:
  Connection_impl * begin() { return enditem.snext; }
  Connection_impl * end() { return &enditem; }
  void insert(Connection_impl *item) {
    item->snext=enditem.snext;
    item->sprev=&enditem;
    enditem.snext->sprev=item;
    enditem.snext=item;
  }
  // Connection_impl -object handles Connection from the list.
  
  G_SList() { 
    enditem.snext=&enditem; enditem.sprev=&enditem; }
  ~G_SList() { 
    while(begin()!=end()) {
      Connection_impl *c=begin();
      if (c->cnn) 
         c->cnn->c=0;
      c->disconnect(); // remove from the lists.
      delete c; // delete the object.
    }
  }
};


// This class is public and you can derive from it. Do not assume anything
// else than its name from this class.
class Gtk_Signal_Base {
    G_SList connectionlist; // support for signalN<> requires this.
public:
  void insert_connection(Connection_impl *item) 
  { connectionlist.insert(item); }

  virtual ~Gtk_Signal_Base() { }
};

typedef void *ignored; // To be used as 2nd template parameter in
 // *Connectors (e.g. MenuFactoryConnector and ToolbarConnector) and
 // to make things a bit more explicit


#endif








