ext/main.cpp

Summary

Maintainability
Test Coverage
/*
 * main.cpp
 *
 *  Created on: 04.05.2012
 *      Author: hanmac
 */

#include "main.hpp"
#include "wxEvtHandler.hpp"

#include <wx/arrstr.h>

#include <wx/filename.h>

infoholdertype infoklassholder;
typeholdertype typeklassholder;

enumregistertype enumregister;

datatypeholdertype datatypeholder;
datatypeholdertype datatypeholder_const;

VALUE global_holder;

typedef std::map<VALUE,std::size_t> ref_countertype;
ref_countertype ref_counter;


void rwx_refobject(VALUE object)
{
    if(ref_counter[object] == 0)
        rb_hash_aset(global_holder,RB_INT2NUM(object),object);
    ref_counter[object]++;
}

bool rwx_unrefobject(VALUE object)
{
    if(ref_counter[object] != 0)
        ref_counter[object]--;
    if(ref_counter[object] == 0) {
        rb_hash_delete(global_holder,RB_INT2NUM(object));
        return true;
    }
    return false;
}

void registerInfo(VALUE klass, const wxClassInfo * info)
{
    infoholdertype::iterator it = infoklassholder.find(info);
    if(it != infoklassholder.end()) {
        //if(it->second != klass)
            //rb_warn("already registered wxClass (%s) as %" PRIsVALUE " but want it as %" PRIsVALUE, wxString(info->GetClassName()).c_str().AsChar(), RB_CLASSNAME(it->second), RB_CLASSNAME(klass));
    } else {
        infoklassholder[info] = klass;
    }
}

void registerDataType(VALUE klass, RUBY_DATA_FUNC freefunc, size_t (*sizefunc)(const void *))
{

    if(!NIL_P(klass))
    {

        rb_data_type_t *parent = NULL;
        if(rb_obj_is_kind_of(klass, rb_cClass)) {
            parent = unwrapDataType(rb_class_superclass(klass));
            //if the class does include EvtHandler,
            // add it as a parent if class doesnt already have one
            if(!parent && rb_class_inherited_p(klass, rb_mWXEvtHandler) == Qtrue) {
                parent = unwrapDataType(rb_mWXEvtHandler);
            }
        }

        rb_data_type_t str = {
            rb_class2name(klass),
            {0, freefunc, sizefunc,},
            parent, NULL,
        };

        datatypeholder[klass] = str;
        if(freefunc) {
            rb_data_type_t str_const = {
                (std::string(rb_class2name(klass)) + "(const)").c_str(),
                {0, 0, sizefunc,},
                &datatypeholder[klass], NULL,
            };
            datatypeholder_const[klass] = str_const;
        }else {
            datatypeholder_const[klass] = str;
        }
    }
}

void registerDataType(VALUE klass)
{
    registerDataType(klass, RUBY_TYPED_NEVER_FREE, NULL);
}

bool check_class(VALUE obj, VALUE klass)
{
    bool result = rb_obj_is_kind_of(obj, klass);
    if(!result)
        rb_raise(rb_eTypeError,
            "Expected %" PRIsVALUE " got %" PRIsVALUE "!",
            RB_CLASSNAME(klass),
            RB_OBJ_CLASSNAME(obj)
        );
    return result;
}

VALUE wrap(wxObject *obj, wxClassInfo *info)
{
    //wxClassInfo *info = obj->GetClassInfo();
    VALUE klass = wrapClass(info);
    if(!NIL_P(klass))
    {
        return wrapTypedPtr(obj,klass);
    }
    rb_warn("%s type unknown",wxString(info->GetClassName()).c_str().AsChar());
    return Qnil;
}


void* unwrapTypedPtr(const VALUE &obj, rb_data_type_t* rbdata)
{
    if(NIL_P(obj))
        return NULL;

    if(!rbdata) {
        rb_raise(rb_eTypeError,"%" PRIsVALUE " unknown datatype", RB_OBJ_CLASSNAME(obj));
        return NULL;
    }

    const rb_data_type_t* rbdata_obj = RTYPEDDATA_TYPE(obj);

    if(rbdata_obj != rbdata) {
        if(!rb_typeddata_inherited_p(rbdata_obj, rbdata)) {
            rb_warn("%s is not a %s but inherit from %s",
                rbdata_obj->wrap_struct_name, rbdata->wrap_struct_name,
                rbdata_obj->parent->wrap_struct_name
            );
        }
    }

    void* data = Check_TypedStruct(obj, rbdata);
    if(!data) {
        rb_raise(
            rb_eRuntimeError, "destroyed object of %" PRIsVALUE, RB_OBJ_CLASSNAME(obj)
        );
        return NULL;
    }
    return data;

}

VALUE wrapClass(const wxClassInfo * info)
{
    infoholdertype::iterator it = infoklassholder.find(info);
    if(it != infoklassholder.end())
        return it->second;
    if(const wxClassInfo *base = info->GetBaseClass1())
        return wrapClass(base);
    return Qnil;
}

const wxClassInfo* unwrapClass(VALUE klass)
{
    for(infoholdertype::iterator it = infoklassholder.begin(); it != infoklassholder.end(); ++it)
    {
        if(it->second == klass)
            return it->first;
    }
    return unwrapClass(rb_class_superclass(klass));
}

rb_data_type_t* unwrapDataType(const VALUE& klass)
{
    if(klass == rb_cObject)
        return NULL;

    datatypeholdertype::iterator it = datatypeholder.find(klass);
    if(it != datatypeholder.end())
        return &it->second;

    for(it = datatypeholder.begin(); it != datatypeholder.end(); ++it)
    {
        if(wxString(rb_class2name(it->first)) == wxString(rb_class2name(klass))) {
            //rb_warn("compare broken for %s", rb_class2name(klass));
            return &it->second;
        }
    }

    if(rb_obj_is_kind_of(klass, rb_cClass))
        return unwrapDataType(rb_class_superclass(klass));

    return NULL;
}


VALUE wrapTypedPtr(void *arg,VALUE klass, bool allowNull)
{
    if(arg || allowNull){
        rb_data_type_t* datatype = unwrapDataType(klass);
        if(!datatype)
            rb_fatal("%" PRIsVALUE " unknown datatype", RB_CLASSNAME(klass));

        return TypedData_Wrap_Struct(klass, datatype, arg);
    }
    return Qnil;
}

DLL_LOCAL void not_valid(VALUE val, VALUE klass)
{
    rb_raise(
        rb_eTypeError,
        "%" PRIsVALUE " is not valid %" PRIsVALUE,
        RB_OBJ_STRING(val), RB_CLASSNAME(klass)
    );
}

#define type_wrap(t,get,set) \
template <>\
t unwrap< t >(const VALUE &val )\
{\
    return get(val);\
}\
template <>\
VALUE wrap< t >(const t &st )\
{\
    return set(st);\
}\

#define type_wrap_array(t) \
template <>\
VALUE wrap< t >(const t &st )\
{\
    VALUE ary = rb_ary_new();\
    for(t::const_iterator it = st.begin(); it != st.end(); ++it)\
        rb_ary_push(ary,wrap(*it));\
    return ary;\
}\
template <>\
t unwrap< t >(const VALUE &val )\
{\
    t arry;\
    if(NIL_P(val))\
        return arry;\
    VALUE dp(rb_Array(val));\
    std::size_t length = RARRAY_LEN(dp);\
    for(std::size_t i = 0; i < length; ++i)\
        arry.Add(unwrap<t::value_type>(RARRAY_AREF(dp,i)));\
    return arry;\
}

template <>
bool unwrap< bool >(const VALUE &val )
{
    return RTEST(val);
}

template <>
VALUE wrap< bool >(const bool &st )
{
    return st ? Qtrue : Qfalse;
}

type_wrap(int,RB_NUM2INT,RB_INT2NUM)
type_wrap(double,NUM2DBL,DBL2NUM)
type_wrap(unsigned int,RB_NUM2UINT,RB_UINT2NUM)
type_wrap(long,RB_NUM2LONG,RB_LONG2NUM)
type_wrap(unsigned long,RB_NUM2ULONG,RB_ULONG2NUM)

template <>
VALUE wrap< wxString >(const wxString &st )
{
#ifdef HAVE_RUBY_ENCODING_H
    return rb_enc_str_new(st.c_str(),strlen(st.c_str()),rb_utf8_encoding());
#else
    return rb_str_new2(st.c_str());
#endif
}
template <>
VALUE wrap< wxChar >(const wxChar &c )
{
    return wrap(wxString(c));
}

template <>
char* unwrap< char* >(const VALUE &val )
{
    if(NIL_P(val))
        return NULL;
    if (rb_obj_is_kind_of(val, rb_cString)){
        return RSTRING_PTR(val);
    }
    else
        return unwrap< char* >(rb_String(val));
}

template <>
wxString unwrap< wxString >(const VALUE &val )
{
    if(NIL_P(val))
        return wxEmptyString;
    else
        return wxString(unwrap< char* >(val));
}

type_wrap_array(wxArrayString)
type_wrap_array(wxArrayInt)

template <>
VALUE wrap< wxDateTime >(const wxDateTime &st )
{
    if(!st.IsValid())
        return Qnil;
    return rb_time_new(st.GetTicks(),st.GetMillisecond() * 10);
}

template <>
wxDateTime unwrap< wxDateTime >(const VALUE &val )
{
// need extra check
//    if(NIL_P(val))
//        return wxDefaultDateTime;

    wxDateTime result;
    result.SetToCurrent();
    result.MakeTimezone(
        RB_NUM2UINT(rb_funcall(val,rb_intern("gmt_offset"),0)) - 3600,
        RTEST(rb_funcall(val,rb_intern("dst?"),0))
    );

    result.Set(
        RB_NUM2UINT(rb_funcall(val,rb_intern("day"),0)),
        (wxDateTime::Month)(RB_NUM2UINT(rb_funcall(val,rb_intern("month"),0))-1),
        RB_NUM2UINT(rb_funcall(val,rb_intern("year"),0)),
        RB_NUM2UINT(rb_funcall(val,rb_intern("hour"),0)),
        RB_NUM2UINT(rb_funcall(val,rb_intern("min"),0)),
        RB_NUM2UINT(rb_funcall(val,rb_intern("sec"),0)),
        RB_NUM2UINT(rb_funcall(val,rb_intern("usec"),0)) / 1000
    );
    return result;
}

enumtype* registerEnum(const std::string& name,const std::string& type ,int def)
{
    enumtype *etype = new enumtype;
    enumregister.insert(std::make_pair(type,etype));
    etype->name = name;
    etype->allow_array = true;
    etype->defaults = def;
    return etype;
}


VALUE wrapenum(const int &arg, const std::string& name)
{
    enumtype::value_type &enummap = enumregister[name]->values;
    enumtype::value_type::iterator it = enummap.find(arg);
    if(it != enummap.end())
        return RB_ID2SYM(it->second);
    bool found = false;

    int carg(arg);

    VALUE result = rb_ary_new();
    for(it = enummap.begin();it != enummap.end();++it)
    {
        if((carg & it->first) != 0)
        {
            found = true;
            rb_ary_push(result,RB_ID2SYM(it->second));
            carg &= ~it->first;
        }
    }
    return found ? result : Qnil;
}

int unwrapenum(const VALUE &arg, const std::string& name)
{
    enumregistertype::iterator it = enumregister.find(name);
    if(it != enumregister.end())
    {
        enumtype* etype = it->second;
        if(NIL_P(arg))
            return etype->defaults;
        else if(RB_SYMBOL_P(arg))
        {
            ID id(RB_SYM2ID(arg));

            for(enumtype::value_type::iterator it2 = etype->values.begin();
                    it2 != etype->values.end();
                    ++it2)
            {
                if(it2->second == id)
                    return it2->first;
            }
            rb_raise(rb_eTypeError,"%s is not a %s-Enum.",rb_id2name(id),etype->name.c_str());
        }else if(rb_obj_is_kind_of(arg,rb_cArray) && etype->allow_array )
        {
            int result = 0;
            size_t count = RARRAY_LEN(arg);
            for(size_t i = 0; i < count; ++i)
                result = result | unwrapenum(RARRAY_AREF(arg,i),name);
        }else
            return RB_NUM2INT(arg);
    }
    return 0;
}

int unwrap_iconflag(const VALUE &val,int mask)
{
    int result = unwrapenum(val,"icon_flag");

    if((result & mask) != result)
        rb_raise(rb_eTypeError,"%" PRIsVALUE " is not a in %d mask", RB_OBJ_STRING(val), mask);
    
    return result;

}

int unwrap_buttonflag(const VALUE& val)
{
    return unwrapenum(val,"button_flag");
}

bool check_file_loadable(const wxString& path)
{
    errno = 0;
    wxFileName dir(wxPathOnly(path));
    dir.MakeAbsolute(wxGetCwd());

    wxFileName file(path);
    file.MakeAbsolute(wxGetCwd());

    if(dir.DirExists())
    {
        if(file.FileExists()){
            if(!file.IsFileReadable())
                errno = EACCES;
        }else
            errno = ENOENT;
    }else
        errno = ENOENT;

    if(errno)
    {
        rb_sys_fail(path.c_str());
        return false;
    }
    return true;

}
bool check_file_saveable(const wxString& path)
{
    errno = 0;

    wxFileName dir(wxPathOnly(path));
    dir.MakeAbsolute(wxGetCwd());

    wxFileName file(path);
    file.MakeAbsolute(wxGetCwd());
    if(dir.DirExists())
    {
        if(file.FileExists() && !file.IsFileWritable())
            errno = EACCES;
        else if(!dir.IsDirWritable())
            errno = EACCES;
    }else
        errno = ENOENT;

    if(errno)
    {
        rb_sys_fail(path.c_str());
        return false;
    }
    return true;
}

bool set_hash_option(VALUE hash,const char* name,VALUE& val)
{
    val = rb_hash_aref(hash,RB_ID2SYM(rb_intern(name)));
    return !NIL_P(val);
}

bool set_ruby_option(VALUE hash,const char* name,VALUE func(VALUE, VALUE), VALUE self)
{
    VALUE temp;
    if(set_hash_option(hash, name, temp))
    {
        func(self, temp);
        return true;
    }
    return false;
}

bool set_hash_flag_option(VALUE hash,const char* name,const int& flag,int& val)
{
    VALUE temp;
    if(set_hash_option(hash, name, temp))
    {
        if(RTEST(temp))
            val |= flag;
        else
            val &= ~flag;

        return true;
    }
    return false;
}

bool nil_check(VALUE window,const char* type,bool raise)
{
    bool result = NIL_P(window);
    if(result && raise)
        rb_raise(rb_eTypeError,"%s can't be nil.",type);
    return !result;
}

bool nil_check(VALUE window,VALUE klass, bool raise)
{
    return nil_check(window,rb_class2name(klass),raise);
}

bool nil_check(VALUE window, bool raise)
{
    return nil_check(window,"window",raise);
}

bool check_index(int &cidx,const std::size_t &count)
{
    bool raise = false;
    if(cidx >= (int)count){
        raise = true;
    }else if(cidx < 0)
    {
        int tmp = cidx + (int)count;
        if(tmp > 0 && tmp <= (int)count)
            cidx = tmp;
        else
            raise = true;
    }

    if(raise)
        rb_raise(rb_eIndexError,"%d out of index",cidx);
    return !raise;
}

namespace RubyWX {
    VALUE _equal_rescue(VALUE val) {
        return Qfalse;
    }
};