From 90825e2392b2d70e43c7a25b8a3752299a933894 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebindings@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- qtruby/rubylib/qtruby/Makefile.am | 15 + qtruby/rubylib/qtruby/Qt.cpp | 2967 ++++++++++++++++++++++++++++++ qtruby/rubylib/qtruby/configure.in.in | 19 + qtruby/rubylib/qtruby/extconf.rb | 5 + qtruby/rubylib/qtruby/handlers.cpp | 1978 ++++++++++++++++++++ qtruby/rubylib/qtruby/lib/Makefile.am | 4 + qtruby/rubylib/qtruby/lib/Qt.rb | 1 + qtruby/rubylib/qtruby/lib/Qt/Makefile.am | 2 + qtruby/rubylib/qtruby/lib/Qt/qtruby.rb | 1978 ++++++++++++++++++++ qtruby/rubylib/qtruby/marshall.h | 46 + qtruby/rubylib/qtruby/qtruby.h | 55 + qtruby/rubylib/qtruby/smokeruby.h | 321 ++++ 12 files changed, 7391 insertions(+) create mode 100644 qtruby/rubylib/qtruby/Makefile.am create mode 100644 qtruby/rubylib/qtruby/Qt.cpp create mode 100644 qtruby/rubylib/qtruby/configure.in.in create mode 100644 qtruby/rubylib/qtruby/extconf.rb create mode 100644 qtruby/rubylib/qtruby/handlers.cpp create mode 100644 qtruby/rubylib/qtruby/lib/Makefile.am create mode 100644 qtruby/rubylib/qtruby/lib/Qt.rb create mode 100644 qtruby/rubylib/qtruby/lib/Qt/Makefile.am create mode 100644 qtruby/rubylib/qtruby/lib/Qt/qtruby.rb create mode 100644 qtruby/rubylib/qtruby/marshall.h create mode 100644 qtruby/rubylib/qtruby/qtruby.h create mode 100644 qtruby/rubylib/qtruby/smokeruby.h (limited to 'qtruby/rubylib/qtruby') diff --git a/qtruby/rubylib/qtruby/Makefile.am b/qtruby/rubylib/qtruby/Makefile.am new file mode 100644 index 00000000..1df273bf --- /dev/null +++ b/qtruby/rubylib/qtruby/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir)/smoke $(all_includes) -I$(RUBY_ARCHDIR) + +noinst_HEADERS = qtruby.h marshall.h smokeruby.h extconf.rb + +noinst_LTLIBRARIES = libqtrubyinternal.la +libqtrubyinternal_la_SOURCES = Qt.cpp handlers.cpp +libqtrubyinternal_la_METASOURCES = AUTO + +rubylibdir = $(RUBY_SITEARCHDIR) +rubylib_LTLIBRARIES = qtruby.la +qtruby_la_SOURCES = +qtruby_la_LDFLAGS = -module $(all_libraries) -version-info 0:0:0 +qtruby_la_LIBADD = libqtrubyinternal.la $(LIB_QT) $(top_builddir)/smoke/qt/libsmokeqt.la + +SUBDIRS = lib diff --git a/qtruby/rubylib/qtruby/Qt.cpp b/qtruby/rubylib/qtruby/Qt.cpp new file mode 100644 index 00000000..a8415512 --- /dev/null +++ b/qtruby/rubylib/qtruby/Qt.cpp @@ -0,0 +1,2967 @@ +/*************************************************************************** + Qt.cpp - description + ------------------- + begin : Fri Jul 4 2003 + copyright : (C) 2003-2004 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#ifdef _BOOL +#define HAS_BOOL +#endif + +#include + +#ifndef QT_VERSION_STR +#define QT_VERSION_STR "Unknown" +#endif + +#undef free +#undef malloc + +#include "marshall.h" +#include "qtruby.h" +#include "smokeruby.h" +#include "smoke.h" + +// #define DEBUG + +#define QTRUBY_VERSION "1.0.13" + +extern Smoke *qt_Smoke; +extern void init_qt_Smoke(); +extern void smokeruby_mark(void * ptr); +extern void smokeruby_free(void * ptr); +extern VALUE qchar_to_s(VALUE self); + +#ifdef DEBUG +int do_debug = qtdb_gc; +#else +int do_debug = qtdb_none; +#endif + +QPtrDict pointer_map(2179); +int object_count = 0; + +QAsciiDict methcache(2179); +QAsciiDict classcache(2179); +// Maps from a classname in the form Qt::Widget to an int id +QIntDict classname(2179); + +extern "C" { +VALUE qt_module = Qnil; +VALUE qext_scintilla_module = Qnil; +VALUE kde_module = Qnil; +VALUE kparts_module = Qnil; +VALUE kio_module = Qnil; +VALUE kns_module = Qnil; +VALUE dom_module = Qnil; +VALUE kontact_module = Qnil; +VALUE kate_module = Qnil; +VALUE ktexteditor_module = Qnil; +VALUE koffice_module = Qnil; +VALUE qt_internal_module = Qnil; +VALUE qt_base_class = Qnil; +VALUE qmetaobject_class = Qnil; +VALUE qvariant_class = Qnil; +VALUE kconfigskeleton_class = Qnil; +VALUE kconfigskeleton_itemenum_class = Qnil; +VALUE kconfigskeleton_itemenum_choice_class = Qnil; +VALUE kio_udsatom_class = Qnil; +VALUE kwin_class = Qnil; +VALUE konsole_part_class = Qnil; +bool application_terminated = false; +}; + +#define logger logger_backend +void rb_str_catf(VALUE self, const char *format, ...) __attribute__ ((format (printf, 2, 3))); + +static VALUE (*_new_kde)(int, VALUE *, VALUE) = 0; +static VALUE (*_kconfigskeletonitem_immutable)(VALUE) = 0; + +Smoke::Index _current_method = 0; + +extern TypeHandler Qt_handlers[]; +void install_handlers(TypeHandler *); + +smokeruby_object *value_obj_info(VALUE ruby_value) { // ptr on success, null on fail + if (TYPE(ruby_value) != T_DATA) { + return 0; + } + + smokeruby_object * o = 0; + Data_Get_Struct(ruby_value, smokeruby_object, o); + return o; +} + +void *value_to_ptr(VALUE ruby_value) { // ptr on success, null on fail + smokeruby_object *o = value_obj_info(ruby_value); + return o; +} + +VALUE getPointerObject(void *ptr); + +bool isQObject(Smoke *smoke, Smoke::Index classId) { + if(qstrcmp(smoke->classes[classId].className, "QObject") == 0) + return true; + for(Smoke::Index *p = smoke->inheritanceList + smoke->classes[classId].parents; + *p; + p++) { + if(isQObject(smoke, *p)) + return true; + } + return false; +} + +bool isDerivedFrom(Smoke *smoke, Smoke::Index classId, Smoke::Index baseId) { + if(classId == 0 && baseId == 0) + return false; + if(classId == baseId) + return true; + for(Smoke::Index *p = smoke->inheritanceList + smoke->classes[classId].parents; + *p; + p++) { + if(isDerivedFrom(smoke, *p, baseId)) + return true; + } + return false; +} + +bool isDerivedFromByName(Smoke *smoke, const char *className, const char *baseClassName) { + if(!smoke || !className || !baseClassName) + return false; + Smoke::Index idClass = smoke->idClass(className); + Smoke::Index idBase = smoke->idClass(baseClassName); + return isDerivedFrom(smoke, idClass, idBase); +} + +VALUE getPointerObject(void *ptr) { + if (pointer_map[ptr] == 0) { + return Qnil; + } else { + return *(pointer_map[ptr]); + } +} + +void unmapPointer(smokeruby_object *o, Smoke::Index classId, void *lastptr) { + void *ptr = o->smoke->cast(o->ptr, o->classId, classId); + if(ptr != lastptr) { + lastptr = ptr; + if (pointer_map[ptr] != 0) { + VALUE * obj_ptr = pointer_map[ptr]; + + if (do_debug & qtdb_gc) { + const char *className = o->smoke->classes[o->classId].className; + qWarning("unmapPointer (%s*)%p -> %p", className, ptr, obj_ptr); + } + + pointer_map.remove(ptr); + free((void*) obj_ptr); + } + } + for(Smoke::Index *i = o->smoke->inheritanceList + o->smoke->classes[classId].parents; + *i; + i++) { + unmapPointer(o, *i, lastptr); + } +} + +// Store pointer in pointer_map hash : "pointer_to_Qt_object" => weak ref to associated Ruby object +// Recurse to store it also as casted to its parent classes. + +void mapPointer(VALUE obj, smokeruby_object *o, Smoke::Index classId, void *lastptr) { + void *ptr = o->smoke->cast(o->ptr, o->classId, classId); + + if (ptr != lastptr) { + lastptr = ptr; + VALUE * obj_ptr = (VALUE *) malloc(sizeof(VALUE)); + memcpy(obj_ptr, &obj, sizeof(VALUE)); + + if (do_debug & qtdb_gc) { + const char *className = o->smoke->classes[o->classId].className; + qWarning("mapPointer (%s*)%p -> %p", className, ptr, (void*)obj); + } + + pointer_map.insert(ptr, obj_ptr); + } + + for(Smoke::Index *i = o->smoke->inheritanceList + o->smoke->classes[classId].parents; + *i; + i++) { + mapPointer(obj, o, *i, lastptr); + } + + return; +} + +Marshall::HandlerFn getMarshallFn(const SmokeType &type); + +class VirtualMethodReturnValue : public Marshall { + Smoke *_smoke; + Smoke::Index _method; + Smoke::Stack _stack; + SmokeType _st; + VALUE _retval; +public: + const Smoke::Method &method() { return _smoke->methods[_method]; } + SmokeType type() { return _st; } + Marshall::Action action() { return Marshall::FromVALUE; } + Smoke::StackItem &item() { return _stack[0]; } + VALUE * var() { return &_retval; } + + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as return-type of virtual method %s::%s", + type().name(), + _smoke->className(method().classId), + _smoke->methodNames[method().name]); + } + + Smoke *smoke() { return _smoke; } + void next() {} + bool cleanup() { return false; } + + VirtualMethodReturnValue(Smoke *smoke, Smoke::Index meth, Smoke::Stack stack, VALUE retval) : + _smoke(smoke), _method(meth), _stack(stack), _retval(retval) { + _st.set(_smoke, method().ret); + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + } +}; + +class VirtualMethodCall : public Marshall { + Smoke *_smoke; + Smoke::Index _method; + Smoke::Stack _stack; + VALUE _obj; + int _cur; + Smoke::Index *_args; + VALUE *_sp; + bool _called; + +public: + SmokeType type() { return SmokeType(_smoke, _args[_cur]); } + Marshall::Action action() { return Marshall::ToVALUE; } + Smoke::StackItem &item() { return _stack[_cur + 1]; } + VALUE * var() { return _sp + _cur; } + const Smoke::Method &method() { return _smoke->methods[_method]; } + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as argument of virtual method %s::%s", + type().name(), + _smoke->className(method().classId), + _smoke->methodNames[method().name]); + } + Smoke *smoke() { return _smoke; } + void callMethod() { + if(_called) return; + _called = true; + + VALUE _retval = rb_funcall2(_obj, + rb_intern(_smoke->methodNames[method().name]), + method().numArgs, + _sp ); + VirtualMethodReturnValue r(_smoke, _method, _stack, _retval); + } + + void next() { + int oldcur = _cur; + _cur++; + while(!_called && _cur < method().numArgs) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + _cur++; + } + callMethod(); + _cur = oldcur; + } + + bool cleanup() { return false; } // is this right? + + VirtualMethodCall(Smoke *smoke, Smoke::Index meth, Smoke::Stack stack, VALUE obj) : + _smoke(smoke), _method(meth), _stack(stack), _obj(obj), _cur(-1), _sp(0), _called(false) { + _sp = (VALUE *) calloc(method().numArgs, sizeof(VALUE)); + + _args = _smoke->argumentList + method().args; + } + + ~VirtualMethodCall() { + free(_sp); + } +}; + +class MethodReturnValue : public Marshall { + Smoke *_smoke; + Smoke::Index _method; + VALUE * _retval; + Smoke::Stack _stack; +public: + MethodReturnValue(Smoke *smoke, Smoke::Index method, Smoke::Stack stack, VALUE * retval) : + _smoke(smoke), _method(method), _retval(retval), _stack(stack) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + } + + const Smoke::Method &method() { return _smoke->methods[_method]; } + SmokeType type() { return SmokeType(_smoke, method().ret); } + Marshall::Action action() { return Marshall::ToVALUE; } + Smoke::StackItem &item() { return _stack[0]; } + VALUE * var() { + return _retval; + } + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as return-type of %s::%s", + type().name(), + qstrcmp(_smoke->className(method().classId), "QGlobalSpace") == 0 ? "" : _smoke->className(method().classId), + _smoke->methodNames[method().name]); + } + Smoke *smoke() { return _smoke; } + void next() {} + bool cleanup() { return false; } +}; + +class MethodCall : public Marshall { + int _cur; + Smoke *_smoke; + Smoke::Stack _stack; + Smoke::Index _method; + Smoke::Index *_args; + VALUE _target; + void *_current_object; + Smoke::Index _current_object_class; + VALUE *_sp; + int _items; + VALUE _retval; + bool _called; +public: + MethodCall(Smoke *smoke, Smoke::Index method, VALUE target, VALUE *sp, int items) : + _cur(-1), _smoke(smoke), _method(method), _target(target), _current_object(0), _sp(sp), _items(items), _called(false) + { + + if (_target != Qnil) { + smokeruby_object *o = value_obj_info(_target); + if (o && o->ptr) { + _current_object = o->ptr; + _current_object_class = o->classId; + } + } + + _args = _smoke->argumentList + _smoke->methods[_method].args; + _items = _smoke->methods[_method].numArgs; + _stack = new Smoke::StackItem[items + 1]; + _retval = Qnil; + } + + ~MethodCall() { + delete[] _stack; + } + + SmokeType type() { + return SmokeType(_smoke, _args[_cur]); + } + + Marshall::Action action() { + return Marshall::FromVALUE; + } + Smoke::StackItem &item() { + return _stack[_cur + 1]; + } + + VALUE * var() { + if(_cur < 0) return &_retval; + return _sp + _cur; + } + + inline const Smoke::Method &method() { + return _smoke->methods[_method]; + } + + void unsupported() { + if (qstrcmp(_smoke->className(method().classId), "QGlobalSpace") == 0) { + rb_raise(rb_eArgError, "Cannot handle '%s' as argument to %s", + type().name(), + _smoke->methodNames[method().name]); + } else { + rb_raise(rb_eArgError, "Cannot handle '%s' as argument to %s::%s", + type().name(), + _smoke->className(method().classId), + _smoke->methodNames[method().name]); + } + } + + Smoke *smoke() { + return _smoke; + } + + inline void callMethod() { + if(_called) return; + _called = true; + + QString className(_smoke->className(method().classId)); + + if ( ! className.endsWith(_smoke->methodNames[method().name]) + && TYPE(_target) != T_DATA + && _target != Qnil + && !(method().flags & Smoke::mf_static) ) + { + rb_raise(rb_eArgError, "Instance is not initialized, cannot call %s", + _smoke->methodNames[method().name]); + } + + if (_target == Qnil && !(method().flags & Smoke::mf_static)) { + rb_raise(rb_eArgError, "%s is not a class method\n", _smoke->methodNames[method().name]); + } + + Smoke::ClassFn fn = _smoke->classes[method().classId].classFn; + void *ptr = _smoke->cast(_current_object, _current_object_class, method().classId); + _items = -1; + (*fn)(method().method, ptr, _stack); + MethodReturnValue r(_smoke, _method, _stack, &_retval); + } + + void next() { + int oldcur = _cur; + _cur++; + + while(!_called && _cur < _items) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + _cur++; + } + callMethod(); + _cur = oldcur; + } + + bool cleanup() { + return true; + } +}; + +class UnencapsulatedQObject : public QObject { +public: + QConnectionList *public_receivers(int signal) const { return receivers(signal); } + void public_activate_signal(QConnectionList *clist, QUObject *o) { activate_signal(clist, o); } +}; + +class EmitSignal : public Marshall { + UnencapsulatedQObject *_qobj; + int _id; + MocArgument *_args; + VALUE *_sp; + int _items; + int _cur; + Smoke::Stack _stack; + bool _called; +public: + EmitSignal(QObject *qobj, int id, int items, VALUE args, VALUE *sp) : + _qobj((UnencapsulatedQObject*)qobj), _id(id), _sp(sp), _items(items), + _cur(-1), _called(false) + { + _items = NUM2INT(rb_ary_entry(args, 0)); + Data_Get_Struct(rb_ary_entry(args, 1), MocArgument, _args); + _stack = new Smoke::StackItem[_items]; + } + ~EmitSignal() { + delete[] _stack; + } + const MocArgument &arg() { return _args[_cur]; } + SmokeType type() { return arg().st; } + Marshall::Action action() { return Marshall::FromVALUE; } + Smoke::StackItem &item() { return _stack[_cur]; } + VALUE * var() { return _sp + _cur; } + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as signal argument", type().name()); + } + Smoke *smoke() { return type().smoke(); } + void emitSignal() { + if(_called) return; + _called = true; + + QConnectionList *clist = _qobj->public_receivers(_id); + if(!clist) return; + + QUObject *o = new QUObject[_items + 1]; + for(int i = 0; i < _items; i++) { + QUObject *po = o + i + 1; + Smoke::StackItem *si = _stack + i; + switch(_args[i].argType) { + case xmoc_bool: + static_QUType_bool.set(po, si->s_bool); + break; + case xmoc_int: + static_QUType_int.set(po, si->s_int); + break; + case xmoc_double: + static_QUType_double.set(po, si->s_double); + break; + case xmoc_charstar: + static_QUType_charstar.set(po, (char*)si->s_voidp); + break; + case xmoc_QString: + static_QUType_QString.set(po, *(QString*)si->s_voidp); + break; + default: + { + const SmokeType &t = _args[i].st; + void *p; + switch(t.elem()) { + case Smoke::t_bool: + p = &si->s_bool; + break; + case Smoke::t_char: + p = &si->s_char; + break; + case Smoke::t_uchar: + p = &si->s_uchar; + break; + case Smoke::t_short: + p = &si->s_short; + break; + case Smoke::t_ushort: + p = &si->s_ushort; + break; + case Smoke::t_int: + p = &si->s_int; + break; + case Smoke::t_uint: + p = &si->s_uint; + break; + case Smoke::t_long: + p = &si->s_long; + break; + case Smoke::t_ulong: + p = &si->s_ulong; + break; + case Smoke::t_float: + p = &si->s_float; + break; + case Smoke::t_double: + p = &si->s_double; + break; + case Smoke::t_enum: + { + // allocate a new enum value + Smoke::EnumFn fn = SmokeClass(t).enumFn(); + if(!fn) { + rb_warning("Unknown enumeration %s\n", t.name()); + p = new int((int)si->s_enum); + break; + } + Smoke::Index id = t.typeId(); + (*fn)(Smoke::EnumNew, id, p, si->s_enum); + (*fn)(Smoke::EnumFromLong, id, p, si->s_enum); + // FIXME: MEMORY LEAK + } + break; + case Smoke::t_class: + case Smoke::t_voidp: + p = si->s_voidp; + break; + default: + p = 0; + break; + } + static_QUType_ptr.set(po, p); + } + } + } + + _qobj->public_activate_signal(clist, o); + delete[] o; + } + void next() { + int oldcur = _cur; + _cur++; + + while(!_called && _cur < _items) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + _cur++; + } + + emitSignal(); + _cur = oldcur; + } + bool cleanup() { return true; } +}; + +class InvokeSlot : public Marshall { + VALUE _obj; + ID _slotname; + int _items; + MocArgument *_args; + QUObject *_o; + int _cur; + bool _called; + VALUE *_sp; + Smoke::Stack _stack; +public: + const MocArgument &arg() { return _args[_cur]; } + SmokeType type() { return arg().st; } + Marshall::Action action() { return Marshall::ToVALUE; } + Smoke::StackItem &item() { return _stack[_cur]; } + VALUE * var() { return _sp + _cur; } + Smoke *smoke() { return type().smoke(); } + bool cleanup() { return false; } + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as slot argument\n", type().name()); + } + void copyArguments() { + for(int i = 0; i < _items; i++) { + QUObject *o = _o + i + 1; + switch(_args[i].argType) { + case xmoc_bool: + _stack[i].s_bool = static_QUType_bool.get(o); + break; + case xmoc_int: + _stack[i].s_int = static_QUType_int.get(o); + break; + case xmoc_double: + _stack[i].s_double = static_QUType_double.get(o); + break; + case xmoc_charstar: + _stack[i].s_voidp = static_QUType_charstar.get(o); + break; + case xmoc_QString: + _stack[i].s_voidp = &static_QUType_QString.get(o); + break; + default: // case xmoc_ptr: + { + const SmokeType &t = _args[i].st; + void *p = static_QUType_ptr.get(o); + switch(t.elem()) { + case Smoke::t_bool: + _stack[i].s_bool = *(bool*)p; + break; + case Smoke::t_char: + _stack[i].s_char = *(char*)p; + break; + case Smoke::t_uchar: + _stack[i].s_uchar = *(unsigned char*)p; + break; + case Smoke::t_short: + _stack[i].s_short = *(short*)p; + break; + case Smoke::t_ushort: + _stack[i].s_ushort = *(unsigned short*)p; + break; + case Smoke::t_int: + _stack[i].s_int = *(int*)p; + break; + case Smoke::t_uint: + _stack[i].s_uint = *(unsigned int*)p; + break; + case Smoke::t_long: + _stack[i].s_long = *(long*)p; + break; + case Smoke::t_ulong: + _stack[i].s_ulong = *(unsigned long*)p; + break; + case Smoke::t_float: + _stack[i].s_float = *(float*)p; + break; + case Smoke::t_double: + _stack[i].s_double = *(double*)p; + break; + case Smoke::t_enum: + { + Smoke::EnumFn fn = SmokeClass(t).enumFn(); + if(!fn) { + rb_warning("Unknown enumeration %s\n", t.name()); + _stack[i].s_enum = *(int*)p; + break; + } + Smoke::Index id = t.typeId(); + (*fn)(Smoke::EnumToLong, id, p, _stack[i].s_enum); + } + break; + case Smoke::t_class: + case Smoke::t_voidp: + _stack[i].s_voidp = p; + break; + } + } + } + } + } + void invokeSlot() { + if(_called) return; + _called = true; + + (void) rb_funcall2(_obj, _slotname, _items, _sp); + } + + void next() { + int oldcur = _cur; + _cur++; + + while(!_called && _cur < _items) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + _cur++; + } + + invokeSlot(); + _cur = oldcur; + } + + InvokeSlot(VALUE obj, ID slotname, VALUE args, QUObject *o) : + _obj(obj), _slotname(slotname), _o(o), _cur(-1), _called(false) + { + _items = NUM2INT(rb_ary_entry(args, 0)); + Data_Get_Struct(rb_ary_entry(args, 1), MocArgument, _args); + _sp = (VALUE *) calloc(_items, sizeof(VALUE)); + _stack = new Smoke::StackItem[_items]; + copyArguments(); + } + + ~InvokeSlot() { + delete[] _stack; + free(_sp); + } +}; + +class QtRubySmokeBinding : public SmokeBinding { +public: + QtRubySmokeBinding(Smoke *s) : SmokeBinding(s) {} + + void deleted(Smoke::Index classId, void *ptr) { + VALUE obj = getPointerObject(ptr); + smokeruby_object *o = value_obj_info(obj); + if(do_debug & qtdb_gc) { + qWarning("%p->~%s()", ptr, smoke->className(classId)); + } + if(!o || !o->ptr) { + return; + } + unmapPointer(o, o->classId, 0); + o->ptr = 0; + } + + bool callMethod(Smoke::Index method, void *ptr, Smoke::Stack args, bool /*isAbstract*/) { + VALUE obj = getPointerObject(ptr); + smokeruby_object *o = value_obj_info(obj); + + if (do_debug & qtdb_virtual) { + Smoke::Method & meth = smoke->methods[method]; + QCString signature(smoke->methodNames[meth.name]); + signature += "("; + + for (int i = 0; i < meth.numArgs; i++) { + if (i != 0) signature += ", "; + signature += smoke->types[smoke->argumentList[meth.args + i]].name; + } + + signature += ")"; + if (meth.flags & Smoke::mf_const) { + signature += " const"; + } + + qWarning( "virtual %p->%s::%s called", + ptr, + smoke->classes[smoke->methods[method].classId].className, + (const char *) signature ); + } + + if(!o) { + if( do_debug & qtdb_virtual ) // if not in global destruction + qWarning("Cannot find object for virtual method %p -> %p", ptr, &obj); + return false; + } + + const char *methodName = smoke->methodNames[smoke->methods[method].name]; + + // If the virtual method hasn't been overriden, just call the C++ one. + if (rb_respond_to(obj, rb_intern(methodName)) == 0) { + return false; + } + + VirtualMethodCall c(smoke, method, args, obj); + c.next(); + return true; + } + + char *className(Smoke::Index classId) { + return classname.find((int) classId); + } +}; + +void rb_str_catf(VALUE self, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char *p = 0; + int len; + if (len = vasprintf(&p, format, ap), len != -1) { + rb_str_cat(self, p, len); + free(p); + } + va_end(ap); +} + +extern "C" { + +static VALUE +qdebug(VALUE klass, VALUE msg) +{ + qDebug("%s", StringValuePtr(msg)); + return klass; +} + +static VALUE +qfatal(VALUE klass, VALUE msg) +{ + qFatal("%s", StringValuePtr(msg)); + return klass; +} + +static VALUE +qwarning(VALUE klass, VALUE msg) +{ + qWarning("%s", StringValuePtr(msg)); + return klass; +} + +// ---------------- Helpers ------------------- + +//---------- All functions except fully qualified statics & enums --------- + +static VALUE qobject_metaobject(VALUE self); +static VALUE kde_package_to_class(const char * package, VALUE base_class); + +VALUE +set_obj_info(const char * className, smokeruby_object * o) +{ + VALUE klass = rb_funcall(qt_internal_module, + rb_intern("find_class"), + 1, + rb_str_new2(className) ); + + Smoke::Index *r = classcache.find(className); + if (r != 0) { + o->classId = (int)*r; + } + + // If the instance is a subclass of QObject, then check to see if the + // className from its QMetaObject is in the Smoke library. If not then + // create a Ruby class for it dynamically. Remove the first letter from + // any class names beginning with 'Q' or 'K' and put them under the Qt:: + // or KDE:: modules respectively. + if (isDerivedFrom(o->smoke, o->classId, o->smoke->idClass("QObject"))) { + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + QMetaObject * meta = qobject->metaObject(); + int classId = o->smoke->idClass(meta->className()); + // The class isn't in the Smoke lib.. + if (classId == 0) { + VALUE new_klass = Qnil; + QString className(meta->className()); + // The konsolePart class is in kdebase, and so it can't be in the Smoke library. + // This hack instantiates a Ruby KDE::KonsolePart instance + if (className == "konsolePart") { + new_klass = konsole_part_class; + } else if (className.startsWith("Q")) { + className.replace("Q", ""); + className = className.mid(0, 1).upper() + className.mid(1); + new_klass = rb_define_class_under(qt_module, className.latin1(), klass); + } else if (kde_module == Qnil) { + new_klass = rb_define_class(className.latin1(), klass); + } else { + new_klass = kde_package_to_class(className.latin1(), klass); + } + + if (new_klass != Qnil) { + klass = new_klass; + } + + // Add a Qt::Object.metaObject method which will do dynamic despatch on the + // metaObject() virtual method so that the true QMetaObject of the class + // is returned, rather than for the one for the parent class that is in + // the Smoke library. + rb_define_method(klass, "metaObject", (VALUE (*) (...)) qobject_metaobject, 0); + } + } + + VALUE obj = Data_Wrap_Struct(klass, smokeruby_mark, smokeruby_free, (void *) o); + return obj; +} + +static VALUE mapObject(VALUE self, VALUE obj); + +VALUE +cast_object_to(VALUE /*self*/, VALUE object, VALUE new_klass) +{ + smokeruby_object *o = value_obj_info(object); + + VALUE new_klassname = rb_funcall(new_klass, rb_intern("name"), 0); + + Smoke::Index * cast_to_id = classcache.find(StringValuePtr(new_klassname)); + if (cast_to_id == 0) { + rb_raise(rb_eArgError, "unable to find class \"%s\" to cast to\n", StringValuePtr(new_klassname)); + } + + smokeruby_object *o_cast = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + memcpy(o_cast, o, sizeof(smokeruby_object)); + + o_cast->allocated = o->allocated; + o->allocated = false; + + o_cast->classId = (int) *cast_to_id; + o_cast->ptr = o->smoke->cast(o->ptr, o->classId, o_cast->classId); + + VALUE obj = Data_Wrap_Struct(new_klass, smokeruby_mark, smokeruby_free, (void *) o_cast); + mapPointer(obj, o_cast, o_cast->classId, 0); + return obj; +} + +const char * +get_VALUEtype(VALUE ruby_value) +{ + char * classname = rb_obj_classname(ruby_value); + const char *r = ""; + if(ruby_value == Qnil) + r = "u"; + else if(TYPE(ruby_value) == T_FIXNUM || TYPE(ruby_value) == T_BIGNUM || qstrcmp(classname, "Qt::Integer") == 0) + r = "i"; + else if(TYPE(ruby_value) == T_FLOAT) + r = "n"; + else if(TYPE(ruby_value) == T_STRING) + r = "s"; + else if(ruby_value == Qtrue || ruby_value == Qfalse || qstrcmp(classname, "Qt::Boolean") == 0) + r = "B"; + else if(qstrcmp(classname, "Qt::Enum") == 0) { + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qenum_type"), 1, ruby_value); + r = StringValuePtr(temp); + } else if(TYPE(ruby_value) == T_DATA) { + smokeruby_object *o = value_obj_info(ruby_value); + if(!o) { + r = "a"; + } else { + r = o->smoke->classes[o->classId].className; + } + } + else { + r = "U"; + } + + return r; +} + +VALUE prettyPrintMethod(Smoke::Index id) +{ + VALUE r = rb_str_new2(""); + Smoke::Method &meth = qt_Smoke->methods[id]; + const char *tname = qt_Smoke->types[meth.ret].name; + if(meth.flags & Smoke::mf_static) rb_str_catf(r, "static "); + rb_str_catf(r, "%s ", (tname ? tname:"void")); + rb_str_catf(r, "%s::%s(", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]); + for(int i = 0; i < meth.numArgs; i++) { + if(i) rb_str_catf(r, ", "); + tname = qt_Smoke->types[qt_Smoke->argumentList[meth.args+i]].name; + rb_str_catf(r, "%s", (tname ? tname:"void")); + } + rb_str_catf(r, ")"); + if(meth.flags & Smoke::mf_const) rb_str_catf(r, " const"); + return r; +} + +//---------- Ruby methods (for all functions except fully qualified statics & enums) --------- + +// Used to display debugging info about the signals a Qt::Object has connected. +// Returns a Hash with keys of the signals names, and values of Arrays of +// Qt::Connections for the target slots +static VALUE +receivers_qobject(VALUE self) +{ + if (TYPE(self) != T_DATA) { + return Qnil; + } + + smokeruby_object * o = 0; + Data_Get_Struct(self, smokeruby_object, o); + UnencapsulatedQObject * qobject = (UnencapsulatedQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + VALUE result = rb_hash_new(); + QStrList signalNames = qobject->metaObject()->signalNames(true); + + for (int sig = 0; sig < qobject->metaObject()->numSignals(true); sig++) { + QConnectionList * clist = qobject->public_receivers(sig); + if (clist != 0) { + VALUE name = rb_str_new2(signalNames.at(sig)); + VALUE members = rb_ary_new(); + + for ( QConnection * connection = clist->first(); + connection != 0; + connection = clist->next() ) + { + VALUE obj = getPointerObject(connection); + if (obj == Qnil) { + smokeruby_object * c = ALLOC(smokeruby_object); + c->classId = o->smoke->idClass("QConnection"); + c->smoke = o->smoke; + c->ptr = connection; + c->allocated = false; + obj = set_obj_info("Qt::Connection", c); + } + + rb_ary_push(members, obj); + } + + rb_hash_aset(result, name, members); + } + } + + return result; +} + +// Takes a variable name and a QProperty with QVariant value, and returns a ' +// variable=value' pair with the value in ruby inspect style +static QCString +inspectProperty(Smoke * smoke, const QMetaProperty * property, const char * name, QVariant & value) +{ + if (property->isEnumType()) { + QMetaObject * metaObject = *(property->meta); + return QCString().sprintf( " %s=%s::%s", + name, + smoke->binding->className(smoke->idClass(metaObject->className())), + property->valueToKey(value.toInt()) ); + } + + switch (value.type()) { + case QVariant::String: + case QVariant::CString: + { + if (value.toString().isNull()) { + return QCString().sprintf(" %s=nil", name); + } else { + return QCString().sprintf( " %s=\"%s\"", + name, + value.toString().latin1() ); + } + } + + case QVariant::Bool: + { + QString rubyName; + QRegExp name_re("^(is|has)(.)(.*)"); + + if (name_re.search(name) != -1) { + rubyName = name_re.cap(2).lower() + name_re.cap(3) + "?"; + } else { + rubyName = name; + } + + return QCString().sprintf(" %s=%s", rubyName.latin1(), value.toString().latin1()); + } + + case QVariant::Color: + { + QColor c = value.toColor(); + return QCString().sprintf(" %s=#", name, c.name().latin1()); + } + + case QVariant::Cursor: + { + QCursor c = value.toCursor(); + return QCString().sprintf(" %s=#", name, c.shape()); + } + + case QVariant::Double: + { + return QCString().sprintf(" %s=%.4f", name, value.toDouble()); + } + + case QVariant::Font: + { + QFont f = value.toFont(); + return QCString().sprintf( " %s=#", + name, + f.family().latin1(), f.pointSize(), f.weight(), + f.italic() ? "true" : "false", f.bold() ? "true" : "false", + f.underline() ? "true" : "false", f.strikeOut() ? "true" : "false" ); + } + + case QVariant::Point: + { + QPoint p = value.toPoint(); + return QCString().sprintf( " %s=#", + name, + p.x(), p.y() ); + } + + case QVariant::Rect: + { + QRect r = value.toRect(); + return QCString().sprintf( " %s=#", + name, + r.left(), r.right(), r.top(), r.bottom() ); + } + + case QVariant::Size: + { + QSize s = value.toSize(); + return QCString().sprintf( " %s=#", + name, + s.width(), s.height() ); + } + + case QVariant::SizePolicy: + { + QSizePolicy s = value.toSizePolicy(); + return QCString().sprintf( " %s=#", + name, + s.horData(), s.verData() ); + } + + case QVariant::Brush: + case QVariant::ColorGroup: + case QVariant::Image: + case QVariant::Palette: + case QVariant::Pixmap: + case QVariant::Region: + { + return QCString().sprintf(" %s=#", name, value.typeName() + 1); + } + + default: + return QCString().sprintf( " %s=%s", + name, + (value.isNull() || value.toString().isNull()) ? "nil" : value.toString().latin1() ); + } +} + +// Retrieves the properties for a QObject and returns them as 'name=value' pairs +// in a ruby inspect string. For example: +// +// # +// +static VALUE +inspect_qobject(VALUE self) +{ + if (TYPE(self) != T_DATA) { + return Qnil; + } + + // Start with # from the original inspect() call + // Drop the closing '>' + VALUE inspect_str = rb_call_super(0, 0); + rb_str_resize(inspect_str, RSTRING(inspect_str)->len - 1); + + smokeruby_object * o = 0; + Data_Get_Struct(self, smokeruby_object, o); + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + + QCString value_list; + value_list.append(QCString().sprintf(" name=\"%s\"", qobject->name())); + + if (qobject->isWidgetType()) { + QWidget * w = (QWidget *) qobject; + value_list.append(QCString().sprintf( ", x=%d, y=%d, width=%d, height=%d", + w->x(), + w->y(), + w->width(), + w->height() ) ); + } + + value_list.append(">"); + rb_str_cat(inspect_str, value_list.data(), strlen(value_list.data())); + + return inspect_str; +} + +// Retrieves the properties for a QObject and pretty_prints them as 'name=value' pairs +// For example: +// +// # +// +static VALUE +pretty_print_qobject(VALUE self, VALUE pp) +{ + if (TYPE(self) != T_DATA) { + return Qnil; + } + + // Start with # + // Drop the closing '>' + VALUE inspect_str = rb_funcall(self, rb_intern("to_s"), 0, 0); + rb_str_resize(inspect_str, RSTRING(inspect_str)->len - 1); + rb_funcall(pp, rb_intern("text"), 1, inspect_str); + rb_funcall(pp, rb_intern("breakable"), 0); + + smokeruby_object * o = 0; + Data_Get_Struct(self, smokeruby_object, o); + UnencapsulatedQObject * qobject = (UnencapsulatedQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + QStrList names = qobject->metaObject()->propertyNames(true); + + QCString value_list; + + if (qobject->parent() != 0) { + QCString parentInspectString; + VALUE obj = getPointerObject(qobject->parent()); + if (obj != Qnil) { + VALUE parent_inspect_str = rb_funcall(obj, rb_intern("to_s"), 0, 0); + rb_str_resize(parent_inspect_str, RSTRING(parent_inspect_str)->len - 1); + parentInspectString = StringValuePtr(parent_inspect_str); + } else { + parentInspectString.sprintf("#<%s:0x0", qobject->parent()->className()); + } + + if (qobject->parent()->isWidgetType()) { + QWidget * w = (QWidget *) qobject->parent(); + value_list = QCString().sprintf( " parent=%s name=\"%s\", x=%d, y=%d, width=%d, height=%d>,\n", + parentInspectString.data(), + w->name(), + w->x(), + w->y(), + w->width(), + w->height() ); + } else { + value_list = QCString().sprintf( " parent=%s name=\"%s\">,\n", + parentInspectString.data(), + qobject->parent()->name() ); + } + + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + } + + if (qobject->children() != 0) { + value_list = QCString().sprintf(" children=Array (%d element(s)),\n", qobject->children()->count()); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + } + + value_list = QCString(" metaObject=#metaObject()->className())); + + if (qobject->metaObject()->superClass() != 0) { + value_list.append(QCString().sprintf(", superClass=#", qobject->metaObject()->superClass())); + } + + if (qobject->metaObject()->numSignals() > 0) { + value_list.append(QCString().sprintf(", signalNames=Array (%d element(s))", qobject->metaObject()->numSignals())); + } + + if (qobject->metaObject()->numSlots() > 0) { + value_list.append(QCString().sprintf(", slotNames=Array (%d element(s))", qobject->metaObject()->numSlots())); + } + + value_list.append(">,\n"); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + + int signalCount = 0; + for (int sig = 0; sig < qobject->metaObject()->numSignals(true); sig++) { + QConnectionList * clist = qobject->public_receivers(sig); + if (clist != 0) { + signalCount++; + } + } + + if (signalCount > 0) { + value_list = QCString().sprintf(" receivers=Hash (%d element(s)),\n", signalCount); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + } + + int index = 0; + const char * name = names.first(); + + if (name != 0) { + QVariant value = qobject->property(name); + const QMetaProperty * property = qobject->metaObject()->property(index, true); + value_list = " " + inspectProperty(o->smoke, property, name, value); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + index++; + + for ( name = names.next(); + name != 0; + name = names.next(), index++ ) + { + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(",\n")); + + value = qobject->property(name); + property = qobject->metaObject()->property(index, true); + value_list = " " + inspectProperty(o->smoke, property, name, value); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + } + } + + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(">")); + + return self; +} + +static VALUE +metaObject(VALUE self) +{ + VALUE metaObject = rb_funcall(qt_internal_module, rb_intern("getMetaObject"), 1, self); + return metaObject; +} + +static VALUE +qobject_metaobject(VALUE self) +{ + smokeruby_object * o = value_obj_info(self); + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + QMetaObject * meta = qobject->metaObject(); + VALUE obj = getPointerObject(meta); + if (obj != Qnil) { + return obj; + } + + smokeruby_object * m = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + m->smoke = o->smoke; + m->classId = m->smoke->idClass("QMetaObject"); + m->ptr = meta; + m->allocated = false; + obj = set_obj_info("Qt::MetaObject", m); + return obj; +} + +static VALUE +new_qvariant(int argc, VALUE * argv, VALUE self) +{ +static Smoke::Index new_qvariant_qlist = 0; +static Smoke::Index new_qvariant_qmap = 0; + + if (new_qvariant_qlist == 0) { + Smoke::Index nameId = qt_Smoke->idMethodName("QVariant?"); + Smoke::Index meth = qt_Smoke->findMethod(qt_Smoke->idClass("QVariant"), nameId); + Smoke::Index i = qt_Smoke->methodMaps[meth].method; + i = -i; // turn into ambiguousMethodList index + while (qt_Smoke->ambiguousMethodList[i] != 0) { + const char * argType = qt_Smoke->types[qt_Smoke->argumentList[qt_Smoke->methods[qt_Smoke->ambiguousMethodList[i]].args]].name; + + if (qstrcmp(argType, "const QValueList&" ) == 0) { + new_qvariant_qlist = qt_Smoke->ambiguousMethodList[i]; + } else if (qstrcmp(argType, "const QMap&" ) == 0) { + new_qvariant_qmap = qt_Smoke->ambiguousMethodList[i]; + } + + i++; + } + } + + if (argc == 1 && TYPE(argv[0]) == T_HASH) { + _current_method = new_qvariant_qmap; + MethodCall c(qt_Smoke, _current_method, self, argv, argc-1); + c.next(); + return *(c.var()); + } else if ( argc == 1 + && TYPE(argv[0]) == T_ARRAY + && RARRAY(argv[0])->len > 0 + && TYPE(rb_ary_entry(argv[0], 0)) != T_STRING ) + { + _current_method = new_qvariant_qlist; + MethodCall c(qt_Smoke, _current_method, self, argv, argc-1); + c.next(); + return *(c.var()); + } + + return rb_call_super(argc, argv); +} + +static QCString * +find_cached_selector(int argc, VALUE * argv, VALUE klass, char * methodName) +{ + // Look in the cache +static QCString * mcid = 0; + if (mcid == 0) { + mcid = new QCString(); + } + *mcid = rb_class2name(klass); + *mcid += ';'; + *mcid += methodName; + for(int i=3; iendsWith("?")) { + smokeruby_object *o = value_obj_info(self); + if(!o || !o->ptr) { + rb_call_super(argc, argv); + } + + // Drop the trailing '?' + pred->replace(pred->length() - 1, 1, ""); + + pred->replace(0, 1, pred->at(0).upper()); + pred->replace(0, 0, QString("is")); + Smoke::Index meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, pred->latin1()); + + if (meth == 0) { + pred->replace(0, 2, QString("has")); + meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, pred->latin1()); + } + + if (meth > 0) { + methodName = (char *) pred->latin1(); + } + } + + VALUE * temp_stack = (VALUE *) calloc(argc+3, sizeof(VALUE)); + temp_stack[0] = rb_str_new2("Qt"); + temp_stack[1] = rb_str_new2(methodName); + temp_stack[2] = klass; + temp_stack[3] = self; + for (int count = 1; count < argc; count++) { + temp_stack[count+3] = argv[count]; + } + + { + QCString * mcid = find_cached_selector(argc+3, temp_stack, klass, methodName); + + if (_current_method == -1) { + // Find the C++ method to call. Do that from Ruby for now + + VALUE retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack); + if (_current_method == -1) { + char * op = rb_id2name(SYM2ID(argv[0])); + if ( qstrcmp(op, "-") == 0 + || qstrcmp(op, "+") == 0 + || qstrcmp(op, "/") == 0 + || qstrcmp(op, "%") == 0 + || qstrcmp(op, "|") == 0 ) + { + // Look for operator methods of the form 'operator+=', 'operator-=' and so on.. + char op1[3]; + op1[0] = op[0]; + op1[1] = '='; + op1[2] = '\0'; + temp_stack[1] = rb_str_new2(op1); + retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack); + } + + if (_current_method == -1) { + free(temp_stack); + + // Check for property getter/setter calls + smokeruby_object *o = value_obj_info(self); + if ( o != 0 + && o->ptr != 0 + && isDerivedFrom(o->smoke, o->classId, o->smoke->idClass("QObject")) ) + { + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); +static QString * prop = 0; + if (prop == 0) { + prop = new QString(); + } + + *prop = rb_id2name(SYM2ID(argv[0])); + QMetaObject * meta = qobject->metaObject(); + if (argc == 1) { + if (prop->endsWith("?")) { + prop->replace(0, 1, pred->at(0).upper()); + prop->replace(0, 0, QString("is")); + if (meta->findProperty(prop->latin1(), true) == -1) { + prop->replace(0, 2, QString("has")); + } + } + + if (meta->findProperty(prop->latin1(), true) != -1) { + VALUE qvariant = rb_funcall(self, rb_intern("property"), 1, rb_str_new2(prop->latin1())); + return rb_funcall(qvariant, rb_intern("to_ruby"), 0); + } + } else if (argc == 2 && prop->endsWith("=")) { + prop->replace("=", ""); + if (meta->findProperty(prop->latin1(), true) != -1) { + VALUE qvariant = rb_funcall(qvariant_class, rb_intern("new"), 1, argv[1]); + return rb_funcall(self, rb_intern("setProperty"), 2, rb_str_new2(prop->latin1()), qvariant); + } + } + } + + rb_call_super(argc, argv); + } + } + // Success. Cache result. + methcache.insert((const char *)*mcid, new Smoke::Index(_current_method)); + } + } + + MethodCall c(qt_Smoke, _current_method, self, temp_stack+4, argc-1); + c.next(); + VALUE result = *(c.var()); + free(temp_stack); + + return result; +} + +static VALUE +class_method_missing(int argc, VALUE * argv, VALUE klass) +{ + VALUE result = Qnil; + char * methodName = rb_id2name(SYM2ID(argv[0])); + VALUE * temp_stack = (VALUE *) calloc(argc+3, sizeof(VALUE)); + temp_stack[0] = rb_str_new2("Qt"); + temp_stack[1] = rb_str_new2(methodName); + temp_stack[2] = klass; + temp_stack[3] = Qnil; + for (int count = 1; count < argc; count++) { + temp_stack[count+3] = argv[count]; + } + + { + QCString * mcid = find_cached_selector(argc+3, temp_stack, klass, methodName); + + if (_current_method == -1) { + VALUE retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack); + Q_UNUSED(retval); + if (_current_method != -1) { + // Success. Cache result. + methcache.insert((const char *)*mcid, new Smoke::Index(_current_method)); + } + } + } + + if (_current_method == -1) { +static QRegExp * rx = 0; + if (rx == 0) { + rx = new QRegExp("[a-zA-Z]+"); + } + + if (rx->search(methodName) == -1) { + // If an operator method hasn't been found as an instance method, + // then look for a class method - after 'op(self,a)' try 'self.op(a)' + VALUE * method_stack = (VALUE *) calloc(argc - 1, sizeof(VALUE)); + method_stack[0] = argv[0]; + for (int count = 1; count < argc - 1; count++) { + method_stack[count] = argv[count+1]; + } + result = method_missing(argc-1, method_stack, argv[1]); + free(method_stack); + free(temp_stack); + return result; + } else { + rb_call_super(argc, argv); + } + } + + MethodCall c(qt_Smoke, _current_method, Qnil, temp_stack+4, argc-1); + c.next(); + result = *(c.var()); + free(temp_stack); + return result; +} + +static VALUE module_method_missing(int argc, VALUE * argv, VALUE /*klass*/) +{ + return class_method_missing(argc, argv, qt_module); +} + +static VALUE kde_module_method_missing(int argc, VALUE * argv, VALUE klass) +{ + return class_method_missing(argc, argv, klass); +} + +/* + +class LCDRange < Qt::Widget + + def initialize(s, parent, name) + super(parent, name) + init() + ... + +For a case such as the above, the QWidget can't be instantiated until +the initializer has been run up to the point where 'super(parent, name)' +is called. Only then, can the number and type of arguments passed to the +constructor be known. However, the rest of the intializer +can't be run until 'self' is a proper T_DATA object with a wrapped C++ +instance. + +The solution is to run the initialize code twice. First, only up to the +'super(parent, name)' call, where the QWidget would get instantiated in +initialize_qt(). And then rb_throw() jumps out of the +initializer returning the wrapped object as a result. + +The second time round 'self' will be the wrapped instance of type T_DATA, +so initialize() can be allowed to proceed to the end. +*/ +static VALUE +initialize_qt(int argc, VALUE * argv, VALUE self) +{ + VALUE retval; + VALUE temp_obj; + + if (TYPE(self) == T_DATA) { + // If a ruby block was passed then run that now + if (rb_block_given_p()) { + rb_funcall(qt_internal_module, rb_intern("run_initializer_block"), 2, self, rb_block_proc()); + } + + return self; + } + + VALUE klass = rb_funcall(self, rb_intern("class"), 0); + VALUE constructor_name = rb_str_new2("new"); + + VALUE * temp_stack = (VALUE *) calloc(argc+4, sizeof(VALUE)); + temp_stack[0] = rb_str_new2("Qt"); + temp_stack[1] = constructor_name; + temp_stack[2] = klass; + temp_stack[3] = self; + for (int count = 0; count < argc; count++) { + temp_stack[count+4] = argv[count]; + } + + { + // Put this in a C block so that the mcid will be de-allocated at the end of the block, + // rather than on f'n exit, to avoid the longjmp problem described below + QCString * mcid = find_cached_selector(argc+4, temp_stack, klass, rb_class2name(klass)); + + if (_current_method == -1) { + retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+4, temp_stack); + if (_current_method != -1) { + // Success. Cache result. + methcache.insert((const char *)*mcid, new Smoke::Index(_current_method)); + } + } + } + + if (_current_method == -1) { + free(temp_stack); + // Another longjmp here.. + rb_raise(rb_eArgError, "unresolved constructor call %s\n", rb_class2name(klass)); + } + + { + // Allocate the MethodCall within a C block. Otherwise, because the continue_new_instance() + // call below will longjmp out, it wouldn't give C++ an opportunity to clean up + MethodCall c(qt_Smoke, _current_method, self, temp_stack+4, argc); + c.next(); + temp_obj = *(c.var()); + } + + smokeruby_object * p = 0; + Data_Get_Struct(temp_obj, smokeruby_object, p); + smokeruby_object * o = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + memcpy(o, p, sizeof(smokeruby_object)); + p->ptr = 0; + p->allocated = false; + o->allocated = true; + VALUE result = Data_Wrap_Struct(klass, smokeruby_mark, smokeruby_free, o); + mapObject(result, result); + free(temp_stack); + // Off with a longjmp, never to return.. + rb_throw("newqt", result); + /*NOTREACHED*/ + return self; +} + +VALUE +new_qt(int argc, VALUE * argv, VALUE klass) +{ + VALUE * temp_stack = (VALUE *) calloc(argc + 1, sizeof(VALUE)); + temp_stack[0] = rb_obj_alloc(klass); + for (int count = 0; count < argc; count++) { + temp_stack[count+1] = argv[count]; + } + + VALUE result = rb_funcall2(qt_internal_module, rb_intern("try_initialize"), argc+1, temp_stack); + rb_obj_call_init(result, argc, argv); + + free(temp_stack); + return result; +} + +static VALUE +new_qapplication(int argc, VALUE * argv, VALUE klass) +{ + VALUE result = Qnil; + + if (argc == 1 && TYPE(argv[0]) == T_ARRAY) { + // Convert '(ARGV)' to '(NUM, [$0]+ARGV)' + VALUE * local_argv = (VALUE *) calloc(argc + 1, sizeof(VALUE)); + VALUE temp = rb_ary_dup(argv[0]); + rb_ary_unshift(temp, rb_gv_get("$0")); + local_argv[0] = INT2NUM(RARRAY(temp)->len); + local_argv[1] = temp; + result = new_qt(2, local_argv, klass); + free(local_argv); + } else { + result = new_qt(argc, argv, klass); + } + + rb_gv_set("$qApp", result); + return result; +} + +// Returns $qApp.ARGV() - the original ARGV array with Qt command line options removed +static VALUE +qapplication_argv(VALUE /*self*/) +{ + VALUE result = rb_ary_new(); + // Drop argv[0], as it isn't included in the ruby global ARGV + for (int index = 1; index < qApp->argc(); index++) { + rb_ary_push(result, rb_str_new2(qApp->argv()[index])); + } + + return result; +} + +//----------------- Sig/Slot ------------------ + + +VALUE +getmetainfo(VALUE self, int &offset, int &index) +{ + char * signalname = rb_id2name(rb_frame_last_func()); + VALUE metaObject_value = rb_funcall(qt_internal_module, rb_intern("getMetaObject"), 1, self); + + smokeruby_object *ometa = value_obj_info(metaObject_value); + if(!ometa) return 0; + QMetaObject *metaobject = (QMetaObject*)ometa->ptr; + + offset = metaobject->signalOffset(); + + VALUE signalInfo = rb_funcall(qt_internal_module, rb_intern("signalInfo"), 2, self, rb_str_new2(signalname)); + VALUE member = rb_ary_entry(signalInfo, 0); + index = NUM2INT(rb_ary_entry(signalInfo, 1)); + return rb_funcall(qt_internal_module, rb_intern("getMocArguments"), 1, member); +} + +VALUE +getslotinfo(VALUE self, int id, char *&slotname, int &index, bool isSignal = false) +{ + VALUE member; + + VALUE metaObject_value = rb_funcall(qt_internal_module, rb_intern("getMetaObject"), 1, self); + smokeruby_object *ometa = value_obj_info(metaObject_value); + if(!ometa) return Qnil; + + QMetaObject *metaobject = (QMetaObject*)ometa->ptr; + + int offset = isSignal ? metaobject->signalOffset() : metaobject->slotOffset(); + + index = id - offset; // where we at + if(index < 0) return Qnil; + + if (isSignal) { + member = rb_funcall(qt_internal_module, rb_intern("signalAt"), 2, self, INT2NUM(index)); + } else { + member = rb_funcall(qt_internal_module, rb_intern("slotAt"), 2, self, INT2NUM(index)); + } + + VALUE mocArgs = rb_funcall(qt_internal_module, rb_intern("getMocArguments"), 1, member); + slotname = StringValuePtr(member); + + return mocArgs; +} + +static VALUE +qt_signal(int argc, VALUE * argv, VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + QObject *qobj = (QObject*)o->smoke->cast( + o->ptr, + o->classId, + o->smoke->idClass("QObject") + ); + if(qobj->signalsBlocked()) return Qfalse; + + int offset; + int index; + + VALUE args = getmetainfo(self, offset, index); + + if(args == Qnil) return Qfalse; + + // Okay, we have the signal info. *whew* + EmitSignal signal(qobj, offset + index, argc, args, argv); + signal.next(); + + return Qtrue; +} + +static VALUE +qt_invoke(int /*argc*/, VALUE * argv, VALUE self) +{ + // Arguments: int id, QUObject *o + int id = NUM2INT(argv[0]); + QUObject *_o = 0; + + Data_Get_Struct(rb_ary_entry(argv[1], 0), QUObject, _o); + if(_o == 0) { + rb_raise(rb_eRuntimeError, "Cannot create QUObject\n"); + } + + smokeruby_object *o = value_obj_info(self); + (void) (QObject*)o->smoke->cast( + o->ptr, + o->classId, + o->smoke->idClass("QObject") + ); + + // Now, I need to find out if this means me + int index; + char *slotname; + bool isSignal = qstrcmp(rb_id2name(rb_frame_last_func()), "qt_emit") == 0; + VALUE mocArgs = getslotinfo(self, id, slotname, index, isSignal); + if(mocArgs == Qnil) { + // No ruby slot/signal found, assume the target is a C++ one + Smoke::Index nameId = o->smoke->idMethodName(isSignal ? "qt_emit$?" : "qt_invoke$?"); + Smoke::Index meth = o->smoke->findMethod(o->classId, nameId); + if(meth > 0) { + Smoke::Method &m = o->smoke->methods[o->smoke->methodMaps[meth].method]; + Smoke::ClassFn fn = o->smoke->classes[m.classId].classFn; + Smoke::StackItem i[3]; + i[1].s_int = id; + i[2].s_class = _o; + (*fn)(m.method, o->ptr, i); + return i[0].s_bool == 1 ? Qtrue : Qfalse; + } + + // Should never happen.. + rb_raise(rb_eRuntimeError, "Cannot find %s::qt_invoke() method\n", + o->smoke->classes[o->classId].className ); + } + + QString name(slotname); +static QRegExp * rx = 0; + if (rx == 0) { + rx = new QRegExp("\\(.*"); + } + name.replace(*rx, ""); + + InvokeSlot slot(self, rb_intern(name.latin1()), mocArgs, _o); + slot.next(); + + return Qtrue; +} + +static VALUE +qobject_connect(int argc, VALUE * argv, VALUE self) +{ + if (rb_block_given_p()) { + if (argc == 1) { + return rb_funcall(qt_internal_module, rb_intern("signal_connect"), 3, self, argv[0], rb_block_proc()); + } else if (argc == 2) { + return rb_funcall(qt_internal_module, rb_intern("connect"), 4, argv[0], argv[1], self, rb_block_proc()); + } else if (argc == 3) { + return rb_funcall(qt_internal_module, rb_intern("connect"), 4, argv[0], argv[1], argv[2], rb_block_proc()); + } else { + rb_raise(rb_eArgError, "Invalid argument list"); + } + } else { + return rb_call_super(argc, argv); + } +} + +// --------------- Ruby C functions for Qt::_internal.* helpers ---------------- + + +static VALUE +getMethStat(VALUE /*self*/) +{ + VALUE result_list = rb_ary_new(); + rb_ary_push(result_list, INT2NUM((int)methcache.size())); + rb_ary_push(result_list, INT2NUM((int)methcache.count())); + return result_list; +} + +static VALUE +getClassStat(VALUE /*self*/) +{ + VALUE result_list = rb_ary_new(); + rb_ary_push(result_list, INT2NUM((int)classcache.size())); + rb_ary_push(result_list, INT2NUM((int)classcache.count())); + return result_list; +} + +static VALUE +getIsa(VALUE /*self*/, VALUE classId) +{ + VALUE parents_list = rb_ary_new(); + + Smoke::Index *parents = + qt_Smoke->inheritanceList + + qt_Smoke->classes[NUM2INT(classId)].parents; + + while(*parents) { + //qWarning("\tparent: %s", qt_Smoke->classes[*parents].className); + rb_ary_push(parents_list, rb_str_new2(qt_Smoke->classes[*parents++].className)); + } + return parents_list; +} + +// Return the class name of a QObject. Note that the name will be in the +// form of Qt::Widget rather than QWidget. Is this a bug or a feature? +static VALUE +class_name(VALUE self) +{ + VALUE klass = rb_funcall(self, rb_intern("class"), 0); + return rb_funcall(klass, rb_intern("name"), 0); +} + +// Allow classnames in both 'Qt::Widget' and 'QWidget' formats to be +// used as an argument to Qt::Object.inherits() +static VALUE +inherits_qobject(int argc, VALUE * argv, VALUE /*self*/) +{ + if (argc != 1) { + return rb_call_super(argc, argv); + } + + Smoke::Index * classId = classcache.find(StringValuePtr(argv[0])); + + if (classId == 0) { + return rb_call_super(argc, argv); + } else { + VALUE super_class = rb_str_new2(qt_Smoke->classes[*classId].className); + return rb_call_super(argc, &super_class); + } +} + +static VALUE +qbytearray_data(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if (o == 0 || o->ptr == 0) { + return Qnil; + } + QByteArray * dataArray = (QByteArray*) o->ptr; + return rb_str_new(dataArray->data(), (long) dataArray->size()); +} + +static VALUE +qbytearray_size(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if (o == 0 || o->ptr == 0) { + return Qnil; + } + QByteArray * dataArray = (QByteArray*) o->ptr; + return UINT2NUM(dataArray->size()); +} + +static VALUE +qbytearray_setRawData(VALUE self, VALUE data) +{ + smokeruby_object *o = value_obj_info(self); + if (o == 0 || o->ptr == 0) { + return Qnil; + } + QByteArray * dataArray = (QByteArray*) o->ptr; + dataArray->setRawData(StringValuePtr(data), RSTRING(data)->len); + return self; +} + +static void +mocargs_free(void * ptr) +{ + MocArgument * mocArgs = (MocArgument *) ptr; + delete[] mocArgs; + return; +} + +static VALUE +allocateMocArguments(VALUE /*self*/, VALUE count_value) +{ + int count = NUM2INT(count_value); + MocArgument * ptr = new MocArgument[count + 1]; + return Data_Wrap_Struct(rb_cObject, 0, mocargs_free, ptr); +} + +static VALUE +setMocType(VALUE /*self*/, VALUE ptr, VALUE idx_value, VALUE name_value, VALUE static_type_value) +{ + int idx = NUM2INT(idx_value); + char *name = StringValuePtr(name_value); + char *static_type = StringValuePtr(static_type_value); + Smoke::Index typeId = qt_Smoke->idType(name); + if(!typeId) return Qfalse; + MocArgument *arg = 0; + Data_Get_Struct(ptr, MocArgument, arg); + arg[idx].st.set(qt_Smoke, typeId); + if(qstrcmp(static_type, "ptr") == 0) + arg[idx].argType = xmoc_ptr; + else if(qstrcmp(static_type, "bool") == 0) + arg[idx].argType = xmoc_bool; + else if(qstrcmp(static_type, "int") == 0) + arg[idx].argType = xmoc_int; + else if(qstrcmp(static_type, "double") == 0) + arg[idx].argType = xmoc_double; + else if(qstrcmp(static_type, "char*") == 0) + arg[idx].argType = xmoc_charstar; + else if(qstrcmp(static_type, "QString") == 0) + arg[idx].argType = xmoc_QString; + return Qtrue; +} + +static VALUE +setDebug(VALUE self, VALUE on_value) +{ + int on = NUM2INT(on_value); + do_debug = on; + return self; +} + +static VALUE +debugging(VALUE /*self*/) +{ + return INT2NUM(do_debug); +} + +static VALUE +getTypeNameOfArg(VALUE /*self*/, VALUE method_value, VALUE idx_value) +{ + int method = NUM2INT(method_value); + int idx = NUM2INT(idx_value); + Smoke::Method &m = qt_Smoke->methods[method]; + Smoke::Index *args = qt_Smoke->argumentList + m.args; + return rb_str_new2((char*)qt_Smoke->types[args[idx]].name); +} + +static VALUE +classIsa(VALUE /*self*/, VALUE className_value, VALUE base_value) +{ + char *className = StringValuePtr(className_value); + char *base = StringValuePtr(base_value); + return isDerivedFromByName(qt_Smoke, className, base) ? Qtrue : Qfalse; +} + +static VALUE +isEnum(VALUE /*self*/, VALUE enumName_value) +{ + char *enumName = StringValuePtr(enumName_value); + Smoke::Index typeId = qt_Smoke->idType(enumName); + return typeId > 0 + && ( (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_enum + || (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_ulong + || (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_long + || (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_uint + || (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_int ) ? Qtrue : Qfalse; +} + +static VALUE +insert_pclassid(VALUE self, VALUE p_value, VALUE ix_value) +{ + char *p = StringValuePtr(p_value); + int ix = NUM2INT(ix_value); + classcache.insert(p, new Smoke::Index((Smoke::Index)ix)); + classname.insert(ix, strdup(p)); + return self; +} + +static VALUE +find_pclassid(VALUE /*self*/, VALUE p_value) +{ + char *p = StringValuePtr(p_value); + Smoke::Index *r = classcache.find(p); + if(r) + return INT2NUM((int)*r); + else + return INT2NUM(0); +} + +static VALUE +insert_mcid(VALUE self, VALUE mcid_value, VALUE ix_value) +{ + char *mcid = StringValuePtr(mcid_value); + int ix = NUM2INT(ix_value); + methcache.insert(mcid, new Smoke::Index((Smoke::Index)ix)); + return self; +} + +static VALUE +find_mcid(VALUE /*self*/, VALUE mcid_value) +{ + char *mcid = StringValuePtr(mcid_value); + Smoke::Index *r = methcache.find(mcid); + if(r) + return INT2NUM((int)*r); + else + return INT2NUM(0); +} + +static VALUE +getVALUEtype(VALUE /*self*/, VALUE ruby_value) +{ + return rb_str_new2(get_VALUEtype(ruby_value)); +} + +static VALUE +make_QUParameter(VALUE /*self*/, VALUE name_value, VALUE type_value, VALUE /*extra*/, VALUE inout_value) +{ + char *name = StringValuePtr(name_value); + char *type = StringValuePtr(type_value); + int inout = NUM2INT(inout_value); + QUParameter *p = new QUParameter; + p->name = new char[strlen(name) + 1]; + strcpy((char*)p->name, name); + if(qstrcmp(type, "bool") == 0) + p->type = &static_QUType_bool; + else if(qstrcmp(type, "int") == 0) + p->type = &static_QUType_int; + else if(qstrcmp(type, "double") == 0) + p->type = &static_QUType_double; + else if(qstrcmp(type, "char*") == 0 || qstrcmp(type, "const char*") == 0) + p->type = &static_QUType_charstar; + else if(qstrcmp(type, "QString") == 0 || qstrcmp(type, "QString&") == 0 || + qstrcmp(type, "const QString") == 0 || qstrcmp(type, "const QString&") == 0) + p->type = &static_QUType_QString; + else + p->type = &static_QUType_ptr; + // Lacking support for several types. Evil. + p->inOut = inout; + p->typeExtra = 0; + return Data_Wrap_Struct(rb_cObject, 0, 0, p); +} + +static VALUE +make_QMetaData(VALUE /*self*/, VALUE name_value, VALUE method) +{ + char *name = StringValuePtr(name_value); + QMetaData *m = new QMetaData; // will be deleted + m->name = new char[strlen(name) + 1]; + strcpy((char*)m->name, name); + Data_Get_Struct(method, QUMethod, m->method); + m->access = QMetaData::Public; + return Data_Wrap_Struct(rb_cObject, 0, 0, m); +} + +static VALUE +make_QUMethod(VALUE /*self*/, VALUE name_value, VALUE params) +{ + char *name = StringValuePtr(name_value); + QUMethod *m = new QUMethod; // permanent memory allocation + m->name = new char[strlen(name) + 1]; // this too + strcpy((char*)m->name, name); + m->parameters = 0; + m->count = RARRAY(params)->len; + + if (m->count > 0) { + m->parameters = new QUParameter[m->count]; + for (long i = 0; i < m->count; i++) { + VALUE param = rb_ary_entry(params, i); + QUParameter *p = 0; + Data_Get_Struct(param, QUParameter, p); + ((QUParameter *) m->parameters)[i] = *p; + delete p; + } + } + return Data_Wrap_Struct(rb_cObject, 0, 0, m); +} + +static VALUE +make_QMetaData_tbl(VALUE /*self*/, VALUE list) +{ + long count = RARRAY(list)->len; + QMetaData *m = new QMetaData[count]; + + for (long i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + + QMetaData *old = 0; + Data_Get_Struct(item, QMetaData, old); + m[i] = *old; + delete old; + } + + return Data_Wrap_Struct(rb_cObject, 0, 0, m); +} + +static VALUE +make_metaObject(VALUE /*self*/, VALUE className_value, VALUE parent, VALUE slot_tbl_value, VALUE slot_count_value, VALUE signal_tbl_value, VALUE signal_count_value) +{ + char *className = strdup(StringValuePtr(className_value)); + + QMetaData * slot_tbl = 0; + int slot_count = 0; + if (slot_tbl_value != Qnil) { + Data_Get_Struct(slot_tbl_value, QMetaData, slot_tbl); + slot_count = NUM2INT(slot_count_value); + } + + QMetaData * signal_tbl = 0; + int signal_count = 0; + if (signal_tbl_value != Qnil) { + Data_Get_Struct(signal_tbl_value, QMetaData, signal_tbl); + signal_count = NUM2INT(signal_count_value); + } + + smokeruby_object *po = value_obj_info(parent); + if(!po || !po->ptr) { + rb_raise(rb_eRuntimeError, "Cannot create metaObject\n"); + } + + QMetaObject *meta = QMetaObject::new_metaobject( + className, (QMetaObject*)po->ptr, + (const QMetaData*)slot_tbl, slot_count, // slots + (const QMetaData*)signal_tbl, signal_count, // signals + 0, 0, // properties + 0, 0, // enums + 0, 0); + + smokeruby_object * o = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + o->smoke = qt_Smoke; + o->classId = qt_Smoke->idClass("QMetaObject"); + o->ptr = meta; + o->allocated = true; + + return Data_Wrap_Struct(qmetaobject_class, smokeruby_mark, smokeruby_free, o); +} + +static VALUE +add_metaobject_methods(VALUE self, VALUE klass) +{ + rb_define_method(klass, "qt_invoke", (VALUE (*) (...)) qt_invoke, -1); + rb_define_method(klass, "qt_emit", (VALUE (*) (...)) qt_invoke, -1); + rb_define_method(klass, "metaObject", (VALUE (*) (...)) metaObject, 0); + return self; +} + +static VALUE +add_signal_methods(VALUE self, VALUE klass, VALUE signalNames) +{ + for (long index = 0; index < RARRAY(signalNames)->len; index++) { + VALUE signal = rb_ary_entry(signalNames, index); + rb_define_method(klass, StringValuePtr(signal), (VALUE (*) (...)) qt_signal, -1); + } + return self; +} + +static VALUE +dispose(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if(!o || !o->ptr) { return Qnil; } + + const char *className = o->smoke->classes[o->classId].className; + if(do_debug & qtdb_gc) printf("Deleting (%s*)%p\n", className, o->ptr); + + unmapPointer(o, o->classId, 0); + object_count--; + + char *methodName = new char[strlen(className) + 2]; + methodName[0] = '~'; + strcpy(methodName + 1, className); + Smoke::Index nameId = o->smoke->idMethodName(methodName); + Smoke::Index meth = o->smoke->findMethod(o->classId, nameId); + if(meth > 0) { + Smoke::Method &m = o->smoke->methods[o->smoke->methodMaps[meth].method]; + Smoke::ClassFn fn = o->smoke->classes[m.classId].classFn; + Smoke::StackItem i[1]; + (*fn)(m.method, o->ptr, i); + } + delete[] methodName; + o->ptr = 0; + o->allocated = false; + + return self; +} + +static VALUE +is_disposed(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if(!o || !o->ptr) { return Qtrue; } + return Qfalse; +} + +static VALUE +mapObject(VALUE self, VALUE obj) +{ + smokeruby_object *o = value_obj_info(obj); + if(!o) + return Qnil; + mapPointer(obj, o, o->classId, 0); + return self; +} + +static VALUE +isaQObject(VALUE /*self*/, VALUE classid) +{ + int classid_value = NUM2INT(classid); + return isQObject(qt_Smoke, classid_value) ? Qtrue : Qfalse; +} + +// Returns the Smoke classId of a ruby instance +static VALUE +idInstance(VALUE /*self*/, VALUE instance) +{ + smokeruby_object *o = value_obj_info(instance); + if(!o) + return Qnil; + + return INT2NUM(o->classId); +} + +static VALUE +idClass(VALUE /*self*/, VALUE name_value) +{ + char *name = StringValuePtr(name_value); + return INT2NUM(qt_Smoke->idClass(name)); +} + +static VALUE +idMethodName(VALUE /*self*/, VALUE name_value) +{ + char *name = StringValuePtr(name_value); + return INT2NUM(qt_Smoke->idMethodName(name)); +} + +static VALUE +idMethod(VALUE /*self*/, VALUE idclass_value, VALUE idmethodname_value) +{ + int idclass = NUM2INT(idclass_value); + int idmethodname = NUM2INT(idmethodname_value); + return INT2NUM(qt_Smoke->idMethod(idclass, idmethodname)); +} + +static VALUE +findMethod(VALUE /*self*/, VALUE c_value, VALUE name_value) +{ + char *c = StringValuePtr(c_value); + char *name = StringValuePtr(name_value); + VALUE result = rb_ary_new(); + Smoke::Index meth = qt_Smoke->findMethod(c, name); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("DAMNIT on %s::%s => %d", c, name, meth); +#endif + if(!meth) { + meth = qt_Smoke->findMethod("QGlobalSpace", name); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("DAMNIT on QGlobalSpace::%s => %d", name, meth); +#endif + } + + if(!meth) { + return result; + // empty list + } else if(meth > 0) { + Smoke::Index i = qt_Smoke->methodMaps[meth].method; + if(!i) { // shouldn't happen + rb_raise(rb_eArgError, "Corrupt method %s::%s", c, name); + } else if(i > 0) { // single match + Smoke::Method &methodRef = qt_Smoke->methods[i]; + if ((methodRef.flags & Smoke::mf_internal) == 0) { + rb_ary_push(result, INT2NUM(i)); + } + } else { // multiple match + i = -i; // turn into ambiguousMethodList index + while(qt_Smoke->ambiguousMethodList[i]) { + Smoke::Method &methodRef = qt_Smoke->methods[qt_Smoke->ambiguousMethodList[i]]; + if ((methodRef.flags & Smoke::mf_internal) == 0) { + rb_ary_push(result, INT2NUM(qt_Smoke->ambiguousMethodList[i])); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("Ambiguous Method %s::%s => %d", c, name, qt_Smoke->ambiguousMethodList[i]); +#endif + + } + i++; + } + } + } + return result; +} + +// findAllMethods(classid [, startingWith]) : returns { "mungedName" => [index in methods, ...], ... } + +static VALUE +findAllMethods(int argc, VALUE * argv, VALUE /*self*/) +{ + VALUE classid = argv[0]; + VALUE result = rb_hash_new(); + if(classid != Qnil) { + Smoke::Index c = (Smoke::Index) NUM2INT(classid); + if (c > qt_Smoke->numClasses) { + return Qnil; + } + char * pat = 0L; + if(argc > 1 && TYPE(argv[1]) == T_STRING) + pat = StringValuePtr(argv[1]); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("findAllMethods called with classid = %d, pat == %s", c, pat); +#endif + Smoke::Index imax = qt_Smoke->numMethodMaps; + Smoke::Index imin = 0, icur = -1, methmin, methmax; + methmin = -1; methmax = -1; // kill warnings + int icmp = -1; + while(imax >= imin) { + icur = (imin + imax) / 2; + icmp = qt_Smoke->leg(qt_Smoke->methodMaps[icur].classId, c); + if(!icmp) { + Smoke::Index pos = icur; + while(icur && qt_Smoke->methodMaps[icur-1].classId == c) + icur --; + methmin = icur; + icur = pos; + while(icur < imax && qt_Smoke->methodMaps[icur+1].classId == c) + icur ++; + methmax = icur; + break; + } + if (icmp > 0) + imax = icur - 1; + else + imin = icur + 1; + } + if(!icmp) { + for(Smoke::Index i=methmin ; i <= methmax ; i++) { + Smoke::Index m = qt_Smoke->methodMaps[i].name; + if(!pat || !qstrncmp(qt_Smoke->methodNames[m], pat, strlen(pat))) { + Smoke::Index ix= qt_Smoke->methodMaps[i].method; + VALUE meths = rb_ary_new(); + if(ix >= 0) { // single match + Smoke::Method &methodRef = qt_Smoke->methods[ix]; + if ((methodRef.flags & Smoke::mf_internal) == 0) { + rb_ary_push(meths, INT2NUM((int)ix)); + } + } else { // multiple match + ix = -ix; // turn into ambiguousMethodList index + while(qt_Smoke->ambiguousMethodList[ix]) { + Smoke::Method &methodRef = qt_Smoke->methods[qt_Smoke->ambiguousMethodList[ix]]; + if ((methodRef.flags & Smoke::mf_internal) == 0) { + rb_ary_push(meths, INT2NUM((int)qt_Smoke->ambiguousMethodList[ix])); + } + ix++; + } + } + rb_hash_aset(result, rb_str_new2(qt_Smoke->methodNames[m]), meths); + } + } + } + } + return result; +} + +/* + Flags values + 0 All methods, except enum values and protected non-static methods + mf_static Static methods only + mf_enum Enums only + mf_protected Protected non-static methods only +*/ + +#define PUSH_QTRUBY_METHOD \ + if ( (methodRef.flags & (Smoke::mf_internal|Smoke::mf_ctor|Smoke::mf_dtor)) == 0 \ + && qstrcmp(qt_Smoke->methodNames[methodRef.name], "operator=") != 0 \ + && qstrcmp(qt_Smoke->methodNames[methodRef.name], "operator!=") != 0 \ + && qstrcmp(qt_Smoke->methodNames[methodRef.name], "operator--") != 0 \ + && qstrcmp(qt_Smoke->methodNames[methodRef.name], "operator++") != 0 \ + && qstrncmp(qt_Smoke->methodNames[methodRef.name], "operator ", strlen("operator ")) != 0 \ + && ( (flags == 0 && (methodRef.flags & (Smoke::mf_static|Smoke::mf_enum|Smoke::mf_protected)) == 0) \ + || ( flags == Smoke::mf_static \ + && (methodRef.flags & Smoke::mf_enum) == 0 \ + && (methodRef.flags & Smoke::mf_static) == Smoke::mf_static ) \ + || (flags == Smoke::mf_enum && (methodRef.flags & Smoke::mf_enum) == Smoke::mf_enum) \ + || ( flags == Smoke::mf_protected \ + && (methodRef.flags & Smoke::mf_static) == 0 \ + && (methodRef.flags & Smoke::mf_protected) == Smoke::mf_protected ) ) ) { \ + if (qstrncmp(qt_Smoke->methodNames[methodRef.name], "operator", strlen("operator")) == 0) { \ + if (op_re.search(qt_Smoke->methodNames[methodRef.name]) != -1) { \ + rb_ary_push(result, rb_str_new2(op_re.cap(1) + op_re.cap(2))); \ + } else { \ + rb_ary_push(result, rb_str_new2(qt_Smoke->methodNames[methodRef.name] + strlen("operator"))); \ + } \ + } else if (predicate_re.search(qt_Smoke->methodNames[methodRef.name]) != -1 && methodRef.numArgs == 0) { \ + rb_ary_push(result, rb_str_new2(predicate_re.cap(2).lower() + predicate_re.cap(3) + "?")); \ + } else if (set_re.search(qt_Smoke->methodNames[methodRef.name]) != -1 && methodRef.numArgs == 1) { \ + rb_ary_push(result, rb_str_new2(set_re.cap(2).lower() + set_re.cap(3) + "=")); \ + } else { \ + rb_ary_push(result, rb_str_new2(qt_Smoke->methodNames[methodRef.name])); \ + } \ + } + +static VALUE +findAllMethodNames(VALUE /*self*/, VALUE result, VALUE classid, VALUE flags_value) +{ + QRegExp predicate_re("^(is|has)(.)(.*)"); + QRegExp set_re("^(set)([A-Z])(.*)"); + QRegExp op_re("operator(.*)(([-%~/+|&*])|(>>)|(<<)|(&&)|(\\|\\|)|(\\*\\*))=$"); + + unsigned short flags = (unsigned short) NUM2UINT(flags_value); + if (classid != Qnil) { + Smoke::Index c = (Smoke::Index) NUM2INT(classid); + if (c > qt_Smoke->numClasses) { + return Qnil; + } +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("findAllMethodNames called with classid = %d", c); +#endif + Smoke::Index imax = qt_Smoke->numMethodMaps; + Smoke::Index imin = 0, icur = -1, methmin, methmax; + methmin = -1; methmax = -1; // kill warnings + int icmp = -1; + + while (imax >= imin) { + icur = (imin + imax) / 2; + icmp = qt_Smoke->leg(qt_Smoke->methodMaps[icur].classId, c); + if (icmp == 0) { + Smoke::Index pos = icur; + while(icur && qt_Smoke->methodMaps[icur-1].classId == c) + icur --; + methmin = icur; + icur = pos; + while(icur < imax && qt_Smoke->methodMaps[icur+1].classId == c) + icur ++; + methmax = icur; + break; + } + if (icmp > 0) + imax = icur - 1; + else + imin = icur + 1; + } + + if (icmp == 0) { + for (Smoke::Index i=methmin ; i <= methmax ; i++) { + Smoke::Index ix= qt_Smoke->methodMaps[i].method; + if (ix >= 0) { // single match + Smoke::Method &methodRef = qt_Smoke->methods[ix]; + PUSH_QTRUBY_METHOD + } else { // multiple match + ix = -ix; // turn into ambiguousMethodList index + while (qt_Smoke->ambiguousMethodList[ix]) { + Smoke::Method &methodRef = qt_Smoke->methods[qt_Smoke->ambiguousMethodList[ix]]; + PUSH_QTRUBY_METHOD + ix++; + } + } + } + } + } + return result; +} + +static VALUE +dumpCandidates(VALUE /*self*/, VALUE rmeths) +{ + VALUE errmsg = rb_str_new2(""); + if(rmeths != Qnil) { + int count = RARRAY(rmeths)->len; + for(int i = 0; i < count; i++) { + rb_str_catf(errmsg, "\t"); + int id = NUM2INT(rb_ary_entry(rmeths, i)); + Smoke::Method &meth = qt_Smoke->methods[id]; + const char *tname = qt_Smoke->types[meth.ret].name; + if(meth.flags & Smoke::mf_enum) { + rb_str_catf(errmsg, "enum "); + rb_str_catf(errmsg, "%s::%s", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]); + rb_str_catf(errmsg, "\n"); + } else { + if(meth.flags & Smoke::mf_static) rb_str_catf(errmsg, "static "); + rb_str_catf(errmsg, "%s ", (tname ? tname:"void")); + rb_str_catf(errmsg, "%s::%s(", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]); + for(int i = 0; i < meth.numArgs; i++) { + if(i) rb_str_catf(errmsg, ", "); + tname = qt_Smoke->types[qt_Smoke->argumentList[meth.args+i]].name; + rb_str_catf(errmsg, "%s", (tname ? tname:"void")); + } + rb_str_catf(errmsg, ")"); + if(meth.flags & Smoke::mf_const) rb_str_catf(errmsg, " const"); + rb_str_catf(errmsg, "\n"); + } + } + } + return errmsg; +} + +static VALUE +isObject(VALUE /*self*/, VALUE obj) +{ + void * ptr = 0; + ptr = value_to_ptr(obj); + return (ptr > 0 ? Qtrue : Qfalse); +} + +static VALUE +setCurrentMethod(VALUE self, VALUE meth_value) +{ + int meth = NUM2INT(meth_value); + // FIXME: damn, this is lame, and it doesn't handle ambiguous methods + _current_method = meth; //qt_Smoke->methodMaps[meth].method; + return self; +} + +static VALUE +getClassList(VALUE /*self*/) +{ + VALUE class_list = rb_ary_new(); + + for(int i = 1; i <= qt_Smoke->numClasses; i++) { + rb_ary_push(class_list, rb_str_new2(qt_Smoke->classes[i].className)); + } + + return class_list; +} + +static VALUE +kde_package_to_class(const char * package, VALUE base_class) +{ + VALUE klass = Qnil; + QString packageName(package); +static QRegExp * scope_op = 0; + if (scope_op == 0) { + scope_op = new QRegExp("^([^:]+)::([^:]+)$"); + } + + if (packageName.startsWith("KDE::ConfigSkeleton::ItemEnum::")) { + klass = rb_define_class_under(kconfigskeleton_itemenum_class, package+strlen("KDE::ConfigSkeleton::EnumItem::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + kconfigskeleton_itemenum_choice_class = klass; + } else if (packageName.startsWith("KDE::ConfigSkeleton::")) { + klass = rb_define_class_under(kconfigskeleton_class, package+strlen("KDE::ConfigSkeleton::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + rb_define_method(klass, "immutable?", (VALUE (*) (...)) _kconfigskeletonitem_immutable, 0); + rb_define_method(klass, "isImmutable", (VALUE (*) (...)) _kconfigskeletonitem_immutable, 0); + } else if (packageName.startsWith("KDE::Win::")) { + klass = rb_define_class_under(kwin_class, package+strlen("KDE::Win::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("KDE::")) { + klass = rb_define_class_under(kde_module, package+strlen("KDE::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("KParts::")) { + klass = rb_define_class_under(kparts_module, package+strlen("KParts::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + if (packageName == "KParts::ReadOnlyPart") { + konsole_part_class = rb_define_class_under(kde_module, "KonsolePart", klass); + } + } else if (packageName.startsWith("KNS::")) { + klass = rb_define_class_under(kns_module, package+strlen("KNS::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("KIO::")) { + klass = rb_define_class_under(kio_module, package+strlen("KIO::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + if (packageName == "KIO::UDSAtom") { + kio_udsatom_class = klass; + } + } else if (packageName.startsWith("DOM::")) { + klass = rb_define_class_under(dom_module, package+strlen("DOM::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("Kontact::")) { + klass = rb_define_class_under(kontact_module, package+strlen("Kontact::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("Ko") && scope_op->search(packageName) == -1) { + klass = rb_define_class_under(koffice_module, package+strlen("Ko"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("Kate::")) { + klass = rb_define_class_under(kate_module, package+strlen("Kate::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("Kate")) { + klass = rb_define_class_under(kate_module, package+strlen("Kate"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("KTextEditor::")) { + klass = rb_define_class_under(ktexteditor_module, package+strlen("KTextEditor::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (scope_op->search(packageName) != -1) { + // If an unrecognised classname of the form 'XXXXXX::YYYYYY' is found, + // then create a module XXXXXX to put the class YYYYYY under + VALUE module = rb_define_module(scope_op->cap(1).latin1()); + klass = rb_define_class_under(module, scope_op->cap(2).latin1(), base_class); + } else if ( packageName.startsWith("K") + && packageName.mid(1, 1).contains(QRegExp("[A-Z]")) == 1 ) + { + klass = rb_define_class_under(kde_module, package+strlen("K"), base_class); + } else { + packageName = packageName.mid(0, 1).upper() + packageName.mid(1); + klass = rb_define_class_under(kde_module, packageName.latin1(), base_class); + } + + return klass; +} + +static VALUE +create_qobject_class(VALUE /*self*/, VALUE package_value) +{ + const char *package = StringValuePtr(package_value); + VALUE klass; + + if (QString(package).startsWith("Qt::")) { + klass = rb_define_class_under(qt_module, package+strlen("Qt::"), qt_base_class); + if (qstrcmp(package, "Qt::Application") == 0) { + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) new_qapplication, -1); + rb_define_method(klass, "ARGV", (VALUE (*) (...)) qapplication_argv, 0); + } + } else if (QString(package).startsWith("Qext::")) { + if (qext_scintilla_module == Qnil) { + qext_scintilla_module = rb_define_module("Qext"); + } + klass = rb_define_class_under(qext_scintilla_module, package+strlen("Qext::"), qt_base_class); + } else { + klass = kde_package_to_class(package, qt_base_class); + } + + rb_define_method(klass, "inspect", (VALUE (*) (...)) inspect_qobject, 0); + rb_define_method(klass, "pretty_print", (VALUE (*) (...)) pretty_print_qobject, 1); + rb_define_method(klass, "receivers", (VALUE (*) (...)) receivers_qobject, 0); + rb_define_method(klass, "className", (VALUE (*) (...)) class_name, 0); + rb_define_method(klass, "inherits", (VALUE (*) (...)) inherits_qobject, -1); + rb_define_method(klass, "connect", (VALUE (*) (...)) qobject_connect, -1); + rb_define_singleton_method(klass, "connect", (VALUE (*) (...)) qobject_connect, -1); + + return klass; +} + +static VALUE +create_qt_class(VALUE /*self*/, VALUE package_value) +{ + const char *package = StringValuePtr(package_value); + VALUE klass; + + if (QString(package).startsWith("Qt::")) { + klass = rb_define_class_under(qt_module, package+strlen("Qt::"), qt_base_class); + } else if (QString(package).startsWith("Qext::")) { + if (qext_scintilla_module == Qnil) { + qext_scintilla_module = rb_define_module("Qext"); + } + klass = rb_define_class_under(qext_scintilla_module, package+strlen("Qext::"), qt_base_class); + } else { + klass = kde_package_to_class(package, qt_base_class); + } + + if (qstrcmp(package, "Qt::MetaObject") == 0) { + qmetaobject_class = klass; + } else if (qstrcmp(package, "Qt::Variant") == 0) { + qvariant_class = klass; + rb_define_singleton_method(qvariant_class, "new", (VALUE (*) (...)) new_qvariant, -1); + } else if (qstrcmp(package, "Qt::ByteArray") == 0) { + rb_define_method(klass, "data", (VALUE (*) (...)) qbytearray_data, 0); + rb_define_method(klass, "size", (VALUE (*) (...)) qbytearray_size, 0); + rb_define_method(klass, "setRawData", (VALUE (*) (...)) qbytearray_setRawData, 1); + } else if (qstrcmp(package, "Qt::Char") == 0) { + rb_define_method(klass, "to_s", (VALUE (*) (...)) qchar_to_s, 0); + } + + return klass; +} + +static VALUE +version(VALUE /*self*/) +{ + return rb_str_new2(QT_VERSION_STR); +} + +static VALUE +qtruby_version(VALUE /*self*/) +{ + return rb_str_new2(QTRUBY_VERSION); +} + +void +set_new_kde(VALUE (*new_kde) (int, VALUE *, VALUE)) +{ + _new_kde = new_kde; + + if (qt_module == Qnil) { + qt_module = rb_define_module("Qt"); + qt_internal_module = rb_define_module_under(qt_module, "Internal"); + qt_base_class = rb_define_class_under(qt_module, "Base", rb_cObject); + } + + kde_module = rb_define_module("KDE"); + rb_define_singleton_method(kde_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kde_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kparts_module = rb_define_module("KParts"); + rb_define_singleton_method(kparts_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kparts_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kns_module = rb_define_module("KNS"); + rb_define_singleton_method(kns_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kns_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kio_module = rb_define_module("KIO"); + rb_define_singleton_method(kio_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kio_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + dom_module = rb_define_module("DOM"); + rb_define_singleton_method(dom_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(dom_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kontact_module = rb_define_module("Kontact"); + rb_define_singleton_method(kontact_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kontact_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + ktexteditor_module = rb_define_module("KTextEditor"); + rb_define_singleton_method(ktexteditor_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(ktexteditor_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kwin_class = rb_define_class_under(kde_module, "Win", qt_base_class); + + kate_module = rb_define_module("Kate"); + rb_define_singleton_method(kate_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kate_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + koffice_module = rb_define_module("Ko"); + rb_define_singleton_method(koffice_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(koffice_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); +} + +void +set_kconfigskeletonitem_immutable(VALUE (*kconfigskeletonitem_immutable) (VALUE)) +{ + _kconfigskeletonitem_immutable = kconfigskeletonitem_immutable; + + kconfigskeleton_class = rb_define_class_under(kde_module, "ConfigSkeleton", qt_base_class); + kconfigskeleton_itemenum_class = rb_define_class_under(kconfigskeleton_class, "ItemEnum", qt_base_class); +} + +static VALUE +set_application_terminated(VALUE /*self*/, VALUE yn) +{ + application_terminated = (yn == Qtrue ? true : false); + return Qnil; +} + +void +Init_qtruby() +{ + if (qt_Smoke != 0L) { + // This function must have been called twice because both + // 'require Qt' and 'require Korundum' statements have + // been included in a ruby program + rb_fatal("require 'Qt' must not follow require 'Korundum'\n"); + return; + } + + init_qt_Smoke(); + qt_Smoke->binding = new QtRubySmokeBinding(qt_Smoke); + install_handlers(Qt_handlers); + + methcache.setAutoDelete(true); + classcache.setAutoDelete(true); + + if (qt_module == Qnil) { + qt_module = rb_define_module("Qt"); + qt_internal_module = rb_define_module_under(qt_module, "Internal"); + qt_base_class = rb_define_class_under(qt_module, "Base", rb_cObject); + } + + rb_define_singleton_method(qt_base_class, "new", (VALUE (*) (...)) new_qt, -1); + rb_define_method(qt_base_class, "initialize", (VALUE (*) (...)) initialize_qt, -1); + rb_define_singleton_method(qt_base_class, "method_missing", (VALUE (*) (...)) class_method_missing, -1); + rb_define_singleton_method(qt_module, "method_missing", (VALUE (*) (...)) module_method_missing, -1); + rb_define_method(qt_base_class, "method_missing", (VALUE (*) (...)) method_missing, -1); + + rb_define_singleton_method(qt_base_class, "const_missing", (VALUE (*) (...)) class_method_missing, -1); + rb_define_singleton_method(qt_module, "const_missing", (VALUE (*) (...)) module_method_missing, -1); + rb_define_method(qt_base_class, "const_missing", (VALUE (*) (...)) method_missing, -1); + + rb_define_method(qt_base_class, "dispose", (VALUE (*) (...)) dispose, 0); + rb_define_method(qt_base_class, "isDisposed", (VALUE (*) (...)) is_disposed, 0); + rb_define_method(qt_base_class, "disposed?", (VALUE (*) (...)) is_disposed, 0); + + rb_define_method(rb_cObject, "qDebug", (VALUE (*) (...)) qdebug, 1); + rb_define_method(rb_cObject, "qFatal", (VALUE (*) (...)) qfatal, 1); + rb_define_method(rb_cObject, "qWarning", (VALUE (*) (...)) qwarning, 1); + + rb_define_module_function(qt_internal_module, "getMethStat", (VALUE (*) (...)) getMethStat, 0); + rb_define_module_function(qt_internal_module, "getClassStat", (VALUE (*) (...)) getClassStat, 0); + rb_define_module_function(qt_internal_module, "getIsa", (VALUE (*) (...)) getIsa, 1); + rb_define_module_function(qt_internal_module, "allocateMocArguments", (VALUE (*) (...)) allocateMocArguments, 1); + rb_define_module_function(qt_internal_module, "setMocType", (VALUE (*) (...)) setMocType, 4); + rb_define_module_function(qt_internal_module, "setDebug", (VALUE (*) (...)) setDebug, 1); + rb_define_module_function(qt_internal_module, "debug", (VALUE (*) (...)) debugging, 0); + rb_define_module_function(qt_internal_module, "getTypeNameOfArg", (VALUE (*) (...)) getTypeNameOfArg, 2); + rb_define_module_function(qt_internal_module, "classIsa", (VALUE (*) (...)) classIsa, 2); + rb_define_module_function(qt_internal_module, "isEnum", (VALUE (*) (...)) isEnum, 1); + rb_define_module_function(qt_internal_module, "insert_pclassid", (VALUE (*) (...)) insert_pclassid, 2); + rb_define_module_function(qt_internal_module, "find_pclassid", (VALUE (*) (...)) find_pclassid, 1); + rb_define_module_function(qt_internal_module, "insert_mcid", (VALUE (*) (...)) insert_mcid, 2); + rb_define_module_function(qt_internal_module, "find_mcid", (VALUE (*) (...)) find_mcid, 1); + rb_define_module_function(qt_internal_module, "getVALUEtype", (VALUE (*) (...)) getVALUEtype, 1); + rb_define_module_function(qt_internal_module, "make_QUParameter", (VALUE (*) (...)) make_QUParameter, 4); + rb_define_module_function(qt_internal_module, "make_QMetaData", (VALUE (*) (...)) make_QMetaData, 2); + rb_define_module_function(qt_internal_module, "make_QUMethod", (VALUE (*) (...)) make_QUMethod, 2); + rb_define_module_function(qt_internal_module, "make_QMetaData_tbl", (VALUE (*) (...)) make_QMetaData_tbl, 1); + rb_define_module_function(qt_internal_module, "make_metaObject", (VALUE (*) (...)) make_metaObject, 6); + rb_define_module_function(qt_internal_module, "addMetaObjectMethods", (VALUE (*) (...)) add_metaobject_methods, 1); + rb_define_module_function(qt_internal_module, "addSignalMethods", (VALUE (*) (...)) add_signal_methods, 2); + rb_define_module_function(qt_internal_module, "mapObject", (VALUE (*) (...)) mapObject, 1); + // isQOjbect => isaQObject + rb_define_module_function(qt_internal_module, "isQObject", (VALUE (*) (...)) isaQObject, 1); + rb_define_module_function(qt_internal_module, "idInstance", (VALUE (*) (...)) idInstance, 1); + rb_define_module_function(qt_internal_module, "idClass", (VALUE (*) (...)) idClass, 1); + rb_define_module_function(qt_internal_module, "idMethodName", (VALUE (*) (...)) idMethodName, 1); + rb_define_module_function(qt_internal_module, "idMethod", (VALUE (*) (...)) idMethod, 2); + rb_define_module_function(qt_internal_module, "findMethod", (VALUE (*) (...)) findMethod, 2); + rb_define_module_function(qt_internal_module, "findAllMethods", (VALUE (*) (...)) findAllMethods, -1); + rb_define_module_function(qt_internal_module, "findAllMethodNames", (VALUE (*) (...)) findAllMethodNames, 3); + rb_define_module_function(qt_internal_module, "dumpCandidates", (VALUE (*) (...)) dumpCandidates, 1); + rb_define_module_function(qt_internal_module, "isObject", (VALUE (*) (...)) isObject, 1); + rb_define_module_function(qt_internal_module, "setCurrentMethod", (VALUE (*) (...)) setCurrentMethod, 1); + rb_define_module_function(qt_internal_module, "getClassList", (VALUE (*) (...)) getClassList, 0); + rb_define_module_function(qt_internal_module, "create_qt_class", (VALUE (*) (...)) create_qt_class, 1); + rb_define_module_function(qt_internal_module, "create_qobject_class", (VALUE (*) (...)) create_qobject_class, 1); + rb_define_module_function(qt_internal_module, "cast_object_to", (VALUE (*) (...)) cast_object_to, 2); + rb_define_module_function(qt_internal_module, "application_terminated=", (VALUE (*) (...)) set_application_terminated, 1); + + rb_define_module_function(qt_module, "version", (VALUE (*) (...)) version, 0); + rb_define_module_function(qt_module, "qtruby_version", (VALUE (*) (...)) qtruby_version, 0); + + rb_require("Qt/qtruby.rb"); + + // Do package initialization + rb_funcall(qt_internal_module, rb_intern("init_all_classes"), 0); +} + +}; diff --git a/qtruby/rubylib/qtruby/configure.in.in b/qtruby/rubylib/qtruby/configure.in.in new file mode 100644 index 00000000..41654f5e --- /dev/null +++ b/qtruby/rubylib/qtruby/configure.in.in @@ -0,0 +1,19 @@ +AC_CHECK_PROG(RUBY, ruby, ruby) + +if test -z "$RUBY"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE qtruby" +else + AC_MSG_CHECKING(for ruby dirs) + RUBY_ARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"archdir"@:>@)'` + RUBY_SITEARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitearchdir"@:>@)'` + RUBY_SITEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitelibdir"@:>@)'` + RUBY_LIBDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"libdir"@:>@)'` + RUBY_LIBRUBYARG=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"LIBRUBYARG_SHARED"@:>@)'` + AC_MSG_RESULT([archdir $RUBY_ARCHDIR, sitearchdir $RUBY_SITEARCHDIR, sitedir $RUBY_SITEDIR, libdir $RUBY_LIBDIR, librubyarg $RUBY_LIBRUBYARG]) + AC_SUBST(RUBY_ARCHDIR) + AC_SUBST(RUBY_SITEARCHDIR) + AC_SUBST(RUBY_SITEDIR) + AC_SUBST(RUBY_LIBDIR) + AC_SUBST(RUBY_LIBRUBYARG) +fi + diff --git a/qtruby/rubylib/qtruby/extconf.rb b/qtruby/rubylib/qtruby/extconf.rb new file mode 100644 index 00000000..0ffd2382 --- /dev/null +++ b/qtruby/rubylib/qtruby/extconf.rb @@ -0,0 +1,5 @@ +require 'mkmf' +dir_config('smoke') +dir_config('qt') +$LOCAL_LIBS += '-lsmokeqt -lqt-mt -lstdc++' +create_makefile("qtruby") diff --git a/qtruby/rubylib/qtruby/handlers.cpp b/qtruby/rubylib/qtruby/handlers.cpp new file mode 100644 index 00000000..6bddf50c --- /dev/null +++ b/qtruby/rubylib/qtruby/handlers.cpp @@ -0,0 +1,1978 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smoke.h" + +#undef DEBUG +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#include + +#include "marshall.h" +#include "qtruby.h" +#include "smokeruby.h" + +#ifndef HINT_BYTES +#define HINT_BYTES HINT_BYTE +#endif + +extern "C" { +extern VALUE set_obj_info(const char * className, smokeruby_object * o); +extern VALUE qt_internal_module; +extern VALUE qvariant_class; +extern bool application_terminated; +}; + +extern bool isDerivedFromByName(Smoke *smoke, const char *className, const char *baseClassName); +extern void mapPointer(VALUE obj, smokeruby_object *o, Smoke::Index classId, void *lastptr); + +static const char * (*_kde_resolve_classname)(Smoke*, int, void*) = 0; + +extern "C" { + +void +set_kde_resolve_classname(const char * (*kde_resolve_classname) (Smoke*, int, void *)) +{ + _kde_resolve_classname = kde_resolve_classname; +} + +}; + +void +mark_qobject_children(QObject * qobject) +{ + VALUE obj; + + const QObjectList *l = qobject->children(); + if (l == 0) { + return; + } + QObjectListIt it( *l ); // iterate over the children + QObject *child; + + while ( (child = it.current()) != 0 ) { + ++it; + obj = getPointerObject(child); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", child->className(), child, (void*)obj); + rb_gc_mark(obj); + } + + mark_qobject_children(child); + } +} + +void +smokeruby_mark(void * p) +{ + VALUE obj; + smokeruby_object * o = (smokeruby_object *) p; + const char *className = o->smoke->classes[o->classId].className; + + if(do_debug & qtdb_gc) qWarning("Checking for mark (%s*)%p\n", className, o->ptr); + + if(o->ptr && o->allocated) { + if (isDerivedFromByName(o->smoke, className, "QListView")) { + QListView * listview = (QListView *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QListView")); + QListViewItemIterator it(listview); + QListViewItem *item; + + while ( (item = it.current()) != 0 ) { + ++it; + obj = getPointerObject(item); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", className, item, (void*)obj); + rb_gc_mark(obj); + } + } + return; + } + + if (isDerivedFromByName(o->smoke, className, "QTable")) { + QTable * table = (QTable *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QTable")); + QTableItem *item; + + for ( int row = 0; row < table->numRows(); row++ ) { + for ( int col = 0; col < table->numCols(); col++ ) { + item = table->item(row, col); + obj = getPointerObject(item); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", className, item, (void*)obj); + rb_gc_mark(obj); + } + } + } + return; + } + + if (isDerivedFromByName(o->smoke, className, "QCanvas")) { + QCanvas * canvas = (QCanvas *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QCanvas")); + QCanvasItemList list = canvas->allItems(); + for ( QCanvasItemList::iterator it = list.begin(); it != list.end(); ++it ) { + obj = getPointerObject(*it); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", className, *it, (void*)obj); + rb_gc_mark(obj); + } + } + return; + } + + if (isDerivedFromByName(o->smoke, className, "QCanvasItem")) { + QCanvasItem * item = (QCanvasItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QCanvasItem")); + QCanvas * canvas = item->canvas(); + obj = getPointerObject(canvas); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", "QCanvas", canvas, (void*)obj); + rb_gc_mark(obj); + } + return; + } + + if (isDerivedFromByName(o->smoke, className, "QObject")) { + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + mark_qobject_children(qobject); + return; + } + } +} + +void +smokeruby_free(void * p) +{ + smokeruby_object *o = (smokeruby_object*)p; + const char *className = o->smoke->classes[o->classId].className; + + if(do_debug & qtdb_gc) qWarning("Checking for delete (%s*)%p allocated: %s\n", className, o->ptr, o->allocated ? "true" : "false"); + + if(application_terminated || !o->allocated || o->ptr == 0) { + free(o); + return; + } + + unmapPointer(o, o->classId, 0); + object_count --; + + if ( qstrcmp(className, "QObject") == 0 + || qstrcmp(className, "QListBoxItem") == 0 + || qstrcmp(className, "QStyleSheetItem") == 0 + || qstrcmp(className, "KCommand") == 0 + || qstrcmp(className, "KNamedCommand") == 0 + || qstrcmp(className, "KMacroCommand") == 0 + || qstrcmp(className, "KAboutData") == 0 + || qstrcmp(className, "KCmdLineArgs") == 0 + || qstrcmp(className, "QSqlCursor") == 0 ) + { + // Don't delete instances of these classes for now + free(o); + return; + } else if (isDerivedFromByName(o->smoke, className, "QLayoutItem")) { + QLayoutItem * item = (QLayoutItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QLayoutItem")); + if (item->layout() != 0 || item->widget() != 0 || item->spacerItem() != 0) { + free(o); + return; + } + } else if (qstrcmp(className, "QIconViewItem") == 0) { + QIconViewItem * item = (QIconViewItem *) o->ptr; + if (item->iconView() != 0) { + free(o); + return; + } + } else if (qstrcmp(className, "QCheckListItem") == 0) { + QCheckListItem * item = (QCheckListItem *) o->ptr; + if (item->parent() != 0 || item->listView() != 0) { + free(o); + return; + } + } else if (qstrcmp(className, "QListViewItem") == 0) { + QListViewItem * item = (QListViewItem *) o->ptr; + if (item->parent() != 0 || item->listView() != 0) { + free(o); + return; + } + } else if (isDerivedFromByName(o->smoke, className, "QTableItem")) { + QTableItem * item = (QTableItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QTableItem")); + if (item->table() != 0) { + free(o); + return; + } + } else if (qstrcmp(className, "QPopupMenu") == 0) { + QPopupMenu * item = (QPopupMenu *) o->ptr; + if (item->parentWidget(false) != 0) { + free(o); + return; + } + } else if (isDerivedFromByName(o->smoke, className, "QWidget")) { + QWidget * qwidget = (QWidget *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QWidget")); + if (qwidget->parentWidget(true) != 0) { + free(o); + return; + } + } else if (isDerivedFromByName(o->smoke, className, "QObject")) { + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + if (qobject->parent() != 0) { + free(o); + return; + } + } + + if(do_debug & qtdb_gc) qWarning("Deleting (%s*)%p\n", className, o->ptr); + + char *methodName = new char[strlen(className) + 2]; + methodName[0] = '~'; + strcpy(methodName + 1, className); + Smoke::Index nameId = o->smoke->idMethodName(methodName); + Smoke::Index meth = o->smoke->findMethod(o->classId, nameId); + if(meth > 0) { + Smoke::Method &m = o->smoke->methods[o->smoke->methodMaps[meth].method]; + Smoke::ClassFn fn = o->smoke->classes[m.classId].classFn; + Smoke::StackItem i[1]; + (*fn)(m.method, o->ptr, i); + } + delete[] methodName; + free(o); + + return; +} + +/* + * Given an approximate classname and a qt instance, try to improve the resolution of the name + * by using the various Qt rtti mechanisms for QObjects, QEvents and QCanvasItems + */ +static const char * +resolve_classname(Smoke* smoke, int classId, void * ptr) +{ + if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QEvent")) { + QEvent * qevent = (QEvent *) smoke->cast(ptr, classId, smoke->idClass("QEvent")); + switch (qevent->type()) { + case QEvent::ChildInserted: + case QEvent::ChildRemoved: + return "Qt::ChildEvent"; + case QEvent::Close: + return "Qt::CloseEvent"; + case QEvent::ContextMenu: + return "Qt::ContextMenuEvent"; +// case QEvent::User: +// return "Qt::CustomEvent"; + case QEvent::DragEnter: + return "Qt::DragEnterEvent"; + case QEvent::DragLeave: + return "Qt::DragLeaveEvent"; + case QEvent::DragMove: + return "Qt::DragMoveEvent"; + case QEvent::DragResponse: + return "Qt::DragResponseEvent"; + case QEvent::Drop: + return "Qt::DropEvent"; + case QEvent::FocusIn: + case QEvent::FocusOut: + return "Qt::FocusEvent"; + case QEvent::Hide: + return "Qt::HideEvent"; + case QEvent::KeyPress: + case QEvent::KeyRelease: + return "Qt::KeyEvent"; + case QEvent::IMStart: + case QEvent::IMCompose: + case QEvent::IMEnd: + return "Qt::IMEvent"; + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + return "Qt::MouseEvent"; + case QEvent::Move: + return "Qt::MoveEvent"; + case QEvent::Paint: + return "Qt::PaintEvent"; + case QEvent::Resize: + return "Qt::ResizeEvent"; + case QEvent::Show: + return "Qt::ShowEvent"; + // case QEvent::Tablet: + // return "Qt::TabletEvent"; + case QEvent::Timer: + return "Qt::TimerEvent"; + case QEvent::Wheel: + return "Qt::WheelEvent"; + default: + break; + } + } else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QObject")) { + QObject * qobject = (QObject *) smoke->cast(ptr, classId, smoke->idClass("QObject")); + QMetaObject * meta = qobject->metaObject(); + + while (meta != 0) { + Smoke::Index classId = smoke->idClass(meta->className()); + if (classId != 0) { + return smoke->binding->className(classId); + } + + meta = meta->superClass(); + } + } else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QCanvasItem")) { + QCanvasItem * qcanvasitem = (QCanvasItem *) smoke->cast(ptr, classId, smoke->idClass("QCanvasItem")); + switch (qcanvasitem->rtti()) { + case QCanvasItem::Rtti_Sprite: + return "Qt::CanvasSprite"; + case QCanvasItem::Rtti_PolygonalItem: + return "Qt::CanvasPolygonalItem"; + case QCanvasItem::Rtti_Text: + return "Qt::CanvasText"; + case QCanvasItem::Rtti_Polygon: + return "Qt::CanvasPolygon"; + case QCanvasItem::Rtti_Rectangle: + return "Qt::CanvasRectangle"; + case QCanvasItem::Rtti_Ellipse: + return "Qt::CanvasEllipse"; + case QCanvasItem::Rtti_Line: + return "Qt::CanvasLine"; + case QCanvasItem::Rtti_Spline: + return "Qt::CanvasSpline"; + default: + break; + } + } else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QListViewItem")) { + QListViewItem * item = (QListViewItem *) smoke->cast(ptr, classId, smoke->idClass("QListViewItem")); + switch (item->rtti()) { + case 0: + return "Qt::ListViewItem"; + case 1: + return "Qt::CheckListItem"; + default: + return "Qt::ListViewItem"; + break; + } + } else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QTableItem")) { + QTableItem * item = (QTableItem *) smoke->cast(ptr, classId, smoke->idClass("QTableItem")); + switch (item->rtti()) { + case 0: + return "Qt::TableItem"; + case 1: + return "Qt::ComboTableItem"; + case 2: + return "Qt::CheckTableItem"; + default: + return "Qt::TableItem"; + break; + } + } + + if (_kde_resolve_classname != 0) { + return (*_kde_resolve_classname)(smoke, classId, ptr); + } + + return smoke->binding->className(classId); +} + +bool +matches_arg(Smoke *smoke, Smoke::Index meth, Smoke::Index argidx, const char *argtype) +{ + Smoke::Index *arg = smoke->argumentList + smoke->methods[meth].args + argidx; + SmokeType type = SmokeType(smoke, *arg); + return type.name() && qstrcmp(type.name(), argtype) == 0; +} + +void * +construct_copy(smokeruby_object *o) +{ + const char *className = o->smoke->className(o->classId); + int classNameLen = strlen(className); + char *ccSig = new char[classNameLen + 2]; // copy constructor signature + strcpy(ccSig, className); + strcat(ccSig, "#"); + Smoke::Index ccId = o->smoke->idMethodName(ccSig); + delete[] ccSig; + + char *ccArg = new char[classNameLen + 8]; + sprintf(ccArg, "const %s&", className); + + Smoke::Index ccMeth = o->smoke->findMethod(o->classId, ccId); + + if(!ccMeth) { + delete[] ccArg; + return 0; + } + Smoke::Index method = o->smoke->methodMaps[ccMeth].method; + if(method > 0) { + // Make sure it's a copy constructor + if(!matches_arg(o->smoke, method, 0, ccArg)) { + delete[] ccArg; + return 0; + } + delete[] ccArg; + ccMeth = method; + } else { + // ambiguous method, pick the copy constructor + Smoke::Index i = -method; + while(o->smoke->ambiguousMethodList[i]) { + if(matches_arg(o->smoke, o->smoke->ambiguousMethodList[i], 0, ccArg)) + break; + i++; + } + delete[] ccArg; + ccMeth = o->smoke->ambiguousMethodList[i]; + if(!ccMeth) + return 0; + } + + // Okay, ccMeth is the copy constructor. Time to call it. + Smoke::StackItem args[2]; + args[0].s_voidp = 0; + args[1].s_voidp = o->ptr; + Smoke::ClassFn fn = o->smoke->classes[o->classId].classFn; + (*fn)(o->smoke->methods[ccMeth].method, 0, args); + return args[0].s_voidp; +} + +void +marshall_basetype(Marshall *m) +{ + switch(m->type().elem()) { + case Smoke::t_bool: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + // A Qt::Boolean has been passed as a value + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qboolean"), 1, *(m->var())); + m->item().s_bool = (temp == Qtrue ? true : false); + } else { + m->item().s_bool = (*(m->var()) == Qtrue ? true : false); + } + break; + case Marshall::ToVALUE: + *(m->var()) = m->item().s_bool ? Qtrue : Qfalse; + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_char: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_char = NUM2CHR(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = CHR2FIX(m->item().s_char); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_uchar: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_uchar = NUM2CHR(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = CHR2FIX(m->item().s_uchar); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_short: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_short = (short) NUM2INT(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = INT2NUM(m->item().s_short); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_ushort: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_ushort = (unsigned short) NUM2UINT(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = UINT2NUM(m->item().s_ushort); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_int: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + m->item().s_int = (int) NUM2INT(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()))); + } else { + m->item().s_int = (int) NUM2INT(*(m->var())); + } + break; + case Marshall::ToVALUE: + *(m->var()) = INT2NUM(m->item().s_int); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_uint: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + m->item().s_int = (unsigned int) NUM2UINT(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()))); + } else { + m->item().s_uint = (unsigned int) NUM2UINT(*(m->var())); + } + break; + case Marshall::ToVALUE: + *(m->var()) = UINT2NUM(m->item().s_uint); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_long: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + m->item().s_int = (long) NUM2LONG(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()))); + } else { + m->item().s_long = (long) NUM2LONG(*(m->var())); + } + break; + case Marshall::ToVALUE: + *(m->var()) = INT2NUM(m->item().s_long); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_ulong: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + m->item().s_int = (unsigned long) NUM2ULONG(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()))); + } else { + m->item().s_ulong = (unsigned long) NUM2ULONG(*(m->var())); + } + break; + case Marshall::ToVALUE: + *(m->var()) = INT2NUM(m->item().s_ulong); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_float: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_float = (float) NUM2DBL(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = rb_float_new((double) m->item().s_float); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_double: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_double = (double) NUM2DBL(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = rb_float_new(m->item().s_double); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_enum: + switch(m->action()) { + case Marshall::FromVALUE: + { + if (TYPE(*(m->var())) == T_OBJECT) { + // A Qt::Enum is a subclass of Qt::Integer, so 'get_qinteger()' can be called ok + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var())); + m->item().s_enum = (long) NUM2LONG(temp); + } else { + m->item().s_enum = (long) NUM2LONG(*(m->var())); + } + } + break; + case Marshall::ToVALUE: + *(m->var()) = rb_funcall( qt_internal_module, + rb_intern("create_qenum"), + 2, INT2NUM(m->item().s_enum), rb_str_new2(m->type().name()) ); + + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_class: + switch(m->action()) { + case Marshall::FromVALUE: + { + if(*(m->var()) == Qnil) { + m->item().s_class = 0; + break; + } + if(TYPE(*(m->var())) != T_DATA) { + rb_raise(rb_eArgError, "Invalid type, expecting %s\n", m->type().name()); + break; + } + + smokeruby_object *o = value_obj_info(*(m->var())); + if(!o || !o->ptr) { + if(m->type().isRef()) { + rb_warning("References can't be nil\n"); + m->unsupported(); + } + m->item().s_class = 0; + break; + } + void *ptr = o->ptr; + if(!m->cleanup() && m->type().isStack()) { + ptr = construct_copy(o); + } + const Smoke::Class &c = m->smoke()->classes[m->type().classId()]; + ptr = o->smoke->cast( + ptr, // pointer + o->classId, // from + o->smoke->idClass(c.className) // to + ); + m->item().s_class = ptr; + break; + } + break; + case Marshall::ToVALUE: + { + if(m->item().s_voidp == 0) { + *(m->var()) = Qnil; + break; + } + + void *p = m->item().s_voidp; + VALUE obj = getPointerObject(p); + if(obj != Qnil) { + *(m->var()) = obj; + break; + } + + smokeruby_object * o = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + o->smoke = m->smoke(); + o->classId = m->type().classId(); + o->ptr = p; + o->allocated = false; + + const char * classname = resolve_classname(o->smoke, o->classId, o->ptr); + + if(m->type().isConst() && m->type().isRef()) { + p = construct_copy( o ); + if(p) { + o->ptr = p; + o->allocated = true; + } + } + + obj = set_obj_info(classname, o); + if (do_debug & qtdb_calls) { + qWarning("allocating %s %p -> %p\n", classname, o->ptr, (void*)obj); + } + + if(m->type().isStack()) { + o->allocated = true; + // Keep a mapping of the pointer so that it is only wrapped once as a ruby VALUE + mapPointer(obj, o, o->classId, 0); + } + + *(m->var()) = obj; + } + break; + default: + m->unsupported(); + break; + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_void(Marshall * /*m*/) {} +static void marshall_unknown(Marshall *m) { + m->unsupported(); +} + +static void marshall_charP(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + if (rv == Qnil) { + m->item().s_voidp = 0; + break; + } + + int len = RSTRING(rv)->len; + char* mem = (char*) malloc(len+1); + memcpy(mem, StringValuePtr(rv), len); + mem[len] ='\0'; + m->item().s_voidp = mem; + } + break; + case Marshall::ToVALUE: + { + char *p = (char*)m->item().s_voidp; + if(p) + *(m->var()) = rb_str_new2(p); + else + *(m->var()) = Qnil; + if(m->cleanup()) + delete[] p; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_ucharP(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + if (rv == Qnil) { + m->item().s_voidp = 0; + break; + } + int len = RSTRING(rv)->len; + char* mem = (char*) malloc(len+1); + memcpy(mem, StringValuePtr(rv), len); + mem[len] ='\0'; + m->item().s_voidp = mem; + } + break; + case Marshall::ToVALUE: + default: + m->unsupported(); + break; + } +} + +static const char * KCODE = 0; +static QTextCodec *codec = 0; + +static void +init_codec() { + VALUE temp = rb_gv_get("$KCODE"); + KCODE = StringValuePtr(temp); + if (qstrcmp(KCODE, "EUC") == 0) { + codec = QTextCodec::codecForName("eucJP"); + } else if (qstrcmp(KCODE, "SJIS") == 0) { + codec = QTextCodec::codecForName("Shift-JIS"); + } +} + +QString* +qstringFromRString(VALUE rstring) { + if (KCODE == 0) { + init_codec(); + } + + QString * s; + if (qstrcmp(KCODE, "UTF8") == 0) + s = new QString(QString::fromUtf8(StringValuePtr(rstring), RSTRING(rstring)->len)); + else if (qstrcmp(KCODE, "EUC") == 0) + s = new QString(codec->toUnicode(StringValuePtr(rstring))); + else if (qstrcmp(KCODE, "SJIS") == 0) + s = new QString(codec->toUnicode(StringValuePtr(rstring))); + else if(qstrcmp(KCODE, "NONE") == 0) + s = new QString(QString::fromLatin1(StringValuePtr(rstring))); + else + s = new QString(QString::fromLocal8Bit(StringValuePtr(rstring), RSTRING(rstring)->len)); + return s; +} + +VALUE +rstringFromQString(QString * s) { + if (KCODE == 0) { + init_codec(); + } + + if (qstrcmp(KCODE, "UTF8") == 0) + return rb_str_new2(s->utf8()); + else if (qstrcmp(KCODE, "EUC") == 0) + return rb_str_new2(codec->fromUnicode(*s)); + else if (qstrcmp(KCODE, "SJIS") == 0) + return rb_str_new2(codec->fromUnicode(*s)); + else if (qstrcmp(KCODE, "NONE") == 0) + return rb_str_new2(s->latin1()); + else + return rb_str_new2(s->local8Bit()); +} + +static void marshall_QString(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + QString* s = 0; + if ( *(m->var()) != Qnil) { + s = qstringFromRString(*(m->var())); + } else { + s = new QString(QString::null); + } + + m->item().s_voidp = s; + m->next(); + + if (!m->type().isConst() && *(m->var()) != Qnil && s != 0 && !s->isNull()) { + rb_str_resize(*(m->var()), 0); + VALUE temp = rstringFromQString(s); + rb_str_cat2(*(m->var()), StringValuePtr(temp)); + } + + if (s != 0 && m->cleanup()) { + delete s; + } + } + break; + case Marshall::ToVALUE: + { + QString *s = (QString*)m->item().s_voidp; + if (s != 0) { + if (s->isNull()) { + *(m->var()) = Qnil; + } else { + *(m->var()) = rstringFromQString(s); + } + if (m->cleanup() || m->type().isStack()) { + delete s; + } + } else { + *(m->var()) = Qnil; + } + } + break; + default: + m->unsupported(); + break; + } +} + +// The only way to convert a QChar to a QString is to +// pass a QChar to a QString constructor. However, +// QStrings aren't in the QtRuby api, so add this +// convenience method 'Qt::Char.to_s' to get a ruby +// string from a Qt::Char. +VALUE +qchar_to_s(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if (o == 0 || o->ptr == 0) { + return Qnil; + } + + QChar * qchar = (QChar*) o->ptr; + QString s(*qchar); + return rstringFromQString(&s); +} + +#if 0 +static const char *not_ascii(const char *s, uint &len) +{ + bool r = false; + for(; *s ; s++, len--) + if((uint)*s > 0x7F) + { + r = true; + break; + } + return r ? s : 0L; +} +#endif + +static void marshall_QCString(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + QCString *s = 0; + VALUE rv = *(m->var()); + if (rv == Qnil) { + s = new QCString(); + } else { + // Add 1 to the ruby string length to allow for a QCString '\0' terminator + s = new QCString(StringValuePtr(*(m->var())), RSTRING(*(m->var()))->len + 1); + } + m->item().s_voidp = s; + + m->next(); + + if (!m->type().isConst() && rv != Qnil && s != 0) { + rb_str_resize(rv, 0); + rb_str_cat2(rv, (const char *)*s); + } + if(s && m->cleanup()) + delete s; + } + break; + case Marshall::ToVALUE: + { + QCString *s = (QCString*)m->item().s_voidp; + if(s && (const char *) *s != 0) { + *(m->var()) = rb_str_new2((const char *)*s); +// const char * p = (const char *)*s; +// uint len = s->length(); +// if(not_ascii(p,len)) +// { +// #if PERL_VERSION == 6 && PERL_SUBVERSION == 0 +// QTextCodec* c = QTextCodec::codecForMib(106); // utf8 +// if(c->heuristicContentMatch(p,len) >= 0) +// #else +// if(is_utf8_string((U8 *)p,len)) +// #endif +// SvUTF8_on(*(m->var())); +// } + } else { + if (m->type().isConst()) { + *(m->var()) = Qnil; + } else { + *(m->var()) = rb_str_new2(""); + } + } + m->next(); + + if (!m->type().isConst() && s != 0) { + *s = (const char *) StringValuePtr(*(m->var())); + } + + if(s && m->cleanup()) + delete s; + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_QCOORD_array(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE av = *(m->var()); + if (TYPE(av) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(av)->len; + QCOORD *coord = new QCOORD[count + 2]; + for(long i = 0; i < count; i++) { + VALUE svp = rb_ary_entry(av, i); + coord[i] = NUM2INT(svp); + } + m->item().s_voidp = coord; + m->next(); + } + break; + default: + m->unsupported(); + } +} + +static void marshall_longlong(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + m->item().s_voidp = new long long; + *(long long *)m->item().s_voidp = rb_num2ll(*(m->var())); + + m->next(); + + if(m->cleanup() && m->type().isConst()) { + delete (long long *) m->item().s_voidp; + } + } + break; + case Marshall::ToVALUE: + { + *(m->var()) = rb_ll2inum(*(long long *) m->item().s_voidp); + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_ulonglong(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + m->item().s_voidp = new unsigned long long; + *(long long *)m->item().s_voidp = rb_num2ull(*(m->var())); + + m->next(); + + if(m->cleanup() && m->type().isConst()) { + delete (unsigned long long *) m->item().s_voidp; + } + } + break; + case Marshall::ToVALUE: + { + *(m->var()) = rb_ull2inum(*(unsigned long long *) m->item().s_voidp); + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_intR(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + int * i = new int; + if (TYPE(rv) == T_OBJECT) { + // A Qt::Integer has been passed as an integer value + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, rv); + *i = NUM2INT(temp); + m->item().s_voidp = i; + m->next(); + rb_funcall(qt_internal_module, rb_intern("set_qinteger"), 2, rv, INT2NUM(*i)); + rv = temp; + } else { + *i = NUM2INT(rv); + m->item().s_voidp = i; + m->next(); + } + if(m->cleanup() && m->type().isConst()) { + delete i; + } else { + m->item().s_voidp = new int((int)NUM2INT(rv)); + } + } + break; + case Marshall::ToVALUE: + { + int *ip = (int*)m->item().s_voidp; + VALUE rv = *(m->var()); + if(!ip) { + rv = Qnil; + break; + } + *(m->var()) = INT2NUM(*ip); + m->next(); + if(!m->type().isConst()) + *ip = NUM2INT(*(m->var())); + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_boolR(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + bool * b = new bool; + if (TYPE(rv) == T_OBJECT) { + // A Qt::Boolean has been passed as a value + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qboolean"), 1, rv); + *b = (temp == Qtrue ? true : false); + m->item().s_voidp = b; + m->next(); + rb_funcall(qt_internal_module, rb_intern("set_qboolean"), 2, rv, (*b ? Qtrue : Qfalse)); + } else { + *b = (rv == Qtrue ? true : false); + m->item().s_voidp = b; + m->next(); + } + if(m->cleanup() && m->type().isConst()) { + delete b; + } + } + break; + case Marshall::ToVALUE: + { + bool *ip = (bool*)m->item().s_voidp; + if(!ip) { + *(m->var()) = Qnil; + break; + } + *(m->var()) = (*ip?Qtrue:Qfalse); + m->next(); + if(!m->type().isConst()) + *ip = *(m->var()) == Qtrue ? true : false; + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_charP_array(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE arglist = *(m->var()); + if (arglist == Qnil + || TYPE(arglist) != T_ARRAY + || RARRAY(arglist)->len == 0 ) + { + m->item().s_voidp = 0; + break; + } + + char **argv = new char *[RARRAY(arglist)->len + 1]; + long i; + for(i = 0; i < RARRAY(arglist)->len; i++) { + VALUE item = rb_ary_entry(arglist, i); + char *s = StringValuePtr(item); + argv[i] = new char[strlen(s) + 1]; + strcpy(argv[i], s); + } + argv[i] = 0; + m->item().s_voidp = argv; + m->next(); + if(m->cleanup()) { + rb_ary_clear(arglist); + for(i = 0; argv[i]; i++) + rb_ary_push(arglist, rb_str_new2(argv[i])); + } + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QStringList(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + + int count = RARRAY(list)->len; + QStringList *stringlist = new QStringList; + + for(long i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + if(TYPE(item) != T_STRING) { + stringlist->append(QString()); + continue; + } + stringlist->append(*(qstringFromRString(item))); + } + + m->item().s_voidp = stringlist; + m->next(); + + + if (stringlist != 0 && !m->type().isConst()) { + rb_ary_clear(list); + for(QStringList::Iterator it = stringlist->begin(); it != stringlist->end(); ++it) + rb_ary_push(list, rstringFromQString(&(*it))); + } + + if (m->cleanup()) + delete stringlist; + break; + } + case Marshall::ToVALUE: + { + QStringList *stringlist = static_cast(m->item().s_voidp); + if(!stringlist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + for(QStringList::Iterator it = stringlist->begin(); it != stringlist->end(); ++it) { + VALUE rv = rstringFromQString(&(*it)); + rb_ary_push(av, rv); + } + + if(m->cleanup()) + delete stringlist; + + *(m->var()) = av; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QStrList(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + + int count = RARRAY(list)->len; + QStrList *stringlist = new QStrList; + + for(long i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + if(TYPE(item) != T_STRING) { + stringlist->append(QString()); + continue; + } + stringlist->append(QString::fromUtf8(StringValuePtr(item), RSTRING(item)->len)); + } + + m->item().s_voidp = stringlist; + m->next(); + + if (!m->type().isConst()) { + rb_ary_clear(list); + for(const char * it = stringlist->first(); it != 0; it = stringlist->next()) + rb_ary_push(list, rb_str_new2(it)); + } + + if (m->cleanup()) { + delete stringlist; + } + break; + } + case Marshall::ToVALUE: + { + QStrList *stringlist = static_cast(m->item().s_voidp); + if(!stringlist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + for(const char * it = stringlist->first(); it != 0; it = stringlist->next()) { + VALUE rv = rb_str_new2(it); + rb_ary_push(av, rv); + } + + if(m->cleanup()) + delete stringlist; + + *(m->var()) = av; + } + break; + default: + m->unsupported(); + break; + } +} + +template +void marshall_ItemList(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(list)->len; + ItemList *cpplist = new ItemList; + long i; + for(i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + // TODO do type checking! + smokeruby_object *o = value_obj_info(item); + if(!o || !o->ptr) + continue; + void *ptr = o->ptr; + ptr = o->smoke->cast( + ptr, // pointer + o->classId, // from + o->smoke->idClass(ItemSTR) // to + ); + cpplist->append((Item*)ptr); + } + + m->item().s_voidp = cpplist; + m->next(); + + if (!m->type().isConst()) { + rb_ary_clear(list); + for(ItemListIterator it = cpplist->begin(); + it != cpplist->end(); + ++it ) + { + VALUE obj = getPointerObject((void*)(*it)); + rb_ary_push(list, obj); + } + } + + if (m->cleanup()) { + delete cpplist; + } + } + break; + case Marshall::ToVALUE: + { + ItemList *valuelist = (ItemList*)m->item().s_voidp; + if(!valuelist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + + for(ItemListIterator it = valuelist->begin(); + it != valuelist->end(); + ++it) { + void *p = *it; + + if(m->item().s_voidp == 0) { + *(m->var()) = Qnil; + break; + } + + VALUE obj = getPointerObject(p); + if(obj == Qnil) { + smokeruby_object * o = ALLOC(smokeruby_object); + o->smoke = m->smoke(); + o->classId = m->smoke()->idClass(ItemSTR); + o->ptr = p; + o->allocated = false; + obj = set_obj_info(resolve_classname(o->smoke, o->classId, o->ptr), o); + } + rb_ary_push(av, obj); + } + + if(m->cleanup()) + delete valuelist; + else + *(m->var()) = av; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QValueListInt(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(list)->len; + QValueList *valuelist = new QValueList; + long i; + for(i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) { + valuelist->append(0); + continue; + } + valuelist->append(NUM2INT(item)); + } + + m->item().s_voidp = valuelist; + m->next(); + + if (!m->type().isConst()) { + rb_ary_clear(list); + for(QValueListIterator it = valuelist->begin(); + it != valuelist->end(); + ++it) + rb_ary_push(list, INT2NUM((int)*it)); + } + + if (m->cleanup()) { + delete valuelist; + } + } + break; + case Marshall::ToVALUE: + { + QValueList *valuelist = (QValueList*)m->item().s_voidp; + if(!valuelist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + + for(QValueListIterator it = valuelist->begin(); + it != valuelist->end(); + ++it) + rb_ary_push(av, INT2NUM(*it)); + + *(m->var()) = av; + + if(m->cleanup()) + delete valuelist; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_voidP(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + if (rv != Qnil) + m->item().s_voidp = (void*)NUM2INT(*(m->var())); + else + m->item().s_voidp = 0; + } + break; + case Marshall::ToVALUE: + { + *(m->var()) = Data_Wrap_Struct(rb_cObject, 0, 0, m->item().s_voidp); + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QMapQStringQString(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE hash = *(m->var()); + if (TYPE(hash) != T_HASH) { + m->item().s_voidp = 0; + break; + } + + QMap * map = new QMap; + + // Convert the ruby hash to an array of key/value arrays + VALUE temp = rb_funcall(hash, rb_intern("to_a"), 0); + + for (long i = 0; i < RARRAY(temp)->len; i++) { + VALUE key = rb_ary_entry(rb_ary_entry(temp, i), 0); + VALUE value = rb_ary_entry(rb_ary_entry(temp, i), 1); + (*map)[QString(StringValuePtr(key))] = QString(StringValuePtr(value)); + } + + m->item().s_voidp = map; + m->next(); + + if(m->cleanup()) + delete map; + } + break; + case Marshall::ToVALUE: + { + QMap *map = (QMap*)m->item().s_voidp; + if(!map) { + *(m->var()) = Qnil; + break; + } + + VALUE hv = rb_hash_new(); + + QMap::Iterator it; + for (it = map->begin(); it != map->end(); ++it) { + rb_hash_aset(hv, rstringFromQString((QString*)&(it.key())), rstringFromQString((QString*) &(it.data()))); + } + + *(m->var()) = hv; + m->next(); + + if(m->cleanup()) + delete map; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QMapQStringQVariant(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE hash = *(m->var()); + if (TYPE(hash) != T_HASH) { + m->item().s_voidp = 0; + break; + } + + QMap * map = new QMap; + + // Convert the ruby hash to an array of key/value arrays + VALUE temp = rb_funcall(hash, rb_intern("to_a"), 0); + + for (long i = 0; i < RARRAY(temp)->len; i++) { + VALUE key = rb_ary_entry(rb_ary_entry(temp, i), 0); + VALUE value = rb_ary_entry(rb_ary_entry(temp, i), 1); + + smokeruby_object *o = value_obj_info(value); + if (!o || !o->ptr || o->classId != o->smoke->idClass("QVariant")) { + // If the value isn't a Qt::Variant, then try and construct + // a Qt::Variant from it + value = rb_funcall(qvariant_class, rb_intern("new"), 1, value); + if (value == Qnil) { + continue; + } + o = value_obj_info(value); + } + + void * ptr = o->ptr; + ptr = o->smoke->cast(ptr, o->classId, o->smoke->idClass("QVariant")); + + (*map)[QString(StringValuePtr(key))] = (QVariant)*(QVariant*)ptr; + } + + m->item().s_voidp = map; + m->next(); + + if(m->cleanup()) + delete map; + } + break; + case Marshall::ToVALUE: + { + QMap *map = (QMap*)m->item().s_voidp; + if(!map) { + *(m->var()) = Qnil; + break; + } + + VALUE hv = rb_hash_new(); + + QMap::Iterator it; + for (it = map->begin(); it != map->end(); ++it) { + void *p = new QVariant(it.data()); + VALUE obj = getPointerObject(p); + + if (obj == Qnil) { + smokeruby_object * o = ALLOC(smokeruby_object); + o->classId = m->smoke()->idClass("QVariant"); + o->smoke = m->smoke(); + o->ptr = p; + o->allocated = true; + obj = set_obj_info("Qt::Variant", o); + } + + rb_hash_aset(hv, rstringFromQString((QString*)&(it.key())), obj); + } + + *(m->var()) = hv; + m->next(); + + if(m->cleanup()) + delete map; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QUObject(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE array = *(m->var()); + if (array != Qnil && TYPE(array) == T_ARRAY) { + VALUE rv = rb_ary_entry(array, 0); + Data_Get_Struct(rv, QUObject, m->item().s_voidp); + } else { + m->item().s_voidp = 0; + } + } + break; + case Marshall::ToVALUE: + { + VALUE rv = Data_Wrap_Struct(rb_cObject, 0, 0, m->item().s_voidp); + VALUE array = rb_ary_new2(1); + rb_ary_push(array, rv); + *(m->var()) = array; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QRgb_array(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(list)->len; + QRgb *rgb = new QRgb[count + 2]; + long i; + for(i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) { + rgb[i] = 0; + continue; + } + + rgb[i] = NUM2UINT(item); + } + m->item().s_voidp = rgb; + m->next(); + } + break; + case Marshall::ToVALUE: + // Implement this with a tied array or something + default: + m->unsupported(); + break; + } +} + +void marshall_QPairintint(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY || RARRAY(list)->len != 2) { + m->item().s_voidp = 0; + break; + } + int int0; + int int1; + VALUE item = rb_ary_entry(list, 0); + if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) { + int0 = 0; + } else { + int0 = NUM2INT(item); + } + + item = rb_ary_entry(list, 1); + if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) { + int1 = 0; + } else { + int1 = NUM2INT(item); + } + + QPair * qpair = new QPair(int0,int1); + m->item().s_voidp = qpair; + m->next(); + if(m->cleanup()) + delete qpair; + } + break; + case Marshall::ToVALUE: + default: + m->unsupported(); + break; + } +} + +#define DEF_LIST_MARSHALLER(ListIdent,ItemList,Item,Itr) namespace { char ListIdent##STR[] = #Item; }; \ + Marshall::HandlerFn marshall_##ListIdent = marshall_ItemList; + +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x030200 +DEF_LIST_MARSHALLER( QPtrListQNetworkOperation, QPtrList, QNetworkOperation, QPtrListStdIterator ) +DEF_LIST_MARSHALLER( QPtrListQToolBar, QPtrList, QToolBar, QPtrListStdIterator ) +DEF_LIST_MARSHALLER( QPtrListQTab, QPtrList, QTab, QPtrListStdIterator ) +DEF_LIST_MARSHALLER( QPtrListQDockWindow, QPtrList, QDockWindow, QPtrListStdIterator ) +DEF_LIST_MARSHALLER( QFileInfoList, QFileInfoList, QFileInfo, QFileInfoList::Iterator ) +DEF_LIST_MARSHALLER( QObjectList, QObjectList, QObject, QPtrListStdIterator ) +DEF_LIST_MARSHALLER( QWidgetList, QWidgetList, QWidget, QPtrListStdIterator ) +#endif + +DEF_LIST_MARSHALLER( QCanvasItemList, QCanvasItemList, QCanvasItem, QValueListIterator ) + +template +void marshall_ValueItemList(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(list)->len; + ItemList *cpplist = new ItemList; + long i; + for(i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + // TODO do type checking! + smokeruby_object *o = value_obj_info(item); + + // Special case for the QValueList type + if ( qstrcmp(ItemSTR, "QVariant") == 0 + && (!o || !o->ptr || o->classId != o->smoke->idClass("QVariant")) ) + { + // If the value isn't a Qt::Variant, then try and construct + // a Qt::Variant from it + item = rb_funcall(qvariant_class, rb_intern("new"), 1, item); + if (item == Qnil) { + continue; + } + o = value_obj_info(item); + } + + if(!o || !o->ptr) + continue; + void *ptr = o->ptr; + ptr = o->smoke->cast( + ptr, // pointer + o->classId, // from + o->smoke->idClass(ItemSTR) // to + ); + cpplist->append(*(Item*)ptr); + } + + m->item().s_voidp = cpplist; + m->next(); + + if (!m->type().isConst()) { + rb_ary_clear(list); + for(ItemListIterator it = cpplist->begin(); + it != cpplist->end(); + ++it) + { + VALUE obj = getPointerObject((void*)&(*it)); + rb_ary_push(list, obj); + } + } + + if (m->cleanup()) { + delete cpplist; + } + } + break; + case Marshall::ToVALUE: + { + ItemList *valuelist = (ItemList*)m->item().s_voidp; + if(!valuelist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + + int ix = m->smoke()->idClass(ItemSTR); + const char * className = m->smoke()->binding->className(ix); + + for(ItemListIterator it = valuelist->begin(); + it != valuelist->end(); + ++it) { + void *p = &(*it); + + if(m->item().s_voidp == 0) { + *(m->var()) = Qnil; + break; + } + + VALUE obj = getPointerObject(p); + if(obj == Qnil) { + smokeruby_object * o = ALLOC(smokeruby_object); + o->smoke = m->smoke(); + o->classId = o->smoke->idClass(ItemSTR); + o->ptr = p; + o->allocated = false; + obj = set_obj_info(className, o); + } + rb_ary_push(av, obj); + } + + if(m->cleanup()) + delete valuelist; + else + *(m->var()) = av; + } + break; + default: + m->unsupported(); + break; + } +} + +#define DEF_VALUELIST_MARSHALLER(ListIdent,ItemList,Item,Itr) namespace dummy { char ListIdent##STR[] = #Item; }; \ + Marshall::HandlerFn marshall_##ListIdent = marshall_ValueItemList; + +DEF_VALUELIST_MARSHALLER( QVariantList, QValueList, QVariant, QValueList::Iterator ) +DEF_VALUELIST_MARSHALLER( QPixmapList, QValueList, QPixmap, QValueList::Iterator ) +DEF_VALUELIST_MARSHALLER( QIconDragItemList, QValueList, QIconDragItem, QValueList::Iterator ) +DEF_VALUELIST_MARSHALLER( QImageTextKeyLangList, QValueList, QImageTextKeyLang, QValueList::Iterator ) +DEF_VALUELIST_MARSHALLER( QUrlInfoList, QValueList, QUrlInfo, QValueList::Iterator ) +DEF_VALUELIST_MARSHALLER( QTranslatorMessageList, QValueList, QTranslatorMessage, QValueList::Iterator ) +DEF_VALUELIST_MARSHALLER( QHostAddressList, QValueList, QHostAddress, QValueList::Iterator ) + +TypeHandler Qt_handlers[] = { + { "QString", marshall_QString }, + { "QString&", marshall_QString }, + { "QString*", marshall_QString }, + { "QCString", marshall_QCString }, + { "QCString&", marshall_QCString }, + { "QCString*", marshall_QCString }, + { "QStringList", marshall_QStringList }, + { "QStringList&", marshall_QStringList }, + { "QStringList*", marshall_QStringList }, + { "QStrList", marshall_QStrList }, + { "QStrList&", marshall_QStrList }, + { "QStrList*", marshall_QStrList }, + { "long long int", marshall_longlong }, + { "long long int&", marshall_longlong }, + { "Q_INT64", marshall_longlong }, + { "Q_INT64&", marshall_longlong }, + { "Q_LLONG", marshall_longlong }, + { "Q_LLONG&", marshall_longlong }, + { "KIO::filesize_t", marshall_longlong }, + { "DOM::DOMTimeStamp", marshall_ulonglong }, + { "unsigned long long int", marshall_ulonglong }, + { "unsigned long long int&", marshall_ulonglong }, + { "Q_UINT64", marshall_ulonglong }, + { "Q_UINT64&", marshall_ulonglong }, + { "Q_ULLONG", marshall_ulonglong }, + { "Q_ULLONG&", marshall_ulonglong }, + { "signed int&", marshall_intR }, + { "int&", marshall_intR }, + { "int*", marshall_intR }, + { "bool&", marshall_boolR }, + { "bool*", marshall_boolR }, + { "char*", marshall_charP }, + { "char**", marshall_charP_array }, + { "uchar*", marshall_ucharP }, + { "QRgb*", marshall_QRgb_array }, + { "QPair&", marshall_QPairintint }, + { "QUObject*", marshall_QUObject }, + { "const QCOORD*", marshall_QCOORD_array }, + { "void", marshall_void }, + { "QValueList", marshall_QValueListInt }, + { "QValueList&", marshall_QValueListInt }, + { "QValueList", marshall_QVariantList }, + { "QValueList&", marshall_QVariantList }, + { "QValueList", marshall_QPixmapList }, + { "QValueList&", marshall_QIconDragItemList }, + { "QValueList", marshall_QImageTextKeyLangList }, + { "QValueList&", marshall_QUrlInfoList }, + { "QValueList", marshall_QTranslatorMessageList }, + { "QValueList", marshall_QHostAddressList }, + { "QCanvasItemList", marshall_QCanvasItemList }, + { "QMap", marshall_QMapQStringQString }, + { "QMap&", marshall_QMapQStringQString }, + { "QMap", marshall_QMapQStringQVariant }, + { "QMap&", marshall_QMapQStringQVariant }, +#if QT_VERSION >= 0x030200 + { "QWidgetList", marshall_QWidgetList }, + { "QWidgetList*", marshall_QWidgetList }, + { "QWidgetList&", marshall_QWidgetList }, + { "QObjectList*", marshall_QObjectList }, + { "QObjectList&", marshall_QObjectList }, + { "QFileInfoList*", marshall_QFileInfoList }, + { "QPtrList", marshall_QPtrListQToolBar }, + { "QPtrList*", marshall_QPtrListQTab }, + { "QPtrList", marshall_QPtrListQDockWindow }, + { "QPtrList*", marshall_QPtrListQDockWindow }, + { "QPtrList", marshall_QPtrListQNetworkOperation }, + { "QPtrList&", marshall_QPtrListQNetworkOperation }, +#endif + { 0, 0 } +}; + +QAsciiDict type_handlers(199); + +void install_handlers(TypeHandler *h) { + while(h->name) { + type_handlers.insert(h->name, h); + h++; + } +} + +Marshall::HandlerFn getMarshallFn(const SmokeType &type) { + if(type.elem()) + return marshall_basetype; + if(!type.name()) + return marshall_void; + TypeHandler *h = type_handlers[type.name()]; + if(h == 0 && type.isConst() && strlen(type.name()) > strlen("const ")) { + h = type_handlers[type.name() + strlen("const ")]; + } + + if(h != 0) { + return h->fn; + } + + return marshall_unknown; +} diff --git a/qtruby/rubylib/qtruby/lib/Makefile.am b/qtruby/rubylib/qtruby/lib/Makefile.am new file mode 100644 index 00000000..e8f6457a --- /dev/null +++ b/qtruby/rubylib/qtruby/lib/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = Qt + +rubylibdir = $(RUBY_SITEDIR) +rubylib_DATA = Qt.rb diff --git a/qtruby/rubylib/qtruby/lib/Qt.rb b/qtruby/rubylib/qtruby/lib/Qt.rb new file mode 100644 index 00000000..70e9c776 --- /dev/null +++ b/qtruby/rubylib/qtruby/lib/Qt.rb @@ -0,0 +1 @@ +require 'qtruby' diff --git a/qtruby/rubylib/qtruby/lib/Qt/Makefile.am b/qtruby/rubylib/qtruby/lib/Qt/Makefile.am new file mode 100644 index 00000000..267582ef --- /dev/null +++ b/qtruby/rubylib/qtruby/lib/Qt/Makefile.am @@ -0,0 +1,2 @@ +qtrubylibdir = $(RUBY_SITEDIR)/Qt +qtrubylib_DATA = qtruby.rb diff --git a/qtruby/rubylib/qtruby/lib/Qt/qtruby.rb b/qtruby/rubylib/qtruby/lib/Qt/qtruby.rb new file mode 100644 index 00000000..50f2eaef --- /dev/null +++ b/qtruby/rubylib/qtruby/lib/Qt/qtruby.rb @@ -0,0 +1,1978 @@ +=begin +/*************************************************************************** + qtruby.rb - description + ------------------- + begin : Fri Jul 4 2003 + copyright : (C) 2003 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +=end + +module Qt + module DebugLevel + Off, Minimal, High, Extensive = 0, 1, 2, 3 + end + + module QtDebugChannel + QTDB_NONE = 0x00 + QTDB_AMBIGUOUS = 0x01 + QTDB_METHOD_MISSING = 0x02 + QTDB_CALLS = 0x04 + QTDB_GC = 0x08 + QTDB_VIRTUAL = 0x10 + QTDB_VERBOSE = 0x20 + QTDB_ALL = QTDB_VERBOSE | QTDB_VIRTUAL | QTDB_GC | QTDB_CALLS | QTDB_METHOD_MISSING | QTDB_AMBIGUOUS + end + + @@debug_level = DebugLevel::Off + def Qt.debug_level=(level) + @@debug_level = level + Internal::setDebug Qt::QtDebugChannel::QTDB_ALL if level >= DebugLevel::Extensive + end + + def Qt.debug_level + @@debug_level + end + + class Base + def self.signals(*signal_list) + meta = Qt::Meta[self.name] || Qt::MetaInfo.new(self) + meta.add_signals(signal_list) + meta.changed = true + end + + def self.slots(*slot_list) + meta = Qt::Meta[self.name] || Qt::MetaInfo.new(self) + meta.add_slots(slot_list) + meta.changed = true + end + + def **(a) + return Qt::**(self, a) + end + def +(a) + return Qt::+(self, a) + end + def ~(a) + return Qt::~(self, a) + end + def -@() + return Qt::-(self) + end + def -(a) + return Qt::-(self, a) + end + def *(a) + return Qt::*(self, a) + end + def /(a) + return Qt::/(self, a) + end + def %(a) + return Qt::%(self, a) + end + def >>(a) + return Qt::>>(self, a) + end + def <<(a) + return Qt::<<(self, a) + end + def &(a) + return Qt::&(self, a) + end + def ^(a) + return Qt::^(self, a) + end + def |(a) + return Qt::|(self, a) + end + +# Module has '<', '<=', '>' and '>=' operator instance methods, so pretend they +# don't exist by calling method_missing() explicitely + def <(a) + begin + Qt::method_missing(:<, self, a) + rescue + super(a) + end + end + + def <=(a) + begin + Qt::method_missing(:<=, self, a) + rescue + super(a) + end + end + + def >(a) + begin + Qt::method_missing(:>, self, a) + rescue + super(a) + end + end + + def >=(a) + begin + Qt::method_missing(:>=, self, a) + rescue + super(a) + end + end + +# Object has a '==' operator instance method, so pretend it +# don't exist by calling method_missing() explicitely + def ==(a) + begin + Qt::method_missing(:==, self, a) + rescue + super(a) + end + end + + def methods(regular=true) + if !regular + return singleton_methods + end + + qt_methods(super, 0x0) + end + + def protected_methods + # From smoke.h, Smoke::mf_protected 0x80 + qt_methods(super, 0x80) + end + + def public_methods + methods + end + + def singleton_methods + # From smoke.h, Smoke::mf_static 0x01 + qt_methods(super, 0x01) + end + + private + def qt_methods(meths, flags) + ids = [] + # These methods are all defined in Qt::Base, even if they aren't supported by a particular + # subclass, so remove them to avoid confusion + meths -= ["%", "&", "*", "**", "+", "-", "-@", "/", "<", "<<", "<=", ">", ">=", ">>", "|", "~", "^"] + classid = Qt::Internal::idInstance(self) + Qt::Internal::getAllParents(classid, ids) + ids << classid + ids.each { |c| Qt::Internal::findAllMethodNames(meths, c, flags) } + return meths.uniq + end + end # Qt::Base + + # Delete the underlying C++ instance after exec returns + # Otherwise, rb_gc_call_finalizer_at_exit() can delete + # stuff that Qt::Application still needs for its cleanup. + class Application < Qt::Base + def exec + method_missing(:exec) + self.dispose + Qt::Internal.application_terminated = true + end + + def exit(*args) + method_missing(:exit, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class BoxLayout < Qt::Base + include Enumerable + + def each + it = iterator() + while it.current + yield it.current + it += 1 + end + end + end + + class Buffer < Qt::Base + def open(*args) + method_missing(:open, *args) + end + end + + class ButtonGroup < Qt::Base + def id(*args) + method_missing(:id, *args) + end + end + + class ByteArray < Qt::Base + def to_s + return data() + end + + def length + return size() + end + + def data=(data) + setRawData(data) + end + end + + class CheckListItem < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class ChildEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class ClassInfo < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class CloseEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Color < Qt::Base + def inspect + str = super + str.sub(/>$/, " %s>" % name) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " %s>" % name) + end + + def name(*args) + method_missing(:name, *args) + end + end + + class Connection < Qt::Base + def inspect + str = super + str.sub(/>$/, " memberName=%s, memberType=%s, object=%s>" % + [memberName.inspect, memberType == 1 ? "SLOT" : "SIGNAL", object.inspect] ) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n memberName=%s,\n memberType=%s,\n object=%s>" % + [memberName.inspect, memberType == 1 ? "SLOT" : "SIGNAL", object.inspect] ) + end + end + + class ContextMenuEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Cursor < Qt::Base + def inspect + str = super + str.sub(/>$/, " shape=%d>" % shape) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " shape=%d>" % shape) + end + end + + class CustomEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Date < Qt::Base + def inspect + str = super + str.sub(/>$/, " %s>" % toString) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " %s>" % toString) + end + end + + class DateTime < Qt::Base + def inspect + str = super + str.sub(/>$/, " %s>" % toString) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " %s>" % toString) + end + end + + class Dialog < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + end + + class DomAttr < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class DomDocumentType < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class DragLeaveEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class DropEvent < Qt::Base + def format(*args) + method_missing(:format, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class EucJpCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class EucKrCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Event < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class EventLoop < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + + def exit(*args) + method_missing(:exit, *args) + end + end + + class File < Qt::Base + def name(*args) + method_missing(:name, *args) + end + + def open(*args) + method_missing(:open, *args) + end + end + + class FocusEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Font < Qt::Base + def inspect + str = super + str.sub(/>$/, " family=%s, pointSize=%d, weight=%d, italic=%s, bold=%s, underline=%s, strikeOut=%s>" % + [family.inspect, pointSize, weight, italic, bold, underline, strikeOut]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n family=%s,\n pointSize=%d,\n weight=%d,\n italic=%s,\n bold=%s,\n underline=%s,\n strikeOut=%s>" % + [family.inspect, pointSize, weight, italic, bold, underline, strikeOut]) + end + end + + class Ftp < Qt::Base + def abort(*args) + method_missing(:abort, *args) + end + end + + class GLContext < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class GLWidget < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class Gb18030Codec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Gb2312Codec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class GbkCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class GridLayout < Qt::Base + include Enumerable + + def each + it = iterator() + while it.current + yield it.current + it += 1 + end + end + end + + class HBoxLayout < Qt::Base + include Enumerable + + def each + it = iterator() + while it.current + yield it.current + it += 1 + end + end + end + + class HebrewCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class HideEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Http < Qt::Base + def abort(*args) + method_missing(:abort, *args) + end + end + + class HttpRequestHeader < Qt::Base + def method(*args) + method_missing(:method, *args) + end + end + + class IconDrag < Qt::Base + def format(*args) + method_missing(:format, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class Image < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class ImageDecoder < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class ImageDrag < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class ImageIO < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class IMEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class JisCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class KeyEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class LCDNumber < Qt::Base + def display(item) + method_missing(:display, item) + end + end + + class Layout < Qt::Base + def freeze(*args) + method_missing(:freeze, *args) + end + end + + class LayoutIterator < Qt::Base + def +(a) + for i in 1..a + send("operator++".to_sym) + end + return self + end + end + + class Library < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class ListView < Qt::Base + include Enumerable + + def each + it = Qt::ListViewItemIterator.new(self) + while it.current + yield it.current + it += 1 + end + end + + def sort(*args) + method_missing(:sort, *args) + end + end + + class ListViewItem < Qt::Base + include Enumerable + + def each + it = Qt::ListViewItemIterator.new(self) + while it.current + yield it.current + it += 1 + end + end + + def sort(*args) + method_missing(:sort, *args) + end + + def inspect + str = super + str.sub!(/>$/, "") + for i in 0..(listView.columns - 1) + str << " text%d=%s," % [i, self.text(i)] + end + str.sub!(/,?$/, ">") + end + + def pretty_print(pp) + str = to_s + str.sub!(/>$/, "") + for i in 0..(listView.columns - 1) + str << " text%d=%s," % [i, self.text(i)] + end + str.sub!(/,?$/, ">") + pp.text str + end + end + + class Locale < Qt::Base + def name(*args) + method_missing(:name, *args) + end + def system(*args) + method_missing(:system, *args) + end + end + + class MenuItem < Qt::Base + def id(*args) + method_missing(:id, *args) + end + end + + class MetaData < Qt::Base + def method(*args) + method_missing(:method, *args) + end + + def name(*args) + method_missing(:name, *args) + end + end + + class MetaEnum < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class MetaProperty < Qt::Base + def id(*args) + method_missing(:id, *args) + end + + def name(*args) + method_missing(:name, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class MouseEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class MoveEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Object < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class PaintEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Picture < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class Pixmap < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class Point < Qt::Base + def inspect + str = super + str.sub(/>$/, " x=%d, y=%d>" % [x, y]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n x=%d,\n y=%d>" % [x, y]) + end + end + + class PolygonScanner < Qt::Base + def scan(*args) + method_missing(:scan, *args) + end + end + + class PopupMenu < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + end + + class Printer < Qt::Base + def abort(*args) + method_missing(:abort, *args) + end + end + + class MetaObject < Qt::Base + def inspect + str = super + str.sub!(/>$/, "") + str << " className=%s," % className + str << " propertyNames=Array (%d element(s))," % numProperties unless numProperties == 0 + str << " signalNames=Array (%d element(s))," % numSignals unless numSignals == 0 + str << " slotNames=Array (%d element(s))," % numSlots unless numSlots == 0 + str << " superClass=%s," % superClass.inspect unless superClass == nil + str.chop! + str << ">" + end + + def pretty_print(pp) + str = to_s + str.sub!(/>$/, "") + str << "\n className=%s," % className + str << "\n propertyNames=Array (%d element(s))," % numProperties unless numProperties == 0 + str << "\n signalNames=Array (%d element(s))," % numSignals unless numSignals == 0 + str << "\n slotNames=Array (%d element(s))," % numSlots unless numSlots == 0 + str << "\n superClass=%s," % superClass.inspect unless superClass == nil + str.chop! + str << ">" + pp.text str + end + end + + class Rect < Qt::Base + def inspect + str = super + str.sub(/>$/, " left=%d, right=%d, top=%d, bottom=%d>" % [left, right, top, bottom]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n left=%d,\n right=%d,\n top=%d,\n bottom=%d>" % [left, right, top, bottom]) + end + end + + class ResizeEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class ShowEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Size < Qt::Base + def inspect + str = super + str.sub(/>$/, " width=%d, height=%d>" % [width, height]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n width=%d,\n height=%d>" % [width, height]) + end + end + + class SizePolicy < Qt::Base + def inspect + str = super + str.sub(/>$/, " horData=%d, verData=%d>" % [horData, verData]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n horData=%d,\n verData=%d>" % [horData, verData]) + end + end + + class SjisCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Socket < Qt::Base + def open(*args) + method_missing(:open, *args) + end + end + + class SocketDevice < Qt::Base + def open(*args) + method_missing(:open, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class SocketNotifier < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class SqlCursor < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + + def name(*args) + method_missing(:name, *args) + end + + def select(*args) + method_missing(:select, *args) + end + end + + class SqlDatabase < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + def open(*args) + method_missing(:open, *args) + end + end + + class SqlDriver < Qt::Base + def open(*args) + method_missing(:open, *args) + end + end + + class SqlError < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class SqlField < Qt::Base + def name(*args) + method_missing(:name, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class SqlFieldInfo < Qt::Base + def name(*args) + method_missing(:name, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class SqlIndex < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class SqlQuery < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + end + + class SqlSelectCursor < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + + def name(*args) + method_missing(:name, *args) + end + + def select(*args) + method_missing(:select, *args) + end + end + + class StoredDrag < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class StyleSheetItem < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class TextDrag < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class TabletEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Time < Qt::Base + def inspect + str = super + str.sub(/>$/, " %s>" % toString) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " %s>" % toString) + end + end + + class TimeEdit < Qt::Base + def display + method_missing(:display) + end + end + + class TimerEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Translator < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class TranslatorMessage < Qt::Base + def hash(*args) + method_missing(:hash, *args) + end + end + + class TsciiCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class UrlInfo < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Utf16Codec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Utf8Codec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Variant < Qt::Base + String = 3 + Date = 26 + Time = 27 + DateTime = 28 + + def initialize(*args) + # In C++, the boolean constructor needs an ugly dummy int argument, + # so special case that here to avoid needing it in Ruby + if args[0] == true || args[0] == false + super(args[0], 0) + else + super + end + end + + def to_a + return toStringList() + end + + def to_f + return toDouble() + end + + def to_i + return toInt() + end + + def to_int + return toInt() + end + + def to_ruby + case type() + when Qt::Variant::Bitmap + return toBitmap + when Qt::Variant::Bool + return toBool + when Qt::Variant::Brush + return toBrush + when Qt::Variant::ByteArray + return toByteArray + when Qt::Variant::Color + return toColor + when Qt::Variant::ColorGroup + return toColorGroup + when Qt::Variant::CString + return toCString + when Qt::Variant::Cursor + return toCursor + when Qt::Variant::Date + return toDate + when Qt::Variant::DateTime + return toDateTime + when Qt::Variant::Double + return toDouble + when Qt::Variant::Font + return toFont + when Qt::Variant::IconSet + return toIconSet + when Qt::Variant::Image + return toImage + when Qt::Variant::Int + return toInt + when Qt::Variant::KeySequence + return toKeySequence + when Qt::Variant::List + return toList + when Qt::Variant::LongLong + return toLongLong + when Qt::Variant::Map + return toMap + when Qt::Variant::Palette + return toPalette + when Qt::Variant::Pen + return toPen + when Qt::Variant::Pixmap + return toPixmap + when Qt::Variant::Point + return toPoint + when Qt::Variant::PointArray + return toPointArray + when Qt::Variant::Rect + return toRect + when Qt::Variant::Region + return toRegion + when Qt::Variant::Size + return toSize + when Qt::Variant::SizePolicy + return toSizePolicy + when Qt::Variant::String + return toString + when Qt::Variant::StringList + return toStringList + when Qt::Variant::Time + return toTime + when Qt::Variant::UInt + return toUint + when Qt::Variant::ULongLong + return toULongLong + end + end + + def inspect + str = super + str.sub(/>$/, " typeName=%s>" % typeName) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " typeName=%s>" % typeName) + end + + def load(*args) + method_missing(:load, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class VBoxLayout < Qt::Base + include Enumerable + + def each + it = iterator() + while it.current + yield it.current + it += 1 + end + end + end + + class WhatsThis < Qt::Base + def WhatsThis.display(*k) + method_missing(:display, *k) + end + end + + class WheelEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Widget < Qt::Base + def raise(*args) + method_missing(:raise, *args) + end + end + + class WidgetStack < Qt::Base + def id(*args) + method_missing(:id, *args) + end + end + + class XmlAttributes < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + # Provides a mutable numeric class for passing to methods with + # C++ 'int*' or 'int&' arg types + class Integer + attr_accessor :value + def initialize(n=0) @value = n end + + def +(n) + return Integer.new(@value + n.to_i) + end + def -(n) + return Integer.new(@value - n.to_i) + end + def *(n) + return Integer.new(@value * n.to_i) + end + def /(n) + return Integer.new(@value / n.to_i) + end + def %(n) + return Integer.new(@value % n.to_i) + end + def **(n) + return Integer.new(@value ** n.to_i) + end + + def |(n) + return Integer.new(@value | n.to_i) + end + def &(n) + return Integer.new(@value & n.to_i) + end + def ^(n) + return Integer.new(@value ^ n.to_i) + end + def <<(n) + return Integer.new(@value << n.to_i) + end + def >>(n) + return Integer.new(@value >> n.to_i) + end + + def >(n) + return @value > n.to_i + end + def >=(n) + return @value >= n.to_i + end + def <(n) + return @value < n.to_i + end + def <=(n) + return @value <= n.to_i + end + + def <=>(n) + if @value < n + return -1 + elsif @value > n + return 1 + else + return 0 + end + end + + def to_f() return @value.to_f end + def to_i() return @value.to_i end + def to_s() return @value.to_s end + + def coerce(n) + [n, @value] + end + end + + # If a C++ enum was converted to an ordinary ruby Integer, the + # name of the type is lost. The enum type name is needed for overloaded + # method resolution when two methods differ only by an enum type. + class Enum < Qt::Integer + attr_accessor :type + def initialize(n, type) + super(n) + @value = n + @type = type + end + + def |(n) + return Enum.new(@value | n.to_i, @type) + end + def &(n) + return Enum.new(@value & n.to_i, @type) + end + def ^(n) + return Enum.new(@value ^ n.to_i, @type) + end + def <(n) + return @value < n.to_i + end + def <=(n) + return @value <= n.to_i + end + def >(n) + return @value > n.to_i + end + def >=(n) + return @value >= n.to_i + end + def <<(n) + return Enum.new(@value << n.to_i, @type) + end + def >>(n) + return Enum.new(@value >> n.to_i, @type) + end + + def ==(n) return @value == n.to_i end + def to_i() return @value end + + def inspect + to_s + end + + def pretty_print(pp) + pp.text "#<%s:0x%8.8x @type=%s, @value=%d>" % [self.class.name, object_id, type, value] + end + end + + # Provides a mutable boolean class for passing to methods with + # C++ 'bool*' or 'bool&' arg types + class Boolean + attr_accessor :value + def initialize(b=false) @value = b end + def nil? + return !@value + end + end + + class SignalBlockInvocation < Qt::Object + def initialize(parent, block, args) + super(parent) + self.class.slots "invoke(#{args})" + @block = block + end + + def invoke(*args) + @block.call(*args) + end + end + + class BlockInvocation < Qt::Object + def initialize(target, block, args) + super(target) + self.class.slots "invoke(#{args})" + @target = target + @block = block + end + + def invoke(*args) + @target.instance_exec(*args, &@block) + end + end + + module Internal + @@classes = {} + @@cpp_names = {} + @@idclass = [] + + def Internal.normalize_classname(classname) + if classname =~ /^Qext/ + now = classname.sub(/^Qext(?=[A-Z])/,'Qext::') + elsif classname =~ /^Q/ + now = classname.sub(/^Q(?=[A-Z])/,'Qt::') + elsif classname =~ /^(KConfigSkeleton|KWin)::/ + now = classname.sub(/^K?(?=[A-Z])/,'KDE::') + elsif classname !~ /::/ + now = classname.sub(/^K?(?=[A-Z])/,'KDE::') + else + now = classname + end + # puts "normalize_classname = was::#{classname}, now::#{now}" + now + end + + def Internal.init_class(c) + classname = Qt::Internal::normalize_classname(c) + classId = Qt::Internal.idClass(c) + insert_pclassid(classname, classId) + @@idclass[classId] = classname + @@cpp_names[classname] = c + klass = isQObject(classId) ? create_qobject_class(classname) \ + : create_qt_class(classname) + @@classes[classname] = klass unless klass.nil? + end + + def Internal.debug_level + Qt.debug_level + end + + def Internal.checkarg(argtype, typename) + puts " #{typename} (#{argtype})" if debug_level >= DebugLevel::High + if argtype == 'i' + if typename =~ /^int&?$|^signed int&?$|^signed$|^Q_INT32&?$/ + return 1 + elsif typename =~ /^(?:short|ushort|unsigned short int|uchar|uint|long|ulong|unsigned long int|unsigned|float|double|Q_UINT32|Q_UINT16|Q_INT16)$/ + return 0 + else + t = typename.sub(/^const\s+/, '') + t.sub!(/[&*]$/, '') + if isEnum(t) + return 0 + end + end + elsif argtype == 'n' + if typename =~ /^double$/ + return 2 + elsif typename =~ /^float$/ + return 1 + elsif typename =~ /^int&?$/ + return 0 + elsif typename =~ /^(?:short|ushort|uint|long|ulong|signed|unsigned|float|double)$/ + return 0 + else + t = typename.sub(/^const\s+/, '') + t.sub!(/[&*]$/, '') + if isEnum(t) + return 0 + end + end + elsif argtype == 'B' + if typename =~ /^(?:bool)[*&]?$/ + return 0 + end + elsif argtype == 's' + if typename =~ /^(const )?((QChar)[*&]?)$/ + return 1 + elsif typename =~ /^(?:u?char\*|const u?char\*|(?:const )?(Q(C?)String)[*&]?)$/ + qstring = !$1.nil? + c = ("C" == $2) + return c ? 2 : (qstring ? 3 : 0) + end + elsif argtype == 'a' + # FIXME: shouldn't be hardcoded. Installed handlers should tell what ruby type they expect. + if typename =~ /^(?: + const\ QCOORD\*| + (?:const\ )? + (?: + QStringList[\*&]?| + QValueList[\*&]?| + QRgb\*| + char\*\* + ) + )$/x + return 0 + end + elsif argtype == 'u' + # Give nil matched against string types a higher score than anything else + if typename =~ /^(?:u?char\*|const u?char\*|(?:const )?((Q(C?)String))[*&]?)$/ + return 1 + # Numerics will give a runtime conversion error, so they fail the match + elsif typename =~ /^(?:short|ushort|uint|long|ulong|signed|unsigned|int)$/ + return -99 + else + return 0 + end + elsif argtype == 'U' + if typename =~ /QStringList/ + return 1 + else + return 0 + end + else + t = typename.sub(/^const\s+/, '') + t.sub!(/[&*]$/, '') + if argtype == t + return 1 + elsif classIsa(argtype, t) + return 0 + elsif isEnum(argtype) and + (t =~ /int|Q_INT32|uint|Q_UINT32|long|ulong/ or isEnum(t)) + return 0 + end + end + return -99 + end + + def Internal.find_class(classname) + # puts @@classes.keys.sort.join "\n" + @@classes[classname] + end + + # Runs the initializer as far as allocating the Qt C++ instance. + # Then use a throw to jump back to here with the C++ instance + # wrapped in a new ruby variable of type T_DATA + def Internal.try_initialize(instance, *args) + initializer = instance.method(:initialize) + catch "newqt" do + initializer.call(*args) + end + end + + # If a block was passed to the constructor, then + # run that now. Either run the context of the new instance + # if no args were passed to the block. Or otherwise, + # run the block in the context of the arg. + def Internal.run_initializer_block(instance, block) + if block.arity == -1 + instance.instance_eval(&block) + elsif block.arity == 1 + block.call(instance) + else + raise ArgumentError, "Wrong number of arguments to block(#{block.arity} for 1)" + end + end + + def Internal.do_method_missing(package, method, klass, this, *args) + if klass.class == Module + classname = klass.name + else + classname = @@cpp_names[klass.name] + if classname.nil? + if klass != Object and klass != Qt + return do_method_missing(package, method, klass.superclass, this, *args) + else + return nil + end + end + end + + if method == "new" + method = classname.dup + method.gsub!(/^(KParts|KIO|KNS|DOM|Kontact|Kate|KTextEditor|KConfigSkeleton::ItemEnum|KConfigSkeleton|KWin)::/,"") + end + method = "operator" + method.sub("@","") if method !~ /[a-zA-Z]+/ + # Change foobar= to setFoobar() + method = 'set' + method[0,1].upcase + method[1,method.length].sub("=", "") if method =~ /.*[^-+%\/|=]=$/ + + methods = [] + methods << method.dup + args.each do |arg| + if arg.nil? + # For each nil arg encountered, triple the number of munged method + # templates, in order to cover all possible types that can match nil + temp = [] + methods.collect! do |meth| + temp << meth + '?' + temp << meth + '#' + meth << '$' + end + methods.concat(temp) + elsif isObject(arg) + methods.collect! { |meth| meth << '#' } + elsif arg.kind_of? Array or arg.kind_of? Hash + methods.collect! { |meth| meth << '?' } + else + methods.collect! { |meth| meth << '$' } + end + end + + methodIds = [] + methods.collect { |meth| methodIds.concat( findMethod(classname, meth) ) } + + if method =~ /_/ && methodIds.length == 0 + # If the method name contains underscores, convert to camel case + # form and try again + method.gsub!(/(.)_(.)/) {$1 + $2.upcase} + return do_method_missing(package, method, klass, this, *args) + end + + if debug_level >= DebugLevel::High + puts "classname == #{classname}" + puts ":: method == #{method}" + puts "-> methodIds == #{methodIds.inspect}" + puts "candidate list:" + prototypes = dumpCandidates(methodIds).split("\n") + line_len = (prototypes.collect { |p| p.length }).max + prototypes.zip(methodIds) { + |prototype,id| puts "#{prototype.ljust line_len} (#{id})" + } + end + + chosen = nil + if methodIds.length > 0 + best_match = -1 + methodIds.each do + |id| + puts "matching => #{id}" if debug_level >= DebugLevel::High + current_match = 0 + (0...args.length).each do + |i| + current_match += checkarg( getVALUEtype(args[i]), getTypeNameOfArg(id, i) ) + end + + # Note that if current_match > best_match, then chosen must be nil + if current_match > best_match + best_match = current_match + chosen = id + # Multiple matches are an error; the equality test below _cannot_ be commented out. + # If ambiguous matches occur the problem must be fixed be adjusting the relative + # ranking of the arg types involved in checkarg(). + elsif current_match == best_match + chosen = nil + end + puts "match => #{id} score: #{current_match}" if debug_level >= DebugLevel::High + end + + puts "Resolved to id: #{chosen}" if !chosen.nil? && debug_level >= DebugLevel::High + end + + if debug_level >= DebugLevel::Minimal && chosen.nil? && method !~ /^operator/ + id = find_pclassid(normalize_classname(klass.name)) + hash = findAllMethods(id) + constructor_names = nil + if method == classname + puts "No matching constructor found, possibles:\n" + constructor_names = hash.keys.grep(/^#{classname}/) + else + puts "Possible prototypes:" + constructor_names = hash.keys + end + method_ids = hash.values_at(*constructor_names).flatten + puts dumpCandidates(method_ids) + end + + puts "setCurrentMethod(#{chosen})" if debug_level >= DebugLevel::High + setCurrentMethod(chosen) if chosen + return nil + end + + def Internal.init_all_classes() + Qt::Internal::getClassList().each do |c| + if c == "Qt" + # Don't change Qt to Qt::t, just leave as is + @@cpp_names["Qt"] = c + elsif c != "QInternal" + Qt::Internal::init_class(c) + end + end + + @@classes['Qt::Integer'] = Qt::Integer + @@classes['Qt::Boolean'] = Qt::Boolean + @@classes['Qt::Enum'] = Qt::Enum + end + + def Internal.get_qinteger(num) + return num.value + end + + def Internal.set_qinteger(num, val) + return num.value = val + end + + def Internal.create_qenum(num, type) + return Qt::Enum.new(num, type) + end + + def Internal.get_qenum_type(e) + return e.type + end + + def Internal.get_qboolean(b) + return b.value + end + + def Internal.set_qboolean(b, val) + return b.value = val + end + + def Internal.getAllParents(class_id, res) + getIsa(class_id).each do |s| + c = idClass(s) + res << c + getAllParents(c, res) + end + end + + def Internal.getSignalNames(klass) + meta = Meta[klass.name] || MetaInfo.new(klass) + signal_names = [] + meta.get_signals.each do |signal| + signal_names.push signal.name + end + return signal_names + end + + def Internal.signalInfo(qobject, signal_name) + signals = Meta[qobject.class.name].get_signals + signals.each_with_index do |signal, i| + if signal.name == signal_name + return [signal.full_name, i] + end + end + end + + def Internal.signalAt(qobject, index) + klass = qobject.class + begin + meta = Meta[klass.name] + klass = klass.superclass + end while meta.nil? and klass != Object + meta.get_signals[index].full_name + end + + def Internal.slotAt(qobject, index) + klass = qobject.class + begin + meta = Meta[klass.name] + klass = klass.superclass + end while meta.nil? and klass != Object + meta.get_slots[index].full_name + end + + def Internal.getMocArguments(member) + argStr = member.sub(/.*\(/, '').sub(/\)$/, '') + args = argStr.scan(/([^,]*<[^>]+>)|([^,]+)/) + mocargs = allocateMocArguments(args.length) + args.each_with_index do |arg, i| + arg = arg.to_s + a = arg.sub(/^const\s+/, '') + a = (a =~ /^(bool|int|double|char\*|QString)&?$/) ? $1 : 'ptr' + valid = setMocType(mocargs, i, arg, a) + end + result = [] + result << args.length << mocargs + result + end + + def Internal.makeMetaData(data) + return nil if data.nil? + tbl = [] + data.each do |entry| + name = entry.name + argStr = entry.arg_types + params = [] + args = argStr.scan(/[^,]+/) + args.each do |arg| + name = '' # umm.. is this the aim?, well. it works. soo... ;-) + param = make_QUParameter(name, arg, 0, 1) + params << param + end + method = make_QUMethod(name, params) + tbl << make_QMetaData(entry.full_name, method) + end + make_QMetaData_tbl(tbl) + end + + def Internal.getMetaObject(qobject) + klass = qobject.class + begin + meta = Meta[klass.name] + klass = klass.superclass + end while meta.nil? and klass != Object + + return nil if meta.nil? + + if meta.metaobject.nil? or meta.changed + slots = meta.get_slots + slotTable = makeMetaData(slots) + signals = meta.get_signals + signalTable = makeMetaData(signals) + meta.metaobject = make_metaObject(qobject.class.name, + qobject.staticMetaObject(), + slotTable, + slots.length, + signalTable, + signals.length) + addSignalMethods(qobject.class, getSignalNames(qobject.class)) + meta.changed = false + end + + meta.metaobject + end + + def Internal.connect(src, signal, target, block) + signature = (signal =~ /\((.*)\)/) ? $1 : "" + return Qt::Object.connect( src, + signal, + Qt::BlockInvocation.new(target, block, signature), + SLOT("invoke(#{signature})") ) + end + + def Internal.signal_connect(src, signal, block) + signature = (signal =~ /\((.*)\)/) ? $1 : "" + return Qt::Object.connect( src, + signal, + Qt::SignalBlockInvocation.new(src, block, signature), + SLOT("invoke(#{signature})") ) + end + end # Qt::Internal + + Meta = {} + + # An entry for each signal or slot + # Example + # foobar(QString,bool) + # :name is 'foobar' + # :full_name is 'foobar(QString,bool)' + # :arg_types is 'QString,bool' + QObjectMember = Struct.new :name, :full_name, :arg_types + + class MetaInfo + attr_accessor :signals, :slots, :metaobject, :mocargs, :changed + def initialize(klass) + Meta[klass.name] = self + @klass = klass + @metaobject = nil + @signals = [] + @slots = [] + @changed = false + Internal.addMetaObjectMethods(klass) + end + + def add_signals(signal_list) + signal_list.each do |signal| + if signal.kind_of? Symbol + signal = signal.to_s + "()" + end + signal = Qt::Object.normalizeSignalSlot(signal) + if signal =~ /([^\s]*)\((.*)\)/ + @signals.push QObjectMember.new($1, signal, $2) + else + qWarning( "#{@klass.name}: Invalid signal format: '#{signal}'" ) + end + end + end + + # Return a list of signals, including inherited ones + def get_signals + all_signals = [] + current = @klass + while current != Qt::Base + meta = Meta[current.name] + if !meta.nil? + all_signals.concat meta.signals + end + current = current.superclass + end + return all_signals + end + + def add_slots(slot_list) + slot_list.each do |slot| + if slot.kind_of? Symbol + slot = slot.to_s + "()" + end + slot = Qt::Object.normalizeSignalSlot(slot) + if slot =~ /([^\s]*)\((.*)\)/ + @slots.push QObjectMember.new($1, slot, $2) + else + qWarning( "#{@klass.name}: Invalid slot format: '#{slot}'" ) + end + end + end + + # Return a list of slots, including inherited ones + def get_slots + all_slots = [] + current = @klass + while current != Qt::Base + meta = Meta[current.name] + if !meta.nil? + all_slots.concat meta.slots + end + current = current.superclass + end + return all_slots + end + end # Qt::MetaInfo + + IO_Direct = 0x0100 + IO_Sequential = 0x0200 + IO_Combined = 0x0300 + IO_TypeMask = 0x0f00 + IO_Raw = 0x0040 + IO_Async = 0x0080 + IO_ReadOnly = 0x0001 + IO_WriteOnly = 0x0002 + IO_ReadWrite = 0x0003 + IO_Append = 0x0004 + IO_Truncate = 0x0008 + IO_Translate = 0x0010 + IO_ModeMask = 0x00ff + IO_Open = 0x1000 + IO_StateMask = 0xf000 + IO_Ok = 0 + IO_ReadError = 1 + IO_WriteError = 2 + IO_FatalError = 3 + IO_ResourceError = 4 + IO_OpenError = 5 + IO_ConnectError = 5 + IO_AbortError = 6 + IO_TimeOutError = 7 + IO_UnspecifiedError= 8 + +end # Qt + +class Object + def SIGNAL(signal) + if signal.kind_of? Symbol + return "2" + signal.to_s + "()" + else + return "2" + signal + end + end + + def SLOT(slot) + if slot.kind_of? Symbol + return "1" + slot.to_s + "()" + else + return "1" + slot + end + end + + def emit(signal) + return signal + end + + # See the discussion here: http://eigenclass.org/hiki.rb?instance_exec + # about implementations of the ruby 1.9 method instance_exec(). This + # version is the one from Rails. It isn't thread safe, but that doesn't + # matter for the intended use in invoking blocks as Qt slots. + def instance_exec(*arguments, &block) + block.bind(self)[*arguments] + end +end + +class Proc + # Part of the Rails Object#instance_exec implementation + def bind(object) + block, time = self, Time.now + (class << object; self end).class_eval do + method_name = "__bind_#{time.to_i}_#{time.usec}" + define_method(method_name, &block) + method = instance_method(method_name) + remove_method(method_name) + method + end.bind(object) + end +end + +class Module + alias_method :_constants, :constants + alias_method :_instance_methods, :instance_methods + alias_method :_protected_instance_methods, :protected_instance_methods + alias_method :_public_instance_methods, :public_instance_methods + + private :_constants, :_instance_methods + private :_protected_instance_methods, :_public_instance_methods + + def constants + qt_methods(_constants, 0x10, true) + end + + def instance_methods(inc_super=true) + qt_methods(_instance_methods(inc_super), 0x0, inc_super) + end + + def protected_instance_methods(inc_super=true) + qt_methods(_protected_instance_methods(inc_super), 0x80, inc_super) + end + + def public_instance_methods(inc_super=true) + qt_methods(_public_instance_methods(inc_super), 0x0, inc_super) + end + + private + def qt_methods(meths, flags, inc_super=true) + if !self.kind_of? Class + return meths + end + + klass = self + classid = 0 + loop do + classid = Qt::Internal::find_pclassid(klass.name) + break if classid > 0 + + klass = klass.superclass + if klass.nil? + return meths + end + end + + # These methods are all defined in Qt::Base, even if they aren't supported by a particular + # subclass, so remove them to avoid confusion + meths -= ["%", "&", "*", "**", "+", "-", "-@", "/", "<", "<<", "<=", ">", ">=", ">>", "|", "~", "^"] + ids = [] + if inc_super + Qt::Internal::getAllParents(classid, ids) + end + ids << classid + ids.each { |c| Qt::Internal::findAllMethodNames(meths, c, flags) } + return meths.uniq + end +end diff --git a/qtruby/rubylib/qtruby/marshall.h b/qtruby/rubylib/qtruby/marshall.h new file mode 100644 index 00000000..cf20b86d --- /dev/null +++ b/qtruby/rubylib/qtruby/marshall.h @@ -0,0 +1,46 @@ +#ifndef MARSHALL_H +#define MARSHALL_H + +#include +#include + +class SmokeType; + +class Marshall { +public: + /** + * FromVALUE is used for virtual function return values and regular + * method arguments. + * + * ToVALUE is used for method return-values and virtual function + * arguments. + */ + typedef void (*HandlerFn)(Marshall *); + enum Action { FromVALUE, ToVALUE }; + virtual SmokeType type() = 0; + virtual Action action() = 0; + virtual Smoke::StackItem &item() = 0; + virtual VALUE * var() = 0; + virtual void unsupported() = 0; + virtual Smoke *smoke() = 0; + /** + * For return-values, next() does nothing. + * For FromRV, next() calls the method and returns. + * For ToRV, next() calls the virtual function and returns. + * + * Required to reset Marshall object to the state it was + * before being called when it returns. + */ + virtual void next() = 0; + /** + * For FromSV, cleanup() returns false when the handler should free + * any allocated memory after next(). + * + * For ToSV, cleanup() returns true when the handler should delete + * the pointer passed to it. + */ + virtual bool cleanup() = 0; + + virtual ~Marshall() {} +}; +#endif diff --git a/qtruby/rubylib/qtruby/qtruby.h b/qtruby/rubylib/qtruby/qtruby.h new file mode 100644 index 00000000..66f9fedc --- /dev/null +++ b/qtruby/rubylib/qtruby/qtruby.h @@ -0,0 +1,55 @@ +/*************************************************************************** + qtruby.h - description + ------------------- + begin : Fri Jul 4 2003 + copyright : (C) 2003 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QTRUBY_H +#define QTRUBY_H + +#include "marshall.h" + +struct smokeruby_object { + bool allocated; + Smoke *smoke; + int classId; + void *ptr; +}; + +struct TypeHandler { + const char *name; + Marshall::HandlerFn fn; +}; + +extern int do_debug; // evil +extern VALUE rv_qapp; +extern int object_count; + +// keep this enum in sync with lib/Qt/debug.pm + +enum QtDebugChannel { + qtdb_none = 0x00, + qtdb_ambiguous = 0x01, + qtdb_method_missing = 0x02, + qtdb_calls = 0x04, + qtdb_gc = 0x08, + qtdb_virtual = 0x10, + qtdb_verbose = 0x20 +}; + +void unmapPointer(smokeruby_object *, Smoke::Index, void*); +smokeruby_object *value_obj_info(VALUE value); +VALUE getPointerObject(void *ptr); + +#endif diff --git a/qtruby/rubylib/qtruby/smokeruby.h b/qtruby/rubylib/qtruby/smokeruby.h new file mode 100644 index 00000000..8f152c10 --- /dev/null +++ b/qtruby/rubylib/qtruby/smokeruby.h @@ -0,0 +1,321 @@ +#ifndef SMOKERUBY_H +#define SMOKERUBY_H + +#include "smoke.h" + +#undef DEBUG +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#include "ruby.h" + +#include + +#include "qtruby.h" +#include "marshall.h" + + +class SmokeRuby; + +class SmokeType { + Smoke::Type *_t; // derived from _smoke and _id, but cached + + Smoke *_smoke; + Smoke::Index _id; +public: + SmokeType() : _t(0), _smoke(0), _id(0) {} + SmokeType(Smoke *s, Smoke::Index i) : _smoke(s), _id(i) { + if(_id < 0 || _id > _smoke->numTypes) _id = 0; + _t = _smoke->types + _id; + } + // default copy constructors are fine, this is a constant structure + + // mutators + void set(Smoke *s, Smoke::Index i) { + _smoke = s; + _id = i; + _t = _smoke->types + _id; + } + + // accessors + Smoke *smoke() const { return _smoke; } + Smoke::Index typeId() const { return _id; } + const Smoke::Type &type() const { return *_t; } + unsigned short flags() const { return _t->flags; } + unsigned short elem() const { return _t->flags & Smoke::tf_elem; } + const char *name() const { return _t->name; } + Smoke::Index classId() const { return _t->classId; } + + // tests + bool isStack() const { return ((flags() & Smoke::tf_ref) == Smoke::tf_stack); } + bool isPtr() const { return ((flags() & Smoke::tf_ref) == Smoke::tf_ptr); } + bool isRef() const { return ((flags() & Smoke::tf_ref) == Smoke::tf_ref); } + bool isConst() const { return (flags() & Smoke::tf_const); } + bool isClass() const { + if(elem() == Smoke::t_class) + return classId() ? true : false; + return false; + } + + bool operator ==(const SmokeType &b) const { + const SmokeType &a = *this; + if(a.name() == b.name()) return true; + if(a.name() && b.name() && qstrcmp(a.name(), b.name()) == 0) + return true; + return false; + } + bool operator !=(const SmokeType &b) const { + const SmokeType &a = *this; + return !(a == b); + } + +}; + +class SmokeClass { + Smoke::Class *_c; + Smoke *_smoke; + Smoke::Index _id; +public: + SmokeClass(const SmokeType &t) { + _smoke = t.smoke(); + _id = t.classId(); + _c = _smoke->classes + _id; + } + SmokeClass(Smoke *smoke, Smoke::Index id) : _smoke(smoke), _id(id) { + _c = _smoke->classes + _id; + } + + Smoke *smoke() const { return _smoke; } + const Smoke::Class &c() const { return *_c; } + Smoke::Index classId() const { return _id; } + const char *className() const { return _c->className; } + Smoke::ClassFn classFn() const { return _c->classFn; } + Smoke::EnumFn enumFn() const { return _c->enumFn; } + bool operator ==(const SmokeClass &b) const { + const SmokeClass &a = *this; + if(a.className() == b.className()) return true; + if(a.className() && b.className() && qstrcmp(a.className(), b.className()) == 0) + return true; + return false; + } + bool operator !=(const SmokeClass &b) const { + const SmokeClass &a = *this; + return !(a == b); + } + bool isa(const SmokeClass &sc) const { + // This is a sick function, if I do say so myself + if(*this == sc) return true; + Smoke::Index *parents = _smoke->inheritanceList + _c->parents; + for(int i = 0; parents[i]; i++) { + if(SmokeClass(_smoke, parents[i]).isa(sc)) return true; + } + return false; + } + + unsigned short flags() const { return _c->flags; } + bool hasConstructor() const { return flags() & Smoke::cf_constructor; } + bool hasCopy() const { return flags() & Smoke::cf_deepcopy; } + bool hasVirtual() const { return flags() & Smoke::cf_virtual; } + bool hasFire() const { return !(flags() & Smoke::cf_undefined); } +}; + +class SmokeMethod { + Smoke::Method *_m; + Smoke *_smoke; + Smoke::Index _id; +public: + SmokeMethod(Smoke *smoke, Smoke::Index id) : _smoke(smoke), _id(id) { + _m = _smoke->methods + _id; + } + + Smoke *smoke() const { return _smoke; } + const Smoke::Method &m() const { return *_m; } + SmokeClass c() const { return SmokeClass(_smoke, _m->classId); } + const char *name() const { return _smoke->methodNames[_m->name]; } + int numArgs() const { return _m->numArgs; } + unsigned short flags() const { return _m->flags; } + SmokeType arg(int i) const { + if(i >= numArgs()) return SmokeType(); + return SmokeType(_smoke, _smoke->argumentList[_m->args + i]); + } + SmokeType ret() const { return SmokeType(_smoke, _m->ret); } + Smoke::Index methodId() const { return _id; } + Smoke::Index method() const { return _m->method; } + + bool isStatic() const { return flags() & Smoke::mf_static; } + bool isConst() const { return flags() & Smoke::mf_const; } + + void call(Smoke::Stack args, void *ptr = 0) const { + Smoke::ClassFn fn = c().classFn(); + (*fn)(method(), ptr, args); + } +}; + +class Smoke_MAGIC { // to be rewritten + SmokeClass _c; + void *_ptr; + bool _isAllocated; +public: + Smoke_MAGIC(void *p, const SmokeClass &c) : + _c(c), _ptr(p), _isAllocated(false) {} + const SmokeClass &c() const { return _c; } + void *ptr() const { return _ptr; } + bool isAllocated() const { return _isAllocated; } + void setAllocated(bool isAllocated) { _isAllocated = isAllocated; } +}; + + +/** + * SmokeObject is a thin wrapper around VALUE objects. Each SmokeObject instance + * increments the refcount of its VALUE for the duration of its existance. + * + * SmokeObject instances are only returned from SmokeRuby, since the method + * of binding data to the scalar must be consistent across all modules. + */ +class SmokeObject { + VALUE rv; + Smoke_MAGIC *m; + +public: + SmokeObject(VALUE obj, Smoke_MAGIC *mag) : rv(obj), m(mag) { + rb_gc_register_address(&rv); + } + + ~SmokeObject() { + rb_gc_unregister_address(&rv); + } + + SmokeObject(const SmokeObject &other) { + rv = other.rv; + m = other.m; + rb_gc_register_address(&rv); + } + + SmokeObject &operator =(const SmokeObject &other) { + rv = other.rv; + m = other.m; + rb_gc_register_address(&rv); + return *this; + } + + const SmokeClass &c() { return m->c(); } + Smoke *smoke() { return c().smoke(); } + VALUE * var() { return &rv; } + void *ptr() { return m->ptr(); } + Smoke::Index classId() { return c().classId(); } + void *cast(const SmokeClass &toc) { + return smoke()->cast( + ptr(), + classId(), + smoke()->idClass(toc.className()) + ); + } + const char *className() { return c().className(); } + + bool isValid() const { return !NIL_P(rv); } + bool isAllocated() const { return m->isAllocated(); } + void setAllocated(bool i) { m->setAllocated(i); } +}; + + +/** + * Since it's not easy to share functions between Ruby modules, the common + * interface between all Smoked libraries and Ruby will be defined in this + * class. There will be only one SmokeRuby instance loaded for an entire Ruby + * process. It has no data members here -- this is only an abstract interface. + */ + +class SmokeRuby { + void *future_extension; +public: + SmokeRuby() : future_extension(0) {} + + // don't need this, we're only defining an interface + virtual ~SmokeRuby() = 0; + + /** + * Registers a Smoke object + */ + virtual void registerSmoke(const char *name, Smoke *smoke) = 0; + + /** + * Gets a smoke object from its name + */ + virtual Smoke *getSmoke(const char *name) = 0; + + /** + * Determines if the named smoke is registered. + */ + bool isSmokeRegistered(const char *name) { return getSmoke(name) ? true : false; } + + virtual void registerHandlers(TypeHandler *handlers) = 0; + + /** + * Returns a new blessed SV referring to the pointer passed. + * Use sv_2mortal() before passing it around. + * + * @param p pointer to the C++ object. The pointer isn't automatically deleted by SmokePerl. + * @param c class of the pointer + * @see #getObject + * @see #deleteObject + */ + virtual SmokeObject newObject(void *p, const SmokeClass &c) = 0; + + /** + * Same as newObject(), except it doesn't treat p as owned by Perl + */ + virtual SmokeObject wrapObject(void *p, const SmokeClass &c) = 0; + + /** + * Any SV* created with newObject() on a class with virtual methods can be + * retrieved again. + */ + virtual SmokeObject getObject(void *p) = 0; + + /** + * Create a SmokeObject from the given VALUE + */ + virtual SmokeObject getObject(VALUE value) = 0; +}; + +/* + * Type handling by moc is simple. + * + * If the type name matches /^(?:const\s+)?\Q$types\E&?$/, use the + * static_QUType, where $types is join('|', qw(bool int double char* QString); + * + * Everything else is passed as a pointer! There are types which aren't + * Smoke::tf_ptr but will have to be passed as a pointer. Make sure to keep + * track of what's what. + */ + +/* + * Simply using typeids isn't enough for signals/slots. It will be possible + * to declare signals and slots which use arguments which can't all be + * found in a single smoke object. Instead, we need to store smoke => typeid + * pairs. We also need additional informatation, such as whether we're passing + * a pointer to the union element. + */ + +enum MocArgumentType { + xmoc_ptr, + xmoc_bool, + xmoc_int, + xmoc_double, + xmoc_charstar, + xmoc_QString +}; + +struct MocArgument { + // smoke object and associated typeid + SmokeType st; + MocArgumentType argType; +}; + +#endif // SMOKERUBY_H -- cgit v1.2.3