Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Ruby_bindings/ruby_dialog.c

Parent Directory Parent Directory | Revision Log Revision Log


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