4 * Created by Toshi Nagata on 08/04/13.
5 * Copyright 2008 Toshi Nagata. All rights reserved.
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.
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.
19 #include <math.h> /* For floor() */
20 #include "ruby_dialog.h"
22 /* Encoded version of rb_str_new2() (defined in ruby_bind.c) */
23 extern VALUE Ruby_NewEncodedStringValue2(const char *str);
26 sTextSymbol, sTextFieldSymbol, sRadioSymbol, sButtonSymbol,
27 sCheckBoxSymbol, sToggleButtonSymbol, sPopUpSymbol, sTextViewSymbol,
28 sViewSymbol, sLineSymbol, sTagSymbol, sTypeSymbol, sTitleSymbol,
29 sRadioGroupSymbol, sTableSymbol,
30 sResizableSymbol, sHasCloseBoxSymbol,
31 sDialogSymbol, sIndexSymbol,
32 sXSymbol, sYSymbol, sWidthSymbol, sHeightSymbol,
33 sOriginSymbol, sSizeSymbol, sFrameSymbol,
34 sEnabledSymbol, sEditableSymbol, sHiddenSymbol, sValueSymbol,
35 sBlockSymbol, sRangeSymbol, sActionSymbol,
36 sAlignSymbol, sRightSymbol, sCenterSymbol,
37 sVerticalAlignSymbol, sBottomSymbol,
38 sMarginSymbol, sPaddingSymbol, sSubItemsSymbol,
39 sHFillSymbol, sVFillSymbol, sFlexSymbol,
40 sIsProcessingActionSymbol,
41 sColorSymbol, sForeColorSymbol, sBackColorSymbol,
42 sStyleSymbol, sSolidSymbol, sTransparentSymbol, sDotSymbol,
43 sLongDashSymbol, sShortDashSymbol, sDotDashSymbol,
44 sFontSymbol, sFamilySymbol, sWeightSymbol,
45 sDefaultSymbol, sRomanSymbol, sSwissSymbol, sFixedSymbol,
46 sNormalSymbol, sSlantSymbol, sItalicSymbol,
47 sMediumSymbol, sBoldSymbol, sLightSymbol,
49 /* Data source for Table (= MyListCtrl) */
50 sOnCountSymbol, sOnGetValueSymbol, sOnSetValueSymbol, sOnSelectionChangedSymbol,
51 sOnSetColorSymbol, sIsItemEditableSymbol, sIsDragAndDropEnabledSymbol, sOnDragSelectionToRowSymbol,
52 sSelectionSymbol, sColumnsSymbol, sRefreshSymbol, sHasPopUpMenuSymbol, sOnPopUpMenuSelectedSymbol;
54 VALUE rb_cDialog = Qfalse;
55 VALUE rb_cDialogItem = Qfalse;
56 VALUE rb_mDeviceContext = Qfalse;
57 VALUE rb_cBitmap = Qfalse;
58 VALUE rb_eDialogError = Qfalse;
59 VALUE gRubyDialogList = Qnil;
61 const RDPoint gZeroPoint = {0, 0};
62 const RDSize gZeroSize = {0, 0};
63 const RDRect gZeroRect = {{0, 0}, {0, 0}};
65 /* True if y-coordinate grows from bottom to top (like Cocoa) */
66 int gRubyDialogIsFlipped = 0;
68 #pragma mark ====== External utility functions and macros ======
70 #define FileStringValuePtr(val) Ruby_FileStringValuePtr(&val)
71 extern char *Ruby_FileStringValuePtr(VALUE *valp);
72 extern VALUE Ruby_NewFileStringValue(const char *fstr);
74 #define EncodedStringValuePtr(val) Ruby_EncodedStringValuePtr(&val)
75 extern char *Ruby_EncodedStringValuePtr(VALUE *valp);
76 extern VALUE Ruby_NewEncodedStringValue(const char *str, int len);
78 extern VALUE Ruby_SetInterruptFlag(VALUE val);
79 extern VALUE Ruby_ObjectAtIndex(VALUE ary, int idx);
80 extern VALUE ValueFromIntGroup(IntGroup *ig);
81 extern IntGroup *IntGroupFromValue(VALUE val);
83 #pragma mark ====== Dialog alloc/init/release ======
85 typedef struct RubyDialogInfo {
86 RubyDialog *dref; /* Reference to a platform-dependent "controller" object */
90 s_RubyDialog_GetController(VALUE self)
93 Data_Get_Struct(self, RubyDialogInfo, di);
99 /* Deallocate handler: may be called when the Ruby object is deallocated while the RubyDialogFrame is not
102 s_RubyDialog_Release(void *p)
105 RubyDialog *dref = ((RubyDialogInfo *)p)->dref;
107 RubyDialogCallback_setRubyObject(dref, Qfalse); /* Stop access to the Ruby object (in case the RubyDialogController is not dealloc'ed in the following line) */
108 RubyDialogCallback_release(dref);
109 ((RubyDialogInfo *)p)->dref = NULL;
115 /* Deallocate the RubyDialogFrame while the Ruby object is still alive */
117 s_RubyDialog_Forget(VALUE self)
120 Data_Get_Struct(self, RubyDialogInfo, di);
122 if (di->dref != NULL) {
123 /* Unregister all messages */
124 /* RubyDialogCallback_Listen(di->dref, NULL, NULL, NULL, NULL, NULL); */
131 s_RubyDialog_Alloc(VALUE klass)
135 // RubyDialog *dref = RubyDialogCallback_new();
136 val = Data_Make_Struct(klass, RubyDialogInfo, 0, s_RubyDialog_Release, di);
138 // RubyDialogCallback_setRubyObject(dref, (RubyValue)val);
142 #pragma mark ====== Set/get item attributes ======
146 * set_attr(key, value) -> value
149 * Set the attributes.
152 s_RubyDialogItem_SetAttr(VALUE self, VALUE key, VALUE val)
155 VALUE dialog_val, type;
160 dialog_val = rb_ivar_get(self, SYM2ID(sDialogSymbol));
161 itag = NUM2INT(rb_ivar_get(self, SYM2ID(sIndexSymbol)));
162 type = rb_ivar_get(self, SYM2ID(sTypeSymbol));
163 if (dialog_val == Qnil || (dref = s_RubyDialog_GetController(dialog_val)) == NULL)
164 rb_raise(rb_eDialogError, "The dialog item does not belong to any dialog (internal error?)");
165 view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
166 key_id = SYM2ID(key);
167 if (key == sRangeSymbol) {
168 /* Range of value (must be an array of two integers or two floats) */
171 if (TYPE(val) != T_ARRAY || RARRAY_LEN(val) != 2)
172 rb_raise(rb_eTypeError, "the attribute 'range' should specify an array of two numbers");
173 val1 = (RARRAY_PTR(val))[0];
174 val2 = (RARRAY_PTR(val))[1];
175 d1 = NUM2DBL(rb_Float(val1));
176 d2 = NUM2DBL(rb_Float(val2));
177 if (!FIXNUM_P(val1) || !FIXNUM_P(val2)) {
178 /* Convert to a range of floats */
179 if (TYPE(val1) != T_FLOAT || TYPE(val2) != T_FLOAT) {
180 val1 = rb_float_new(NUM2DBL(val1));
181 val2 = rb_float_new(NUM2DBL(val2));
182 val = rb_ary_new3(2, val1, val2);
186 rb_raise(rb_eArgError, "invalid number range [%g,%g]", d1, d2);
187 rb_ivar_set(self, key_id, val);
188 } else if (key == sValueSymbol) {
190 if (type == sTextFieldSymbol || type == sTextViewSymbol) {
191 RubyDialogCallback_setStringToItem(view, (val == Qnil ? "" : EncodedStringValuePtr(val)));
192 } else if (type == sPopUpSymbol) {
193 RubyDialogCallback_setSelectedSubItem(view, (val == Qnil ? 0 : NUM2INT(rb_Integer(val))));
194 } else if (type == sCheckBoxSymbol || type == sRadioSymbol || type == sToggleButtonSymbol) {
195 RubyDialogCallback_setStateForItem(view, (val == Qnil ? 0 : NUM2INT(rb_Integer(val))));
197 } else if (key == sTitleSymbol) {
199 char *p = EncodedStringValuePtr(val);
200 if (type == sTextSymbol)
201 RubyDialogCallback_setStringToItem(view, p);
202 else RubyDialogCallback_setTitleToItem(view, p);
203 } else if (key == sEnabledSymbol) {
205 flag = (val != Qnil && val != Qfalse);
206 RubyDialogCallback_setEnabledForItem(view, flag);
207 } else if (key == sEditableSymbol) {
209 flag = (val != Qnil && val != Qfalse);
210 RubyDialogCallback_setEditableForItem(view, flag);
211 } else if (key == sHiddenSymbol) {
213 flag = (val != Qnil && val != Qfalse);
214 RubyDialogCallback_setHiddenForItem(view, flag);
215 } else if (key == sSubItemsSymbol) {
217 if (type == sPopUpSymbol) {
220 val = rb_ary_to_ary(val);
221 len2 = RARRAY_LEN(val);
222 ptr2 = RARRAY_PTR(val);
223 while (RubyDialogCallback_deleteSubItem(view, 0) >= 0);
224 for (j = 0; j < len2; j++) {
225 VALUE val2 = ptr2[j];
226 RubyDialogCallback_appendSubItem(view, EncodedStringValuePtr(val2));
228 RubyDialogCallback_resizeToBest(view);
229 RubyDialogCallback_setSelectedSubItem(view, 0);
231 } else if (key == sXSymbol || key == sYSymbol || key == sWidthSymbol || key == sHeightSymbol) {
232 /* Frame components */
234 float f = NUM2DBL(rb_Float(val));
235 frame = RubyDialogCallback_frameOfItem(view);
238 else if (key == sYSymbol)
240 else if (key == sWidthSymbol)
241 frame.size.width = f;
243 frame.size.height = f;
244 RubyDialogCallback_setFrameOfItem(view, frame);
245 } else if (key == sOriginSymbol || key == sSizeSymbol) {
246 /* Frame components */
248 float f0 = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 0)));
249 float f1 = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 1)));
250 frame = RubyDialogCallback_frameOfItem(view);
251 if (key == sOriginSymbol) {
255 frame.size.width = f0;
256 frame.size.height = f1;
258 RubyDialogCallback_setFrameOfItem(view, frame);
259 } else if (key == sFrameSymbol) {
260 /* Frame (x, y, width, height) */
262 frame.origin.x = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 0)));
263 frame.origin.y = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 1)));
264 frame.size.width = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 2)));
265 frame.size.height = NUM2DBL(rb_Float(Ruby_ObjectAtIndex(val, 3)));
266 RubyDialogCallback_setFrameOfItem(view, frame);
267 } else if (key == sFlexSymbol) {
268 /* Flex flags: [left, top, right, bottom, width, height] (0: fixed, 1: flex) */
271 rb_ivar_set(self, key_id, val);
273 if (rb_obj_is_kind_of(val, rb_mEnumerable)) {
274 for (i = 0; i < 6; i++) {
275 VALUE gval = Ruby_ObjectAtIndex(val, i);
276 if (RTEST(gval) && NUM2INT(rb_Integer(gval)) != 0)
279 } else if (rb_obj_is_kind_of(val, rb_cNumeric)) {
280 flex = NUM2INT(rb_Integer(val));
282 rb_raise(rb_eDialogError, "the 'flex' attribute should be either an integer or an array of 4 boolean/integers");
284 rb_ivar_set(self, key_id, INT2NUM(flex));
286 } else if (key == sForeColorSymbol || key == sBackColorSymbol) {
288 val = rb_ary_to_ary(val);
289 col[0] = col[1] = col[2] = col[3] = 1.0;
290 for (i = 0; i < 4 && i < RARRAY_LEN(val); i++)
291 col[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i]));
292 if (key == sForeColorSymbol)
293 RubyDialogCallback_setForegroundColorForItem(view, col);
295 RubyDialogCallback_setBackgroundColorForItem(view, col);
296 } else if (key == sFontSymbol) {
297 int size, family, style, weight;
298 size = family = style = weight = 0;
299 val = rb_ary_to_ary(val);
300 for (i = 0; i < RARRAY_LEN(val); i++) {
301 VALUE vali = RARRAY_PTR(val)[i];
302 if (rb_obj_is_kind_of(vali, rb_cNumeric)) {
303 size = NUM2INT(rb_Integer(vali));
304 } else if (vali == sDefaultSymbol) {
306 } else if (vali == sRomanSymbol) {
308 } else if (vali == sSwissSymbol) {
310 } else if (vali == sFixedSymbol) {
312 } else if (vali == sNormalSymbol) {
314 } else if (vali == sSlantSymbol) {
316 } else if (vali == sItalicSymbol) {
318 } else if (vali == sMediumSymbol) {
320 } else if (vali == sBoldSymbol) {
322 } else if (vali == sLightSymbol) {
324 } else if (vali != Qnil) {
325 vali = rb_inspect(vali);
326 rb_raise(rb_eDialogError, "unknown font specification (%s)", EncodedStringValuePtr(vali));
329 RubyDialogCallback_setFontForItem(view, size, family, style, weight);
330 } else if (key == sSelectionSymbol) {
331 /* Selection (for Table == MyListCtrl item) */
332 if (type == sTableSymbol) {
333 IntGroup *ig = IntGroupFromValue(val);
334 RubyDialogCallback_setSelectedTableRows((RDItem *)view, ig, 0);
337 count = RubyDialog_GetTableItemCount((RubyValue)dialog_val, (RDItem *)view);
338 for (row = 0; row < count; row++) {
339 int flag = (IntGroupLookup(ig, row, NULL) != 0);
340 RubyDialogCallback_setTableRowSelected((RDItem *)view, row, flag);
343 } else if (key == sColumnsSymbol) {
344 /* Columns (for Table == MyListCtrl item) */
345 if (type == sTableSymbol) {
346 /* The value should be an array of [name, width, align (0: natural, 1: right, 2: center)] */
349 val = rb_ary_to_ary(val);
350 for (col = RubyDialogCallback_countTableColumn((RDItem *)view) - 1; col >= 0; col--) {
351 RubyDialogCallback_deleteTableColumn((RDItem *)view, col);
353 for (col = 0; col < RARRAY_LEN(val); col++) {
355 int format, width, len;
356 cval = rb_ary_to_ary(RARRAY_PTR(val)[col]);
357 len = RARRAY_LEN(cval);
359 heading = EncodedStringValuePtr(RARRAY_PTR(cval)[0]);
362 width = NUM2INT(rb_Integer(RARRAY_PTR(cval)[1]));
365 format = NUM2INT(rb_Integer(RARRAY_PTR(cval)[2]));
367 RubyDialogCallback_insertTableColumn((RDItem *)view, col, heading, format, width);
370 } else if (key == sRefreshSymbol) {
371 /* Refresh (for Table == MyListCtrl item) */
372 if (type == sTableSymbol) {
374 RubyDialogCallback_refreshTable((RDItem *)view);
378 if (key == sTagSymbol && rb_obj_is_kind_of(val, rb_cInteger))
379 rb_raise(rb_eDialogError, "the dialog item tag must not be integers");
380 rb_ivar_set(self, key_id, val);
382 RubyDialogCallback_setNeedsDisplay(view, 1);
390 * Get the attribute for the key.
393 s_RubyDialogItem_Attr(VALUE self, VALUE key)
396 VALUE dialog_val, index_val, type, val;
402 dialog_val = rb_ivar_get(self, SYM2ID(sDialogSymbol));
403 if (key == sDialogSymbol)
405 index_val = rb_ivar_get(self, SYM2ID(sIndexSymbol));
406 if (key == sIndexSymbol)
408 itag = NUM2INT(index_val);
409 type = rb_ivar_get(self, SYM2ID(sTypeSymbol));
410 if (key == sTypeSymbol)
412 if (dialog_val == Qnil || (dref = s_RubyDialog_GetController(dialog_val)) == NULL)
413 rb_raise(rb_eDialogError, "The dialog item does not belong to any dialog (internal error?)");
414 view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
415 key_id = SYM2ID(key);
419 if (key == sValueSymbol) {
421 if (type == sTextFieldSymbol) {
422 /* Is range specified? */
423 VALUE range = rb_ivar_get(self, SYM2ID(sRangeSymbol));
424 cp = RubyDialogCallback_getStringPtrFromItem(view);
426 if (TYPE(range) == T_ARRAY) {
427 if (FIXNUM_P((RARRAY_PTR(range))[0]))
428 val = INT2NUM(atoi(cp));
430 val = rb_float_new(atof(cp));
431 } else val = Ruby_NewEncodedStringValue2(cp);
434 } else if (type == sTextViewSymbol) {
435 cp = RubyDialogCallback_getStringPtrFromItem(view);
437 val = Ruby_NewEncodedStringValue2(cp);
440 } else if (type == sPopUpSymbol) {
441 int n = RubyDialogCallback_selectedSubItem(view);
444 } else if (type == sCheckBoxSymbol || type == sRadioSymbol || type == sToggleButtonSymbol) {
445 val = INT2NUM(RubyDialogCallback_getStateForItem(view));
447 } else if (key == sTitleSymbol) {
448 cp = RubyDialogCallback_titleOfItem(view);
450 val = Ruby_NewEncodedStringValue2(cp);
453 } else if (key == sEnabledSymbol) {
455 flag = RubyDialogCallback_isItemEnabled(view);
456 val = (flag ? Qtrue : Qfalse);
457 } else if (key == sEditableSymbol) {
459 flag = RubyDialogCallback_isItemEditable(view);
460 val = (flag ? Qtrue : Qfalse);
461 } else if (key == sHiddenSymbol) {
463 flag = RubyDialogCallback_isItemHidden(view);
464 val = (flag ? Qtrue : Qfalse);
465 } else if (key == sSubItemsSymbol) {
468 for (i = 0; (cp = RubyDialogCallback_titleOfSubItem(view, i)) != NULL; i++) {
469 rb_ary_push(val, Ruby_NewEncodedStringValue2(cp));
472 } else if (key == sXSymbol || key == sYSymbol || key == sWidthSymbol || key == sHeightSymbol) {
473 /* Frame components */
476 frame = RubyDialogCallback_frameOfItem(view);
479 else if (key == sYSymbol)
481 else if (key == sWidthSymbol)
482 f = frame.size.width;
484 f = frame.size.height;
485 val = rb_float_new(f);
486 } else if (key == sOriginSymbol || key == sSizeSymbol) {
487 /* Frame components */
490 frame = RubyDialogCallback_frameOfItem(view);
491 if (key == sOriginSymbol) {
495 f0 = frame.size.width;
496 f1 = frame.size.height;
498 val = rb_ary_new3(2, rb_float_new(f0), rb_float_new(f1));
500 } else if (key == sFrameSymbol) {
501 /* Frame (x, y, width, height) */
502 RDRect frame = RubyDialogCallback_frameOfItem(view);
503 val = rb_ary_new3(4, rb_float_new(frame.origin.x), rb_float_new(frame.origin.y), rb_float_new(frame.size.width), rb_float_new(frame.size.height));
505 } else if (key == sFlexSymbol) {
507 val = rb_ivar_get(self, key_id);
509 flex = NUM2INT(rb_Integer(val));
511 for (i = 0; i < 6; i++) {
512 rb_ary_push(val, ((flex & (1 << i)) ? INT2FIX(1) : INT2FIX(0)));
515 } else if (key == sForeColorSymbol || key == sBackColorSymbol) {
517 if (key == sForeColorSymbol)
518 RubyDialogCallback_getForegroundColorForItem(view, col);
520 RubyDialogCallback_getBackgroundColorForItem(view, col);
521 val = rb_ary_new3(4, rb_float_new(col[0]), rb_float_new(col[1]), rb_float_new(col[2]), rb_float_new(col[3]));
522 } else if (key == sFontSymbol) {
523 int size, family, style, weight;
524 VALUE fval, sval, wval;
525 if (RubyDialogCallback_getFontForItem(view, &size, &family, &style, &weight) == 0)
526 rb_raise(rb_eDialogError, "Cannot get font for dialog item");
527 fval = (family == 1 ? sDefaultSymbol :
528 (family == 2 ? sRomanSymbol :
529 (family == 3 ? sSwissSymbol :
530 (family == 4 ? sFixedSymbol :
532 sval = (style == 1 ? sNormalSymbol :
533 (style == 2 ? sSlantSymbol :
534 (style == 3 ? sItalicSymbol :
536 wval = (weight == 1 ? sMediumSymbol :
537 (weight == 2 ? sBoldSymbol :
538 (weight == 3 ? sLightSymbol :
540 val = rb_ary_new3(4, INT2NUM(size), fval, sval, wval);
542 } else if (key == sSelectionSymbol) {
543 /* Selection (for Table == MyTextCtrl item) */
544 if (type == sTableSymbol) {
545 IntGroup *ig = RubyDialogCallback_selectedTableRows((RDItem *)view);
546 val = ValueFromIntGroup(ig);
549 count = RubyDialog_GetTableItemCount((RubyValue)dialog_val, (RDItem *)view);
550 for (row = 0; row < count; row++) {
551 if (RubyDialogCallback_isTableRowSelected((RDItem *)view, row))
552 IntGroupAdd(ig, row, 1);
554 val = ValueFromIntGroup(ig); */
557 val = rb_ivar_get(self, key_id);
565 * append_string(val) -> self
567 * Append the given string to the end. Only usable for the text control.
570 s_RubyDialogItem_AppendString(VALUE self, VALUE val)
572 VALUE dialog_val, index_val;
577 dialog_val = rb_ivar_get(self, SYM2ID(sDialogSymbol));
578 index_val = rb_ivar_get(self, SYM2ID(sIndexSymbol));
579 itag = NUM2INT(index_val);
580 if (dialog_val == Qnil || (dref = s_RubyDialog_GetController(dialog_val)) == NULL)
581 rb_raise(rb_eDialogError, "The dialog item does not belong to any dialog (internal error?)");
582 view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
583 val = rb_str_to_str(val);
584 if (RubyDialogCallback_appendString(view, EncodedStringValuePtr(val)) == 0)
585 rb_raise(rb_eDialogError, "Cannot append string to the dialog item");
591 * refresh_rect(rectArray, eraseBackground = true) -> self
593 * Request refreshing part of the content in the next update event.
594 * rectArray = [x, y, width, height].
597 s_RubyDialogItem_RefreshRect(int argc, VALUE *argv, VALUE self)
600 VALUE dialog_val, index_val;
606 dialog_val = rb_ivar_get(self, SYM2ID(sDialogSymbol));
607 index_val = rb_ivar_get(self, SYM2ID(sIndexSymbol));
608 itag = NUM2INT(index_val);
609 if (dialog_val == Qnil || (dref = s_RubyDialog_GetController(dialog_val)) == NULL)
610 rb_raise(rb_eDialogError, "The dialog item does not belong to any dialog (internal error?)");
611 view = RubyDialogCallback_dialogItemAtIndex(dref, itag);
612 rb_scan_args(argc, argv, "11", &rval, &fval);
615 rval = rb_ary_to_ary(rval);
616 if (RARRAY_LEN(rval) != 4)
617 rb_raise(rb_eArgError, "The rectangle should be given as an array of four numerics (x, y, width, height)");
618 rect.origin.x = NUM2DBL(rb_Float(RARRAY_PTR(rval)[0]));
619 rect.origin.y = NUM2DBL(rb_Float(RARRAY_PTR(rval)[1]));
620 rect.size.width = NUM2DBL(rb_Float(RARRAY_PTR(rval)[2]));
621 rect.size.height = NUM2DBL(rb_Float(RARRAY_PTR(rval)[3]));
622 RubyDialogCallback_setNeedsDisplayInRect(view, rect, RTEST(fval));
626 #pragma mark ====== Dialog methods ======
629 s_RubyDialog_Initialize(int argc, VALUE *argv, VALUE self)
632 VALUE val1, val2, val3, val4;
634 char *title1, *title2;
638 Data_Get_Struct(self, RubyDialogInfo, di);
640 rb_scan_args(argc, argv, "04", &val1, &val2, &val3, &val4);
645 optval = rb_hash_aref(val4, sResizableSymbol);
647 style |= rd_Resizable;
648 optval = rb_hash_aref(val4, sHasCloseBoxSymbol);
650 style |= rd_HasCloseBox;
653 di->dref = dref = RubyDialogCallback_new(style);
654 RubyDialogCallback_setRubyObject(dref, (RubyValue)self);
657 char *p = EncodedStringValuePtr(val1);
658 RubyDialogCallback_setWindowTitle(dref, p);
661 title1 = EncodedStringValuePtr(val2);
664 title2 = EncodedStringValuePtr(val3);
667 // Array of item informations
668 items = rb_ary_new();
670 if (val2 == Qnil && argc < 2) {
671 /* The 2nd argument is omitted (nil is not explicitly given) */
672 title1 = "OK"; /* Default title */
674 if (val3 == Qnil && argc < 3) {
675 /* The 3rd argument is omitted (nil is not explicitly given) */
676 title2 = "Cancel"; /* Default title */
679 /* Create standard buttons */
680 /* (When title{1,2} == NULL, the buttons are still created but set to hidden) */
681 RubyDialogCallback_createStandardButtons(dref, title1, title2);
682 for (i = 0; i < 2; i++) {
684 item = rb_class_new_instance(0, NULL, rb_cDialogItem);
685 rb_ivar_set(item, SYM2ID(sDialogSymbol), self);
686 rb_ivar_set(item, SYM2ID(sIndexSymbol), INT2NUM(i));
687 rb_ivar_set(item, SYM2ID(sTagSymbol), Ruby_NewEncodedStringValue2(i == 0 ? "ok" : "cancel"));
688 rb_ivar_set(item, SYM2ID(sTypeSymbol), sButtonSymbol);
689 rb_ary_push(items, item);
692 rb_iv_set(self, "_items", items);
694 if (rb_ary_includes(gRubyDialogList, self) == Qfalse)
695 rb_ary_push(gRubyDialogList, self);
701 s_RubyDialog_ItemIndexForTagNoRaise(VALUE self, VALUE tag)
703 VALUE items = rb_iv_get(self, "_items");
704 int len = RARRAY_LEN(items);
705 VALUE *ptr = RARRAY_PTR(items);
709 if (i < 0 || i >= len)
713 for (i = 0; i < len; i++) {
714 if (rb_equal(tag, rb_ivar_get(ptr[i], SYM2ID(sTagSymbol))) == Qtrue)
721 s_RubyDialog_ItemIndexForTag(VALUE self, VALUE tag)
723 int i = s_RubyDialog_ItemIndexForTagNoRaise(self, tag);
725 rb_raise(rb_eDialogError, "item number (%d) out of range", i);
727 rb_raise(rb_eDialogError, "Dialog has no item with tag %s", EncodedStringValuePtr(tag));
733 * item_at_index(index) -> DialogItem
735 * Get the dialog item at index. If no such item exists, exception is raised.
738 s_RubyDialog_ItemAtIndex(VALUE self, VALUE ival)
741 int idx = NUM2INT(rb_Integer(ival));
742 items = rb_iv_get(self, "_items");
743 if (idx < 0 || idx > RARRAY_LEN(items))
744 rb_raise(rb_eRangeError, "item index (%d) out of range", idx);
745 else if (idx == RARRAY_LEN(items))
746 return Qnil; /* This may happen when this function is called during creation of item */
747 return RARRAY_PTR(items)[idx];
752 * item_with_tag(tag) -> DialogItem
754 * Get the dialog item which has the given tag. If no such item exists, returns nil.
757 s_RubyDialog_ItemWithTag(VALUE self, VALUE tval)
759 int idx = s_RubyDialog_ItemIndexForTagNoRaise(self, tval);
761 VALUE items = rb_iv_get(self, "_items");
762 return RARRAY_PTR(items)[idx];
768 * set_attr(tag, hash)
769 * set_attr(tag, key1, val1, key2, val2,...)
771 * Set the attributes given in the hash or in the argument list.
774 s_RubyDialog_SetAttr(int argc, VALUE *argv, VALUE self)
777 VALUE items = rb_iv_get(self, "_items");
778 VALUE *ptr = RARRAY_PTR(items);
779 VALUE tval, aval, item, key, val, *kptr;
780 rb_scan_args(argc, argv, "1*", &tval, &aval);
781 itag = s_RubyDialog_ItemIndexForTag(self, tval);
783 if (RARRAY_LEN(aval) == 1) {
784 VALUE hval = RARRAY_PTR(aval)[0];
785 VALUE keys = rb_funcall(hval, rb_intern("keys"), 0);
786 klen = RARRAY_LEN(keys);
787 kptr = RARRAY_PTR(keys);
788 for (i = 0; i < klen; i++) {
790 val = rb_hash_aref(hval, key);
791 s_RubyDialogItem_SetAttr(item, key, val);
793 } else if (RARRAY_LEN(aval) % 2 == 1)
794 rb_raise(rb_eArgError, "set_attr: the arguments should be assigned as key-value pairs");
796 klen = RARRAY_LEN(aval);
797 kptr = RARRAY_PTR(aval);
798 for (i = 0; i < klen; i += 2) {
801 s_RubyDialogItem_SetAttr(item, key, val);
811 * Get the attribute for the key.
814 s_RubyDialog_Attr(VALUE self, VALUE tag, VALUE key)
816 VALUE items = rb_iv_get(self, "_items");
817 VALUE *ptr = RARRAY_PTR(items);
818 int itag = s_RubyDialog_ItemIndexForTag(self, tag);
819 VALUE item = ptr[itag];
820 return s_RubyDialogItem_Attr(item, key);
827 * Run the modal session for this dialog.
830 s_RubyDialog_Run(VALUE self)
834 RubyDialog *dref = s_RubyDialog_GetController(self);
836 iflag = Ruby_SetInterruptFlag(Qfalse);
837 retval = RubyDialogCallback_runModal(dref);
838 Ruby_SetInterruptFlag(iflag);
839 RubyDialogCallback_destroy(dref);
840 s_RubyDialog_Forget(self);
841 return rb_iv_get(self, "_retval");
848 * Show the dialog modelessly. This is to be used with Dialog#hide in pairs.
849 * To avoid garbage collection by Ruby interpreter, the dialog being shown is
850 * registered in a global variable, and unregistered when it is hidden.
851 * Mixing Dialog#show and Dialog#run will lead to unpredictable results, including crash.
854 s_RubyDialog_Show(VALUE self)
856 RubyDialog *dref = s_RubyDialog_GetController(self);
857 RubyDialogCallback_show(dref);
858 // if (rb_ary_includes(gRubyDialogList, self) == Qfalse)
859 // rb_ary_push(gRubyDialogList, self);
867 * Hide the modeless dialog. This is to be used with Dialog#show in pairs.
868 * Mixing Dialog#hide and Dialog#run will lead to unpredictable results, including crash.
871 s_RubyDialog_Hide(VALUE self)
873 RubyDialog *dref = s_RubyDialog_GetController(self);
874 RubyDialogCallback_hide(dref);
880 * is_active -> Boolean
882 * Returns whether this dialog is 'active' (i.e. the user is working on it) or not.
885 s_RubyDialog_IsActive(VALUE self)
887 RubyDialog *dref = s_RubyDialog_GetController(self);
888 if (RubyDialogCallback_isActive(dref))
897 * Close the modeless dialog. The normal close handler of the platform is invoked.
898 * If the dialog is registered in the ruby_dialog_list global variable, it becomes unregistered.
899 * If force is true, or this method is called recursively, the window will be destroyed unconditionally.
902 s_RubyDialog_Close(VALUE self)
904 RubyDialog *dref = s_RubyDialog_GetController(self);
905 RubyDialogCallback_close(dref);
911 * layout(columns, i11, ..., i1c, i21, ..., i2c, ..., ir1, ..., irc [, options]) => integer
913 * Layout items in a table. The first argument is the number of columns, and must be a positive integer.
914 * If the last argument is a hash, then it contains the layout options.
915 * The ixy is the item identifier (a non-negative integer) or [identifier, hash], where the hash
916 * contains the specific options for the item.
917 * Returns an integer that represents the dialog item.
920 s_RubyDialog_Layout(int argc, VALUE *argv, VALUE self)
922 VALUE items, oval, *opts, new_item;
923 int row, col, i, j, n, itag, nitems, *itags;
926 float *widths, *heights;
929 RDItem *layoutView, *ditem;
930 RDSize contentMinSize;
932 float col_padding = 8.0; /* Padding between columns */
933 float row_padding = 8.0; /* Padding between rows */
936 dref = s_RubyDialog_GetController(self);
937 contentMinSize = RubyDialogCallback_windowMinSize(dref);
938 items = rb_iv_get(self, "_items");
939 nitems = RARRAY_LEN(items);
941 autoResizeFlag = RubyDialogCallback_isAutoResizeEnabled(dref);
942 RubyDialogCallback_setAutoResizeEnabled(dref, 0);
944 if (argc > 0 && rb_obj_is_kind_of(argv[argc - 1], rb_cHash)) {
946 oval = argv[argc - 1];
948 oval1 = rb_hash_aref(oval, sPaddingSymbol);
949 if (rb_obj_is_kind_of(oval1, rb_cNumeric))
950 col_padding = row_padding = NUM2DBL(oval1);
951 oval1 = rb_hash_aref(oval, sMarginSymbol);
952 if (rb_obj_is_kind_of(oval1, rb_cNumeric))
953 margin = NUM2DBL(oval1);
958 if (--argc < 0 || (col = NUM2INT(rb_Integer(argv[0]))) <= 0 || argc < col)
959 rb_raise(rb_eArgError, "wrong arguments; the first argument (col) must be a positive integer, and at least col arguments must follow");
960 row = (argc + col - 1) / col; /* It actually means (int)(ceil((argc - 1.0) / col)) */
963 /* Allocate temporary storage */
964 itags = (int *)calloc(sizeof(int), row * col);
965 opts = (VALUE *)calloc(sizeof(VALUE), row * col);
966 sizes = (RDSize *)calloc(sizeof(RDSize), row * col);
967 widths = (float *)calloc(sizeof(float), col);
968 heights = (float *)calloc(sizeof(float), row);
969 if (itags == NULL || sizes == NULL || opts == NULL || widths == NULL || heights == NULL)
970 rb_raise(rb_eNoMemError, "out of memory during layout");
972 /* Get frame sizes */
973 for (i = 0; i < row; i++) {
974 for (j = 0; j < col; j++) {
980 if (TYPE(argval) == T_ARRAY && RARRAY_LEN(argval) == 2) {
981 opts[n] = RARRAY_PTR(argval)[1];
982 if (TYPE(opts[n]) != T_HASH)
983 rb_raise(rb_eTypeError, "The layout options should be given as a hash");
984 argval = RARRAY_PTR(argval)[0];
988 else if (rb_obj_is_kind_of(argval, rb_cDialogItem))
989 itag = NUM2INT(s_RubyDialogItem_Attr(argval, sIndexSymbol));
990 else if (FIXNUM_P(argval))
991 itag = FIX2INT(argval);
993 itag = s_RubyDialog_ItemIndexForTag(self, argval);
995 rb_raise(rb_eRangeError, "item tag (%d) is out of range (should be 0..%d)", itag, nitems - 1);
996 if (itag >= 0 && (ditem = RubyDialogCallback_dialogItemAtIndex(dref, itag)) != NULL) {
997 sizes[n] = RubyDialogCallback_frameOfItem(ditem).size;
1000 /* printf("sizes(%d,%d) = [%f,%f]\n", i, j, sizes[n-2].width, sizes[n-2].height); */
1004 /* Calculate required widths */
1005 for (j = 0; j < col; j++) {
1007 for (i = 0; i < row; i++) {
1008 for (n = j; n >= 0; n--) {
1009 f = sizes[i * col + n].width;
1011 f += (n > 0 ? widths[n - 1] : 0.0);
1015 if (j < col - 1 && sizes[i * col + j + 1].width == 0.0)
1016 continue; /* The next right item is empty */
1020 fmin += col_padding;
1022 /* printf("widths[%d]=%f\n", j, fmin); */
1025 /* Calculate required heights */
1027 for (i = 0; i < row; i++) {
1028 for (j = 0; j < col; j++) {
1029 for (n = i; n >= 0; n--) {
1030 f = sizes[n * col + j].height;
1032 f += (n > 0 ? heights[n - 1] : 0.0);
1039 fmin += row_padding;
1041 /* printf("heights[%d]=%f\n", i, fmin); */
1044 /* Calculate layout view size */
1045 layoutFrame.size.width = widths[col - 1];
1046 layoutFrame.size.height = heights[row - 1];
1047 layoutFrame.origin.x = margin;
1048 layoutFrame.origin.y = margin;
1049 /* printf("layoutFrame = [%f,%f,%f,%f]\n", layoutFrame.origin.x, layoutFrame.origin.y, layoutFrame.size.width, layoutFrame.size.height); */
1051 /* Create a layout view */
1052 layoutView = RubyDialogCallback_createItem(dref, "layout_view", "", layoutFrame);
1054 /* Move the subviews into the layout view */
1055 for (i = 0; i < row; i++) {
1056 for (j = 0; j < col; j++) {
1058 if (n < argc && (itag = itags[n]) > 0 && itag < nitems) {
1064 ditem = RubyDialogCallback_dialogItemAtIndex(dref, itag);
1065 item = (RARRAY_PTR(items))[itag];
1066 type = rb_ivar_get(item, SYM2ID(sTypeSymbol));
1067 if (type == sTextSymbol)
1070 cell.origin.x = (j > 0 ? widths[j - 1] : 0.0);
1071 cell.origin.y = (i > 0 ? heights[i - 1] : 0.0);
1072 for (k = j + 1; k < col; k++) {
1073 if (itags[i * col + k] != -1)
1076 cell.size.width = widths[k - 1] - cell.origin.x;
1077 for (k = i + 1; k < row; k++) {
1078 if (itags[k * col + j] != -2)
1081 cell.size.height = heights[k - 1] - cell.origin.y;
1082 pt.x = cell.origin.x + col_padding * 0.5;
1083 pt.y = cell.origin.y + row_padding * 0.5 + offset;
1085 /* Handle item-specific options */
1086 /* They can either be specified as layout options or as item attributes */
1089 if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sHFillSymbol)) == Qnil)
1090 oval1 = rb_ivar_get(item, SYM2ID(sHFillSymbol));
1092 sizes[n].width = cell.size.width - col_padding;
1095 if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sVFillSymbol)) == Qnil)
1096 oval1 = rb_ivar_get(item, SYM2ID(sVFillSymbol));
1098 sizes[n].height = cell.size.height - row_padding;
1102 RDRect newFrameRect = RubyDialogCallback_frameOfItem(ditem);
1103 newFrameRect.size.width = sizes[n].width;
1104 newFrameRect.size.height = sizes[n].height;
1105 RubyDialogCallback_setFrameOfItem(ditem, newFrameRect);
1107 if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sAlignSymbol)) == Qnil)
1108 oval1 = rb_ivar_get(item, SYM2ID(sAlignSymbol));
1109 if (oval1 == sCenterSymbol)
1110 pt.x += (cell.size.width - sizes[n].width - col_padding) * 0.5;
1111 else if (oval1 == sRightSymbol)
1112 pt.x += (cell.size.width - sizes[n].width) - col_padding;
1113 if (!RTEST(opts[n]) || (oval1 = rb_hash_aref(opts[n], sVerticalAlignSymbol)) == Qnil)
1114 oval1 = rb_ivar_get(item, SYM2ID(sVerticalAlignSymbol));
1115 if (oval1 == sCenterSymbol)
1116 pt.y += (cell.size.height - sizes[n].height - row_padding) * 0.5;
1117 else if (oval1 == sBottomSymbol)
1118 pt.y += (cell.size.height - sizes[n].height) - row_padding;
1120 RubyDialogCallback_moveItemUnderView(ditem, layoutView, pt);
1131 /* Index for the layout view */
1132 itag = RARRAY_LEN(items);
1134 /* Create a new item object for the layout view and push to _items */
1135 new_item = rb_class_new_instance(0, NULL, rb_cDialogItem);
1136 rb_ivar_set(new_item, SYM2ID(sTypeSymbol), sViewSymbol);
1137 rb_ivar_set(new_item, SYM2ID(sDialogSymbol), self);
1138 rb_ivar_set(new_item, SYM2ID(sIndexSymbol), INT2NUM(itag));
1139 rb_ary_push(items, new_item);
1142 /* Set the attributes given in the option hash */
1143 VALUE keys = rb_funcall(oval, rb_intern("keys"), 0);
1144 for (i = 0; i < RARRAY_LEN(keys); i++) {
1145 VALUE kval = RARRAY_PTR(keys)[i];
1146 if (TYPE(kval) == T_SYMBOL)
1147 s_RubyDialogItem_SetAttr(new_item, kval, rb_hash_aref(oval, kval));
1151 RubyDialogCallback_setAutoResizeEnabled(dref, autoResizeFlag);
1158 * item(type, hash) -> DialogItem
1160 * Create a dialog item. Type is one of the following symbols; <tt>:text, :textfield, :radio,
1161 * :checkbox, :popup</tt>. Hash is the attributes that can be set by set_attr.
1162 * Returns an integer that represents the item. (0 and 1 are reserved for "OK" and "Cancel")
1165 s_RubyDialog_Item(int argc, VALUE *argv, VALUE self)
1167 int itag; /* Integer tag for NSControl */
1171 // NSDictionary *attr;
1173 VALUE type, hash, val, items;
1177 if (argc == 1 && FIXNUM_P(argv[0])) {
1178 return s_RubyDialog_ItemAtIndex(self, argv[0]);
1181 dref = s_RubyDialog_GetController(self);
1182 rb_scan_args(argc, argv, "11", &type, &hash);
1184 hash = rb_hash_new();
1185 else if (TYPE(hash) != T_HASH)
1186 rb_raise(rb_eDialogError, "The second argument of Dialog#item must be a hash");
1187 rect.size.width = rect.size.height = 1.0;
1188 rect.origin.x = rect.origin.y = 0.0;
1190 val = rb_hash_aref(hash, sTitleSymbol);
1192 title = EncodedStringValuePtr(val);
1197 Check_Type(type, T_SYMBOL);
1199 /* if (type == sTextViewSymbol)
1200 font = [NSFont userFixedPitchFontOfSize: 0];
1202 font = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
1203 attr = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, nil];
1204 brect.origin.x = brect.origin.y = 0.0;
1205 brect.size = [title sizeWithAttributes: attr];
1206 brect.size.width += 8;
1208 /* Set rect if specified */
1209 rect.origin.x = rect.origin.y = 0.0;
1210 rect.size.width = rect.size.height = 0.0;
1211 val = rb_hash_aref(hash, sXSymbol);
1212 if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
1213 rect.origin.x = dval;
1214 val = rb_hash_aref(hash, sYSymbol);
1215 if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
1216 rect.origin.y = dval;
1217 val = rb_hash_aref(hash, sWidthSymbol);
1218 if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
1219 rect.size.width = dval;
1220 val = rb_hash_aref(hash, sHeightSymbol);
1221 if (!NIL_P(val) && (dval = NUM2DBL(rb_Float(val))) > 0.0)
1222 rect.size.height = dval;
1224 /* Create a new DialogItem */
1225 new_item = rb_class_new_instance(0, NULL, rb_cDialogItem);
1226 rb_ivar_set(new_item, SYM2ID(sTypeSymbol), type);
1228 /* Direction for the separator line */
1229 /* The direction can be specified either by specifying non-square frame size or "vertical" flag */
1230 if (type == sLineSymbol) {
1231 VALUE val1 = rb_hash_aref(hash, ID2SYM(rb_intern("vertical")));
1232 if (rect.size.width == 0)
1233 rect.size.width = 1;
1234 if (rect.size.height == 0)
1235 rect.size.height = 1;
1236 if (rect.size.width == rect.size.height) {
1238 rect.size.height++; /* vertical */
1239 else rect.size.width++; /* horizontal */
1243 if (RubyDialogCallback_createItem(dref, rb_id2name(SYM2ID(type)), title, rect) == NULL)
1244 rb_raise(rb_eDialogError, "item type :%s is not implemented", rb_id2name(SYM2ID(type)));
1246 /* Push to _items */
1247 items = rb_iv_get(self, "_items");
1248 rb_ary_push(items, new_item);
1251 itag = RARRAY_LEN(items) - 1;
1252 val = INT2NUM(itag);
1253 rb_ivar_set(new_item, SYM2ID(sIndexSymbol), val);
1254 rb_ivar_set(new_item, SYM2ID(sDialogSymbol), self);
1256 /* Set attributes */
1261 s_RubyDialog_SetAttr(2, vals, self);
1264 /* Set internal attributes */
1265 rb_ivar_set(new_item, SYM2ID(sIsProcessingActionSymbol), Qfalse);
1267 /* Type-specific attributes */
1268 if (type == sLineSymbol) {
1269 if (rect.size.width > rect.size.height && rect.size.width == 2)
1270 rb_ivar_set(new_item, SYM2ID(sHFillSymbol), Qtrue);
1271 else if (rect.size.width < rect.size.height && rect.size.height == 2)
1272 rb_ivar_set(new_item, SYM2ID(sVFillSymbol), Qtrue);
1275 if (type == sTableSymbol) {
1276 RDItem *rd_item = RubyDialogCallback_dialogItemAtIndex(dref, itag);
1277 RubyDialogCallback_refreshTable(rd_item);
1285 * _items -> Array of DialogItems
1287 * Returns an internal array of items. For debugging use only.
1290 s_RubyDialog_Items(VALUE self)
1292 return rb_iv_get(self, "_items");
1299 * Returns the number of items.
1302 s_RubyDialog_Nitems(VALUE self)
1304 VALUE items = rb_iv_get(self, "_items");
1305 int nitems = RARRAY_LEN(items);
1306 return INT2NUM(nitems);
1311 * each_item {|item| ...}
1313 * Iterate the given block with the DialogItem object as the argument.
1316 s_RubyDialog_EachItem(VALUE self)
1318 VALUE items = rb_iv_get(self, "_items");
1319 int nitems = RARRAY_LEN(items);
1321 for (i = 0; i < nitems; i++) {
1322 rb_yield(RARRAY_PTR(items)[i]);
1329 * radio_group(Array)
1331 * Group radio buttons as a mutually exclusive group. The array elements can be
1332 * DialogItems, Integers (item index) or other values (item tag).
1335 s_RubyDialog_RadioGroup(VALUE self, VALUE aval)
1339 VALUE items = rb_iv_get(self, "_items");
1340 int nitems = RARRAY_LEN(items);
1341 aval = rb_ary_to_ary(aval);
1342 n = RARRAY_LEN(aval);
1344 /* Build a new array with checked arguments */
1345 gval = rb_ary_new2(n);
1346 for (i = 0; i < n; i++) {
1347 VALUE tval = RARRAY_PTR(aval)[i];
1348 if (rb_obj_is_kind_of(tval, rb_cDialogItem)) {
1349 j = NUM2INT(s_RubyDialogItem_Attr(tval, sIndexSymbol));
1351 j = s_RubyDialog_ItemIndexForTag(self, tval);
1352 if (j < 0 || j >= nitems)
1354 tval = RARRAY_PTR(items)[j];
1356 if (rb_ivar_get(tval, SYM2ID(sTypeSymbol)) != sRadioSymbol)
1358 rb_ary_push(gval, INT2NUM(j));
1361 rb_raise(rb_eDialogError, "the item %d (at index %d) does not represent a radio button", j, i);
1363 /* Set the radio group array to the specified items. If the item already belongs to a radio group,
1364 then it is removed from that group. */
1365 /* All items share the common array (gval in the above). This allows removing easy. */
1366 for (i = 0; i < n; i++) {
1368 j = NUM2INT(RARRAY_PTR(gval)[i]);
1369 gval2 = rb_ivar_get(RARRAY_PTR(items)[j], SYM2ID(sRadioGroupSymbol));
1371 rb_ary_delete(gval2, INT2NUM(j)); /* Remove j from gval2 */
1372 rb_ivar_set(RARRAY_PTR(items)[j], SYM2ID(sRadioGroupSymbol), gval);
1379 * end_modal(item = 0, retval = nil) -> nil
1381 * End the modal session. The argument item is either the DialogItem object or
1382 * the index (0 for OK, 1 for Cancel). If the second argument is given, it will
1383 * be the return value of Dialog#run. Otherwise, the return value will be a hash
1384 * including the key-value pairs for all "tagged" dialog items plus :status=>true
1385 * (if OK is pressed) or false (if Cancel is pressed).
1386 * This method itself returns nil.
1389 s_RubyDialog_EndModal(int argc, VALUE *argv, VALUE self)
1392 VALUE retval = Qundef;
1396 if (rb_obj_is_kind_of(argv[0], rb_cDialogItem)) {
1397 flag = NUM2INT(s_RubyDialogItem_Attr(argv[0], sIndexSymbol));
1399 flag = NUM2INT(rb_Integer(argv[0]));
1404 if (retval == Qundef) {
1405 /* The default return value */
1406 VALUE items = rb_iv_get(self, "_items");
1407 int len = RARRAY_LEN(items);
1408 VALUE *ptr = RARRAY_PTR(items);
1410 retval = rb_hash_new();
1411 /* Get values for controls with defined tags */
1412 for (i = 2; i < len; i++) {
1413 /* Items 0, 1 are OK/Cancel buttons */
1414 /* VALUE type = rb_hash_aref(ptr[i], sTypeSymbol); */
1415 VALUE tag = rb_ivar_get(ptr[i], SYM2ID(sTagSymbol));
1418 val = s_RubyDialogItem_Attr(ptr[i], sValueSymbol);
1419 rb_hash_aset(retval, tag, val);
1422 rb_hash_aset(retval, ID2SYM(rb_intern("status")), INT2NUM(flag));
1424 rb_iv_set(self, "_retval", retval);
1425 RubyDialogCallback_endModal(s_RubyDialog_GetController(self), (flag ? 1 : 0));
1426 if (rb_ary_includes(gRubyDialogList, self) == Qtrue)
1427 rb_ary_delete(gRubyDialogList, self);
1432 s_RubyDialog_CallActionProc(VALUE self, VALUE aval, int argc, VALUE *argv)
1436 if (TYPE(aval) == T_SYMBOL)
1437 return rb_funcall2(self, SYM2ID(aval), argc, argv);
1438 else if (rb_obj_is_kind_of(aval, rb_cProc))
1439 return rb_funcall2(aval, rb_intern("call"), argc, argv);
1441 VALUE insval = rb_inspect(aval);
1442 rb_raise(rb_eTypeError, "Cannot call action method '%s'", EncodedStringValuePtr(insval));
1444 return Qnil; /* Not reached */
1451 * Do the default action for the dialog item. The item is given as the argument.
1452 * If the item is OK (index == 0) or Cancel (index == 1), the modal session of this dialog will end.
1453 * Otherwise, the "action" attribute is looked for the item, and if found
1454 * it is called with the given index as the argument (the attribute must be
1455 * either a symbol (method name) or a Proc object).
1456 * If the "action" attribute is not found, do nothing.
1458 * It is likely that you will create a new RubyDialog of your own, and
1459 * define a singleton method named +action+ that overrides this method.
1460 * When it is invoked, you can process item-specific actions according to
1461 * the argument index, and if you want to continue the default behavior
1462 * (e.g. to end modal session when "OK" is pressed), just call this
1463 * version by +super+.
1466 s_RubyDialog_Action(VALUE self, VALUE item)
1469 int ival = NUM2INT(s_RubyDialogItem_Attr(item, sIndexSymbol));
1470 if (ival == 0 || ival == 1) {
1471 RubyDialogCallback_endModal(s_RubyDialog_GetController(self), ival);
1474 aval = s_RubyDialogItem_Attr(item, sActionSymbol);
1475 return s_RubyDialog_CallActionProc(self, aval, 1, &item);
1480 * start_timer(interval, action = nil)
1482 * Start dialog-specific interval timer. The timer interval is described in seconds (floating point
1483 * is allowed, however the resolution is not better than milliseconds on wxWidgets).
1484 * The action is either a symbol (method name) or a Proc object.
1485 * If no action is given, then the last set value is used.
1486 * If the timer is already running, it is stopped before new timer is run.
1489 s_RubyDialog_StartTimer(int argc, VALUE *argv, VALUE self)
1491 VALUE itval, actval;
1493 RubyDialog *dref = s_RubyDialog_GetController(self);
1494 rb_scan_args(argc, argv, "11", &itval, &actval);
1496 rb_iv_set(self, "_timer_action", actval);
1497 dval = NUM2DBL(rb_Float(itval));
1498 if (RubyDialogCallback_startIntervalTimer(dref, dval) == 0)
1499 rb_raise(rb_eDialogError, "Cannot start timer for dialog");
1507 * Stop dialog-specific interval timer. Do nothing if no timer is running.
1510 s_RubyDialog_StopTimer(VALUE self)
1512 RubyDialogCallback_stopIntervalTimer(s_RubyDialog_GetController(self));
1518 * on_key(action = nil)
1520 * Set keydown action method. When a keydown event occurs and no other controls
1521 * in this dialog accept the event, the action method (if non-nil) is invoked
1522 * with the keycode integer as the single argument.
1523 * The action is either a symbol (method name) or a Proc object.
1526 s_RubyDialog_OnKey(int argc, VALUE *argv, VALUE self)
1529 RubyDialog *dref = s_RubyDialog_GetController(self);
1530 rb_scan_args(argc, argv, "01", &actval);
1531 rb_iv_set(self, "_key_action", actval);
1532 RubyDialogCallback_enableOnKeyHandler(dref, (actval != Qnil));
1538 * size -> [width, height]
1540 * Get the size for this dialog.
1543 s_RubyDialog_Size(VALUE self)
1545 RDSize size = RubyDialogCallback_windowSize(s_RubyDialog_GetController(self));
1546 return rb_ary_new3(2, INT2NUM(floor(size.width + 0.5)), INT2NUM(floor(size.height + 0.5)));
1551 * set_size([width, height])
1552 * set_size(width, height)
1554 * Set the size for this dialog.
1557 s_RubyDialog_SetSize(int argc, VALUE *argv, VALUE self)
1561 rb_scan_args(argc, argv, "11", &wval, &hval);
1563 hval = Ruby_ObjectAtIndex(wval, 1);
1564 wval = Ruby_ObjectAtIndex(wval, 0);
1566 size.width = NUM2INT(rb_Integer(wval));
1567 size.height = NUM2INT(rb_Integer(hval));
1568 RubyDialogCallback_setWindowSize(s_RubyDialog_GetController(self), size);
1574 * min_size -> [width, height]
1576 * Get the minimum size for this dialog.
1579 s_RubyDialog_MinSize(VALUE self)
1581 RDSize size = RubyDialogCallback_windowMinSize(s_RubyDialog_GetController(self));
1582 return rb_ary_new3(2, INT2NUM(floor(size.width + 0.5)), INT2NUM(floor(size.height + 0.5)));
1588 * set_min_size([width, height])
1589 * set_min_size(width, height)
1591 * Set the minimum size for this dialog.
1594 s_RubyDialog_SetMinSize(int argc, VALUE *argv, VALUE self)
1598 rb_scan_args(argc, argv, "02", &wval, &hval);
1600 size = RubyDialogCallback_windowSize(s_RubyDialog_GetController(self));
1603 hval = Ruby_ObjectAtIndex(wval, 1);
1604 wval = Ruby_ObjectAtIndex(wval, 0);
1606 size.width = NUM2INT(rb_Integer(wval));
1607 size.height = NUM2INT(rb_Integer(hval));
1609 RubyDialogCallback_setWindowMinSize(s_RubyDialog_GetController(self), size);
1616 * listen(obj, str, pr)
1618 * Listen to the event invoked by the object. str = the name of the event (dependent on the
1619 * object), pr = the callback procedure. The first argument to the callback procedure is
1620 * always obj. Other arguments are dependent on the event and the object.
1623 s_RubyDialog_Listen(VALUE self, VALUE oval, VALUE sval, VALUE pval)
1630 sptr = EncodedStringValuePtr(sval);
1631 if (rb_obj_is_kind_of(oval, rb_cMolecule)) {
1632 Molecule *mol = MoleculeFromValue(oval);
1633 i = RubyDialogCallback_Listen(s_RubyDialog_GetController(self), mol, "Molecule", sptr, (RubyValue)oval, (RubyValue)pval);
1636 case -1: rb_raise(rb_eDialogError, "This dialog cannot be listened to."); break;
1637 case -2: rb_raise(rb_eDialogError, "This message is not supported"); break;
1640 /* Keep the objects in the internal array, to protect from GC */
1641 ID id = rb_intern("listen");
1642 VALUE aval = rb_ivar_get(self, id);
1644 aval = rb_ary_new();
1645 rb_ivar_set(self, id, aval);
1647 if (pval == Qfalse || pval == Qnil) {
1648 rb_ary_delete_at(aval, i);
1650 rb_ary_store(aval, i, rb_ary_new3(2, oval, pval));
1654 rb_raise(rb_eDialogError, "Dialog#listen is presently only available for Molecule object");
1662 * save_panel(message = nil, directory = nil, default_filename = nil, wildcard = nil)
1664 * Display the "save as" dialog and returns the fullpath filename.
1667 s_RubyDialog_SavePanel(int argc, VALUE *argv, VALUE klass)
1669 VALUE mval, dval, fval, wval, iflag;
1670 const char *mp, *dp, *wp;
1673 rb_scan_args(argc, argv, "04", &mval, &dval, &fval, &wval);
1676 else mp = EncodedStringValuePtr(mval);
1679 else dp = FileStringValuePtr(dval);
1683 strncpy(buf, FileStringValuePtr(fval), 1023);
1688 else wp = FileStringValuePtr(wval);
1689 iflag = Ruby_SetInterruptFlag(Qfalse);
1690 n = RubyDialogCallback_savePanel(mp, dp, wp, buf, sizeof buf);
1691 Ruby_SetInterruptFlag(iflag);
1693 return Ruby_NewFileStringValue(buf);
1699 * open_panel(message = nil, directory = nil, wildcard = nil, for_directories = false, multiple_selection = false)
1701 * Display the "open" dialog and returns the fullpath filename.
1704 s_RubyDialog_OpenPanel(int argc, VALUE *argv, VALUE klass)
1706 VALUE mval, dval, fval, mulval, wval, iflag;
1707 const char *mp, *dp, *wp;
1709 int for_directories = 0, multiple_selection = 0;
1711 rb_scan_args(argc, argv, "05", &mval, &dval, &wval, &fval, &mulval);
1714 else mp = EncodedStringValuePtr(mval);
1717 else dp = FileStringValuePtr(dval);
1720 else wp = FileStringValuePtr(wval);
1721 if (fval != Qnil && fval != Qfalse)
1722 for_directories = 1;
1723 if (mulval != Qnil && mulval != Qfalse) {
1724 multiple_selection = 1;
1725 if (for_directories && multiple_selection)
1726 rb_raise(rb_eDialogError, "open_panel for directories allows only single selection");
1728 iflag = Ruby_SetInterruptFlag(Qfalse);
1729 n = RubyDialogCallback_openPanel(mp, dp, wp, &ary, for_directories, multiple_selection);
1730 Ruby_SetInterruptFlag(iflag);
1733 if (multiple_selection) {
1735 retval = rb_ary_new();
1736 for (i = 0; i < n; i++) {
1737 rb_ary_push(retval, Ruby_NewFileStringValue(ary[i]));
1741 retval = Ruby_NewFileStringValue(ary[0]);
1749 #pragma mark ====== Table view support ======
1752 s_RubyDialog_doTableAction(VALUE val)
1754 VALUE ival, itval, pval, retval;
1756 void **vp = (void **)val;
1757 VALUE self = (VALUE)vp[0];
1758 RDItem *ip = (RDItem *)vp[1];
1759 VALUE sym = (VALUE)vp[2];
1760 RubyDialog *dref = s_RubyDialog_GetController(self);
1761 int idx = RubyDialogCallback_indexOfItem(dref, ip);
1763 return Qnil; /* No such item (this cannot happen) */
1764 ival = INT2NUM(idx);
1765 itval = s_RubyDialog_ItemAtIndex(self, ival);
1766 pval = rb_ivar_get(itval, SYM2ID(sym));
1768 return Qnil; /* No action is defined: return the default value */
1771 if (sym == sOnCountSymbol) {
1772 retval = s_RubyDialog_CallActionProc(self, pval, 1, args);
1773 vp[3] = (void *)(intptr_t)(NUM2INT(rb_Integer(retval)));
1775 } else if (sym == sOnGetValueSymbol) {
1776 args[1] = INT2NUM((int)vp[3]);
1777 args[2] = INT2NUM((int)vp[4]);
1778 retval = s_RubyDialog_CallActionProc(self, pval, 3, args);
1779 retval = rb_str_to_str(retval);
1780 vp[5] = strdup(EncodedStringValuePtr(retval));
1782 } else if (sym == sOnSetValueSymbol) {
1783 args[1] = INT2NUM((int)vp[3]);
1784 args[2] = INT2NUM((int)vp[4]);
1785 args[3] = Ruby_NewEncodedStringValue2((char *)vp[5]);
1786 retval = s_RubyDialog_CallActionProc(self, pval, 4, args);
1787 vp[6] = (void *)(intptr_t)(NUM2INT(rb_Integer(retval)));
1789 } else if (sym == sOnDragSelectionToRowSymbol) {
1790 args[1] = INT2NUM((int)vp[3]);
1791 retval = s_RubyDialog_CallActionProc(self, pval, 2, args);
1793 } else if (sym == sIsItemEditableSymbol) {
1794 args[1] = INT2NUM((int)vp[3]);
1795 args[2] = INT2NUM((int)vp[4]);
1796 retval = s_RubyDialog_CallActionProc(self, pval, 3, args);
1797 vp[5] = (void *)(intptr_t)(RTEST(retval) ? 1 : 0);
1799 } else if (sym == sIsDragAndDropEnabledSymbol) {
1800 retval = s_RubyDialog_CallActionProc(self, pval, 1, args);
1801 vp[3] = (void *)(intptr_t)(RTEST(retval) ? 1 : 0);
1803 } else if (sym == sOnSelectionChangedSymbol) {
1804 retval = s_RubyDialog_CallActionProc(self, pval, 1, args);
1805 vp[3] = (void *)(intptr_t)(RTEST(retval) ? 1 : 0);
1807 } else if (sym == sOnSetColorSymbol) {
1808 float *fg = (float *)vp[5];
1809 float *bg = (float *)vp[6];
1812 args[1] = INT2NUM((int)vp[3]);
1813 args[2] = INT2NUM((int)vp[4]);
1814 retval = s_RubyDialog_CallActionProc(self, pval, 3, args);
1817 retval = rb_ary_to_ary(retval);
1818 if (RARRAY_LEN(retval) >= 1 && fg != NULL) {
1819 if (RARRAY_PTR(retval)[0] != Qnil) {
1820 cval = rb_ary_to_ary(RARRAY_PTR(retval)[0]);
1821 for (i = 0; i < 4 && i < RARRAY_LEN(cval); i++) {
1822 fg[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
1827 if (RARRAY_LEN(retval) >= 2 && bg != NULL) {
1828 cval = rb_ary_to_ary(RARRAY_PTR(retval)[1]);
1829 for (i = 0; i < 4 && i < RARRAY_LEN(cval); i++) {
1830 bg[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
1834 vp[7] = (void *)(intptr_t)n;
1836 } else if (sym == sHasPopUpMenuSymbol) {
1837 args[1] = INT2NUM((int)vp[3]);
1838 args[2] = INT2NUM((int)vp[4]);
1839 retval = s_RubyDialog_CallActionProc(self, pval, 3, args);
1840 if (retval == Qnil) {
1845 retval = rb_ary_to_ary(retval);
1846 n = RARRAY_LEN(retval);
1847 vp[6] = (void *)(intptr_t)n;
1848 titles = ALLOC_N(char *, n);
1849 *((char ***)vp[5]) = titles;
1850 for (i = 0; i < n; i++) {
1851 VALUE tval = RARRAY_PTR(retval)[i];
1852 titles[i] = strdup(EncodedStringValuePtr(tval));
1856 } else if (sym == sOnPopUpMenuSelectedSymbol) {
1857 args[1] = INT2NUM((int)vp[3]);
1858 args[2] = INT2NUM((int)vp[4]);
1859 args[3] = INT2NUM((int)vp[5]);
1860 retval = s_RubyDialog_CallActionProc(self, pval, 4, args);
1866 RubyDialog_GetTableItemCount(RubyValue self, RDItem *ip)
1869 void *vp[4] = { (void *)self, (void *)ip, (void *)sOnCountSymbol, NULL };
1870 VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1872 Ruby_showError(status);
1874 } else if (val == Qnil)
1876 else return (int)vp[3];
1880 RubyDialog_GetTableItemText(RubyValue self, RDItem *ip, int row, int column, char *buf, int buflen)
1883 void *vp[6] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnGetValueSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, NULL };
1884 VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1885 if (status != 0 || val == Qnil) {
1888 strncpy(buf, (char *)vp[5], buflen - 1);
1889 buf[buflen - 1] = 0;
1894 RubyDialog_SetTableItemText(RubyValue self, RDItem *ip, int row, int column, const char *str)
1897 void *vp[7] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnSetValueSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, (void *)str, NULL };
1898 VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1899 if (status != 0 || val == Qnil) {
1906 RubyDialog_DragTableSelectionToRow(RubyValue self, RDItem *ip, int row)
1909 void *vp[5] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnDragSelectionToRowSymbol, (void *)(intptr_t)row, NULL };
1910 rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1912 Ruby_showError(status);
1916 RubyDialog_IsTableItemEditable(RubyValue self, RDItem *ip, int row, int column)
1919 void *vp[6] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sIsItemEditableSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, NULL };
1920 VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1921 if (status != 0 || val == Qnil)
1923 else return (int)vp[5];
1927 RubyDialog_IsTableDragAndDropEnabled(RubyValue self, RDItem *ip, int row)
1930 void *vp[4] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sIsDragAndDropEnabledSymbol, NULL };
1931 VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1932 if (status != 0 || val == Qnil)
1934 else return (int)vp[3];
1938 RubyDialog_OnTableSelectionChanged(RubyValue self, RDItem *ip)
1941 void *vp[4] = { (void *)self, (void *)ip, (void *)sOnSelectionChangedSymbol, NULL };
1942 rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1944 Ruby_showError(status);
1948 RubyDialog_SetTableItemColor(RubyValue self, RDItem *ip, int row, int column, float *fg, float *bg)
1951 void *vp[8] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnSetColorSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, (void *)fg, (void *)bg, NULL };
1952 VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1953 if (status != 0 || val == Qnil)
1955 else return (int)vp[7];
1959 RubyDialog_HasPopUpMenu(RubyValue self, RDItem *ip, int row, int column, char ***menu_titles)
1962 void *vp[7] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sHasPopUpMenuSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, (void *)menu_titles, NULL };
1963 VALUE val = rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1964 if (status != 0 || val == Qnil)
1966 else return (int)vp[6];
1970 RubyDialog_OnPopUpMenuSelected(RubyValue self, RDItem *ip, int row, int column, int selected_index)
1973 void *vp[7] = { (void *)(uintptr_t)self, (void *)ip, (void *)(uintptr_t)sOnPopUpMenuSelectedSymbol, (void *)(intptr_t)row, (void *)(intptr_t)column, (void *)(intptr_t)selected_index, NULL };
1974 rb_protect(s_RubyDialog_doTableAction, (VALUE)vp, &status);
1976 Ruby_showError(status);
1979 #pragma mark ====== Utility function ======
1982 RubyDialog_validateItemContent(RubyValue self, RDItem *ip, const char *s)
1984 VALUE items, item, val, val_min, val_max;
1986 RubyDialog *dref = s_RubyDialog_GetController((VALUE)self);
1989 items = rb_iv_get(((VALUE)self), "_items");
1990 nitems = RARRAY_LEN(items);
1991 itag = RubyDialogCallback_indexOfItem(dref, ip);
1992 if (itag < 0 || itag >= nitems)
1993 return 1; /* Accept anything */
1995 item = (RARRAY_PTR(items))[itag];
1996 val = rb_ivar_get(item, SYM2ID(sRangeSymbol));
1998 return 1; /* Accept anything */
2000 val_min = Ruby_ObjectAtIndex(val, 0);
2001 val_max = Ruby_ObjectAtIndex(val, 1);
2002 if (FIXNUM_P(val_min) && FIXNUM_P(val_max)) {
2004 int imin = NUM2INT(val_min);
2005 int imax = NUM2INT(val_max);
2006 if (ival < imin || ival > imax)
2008 snprintf(buf, sizeof buf, "%d", ival);
2009 RubyDialogCallback_setStringToItem(ip, buf);
2012 double dmin = NUM2DBL(rb_Float(val_min));
2013 double dmax = NUM2DBL(rb_Float(val_max));
2014 if (d < dmin || d > dmax)
2021 s_RubyDialog_doItemAction(VALUE val)
2024 void **vp = (void **)val;
2025 VALUE self = (VALUE)vp[0];
2027 RDItem *ip = (RDItem *)vp[1];
2029 int options = (int)vp[2];
2030 VALUE ival, itval, actval, tval, aval;
2031 RubyDialog *dref = s_RubyDialog_GetController(self);
2032 VALUE items = rb_iv_get(self, "_items");
2033 int nitems = RARRAY_LEN(items);
2034 int idx = RubyDialogCallback_indexOfItem(dref, ip);
2035 static VALUE sTextActionSym = Qfalse, sEscapeActionSym, sReturnActionSym;
2037 if (sTextActionSym == Qfalse) {
2038 sTextActionSym = ID2SYM(rb_intern("text_action"));
2039 sEscapeActionSym = ID2SYM(rb_intern("escape_action"));
2040 sReturnActionSym = ID2SYM(rb_intern("return_action"));
2045 ival = INT2NUM(idx);
2046 itval = s_RubyDialog_ItemAtIndex(self, ival);
2047 flag = rb_ivar_get(itval, SYM2ID(sIsProcessingActionSymbol));
2049 return Qnil; /* Avoid recursive calling action proc for the same item */
2051 rb_ivar_set(itval, SYM2ID(sIsProcessingActionSymbol), Qtrue);
2053 /* Handle radio group */
2054 tval = s_RubyDialogItem_Attr(itval, sTypeSymbol);
2055 aval = sActionSymbol;
2056 if (tval == sRadioSymbol) {
2057 VALUE gval = s_RubyDialogItem_Attr(itval, sRadioGroupSymbol);
2059 /* All other radio buttons with no radio group will be deselected */
2061 for (i = 0; i < nitems; i++) {
2064 radioval = RARRAY_PTR(items)[i];
2065 if (s_RubyDialogItem_Attr(radioval, sTypeSymbol) == sRadioSymbol
2066 && s_RubyDialogItem_Attr(radioval, sRadioGroupSymbol) == Qnil) {
2067 ip2 = RubyDialogCallback_dialogItemAtIndex(dref, i);
2068 RubyDialogCallback_setStateForItem(ip2, 0);
2071 } else if (TYPE(gval) == T_ARRAY) {
2072 n = RARRAY_LEN(gval);
2073 for (i = 0; i < n; i++) {
2074 j = NUM2INT(RARRAY_PTR(gval)[i]);
2075 if (j >= 0 && j < nitems && j != idx) {
2076 ip2 = RubyDialogCallback_dialogItemAtIndex(dref, j);
2077 RubyDialogCallback_setStateForItem(ip2, 0); /* Deselect */
2083 if (tval == sTextFieldSymbol || tval == sTextViewSymbol) {
2085 aval = sTextActionSym; /* Action for every text update */
2088 if (tval == sTextFieldSymbol) {
2090 aval = sReturnActionSym;
2091 else if (options == 4)
2092 aval = sEscapeActionSym;
2095 /* If the item has the "action" attribute, call it */
2096 actval = s_RubyDialogItem_Attr(itval, aval);
2097 if (actval != Qnil) {
2098 if (TYPE(actval) == T_SYMBOL)
2099 rb_funcall(self, SYM2ID(actval), 1, itval);
2101 rb_funcall(actval, rb_intern("call"), 1, itval);
2102 } else if (rb_respond_to(itval, SYM2ID(aval))) {
2103 /* If "action" method is defined, then call it without arguments */
2104 rb_funcall(itval, SYM2ID(aval), 0);
2105 } else if (aval == sReturnActionSym || aval == sEscapeActionSym) {
2106 /* Enter or escape is pressed on text field */
2107 if (RubyDialogCallback_isModal(dref)) {
2108 rb_ivar_set(itval, SYM2ID(sIsProcessingActionSymbol), Qfalse);
2109 itval = s_RubyDialog_ItemAtIndex(self, (aval == sReturnActionSym ? INT2FIX(0) : INT2FIX(1)));
2110 s_RubyDialog_EndModal(1, &itval, self);
2113 /* Default action (only for default buttons) */
2114 if (RubyDialogCallback_isModal(dref)) {
2115 if (idx == 0 || idx == 1) {
2116 rb_ivar_set(itval, SYM2ID(sIsProcessingActionSymbol), Qfalse);
2117 s_RubyDialog_EndModal(1, &itval, self);
2122 rb_ivar_set(itval, SYM2ID(sIsProcessingActionSymbol), Qfalse);
2127 /* Action for dialog items.
2128 Get the item number, and call "action" method of the RubyDialog object with
2129 the item number (integer) as the argument. The default "action" method is
2130 defined as s_RubyDialog_action. */
2132 RubyDialog_doItemAction(RubyValue self, RDItem *ip, int options)
2136 vp[0] = (void *)(uintptr_t)self;
2138 vp[2] = (void *)(intptr_t)options;
2139 rb_protect(s_RubyDialog_doItemAction, (VALUE)vp, &status);
2141 Ruby_showError(status);
2145 s_RubyDialog_doPaintAction(VALUE val)
2147 void **vp = (void **)val;
2148 VALUE self = (VALUE)vp[0];
2149 RDItem *ip = (RDItem *)vp[1];
2150 VALUE ival, itval, actval;
2151 RubyDialog *dref = s_RubyDialog_GetController(self);
2152 int idx = RubyDialogCallback_indexOfItem(dref, ip);
2155 ival = INT2NUM(idx);
2156 itval = s_RubyDialog_ItemAtIndex(self, ival);
2157 actval = s_RubyDialogItem_Attr(itval, sOnPaintSymbol);
2158 if (actval != Qnil) {
2159 if (TYPE(actval) == T_SYMBOL)
2160 rb_funcall(self, SYM2ID(actval), 1, itval);
2162 rb_funcall(actval, rb_intern("call"), 1, itval);
2167 /* Paint action for view item. */
2169 RubyDialog_doPaintAction(RubyValue self, RDItem *ip)
2173 vp[0] = (void *)self;
2175 rb_protect(s_RubyDialog_doPaintAction, (VALUE)vp, &status);
2177 /* Stop timer before showing error dialog */
2178 RubyDialogCallback_stopIntervalTimer(s_RubyDialog_GetController((VALUE)self));
2179 Ruby_showError(status);
2184 s_RubyDialog_doTimerAction(VALUE self)
2186 VALUE actval = rb_iv_get(self, "_timer_action");
2187 if (actval != Qnil) {
2188 if (TYPE(actval) == T_SYMBOL)
2189 rb_funcall(self, SYM2ID(actval), 0);
2191 rb_funcall(actval, rb_intern("call"), 0);
2197 RubyDialog_doTimerAction(RubyValue self)
2200 rb_protect(s_RubyDialog_doTimerAction, (VALUE)self, &status);
2202 /* Stop timer before showing error dialog */
2203 RubyDialogCallback_stopIntervalTimer(s_RubyDialog_GetController((VALUE)self));
2204 Ruby_showError(status);
2209 s_RubyDialog_doKeyAction(VALUE val)
2211 void **values = (void **)val;
2212 VALUE self = (VALUE)values[0];
2213 int keyCode = (int)values[1];
2214 VALUE actval = rb_iv_get(self, "_key_action");
2215 if (actval != Qnil) {
2216 if (TYPE(actval) == T_SYMBOL)
2217 rb_funcall(self, SYM2ID(actval), 1, INT2NUM(keyCode));
2219 rb_funcall(actval, rb_intern("call"), 1, INT2NUM(keyCode));
2225 RubyDialog_doKeyAction(RubyValue self, int keyCode)
2229 values[0] = (void *)self;
2230 values[1] = (void *)(intptr_t)keyCode;
2231 rb_protect(s_RubyDialog_doKeyAction, (VALUE)values, &status);
2233 /* Stop timer before showing error dialog */
2234 RubyDialogCallback_stopIntervalTimer(s_RubyDialog_GetController((VALUE)self));
2235 Ruby_showError(status);
2240 s_RubyDialog_getFlexFlags(VALUE val)
2242 VALUE self = (VALUE)(((void **)val)[0]);
2243 RDItem *ip = (RDItem *)(((void **)val)[1]);
2245 RubyDialog *dref = s_RubyDialog_GetController(self);
2246 int idx = RubyDialogCallback_indexOfItem(dref, ip);
2248 return Qnil; /* No such item (this cannot happen) */
2249 itval = s_RubyDialog_ItemAtIndex(self, INT2NUM(idx));
2250 pval = rb_ivar_get(itval, SYM2ID(sFlexSymbol));
2252 return Qnil; /* Not set */
2254 pval = rb_Integer(pval);
2255 ((void **)val)[2] = (void *)(intptr_t)(NUM2INT(pval));
2261 RubyDialog_getFlexFlags(RubyValue self, RDItem *ip)
2265 void *args[3] = { (void *)self, (void *)ip, NULL };
2266 rval = rb_protect(s_RubyDialog_getFlexFlags, (VALUE)args, &status);
2269 else if (rval == Qnil)
2271 else return (int)args[2];
2275 s_RubyDialog_doCloseWindow(VALUE val)
2277 void **values = (void **)val;
2278 VALUE self = (VALUE)values[0];
2279 int isModal = (int)values[1];
2281 rb_funcall(self, rb_intern("end_modal"), 1, INT2NUM(1));
2285 VALUE actval = rb_iv_get(self, "@on_close");
2286 if (actval != Qnil) {
2287 if (TYPE(actval) == T_SYMBOL)
2288 rval = rb_funcall(self, SYM2ID(actval), 0);
2290 rval = rb_funcall(actval, rb_intern("call"), 0);
2291 } else rval = Qtrue;
2293 if (rb_ary_includes(gRubyDialogList, self) == Qtrue)
2294 rb_ary_delete(gRubyDialogList, self);
2295 RubyDialogCallback_destroy(s_RubyDialog_GetController(self));
2296 s_RubyDialog_Forget(self);
2302 /* Handle close box. Invokes Dialog.end_modal or Dialog.close in Ruby world */
2304 RubyDialog_doCloseWindow(RubyValue self, int isModal)
2308 void *args[2] = { (void *)(uintptr_t)self, (void *)(intptr_t)isModal };
2309 rval = rb_protect(s_RubyDialog_doCloseWindow, (VALUE)args, &status);
2311 Ruby_showError(status);
2315 #pragma mark ====== Device Context Methods ======
2317 static RDDeviceContext *
2318 s_RubyDialog_GetDeviceContext(VALUE self)
2320 if (rb_obj_is_kind_of(self, rb_cDialog)) {
2321 RubyDialog *dref = s_RubyDialog_GetController(self);
2322 return RubyDialogCallback_getDeviceContextForRubyDialog(dref);
2323 } else if (rb_obj_is_kind_of(self, rb_cBitmap)) {
2325 Data_Get_Struct(self, RDBitmap, bitmap);
2326 return RubyDialogCallback_getDeviceContextForBitmap(bitmap);
2328 rb_raise(rb_eDialogError, "No graphic device context is available");
2329 return NULL; /* Not reached */
2337 * Clear the drawing context.
2340 s_RubyDialog_Clear(VALUE self)
2342 RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2343 RubyDialogCallback_clear(dc);
2349 * draw_ellipse(x1, y1, radius1 [, radius2])
2351 * Draw an ellipse in the graphic context.
2354 s_RubyDialog_DrawEllipse(int argc, VALUE *argv, VALUE self)
2356 VALUE xval, yval, rval1, rval2;
2357 RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2358 rb_scan_args(argc, argv, "31", &xval, &yval, &rval1, &rval2);
2361 RubyDialogCallback_drawEllipse(dc, NUM2DBL(rb_Float(xval)), NUM2DBL(rb_Float(yval)), NUM2DBL(rb_Float(rval1)), NUM2DBL(rb_Float(rval2)));
2367 * draw_line(x1, y1, x2, y2, ...)
2368 * draw_line([x1, y1], [x2, y2], ...)
2369 * draw_line([x1, y1, x2, y2, ...])
2371 * Draw a series of line segments in the graphic context.
2374 s_RubyDialog_DrawLine(int argc, VALUE *argv, VALUE self)
2378 RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2381 if (rb_obj_is_kind_of(argv[0], rb_mEnumerable)) {
2382 VALUE aval = rb_ary_to_ary(argv[0]);
2383 if (RARRAY_LEN(aval) == 2) {
2384 /* The second form */
2387 rb_raise(rb_eDialogError, "Too few coordinates are given (requires at least two points)");
2388 coords = (float *)calloc(sizeof(float), ncoords * 2);
2389 coords[0] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[0]));
2390 coords[1] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[1]));
2391 for (i = 1; i < ncoords; i++) {
2392 aval = rb_ary_to_ary(argv[i]);
2393 if (RARRAY_LEN(aval) < 2)
2394 rb_raise(rb_eDialogError, "The coordinate should be an array of two numerics");
2395 coords[i * 2] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[0]));
2396 coords[i * 2 + 1] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[1]));
2399 /* The third form */
2400 if (RARRAY_LEN(aval) % 2 == 1)
2401 rb_raise(rb_eDialogError, "An odd number of numerics are given; the coordinate values should be given in pairs");
2402 ncoords = RARRAY_LEN(aval) / 2;
2404 rb_raise(rb_eDialogError, "Too few coordinates are given (requires at least two points)");
2405 coords = (float *)calloc(sizeof(float), ncoords * 2);
2406 for (i = 0; i < ncoords * 2; i++) {
2407 coords[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
2411 /* The first form */
2414 rb_raise(rb_eDialogError, "Too few coordinates are given (requires at least two points)");
2416 rb_raise(rb_eDialogError, "An odd number of numerics are given; the coordinate values should be given in pairs");
2417 coords = (float *)calloc(sizeof(float), ncoords * 2);
2418 for (i = 0; i < ncoords * 2; i++) {
2419 coords[i] = NUM2DBL(rb_Float(argv[i]));
2422 RubyDialogCallback_drawLine(dc, ncoords, coords);
2428 * draw_rectangle(ary [, round])
2429 * draw_rectangle(x, y, width, height [, round])
2431 * Draw a rectangle in the graphic context. If the first argument is an array, it should contain
2432 * four numerics [x, y, width, height]. If round is given and positive,
2433 * then draw a rounded rectangle with the given radius.
2436 s_RubyDialog_DrawRectangle(int argc, VALUE *argv, VALUE self)
2438 VALUE xval, yval, wval, hval, rval;
2439 RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2442 rb_scan_args(argc, argv, "41", &xval, &yval, &wval, &hval, &rval);
2444 rb_scan_args(argc, argv, "11", &xval, &rval);
2445 xval = rb_ary_to_ary(xval);
2446 if (RARRAY_LEN(xval) < 4)
2447 rb_raise(rb_eDialogError, "The dimension of rectangle should be given as four numerics (x, y, width, height) or an array of four numerics.");
2448 hval = RARRAY_PTR(xval)[3];
2449 wval = RARRAY_PTR(xval)[2];
2450 yval = RARRAY_PTR(xval)[1];
2451 xval = RARRAY_PTR(xval)[0];
2455 else r = NUM2DBL(rb_Float(rval));
2456 RubyDialogCallback_drawRectangle(dc, NUM2DBL(rb_Float(xval)), NUM2DBL(rb_Float(yval)), NUM2DBL(rb_Float(wval)), NUM2DBL(rb_Float(hval)), r);
2463 * draw_text(string, x, y)
2465 * Draw a string in the graphic context.
2468 s_RubyDialog_DrawText(VALUE self, VALUE sval, VALUE xval, VALUE yval)
2470 RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2471 const char *s = EncodedStringValuePtr(sval);
2472 float x = NUM2DBL(rb_Float(xval));
2473 float y = NUM2DBL(rb_Float(yval));
2474 RubyDialogCallback_drawText(dc, s, x, y);
2483 * Set the default font for the graphic context (not for the dialog!).
2484 * If the argument is nil, then the default font is set.
2485 * Otherwise, the font is set according to the attributes in the given hash.
2486 * The following keys are implemented: :size (font size),
2487 * :family (:default, :roman, :swiss, :fixed), :style (:normal, :italic, :slant),
2488 * :weight (:medium, :light, :bold)
2491 s_RubyDialog_Font(int argc, VALUE *argv, VALUE self)
2494 RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2495 rb_scan_args(argc, argv, "01", &hval);
2497 RubyDialogCallback_setFont(dc, NULL);
2501 VALUE keys = rb_funcall(hval, rb_intern("keys"), 0);
2503 args = (void **)calloc(sizeof(void *), RARRAY_LEN(keys) * 2 + 1);
2504 for (i = 0; i < RARRAY_LEN(keys); i++) {
2505 VALUE kval = RARRAY_PTR(keys)[i];
2506 VALUE aval = rb_hash_aref(hval, RARRAY_PTR(keys)[i]);
2507 if (kval == sSizeSymbol) {
2508 width = NUM2DBL(rb_Float(aval));
2509 args[i * 2] = "size";
2510 args[i * 2 + 1] = &width;
2511 args[i * 2 + 2] = NULL;
2512 } else if (kval == sStyleSymbol) {
2513 if (aval == sNormalSymbol)
2515 else if (aval == sItalicSymbol)
2517 else if (aval == sSlantSymbol)
2520 args[i * 2] = "style";
2521 args[i * 2 + 1] = (void *)(intptr_t)j;
2522 args[i * 2 + 2] = NULL;
2523 } else if (kval == sWeightSymbol) {
2524 if (aval == sMediumSymbol)
2526 else if (aval == sLightSymbol)
2528 else if (aval == sBoldSymbol)
2531 args[i * 2] = "weight";
2532 args[i * 2 + 1] = (void *)(intptr_t)j;
2533 args[i * 2 + 2] = NULL;
2534 } else if (kval == sFamilySymbol) {
2535 if (aval == sDefaultSymbol)
2537 else if (aval == sRomanSymbol)
2539 else if (aval == sSwissSymbol)
2541 else if (aval == sFixedSymbol)
2544 args[i * 2] = "family";
2545 args[i * 2 + 1] = (void *)(intptr_t)j;
2546 args[i * 2 + 2] = NULL;
2549 RubyDialogCallback_setFont(dc, args);
2560 * Set the drawing pen for the graphic context. If the argument is nil, then
2561 * null pen is set. Otherwise, an appropriate pen is created from the given hash.
2562 * The following keys are implemented: :color (foreground color), :width (pen width),
2563 * :style (:solid, :transparent, :dot, :long_dash, :short_dash, :dot_dash)
2566 s_RubyDialog_Pen(int argc, VALUE *argv, VALUE self)
2569 RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2570 rb_scan_args(argc, argv, "01", &hval);
2572 RubyDialogCallback_setPen(dc, NULL);
2576 VALUE keys = rb_funcall(hval, rb_intern("keys"), 0);
2577 float forecolor[4], width;
2578 args = (void **)calloc(sizeof(void *), RARRAY_LEN(keys) * 2 + 1);
2579 for (i = 0; i < RARRAY_LEN(keys); i++) {
2580 VALUE kval = RARRAY_PTR(keys)[i];
2581 VALUE aval = rb_hash_aref(hval, RARRAY_PTR(keys)[i]);
2582 if (kval == sForeColorSymbol || kval == sColorSymbol) {
2583 aval = rb_ary_to_ary(aval);
2584 for (j = 0; j < RARRAY_LEN(aval) && j < 4; j++) {
2585 forecolor[j] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[j]));
2592 args[i * 2] = "color";
2593 args[i * 2 + 1] = forecolor;
2594 args[i * 2 + 2] = NULL;
2595 } else if (kval == sWidthSymbol) {
2596 width = NUM2DBL(rb_Float(aval));
2597 args[i * 2] = "width";
2598 args[i * 2 + 1] = &width;
2599 args[i * 2 + 2] = NULL;
2600 } else if (kval == sStyleSymbol) {
2601 if (aval == sSolidSymbol)
2603 else if (aval == sTransparentSymbol)
2605 else if (aval == sDotSymbol)
2607 else if (aval == sLongDashSymbol)
2609 else if (aval == sShortDashSymbol)
2611 else if (aval == sDotDashSymbol)
2614 args[i * 2] = "style";
2615 args[i * 2 + 1] = (void *)(intptr_t)j;
2616 args[i * 2 + 2] = NULL;
2619 RubyDialogCallback_setPen(dc, args);
2627 * brush(:color=>[r, g, b, a])
2630 * Set the painting brush for the graphic context. If the argument is nil, then
2631 * null brush is set. Otherwise, an appropriate brush is created from the given hash.
2632 * (Currently only foreground color is implemented.)
2635 s_RubyDialog_Brush(int argc, VALUE *argv, VALUE self)
2638 RDDeviceContext *dc = s_RubyDialog_GetDeviceContext(self);
2639 rb_scan_args(argc, argv, "01", &hval);
2641 RubyDialogCallback_setBrush(dc, NULL);
2645 VALUE keys = rb_funcall(hval, rb_intern("keys"), 0);
2647 args = (void **)malloc(sizeof(void *) * (RARRAY_LEN(keys) * 2 + 1));
2648 for (i = 0; i < RARRAY_LEN(keys); i++) {
2649 VALUE kval = RARRAY_PTR(keys)[i];
2650 VALUE aval = rb_hash_aref(hval, RARRAY_PTR(keys)[i]);
2651 if (kval == sForeColorSymbol || kval == sColorSymbol) {
2652 aval = rb_ary_to_ary(aval);
2653 for (j = 0; j < RARRAY_LEN(aval) && j < 4; j++) {
2654 forecolor[j] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[j]));
2661 args[i * 2] = "color";
2662 args[i * 2 + 1] = forecolor;
2663 args[i * 2 + 2] = NULL;
2666 RubyDialogCallback_setBrush(dc, args);
2672 #pragma mark ====== Bitmap methods ======
2675 s_Bitmap_Alloc(VALUE klass)
2677 return Data_Wrap_Struct(klass, NULL, (RUBY_DATA_FUNC)RubyDialogCallback_releaseBitmap, NULL);
2681 s_Bitmap_Initialize(int argc, VALUE *argv, VALUE self)
2683 VALUE wval, hval, dval;
2684 int width, height, depth;
2686 rb_scan_args(argc, argv, "21", &wval, &hval, &dval);
2689 else depth = NUM2INT(rb_Integer(dval));
2690 width = NUM2INT(rb_Integer(wval));
2691 height = NUM2INT(rb_Integer(hval));
2692 if (width <= 0 || width >= 32768)
2693 rb_raise(rb_eDialogError, "Bitmap width (%d) is out of range (1..32767)", width);
2694 if (height <= 0 || height >= 32768)
2695 rb_raise(rb_eDialogError, "Bitmap height (%d) is out of range (1..32767)", height);
2697 rb_raise(rb_eDialogError, "Only depth = 32 is supported currently");
2698 bitmap = RubyDialogCallback_createBitmap(width, height, depth);
2699 DATA_PTR(self) = bitmap;
2704 s_Bitmap_SetFocusCallbackSub(VALUE v)
2706 return rb_obj_instance_eval(0, NULL, v);
2710 s_Bitmap_SetFocusCallback(void *p)
2713 VALUE *vp = (VALUE *)p;
2714 vp[1] = rb_protect(s_Bitmap_SetFocusCallbackSub, vp[0], &status);
2715 vp[2] = (VALUE)status;
2719 s_Bitmap_FocusExec(int argc, VALUE *argv, VALUE self)
2724 Data_Get_Struct(self, RDBitmap, bitmap);
2725 RubyDialogCallback_executeWithFocusOnBitmap(bitmap, s_Bitmap_SetFocusCallback, v);
2727 rb_jump_tag((int)v[2]);
2732 s_Bitmap_SaveToFile(VALUE self, VALUE fnval)
2735 const char *fn = FileStringValuePtr(fnval);
2736 Data_Get_Struct(self, RDBitmap, bitmap);
2737 if (RubyDialogCallback_saveBitmapToFile(bitmap, fn))
2743 #pragma mark ====== Initialize class ======
2746 RubyDialogInitClass(void)
2749 if (rb_cDialog != Qfalse)
2751 parent = (VALUE)RubyDialogCallback_parentModule();
2752 if (parent != Qfalse)
2753 rb_cDialog = rb_define_class_under(parent, "Dialog", rb_cObject);
2755 rb_cDialog = rb_define_class("Dialog", rb_cObject);
2756 rb_define_alloc_func(rb_cDialog, s_RubyDialog_Alloc);
2757 rb_define_private_method(rb_cDialog, "initialize", s_RubyDialog_Initialize, -1);
2758 rb_define_method(rb_cDialog, "run", s_RubyDialog_Run, 0);
2759 rb_define_method(rb_cDialog, "item", s_RubyDialog_Item, -1);
2760 rb_define_method(rb_cDialog, "item_at_index", s_RubyDialog_ItemAtIndex, 1);
2761 rb_define_method(rb_cDialog, "item_with_tag", s_RubyDialog_ItemWithTag, 1);
2762 rb_define_method(rb_cDialog, "layout", s_RubyDialog_Layout, -1);
2763 rb_define_method(rb_cDialog, "_items", s_RubyDialog_Items, 0);
2764 rb_define_method(rb_cDialog, "nitems", s_RubyDialog_Nitems, 0);
2765 rb_define_method(rb_cDialog, "each_item", s_RubyDialog_EachItem, 0);
2766 rb_define_method(rb_cDialog, "set_attr", s_RubyDialog_SetAttr, -1);
2767 rb_define_method(rb_cDialog, "attr", s_RubyDialog_Attr, 2);
2768 rb_define_method(rb_cDialog, "radio_group", s_RubyDialog_RadioGroup, -2);
2769 rb_define_method(rb_cDialog, "action", s_RubyDialog_Action, 1);
2770 rb_define_method(rb_cDialog, "end_modal", s_RubyDialog_EndModal, -1);
2771 rb_define_method(rb_cDialog, "show", s_RubyDialog_Show, 0);
2772 rb_define_method(rb_cDialog, "hide", s_RubyDialog_Hide, 0);
2773 rb_define_method(rb_cDialog, "active?", s_RubyDialog_IsActive, 0);
2774 rb_define_method(rb_cDialog, "close", s_RubyDialog_Close, 0);
2775 rb_define_method(rb_cDialog, "start_timer", s_RubyDialog_StartTimer, -1);
2776 rb_define_method(rb_cDialog, "stop_timer", s_RubyDialog_StopTimer, 0);
2777 rb_define_method(rb_cDialog, "on_key", s_RubyDialog_OnKey, -1);
2778 rb_define_method(rb_cDialog, "set_size", s_RubyDialog_SetSize, -1);
2779 rb_define_method(rb_cDialog, "size", s_RubyDialog_Size, 0);
2780 rb_define_method(rb_cDialog, "set_min_size", s_RubyDialog_SetMinSize, -1);
2781 rb_define_method(rb_cDialog, "min_size", s_RubyDialog_MinSize, 0);
2782 /* rb_define_method(rb_cDialog, "listen", s_RubyDialog_Listen, 3); */
2783 rb_define_singleton_method(rb_cDialog, "save_panel", s_RubyDialog_SavePanel, -1);
2784 rb_define_singleton_method(rb_cDialog, "open_panel", s_RubyDialog_OpenPanel, -1);
2786 if (parent != Qfalse)
2787 rb_cDialogItem = rb_define_class_under(parent, "DialogItem", rb_cObject);
2789 rb_cDialogItem = rb_define_class("DialogItem", rb_cObject);
2791 rb_define_method(rb_cDialogItem, "[]=", s_RubyDialogItem_SetAttr, 2);
2792 rb_define_method(rb_cDialogItem, "[]", s_RubyDialogItem_Attr, 1);
2793 rb_define_alias(rb_cDialogItem, "set_attr", "[]=");
2794 rb_define_alias(rb_cDialogItem, "attr", "[]");
2795 rb_define_method(rb_cDialogItem, "append_string", s_RubyDialogItem_AppendString, 1);
2796 rb_define_method(rb_cDialogItem, "refresh_rect", s_RubyDialogItem_RefreshRect, -1);
2798 if (parent != Qfalse)
2799 rb_mDeviceContext = rb_define_module_under(parent, "DeviceContext");
2801 rb_mDeviceContext = rb_define_module("DeviceContext");
2803 rb_define_method(rb_mDeviceContext, "clear", s_RubyDialog_Clear, 0);
2804 rb_define_method(rb_mDeviceContext, "draw_ellipse", s_RubyDialog_DrawEllipse, -1);
2805 rb_define_method(rb_mDeviceContext, "draw_line", s_RubyDialog_DrawLine, -1);
2806 rb_define_method(rb_mDeviceContext, "draw_polygon", s_RubyDialog_DrawLine, -1);
2807 rb_define_method(rb_mDeviceContext, "draw_rectangle", s_RubyDialog_DrawRectangle, -1);
2808 rb_define_method(rb_mDeviceContext, "draw_text", s_RubyDialog_DrawText, 3);
2809 rb_define_method(rb_mDeviceContext, "font", s_RubyDialog_Font, -1);
2810 rb_define_method(rb_mDeviceContext, "pen", s_RubyDialog_Pen, -1);
2811 rb_define_method(rb_mDeviceContext, "brush", s_RubyDialog_Brush, -1);
2813 rb_include_module(rb_cDialog, rb_mDeviceContext);
2815 if (parent != Qfalse)
2816 rb_cBitmap = rb_define_class_under(parent, "Bitmap", rb_cObject);
2818 rb_cBitmap = rb_define_class("Bitmap", rb_cObject);
2820 rb_include_module(rb_cBitmap, rb_mDeviceContext);
2821 rb_define_alloc_func(rb_cBitmap, s_Bitmap_Alloc);
2822 rb_define_private_method(rb_cBitmap, "initialize", s_Bitmap_Initialize, -1);
2823 rb_define_method(rb_cBitmap, "focus_exec", s_Bitmap_FocusExec, -1);
2824 rb_define_method(rb_cBitmap, "save_to_file", s_Bitmap_SaveToFile, 1);
2826 if (parent != Qfalse)
2827 rb_eDialogError = rb_define_class_under(parent, "DialogError", rb_eStandardError);
2829 rb_eDialogError = rb_define_class("DialogError", rb_eStandardError);
2832 static VALUE *sTable1[] = {
2833 &sTextSymbol, &sTextFieldSymbol, &sRadioSymbol, &sButtonSymbol,
2834 &sCheckBoxSymbol, &sToggleButtonSymbol, &sPopUpSymbol, &sTextViewSymbol,
2835 &sViewSymbol, &sTableSymbol,
2836 &sResizableSymbol, &sHasCloseBoxSymbol,
2837 &sDialogSymbol, &sIndexSymbol, &sLineSymbol, &sTagSymbol,
2838 &sTypeSymbol, &sTitleSymbol, &sXSymbol, &sYSymbol,
2839 &sWidthSymbol, &sHeightSymbol, &sOriginSymbol, &sSizeSymbol,
2840 &sFrameSymbol, &sEnabledSymbol, &sEditableSymbol, &sHiddenSymbol,
2841 &sValueSymbol, &sRadioGroupSymbol, &sBlockSymbol, &sRangeSymbol,
2842 &sActionSymbol, &sAlignSymbol, &sRightSymbol, &sCenterSymbol,
2843 &sVerticalAlignSymbol, &sBottomSymbol, &sMarginSymbol, &sPaddingSymbol,
2844 &sSubItemsSymbol, &sHFillSymbol, &sVFillSymbol, &sFlexSymbol,
2845 &sIsProcessingActionSymbol,
2846 &sColorSymbol, &sForeColorSymbol, &sBackColorSymbol,
2847 &sStyleSymbol, &sSolidSymbol, &sTransparentSymbol, &sDotSymbol,
2848 &sLongDashSymbol, &sShortDashSymbol, &sDotDashSymbol,
2849 &sFontSymbol, &sFamilySymbol, &sWeightSymbol,
2850 &sDefaultSymbol, &sRomanSymbol, &sSwissSymbol,
2851 &sFixedSymbol, &sNormalSymbol, &sSlantSymbol, &sItalicSymbol,
2852 &sMediumSymbol, &sBoldSymbol, &sLightSymbol,
2854 &sOnCountSymbol, &sOnGetValueSymbol, &sOnSetValueSymbol, &sOnSelectionChangedSymbol,
2855 &sOnSetColorSymbol, &sIsItemEditableSymbol, &sIsDragAndDropEnabledSymbol, &sOnDragSelectionToRowSymbol,
2856 &sSelectionSymbol, &sColumnsSymbol, &sRefreshSymbol, &sHasPopUpMenuSymbol, &sOnPopUpMenuSelectedSymbol
2858 static const char *sTable2[] = {
2859 "text", "textfield", "radio", "button",
2860 "checkbox", "togglebutton", "popup", "textview",
2862 "resizable", "has_close_box",
2863 "dialog", "index", "line", "tag",
2864 "type", "title", "x", "y",
2865 "width", "height", "origin", "size",
2866 "frame", "enabled", "editable", "hidden",
2867 "value", "radio_group", "block", "range",
2868 "action", "align", "right", "center",
2869 "vertical_align", "bottom", "margin", "padding",
2870 "subitems", "hfill", "vfill", "flex",
2871 "is_processing_action",
2872 "color", "foreground_color", "background_color",
2873 "style", "solid", "transparent", "dot",
2874 "long_dash", "short_dash", "dot_dash",
2875 "font", "family", "weight",
2876 "default", "roman", "swiss",
2877 "fixed", "normal", "slant", "italic",
2878 "medium", "bold", "light",
2880 "on_count", "on_get_value", "on_set_value", "on_selection_changed",
2881 "on_set_color", "is_item_editable", "is_drag_and_drop_enabled", "on_drag_selection_to_row",
2882 "selection", "columns", "refresh", "has_popup_menu", "on_popup_menu_selected"
2885 for (i = 0; i < sizeof(sTable1) / sizeof(sTable1[0]); i++)
2886 *(sTable1[i]) = ID2SYM(rb_intern(sTable2[i]));
2889 /* Global variable to hold open dialogs */
2890 rb_define_variable("$ruby_dialogs", &gRubyDialogList);
2891 gRubyDialogList = rb_ary_new();