From bd61ebf077464b0711cdd7ba389a822729b4354b Mon Sep 17 00:00:00 2001 From: David Ball Date: Sat, 14 Jan 2012 14:44:14 -0500 Subject: [PATCH 1/9] Imported swick's object properties mechanism and camelCase() function. Modified camelCase() so that it works with property-name as well as property_name. Modified property activation so that object access security would not throw an Exception, by installing try{}catch{} to trap errors in property setters and getters. Added object.new() as a JS object constructor, but then disabled it due to ambiguity--objects have multiple new() functions from gir and there's no way to know which one is a better candidate, if any. --- gir.js | 109 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 23 deletions(-) diff --git a/gir.js b/gir.js index 0fcf77f..454ef75 100644 --- a/gir.js +++ b/gir.js @@ -174,19 +174,59 @@ gir.init = function() { } }; -//create callable method object -function CallableMethod(methodName) { - //the internal function does all the hard work - var invocation = function() { - var args = Array.prototype.slice.call(arguments); - if (args == undefined) args = new Array(); - for (var i = args.length; i > 0; i--) - args[i] = args[i-1]; - args[0] = methodName; - //call the method on the gir provided object - this.__call__.apply(this, args); - }; - return invocation; +//converts long_method_names to properCamelCasing :-) +//converts long-property-names to longPropertyNames +//Merged from swick/node-gir +function camelCase(x) { + return x.replace(/[-]+/g, function (h) { + return '_'; + }).replace(/\_[a-z]/g, function(h) { + return h.substr(1).toUpperCase(); + }); +} + +//create method object +//simplified using merge from swick/node-gir +function defineObjectMethod(obj, method_name) { + //converts method name to camelCasing on the object + obj.prototype[camelCase(method_name)] = function(method_name) { + //the internal function does all the hard work + var invocation = function() { + var args = Array.prototype.slice.call(arguments); + if (args == undefined) args = new Array(); + args.unshift(method_name); + //call the method on the gir provided object +console.log('called',method_name,'in',obj); + this.__call__.apply(this, args); + }; + return invocation; + }(method_name); +} + +//create property object +//merged from swick/node-gir +//added try{}catch{} block because of TypeError: Illegal invocation, in what appears +//to be accessing beyond object access boundaries, e.g. trying to access a private member, or +//setting a getter-only property, or something to that effect. +//perhaps someone can figure out why this version throws TypeError: Illegal invocation +//across certain boundaries while the swick/node-gir does not :~ could be a diff in binaries +function defineObjectProperty(obj, property_name) { + obj.prototype.__defineGetter__(camelCase(property_name), function() { + try { + return this.__get_property__.apply(this, [property_name]); + } catch (err) { + //debug:console.warn('[node-gir] Error trying to install getter ' + camelCase(property_name) + '.', err); + return undefined; + } + }); + obj.prototype.__defineSetter__(camelCase(property_name), function(newValue) { + try { + return this.__set_property__.apply(this, [property_name, newValue]); + } catch (err) { + //debug:console.warn('[node-gir] Error trying to install setter ' + camelCase(property_name) + '.', err); + return newValue; + } + }); } //override default loader @@ -205,6 +245,7 @@ gir.load = function() { //for each object within the loaded gir module: // task 1. figure out which loaded objects can trigger events, and add EventEmitter as needed // task 2. make method names callable methods + // task 3. properties need getter/setter set (merge from swick/node-gir) for (var subobj in obj) { //task 1: add EventEmitter as needed //determine whether eventable @@ -225,23 +266,45 @@ gir.load = function() { } } - //task 2: expose object methods to objects and make them callable + //task 2: expose methods and make them callable + //task 3: expose properties and make them settable/gettable for (var prop in obj[subobj]) { switch (prop) { - case '__methods__': - for (var method_offset in obj[subobj][prop]) { - var method_name = obj[subobj][prop][method_offset]; - //debug:console.log(subobj + '.' + method_name + '() discovered'); + case '__methods__': //task 2 + for (var method in obj[subobj][prop]) { + var method_name = obj[subobj][prop][method]; //add method handler to object if possible - if (obj[subobj].prototype[method_name] != undefined) - {}//debug:console.warn("[node-gir] " + subobj + " object provides it's own " + method_name + " method. Not replacing existing method. :-("); - else - obj[subobj].prototype[method_name] = CallableMethod(method_name); + if (!obj[subobj].prototype[method_name]) + defineObjectMethod(obj[subobj], method_name); + //else + //debug:console.warn("[node-gir] " + subobj + " object provides it's own " + method_name + " method. Not replacing existing method. :-("); + + //Not sure if this would do anything positive: + //Check for new(), and makes new() the default constructor if it's in the gir object. + //Disabled until it would serve useful. It simply assigns the new() function as + //the JS constructor for the object. There is a small problem: some objects have multiple + //new() functions, probably due to object inheritence or overloading. :~ Not sure really, + //but it seems to add no new functionality while simultaneously not taking anything away. + //It may be thrown away in a future commit if it is decidedly useless. + //DISABLED: + /*if (method_name == 'new') { + //debug:console.log('[node-gir] Using new() as the default constructor for', subobj, 'provided by gir.'); + obj[subobj].prototype.constructor = defineObjectMethod(obj[subobj], 'new'); + }*/ + } + break; + case '__properties__': //task 3 + for (var property in obj[subobj][prop]) { + var property_name = obj[subobj][prop][property]; + //add property handler to object if possible + if (!obj[subobj].prototype[property_name]) + defineObjectProperty(obj[subobj], property_name); + //else + //debug:console.warn("[node-gir] " + subobj + " object provides it's own " + property_name + " property. Not replacing existing property. :-("); } break; } } - //console.log(subobj, obj[subobj]); } //keep the loader in the loaded object in case caller wants to reuse the loader From 1ee0c1e6b753234166fd720be54e7ba821bb4af4 Mon Sep 17 00:00:00 2001 From: David Ball Date: Sat, 14 Jan 2012 15:01:53 -0500 Subject: [PATCH 2/9] Updated examples to camelCasing. Improved multiple_test. Replaced dump_* examples to a single inspector example, where any object can be interrogated from the command line. Improved Clutter example, however, it does not work due to a problem with static object member, StageManager. --- examples/browser.js | 24 ------------------- examples/clutter.js | 33 +++++++++++++++++++++++++- examples/dump-clutter.js | 2 -- examples/dump-midgard.js | 2 -- examples/gtk.js | 28 ++++++++++++++++++++++ examples/gtk_test.js | 29 ----------------------- examples/inspector.js | 49 +++++++++++++++++++++++++++++++++++++++ examples/multiple_test.js | 49 +++++++++++++++++++++++++++++++++++---- examples/notify.js | 36 +++++++++++++++++++++++++++- examples/notify_test.js | 35 ---------------------------- examples/webkit.js | 27 ++++++++++++++++++++- 11 files changed, 215 insertions(+), 99 deletions(-) delete mode 100644 examples/browser.js delete mode 100644 examples/dump-clutter.js delete mode 100644 examples/dump-midgard.js delete mode 100644 examples/gtk_test.js create mode 100644 examples/inspector.js delete mode 100644 examples/notify_test.js diff --git a/examples/browser.js b/examples/browser.js deleted file mode 100644 index 251fcce..0000000 --- a/examples/browser.js +++ /dev/null @@ -1,24 +0,0 @@ -var gtk = require("./gtk") - , WebKit = require("./webkit"); - -gtk.init(0); - -var win = new gtk.Window(); - -win.on('destroy', function() { - console.log('Window destroyed'); - gtk.mainQuit(); - process.exit(); -}); - -var sw = new gtk.ScrolledWindow(); -win.add(sw); - -var view = new WebKit.WebView(); -view.load_uri("http://www.google.com/"); -sw.add(view); - -win.set_size_request(640, 480); -win.show_all(); - -gtk.main(); diff --git a/examples/clutter.js b/examples/clutter.js index 8c3c4b4..d4d8456 100644 --- a/examples/clutter.js +++ b/examples/clutter.js @@ -1,2 +1,33 @@ var gir = require('../gir') - , clutter = module.exports = gir.load('Clutter'); + , clutter = gir.load('Clutter'); + +clutter.init(0); + +//I used .prototype because I need a static object. Not sure if this is right. +var sm = clutter.StageManager.prototype; + +console.log('StageManager:', sm); + +/* TODO: Fix throws TypeError: Illegal invocation */ +var stage = sm.getDefaultStage(); + +//for (var s in clutter) console.log(s); + +console.log('StageManager:', sm, 'Stage:', stage); + +stage.title = 'Node.JS Clutter Example'; +stage.setSize(400,300); +stage.setColor(0,0,0,127); +stage.show(); + +stage.on('button-press-event', function(a,b,c) { + console.log('button press event', a, b, c); +}); + +stage.on('destroy', function() { + console.log('destroy'); + clutter.mainQuit(); + process.exit(); +}); + +clutter.main(); diff --git a/examples/dump-clutter.js b/examples/dump-clutter.js deleted file mode 100644 index 6f848cd..0000000 --- a/examples/dump-clutter.js +++ /dev/null @@ -1,2 +0,0 @@ -var clutter = require('./clutter.js'); -console.log(clutter); diff --git a/examples/dump-midgard.js b/examples/dump-midgard.js deleted file mode 100644 index c0b0f42..0000000 --- a/examples/dump-midgard.js +++ /dev/null @@ -1,2 +0,0 @@ -var Midgard = require('./Midgard'); -console.log(Midgard); diff --git a/examples/gtk.js b/examples/gtk.js index af902e7..13e0dda 100644 --- a/examples/gtk.js +++ b/examples/gtk.js @@ -1,2 +1,30 @@ var gir = require('../gir') , gtk = module.exports = gir.load('Gtk', '3.0'); + +gtk.init(0); + +var win = new gtk.Window({type: gtk.WindowType.toplevel, title:"Node.JS GTK Window"}); +var button = new gtk.Button(); + +win.borderWidth = 10; + +button.label = "CLICK ME!"; + +win.add(button); +win.showAll(); + +var w2 = button.getParentWindow(); +console.log(w2); + +win.on("destroy", function() { + console.log("destroyed", arguments[0] instanceof gtk.Window); + gtk.mainQuit(); +}); +button.on("clicked", function() { + console.log("click :)", arguments[0] instanceof gtk.Button, arguments[0] == button); +}); + +console.log(win.name = "test"); +console.log(win.name); + +gtk.main(); diff --git a/examples/gtk_test.js b/examples/gtk_test.js deleted file mode 100644 index 7426f84..0000000 --- a/examples/gtk_test.js +++ /dev/null @@ -1,29 +0,0 @@ -var gtk = require("./gtk"); - -gtk.init(0); - -var win = new gtk.Window({type: gtk.WindowType.toplevel, title:"Node.JS GTK Window"}); -var button = new gtk.Button(); - -win.set_border_width(10); - -button.set_label("hallo, welt!"); - -win.add(button); -win.show_all(); - -var w2 = button.get_parent_window(); -console.log(w2); - -win.on("destroy", function() { - console.log("destroyed", arguments[0] instanceof gtk.Window); - gtk.mainQuit(); -}); -button.on("clicked", function() { - console.log("click :)", arguments[0] instanceof gtk.Button, arguments[0] == button); -}); - -console.log(win.set_property("name", "test")); -console.log(win.__get_property__("name")); - -gtk.main(); diff --git a/examples/inspector.js b/examples/inspector.js new file mode 100644 index 0000000..fe9e0bf --- /dev/null +++ b/examples/inspector.js @@ -0,0 +1,49 @@ +var gir = require('../gir'); +process.argv.forEach(function (val, index, args) { + if (args.length < 3) { + console.log('Usage: node inspector type [version [object [object ...]]]'); + console.log('Description: Inspects each type name passed as an argument.'); + console.log(''); + console.log('Parameters:'); + console.log(' type: required type name to load from girepository'); + console.log(' version: optional, type version to load from girepository'); + console.log(' object: optional, multiple, fields you intend to inspect'); + console.log(''); + console.log('Example 1: node inspector Gtk'); + console.log(' Prints tree for Gtk'); + console.log(''); + console.log('Example 2: node inspector Gtk 3.0'); + console.log(' Prints tree for Gtk version 3.0.'); + console.log(''); + console.log('Example 3: node inspector Notify > notify_tree.txt'); + console.log(' Prints tree for Notify (stdout) to a file.'); + console.log(''); + console.log('Example 4: node inspector Clutter 1.0 Actor Texture'); + console.log(' Prints tree for Clutter version 1.0 Actor and Texture objects.'); + console.log(''); + process.exit(); + } + else { + var mod = args[2], ver = args[3]; + if (ver != undefined) { + var module = gir.load(mod, ver); + if (args.length > 4) for (var a = 4; a < args.length; a++) { + console.log('Type information for ' + mod + ' ' + ver + ' / ' + args[a] + ':'); + console.log(module[args[a]]) + console.log(''); + } + else { + console.log('Type information for ' + mod + ' ' + ver + ':'); + console.log(module); + console.log(''); + } + } + else { + var module = gir.load(mod); + console.log('Type information for ' + mod + ':'); + console.log(module); + console.log(''); + } + process.exit(); + } +}); diff --git a/examples/multiple_test.js b/examples/multiple_test.js index fd91e73..8caa591 100644 --- a/examples/multiple_test.js +++ b/examples/multiple_test.js @@ -1,5 +1,46 @@ -var gtk = require('./gtk') - , notify = require('./notify'); +var gir = require('../gir') + , gtk = gir.load('Gtk', '3.0') + , WebKit = gir.load('WebKit', '3.0') + , notify = gir.load('Notify') + , appTitle = 'Node.JS Gtk+Webkit+Notify Multiple Example'; -notify.init('a'); -gtk.init(0, null); +gtk.init(0); +notify.init(appTitle); + +var win = new gtk.Window(); + +win.on('destroy', function() { + console.log('Window destroyed'); + gtk.mainQuit(); + process.exit(); +}); + +var sw = new gtk.ScrolledWindow(); +win.add(sw); + +var view = new WebKit.WebView(); + +view.on('title-changed', function() { + win.title = view.title + ' - ' + appTitle; +}); + +view.on('load-finished', function() { + var n = new notify.Notification(); + n.update('URL Loaded', view.uri); + setTimeout(function () { + n.close(); + }, 1500); + n.show(); +}); + +view.loadUri("http://www.yahoo.com/"); +sw.add(view); + +win.setSizeRequest(640, 480); +win.showAll(); + +win.title = appTitle; + +console.log(WebKit.WebView.__methods__); + +gtk.main(); diff --git a/examples/notify.js b/examples/notify.js index e83d079..747bddf 100644 --- a/examples/notify.js +++ b/examples/notify.js @@ -1,2 +1,36 @@ +//docs: http://developer.gnome.org/libnotify/0.7/NotifyNotification.html var gir = require('../gir') - , notify = module.exports = gir.load('Notify'); + , notify = gir.load('Notify'); + +console.log(notify.init('notify_test.js sample application')); + +var n = new notify.Notification(); +var created = n.__call__('new', 'a', 'a', 'a', 'a'); + +for(var k in n) { + console.log(k); +} + +console.log(notify.Notification.__methods__); + +n.update('Notify Test', 'This is a test notification message via Node.JS.'); +n.show(); + +setTimeout( + function () { + n.update('Notification Update', 'The status has been updated!'); + n.show(); + setTimeout( + function () { + n.update('Ethan', 'This message will self-destruct in 5 seconds.'); + n.show(); + setTimeout( + function () { + n.close(); + console.log('Adios!'); + }, 5000 + ); + }, 4000 + ); + }, 3000 +); diff --git a/examples/notify_test.js b/examples/notify_test.js deleted file mode 100644 index f0bae00..0000000 --- a/examples/notify_test.js +++ /dev/null @@ -1,35 +0,0 @@ -//docs: http://developer.gnome.org/libnotify/0.7/NotifyNotification.html -var notify = require('./notify'); - -console.log(notify.init('notify_test.js sample application')); - -var n = new notify.Notification(); -var created = n.__call__('new', 'a', 'a', 'a', 'a'); - -for(var k in n) { - console.log(k); -} - -console.log(notify.Notification.__methods__); - -n.update('Notify Test', 'This is a test notification message via Node.JS.'); -n.show(); - -setTimeout( - function () { - n.update('Notification Update', 'The status has been updated!'); - n.show(); - setTimeout( - function () { - n.update('Ethan', 'This message will self-destruct in 5 seconds.'); - n.show(); - setTimeout( - function () { - n.close(); - console.log('Adios!'); - }, 5000 - ); - }, 4000 - ); - }, 3000 -); diff --git a/examples/webkit.js b/examples/webkit.js index 744b0ab..bac09f4 100644 --- a/examples/webkit.js +++ b/examples/webkit.js @@ -1,2 +1,27 @@ var gir = require('../gir') - , webkit = module.exports = gir.load('WebKit', '3.0'); + , gtk = gir.load('Gtk', '3.0') + , WebKit = gir.load('WebKit', '3.0'); + +gtk.init(0); + +var win = new gtk.Window(); + +win.on('destroy', function() { + console.log('Window destroyed'); + gtk.mainQuit(); + process.exit(); +}); + +var sw = new gtk.ScrolledWindow(); +win.add(sw); + +var view = new WebKit.WebView(); +view.loadUri("http://www.google.com/"); +sw.add(view); + +win.setSizeRequest(640, 480); +win.showAll(); + +win.title = 'Node.JS Webkit Example'; + +gtk.main(); From 65607715273801c378ee50d67f26bdf61fe6a9d4 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Sat, 17 Sep 2011 14:07:02 +0200 Subject: [PATCH 3/9] get controll over the namespaces --- examples/namespaces.js | 14 +++++ src/namespace_loader.cc | 117 +++++++++++++++++++++++++++++++++++++++- src/namespace_loader.h | 5 ++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 examples/namespaces.js diff --git a/examples/namespaces.js b/examples/namespaces.js new file mode 100644 index 0000000..d319609 --- /dev/null +++ b/examples/namespaces.js @@ -0,0 +1,14 @@ +var gir = require("../gir"); + +gir.init(); +console.log(gir.searchPath()); + +gir.load("Gtk"); + +console.log(gir.loadedNamespaces()); + +console.log(gir.getDependencies("Gtk")); + +console.log(gir.getVersions("Gtk")); + +console.log(gir.isRegistered("Gtk", "3.0")); diff --git a/src/namespace_loader.cc b/src/namespace_loader.cc index c1d1a06..20dcc0d 100644 --- a/src/namespace_loader.cc +++ b/src/namespace_loader.cc @@ -15,7 +15,12 @@ std::map NamespaceLoader::type_libs; void NamespaceLoader::Initialize(Handle target) { GIR_SET_METHOD(target, "load", NamespaceLoader::Load); - GIR_SET_METHOD(target, "search_path", NamespaceLoader::SearchPath); + GIR_SET_METHOD(target, "searchPath", NamespaceLoader::SearchPath); + GIR_SET_METHOD(target, "isRegistered", NamespaceLoader::IsRegistered); + GIR_SET_METHOD(target, "getDependencies", NamespaceLoader::GetDependencies); + GIR_SET_METHOD(target, "loadedNamespaces", NamespaceLoader::LoadedNamespaces); + GIR_SET_METHOD(target, "getVersion", NamespaceLoader::GetVersion); + GIR_SET_METHOD(target, "getVersions", NamespaceLoader::GetVersions); } Handle NamespaceLoader::Load(const Arguments &args) { @@ -157,6 +162,116 @@ Handle NamespaceLoader::SearchPath(const Arguments &args) { return scope.Close(res); } +Handle NamespaceLoader::IsRegistered(const Arguments &args) { + HandleScope scope; + + if(!repo) { + repo = g_irepository_get_default(); + } + + if(args.Length() < 1 || !args[0]->IsString()) { + return BAD_ARGS(); + } + + String::Utf8Value namespace_(args[0]->ToString()); + char *version = NULL; + + if(args.Length() > 1 && args[1]->IsString()) { + String::Utf8Value version_(args[1]); + version = *version_; + } + + return scope.Close(Boolean::New(g_irepository_is_registered(repo, *namespace_, version))); +} + +Handle NamespaceLoader::GetDependencies(const Arguments &args) { + HandleScope scope; + + if(!repo) { + repo = g_irepository_get_default(); + } + + if(args.Length() < 1 || !args[0]->IsString()) { + return BAD_ARGS(); + } + + String::Utf8Value namespace_(args[0]->ToString()); + + char **versions = g_irepository_get_dependencies(repo, *namespace_); + + int size = 0; + while(versions[size] != NULL) { size++; } + + Handle res = Array::New(size); + for(int i=0; versions[i] != NULL; i++) { + res->Set(i, String::New(versions[i])); + } + + return scope.Close(res); +} + +Handle NamespaceLoader::LoadedNamespaces(const Arguments &args) { + HandleScope scope; + + if(!repo) { + repo = g_irepository_get_default(); + } + + char **namespaces = g_irepository_get_loaded_namespaces(repo); + + int size = 0; + while(namespaces[size] != NULL) { size++; } + + Handle res = Array::New(size); + for(int i=0; namespaces[i] != NULL; i++) { + res->Set(i, String::New(namespaces[i])); + } + + return scope.Close(res); +} + +Handle NamespaceLoader::GetVersion(const Arguments &args) { + HandleScope scope; + + if(!repo) { + repo = g_irepository_get_default(); + } + + if(args.Length() < 1 || !args[0]->IsString()) { + return BAD_ARGS(); + } + + String::Utf8Value namespace_(args[0]->ToString()); + + const char *version = g_irepository_get_version(repo, *namespace_); + + return scope.Close(String::New(version)); +} + +Handle NamespaceLoader::GetVersions(const Arguments &args) { + HandleScope scope; + + if(!repo) { + repo = g_irepository_get_default(); + } + + if(args.Length() < 1 || !args[0]->IsString()) { + return BAD_ARGS(); + } + + String::Utf8Value namespace_(args[0]->ToString()); + + GList *versions = g_irepository_enumerate_versions(repo, *namespace_); + int length = g_list_length(versions); + Handle res = Array::New(length); + + for(int i=0; iSet(i, String::New((char*)g_list_nth_data(versions, i))); + } + + return scope.Close(res); +} + } diff --git a/src/namespace_loader.h b/src/namespace_loader.h index 66ac2cf..24321ac 100644 --- a/src/namespace_loader.h +++ b/src/namespace_loader.h @@ -17,6 +17,11 @@ class NamespaceLoader { static v8::Handle Load(const v8::Arguments &args); static v8::Handle SearchPath(const v8::Arguments &args); + static v8::Handle IsRegistered(const v8::Arguments &args); + static v8::Handle GetDependencies(const v8::Arguments &args); + static v8::Handle LoadedNamespaces(const v8::Arguments &args); + static v8::Handle GetVersion(const v8::Arguments &args); + static v8::Handle GetVersions(const v8::Arguments &args); private: static v8::Handle LoadNamespace(char *namespace_, char *version); From a89be7686f5643407a46c235cd66d0195affd4e5 Mon Sep 17 00:00:00 2001 From: David Ball Date: Sun, 15 Jan 2012 01:30:05 -0500 Subject: [PATCH 4/9] cherry-picked swick's d70009ee9ef40aa728df425658accf2ab97dfcda object.cc changed before adding my own object.cc proposed code changes --- src/types/object.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/types/object.cc b/src/types/object.cc index 700be66..8e6096e 100644 --- a/src/types/object.cc +++ b/src/types/object.cc @@ -226,8 +226,13 @@ void GIRObject::Initialize(Handle target, char *namespace_) { std::vector::iterator it; std::vector::iterator temp; GIObjectInfo* parent; + std::vector roots; + Handle objs = Array::New(templates.size()); + int i = 0; for(it = templates.begin(); it != templates.end(); ++it) { + objs->Set(i++, String::New(g_base_info_get_name(it->info))); + parent = g_object_info_get_parent(it->info); if(strcmp(it->namespace_, namespace_) != 0 || !parent) { continue; @@ -236,8 +241,12 @@ void GIRObject::Initialize(Handle target, char *namespace_) { for(temp = templates.begin(); temp != templates.end(); ++temp) { if(g_base_info_equal(temp->info, parent)) { it->function->Inherit(temp->function); + break; } } + if(temp == templates.end()) { + roots.push_back(g_base_info_get_name(it->info)); + } } for(it = templates.begin(); it != templates.end(); ++it) { if(strcmp(it->namespace_, namespace_) == 0) { @@ -245,6 +254,16 @@ void GIRObject::Initialize(Handle target, char *namespace_) { } } + int rootsLength = roots.size(); + Handle v8roots = Array::New(rootsLength); + i = 0; + for(std::vector::iterator it = roots.begin(); it != roots.end(); it++) { + v8roots->Set(i++, String::New(*it)); + } + + target->Set(String::New("__roots__"), v8roots); + target->Set(String::New("__objects__"), objs); + emit_symbol = NODE_PSYMBOL("emit"); } From 47483b9bf764e02e3d60f8db43b578ea22c769b1 Mon Sep 17 00:00:00 2001 From: David Ball Date: Sun, 15 Jan 2012 17:11:03 -0500 Subject: [PATCH 5/9] Changed load() to import() in C++ land. --- src/namespace_loader.cc | 4 ++-- src/namespace_loader.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/namespace_loader.cc b/src/namespace_loader.cc index 20dcc0d..890e6b1 100644 --- a/src/namespace_loader.cc +++ b/src/namespace_loader.cc @@ -14,7 +14,7 @@ GIRepository *NamespaceLoader::repo = NULL; std::map NamespaceLoader::type_libs; void NamespaceLoader::Initialize(Handle target) { - GIR_SET_METHOD(target, "load", NamespaceLoader::Load); + GIR_SET_METHOD(target, "import", NamespaceLoader::Import); GIR_SET_METHOD(target, "searchPath", NamespaceLoader::SearchPath); GIR_SET_METHOD(target, "isRegistered", NamespaceLoader::IsRegistered); GIR_SET_METHOD(target, "getDependencies", NamespaceLoader::GetDependencies); @@ -23,7 +23,7 @@ void NamespaceLoader::Initialize(Handle target) { GIR_SET_METHOD(target, "getVersions", NamespaceLoader::GetVersions); } -Handle NamespaceLoader::Load(const Arguments &args) { +Handle NamespaceLoader::Import(const Arguments &args) { HandleScope scope; if(args.Length() < 1) { diff --git a/src/namespace_loader.h b/src/namespace_loader.h index 24321ac..fb5b658 100644 --- a/src/namespace_loader.h +++ b/src/namespace_loader.h @@ -14,7 +14,7 @@ class NamespaceLoader { static std::map type_libs; static void Initialize(v8::Handle target); - static v8::Handle Load(const v8::Arguments &args); + static v8::Handle Import(const v8::Arguments &args); static v8::Handle SearchPath(const v8::Arguments &args); static v8::Handle IsRegistered(const v8::Arguments &args); From 2d4e5f799b3ff4da3450c32daab8681b9ce5d152 Mon Sep 17 00:00:00 2001 From: David Ball Date: Mon, 16 Jan 2012 00:26:56 -0500 Subject: [PATCH 6/9] renamed load() to import(), left load() in place for now, added onSignalShortcut event registration, v_funcs, and fields, added some type reflection, renamed some things, cleaned up some comments, moved all the c-land functions to a sub-object --- gir.js | 327 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 224 insertions(+), 103 deletions(-) diff --git a/gir.js b/gir.js index 454ef75..ae2b0b3 100644 --- a/gir.js +++ b/gir.js @@ -1,16 +1,16 @@ /** * Example use of this module: * var gir = require('./path/to/gir.js') - * , gtk = gir.load('Gtk', '3.0'); + * , gtk = gir.import('Gtk', '3.0'); **/ -//import gir library and EventEmitter -var gir = module.exports = require('./build/Release/girepository.node') +//define export object, import girepository and EventEmitter onto export object +var gir = module.exports = { '_girepository': require('./build/Release/girepository.node') } , EventEmitter = require('events').EventEmitter; /******************************************************************************/ -/* BEGIN HELPERS */ +/* BEGIN EXTERNAL HELPERS */ /** * Adopted from jquery's extend method. Under the terms of MIT License. @@ -152,31 +152,15 @@ function makeArray(array, results) { return ret; } -/* END HELPERS */ +/* END EXTERNAL HELPERS */ /******************************************************************************/ -/* BEGIN LOGIC */ - -//save default module routines -gir._gir_baseInit = gir.init; -gir._gir_baseLoad = gir.load; - -//add init flag property -gir._gir_hasInit = false; +/* BEGIN INTERNAL HELPERS */ -//override default init -gir.init = function() { - //don't init twice, seems useless to do so - if (!this._gir_hasInit) { - this._gir_hasInit = true; - return gir['_gir_baseInit'].apply(this, Array.prototype.slice.call(arguments)); - } -}; - -//converts long_method_names to properCamelCasing :-) -//converts long-property-names to longPropertyNames -//Merged from swick/node-gir +/** + * Converts underscored_names or dashed-names to properCamelCasing. + **/ function camelCase(x) { return x.replace(/[-]+/g, function (h) { return '_'; @@ -185,57 +169,159 @@ function camelCase(x) { }); } -//create method object -//simplified using merge from swick/node-gir -function defineObjectMethod(obj, method_name) { - //converts method name to camelCasing on the object - obj.prototype[camelCase(method_name)] = function(method_name) { - //the internal function does all the hard work - var invocation = function() { - var args = Array.prototype.slice.call(arguments); - if (args == undefined) args = new Array(); - args.unshift(method_name); - //call the method on the gir provided object -console.log('called',method_name,'in',obj); - this.__call__.apply(this, args); - }; - return invocation; - }(method_name); +/** + * Creates a onCamelCasedEvent() on the instance object's prototype that + * matches the specified regular_event_name. The method will be dispatched + * to on('event_name', callback). e.g. onNotify, onClosed, onDestroy + **/ +function defineInstanceEvent(obj, event_name) { + var camelEventName = camelCase('on_'+event_name); + //add event handler to object if possible + if (!obj.prototype[camelEventName]) + //converts method name to camelCasing on the object + obj.prototype[camelCase('on_'+event_name)] = function(event_name) { + var invocation = function() { + var args = Array.prototype.slice.call(arguments); + if (args == undefined) args = [function noCallback() { }]; + args.unshift(event_name); + //debug:console.log('called',event_name,'in',obj,'with',args[1]); + return this.on.apply(this, args); + }; + return invocation; + }(event_name); } -//create property object -//merged from swick/node-gir +/** + * Creates a camelCasedMethod() on the instance object's prototype that + * matches the specified regular_method_name. The method will be dispatched + * to __call__('method_name', arg0, argx, ...). + **/ +function defineInstanceMethod(obj, method_name) { + var camelMethodName = camelCase(method_name); + //add method handler to object if possible + if (!obj.prototype[camelMethodName]) + //converts method name to camelCasing on the object + obj.prototype[camelMethodName] = function(method_name) { + //the internal function does all the hard work + var invocation = function() { + var args = Array.prototype.slice.call(arguments); + if (args == undefined) args = new Array(); + args.unshift(method_name); + //this helped me to catch a recent problem with object.cc + //debug:console.log('called',method_name,'in',obj); + //debug:for (var x in obj) console.log(x); + return this.__call__.apply(this, args); + }; + return invocation; + }(method_name); + //else + //debug:console.warn("[node-gir] " + subobj + " object provides it's own " + method_name + " method. Not replacing existing method. :-("); + + //Not sure if this would do anything positive: + //Check for new(), and makes new() the default constructor if it's in the gir object. + //Disabled until it would serve useful. It simply assigns the new() function as + //the JS constructor for the object. There is a small problem: some objects have multiple + //new() functions, probably due to object inheritence or overloading. :~ Not sure really, + //but it seems to add no new functionality while simultaneously not taking anything away. + //It may be thrown away in a future commit if it is decidedly useless. + //DISABLED: + /*if (method_name == 'new') { + //debug:console.log('[node-gir] Using new() as the default constructor for', subobj, 'provided by gir.'); + obj[subobj].prototype.constructor = defineObjectMethod(obj[subobj], 'new'); + }*/ +} + +/** + * Creates a camelCasedVFunc() on the instance object's prototype that + * matches the specified regular_method_name. The method will be dispatched + * to __call_v_func__('v_func_name', arg0, argx, ...). + **/ +function defineInstanceVFunc(obj, v_name) { + var camelVName = camelCase(v_name); + //add method handler to object if possible + if (!obj.prototype[camelVName]) + //converts method name to camelCasing on the object + obj.prototype[camelVName] = function(v_name) { + //the internal function does all the hard work + var invocation = function() { + var args = Array.prototype.slice.call(arguments); + if (args == undefined) args = new Array(); + args.unshift(v_name); + //call the method on the gir provided object + //debug:console.log('called',v_name,'in',obj); + this.__call_v_func__.apply(this, args); + }; + return invocation; + }(v_name); + //else + //debug:console.warn("[node-gir] " + subobj + " object provides it's own " + method_name + " method. Not replacing existing method. :-("); +} + +/** + * Creates a camelCasedProperty on the instance object's prototype that + * matches the specified regular-property-name. The getter is dispatched to + * __get_property__('property-name'). The setter is dispatched to + * __set_property__('property-name', value). + **/ //added try{}catch{} block because of TypeError: Illegal invocation, in what appears //to be accessing beyond object access boundaries, e.g. trying to access a private member, or //setting a getter-only property, or something to that effect. //perhaps someone can figure out why this version throws TypeError: Illegal invocation //across certain boundaries while the swick/node-gir does not :~ could be a diff in binaries -function defineObjectProperty(obj, property_name) { - obj.prototype.__defineGetter__(camelCase(property_name), function() { - try { - return this.__get_property__.apply(this, [property_name]); - } catch (err) { - //debug:console.warn('[node-gir] Error trying to install getter ' + camelCase(property_name) + '.', err); - return undefined; - } - }); - obj.prototype.__defineSetter__(camelCase(property_name), function(newValue) { - try { - return this.__set_property__.apply(this, [property_name, newValue]); - } catch (err) { - //debug:console.warn('[node-gir] Error trying to install setter ' + camelCase(property_name) + '.', err); - return newValue; - } - }); +function defineInstanceProperty(obj, property_name) { + var camelPropertyName = camelCase(property_name); + //add property handler to object if possible + if (!obj.prototype[camelPropertyName]) { + obj.prototype.__defineGetter__(camelPropertyName, function() { + try { + return this.__get_property__.apply(this, [property_name]); + } catch (err) { + //debug:console.warn('[node-gir] Error trying to install getter ' + camelCase(property_name) + '.', err); + return undefined; + } + }); + obj.prototype.__defineSetter__(camelPropertyName, function(newValue) { + try { + return this.__set_property__.apply(this, [property_name, newValue]); + } catch (err) { + //debug:console.warn('[node-gir] Error trying to install setter ' + camelCase(property_name) + '.', err); + return newValue; + } + }); + } + //else + //debug:console.warn("[node-gir] " + subobj + " object provides it's own " + property_name + " property. Not replacing existing property. :-("); } -//override default loader -gir.load = function() { +/* END INTERNAL HELPERS */ + +/******************************************************************************/ + +/* BEGIN LOGIC */ + +//save default module routines +gir._girepository.__init__ = gir._girepository.init; +gir._girepository.__import__ = gir._girepository.import; + +//add init flag property +gir._girepository._has_init = false; + +//override default init +gir._girepository.init = function() { + //don't init twice, seems useless to do so + if (!gir._girepository._has_init) { + gir._girepository._has_init = true; + return gir._girepository.__init__.apply(gir._girepository, Array.prototype.slice.call(arguments)); + } +}; + +//override default namespace loader +gir.load = gir.import = gir._girepository.import = function() { //auto-init if needed - if (!this._gir_hasInit) this.init(); + if (!gir._girepository._has_init) gir._girepository.init(); //load gir module - var obj = gir['_gir_baseLoad'].apply(this, Array.prototype.slice.call(arguments)); + var obj = gir._girepository.__import__.apply(gir._girepository, Array.prototype.slice.call(arguments)); //check for error if (!obj) return obj; @@ -243,9 +329,12 @@ gir.load = function() { //TODO: consider storing loaded module gir somewhere now so that it can be unloaded later ? //for each object within the loaded gir module: - // task 1. figure out which loaded objects can trigger events, and add EventEmitter as needed - // task 2. make method names callable methods - // task 3. properties need getter/setter set (merge from swick/node-gir) + // task 1: add EventEmitter as needed + // task 2: setup signals as onEventNames shortcut functions + // task 3: expose methods and make them callable + // task 4: expose vfuncs and make them callable + // task 5: expose properties and make them settable/gettable + // task 6: expose fields and make them settable/gettable for (var subobj in obj) { //task 1: add EventEmitter as needed //determine whether eventable @@ -255,63 +344,95 @@ gir.load = function() { //combine EventEmitter logic with eventable gir objects extend(true, obj[subobj].prototype, EventEmitter.prototype); //check for prop __watch_signal__, if found, override EventEmitter.on() - if (obj[subobj].prototype['__watch_signal__'] != undefined) { - obj[subobj].prototype._baseEventEmitter_on = obj[subobj].prototype.on; + if (obj[subobj]['__signals__'] != undefined) { + obj[subobj].prototype._EventEmitter_on = obj[subobj].prototype.on; obj[subobj].prototype.on = function () { + var args = Array.prototype.slice.call(arguments); //tell gir loaded object to listen for the signal - this.__watch_signal__(arguments[0]); - //dispatch normally - this._baseEventEmitter_on(arguments[0], arguments[1]); + if (this['__watch_signal__'] != undefined) + this.__watch_signal__(args[0]); + //console.log('on hit with', args); + //dispatch EventEmitter normally + this._EventEmitter_on.apply(this, args); }; } } - //task 2: expose methods and make them callable - //task 3: expose properties and make them settable/gettable + //task 2: setup signals as onEventNames shortcut functions + //task 3: expose methods and make them callable + //task 4: expose vfuncs and make them callable + //task 5: expose properties and make them settable/gettable + //task 6: expose fields and make them settable/gettable for (var prop in obj[subobj]) { switch (prop) { - case '__methods__': //task 2 + case '__signals__': //task 2 + for (var signal in obj[subobj][prop]) { + var signal_name = obj[subobj][prop][signal]; + defineInstanceEvent(obj[subobj], signal_name); + } + break; + case '__methods__': //task 3 for (var method in obj[subobj][prop]) { var method_name = obj[subobj][prop][method]; - //add method handler to object if possible - if (!obj[subobj].prototype[method_name]) - defineObjectMethod(obj[subobj], method_name); - //else - //debug:console.warn("[node-gir] " + subobj + " object provides it's own " + method_name + " method. Not replacing existing method. :-("); - - //Not sure if this would do anything positive: - //Check for new(), and makes new() the default constructor if it's in the gir object. - //Disabled until it would serve useful. It simply assigns the new() function as - //the JS constructor for the object. There is a small problem: some objects have multiple - //new() functions, probably due to object inheritence or overloading. :~ Not sure really, - //but it seems to add no new functionality while simultaneously not taking anything away. - //It may be thrown away in a future commit if it is decidedly useless. - //DISABLED: - /*if (method_name == 'new') { - //debug:console.log('[node-gir] Using new() as the default constructor for', subobj, 'provided by gir.'); - obj[subobj].prototype.constructor = defineObjectMethod(obj[subobj], 'new'); - }*/ + defineInstanceMethod(obj[subobj], method_name); + } + break; + case '__v_funcs__': //task 4 + for (var v in obj[subobj][prop]) { + var v_name = obj[subobj][prop][v]; + //i don't yet know the value of a v_func + defineInstanceVFunc(obj[subobj], v_name); } break; - case '__properties__': //task 3 + case '__properties__': //task 5 for (var property in obj[subobj][prop]) { var property_name = obj[subobj][prop][property]; - //add property handler to object if possible - if (!obj[subobj].prototype[property_name]) - defineObjectProperty(obj[subobj], property_name); - //else - //debug:console.warn("[node-gir] " + subobj + " object provides it's own " + property_name + " property. Not replacing existing property. :-("); + defineInstanceProperty(obj[subobj], property_name); + } + break; + case '__fields__': //task 6 + for (var field in obj[subobj][prop]) { + var field_name = obj[subobj][prop][field]; + //this requires changes to the C++ code + //add field reference if possible + //if (!obj[subobj].prototype[field_name]) + //defineInstanceField(obj[subobj], field_name); + //debug:console.log('found field', field_name, 'in', subobj); } break; } } + + //keep the object name in the object for reflection + if (obj[subobj].__name__ != undefined) + console.warn("[node-gir]", arguments[0]+'.'+subobj, "provides it's own __name__. Not replacing __name__."); + else + obj[subobj].__name__ = subobj; + + //keep the namespace loader in the loaded object in case caller wants to reuse the loader + if (obj[subobj].__gir__ != undefined) + console.warn("[node-gir]", arguments[0], "provides it's own __gir__. Not replacing __gir__. Strange error? :-("); + else + obj[subobj].__gir__ = this; } - //keep the loader in the loaded object in case caller wants to reuse the loader - if (obj.gir != undefined) - console.warn("[node-gir] Object provides it's own gir. Not replacing gir. Strange error? :-("); + //keep the namespace name in the loaded object for reflection + if (obj.__name__ != undefined) + console.warn("[node-gir]", arguments[0], "provides it's own __name__. Not replacing __name__."); + else + obj.__name__ = arguments[0]; + + //keep the namespace version in the loaded object for reflection + if (obj.__version__ != undefined) + console.warn("[node-gir]", arguments[0], "provides it's own __version__. Not replacing __version__."); + else + obj.__version__ = arguments[1] ? arguments[1] : gir._girepository.getVersion(arguments[0]); + + //keep the namespace loader in the loaded namespace in case caller wants to reuse the loader + if (obj.__gir__ != undefined) + console.warn("[node-gir]", arguments[0], "provides it's own __gir__. Not replacing __gir__. Strange error? :-("); else - obj.gir = this; + obj.__gir__ = this; //return the brutally overridden object return obj; From 924510d28cd5e3c9b7b027590fe3b5f594ad8a6e Mon Sep 17 00:00:00 2001 From: David Ball Date: Mon, 16 Jan 2012 00:31:46 -0500 Subject: [PATCH 7/9] updated example code for gir.import(), added gui to inspector example, observed some new issues in inspector code. c++ needs some updates, but i don't think i'm up to the challenge. --- examples/clutter.js | 8 +- examples/gtk.js | 45 ++++--- examples/inspector.js | 254 ++++++++++++++++++++++++++++++++++---- examples/libxml2.js | 2 +- examples/midgard.js | 2 +- examples/multiple_test.js | 16 ++- examples/namespaces.js | 2 +- examples/notify.js | 6 +- examples/webkit.js | 14 ++- 9 files changed, 288 insertions(+), 61 deletions(-) diff --git a/examples/clutter.js b/examples/clutter.js index d4d8456..7db14f8 100644 --- a/examples/clutter.js +++ b/examples/clutter.js @@ -1,5 +1,5 @@ var gir = require('../gir') - , clutter = gir.load('Clutter'); + , clutter = gir.import('Clutter'); clutter.init(0); @@ -15,16 +15,16 @@ var stage = sm.getDefaultStage(); console.log('StageManager:', sm, 'Stage:', stage); -stage.title = 'Node.JS Clutter Example'; +stage.title = 'node-gir Clutter Example'; stage.setSize(400,300); stage.setColor(0,0,0,127); stage.show(); -stage.on('button-press-event', function(a,b,c) { +stage.onButtonPressEvent(function(a,b,c) { console.log('button press event', a, b, c); }); -stage.on('destroy', function() { +stage.onDestroy(function() { console.log('destroy'); clutter.mainQuit(); process.exit(); diff --git a/examples/gtk.js b/examples/gtk.js index 13e0dda..2081982 100644 --- a/examples/gtk.js +++ b/examples/gtk.js @@ -1,30 +1,47 @@ var gir = require('../gir') - , gtk = module.exports = gir.load('Gtk', '3.0'); + , gtk = gir.import('Gtk', '3.0'); gtk.init(0); -var win = new gtk.Window({type: gtk.WindowType.toplevel, title:"Node.JS GTK Window"}); -var button = new gtk.Button(); +var win = new gtk.Window({type: gtk.WindowType.toplevel, title:"GTK Example"}) + , vbox = new gtk.VBox(1,3) + , label = new gtk.Label() + , button = new gtk.Button() + , quitButton = new gtk.Button(); win.borderWidth = 10; +win.widthRequest = 200; button.label = "CLICK ME!"; +quitButton.label = "Quit"; -win.add(button); -win.showAll(); +vbox.add(button); +vbox.add(label); +vbox.add(quitButton); -var w2 = button.getParentWindow(); -console.log(w2); +win.name = "gtktest"; +win.add(vbox); +win.showAll(); -win.on("destroy", function() { - console.log("destroyed", arguments[0] instanceof gtk.Window); - gtk.mainQuit(); +//window destroyed +win.onDestroy(function() { + //debug:console.log("destroyed", arguments[0] instanceof gtk.Window); + gtk.mainQuit(); + process.exit(); }); -button.on("clicked", function() { - console.log("click :)", arguments[0] instanceof gtk.Button, arguments[0] == button); + +var clicks = 0; +//user clicked CLICK ME! button +button.onClicked(function() { + //debug:console.log("click :)", arguments[0] instanceof gtk.Button, arguments[0] == button); + //testing objects returned from members in c-land:console.log("click :)", button, button, button.window); + label.label = 'Clicked ' + ++clicks + ' times.'; }); -console.log(win.name = "test"); -console.log(win.name); +//user clicked Quit button +quitButton.onClicked(function() { + win.destroy(); +}); +//start event loop gtk.main(); diff --git a/examples/inspector.js b/examples/inspector.js index fe9e0bf..5ad2ef8 100644 --- a/examples/inspector.js +++ b/examples/inspector.js @@ -1,49 +1,255 @@ -var gir = require('../gir'); +var gir = require('../gir') + , util = require('util'); + +console.warn('[inspector] Using node version', process.version + '.'); + process.argv.forEach(function (val, index, args) { if (args.length < 3) { - console.log('Usage: node inspector type [version [object [object ...]]]'); + console.log('Usage: node inspector [options] type [version [object [object ...]]]'); console.log('Description: Inspects each type name passed as an argument.'); console.log(''); + console.log('Syntax 1: node inspector type [version [object [object ...]]]'); + console.log('Syntax 2: node inspector [-v] type [version]'); + console.log('Syntax 3: node inspector [-g] type [version] [object]'); + console.log(''); console.log('Parameters:'); - console.log(' type: required type name to load from girepository'); - console.log(' version: optional, type version to load from girepository'); - console.log(' object: optional, multiple, fields you intend to inspect'); + console.log(' options: optional, one or more options.'); + console.log(' type: required type name to load from girepository.'); + console.log(' version: optional, type version to load from girepository.'); + console.log(' object: optional, multiple, fields you intend to inspect.'); + console.log(''); + console.log('Options:'); + console.log(''); + console.log(' -v Prints version list for a given type. Do not provide objects with -v.'); + console.log(' -g Loads in GTK GUI mode. Only the first object will be browsed, if provided.'); console.log(''); console.log('Example 1: node inspector Gtk'); - console.log(' Prints tree for Gtk'); + console.log(' Prints tree for Gtk to stdout.'); console.log(''); console.log('Example 2: node inspector Gtk 3.0'); - console.log(' Prints tree for Gtk version 3.0.'); + console.log(' Prints tree for Gtk version 3.0 to stdout.'); console.log(''); console.log('Example 3: node inspector Notify > notify_tree.txt'); - console.log(' Prints tree for Notify (stdout) to a file.'); + console.log(' Prints tree for Notify with stdout redirected to a file.'); console.log(''); console.log('Example 4: node inspector Clutter 1.0 Actor Texture'); - console.log(' Prints tree for Clutter version 1.0 Actor and Texture objects.'); + console.log(' Prints tree for Clutter version 1.0 Actor and Texture objects.'); + console.log(''); + console.log('Example 5: node inspector -g Gtk 3.0 Window'); + console.log(' Opens GUI and browses to the GTK 3.0 Window object.'); console.log(''); process.exit(); } else { + if (args[2] == '-v') { + dumpTypeVersions(importType(args[3])); + process.exit(); + } + else if (args[2] == '-g') { + if (args.length > 3) + launchGtk.apply(this, args.slice(3)); + else + launchGtk(); + process.exit(); + } var mod = args[2], ver = args[3]; + var module = importType(mod, ver); if (ver != undefined) { - var module = gir.load(mod, ver); if (args.length > 4) for (var a = 4; a < args.length; a++) { - console.log('Type information for ' + mod + ' ' + ver + ' / ' + args[a] + ':'); - console.log(module[args[a]]) - console.log(''); + dumpTypeInfo(module, args[a]); } - else { - console.log('Type information for ' + mod + ' ' + ver + ':'); - console.log(module); - console.log(''); - } - } - else { - var module = gir.load(mod); - console.log('Type information for ' + mod + ':'); - console.log(module); - console.log(''); + else + dumpTypeInfo(module); } + else + dumpTypeInfo(module); process.exit(); } }); + +/** + * Imports a girepository type with a specified version. + **/ +function importType(type, ver) { + if (ver != undefined) + try { + return gir.import(type, ver); + } + catch (e) { + console.warn('In importType(),', e); + return gir.import(type); + } + else + return gir.import(type); +} + +/** + * Prints a type or some of its objects out to a string. + **/ +function getTypeInfo(loadedType, obj) { + if (obj != undefined) + return 'Type information for .import(\'' + loadedType.__name__ + '\',\'' + loadedType.__version__ + '\').' + obj + ':' + + '\n' + util.inspect(loadedType[obj]) + '\n'; + else + return 'Type information for .import(\'' + loadedType.__name__ + '\',\'' + loadedType.__version__ + '\')' + ':' + + '\n' + util.inspect(loadedType) + '\n'; +} + +/** + * Prints a type or some of its objects out to a string. + **/ +function getTypeVersions(loadedType) { + return gir._girepository.getVersions(loadedType.__name__); +} + +/** + * Dumps a type or some of its objects out to stdout. + **/ +function dumpTypeInfo(loadedType, obj) { + if (obj != undefined) + console.log(getTypeInfo(loadedType, obj)); + else + console.log(getTypeInfo(loadedType)); +} + +/** + * Dumps type versions to stdout. + **/ +function dumpTypeVersions(loadedType) { + console.log("'"+loadedType.__name__+"'", 'available in versions', getTypeVersions(loadedType)); +} + +/** + * In progress: Dumps complete tree of every member of every installed version + * of every object returned when loading the namespace from girepository. + * Will appear in the tree view. + **/ +function gtkDumpTypeTree(win, type, brwsVer, brwsObj) { +console.log(win, type, brwsVer, brwsObj); + var loadedType = importType(type, brwsVer); + //dump info to gtk + gtkDumpTypeInfo(win, loadedType, brwsObj); + //dump info to console + dumpTypeInfo(loadedType, brwsObj); +} + +/** + * Dumps type information to GTK window. + **/ +function gtkDumpTypeInfo(win, loadedType, obj) { + if (obj != undefined) + win.textInfoBuffer.text = getTypeInfo(loadedType, obj); + else + win.textInfoBuffer.text = getTypeInfo(loadedType); +} + +/** + * Launches the GTK GUI. Returns the Gtk.Window object. + **/ +function launchGtk(type, brwsVer, brwsObj) { + var gtk = gir.import('Gtk'); + console.warn('[inspector] Using GTK version', gtk.__version__ + '.'); + gtk.init(0); + + var win = new gtk.Window({type: gtk.WindowType.toplevel, title:'GIR Inspector', name:'GIR Inspector'}); + win.gtk = gtk; + win.vboxOuter = new win.gtk.VBox(1,2); + win.hboxInner = new win.gtk.HBox(1,2); + + win.menuBar = new win.gtk.MenuBar(); + + win.menuFile = new win.gtk.Menu(); + win.menuFileFile = new win.gtk.MenuItem('File'); + win.menuFileQuit = new win.gtk.MenuItem('Quit'); + + win.menuSamples = new win.gtk.Menu(); + win.menuSamplesSamples = new win.gtk.MenuItem('Samples'); + + win.menuHelp = new win.gtk.Menu(); + win.menuHelpHelp = new win.gtk.MenuItem('Help'); + win.menuHelpAbout = new win.gtk.MenuItem('About...'); + + //these menus do not work, and i greatly suspect it may have something to do with the issues below + win.menuFileFile.setSubmenu(win.menuFile); + win.menuSamplesSamples.setSubmenu(win.menuSamples); + win.menuHelpHelp.setSubmenu(win.menuHelp); + + win.menuFile.append(win.menuFileQuit); + win.menuHelp.append(win.menuHelpAbout); + + win.menuBar.append(win.menuFile); + win.menuBar.append(win.menuSamples); + win.menuBar.append(win.menuHelp); + + win.vboxOuter.packStart(win.menuBar, false, true, 0); + win.vboxOuter.packStart(win.hboxInner, true, true, 0); + + win.vboxInnerLeft = new win.gtk.VBox(1,2); + win.hboxInnerLeftTop = new win.gtk.HBox(1,3); + + win.treeStore = new win.gtk.TreeStore('Inspect'); + win.treeView = new win.gtk.TreeView(); + win.treeView.model = win.treeStore; + win.treeColumn = new win.gtk.TreeViewColumn({title:'Inspect'}); + win.treeView.appendColumn(win.treeColumn); + win.treeView.heightRequest = 300; + + win.labelSearch = new win.gtk.Label({label: 'Object Type:'}); + win.entrySearch = new win.gtk.Entry(); + win.entrySearch.widthRequest = 200; + win.buttonSearch = new win.gtk.Button({label: 'Import Object'}); + win.buttonSearch.onClicked(function () { + gtkDumpTypeTree(win, win.entrySearch.text) + }); + + win.scrolledTextWindow = new win.gtk.ScrolledWindow(); + win.textInfo = new win.gtk.TextView(); + win.textInfoBuffer = win.textInfo.getBuffer(); + win.textInfoBufferFontTag = new win.gtk.TextTag(); + //win.textInfoBufferFontTag = win.textInfoBuffer.createTag('monofont'); //fails, see next comment + win.textInfoBufferFontTag.family = 'monospace'; + win.textInfoBuffer.tagTable.add(win.textInfoBuffer); +//debug: for (var x in win.gtk.TextBuffer.__methods__) console.log(win.gtk.TextBuffer.__methods__[x]); + win.textInfoBuffer.text = 'Enter an object type name in the search box, then click Import Object.'; +// this is broken! i think the problem is in object.cc in CallMethod(). basically, CallMethod() +// is returning a valid object with __properties__ and friends, but for some reason it left it's +// good old pals __call__, __get_property__, etc. behind. that means the object returned by a +// method can not be further interacted with. i can't seem to find the the perfect spot to plant +// an additional call to GIRObject::SetPrototypeMethods() in or around CallMethod() or the function.cc +// Func::Call(). it returns 'bad arguments', but it is the complete lack of __call__ on the object +// causing the error. --david +// win.textInfoBuffer.applyTag(win.textInfoBufferFontTag, win.textInfoBuffer.getStartIter(), win.textInfoBuffer.getEndIter()); + win.textInfo.editable = false; + win.scrolledTextWindow.widthRequest = 480; + + win.hboxInnerLeftTop.packStart(win.labelSearch, false, false, 5); + win.hboxInnerLeftTop.packStart(win.entrySearch, false, false, 5); + win.hboxInnerLeftTop.packStart(win.buttonSearch, false, false, 0); + win.vboxInnerLeft.packStart(win.hboxInnerLeftTop, false, false, 5); + win.vboxInnerLeft.packStart(win.treeView, true, true, 0); + + win.hboxInner.packStart(win.vboxInnerLeft, false, true, 5); + win.scrolledTextWindow.add(win.textInfo); + win.hboxInner.packStart(win.scrolledTextWindow, true, true, 0); + + win.add(win.vboxOuter); + + win.onDestroy(function() { + win.gtk.mainQuit(); + process.exit(); + }); + win.menuFileQuit.onActivate(function () { win.destroy(); }); + + win.menuBar.showAll(); + win.showAll(); + + if (type != undefined) { + win.entrySearch.text = type; + gtkDumpTypeTree(win, type, brwsVer, brwsObj); + } + + win.heightRequest = 500; + win.gtk.main(); + + return win; +} diff --git a/examples/libxml2.js b/examples/libxml2.js index f97c75c..ec9260a 100644 --- a/examples/libxml2.js +++ b/examples/libxml2.js @@ -1,2 +1,2 @@ var gir = require('../gir'), - , libxml2 = exports.gtk = gir.load('libxml2'); + , libxml2 = exports.gtk = gir.import('libxml2'); diff --git a/examples/midgard.js b/examples/midgard.js index 5909752..4c2d2f3 100644 --- a/examples/midgard.js +++ b/examples/midgard.js @@ -1,2 +1,2 @@ var gir = require('../gir') - , Midgard = module.exports = gir.load('Midgard'); + , Midgard = module.exports = gir.import('Midgard'); diff --git a/examples/multiple_test.js b/examples/multiple_test.js index 8caa591..668d866 100644 --- a/examples/multiple_test.js +++ b/examples/multiple_test.js @@ -1,15 +1,15 @@ var gir = require('../gir') - , gtk = gir.load('Gtk', '3.0') - , WebKit = gir.load('WebKit', '3.0') - , notify = gir.load('Notify') - , appTitle = 'Node.JS Gtk+Webkit+Notify Multiple Example'; + , gtk = gir.import('Gtk', '3.0') + , WebKit = gir.import('WebKit', '3.0') + , notify = gir.import('Notify') + , appTitle = 'WebKit with Notify Example'; gtk.init(0); notify.init(appTitle); var win = new gtk.Window(); -win.on('destroy', function() { +win.onDestroy(function() { console.log('Window destroyed'); gtk.mainQuit(); process.exit(); @@ -20,11 +20,11 @@ win.add(sw); var view = new WebKit.WebView(); -view.on('title-changed', function() { +view.onTitleChanged(function() { win.title = view.title + ' - ' + appTitle; }); -view.on('load-finished', function() { +view.onLoadFinished(function() { var n = new notify.Notification(); n.update('URL Loaded', view.uri); setTimeout(function () { @@ -41,6 +41,4 @@ win.showAll(); win.title = appTitle; -console.log(WebKit.WebView.__methods__); - gtk.main(); diff --git a/examples/namespaces.js b/examples/namespaces.js index d319609..445db31 100644 --- a/examples/namespaces.js +++ b/examples/namespaces.js @@ -3,7 +3,7 @@ var gir = require("../gir"); gir.init(); console.log(gir.searchPath()); -gir.load("Gtk"); +gir.import("Gtk"); console.log(gir.loadedNamespaces()); diff --git a/examples/notify.js b/examples/notify.js index 747bddf..5612224 100644 --- a/examples/notify.js +++ b/examples/notify.js @@ -2,7 +2,7 @@ var gir = require('../gir') , notify = gir.load('Notify'); -console.log(notify.init('notify_test.js sample application')); +console.log(notify.init('Notify Example')); var n = new notify.Notification(); var created = n.__call__('new', 'a', 'a', 'a', 'a'); @@ -13,7 +13,7 @@ for(var k in n) { console.log(notify.Notification.__methods__); -n.update('Notify Test', 'This is a test notification message via Node.JS.'); +n.update('Notify Example', 'This is an example notification from Node.JS.'); n.show(); setTimeout( @@ -26,8 +26,8 @@ setTimeout( n.show(); setTimeout( function () { - n.close(); console.log('Adios!'); + n.close(); }, 5000 ); }, 4000 diff --git a/examples/webkit.js b/examples/webkit.js index bac09f4..9a45b4b 100644 --- a/examples/webkit.js +++ b/examples/webkit.js @@ -1,12 +1,13 @@ var gir = require('../gir') - , gtk = gir.load('Gtk', '3.0') - , WebKit = gir.load('WebKit', '3.0'); + , gtk = gir.import('Gtk', '3.0') + , WebKit = gir.import('WebKit', '3.0') + , appTitle = 'Webkit Example'; gtk.init(0); var win = new gtk.Window(); -win.on('destroy', function() { +win.onDestroy(function() { console.log('Window destroyed'); gtk.mainQuit(); process.exit(); @@ -16,12 +17,17 @@ var sw = new gtk.ScrolledWindow(); win.add(sw); var view = new WebKit.WebView(); + +view.onTitleChanged(function() { + win.title = view.title + ' - ' + appTitle; +}); + view.loadUri("http://www.google.com/"); sw.add(view); win.setSizeRequest(640, 480); win.showAll(); -win.title = 'Node.JS Webkit Example'; +win.title = appTitle; gtk.main(); From 3b430ee09e09ffbe747765293793c81666b9971b Mon Sep 17 00:00:00 2001 From: David Ball Date: Mon, 16 Jan 2012 01:07:46 -0500 Subject: [PATCH 8/9] Updated README. --- README.markdown | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.markdown b/README.markdown index 2cbc39b..7fc9ae9 100644 --- a/README.markdown +++ b/README.markdown @@ -1,7 +1,7 @@ # node-gir Node-gir is node bindings to the girepository library making it possible to make -automatic and dynamic calls to any library that has GI annotations installed. +automatic and dynamic calls to any library that has GI annotations installed. This will make it possible to script a gnome desktop system entirely from node much in the way it's done today with Seed, GJS or pygtk. @@ -74,7 +74,7 @@ missing part is node bindings to libgirepository. Hence this project. ## Why not use Seed or GJS Because they are nice, but not what I'm looking for. Node is really popular and -it would be nice to be able to use it for desktop tools and applications. +it would be nice to be able to use it for desktop tools and applications. ## Implementation Notes @@ -86,31 +86,31 @@ Here are some links and notes as I try to figure out how to do this. ## API Ideas Some of these ideas will go in this binding and some will go in nice wrappers -that use it. I'll know more as we progress. +that use it. I'll know more as we progress. - - Use `camelCase` for methods that are bound to look JavaScripty. - - Use `.on(name, callback)` to attach signals. - - Keep the same constructor style used by Seed and GJS + - Keep the same constructor style used by Seed and GJS. - Make the module system as node-like as possible. + - Identify static objects/members versus instance objects/members. ## Things which work - - All classes get created - - classes get inherited - - A class has lists of all its properties, methods, signals, vfuncs and fields - - You can create a class - - functions can be called (but it does not work so well) - - property values can be set/get - - events can be watched - - flags, enums etc are set + - All classes get created. Classes get inherited. + - A class has lists of all its properties, methods, signals, vfuncs, and fields. Use inspector example to print a complete dump of object members prior to an object instance. Object instances may vary from the prototypes. + - Properties, methods, and vfuncs use `conventionalCamelCasing` for names. They are wired into the `otherwise\_unconventional\_underscores` and `dashes-in-names` counterparts on the back end. + - Events can be watched. Signals are associated with `EventEmitter` object provided by Node. Use `.on('signal-name', callback)` to attach signals, or use shortcut syntax `onSignalName(callback)`. + - Methods can be called. + - Property values can be set/get. JavaScript getters/setters are in-place to ensure that properties are queried properly. + - Flags, Enums, etc., are set. ## Things which dont work (correct) - Conversion between a v8 value and a GValue/GArgument is veeeery buggy (but everything needs it so most things are buggy) - - The API is inconsistent (classes just have \_\_call\_method\_\_, \_\_get\_prroperty\_\_ etc + - The API is becoming more consistent (classes just have \_\_call\_method\_\_, \_\_get\_prroperty\_\_ etc but the namespace has all methods [ gst.main(), gst.mainQuit()] - No support for libev/libuv; glib is using its own stuff (gst.main()) - There is no good way to delete an object (memory management sucks at all) - - You can't pass construction parameters to g_object_new + - You can't pass construction parameters to g\_object\_new - Only the GObject and Function type is implementet yet (left are GIInterfaceInfo and GIStructInfo) - types/function.cc need a rewrite + - Static members fail, usually with TypeError. This probably means the actual objects need members to be set in addition to the prototypes, but there is no way to tell which is static and which is instance. + - Some objects that are returned by methods (and possibly properties) do not have the `\_\_call\_\_`, `\_\_get\_property\_\_`, `\_\_set\_property\_\_`, and friends prototype members. This produces 'Error: bad arguments' on the JavaScript consumer. From 29d5b2455c0aefcb1e163786299f35a8af53a7b3 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 23 Jan 2012 18:34:09 +0100 Subject: [PATCH 9/9] remove unnecessary dependencies --- src/types/function.cc | 2 -- wscript | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/types/function.cc b/src/types/function.cc index ef4cf7e..7c1b175 100644 --- a/src/types/function.cc +++ b/src/types/function.cc @@ -5,8 +5,6 @@ #include #include -#include -#include using namespace v8; diff --git a/wscript b/wscript index c250336..ccea997 100644 --- a/wscript +++ b/wscript @@ -14,8 +14,6 @@ def configure(conf): conf.check_tool("node_addon") conf.check_cfg(package='gobject-introspection-1.0', uselib_store='GIREPOSITORY', args='--cflags --libs') conf.check_cfg(package='glib', uselib_store='GLIB', args='--cflags --libs') - conf.check_cfg(package='gtk+-2.0', uselib_store='GTK', args='--cflags --libs') - conf.check_cfg(package='gdk-2.0', uselib_store='GDK', args='--cflags --libs') def build(bld): obj = bld.new_task_gen("cxx", "shlib", "node_addon")