OSDN Git Service

f8bcda0171337a5ff9c2996003704720730389e4
[molby/Molby.git] / MolLib / Ruby_bind / ruby_dialog.c
1 /*
2  *  ruby_dialog.c
3  *
4  *  Created by Toshi Nagata on 08/04/13.
5  *  Copyright 2008 Toshi Nagata. All rights reserved.
6
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation version 2 of the License.
10  
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16 */
17
18 #include <ruby.h>
19 #include <math.h>   /*  For floor()  */
20 #include "ruby_dialog.h"
21
22 /*  Encoded version of rb_str_new2()  (defined in ruby_bind.c)  */
23 extern VALUE Ruby_NewEncodedStringValue2(const char *str);
24
25 static VALUE
26         sTextSymbol, sTextFieldSymbol, sRadioSymbol, sButtonSymbol,
27         sCheckBoxSymbol, sToggleButtonSymbol, sPopUpSymbol, sTextViewSymbol,
28     sViewSymbol, sLineSymbol, sTagSymbol, sTypeSymbol, sTitleSymbol, 
29         sRadioGroupSymbol, sTableSymbol,
30         sResizableSymbol, sHasCloseBoxSymbol,
31         sDialogSymbol, sIndexSymbol,
32         sXSymbol, sYSymbol, sWidthSymbol, sHeightSymbol, 
33         sOriginSymbol, sSizeSymbol, sFrameSymbol,
34         sEnabledSymbol, sEditableSymbol, sHiddenSymbol, sValueSymbol,
35         sBlockSymbol, sRangeSymbol, sActionSymbol,
36         sAlignSymbol, sRightSymbol, sCenterSymbol,
37         sVerticalAlignSymbol, sBottomSymbol, 
38         sMarginSymbol, sPaddingSymbol, sSubItemsSymbol,
39         sHFillSymbol, sVFillSymbol, sFlexSymbol,
40         sIsProcessingActionSymbol,
41         sColorSymbol, sForeColorSymbol, sBackColorSymbol,
42         sStyleSymbol, sSolidSymbol, sTransparentSymbol, sDotSymbol,
43         sLongDashSymbol, sShortDashSymbol, sDotDashSymbol,
44         sFontSymbol, sFamilySymbol, sWeightSymbol,
45         sDefaultSymbol, sRomanSymbol, sSwissSymbol, sFixedSymbol,
46         sNormalSymbol, sSlantSymbol, sItalicSymbol,
47         sMediumSymbol, sBoldSymbol, sLightSymbol,
48         sOnPaintSymbol,
49         /*  Data source for Table (= MyListCtrl)  */
50         sOnCountSymbol, sOnGetValueSymbol, sOnSetValueSymbol, sOnSelectionChangedSymbol,
51         sOnSetColorSymbol, sIsItemEditableSymbol, sIsDragAndDropEnabledSymbol, sOnDragSelectionToRowSymbol,
52         sSelectionSymbol, sColumnsSymbol, sRefreshSymbol, sHasPopUpMenuSymbol, sOnPopUpMenuSelectedSymbol;
53
54 VALUE rb_cDialog = Qfalse;
55 VALUE rb_cDialogItem = Qfalse;
56 VALUE rb_mDeviceContext = Qfalse;
57 VALUE rb_cBitmap = Qfalse;
58 VALUE rb_eDialogError = Qfalse;
59 VALUE gRubyDialogList = Qnil;
60
61 const RDPoint gZeroPoint = {0, 0};
62 const RDSize gZeroSize = {0, 0};
63 const RDRect gZeroRect = {{0, 0}, {0, 0}};
64
65 /*  True if y-coordinate grows from bottom to top (like Cocoa)  */
66 int gRubyDialogIsFlipped = 0;
67
68 #pragma mark ====== External utility functions and macros ======
69
70 #define FileStringValuePtr(val) Ruby_FileStringValuePtr(&val)
71 extern char *Ruby_FileStringValuePtr(VALUE *valp);
72 extern VALUE Ruby_NewFileStringValue(const char *fstr);
73
74 #define EncodedStringValuePtr(val) Ruby_EncodedStringValuePtr(&val)
75 extern char *Ruby_EncodedStringValuePtr(VALUE *valp);
76 extern VALUE Ruby_NewEncodedStringValue(const char *str, int len);
77
78 extern VALUE Ruby_SetInterruptFlag(VALUE val);
79 extern VALUE Ruby_ObjectAtIndex(VALUE ary, int idx);
80 extern VALUE ValueFromIntGroup(IntGroup *ig);
81 extern IntGroup *IntGroupFromValue(VALUE val);
82
83 #pragma mark ====== Dialog alloc/init/release ======
84
85 typedef struct RubyDialogInfo {
86         RubyDialog *dref;  /*  Reference to a platform-dependent "controller" object  */
87 } RubyDialogInfo;
88
89 static RubyDialog *
90 s_RubyDialog_GetController(VALUE self)
91 {
92         RubyDialogInfo *di;
93         Data_Get_Struct(self, RubyDialogInfo, di);
94         if (di != NULL)
95                 return di->dref;
96         else return NULL;
97 }
98
99 /*  Deallocate handler: may be called when the Ruby object is deallocated while the RubyDialogFrame is not
100     deallocated yet.  */
101 static void
102 s_RubyDialog_Release(void *p)
103 {
104         if (p != NULL) {
105                 RubyDialog *dref = ((RubyDialogInfo *)p)->dref;
106                 if (dref != NULL) {
107                         RubyDialogCallback_setRubyObject(dref, Qfalse); /* Stop access to the Ruby object (in case the RubyDialogController is not dealloc'ed in the following line) */
108                         RubyDialogCallback_release(dref);
109                         ((RubyDialogInfo *)p)->dref = NULL;
110                 }
111                 free(p);
112         }
113 }
114
115 /*  Deallocate the RubyDialogFrame while the Ruby object is still alive  */
116 static void
117 s_RubyDialog_Forget(VALUE self)
118 {
119         RubyDialogInfo *di;
120         Data_Get_Struct(self, RubyDialogInfo, di);
121         if (di != NULL) {
122                 if (di->dref != NULL) {
123                         /*  Unregister all messages  */
124                 /*      RubyDialogCallback_Listen(di->dref, NULL, NULL, NULL, NULL, NULL); */
125                 }
126                 di->dref = NULL;
127         }
128 }
129
130 static VALUE
131 s_RubyDialog_Alloc(VALUE klass)
132 {
133         VALUE val;
134         RubyDialogInfo *di;
135 //      RubyDialog *dref = RubyDialogCallback_new();
136         val = Data_Make_Struct(klass, RubyDialogInfo, 0, s_RubyDialog_Release, di);
137         di->dref = NULL;
138 //      RubyDialogCallback_setRubyObject(dref, (RubyValue)val);
139         return val;
140 }
141
142 #pragma mark ====== Set/get item attributes ======
143
144 /*
145  *  call-seq:
146  *     set_attr(key, value) -> value
147  *     self[key] = value
148  *
149  *  Set the attributes.
150  */
151 static VALUE
152 s_RubyDialogItem_SetAttr(VALUE self, VALUE key, VALUE val)
153 {
154         int flag, itag, i;
155         VALUE dialog_val, type;
156         RubyDialog *dref;
157         RDItem *view;
158         ID key_id;
159         
160         dialog_val = rb_ivar_get(self, SYM2ID(sDialogSymbol));
161         itag = NUM2INT(rb_ivar_get(self, SYM2ID(sIndexSymbol)));
162         type = rb_ivar_get(self, SYM2ID(sTypeSymbol));
163         if (dialog_val == Qnil || (dref = s_RubyDialog_GetController(dialog_val)) == NULL)
164                 rb_raise(rb_eDialogError, "The dialog item does not belong to any dialog (internal error?)");
165         view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
166         key_id = SYM2ID(key);
167         if (key == sRangeSymbol) {
168                 /*  Range of value (must be an array of two integers or two floats)  */
169                 VALUE val1, val2;
170                 double d1, d2;
171                 if (TYPE(val) != T_ARRAY || RARRAY_LEN(val) != 2)
172                         rb_raise(rb_eTypeError, "the attribute 'range' should specify an array of two numbers");
173                 val1 = (RARRAY_PTR(val))[0];
174                 val2 = (RARRAY_PTR(val))[1];
175                 d1 = NUM2DBL(rb_Float(val1));
176                 d2 = NUM2DBL(rb_Float(val2));
177                 if (!FIXNUM_P(val1) || !FIXNUM_P(val2)) {
178                         /*  Convert to a range of floats  */
179                         if (TYPE(val1) != T_FLOAT || TYPE(val2) != T_FLOAT) {
180                                 val1 = rb_float_new(NUM2DBL(val1));
181                                 val2 = rb_float_new(NUM2DBL(val2));
182                                 val = rb_ary_new3(2, val1, val2);
183                         }
184                 }
185                 if (d1 > d2)
186                         rb_raise(rb_eArgError, "invalid number range [%g,%g]", d1, d2);
187                 rb_ivar_set(self, key_id, val);
188         } else if (key == sValueSymbol) {
189                 /*  Value  */
190                 if (type == sTextFieldSymbol || type == sTextViewSymbol) {
191                         RubyDialogCallback_setStringToItem(view, (val == Qnil ? "" : EncodedStringValuePtr(val)));
192                 } else if (type == sPopUpSymbol) {
193                         RubyDialogCallback_setSelectedSubItem(view, (val == Qnil ? 0 : NUM2INT(rb_Integer(val))));
194                 } else if (type == sCheckBoxSymbol || type == sRadioSymbol || type == sToggleButtonSymbol) {
195                         RubyDialogCallback_setStateForItem(view, (val == Qnil ? 0 : NUM2INT(rb_Integer(val))));
196                 }
197         } else if (key == sTitleSymbol) {
198                 /*  Title  */
199                 char *p = EncodedStringValuePtr(val);
200                 if (type == sTextSymbol)
201                         RubyDialogCallback_setStringToItem(view, p);
202                 else RubyDialogCallback_setTitleToItem(view, p);
203         } else if (key == sEnabledSymbol) {
204                 /*  Enabled  */
205                 flag = (val != Qnil && val != Qfalse);
206                 RubyDialogCallback_setEnabledForItem(view, flag);
207         } else if (key == sEditableSymbol) {
208                 /*  Editable  */
209                 flag = (val != Qnil && val != Qfalse);
210                 RubyDialogCallback_setEditableForItem(view, flag);
211         } else if (key == sHiddenSymbol) {
212                 /*  Hidden  */
213                 flag = (val != Qnil && val != Qfalse);
214                 RubyDialogCallback_setHiddenForItem(view, flag);
215         } else if (key == sSubItemsSymbol) {
216                 /*  SubItems  */
217                 if (type == sPopUpSymbol) {
218                         int j, len2;
219                         VALUE *ptr2;
220                         val = rb_ary_to_ary(val);
221                         len2 = RARRAY_LEN(val);
222                         ptr2 = RARRAY_PTR(val);
223                         while (RubyDialogCallback_deleteSubItem(view, 0) >= 0);
224                         for (j = 0; j < len2; j++) {
225                                 VALUE val2 = ptr2[j];
226                                 RubyDialogCallback_appendSubItem(view, EncodedStringValuePtr(val2));
227                         }
228                         RubyDialogCallback_resizeToBest(view);
229                         RubyDialogCallback_setSelectedSubItem(view, 0);
230                 }                       
231         } else if (key == sXSymbol || key == sYSymbol || key == sWidthSymbol || key == sHeightSymbol) {
232                 /*  Frame components  */
233                 RDRect frame;
234                 float f = NUM2DBL(rb_Float(val));
235                 frame = RubyDialogCallback_frameOfItem(view);
236                 if (key == sXSymbol)
237                         frame.origin.x = f;
238                 else if (key == sYSymbol)
239                         frame.origin.y = f;
240                 else if (key == sWidthSymbol)
241                         frame.size.width = f;
242                 else
243                         frame.size.height = f;
244                 RubyDialogCallback_setFrameOfItem(view, frame);
245         } else if (key == sOriginSymbol || key == sSizeSymbol) {
246                 /*  Frame components  */
247                 RDRect frame;
248                 float f0 = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 0)));
249                 float f1 = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 1)));
250                 frame = RubyDialogCallback_frameOfItem(view);
251                 if (key == sOriginSymbol) {
252                         frame.origin.x = f0;
253                         frame.origin.y = f1;
254                 } else {
255                         frame.size.width = f0;
256                         frame.size.height = f1;
257                 }
258                 RubyDialogCallback_setFrameOfItem(view, frame);
259         } else if (key == sFrameSymbol) {
260                 /*  Frame (x, y, width, height)  */
261                 RDRect frame;
262                 frame.origin.x = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 0)));
263                 frame.origin.y = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 1)));
264                 frame.size.width = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 2)));
265                 frame.size.height = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 3)));
266                 RubyDialogCallback_setFrameOfItem(view, frame);
267         } else if (key == sFlexSymbol) {
268                 /*  Flex flags: [left, top, right, bottom, width, height] (0: fixed, 1: flex)  */
269                 int flex = 0;
270                 if (val == Qnil) {
271                         rb_ivar_set(self, key_id, val);
272                 } else {
273                         if (rb_obj_is_kind_of(val, rb_mEnumerable)) {
274                                 for (i = 0; i < 6; i++) {
275                                         VALUE gval = Ruby_ObjectAtIndex(val, i);
276                                         if (RTEST(gval) && NUM2INT(rb_Integer(gval)) != 0)
277                                                 flex |= (1 << i);
278                                 }
279                         } else if (rb_obj_is_kind_of(val, rb_cNumeric)) {
280                                 flex = NUM2INT(rb_Integer(val));
281                         } else {
282                                 rb_raise(rb_eDialogError, "the 'flex' attribute should be either an integer or an array of 4 boolean/integers");
283                         }
284                         rb_ivar_set(self, key_id, INT2NUM(flex));
285                 }
286         } else if (key == sForeColorSymbol || key == sBackColorSymbol) {
287                 double col[4];
288                 val = rb_ary_to_ary(val);
289                 col[0] = col[1] = col[2] = col[3] = 1.0;
290                 for (i = 0; i < 4 && i < RARRAY_LEN(val); i++)
291                         col[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i]));
292                 if (key == sForeColorSymbol)
293                         RubyDialogCallback_setForegroundColorForItem(view, col);
294                 else
295                         RubyDialogCallback_setBackgroundColorForItem(view, col);
296         } else if (key == sFontSymbol) {
297                 int size, family, style, weight;
298                 size = family = style = weight = 0;
299                 val = rb_ary_to_ary(val);
300                 for (i = 0; i < RARRAY_LEN(val); i++) {
301                         VALUE vali = RARRAY_PTR(val)[i];
302                         if (rb_obj_is_kind_of(vali, rb_cNumeric)) {
303                                 size = NUM2INT(rb_Integer(vali));
304                         } else if (vali == sDefaultSymbol) {
305                                 family = 1;
306                         } else if (vali == sRomanSymbol) {
307                                 family = 2;
308                         } else if (vali == sSwissSymbol) {
309                                 family = 3;
310                         } else if (vali == sFixedSymbol) {
311                                 family = 4;
312                         } else if (vali == sNormalSymbol) {
313                                 style = 1;
314                         } else if (vali == sSlantSymbol) {
315                                 style = 2;
316                         } else if (vali == sItalicSymbol) {
317                                 style = 3;
318                         } else if (vali == sMediumSymbol) {
319                                 weight = 1;
320                         } else if (vali == sBoldSymbol) {
321                                 weight = 2;
322                         } else if (vali == sLightSymbol) {
323                                 weight = 3;
324                         } else if (vali != Qnil) {
325                                 vali = rb_inspect(vali);
326                                 rb_raise(rb_eDialogError, "unknown font specification (%s)", EncodedStringValuePtr(vali));
327                         }
328                 }
329                 RubyDialogCallback_setFontForItem(view, size, family, style, weight);
330         } else if (key == sSelectionSymbol) {
331                 /*  Selection (for Table == MyListCtrl item)  */
332                 if (type == sTableSymbol) {
333                         IntGroup *ig = IntGroupFromValue(val);
334                         RubyDialogCallback_setSelectedTableRows((RDItem *)view, ig, 0);
335                         IntGroupRelease(ig);
336                 /*      int row, count;
337                         count = RubyDialog_GetTableItemCount((RubyValue)dialog_val, (RDItem *)view);
338                         for (row = 0; row < count; row++) {
339                                 int flag = (IntGroupLookup(ig, row, NULL) != 0);
340                                 RubyDialogCallback_setTableRowSelected((RDItem *)view, row, flag);
341                         } */
342                 }
343         } else if (key == sColumnsSymbol) {
344                 /*  Columns (for Table == MyListCtrl item)  */
345                 if (type == sTableSymbol) {
346                         /*  The value should be an array of [name, width, align (0: natural, 1: right, 2: center)] */
347                         int col;
348                         VALUE cval;
349                         val = rb_ary_to_ary(val);
350                         for (col = RubyDialogCallback_countTableColumn((RDItem *)view) - 1; col >= 0; col--) {
351                                 RubyDialogCallback_deleteTableColumn((RDItem *)view, col);
352                         }
353                         for (col = 0; col < RARRAY_LEN(val); col++) {
354                                 const char *heading;
355                                 int format, width, len;
356                                 cval = rb_ary_to_ary(RARRAY_PTR(val)[col]);
357                                 len = RARRAY_LEN(cval);
358                                 if (len >= 1) {
359                                         heading = EncodedStringValuePtr(RARRAY_PTR(cval)[0]);
360                                 } else heading = "";
361                                 if (len >= 2) {
362                                         width = NUM2INT(rb_Integer(RARRAY_PTR(cval)[1]));
363                                 } else width = -1;
364                                 if (len >= 3) {
365                                         format = NUM2INT(rb_Integer(RARRAY_PTR(cval)[2]));
366                                 } else format = 0;
367                                 RubyDialogCallback_insertTableColumn((RDItem *)view, col, heading, format, width);
368                         }
369                 }
370         } else if (key == sRefreshSymbol) {
371                 /*  Refresh (for Table == MyListCtrl item)  */
372                 if (type == sTableSymbol) {
373                         if (RTEST(val)) {
374                                 RubyDialogCallback_refreshTable((RDItem *)view);
375                         }
376                 }                       
377         } else {
378                 if (key == sTagSymbol && rb_obj_is_kind_of(val, rb_cInteger))
379                         rb_raise(rb_eDialogError, "the dialog item tag must not be integers");                          
380                 rb_ivar_set(self, key_id, val);
381         }
382         RubyDialogCallback_setNeedsDisplay(view, 1);
383         return val;
384 }
385
386 /*
387  *  call-seq:
388  *     attr(key) -> value
389  *
390  *  Get the attribute for the key.
391  */
392 static VALUE
393 s_RubyDialogItem_Attr(VALUE self, VALUE key)
394 {
395         int flag, itag;
396         VALUE dialog_val, index_val, type, val;
397         RubyDialog *dref;
398         RDItem *view;
399         ID key_id;
400         char *cp;
401         
402         dialog_val = rb_ivar_get(self, SYM2ID(sDialogSymbol));
403         if (key == sDialogSymbol)
404                 return dialog_val;
405         index_val = rb_ivar_get(self, SYM2ID(sIndexSymbol));
406         if (key == sIndexSymbol)
407                 return index_val;
408         itag = NUM2INT(index_val);
409         type = rb_ivar_get(self, SYM2ID(sTypeSymbol));
410         if (key == sTypeSymbol)
411                 return type;
412         if (dialog_val == Qnil || (dref = s_RubyDialog_GetController(dialog_val)) == NULL)
413                 rb_raise(rb_eDialogError, "The dialog item does not belong to any dialog (internal error?)");
414         view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
415         key_id = SYM2ID(key);
416
417         val = Qnil;
418         
419         if (key == sValueSymbol) {
420                 /*  Value  */
421                 if (type == sTextFieldSymbol) {
422                         /*  Is range specified?  */
423                         VALUE range = rb_ivar_get(self, SYM2ID(sRangeSymbol));
424                         cp = RubyDialogCallback_getStringPtrFromItem(view);
425                         if (cp != NULL) {
426                                 if (TYPE(range) == T_ARRAY) {
427                                         if (FIXNUM_P((RARRAY_PTR(range))[0]))
428                                                 val = INT2NUM(atoi(cp));
429                                         else
430                                                 val = rb_float_new(atof(cp));
431                                 } else val = Ruby_NewEncodedStringValue2(cp);
432                                 free(cp);
433                         }
434                 } else if (type == sTextViewSymbol) {
435                         cp = RubyDialogCallback_getStringPtrFromItem(view);
436                         if (cp != NULL) {
437                                 val = Ruby_NewEncodedStringValue2(cp);
438                                 free(cp);
439                         }
440                 } else if (type == sPopUpSymbol) {
441                         int n = RubyDialogCallback_selectedSubItem(view);
442                         if (n >= 0)
443                                 val = INT2NUM(n);
444                 } else if (type == sCheckBoxSymbol || type == sRadioSymbol || type == sToggleButtonSymbol) {
445                         val = INT2NUM(RubyDialogCallback_getStateForItem(view));
446                 }
447         } else if (key == sTitleSymbol) {
448                 cp = RubyDialogCallback_titleOfItem(view);
449                 if (cp != NULL) {
450                         val = Ruby_NewEncodedStringValue2(cp);
451                         free(cp);
452                 }
453         } else if (key == sEnabledSymbol) {
454                 /*  Enabled  */
455                 flag = RubyDialogCallback_isItemEnabled(view);
456                 val = (flag ? Qtrue : Qfalse);
457         } else if (key == sEditableSymbol) {
458                 /*  Editable  */
459                 flag = RubyDialogCallback_isItemEditable(view);
460                 val = (flag ? Qtrue : Qfalse);
461         } else if (key == sHiddenSymbol) {
462                 /*  Hidden  */
463                 flag = RubyDialogCallback_isItemHidden(view);
464                 val = (flag ? Qtrue : Qfalse);
465         } else if (key == sSubItemsSymbol) {
466                 int i;
467                 val = rb_ary_new();
468                 for (i = 0; (cp = RubyDialogCallback_titleOfSubItem(view, i)) != NULL; i++) {
469                         rb_ary_push(val, Ruby_NewEncodedStringValue2(cp));
470                         free(cp);
471                 }
472         } else if (key == sXSymbol || key == sYSymbol || key == sWidthSymbol || key == sHeightSymbol) {
473                 /*  Frame components  */
474                 RDRect frame;
475                 float f;
476                 frame = RubyDialogCallback_frameOfItem(view);
477                 if (key == sXSymbol)
478                         f = frame.origin.x;
479                 else if (key == sYSymbol)
480                         f = frame.origin.y;
481                 else if (key == sWidthSymbol)
482                         f = frame.size.width;
483                 else
484                         f = frame.size.height;
485                 val = rb_float_new(f);
486         } else if (key == sOriginSymbol || key == sSizeSymbol) {
487                 /*  Frame components  */
488                 RDRect frame;
489                 float f0, f1;
490                 frame = RubyDialogCallback_frameOfItem(view);
491                 if (key == sOriginSymbol) {
492                         f0 = frame.origin.x;
493                         f1 = frame.origin.y;
494                 } else {
495                         f0 = frame.size.width;
496                         f1 = frame.size.height;
497                 }
498                 val = rb_ary_new3(2, rb_float_new(f0), rb_float_new(f1));
499                 rb_obj_freeze(val);
500         } else if (key == sFrameSymbol) {
501                 /*  Frame (x, y, width, height)  */
502                 RDRect frame = RubyDialogCallback_frameOfItem(view);
503                 val = rb_ary_new3(4, rb_float_new(frame.origin.x), rb_float_new(frame.origin.y), rb_float_new(frame.size.width), rb_float_new(frame.size.height));
504                 rb_obj_freeze(val);
505         } else if (key == sFlexSymbol) {
506                 int i, flex;
507                 val = rb_ivar_get(self, key_id);
508                 if (val != Qnil) {
509                         flex = NUM2INT(rb_Integer(val));
510                         val = rb_ary_new();
511                         for (i = 0; i < 6; i++) {
512                                 rb_ary_push(val, ((flex & (1 << i)) ? INT2FIX(1) : INT2FIX(0)));
513                         }
514                 }
515         } else if (key == sForeColorSymbol || key == sBackColorSymbol) {
516                 double col[4];
517                 if (key == sForeColorSymbol)
518                         RubyDialogCallback_getForegroundColorForItem(view, col);
519                 else
520                         RubyDialogCallback_getBackgroundColorForItem(view, col);
521                 val = rb_ary_new3(4, rb_float_new(col[0]), rb_float_new(col[1]), rb_float_new(col[2]), rb_float_new(col[3]));
522         } else if (key == sFontSymbol) {
523                 int size, family, style, weight;
524                 VALUE fval, sval, wval;
525                 if (RubyDialogCallback_getFontForItem(view, &size, &family, &style, &weight) == 0)
526                         rb_raise(rb_eDialogError, "Cannot get font for dialog item");
527                 fval = (family == 1 ? sDefaultSymbol :
528                                 (family == 2 ? sRomanSymbol :
529                                  (family == 3 ? sSwissSymbol :
530                                   (family == 4 ? sFixedSymbol :
531                                    Qnil))));
532                 sval = (style == 1 ? sNormalSymbol :
533                                 (style == 2 ? sSlantSymbol :
534                                  (style == 3 ? sItalicSymbol :
535                                   Qnil)));
536                 wval = (weight == 1 ? sMediumSymbol :
537                                 (weight == 2 ? sBoldSymbol :
538                                  (weight == 3 ? sLightSymbol :
539                                   Qnil)));
540                 val = rb_ary_new3(4, INT2NUM(size), fval, sval, wval);
541                 rb_obj_freeze(val);
542         } else if (key == sSelectionSymbol) {
543                 /*  Selection (for Table == MyTextCtrl item)  */
544                 if (type == sTableSymbol) {
545                         IntGroup *ig = RubyDialogCallback_selectedTableRows((RDItem *)view);
546                         val = ValueFromIntGroup(ig);
547                         IntGroupRelease(ig);
548                 /*      int row, count;
549                         count = RubyDialog_GetTableItemCount((RubyValue)dialog_val, (RDItem *)view);
550                         for (row = 0; row < count; row++) {
551                                 if (RubyDialogCallback_isTableRowSelected((RDItem *)view, row))
552                                         IntGroupAdd(ig, row, 1);
553                         }
554                         val = ValueFromIntGroup(ig); */
555                 } else val = Qnil;
556         } else {
557                 val = rb_ivar_get(self, key_id);
558         }
559         
560         return val;
561 }
562
563 /*
564  *  call-seq:
565  *     append_string(val) -> self
566  *
567  *  Append the given string to the end. Only usable for the text control.
568  */
569 static VALUE
570 s_RubyDialogItem_AppendString(VALUE self, VALUE val)
571 {
572         VALUE dialog_val, index_val;
573         int itag;
574         RubyDialog *dref;
575         RDItem *view;
576         
577         dialog_val = rb_ivar_get(self, SYM2ID(sDialogSymbol));
578         index_val = rb_ivar_get(self, SYM2ID(sIndexSymbol));
579         itag = NUM2INT(index_val);
580         if (dialog_val == Qnil || (dref = s_RubyDialog_GetController(dialog_val)) == NULL)
581                 rb_raise(rb_eDialogError, "The dialog item does not belong to any dialog (internal error?)");
582         view = RubyDialogCallback_dialogItemAtIndex(dref, itag);        
583         val = rb_str_to_str(val);
584         if (RubyDialogCallback_appendString(view, EncodedStringValuePtr(val)) == 0)
585                 rb_raise(rb_eDialogError, "Cannot append string to the dialog item");
586         return self;
587 }
588
589 /*
590  *  call-seq:
591  *     refresh_rect(rectArray, eraseBackground = true) -> self
592  *
593  *  Request refreshing part of the content in the next update event.
594  *  rectArray = [x, y, width, height].
595  */
596 static VALUE
597 s_RubyDialogItem_RefreshRect(int argc, VALUE *argv, VALUE self)
598 {
599         VALUE rval, fval;
600         VALUE dialog_val, index_val;
601         int itag;
602         RubyDialog *dref;
603         RDItem *view;
604         RDRect rect;
605
606         dialog_val = rb_ivar_get(self, SYM2ID(sDialogSymbol));
607         index_val = rb_ivar_get(self, SYM2ID(sIndexSymbol));
608         itag = NUM2INT(index_val);
609         if (dialog_val == Qnil || (dref = s_RubyDialog_GetController(dialog_val)) == NULL)
610                 rb_raise(rb_eDialogError, "The dialog item does not belong to any dialog (internal error?)");
611         view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
612         rb_scan_args(argc, argv, "11", &rval, &fval);
613         if (argc == 1)
614                 fval = Qtrue;
615         rval = rb_ary_to_ary(rval);
616         if (RARRAY_LEN(rval) != 4)
617                 rb_raise(rb_eArgError, "The rectangle should be given as an array of four numerics (x, y, width, height)");
618         rect.origin.x = NUM2DBL(rb_Float(RARRAY_PTR(rval)[0]));
619         rect.origin.y = NUM2DBL(rb_Float(RARRAY_PTR(rval)[1]));
620         rect.size.width = NUM2DBL(rb_Float(RARRAY_PTR(rval)[2]));
621         rect.size.height = NUM2DBL(rb_Float(RARRAY_PTR(rval)[3]));
622         RubyDialogCallback_setNeedsDisplayInRect(view, rect, RTEST(fval));
623         return self;
624 }
625
626 #pragma mark ====== Dialog methods ======
627
628 static VALUE
629 s_RubyDialog_Initialize(int argc, VALUE *argv, VALUE self)
630 {
631         int i, style;
632         VALUE val1, val2, val3, val4;
633         VALUE items;
634         char *title1, *title2;
635         RubyDialogInfo *di;
636         RubyDialog *dref;
637
638         Data_Get_Struct(self, RubyDialogInfo, di);
639
640         rb_scan_args(argc, argv, "04", &val1, &val2, &val3, &val4);
641
642         style = 0;
643         if (val4 != Qnil) {
644                 VALUE optval;
645                 optval = rb_hash_aref(val4, sResizableSymbol);
646                 if (RTEST(optval))
647                         style |= rd_Resizable;
648                 optval = rb_hash_aref(val4, sHasCloseBoxSymbol);
649                 if (RTEST(optval))
650                         style |= rd_HasCloseBox;
651         }
652         
653         di->dref = dref = RubyDialogCallback_new(style);
654         RubyDialogCallback_setRubyObject(dref, (RubyValue)self);
655         
656         if (!NIL_P(val1)) {
657                 char *p = EncodedStringValuePtr(val1);
658                 RubyDialogCallback_setWindowTitle(dref, p);
659         }
660         if (val2 != Qnil)
661                 title1 = EncodedStringValuePtr(val2);
662         else title1 = NULL;
663         if (val3 != Qnil)
664                 title2 = EncodedStringValuePtr(val3);
665         else title2 = NULL;
666
667         //  Array of item informations
668         items = rb_ary_new();
669         
670         if (val2 == Qnil && argc < 2) {
671                 /*  The 2nd argument is omitted (nil is not explicitly given)  */
672                 title1 = "OK";  /*  Default title  */
673         }
674         if (val3 == Qnil && argc < 3) {
675                 /*  The 3rd argument is omitted (nil is not explicitly given)  */
676                 title2 = "Cancel";  /*  Default title  */
677         }
678         
679         /*  Create standard buttons  */
680         /*  (When title{1,2} == NULL, the buttons are still created but set to hidden)  */
681         RubyDialogCallback_createStandardButtons(dref, title1, title2);
682         for (i = 0; i < 2; i++) {
683                 VALUE item;
684                 item = rb_class_new_instance(0, NULL, rb_cDialogItem);
685                 rb_ivar_set(item, SYM2ID(sDialogSymbol), self);
686                 rb_ivar_set(item, SYM2ID(sIndexSymbol), INT2NUM(i));
687                 rb_ivar_set(item, SYM2ID(sTagSymbol), Ruby_NewEncodedStringValue2(i == 0 ? "ok" : "cancel"));
688                 rb_ivar_set(item, SYM2ID(sTypeSymbol), sButtonSymbol);
689                 rb_ary_push(items, item);
690         }
691         
692         rb_iv_set(self, "_items", items);
693         
694         if (rb_ary_includes(gRubyDialogList, self) == Qfalse)
695                 rb_ary_push(gRubyDialogList, self);
696
697         return Qnil;
698 }
699
700 static int
701 s_RubyDialog_ItemIndexForTagNoRaise(VALUE self, VALUE tag)
702 {
703         VALUE items = rb_iv_get(self, "_items");
704         int len = RARRAY_LEN(items);
705         VALUE *ptr = RARRAY_PTR(items);
706         int i;
707         if (FIXNUM_P(tag)) {
708                 i = NUM2INT(tag);
709                 if (i < 0 || i >= len)
710                         return -1;
711                 else return i;
712         }
713         for (i = 0; i < len; i++) {
714                 if (rb_equal(tag, rb_ivar_get(ptr[i], SYM2ID(sTagSymbol))) == Qtrue)
715                         return i;
716         }
717         return -2;
718 }
719
720 static int
721 s_RubyDialog_ItemIndexForTag(VALUE self, VALUE tag)
722 {
723         int i = s_RubyDialog_ItemIndexForTagNoRaise(self, tag);
724         if (i == -1)
725                 rb_raise(rb_eDialogError, "item number (%d) out of range", i);
726         else if (i == -2)
727                 rb_raise(rb_eDialogError, "Dialog has no item with tag %s", EncodedStringValuePtr(tag));
728         return i;
729 }
730
731 /*
732  *  call-seq:
733  *     item_at_index(index) -> DialogItem
734  *
735  *  Get the dialog item at index. If no such item exists, exception is raised.
736  */
737 static VALUE
738 s_RubyDialog_ItemAtIndex(VALUE self, VALUE ival)
739 {
740         VALUE items;
741         int idx = NUM2INT(rb_Integer(ival));
742         items = rb_iv_get(self, "_items");
743         if (idx < 0 || idx > RARRAY_LEN(items))
744                 rb_raise(rb_eRangeError, "item index (%d) out of range", idx);
745         else if (idx == RARRAY_LEN(items))
746                 return Qnil;  /*  This may happen when this function is called during creation of item  */
747         return RARRAY_PTR(items)[idx];
748 }
749
750 /*
751  *  call-seq:
752  *     item_with_tag(tag) -> DialogItem
753  *
754  *  Get the dialog item which has the given tag. If no such item exists, returns nil.
755  */
756 static VALUE
757 s_RubyDialog_ItemWithTag(VALUE self, VALUE tval)
758 {
759         int idx = s_RubyDialog_ItemIndexForTagNoRaise(self, tval);
760         if (idx >= 0) {
761                 VALUE items = rb_iv_get(self, "_items");
762                 return RARRAY_PTR(items)[idx];
763         } else return Qnil;
764 }
765
766 /*
767  *  call-seq:
768  *     set_attr(tag, hash)
769  *     set_attr(tag, key1, val1, key2, val2,...)
770  *
771  *  Set the attributes given in the hash or in the argument list.
772  */
773 static VALUE
774 s_RubyDialog_SetAttr(int argc, VALUE *argv, VALUE self)
775 {
776         int i, itag, klen;
777         VALUE items = rb_iv_get(self, "_items");
778         VALUE *ptr = RARRAY_PTR(items);
779         VALUE tval, aval, item, key, val, *kptr;
780         rb_scan_args(argc, argv, "1*", &tval, &aval);
781         itag = s_RubyDialog_ItemIndexForTag(self, tval);
782         item = ptr[itag];
783         if (RARRAY_LEN(aval) == 1) {
784                 VALUE hval = RARRAY_PTR(aval)[0];
785                 VALUE keys = rb_funcall(hval, rb_intern("keys"), 0);
786                 klen = RARRAY_LEN(keys);
787                 kptr = RARRAY_PTR(keys);
788                 for (i = 0; i < klen; i++) {
789                         key = kptr[i];
790                         val = rb_hash_aref(hval, key);
791                         s_RubyDialogItem_SetAttr(item, key, val);
792                 }
793         } else if (RARRAY_LEN(aval) % 2 == 1)
794                 rb_raise(rb_eArgError, "set_attr: the arguments should be assigned as key-value pairs");
795         else {
796                 klen = RARRAY_LEN(aval);
797                 kptr = RARRAY_PTR(aval);
798                 for (i = 0; i < klen; i += 2) {
799                         key = kptr[i];
800                         val = kptr[i + 1];
801                         s_RubyDialogItem_SetAttr(item, key, val);
802                 }
803         }
804         return item;
805 }
806
807 /*
808  *  call-seq:
809  *     attr(tag, key)
810  *
811  *  Get the attribute for the key.
812  */
813 static VALUE
814 s_RubyDialog_Attr(VALUE self, VALUE tag, VALUE key)
815 {
816         VALUE items = rb_iv_get(self, "_items");
817         VALUE *ptr = RARRAY_PTR(items);
818         int itag = s_RubyDialog_ItemIndexForTag(self, tag);
819         VALUE item = ptr[itag];
820         return s_RubyDialogItem_Attr(item, key);
821 }
822
823 /*
824  *  call-seq:
825  *     run
826  *
827  *  Run the modal session for this dialog.
828  */
829 static VALUE
830 s_RubyDialog_Run(VALUE self)
831 {
832         int retval;
833         VALUE iflag;
834         RubyDialog *dref = s_RubyDialog_GetController(self);
835
836         iflag = Ruby_SetInterruptFlag(Qfalse);
837         retval = RubyDialogCallback_runModal(dref);
838         Ruby_SetInterruptFlag(iflag);
839         RubyDialogCallback_destroy(dref);
840         s_RubyDialog_Forget(self);
841         return rb_iv_get(self, "_retval");
842 }
843
844 /*
845  *  call-seq:
846  *     show
847  *
848  *  Show the dialog modelessly. This is to be used with Dialog#hide in pairs.
849  *  To avoid garbage collection by Ruby interpreter, the dialog being shown is 
850  *  registered in a global variable, and unregistered when it is hidden.
851  *  Mixing Dialog#show and Dialog#run will lead to unpredictable results, including crash.
852  */
853 static VALUE
854 s_RubyDialog_Show(VALUE self)
855 {
856         RubyDialog *dref = s_RubyDialog_GetController(self);
857         RubyDialogCallback_show(dref);
858 //      if (rb_ary_includes(gRubyDialogList, self) == Qfalse)
859 //              rb_ary_push(gRubyDialogList, self);
860         return self;
861 }
862
863 /*
864  *  call-seq:
865  *     hide
866  *
867  *  Hide the modeless dialog. This is to be used with Dialog#show in pairs.
868  *  Mixing Dialog#hide and Dialog#run will lead to unpredictable results, including crash.
869  */
870 static VALUE
871 s_RubyDialog_Hide(VALUE self)
872 {
873         RubyDialog *dref = s_RubyDialog_GetController(self);
874         RubyDialogCallback_hide(dref);
875         return self;
876 }
877
878 /*
879  *  call-seq:
880  *     is_active -> Boolean
881  *
882  *  Returns whether this dialog is 'active' (i.e. the user is working on it) or not.
883  */
884 static VALUE
885 s_RubyDialog_IsActive(VALUE self)
886 {
887         RubyDialog *dref = s_RubyDialog_GetController(self);
888         if (RubyDialogCallback_isActive(dref))
889                 return Qtrue;
890         else return Qfalse;
891 }
892
893 /*
894  *  call-seq:
895  *     close
896  *
897  *  Close the modeless dialog. The normal close handler of the platform is invoked.
898  *  If the dialog is registered in the ruby_dialog_list global variable, it becomes unregistered.
899  *  If force is true, or this method is called recursively, the window will be destroyed unconditionally.
900  */
901 static VALUE
902 s_RubyDialog_Close(VALUE self)
903 {
904         RubyDialog *dref = s_RubyDialog_GetController(self);
905         RubyDialogCallback_close(dref);
906         return self;
907 }
908
909 /*
910  *  call-seq:
911  *     layout(columns, i11, ..., i1c, i21, ..., i2c, ..., ir1, ..., irc [, options]) => integer
912  *
913  *  Layout items in a table. The first argument is the number of columns, and must be a positive integer.
914  *  If the last argument is a hash, then it contains the layout options.
915  *  The ixy is the item identifier (a non-negative integer) or [identifier, hash], where the hash
916  *  contains the specific options for the item.
917  *  Returns an integer that represents the dialog item.
918  */
919 static VALUE
920 s_RubyDialog_Layout(int argc, VALUE *argv, VALUE self)
921 {
922         VALUE items, oval, *opts, new_item;
923         int row, col, i, j, n, itag, nitems, *itags;
924         int autoResizeFlag;
925         RubyDialog *dref;
926         float *widths, *heights;
927         float f, fmin;
928         RDSize *sizes;
929         RDItem *layoutView, *ditem;
930         RDSize contentMinSize;
931         RDRect layoutFrame;
932         float col_padding = 8.0;  /*  Padding between columns  */
933         float row_padding = 8.0;  /*  Padding between rows  */
934         float margin = 10.0;
935
936         dref = s_RubyDialog_GetController(self);
937         contentMinSize = RubyDialogCallback_windowMinSize(dref);
938         items = rb_iv_get(self, "_items");
939         nitems = RARRAY_LEN(items);
940         
941         autoResizeFlag = RubyDialogCallback_isAutoResizeEnabled(dref);
942         RubyDialogCallback_setAutoResizeEnabled(dref, 0);
943
944         if (argc > 0 && rb_obj_is_kind_of(argv[argc - 1], rb_cHash)) {
945                 VALUE oval1;
946                 oval = argv[argc - 1];
947                 argc--;
948                 oval1 = rb_hash_aref(oval, sPaddingSymbol);
949                 if (rb_obj_is_kind_of(oval1, rb_cNumeric))
950                         col_padding = row_padding = NUM2DBL(oval1);
951                 oval1 = rb_hash_aref(oval, sMarginSymbol);
952                 if (rb_obj_is_kind_of(oval1, rb_cNumeric))
953                         margin = NUM2DBL(oval1);
954         } else {
955                 oval = Qnil;
956         }
957         
958         if (--argc < 0 || (col = NUM2INT(rb_Integer(argv[0]))) <= 0 || argc < col)
959                 rb_raise(rb_eArgError, "wrong arguments; the first argument (col) must be a positive integer, and at least col arguments must follow");
960         row = (argc + col - 1) / col;  /*  It actually means (int)(ceil((argc - 1.0) / col))  */
961         argv++;
962
963         /*  Allocate temporary storage  */
964         itags = (int *)calloc(sizeof(int), row * col);
965         opts = (VALUE *)calloc(sizeof(VALUE), row * col);
966         sizes = (RDSize *)calloc(sizeof(RDSize), row * col);
967         widths = (float *)calloc(sizeof(float), col);
968         heights = (float *)calloc(sizeof(float), row);
969         if (itags == NULL || sizes == NULL || opts == NULL || widths == NULL || heights == NULL)
970                 rb_raise(rb_eNoMemError, "out of memory during layout");
971         
972         /*  Get frame sizes  */
973         for (i = 0; i < row; i++) {
974                 for (j = 0; j < col; j++) {
975                         VALUE argval;
976                         n = i * col + j;
977                         if (n >= argc)
978                                 break;
979                         argval = argv[n];
980                         if (TYPE(argval) == T_ARRAY && RARRAY_LEN(argval) == 2) {
981                                 opts[n] = RARRAY_PTR(argval)[1];
982                                 if (TYPE(opts[n]) != T_HASH)
983                                         rb_raise(rb_eTypeError, "The layout options should be given as a hash");
984                                 argval = RARRAY_PTR(argval)[0];
985                         }
986                         if (argval == Qnil)
987                                 itag = -1;
988                         else if (rb_obj_is_kind_of(argval, rb_cDialogItem))
989                                 itag = NUM2INT(s_RubyDialogItem_Attr(argval, sIndexSymbol));
990                         else if (FIXNUM_P(argval))
991                                 itag = FIX2INT(argval);
992                         else
993                                 itag = s_RubyDialog_ItemIndexForTag(self, argval);
994                         if (itag >= nitems)
995                                 rb_raise(rb_eRangeError, "item tag (%d) is out of range (should be 0..%d)", itag, nitems - 1);
996                         if (itag >= 0 && (ditem = RubyDialogCallback_dialogItemAtIndex(dref, itag)) != NULL) {
997                                 sizes[n] = RubyDialogCallback_frameOfItem(ditem).size;
998                         }
999                         itags[n] = itag;
1000                 /*      printf("sizes(%d,%d) = [%f,%f]\n", i, j, sizes[n-2].width, sizes[n-2].height); */
1001                 }
1002         }
1003         
1004         /*  Calculate required widths  */
1005         for (j = 0; j < col; j++) {
1006                 fmin = 0.0;
1007                 for (i = 0; i < row; i++) {
1008                         for (n = j; n >= 0; n--) {
1009                                 f = sizes[i * col + n].width;
1010                                 if (f > 0.0) {
1011                                         f += (n > 0 ? widths[n - 1] : 0.0);
1012                                         break;
1013                                 }
1014                         }
1015                         if (j < col - 1 && sizes[i * col + j + 1].width == 0.0)
1016                                 continue;  /*  The next right item is empty  */
1017                         if (fmin < f)
1018                                 fmin = f;
1019                 }
1020                 fmin += col_padding;
1021                 widths[j] = fmin;
1022         /*      printf("widths[%d]=%f\n", j, fmin); */
1023         }
1024
1025         /*  Calculate required heights  */
1026         fmin = 0.0;
1027         for (i = 0; i < row; i++) {
1028                 for (j = 0; j < col; j++) {
1029                         for (n = i; n >= 0; n--) {
1030                                 f = sizes[n * col + j].height;
1031                                 if (f > 0.0) {
1032                                         f += (n > 0 ? heights[n - 1] : 0.0);
1033                                         break;
1034                                 }
1035                         }
1036                         if (fmin < f)
1037                                 fmin = f;
1038                 }
1039                 fmin += row_padding;
1040                 heights[i] = fmin;
1041         /*      printf("heights[%d]=%f\n", i, fmin); */
1042         }
1043         
1044         /*  Calculate layout view size  */
1045         layoutFrame.size.width = widths[col - 1];
1046         layoutFrame.size.height = heights[row - 1];
1047         layoutFrame.origin.x = margin;
1048         layoutFrame.origin.y = margin;
1049 /*      printf("layoutFrame = [%f,%f,%f,%f]\n", layoutFrame.origin.x, layoutFrame.origin.y, layoutFrame.size.width, layoutFrame.size.height); */
1050
1051         /*  Create a layout view  */
1052         layoutView = RubyDialogCallback_createItem(dref, "layout_view", "", layoutFrame);
1053
1054         /*  Move the subviews into the layout view  */
1055         for (i = 0; i < row; i++) {
1056                 for (j = 0; j < col; j++) {
1057                         n = i * col + j;
1058                         if (n < argc && (itag = itags[n]) > 0 && itag < nitems) {
1059                                 RDPoint pt;
1060                                 float offset;
1061                                 RDRect cell;
1062                                 VALUE type, item;
1063                                 int k;
1064                                 ditem = RubyDialogCallback_dialogItemAtIndex(dref, itag);
1065                                 item = (RARRAY_PTR(items))[itag];
1066                                 type = rb_ivar_get(item, SYM2ID(sTypeSymbol));
1067                                 if (type == sTextSymbol)
1068                                         offset = 3.0;
1069                                 else offset = 0.0;
1070                                 cell.origin.x = (j > 0 ? widths[j - 1] : 0.0);
1071                                 cell.origin.y = (i > 0 ? heights[i - 1] : 0.0);
1072                                 for (k = j + 1; k < col; k++) {
1073                                         if (itags[i * col + k] != -1)
1074                                                 break;
1075                                 }
1076                                 cell.size.width = widths[k - 1] - cell.origin.x;
1077                                 for (k = i + 1; k < row; k++) {
1078                                         if (itags[k * col + j] != -2)
1079                                                 break;
1080                                 }
1081                                 cell.size.height = heights[k - 1] - cell.origin.y;
1082                                 pt.x = cell.origin.x + col_padding * 0.5;
1083                                 pt.y = cell.origin.y + row_padding * 0.5 + offset;
1084                                 {
1085                                         /*  Handle item-specific options  */
1086                                         /*  They can either be specified as layout options or as item attributes  */
1087                                         VALUE oval1;
1088                                         int resize = 0;
1089                                         if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sHFillSymbol)) == Qnil)
1090                                                 oval1 = rb_ivar_get(item, SYM2ID(sHFillSymbol));
1091                                         if (RTEST(oval1)) {
1092                                                 sizes[n].width = cell.size.width - col_padding;
1093                                                 resize = 1;
1094                                         }
1095                                         if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sVFillSymbol)) == Qnil)
1096                                                 oval1 = rb_ivar_get(item, SYM2ID(sVFillSymbol));
1097                                         if (RTEST(oval1)) {
1098                                                 sizes[n].height = cell.size.height - row_padding;
1099                                                 resize = 1;
1100                                         }
1101                                         if (resize) {
1102                                                 RDRect newFrameRect = RubyDialogCallback_frameOfItem(ditem);
1103                                                 newFrameRect.size.width = sizes[n].width;
1104                                                 newFrameRect.size.height = sizes[n].height;
1105                                                 RubyDialogCallback_setFrameOfItem(ditem, newFrameRect);
1106                                         }
1107                                         if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sAlignSymbol)) == Qnil)
1108                                                 oval1 = rb_ivar_get(item, SYM2ID(sAlignSymbol));
1109                                         if (oval1 == sCenterSymbol)
1110                                                 pt.x += (cell.size.width - sizes[n].width - col_padding) * 0.5;
1111                                         else if (oval1 == sRightSymbol)
1112                                                 pt.x += (cell.size.width - sizes[n].width) - col_padding;
1113                                         if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sVerticalAlignSymbol)) == Qnil)
1114                                                 oval1 = rb_ivar_get(item, SYM2ID(sVerticalAlignSymbol));
1115                                         if (oval1 == sCenterSymbol)
1116                                                 pt.y += (cell.size.height - sizes[n].height - row_padding) * 0.5;
1117                                         else if (oval1 == sBottomSymbol)
1118                                                 pt.y += (cell.size.height - sizes[n].height) - row_padding;
1119                                 }
1120                                 RubyDialogCallback_moveItemUnderView(ditem, layoutView, pt);
1121                         }
1122                 }
1123         }
1124         
1125         free(sizes);
1126         free(widths);
1127         free(heights);
1128         free(opts);
1129         free(itags);
1130         
1131         /*  Index for the layout view  */
1132         itag = RARRAY_LEN(items);
1133
1134         /*  Create a new item object for the layout view and push to _items */
1135         new_item = rb_class_new_instance(0, NULL, rb_cDialogItem);
1136         rb_ivar_set(new_item, SYM2ID(sTypeSymbol), sViewSymbol);
1137         rb_ivar_set(new_item, SYM2ID(sDialogSymbol), self);
1138         rb_ivar_set(new_item, SYM2ID(sIndexSymbol), INT2NUM(itag));
1139         rb_ary_push(items, new_item);
1140
1141         if (oval != Qnil) {
1142                 /*  Set the attributes given in the option hash  */
1143                 VALUE keys = rb_funcall(oval, rb_intern("keys"), 0);
1144                 for (i = 0; i < RARRAY_LEN(keys); i++) {
1145                         VALUE kval = RARRAY_PTR(keys)[i];
1146                         if (TYPE(kval) == T_SYMBOL)
1147                                 s_RubyDialogItem_SetAttr(new_item, kval, rb_hash_aref(oval, kval));
1148                 }
1149         }
1150         
1151         RubyDialogCallback_setAutoResizeEnabled(dref, autoResizeFlag);
1152
1153         return new_item;
1154 }
1155
1156 /*
1157  *  call-seq:
1158  *     item(type, hash) -> DialogItem
1159  *
1160  *  Create a dialog item. Type is one of the following symbols; <tt>:text, :textfield, :radio,
1161  *  :checkbox, :popup</tt>. Hash is the attributes that can be set by set_attr.
1162  *  Returns an integer that represents the item. (0 and 1 are reserved for "OK" and "Cancel")
1163  */
1164 static VALUE
1165 s_RubyDialog_Item(int argc, VALUE *argv, VALUE self)
1166 {
1167         int itag;  /*  Integer tag for NSControl  */
1168         RDRect rect;
1169         const char *title;
1170         double dval;
1171 //      NSDictionary *attr;
1172 //      NSFont *font;
1173         VALUE type, hash, val, items;
1174         VALUE new_item;
1175         RubyDialog *dref;
1176
1177         if (argc == 1 && FIXNUM_P(argv[0])) {
1178                 return s_RubyDialog_ItemAtIndex(self, argv[0]);
1179         }
1180
1181         dref = s_RubyDialog_GetController(self);
1182         rb_scan_args(argc, argv, "11", &type, &hash);
1183         if (NIL_P(hash))
1184                 hash = rb_hash_new();
1185         else if (TYPE(hash) != T_HASH)
1186                 rb_raise(rb_eDialogError, "The second argument of Dialog#item must be a hash");
1187         rect.size.width = rect.size.height = 1.0;
1188         rect.origin.x = rect.origin.y = 0.0;
1189
1190         val = rb_hash_aref(hash, sTitleSymbol);
1191         if (!NIL_P(val)) {
1192                 title = EncodedStringValuePtr(val);
1193         } else {
1194                 title = "";
1195         }
1196
1197         Check_Type(type, T_SYMBOL);
1198         
1199 /*      if (type == sTextViewSymbol)
1200                 font = [NSFont userFixedPitchFontOfSize: 0];
1201         else
1202                 font = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
1203         attr = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, nil];
1204         brect.origin.x = brect.origin.y = 0.0;
1205         brect.size = [title sizeWithAttributes: attr];
1206         brect.size.width += 8;
1207 */
1208         /*  Set rect if specified  */
1209         rect.origin.x = rect.origin.y = 0.0;
1210         rect.size.width = rect.size.height = 0.0;
1211         val = rb_hash_aref(hash, sXSymbol);
1212         if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
1213                 rect.origin.x = dval;
1214         val = rb_hash_aref(hash, sYSymbol);
1215         if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
1216                 rect.origin.y = dval;
1217         val = rb_hash_aref(hash, sWidthSymbol);
1218         if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
1219                 rect.size.width = dval;
1220         val = rb_hash_aref(hash, sHeightSymbol);
1221         if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
1222                 rect.size.height = dval;
1223
1224         /*  Create a new DialogItem  */
1225         new_item = rb_class_new_instance(0, NULL, rb_cDialogItem);
1226         rb_ivar_set(new_item, SYM2ID(sTypeSymbol), type);
1227
1228         /*  Direction for the separator line  */
1229         /*  The direction can be specified either by specifying non-square frame size or "vertical" flag  */
1230         if (type == sLineSymbol) {
1231                 VALUE val1 = rb_hash_aref(hash, ID2SYM(rb_intern("vertical")));
1232                 if (rect.size.width == 0)
1233                         rect.size.width = 1;
1234                 if (rect.size.height == 0)
1235                         rect.size.height = 1;
1236                 if (rect.size.width == rect.size.height) {
1237                         if (RTEST(val1))
1238                                 rect.size.height++;  /*  vertical  */
1239                         else rect.size.width++;  /*  horizontal  */
1240                 }
1241         }
1242         
1243         if (RubyDialogCallback_createItem(dref, rb_id2name(SYM2ID(type)), title, rect) == NULL)
1244                 rb_raise(rb_eDialogError, "item type :%s is not implemented", rb_id2name(SYM2ID(type)));
1245
1246         /*  Push to _items  */
1247         items = rb_iv_get(self, "_items");
1248         rb_ary_push(items, new_item);
1249
1250         /*  Item index  */
1251         itag = RARRAY_LEN(items) - 1;
1252         val = INT2NUM(itag);
1253         rb_ivar_set(new_item, SYM2ID(sIndexSymbol), val);
1254         rb_ivar_set(new_item, SYM2ID(sDialogSymbol), self);
1255         
1256         /*  Set attributes  */
1257         {
1258                 VALUE vals[2];
1259                 vals[0] = val;
1260                 vals[1] = hash;
1261                 s_RubyDialog_SetAttr(2, vals, self);
1262         }
1263         
1264         /*  Set internal attributes  */
1265         rb_ivar_set(new_item, SYM2ID(sIsProcessingActionSymbol), Qfalse);
1266         
1267         /*  Type-specific attributes  */
1268         if (type == sLineSymbol) {
1269                 if (rect.size.width > rect.size.height && rect.size.width == 2)
1270                         rb_ivar_set(new_item, SYM2ID(sHFillSymbol), Qtrue);
1271                 else if (rect.size.width < rect.size.height && rect.size.height == 2)
1272                         rb_ivar_set(new_item, SYM2ID(sVFillSymbol), Qtrue);
1273         }
1274
1275         if (type == sTableSymbol) {
1276                 RDItem *rd_item = RubyDialogCallback_dialogItemAtIndex(dref, itag);
1277                 RubyDialogCallback_refreshTable(rd_item);
1278         }
1279         
1280         return new_item;
1281 }
1282
1283 /*
1284  *  call-seq:
1285  *     _items -> Array of DialogItems
1286  *
1287  *  Returns an internal array of items. For debugging use only.
1288  */
1289 static VALUE
1290 s_RubyDialog_Items(VALUE self)
1291 {
1292         return rb_iv_get(self, "_items");
1293 }
1294
1295 /*
1296  *  call-seq:
1297  *     nitems -> integer
1298  *
1299  *  Returns the number of items.
1300  */
1301 static VALUE
1302 s_RubyDialog_Nitems(VALUE self)
1303 {
1304         VALUE items = rb_iv_get(self, "_items");
1305         int nitems = RARRAY_LEN(items);
1306         return INT2NUM(nitems);
1307 }
1308
1309 /*
1310  *  call-seq:
1311  *     each_item {|item| ...}
1312  *
1313  *  Iterate the given block with the DialogItem object as the argument.
1314  */
1315 static VALUE
1316 s_RubyDialog_EachItem(VALUE self)
1317 {
1318         VALUE items = rb_iv_get(self, "_items");
1319         int nitems = RARRAY_LEN(items);
1320         int i;
1321         for (i = 0; i < nitems; i++) {
1322                 rb_yield(RARRAY_PTR(items)[i]);
1323         }
1324     return self;
1325 }
1326
1327 /*
1328  *  call-seq:
1329  *     radio_group(Array)
1330  *
1331  *  Group radio buttons as a mutually exclusive group. The array elements can be
1332  *  DialogItems, Integers (item index) or other values (item tag).
1333  */
1334 static VALUE
1335 s_RubyDialog_RadioGroup(VALUE self, VALUE aval)
1336 {
1337         int i, j, n;
1338         VALUE gval;
1339         VALUE items = rb_iv_get(self, "_items");
1340         int nitems = RARRAY_LEN(items);
1341         aval = rb_ary_to_ary(aval);
1342         n = RARRAY_LEN(aval);
1343
1344         /*  Build a new array with checked arguments  */
1345         gval = rb_ary_new2(n);
1346         for (i = 0; i < n; i++) {
1347                 VALUE tval = RARRAY_PTR(aval)[i];
1348                 if (rb_obj_is_kind_of(tval, rb_cDialogItem)) {
1349                         j = NUM2INT(s_RubyDialogItem_Attr(tval, sIndexSymbol));
1350                 } else {
1351                         j = s_RubyDialog_ItemIndexForTag(self, tval);
1352                         if (j < 0 || j >= nitems)
1353                                 break;
1354                         tval = RARRAY_PTR(items)[j];
1355                 }
1356                 if (rb_ivar_get(tval, SYM2ID(sTypeSymbol)) != sRadioSymbol)
1357                         break;
1358                 rb_ary_push(gval, INT2NUM(j));
1359         }
1360         if (i < n)
1361                 rb_raise(rb_eDialogError, "the item %d (at index %d) does not represent a radio button", j, i);
1362         
1363         /*  Set the radio group array to the specified items. If the item already belongs to a radio group,
1364             then it is removed from that group. */
1365         /*  All items share the common array (gval in the above). This allows removing easy.  */
1366         for (i = 0; i < n; i++) {
1367                 VALUE gval2;
1368                 j = NUM2INT(RARRAY_PTR(gval)[i]);
1369                 gval2 = rb_ivar_get(RARRAY_PTR(items)[j], SYM2ID(sRadioGroupSymbol));
1370                 if (gval2 != Qnil)
1371                         rb_ary_delete(gval2, INT2NUM(j));  /*  Remove j from gval2  */
1372                 rb_ivar_set(RARRAY_PTR(items)[j], SYM2ID(sRadioGroupSymbol), gval);
1373         }
1374         return gval;
1375 }
1376
1377 /*
1378  *  call-seq:
1379  *     end_modal(item = 0, retval = nil) -> nil
1380  *
1381  *  End the modal session. The argument item is either the DialogItem object or
1382  *  the index (0 for OK, 1 for Cancel). If the second argument is given, it will
1383  *  be the return value of Dialog#run. Otherwise, the return value will be a hash
1384  *  including the key-value pairs for all "tagged" dialog items plus :status=>true
1385  *  (if OK is pressed) or false (if Cancel is pressed).
1386  *  This method itself returns nil.
1387  */
1388 static VALUE
1389 s_RubyDialog_EndModal(int argc, VALUE *argv, VALUE self)
1390 {
1391         int flag;
1392         VALUE retval = Qundef;
1393         if (argc == 0) {
1394                 flag = 0;
1395         } else {
1396                 if (rb_obj_is_kind_of(argv[0], rb_cDialogItem)) {
1397                         flag = NUM2INT(s_RubyDialogItem_Attr(argv[0], sIndexSymbol));
1398                 } else {
1399                         flag = NUM2INT(rb_Integer(argv[0]));
1400                 }
1401                 if (argc > 1)
1402                         retval = argv[1];
1403         }
1404         if (retval == Qundef) {
1405                 /*  The default return value  */
1406                 VALUE items = rb_iv_get(self, "_items");
1407                 int len = RARRAY_LEN(items);
1408                 VALUE *ptr = RARRAY_PTR(items);
1409                 int i;
1410                 retval = rb_hash_new();
1411                 /*  Get values for controls with defined tags  */
1412                 for (i = 2; i < len; i++) {
1413                         /*  Items 0, 1 are OK/Cancel buttons  */
1414                         /*      VALUE type = rb_hash_aref(ptr[i], sTypeSymbol); */
1415                         VALUE tag = rb_ivar_get(ptr[i], SYM2ID(sTagSymbol));
1416                         if (tag != Qnil) {
1417                                 VALUE val;
1418                                 val = s_RubyDialogItem_Attr(ptr[i], sValueSymbol);
1419                                 rb_hash_aset(retval, tag, val);
1420                         }
1421                 }
1422                 rb_hash_aset(retval, ID2SYM(rb_intern("status")), INT2NUM(flag));
1423         }
1424         rb_iv_set(self, "_retval", retval);
1425         RubyDialogCallback_endModal(s_RubyDialog_GetController(self), (flag ? 1 : 0));
1426         if (rb_ary_includes(gRubyDialogList, self) == Qtrue)
1427                 rb_ary_delete(gRubyDialogList, self);
1428         return Qnil;
1429 }
1430
1431 static VALUE
1432 s_RubyDialog_CallActionProc(VALUE self, VALUE aval, int argc, VALUE *argv)
1433 {
1434         if (aval == Qnil)
1435                 return Qnil;
1436         if (TYPE(aval) == T_SYMBOL)
1437                 return rb_funcall2(self, SYM2ID(aval), argc, argv);
1438         else if (rb_obj_is_kind_of(aval, rb_cProc))
1439                 return rb_funcall2(aval, rb_intern("call"), argc, argv);
1440         else {
1441                 VALUE insval = rb_inspect(aval);
1442                 rb_raise(rb_eTypeError, "Cannot call action method '%s'", EncodedStringValuePtr(insval));
1443         }
1444         return Qnil;  /*  Not reached  */
1445 }
1446
1447 /*
1448  *  call-seq:
1449  *     action(item)
1450  *
1451  *  Do the default action for the dialog item. The item is given as the argument.
1452  *  If the item is OK (index == 0) or Cancel (index == 1), the modal session of this dialog will end.
1453  *  Otherwise, the "action" attribute is looked for the item, and if found
1454  *  it is called with the given index as the argument (the attribute must be
1455  *  either a symbol (method name) or a Proc object).
1456  *  If the "action" attribute is not found, do nothing.
1457  *
1458  *  It is likely that you will create a new RubyDialog of your own, and
1459  *  define a singleton method named +action+ that overrides this method.
1460  *  When it is invoked, you can process item-specific actions according to
1461  *  the argument index, and if you want to continue the default behavior
1462  *  (e.g. to end modal session when "OK" is pressed), just call this
1463  *  version by +super+.
1464  */
1465 static VALUE
1466 s_RubyDialog_Action(VALUE self, VALUE item)
1467 {
1468         VALUE aval;
1469         int ival = NUM2INT(s_RubyDialogItem_Attr(item, sIndexSymbol));
1470         if (ival == 0 || ival == 1) {
1471                 RubyDialogCallback_endModal(s_RubyDialog_GetController(self), ival);
1472                 return Qnil;
1473         }
1474         aval = s_RubyDialogItem_Attr(item, sActionSymbol);
1475         return s_RubyDialog_CallActionProc(self, aval, 1, &item);
1476 }
1477
1478 /*
1479  *  call-seq:
1480  *     start_timer(interval, action = nil)
1481  *
1482  *  Start dialog-specific interval timer. The timer interval is described in seconds (floating point
1483  *  is allowed, however the resolution is not better than milliseconds on wxWidgets).
1484  *  The action is either a symbol (method name) or a Proc object.
1485  *  If no action is given, then the last set value is used.
1486  *  If the timer is already running, it is stopped before new timer is run.
1487  */
1488 static VALUE
1489 s_RubyDialog_StartTimer(int argc, VALUE *argv, VALUE self)
1490 {
1491         VALUE itval, actval;
1492         double dval;
1493         RubyDialog *dref = s_RubyDialog_GetController(self);
1494         rb_scan_args(argc, argv, "11", &itval, &actval);
1495         if (actval != Qnil)
1496                 rb_iv_set(self, "_timer_action", actval);
1497         dval = NUM2DBL(rb_Float(itval));
1498         if (RubyDialogCallback_startIntervalTimer(dref, dval) == 0)
1499                 rb_raise(rb_eDialogError, "Cannot start timer for dialog");
1500         return self;
1501 }
1502
1503 /*
1504  *  call-seq:
1505  *     stop_timer()
1506  *
1507  *  Stop dialog-specific interval timer. Do nothing if no timer is running.
1508  */
1509 static VALUE
1510 s_RubyDialog_StopTimer(VALUE self)
1511 {
1512         RubyDialogCallback_stopIntervalTimer(s_RubyDialog_GetController(self));
1513         return self;
1514 }
1515
1516 /*
1517  *  call-seq:
1518  *     on_key(action = nil)
1519  *
1520  *  Set keydown action method. When a keydown event occurs and no other controls
1521  *  in this dialog accept the event, the action method (if non-nil) is invoked
1522  *  with the keycode integer as the single argument. 
1523  *  The action is either a symbol (method name) or a Proc object.
1524  */
1525 static VALUE
1526 s_RubyDialog_OnKey(int argc, VALUE *argv, VALUE self)
1527 {
1528         VALUE actval;
1529         RubyDialog *dref = s_RubyDialog_GetController(self);
1530         rb_scan_args(argc, argv, "01", &actval);
1531         rb_iv_set(self, "_key_action", actval);
1532         RubyDialogCallback_enableOnKeyHandler(dref, (actval != Qnil));
1533         return self;
1534 }
1535
1536 /*
1537  *  call-seq:
1538  *     size -> [width, height]
1539  *
1540  *  Get the size for this dialog.
1541  */
1542 static VALUE
1543 s_RubyDialog_Size(VALUE self)
1544 {
1545         RDSize size = RubyDialogCallback_windowSize(s_RubyDialog_GetController(self));
1546         return rb_ary_new3(2, INT2NUM(floor(size.width + 0.5)), INT2NUM(floor(size.height + 0.5)));
1547 }
1548
1549 /*
1550  *  call-seq:
1551  *     set_size([width, height])
1552  *     set_size(width, height)
1553  *
1554  *  Set the size for this dialog.
1555  */
1556 static VALUE
1557 s_RubyDialog_SetSize(int argc, VALUE *argv, VALUE self)
1558 {
1559         RDSize size;
1560         VALUE wval, hval;
1561         rb_scan_args(argc, argv, "11", &wval, &hval);
1562         if (hval == Qnil) {
1563                 hval = Ruby_ObjectAtIndex(wval, 1);
1564                 wval = Ruby_ObjectAtIndex(wval, 0);
1565         }
1566         size.width = NUM2INT(rb_Integer(wval));
1567         size.height = NUM2INT(rb_Integer(hval));
1568         RubyDialogCallback_setWindowSize(s_RubyDialog_GetController(self), size);
1569         return self;
1570 }
1571
1572 /*
1573  *  call-seq:
1574  *     min_size -> [width, height]
1575  *
1576  *  Get the minimum size for this dialog.
1577  */
1578 static VALUE
1579 s_RubyDialog_MinSize(VALUE self)
1580 {
1581         RDSize size = RubyDialogCallback_windowMinSize(s_RubyDialog_GetController(self));
1582         return rb_ary_new3(2, INT2NUM(floor(size.width + 0.5)), INT2NUM(floor(size.height + 0.5)));
1583 }
1584
1585 /*
1586  *  call-seq:
1587  *     set_min_size
1588  *     set_min_size([width, height])
1589  *     set_min_size(width, height)
1590  *
1591  *  Set the minimum size for this dialog.
1592  */
1593 static VALUE
1594 s_RubyDialog_SetMinSize(int argc, VALUE *argv, VALUE self)
1595 {
1596         RDSize size;
1597         VALUE wval, hval;
1598         rb_scan_args(argc, argv, "02", &wval, &hval);
1599         if (wval == Qnil) {
1600                 size = RubyDialogCallback_windowSize(s_RubyDialog_GetController(self));
1601         } else {
1602                 if (hval == Qnil) {
1603                         hval = Ruby_ObjectAtIndex(wval, 1);
1604                         wval = Ruby_ObjectAtIndex(wval, 0);
1605                 }
1606                 size.width = NUM2INT(rb_Integer(wval));
1607                 size.height = NUM2INT(rb_Integer(hval));
1608         }
1609         RubyDialogCallback_setWindowMinSize(s_RubyDialog_GetController(self), size);
1610         return self;
1611 }
1612
1613 #if 0
1614 /*
1615  *  call-seq:
1616  *     listen(obj, str, pr)
1617  *
1618  *  Listen to the event invoked by the object. str = the name of the event (dependent on the
1619  *  object), pr = the callback procedure. The first argument to the callback procedure is
1620  *  always obj. Other arguments are dependent on the event and the object.
1621  */
1622 static VALUE
1623 s_RubyDialog_Listen(VALUE self, VALUE oval, VALUE sval, VALUE pval)
1624 {
1625         int i;
1626         const char *sptr;
1627         if (sval == Qnil)
1628                 sptr = NULL;
1629         else
1630                 sptr = EncodedStringValuePtr(sval);
1631         if (rb_obj_is_kind_of(oval, rb_cMolecule)) {
1632                 Molecule *mol = MoleculeFromValue(oval);
1633                 i = RubyDialogCallback_Listen(s_RubyDialog_GetController(self), mol, "Molecule", sptr, (RubyValue)oval, (RubyValue)pval);
1634                 if (i < 0) {
1635                         switch (i) {
1636                                 case -1: rb_raise(rb_eDialogError, "This dialog cannot be listened to."); break;
1637                                 case -2: rb_raise(rb_eDialogError, "This message is not supported"); break;
1638                         }
1639                 } else {
1640                         /*  Keep the objects in the internal array, to protect from GC  */
1641                         ID id = rb_intern("listen");
1642                         VALUE aval = rb_ivar_get(self, id);
1643                         if (aval == Qnil) {
1644                                 aval = rb_ary_new();
1645                                 rb_ivar_set(self, id, aval);
1646                         }
1647                         if (pval == Qfalse || pval == Qnil) {
1648                                 rb_ary_delete_at(aval, i);
1649                         } else {
1650                                 rb_ary_store(aval, i, rb_ary_new3(2, oval, pval));
1651                         }
1652                 }
1653         } else {
1654                 rb_raise(rb_eDialogError, "Dialog#listen is presently only available for Molecule object");
1655         }
1656         return self;
1657 }
1658 #endif
1659
1660 /*
1661  *  call-seq:
1662  *     save_panel(message = nil, directory = nil, default_filename = nil, wildcard = nil)
1663  *
1664  *  Display the "save as" dialog and returns the fullpath filename.
1665  */
1666 static VALUE
1667 s_RubyDialog_SavePanel(int argc, VALUE *argv, VALUE klass)
1668 {
1669         VALUE mval, dval, fval, wval, iflag;
1670         const char *mp, *dp, *wp;
1671         int n;
1672         char buf[1024];
1673         rb_scan_args(argc, argv, "04", &mval, &dval, &fval, &wval);
1674         if (mval == Qnil)
1675                 mp = NULL;
1676         else mp = EncodedStringValuePtr(mval);
1677         if (dval == Qnil)
1678                 dp = NULL;
1679         else dp = FileStringValuePtr(dval);
1680         if (fval == Qnil)
1681                 buf[0] = 0;
1682         else {
1683                 strncpy(buf, FileStringValuePtr(fval), 1023);
1684                 buf[1023] = 0;
1685         }
1686         if (wval == Qnil)
1687                 wp = NULL;
1688         else wp = FileStringValuePtr(wval);
1689         iflag = Ruby_SetInterruptFlag(Qfalse);
1690         n = RubyDialogCallback_savePanel(mp, dp, wp, buf, sizeof buf);
1691         Ruby_SetInterruptFlag(iflag);
1692         if (n > 0)
1693                 return Ruby_NewFileStringValue(buf);
1694         else return Qnil;
1695 }
1696
1697 /*
1698  *  call-seq:
1699  *     open_panel(message = nil, directory = nil, wildcard = nil, for_directories = false, multiple_selection = false)
1700  *
1701  *  Display the "open" dialog and returns the fullpath filename.
1702  */
1703 static VALUE
1704 s_RubyDialog_OpenPanel(int argc, VALUE *argv, VALUE klass)
1705 {
1706         VALUE mval, dval, fval, mulval, wval, iflag;
1707         const char *mp, *dp, *wp;
1708         char **ary;
1709         int for_directories = 0, multiple_selection = 0;
1710         int n;
1711         rb_scan_args(argc, argv, "05", &mval, &dval, &wval, &fval, &mulval);
1712         if (mval == Qnil)
1713                 mp = NULL;
1714         else mp = EncodedStringValuePtr(mval);
1715         if (dval == Qnil)
1716                 dp = NULL;
1717         else dp = FileStringValuePtr(dval);
1718         if (wval == Qnil)
1719                 wp = NULL;
1720         else wp = FileStringValuePtr(wval);
1721         if (fval != Qnil && fval != Qfalse)
1722                 for_directories = 1;
1723         if (mulval != Qnil && mulval != Qfalse) {
1724                 multiple_selection = 1;
1725                 if (for_directories && multiple_selection)
1726                         rb_raise(rb_eDialogError, "open_panel for directories allows only single selection");
1727         }
1728         iflag = Ruby_SetInterruptFlag(Qfalse);
1729         n = RubyDialogCallback_openPanel(mp, dp, wp, &ary, for_directories, multiple_selection);
1730         Ruby_SetInterruptFlag(iflag);
1731         if (n > 0) {
1732                 VALUE retval;
1733                 if (multiple_selection) {
1734                         int i;
1735                         retval = rb_ary_new();
1736                         for (i = 0; i < n; i++) {
1737                                 rb_ary_push(retval, Ruby_NewFileStringValue(ary[i]));
1738                                 free(ary[i]);
1739                         }
1740                 } else {
1741                         retval = Ruby_NewFileStringValue(ary[0]);
1742                         free(ary[0]);
1743                 }
1744                 free(ary);
1745                 return retval;
1746         } else return Qnil;
1747 }
1748
1749 #pragma mark ====== Table view support ======
1750
1751 static VALUE
1752 s_RubyDialog_doTableAction(VALUE val)
1753 {
1754         VALUE ival, itval, pval, retval;
1755         VALUE args[5];
1756         void **vp = (void **)val;
1757         VALUE self = (VALUE)vp[0];
1758         RDItem *ip = (RDItem *)vp[1];
1759         VALUE sym = (VALUE)vp[2];
1760         RubyDialog *dref = s_RubyDialog_GetController(self);
1761         int idx = RubyDialogCallback_indexOfItem(dref, ip);
1762         if (idx < 0)
1763                 return Qnil;   /*  No such item (this cannot happen)  */
1764         ival = INT2NUM(idx);
1765         itval = s_RubyDialog_ItemAtIndex(self, ival);
1766         pval = rb_ivar_get(itval, SYM2ID(sym));
1767         if (pval == Qnil)
1768                 return Qnil;   /*  No action is defined: return the default value  */
1769         args[0] = itval;
1770
1771         if (sym == sOnCountSymbol) {
1772                 retval = s_RubyDialog_CallActionProc(self, pval, 1, args);
1773                 vp[3] = (void *)(intptr_t)(NUM2INT(rb_Integer(retval)));
1774                 return retval;
1775         } else if (sym == sOnGetValueSymbol) {
1776                 args[1] = INT2NUM((int)vp[3]);
1777                 args[2] = INT2NUM((int)vp[4]);
1778                 retval = s_RubyDialog_CallActionProc(self, pval, 3, args);
1779                 retval = rb_str_to_str(retval);
1780                 vp[5] = strdup(EncodedStringValuePtr(retval));
1781                 return retval;
1782         } else if (sym == sOnSetValueSymbol) {
1783                 args[1] = INT2NUM((int)vp[3]);
1784                 args[2] = INT2NUM((int)vp[4]);
1785                 args[3] = Ruby_NewEncodedStringValue2((char *)vp[5]);
1786                 retval = s_RubyDialog_CallActionProc(self, pval, 4, args);
1787                 vp[6] = (void *)(intptr_t)(NUM2INT(rb_Integer(retval)));
1788                 return retval;
1789         } else if (sym == sOnDragSelectionToRowSymbol) {
1790                 args[1] = INT2NUM((int)vp[3]);
1791                 retval = s_RubyDialog_CallActionProc(self, pval, 2, args);
1792                 return retval;
1793         } else if (sym == sIsItemEditableSymbol) {
1794                 args[1] = INT2NUM((int)vp[3]);
1795                 args[2] = INT2NUM((int)vp[4]);
1796                 retval = s_RubyDialog_CallActionProc(self, pval, 3, args);
1797                 vp[5] = (void *)(intptr_t)(RTEST(retval) ? 1 : 0);
1798                 return retval;
1799         } else if (sym == sIsDragAndDropEnabledSymbol) {
1800                 retval = s_RubyDialog_CallActionProc(self, pval, 1, args);
1801                 vp[3] = (void *)(intptr_t)(RTEST(retval) ? 1 : 0);
1802                 return retval;
1803         } else if (sym == sOnSelectionChangedSymbol) {
1804                 retval = s_RubyDialog_CallActionProc(self, pval, 1, args);
1805                 vp[3] = (void *)(intptr_t)(RTEST(retval) ? 1 : 0);
1806                 return retval;
1807         } else if (sym == sOnSetColorSymbol) {
1808                 float *fg = (float *)vp[5];
1809                 float *bg = (float *)vp[6];
1810                 int i, n = 0;
1811                 VALUE cval;
1812                 args[1] = INT2NUM((int)vp[3]);
1813                 args[2] = INT2NUM((int)vp[4]);
1814                 retval = s_RubyDialog_CallActionProc(self, pval, 3, args);
1815                 if (retval == Qnil)
1816                         return Qnil;
1817                 retval = rb_ary_to_ary(retval);
1818                 if (RARRAY_LEN(retval) >= 1 && fg != NULL) {
1819                         if (RARRAY_PTR(retval)[0] != Qnil) {
1820                                 cval = rb_ary_to_ary(RARRAY_PTR(retval)[0]);
1821                                 for (i = 0; i < 4 && i < RARRAY_LEN(cval); i++) {
1822                                         fg[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
1823                                 }
1824                                 n = 1;
1825                         } else n = 0;
1826                 }
1827                 if (RARRAY_LEN(retval) >= 2 && bg != NULL) {
1828                         cval = rb_ary_to_ary(RARRAY_PTR(retval)[1]);
1829                         for (i = 0; i < 4 && i < RARRAY_LEN(cval); i++) {
1830                                 bg[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
1831                         }
1832                         n |= 2;
1833                 }
1834                 vp[7] = (void *)(intptr_t)n;
1835                 return retval;
1836         } else if (sym == sHasPopUpMenuSymbol) {
1837                 args[1] = INT2NUM((int)vp[3]);
1838                 args[2] = INT2NUM((int)vp[4]);
1839                 retval = s_RubyDialog_CallActionProc(self, pval, 3, args);
1840                 if (retval == Qnil) {
1841                         vp[6] = (void *)0;
1842                 } else {
1843                         int i, n;
1844                         char **titles;
1845                         retval = rb_ary_to_ary(retval);
1846                         n = RARRAY_LEN(retval);
1847                         vp[6] = (void *)(intptr_t)n;
1848                         titles = ALLOC_N(char *, n);
1849                         *((char ***)vp[5]) = titles;
1850                         for (i = 0; i < n; i++) {
1851                                 VALUE tval = RARRAY_PTR(retval)[i];
1852                                 titles[i] = strdup(EncodedStringValuePtr(tval));
1853                         }
1854                 }
1855                 return retval;
1856         } else if (sym == sOnPopUpMenuSelectedSymbol) {
1857                 args[1] = INT2NUM((int)vp[3]);
1858                 args[2] = INT2NUM((int)vp[4]);
1859                 args[3] = INT2NUM((int)vp[5]);
1860                 retval = s_RubyDialog_CallActionProc(self, pval, 4, args);
1861                 return retval;
1862         } else return Qnil;
1863 }
1864
1865 int
1866 RubyDialog_GetTableItemCount(RubyValue self, RDItem *ip)
1867 {
1868         int status;
1869         void *vp[4] = { (void *)self, (void *)ip, (void *)sOnCountSymbol, NULL };
1870         VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1871         if (status != 0) {
1872                 Ruby_showError(status);
1873                 return 0;
1874         } else if (val == Qnil)
1875                 return 0;
1876         else return (int)vp[3]; 
1877 }
1878
1879 void
1880 RubyDialog_GetTableItemText(RubyValue self, RDItem *ip, int row, int column, char *buf, int buflen)
1881 {
1882         int status;
1883         void *vp[6] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnGetValueSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, NULL };
1884         VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1885         if (status != 0 || val == Qnil) {
1886                 buf[0] = 0;
1887         } else {
1888                 strncpy(buf, (char *)vp[5], buflen - 1);
1889                 buf[buflen - 1] = 0;
1890         }
1891 }
1892
1893 int
1894 RubyDialog_SetTableItemText(RubyValue self, RDItem *ip, int row, int column, const char *str)
1895 {
1896         int status;
1897         void *vp[7] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnSetValueSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, (void *)str, NULL };
1898         VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1899         if (status != 0 || val == Qnil) {
1900                 return -1;
1901         } else
1902                 return (int)vp[6];
1903 }
1904
1905 void
1906 RubyDialog_DragTableSelectionToRow(RubyValue self, RDItem *ip, int row)
1907 {
1908         int status;
1909         void *vp[5] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnDragSelectionToRowSymbol, (void *)(intptr_t)row, NULL };
1910         rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1911         if (status != 0)
1912                 Ruby_showError(status);
1913 }
1914
1915 int
1916 RubyDialog_IsTableItemEditable(RubyValue self, RDItem *ip, int row, int column)
1917 {
1918         int status;
1919         void *vp[6] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sIsItemEditableSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, NULL };
1920         VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1921         if (status != 0 || val == Qnil)
1922                 return 0;
1923         else return (int)vp[5]; 
1924 }
1925
1926 int
1927 RubyDialog_IsTableDragAndDropEnabled(RubyValue self, RDItem *ip, int row)
1928 {
1929         int status;
1930         void *vp[4] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sIsDragAndDropEnabledSymbol, NULL };
1931         VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1932         if (status != 0 || val == Qnil)
1933                 return 0;
1934         else return (int)vp[3]; 
1935 }
1936
1937 void
1938 RubyDialog_OnTableSelectionChanged(RubyValue self, RDItem *ip)
1939 {
1940         int status;
1941         void *vp[4] = { (void *)self, (void *)ip, (void *)sOnSelectionChangedSymbol, NULL };
1942         rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1943         if (status != 0)
1944                 Ruby_showError(status);
1945 }
1946
1947 int
1948 RubyDialog_SetTableItemColor(RubyValue self, RDItem *ip, int row, int column, float *fg, float *bg)
1949 {
1950         int status;
1951         void *vp[8] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnSetColorSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, (void *)fg, (void *)bg, NULL };
1952         VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1953         if (status != 0 || val == Qnil)
1954                 return 0;
1955         else return (int)vp[7];
1956 }
1957
1958 int
1959 RubyDialog_HasPopUpMenu(RubyValue self, RDItem *ip, int row, int column, char ***menu_titles)
1960 {
1961         int status;
1962         void *vp[7] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sHasPopUpMenuSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, (void *)menu_titles, NULL };
1963         VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1964         if (status != 0 || val == Qnil)
1965                 return 0;
1966         else return (int)vp[6];
1967 }
1968
1969 void
1970 RubyDialog_OnPopUpMenuSelected(RubyValue self, RDItem *ip, int row, int column, int selected_index)
1971 {
1972         int status;
1973         void *vp[7] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnPopUpMenuSelectedSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, (void *)(intptr_t)selected_index, NULL };
1974         rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1975         if (status != 0)
1976                 Ruby_showError(status);
1977 }
1978         
1979 #pragma mark ====== Utility function ======
1980
1981 int
1982 RubyDialog_validateItemContent(RubyValue self, RDItem *ip, const char *s)
1983 {
1984         VALUE items, item, val, val_min, val_max;
1985         int nitems, itag;
1986         RubyDialog *dref = s_RubyDialog_GetController((VALUE)self);
1987         char buf[80];
1988         
1989         items = rb_iv_get(((VALUE)self), "_items");
1990         nitems = RARRAY_LEN(items);
1991         itag = RubyDialogCallback_indexOfItem(dref, ip);
1992         if (itag < 0 || itag >= nitems)
1993                 return 1;  /*  Accept anything  */
1994         
1995         item = (RARRAY_PTR(items))[itag];
1996         val = rb_ivar_get(item, SYM2ID(sRangeSymbol));
1997         if (NIL_P(val))
1998                 return 1;  /*  Accept anything  */
1999         
2000         val_min = Ruby_ObjectAtIndex(val, 0);
2001         val_max = Ruby_ObjectAtIndex(val, 1);
2002         if (FIXNUM_P(val_min) && FIXNUM_P(val_max)) {
2003                 int ival = atoi(s);
2004                 int imin = NUM2INT(val_min);
2005                 int imax = NUM2INT(val_max);
2006                 if (ival < imin || ival > imax)
2007                         return 0;
2008                 snprintf(buf, sizeof buf, "%d", ival);
2009                 RubyDialogCallback_setStringToItem(ip, buf);
2010         } else {
2011                 double d = atof(s);
2012                 double dmin = NUM2DBL(rb_Float(val_min));
2013                 double dmax = NUM2DBL(rb_Float(val_max));
2014                 if (d < dmin || d > dmax)
2015                         return 0;
2016         }
2017         return 1;
2018 }
2019
2020 static VALUE
2021 s_RubyDialog_doItemAction(VALUE val)
2022 {
2023         int i, j, n;
2024         void **vp = (void **)val;
2025         VALUE self = (VALUE)vp[0];
2026         VALUE flag;
2027         RDItem *ip = (RDItem *)vp[1];
2028         RDItem *ip2;
2029         int options = (int)vp[2];
2030         VALUE ival, itval, actval, tval, aval;
2031         RubyDialog *dref = s_RubyDialog_GetController(self);
2032         VALUE items = rb_iv_get(self, "_items");
2033         int nitems = RARRAY_LEN(items);
2034         int idx = RubyDialogCallback_indexOfItem(dref, ip);
2035         static VALUE sTextActionSym = Qfalse, sEscapeActionSym, sReturnActionSym;
2036
2037         if (sTextActionSym == Qfalse) {
2038                 sTextActionSym = ID2SYM(rb_intern("text_action"));
2039                 sEscapeActionSym = ID2SYM(rb_intern("escape_action"));
2040                 sReturnActionSym = ID2SYM(rb_intern("return_action"));
2041         }
2042         
2043         if (idx < 0)
2044                 return Qnil;
2045         ival = INT2NUM(idx);
2046         itval = s_RubyDialog_ItemAtIndex(self, ival);
2047         flag = rb_ivar_get(itval, SYM2ID(sIsProcessingActionSymbol));
2048         if (flag == Qtrue)
2049                 return Qnil;  /*  Avoid recursive calling action proc for the same item  */
2050
2051         rb_ivar_set(itval, SYM2ID(sIsProcessingActionSymbol), Qtrue);
2052
2053         /*  Handle radio group  */
2054         tval = s_RubyDialogItem_Attr(itval, sTypeSymbol);
2055         aval = sActionSymbol;
2056         if (tval == sRadioSymbol) {
2057                 VALUE gval = s_RubyDialogItem_Attr(itval, sRadioGroupSymbol);
2058                 if (gval == Qnil) {
2059                         /*  All other radio buttons with no radio group will be deselected  */
2060                         VALUE radioval;
2061                         for (i = 0; i < nitems; i++) {
2062                                 if (i == idx)
2063                                         continue;
2064                                 radioval = RARRAY_PTR(items)[i];
2065                                 if (s_RubyDialogItem_Attr(radioval, sTypeSymbol) == sRadioSymbol
2066                                         && s_RubyDialogItem_Attr(radioval, sRadioGroupSymbol) == Qnil) {
2067                                         ip2 = RubyDialogCallback_dialogItemAtIndex(dref, i);
2068                                         RubyDialogCallback_setStateForItem(ip2, 0);
2069                                 }
2070                         }
2071                 } else if (TYPE(gval) == T_ARRAY) {
2072                         n = RARRAY_LEN(gval);
2073                         for (i = 0; i < n; i++) {
2074                                 j = NUM2INT(RARRAY_PTR(gval)[i]);
2075                                 if (j >= 0 && j < nitems && j != idx) {
2076                                         ip2 = RubyDialogCallback_dialogItemAtIndex(dref, j);
2077                                         RubyDialogCallback_setStateForItem(ip2, 0);  /*  Deselect  */
2078                                 }
2079                         }
2080                 }
2081         }
2082         
2083         if (tval == sTextFieldSymbol || tval == sTextViewSymbol) {
2084                 if (options == 1)
2085                         aval = sTextActionSym;   /*  Action for every text update  */
2086         }
2087         
2088         if (tval == sTextFieldSymbol) {
2089                 if (options == 2)
2090                         aval = sReturnActionSym;
2091                 else if (options == 4)
2092                         aval = sEscapeActionSym;
2093         }
2094         
2095         /*  If the item has the "action" attribute, call it  */
2096         actval = s_RubyDialogItem_Attr(itval, aval);
2097         if (actval != Qnil) {
2098                 if (TYPE(actval) == T_SYMBOL)
2099                         rb_funcall(self, SYM2ID(actval), 1, itval);
2100                 else
2101                         rb_funcall(actval, rb_intern("call"), 1, itval);
2102         } else if (rb_respond_to(itval, SYM2ID(aval))) {
2103                 /*  If "action" method is defined, then call it without arguments  */
2104                 rb_funcall(itval, SYM2ID(aval), 0);
2105         } else if (aval == sReturnActionSym || aval == sEscapeActionSym) {
2106                 /*  Enter or escape is pressed on text field  */
2107                 if (RubyDialogCallback_isModal(dref)) {
2108                         rb_ivar_set(itval, SYM2ID(sIsProcessingActionSymbol), Qfalse);
2109                         itval = s_RubyDialog_ItemAtIndex(self, (aval == sReturnActionSym ? INT2FIX(0) : INT2FIX(1)));
2110                         s_RubyDialog_EndModal(1, &itval, self);         
2111                 }
2112         } else {
2113                 /*  Default action (only for default buttons)  */
2114                 if (RubyDialogCallback_isModal(dref)) {
2115                         if (idx == 0 || idx == 1) {
2116                                 rb_ivar_set(itval, SYM2ID(sIsProcessingActionSymbol), Qfalse);
2117                                 s_RubyDialog_EndModal(1, &itval, self);
2118                         }
2119                 }
2120         }
2121
2122         rb_ivar_set(itval, SYM2ID(sIsProcessingActionSymbol), Qfalse);
2123         
2124         return Qnil;
2125 }
2126
2127 /*  Action for dialog items.
2128  Get the item number, and call "action" method of the RubyDialog object with
2129  the item number (integer) as the argument. The default "action" method is
2130  defined as s_RubyDialog_action.  */
2131 void
2132 RubyDialog_doItemAction(RubyValue self, RDItem *ip, int options)
2133 {
2134         int status;
2135         void *vp[3];
2136         vp[0] = (void *)(uintptr_t)self;
2137         vp[1] = ip;
2138         vp[2] = (void *)(intptr_t)options;
2139         rb_protect(s_RubyDialog_doItemAction, (VALUE)vp, &status);
2140         if (status != 0)
2141                 Ruby_showError(status);
2142 }
2143
2144 static VALUE
2145 s_RubyDialog_doPaintAction(VALUE val)
2146 {
2147         void **vp = (void **)val;
2148         VALUE self = (VALUE)vp[0];
2149         RDItem *ip = (RDItem *)vp[1];
2150         VALUE ival, itval, actval;
2151         RubyDialog *dref = s_RubyDialog_GetController(self);
2152         int idx = RubyDialogCallback_indexOfItem(dref, ip);
2153         if (idx < 0)
2154                 return Qnil;
2155         ival = INT2NUM(idx);
2156         itval = s_RubyDialog_ItemAtIndex(self, ival);
2157         actval = s_RubyDialogItem_Attr(itval, sOnPaintSymbol);
2158         if (actval != Qnil) {
2159                 if (TYPE(actval) == T_SYMBOL)
2160                         rb_funcall(self, SYM2ID(actval), 1, itval);
2161                 else
2162                         rb_funcall(actval, rb_intern("call"), 1, itval);
2163         }
2164         return Qnil;
2165 }
2166
2167 /*  Paint action for view item.  */
2168 void
2169 RubyDialog_doPaintAction(RubyValue self, RDItem *ip)
2170 {
2171         int status;
2172         void *vp[2];
2173         vp[0] = (void *)self;
2174         vp[1] = ip;
2175         rb_protect(s_RubyDialog_doPaintAction, (VALUE)vp, &status);
2176         if (status != 0) {
2177                 /*  Stop timer before showing error dialog  */
2178                 RubyDialogCallback_stopIntervalTimer(s_RubyDialog_GetController((VALUE)self));
2179                 Ruby_showError(status);
2180         }
2181 }
2182
2183 static VALUE
2184 s_RubyDialog_doTimerAction(VALUE self)
2185 {
2186         VALUE actval = rb_iv_get(self, "_timer_action");
2187         if (actval != Qnil) {
2188                 if (TYPE(actval) == T_SYMBOL)
2189                         rb_funcall(self, SYM2ID(actval), 0);
2190                 else
2191                         rb_funcall(actval, rb_intern("call"), 0);
2192         }
2193         return Qnil;
2194 }
2195         
2196 void
2197 RubyDialog_doTimerAction(RubyValue self)
2198 {
2199         int status;
2200         rb_protect(s_RubyDialog_doTimerAction, (VALUE)self, &status);
2201         if (status != 0) {
2202                 /*  Stop timer before showing error dialog  */
2203                 RubyDialogCallback_stopIntervalTimer(s_RubyDialog_GetController((VALUE)self));
2204                 Ruby_showError(status);
2205         }
2206 }
2207
2208 static VALUE
2209 s_RubyDialog_doKeyAction(VALUE val)
2210 {
2211         void **values = (void **)val;
2212         VALUE self = (VALUE)values[0];
2213         int keyCode = (int)values[1];
2214         VALUE actval = rb_iv_get(self, "_key_action");
2215         if (actval != Qnil) {
2216                 if (TYPE(actval) == T_SYMBOL)
2217                         rb_funcall(self, SYM2ID(actval), 1, INT2NUM(keyCode));
2218                 else
2219                         rb_funcall(actval, rb_intern("call"), 1, INT2NUM(keyCode));
2220         }
2221         return Qnil;
2222 }
2223
2224 void
2225 RubyDialog_doKeyAction(RubyValue self, int keyCode)
2226 {
2227         int status;
2228         void *values[2];
2229         values[0] = (void *)self;
2230         values[1] = (void *)(intptr_t)keyCode;
2231         rb_protect(s_RubyDialog_doKeyAction, (VALUE)values, &status);
2232         if (status != 0) {
2233                 /*  Stop timer before showing error dialog  */
2234                 RubyDialogCallback_stopIntervalTimer(s_RubyDialog_GetController((VALUE)self));
2235                 Ruby_showError(status);
2236         }
2237 }
2238
2239 static VALUE
2240 s_RubyDialog_getFlexFlags(VALUE val)
2241 {
2242         VALUE self = (VALUE)(((void **)val)[0]);
2243         RDItem *ip = (RDItem *)(((void **)val)[1]);
2244         VALUE itval, pval;
2245         RubyDialog *dref = s_RubyDialog_GetController(self);
2246         int idx = RubyDialogCallback_indexOfItem(dref, ip);
2247         if (idx < 0)
2248                 return Qnil;  /*  No such item (this cannot happen)  */
2249         itval = s_RubyDialog_ItemAtIndex(self, INT2NUM(idx));
2250         pval = rb_ivar_get(itval, SYM2ID(sFlexSymbol));
2251         if (pval == Qnil)
2252                 return Qnil;  /*  Not set  */
2253         else {
2254                 pval = rb_Integer(pval);
2255                 ((void **)val)[2] = (void *)(intptr_t)(NUM2INT(pval));
2256                 return pval;
2257         }
2258 }
2259
2260 int
2261 RubyDialog_getFlexFlags(RubyValue self, RDItem *ip)
2262 {
2263         int status;
2264         VALUE rval;
2265         void *args[3] = { (void *)self, (void *)ip, NULL };
2266         rval = rb_protect(s_RubyDialog_getFlexFlags, (VALUE)args, &status);
2267         if (status != 0)
2268                 return -1;
2269         else if (rval == Qnil)
2270                 return -1;
2271         else return (int)args[2];
2272 }
2273
2274 static VALUE
2275 s_RubyDialog_doCloseWindow(VALUE val)
2276 {
2277         void **values = (void **)val;
2278         VALUE self = (VALUE)values[0];
2279         int isModal = (int)values[1];
2280         if (isModal) {
2281                 rb_funcall(self, rb_intern("end_modal"), 1, INT2NUM(1));
2282                 return Qnil;
2283         } else {
2284                 VALUE rval;
2285                 VALUE actval = rb_iv_get(self, "@on_close");
2286                 if (actval != Qnil) {
2287                         if (TYPE(actval) == T_SYMBOL)
2288                                 rval = rb_funcall(self, SYM2ID(actval), 0);
2289                         else
2290                                 rval = rb_funcall(actval, rb_intern("call"), 0);
2291                 } else rval = Qtrue;
2292                 if (RTEST(rval)) {
2293                         if (rb_ary_includes(gRubyDialogList, self) == Qtrue)
2294                                 rb_ary_delete(gRubyDialogList, self);
2295                         RubyDialogCallback_destroy(s_RubyDialog_GetController(self));
2296                         s_RubyDialog_Forget(self);
2297                 }
2298                 return rval;
2299         }
2300 }
2301
2302 /*  Handle close box.  Invokes Dialog.end_modal or Dialog.close in Ruby world  */
2303 void
2304 RubyDialog_doCloseWindow(RubyValue self, int isModal)
2305 {
2306         int status;
2307         VALUE rval;
2308         void *args[2] = { (void *)(uintptr_t)self, (void *)(intptr_t)isModal };
2309         rval = rb_protect(s_RubyDialog_doCloseWindow, (VALUE)args, &status);
2310         if (status != 0) {
2311                 Ruby_showError(status);
2312         }
2313 }
2314
2315 #pragma mark ====== Device Context Methods ======
2316
2317 static RDDeviceContext *
2318 s_RubyDialog_GetDeviceContext(VALUE self)
2319 {
2320         if (rb_obj_is_kind_of(self, rb_cDialog)) {
2321                 RubyDialog *dref = s_RubyDialog_GetController(self);
2322                 return RubyDialogCallback_getDeviceContextForRubyDialog(dref);
2323         } else if (rb_obj_is_kind_of(self, rb_cBitmap)) {
2324                 RDBitmap *bitmap;
2325                 Data_Get_Struct(self, RDBitmap, bitmap);
2326                 return RubyDialogCallback_getDeviceContextForBitmap(bitmap);
2327         } else {
2328                 rb_raise(rb_eDialogError, "No graphic device context is available");
2329                 return NULL;  /* Not reached */
2330         }
2331 }
2332
2333 /*
2334  *  call-seq:
2335  *     clear
2336  *
2337  *  Clear the drawing context.
2338  */
2339 static VALUE
2340 s_RubyDialog_Clear(VALUE self)
2341 {
2342         RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2343         RubyDialogCallback_clear(dc);
2344         return Qnil;
2345 }
2346
2347 /*
2348  *  call-seq:
2349  *     draw_ellipse(x1, y1, radius1 [, radius2])
2350  *
2351  *  Draw an ellipse in the graphic context.
2352  */
2353 static VALUE
2354 s_RubyDialog_DrawEllipse(int argc, VALUE *argv, VALUE self)
2355 {
2356         VALUE xval, yval, rval1, rval2;
2357         RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2358         rb_scan_args(argc, argv, "31", &xval, &yval, &rval1, &rval2);
2359         if (rval2 == Qnil)
2360                 rval2 = rval1;
2361         RubyDialogCallback_drawEllipse(dc, NUM2DBL(rb_Float(xval)), NUM2DBL(rb_Float(yval)), NUM2DBL(rb_Float(rval1)), NUM2DBL(rb_Float(rval2)));
2362         return Qnil;
2363 }
2364
2365 /*
2366  *  call-seq:
2367  *     draw_line(x1, y1, x2, y2, ...)
2368  *     draw_line([x1, y1], [x2, y2], ...)
2369  *     draw_line([x1, y1, x2, y2, ...])
2370  *
2371  *  Draw a series of line segments in the graphic context.
2372  */
2373 static VALUE
2374 s_RubyDialog_DrawLine(int argc, VALUE *argv, VALUE self)
2375 {
2376         float *coords;
2377         int ncoords, i;
2378         RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2379         if (argc == 0)
2380                 return Qnil;
2381         if (rb_obj_is_kind_of(argv[0], rb_mEnumerable)) {
2382                 VALUE aval = rb_ary_to_ary(argv[0]);
2383                 if (RARRAY_LEN(aval) == 2) {
2384                         /*  The second form  */
2385                         ncoords = argc;
2386                         if (ncoords < 2)
2387                                 rb_raise(rb_eDialogError, "Too few coordinates are given (requires at least two points)");
2388                         coords = (float *)calloc(sizeof(float), ncoords * 2);
2389                         coords[0] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[0]));
2390                         coords[1] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[1]));
2391                         for (i = 1; i < ncoords; i++) {
2392                                 aval = rb_ary_to_ary(argv[i]);
2393                                 if (RARRAY_LEN(aval) < 2)
2394                                         rb_raise(rb_eDialogError, "The coordinate should be an array of two numerics");
2395                                 coords[i * 2] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[0]));
2396                                 coords[i * 2 + 1] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[1]));
2397                         }
2398                 } else {
2399                         /*  The third form  */
2400                         if (RARRAY_LEN(aval) % 2 == 1)
2401                                 rb_raise(rb_eDialogError, "An odd number of numerics are given; the coordinate values should be given in pairs");
2402                         ncoords = RARRAY_LEN(aval) / 2;
2403                         if (ncoords < 2)
2404                                 rb_raise(rb_eDialogError, "Too few coordinates are given (requires at least two points)");
2405                         coords = (float *)calloc(sizeof(float), ncoords * 2);
2406                         for (i = 0; i < ncoords * 2; i++) {
2407                                 coords[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
2408                         }
2409                 }
2410         } else {
2411                 /*  The first form  */
2412                 ncoords = argc / 2;
2413                 if (ncoords < 2)
2414                         rb_raise(rb_eDialogError, "Too few coordinates are given (requires at least two points)");
2415                 if (argc % 2 == 1)
2416                         rb_raise(rb_eDialogError, "An odd number of numerics are given; the coordinate values should be given in pairs");
2417                 coords = (float *)calloc(sizeof(float), ncoords * 2);
2418                 for (i = 0; i < ncoords * 2; i++) {
2419                         coords[i] = NUM2DBL(rb_Float(argv[i]));
2420                 }
2421         }
2422         RubyDialogCallback_drawLine(dc, ncoords, coords);
2423         return Qnil;
2424 }
2425
2426 /*
2427  *  call-seq:
2428  *     draw_rectangle(ary [, round])
2429  *     draw_rectangle(x, y, width, height [, round])
2430  *
2431  *  Draw a rectangle in the graphic context. If the first argument is an array, it should contain
2432  *  four numerics [x, y, width, height]. If round is given and positive, 
2433  *  then draw a rounded rectangle with the given radius.
2434  */
2435 static VALUE
2436 s_RubyDialog_DrawRectangle(int argc, VALUE *argv, VALUE self)
2437 {
2438         VALUE xval, yval, wval, hval, rval;
2439         RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2440         float r;
2441         if (argc >= 4) {
2442                 rb_scan_args(argc, argv, "41", &xval, &yval, &wval, &hval, &rval);
2443         } else {
2444                 rb_scan_args(argc, argv, "11", &xval, &rval);
2445                 xval = rb_ary_to_ary(xval);
2446                 if (RARRAY_LEN(xval) < 4)
2447                         rb_raise(rb_eDialogError, "The dimension of rectangle should be given as four numerics (x, y, width, height) or an array of four numerics.");
2448                 hval = RARRAY_PTR(xval)[3];
2449                 wval = RARRAY_PTR(xval)[2];
2450                 yval = RARRAY_PTR(xval)[1];
2451                 xval = RARRAY_PTR(xval)[0];
2452         }
2453         if (rval == Qnil)
2454                 r = 0.0;
2455         else r = NUM2DBL(rb_Float(rval));
2456         RubyDialogCallback_drawRectangle(dc, NUM2DBL(rb_Float(xval)), NUM2DBL(rb_Float(yval)), NUM2DBL(rb_Float(wval)), NUM2DBL(rb_Float(hval)), r);
2457         return Qnil;
2458 }
2459
2460
2461 /*
2462  *  call-seq:
2463  *     draw_text(string, x, y)
2464  *
2465  *  Draw a string in the graphic context.
2466  */
2467 static VALUE
2468 s_RubyDialog_DrawText(VALUE self, VALUE sval, VALUE xval, VALUE yval)
2469 {
2470         RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2471         const char *s = EncodedStringValuePtr(sval);
2472         float x = NUM2DBL(rb_Float(xval));
2473         float y = NUM2DBL(rb_Float(yval));
2474         RubyDialogCallback_drawText(dc, s, x, y);
2475         return Qnil;
2476 }
2477
2478 /*
2479  *  call-seq:
2480  *     font(hash)
2481  *     font(nil)
2482  *
2483  *  Set the default font for the graphic context (not for the dialog!).
2484  *  If the argument is nil, then the default font is set.
2485  *  Otherwise, the font is set according to the attributes in the given hash.
2486  *  The following keys are implemented: :size (font size),
2487  *  :family (:default, :roman, :swiss, :fixed), :style (:normal, :italic, :slant),
2488  *  :weight (:medium, :light, :bold)
2489  */
2490 static VALUE
2491 s_RubyDialog_Font(int argc, VALUE *argv, VALUE self)
2492 {
2493         VALUE hval;
2494         RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2495         rb_scan_args(argc, argv, "01", &hval);
2496         if (hval == Qnil)
2497                 RubyDialogCallback_setFont(dc, NULL);
2498         else {
2499                 void **args;
2500                 int i, j;
2501                 VALUE keys = rb_funcall(hval, rb_intern("keys"), 0);
2502                 float width;
2503                 args = (void **)calloc(sizeof(void *), RARRAY_LEN(keys) * 2 + 1);
2504                 for (i = 0; i < RARRAY_LEN(keys); i++) {
2505                         VALUE kval = RARRAY_PTR(keys)[i];
2506                         VALUE aval = rb_hash_aref(hval, RARRAY_PTR(keys)[i]);
2507                         if (kval == sSizeSymbol) {
2508                                 width = NUM2DBL(rb_Float(aval));
2509                                 args[i * 2] = "size";
2510                                 args[i * 2 + 1] = &width;
2511                                 args[i * 2 + 2] = NULL;
2512                         } else if (kval == sStyleSymbol) {
2513                                 if (aval == sNormalSymbol)
2514                                         j = 0;
2515                                 else if (aval == sItalicSymbol)
2516                                         j = 1;
2517                                 else if (aval == sSlantSymbol)
2518                                         j = 2;
2519                                 else j = 0;
2520                                 args[i * 2] = "style";
2521                                 args[i * 2 + 1] = (void *)(intptr_t)j;
2522                                 args[i * 2 + 2] = NULL;
2523                         } else if (kval == sWeightSymbol) {
2524                                 if (aval == sMediumSymbol)
2525                                         j = 0;
2526                                 else if (aval == sLightSymbol)
2527                                         j = 1;
2528                                 else if (aval == sBoldSymbol)
2529                                         j = 2;
2530                                 else j = 0;
2531                                 args[i * 2] = "weight";
2532                                 args[i * 2 + 1] = (void *)(intptr_t)j;
2533                                 args[i * 2 + 2] = NULL;
2534                         } else if (kval == sFamilySymbol) {
2535                                 if (aval == sDefaultSymbol)
2536                                         j = 0;
2537                                 else if (aval == sRomanSymbol)
2538                                         j = 1;
2539                                 else if (aval == sSwissSymbol)
2540                                         j = 2;
2541                                 else if (aval == sFixedSymbol)
2542                                         j = 3;
2543                                 else j = 0;
2544                                 args[i * 2] = "family";
2545                                 args[i * 2 + 1] = (void *)(intptr_t)j;
2546                                 args[i * 2 + 2] = NULL;
2547                         }
2548                 }
2549                 RubyDialogCallback_setFont(dc, args);
2550                 free(args);
2551         }
2552         return Qnil;
2553 }
2554
2555 /*
2556  *  call-seq:
2557  *     pen(hash)
2558  *     pen(nil)
2559  *
2560  *  Set the drawing pen for the graphic context. If the argument is nil, then
2561  *  null pen is set. Otherwise, an appropriate pen is created from the given hash.
2562  *  The following keys are implemented: :color (foreground color), :width (pen width),
2563  *  :style (:solid, :transparent, :dot, :long_dash, :short_dash, :dot_dash)
2564  */
2565 static VALUE
2566 s_RubyDialog_Pen(int argc, VALUE *argv, VALUE self)
2567 {
2568         VALUE hval;
2569         RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2570         rb_scan_args(argc, argv, "01", &hval);
2571         if (hval == Qnil)
2572                 RubyDialogCallback_setPen(dc, NULL);
2573         else {
2574                 void **args;
2575                 int i, j;
2576                 VALUE keys = rb_funcall(hval, rb_intern("keys"), 0);
2577                 float forecolor[4], width;
2578                 args = (void **)calloc(sizeof(void *), RARRAY_LEN(keys) * 2 + 1);
2579                 for (i = 0; i < RARRAY_LEN(keys); i++) {
2580                         VALUE kval = RARRAY_PTR(keys)[i];
2581                         VALUE aval = rb_hash_aref(hval, RARRAY_PTR(keys)[i]);
2582                         if (kval == sForeColorSymbol || kval == sColorSymbol) {
2583                                 aval = rb_ary_to_ary(aval);
2584                                 for (j = 0; j < RARRAY_LEN(aval) && j < 4; j++) {
2585                                         forecolor[j] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[j]));
2586                                 }
2587                                 if (j < 4) {
2588                                         for (; j < 3; j++)
2589                                                 forecolor[j] = 0.0;
2590                                         forecolor[3] = 1.0;
2591                                 }
2592                                 args[i * 2] = "color";
2593                                 args[i * 2 + 1] = forecolor;
2594                                 args[i * 2 + 2] = NULL;
2595                         } else if (kval == sWidthSymbol) {
2596                                 width = NUM2DBL(rb_Float(aval));
2597                                 args[i * 2] = "width";
2598                                 args[i * 2 + 1] = &width;
2599                                 args[i * 2 + 2] = NULL;
2600                         } else if (kval == sStyleSymbol) {
2601                                 if (aval == sSolidSymbol)
2602                                         j = 0;
2603                                 else if (aval == sTransparentSymbol)
2604                                         j = 1;
2605                                 else if (aval == sDotSymbol)
2606                                         j = 2;
2607                                 else if (aval == sLongDashSymbol)
2608                                         j = 3;
2609                                 else if (aval == sShortDashSymbol)
2610                                         j = 4;
2611                                 else if (aval == sDotDashSymbol)
2612                                         j = 5;
2613                                 else j = 0;
2614                                 args[i * 2] = "style";
2615                                 args[i * 2 + 1] = (void *)(intptr_t)j;
2616                                 args[i * 2 + 2] = NULL;
2617                         }
2618                 }
2619                 RubyDialogCallback_setPen(dc, args);
2620                 free(args);
2621         }
2622         return Qnil;
2623 }
2624
2625 /*
2626  *  call-seq:
2627  *     brush(:color=>[r, g, b, a])
2628  *     brush(nil)
2629  *
2630  *  Set the painting brush for the graphic context. If the argument is nil, then
2631  *  null brush is set. Otherwise, an appropriate brush is created from the given hash.
2632  *  (Currently only foreground color is implemented.)
2633  */
2634 static VALUE
2635 s_RubyDialog_Brush(int argc, VALUE *argv, VALUE self)
2636 {
2637         VALUE hval;
2638         RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2639         rb_scan_args(argc, argv, "01", &hval);
2640         if (hval == Qnil)
2641                 RubyDialogCallback_setBrush(dc, NULL);
2642         else {
2643                 void **args;
2644                 int i, j;
2645                 VALUE keys = rb_funcall(hval, rb_intern("keys"), 0);
2646                 float forecolor[4];
2647                 args = (void **)malloc(sizeof(void *) * (RARRAY_LEN(keys) * 2 + 1));
2648                 for (i = 0; i < RARRAY_LEN(keys); i++) {
2649                         VALUE kval = RARRAY_PTR(keys)[i];
2650                         VALUE aval = rb_hash_aref(hval, RARRAY_PTR(keys)[i]);
2651                         if (kval == sForeColorSymbol || kval == sColorSymbol) {
2652                                 aval = rb_ary_to_ary(aval);
2653                                 for (j = 0; j < RARRAY_LEN(aval) && j < 4; j++) {
2654                                         forecolor[j] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[j]));
2655                                 }
2656                                 if (j < 4) {
2657                                         for (; j < 3; j++)
2658                                                 forecolor[j] = 0.0;
2659                                         forecolor[3] = 1.0;
2660                                 }
2661                                 args[i * 2] = "color";
2662                                 args[i * 2 + 1] = forecolor;
2663                                 args[i * 2 + 2] = NULL;
2664                         }
2665                 }
2666                 RubyDialogCallback_setBrush(dc, args);
2667                 free(args);
2668         }
2669         return Qnil;
2670 }
2671
2672 #pragma mark ====== Bitmap methods ======
2673
2674 static VALUE
2675 s_Bitmap_Alloc(VALUE klass)
2676 {
2677         return Data_Wrap_Struct(klass, NULL, (RUBY_DATA_FUNC)RubyDialogCallback_releaseBitmap, NULL);
2678 }
2679
2680 static VALUE
2681 s_Bitmap_Initialize(int argc, VALUE *argv, VALUE self)
2682 {
2683         VALUE wval, hval, dval;
2684         int width, height, depth;
2685         RDBitmap *bitmap;
2686         rb_scan_args(argc, argv, "21", &wval, &hval, &dval);
2687         if (dval == Qnil)
2688                 depth = 32;
2689         else depth = NUM2INT(rb_Integer(dval));
2690         width = NUM2INT(rb_Integer(wval));
2691         height = NUM2INT(rb_Integer(hval));
2692         if (width <= 0 || width >= 32768)
2693                 rb_raise(rb_eDialogError, "Bitmap width (%d) is out of range (1..32767)", width);
2694         if (height <= 0 || height >= 32768)
2695                 rb_raise(rb_eDialogError, "Bitmap height (%d) is out of range (1..32767)", height);
2696         if (depth != 32)
2697                 rb_raise(rb_eDialogError, "Only depth = 32 is supported currently");
2698         bitmap = RubyDialogCallback_createBitmap(width, height, depth);
2699         DATA_PTR(self) = bitmap;
2700         return self;
2701 }
2702
2703 static VALUE
2704 s_Bitmap_SetFocusCallbackSub(VALUE v)
2705 {
2706         return rb_obj_instance_eval(0, NULL, v);
2707 }
2708
2709 static void
2710 s_Bitmap_SetFocusCallback(void *p)
2711 {
2712         int status;
2713         VALUE *vp = (VALUE *)p;
2714         vp[1] = rb_protect(s_Bitmap_SetFocusCallbackSub, vp[0], &status);
2715         vp[2] = (VALUE)status;
2716 }
2717
2718 static VALUE
2719 s_Bitmap_FocusExec(int argc, VALUE *argv, VALUE self)
2720 {
2721         VALUE v[3];
2722         RDBitmap *bitmap;
2723         v[0] = self;
2724         Data_Get_Struct(self, RDBitmap, bitmap);
2725         RubyDialogCallback_executeWithFocusOnBitmap(bitmap, s_Bitmap_SetFocusCallback, v);
2726         if ((int)v[2] != 0)
2727                 rb_jump_tag((int)v[2]);
2728         return self;
2729 }
2730
2731 static VALUE
2732 s_Bitmap_SaveToFile(VALUE self, VALUE fnval)
2733 {
2734         RDBitmap *bitmap;
2735         const char *fn = FileStringValuePtr(fnval);
2736         Data_Get_Struct(self, RDBitmap, bitmap);
2737         if (RubyDialogCallback_saveBitmapToFile(bitmap, fn))
2738                 return self;
2739         else return Qnil;
2740 }
2741
2742
2743 #pragma mark ====== Initialize class ======
2744
2745 void
2746 RubyDialogInitClass(void)
2747 {
2748         VALUE parent;
2749         if (rb_cDialog != Qfalse)
2750                 return;
2751         parent = (VALUE)RubyDialogCallback_parentModule();
2752         if (parent != Qfalse)
2753                 rb_cDialog = rb_define_class_under(parent, "Dialog", rb_cObject);
2754         else
2755                 rb_cDialog = rb_define_class("Dialog", rb_cObject);
2756         rb_define_alloc_func(rb_cDialog, s_RubyDialog_Alloc);
2757         rb_define_private_method(rb_cDialog, "initialize", s_RubyDialog_Initialize, -1);
2758         rb_define_method(rb_cDialog, "run", s_RubyDialog_Run, 0);
2759         rb_define_method(rb_cDialog, "item", s_RubyDialog_Item, -1);
2760         rb_define_method(rb_cDialog, "item_at_index", s_RubyDialog_ItemAtIndex, 1);
2761         rb_define_method(rb_cDialog, "item_with_tag", s_RubyDialog_ItemWithTag, 1);
2762         rb_define_method(rb_cDialog, "layout", s_RubyDialog_Layout, -1);
2763         rb_define_method(rb_cDialog, "_items", s_RubyDialog_Items, 0);
2764         rb_define_method(rb_cDialog, "nitems", s_RubyDialog_Nitems, 0);
2765         rb_define_method(rb_cDialog, "each_item", s_RubyDialog_EachItem, 0);
2766         rb_define_method(rb_cDialog, "set_attr", s_RubyDialog_SetAttr, -1);
2767         rb_define_method(rb_cDialog, "attr", s_RubyDialog_Attr, 2);
2768         rb_define_method(rb_cDialog, "radio_group", s_RubyDialog_RadioGroup, -2);
2769         rb_define_method(rb_cDialog, "action", s_RubyDialog_Action, 1);
2770         rb_define_method(rb_cDialog, "end_modal", s_RubyDialog_EndModal, -1);
2771         rb_define_method(rb_cDialog, "show", s_RubyDialog_Show, 0);
2772         rb_define_method(rb_cDialog, "hide", s_RubyDialog_Hide, 0);
2773         rb_define_method(rb_cDialog, "active?", s_RubyDialog_IsActive, 0);
2774         rb_define_method(rb_cDialog, "close", s_RubyDialog_Close, 0);
2775         rb_define_method(rb_cDialog, "start_timer", s_RubyDialog_StartTimer, -1);
2776         rb_define_method(rb_cDialog, "stop_timer", s_RubyDialog_StopTimer, 0);
2777         rb_define_method(rb_cDialog, "on_key", s_RubyDialog_OnKey, -1);
2778         rb_define_method(rb_cDialog, "set_size", s_RubyDialog_SetSize, -1);
2779         rb_define_method(rb_cDialog, "size", s_RubyDialog_Size, 0);
2780         rb_define_method(rb_cDialog, "set_min_size", s_RubyDialog_SetMinSize, -1);
2781         rb_define_method(rb_cDialog, "min_size", s_RubyDialog_MinSize, 0);
2782 /*      rb_define_method(rb_cDialog, "listen", s_RubyDialog_Listen, 3); */
2783         rb_define_singleton_method(rb_cDialog, "save_panel", s_RubyDialog_SavePanel, -1);
2784         rb_define_singleton_method(rb_cDialog, "open_panel", s_RubyDialog_OpenPanel, -1);
2785
2786         if (parent != Qfalse)
2787                 rb_cDialogItem = rb_define_class_under(parent, "DialogItem", rb_cObject);
2788         else
2789                 rb_cDialogItem = rb_define_class("DialogItem", rb_cObject);
2790
2791         rb_define_method(rb_cDialogItem, "[]=", s_RubyDialogItem_SetAttr, 2);
2792         rb_define_method(rb_cDialogItem, "[]", s_RubyDialogItem_Attr, 1);
2793         rb_define_alias(rb_cDialogItem, "set_attr", "[]=");
2794         rb_define_alias(rb_cDialogItem, "attr", "[]");
2795         rb_define_method(rb_cDialogItem, "append_string", s_RubyDialogItem_AppendString, 1);
2796         rb_define_method(rb_cDialogItem, "refresh_rect", s_RubyDialogItem_RefreshRect, -1);
2797         
2798         if (parent != Qfalse)
2799                 rb_mDeviceContext = rb_define_module_under(parent, "DeviceContext");
2800         else
2801                 rb_mDeviceContext = rb_define_module("DeviceContext");
2802
2803         rb_define_method(rb_mDeviceContext, "clear", s_RubyDialog_Clear, 0);
2804         rb_define_method(rb_mDeviceContext, "draw_ellipse", s_RubyDialog_DrawEllipse, -1);
2805         rb_define_method(rb_mDeviceContext, "draw_line", s_RubyDialog_DrawLine, -1);
2806         rb_define_method(rb_mDeviceContext, "draw_polygon", s_RubyDialog_DrawLine, -1);
2807         rb_define_method(rb_mDeviceContext, "draw_rectangle", s_RubyDialog_DrawRectangle, -1);
2808         rb_define_method(rb_mDeviceContext, "draw_text", s_RubyDialog_DrawText, 3);
2809         rb_define_method(rb_mDeviceContext, "font", s_RubyDialog_Font, -1);
2810         rb_define_method(rb_mDeviceContext, "pen", s_RubyDialog_Pen, -1);
2811         rb_define_method(rb_mDeviceContext, "brush", s_RubyDialog_Brush, -1);
2812
2813     rb_include_module(rb_cDialog, rb_mDeviceContext);
2814         
2815         if (parent != Qfalse)
2816                 rb_cBitmap = rb_define_class_under(parent, "Bitmap", rb_cObject);
2817         else
2818                 rb_cBitmap = rb_define_class("Bitmap", rb_cObject);
2819
2820         rb_include_module(rb_cBitmap, rb_mDeviceContext);
2821         rb_define_alloc_func(rb_cBitmap, s_Bitmap_Alloc);
2822         rb_define_private_method(rb_cBitmap, "initialize", s_Bitmap_Initialize, -1);
2823         rb_define_method(rb_cBitmap, "focus_exec", s_Bitmap_FocusExec, -1);
2824         rb_define_method(rb_cBitmap, "save_to_file", s_Bitmap_SaveToFile, 1);
2825         
2826         if (parent != Qfalse)
2827                 rb_eDialogError = rb_define_class_under(parent, "DialogError", rb_eStandardError);
2828         else
2829                 rb_eDialogError = rb_define_class("DialogError", rb_eStandardError);
2830
2831         {
2832                 static VALUE *sTable1[] = {
2833                         &sTextSymbol, &sTextFieldSymbol, &sRadioSymbol, &sButtonSymbol,
2834                         &sCheckBoxSymbol, &sToggleButtonSymbol, &sPopUpSymbol, &sTextViewSymbol,
2835                         &sViewSymbol, &sTableSymbol,
2836                         &sResizableSymbol, &sHasCloseBoxSymbol,
2837                         &sDialogSymbol, &sIndexSymbol, &sLineSymbol, &sTagSymbol,
2838                         &sTypeSymbol, &sTitleSymbol, &sXSymbol, &sYSymbol,
2839                         &sWidthSymbol, &sHeightSymbol, &sOriginSymbol, &sSizeSymbol,
2840                         &sFrameSymbol, &sEnabledSymbol, &sEditableSymbol, &sHiddenSymbol,
2841                         &sValueSymbol, &sRadioGroupSymbol, &sBlockSymbol, &sRangeSymbol,
2842                         &sActionSymbol, &sAlignSymbol, &sRightSymbol, &sCenterSymbol,
2843                         &sVerticalAlignSymbol, &sBottomSymbol, &sMarginSymbol, &sPaddingSymbol,
2844                         &sSubItemsSymbol, &sHFillSymbol, &sVFillSymbol, &sFlexSymbol,
2845                         &sIsProcessingActionSymbol,
2846                         &sColorSymbol, &sForeColorSymbol, &sBackColorSymbol,
2847                         &sStyleSymbol, &sSolidSymbol, &sTransparentSymbol, &sDotSymbol,
2848                         &sLongDashSymbol, &sShortDashSymbol, &sDotDashSymbol,
2849                         &sFontSymbol, &sFamilySymbol, &sWeightSymbol,
2850                         &sDefaultSymbol, &sRomanSymbol, &sSwissSymbol,
2851                         &sFixedSymbol, &sNormalSymbol, &sSlantSymbol, &sItalicSymbol,
2852                         &sMediumSymbol, &sBoldSymbol, &sLightSymbol,
2853                         &sOnPaintSymbol,
2854                         &sOnCountSymbol, &sOnGetValueSymbol, &sOnSetValueSymbol, &sOnSelectionChangedSymbol,
2855                         &sOnSetColorSymbol, &sIsItemEditableSymbol, &sIsDragAndDropEnabledSymbol, &sOnDragSelectionToRowSymbol,
2856                         &sSelectionSymbol, &sColumnsSymbol, &sRefreshSymbol, &sHasPopUpMenuSymbol, &sOnPopUpMenuSelectedSymbol
2857                 };
2858                 static const char *sTable2[] = {
2859                         "text", "textfield", "radio", "button",
2860                         "checkbox", "togglebutton", "popup", "textview",
2861                         "view", "table",
2862                         "resizable", "has_close_box",
2863                         "dialog", "index", "line", "tag",
2864                         "type", "title", "x", "y",
2865                         "width", "height", "origin", "size",
2866                         "frame", "enabled", "editable", "hidden",
2867                         "value", "radio_group", "block", "range",
2868                         "action", "align", "right", "center",
2869                         "vertical_align", "bottom", "margin", "padding",
2870                         "subitems", "hfill", "vfill", "flex",
2871                         "is_processing_action",
2872                         "color", "foreground_color", "background_color",
2873                         "style", "solid", "transparent", "dot",
2874                         "long_dash", "short_dash", "dot_dash",
2875                         "font", "family", "weight",
2876                         "default", "roman", "swiss",
2877                         "fixed", "normal", "slant", "italic",
2878                         "medium", "bold", "light",
2879                         "on_paint",
2880                         "on_count", "on_get_value", "on_set_value", "on_selection_changed",
2881                         "on_set_color", "is_item_editable", "is_drag_and_drop_enabled", "on_drag_selection_to_row",
2882                         "selection", "columns", "refresh", "has_popup_menu", "on_popup_menu_selected"
2883                 };
2884                 int i;
2885                 for (i = 0; i < sizeof(sTable1) / sizeof(sTable1[0]); i++)
2886                         *(sTable1[i]) = ID2SYM(rb_intern(sTable2[i]));
2887         }
2888         
2889         /*  Global variable to hold open dialogs */
2890         rb_define_variable("$ruby_dialogs", &gRubyDialogList);
2891         gRubyDialogList = rb_ary_new();
2892 }