/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2011 Ruby-GNOME2 Project Team * Copyright (C) 2004-2006 Ruby-GNOME2 Project Team * Copyright (C) 2002,2003 Masahiro Sakai * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ #include "rbgprivate.h" #include #define RG_TARGET_NAMESPACE rbgobj_cEnum VALUE RG_TARGET_NAMESPACE; static ID id_new; static ID id_to_s; static ID id_to_i; /**********************************************************************/ static gchar * nick_to_const_name(const gchar *nick) { gchar *const_name; gchar *p; if (!nick) return NULL; const_name = g_strdup(nick); for (p = const_name; *p; p++) { if (*p == '-' || *p == ' ') *p = '_'; else *p = g_ascii_toupper(*p); } return const_name; } VALUE rg_enum_resolve_value(VALUE klass, VALUE nick) { VALUE value = Qnil; gchar *const_nick; ID const_nick_id; if (RVAL2CBOOL(rb_obj_is_kind_of(nick, klass))) return nick; nick = rb_funcall(nick, id_to_s, 0); const_nick = nick_to_const_name(RVAL2CSTR(nick)); const_nick_id = rb_intern(const_nick); if (rb_const_defined(klass, const_nick_id)) { value = rb_const_get(klass, const_nick_id); } g_free(const_nick); return value; } void rg_enum_add_constants(VALUE mod, GType enum_type, const gchar *strip_prefix) { GEnumClass *gclass; guint i; int prefix_len = strlen(strip_prefix); gclass = G_ENUM_CLASS(g_type_class_ref(enum_type)); for (i = 0; i < gclass->n_values; i++) { const GEnumValue* value = &gclass->values[i]; if (strncmp(value->value_name, strip_prefix, prefix_len)) { g_warning("\"%s\" doesn't have prefix \"%s\"", value->value_name, strip_prefix); } else { const char* name = value->value_name + prefix_len; rbgobj_define_const(mod, name, rbgobj_make_enum(value->value, enum_type)); } } g_type_class_unref(gclass); } /**********************************************************************/ typedef struct { GEnumClass* gclass; gint value; GEnumValue* info; } enum_holder; static void enum_free(enum_holder* p) { g_type_class_unref(p->gclass); free(p); } static enum_holder* enum_get_holder(VALUE obj) { enum_holder* p; Data_Get_Struct(obj, enum_holder, p); return p; } static VALUE make_enum(gint n, VALUE klass) { return rb_funcall(klass, id_new, 1, INT2NUM(n)); } VALUE rbgobj_make_enum(gint n, GType gtype) { return make_enum(n, GTYPE2CLASS(gtype)); } gint rbgobj_get_enum(VALUE obj, GType gtype) { VALUE klass; if (!g_type_is_a(gtype, G_TYPE_ENUM)) rb_raise(rb_eTypeError, "%s is not a %s: %s", g_type_name(gtype), g_type_name(G_TYPE_ENUM), RBG_INSPECT(obj)); /* for compatibility */ if (rb_obj_is_kind_of(obj, rb_cInteger)) obj = rbgobj_make_enum(NUM2INT(obj), gtype); klass = GTYPE2CLASS(gtype); if (!rb_obj_is_kind_of(obj, klass)) { VALUE enum_value; enum_value = rg_enum_resolve_value(klass, obj); if (!NIL_P(enum_value)) obj = enum_value; } if (rb_obj_is_kind_of(obj, klass)) return enum_get_holder(obj)->value; else rb_raise(rb_eTypeError, "not a %s: %s", rb_class2name(klass), RBG_INSPECT(obj)); } /**********************************************************************/ void rbgobj_init_enum_class(VALUE klass) { GEnumClass* gclass = g_type_class_ref(CLASS2GTYPE(klass)); guint i; for (i = 0; i < gclass->n_values; i++) { GEnumValue* entry = &(gclass->values[i]); gchar *const_nick_name; const_nick_name = nick_to_const_name(entry->value_nick); #if 0 { ID id = rb_intern(const_nick_name); if (rb_is_const_id(id)) { VALUE value; value = make_enum(entry->value, klass); rb_define_const(klass, const_nick_name, value); } } #else { if (const_nick_name) { VALUE value; value = make_enum(entry->value, klass); rbgobj_define_const(klass, const_nick_name, value); } } #endif g_free(const_nick_name); } g_type_class_unref(gclass); } static VALUE rg_s_range(VALUE self) { GEnumClass* gclass = g_type_class_ref(CLASS2GTYPE(self)); VALUE result = rb_range_new(INT2NUM(gclass->minimum), INT2NUM(gclass->maximum), FALSE); g_type_class_unref(gclass); return result; } struct enum_s_values_body_args { GEnumClass *gclass; VALUE self; }; static VALUE enum_s_values_body(VALUE value) { struct enum_s_values_body_args *args = (struct enum_s_values_body_args *)value; VALUE result = rb_ary_new(); guint i; for (i = 0; i < args->gclass->n_values; i++) rb_ary_push(result, make_enum(args->gclass->values[i].value, args->self)); return result; } static VALUE enum_s_values_ensure(VALUE gclass) { g_type_class_unref((GEnumClass *)gclass); return Qnil; } static VALUE rg_s_values(VALUE self) { struct enum_s_values_body_args args = { g_type_class_ref(CLASS2GTYPE(self)), self }; return rb_ensure(enum_s_values_body, (VALUE)&args, enum_s_values_ensure, (VALUE)args.gclass); } static VALUE enum_s_allocate(VALUE self) { GType gtype = CLASS2GTYPE(self); if (G_TYPE_IS_ABSTRACT(gtype)) { rb_raise(rb_eTypeError, "abstract class"); } else { enum_holder* p; VALUE result = Data_Make_Struct(self, enum_holder, NULL, enum_free, p); p->gclass = g_type_class_ref(gtype); p->info = NULL; return result; } } static VALUE rg_initialize(int argc, VALUE *argv, VALUE self) { enum_holder* p = enum_get_holder(self); VALUE rb_value; VALUE klass; rb_scan_args(argc, argv, "01", &rb_value); klass = CLASS_OF(self); switch (TYPE(rb_value)) { case RUBY_T_NIL: p->value = 0; break; case RUBY_T_FIXNUM: p->value = NUM2UINT(rb_value); break; case RUBY_T_STRING: case RUBY_T_SYMBOL: { const gchar *name; name = RVAL2CSTR_ACCEPT_SYMBOL(rb_value); p->info = g_enum_get_value_by_name(p->gclass, name); if (!p->info) { gchar *nick; nick = rbg_name_to_nick(name); p->info = g_enum_get_value_by_nick(p->gclass, nick); g_free(nick); } if (!p->info) { rb_raise(rb_eArgError, "unknown enum name: <%s>(%s)", name, g_type_name(G_TYPE_FROM_CLASS(p->gclass))); } p->value = p->info->value; break; } default: if (RVAL2CBOOL(rb_obj_is_kind_of(rb_value, klass))) { p->value = NUM2INT(rb_funcall(rb_value, id_to_i, 0)); } else { rb_raise(rb_eArgError, "enum value must be one of " "nil, Fixnum, String, Symbol o %s: " "<%s>(%s)", RBG_INSPECT(klass), RBG_INSPECT(rb_value), g_type_name(G_TYPE_FROM_CLASS(p->gclass))); } break; } if (!p->info) { p->info = g_enum_get_value(p->gclass, p->value); } return Qnil; } static VALUE rg_to_i(VALUE self) { enum_holder* p = enum_get_holder(self); return INT2NUM(p->value); } static VALUE rg_name(VALUE self) { enum_holder* p = enum_get_holder(self); return p->info ? rb_str_new2(p->info->value_name) : Qnil; } static VALUE rg_nick(VALUE self) { enum_holder* p = enum_get_holder(self); return p->info ? rb_str_new2(p->info->value_nick) : Qnil; } static VALUE rg_inspect(VALUE self) { const char* cname = rb_class2name(CLASS_OF(self)); enum_holder* p = enum_get_holder(self); gchar* str; VALUE result; if (p->info) str = g_strdup_printf("#<%s %s>", cname, p->info->value_nick); else str = g_strdup_printf("#<%s %d>", cname, p->value); result = rb_str_new2(str); g_free(str); return result; } static VALUE rg_operator_enum_eqv(VALUE self, VALUE rhs) { enum_holder* p = enum_get_holder(self); GType gtype = G_TYPE_FROM_CLASS(p->gclass); VALUE klass = GTYPE2CLASS(gtype); if (!rb_obj_is_kind_of(rhs, rb_cInteger)) { rhs = rg_enum_resolve_value(klass, rhs); if (CLASS_OF(rhs) != CLASS_OF(self)) return Qnil; } return CBOOL2RVAL(rbgobj_get_enum(self, gtype) == rbgobj_get_enum(rhs, gtype)); } static VALUE rg_hash(VALUE self) { enum_holder* p = enum_get_holder(self); return UINT2NUM(p->value ^ G_TYPE_FROM_CLASS(p->gclass)); } static VALUE rg_coerce(VALUE self, VALUE other) { enum_holder *holder; GType gtype; if (!rb_obj_is_kind_of(other, rb_cInteger)) rb_raise(rb_eTypeError, "can't coerce"); holder = enum_get_holder(self); gtype = G_TYPE_FROM_CLASS(holder->gclass); other = rbgobj_make_enum(NUM2INT(other), gtype); return rb_ary_new3(2, other, self); } /**********************************************************************/ void Init_gobject_genums(void) { id_new = rb_intern("new"); id_to_s = rb_intern("to_s"); id_to_i = rb_intern("to_i"); RG_TARGET_NAMESPACE = G_DEF_CLASS(G_TYPE_ENUM, "Enum", mGLib); rbg_define_singleton_method(RG_TARGET_NAMESPACE, "gtype", generic_s_gtype, 0); rbg_define_method(RG_TARGET_NAMESPACE, "gtype", generic_gtype, 0); RG_DEF_SMETHOD(range, 0); RG_DEF_SMETHOD(values, 0); rb_define_alloc_func(RG_TARGET_NAMESPACE, enum_s_allocate); RG_DEF_METHOD(initialize, -1); RG_DEF_METHOD(to_i, 0); RG_DEF_METHOD(name, 0); RG_DEF_METHOD(nick, 0); RG_DEF_METHOD(inspect, 0); RG_DEF_METHOD_OPERATOR("==", enum_eqv, 1); RG_DEF_METHOD(hash, 0); RG_DEF_ALIAS("eql?", "=="); /* for compatibility */ RG_DEF_METHOD(coerce, 1); RG_DEF_ALIAS("to_int", "to_i"); }