qtbindings 4.8.6.1 → 4.8.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b9baa4afefcf8d0ed536de82ee1f1e1b0e3006f
4
- data.tar.gz: 5983bda5c916bf7eab47a310c2ee6d85cc811441
3
+ metadata.gz: 874c746da6fcf13bcc0fb15ad796433b0b15096e
4
+ data.tar.gz: 1de49eb84b084043466eb169b6f1f08693ac872d
5
5
  SHA512:
6
- metadata.gz: 77a35e23b4a07ffb93d1be557d3b74f2987fdc401b1cf390e144413a38832ae707acd4ada0cae0e847fe7b44527506b40e824a16537a4a3120e0c9ebc0278ca3
7
- data.tar.gz: 18922390ecad498679aaf990fa8265e7aa78fc42e617eb2ea4aabc8bc6d04fc03e936577ba825e4f6e6e5c1154abdc2be21621e35c5136657a308d2a8fa85278
6
+ metadata.gz: 0e707b3a1425145c4a03d1102d315c4765301d0f35fd26a26fffe276028065907388e38f1aae4826921b6ad8468a9fe7e681f3a74f65d91ea1f9c489c5c50244
7
+ data.tar.gz: 9a7cd631a80d509a3b23fa5a5e1d1ab63d318c9b7d4070e9f4f6a2f833da4fe3aa35d7f564b75989ea7736c86f4ba17bd8d64c89a53b027b0aab85891a4bbe6f
File without changes
data/bin/rbrcc CHANGED
File without changes
data/bin/rbuic4 CHANGED
File without changes
File without changes
File without changes
@@ -1,1310 +1,1327 @@
1
- /***************************************************************************
2
- Qt.cpp - description
3
- -------------------
4
- begin : Fri Jul 4 2003
5
- copyright : (C) 2003-2006 by Richard Dale
6
- email : Richard_Dale@tipitina.demon.co.uk
7
- ***************************************************************************/
8
-
9
- /***************************************************************************
10
- * *
11
- * This program is free software; you can redistribute it and/or modify *
12
- * it under the terms of the GNU Lesser General Public License as *
13
- * published by the Free Software Foundation; either version 2 of the *
14
- * License, or (at your option) any later version. *
15
- * *
16
- ***************************************************************************/
17
-
18
- #ifndef _GNU_SOURCE
19
- #define _GNU_SOURCE
20
- #endif
21
- #include <stdio.h>
22
- #include <stdarg.h>
23
-
24
- #include <QtCore/qabstractitemmodel.h>
25
- #include <QtCore/qglobal.h>
26
- #include <QtCore/qhash.h>
27
- #include <QtCore/qline.h>
28
- #include <QtCore/qmetaobject.h>
29
- #include <QtCore/qobject.h>
30
- #include <QtCore/qrect.h>
31
- #include <QtCore/qregexp.h>
32
- #include <QtCore/qstring.h>
33
- #include <QtCore/qvariant.h>
34
- #include <QtCore/qmutex.h>
35
- #include <QtGui/qapplication.h>
36
- #include <QtGui/qbitmap.h>
37
- #include <QtGui/qcolor.h>
38
- #include <QtGui/qcursor.h>
39
- #include <QtGui/qfont.h>
40
- #include <QtGui/qicon.h>
41
- #include <QtGui/qitemselectionmodel.h>
42
- #include <QtGui/qpalette.h>
43
- #include <QtGui/qpen.h>
44
- #include <QtGui/qpixmap.h>
45
- #include <QtGui/qpolygon.h>
46
- #include <QtGui/qtextformat.h>
47
- #include <QtGui/qwidget.h>
48
-
49
- #ifdef QT_QTDBUS
50
- #include <QtDBus/qdbusargument.h>
51
- #endif
52
-
53
- #undef DEBUG
54
- #ifndef __USE_POSIX
55
- #define __USE_POSIX
56
- #endif
57
- #ifndef __USE_XOPEN
58
- #define __USE_XOPEN
59
- #endif
60
- #ifdef _BOOL
61
- #define HAS_BOOL
62
- #endif
63
-
64
- #include <ruby.h>
65
-
66
- #include <smoke/smoke.h>
67
- #include <smoke/qtcore_smoke.h>
68
-
69
- #undef free
70
- #undef malloc
71
-
72
- #include "marshall.h"
73
- #include "qtruby.h"
74
- #include "smokeruby.h"
75
- #include "smoke.h"
76
- #include "marshall_types.h"
77
- // #define DEBUG
78
-
79
- extern "C" {
80
- VALUE qt_internal_module = Qnil;
81
- VALUE qt_module = Qnil;
82
-
83
- VALUE qlistmodel_class = Qnil;
84
- VALUE qmetaobject_class = Qnil;
85
- VALUE qtablemodel_class = Qnil;
86
- VALUE qt_base_class = Qnil;
87
- VALUE qvariant_class = Qnil;
88
-
89
- VALUE moduleindex_class = Qnil;
90
-
91
- bool application_terminated = false;
92
- }
93
-
94
- QList<Smoke*> smokeList;
95
- QHash<Smoke*, QtRubyModule> qtruby_modules;
96
-
97
- #ifdef DEBUG
98
- int do_debug = qtdb_gc;
99
- #else
100
- int do_debug = qtdb_none;
101
- #endif
102
-
103
- typedef QHash<void *, SmokeValue> PointerMap;
104
- static QMutex pointer_map_mutex;
105
- Q_GLOBAL_STATIC(PointerMap, pointer_map)
106
- int object_count = 0;
107
-
108
- // FIXME:
109
- // Don't the two following hashs create memory leaks by using pointers to Smoke::(Module)Index ?
110
- QHash<QByteArray, Smoke::ModuleIndex *> methcache;
111
- QHash<QByteArray, Smoke::ModuleIndex *> classcache;
112
-
113
- QHash<Smoke::ModuleIndex, QByteArray*> IdToClassNameMap;
114
-
115
- #define logger logger_backend
116
-
117
- Smoke::ModuleIndex _current_method;
118
-
119
- smokeruby_object *
120
- alloc_smokeruby_object(bool allocated, Smoke * smoke, int classId, void * ptr)
121
- {
122
- smokeruby_object * o = ALLOC(smokeruby_object);
123
- o->classId = classId;
124
- o->smoke = smoke;
125
- o->ptr = ptr;
126
- o->allocated = allocated;
127
- return o;
128
- }
129
-
130
- void
131
- free_smokeruby_object(smokeruby_object * o)
132
- {
133
- o->ptr = 0;
134
- xfree(o);
135
- return;
136
- }
137
-
138
- smokeruby_object *value_obj_info(VALUE ruby_value) { // ptr on success, null on fail
139
- if (TYPE(ruby_value) != T_DATA) {
140
- return 0;
141
- }
142
-
143
- smokeruby_object * o = 0;
144
- Data_Get_Struct(ruby_value, smokeruby_object, o);
145
- return o;
146
- }
147
-
148
- void *value_to_ptr(VALUE ruby_value) { // ptr on success, null on fail
149
- smokeruby_object *o = value_obj_info(ruby_value);
150
- return o;
151
- }
152
-
153
- VALUE getPointerObject(void *ptr) {
154
- return getSmokeValue(ptr).value;
155
- }
156
-
157
- SmokeValue getSmokeValue(void *ptr) {
158
- pointer_map_mutex.lock();
159
-
160
- if (!pointer_map() || !pointer_map()->contains(ptr)) {
161
- if (do_debug & qtdb_gc) {
162
- qWarning("getPointerObject %p -> nil", ptr);
163
- if (!pointer_map()) {
164
- qWarning("getPointerObject pointer_map deleted");
165
- }
166
- }
167
- pointer_map_mutex.unlock();
168
- return SmokeValue();
169
- } else {
170
- if (do_debug & qtdb_gc) {
171
- qWarning("getPointerObject %p -> %p", ptr, (void *) pointer_map()->operator[](ptr).value);
172
- }
173
- pointer_map_mutex.unlock();
174
- return pointer_map()->operator[](ptr);
175
- }
176
- }
177
-
178
- void unmapPointer(void *ptr, Smoke *smoke, Smoke::Index fromClassId, Smoke::Index toClassId, void *lastptr) {
179
- pointer_map_mutex.lock();
180
- ptr = smoke->cast(ptr, fromClassId, toClassId);
181
-
182
- if (ptr != lastptr) {
183
- lastptr = ptr;
184
- if (pointer_map() && pointer_map()->contains(ptr)) {
185
- VALUE obj_ptr = pointer_map()->operator[](ptr).value;
186
-
187
- if (do_debug & qtdb_gc) {
188
- const char *className = smoke->classes[fromClassId].className;
189
- qWarning("unmapPointer (%s*)%p -> %p size: %d", className, ptr, (void*)(&obj_ptr), pointer_map()->size() - 1);
190
- }
191
-
192
- pointer_map()->remove(ptr);
193
- }
194
- }
195
-
196
- if (smoke->classes[toClassId].external) {
197
- // encountered external class
198
- Smoke::ModuleIndex mi = Smoke::findClass(smoke->classes[toClassId].className);
199
- if (!mi.index || !mi.smoke) return;
200
-
201
- smoke = mi.smoke;
202
- toClassId = mi.index;
203
- }
204
-
205
- pointer_map_mutex.unlock();
206
- for (Smoke::Index *i = smoke->inheritanceList + smoke->classes[toClassId].parents; *i; i++) {
207
- unmapPointer(ptr, smoke, toClassId, *i, lastptr);
208
- }
209
-
210
- }
211
-
212
- void unmapPointer(smokeruby_object *o, Smoke::Index classId, void *lastptr) {
213
- unmapPointer(o->ptr, o->smoke, o->classId, classId, lastptr);
214
- }
215
-
216
- // Store pointer in pointer_map hash : "pointer_to_Qt_object" => weak ref to associated Ruby object
217
- // Recurse to store it also as casted to its parent classes.
218
-
219
- void mapPointer(VALUE obj, smokeruby_object* o, void *ptr, Smoke *smoke, Smoke::Index fromClassId, Smoke::Index toClassId, void *lastptr) {
220
- pointer_map_mutex.unlock();
221
-
222
- ptr = smoke->cast(ptr, fromClassId, toClassId);
223
-
224
- if (ptr != lastptr) {
225
- lastptr = ptr;
226
-
227
- if (do_debug & qtdb_gc) {
228
- const char *className = smoke->classes[fromClassId].className;
229
- qWarning("mapPointer (%s*)%p -> %p size: %d", className, ptr, (void*)obj, pointer_map()->size() + 1);
230
- }
231
-
232
- SmokeValue value(obj, o);
233
- pointer_map()->insert(ptr, value);
234
- }
235
-
236
- if (smoke->classes[toClassId].external) {
237
- // encountered external class
238
- Smoke::ModuleIndex mi = Smoke::findClass(smoke->classes[toClassId].className);
239
- if (!mi.index || !mi.smoke) return;
240
-
241
- smoke = mi.smoke;
242
- toClassId = mi.index;
243
- }
244
-
245
- pointer_map_mutex.unlock();
246
- for (Smoke::Index *i = smoke->inheritanceList + smoke->classes[toClassId].parents; *i; i++) {
247
- mapPointer(obj, o, ptr, smoke, toClassId, *i, lastptr);
248
- }
249
-
250
- return;
251
- }
252
-
253
- void mapPointer(VALUE obj, smokeruby_object *o, Smoke::Index classId, void *lastptr) {
254
- mapPointer(obj, o, o->ptr, o->smoke, o->classId, classId, lastptr);
255
- }
256
-
257
- namespace QtRuby {
258
-
259
- Binding::Binding() : SmokeBinding(0) {}
260
- Binding::Binding(Smoke *s) : SmokeBinding(s) {}
261
-
262
- void
263
- Binding::deleted(Smoke::Index classId, void *ptr) {
264
- if (!pointer_map()) {
265
- return;
266
- }
267
-
268
- smokeruby_object *o = getSmokeValue(ptr).o;
269
- if (do_debug & qtdb_gc) {
270
- qWarning("unmapping: o = %p, ptr = %p\n", o, ptr);
271
- qWarning("%p->~%s()", ptr, smoke->className(classId));
272
- }
273
- if (!o || !o->ptr) {
274
- return;
275
- }
276
- unmapPointer(o, o->classId, 0);
277
- o->ptr = 0;
278
- }
279
-
280
- bool
281
- Binding::callMethod(Smoke::Index method, void *ptr, Smoke::Stack args, bool /*isAbstract*/) {
282
- VALUE obj = getPointerObject(ptr);
283
- smokeruby_object *o = value_obj_info(obj);
284
-
285
- if (do_debug & qtdb_virtual) {
286
- const Smoke::Method & meth = smoke->methods[method];
287
- QByteArray signature(smoke->methodNames[meth.name]);
288
- signature += "(";
289
- for (int i = 0; i < meth.numArgs; i++) {
290
- if (i != 0) signature += ", ";
291
- signature += smoke->types[smoke->argumentList[meth.args + i]].name;
292
- }
293
- signature += ")";
294
- if (meth.flags & Smoke::mf_const) {
295
- signature += " const";
296
- }
297
- qWarning( "module: %s virtual %p->%s::%s called",
298
- smoke->moduleName(),
299
- ptr,
300
- smoke->classes[smoke->methods[method].classId].className,
301
- (const char *) signature );
302
- }
303
-
304
- if (o == 0) {
305
- if( do_debug & qtdb_virtual ) // if not in global destruction
306
- qWarning("Cannot find object for virtual method %p -> %p", ptr, &obj);
307
- return false;
308
- }
309
- const char *methodName = smoke->methodNames[smoke->methods[method].name];
310
- if (qstrncmp(methodName, "operator", sizeof("operator") - 1) == 0) {
311
- methodName += (sizeof("operator") - 1);
312
- }
313
-
314
- // If not in a ruby thread, just call the C++ version
315
- // During GC, avoid checking for override and just call the C++ version
316
- // If the virtual method hasn't been overriden, just call the C++ one.
317
- #ifdef HAVE_RUBY_RUBY_H
318
- int ruby_thread = ruby_native_thread_p();
319
- if ((ruby_thread == 0) || rb_during_gc() || rb_respond_to(obj, rb_intern(methodName)) == 0) {
320
- return false;
321
- }
322
- #else
323
- if (rb_during_gc() || ruby_stack_check() || rb_respond_to(obj, rb_intern(methodName)) == 0) {
324
- return false;
325
- }
326
- #endif
327
-
328
- QtRuby::VirtualMethodCall c(smoke, method, args, obj, ALLOCA_N(VALUE, smoke->methods[method].numArgs));
329
- c.next();
330
- return true;
331
- }
332
-
333
- char*
334
- Binding::className(Smoke::Index classId) {
335
- Smoke::ModuleIndex mi(smoke, classId);
336
- return (char *) (const char *) *(IdToClassNameMap.value(mi));
337
- }
338
-
339
- /*
340
- Converts a C++ value returned by a signal invocation to a Ruby
341
- reply type
342
- */
343
- class SignalReturnValue : public Marshall {
344
- QList<MocArgument*> _replyType;
345
- Smoke::Stack _stack;
346
- VALUE * _result;
347
- public:
348
- SignalReturnValue(void ** o, VALUE * result, QList<MocArgument*> replyType)
349
- {
350
- _result = result;
351
- _replyType = replyType;
352
- _stack = new Smoke::StackItem[1];
353
- smokeStackFromQtStack(_stack, o, 0, 1, _replyType);
354
- Marshall::HandlerFn fn = getMarshallFn(type());
355
- (*fn)(this);
356
- }
357
-
358
- SmokeType type() {
359
- return _replyType[0]->st;
360
- }
361
- Marshall::Action action() { return Marshall::ToVALUE; }
362
- Smoke::StackItem &item() { return _stack[0]; }
363
- VALUE * var() {
364
- return _result;
365
- }
366
-
367
- void unsupported()
368
- {
369
- rb_raise(rb_eArgError, "Cannot handle '%s' as signal reply-type", type().name());
370
- }
371
- Smoke *smoke() { return type().smoke(); }
372
-
373
- void next() {}
374
-
375
- bool cleanup() { return false; }
376
-
377
- ~SignalReturnValue() {
378
- delete[] _stack;
379
- }
380
- };
381
-
382
- /* Note that the SignalReturnValue and EmitSignal classes really belong in
383
- marshall_types.cpp. However, for unknown reasons they don't link with certain
384
- versions of gcc. So they were moved here in to work round that bug.
385
- */
386
- EmitSignal::EmitSignal(QObject *obj, int id, int /*items*/, QList<MocArgument*> args, VALUE *sp, VALUE * result) : SigSlotBase(args),
387
- _obj(obj), _id(id)
388
- {
389
- _sp = sp;
390
- _result = result;
391
- }
392
-
393
- Marshall::Action
394
- EmitSignal::action()
395
- {
396
- return Marshall::FromVALUE;
397
- }
398
-
399
- Smoke::StackItem &
400
- EmitSignal::item()
401
- {
402
- return _stack[_cur];
403
- }
404
-
405
- const char *
406
- EmitSignal::mytype()
407
- {
408
- return "signal";
409
- }
410
-
411
- void
412
- EmitSignal::emitSignal()
413
- {
414
- if (_called) return;
415
- _called = true;
416
- void ** o = new void*[_items];
417
- smokeStackToQtStack(_stack, o + 1, 1, _items, _args);
418
- void * ptr;
419
- o[0] = &ptr;
420
- prepareReturnValue(o);
421
-
422
- _obj->metaObject()->activate(_obj, _id, o);
423
-
424
- if (_args[0]->argType != xmoc_void) {
425
- SignalReturnValue r(o, _result, _args);
426
- }
427
- delete[] o;
428
- }
429
-
430
- void
431
- EmitSignal::mainfunction()
432
- {
433
- emitSignal();
434
- }
435
-
436
- bool
437
- EmitSignal::cleanup()
438
- {
439
- return true;
440
- }
441
-
442
- InvokeNativeSlot::InvokeNativeSlot(QObject *obj, int id, int /*items*/, QList<MocArgument*> args, VALUE *sp, VALUE * result) : SigSlotBase(args),
443
- _obj(obj), _id(id)
444
- {
445
- _sp = sp;
446
- _result = result;
447
- }
448
-
449
- Marshall::Action
450
- InvokeNativeSlot::action()
451
- {
452
- return Marshall::FromVALUE;
453
- }
454
-
455
- Smoke::StackItem &
456
- InvokeNativeSlot::item()
457
- {
458
- return _stack[_cur];
459
- }
460
-
461
- const char *
462
- InvokeNativeSlot::mytype()
463
- {
464
- return "slot";
465
- }
466
-
467
- void
468
- InvokeNativeSlot::invokeSlot()
469
- {
470
- if (_called) return;
471
- _called = true;
472
- void ** o = new void*[_items];
473
- smokeStackToQtStack(_stack, o + 1, 1, _items, _args);
474
- void * ptr;
475
- o[0] = &ptr;
476
- prepareReturnValue(o);
477
-
478
- _obj->qt_metacall(QMetaObject::InvokeMetaMethod, _id, o);
479
-
480
- if (_args[0]->argType != xmoc_void) {
481
- SignalReturnValue r(o, _result, _args);
482
- }
483
- delete[] o;
484
- }
485
-
486
- void
487
- InvokeNativeSlot::mainfunction()
488
- {
489
- invokeSlot();
490
- }
491
-
492
- bool
493
- InvokeNativeSlot::cleanup()
494
- {
495
- return true;
496
- }
497
-
498
- }
499
-
500
- VALUE rb_str_catf(VALUE self, const char *format, ...)
501
- {
502
- #define CAT_BUFFER_SIZE 2048
503
- static char p[CAT_BUFFER_SIZE];
504
- va_list ap;
505
- va_start(ap, format);
506
- qvsnprintf(p, CAT_BUFFER_SIZE, format, ap);
507
- p[CAT_BUFFER_SIZE - 1] = '\0';
508
- rb_str_cat2(self, p);
509
- va_end(ap);
510
- return self;
511
- }
512
-
513
- const char *
514
- resolve_classname(smokeruby_object * o)
515
- {
516
- if (Smoke::isDerivedFrom(o->smoke->classes[o->classId].className, "QObject")) {
517
- QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
518
- const QMetaObject * meta = qobject->metaObject();
519
-
520
- while (meta != 0) {
521
- Smoke::ModuleIndex mi = o->smoke->findClass(meta->className());
522
- o->smoke = mi.smoke;
523
- o->classId = mi.index;
524
- if (o->smoke != 0) {
525
- if (o->classId != 0) {
526
- return qtruby_modules[o->smoke].binding->className(o->classId);
527
- }
528
- }
529
-
530
- meta = meta->superClass();
531
- }
532
- }
533
-
534
- if (o->smoke->classes[o->classId].external) {
535
- Smoke::ModuleIndex mi = o->smoke->findClass(o->smoke->className(o->classId));
536
- o->smoke = mi.smoke;
537
- o->classId = mi.index;
538
- return qtruby_modules.value(mi.smoke).resolve_classname(o);
539
- }
540
- return qtruby_modules.value(o->smoke).resolve_classname(o);
541
- }
542
-
543
- VALUE
544
- findMethod(VALUE /*self*/, VALUE c_value, VALUE name_value)
545
- {
546
- char *c = StringValuePtr(c_value);
547
- char *name = StringValuePtr(name_value);
548
- VALUE result = rb_ary_new();
549
- Smoke::ModuleIndex classId = Smoke::findClass(c);
550
- Smoke::ModuleIndex meth = Smoke::NullModuleIndex;
551
- QList<Smoke::ModuleIndex> milist;
552
- if (classId.smoke != 0) {
553
- meth = classId.smoke->findMethod(c, name);
554
- }
555
- #ifdef DEBUG
556
- if (do_debug & qtdb_calls) qWarning("Found method %s::%s => %d", c, name, meth.index);
557
- #endif
558
- if(!meth.index) {
559
- // since every smoke module defines a class 'QGlobalSpace' we can't rely on the classMap,
560
- // so we search for methods by hand
561
- foreach (Smoke* s, smokeList) {
562
- Smoke::ModuleIndex cid = s->idClass("QGlobalSpace");
563
- Smoke::ModuleIndex mnid = s->idMethodName(name);
564
- if (!cid.index || !mnid.index) continue;
565
- meth = s->idMethod(cid.index, mnid.index);
566
- if (meth.index) milist.append(meth);
567
- }
568
- #ifdef DEBUG
569
- if (do_debug & qtdb_calls) qWarning("Found method QGlobalSpace::%s => %d", name, meth.index);
570
- #endif
571
- }
572
- else
573
- {
574
- milist.append(meth);
575
- }
576
-
577
- if (milist.count() == 0) {
578
- return result;
579
- // empty list
580
- } else {
581
- foreach (Smoke::ModuleIndex meth, milist) {
582
- if (meth.index > 0) {
583
- Smoke::Index i = meth.smoke->methodMaps[meth.index].method;
584
- if (i == 0) { // shouldn't happen
585
- rb_raise(rb_eArgError, "Corrupt method %s::%s", c, name);
586
- } else if(i > 0) { // single match
587
- const Smoke::Method &methodRef = meth.smoke->methods[i];
588
- if ((methodRef.flags & Smoke::mf_internal) == 0) {
589
- rb_ary_push(result, rb_funcall(moduleindex_class, rb_intern("new"), 2, INT2NUM(smokeList.indexOf(meth.smoke)), INT2NUM(i)));
590
- }
591
- } else { // multiple match
592
- i = -i; // turn into ambiguousMethodList index
593
- while (meth.smoke->ambiguousMethodList[i]) {
594
- const Smoke::Method &methodRef = meth.smoke->methods[meth.smoke->ambiguousMethodList[i]];
595
- if ((methodRef.flags & Smoke::mf_internal) == 0) {
596
- rb_ary_push(result, rb_funcall(moduleindex_class, rb_intern("new"), 2, INT2NUM(smokeList.indexOf(meth.smoke)), INT2NUM(meth.smoke->ambiguousMethodList[i])));
597
- //#ifdef DEBUG
598
- if (do_debug & qtdb_calls) qWarning("Ambiguous Method %s::%s => %d", c, name, meth.smoke->ambiguousMethodList[i]);
599
- //#endif
600
-
601
- }
602
- i++;
603
- }
604
- }
605
- }
606
- }
607
- }
608
- return result;
609
- }
610
-
611
- // findAllMethods(ModuleIndex [, startingWith]) : returns { "mungedName" => [index in methods, ...], ... }
612
-
613
- VALUE
614
- findAllMethods(int argc, VALUE * argv, VALUE /*self*/)
615
- {
616
- VALUE rb_mi = argv[0];
617
- VALUE result = rb_hash_new();
618
- if (rb_mi != Qnil) {
619
- Smoke::Index c = (Smoke::Index) NUM2INT(rb_funcall(rb_mi, rb_intern("index"), 0));
620
- Smoke *smoke = smokeList[NUM2INT(rb_funcall(rb_mi, rb_intern("smoke"), 0))];
621
- if (c > smoke->numClasses) {
622
- return Qnil;
623
- }
624
- char * pat = 0L;
625
- if(argc > 1 && TYPE(argv[1]) == T_STRING)
626
- pat = StringValuePtr(argv[1]);
627
- #ifdef DEBUG
628
- if (do_debug & qtdb_calls) qWarning("findAllMethods called with classid = %d, pat == %s", c, pat);
629
- #endif
630
- Smoke::Index imax = smoke->numMethodMaps;
631
- Smoke::Index imin = 0, icur = -1, methmin, methmax;
632
- methmin = -1; methmax = -1; // kill warnings
633
- int icmp = -1;
634
- while(imax >= imin) {
635
- icur = (imin + imax) / 2;
636
- icmp = smoke->leg(smoke->methodMaps[icur].classId, c);
637
- if (icmp == 0) {
638
- Smoke::Index pos = icur;
639
- while (icur && smoke->methodMaps[icur-1].classId == c)
640
- icur --;
641
- methmin = icur;
642
- icur = pos;
643
- while(icur < imax && smoke->methodMaps[icur+1].classId == c)
644
- icur ++;
645
- methmax = icur;
646
- break;
647
- }
648
- if (icmp > 0)
649
- imax = icur - 1;
650
- else
651
- imin = icur + 1;
652
- }
653
- if (icmp == 0) {
654
- for (Smoke::Index i = methmin; i <= methmax; i++) {
655
- Smoke::Index m = smoke->methodMaps[i].name;
656
- if (pat == 0L || strncmp(smoke->methodNames[m], pat, strlen(pat)) == 0) {
657
- Smoke::Index ix = smoke->methodMaps[i].method;
658
- VALUE meths = rb_ary_new();
659
- if (ix >= 0) { // single match
660
- const Smoke::Method &methodRef = smoke->methods[ix];
661
- if ((methodRef.flags & Smoke::mf_internal) == 0) {
662
- rb_ary_push(meths, rb_funcall(moduleindex_class, rb_intern("new"), 2, INT2NUM(smokeList.indexOf(smoke)), INT2NUM((int) ix)));
663
- }
664
- } else { // multiple match
665
- ix = -ix; // turn into ambiguousMethodList index
666
- while (smoke->ambiguousMethodList[ix]) {
667
- const Smoke::Method &methodRef = smoke->methods[smoke->ambiguousMethodList[ix]];
668
- if ((methodRef.flags & Smoke::mf_internal) == 0) {
669
- rb_ary_push(meths, rb_funcall(moduleindex_class, rb_intern("new"), 2, INT2NUM(smokeList.indexOf(smoke)), INT2NUM((int)smoke->ambiguousMethodList[ix])));
670
- }
671
- ix++;
672
- }
673
- }
674
- rb_hash_aset(result, rb_str_new2(smoke->methodNames[m]), meths);
675
- }
676
- }
677
- }
678
- }
679
- return result;
680
- }
681
-
682
- /*
683
- Flags values
684
- 0 All methods, except enum values and protected non-static methods
685
- mf_static Static methods only
686
- mf_enum Enums only
687
- mf_protected Protected non-static methods only
688
- */
689
-
690
- #define PUSH_QTRUBY_METHOD \
691
- if ( (methodRef.flags & (Smoke::mf_internal|Smoke::mf_ctor|Smoke::mf_dtor)) == 0 \
692
- && strcmp(s->methodNames[methodRef.name], "operator=") != 0 \
693
- && strcmp(s->methodNames[methodRef.name], "operator!=") != 0 \
694
- && strcmp(s->methodNames[methodRef.name], "operator--") != 0 \
695
- && strcmp(s->methodNames[methodRef.name], "operator++") != 0 \
696
- && strncmp(s->methodNames[methodRef.name], "operator ", strlen("operator ")) != 0 \
697
- && ( (flags == 0 && (methodRef.flags & (Smoke::mf_static|Smoke::mf_enum|Smoke::mf_protected)) == 0) \
698
- || ( flags == Smoke::mf_static \
699
- && (methodRef.flags & Smoke::mf_enum) == 0 \
700
- && (methodRef.flags & Smoke::mf_static) == Smoke::mf_static ) \
701
- || (flags == Smoke::mf_enum && (methodRef.flags & Smoke::mf_enum) == Smoke::mf_enum) \
702
- || ( flags == Smoke::mf_protected \
703
- && (methodRef.flags & Smoke::mf_static) == 0 \
704
- && (methodRef.flags & Smoke::mf_protected) == Smoke::mf_protected ) ) ) { \
705
- if (strncmp(s->methodNames[methodRef.name], "operator", strlen("operator")) == 0) { \
706
- if (op_re.indexIn(s->methodNames[methodRef.name]) != -1) { \
707
- rb_ary_push(result, rb_str_new2((op_re.cap(1) + op_re.cap(2)).toLatin1())); \
708
- } else { \
709
- rb_ary_push(result, rb_str_new2(s->methodNames[methodRef.name] + strlen("operator"))); \
710
- } \
711
- } else if (predicate_re.indexIn(s->methodNames[methodRef.name]) != -1 && methodRef.numArgs == 0) { \
712
- rb_ary_push(result, rb_str_new2((predicate_re.cap(2).toLower() + predicate_re.cap(3) + "?").toLatin1())); \
713
- } else if (set_re.indexIn(s->methodNames[methodRef.name]) != -1 && methodRef.numArgs == 1) { \
714
- rb_ary_push(result, rb_str_new2((set_re.cap(2).toLower() + set_re.cap(3) + "=").toLatin1())); \
715
- } else { \
716
- rb_ary_push(result, rb_str_new2(s->methodNames[methodRef.name])); \
717
- } \
718
- }
719
-
720
- VALUE
721
- findAllMethodNames(VALUE /*self*/, VALUE result, VALUE classid, VALUE flags_value)
722
- {
723
- QRegExp predicate_re("^(is|has)(.)(.*)");
724
- QRegExp set_re("^(set)([A-Z])(.*)");
725
- QRegExp op_re("operator(.*)(([-%~/+|&*])|(>>)|(<<)|(&&)|(\\|\\|)|(\\*\\*))=$");
726
-
727
- unsigned short flags = (unsigned short) NUM2UINT(flags_value);
728
- if (classid != Qnil) {
729
- Smoke::Index c = (Smoke::Index) NUM2INT(rb_funcall(classid, rb_intern("index"), 0));
730
- Smoke* s = smokeList[NUM2INT(rb_funcall(classid, rb_intern("smoke"), 0))];
731
- if (c > s->numClasses) {
732
- return Qnil;
733
- }
734
- #ifdef DEBUG
735
- if (do_debug & qtdb_calls) qWarning("findAllMethodNames called with classid = %d in module %s", c, s->moduleName());
736
- #endif
737
- Smoke::Index imax = s->numMethodMaps;
738
- Smoke::Index imin = 0, icur = -1, methmin, methmax;
739
- methmin = -1; methmax = -1; // kill warnings
740
- int icmp = -1;
741
-
742
- while (imax >= imin) {
743
- icur = (imin + imax) / 2;
744
- icmp = s->leg(s->methodMaps[icur].classId, c);
745
- if (icmp == 0) {
746
- Smoke::Index pos = icur;
747
- while(icur && s->methodMaps[icur-1].classId == c)
748
- icur --;
749
- methmin = icur;
750
- icur = pos;
751
- while(icur < imax && s->methodMaps[icur+1].classId == c)
752
- icur ++;
753
- methmax = icur;
754
- break;
755
- }
756
- if (icmp > 0)
757
- imax = icur - 1;
758
- else
759
- imin = icur + 1;
760
- }
761
-
762
- if (icmp == 0) {
763
- for (Smoke::Index i=methmin ; i <= methmax ; i++) {
764
- Smoke::Index ix= s->methodMaps[i].method;
765
- if (ix > 0) { // single match
766
- const Smoke::Method &methodRef = s->methods[ix];
767
- PUSH_QTRUBY_METHOD
768
- } else { // multiple match
769
- ix = -ix; // turn into ambiguousMethodList index
770
- while (s->ambiguousMethodList[ix]) {
771
- const Smoke::Method &methodRef = s->methods[s->ambiguousMethodList[ix]];
772
- PUSH_QTRUBY_METHOD
773
- ix++;
774
- }
775
- }
776
- }
777
- }
778
- }
779
- return result;
780
- }
781
-
782
- QByteArray *
783
- find_cached_selector(int argc, VALUE * argv, VALUE klass, const char * methodName)
784
- {
785
- // Look in the cache
786
- static QByteArray * mcid = 0;
787
- if (mcid == 0) {
788
- mcid = new QByteArray();
789
- }
790
-
791
- *mcid = rb_class2name(klass);
792
- *mcid += ';';
793
- *mcid += methodName;
794
- for(int i=4; i<argc ; i++)
795
- {
796
- *mcid += ';';
797
- *mcid += value_to_type_flag(argv[i]);
798
- }
799
- Smoke::ModuleIndex *rcid = methcache.value(*mcid);
800
- #ifdef DEBUG
801
- if (do_debug & qtdb_calls) qWarning("method_missing mcid: %s", (const char *) *mcid);
802
- #endif
803
-
804
- if (rcid) {
805
- // Got a hit
806
- #ifdef DEBUG
807
- if (do_debug & qtdb_calls) qWarning("method_missing cache hit, mcid: %s", (const char *) *mcid);
808
- #endif
809
- _current_method.smoke = rcid->smoke;
810
- _current_method.index = rcid->index;
811
- } else {
812
- _current_method.smoke = 0;
813
- _current_method.index = -1;
814
- }
815
-
816
- return mcid;
817
- }
818
-
819
- VALUE
820
- method_missing(int argc, VALUE * argv, VALUE self)
821
- {
822
- const char * methodName = rb_id2name(SYM2ID(argv[0]));
823
- VALUE klass = rb_funcall(self, rb_intern("class"), 0);
824
-
825
- VALUE retval = Qnil;
826
-
827
- // Look for 'thing?' methods, and try to match isThing() or hasThing() in the Smoke runtime
828
- static QByteArray * pred = 0;
829
- if (pred == 0) {
830
- pred = new QByteArray();
831
- }
832
-
833
- *pred = methodName;
834
- if (pred->endsWith("?")) {
835
- smokeruby_object *o = value_obj_info(self);
836
- if(!o || !o->ptr) {
837
- return rb_call_super(argc, argv);
838
- }
839
-
840
- // Drop the trailing '?'
841
- pred->replace(pred->length() - 1, 1, "");
842
-
843
- pred->replace(0, 1, pred->mid(0, 1).toUpper());
844
- pred->replace(0, 0, "is");
845
- Smoke::ModuleIndex meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, (const char *) *pred);
846
-
847
- if (meth.index == 0) {
848
- pred->replace(0, 2, "has");
849
- meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, *pred);
850
- }
851
-
852
- if (meth.index > 0) {
853
- methodName = (char *) (const char *) *pred;
854
- }
855
- }
856
-
857
- VALUE * temp_stack = ALLOCA_N(VALUE, argc+3);
858
- temp_stack[0] = rb_str_new2("Qt");
859
- temp_stack[1] = rb_str_new2(methodName);
860
- temp_stack[2] = klass;
861
- temp_stack[3] = self;
862
- for (int count = 1; count < argc; count++) {
863
- temp_stack[count+3] = argv[count];
864
- }
865
-
866
- {
867
- QByteArray * mcid = find_cached_selector(argc+3, temp_stack, klass, methodName);
868
-
869
- if (_current_method.index == -1) {
870
- // Find the C++ method to call. Do that from Ruby for now
871
-
872
- retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack);
873
- if (_current_method.index == -1) {
874
- const char * op = rb_id2name(SYM2ID(argv[0]));
875
- if ( qstrcmp(op, "-") == 0
876
- || qstrcmp(op, "+") == 0
877
- || qstrcmp(op, "/") == 0
878
- || qstrcmp(op, "%") == 0
879
- || qstrcmp(op, "|") == 0 )
880
- {
881
- // Look for operator methods of the form 'operator+=', 'operator-=' and so on..
882
- char op1[3];
883
- op1[0] = op[0];
884
- op1[1] = '=';
885
- op1[2] = '\0';
886
- temp_stack[1] = rb_str_new2(op1);
887
- retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack);
888
- }
889
-
890
- if (_current_method.index == -1) {
891
- // Check for property getter/setter calls, and for slots in QObject classes
892
- // not in the smoke library
893
- smokeruby_object *o = value_obj_info(self);
894
- if ( o != 0
895
- && o->ptr != 0
896
- && Smoke::isDerivedFrom(Smoke::ModuleIndex(o->smoke, o->classId), Smoke::findClass("QObject")) )
897
- {
898
- QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
899
- static QByteArray * name = 0;
900
- if (name == 0) {
901
- name = new QByteArray();
902
- }
903
-
904
- *name = rb_id2name(SYM2ID(argv[0]));
905
- const QMetaObject * meta = qobject->metaObject();
906
-
907
- if (argc == 1) {
908
- if (name->endsWith("?")) {
909
- name->replace(0, 1, pred->mid(0, 1).toUpper());
910
- name->replace(0, 0, "is");
911
- if (meta->indexOfProperty(*name) == -1) {
912
- name->replace(0, 2, "has");
913
- }
914
- }
915
-
916
- if (meta->indexOfProperty(*name) != -1) {
917
- VALUE qvariant = rb_funcall(self, rb_intern("property"), 1, rb_str_new2(*name));
918
- return rb_funcall(qvariant, rb_intern("value"), 0);
919
- }
920
- }
921
-
922
- if (argc == 2 && name->endsWith("=")) {
923
- name->replace("=", "");
924
- if (meta->indexOfProperty(*name) != -1) {
925
- VALUE qvariant = rb_funcall(self, rb_intern("qVariantFromValue"), 1, argv[1]);
926
- return rb_funcall(self, rb_intern("setProperty"), 2, rb_str_new2(*name), qvariant);
927
- }
928
- }
929
-
930
- int classId = o->smoke->idClass(meta->className()).index;
931
-
932
- // The class isn't in the Smoke lib. But if it is called 'local::Merged'
933
- // it is from a QDBusInterface and the slots are remote, so don't try to
934
- // those.
935
- while ( classId == 0
936
- && qstrcmp(meta->className(), "local::Merged") != 0
937
- && qstrcmp(meta->superClass()->className(), "QDBusAbstractInterface") != 0 )
938
- {
939
- // Assume the QObject has slots which aren't in the Smoke library, so try
940
- // and call the slot directly
941
- for (int id = meta->methodOffset(); id < meta->methodCount(); id++) {
942
- if (meta->method(id).methodType() == QMetaMethod::Slot) {
943
- QByteArray signature(meta->method(id).signature());
944
- QByteArray methodName = signature.mid(0, signature.indexOf('('));
945
-
946
- // Don't check that the types of the ruby args match the c++ ones for now,
947
- // only that the name and arg count is the same.
948
- if (*name == methodName && meta->method(id).parameterTypes().count() == (argc - 1)) {
949
- QList<MocArgument*> args = get_moc_arguments( o->smoke, meta->method(id).typeName(),
950
- meta->method(id).parameterTypes() );
951
- VALUE result = Qnil;
952
- QtRuby::InvokeNativeSlot slot(qobject, id, argc - 1, args, argv + 1, &result);
953
- slot.next();
954
- return result;
955
- }
956
- }
957
- }
958
- meta = meta->superClass();
959
- classId = o->smoke->idClass(meta->className()).index;
960
- }
961
- }
962
-
963
- return rb_call_super(argc, argv);
964
- }
965
- }
966
- // Success. Cache result.
967
- methcache.insert(*mcid, new Smoke::ModuleIndex(_current_method));
968
- }
969
- }
970
- QtRuby::MethodCall c(_current_method.smoke, _current_method.index, self, temp_stack+4, argc-1);
971
- c.next();
972
- VALUE result = *(c.var());
973
- return result;
974
- }
975
-
976
- VALUE
977
- class_method_missing(int argc, VALUE * argv, VALUE klass)
978
- {
979
- VALUE result = Qnil;
980
- VALUE retval = Qnil;
981
- const char * methodName = rb_id2name(SYM2ID(argv[0]));
982
- VALUE * temp_stack = ALLOCA_N(VALUE, argc+3);
983
- temp_stack[0] = rb_str_new2("Qt");
984
- temp_stack[1] = rb_str_new2(methodName);
985
- temp_stack[2] = klass;
986
- temp_stack[3] = Qnil;
987
- for (int count = 1; count < argc; count++) {
988
- temp_stack[count+3] = argv[count];
989
- }
990
-
991
- {
992
- QByteArray * mcid = find_cached_selector(argc+3, temp_stack, klass, methodName);
993
-
994
- if (_current_method.index == -1) {
995
- retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack);
996
- if (_current_method.index != -1) {
997
- // Success. Cache result.
998
- methcache.insert(*mcid, new Smoke::ModuleIndex(_current_method));
999
- }
1000
- }
1001
- }
1002
-
1003
- if (_current_method.index == -1) {
1004
- static QRegExp * rx = 0;
1005
- if (rx == 0) {
1006
- rx = new QRegExp("[a-zA-Z]+");
1007
- }
1008
-
1009
- if (rx->indexIn(methodName) == -1) {
1010
- // If an operator method hasn't been found as an instance method,
1011
- // then look for a class method - after 'op(self,a)' try 'self.op(a)'
1012
- VALUE * method_stack = ALLOCA_N(VALUE, argc - 1);
1013
- method_stack[0] = argv[0];
1014
- for (int count = 1; count < argc - 1; count++) {
1015
- method_stack[count] = argv[count+1];
1016
- }
1017
- result = method_missing(argc-1, method_stack, argv[1]);
1018
- return result;
1019
- } else {
1020
- return rb_call_super(argc, argv);
1021
- }
1022
- }
1023
- QtRuby::MethodCall c(_current_method.smoke, _current_method.index, Qnil, temp_stack+4, argc-1);
1024
- c.next();
1025
- result = *(c.var());
1026
- return result;
1027
- }
1028
-
1029
- QList<MocArgument*>
1030
- get_moc_arguments(Smoke* smoke, const char * typeName, QList<QByteArray> methodTypes)
1031
- {
1032
- static QRegExp * rx = 0;
1033
- if (rx == 0) {
1034
- rx = new QRegExp("^(bool|int|uint|long|ulong|double|char\\*|QString)&?$");
1035
- }
1036
- methodTypes.prepend(QByteArray(typeName));
1037
- QList<MocArgument*> result;
1038
-
1039
- foreach (QByteArray name, methodTypes) {
1040
- MocArgument *arg = new MocArgument;
1041
- Smoke::Index typeId = 0;
1042
-
1043
- if (name.isEmpty()) {
1044
- arg->argType = xmoc_void;
1045
- result.append(arg);
1046
- } else {
1047
- name.replace("const ", "");
1048
- QString staticType = (rx->indexIn(name) != -1 ? rx->cap(1) : "ptr");
1049
- if (staticType == "ptr") {
1050
- arg->argType = xmoc_ptr;
1051
- QByteArray targetType = name;
1052
- typeId = smoke->idType(targetType.constData());
1053
- if (typeId == 0 && !name.contains('*')) {
1054
- if (!name.contains("&")) {
1055
- targetType += "&";
1056
- }
1057
- typeId = smoke->idType(targetType.constData());
1058
- }
1059
-
1060
- // This shouldn't be necessary because the type of the slot arg should always be in the
1061
- // smoke module of the slot being invoked. However, that isn't true for a dataUpdated()
1062
- // slot in a PlasmaScripting::Applet
1063
- if (typeId == 0) {
1064
- QHash<Smoke*, QtRubyModule>::const_iterator it;
1065
- for (it = qtruby_modules.constBegin(); it != qtruby_modules.constEnd(); ++it) {
1066
- smoke = it.key();
1067
- targetType = name;
1068
- typeId = smoke->idType(targetType.constData());
1069
- if (typeId != 0) {
1070
- break;
1071
- }
1072
-
1073
- if (typeId == 0 && !name.contains('*')) {
1074
- if (!name.contains("&")) {
1075
- targetType += "&";
1076
- }
1077
-
1078
- typeId = smoke->idType(targetType.constData());
1079
-
1080
- if (typeId != 0) {
1081
- break;
1082
- }
1083
- }
1084
- }
1085
- }
1086
- } else if (staticType == "bool") {
1087
- arg->argType = xmoc_bool;
1088
- smoke = qtcore_Smoke;
1089
- typeId = smoke->idType(name.constData());
1090
- } else if (staticType == "int") {
1091
- arg->argType = xmoc_int;
1092
- smoke = qtcore_Smoke;
1093
- typeId = smoke->idType(name.constData());
1094
- } else if (staticType == "uint") {
1095
- arg->argType = xmoc_uint;
1096
- smoke = qtcore_Smoke;
1097
- typeId = smoke->idType("unsigned int");
1098
- } else if (staticType == "long") {
1099
- arg->argType = xmoc_long;
1100
- smoke = qtcore_Smoke;
1101
- typeId = smoke->idType(name.constData());
1102
- } else if (staticType == "ulong") {
1103
- arg->argType = xmoc_ulong;
1104
- smoke = qtcore_Smoke;
1105
- typeId = smoke->idType("unsigned long");
1106
- } else if (staticType == "double") {
1107
- arg->argType = xmoc_double;
1108
- smoke = qtcore_Smoke;
1109
- typeId = smoke->idType(name.constData());
1110
- } else if (staticType == "char*") {
1111
- arg->argType = xmoc_charstar;
1112
- smoke = qtcore_Smoke;
1113
- typeId = smoke->idType(name.constData());
1114
- } else if (staticType == "QString") {
1115
- arg->argType = xmoc_QString;
1116
- name += "*";
1117
- smoke = qtcore_Smoke;
1118
- typeId = smoke->idType(name.constData());
1119
- }
1120
-
1121
- if (typeId == 0) {
1122
- rb_raise(rb_eArgError, "Cannot handle '%s' as slot argument\n", name.constData());
1123
- return result;
1124
- }
1125
-
1126
- arg->st.set(smoke, typeId);
1127
- result.append(arg);
1128
- }
1129
- }
1130
-
1131
- return result;
1132
- }
1133
-
1134
- extern "C"
1135
- {
1136
- // ---------------- Helpers -------------------
1137
-
1138
- //---------- All functions except fully qualified statics & enums ---------
1139
-
1140
- VALUE
1141
- mapObject(VALUE self, VALUE obj)
1142
- {
1143
- smokeruby_object *o = value_obj_info(obj);
1144
- if(!o)
1145
- return Qnil;
1146
- mapPointer(obj, o, o->classId, 0);
1147
- return self;
1148
- }
1149
-
1150
- VALUE set_obj_info(const char * className, smokeruby_object * o);
1151
-
1152
- VALUE
1153
- qobject_metaobject(VALUE self)
1154
- {
1155
- smokeruby_object * o = value_obj_info(self);
1156
- QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
1157
- QMetaObject * meta = (QMetaObject *) qobject->metaObject();
1158
- VALUE obj = getPointerObject(meta);
1159
- if (obj != Qnil) {
1160
- return obj;
1161
- }
1162
-
1163
- smokeruby_object * m = alloc_smokeruby_object( false,
1164
- o->smoke,
1165
- o->smoke->idClass("QMetaObject").index,
1166
- meta );
1167
-
1168
- obj = set_obj_info("Qt::MetaObject", m);
1169
- return obj;
1170
- }
1171
-
1172
- VALUE
1173
- set_obj_info(const char * className, smokeruby_object * o)
1174
- {
1175
- VALUE klass = rb_funcall(qt_internal_module,
1176
- rb_intern("find_class"),
1177
- 1,
1178
- rb_str_new2(className) );
1179
- if (klass == Qnil) {
1180
- rb_raise(rb_eRuntimeError, "Class '%s' not found", className);
1181
- }
1182
-
1183
- Smoke::ModuleIndex *r = classcache.value(className);
1184
- if (r != 0) {
1185
- o->classId = (int) r->index;
1186
- }
1187
- // If the instance is a subclass of QObject, then check to see if the
1188
- // className from its QMetaObject is in the Smoke library. If not then
1189
- // create a Ruby class for it dynamically. Remove the first letter from
1190
- // any class names beginning with 'Q' or 'K' and put them under the Qt::
1191
- // or KDE:: modules respectively.
1192
- if (o->smoke->isDerivedFrom(o->smoke, o->classId, o->smoke->idClass("QObject").smoke, o->smoke->idClass("QObject").index)) {
1193
- QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
1194
- const QMetaObject * meta = qobject->metaObject();
1195
- int classId = o->smoke->idClass(meta->className()).index;
1196
- // The class isn't in the Smoke lib..
1197
- if (classId == 0) {
1198
- VALUE new_klass = Qnil;
1199
- QByteArray className(meta->className());
1200
-
1201
- if (className == "QTableModel") {
1202
- new_klass = qtablemodel_class;
1203
- } else if (className == "QListModel") {
1204
- new_klass = qlistmodel_class;
1205
- } else if (className.startsWith("Q")) {
1206
- className.replace("Q", "");
1207
- className = className.mid(0, 1).toUpper() + className.mid(1);
1208
- new_klass = rb_define_class_under(qt_module, className, klass);
1209
- } else {
1210
- new_klass = rb_define_class(className, klass);
1211
- }
1212
-
1213
- if (new_klass != Qnil) {
1214
- klass = new_klass;
1215
-
1216
- for (int id = meta->enumeratorOffset(); id < meta->enumeratorCount(); id++) {
1217
- // If there are any enum keys with the same scope as the new class then
1218
- // add them
1219
- if (qstrcmp(meta->className(), meta->enumerator(id).scope()) == 0) {
1220
- for (int i = 0; i < meta->enumerator(id).keyCount(); i++) {
1221
- rb_define_const( klass,
1222
- meta->enumerator(id).key(i),
1223
- INT2NUM(meta->enumerator(id).value(i)) );
1224
- }
1225
- }
1226
- }
1227
- }
1228
-
1229
- // Add a Qt::Object.metaObject method which will do dynamic despatch on the
1230
- // metaObject() virtual method so that the true QMetaObject of the class
1231
- // is returned, rather than for the one for the parent class that is in
1232
- // the Smoke library.
1233
- rb_define_method(klass, "metaObject", (VALUE (*) (...)) qobject_metaobject, 0);
1234
- }
1235
- }
1236
-
1237
- VALUE obj = Data_Wrap_Struct(klass, smokeruby_mark, smokeruby_free, (void *) o);
1238
- return obj;
1239
- }
1240
-
1241
- VALUE
1242
- kross2smoke(VALUE /*self*/, VALUE krobject, VALUE new_klass)
1243
- {
1244
- VALUE new_klassname = rb_funcall(new_klass, rb_intern("name"), 0);
1245
-
1246
- Smoke::ModuleIndex * cast_to_id = classcache.value(StringValuePtr(new_klassname));
1247
- if (cast_to_id == 0) {
1248
- rb_raise(rb_eArgError, "unable to find class \"%s\" to cast to\n", StringValuePtr(new_klassname));
1249
- }
1250
-
1251
- void* o;
1252
- Data_Get_Struct(krobject, void, o);
1253
-
1254
- smokeruby_object * o_cast = alloc_smokeruby_object(false, cast_to_id->smoke, (int) cast_to_id->index, o);
1255
-
1256
- VALUE obj = Data_Wrap_Struct(new_klass, smokeruby_mark, smokeruby_free, (void *) o_cast);
1257
- mapPointer(obj, o_cast, o_cast->classId, 0);
1258
- return obj;
1259
- }
1260
-
1261
- const char *
1262
- value_to_type_flag(VALUE ruby_value)
1263
- {
1264
- const char * classname = rb_obj_classname(ruby_value);
1265
- const char *r = "";
1266
- if (ruby_value == Qnil)
1267
- r = "u";
1268
- else if (TYPE(ruby_value) == T_FIXNUM || TYPE(ruby_value) == T_BIGNUM || qstrcmp(classname, "Qt::Integer") == 0)
1269
- r = "i";
1270
- else if (TYPE(ruby_value) == T_FLOAT)
1271
- r = "n";
1272
- else if (TYPE(ruby_value) == T_STRING)
1273
- r = "s";
1274
- else if(ruby_value == Qtrue || ruby_value == Qfalse || qstrcmp(classname, "Qt::Boolean") == 0)
1275
- r = "B";
1276
- else if (qstrcmp(classname, "Qt::Enum") == 0) {
1277
- VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qenum_type"), 1, ruby_value);
1278
- r = StringValuePtr(temp);
1279
- } else if (TYPE(ruby_value) == T_DATA) {
1280
- smokeruby_object *o = value_obj_info(ruby_value);
1281
- if (o == 0 || o->smoke == 0) {
1282
- r = "a";
1283
- } else {
1284
- r = o->smoke->classes[o->classId].className;
1285
- }
1286
- } else {
1287
- r = "U";
1288
- }
1289
-
1290
- return r;
1291
- }
1292
-
1293
- VALUE prettyPrintMethod(Smoke::Index id)
1294
- {
1295
- VALUE r = rb_str_new2("");
1296
- const Smoke::Method &meth = qtcore_Smoke->methods[id];
1297
- const char *tname = qtcore_Smoke->types[meth.ret].name;
1298
- if(meth.flags & Smoke::mf_static) rb_str_catf(r, "static ");
1299
- rb_str_catf(r, "%s ", (tname ? tname:"void"));
1300
- rb_str_catf(r, "%s::%s(", qtcore_Smoke->classes[meth.classId].className, qtcore_Smoke->methodNames[meth.name]);
1301
- for(int i = 0; i < meth.numArgs; i++) {
1302
- if(i) rb_str_catf(r, ", ");
1303
- tname = qtcore_Smoke->types[qtcore_Smoke->argumentList[meth.args+i]].name;
1304
- rb_str_catf(r, "%s", (tname ? tname:"void"));
1305
- }
1306
- rb_str_catf(r, ")");
1307
- if(meth.flags & Smoke::mf_const) rb_str_catf(r, " const");
1308
- return r;
1309
- }
1310
- }
1
+ /***************************************************************************
2
+ Qt.cpp - description
3
+ -------------------
4
+ begin : Fri Jul 4 2003
5
+ copyright : (C) 2003-2006 by Richard Dale
6
+ email : Richard_Dale@tipitina.demon.co.uk
7
+ ***************************************************************************/
8
+
9
+ /***************************************************************************
10
+ * *
11
+ * This program is free software; you can redistribute it and/or modify *
12
+ * it under the terms of the GNU Lesser General Public License as *
13
+ * published by the Free Software Foundation; either version 2 of the *
14
+ * License, or (at your option) any later version. *
15
+ * *
16
+ ***************************************************************************/
17
+
18
+ #ifndef _GNU_SOURCE
19
+ #define _GNU_SOURCE
20
+ #endif
21
+ #include <stdio.h>
22
+ #include <stdarg.h>
23
+
24
+ #include <QtCore/qabstractitemmodel.h>
25
+ #include <QtCore/qglobal.h>
26
+ #include <QtCore/qhash.h>
27
+ #include <QtCore/qline.h>
28
+ #include <QtCore/qmetaobject.h>
29
+ #include <QtCore/qobject.h>
30
+ #include <QtCore/qrect.h>
31
+ #include <QtCore/qregexp.h>
32
+ #include <QtCore/qstring.h>
33
+ #include <QtCore/qvariant.h>
34
+ #include <QtCore/qmutex.h>
35
+ #include <QtGui/qapplication.h>
36
+ #include <QtGui/qbitmap.h>
37
+ #include <QtGui/qcolor.h>
38
+ #include <QtGui/qcursor.h>
39
+ #include <QtGui/qfont.h>
40
+ #include <QtGui/qicon.h>
41
+ #include <QtGui/qitemselectionmodel.h>
42
+ #include <QtGui/qpalette.h>
43
+ #include <QtGui/qpen.h>
44
+ #include <QtGui/qpixmap.h>
45
+ #include <QtGui/qpolygon.h>
46
+ #include <QtGui/qtextformat.h>
47
+ #include <QtGui/qwidget.h>
48
+
49
+ #ifdef QT_QTDBUS
50
+ #include <QtDBus/qdbusargument.h>
51
+ #endif
52
+
53
+ #undef DEBUG
54
+ #ifndef __USE_POSIX
55
+ #define __USE_POSIX
56
+ #endif
57
+ #ifndef __USE_XOPEN
58
+ #define __USE_XOPEN
59
+ #endif
60
+ #ifdef _BOOL
61
+ #define HAS_BOOL
62
+ #endif
63
+
64
+ #include <ruby.h>
65
+
66
+ #include <smoke/smoke.h>
67
+ #include <smoke/qtcore_smoke.h>
68
+
69
+ #undef free
70
+ #undef malloc
71
+
72
+ #include "marshall.h"
73
+ #include "qtruby.h"
74
+ #include "smokeruby.h"
75
+ #include "smoke.h"
76
+ #include "marshall_types.h"
77
+ // #define DEBUG
78
+
79
+ extern "C" {
80
+ VALUE qt_internal_module = Qnil;
81
+ VALUE qt_module = Qnil;
82
+
83
+ VALUE qlistmodel_class = Qnil;
84
+ VALUE qmetaobject_class = Qnil;
85
+ VALUE qtablemodel_class = Qnil;
86
+ VALUE qt_base_class = Qnil;
87
+ VALUE qvariant_class = Qnil;
88
+
89
+ VALUE moduleindex_class = Qnil;
90
+
91
+ bool application_terminated = false;
92
+ }
93
+
94
+ QList<Smoke*> smokeList;
95
+ QHash<Smoke*, QtRubyModule> qtruby_modules;
96
+
97
+ #ifdef DEBUG
98
+ int do_debug = qtdb_gc;
99
+ #else
100
+ int do_debug = qtdb_none;
101
+ #endif
102
+
103
+ typedef QHash<void *, SmokeValue> PointerMap;
104
+ static QMutex pointer_map_mutex;
105
+ Q_GLOBAL_STATIC(PointerMap, pointer_map)
106
+ int object_count = 0;
107
+
108
+ // FIXME:
109
+ // Don't the two following hashs create memory leaks by using pointers to Smoke::(Module)Index ?
110
+ QHash<QByteArray, Smoke::ModuleIndex *> methcache;
111
+ QHash<QByteArray, Smoke::ModuleIndex *> classcache;
112
+
113
+ QHash<Smoke::ModuleIndex, QByteArray*> IdToClassNameMap;
114
+
115
+ #define logger logger_backend
116
+
117
+ Smoke::ModuleIndex _current_method;
118
+
119
+ smokeruby_object *
120
+ alloc_smokeruby_object(bool allocated, Smoke * smoke, int classId, void * ptr)
121
+ {
122
+ smokeruby_object * o = ALLOC(smokeruby_object);
123
+ o->classId = classId;
124
+ o->smoke = smoke;
125
+ o->ptr = ptr;
126
+ o->allocated = allocated;
127
+ return o;
128
+ }
129
+
130
+ void
131
+ free_smokeruby_object(smokeruby_object * o)
132
+ {
133
+ o->ptr = 0;
134
+ xfree(o);
135
+ return;
136
+ }
137
+
138
+ smokeruby_object *value_obj_info(VALUE ruby_value) { // ptr on success, null on fail
139
+ if (TYPE(ruby_value) != T_DATA) {
140
+ return 0;
141
+ }
142
+
143
+ smokeruby_object * o = 0;
144
+ Data_Get_Struct(ruby_value, smokeruby_object, o);
145
+ return o;
146
+ }
147
+
148
+ void *value_to_ptr(VALUE ruby_value) { // ptr on success, null on fail
149
+ smokeruby_object *o = value_obj_info(ruby_value);
150
+ return o;
151
+ }
152
+
153
+ VALUE getPointerObject(void *ptr) {
154
+ return getSmokeValue(ptr).value;
155
+ }
156
+
157
+ SmokeValue getSmokeValue(void *ptr) {
158
+ pointer_map_mutex.lock();
159
+
160
+ if (!pointer_map() || !pointer_map()->contains(ptr)) {
161
+ if (do_debug & qtdb_gc) {
162
+ qWarning("getPointerObject %p -> nil", ptr);
163
+ if (!pointer_map()) {
164
+ qWarning("getPointerObject pointer_map deleted");
165
+ }
166
+ }
167
+ pointer_map_mutex.unlock();
168
+ return SmokeValue();
169
+ } else {
170
+ if (do_debug & qtdb_gc) {
171
+ qWarning("getPointerObject %p -> %p", ptr, (void *) pointer_map()->operator[](ptr).value);
172
+ }
173
+ pointer_map_mutex.unlock();
174
+ return pointer_map()->operator[](ptr);
175
+ }
176
+ }
177
+
178
+ void unmapPointer(void *ptr, Smoke *smoke, Smoke::Index fromClassId, Smoke::Index toClassId, void *lastptr) {
179
+ pointer_map_mutex.lock();
180
+ ptr = smoke->cast(ptr, fromClassId, toClassId);
181
+
182
+ if (ptr != lastptr) {
183
+ lastptr = ptr;
184
+ if (pointer_map() && pointer_map()->contains(ptr)) {
185
+ VALUE obj_ptr = pointer_map()->operator[](ptr).value;
186
+
187
+ if (do_debug & qtdb_gc) {
188
+ const char *className = smoke->classes[fromClassId].className;
189
+ qWarning("unmapPointer (%s*)%p -> %p size: %d", className, ptr, (void*)(&obj_ptr), pointer_map()->size() - 1);
190
+ }
191
+
192
+ pointer_map()->remove(ptr);
193
+ }
194
+ }
195
+
196
+ if (smoke->classes[toClassId].external) {
197
+ // encountered external class
198
+ Smoke::ModuleIndex mi = Smoke::findClass(smoke->classes[toClassId].className);
199
+ if (!mi.index || !mi.smoke) return;
200
+
201
+ smoke = mi.smoke;
202
+ toClassId = mi.index;
203
+ }
204
+
205
+ pointer_map_mutex.unlock();
206
+ for (Smoke::Index *i = smoke->inheritanceList + smoke->classes[toClassId].parents; *i; i++) {
207
+ unmapPointer(ptr, smoke, toClassId, *i, lastptr);
208
+ }
209
+
210
+ }
211
+
212
+ void unmapPointer(smokeruby_object *o, Smoke::Index classId, void *lastptr) {
213
+ unmapPointer(o->ptr, o->smoke, o->classId, classId, lastptr);
214
+ }
215
+
216
+ // Store pointer in pointer_map hash : "pointer_to_Qt_object" => weak ref to associated Ruby object
217
+ // Recurse to store it also as casted to its parent classes.
218
+
219
+ void mapPointer(VALUE obj, smokeruby_object* o, void *ptr, Smoke *smoke, Smoke::Index fromClassId, Smoke::Index toClassId, void *lastptr) {
220
+ pointer_map_mutex.unlock();
221
+
222
+ ptr = smoke->cast(ptr, fromClassId, toClassId);
223
+
224
+ if (ptr != lastptr) {
225
+ lastptr = ptr;
226
+
227
+ if (do_debug & qtdb_gc) {
228
+ const char *className = smoke->classes[fromClassId].className;
229
+ qWarning("mapPointer (%s*)%p -> %p size: %d", className, ptr, (void*)obj, pointer_map()->size() + 1);
230
+ }
231
+
232
+ SmokeValue value(obj, o);
233
+ pointer_map()->insert(ptr, value);
234
+ }
235
+
236
+ if (smoke->classes[toClassId].external) {
237
+ // encountered external class
238
+ Smoke::ModuleIndex mi = Smoke::findClass(smoke->classes[toClassId].className);
239
+ if (!mi.index || !mi.smoke) return;
240
+
241
+ smoke = mi.smoke;
242
+ toClassId = mi.index;
243
+ }
244
+
245
+ pointer_map_mutex.unlock();
246
+ for (Smoke::Index *i = smoke->inheritanceList + smoke->classes[toClassId].parents; *i; i++) {
247
+ mapPointer(obj, o, ptr, smoke, toClassId, *i, lastptr);
248
+ }
249
+
250
+ return;
251
+ }
252
+
253
+ void mapPointer(VALUE obj, smokeruby_object *o, Smoke::Index classId, void *lastptr) {
254
+ mapPointer(obj, o, o->ptr, o->smoke, o->classId, classId, lastptr);
255
+ }
256
+
257
+ namespace QtRuby {
258
+
259
+ Binding::Binding() : SmokeBinding(0) {}
260
+ Binding::Binding(Smoke *s) : SmokeBinding(s) {}
261
+
262
+ void
263
+ Binding::deleted(Smoke::Index classId, void *ptr) {
264
+ if (!pointer_map()) {
265
+ return;
266
+ }
267
+
268
+ smokeruby_object *o = getSmokeValue(ptr).o;
269
+ if (do_debug & qtdb_gc) {
270
+ qWarning("unmapping: o = %p, ptr = %p\n", o, ptr);
271
+ qWarning("%p->~%s()", ptr, smoke->className(classId));
272
+ }
273
+ if (!o || !o->ptr) {
274
+ return;
275
+ }
276
+ unmapPointer(o, o->classId, 0);
277
+ o->ptr = 0;
278
+ }
279
+
280
+ bool
281
+ Binding::callMethod(Smoke::Index method, void *ptr, Smoke::Stack args, bool /*isAbstract*/) {
282
+ VALUE obj = getPointerObject(ptr);
283
+ smokeruby_object *o = value_obj_info(obj);
284
+
285
+ if (do_debug & qtdb_virtual) {
286
+ const Smoke::Method & meth = smoke->methods[method];
287
+ QByteArray signature(smoke->methodNames[meth.name]);
288
+ signature += "(";
289
+ for (int i = 0; i < meth.numArgs; i++) {
290
+ if (i != 0) signature += ", ";
291
+ signature += smoke->types[smoke->argumentList[meth.args + i]].name;
292
+ }
293
+ signature += ")";
294
+ if (meth.flags & Smoke::mf_const) {
295
+ signature += " const";
296
+ }
297
+ qWarning( "module: %s virtual %p->%s::%s called",
298
+ smoke->moduleName(),
299
+ ptr,
300
+ smoke->classes[smoke->methods[method].classId].className,
301
+ (const char *) signature );
302
+ }
303
+
304
+ if (o == 0) {
305
+ if( do_debug & qtdb_virtual ) // if not in global destruction
306
+ qWarning("Cannot find object for virtual method %p -> %p", ptr, &obj);
307
+ return false;
308
+ }
309
+ const char *methodName = smoke->methodNames[smoke->methods[method].name];
310
+ if (qstrncmp(methodName, "operator", sizeof("operator") - 1) == 0) {
311
+ methodName += (sizeof("operator") - 1);
312
+ }
313
+
314
+ // If not in a ruby thread, just call the C++ version
315
+ // During GC, avoid checking for override and just call the C++ version
316
+ // If the virtual method hasn't been overriden, just call the C++ one.
317
+ #ifdef HAVE_RUBY_RUBY_H
318
+ int ruby_thread = ruby_native_thread_p();
319
+ if ((ruby_thread == 0) || rb_during_gc() || rb_respond_to(obj, rb_intern(methodName)) == 0) {
320
+ return false;
321
+ }
322
+ #else
323
+ if (rb_during_gc() || ruby_stack_check() || rb_respond_to(obj, rb_intern(methodName)) == 0) {
324
+ return false;
325
+ }
326
+ #endif
327
+
328
+ QtRuby::VirtualMethodCall c(smoke, method, args, obj, ALLOCA_N(VALUE, smoke->methods[method].numArgs));
329
+ c.next();
330
+ return true;
331
+ }
332
+
333
+ char*
334
+ Binding::className(Smoke::Index classId) {
335
+ Smoke::ModuleIndex mi(smoke, classId);
336
+ return (char *) (const char *) *(IdToClassNameMap.value(mi));
337
+ }
338
+
339
+ /*
340
+ Converts a C++ value returned by a signal invocation to a Ruby
341
+ reply type
342
+ */
343
+ class SignalReturnValue : public Marshall {
344
+ QList<MocArgument*> _replyType;
345
+ Smoke::Stack _stack;
346
+ VALUE * _result;
347
+ public:
348
+ SignalReturnValue(void ** o, VALUE * result, QList<MocArgument*> replyType)
349
+ {
350
+ _result = result;
351
+ _replyType = replyType;
352
+ _stack = new Smoke::StackItem[1];
353
+ smokeStackFromQtStack(_stack, o, 0, 1, _replyType);
354
+ Marshall::HandlerFn fn = getMarshallFn(type());
355
+ (*fn)(this);
356
+ }
357
+
358
+ SmokeType type() {
359
+ return _replyType[0]->st;
360
+ }
361
+ Marshall::Action action() { return Marshall::ToVALUE; }
362
+ Smoke::StackItem &item() { return _stack[0]; }
363
+ VALUE * var() {
364
+ return _result;
365
+ }
366
+
367
+ void unsupported()
368
+ {
369
+ rb_raise(rb_eArgError, "Cannot handle '%s' as signal reply-type", type().name());
370
+ }
371
+ Smoke *smoke() { return type().smoke(); }
372
+
373
+ void next() {}
374
+
375
+ bool cleanup() { return false; }
376
+
377
+ ~SignalReturnValue() {
378
+ delete[] _stack;
379
+ }
380
+ };
381
+
382
+ /* Note that the SignalReturnValue and EmitSignal classes really belong in
383
+ marshall_types.cpp. However, for unknown reasons they don't link with certain
384
+ versions of gcc. So they were moved here in to work round that bug.
385
+ */
386
+ EmitSignal::EmitSignal(QObject *obj, int id, int /*items*/, QList<MocArgument*> args, VALUE *sp, VALUE * result) : SigSlotBase(args),
387
+ _obj(obj), _id(id)
388
+ {
389
+ _sp = sp;
390
+ _result = result;
391
+ }
392
+
393
+ Marshall::Action
394
+ EmitSignal::action()
395
+ {
396
+ return Marshall::FromVALUE;
397
+ }
398
+
399
+ Smoke::StackItem &
400
+ EmitSignal::item()
401
+ {
402
+ return _stack[_cur];
403
+ }
404
+
405
+ const char *
406
+ EmitSignal::mytype()
407
+ {
408
+ return "signal";
409
+ }
410
+
411
+ void
412
+ EmitSignal::emitSignal()
413
+ {
414
+ if (_called) return;
415
+ _called = true;
416
+ void ** o = new void*[_items];
417
+ smokeStackToQtStack(_stack, o + 1, 1, _items, _args);
418
+ void * ptr;
419
+ o[0] = &ptr;
420
+ prepareReturnValue(o);
421
+
422
+ _obj->metaObject()->activate(_obj, _id, o);
423
+
424
+ if (_args[0]->argType != xmoc_void) {
425
+ SignalReturnValue r(o, _result, _args);
426
+ }
427
+ delete[] o;
428
+ }
429
+
430
+ void
431
+ EmitSignal::mainfunction()
432
+ {
433
+ emitSignal();
434
+ }
435
+
436
+ bool
437
+ EmitSignal::cleanup()
438
+ {
439
+ return true;
440
+ }
441
+
442
+ InvokeNativeSlot::InvokeNativeSlot(QObject *obj, int id, int /*items*/, QList<MocArgument*> args, VALUE *sp, VALUE * result) : SigSlotBase(args),
443
+ _obj(obj), _id(id)
444
+ {
445
+ _sp = sp;
446
+ _result = result;
447
+ }
448
+
449
+ Marshall::Action
450
+ InvokeNativeSlot::action()
451
+ {
452
+ return Marshall::FromVALUE;
453
+ }
454
+
455
+ Smoke::StackItem &
456
+ InvokeNativeSlot::item()
457
+ {
458
+ return _stack[_cur];
459
+ }
460
+
461
+ const char *
462
+ InvokeNativeSlot::mytype()
463
+ {
464
+ return "slot";
465
+ }
466
+
467
+ void
468
+ InvokeNativeSlot::invokeSlot()
469
+ {
470
+ if (_called) return;
471
+ _called = true;
472
+ void ** o = new void*[_items];
473
+ smokeStackToQtStack(_stack, o + 1, 1, _items, _args);
474
+ void * ptr;
475
+ o[0] = &ptr;
476
+ prepareReturnValue(o);
477
+
478
+ _obj->qt_metacall(QMetaObject::InvokeMetaMethod, _id, o);
479
+
480
+ if (_args[0]->argType != xmoc_void) {
481
+ SignalReturnValue r(o, _result, _args);
482
+ }
483
+ delete[] o;
484
+ }
485
+
486
+ void
487
+ InvokeNativeSlot::mainfunction()
488
+ {
489
+ invokeSlot();
490
+ }
491
+
492
+ bool
493
+ InvokeNativeSlot::cleanup()
494
+ {
495
+ return true;
496
+ }
497
+
498
+ }
499
+
500
+ VALUE rb_str_catf(VALUE self, const char *format, ...)
501
+ {
502
+ #define CAT_BUFFER_SIZE 2048
503
+ static char p[CAT_BUFFER_SIZE];
504
+ va_list ap;
505
+ va_start(ap, format);
506
+ qvsnprintf(p, CAT_BUFFER_SIZE, format, ap);
507
+ p[CAT_BUFFER_SIZE - 1] = '\0';
508
+ rb_str_cat2(self, p);
509
+ va_end(ap);
510
+ return self;
511
+ }
512
+
513
+ const char *
514
+ resolve_classname(smokeruby_object * o)
515
+ {
516
+ if (Smoke::isDerivedFrom(o->smoke->classes[o->classId].className, "QObject")) {
517
+ QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
518
+ const QMetaObject * meta = qobject->metaObject();
519
+
520
+ while (meta != 0) {
521
+ Smoke::ModuleIndex mi = o->smoke->findClass(meta->className());
522
+ o->smoke = mi.smoke;
523
+ o->classId = mi.index;
524
+ if (o->smoke != 0) {
525
+ if (o->classId != 0) {
526
+ return qtruby_modules[o->smoke].binding->className(o->classId);
527
+ }
528
+ }
529
+
530
+ meta = meta->superClass();
531
+ }
532
+ }
533
+
534
+ if (o->smoke->classes[o->classId].external) {
535
+ Smoke::ModuleIndex mi = o->smoke->findClass(o->smoke->className(o->classId));
536
+ o->smoke = mi.smoke;
537
+ o->classId = mi.index;
538
+ return qtruby_modules.value(mi.smoke).resolve_classname(o);
539
+ }
540
+ return qtruby_modules.value(o->smoke).resolve_classname(o);
541
+ }
542
+
543
+ VALUE
544
+ findMethod(VALUE /*self*/, VALUE c_value, VALUE name_value)
545
+ {
546
+ char *c = StringValuePtr(c_value);
547
+ char *name = StringValuePtr(name_value);
548
+ VALUE result = rb_ary_new();
549
+ Smoke::ModuleIndex classId = Smoke::findClass(c);
550
+ Smoke::ModuleIndex meth = Smoke::NullModuleIndex;
551
+ QList<Smoke::ModuleIndex> milist;
552
+ if (classId.smoke != 0) {
553
+ meth = classId.smoke->findMethod(c, name);
554
+ }
555
+ #ifdef DEBUG
556
+ if (do_debug & qtdb_calls) qWarning("Found method %s::%s => %d", c, name, meth.index);
557
+ #endif
558
+ if(!meth.index) {
559
+ // since every smoke module defines a class 'QGlobalSpace' we can't rely on the classMap,
560
+ // so we search for methods by hand
561
+ foreach (Smoke* s, smokeList) {
562
+ Smoke::ModuleIndex cid = s->idClass("QGlobalSpace");
563
+ Smoke::ModuleIndex mnid = s->idMethodName(name);
564
+ if (!cid.index || !mnid.index) continue;
565
+ meth = s->idMethod(cid.index, mnid.index);
566
+ if (meth.index) milist.append(meth);
567
+ }
568
+ #ifdef DEBUG
569
+ if (do_debug & qtdb_calls) qWarning("Found method QGlobalSpace::%s => %d", name, meth.index);
570
+ #endif
571
+ }
572
+ else
573
+ {
574
+ milist.append(meth);
575
+ }
576
+
577
+ if (milist.count() == 0) {
578
+ return result;
579
+ // empty list
580
+ } else {
581
+ foreach (Smoke::ModuleIndex meth, milist) {
582
+ if (meth.index > 0) {
583
+ Smoke::Index i = meth.smoke->methodMaps[meth.index].method;
584
+ if (i == 0) { // shouldn't happen
585
+ rb_raise(rb_eArgError, "Corrupt method %s::%s", c, name);
586
+ } else if(i > 0) { // single match
587
+ const Smoke::Method &methodRef = meth.smoke->methods[i];
588
+ if ((methodRef.flags & Smoke::mf_internal) == 0) {
589
+ rb_ary_push(result, rb_funcall(moduleindex_class, rb_intern("new"), 2, INT2NUM(smokeList.indexOf(meth.smoke)), INT2NUM(i)));
590
+ }
591
+ } else { // multiple match
592
+ i = -i; // turn into ambiguousMethodList index
593
+ while (meth.smoke->ambiguousMethodList[i]) {
594
+ const Smoke::Method &methodRef = meth.smoke->methods[meth.smoke->ambiguousMethodList[i]];
595
+ if ((methodRef.flags & Smoke::mf_internal) == 0) {
596
+ rb_ary_push(result, rb_funcall(moduleindex_class, rb_intern("new"), 2, INT2NUM(smokeList.indexOf(meth.smoke)), INT2NUM(meth.smoke->ambiguousMethodList[i])));
597
+ //#ifdef DEBUG
598
+ if (do_debug & qtdb_calls) qWarning("Ambiguous Method %s::%s => %d", c, name, meth.smoke->ambiguousMethodList[i]);
599
+ //#endif
600
+
601
+ }
602
+ i++;
603
+ }
604
+ }
605
+ }
606
+ }
607
+ }
608
+ return result;
609
+ }
610
+
611
+ // findAllMethods(ModuleIndex [, startingWith]) : returns { "mungedName" => [index in methods, ...], ... }
612
+
613
+ VALUE
614
+ findAllMethods(int argc, VALUE * argv, VALUE /*self*/)
615
+ {
616
+ VALUE rb_mi = argv[0];
617
+ VALUE result = rb_hash_new();
618
+ if (rb_mi != Qnil) {
619
+ Smoke::Index c = (Smoke::Index) NUM2INT(rb_funcall(rb_mi, rb_intern("index"), 0));
620
+ Smoke *smoke = smokeList[NUM2INT(rb_funcall(rb_mi, rb_intern("smoke"), 0))];
621
+ if (c > smoke->numClasses) {
622
+ return Qnil;
623
+ }
624
+ char * pat = 0L;
625
+ if(argc > 1 && TYPE(argv[1]) == T_STRING)
626
+ pat = StringValuePtr(argv[1]);
627
+ #ifdef DEBUG
628
+ if (do_debug & qtdb_calls) qWarning("findAllMethods called with classid = %d, pat == %s", c, pat);
629
+ #endif
630
+ Smoke::Index imax = smoke->numMethodMaps;
631
+ Smoke::Index imin = 0, icur = -1, methmin, methmax;
632
+ methmin = -1; methmax = -1; // kill warnings
633
+ int icmp = -1;
634
+ while(imax >= imin) {
635
+ icur = (imin + imax) / 2;
636
+ icmp = smoke->leg(smoke->methodMaps[icur].classId, c);
637
+ if (icmp == 0) {
638
+ Smoke::Index pos = icur;
639
+ while (icur && smoke->methodMaps[icur-1].classId == c)
640
+ icur --;
641
+ methmin = icur;
642
+ icur = pos;
643
+ while(icur < imax && smoke->methodMaps[icur+1].classId == c)
644
+ icur ++;
645
+ methmax = icur;
646
+ break;
647
+ }
648
+ if (icmp > 0)
649
+ imax = icur - 1;
650
+ else
651
+ imin = icur + 1;
652
+ }
653
+ if (icmp == 0) {
654
+ for (Smoke::Index i = methmin; i <= methmax; i++) {
655
+ Smoke::Index m = smoke->methodMaps[i].name;
656
+ if (pat == 0L || strncmp(smoke->methodNames[m], pat, strlen(pat)) == 0) {
657
+ Smoke::Index ix = smoke->methodMaps[i].method;
658
+ VALUE meths = rb_ary_new();
659
+ if (ix >= 0) { // single match
660
+ const Smoke::Method &methodRef = smoke->methods[ix];
661
+ if ((methodRef.flags & Smoke::mf_internal) == 0) {
662
+ rb_ary_push(meths, rb_funcall(moduleindex_class, rb_intern("new"), 2, INT2NUM(smokeList.indexOf(smoke)), INT2NUM((int) ix)));
663
+ }
664
+ } else { // multiple match
665
+ ix = -ix; // turn into ambiguousMethodList index
666
+ while (smoke->ambiguousMethodList[ix]) {
667
+ const Smoke::Method &methodRef = smoke->methods[smoke->ambiguousMethodList[ix]];
668
+ if ((methodRef.flags & Smoke::mf_internal) == 0) {
669
+ rb_ary_push(meths, rb_funcall(moduleindex_class, rb_intern("new"), 2, INT2NUM(smokeList.indexOf(smoke)), INT2NUM((int)smoke->ambiguousMethodList[ix])));
670
+ }
671
+ ix++;
672
+ }
673
+ }
674
+ rb_hash_aset(result, rb_str_new2(smoke->methodNames[m]), meths);
675
+ }
676
+ }
677
+ }
678
+ }
679
+ return result;
680
+ }
681
+
682
+ /*
683
+ Flags values
684
+ 0 All methods, except enum values and protected non-static methods
685
+ mf_static Static methods only
686
+ mf_enum Enums only
687
+ mf_protected Protected non-static methods only
688
+ */
689
+
690
+ #define PUSH_QTRUBY_METHOD \
691
+ if ( (methodRef.flags & (Smoke::mf_internal|Smoke::mf_ctor|Smoke::mf_dtor)) == 0 \
692
+ && strcmp(s->methodNames[methodRef.name], "operator=") != 0 \
693
+ && strcmp(s->methodNames[methodRef.name], "operator!=") != 0 \
694
+ && strcmp(s->methodNames[methodRef.name], "operator--") != 0 \
695
+ && strcmp(s->methodNames[methodRef.name], "operator++") != 0 \
696
+ && strncmp(s->methodNames[methodRef.name], "operator ", strlen("operator ")) != 0 \
697
+ && ( (flags == 0 && (methodRef.flags & (Smoke::mf_static|Smoke::mf_enum|Smoke::mf_protected)) == 0) \
698
+ || ( flags == Smoke::mf_static \
699
+ && (methodRef.flags & Smoke::mf_enum) == 0 \
700
+ && (methodRef.flags & Smoke::mf_static) == Smoke::mf_static ) \
701
+ || (flags == Smoke::mf_enum && (methodRef.flags & Smoke::mf_enum) == Smoke::mf_enum) \
702
+ || ( flags == Smoke::mf_protected \
703
+ && (methodRef.flags & Smoke::mf_static) == 0 \
704
+ && (methodRef.flags & Smoke::mf_protected) == Smoke::mf_protected ) ) ) { \
705
+ if (strncmp(s->methodNames[methodRef.name], "operator", strlen("operator")) == 0) { \
706
+ if (op_re.indexIn(s->methodNames[methodRef.name]) != -1) { \
707
+ rb_ary_push(result, rb_str_new2((op_re.cap(1) + op_re.cap(2)).toLatin1())); \
708
+ } else { \
709
+ rb_ary_push(result, rb_str_new2(s->methodNames[methodRef.name] + strlen("operator"))); \
710
+ } \
711
+ } else if (predicate_re.indexIn(s->methodNames[methodRef.name]) != -1 && methodRef.numArgs == 0) { \
712
+ rb_ary_push(result, rb_str_new2((predicate_re.cap(2).toLower() + predicate_re.cap(3) + "?").toLatin1())); \
713
+ } else if (set_re.indexIn(s->methodNames[methodRef.name]) != -1 && methodRef.numArgs == 1) { \
714
+ rb_ary_push(result, rb_str_new2((set_re.cap(2).toLower() + set_re.cap(3) + "=").toLatin1())); \
715
+ } else { \
716
+ rb_ary_push(result, rb_str_new2(s->methodNames[methodRef.name])); \
717
+ } \
718
+ }
719
+
720
+ VALUE
721
+ findAllMethodNames(VALUE /*self*/, VALUE result, VALUE classid, VALUE flags_value)
722
+ {
723
+ QRegExp predicate_re("^(is|has)(.)(.*)");
724
+ QRegExp set_re("^(set)([A-Z])(.*)");
725
+ QRegExp op_re("operator(.*)(([-%~/+|&*])|(>>)|(<<)|(&&)|(\\|\\|)|(\\*\\*))=$");
726
+
727
+ unsigned short flags = (unsigned short) NUM2UINT(flags_value);
728
+ if (classid != Qnil) {
729
+ Smoke::Index c = (Smoke::Index) NUM2INT(rb_funcall(classid, rb_intern("index"), 0));
730
+ Smoke* s = smokeList[NUM2INT(rb_funcall(classid, rb_intern("smoke"), 0))];
731
+ if (c > s->numClasses) {
732
+ return Qnil;
733
+ }
734
+ #ifdef DEBUG
735
+ if (do_debug & qtdb_calls) qWarning("findAllMethodNames called with classid = %d in module %s", c, s->moduleName());
736
+ #endif
737
+ Smoke::Index imax = s->numMethodMaps;
738
+ Smoke::Index imin = 0, icur = -1, methmin, methmax;
739
+ methmin = -1; methmax = -1; // kill warnings
740
+ int icmp = -1;
741
+
742
+ while (imax >= imin) {
743
+ icur = (imin + imax) / 2;
744
+ icmp = s->leg(s->methodMaps[icur].classId, c);
745
+ if (icmp == 0) {
746
+ Smoke::Index pos = icur;
747
+ while(icur && s->methodMaps[icur-1].classId == c)
748
+ icur --;
749
+ methmin = icur;
750
+ icur = pos;
751
+ while(icur < imax && s->methodMaps[icur+1].classId == c)
752
+ icur ++;
753
+ methmax = icur;
754
+ break;
755
+ }
756
+ if (icmp > 0)
757
+ imax = icur - 1;
758
+ else
759
+ imin = icur + 1;
760
+ }
761
+
762
+ if (icmp == 0) {
763
+ for (Smoke::Index i=methmin ; i <= methmax ; i++) {
764
+ Smoke::Index ix= s->methodMaps[i].method;
765
+ if (ix > 0) { // single match
766
+ const Smoke::Method &methodRef = s->methods[ix];
767
+ PUSH_QTRUBY_METHOD
768
+ } else { // multiple match
769
+ ix = -ix; // turn into ambiguousMethodList index
770
+ while (s->ambiguousMethodList[ix]) {
771
+ const Smoke::Method &methodRef = s->methods[s->ambiguousMethodList[ix]];
772
+ PUSH_QTRUBY_METHOD
773
+ ix++;
774
+ }
775
+ }
776
+ }
777
+ }
778
+ }
779
+ return result;
780
+ }
781
+
782
+ QByteArray *
783
+ find_cached_selector(int argc, VALUE * argv, VALUE klass, const char * methodName)
784
+ {
785
+ // Look in the cache
786
+ static QByteArray * mcid = 0;
787
+ if (mcid == 0) {
788
+ mcid = new QByteArray();
789
+ }
790
+
791
+ *mcid = rb_class2name(klass);
792
+ *mcid += ';';
793
+ *mcid += methodName;
794
+ for(int i=4; i<argc ; i++)
795
+ {
796
+ *mcid += ';';
797
+ *mcid += value_to_type_flag(argv[i]);
798
+ }
799
+ Smoke::ModuleIndex *rcid = methcache.value(*mcid);
800
+ #ifdef DEBUG
801
+ if (do_debug & qtdb_calls) qWarning("method_missing mcid: %s", (const char *) *mcid);
802
+ #endif
803
+
804
+ if (rcid) {
805
+ // Got a hit
806
+ #ifdef DEBUG
807
+ if (do_debug & qtdb_calls) qWarning("method_missing cache hit, mcid: %s", (const char *) *mcid);
808
+ #endif
809
+ _current_method.smoke = rcid->smoke;
810
+ _current_method.index = rcid->index;
811
+ } else {
812
+ _current_method.smoke = 0;
813
+ _current_method.index = -1;
814
+ }
815
+
816
+ return mcid;
817
+ }
818
+
819
+ VALUE
820
+ method_missing(int argc, VALUE * argv, VALUE self)
821
+ {
822
+ const char * methodName = rb_id2name(SYM2ID(argv[0]));
823
+ VALUE klass = rb_funcall(self, rb_intern("class"), 0);
824
+
825
+ VALUE retval = Qnil;
826
+
827
+ // Look for 'thing?' methods, and try to match isThing() or hasThing() in the Smoke runtime
828
+ static QByteArray * pred = 0;
829
+ static VALUE mainThread = Qnil;
830
+ if (pred == 0) {
831
+ pred = new QByteArray();
832
+ }
833
+ if (mainThread == Qnil) {
834
+ mainThread = rb_thread_main();
835
+ }
836
+
837
+ if (rb_thread_current() != mainThread) {
838
+ rb_raise(rb_eRuntimeError, "Qt methods cannot be called from outside of the main thread");
839
+ }
840
+
841
+ *pred = methodName;
842
+ if (pred->endsWith("?")) {
843
+ smokeruby_object *o = value_obj_info(self);
844
+ if(!o || !o->ptr) {
845
+ return rb_call_super(argc, argv);
846
+ }
847
+
848
+ // Drop the trailing '?'
849
+ pred->replace(pred->length() - 1, 1, "");
850
+
851
+ pred->replace(0, 1, pred->mid(0, 1).toUpper());
852
+ pred->replace(0, 0, "is");
853
+ Smoke::ModuleIndex meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, (const char *) *pred);
854
+
855
+ if (meth.index == 0) {
856
+ pred->replace(0, 2, "has");
857
+ meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, *pred);
858
+ }
859
+
860
+ if (meth.index > 0) {
861
+ methodName = (char *) (const char *) *pred;
862
+ }
863
+ }
864
+
865
+ VALUE * temp_stack = ALLOCA_N(VALUE, argc+3);
866
+ temp_stack[0] = rb_str_new2("Qt");
867
+ temp_stack[1] = rb_str_new2(methodName);
868
+ temp_stack[2] = klass;
869
+ temp_stack[3] = self;
870
+ for (int count = 1; count < argc; count++) {
871
+ temp_stack[count+3] = argv[count];
872
+ }
873
+
874
+ {
875
+ QByteArray * mcid = find_cached_selector(argc+3, temp_stack, klass, methodName);
876
+
877
+ if (_current_method.index == -1) {
878
+ // Find the C++ method to call. Do that from Ruby for now
879
+
880
+ retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack);
881
+ if (_current_method.index == -1) {
882
+ const char * op = rb_id2name(SYM2ID(argv[0]));
883
+ if ( qstrcmp(op, "-") == 0
884
+ || qstrcmp(op, "+") == 0
885
+ || qstrcmp(op, "/") == 0
886
+ || qstrcmp(op, "%") == 0
887
+ || qstrcmp(op, "|") == 0 )
888
+ {
889
+ // Look for operator methods of the form 'operator+=', 'operator-=' and so on..
890
+ char op1[3];
891
+ op1[0] = op[0];
892
+ op1[1] = '=';
893
+ op1[2] = '\0';
894
+ temp_stack[1] = rb_str_new2(op1);
895
+ retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack);
896
+ }
897
+
898
+ if (_current_method.index == -1) {
899
+ // Check for property getter/setter calls, and for slots in QObject classes
900
+ // not in the smoke library
901
+ smokeruby_object *o = value_obj_info(self);
902
+ if ( o != 0
903
+ && o->ptr != 0
904
+ && Smoke::isDerivedFrom(Smoke::ModuleIndex(o->smoke, o->classId), Smoke::findClass("QObject")) )
905
+ {
906
+ QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
907
+ static QByteArray * name = 0;
908
+ if (name == 0) {
909
+ name = new QByteArray();
910
+ }
911
+
912
+ *name = rb_id2name(SYM2ID(argv[0]));
913
+ const QMetaObject * meta = qobject->metaObject();
914
+
915
+ if (argc == 1) {
916
+ if (name->endsWith("?")) {
917
+ name->replace(0, 1, pred->mid(0, 1).toUpper());
918
+ name->replace(0, 0, "is");
919
+ if (meta->indexOfProperty(*name) == -1) {
920
+ name->replace(0, 2, "has");
921
+ }
922
+ }
923
+
924
+ if (meta->indexOfProperty(*name) != -1) {
925
+ VALUE qvariant = rb_funcall(self, rb_intern("property"), 1, rb_str_new2(*name));
926
+ return rb_funcall(qvariant, rb_intern("value"), 0);
927
+ }
928
+ }
929
+
930
+ if (argc == 2 && name->endsWith("=")) {
931
+ name->replace("=", "");
932
+ if (meta->indexOfProperty(*name) != -1) {
933
+ VALUE qvariant = rb_funcall(self, rb_intern("qVariantFromValue"), 1, argv[1]);
934
+ return rb_funcall(self, rb_intern("setProperty"), 2, rb_str_new2(*name), qvariant);
935
+ }
936
+ }
937
+
938
+ int classId = o->smoke->idClass(meta->className()).index;
939
+
940
+ // The class isn't in the Smoke lib. But if it is called 'local::Merged'
941
+ // it is from a QDBusInterface and the slots are remote, so don't try to
942
+ // those.
943
+ while ( classId == 0
944
+ && qstrcmp(meta->className(), "local::Merged") != 0
945
+ && qstrcmp(meta->superClass()->className(), "QDBusAbstractInterface") != 0 )
946
+ {
947
+ // Assume the QObject has slots which aren't in the Smoke library, so try
948
+ // and call the slot directly
949
+ for (int id = meta->methodOffset(); id < meta->methodCount(); id++) {
950
+ if (meta->method(id).methodType() == QMetaMethod::Slot) {
951
+ QByteArray signature(meta->method(id).signature());
952
+ QByteArray methodName = signature.mid(0, signature.indexOf('('));
953
+
954
+ // Don't check that the types of the ruby args match the c++ ones for now,
955
+ // only that the name and arg count is the same.
956
+ if (*name == methodName && meta->method(id).parameterTypes().count() == (argc - 1)) {
957
+ QList<MocArgument*> args = get_moc_arguments( o->smoke, meta->method(id).typeName(),
958
+ meta->method(id).parameterTypes() );
959
+ VALUE result = Qnil;
960
+ QtRuby::InvokeNativeSlot slot(qobject, id, argc - 1, args, argv + 1, &result);
961
+ slot.next();
962
+ return result;
963
+ }
964
+ }
965
+ }
966
+ meta = meta->superClass();
967
+ classId = o->smoke->idClass(meta->className()).index;
968
+ }
969
+ }
970
+
971
+ return rb_call_super(argc, argv);
972
+ }
973
+ }
974
+ // Success. Cache result.
975
+ methcache.insert(*mcid, new Smoke::ModuleIndex(_current_method));
976
+ }
977
+ }
978
+ QtRuby::MethodCall c(_current_method.smoke, _current_method.index, self, temp_stack+4, argc-1);
979
+ c.next();
980
+ VALUE result = *(c.var());
981
+ return result;
982
+ }
983
+
984
+ VALUE
985
+ class_method_missing(int argc, VALUE * argv, VALUE klass)
986
+ {
987
+ VALUE result = Qnil;
988
+ VALUE retval = Qnil;
989
+ const char * methodName = rb_id2name(SYM2ID(argv[0]));
990
+ VALUE * temp_stack = ALLOCA_N(VALUE, argc+3);
991
+ static VALUE mainThread = Qnil;
992
+ if (mainThread == Qnil) {
993
+ mainThread = rb_thread_main();
994
+ }
995
+ temp_stack[0] = rb_str_new2("Qt");
996
+ temp_stack[1] = rb_str_new2(methodName);
997
+ temp_stack[2] = klass;
998
+ temp_stack[3] = Qnil;
999
+
1000
+ if (rb_thread_current() != mainThread) {
1001
+ rb_raise(rb_eRuntimeError, "Qt methods cannot be called from outside of the main thread");
1002
+ }
1003
+
1004
+ for (int count = 1; count < argc; count++) {
1005
+ temp_stack[count+3] = argv[count];
1006
+ }
1007
+
1008
+ {
1009
+ QByteArray * mcid = find_cached_selector(argc+3, temp_stack, klass, methodName);
1010
+
1011
+ if (_current_method.index == -1) {
1012
+ retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack);
1013
+ if (_current_method.index != -1) {
1014
+ // Success. Cache result.
1015
+ methcache.insert(*mcid, new Smoke::ModuleIndex(_current_method));
1016
+ }
1017
+ }
1018
+ }
1019
+
1020
+ if (_current_method.index == -1) {
1021
+ static QRegExp * rx = 0;
1022
+ if (rx == 0) {
1023
+ rx = new QRegExp("[a-zA-Z]+");
1024
+ }
1025
+
1026
+ if (rx->indexIn(methodName) == -1) {
1027
+ // If an operator method hasn't been found as an instance method,
1028
+ // then look for a class method - after 'op(self,a)' try 'self.op(a)'
1029
+ VALUE * method_stack = ALLOCA_N(VALUE, argc - 1);
1030
+ method_stack[0] = argv[0];
1031
+ for (int count = 1; count < argc - 1; count++) {
1032
+ method_stack[count] = argv[count+1];
1033
+ }
1034
+ result = method_missing(argc-1, method_stack, argv[1]);
1035
+ return result;
1036
+ } else {
1037
+ return rb_call_super(argc, argv);
1038
+ }
1039
+ }
1040
+ QtRuby::MethodCall c(_current_method.smoke, _current_method.index, Qnil, temp_stack+4, argc-1);
1041
+ c.next();
1042
+ result = *(c.var());
1043
+ return result;
1044
+ }
1045
+
1046
+ QList<MocArgument*>
1047
+ get_moc_arguments(Smoke* smoke, const char * typeName, QList<QByteArray> methodTypes)
1048
+ {
1049
+ static QRegExp * rx = 0;
1050
+ if (rx == 0) {
1051
+ rx = new QRegExp("^(bool|int|uint|long|ulong|double|char\\*|QString)&?$");
1052
+ }
1053
+ methodTypes.prepend(QByteArray(typeName));
1054
+ QList<MocArgument*> result;
1055
+
1056
+ foreach (QByteArray name, methodTypes) {
1057
+ MocArgument *arg = new MocArgument;
1058
+ Smoke::Index typeId = 0;
1059
+
1060
+ if (name.isEmpty()) {
1061
+ arg->argType = xmoc_void;
1062
+ result.append(arg);
1063
+ } else {
1064
+ name.replace("const ", "");
1065
+ QString staticType = (rx->indexIn(name) != -1 ? rx->cap(1) : "ptr");
1066
+ if (staticType == "ptr") {
1067
+ arg->argType = xmoc_ptr;
1068
+ QByteArray targetType = name;
1069
+ typeId = smoke->idType(targetType.constData());
1070
+ if (typeId == 0 && !name.contains('*')) {
1071
+ if (!name.contains("&")) {
1072
+ targetType += "&";
1073
+ }
1074
+ typeId = smoke->idType(targetType.constData());
1075
+ }
1076
+
1077
+ // This shouldn't be necessary because the type of the slot arg should always be in the
1078
+ // smoke module of the slot being invoked. However, that isn't true for a dataUpdated()
1079
+ // slot in a PlasmaScripting::Applet
1080
+ if (typeId == 0) {
1081
+ QHash<Smoke*, QtRubyModule>::const_iterator it;
1082
+ for (it = qtruby_modules.constBegin(); it != qtruby_modules.constEnd(); ++it) {
1083
+ smoke = it.key();
1084
+ targetType = name;
1085
+ typeId = smoke->idType(targetType.constData());
1086
+ if (typeId != 0) {
1087
+ break;
1088
+ }
1089
+
1090
+ if (typeId == 0 && !name.contains('*')) {
1091
+ if (!name.contains("&")) {
1092
+ targetType += "&";
1093
+ }
1094
+
1095
+ typeId = smoke->idType(targetType.constData());
1096
+
1097
+ if (typeId != 0) {
1098
+ break;
1099
+ }
1100
+ }
1101
+ }
1102
+ }
1103
+ } else if (staticType == "bool") {
1104
+ arg->argType = xmoc_bool;
1105
+ smoke = qtcore_Smoke;
1106
+ typeId = smoke->idType(name.constData());
1107
+ } else if (staticType == "int") {
1108
+ arg->argType = xmoc_int;
1109
+ smoke = qtcore_Smoke;
1110
+ typeId = smoke->idType(name.constData());
1111
+ } else if (staticType == "uint") {
1112
+ arg->argType = xmoc_uint;
1113
+ smoke = qtcore_Smoke;
1114
+ typeId = smoke->idType("unsigned int");
1115
+ } else if (staticType == "long") {
1116
+ arg->argType = xmoc_long;
1117
+ smoke = qtcore_Smoke;
1118
+ typeId = smoke->idType(name.constData());
1119
+ } else if (staticType == "ulong") {
1120
+ arg->argType = xmoc_ulong;
1121
+ smoke = qtcore_Smoke;
1122
+ typeId = smoke->idType("unsigned long");
1123
+ } else if (staticType == "double") {
1124
+ arg->argType = xmoc_double;
1125
+ smoke = qtcore_Smoke;
1126
+ typeId = smoke->idType(name.constData());
1127
+ } else if (staticType == "char*") {
1128
+ arg->argType = xmoc_charstar;
1129
+ smoke = qtcore_Smoke;
1130
+ typeId = smoke->idType(name.constData());
1131
+ } else if (staticType == "QString") {
1132
+ arg->argType = xmoc_QString;
1133
+ name += "*";
1134
+ smoke = qtcore_Smoke;
1135
+ typeId = smoke->idType(name.constData());
1136
+ }
1137
+
1138
+ if (typeId == 0) {
1139
+ rb_raise(rb_eArgError, "Cannot handle '%s' as slot argument\n", name.constData());
1140
+ return result;
1141
+ }
1142
+
1143
+ arg->st.set(smoke, typeId);
1144
+ result.append(arg);
1145
+ }
1146
+ }
1147
+
1148
+ return result;
1149
+ }
1150
+
1151
+ extern "C"
1152
+ {
1153
+ // ---------------- Helpers -------------------
1154
+
1155
+ //---------- All functions except fully qualified statics & enums ---------
1156
+
1157
+ VALUE
1158
+ mapObject(VALUE self, VALUE obj)
1159
+ {
1160
+ smokeruby_object *o = value_obj_info(obj);
1161
+ if(!o)
1162
+ return Qnil;
1163
+ mapPointer(obj, o, o->classId, 0);
1164
+ return self;
1165
+ }
1166
+
1167
+ VALUE set_obj_info(const char * className, smokeruby_object * o);
1168
+
1169
+ VALUE
1170
+ qobject_metaobject(VALUE self)
1171
+ {
1172
+ smokeruby_object * o = value_obj_info(self);
1173
+ QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
1174
+ QMetaObject * meta = (QMetaObject *) qobject->metaObject();
1175
+ VALUE obj = getPointerObject(meta);
1176
+ if (obj != Qnil) {
1177
+ return obj;
1178
+ }
1179
+
1180
+ smokeruby_object * m = alloc_smokeruby_object( false,
1181
+ o->smoke,
1182
+ o->smoke->idClass("QMetaObject").index,
1183
+ meta );
1184
+
1185
+ obj = set_obj_info("Qt::MetaObject", m);
1186
+ return obj;
1187
+ }
1188
+
1189
+ VALUE
1190
+ set_obj_info(const char * className, smokeruby_object * o)
1191
+ {
1192
+ VALUE klass = rb_funcall(qt_internal_module,
1193
+ rb_intern("find_class"),
1194
+ 1,
1195
+ rb_str_new2(className) );
1196
+ if (klass == Qnil) {
1197
+ rb_raise(rb_eRuntimeError, "Class '%s' not found", className);
1198
+ }
1199
+
1200
+ Smoke::ModuleIndex *r = classcache.value(className);
1201
+ if (r != 0) {
1202
+ o->classId = (int) r->index;
1203
+ }
1204
+ // If the instance is a subclass of QObject, then check to see if the
1205
+ // className from its QMetaObject is in the Smoke library. If not then
1206
+ // create a Ruby class for it dynamically. Remove the first letter from
1207
+ // any class names beginning with 'Q' or 'K' and put them under the Qt::
1208
+ // or KDE:: modules respectively.
1209
+ if (o->smoke->isDerivedFrom(o->smoke, o->classId, o->smoke->idClass("QObject").smoke, o->smoke->idClass("QObject").index)) {
1210
+ QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject").index);
1211
+ const QMetaObject * meta = qobject->metaObject();
1212
+ int classId = o->smoke->idClass(meta->className()).index;
1213
+ // The class isn't in the Smoke lib..
1214
+ if (classId == 0) {
1215
+ VALUE new_klass = Qnil;
1216
+ QByteArray className(meta->className());
1217
+
1218
+ if (className == "QTableModel") {
1219
+ new_klass = qtablemodel_class;
1220
+ } else if (className == "QListModel") {
1221
+ new_klass = qlistmodel_class;
1222
+ } else if (className.startsWith("Q")) {
1223
+ className.replace("Q", "");
1224
+ className = className.mid(0, 1).toUpper() + className.mid(1);
1225
+ new_klass = rb_define_class_under(qt_module, className, klass);
1226
+ } else {
1227
+ new_klass = rb_define_class(className, klass);
1228
+ }
1229
+
1230
+ if (new_klass != Qnil) {
1231
+ klass = new_klass;
1232
+
1233
+ for (int id = meta->enumeratorOffset(); id < meta->enumeratorCount(); id++) {
1234
+ // If there are any enum keys with the same scope as the new class then
1235
+ // add them
1236
+ if (qstrcmp(meta->className(), meta->enumerator(id).scope()) == 0) {
1237
+ for (int i = 0; i < meta->enumerator(id).keyCount(); i++) {
1238
+ rb_define_const( klass,
1239
+ meta->enumerator(id).key(i),
1240
+ INT2NUM(meta->enumerator(id).value(i)) );
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+
1246
+ // Add a Qt::Object.metaObject method which will do dynamic despatch on the
1247
+ // metaObject() virtual method so that the true QMetaObject of the class
1248
+ // is returned, rather than for the one for the parent class that is in
1249
+ // the Smoke library.
1250
+ rb_define_method(klass, "metaObject", (VALUE (*) (...)) qobject_metaobject, 0);
1251
+ }
1252
+ }
1253
+
1254
+ VALUE obj = Data_Wrap_Struct(klass, smokeruby_mark, smokeruby_free, (void *) o);
1255
+ return obj;
1256
+ }
1257
+
1258
+ VALUE
1259
+ kross2smoke(VALUE /*self*/, VALUE krobject, VALUE new_klass)
1260
+ {
1261
+ VALUE new_klassname = rb_funcall(new_klass, rb_intern("name"), 0);
1262
+
1263
+ Smoke::ModuleIndex * cast_to_id = classcache.value(StringValuePtr(new_klassname));
1264
+ if (cast_to_id == 0) {
1265
+ rb_raise(rb_eArgError, "unable to find class \"%s\" to cast to\n", StringValuePtr(new_klassname));
1266
+ }
1267
+
1268
+ void* o;
1269
+ Data_Get_Struct(krobject, void, o);
1270
+
1271
+ smokeruby_object * o_cast = alloc_smokeruby_object(false, cast_to_id->smoke, (int) cast_to_id->index, o);
1272
+
1273
+ VALUE obj = Data_Wrap_Struct(new_klass, smokeruby_mark, smokeruby_free, (void *) o_cast);
1274
+ mapPointer(obj, o_cast, o_cast->classId, 0);
1275
+ return obj;
1276
+ }
1277
+
1278
+ const char *
1279
+ value_to_type_flag(VALUE ruby_value)
1280
+ {
1281
+ const char * classname = rb_obj_classname(ruby_value);
1282
+ const char *r = "";
1283
+ if (ruby_value == Qnil)
1284
+ r = "u";
1285
+ else if (TYPE(ruby_value) == T_FIXNUM || TYPE(ruby_value) == T_BIGNUM || qstrcmp(classname, "Qt::Integer") == 0)
1286
+ r = "i";
1287
+ else if (TYPE(ruby_value) == T_FLOAT)
1288
+ r = "n";
1289
+ else if (TYPE(ruby_value) == T_STRING)
1290
+ r = "s";
1291
+ else if(ruby_value == Qtrue || ruby_value == Qfalse || qstrcmp(classname, "Qt::Boolean") == 0)
1292
+ r = "B";
1293
+ else if (qstrcmp(classname, "Qt::Enum") == 0) {
1294
+ VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qenum_type"), 1, ruby_value);
1295
+ r = StringValuePtr(temp);
1296
+ } else if (TYPE(ruby_value) == T_DATA) {
1297
+ smokeruby_object *o = value_obj_info(ruby_value);
1298
+ if (o == 0 || o->smoke == 0) {
1299
+ r = "a";
1300
+ } else {
1301
+ r = o->smoke->classes[o->classId].className;
1302
+ }
1303
+ } else {
1304
+ r = "U";
1305
+ }
1306
+
1307
+ return r;
1308
+ }
1309
+
1310
+ VALUE prettyPrintMethod(Smoke::Index id)
1311
+ {
1312
+ VALUE r = rb_str_new2("");
1313
+ const Smoke::Method &meth = qtcore_Smoke->methods[id];
1314
+ const char *tname = qtcore_Smoke->types[meth.ret].name;
1315
+ if(meth.flags & Smoke::mf_static) rb_str_catf(r, "static ");
1316
+ rb_str_catf(r, "%s ", (tname ? tname:"void"));
1317
+ rb_str_catf(r, "%s::%s(", qtcore_Smoke->classes[meth.classId].className, qtcore_Smoke->methodNames[meth.name]);
1318
+ for(int i = 0; i < meth.numArgs; i++) {
1319
+ if(i) rb_str_catf(r, ", ");
1320
+ tname = qtcore_Smoke->types[qtcore_Smoke->argumentList[meth.args+i]].name;
1321
+ rb_str_catf(r, "%s", (tname ? tname:"void"));
1322
+ }
1323
+ rb_str_catf(r, ")");
1324
+ if(meth.flags & Smoke::mf_const) rb_str_catf(r, " const");
1325
+ return r;
1326
+ }
1327
+ }