/*************************************************************************** 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 TQT_VERSION_STR #define TQT_VERSION_STR "Unknown" #endif #undef free #undef malloc #include "marshall.h" #include "qtruby.h" #include "smokeruby.h" #include "smoke.h" #define HAVE_STRLCAT_PROTO 1 #define HAVE_STRLCPY_PROTO 1 #include "config.h" #ifndef HAVE_RUBY_1_9 #define RARRAY_LEN(x) (RARRAY(x)->len) #define RSTRING_LEN(x) (RSTRING(x)->len) #define rb_str_catf_1 rb_str_catf #endif // #define DEBUG #define TQTRUBY_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 TQPtrDict pointer_map(2179); int object_count = 0; TQAsciiDict methcache(2179); TQAsciiDict classcache(2179); // Maps from a classname in the form TQt::Widget to an int id TQIntDict classname(2179); extern "C" { VALUE qt_module = Qnil; VALUE qext_scintilla_module = Qnil; VALUE kde_module = Qnil; VALUE tdeparts_module = Qnil; VALUE tdeio_module = Qnil; VALUE kns_module = Qnil; VALUE dom_module = Qnil; VALUE kontact_module = Qnil; VALUE kate_module = Qnil; VALUE tdetexteditor_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 tdeconfigskeleton_class = Qnil; VALUE tdeconfigskeleton_itemenum_class = Qnil; VALUE tdeconfigskeleton_itemenum_choice_class = Qnil; VALUE tdeio_udsatom_class = Qnil; VALUE twin_class = Qnil; VALUE konsole_part_class = Qnil; bool application_terminated = false; }; #define logger logger_backend void rb_str_catf_1(VALUE self, const char *format, ...) __attribute__ ((format (printf, 2, 3))); static VALUE (*_new_kde)(int, VALUE *, VALUE) = 0; static VALUE (*_tdeconfigskeletonitem_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 isTQObject(Smoke *smoke, Smoke::Index classId) { if(tqstrcmp(smoke->classes[classId].className, "TQObject") == 0) return true; for(Smoke::Index *p = smoke->inheritanceList + smoke->classes[classId].parents; *p; p++) { if(isTQObject(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; tqWarning("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; tqWarning("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(), tqstrcmp(_smoke->className(method().classId), "TQGlobalSpace") == 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 (tqstrcmp(_smoke->className(method().classId), "TQGlobalSpace") == 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; TQString 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 UnencapsulatedTQObject : public TQObject { public: TQConnectionList *public_receivers(int signal) const { return receivers(signal); } void public_activate_signal(TQConnectionList *clist, TQUObject *o) { activate_signal(clist, o); } }; class EmitSignal : public Marshall { UnencapsulatedTQObject *_qobj; int _id; MocArgument *_args; VALUE *_sp; int _items; int _cur; Smoke::Stack _stack; bool _called; public: EmitSignal(TQObject *qobj, int id, int items, VALUE args, VALUE *sp) : _qobj((UnencapsulatedTQObject*)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; TQConnectionList *clist = _qobj->public_receivers(_id); if(!clist) return; TQUObject *o = new TQUObject[_items + 1]; for(int i = 0; i < _items; i++) { TQUObject *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_TQString.set(po, *(TQString*)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; TQUObject *_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++) { TQUObject *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_TQString.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, TQUObject *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) { tqWarning("%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]; TQCString 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"; } tqWarning( "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 tqWarning("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_1(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) { tqDebug("%s", StringValuePtr(msg)); return klass; } static VALUE qfatal(VALUE klass, VALUE msg) { tqFatal("%s", StringValuePtr(msg)); return klass; } static VALUE qwarning(VALUE klass, VALUE msg) { tqWarning("%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 TQObject, then check to see if the // className from its TQMetaObject 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 TQt:: // or KDE:: modules respectively. if (isDerivedFrom(o->smoke, o->classId, o->smoke->idClass("TQObject"))) { TQObject * qobject = (TQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject")); TQMetaObject * 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; TQString className(meta->className()); // The konsolePart class is in tdebase, 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 TQt::Object.metaObject method which will do dynamic despatch on the // metaObject() virtual method so that the true TQMetaObject 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) { const 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 || tqstrcmp(classname, "TQt::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 || tqstrcmp(classname, "TQt::Boolean") == 0) r = "B"; else if(tqstrcmp(classname, "TQt::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_1(r, "static "); rb_str_catf_1(r, "%s ", (tname ? tname:"void")); rb_str_catf_1(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_1(r, ", "); tname = qt_Smoke->types[qt_Smoke->argumentList[meth.args+i]].name; rb_str_catf_1(r, "%s", (tname ? tname:"void")); } rb_str_catf_1(r, ")"); if(meth.flags & Smoke::mf_const) rb_str_catf_1(r, " const"); return r; } //---------- Ruby methods (for all functions except fully qualified statics & enums) --------- // Used to display debugging info about the signals a TQt::Object has connected. // Returns a Hash with keys of the signals names, and values of Arrays of // TQt::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); UnencapsulatedTQObject * qobject = (UnencapsulatedTQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject")); VALUE result = rb_hash_new(); TQStrList signalNames = qobject->metaObject()->signalNames(true); for (int sig = 0; sig < qobject->metaObject()->numSignals(true); sig++) { TQConnectionList * clist = qobject->public_receivers(sig); if (clist != 0) { VALUE name = rb_str_new2(signalNames.at(sig)); VALUE members = rb_ary_new(); for ( TQConnection * 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("TQConnection"); c->smoke = o->smoke; c->ptr = connection; c->allocated = false; obj = set_obj_info("TQt::Connection", c); } rb_ary_push(members, obj); } rb_hash_aset(result, name, members); } } return result; } // Takes a variable name and a TQProperty with TQVariant value, and returns a ' // variable=value' pair with the value in ruby inspect style static TQCString inspectProperty(Smoke * smoke, const TQMetaProperty * property, const char * name, TQVariant & value) { if (property->isEnumType()) { TQMetaObject * metaObject = *(property->meta); return TQCString().sprintf( " %s=%s::%s", name, smoke->binding->className(smoke->idClass(metaObject->className())), property->valueToKey(value.toInt()) ); } switch (value.type()) { case TQVariant::String: case TQVariant::CString: { if (value.toString().isNull()) { return TQCString().sprintf(" %s=nil", name); } else { return TQCString().sprintf( " %s=\"%s\"", name, value.toString().latin1() ); } } case TQVariant::Bool: { TQString rubyName; TQRegExp name_re("^(is|has)(.)(.*)"); if (name_re.search(name) != -1) { rubyName = name_re.cap(2).lower() + name_re.cap(3) + "?"; } else { rubyName = name; } return TQCString().sprintf(" %s=%s", rubyName.latin1(), value.toString().latin1()); } case TQVariant::Color: { TQColor c = value.toColor(); return TQCString().sprintf(" %s=#", name, c.name().latin1()); } case TQVariant::Cursor: { TQCursor c = value.toCursor(); return TQCString().sprintf(" %s=#", name, c.shape()); } case TQVariant::Double: { return TQCString().sprintf(" %s=%.4f", name, value.toDouble()); } case TQVariant::Font: { TQFont f = value.toFont(); return TQCString().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 TQVariant::Point: { TQPoint p = value.toPoint(); return TQCString().sprintf( " %s=#", name, p.x(), p.y() ); } case TQVariant::Rect: { TQRect r = value.toRect(); return TQCString().sprintf( " %s=#", name, r.left(), r.right(), r.top(), r.bottom() ); } case TQVariant::Size: { TQSize s = value.toSize(); return TQCString().sprintf( " %s=#", name, s.width(), s.height() ); } case TQVariant::SizePolicy: { TQSizePolicy s = value.toSizePolicy(); return TQCString().sprintf( " %s=#", name, s.horData(), s.verData() ); } case TQVariant::Brush: case TQVariant::ColorGroup: case TQVariant::Image: case TQVariant::Palette: case TQVariant::Pixmap: case TQVariant::Region: { return TQCString().sprintf(" %s=#", name, value.typeName() + 1); } default: return TQCString().sprintf( " %s=%s", name, (value.isNull() || value.toString().isNull()) ? "nil" : value.toString().latin1() ); } } // Retrieves the properties for a TQObject 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_LEN(inspect_str) - 1); smokeruby_object * o = 0; Data_Get_Struct(self, smokeruby_object, o); TQObject * qobject = (TQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject")); TQCString value_list; value_list.append(TQCString().sprintf(" name=\"%s\"", qobject->name())); if (qobject->isWidgetType()) { TQWidget * w = (TQWidget *) qobject; value_list.append(TQCString().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 TQObject 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_LEN(inspect_str) - 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); UnencapsulatedTQObject * qobject = (UnencapsulatedTQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject")); TQStrList names = qobject->metaObject()->propertyNames(true); TQCString value_list; if (qobject->parent() != 0) { TQCString 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_LEN(parent_inspect_str) - 1); parentInspectString = StringValuePtr(parent_inspect_str); } else { parentInspectString.sprintf("#<%s:0x0", qobject->parent()->className()); } if (qobject->parent()->isWidgetType()) { TQWidget * w = (TQWidget *) qobject->parent(); value_list = TQCString().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 = TQCString().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 = TQCString().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 = TQCString(" metaObject=#metaObject()->className())); if (qobject->metaObject()->superClass() != 0) { value_list.append(TQCString().sprintf(", superClass=#", qobject->metaObject()->superClass())); } if (qobject->metaObject()->numSignals() > 0) { value_list.append(TQCString().sprintf(", signalNames=Array (%d element(s))", qobject->metaObject()->numSignals())); } if (qobject->metaObject()->numSlots() > 0) { value_list.append(TQCString().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++) { TQConnectionList * clist = qobject->public_receivers(sig); if (clist != 0) { signalCount++; } } if (signalCount > 0) { value_list = TQCString().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) { TQVariant value = qobject->property(name); const TQMetaProperty * 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); TQObject * qobject = (TQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject")); TQMetaObject * 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("TQMetaObject"); m->ptr = meta; m->allocated = false; obj = set_obj_info("TQt::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("TQVariant?"); Smoke::Index meth = qt_Smoke->findMethod(qt_Smoke->idClass("TQVariant"), 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 (tqstrcmp(argType, "const TQValueList&" ) == 0) { new_qvariant_qlist = qt_Smoke->ambiguousMethodList[i]; } else if (tqstrcmp(argType, "const TQStringVariantMap&" ) == 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_LEN(argv[0]) > 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 TQCString * find_cached_selector(int argc, VALUE * argv, VALUE klass, const char * methodName) { // Look in the cache static TQCString * mcid = 0; if (mcid == 0) { mcid = new TQCString(); } *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, TQString("is")); Smoke::Index meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, pred->latin1()); if (meth == 0) { pred->replace(0, 2, TQString("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]; } { TQCString * 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) { const char * op = rb_id2name(SYM2ID(argv[0])); if ( tqstrcmp(op, "-") == 0 || tqstrcmp(op, "+") == 0 || tqstrcmp(op, "/") == 0 || tqstrcmp(op, "%") == 0 || tqstrcmp(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("TQObject")) ) { TQObject * qobject = (TQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("TQObject")); static TQString * prop = 0; if (prop == 0) { prop = new TQString(); } *prop = rb_id2name(SYM2ID(argv[0])); TQMetaObject * meta = qobject->metaObject(); if (argc == 1) { if (prop->endsWith("?")) { prop->replace(0, 1, pred->at(0).upper()); prop->replace(0, 0, TQString("is")); if (meta->findProperty(prop->latin1(), true) == -1) { prop->replace(0, 2, TQString("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; const 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]; } { TQCString * 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 TQRegExp * rx = 0; if (rx == 0) { rx = new TQRegExp("[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 < TQt::Widget def initialize(s, parent, name) super(parent, name) init() ... For a case such as the above, the TQWidget 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 TQWidget 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 TQCString * 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_LEN(temp)); 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 < tqApp->argc(); index++) { rb_ary_push(result, rb_str_new2(tqApp->argv()[index])); } return result; } //----------------- Sig/Slot ------------------ VALUE getmetainfo(VALUE self, int &offset, int &index) { const char * signalname = rb_id2name(rb_frame_this_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; TQMetaObject *metaobject = (TQMetaObject*)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; TQMetaObject *metaobject = (TQMetaObject*)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); TQObject *qobj = (TQObject*)o->smoke->cast( o->ptr, o->classId, o->smoke->idClass("TQObject") ); 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 tqt_invoke(int /*argc*/, VALUE * argv, VALUE self) { // Arguments: int id, TQUObject *o int id = NUM2INT(argv[0]); TQUObject *_o = 0; Data_Get_Struct(rb_ary_entry(argv[1], 0), TQUObject, _o); if(_o == 0) { rb_raise(rb_eRuntimeError, "Cannot create TQUObject\n"); } smokeruby_object *o = value_obj_info(self); (void) (TQObject*)o->smoke->cast( o->ptr, o->classId, o->smoke->idClass("TQObject") ); // Now, I need to find out if this means me int index; char *slotname; bool isSignal = tqstrcmp(rb_id2name(rb_frame_this_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::tqt_invoke() method\n", o->smoke->classes[o->classId].className ); } TQString name(slotname); static TQRegExp * rx = 0; if (rx == 0) { rx = new TQRegExp("\\(.*"); } 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 TQt::_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) { //tqWarning("\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 TQObject. Note that the name will be in the // form of TQt::Widget rather than TQWidget. 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 'TQt::Widget' and 'TQWidget' formats to be // used as an argument to TQt::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; } TQByteArray * dataArray = (TQByteArray*) 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; } TQByteArray * dataArray = (TQByteArray*) 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; } TQByteArray * dataArray = (TQByteArray*) o->ptr; dataArray->setRawData(StringValuePtr(data), RSTRING_LEN(data)); 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(tqstrcmp(static_type, "ptr") == 0) arg[idx].argType = xmoc_ptr; else if(tqstrcmp(static_type, "bool") == 0) arg[idx].argType = xmoc_bool; else if(tqstrcmp(static_type, "int") == 0) arg[idx].argType = xmoc_int; else if(tqstrcmp(static_type, "double") == 0) arg[idx].argType = xmoc_double; else if(tqstrcmp(static_type, "char*") == 0) arg[idx].argType = xmoc_charstar; else if(tqstrcmp(static_type, "TQString") == 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); TQUParameter *p = new TQUParameter; p->name = new char[strlen(name) + 1]; strcpy((char*)p->name, name); if(tqstrcmp(type, "bool") == 0) p->type = &static_QUType_bool; else if(tqstrcmp(type, "int") == 0) p->type = &static_QUType_int; else if(tqstrcmp(type, "double") == 0) p->type = &static_QUType_double; else if(tqstrcmp(type, "char*") == 0 || tqstrcmp(type, "const char*") == 0) p->type = &static_QUType_charstar; else if(tqstrcmp(type, "TQString") == 0 || tqstrcmp(type, "TQString&") == 0 || tqstrcmp(type, "const TQString") == 0 || tqstrcmp(type, "const TQString&") == 0) p->type = &static_QUType_TQString; 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); TQMetaData *m = new TQMetaData; // will be deleted m->name = new char[strlen(name) + 1]; strcpy((char*)m->name, name); Data_Get_Struct(method, TQUMethod, m->method); m->access = TQMetaData::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); TQUMethod *m = new TQUMethod; // permanent memory allocation m->name = new char[strlen(name) + 1]; // this too strcpy((char*)m->name, name); m->parameters = 0; m->count = RARRAY_LEN(params); if (m->count > 0) { m->parameters = new TQUParameter[m->count]; for (long i = 0; i < m->count; i++) { VALUE param = rb_ary_entry(params, i); TQUParameter *p = 0; Data_Get_Struct(param, TQUParameter, p); ((TQUParameter *) 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_LEN(list); TQMetaData *m = new TQMetaData[count]; for (long i = 0; i < count; i++) { VALUE item = rb_ary_entry(list, i); TQMetaData *old = 0; Data_Get_Struct(item, TQMetaData, 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)); TQMetaData * slot_tbl = 0; int slot_count = 0; if (slot_tbl_value != Qnil) { Data_Get_Struct(slot_tbl_value, TQMetaData, slot_tbl); slot_count = NUM2INT(slot_count_value); } TQMetaData * signal_tbl = 0; int signal_count = 0; if (signal_tbl_value != Qnil) { Data_Get_Struct(signal_tbl_value, TQMetaData, 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"); } TQMetaObject *meta = TQMetaObject::new_metaobject( className, (TQMetaObject*)po->ptr, (const TQMetaData*)slot_tbl, slot_count, // slots (const TQMetaData*)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("TQMetaObject"); 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 (*) (...)) tqt_invoke, -1); rb_define_method(klass, "qt_emit", (VALUE (*) (...)) tqt_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_LEN(signalNames); 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 isaTQObject(VALUE /*self*/, VALUE classid) { int classid_value = NUM2INT(classid); return isTQObject(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) tqWarning("DAMNIT on %s::%s => %d", c, name, meth); #endif if(!meth) { meth = qt_Smoke->findMethod("TQGlobalSpace", name); #ifdef DEBUG if (do_debug & qtdb_calls) tqWarning("DAMNIT on TQGlobalSpace::%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) tqWarning("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) tqWarning("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 || !tqstrncmp(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 \ && tqstrcmp(qt_Smoke->methodNames[methodRef.name], "operator=") != 0 \ && tqstrcmp(qt_Smoke->methodNames[methodRef.name], "operator!=") != 0 \ && tqstrcmp(qt_Smoke->methodNames[methodRef.name], "operator--") != 0 \ && tqstrcmp(qt_Smoke->methodNames[methodRef.name], "operator++") != 0 \ && tqstrncmp(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 (tqstrncmp(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) { TQRegExp predicate_re("^(is|has)(.)(.*)"); TQRegExp set_re("^(set)([A-Z])(.*)"); TQRegExp 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) tqWarning("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_LEN(rmeths); for(int i = 0; i < count; i++) { rb_str_catf_1(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_1(errmsg, "enum "); rb_str_catf_1(errmsg, "%s::%s", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]); rb_str_catf_1(errmsg, "\n"); } else { if(meth.flags & Smoke::mf_static) rb_str_catf(errmsg, "static "); rb_str_catf_1(errmsg, "%s ", (tname ? tname:"void")); rb_str_catf_1(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_1(errmsg, ", "); tname = qt_Smoke->types[qt_Smoke->argumentList[meth.args+i]].name; rb_str_catf_1(errmsg, "%s", (tname ? tname:"void")); } rb_str_catf_1(errmsg, ")"); if(meth.flags & Smoke::mf_const) rb_str_catf_1(errmsg, " const"); rb_str_catf_1(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; TQString packageName(package); static TQRegExp * scope_op = 0; if (scope_op == 0) { scope_op = new TQRegExp("^([^:]+)::([^:]+)$"); } if (packageName.startsWith("KDE::ConfigSkeleton::ItemEnum::")) { klass = rb_define_class_under(tdeconfigskeleton_itemenum_class, package+strlen("KDE::ConfigSkeleton::EnumItem::"), base_class); rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); tdeconfigskeleton_itemenum_choice_class = klass; } else if (packageName.startsWith("KDE::ConfigSkeleton::")) { klass = rb_define_class_under(tdeconfigskeleton_class, package+strlen("KDE::ConfigSkeleton::"), base_class); rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); rb_define_method(klass, "immutable?", (VALUE (*) (...)) _tdeconfigskeletonitem_immutable, 0); rb_define_method(klass, "isImmutable", (VALUE (*) (...)) _tdeconfigskeletonitem_immutable, 0); } else if (packageName.startsWith("KDE::Win::")) { klass = rb_define_class_under(twin_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(tdeparts_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("TDEIO::")) { klass = rb_define_class_under(tdeio_module, package+strlen("TDEIO::"), base_class); rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); if (packageName == "TDEIO::UDSAtom") { tdeio_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(tdetexteditor_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(TQRegExp("[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 (TQString(package).startsWith("TQt::")) { klass = rb_define_class_under(qt_module, package+strlen("TQt::"), qt_base_class); if (tqstrcmp(package, "TQt::Application") == 0) { rb_define_singleton_method(klass, "new", (VALUE (*) (...)) new_qapplication, -1); rb_define_method(klass, "ARGV", (VALUE (*) (...)) qapplication_argv, 0); } } else if (TQString(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 (TQString(package).startsWith("TQt::")) { klass = rb_define_class_under(qt_module, package+strlen("TQt::"), qt_base_class); } else if (TQString(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 (tqstrcmp(package, "TQt::MetaObject") == 0) { qmetaobject_class = klass; } else if (tqstrcmp(package, "TQt::Variant") == 0) { qvariant_class = klass; rb_define_singleton_method(qvariant_class, "new", (VALUE (*) (...)) new_qvariant, -1); } else if (tqstrcmp(package, "TQt::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 (tqstrcmp(package, "TQt::Char") == 0) { rb_define_method(klass, "to_s", (VALUE (*) (...)) qchar_to_s, 0); } return klass; } static VALUE version(VALUE /*self*/) { return rb_str_new2(TQT_VERSION_STR); } static VALUE qtruby_version(VALUE /*self*/) { return rb_str_new2(TQTRUBY_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); tdeparts_module = rb_define_module("KParts"); rb_define_singleton_method(tdeparts_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); rb_define_singleton_method(tdeparts_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); tdeio_module = rb_define_module("TDEIO"); rb_define_singleton_method(tdeio_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); rb_define_singleton_method(tdeio_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); tdetexteditor_module = rb_define_module("KTextEditor"); rb_define_singleton_method(tdetexteditor_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); rb_define_singleton_method(tdetexteditor_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); twin_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_tdeconfigskeletonitem_immutable(VALUE (*tdeconfigskeletonitem_immutable) (VALUE)) { _tdeconfigskeletonitem_immutable = tdeconfigskeletonitem_immutable; tdeconfigskeleton_class = rb_define_class_under(kde_module, "ConfigSkeleton", qt_base_class); tdeconfigskeleton_itemenum_class = rb_define_class_under(tdeconfigskeleton_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); // isTQOjbect => isaTQObject rb_define_module_function(qt_internal_module, "isTQObject", (VALUE (*) (...)) isaTQObject, 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); } };