Skip to content
Snippets Groups Projects
FXRuby.cpp 63.6 KiB
Newer Older
/***********************************************************************
 * FXRuby -- the Ruby language bindings for the FOX GUI toolkit.
 * Copyright (c) 2001-2003 by J. Lyle Johnson. All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * For further information please contact the author by e-mail
 * at "lyle@users.sourceforge.net".
 ***********************************************************************/

/***********************************************************************
 * $Id: FXRuby.cpp 2933 2008-12-29 20:19:33Z lyle $
 ***********************************************************************/

#ifdef _MSC_VER
#pragma warning (disable : 4786)
#endif

#include "FXRbCommon.h"

#include "version.h"

#if RUBY_VERSION_CODE < 167
#define RB_RESCUE2_BROKEN_PROTOTYPE 1
#endif

// The prototype for st_foreach() changed at Ruby version 1.8.2
#if RUBY_VERSION_CODE < 182
#define ST_BROKEN_PROTOTYPES 1
#endif

#include "impl.h"

#ifdef __CYGWIN__
#include <io.h>		// for get_osf_handle()
#endif

#ifdef HAVE_SIGNAL_H
#include <signal.h>	// for definitions of SIGINT, etc.
#endif

extern "C" {
#include "rubyio.h"	// for GetOpenFile(), etc.
}
#else
#include "ruby/io.h"
#endif

// Symbol table functions from Ruby. If we included "st.h" directly
// we'd be dealing with broken prototypes anyways, so just duplicate
// the needed declarations here with the correct prototypes.

#if defined(ST_BROKEN_PROTOTYPES)

extern "C" {

struct st_table;

typedef char * st_data_t; /* this type changed to unsigned long at Ruby 1.8.2 */

st_table *st_init_strtable();
st_table *st_init_numtable();
int st_lookup(st_table *table, st_data_t key, st_data_t *value);
int st_insert(st_table *table, st_data_t key, st_data_t value);
int st_delete(st_table *table, st_data_t *key, st_data_t *value);
void st_foreach(st_table *table, int (*func)(st_data_t, st_data_t, st_data_t), st_data_t arg);

}

#else

#ifdef RUBY_1_9

#include "ruby/st.h"

#else

extern "C" {
#include "st.h"
}

#endif /* RUBY_1_9 */

#endif /* ST_BROKEN_PROTOTYPES */

// Opaque type declaration from SWIG runtime
struct swig_type_info;

// Wrapper around SWIG_TypeQuery() that caches results for performance
swig_type_info *FXRbTypeQuery(const char *desc){
  FXASSERT(desc!=0);
  static st_table *types=st_init_strtable();
  swig_type_info *typeinfo=0;
  if(st_lookup(types,reinterpret_cast<st_data_t>(const_cast<char*>(desc)),reinterpret_cast<st_data_t *>(&typeinfo))==0){
    typeinfo=SWIG_Ruby_TypeQuery(desc);
    st_insert(types,reinterpret_cast<st_data_t>(strdup(desc)),reinterpret_cast<st_data_t>(typeinfo));
    }
  FXASSERT(typeinfo!=0);
  return typeinfo;
  }


/**
 * The FXRuby_Objects hash table basically maps C++ objects to Ruby instances.
 * Each key in the table is a pointer to a C++ object we've seen before, and
 * the corresponding value is an FXRubyObjDesc struct (see below) that tells
 * us which Ruby instance corresponds to that C++ object.
 */

static st_table * FXRuby_Objects;

/**
 * Each value in the FXRuby_Objects hash table is an instance of this
 * struct. It identifies the Ruby instance associated with a C++ object.
 * It also indicates whether this is merely a "borrowed" reference to
 * some C++ object (i.e. it's not one we need to destroy later).
 */

struct FXRubyObjDesc {
  VALUE obj;
  bool borrowed;
  };


/**
 * FXRbNewPointerObj() is a wrapper around SWIG_Ruby_NewPointerObj() that also
 * registers this C++ object & Ruby instance pair in our FXRuby_Objects
 * hash table. This function should only get called for methods that return
 * a reference to an already-existing C++ object (i.e. one that FOX "owns")
 * and for that reason we mark these objects as "borrowed".
 */

VALUE FXRbNewPointerObj(void *ptr,swig_type_info* ty){
  if(ptr!=0){
    FXASSERT(ty!=0);
    VALUE obj;
    FXRubyObjDesc *desc;
    if(FXMALLOC(&desc,FXRubyObjDesc,1)){
      obj=SWIG_Ruby_NewPointerObj(ptr,ty,1);
      desc->obj=obj;
      desc->borrowed=true;
      st_insert(FXRuby_Objects,reinterpret_cast<st_data_t>(ptr),reinterpret_cast<st_data_t>(desc));
      return obj;
      }
    else{
      FXASSERT(FALSE);
      return Qnil;
      }
    }
  else{
    return Qnil;
    }
  }


/**
 * FXRbIsBorrowed() returns true if the specified C++ object is one that
 * FOX owns (i.e. it's borrowed).
 */

bool FXRbIsBorrowed(void* ptr){
  FXASSERT(ptr!=0);
  FXRubyObjDesc *desc;
  if(st_lookup(FXRuby_Objects,reinterpret_cast<st_data_t>(ptr),reinterpret_cast<st_data_t *>(&desc))!=0){
    return desc->borrowed;
    }
  else{
    return true;
    }
  }


/**
 * FXRbConvertPtr() is just a wrapper around SWIG_Ruby_ConvertPtr().
 */

void* FXRbConvertPtr(VALUE obj,swig_type_info* ty){
  void *ptr;
  SWIG_Ruby_ConvertPtr(obj,&ptr,ty,1);
  return ptr;
  }


// Should we catch exceptions thrown by message handlers?
FXbool FXRbCatchExceptions=FALSE;

// Returns an FXInputHandle for this Ruby file object
FXInputHandle FXRbGetReadFileHandle(VALUE obj) {
#ifdef RUBY_1_9
  rb_io_t *fptr;
  GetOpenFile(obj, fptr);
  FILE *fpr=fptr->stdio_file;
#else
  OpenFile *fptr;
  GetOpenFile(obj, fptr);
  FILE *fpr=GetReadFile(fptr);
#ifdef WIN32
#ifdef __CYGWIN__
  return (FXInputHandle) get_osfhandle(fileno(fpr));
#else
  return (FXInputHandle) _get_osfhandle(_fileno(fpr));
#endif
#else
  return (FXInputHandle) fileno(fpr);
#endif
  }


// Returns an FXInputHandle for this Ruby file object
FXInputHandle FXRbGetWriteFileHandle(VALUE obj) {
#ifdef RUBY_1_9
  rb_io_t *fptr;
  GetOpenFile(obj, fptr);
  FILE *fpw=fptr->stdio_file;
#else
  OpenFile *fptr;
  GetOpenFile(obj, fptr);
  FILE *fpw=GetWriteFile(fptr);
#ifdef WIN32
#ifdef __CYGWIN__
  return (FXInputHandle) get_osfhandle(fileno(fpw));
#else
  return (FXInputHandle) _get_osfhandle(_fileno(fpw));
#endif
#else
  return (FXInputHandle) fileno(fpw);
#endif
  }


// Register this Ruby class instance
void FXRbRegisterRubyObj(VALUE rubyObj,const void* foxObj) {
  FXASSERT(!NIL_P(rubyObj));
  FXASSERT(foxObj!=0);
  FXRubyObjDesc* desc;
  FXTRACE((1,"FXRbRegisterRubyObj(rubyObj=%d,foxObj=%p)\n",static_cast<int>(rubyObj),foxObj));
  if(FXMALLOC(&desc,FXRubyObjDesc,1)){
    desc->obj=rubyObj;
    desc->borrowed=false;
    st_insert(FXRuby_Objects,reinterpret_cast<st_data_t>(const_cast<void*>(foxObj)),reinterpret_cast<st_data_t>(desc));
    }
  else{
    FXASSERT(FALSE);
    }
  FXASSERT(FXRbGetRubyObj(foxObj,false)==rubyObj);
  }


/**
 * Remove this mapping between a Ruby instance and a C++ object
 */
void FXRbUnregisterRubyObj(const void* foxObj){
  if(foxObj!=0){
    FXRubyObjDesc* desc;
    if(st_lookup(FXRuby_Objects,reinterpret_cast<st_data_t>(const_cast<void*>(foxObj)),reinterpret_cast<st_data_t *>(&desc))!=0){
      DATA_PTR(desc->obj)=0;
      FXFREE(&desc);
      st_delete(FXRuby_Objects,reinterpret_cast<st_data_t *>(const_cast<void**>(&foxObj)),reinterpret_cast<st_data_t *>(0));
      FXASSERT(st_lookup(FXRuby_Objects,reinterpret_cast<st_data_t>(const_cast<void*>(foxObj)),reinterpret_cast<st_data_t *>(0))==0);
      }
    }
  }


VALUE to_ruby(const FXObject* obj){
  if(obj!=0){
    FXString className=obj->getClassName();
    if(className.length()>3){
      if(className.left(4)=="FXRb"){ className.replace(0,4,"FX"); }
      }
    FXString desc=className+" *";
    return FXRbGetRubyObj(obj,desc.text());
    }
  return Qnil;
  }


/**
 * Return the registered Ruby class instance associated with this
 * FOX object, or Qnil if not found.
 */
VALUE FXRbGetRubyObj(const void *foxObj,bool searchBoth){
  FXRubyObjDesc* desc;
  if(foxObj!=0 && st_lookup(FXRuby_Objects,reinterpret_cast<st_data_t>(const_cast<void*>(foxObj)),reinterpret_cast<st_data_t *>(&desc))!=0){
    FXASSERT(desc!=0);
    if(searchBoth){
      return desc->obj;
      }
    else{
      return desc->borrowed ? Qnil : desc->obj;
      }
    }
  else{
    return Qnil;
    }
  }

/**
 * Return the registered Ruby class instance associated with this
 * FOX object, or a new registered instance if not found.
 */
VALUE FXRbGetRubyObj(const void *foxObj,swig_type_info* ty){
  if(foxObj!=0){
    VALUE rbObj=FXRbGetRubyObj(foxObj,true);
    return NIL_P(rbObj) ? FXRbNewPointerObj(const_cast<void*>(foxObj),ty) : rbObj;
    }
  else{
    return Qnil;
    }
  }

VALUE FXRbGetRubyObj(const void *foxObj,const char *type){
  if(foxObj!=0){
    FXASSERT(type!=0);
    VALUE rbObj=FXRbGetRubyObj(foxObj,true);
    return NIL_P(rbObj) ? FXRbNewPointerObj(const_cast<void*>(foxObj),FXRbTypeQuery(type)) : rbObj;
    }
  else{
    return Qnil;
    }
  }

/**
 * Look up the Ruby instance associated with this C++ object, if any, and mark
 * that instance as in use.
 *
 * We previously only marked those Ruby instances that were "non-borrowed"
 * references to C++ objects, as a way to reduce the number of Ruby instances
 * on the heap.
 */
void FXRbGcMark(void *obj){
  if(obj){
    /**
     * If the 2nd argument to FXRbGetRubyObj() is false, we only mark
     * non-borrowed references. This has led to problems in the past
     * (see e.g. SourceForge Bug #703721). I think the correct solution
     * is to mark any Ruby reference this object, "borrowed" or not;
     * so the 2nd argument to FXRbGetRubyObj() is now true.
     *
     * If you feel compelled to change this back to FXGetRubyObj(obj,false),
     * please think about it first. Especially make sure that the shutter.rb
     * example program works if you invoke the GC in ShutterWindow#create;
     * make sure that the shutter items' contents don't get blown away!
     */
    VALUE value=FXRbGetRubyObj(obj,true);
    if(!NIL_P(value)){
      rb_gc_mark(value);
      }
    }
  }

//----------------------------------------------------------------------

// Returns a Ruby array of floats
VALUE FXRbMakeArray(const FXfloat* values,FXint size){
  VALUE result=rb_ary_new();
  for(FXint i=0; i<size; i++)
    rb_ary_push(result,rb_float_new(values[i]));
  return result;
  }

// Returns a Ruby array of floats
VALUE FXRbMakeArray(const FXdouble* values,FXint size){
  VALUE result=rb_ary_new();
  for(FXint i=0; i<size; i++)
    rb_ary_push(result,rb_float_new(values[i]));
  return result;
  }

// Returns a Ruby array of integers
VALUE FXRbMakeArray(const FXint* values,FXint size){
  VALUE result=rb_ary_new();
  for(FXint i=0; i<size; i++)
    rb_ary_push(result,INT2NUM(values[i]));
  return result;
  }

// Returns a Ruby array of integers
VALUE FXRbMakeArray(const FXuint* values,FXint size){
  VALUE result=rb_ary_new();
  for(FXint i=0; i<size; i++)
    rb_ary_push(result,UINT2NUM(values[i]));
  return result;
  }

// Returns a Ruby array of integers
VALUE FXRbMakeArray(const FXchar* dashpattern,FXuint dashlength){
  VALUE result=rb_ary_new();
  for(FXuint i=0; i<dashlength; i++)
    rb_ary_push(result,INT2NUM(dashpattern[i]));
  return result;
  }

// Returns a Ruby array of FXArcs
VALUE FXRbMakeArray(const FXArc* arcs,FXuint narcs){
  VALUE result=rb_ary_new();
  for(FXuint i=0; i<narcs; i++)
    rb_ary_push(result,FXRbGetRubyObj(&arcs[i],"FXArc *"));
  return result;
  }

// Returns a Ruby array of FXPoints
VALUE FXRbMakeArray(const FXPoint* points,FXuint npoints){
  VALUE result=rb_ary_new();
  for(FXuint i=0; i<npoints; i++)
    rb_ary_push(result,FXRbGetRubyObj(&points[i],"FXPoint *"));
  return result;
  }

// Returns a Ruby array of FXRectangles
VALUE FXRbMakeArray(const FXRectangle* rectangles,FXuint nrectangles){
  VALUE result=rb_ary_new();
  for(FXuint i=0; i<nrectangles; i++)
    rb_ary_push(result,FXRbGetRubyObj(&rectangles[i],"FXRectangle *"));
  return result;
  }
  
// Returns a Ruby array of FXSegments
VALUE FXRbMakeArray(const FXSegment* segments,FXuint nsegments){
  VALUE result=rb_ary_new();
  for(FXuint i=0; i<nsegments; i++)
    rb_ary_push(result,FXRbGetRubyObj(&segments[i],"FXSegment *"));
  return result;
  }

// Returns a Ruby array of FXColor values
VALUE FXRbMakeColorArray(const FXColor* colors,FXint w,FXint h){
  VALUE result=rb_ary_new();
  FXint size=w*h;
  for(FXuint i=0; i<size; i++)
    rb_ary_push(result,to_ruby(colors[i]));
  return result;
  }

//----------------------------------------------------------------------

/**
 * Based on the sender object's class and the selector, convert the message
 * data stored in the void pointer (ptr) to a suitable Ruby object.
 *
 * This function is used when we need to pass message data into a Ruby
 * code block (or method) that is acting as a FOX message handler.
 */
static VALUE FXRbConvertMessageData(FXObject* sender,FXObject* recv,FXSelector sel,void* ptr){
  FXTRACE((1,"FXRbConvertMessageData(%s(%p),FXSEL(%s,%d),%p)\n",sender->getClassName(),sender,FXDebugTarget::messageTypeName[FXSELTYPE(sel)],FXSELID(sel),ptr));
  FXInputHandle fff;
  FXushort type=FXSELTYPE(sel);
  FXushort id=FXSELID(sel);

  FXASSERT(type!=SEL_NONE);
  FXASSERT(type!=SEL_LAST);

  if(type==SEL_KEYPRESS ||
     type==SEL_KEYRELEASE ||
     type==SEL_LEFTBUTTONPRESS ||
     type==SEL_LEFTBUTTONRELEASE ||
     type==SEL_MIDDLEBUTTONPRESS ||
     type==SEL_MIDDLEBUTTONRELEASE ||
     type==SEL_RIGHTBUTTONPRESS ||
     type==SEL_RIGHTBUTTONRELEASE ||
     type==SEL_MOTION ||
     type==SEL_ENTER ||
     type==SEL_LEAVE ||
     type==SEL_FOCUSIN ||
     type==SEL_FOCUSOUT ||
     type==SEL_KEYMAP ||
     type==SEL_UNGRABBED ||
     type==SEL_PAINT ||
     type==SEL_CREATE ||
     type==SEL_DESTROY ||
     type==SEL_UNMAP ||
     type==SEL_MAP ||
     type==SEL_CONFIGURE ||
     type==SEL_SELECTION_LOST ||
     type==SEL_SELECTION_GAINED ||
     type==SEL_SELECTION_REQUEST ||
     type==SEL_RAISED ||
     type==SEL_LOWERED ||
     type==SEL_MOUSEWHEEL ||
     type==SEL_BEGINDRAG ||
     type==SEL_ENDDRAG ||
     type==SEL_TIMEOUT ||
     type==SEL_CLIPBOARD_LOST ||
     type==SEL_CLIPBOARD_GAINED ||
     type==SEL_CLIPBOARD_REQUEST ||
     type==SEL_CHORE ||
     type==SEL_FOCUS_SELF ||
     type==SEL_FOCUS_RIGHT ||
     type==SEL_FOCUS_LEFT ||
     type==SEL_FOCUS_DOWN ||
     type==SEL_FOCUS_UP ||
     type==SEL_FOCUS_NEXT ||
     type==SEL_FOCUS_PREV ||
     type==SEL_DND_ENTER ||
     type==SEL_DND_LEAVE ||
     type==SEL_DND_DROP ||
     type==SEL_DND_MOTION ||
     type==SEL_DND_REQUEST ||
     type==SEL_PICKED ||
     type==SEL_SESSION_NOTIFY ||
     type==SEL_SESSION_CLOSED) {
    return to_ruby(reinterpret_cast<FXEvent*>(ptr));
    }
  else if(type==SEL_DRAGGED && !sender->isMemberOf(FXMETACLASS(FXGLViewer))){
    return to_ruby(reinterpret_cast<FXEvent*>(ptr));
    }
  else if(type==SEL_SIGNAL){
    return to_ruby(static_cast<int>(reinterpret_cast<long>(ptr)));
    }
  else if(type==SEL_IO_READ ||
          type==SEL_IO_WRITE ||
	  type==SEL_IO_EXCEPT){
#ifdef WIN32
    fff=reinterpret_cast<FXInputHandle>(ptr);
#else
    fff=static_cast<FXInputHandle>(reinterpret_cast<long>(ptr));
#endif
    return Qnil;
    }
  else if(type==SEL_CLOSE ||
          type==SEL_DELETE ||
          type==SEL_MINIMIZE ||
          type==SEL_RESTORE ||
          type==SEL_MAXIMIZE ||
          type==SEL_UPDATE ||
          type==SEL_QUERY_HELP ||
	  type==SEL_QUERY_TIP){
    FXASSERT(ptr==0);
    return Qnil;
    }
  else if(sender->isMemberOf(FXMETACLASS(FX4Splitter))){
    if(type==SEL_CHANGED||type==SEL_COMMAND) return Qnil;
    }
  else if(sender->isMemberOf(FXMETACLASS(FXArrowButton))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXuint>(reinterpret_cast<FXuval>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXPicker))){
    if(type==SEL_COMMAND || type==SEL_CHANGED) return to_ruby(reinterpret_cast<FXPoint*>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXButton))){
    if(type==SEL_CLICKED ||
       type==SEL_DOUBLECLICKED ||
       type==SEL_TRIPLECLICKED ||
       type==SEL_COMMAND) return to_ruby(static_cast<FXuint>(reinterpret_cast<FXuval>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXCheckButton))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXuchar>(reinterpret_cast<FXuval>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXColorBar))){
    if(type==SEL_CHANGED || type==SEL_COMMAND){
      FXfloat* hsv=reinterpret_cast<FXfloat*>(ptr);
      return rb_ary_new3(3,to_ruby(hsv[0]),to_ruby(hsv[1]),to_ruby(hsv[2]));
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXColorDialog))){
    if(type==SEL_CHANGED || type==SEL_COMMAND) return to_ruby(static_cast<FXColor>(reinterpret_cast<unsigned long>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXColorRing))){
    if(type==SEL_CHANGED || type==SEL_COMMAND){
      FXfloat* hsv=reinterpret_cast<FXfloat*>(ptr);
      return rb_ary_new3(3,to_ruby(hsv[0]),to_ruby(hsv[1]),to_ruby(hsv[2]));
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXColorSelector))){
    if(type==SEL_CHANGED || type==SEL_COMMAND) return to_ruby(static_cast<FXColor>(reinterpret_cast<unsigned long>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXColorWell))){
    if(type==SEL_CHANGED ||
       type==SEL_COMMAND ||
       type==SEL_CLICKED ||
       type==SEL_DOUBLECLICKED ||
       type==SEL_TRIPLECLICKED) {
	       VALUE v=to_ruby(static_cast<FXColor>(reinterpret_cast<unsigned long>(ptr)));
	       return v;
    }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXColorWheel))){
    if(type==SEL_CHANGED || type==SEL_COMMAND){
      FXfloat* hsv=reinterpret_cast<FXfloat*>(ptr);
      return rb_ary_new3(3,to_ruby(hsv[0]),to_ruby(hsv[1]),to_ruby(hsv[2]));
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXComboBox))){
    if(type==SEL_CHANGED || type==SEL_COMMAND) return to_ruby(reinterpret_cast<FXchar*>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXDataTarget))){
    if(type==SEL_COMMAND || type==SEL_CHANGED){
      if(recv->isMemberOf(FXMETACLASS(FXWindow))){
        switch(id){
          case FXWindow::ID_SETINTVALUE:
            return to_ruby(*reinterpret_cast<FXint*>(ptr));
          case FXWindow::ID_SETREALVALUE:
            return to_ruby(*reinterpret_cast<FXdouble*>(ptr));
          case FXWindow::ID_SETSTRINGVALUE:
            return to_ruby(*reinterpret_cast<FXString*>(ptr));
          case FXWindow::ID_GETINTVALUE:
          case FXWindow::ID_GETREALVALUE:
          case FXWindow::ID_GETSTRINGVALUE:
            FXASSERT(FALSE);
            break;
          default:
            FXASSERT(FALSE);
          }
        }
      else{
        // It's not a window object...
        FXASSERT(sender->isMemberOf(FXMETACLASS(FXRbDataTarget)));
        return dynamic_cast<FXRbDataTarget*>(sender)->getValue();
        }
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXDial))){
    if(type==SEL_CHANGED || type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXDirBox))){
    if(type==SEL_CHANGED || type==SEL_COMMAND) return to_ruby(reinterpret_cast<FXchar*>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXDockBar))){
    if(type==SEL_DOCKED || type==SEL_FLOATED) return FXRbGetRubyObj(ptr,FXRbTypeQuery("FXDockSite *"));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXFileList))){
    if (type==SEL_CHANGED ||
        type==SEL_CLICKED ||
        type==SEL_DOUBLECLICKED ||
        type==SEL_TRIPLECLICKED ||
        type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXFoldingList))){
    if(type==SEL_COLLAPSED ||
       type==SEL_EXPANDED ||
       type==SEL_COMMAND ||
       type==SEL_CHANGED ||
       type==SEL_CLICKED ||
       type==SEL_DOUBLECLICKED ||
       type==SEL_TRIPLECLICKED ||
       type==SEL_OPENED ||
       type==SEL_CLOSED ||
       type==SEL_SELECTED ||
       type==SEL_DESELECTED ||
       type==SEL_INSERTED ||
       type==SEL_DELETED){
      return FXRbGetRubyObj(ptr,FXRbTypeQuery("FXFoldingItem *"));
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXGLViewer))){
    if(type==SEL_CHANGED ||
       type==SEL_CLICKED ||
       type==SEL_DOUBLECLICKED ||
       type==SEL_TRIPLECLICKED ||
       type==SEL_DRAGGED){
      return FXRbGetRubyObj(ptr,FXRbTypeQuery("FXGLObject *"));
      }
    else if(type==SEL_COMMAND){
      if(id==FXWindow::ID_QUERY_MENU)
        return to_ruby(reinterpret_cast<FXEvent*>(ptr));
      else
        return FXRbGetRubyObj(ptr,FXRbTypeQuery("FXGLObject *"));
      }
    else if(type==SEL_LASSOED ||
            type==SEL_INSERTED ||
            type==SEL_DELETED ||
            type==SEL_SELECTED ||
            type==SEL_DESELECTED){
      VALUE ary=rb_ary_new();
      // FXGLObject** objlist=reinterpret_cast<FXGLObject**>(ptr);
      // FIXME: objlist is a NULL-terminated array of pointers to FXGLObject
      return ary;
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXGradientBar))){
    if(type==SEL_CHANGED){
      return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
      }
    else if(type==SEL_SELECTED || type==SEL_DESELECTED){
      return Qnil;
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXHeader))){
    if(type==SEL_COMMAND ||
       type==SEL_CHANGED ||
       type==SEL_CLICKED ||
       type==SEL_REPLACED ||
       type==SEL_INSERTED ||
       type==SEL_DELETED) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXIconList))){
    if(type==SEL_CHANGED ||
       type==SEL_CLICKED ||
       type==SEL_DOUBLECLICKED ||
       type==SEL_TRIPLECLICKED ||
       type==SEL_COMMAND ||
       type==SEL_SELECTED ||
       type==SEL_DESELECTED ||
       type==SEL_REPLACED ||
       type==SEL_INSERTED ||
       type==SEL_DELETED) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXKnob))){
    if(type==SEL_CHANGED || type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXList))){
    if(type==SEL_CHANGED ||
       type==SEL_CLICKED ||
       type==SEL_DOUBLECLICKED ||
       type==SEL_TRIPLECLICKED ||
       type==SEL_SELECTED ||
       type==SEL_DESELECTED ||
       type==SEL_REPLACED ||
       type==SEL_INSERTED ||
       type==SEL_DELETED ||
       type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXListBox))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    else if(type==SEL_CHANGED) return Qnil;
    }
  else if(sender->isMemberOf(FXMETACLASS(FXMDIChild))){
    if(type==SEL_MINIMIZE ||
       type==SEL_MAXIMIZE ||
       type==SEL_CLOSE ||
       type==SEL_RESTORE){
       return Qnil;
      }
    else if(type==SEL_SELECTED ||
            type==SEL_DESELECTED){
      return FXRbGetRubyObj(ptr,FXRbTypeQuery("FXMDIChild *"));
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXMDIClient))){
    if(type==SEL_CHANGED) return FXRbGetRubyObj(ptr,FXRbTypeQuery("FXMDIChild *"));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXMenuCheck))){
    if(type==SEL_COMMAND) return to_ruby(reinterpret_cast<FXuval>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXMenuRadio))){
    if(type==SEL_COMMAND) return to_ruby(reinterpret_cast<FXuval>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXMenuCommand))){
    if(type==SEL_COMMAND) return to_ruby(true);
    }
  else if(sender->isMemberOf(FXMETACLASS(FXOption))){
    if(type==SEL_COMMAND) return to_ruby(reinterpret_cast<FXEvent*>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXOptionMenu))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXRadioButton))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXuchar>(reinterpret_cast<FXuval>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXRealSlider))){
    if(type==SEL_CHANGED || type==SEL_COMMAND)
      return to_ruby(*(reinterpret_cast<FXdouble *>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXRealSpinner))){
    if(type==SEL_COMMAND || type==SEL_CHANGED) return to_ruby(*(reinterpret_cast<FXdouble *>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXRecentFiles))){
    if(type==SEL_COMMAND) return to_ruby(reinterpret_cast<FXchar*>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXRuler))){
    if(type==SEL_CHANGED) return Qnil;
    }
  else if(sender->isMemberOf(FXMETACLASS(FXScrollBar))){
    if(type==SEL_CHANGED || type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXShutter))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXSlider))){
    if(type==SEL_CHANGED || type==SEL_COMMAND)
      return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXSpinner))){
    if(type==SEL_CHANGED || type==SEL_COMMAND)
      return to_ruby(static_cast<FXint>(reinterpret_cast<long>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXSplitter))){
    if(type==SEL_CHANGED || type==SEL_COMMAND)
      return to_ruby(reinterpret_cast<FXWindow *>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXSwitcher))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXTabBar))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXTable))){
    if(type==SEL_CLICKED ||
       type==SEL_DOUBLECLICKED ||
       type==SEL_TRIPLECLICKED ||
       type==SEL_CHANGED ||
       type==SEL_COMMAND ||
       type==SEL_SELECTED ||
       type == SEL_DESELECTED) return to_ruby(reinterpret_cast<FXTablePos*>(ptr));
    else if(type == SEL_INSERTED ||
            type == SEL_DELETED ||
            type == SEL_REPLACED){
	      return to_ruby(reinterpret_cast<FXTableRange*>(ptr));
	      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXText))){
    if     (type == SEL_COMMAND) {
      switch(id){
        case FXText::ID_COPY_SEL:
        case FXText::ID_PASTE_SEL:
        case FXText::ID_DELETE_SEL:
          return Qnil;
          break;
        default:
          FXASSERT(FALSE);
          return reinterpret_cast<VALUE>(ptr); // pass-through as is
        }
      }
    else if(type==SEL_CHANGED){
      return to_ruby(static_cast<FXint>(reinterpret_cast<FXival>(ptr)));
      }
    else if(type==SEL_SELECTED ||
            type == SEL_DESELECTED) {
      FXint* what=reinterpret_cast<FXint*>(ptr);
      FXASSERT(what!=0);
      VALUE ary=rb_ary_new();
      rb_ary_push(ary,to_ruby(what[0])); // start position of text
      rb_ary_push(ary,to_ruby(what[1])); // length of text
      return ary;
      }
    else if(type==SEL_INSERTED || type==SEL_DELETED || type==SEL_REPLACED) {
      return to_ruby(reinterpret_cast<FXTextChange*>(ptr));
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXTextField))){
    if(type==SEL_CHANGED ||
       type==SEL_COMMAND ||
       type==SEL_VERIFY) return to_ruby(reinterpret_cast<FXchar*>(ptr));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXToggleButton))){
    if(type==SEL_COMMAND) return to_ruby(static_cast<FXuchar>(reinterpret_cast<FXuval>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXToolBarTab))){
    if (type==SEL_COMMAND) return to_ruby(static_cast<FXbool>(reinterpret_cast<FXuval>(ptr)));
    }
  else if(sender->isMemberOf(FXMETACLASS(FXTopWindow))){
    if(type==SEL_MINIMIZE ||
       type==SEL_MAXIMIZE ||
       type==SEL_RESTORE ||
       type==SEL_CLOSE){
      return Qnil;
      }
    else if (type==SEL_SESSION_NOTIFY ||
             type==SEL_SESSION_CLOSED) {
      return to_ruby(reinterpret_cast<FXEvent*>(ptr));
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXTreeList))){
    if(type==SEL_COLLAPSED ||
       type==SEL_EXPANDED ||
       type==SEL_COMMAND ||
       type==SEL_CHANGED ||
       type==SEL_CLICKED ||
       type==SEL_DOUBLECLICKED ||
       type==SEL_TRIPLECLICKED ||
       type==SEL_OPENED ||
       type==SEL_CLOSED ||
       type==SEL_SELECTED ||
       type==SEL_DESELECTED ||
       type==SEL_INSERTED ||
       type==SEL_DELETED){
      return FXRbGetRubyObj(ptr,FXRbTypeQuery("FXTreeItem *"));
      }
    }
  else if(sender->isMemberOf(FXMETACLASS(FXTreeListBox))){
    if(type==SEL_CHANGED || type==SEL_COMMAND)
      return FXRbGetRubyObj(ptr,FXRbTypeQuery("FXTreeItem *"));
    }
#ifdef WITH_FXSCINTILLA
  else if(sender->isMemberOf(FXMETACLASS(FXScintilla))){
    if(type==SEL_COMMAND){
      return FXRbGetRubyObj(ptr,FXRbTypeQuery("SCNotification *"));
      }
    }
#endif
  else{
    FXTRACE((100,"%s:%d: message data passed through as-is\n",__FILE__,__LINE__));
    return reinterpret_cast<VALUE>(ptr); // pass-through as is
    }
  FXTRACE((100,"%s:%d: message data passed through as-is\n",__FILE__,__LINE__));
  return reinterpret_cast<VALUE>(ptr); // pass-through as is
  }


/**
 * When a Ruby instance (e.g. one of your widgets) calls handle() on another
 * object, e.g.
 *
 *     def onUpdAnswer(sender, sel, ptr)
 *       if theAnswerIsTwo?
 *         sender.handle(self, FXSEL(SEL_COMMAND,FXWindow::ID_SETVALUE), 2)
 *       else
 *         sender.handle(self, FXSEL(SEL_COMMAND,FXWindow::ID_SETVALUE), 3)
 *       end
 *       return 1
 *     end
 *
 * it's usually the case that this will get passed along to the underlying
 * C++ object. In that case, we need to convert the message data (e.g. the
 * Fixnums "2" or "3" in the example above) from Ruby objects back into
 * the appropriate C++ objects. That's what this function is for.
 */
void* FXRbGetExpectedData(VALUE recv,FXSelector key,VALUE value){
  void *ptr;
  static FXint intValue;
  static FXint intRange[2];
  static FXdouble realValue;
  static FXdouble realRange[2];
  static FXString stringValue;
  static FXColor colorValue;
  FXushort type=FXSELTYPE(key);
  FXushort id=FXSELID(key);

  // Extract the FOX object (the receiver) from this Ruby instance
  FXObject* obj;
  Data_Get_Struct(recv,FXObject,obj);

  FXASSERT(type!=SEL_NONE);
  FXASSERT(type!=SEL_LAST);
  switch(type){
    case SEL_KEYPRESS:
    case SEL_KEYRELEASE:
    case SEL_LEFTBUTTONPRESS:
    case SEL_LEFTBUTTONRELEASE:
    case SEL_MIDDLEBUTTONPRESS:
    case SEL_MIDDLEBUTTONRELEASE:
    case SEL_RIGHTBUTTONPRESS:
    case SEL_RIGHTBUTTONRELEASE:
    case SEL_MOTION:
    case SEL_ENTER:
    case SEL_LEAVE:
    case SEL_FOCUSIN:
    case SEL_FOCUSOUT:
    case SEL_KEYMAP:
    case SEL_UNGRABBED:
    case SEL_PAINT:
    case SEL_CREATE:
    case SEL_DESTROY:
    case SEL_UNMAP:
    case SEL_MAP:
    case SEL_CONFIGURE:
    case SEL_SELECTION_LOST:
    case SEL_SELECTION_GAINED:
    case SEL_SELECTION_REQUEST:
    case SEL_RAISED:
    case SEL_LOWERED:
    case SEL_MOUSEWHEEL:
    case SEL_BEGINDRAG:
    case SEL_ENDDRAG:
    case SEL_LASSOED:
    case SEL_TIMEOUT:
    case SEL_CLIPBOARD_LOST:
    case SEL_CLIPBOARD_GAINED:
    case SEL_CLIPBOARD_REQUEST:
    case SEL_CHORE:
    case SEL_FOCUS_SELF:
    case SEL_FOCUS_RIGHT:
    case SEL_FOCUS_LEFT:
    case SEL_FOCUS_DOWN:
    case SEL_FOCUS_UP:
    case SEL_FOCUS_NEXT:
    case SEL_FOCUS_PREV:
    case SEL_DND_ENTER:
    case SEL_DND_LEAVE:
    case SEL_DND_DROP:
    case SEL_DND_MOTION:
    case SEL_DND_REQUEST:
    case SEL_PICKED:
      SWIG_Ruby_ConvertPtr(value,&ptr,FXRbTypeQuery("FXEvent *"),1);
      return ptr;
    case SEL_IO_READ:
    case SEL_IO_WRITE:
    case SEL_IO_EXCEPT:
      return 0; // should be an FXInputHandle?
    case SEL_SIGNAL:
      return reinterpret_cast<void*>(static_cast<long>(NUM2INT(value)));
    case SEL_CLOSE:
    case SEL_DELETE: