Tweaked argument handling in Message#initialize.
[ruby-edje.git] / src / rb_edje.c
1 /*
2  * $Id: rb_edje.c 332 2005-04-28 18:37:16Z tilman $
3  *
4  * Copyright (C) 2004 Tilman Sauerbeck (tilman at code-monkey de)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <ruby.h>
22 #include <stdbool.h>
23
24 #include <Edje.h>
25 #include <evas/rb_evas.h>
26 #include <evas/rb_evas_object.h>
27
28 #define __RB_EDJE_C
29 #include "rb_edje.h"
30 #include "rb_edje_main.h"
31 #include "rb_part.h"
32
33 VALUE cEdje;
34 static VALUE cMsg, eEdjeError;
35
36 static void c_mark (RbEdje *e)
37 {
38         c_evas_object_mark (&e->real);
39
40         if (!NIL_P (e->parts))
41                 rb_gc_mark (e->parts);
42
43         if (!NIL_P (e->callbacks))
44                 rb_gc_mark (e->callbacks);
45
46         if (!NIL_P (e->on_text_changed_cb))
47                 rb_gc_mark (e->on_text_changed_cb);
48 }
49
50 static void c_free (RbEdje *e)
51 {
52         c_evas_object_free (&e->real, false);
53         free (e);
54
55         edje_shutdown ();
56 }
57
58 /*
59  * call-seq:
60  *  Edje::Edje.new(evas) => edje
61  *
62  * Creates an Edje::Edje object.
63  */
64 static VALUE c_new (VALUE klass, VALUE evas)
65 {
66         VALUE self, argv[1];
67         RbEdje *edje = NULL;
68
69         CHECK_CLASS (evas, cEvas);
70         GET_OBJ (evas, RbEvas, e);
71
72         edje_init ();
73
74         self = Data_Make_Struct (klass, RbEdje, c_mark, c_free, edje);
75
76         edje->real.real = edje_object_add (e->real);
77         edje->parts = Qnil;
78         edje->callbacks = Qnil;
79         edje->on_text_changed_cb = Qnil;
80
81         argv[0] = evas;
82         rb_obj_call_init (self, 1, argv);
83
84         return self;
85 }
86
87 /*
88  * call-seq:
89  *  edje.freeze => nil
90  *
91  * Freezes <i>edje</i>.
92  */
93 static VALUE c_freeze (VALUE self)
94 {
95         GET_OBJ (self, RbEdje, e);
96
97         edje_object_freeze (e->real.real);
98
99         return Qnil;
100 }
101
102 /*
103  * call-seq:
104  *  edje.thaw => nil
105  *
106  * Thaws <i>edje</i>.
107  */
108 static VALUE c_thaw (VALUE self)
109 {
110         GET_OBJ (self, RbEdje, e);
111
112         edje_object_thaw (e->real.real);
113
114         return Qnil;
115 }
116
117 /*
118  * call-seq:
119  *  edje.load(eet, group) => nil
120  *
121  * Loads <i>eet</i> into <i>edje</i>. <i>group</i> is the
122  * name of the group to be displayed.
123  */
124 static VALUE c_load (VALUE self, VALUE eet, VALUE group)
125 {
126         GET_OBJ (self, RbEdje, e);
127
128         Check_Type (eet, T_STRING);
129         Check_Type (group, T_STRING);
130
131         if (!edje_object_file_set (e->real.real, StringValuePtr (eet),
132                                    StringValuePtr (group)))
133                 rb_raise (rb_eException, "Cannot load eet");
134
135         return Qnil;
136 }
137
138 /*
139  * call-seq:
140  *  edje.get_size_min => array
141  *
142  * Returns an array that contains the minimum size
143  * of <i>edje</i>.
144  */
145 static VALUE c_get_size_min (VALUE self)
146 {
147         int w = 0, h = 0;
148
149         GET_OBJ (self, RbEdje, e);
150
151         edje_object_size_min_get (e->real.real, &w, &h);
152
153         return rb_ary_new3 (2, INT2FIX (w), INT2FIX (h));
154 }
155
156 /*
157  * call-seq:
158  *  edje.get_size_max => array
159  *
160  * Returns an array that contains the maximum size
161  * of <i>edje</i>.
162  */
163 static VALUE c_get_size_max (VALUE self)
164 {
165         int w = 0, h = 0;
166
167         GET_OBJ (self, RbEdje, e);
168
169         edje_object_size_max_get (e->real.real, &w, &h);
170
171         return rb_ary_new3 (2, INT2FIX (w), INT2FIX (h));
172 }
173
174 /*
175  * call-seq:
176  *  edje.part_exists?(part) => true or false
177  *
178  * Returns true if <i>edje</i> has a part called <i>part</i>,
179  * else returns false.
180  */
181 static VALUE c_part_exists_get (VALUE self, VALUE name)
182 {
183         int r;
184
185         GET_OBJ (self, RbEdje, e);
186
187         Check_Type (name, T_STRING);
188
189         r = edje_object_part_exists (e->real.real, StringValuePtr (name));
190
191         return r ? Qtrue : Qfalse;
192 }
193
194 /*
195  * call-seq:
196  *  edje.part(part_name) => part
197  *
198  * Returns the <code>Edje::Part</code> object that corresponds to
199  * <i>part_name</i>. If there's no part with that name in <i>edje</i>,
200  * an exception is raised.
201  */
202 static VALUE c_part_get (VALUE self, VALUE name)
203 {
204         VALUE part;
205         const char *cname = StringValuePtr (name);
206
207         GET_OBJ (self, RbEdje, e);
208
209         if (!edje_object_part_exists (e->real.real, cname)) {
210                 rb_raise (rb_eException, "Unknown part name - %s", cname);
211                 return Qnil;
212         }
213
214         if (NIL_P (e->parts))
215                 e->parts = rb_hash_new ();
216
217         if (NIL_P (part = rb_hash_aref (e->parts, name))) {
218                 part = TO_PART (self, name);
219                 rb_hash_aset (e->parts, name, part);
220         }
221
222         return part;
223 }
224
225 static void on_text_changed (void *data, Evas_Object *eo,
226                              const char *part_name)
227 {
228         VALUE self = (VALUE) data, part, name;
229
230         GET_OBJ (self, RbEdje, e);
231
232         name = rb_str_new2 (part_name);
233
234         if (NIL_P (e->parts))
235                 e->parts = rb_hash_new ();
236
237         if (NIL_P (part = rb_hash_aref (e->parts, name))) {
238                 part = TO_PART (self, name);
239                 rb_hash_aset (e->parts, name, part);
240         }
241
242         rb_funcall (e->on_text_changed_cb,
243                     rb_intern ("call"), 1, part);
244 }
245
246 /*
247  * call-seq:
248  *  edje.on_text_changed { |part_obj| block }
249  *
250  * Registers a callback that will get called when the text
251  * of any part is changed in <i>edje</i>.
252  * The block is passed the <code>Edje::Part</code> object
253  * of which the text changed.
254  */
255 static VALUE c_on_text_changed (VALUE self)
256 {
257         GET_OBJ (self, RbEdje, e);
258
259         if (!rb_block_given_p ())
260                 return Qnil;
261
262         e->on_text_changed_cb = rb_block_proc ();
263
264         edje_object_text_change_cb_set (e->real.real, on_text_changed,
265                                         (void *) self);
266
267         return Qnil;
268 }
269
270 /*
271  * call-seq:
272  *  edje.emit_signal(signal, source) => nil
273  *
274  * Emits a signal to <i>edje</i>.
275  *
276  *  edje.emit_signal("signal_foo", "part_bar") #=> nil
277  */
278 static VALUE c_emit_signal (VALUE self, VALUE signal, VALUE source)
279 {
280         GET_OBJ (self, RbEdje, e);
281
282         Check_Type (signal, T_STRING);
283         Check_Type (source, T_STRING);
284
285         edje_object_signal_emit (e->real.real, StringValuePtr (signal),
286                                  StringValuePtr (source));
287
288         return Qnil;
289 }
290
291 static void on_signal (void *data, Evas_Object *o,
292                        const char *signal, const char *src)
293 {
294         rb_funcall ((VALUE) data, rb_intern ("call"), 2,
295                     rb_str_new2 (signal), rb_str_new2 (src));
296 }
297
298 /*
299  * call-seq:
300  *  edje.on_signal(signal [, source]) { |signal, source| block } => nil
301  *
302  * Registers a callback that will get called when <i>signal</i>
303  * is emitted by <i>source</i>.
304  * If source is nil, "*" will be used instead.
305  * The block is passed two strings, signal and source, which identify
306  * the emission.
307  */
308 static VALUE c_on_signal (int argc, VALUE *argv, VALUE self)
309 {
310         VALUE signal, src, cb;
311         char *ssrc = "*";
312
313         GET_OBJ (self, RbEdje, e);
314
315         rb_scan_args (argc, argv, "11", &signal, &src);
316
317         Check_Type (signal, T_STRING);
318
319         if (!NIL_P (src)) {
320                 Check_Type (src, T_STRING);
321                 ssrc = StringValuePtr (src);
322         }
323
324         if (!rb_block_given_p ())
325                 return Qnil;
326
327         cb = rb_block_proc ();
328
329         if (NIL_P (e->callbacks))
330                 e->callbacks = rb_ary_new ();
331
332         rb_ary_push (e->callbacks, cb);
333
334         edje_object_signal_callback_add (e->real.real,
335                                          StringValuePtr (signal),
336                                          ssrc, on_signal, (void *) cb);
337
338         return Qnil;
339 }
340
341 /*
342  * call-seq:
343  *  edje.play? => true or false
344  *
345  * Returns true if <i>edje</i> is in play mode, else returns false.
346  */
347 static VALUE c_play_get (VALUE self)
348 {
349         GET_OBJ (self, RbEdje, e);
350
351         return edje_object_play_get (e->real.real) ? Qtrue : Qfalse;
352 }
353
354 /*
355  * call-seq:
356  *  edje.play(true or false)
357  *
358  * Sets <i>edje</i> to play resp. pause mode.
359  */
360 static VALUE c_play_set (VALUE self, VALUE val)
361 {
362         GET_OBJ (self, RbEdje, e);
363
364         CHECK_BOOL(val);
365
366         edje_object_play_set (e->real.real, val == Qtrue);
367
368         return Qnil;
369 }
370
371 /*
372  * call-seq:
373  *  edje.animation? => true or false
374  *
375  * Returns the animation state of <i>edje</i>.
376  */
377 static VALUE c_animation_get (VALUE self)
378 {
379         GET_OBJ (self, RbEdje, e);
380
381         return edje_object_animation_get (e->real.real) ? Qtrue : Qfalse;
382 }
383
384 /*
385  * call-seq:
386  *  edje.animation(true or false)
387  *
388  * Sets the animation state of <i>edje</i>.
389  */
390 static VALUE c_animation_set (VALUE self, VALUE val)
391 {
392         GET_OBJ (self, RbEdje, e);
393
394         CHECK_BOOL(val);
395
396         edje_object_animation_set (e->real.real, val == Qtrue);
397
398         return Qnil;
399 }
400
401 static VALUE c_data_get (VALUE self, VALUE key)
402 {
403         const char *s;
404
405         GET_OBJ (self, RbEdje, e);
406
407         Check_Type (key, T_STRING);
408
409         s = edje_object_data_get (e->real.real, StringValuePtr (key));
410
411         return s ? rb_str_new2 (s) : Qnil;
412 }
413
414 static Edje_Message_Type get_msg_type (VALUE val)
415 {
416         VALUE ary, entry;
417         Edje_Message_Type type;
418         int i, len;
419
420         if (NIL_P (val))
421                 return EDJE_MESSAGE_NONE;
422
423         if (!NIL_P (rb_check_string_type (val))) {
424                 return EDJE_MESSAGE_STRING;
425         } else if (rb_obj_is_kind_of (val, rb_cFixnum)) {
426                 return EDJE_MESSAGE_INT;
427         } else if (rb_obj_is_kind_of (val, rb_cFloat)) {
428                 return EDJE_MESSAGE_FLOAT;
429         } else if (NIL_P (ary = rb_check_array_type (val)))
430                 return EDJE_MESSAGE_NONE;
431
432         len = RARRAY (ary)->len;
433         if (len <= 0)
434                 return EDJE_MESSAGE_NONE;
435
436         entry = rb_ary_entry (ary, 0);
437
438         if (rb_obj_is_kind_of (entry, rb_cFixnum))
439                 return EDJE_MESSAGE_INT_SET;
440         else if (rb_obj_is_kind_of (entry, rb_cFloat))
441                 return EDJE_MESSAGE_FLOAT_SET;
442         else if (NIL_P (rb_check_string_type (entry)))
443                 return EDJE_MESSAGE_NONE;
444
445         /* first entry is a string.
446          * so if we only have one entry, it's a string set
447          */
448         if (len == 1)
449                 return EDJE_MESSAGE_STRING_SET;
450
451         entry = rb_ary_entry (ary, 1);
452
453         if (!NIL_P (rb_check_string_type (entry)))
454                 type = EDJE_MESSAGE_STRING_SET;
455         else if (rb_obj_is_kind_of (entry, rb_cFixnum))
456                 type = len == 2 ? EDJE_MESSAGE_STRING_INT : EDJE_MESSAGE_STRING_INT_SET;
457         else if (rb_obj_is_kind_of (entry, rb_cFloat))
458                 type = len == 2 ? EDJE_MESSAGE_STRING_FLOAT :EDJE_MESSAGE_STRING_FLOAT_SET;
459         else
460                 return EDJE_MESSAGE_NONE;
461
462         switch (type) {
463                 case EDJE_MESSAGE_STRING_SET:
464                         for (i = 2; i < len; i++) {
465                                 entry = rb_ary_entry (ary, i);
466                                 if (NIL_P(rb_check_string_type (entry)))
467                                         return EDJE_MESSAGE_NONE;
468                         }
469
470                         break;
471                 case EDJE_MESSAGE_INT_SET:
472                 case EDJE_MESSAGE_STRING_INT_SET:
473                         for (i = 2; i < len; i++) {
474                                 entry = rb_ary_entry (ary, i);
475                                 if (!rb_obj_is_kind_of (entry, rb_cFixnum))
476                                         return EDJE_MESSAGE_NONE;
477                         }
478
479                         break;
480                 case EDJE_MESSAGE_FLOAT_SET:
481                 case EDJE_MESSAGE_STRING_FLOAT_SET:
482                         for (i = 2; i < len; i++) {
483                                 entry = rb_ary_entry (ary, 2);
484                                 if (!rb_obj_is_kind_of (entry, rb_cFloat))
485                                         return EDJE_MESSAGE_NONE;
486                         }
487
488                         break;
489                 default:
490                         break;
491         }
492
493         return type;
494 }
495
496 static VALUE c_send_message (VALUE self, VALUE msg)
497 {
498         Edje_Message_String msg_s;
499         Edje_Message_Int msg_i;
500         Edje_Message_Float msg_f;
501         Edje_Message_String_Set *s_set = NULL;
502         Edje_Message_Int_Set *i_set = NULL;
503         Edje_Message_Float_Set *f_set = NULL;
504         Edje_Message_String_Int si;
505         Edje_Message_String_Float sf;
506         Edje_Message_String_Int_Set *si_set = NULL;
507         Edje_Message_String_Float_Set *sf_set = NULL;
508         Edje_Message_Type type;
509         void *data = NULL;
510         int id, i, len = 0;
511         bool free_data = false;
512         VALUE v, ary, entry;
513
514         GET_OBJ (self, RbEdje, e);
515
516         CHECK_CLASS (msg, cMsg);
517
518         id = NUM2INT (rb_iv_get (msg, "@id"));
519         v = rb_iv_get (msg, "@value");
520
521         type = get_msg_type (v);
522         ary = rb_check_array_type (v);
523
524         switch (type) {
525                 case EDJE_MESSAGE_NONE:
526                         rb_raise (eEdjeError, "unsupported value");
527                         return Qnil;
528                 case EDJE_MESSAGE_SIGNAL:
529                         return Qnil; /* cannot happen */
530                 case EDJE_MESSAGE_STRING:
531                         msg_s.str = StringValuePtr (v);
532                         data = &msg_s;
533                         break;
534                 case EDJE_MESSAGE_INT:
535                         msg_i.val = FIX2INT (v);
536                         data = &msg_i;
537                         break;
538                 case EDJE_MESSAGE_FLOAT:
539                         msg_f.val = NUM2DBL (v);
540                         data = &msg_f;
541                         break;
542                 case EDJE_MESSAGE_STRING_SET:
543                         s_set = malloc (sizeof (Edje_Message_String_Set) + ((len - 1) * sizeof (char *)));
544                         s_set->count = len;
545                         free_data = true;
546
547                         for (i = 0; i < len; i++) {
548                                 entry = rb_ary_entry (ary, i);
549                                 s_set->str[i] = StringValuePtr (entry);
550                         }
551
552                         data = s_set;
553
554                         break;
555                 case EDJE_MESSAGE_INT_SET:
556                         i_set = malloc (sizeof (Edje_Message_Int_Set) + ((len - 1) * sizeof (int)));
557                         i_set->count = len;
558                         free_data = true;
559
560                         for (i = 0; i < len; i++) {
561                                 entry = rb_ary_entry (ary, i);
562                                 i_set->val[i] = FIX2INT (entry);
563                         }
564
565                         data = i_set;
566
567                         break;
568                 case EDJE_MESSAGE_FLOAT_SET:
569                         f_set = malloc (sizeof (Edje_Message_Float_Set) + ((len - 1) * sizeof (double)));
570                         f_set->count = len;
571                         free_data = true;
572
573                         for (i = 0; i < len; i++) {
574                                 entry = rb_ary_entry (ary, i);
575                                 f_set->val[i] = NUM2DBL (entry);
576                         }
577
578                         data = f_set;
579
580                         break;
581                 case EDJE_MESSAGE_STRING_INT:
582                         entry = rb_ary_entry (ary, 0);
583                         si.str = StringValuePtr (entry);
584                         entry = rb_ary_entry (ary, 1);
585                         si.val = FIX2INT (entry);
586
587                         data = &si;
588
589                         break;
590                 case EDJE_MESSAGE_STRING_FLOAT:
591                         entry = rb_ary_entry (ary, 0);
592                         sf.str = StringValuePtr (entry);
593                         entry = rb_ary_entry (ary, 1);
594                         sf.val = NUM2DBL (entry);
595
596                         data = &sf;
597
598                         break;
599                 case EDJE_MESSAGE_STRING_INT_SET:
600                         si_set = malloc (sizeof (Edje_Message_String_Int_Set) + ((len - 1) * sizeof (int)));
601                         si_set->count = len - 1;
602                         free_data = true;
603
604                         entry = rb_ary_entry (ary, 0);
605                         si_set->str = StringValuePtr (entry);
606
607                         for (i = 1; i < len; i++) {
608                                 entry = rb_ary_entry (ary, i);
609                                 si_set->val[i - 1] = FIX2INT (entry);
610                         }
611
612                         data = &si_set;
613
614                         break;
615                 case EDJE_MESSAGE_STRING_FLOAT_SET:
616                         sf_set = malloc (sizeof (Edje_Message_String_Float_Set) + ((len - 1) * sizeof (double)));
617                         sf_set->count = len - 1;
618                         free_data = true;
619
620                         entry = rb_ary_entry (ary, 0);
621                         sf_set->str = StringValuePtr (entry);
622
623                         for (i = 1; i < len; i++) {
624                                 entry = rb_ary_entry (ary, i);
625                                 sf_set->val[i - 1] = NUM2DBL (entry);
626                         }
627
628                         data = &sf_set;
629
630                         break;
631         }
632
633         edje_object_message_send (e->real.real, type, id, data);
634
635         if (free_data)
636                 free (data);
637
638         return Qnil;
639 }
640
641 static VALUE c_msg_init (int argc, VALUE *argv, VALUE self)
642 {
643         VALUE id, val;
644
645         if (argc == 2)
646                 rb_scan_args (argc, argv, "11", &id, &val);
647         else
648                 rb_scan_args (argc, argv, "1*", &id, &val);
649
650         Check_Type (id, T_FIXNUM);
651
652         rb_iv_set (self, "@id", UINT2NUM (id));
653         rb_iv_set (self, "@value", val);
654
655         return self;
656 }
657
658 void Init_Edje (void)
659 {
660         cEdje = rb_define_class_under (mEdje, "Edje", cEvasObject);
661
662         rb_define_singleton_method (cEdje, "new", c_new, 1);
663         rb_define_method (cEdje, "freeze", c_freeze, 0);
664         rb_define_method (cEdje, "thaw", c_thaw, 0);
665         rb_define_method (cEdje, "load", c_load, 2);
666         rb_define_method (cEdje, "get_size_min", c_get_size_min, 0);
667         rb_define_method (cEdje, "get_size_max", c_get_size_max, 0);
668         rb_define_method (cEdje, "part_exists?", c_part_exists_get, 1);
669         rb_define_method (cEdje, "part", c_part_get, 1);
670         rb_define_method (cEdje, "on_text_changed", c_on_text_changed, 0);
671         rb_define_method (cEdje, "emit_signal", c_emit_signal, 2);
672         rb_define_method (cEdje, "on_signal", c_on_signal, -1);
673         rb_define_method (cEdje, "play?", c_play_get, 0);
674         rb_define_method (cEdje, "play=", c_play_set, 1);
675         rb_define_method (cEdje, "animation?", c_animation_get, 0);
676         rb_define_method (cEdje, "animation=", c_animation_set, 1);
677         rb_define_method (cEdje, "data", c_data_get, 1);
678         rb_define_method (cEdje, "send_message", c_send_message, 1);
679
680         cMsg = rb_define_class_under (mEdje, "Message", rb_cObject);
681
682         rb_define_method (cMsg, "initialize", c_msg_init, -1);
683
684         rb_define_attr (cMsg, "id", 1, 1);
685         rb_define_attr (cMsg, "value", 1, 1);
686
687         eEdjeError = rb_define_class_under (mEdje, "EdjeError",
688                                            rb_eStandardError);
689 }