diff --git a/deps/ext.jl b/deps/ext.jl index 899e28f3..0dd86a6f 100644 --- a/deps/ext.jl +++ b/deps/ext.jl @@ -21,8 +21,10 @@ if OS_NAME == :Windows const libgobject = "libgobject-2.0-0" const libglib = "libglib-2.0-0" const libgdk_pixbuf = "libgdk_pixbuf-2.0-0" + const libgi = "libgirepository-1.0-0" # unverifie else const libgobject = "libgobject-2.0" const libglib = "libglib-2.0" const libgdk_pixbuf = "libgdk_pixbuf-2.0" + const libgi = "libgirepository-1.0" end diff --git a/src/GI.jl b/src/GI.jl new file mode 100644 index 00000000..7ba7413a --- /dev/null +++ b/src/GI.jl @@ -0,0 +1,14 @@ +module GI + using GLib + using GLib.MutableTypes + import Base: convert, show, showcompact, length, getindex, setindex! + + # gimport interface (not final in any way) + export @gimport + + export extract_type, ensure_name, ensure_method + + include(joinpath("..","deps","ext.jl")) + include("girepo.jl") + include("giimport.jl") +end diff --git a/src/GLib.jl b/src/GLib.jl new file mode 100644 index 00000000..0f3c254f --- /dev/null +++ b/src/GLib.jl @@ -0,0 +1,37 @@ +module GLib +import Base: convert, show, showall, showcompact, run, size, length, getindex, setindex!, + insert!, push!, unshift!, shift!, pop!, splice!, delete!, + start, next, done, parent, isempty, empty!, first, last, in, + eltype, copy +import Base.Graphics: width, height, getgc +export GObject, GObjectI, GType, @GType, make_gvalue +export Enum, GError, GValue, gvalue +export GSList, gslist,gslist2, gc_ref, gc_ref_closure +export signal_connect, signal_emit +export bytestring, GConnectFlags +include(joinpath("..","deps","ext.jl")) +bytestring(s) = Base.bytestring(s) +bytestring(s::Symbol) = s +bytestring(s::Ptr{Uint8},own::Bool) = UTF8String(pointer_to_array(s,int(ccall(:strlen,Csize_t,(Ptr{Uint8},),s)),own)) + +ccall((:g_type_init,libgobject),Void,()) +include("MutableTypes.jl") +using MutableTypes +include("gslist.jl") +include("gobject.jl") +include("gvalues.jl") +include("gerror.jl") +include("signals.jl") +sizeof_gclosure = 0 +function init() + global sizeof_gclosure = WORD_SIZE + closure = C_NULL + while closure == C_NULL + sizeof_gclosure += WORD_SIZE + closure = ccall((:g_closure_new_simple,libgobject),Ptr{Void},(Int,Ptr{Void}),sizeof_gclosure,C_NULL) + end + ccall((:g_closure_sink,libgobject),Void,(Ptr{Void},),closure) +end +init() +end + diff --git a/src/Gtk.jl b/src/Gtk.jl index ae240032..e7705deb 100644 --- a/src/Gtk.jl +++ b/src/Gtk.jl @@ -2,10 +2,10 @@ module Gtk using Cairo -include("MutableTypes.jl") -using .MutableTypes +using GLib +using GLib.MutableTypes -import Base: convert, show, showall, run, size, length, getindex, setindex!, +import Base: convert, show, showall, showcompact, run, size, length, getindex, setindex!, insert!, push!, unshift!, shift!, pop!, splice!, delete!, start, next, done, parent, isempty, empty!, first, last, in, eltype, copy @@ -71,18 +71,11 @@ export GdkKeySyms, GdkScrollDirection, GtkJustification # set_position # local function, handles Symbol and makes UTF8-strings easier -bytestring(s) = Base.bytestring(s) -bytestring(s::Symbol) = s -bytestring(s::Ptr{Uint8},own::Bool) = UTF8String(pointer_to_array(s,int(ccall(:strlen,Csize_t,(Ptr{Uint8},),s)),own)) - typealias Index Union(Integer,AbstractVector{TypeVar(:I,Integer)}) include(joinpath("..","deps","ext.jl")) -ccall((:g_type_init,libgobject),Void,()) -include("gslist.jl") + include("gtktypes.jl") -include("gvalues.jl") -include("gerror.jl") include("gdk.jl") include("events.jl") include("container.jl") @@ -124,6 +117,7 @@ for container in subtypes(GtkContainerI,true) @eval $(symbol(string(container)))(child::GtkWidgetI,vargs...) = push!($container(vargs...),child) end for orientable in tuple(:GtkPaned, :GtkScale, [sym.name.name for sym in subtypes(GtkBoxI)]...) + if endswith(string(orientable),"I") continue end # Sorry :( @eval $orientable(orientation::Symbol,vargs...) = $orientable( (orientation==:v ? true : (orientation==:h ? false : @@ -252,3 +246,4 @@ export Canvas, Window init() end + diff --git a/src/buttons.jl b/src/buttons.jl index 8dfabe4b..3cbf4ac8 100644 --- a/src/buttons.jl +++ b/src/buttons.jl @@ -13,26 +13,26 @@ #GtkSwitch — A "light switch" style toggle #GtkLockButton — A widget to unlock or lock privileged operations -@GType GtkButton <: GtkBin +@GType GtkButton GtkButton() = GtkButton(ccall((:gtk_button_new,libgtk),Ptr{GObject},())) GtkButton(title::String) = GtkButton(ccall((:gtk_button_new_with_mnemonic,libgtk),Ptr{GObject}, (Ptr{Uint8},), bytestring(title))) -@GType GtkCheckButton <: GtkBin +@GType GtkCheckButton GtkCheckButton() = GtkCheckButton(ccall((:gtk_check_button_new,libgtk),Ptr{GObject},())) GtkCheckButton(title::String) = GtkCheckButton(ccall((:gtk_check_button_new_with_mnemonic,libgtk),Ptr{GObject}, (Ptr{Uint8},), bytestring(title))) -@GType GtkToggleButton <: GtkBin +@GType GtkToggleButton GtkToggleButton() = GtkToggleButton(ccall((:gtk_toggle_button_new,libgtk),Ptr{GObject},())) GtkToggleButton(title::String) = GtkToggleButton(ccall((:gtk_toggle_button_new_with_mnemonic,libgtk),Ptr{GObject}, (Ptr{Uint8},), bytestring(title))) if gtk_version >= 3 -@GType GtkSwitch <: GtkWidget +@GType GtkSwitch GtkSwitch() = GtkSwitch(ccall((:gtk_switch_new,libgtk),Ptr{GObject},())) function GtkSwitch(active::Bool) b = GtkSwitch() @@ -43,7 +43,7 @@ else const GtkSwitch = GtkToggleButton end -@GType GtkRadioButton <: GtkBin +@GType GtkRadioButton GtkRadioButton(group::Ptr{Void}=C_NULL) = GtkRadioButton(ccall((:gtk_radio_button_new,libgtk),Ptr{GObject}, (Ptr{Void},),group)) @@ -157,7 +157,7 @@ for btn in (:GtkCheckButton, :GtkToggleButton, :GtkRadioButton) end -@GType GtkLinkButton <: GtkBin +@GType GtkLinkButton GtkLinkButton(uri::String) = GtkLinkButton(ccall((:gtk_link_button_new,libgtk),Ptr{GObject}, (Ptr{Uint8},),bytestring(uri))) @@ -177,7 +177,7 @@ end #TODO: @GType GtkScaleButton -@GType GtkVolumeButton <: GtkBin +@GType GtkVolumeButton GtkVolumeButton() = GtkVolumeButton(ccall((:gtk_volume_button_new,libgtk),Ptr{GObject},())) function GtkVolumeButton(value::Real) # 0<=value<=1 b = GtkVolumeButton() diff --git a/src/displays.jl b/src/displays.jl index 5a75ca5f..ce4fc445 100644 --- a/src/displays.jl +++ b/src/displays.jl @@ -287,7 +287,7 @@ baremodule GtkIconSize end end -@GType GtkImage <: GtkWidget +@GType GtkImage GtkImage(pixbuf::GdkPixbuf) = GtkImage(ccall((:gtk_image_new_from_pixbuf,libgtk),Ptr{GObject},(Ptr{GObject},),pixbuf)) GtkImage(filename::String) = GtkImage(ccall((:gtk_image_new_from_file,libgtk),Ptr{GObject},(Ptr{Uint8},),bytestring(filename))) @@ -310,14 +310,14 @@ end empty!(img::GtkImage) = ccall((:gtk_image_clear,libgtk),Void,(Ptr{GObject},),img) GdkPixbuf(img::GtkImage) = GdkPixbuf(ccall((:gtk_image_get_pixbuf,libgtk),Ptr{GObject},(Ptr{GObject},),img)) -@GType GtkProgressBar <: GtkWidget +@GType GtkProgressBar GtkProgressBar() = GtkProgressBar(ccall((:gtk_progress_bar_new,libgtk),Ptr{GObject},())) pulse(progress::GtkProgressBar) = ccall((:gtk_progress_bar_pulse,libgtk),Void,(Ptr{GObject},),progress) -@GType GtkSpinner <: GtkWidget +@GType GtkSpinner GtkSpinner() = GtkSpinner(ccall((:gtk_spinner_new,libgtk),Ptr{GObject},())) -@GType GtkStatusbar <: GtkBox +@GType GtkStatusbar GtkStatusbar() = GtkStatusbar(ccall((:gtk_statusbar_new,libgtk),Ptr{GObject},())) context_id(status::GtkStatusbar,source) = ccall((:gtk_statusbar_get_context_id,libgtk),Cuint,(Ptr{GObject},Ptr{Uint8}), @@ -342,3 +342,4 @@ empty!(status::GtkStatusbar,context) = @GType GtkStatusIcon GtkStatusIcon() = GtkStatusIcon(ccall((:gtk_status_icon_new,libgtk),Ptr{GObject},())) + diff --git a/src/events.jl b/src/events.jl index c68d64cf..c42b10e7 100644 --- a/src/events.jl +++ b/src/events.jl @@ -16,119 +16,17 @@ function gtk_doevent() end end -sizeof_gclosure = 0 function init() GError() do error_check ccall((:gtk_init_with_args,libgtk), Bool, (Ptr{Void}, Ptr{Void}, Ptr{Uint8}, Ptr{Void}, Ptr{Uint8}, Ptr{GError}), C_NULL, C_NULL, "Julia Gtk Bindings", C_NULL, C_NULL, error_check) end - global sizeof_gclosure = WORD_SIZE - closure = C_NULL - while closure == C_NULL - sizeof_gclosure += WORD_SIZE - closure = ccall((:g_closure_new_simple,libgobject),Ptr{Void},(Int,Ptr{Void}),sizeof_gclosure,C_NULL) - end - ccall((:g_closure_sink,libgobject),Void,(Ptr{Void},),closure) global timeout timeout = Base.TimeoutAsyncWork(gtk_doevent) Base.start_timer(timeout,.1,.005) end -# id = signal_connect(widget, :event, Void, (ArgsT...)) do ptr, evt_args..., closure -# stuff -# end -function signal_connect(cb::Function,w::GObject,sig::Union(String,Symbol), - RT::Type,param_types::Tuple,after::Bool=false,closure=w) #TODO: assert that length(param_types) is correct - if isgeneric(cb) - callback = cfunction(cb,RT,tuple(Ptr{GObject},param_types...,typeof(closure))) - return ccall((:g_signal_connect_data,libgobject), Culong, - (Ptr{GObject}, Ptr{Uint8}, Ptr{Void}, Any, Ptr{Void}, Enum), - w, - bytestring(sig), - callback, - closure, - gc_ref_closure(closure), - after*GConnectFlags.AFTER) - end - # oops, Julia doesn't support this natively yet -- fake it instead - return _signal_connect(cb, w, sig, after, true,param_types,closure) -end - -# id = signal_connect(widget, :event) do obj, evt_args... -# stuff -# end -function signal_connect(cb::Function,w::GObject,sig::Union(String,Symbol),after::Bool=false) - _signal_connect(cb, w, sig, after, false,nothing,nothing) -end -function _signal_connect(cb::Function,w::GObject,sig::Union(String,Symbol),after::Bool,gtk_call_conv::Bool,param_types,closure) - closuref = ccall((:g_closure_new_object,libgobject), Ptr{Void}, (Cuint, Ptr{GObject}), sizeof_gclosure::Int+WORD_SIZE*2, w) - closure_env = convert(Ptr{Any},closuref+sizeof_gclosure) - unsafe_store!(closure_env, cb, 1) - if gtk_call_conv - env = Any[param_types,closure] - unsafe_store!(closure_env, env, 2) - ccall((:g_closure_add_invalidate_notifier,libgobject), Void, - (Ptr{Void}, Any, Ptr{Void}), closuref, env, gc_ref_closure(env)) - else - unsafe_store!(convert(Ptr{Int},closure_env), 0, 2) - end - ccall((:g_closure_add_invalidate_notifier,libgobject), Void, - (Ptr{Void}, Any, Ptr{Void}), closuref, cb, gc_ref_closure(cb)) - ccall((:g_closure_set_marshal,libgobject), Void, - (Ptr{Void}, Ptr{Void}), closuref, JuliaClosureMarshal) - return ccall((:g_signal_connect_closure,libgobject), Culong, - (Ptr{GObject}, Ptr{Uint8}, Ptr{Void}, Cint), w, bytestring(sig), closuref, after) -end -function GClosureMarshal(closuref, return_value, n_param_values, - param_values, invocation_hint, marshal_data) - try - closure_env = convert(Ptr{Any},closuref+sizeof_gclosure) - cb = unsafe_load(closure_env, 1) - gtk_calling_convention = (0 != unsafe_load(convert(Ptr{Int},closure_env), 2)) - params = Array(Any, n_param_values) - if gtk_calling_convention - # compatibility mode, if we must - param_types,closure = unsafe_load(closure_env, 2)::Array{Any,1} - length(param_types)+1 == n_param_values || error("GCallback called with the wrong number of parameters") - for i = 1:n_param_values - gv = mutable(param_values,i) - g_type = unsafe_load(gv).g_type - # avoid auto-unboxing for some builtin types in gtk_calling_convention mode - if bool(ccall((:g_type_is_a,libgobject),Cint,(Int,Int),g_type,gobject_id)) - params[i] = ccall((:g_value_get_object,libgobject), Ptr{GObject}, (Ptr{GValue},), gv) - elseif bool(ccall((:g_type_is_a,libgobject),Cint,(Int,Int),g_type,gboxed_id)) - params[i] = ccall((:g_value_get_boxed,libgobject), Ptr{Void}, (Ptr{GValue},), gv) - elseif bool(ccall((:g_type_is_a,libgobject),Cint,(Int,Int),g_type,gstring_id)) - params[i] = ccall((:g_value_get_string,libgobject), Ptr{Void}, (Ptr{GValue},), gv) - else - params[i] = gv[] - end - if i > 1 - params[i] = convert(param_types[i-1], params[i]) - end - end - push!(params, closure) - else - for i = 1:n_param_values - params[i] = mutable(param_values,i)[] - end - end - retval = cb(params...) # widget, args... - if return_value != C_NULL && retval !== nothing - g_type = unsafe_load(return_value).g_type - if g_type != gvoid_id && g_type != 0 - return_value[] = gvalue(retval) - end - end - catch e - Base.display_error(e,catch_backtrace()) - end - return nothing -end -JuliaClosureMarshal = cfunction(GClosureMarshal, Void, - (Ptr{Void}, Ptr{GValue}, Cuint, Ptr{GValue}, Ptr{Void}, Ptr{Void})) - add_events(widget::GtkWidgetI, mask::Integer) = ccall((:gtk_widget_add_events,libgtk),Void,(Ptr{GObject},Enum),widget,mask) @@ -147,28 +45,6 @@ add_events(widget::GtkWidgetI, mask::Integer) = ccall((:gtk_widget_add_events,li # https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-accel-closures-changed -signal_handler_disconnect(w::GObject, handler_id::Culong) = - ccall(:g_signal_handler_disconnect, Void, (Ptr{GObject}, Culong), w, handler_id) - -signal_handler_block(w::GObject, handler_id::Culong) = - ccall(:g_signal_handler_block, Void, (Ptr{GObject}, Culong), w, handler_id) - -signal_handler_unblock(w::GObject, handler_id::Culong) = - ccall(:g_signal_handler_unblock, Void, (Ptr{GObject}, Culong), w, handler_id) - -function signal_emit(w::GObject, sig::Union(String,Symbol), RT::Type, args...) - i = isa(sig, String) ? search(sig, "::") : (0:-1) - if !isempty(i) - detail = @quark_str sig[last(i)+1:end] - sig = sig[1:first(i)-1] - else - detail = uint32(0) - end - signal_id = ccall((:g_signal_lookup,libgobject),Cuint,(Ptr{Uint8},Csize_t), sig, G_OBJECT_CLASS_TYPE(w)) - return_value = RT===Void ? C_NULL : gvalue(RT) - ccall((:g_signal_emitv,libgobject),Void,(Ptr{GValue},Cuint,Uint32,Ptr{GValue}),gvalues(w, args...),signal_id,detail,return_value) - return_value[RT] -end function on_signal_resize(resize_cb::Function, widget::GtkWidgetI, vargs...) signal_connect(resize_cb, widget, "size-allocate", Void, (Ptr{GdkRectangle},), vargs...) diff --git a/src/giimport.jl b/src/giimport.jl new file mode 100644 index 00000000..885bd869 --- /dev/null +++ b/src/giimport.jl @@ -0,0 +1,167 @@ + +const _gi_modules = Dict{Symbol,Module}() +const _gi_modsyms = Dict{(Symbol,Symbol),Any}() + +function create_module(modname,gns) + mod = eval(Expr(:toplevel, :(module ($modname) + const _gi_ns = $gns + const GI = $GI + end), modname)) +end + +function init_ns(name::Symbol) + if haskey(_gi_modules,name) + return + end + gns = GINamespace(name) + for path=get_shlibs(gns) + dlopen(path,RTLD_GLOBAL) + end + # use submodules to GI module later + modname = symbol("_$name") + mod = GI.create_module(modname,gns) + _gi_modules[name] = mod + mod +end + + +init_ns(:GObject) +_ns(name) = (init_ns(name); _gi_modules[name]) + +ensure_name(mod::Module, name) = ensure_name(mod._gi_ns, name) +function ensure_name(ns::GINamespace, name::Symbol) + if haskey(_gi_modsyms,(ns.name, name)) + return _gi_modsyms[(ns.name, name)] + end + sym = load_name(ns,name,ns[name]) + _gi_modsyms[(ns.name,name)] = sym + sym +end + +function load_name(ns,name,info::GIObjectInfo) + rt = create_type(info) + if find_method(ns[name], :new) != nothing + #create_type might not do this, as there will be mutual dependency + ensure_method(ns,name,:new) + end + rt.wrapper +end + +function load_name(ns,name,info::GIFunctionInfo) + create_method(info) +end + +peval(mod, expr) = (print(expr,'\n'); eval(mod,expr)) + +function extract_type(info::GIObjectInfo,ret=false) + rt = create_type(info) + ret ? rt.iface : rt.wrapper +end + +function create_type(info::GIObjectInfo) + g_type = get_g_type(info) + GLib.register_gtype(g_type) +end + +const _gi_methods = Dict{(Symbol,Symbol,Symbol),Any}() +ensure_method(mod::Module, rtype, method) = ensure_method(mod._gi_ns,rtype,method) + +function ensure_method(ns::GINamespace, rtype::Symbol, method::Symbol) + qname = (ns.name,rtype,method) + if haskey( _gi_methods, qname) + return _gi_methods[qname] + end + info = ns[rtype][method] + meth = create_method(info) + _gi_methods[qname] = meth + return meth +end + +c_type(t) = t +c_type{T<:GObjectI}(t::Type{T}) = Ptr{GObjectI} +c_type{T<:ByteString}(t::Type{T}) = Ptr{Uint8} + +j_type(t) = t +j_type{T<:Integer}(::Type{T}) = Integer +function create_method(info::GIFunctionInfo) + ns = get_namespace(info) + NS = _ns(ns) + name = get_name(info) + flags = get_flags(info) + args = get_args(info) + argtypes = Type[extract_type(a) for a in args] + argnames = [symbol("_$(get_name(a))") for a in args] + if flags & IS_METHOD != 0 + object = get_container(info) + rt = create_type(object) + unshift!(argtypes, rt.iface) + unshift!(argnames, :__instance) + end + if flags & IS_CONSTRUCTOR != 0 + if name == :new + name = symbol("$(get_name(get_container(info)))_new") + end + end + rettype = extract_type(get_return_type(info),true) + cargtypes = Expr(:tuple, Any[c_type(a) for a in argtypes]...) + crettype = c_type(rettype) + symb = get_symbol(info) + j_call = Expr(:call, name, [ :($(argnames[i])::$(j_type(argtypes[i]))) for i=1:length(argtypes) ]... ) + c_call = :(ccall($(string(symb)), $(c_type(rettype)), $cargtypes)) + append!(c_call.args, argnames) + if rettype == None + #pass + elseif rettype <: GObjectI + c_call = :( GI.GSubType($rettype,$c_call) ) + elseif rettype <: ByteString + c_call = :( bytestring($c_call) ) + end + eval(NS, Expr(:function, j_call, quote $c_call end)) + return eval(NS, name) +end + + +# used when returning gobject +function GSubType{T<:GObjectI}(::Type{T}, hnd::Ptr{GObjectI}) + if hnd == C_NULL + error("can't handle NULL returns yet!") + end + g_type = GLib.G_OBJECT_CLASS_TYPE(hnd) + rt = GLib.register_gtype(g_type) + #TODO: lookup existing julia wrapper first + return rt.wrapper(hnd)::T +end + + +#some convenience macros, just for the show +macro gimport(ns, names) + _name = (ns == :Gtk) ? :_Gtk : ns + NS = _ns(ns) + ns = GINamespace(ns) + q = quote $(esc(_name)) = $(NS) end + if isa(names,Expr) && names.head == :tuple + names = names.args + else + names = [names] + end + for item in names + if isa(item,Symbol) + name = item; meths = [] + else + name = item.args[1] + meths = item.args[2:end] + end + info = NS._gi_ns[name] + push!(q.args, :(const $(esc(name)) = $(ensure_name(NS, name)))) + for meth in meths + push!(q.args, :(const $(esc(meth)) = $(GI.ensure_method(NS, name, meth)))) + end + if isa(ns[name], GIObjectInfo) && find_method(ns[name], :new) != nothing + push!(q.args, :(const $(esc(symbol("$(name)_new"))) = $(GI.ensure_method(NS, name, :new)))) + end + end + print(q) + q +end + + diff --git a/src/girepo.jl b/src/girepo.jl new file mode 100644 index 00000000..f03df7ff --- /dev/null +++ b/src/girepo.jl @@ -0,0 +1,244 @@ +abstract GIRepository +const girepo = ccall((:g_irepository_get_default, libgi), Ptr{GIRepository}, () ) + +abstract GITypelib + +abstract GIBaseInfo +# a GIBaseInfo we own a reference to +type GIInfo{Typeid} + handle::Ptr{GIBaseInfo} +end + +function GIInfo(h::Ptr{GIBaseInfo}) + if h == C_NULL + error("Cannot constrct GIInfo from NULL") + end + typeid = int(ccall((:g_base_info_get_type, libgi), Enum, (Ptr{GIBaseInfo},), h)) + info = GIInfo{typeid}(h) + finalizer(info, info_unref) + info +end +# don't call directly, called by gc +function info_unref(info::GIInfo) + #core dumps on reload("GTK.jl"), + #ccall((:g_base_info_unref, libgi), Void, (Ptr{GIBaseInfo},), info.handle) + info.handle = C_NULL +end + +convert(::Type{Ptr{GIBaseInfo}},w::GIInfo) = w.handle + +const GIInfoTypesShortNames = (:Invalid, :Function, :Callback, :Struct, :Boxed, :Enum, :Flags, :Object, :Interface, :Constant, :Unknown, :Union, :Value, :Signal, :VFunc, :Property, :Field, :Arg, :Type, :Unresolved) +const GIInfoTypeNames = [ Base.symbol("GI$(name)Info") for name in GIInfoTypesShortNames] + +const GIInfoTypes = Dict{Symbol, Type}() + +for (i,itype) in enumerate(GIInfoTypesShortNames) + let lowername = symbol(lowercase(string(itype))) + @eval typealias $(GIInfoTypeNames[i]) GIInfo{$(i-1)} + GIInfoTypes[lowername] = GIInfo{i-1} + end +end + +typealias GICallableInfo Union(GIFunctionInfo,GIVFuncInfo, GICallbackInfo, GISignalInfo) +typealias GIRegisteredTypeInfo Union(GIEnumInfo,GIInterfaceInfo, GIObjectInfo, GIStructInfo, GIUnionInfo) + +show{Typeid}(io::IO, ::Type{GIInfo{Typeid}}) = print(io, GIInfoTypeNames[Typeid+1]) + +function show(io::IO, info::GIInfo) + show(io, typeof(info)) + print(io,"(:$(get_namespace(info)), :$(get_name(info)))") +end + +show(io::IO, info::GITypeInfo) = print(io,"GITypeInfo($(extract_type(info)))") +show(io::IO, info::GIArgInfo) = print(io,"GIArgInfo(:$(get_name(info)),$(extract_type(info)))") +showcompact(io::IO, info::GIArgInfo) = show(io,info) # bug in show.jl ? + +function show(io::IO, info::GIFunctionInfo) + print(io, "$(get_namespace(info)).$(get_name(info))(") + for arg in get_args(info) + print(io, "$(get_name(arg))::$(extract_type(arg)), ") + end + print(io,")\n") +end + + +immutable GINamespace + name::Symbol + function GINamespace(namespace::Symbol, version=nothing) + #TODO: stricter version sematics? + gi_require(namespace, version) + new(namespace) + end +end +convert(::Type{Symbol}, ns::GINamespace) = ns.name +convert(::Type{Ptr{Uint8}}, ns::GINamespace) = convert(Ptr{Uint8}, ns.name) + +function gi_require(namespace, version=nothing) + if version==nothing + version = C_NULL + end + GError() do error_check + typelib = ccall((:g_irepository_require, libgi), Ptr{GITypelib}, + (Ptr{GIRepository}, Ptr{Uint8}, Ptr{Uint8}, Cint, Ptr{Ptr{GError}}), + girepo, namespace, version, 0, error_check) + return typelib !== C_NULL + end +end + +function gi_find_by_name(namespace, name) + info = ccall((:g_irepository_find_by_name, libgi), Ptr{GIBaseInfo}, + (Ptr{GIRepository}, Ptr{Uint8}, Ptr{Uint8}), girepo, namespace, name) + if info == C_NULL + error("Name $name not found in $namespace") + end + GIInfo(info) +end + +#GIInfo(namespace, name::Symbol) = gi_find_by_name(namespace, name) + +#TODO: make ns behave more like Array and/or Dict{Symbol,GIInfo}? +length(ns::GINamespace) = int(ccall((:g_irepository_get_n_infos, libgi), Cint, (Ptr{GIRepository}, Ptr{Uint8}), girepo, ns)) +function getindex(ns::GINamespace, i::Integer) + GIInfo(ccall((:g_irepository_get_info, libgi), Ptr{GIBaseInfo}, (Ptr{GIRepository}, Ptr{Uint8}, Cint), girepo, ns, i-1 )) +end +getindex(ns::GINamespace, name::Symbol) = gi_find_by_name(ns, name) + +function get_all{T<:GIInfo}(ns::GINamespace, t::Type{T}) + all = GIInfo[] + for i=1:length(ns) + info = ns[i] + if isa(info,t) + push!(all,info) + end + end + all +end + + +function get_shlibs(ns) + names = bytestring(ccall((:g_irepository_get_shared_library, libgi), Ptr{Uint8}, (Ptr{GIRepository}, Ptr{Uint8}), girepo, ns)) + split(names,",") +end +get_shlibs(info::GIInfo) = get_shlibs(get_namespace(info)) + +function find_by_gtype(gtypeid::Csize_t) + GIInfo(ccall((:g_irepository_find_by_gtype, libgi), Ptr{GIBaseInfo}, (Ptr{GIRepository}, Csize_t), girepo, gtypeid)) +end + +GIInfoTypes[:method] = GIFunctionInfo +GIInfoTypes[:callable] = GICallableInfo +GIInfoTypes[:registered_type] = GIRegisteredTypeInfo +GIInfoTypes[:base] = GIInfo + +# one-> many relationships +for (owner, property) in [ + (:object, :method), (:object, :signal), (:object, :interface), + (:object, :property), (:object, :constant), (:object, :field), + (:interface, :method), (:interface, :signal), (:callable, :arg)] + @eval function $(symbol("get_$(property)s"))(info::$(GIInfoTypes[owner])) + n = int(ccall(($("g_$(owner)_info_get_n_$(property)s"), libgi), Cint, (Ptr{GIBaseInfo},), info)) + GIInfo[ GIInfo( ccall(($("g_$(owner)_info_get_$property"), libgi), Ptr{GIBaseInfo}, (Ptr{GIBaseInfo}, Cint), info, i)) for i=0:n-1] + end + if property == :method + @eval function $(symbol("find_$(property)"))(info::$(GIInfoTypes[owner]), name) + ptr = ccall(($("g_$(owner)_info_find_$(property)"), libgi), Ptr{GIBaseInfo}, (Ptr{GIBaseInfo},Ptr{Uint8}), info, name) + (ptr == C_NULL) ? nothing : GIInfo(ptr) + end + end +end +getindex(info::GIRegisteredTypeInfo, name::Symbol) = find_method(info, name) + +# one->one +_unit(x) = x +# reuse gvalues.jl instead? +# FIXME: memory management of GIInfo:s +_types = [GIInfo=>(Ptr{GIBaseInfo},GIInfo), + Symbol=>(Ptr{Uint8}, (x -> symbol(bytestring(x))))] +for (owner,property,typ) in [ + (:base, :name, Symbol), (:base, :namespace, Symbol), + (:base, :container, GIInfo), (:registered_type, :g_type, GType), (:object, :parent, GIInfo), + (:callable, :return_type, GIInfo), (:callable, :caller_owns, Enum), + (:function, :flags, Enum), (:function, :symbol, Symbol), + (:arg, :type, GIInfo), (:arg, :direction, Enum), + (:type, :tag, Enum), (:type, :interface, GIInfo), (:constant, :type, GIInfo)] + + ctype, conv = get(_types, typ, (typ,_unit)) + @eval function $(symbol("get_$(property)"))(info::$(GIInfoTypes[owner])) + $conv(ccall(($("g_$(owner)_info_get_$(property)"), libgi), $ctype, (Ptr{GIBaseInfo},), info)) + end +end + +get_name(info::GITypeInfo) = symbol("") +get_name(info::GIInvalidInfo) = symbol("") + + +qual_name(info::GIRegisteredTypeInfo) = (get_namespace(info),get_name(info)) + +for (owner,flag) in [ (:type, :is_pointer) ] + @eval function $flag(info::$(GIInfoTypes[owner])) + ret = ccall(($("g_$(owner)_info_$(flag)"), libgi), Cint, (Ptr{GIBaseInfo},), info) + return ret != 0 + end +end + + +const typetag_primitive = [ + Void,Bool,Int8,Uint8, + Int16,Uint16,Int32,Uint32, + Int64,Uint64,Cfloat,Cdouble, + GType, + ByteString + ] +const TAG_BASIC_MAX = 13 +const TAG_ARRAY = 15 +const TAG_INTERFACE = 16 + +extract_type(info::Union(GIArgInfo,GIConstantInfo),ret=false) = extract_type(get_type(info),ret) + +function extract_type(info::GITypeInfo,ret=false) + tag = get_tag(info) + if tag <= TAG_BASIC_MAX + basetype = typetag_primitive[tag+1] + elseif tag == TAG_INTERFACE + # Object Types n such + iface = get_interface(info) + basetype = extract_type(iface,ret) + elseif tag == TAG_ARRAY + basetype = Void + else + print(tag) + return Nothing + end + # GObjects are implicit pointers + if is_pointer(info) && (!(basetype <: Union(GObjectI,ByteString)) || basetype == Void) + Ptr{basetype} + else + basetype + end +end + +abstract GStruct #placeholder +function extract_type(info::GIStructInfo,ret) + GStruct # TODO: specialize +end + +extract_type(info::GIEnumInfo,ret) = Enum + +const IS_METHOD = 1 << 0 +const IS_CONSTRUCTOR = 1 << 1 + +function get_value(info::GIConstantInfo) + typ = extract_type(info) + x = Array(Int64,1) #or mutable + size = ccall((:g_constant_info_get_value,libgi),Cint,(Ptr{GIBaseInfo}, Ptr{Void}), info, x) + if typ <: Number + unsafe_load(Base.cconvert(Ptr{typ}, x)) + elseif typ == ByteString + #val = bytestring(unsafe_load(Base.cconvert(Ptr{Uint8},x))) + #ccall((:g_constant_info_free_value,libgi), Void, (Ptr{GIBaseInfo}, Ptr{Void}), info, x) + #val + nothing + else + nothing#unimplemented + end +end diff --git a/src/gobject.jl b/src/gobject.jl new file mode 100644 index 00000000..42b0de9a --- /dev/null +++ b/src/gobject.jl @@ -0,0 +1,102 @@ +abstract GObjectI +typealias GObject GObjectI + +typealias Enum Int32 +# Alternative object construction style. This would let us share constructors +# by creating const aliases: `const Z = GObject{:Z}` +type GObjectAny <: GObjectI + handle::Ptr{GObject} + GObjectAny(handle::Ptr{GObject}) = (handle != C_NULL ? gc_ref(new(handle)) : error("Cannot construct $gname with a NULL pointer")) +end + + +macro quark_str(q) + :( ccall((:g_quark_from_string, libglib), Uint32, (Ptr{Uint8},), bytestring($q)) ) +end +const jlref_quark = quark"julia_ref" + +# All GtkWidgets are expected to have a 'handle' field +# of type Ptr{GObjectI} corresponding to the Gtk object +# and an 'all' field which has type GdkRectangle +# corresponding to the rectangle allocated to the object, +# or to override the size, width, and height methods +convert(::Type{Ptr{GObjectI}},w::GObjectI) = w.handle +convert{T<:GObjectI}(::Type{T},w::Ptr{T}) = convert(T,convert(Ptr{GObjectI},w)) +function convert{T<:GObjectI}(::Type{T},w::Ptr{GObjectI}) + x = ccall((:g_object_get_qdata, libgobject), Ptr{GObjectI}, (Ptr{GObjectI},Uint32), w, jlref_quark) + x == C_NULL && error("GObject didn't have a corresponding Julia object") + unsafe_pointer_to_objref(x)::T +end +### Garbage collection [prevention] + +const gc_preserve = ObjectIdDict() # reference counted closures +function gc_ref(x::ANY) + global gc_preserve + gc_preserve[x] = (get(gc_preserve, x, 0)::Int)+1 + x +end +function gc_unref(x::ANY) + global gc_preserve + count = get(gc_preserve, x, 0)::Int-1 + if count <= 0 + delete!(gc_preserve, x) + end + nothing +end +gc_ref_closure{T}(x::T) = (gc_ref(x);cfunction(gc_unref, Void, (T, Ptr{Void}))) +gc_unref(x::Any, ::Ptr{Void}) = gc_unref(x) + +const gc_preserve_gtk = WeakKeyDict{GObjectI,Union(Bool,GObjectI)}() # gtk objects +function gc_ref{T<:GObjectI}(x::T) + global gc_preserve_gtk + addref = function() + ccall((:g_object_ref_sink,libgobject),Ptr{GObjectI},(Ptr{GObjectI},),x) + finalizer(x,function(x) + global gc_preserve_gtk + gc_preserve_gtk[x] = x # convert to a strong-reference + ccall((:g_object_unref,libgobject),Void,(Ptr{GObjectI},),x) # may clear the strong reference + end) + gc_preserve_gtk[x] = true # record the existence of the object, but allow the finalizer + end + ref = get(gc_preserve_gtk,x,nothing) + if isa(ref,Nothing) + ccall((:g_object_set_qdata_full, libgobject), Void, + (Ptr{GObjectI}, Uint32, Any, Ptr{Void}), x, jlref_quark, x, + cfunction(gc_unref, Void, (T,))) # add a circular reference to the Julia object in the GObjectI + addref() + elseif !isa(ref,WeakRef) + # oops, we previously deleted the link, but now it's back + addref() + else + # already gc-protected, nothing to do + end + x +end + + +function gc_unref_weak(x::GObjectI) + # this strongly destroys and invalidates the object + # it is intended to be called by Gtk, not in user code function + # note: this may be called multiple times by Gtk + x.handle = C_NULL + global gc_preserve_gtk + delete!(gc_preserve_gtk, x) + nothing +end +function gc_unref(x::GObjectI) + # this strongly destroys and invalidates the object + # it is intended to be called by Gtk, not in user code function + ref = ccall((:g_object_get_qdata,libgobject),Ptr{Void},(Ptr{GObjectI},Uint32),x,jlref_quark) + if ref != C_NULL && x !== unsafe_pointer_to_objref(ref) + # We got called because we are no longer the default object for this handle, but we are still alive + warn("Duplicate Julia object creation detected for GObject") + ccall((:g_object_weak_ref,libgobject),Void,(Ptr{GObjectI},Ptr{Void},Any),x,cfunction(gc_unref_weak,Void,(typeof(x),)),x) + else + ccall((:g_object_steal_qdata,libgobject),Any,(Ptr{GObjectI},Uint32),x,jlref_quark) + gc_unref_weak(x) + end + nothing +end +gc_unref(::Ptr{GObjectI}, x::GObjectI) = gc_unref(x) +gc_ref_closure(x::GObjectI) = C_NULL + diff --git a/src/gtktypes.jl b/src/gtktypes.jl index 15c1abae..c6890b82 100644 --- a/src/gtktypes.jl +++ b/src/gtktypes.jl @@ -1,71 +1,15 @@ -abstract GObjectI -typealias GObject GObjectI -abstract GtkWidgetI <: GObjectI -abstract GtkContainerI <: GtkWidgetI -abstract GtkBinI <: GtkContainerI -abstract GtkWindowI <: GtkBinI -abstract GtkDialogI <: GtkWindowI -abstract GtkBoxI <: GtkContainerI -abstract GtkMenuShellI <: GtkContainerI -abstract GtkMenuItemI <: GtkMenuShellI +ccall((:gtk_test_register_all_types,libgtk), Void, ()) +@GType GtkWidget +@GType GtkContainer +@GType GtkBin +@GType GtkWindow +@GType GtkDialog +@GType GtkBox +@GType GtkMenuShell +@GType GtkMenuItem -# Alternative object construction style. This would let us share constructors -# by creating const aliases: `const Z = GObject{:Z}` -type GObjectAny{Name} <: GObjectI - handle::Ptr{GObject} - GObjectAny(handle::Ptr{GObject}) = (handle != C_NULL ? gc_ref(new(handle)) : error("Cannot construct $gname with a NULL pointer")) -end -#type GtkWidgetAny{T} <: GtkWidgetI -# handle::Ptr{GObject} -# GtkWidgetAny(handle::Ptr{GObject}) = gc_ref(new(handle)) -#end -#type GtkContainerAny{T} <: GtkContainerI -# handle::Ptr{GObject} -# GtkContainerAny(handle::Ptr{GObject}) = gc_ref(new(handle)) -#end -#type GtkBinAny{T} <: GtkBinI -# handle::Ptr{GObject} -# GtkBinAny(handle::Ptr{GObject}) = gc_ref(new(handle)) -#end -#type GtkBoxAny{T} <: GtkBoxI -# handle::Ptr{GObject} -# GtkBoxAny(handle::Ptr{GObject}) = gc_ref(new(handle)) -#end - -macro GType(gname) - if isa(gname,Expr) - @assert(gname.head == :comparison && length(gname.args) == 3 && gname.args[2] == :<:, "invalid GType expr") - super = gname.args[3] - gname = gname.args[1] - else - super = :GObject - end - gname = gname::Symbol - quote - type $(esc(gname)) <: $(esc(symbol(string(super,'I')))) - handle::Ptr{GObjectI} - $(esc(gname))(handle::Ptr{GObjectI}) = (handle != C_NULL ? gc_ref(new(handle)) : error("Cannot construct $gname with a NULL pointer")) - end - end -end +typealias Widget GtkWidget -macro quark_str(q) - :( ccall((:g_quark_from_string, libglib), Uint32, (Ptr{Uint8},), bytestring($q)) ) -end -const jlref_quark = quark"julia_ref" - -# All GtkWidgets are expected to have a 'handle' field -# of type Ptr{GObjectI} corresponding to the Gtk object -# and an 'all' field which has type GdkRectangle -# corresponding to the rectangle allocated to the object, -# or to override the size, width, and height methods -convert(::Type{Ptr{GObjectI}},w::GObjectI) = w.handle -convert{T<:GObjectI}(::Type{T},w::Ptr{T}) = convert(T,convert(Ptr{GObjectI},w)) -function convert{T<:GObjectI}(::Type{T},w::Ptr{GObjectI}) - x = ccall((:g_object_get_qdata, libgobject), Ptr{GObjectI}, (Ptr{GObjectI},Uint32), w, jlref_quark) - x == C_NULL && error("GObject didn't have a corresponding Julia object") - unsafe_pointer_to_objref(x)::T -end convert(::Type{Ptr{GObjectI}},w::String) = convert(Ptr{GObjectI},GtkLabel(w)) eltype{T<:GObjectI}(::GSList{T}) = T @@ -95,7 +39,7 @@ else height(w::GtkWidgetI) = allocation(w).height size(w::GtkWidgetI) = (a=allocation(w);(a.width,a.height)) end -show(io::IO, w::GObjectI) = print(io,typeof(w)) +#show(io::IO, w::GObjectI) = print(io,typeof(w)) ### Functions and methods common to all GtkWidget objects #GtkAdjustment(lower,upper,value=lower,step_increment=0,page_increment=0,page_size=0) = @@ -109,23 +53,10 @@ show(w::GtkWidgetI) = ccall((:gtk_widget_show,libgtk),Void,(Ptr{GObjectI},),w) showall(w::GtkWidgetI) = ccall((:gtk_widget_show_all,libgtk),Void,(Ptr{GObjectI},),w) ### Miscellaneous types -typealias Enum Int32 baremodule GtkWindowType const TOPLEVEL = 0 const POPUP = 1 end -baremodule GConnectFlags - const AFTER = 1 - const SWAPPED = 2 - get(s::Symbol) = - if s === :after - AFTER - elseif s === :swapped - SWAPPED - else - Main.Base.error(Main.Base.string("invalid GConnectFlag ",s)) - end -end baremodule GtkPositionType const LEFT = 0 const RIGHT = 1 @@ -145,77 +76,23 @@ baremodule GtkPositionType end end -### Garbage collection [prevention] -const gc_preserve = ObjectIdDict() # reference counted closures -function gc_ref(x::ANY) - global gc_preserve - gc_preserve[x] = (get(gc_preserve, x, 0)::Int)+1 - x -end -function gc_unref(x::ANY) - global gc_preserve - count = get(gc_preserve, x, 0)::Int-1 - if count <= 0 - delete!(gc_preserve, x) - end - nothing +function getindex{T}(w::GtkWidgetI, child::GtkWidgetI, name::Union(String,Symbol), ::Type{T}) + v = gvalue(T) + ccall((:gtk_container_child_get_property,libgtk), Void, + (Ptr{GObject}, Ptr{GObject}, Ptr{Uint8}, Ptr{GValue}), w, child, bytestring(name), v) + val = v[T] + ccall((:g_value_unset,libgobject),Void,(Ptr{GValue},), v) + return val end -gc_ref_closure{T}(x::T) = (gc_ref(x);cfunction(gc_unref, Void, (T, Ptr{Void}))) -gc_unref(x::Any, ::Ptr{Void}) = gc_unref(x) -const gc_preserve_gtk = WeakKeyDict{GObjectI,Union(Bool,GObjectI)}() # gtk objects -function gc_ref{T<:GObjectI}(x::T) - global gc_preserve_gtk - addref = function() - ccall((:g_object_ref_sink,libgobject),Ptr{GObjectI},(Ptr{GObjectI},),x) - finalizer(x,function(x) - global gc_preserve_gtk - gc_preserve_gtk[x] = x # convert to a strong-reference - ccall((:g_object_unref,libgobject),Void,(Ptr{GObjectI},),x) # may clear the strong reference - end) - gc_preserve_gtk[x] = true # record the existence of the object, but allow the finalizer - end - ref = get(gc_preserve_gtk,x,nothing) - if isa(ref,Nothing) - ccall((:g_object_set_qdata_full, libgobject), Void, - (Ptr{GObjectI}, Uint32, Any, Ptr{Void}), x, jlref_quark, x, - cfunction(gc_unref, Void, (T,))) # add a circular reference to the Julia object in the GObjectI - addref() - elseif !isa(ref,WeakRef) - # oops, we previously deleted the link, but now it's back - addref() - else - # already gc-protected, nothing to do - end - x -end - - -function gc_unref_weak(x::GObjectI) - # this strongly destroys and invalidates the object - # it is intended to be called by Gtk, not in user code function - # note: this may be called multiple times by Gtk - x.handle = C_NULL - global gc_preserve_gtk - delete!(gc_preserve_gtk, x) - nothing -end -function gc_unref(x::GObjectI) - # this strongly destroys and invalidates the object - # it is intended to be called by Gtk, not in user code function - ref = ccall((:g_object_get_qdata,libgobject),Ptr{Void},(Ptr{GObjectI},Uint32),x,jlref_quark) - if ref != C_NULL && x !== unsafe_pointer_to_objref(ref) - # We got called because we are no longer the default object for this handle, but we are still alive - warn("Duplicate Julia object creation detected for GObject") - ccall((:g_object_weak_ref,libgobject),Void,(Ptr{GObjectI},Ptr{Void},Any),x,cfunction(gc_unref_weak,Void,(typeof(x),)),x) - else - ccall((:g_object_steal_qdata,libgobject),Any,(Ptr{GObjectI},Uint32),x,jlref_quark) - gc_unref_weak(x) - end - nothing +#setindex!{T}(w::GtkWidgetI, value, child::GtkWidgetI, ::Type{T}) = error("missing Gtk property-name to set") +setindex!{T}(w::GtkWidgetI, value, child::GtkWidgetI, name::Union(String,Symbol), ::Type{T}) = setindex!(w, convert(T,value), child, name) +function setindex!(w::GtkWidgetI, value, child::GtkWidgetI, name::Union(String,Symbol)) + v = gvalue(value) + ccall((:gtk_container_child_set_property,libgtk), Void, + (Ptr{GObject}, Ptr{GObject}, Ptr{Uint8}, Ptr{GValue}), w, child, bytestring(name), v) + w end -gc_unref(::Ptr{GObjectI}, x::GObjectI) = gc_unref(x) -gc_ref_closure(x::GObjectI) = C_NULL baremodule GtkJustification diff --git a/src/gvalues.jl b/src/gvalues.jl index f3d9f59a..f00cd231 100644 --- a/src/gvalues.jl +++ b/src/gvalues.jl @@ -1,10 +1,11 @@ +typealias GType Csize_t ### Getting and Setting Properties immutable GParamSpec g_type_instance::Ptr{Void} name::Ptr{Uint8} flags::Cint - value_type::Csize_t - owner_type::Csize_t + value_type::GType + owner_type::GType end const fundamental_types = ( @@ -34,15 +35,25 @@ const fundamental_types = ( #(:GVariant, Ptr{GVariant}, GVariant, :variant), ) # NOTE: in general do not cache ids, except for the fundamental values -g_type_from_name(name::Symbol) = ccall((:g_type_from_name,libgobject),Int,(Ptr{Uint8},),name) +g_type_from_name(name::Symbol) = ccall((:g_type_from_name,libgobject),GType,(Ptr{Uint8},),name) # these constants are used elsewhere + const gvoid_id = g_type_from_name(:void) const gboxed_id = g_type_from_name(:GBoxed) const gobject_id = g_type_from_name(:GObject) const gstring_id = g_type_from_name(:gchararray) +g_type_parent(child::GType ) = ccall((:g_type_parent, libgobject), GType, (GType,), child) +g_type_name(g_type::GType) = bytestring(ccall((:g_type_name,libgobject),Ptr{Uint8},(GType,),g_type)) + +g_type_test_flags(g_type::GType, flag) = ccall((:g_type_test_flags,libgobject), Bool, (GType,Enum), g_type, flag) +const G_TYPE_FLAG_CLASSED = 1 << 0 +const G_TYPE_FLAG_INSTANTIATABLE = 1 << 1 +const G_TYPE_FLAG_DERIVABLE = 1 << 2 +const G_TYPE_FLAG_DEEP_DERIVABLE = 1 << 3 + immutable GValue - g_type::Csize_t + g_type::GType field2::Uint64 field3::Uint64 GValue() = new(0,0,0) @@ -81,16 +92,16 @@ function make_gvalue(pass_x,as_ctype,to_gtype,with_id,allow_reverse::Bool=true,f end if pass_x !== None eval(quote - function Base.setindex!{T<:$pass_x}(v::Gtk.GV, ::Type{T}) - ccall((:g_value_init,Gtk.libgobject),Void,(Ptr{Gtk.GValue},Csize_t), v, $with_id) + function Base.setindex!{T<:$pass_x}(v::GLib.GV, ::Type{T}) + ccall((:g_value_init,GLib.libgobject),Void,(Ptr{GLib.GValue},GType), v, $with_id) v end - function Base.setindex!{T<:$pass_x}(v::Gtk.GV, x::T) - $(if to_gtype == :string; :(x = Gtk.bytestring(x)) end) - $(if to_gtype == :pointer || to_gtype == :boxed; :(x = Gtk.mutable(x)) end) - ccall(($(string("g_value_set_",to_gtype)),Gtk.libgobject),Void,(Ptr{Gtk.GValue},$as_ctype), v, x) - if isa(v, Gtk.MutableTypes.MutableX) - finalizer(v, (v::Gtk.MutableTypes.MutableX)->ccall((:g_value_unset,Gtk.libgobject),Void,(Ptr{Gtk.GValue},), v)) + function Base.setindex!{T<:$pass_x}(v::GLib.GV, x::T) + $(if to_gtype == :string; :(x = GLib.bytestring(x)) end) + $(if to_gtype == :pointer || to_gtype == :boxed; :(x = GLib.mutable(x)) end) + ccall(($(string("g_value_set_",to_gtype)),GLib.libgobject),Void,(Ptr{GLib.GValue},$as_ctype), v, x) + if isa(v, GLib.MutableTypes.MutableX) + finalizer(v, (v::GLib.MutableTypes.MutableX)->ccall((:g_value_unset,GLib.libgobject),Void,(Ptr{GLib.GValue},), v)) end v end @@ -99,9 +110,9 @@ function make_gvalue(pass_x,as_ctype,to_gtype,with_id,allow_reverse::Bool=true,f to_gtype = :string end eval(quote - function Base.getindex{T<:$pass_x}(v::Gtk.GV,::Type{T}) - x = ccall(($(string("g_value_get_",to_gtype)),Gtk.libgobject),$as_ctype,(Ptr{Gtk.GValue},), v) - $(if to_gtype == :string; :(x = Gtk.bytestring(x)) end) + function Base.getindex{T<:$pass_x}(v::GLib.GV,::Type{T}) + x = ccall(($(string("g_value_get_",to_gtype)),GLib.libgobject),$as_ctype,(Ptr{GLib.GValue},), v) + $(if to_gtype == :string; :(x = GLib.bytestring(x)) end) $(if pass_x == Symbol; :(x = symbol(x)) end) return Base.convert(T,x) end @@ -112,9 +123,9 @@ function make_gvalue(pass_x,as_ctype,to_gtype,with_id,allow_reverse::Bool=true,f to_gtype = :string end fn = eval(quote - function(v::Gtk.GV) - x = ccall(($(string("g_value_get_",to_gtype)),Gtk.libgobject),$as_ctype,(Ptr{Gtk.GValue},), v) - $(if to_gtype == :string; :(x = Gtk.bytestring(x)) end) + function(v::GLib.GV) + x = ccall(($(string("g_value_get_",to_gtype)),GLib.libgobject),$as_ctype,(Ptr{GLib.GValue},), v) + $(if to_gtype == :string; :(x = GLib.bytestring(x)) end) $(if pass_x !== None :(return Base.convert($pass_x,x)) else @@ -158,7 +169,7 @@ function getindex(gv::Union(Mutable{GValue}, Ptr{GValue})) return fundamental_fns[i](gv) end end - typename = bytestring(ccall((:g_type_name,libgobject),Ptr{Uint8},(Int,),g_type)) + typename = g_type_name(g_type) error("Could not convert GValue of type $typename to Julia type") end #end @@ -183,15 +194,6 @@ function getindex{T}(w::GObject, name::Union(String,Symbol), ::Type{T}) return val end -function getindex{T}(w::GtkWidgetI, child::GtkWidgetI, name::Union(String,Symbol), ::Type{T}) - v = gvalue(T) - ccall((:gtk_container_child_get_property,libgtk), Void, - (Ptr{GObject}, Ptr{GObject}, Ptr{Uint8}, Ptr{GValue}), w, child, bytestring(name), v) - val = v[T] - ccall((:g_value_unset,libgobject),Void,(Ptr{GValue},), v) - return val -end - setindex!{T}(w::GObject, value, name::Union(String,Symbol), ::Type{T}) = setindex!(w, convert(T,value), name) function setindex!(w::GObject, value, name::Union(String,Symbol)) v = gvalue(value) @@ -200,18 +202,11 @@ function setindex!(w::GObject, value, name::Union(String,Symbol)) w end -#setindex!{T}(w::GtkWidgetI, value, child::GtkWidgetI, ::Type{T}) = error("missing Gtk property-name to set") -setindex!{T}(w::GtkWidgetI, value, child::GtkWidgetI, name::Union(String,Symbol), ::Type{T}) = setindex!(w, convert(T,value), child, name) -function setindex!(w::GtkWidgetI, value, child::GtkWidgetI, name::Union(String,Symbol)) - v = gvalue(value) - ccall((:gtk_container_child_set_property,libgtk), Void, - (Ptr{GObject}, Ptr{GObject}, Ptr{Uint8}, Ptr{GValue}), w, child, bytestring(name), v) - w -end +G_TYPE_FROM_CLASS(w::Ptr{Void}) = unsafe_load(convert(Ptr{GType},w)) +G_OBJECT_GET_CLASS(w::GObject) = G_OBJECT_GET_CLASS(w.handle) +G_OBJECT_GET_CLASS(hnd::Ptr{GObjectI}) = unsafe_load(convert(Ptr{Ptr{Void}},hnd)) +G_OBJECT_CLASS_TYPE(w) = G_TYPE_FROM_CLASS(G_OBJECT_GET_CLASS(w)) -G_TYPE_FROM_CLASS(w::Ptr{Void}) = unsafe_load(convert(Ptr{Csize_t},w)) -G_OBJECT_GET_CLASS(w::GObject) = unsafe_load(convert(Ptr{Ptr{Void}},w.handle)) -G_OBJECT_CLASS_TYPE(w::GObject) = G_TYPE_FROM_CLASS(G_OBJECT_GET_CLASS(w)) function show(io::IO, w::GObject) print(io,typeof(w),'(') @@ -249,6 +244,58 @@ function show(io::IO, w::GObject) ccall((:g_value_unset,libgobject),Ptr{Void},(Ptr{GValue},), v) end +#this should probably be merged with gtype_values +immutable RegisteredType + iface::Type + wrapper::Type +end +const registered_gtypes = Dict{GType,RegisteredType}() +registered_gtypes[gobject_id] = RegisteredType(GObjectI, GObjectAny) + +function register_gtype(typename::Symbol) + gtype = g_type_from_name(typename) + if gtype == C_NULL + error("no such GType: $typename") + end + register_gtype(gtype) +end + +function register_gtype(g_type::GType) + if haskey(registered_gtypes,g_type) + return registered_gtypes[g_type] + end + #if g_type_test_flags(g_type, G_TYPE_FLAG_CLASSED) + # error("not implemented yet") + #end + name = symbol(g_type_name(g_type)) + iname = symbol("$(name)I") + pgtype = g_type_parent(g_type) # TODO: For struct types this may be zero + parent = register_gtype(pgtype) + + regtype = eval(quote + abstract ($iname) <: $(parent.iface) + #TODO: check if instantiable + #this could also be used for structs + #that are not GOBjocts + type ($name) <: ($iname) + handle::Ptr{GObjectI} + $name(handle::Ptr{GObjectI}) = (handle != C_NULL ? GLib.gc_ref(new(handle)) : error($("Cannot construct $name with a NULL pointer"))) + end #FIXME + RegisteredType($iname, $name) + end) + registered_gtypes[g_type] = regtype + regtype +end + + +macro GType(name) + iname = symbol("$(name)I") + regtype = register_gtype(name) # TODO: For struct types this may be zero + quote + const $(esc(name)) = $(regtype.wrapper) + const $(esc(iname)) = $(regtype.iface) + end +end #immutable GTypeQuery # g_type::Int # type_name::Ptr{Uint8} diff --git a/src/input.jl b/src/input.jl index e6530df3..67cecc12 100644 --- a/src/input.jl +++ b/src/input.jl @@ -8,10 +8,10 @@ #GtkSpinButton — Retrieve an integer or floating-point number from the user #GtkEditable — Interface for text-editing widgets -@GType GtkEntry <: GtkWidget +@GType GtkEntry GtkEntry() = GtkEntry(ccall((:gtk_entry_new,libgtk),Ptr{GObject},())) -@GType GtkScale <: GtkWidget +@GType GtkScale if gtk_version == 3 GtkScale(vertical::Bool,min,max,step) = GtkScale(ccall((:gtk_scale_new_with_range,libgtk),Ptr{GObject}, (Cint,Cdouble,Cdouble,Cdouble),vertical,min,max,step)) @@ -40,7 +40,7 @@ function push!(scale::GtkScale, value, position::Symbol) end empty!(scale::GtkScale) = ccall((:gtk_scale_clear_marks,libgtk),Void,(Ptr{GObject},),scale) -@GType GtkAdjustment <: GObject +@GType GtkAdjustment GtkAdjustment(value,lower,upper,step_increment,page_increment,page_size) = GtkAdjustment(ccall((:gtk_adjustment_new,libgtk), Ptr{GObject}, (Float64,Float64,Float64,Float64,Float64,Float64), @@ -49,7 +49,7 @@ GtkAdjustment(value,lower,upper,step_increment,page_increment,page_size) = GtkAdjustment(scale::GtkScale) = GtkAdjustment(ccall((:gtk_range_get_adjustment,libgtk), Ptr{GObject},(Ptr{GObject},), scale)) -@GType GtkSpinButton <: GtkWidget +@GType GtkSpinButton GtkSpinButton(min,max,step) = GtkSpinButton(ccall((:gtk_spin_button_new_with_range,libgtk),Ptr{GObject}, (Cdouble,Cdouble,Cdouble),min,max,step)) GtkSpinButton(scale::Ranges) = GtkSpinButton(minimum(scale),maximum(scale),step(scale)) diff --git a/src/layout.jl b/src/layout.jl index 7e4819a3..f5345159 100644 --- a/src/layout.jl +++ b/src/layout.jl @@ -21,7 +21,7 @@ if gtk_version == 3 ### GtkGrid was introduced in Gtk3 (replaces GtkTable) - @GType GtkGrid <: GtkContainer + @GType GtkGrid GtkGrid() = GtkGrid(ccall((:gtk_grid_new, libgtk), Ptr{GObject}, ())) function getindex(grid::GtkGrid, i::Integer, j::Integer) @@ -58,7 +58,7 @@ else end ### GtkTable was deprecated in Gtk3 (replaced by GtkGrid) -@GType GtkTable <: GtkContainer +@GType GtkTable GtkTable(x::Integer, y::Integer, homogeneous::Bool=false) = GtkTable(ccall((:gtk_table_new, libgtk), Ptr{GObject}, (Cint, Cint, Cint), x, y, homogeneous)) GtkTable(homogeneous::Bool=false) = GtkTable(0,0,homogeneous) setindex!{T<:Integer,R<:Integer}(grid::GtkTable, child, i::Union(T,Range1{T}), j::Union(R,Range1{R})) = @@ -70,20 +70,20 @@ setindex!{T<:Integer,R<:Integer}(grid::GtkTable, child, i::Union(T,Range1{T}), j # (Ptr{GObject}, Ptr{GObject}, Cint, Cint, Cint, Cint), grid, child, first(i)-1, last(i), first(j)-1, last(j)) ### GtkAlignment was deprecated in Gtk3 (replaced by properties "halign", "valign", and "margin") -@GType GtkAlignment <: GtkBin +@GType GtkAlignment GtkAlignment(xalign, yalign, xscale, yscale) = # % of available space, 0<=a<=1 GtkAlignment(ccall((:gtk_alignment_new, libgtk), Ptr{GObject}, (Cfloat, Cfloat, Cfloat, Cfloat), xalign, yalign, xscale, yscale)) ### GtkFrame — A bin with a decorative frame and optional label -@GType GtkFrame <: GtkBin +@GType GtkFrame GtkFrame(label::String) = GtkFrame(ccall((:gtk_frame_new, libgtk), Ptr{GObject}, (Ptr{Uint8},), bytestring(label))) GtkFrame() = GtkFrame(ccall((:gtk_frame_new, libgtk), Ptr{GObject}, (Ptr{Uint8},), C_NULL)) ### GtkAspectFrame -@GType GtkAspectFrame <: GtkBin +@GType GtkAspectFrame GtkAspectFrame(label, xalign, yalign, ratio) = # % of available space, 0<=a<=1 GtkAspectFrame(ccall((:gtk_aspect_frame_new, libgtk), Ptr{GObject}, (Ptr{Uint8}, Cfloat, Cfloat, Cfloat, Cint), bytestring(label), xalign, yalign, ratio, false)) @@ -92,7 +92,7 @@ GtkAspectFrame(label, xalign, yalign) = # % of available space, 0<=a<=1. Uses th (Ptr{Uint8}, Cfloat, Cfloat, Cfloat, Cint), bytestring(label), xalign, yalign, 1., true)) ### GtkBox -@GType GtkBox <: GtkBox +@GType GtkBox if gtk_version == 3 GtkBox(vertical::Bool, spacing=0) = GtkBox(ccall((:gtk_box_new, libgtk), Ptr{GObject}, @@ -111,7 +111,7 @@ else end ### GtkButtonBox -@GType GtkButtonBox <: GtkBox +@GType GtkButtonBox if gtk_version == 3 GtkButtonBox(vertical::Bool) = GtkButtonBox(ccall((:gtk_button_box_new, libgtk), Ptr{GObject}, @@ -131,7 +131,7 @@ end # this is a bad option, so I'm leaving it out ### GtkPaned -@GType GtkPaned <: GtkContainer +@GType GtkPaned if gtk_version == 3 GtkPaned(vertical::Bool, spacing=0) = GtkPaned(ccall((:gtk_paned_new, libgtk), Ptr{GObject}, @@ -179,7 +179,7 @@ function setindex(grid::GtkPaned, child, i::Integer, resize::Bool, shrink::Bool= end ### GtkLayout -@GType GtkLayout <: GtkContainer +@GType GtkLayout function GtkLayout(width, height) layout = ccall((:gtk_layout_new, libgtk), Ptr{GObject}, (Ptr{Void},Ptr{Void}), C_NULL, C_NULL) @@ -198,13 +198,13 @@ width(layout::GtkLayout) = size(layout)[1] height(layout::GtkLayout) = size(layout)[2] ### GtkExpander -@GType GtkExpander <: GtkBin +@GType GtkExpander GtkExpander(title) = GtkExpander(ccall((:gtk_expander_new, libgtk), Ptr{GObject}, (Ptr{Uint8},), bytestring(title))) ### GtkNotebook -@GType GtkNotebook <: GtkContainer +@GType GtkNotebook GtkNotebook() = GtkNotebook(ccall((:gtk_notebook_new, libgtk), Ptr{GObject},())) function insert!(w::GtkNotebook, position::Integer, x::Union(GtkWidgetI,String), label::String) ccall((:gtk_notebook_insert_page,libgtk), Cint, @@ -233,7 +233,7 @@ end ### GtkOverlay if gtk_version == 3 - @GType GtkOverlay <: GtkContainer #technically, this is a GtkBin, except it behaves more like a container + @GType GtkOverlay #technically, this is a GtkBin, except it behaves more like a container GtkOverlay() = GtkOverlay(ccall((:gtk_overlay_new, libgtk), Ptr{GObject}, (Ptr{Uint8},), bytestring(title))) GtkOverlay(w::GtkWidgetI) = invoke(push!, (GtkContainer,), GtkOverlay(), w) diff --git a/src/lists.jl b/src/lists.jl index 87d286a1..30ec1b4d 100644 --- a/src/lists.jl +++ b/src/lists.jl @@ -28,7 +28,7 @@ #GtkComboBox — A widget used to choose from a list of items #GtkComboBoxText — A simple, text-only combo box -@GType GtkComboBoxText <: GtkWidget +@GType GtkComboBoxText GtkComboBoxText(with_entry::Bool=false) = GtkComboBoxText( if with_entry ccall((:gtk_combo_box_text_new_with_entry,libgtk),Ptr{GObject},()) diff --git a/src/menus.jl b/src/menus.jl index fdd960f5..5753e72d 100644 --- a/src/menus.jl +++ b/src/menus.jl @@ -19,18 +19,18 @@ #GtkToggleToolButton — A GtkToolItem containing a toggle button #GtkRadioToolButton — A toolbar item that contains a radio button -@GType GtkMenuItem <: GtkMenuItem +@GType GtkMenuItem GtkMenuItem() = GtkMenuItem(ccall((:gtk_menu_item_new,libgtk),Ptr{GObject},())) GtkMenuItem(label::String) = GtkMenuItem(ccall((:gtk_menu_item_new_with_mnemonic,libgtk),Ptr{GObject}, (Ptr{Uint8},), bytestring(label))) -@GType GtkSeparatorMenuItem <: GtkMenuItem +@GType GtkSeparatorMenuItem GtkSeparatorMenuItem() = GtkSeparatorMenuItem(ccall((:gtk_separator_menu_item_new,libgtk),Ptr{GObject},())) -@GType GtkMenu <: GtkMenuShell +@GType GtkMenu GtkMenu() = GtkMenu(ccall((:gtk_menu_new,libgtk),Ptr{GObject},())) function GtkMenu(item::GtkMenuItem) menu = GtkMenu() @@ -41,7 +41,7 @@ function GtkMenu(item::GtkMenuItem) end -@GType GtkMenuBar <: GtkMenuShell +@GType GtkMenuBar GtkMenuBar() = GtkMenuBar(ccall((:gtk_menu_bar_new,libgtk),Ptr{GObject},())) diff --git a/src/selectors.jl b/src/selectors.jl index 329e4f52..9577b21a 100644 --- a/src/selectors.jl +++ b/src/selectors.jl @@ -18,7 +18,7 @@ push!(widget::GtkDialogI, text::String, response::Integer) = ccall((:gtk_dialog_add_button,libgtk), Ptr{GObject}, (Ptr{GObject},Ptr{Uint8},Cint), widget, text, response) -@GType GtkFileChooserDialog <: GtkDialog +@GType GtkFileChooserDialog function GtkFileChooserDialog(title::String, parent::GtkContainerI, action::Integer, button_text_response...) n = length(button_text_response) if !iseven(n) diff --git a/src/signals.jl b/src/signals.jl new file mode 100644 index 00000000..53f5b9e0 --- /dev/null +++ b/src/signals.jl @@ -0,0 +1,129 @@ +# id = signal_connect(widget, :event, Void, (ArgsT...)) do ptr, evt_args..., closure +# stuff +# end +function signal_connect(cb::Function,w::GObject,sig::Union(String,Symbol), + RT::Type,param_types::Tuple,after::Bool=false,closure=w) #TODO: assert that length(param_types) is correct + if isgeneric(cb) + callback = cfunction(cb,RT,tuple(Ptr{GObject},param_types...,typeof(closure))) + return ccall((:g_signal_connect_data,libgobject), Culong, + (Ptr{GObject}, Ptr{Uint8}, Ptr{Void}, Any, Ptr{Void}, Enum), + w, + bytestring(sig), + callback, + closure, + gc_ref_closure(closure), + after*GConnectFlags.AFTER) + end + # oops, Julia doesn't support this natively yet -- fake it instead + return _signal_connect(cb, w, sig, after, true,param_types,closure) +end + +# id = signal_connect(widget, :event) do obj, evt_args... +# stuff +# end +function signal_connect(cb::Function,w::GObject,sig::Union(String,Symbol),after::Bool=false) + _signal_connect(cb, w, sig, after, false,nothing,nothing) +end +function _signal_connect(cb::Function,w::GObject,sig::Union(String,Symbol),after::Bool,gtk_call_conv::Bool,param_types,closure) + closuref = ccall((:g_closure_new_object,libgobject), Ptr{Void}, (Cuint, Ptr{GObject}), sizeof_gclosure::Int+WORD_SIZE*2, w) + closure_env = convert(Ptr{Any},closuref+sizeof_gclosure) + unsafe_store!(closure_env, cb, 1) + if gtk_call_conv + env = Any[param_types,closure] + unsafe_store!(closure_env, env, 2) + ccall((:g_closure_add_invalidate_notifier,libgobject), Void, + (Ptr{Void}, Any, Ptr{Void}), closuref, env, gc_ref_closure(env)) + else + unsafe_store!(convert(Ptr{Int},closure_env), 0, 2) + end + ccall((:g_closure_add_invalidate_notifier,libgobject), Void, + (Ptr{Void}, Any, Ptr{Void}), closuref, cb, gc_ref_closure(cb)) + ccall((:g_closure_set_marshal,libgobject), Void, + (Ptr{Void}, Ptr{Void}), closuref, JuliaClosureMarshal) + return ccall((:g_signal_connect_closure,libgobject), Culong, + (Ptr{GObject}, Ptr{Uint8}, Ptr{Void}, Cint), w, bytestring(sig), closuref, after) +end +function GClosureMarshal(closuref, return_value, n_param_values, + param_values, invocation_hint, marshal_data) + try + closure_env = convert(Ptr{Any},closuref+sizeof_gclosure) + cb = unsafe_load(closure_env, 1) + gtk_calling_convention = (0 != unsafe_load(convert(Ptr{Int},closure_env), 2)) + params = Array(Any, n_param_values) + if gtk_calling_convention + # compatibility mode, if we must + param_types,closure = unsafe_load(closure_env, 2)::Array{Any,1} + length(param_types)+1 == n_param_values || error("GCallback called with the wrong number of parameters") + for i = 1:n_param_values + gv = mutable(param_values,i) + g_type = unsafe_load(gv).g_type + # avoid auto-unboxing for some builtin types in gtk_calling_convention mode + if bool(ccall((:g_type_is_a,libgobject),Cint,(Int,Int),g_type,gobject_id)) + params[i] = ccall((:g_value_get_object,libgobject), Ptr{GObject}, (Ptr{GValue},), gv) + elseif bool(ccall((:g_type_is_a,libgobject),Cint,(Int,Int),g_type,gboxed_id)) + params[i] = ccall((:g_value_get_boxed,libgobject), Ptr{Void}, (Ptr{GValue},), gv) + elseif bool(ccall((:g_type_is_a,libgobject),Cint,(Int,Int),g_type,gstring_id)) + params[i] = ccall((:g_value_get_string,libgobject), Ptr{Void}, (Ptr{GValue},), gv) + else + params[i] = gv[] + end + if i > 1 + params[i] = convert(param_types[i-1], params[i]) + end + end + push!(params, closure) + else + for i = 1:n_param_values + params[i] = mutable(param_values,i)[] + end + end + retval = cb(params...) # widget, args... + if return_value != C_NULL && retval !== nothing + g_type = unsafe_load(return_value).g_type + if g_type != gvoid_id && g_type != 0 + return_value[] = gvalue(retval) + end + end + catch e + Base.display_error(e,catch_backtrace()) + end + return nothing +end +JuliaClosureMarshal = cfunction(GClosureMarshal, Void, + (Ptr{Void}, Ptr{GValue}, Cuint, Ptr{GValue}, Ptr{Void}, Ptr{Void})) + +signal_handler_disconnect(w::GObject, handler_id::Culong) = + ccall(:g_signal_handler_disconnect, Void, (Ptr{GObject}, Culong), w, handler_id) + +signal_handler_block(w::GObject, handler_id::Culong) = + ccall(:g_signal_handler_block, Void, (Ptr{GObject}, Culong), w, handler_id) + +signal_handler_unblock(w::GObject, handler_id::Culong) = + ccall(:g_signal_handler_unblock, Void, (Ptr{GObject}, Culong), w, handler_id) + +function signal_emit(w::GObject, sig::Union(String,Symbol), RT::Type, args...) + i = isa(sig, String) ? search(sig, "::") : (0:-1) + if !isempty(i) + detail = @quark_str sig[last(i)+1:end] + sig = sig[1:first(i)-1] + else + detail = uint32(0) + end + signal_id = ccall((:g_signal_lookup,libgobject),Cuint,(Ptr{Uint8},Csize_t), sig, G_OBJECT_CLASS_TYPE(w)) + return_value = RT===Void ? C_NULL : gvalue(RT) + ccall((:g_signal_emitv,libgobject),Void,(Ptr{GValue},Cuint,Uint32,Ptr{GValue}),gvalues(w, args...),signal_id,detail,return_value) + return_value[RT] +end + +baremodule GConnectFlags + const AFTER = 1 + const SWAPPED = 2 + get(s::Symbol) = + if s === :after + AFTER + elseif s === :swapped + SWAPPED + else + Main.Base.error(Main.Base.string("invalid GConnectFlag ",s)) + end +end diff --git a/src/text.jl b/src/text.jl index 2cd30937..1ab75972 100644 --- a/src/text.jl +++ b/src/text.jl @@ -13,7 +13,7 @@ #TODO: GtkAccel manager objects -@GType GtkLabel <: GtkWidget +@GType GtkLabel GtkLabel(title) = GtkLabel( ccall((:gtk_label_new,libgtk),Ptr{GObject},(Ptr{Uint8},), bytestring(title))) @@ -21,7 +21,7 @@ GtkLabel(title) = GtkLabel( GtkTextBuffer() = GtkTextBuffer( ccall((:gtk_text_buffer_new,libgtk),Ptr{GObject},(Ptr{GObject},),C_NULL)) -@GType GtkTextView <: GtkWidget +@GType GtkTextView GtkTextView(buffer=GtkTextBuffer()) = GtkTextView( ccall((:gtk_text_view_new_with_buffer,libgtk),Ptr{GObject},(Ptr{GObject},),buffer)) diff --git a/src/windows.jl b/src/windows.jl index 1ed531e1..34002fa2 100644 --- a/src/windows.jl +++ b/src/windows.jl @@ -1,4 +1,4 @@ -@GType GtkWindow <: GtkWindow +@GType GtkWindow function GtkWindow(title=nothing, w=-1, h=-1, resizable=true, toplevel=true) hnd = ccall((:gtk_window_new,libgtk),Ptr{GObject},(Enum,), toplevel?GtkWindowType.TOPLEVEL:GtkWindowType.POPUP) diff --git a/test/girepo_test.jl b/test/girepo_test.jl new file mode 100644 index 00000000..66c2266f --- /dev/null +++ b/test/girepo_test.jl @@ -0,0 +1,30 @@ +using GI + +gtk = GI.GINamespace(:Gtk) + +window = gtk[:Window] +@assert isa(window, GI.GIObjectInfo) + +wnew = GI.find_method(window,:new) +wmove = GI.find_method(window,:move) + +args = GI.get_args(wmove) +@assert length(args) == 2 + +argx = args[1] +@assert GI.get_name(argx) == :x +@assert GI.extract_type(argx) == Int32 + +GI.ensure_name(gtk, :Window) +GI.ensure_method(gtk, :Window, :move) + +@gimport Gtk init, main, Window(move,set_title,get_title), Widget(show) +init(0,C_NULL) +w = Window_new(0) +show(w) #NB: currently doesn't extend Base.show +move(w,100,100) + +#string passing +set_title(w,"GI test") +@assert get_title(w) == "GI test" +main() diff --git a/test/girepo_test_clutter.jl b/test/girepo_test_clutter.jl new file mode 100644 index 00000000..6d74a4ae --- /dev/null +++ b/test/girepo_test_clutter.jl @@ -0,0 +1,8 @@ +using GI +@gimport Clutter init, Actor +Clutter.init(0, C_NULL) +actor = Clutter.Actor_new() +display(actor) +@assert isa(actor,GI.GObject) +@assert isa(actor,Actor) + diff --git a/test/tests.jl b/test/tests.jl index 8caa1c77..01012613 100644 --- a/test/tests.jl +++ b/test/tests.jl @@ -97,7 +97,7 @@ g2[b22,:pack_type] = 1 #GTK_PACK_END destroy(w) ## ButtonBox -bb = ButtonBox(:h) +bb = ButtonBox(false) w = Window(bb, "ButtonBox") cancel = Button("Cancel") ok = Button("OK")