Today, I've learned something about Adobe Flex. That's not surprising, since I'm using Flex for the first time. So this is just a quick "note-to-self" kind of post. I was trying to use objects of a subclass of flash.utils.Proxy inside a mx.controls.DataGrid component. Displaying them worked as expected, but I could only select the last row in the list. After spending about half an hour stepping through DataGrid and related code from the Flex framework, I finally got it to work. Maybe this post will spare someone the dubious pleasure of wading through heaps and heaps of frameworky do-nothing-and-do-lots-of-it code. It turns out, that DataGrid uses a unique ID to keep track of the objects it is supposed to display. This ID is usually provided by the runtime. There is even documentation about it. Of course I only found that afterwards. Finding out whether reading this would have solved my problem might have taken just as long... Anyway, here is some code to illustrate the problem and the solution. Apologies for the FooBar nonsense. I couldn't come up with a simple example.
Let's say we have a model object called "FooBar" with two properties, "spam" and "eggs".
package flex_proxy_example
{
import mx.collections.ArrayCollection;
public class FooBar
{
public var spam:String;
public var eggs:int;
public function FooBar(spam:String, eggs:int)
{
this.spam = spam;
this.eggs = eggs;
}
public static function getAll():ArrayCollection
{
var result:ArrayCollection = new ArrayCollection();
result.addItem(new FooBar("foo", 5));
result.addItem(new FooBar("bar", 23));
result.addItem(new FooBar("baz", 42));
result.addItem(new FooBar("spam", 1337));
result.addItem(new FooBar("egs", 4711));
return result;
}
}
}
We want to display a collection of these in a DataGrid. For some reason we want to massage the value of the "eggs" property before displaying it. This can be done by using a collection of proxy objects as the dataProvider of our DataGrid, instead of the model objects themselves. Flex 3 provides a class to create proxy objects, sensibly called "Proxy". The proper way to use this class is to subclass it and implement (at least) the methods getProperty and callProperty. Here's how our FooBarProxy class might look like:
package flex_proxy_example
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class FooBarProxy extends Proxy
{
private var foobar:FooBar;
public function FooBarProxy(foobar:FooBar)
{
this.foobar = foobar;
}
override flash_proxy function getProperty(name:*):*
{
if (name.toString() == "eggs")
return foobar.eggs + 12345;
return foobar[name];
}
override flash_proxy function callProperty(methodName:*, ... args):*
{
return foobar[methodName].apply(foobar, args);
}
}
}
And here is a minimal Flex application, using the above classes:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="creationComplete()">
<mx:Script>
<![CDATA[
import flex_proxy_example.FooBarProxy;
import flex_proxy_example.FooBar;
import mx.collections.ArrayCollection;
private var bunchOfFoobars:ArrayCollection;
public function creationComplete():void
{
bunchOfFoobars = new ArrayCollection();
for each (var fb:FooBar in FooBar.getAll())
{
bunchOfFoobars.addItem(new FooBarProxy(fb));
}
dg.dataProvider = bunchOfFoobars;
}
]]>
</mx:Script>
<mx:DataGrid id="dg">
<mx:columns>
<mx:DataGridColumn dataField="spam" />
<mx:DataGridColumn dataField="eggs" />
</mx:columns>
</mx:DataGrid>
</mx:Application>
There is just one little problem. Selecting anything but the last element of the DataGrid won't work. This is because the proxy objects all produce the same string as their UID. As the above linked documentation suggests, we can add our own UID property to the FooBarProxy class and everything will be peachy. This is how the fixed code looks:
package flex_proxy_example
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
import mx.core.IUID;
import mx.utils.UIDUtil;
public class FooBarProxy extends Proxy implements IUID
{
private var foobar:FooBar;
private var _uid:String;
public function FooBarProxy(foobar:FooBar)
{
this.foobar = foobar;
this._uid = UIDUtil.createUID();
}
public function get uid():String
{
return _uid;
}
public function set uid(u:String):void
{
// ignored
}
override flash_proxy function getProperty(name:*):*
{
if (name.toString() == "eggs")
return foobar.eggs + 12345;
return foobar[name];
}
override flash_proxy function callProperty(methodName:*, ... args):*
{
return foobar[methodName].apply(foobar, args);
}
}
}
I was too lazy to upload the example app somewhere and embed it into this post. It's pointless anyway. The code is available at http://hg.haikoschol.com/flex_proxy_example/src, simply because I have so much free space on Bitbucket. ;)
Friday, June 25, 2010
Sunday, March 21, 2010
FizzBuzz? SRSLY?
I was playing with Visual Studio 2010 and could not resist. Does that mean I'm not a good software developer? Anyway, without further ado, I present to you my ridiculously overengineered, gold-plated version of FizzBuzz in C++. Complete with lazy evaluation via a custom random access iterator. I'm not a C++ guru, so I probably got a lot of stuff wrong. If you take the time to write a comment about my mistakes, it won't have been a complete waste of time. Well, not for me at least.
#include <iostream>
#include <string>
#include <sstream>
#include <iterator>
#include <stdexcept>
class fizzbuzz {
public:
class const_iterator;
typedef std::pair<int, int> range;
fizzbuzz(const range& range = range(0, 100),
int fizz_num = 3,
int buzz_num = 5,
const std::string& fizz_word = "Fizz",
const std::string& buzz_word = "Buzz")
: _range(range),
_fizz_num(fizz_num),
_buzz_num(buzz_num),
_fizz_word(fizz_word),
_buzz_word(buzz_word) {}
range get_range() const { return _range; }
int get_fizz_num() const { return _fizz_num; }
int get_buzz_num() const { return _buzz_num; }
std::string get_fizz_word() const { return _fizz_word; }
std::string get_buzz_word() const { return _buzz_word; }
int get_fizz_num() { return _fizz_num; }
int get_buzz_num() { return _buzz_num; }
std::string get_fizz_word() { return _fizz_word; }
std::string get_buzz_word() { return _buzz_word; }
void set_fizz_num(int num) { _fizz_num = num; }
void set_buzz_num(int num) { _buzz_num = num; }
void set_fizz_word(const std::string& word) { _fizz_word = word; }
void set_buzz_word(const std::string& word) { _buzz_word = word; }
const_iterator begin() const { return const_iterator(this); }
const_iterator end() const { return const_iterator(this, true); }
class const_iterator {
public:
typedef std::string value_type;
typedef std::random_access_iterator_tag iterator_category;
const_iterator(const fizzbuzz* fb, bool end=false) :
_fb(fb) {
if (end) {
_current = _fb->get_range().second;
} else {
_current = _fb->get_range().first;
}
}
const_iterator(const const_iterator& other) {
*this = other;
}
const_iterator& operator=(const const_iterator& rhs) {
if (this == &rhs)
return *this;
this->_fb = rhs._fb;
this->_current = rhs._current;
return *this;
}
value_type operator*() const {
if ((_current < _fb->get_range().first) ||
(_current >= _fb->get_range().second))
throw std::out_of_range("fizz went buzz(t)");
return _fb->get_value(_current);
}
const_iterator& operator++() {
_current++;
return *this;
}
const_iterator& operator++(int) {
_current++;
return *this;
}
const_iterator& operator--() {
_current--;
return *this;
}
const_iterator& operator--(int) {
_current--;
return *this;
}
value_type operator[](int index) const {
if ((index < _fb->get_range().first) ||
(index >= _fb->get_range().second))
throw std::out_of_range("fizz went buzz(t)");
return _fb->get_value(index);
}
bool operator==(const const_iterator& rhs) const {
return rhs._current == _current;
}
bool operator!=(const const_iterator& rhs) const {
return !(*this == rhs);
}
private:
const fizzbuzz* _fb;
int _current;
};
private:
bool fizz(int num) const { return num % _fizz_num == 0; }
bool buzz(int num) const { return num % _buzz_num == 0; }
const std::string get_value(int num) const {
if (fizz(num) && buzz(num))
return _fizz_word + _buzz_word;
if (fizz(num))
return _fizz_word;
if (buzz(num))
return _buzz_word;
std::stringstream s;
s << num;
return s.str();
}
range _range;
int _fizz_num;
int _buzz_num;
std::string _fizz_word;
std::string _buzz_word;
};
int main()
{
fizzbuzz fb(fizzbuzz::range(0, 100));
for (fizzbuzz::const_iterator it(fb.begin()); it != fb.end(); ++it) {
std::cout << *it << std::endl;
}
std::cout << std::endl;
std::cout << "And now for some random access..." << std::endl << std::endl;
fizzbuzz::const_iterator it(fb.begin());
std::cout << "it[5]: " << it[5] << std::endl;
std::cout << "it[15]: " << it[15] << std::endl;
std::cout << "it[33]: " << it[33] << std::endl;
std::cout << "it[49]: " << it[49] << std::endl;
return 0;
}
Tuesday, January 26, 2010
Running Groovy Code from XML Files
For an upcoming Java project, I'm having a look at Groovy. An obvious first opportunity for using it is unit tests. Here's another use case I just made up (that has nothing to do with the project whatsoever ;). Suppose you have two domain objects. One representing a mailing address, maybe stored in a database. The other one representing some kind of form that should be filled from the available data, which includes, among other things, an address. So you might come up with an XML format that maps fields from one object to the other. Maybe one contains a field "city" and the other one a field named "town" and you have something like
That's not very exciting. But then you have something like "streetName" and "streetNumber" in one object and "street" in the other one. Now you need to do some simple string manipulation to map fields from source to destination. So, extend the XML! Maybe like this?
Doesn't look so bad, does it? Until you need to do the opposite, split up a field. You end up extending your XML format, slowly implementing a programming language. In XML. Not exactly pretty, IMHO. And lot's of pointless work to implement. What's the alternative? Don't use XML if you need logic! Use a *programming* language! so you write the mappings in Java. This is very time consuming as well. Suppose you want to generate some of these mappings automatically. Maybe give your users a tool to create them easily, with an intuitive interface. Generating Java code from that might not be such a good idea.
Ok, enough of coming up with reasons and examples why you'd want to do this. Let's just agree, that we want to embed little snippets of Groovy code in an XML mapping document to perform simple transformations of source to destination fields. The scope in which the snippets are run should contain the source object and they should return a string, which is the content of the destination field. Our domain objects might look like this:
and this:
So a mapping with a Groovy snippet in it might look like this:
Now on to the actual point of this blog post. How do we execute the Groovy code that we pull out of the XML file as a string? There are several ways to run Groovy code from Java. As far as I know, some of them allow having a restricted security context. If you take arbitrary text from the network and run it as Groovy scripts, you want to make absolutely sure, that it's not possible to call System.exit() or worse (much, much worse ;). Come to think about it, you probably never want to do something like that. Anyway, we'll just look at the one I tried first and found to be working just fine; GroovyShell. For more information about available options have a look at these docs or this blog post.
Say we have a class that does the mapping. It reads the XML file, pulls data out of the source object, puts it into the destination object and when it encounters a non-empty tag, it runs the code inside. For the sake of loose coupling, we'll introduce a horrendously named interface that we can use as parameters for source and destination objects.
When I made up this example, I didn't realize that it would either require something like this setField()/getField() stuff or reflection. Well, maybe there's another, better way. I didn't think about this all that much. If you're used to coding in Python, you tend to take it for granted to be able to seamlessly go back and forth between strings containing the names of and actual functions/method/members. Gotta get back into the Java mindset I guess. Anyway, a rough sketch of the mapper class:
So there it is, inside applyTransformation(). Shouldn't come as a surprise, since all the documentation and blog posts on the subject contain this code. But since I had to do the legwork of putting this together anyway, I figured I might as well blog about it. Considering how little I've managed to blog at all so far... As a little bonus, you may now laugh at my very first Groovy code that I wrote to generate those domain classes that consist of 120% Pure Premium Boilerplate(tm). It's probably not idiomatic and could be written in half as many lines of code. The capitalizeFirstLetter() function looks particularily cumbersome to me. I just ran with the first thing that worked inside groovysh. The template mechanism that's built into Groovy strings is neat. I probably don't need to toString() calls, likewise in the XML file.
And get the following, exhilarating output:
<mapping>
<destination>town</destination>
<source>city</source>
<mapping>
That's not very exciting. But then you have something like "streetName" and "streetNumber" in one object and "street" in the other one. Now you need to do some simple string manipulation to map fields from source to destination. So, extend the XML! Maybe like this?
<mapping>
<destination>street</destination>
<source>
<combine>
<field>streetName</field>
<field>streetNumber</field>
</combine>
</source>
</mapping>
Doesn't look so bad, does it? Until you need to do the opposite, split up a field. You end up extending your XML format, slowly implementing a programming language. In XML. Not exactly pretty, IMHO. And lot's of pointless work to implement. What's the alternative? Don't use XML if you need logic! Use a *programming* language! so you write the mappings in Java. This is very time consuming as well. Suppose you want to generate some of these mappings automatically. Maybe give your users a tool to create them easily, with an intuitive interface. Generating Java code from that might not be such a good idea.
Ok, enough of coming up with reasons and examples why you'd want to do this. Let's just agree, that we want to embed little snippets of Groovy code in an XML mapping document to perform simple transformations of source to destination fields. The scope in which the snippets are run should contain the source object and they should return a string, which is the content of the destination field. Our domain objects might look like this:
public class Address {
private HashMap<String, String> fields = new HashMap<String, String>();
public Address() {
setField("firstName", "");
setField("lastName", "");
setField("street", "");
setField("zipcode", "");
setField("city", "");
}
public Address(
String firstName,
String lastName,
String street,
String zipcode,
String city) {
setField("firstName", firstName);
setField("lastName", lastName);
setField("street", street);
setField("zipcode", zipcode);
setField("city", city);
}
public void setField(String key, String value) {
fields.put(key, value);
}
public String getField(String key) {
return fields.get(key);
}
public String toString() {
return fields.toString();
}
public String getFirstName() {
return getField("firstName");
}
public void setFirstName(String firstName) {
setField("firstName", firstName);
}
public String getLastName() {
return getField("lastName");
}
// ...
and this:
public class FormWithAddress {
private HashMap<String, String> fields = new HashMap<String, String>();
public FormWithAddress() {
setField("name", "");
setField("streetName", "");
setField("streetNumber", "");
setField("zipcode", "");
setField("city", "");
}
public FormWithAddress(
String name,
String streetName,
String streetNumber,
String zipcode,
String city) {
setField("name", name);
setField("streetName", streetName);
setField("streetNumber", streetNumber);
setField("zipcode", zipcode);
setField("city", city);
}
public void setField(String key, String value) {
fields.put(key, value);
}
public String getField(String key) {
return fields.get(key);
}
public String toString() {
return fields.toString();
}
public String getName() {
return getField("name");
}
public void setName(String name) {
setField("name", name);
}
public String getStreetName() {
return getField("streetName");
}
public void setStreetName(String streetName) {
setField("streetName", streetName);
}
// yadda yadda yadda
So a mapping with a Groovy snippet in it might look like this:
<mapping>
<source></source>
<execute><![CDATA[
firstName = address.getField("firstName")
lastName = address.getField("lastName")
return "${lastName}, ${firstName}".toString()
]]></execute>
<destination>name</destination>
</mapping>
Now on to the actual point of this blog post. How do we execute the Groovy code that we pull out of the XML file as a string? There are several ways to run Groovy code from Java. As far as I know, some of them allow having a restricted security context. If you take arbitrary text from the network and run it as Groovy scripts, you want to make absolutely sure, that it's not possible to call System.exit() or worse (much, much worse ;). Come to think about it, you probably never want to do something like that. Anyway, we'll just look at the one I tried first and found to be working just fine; GroovyShell. For more information about available options have a look at these docs or this blog post.
Say we have a class that does the mapping. It reads the XML file, pulls data out of the source object, puts it into the destination object and when it encounters a non-empty
public interface FieldsGettableSettable {
public String getField(String key);
public void setField(String key, String value);
}
When I made up this example, I didn't realize that it would either require something like this setField()/getField() stuff or reflection. Well, maybe there's another, better way. I didn't think about this all that much. If you're used to coding in Python, you tend to take it for granted to be able to seamlessly go back and forth between strings containing the names of and actual functions/method/members. Gotta get back into the Java mindset I guess. Anyway, a rough sketch of the mapper class:
public class FormMapper {
public FormMapper(FieldsGettableSettable from, FieldsGettableSettable to,
String mappingFilename) {
// ...
}
public void performMapping() {
// ...
}
private void mapField(String source, String destination, String execute) {
if ((source != null && !source.trim().equals("")) &&
(destination != null && !destination.trim().equals(""))) {
to.setField(destination, from.getField(source));
} else {
applyTransformation(execute, destination);
}
}
private void applyTransformation(String transformation,
String destination)
throws CompilationFailedException {
Binding binding = new Binding();
binding.setVariable("address", from);
GroovyShell shell = new GroovyShell(binding);
Object result = shell.evaluate(transformation);
assert result instanceof String;
to.setField(destination, (String) result);
}
}
So there it is, inside applyTransformation(). Shouldn't come as a surprise, since all the documentation and blog posts on the subject contain this code. But since I had to do the legwork of putting this together anyway, I figured I might as well blog about it. Considering how little I've managed to blog at all so far... As a little bonus, you may now laugh at my very first Groovy code that I wrote to generate those domain classes that consist of 120% Pure Premium Boilerplate(tm). It's probably not idiomatic and could be written in half as many lines of code. The capitalizeFirstLetter() function looks particularily cumbersome to me. I just ran with the first thing that worked inside groovysh. The template mechanism that's built into Groovy strings is neat. I probably don't need to toString() calls, likewise in the XML file.
For what it's worth, all of this stuff is available at http://hg.zeropatience.net/groovyinxml/. Ah, right, an example... So we use the mapper class like this:
#!/usr/bin/env groovy
def capitalizeFirstLetter(str, result="") {
result += str[0].toUpperCase()
result += str[1..str.length()-1]
return result
}
def generateGetter(fieldName) {
methodName = capitalizeFirstLetter(fieldName, "get")
return """\
public String ${methodName}() {
return getField("${fieldName}");
}
"""
}
def generateSetter(fieldName) {
methodName = capitalizeFirstLetter(fieldName, "set")
return """\
public void ${methodName}(String ${fieldName}) {
setField("${fieldName}", ${fieldName});
}
"""
}
def generateDefaultConstructor(name, fields) {
result = " public ${name}() {\n";
for (field in fields) {
result += " setField(\"${field}\", \"\");\n"
}
result += " }\n\n"
return result
}
def generateFieldsConstructor(name, fields) {
result = " public ${name}(\n"
len = fields.size()
fields.eachWithIndex() { field, i ->
if (i+1 < len)
result += " String ${field},\n"
else
result += " String ${field}) {\n"
}
for (field in fields) {
result += " setField(\"${field}\", ${field});\n"
}
result += " }\n\n"
return result
}
def generateClass(name, pkg, fields) {
code = """\
package ${pkg};
import java.util.HashMap;
public class ${name} {
private HashMap<String, String> fields = new HashMap<String, String>();
"""
code += generateDefaultConstructor(name, fields)
code += generateFieldsConstructor(name, fields)
code += """\
public void setField(String key, String value) {
fields.put(key, value);
}
public String getField(String key) {
return fields.get(key);
}
public String toString() {
return fields.toString();
}
"""
for (field in fields) {
code += generateGetter(field)
code += generateSetter(field)
}
code += """\
}
"""
return code.toString()
}
if (args.size() < 3) {
print "usage: ./GenCls.groovy <Class Name> <Package> <Field 1> [Field 2] ..."
System.exit(1)
}
print generateClass(args[0], args[1], args[2..args.size()-1])
public class Main {
public static void main(String[] args) {
Address address = new Address("Homer",
"Simpson",
"742 Evergreen Terrace",
"0xBADC0DE",
"Springfield");
FormWithAddress formWithAddress = new FormWithAddress();
try {
FormMapper formMapper = new FormMapper(address,
formWithAddress,
"Address2FormWithAddress.xml");
formMapper.performMapping();
} catch (Exception ex) {
System.err.println(ex.getLocalizedMessage());
System.exit(1);
}
System.out.println("\nMAPPED\n\n");
System.out.println(address);
System.out.println("\n\nTO\n\n");
System.out.println(formWithAddress);
}
}
And get the following, exhilarating output:
MAPPEDThat's all.
{lastName=Simpson, zipcode=0xBADC0DE, street=742 Evergreen Terrace, firstName=Homer, city=Springfield}
TO
{zipcode=0xBADC0DE, name=Simpson, Homer, streetNumber=742, streetName=Evergreen Terrace,
city=Springfield}
Friday, December 18, 2009
Useful Keyboard Shortcuts with AppleScript on Mac OS X
I've been using Ubuntu as my main OS for a while now. Gnome has a GUI for configuring global keyboard shortcuts (System -> Preferences -> Keyboard Shortcuts). For example I've set "Launch email client" to Ctrl+Alt+M and "Launch web browser" to Ctrl+Alt+W. I've also added a custom shortcut for launching GVim, another application that I frequently launch and close. Another action that I often perform via a shortcut is "Maximize window vertically" (Shift+Ctrl+Alt+Down). On high resolution screens (like 1920x1200), maximizing windows becomes less useful. When I need to see a lot of output in a terminal window for example, maximizing vertically makes much more sense. Being able to toggle play/pause on your music player of choice is also very useful when clicking on youtube videos embedded in blog posts or answering the phone. Banshee hooks into the Gnome mechanism, so I set Ctrl-F8 to toggle play/pause. What's interesting is that some other applications also support this mechanism. So when I launch Totem, it hijacks my shortcut. This is not really what I want most of the time.
The other day I was using my old Macbook and found myself using these shortcuts. Of course, nothing happened so I set out to fix this. Actually, I started using the shortcuts for launching a browser, shell and email client on Mac OS X. Back then I was using Quicksilver and had those shortcuts configured as global triggers. (Don't use the Quicksilver app from that link, BTW. Use Google Quick Search Box or Launchbar.) For toggling iTunes I used Sizzling Keys, which still works nicely. Nowadays, Quicksilver is bitrotting, so I was looking for something else. Surprisingly (to me, anyway), AppleScript is actually pretty useful. One nice feature of the Quicksilver triggers was that applications that are already running are not launched again. Instead, the running instance was activated (like when alt-tabbing to it). This was probably due to the one-instance-per-app behaviour that is common on Mac OS X. In my scripts, I check whether the app is running, which might not be neccessary. But it doesn't hurt and might be faster, so whatever. At any rate, for most apps I prefer this behaviour to the one on Ubuntu, where you can easily launch two instances of Thunderbird, for example. Which usually doesn't make sense.
Well, launching apps via AppleScript is not all that interesting. What about "Maximize window vertically"? The below code fragment determines the size of your screen, grabs the window that has the focus and resizes it vertically. You don't even need to figure out whether the dock is visible and how large it is. It will automatically do the right thing.
While we're at it, let's add some more useful window manipulation. As you can see in the screenshot above, there is a script to maximize the active window. Admit it, you sometimes miss a simple, consistent maximize function on the Mac. Yes, as a proper fanboy I should be claiming that the mac "maximize" behaviour is much smarter, resizes windows to the optimal size, etc. etc. Bullshit. As pointed out above, on a very large monitor maximizing a window to take up the whole screen is often useless. But my Macbook only does 1280x800. And oftentimes I want Safari to maximize, dammit. So now it does. The other two, "Maximize Window Left" and "Maximize Window Right" replicate a feature of Windows 7 that I found to be quite useful. "Maximize Window Left" puts the active window on the left side of the screen, maximizes it vertically and resizes it horizontally to take up half of the space. "Maximize Window Right" does the same thing on the right half of the screen. I already miss this on Ubuntu. It probably can be done in a window manager independent way by sending the right kind of data to the X server. This python library seems to be useful for doing that. I'm too lazy to try right now, though.
For the most part I like the whole shortcut functionality on the Mac better. Not launching apps that are already running for example is nice. Of course, everything can be done on Linux, but when you find yourself knee-deep in yak hair, consider putting down the text editor, scripting language and man page and go back to doing something useful. ;) What sucks about the AppleScript solution is that it doesn't work with all applications. I had some issues with MacVim and Firefox, IIRC. Anyway, Good Enough(tm), 80/20 and all that. Ah, almost forgot; you can download my scripts here.
The other day I was using my old Macbook and found myself using these shortcuts. Of course, nothing happened so I set out to fix this. Actually, I started using the shortcuts for launching a browser, shell and email client on Mac OS X. Back then I was using Quicksilver and had those shortcuts configured as global triggers. (Don't use the Quicksilver app from that link, BTW. Use Google Quick Search Box or Launchbar.) For toggling iTunes I used Sizzling Keys, which still works nicely. Nowadays, Quicksilver is bitrotting, so I was looking for something else. Surprisingly (to me, anyway), AppleScript is actually pretty useful. One nice feature of the Quicksilver triggers was that applications that are already running are not launched again. Instead, the running instance was activated (like when alt-tabbing to it). This was probably due to the one-instance-per-app behaviour that is common on Mac OS X. In my scripts, I check whether the app is running, which might not be neccessary. But it doesn't hurt and might be faster, so whatever. At any rate, for most apps I prefer this behaviour to the one on Ubuntu, where you can easily launch two instances of Thunderbird, for example. Which usually doesn't make sense.
Well, launching apps via AppleScript is not all that interesting. What about "Maximize window vertically"? The below code fragment determines the size of your screen, grabs the window that has the focus and resizes it vertically. You don't even need to figure out whether the dock is visible and how large it is. It will automatically do the right thing.
tell application "Finder"If you don't consider this to be the right thing, try Alt+Cmd+D to hide the dock, then maximize vertically again and unhide the dock (Alt+Cmd+D again). But I'm getting ahead of myself. So you have a bunch of scripts for manipulating the active window. How do you put those on global keyboard shortcuts? This might be possible without any third-party tools. I think you can put custom scripts in the services menu and assign those to keyboard shortcuts in System Preferences. I didn't try that though. There's a nice free tool called FastScripts Lite from the awesome indie mac developer Daniel Jalkut. As the "Lite" suggests, there is an even better version available that costs some money.
set desktopFrame to bounds of window of desktop
set screenHeight to item 4 of desktopFrame
end tell
tell application "System Events"
set activeApplication to name of the first process whose frontmost is true
end tell
tell application activeApplication
set appFrame to bounds of the first window
set xpos to item 1 of appFrame
set ypos to 0
set width to item 3 of appFrame
set height to screenHeight
set bounds of the first window to {xpos, ypos, width, height}
end tell
For the most part I like the whole shortcut functionality on the Mac better. Not launching apps that are already running for example is nice. Of course, everything can be done on Linux, but when you find yourself knee-deep in yak hair, consider putting down the text editor, scripting language and man page and go back to doing something useful. ;) What sucks about the AppleScript solution is that it doesn't work with all applications. I had some issues with MacVim and Firefox, IIRC. Anyway, Good Enough(tm), 80/20 and all that. Ah, almost forgot; you can download my scripts here.

Sunday, November 8, 2009
Using the Mozilla ActiveX Control in a Qt Application
The cross-platform application framework Qt includes WebKit, one of the three rendering engines that power todays web browsers. Creating a basic browser with Qt and WebKit takes less than five minutes.
But what about the other two engines, Internet Explorer and Gecko (used by Firefox)? Windows has a component technology called COM which allows applications to offer and consume object-oriented interfaces in a programming language independent way. One flavor of this technology is called ActiveX, which, if my understanding of this is correct, basically means "COM controls that put something on the screen". I haven't written much code that made use of Windows specific technologies like COM, so I might get some things wrong. But reading that Wikipedia article, I feel like I have a pretty good idea of how this is supposed to work. This should be sufficient to make sense of the Qt documentation on their ActiveQt Framework. All the tedious plumbing details are hopefully hidden inside ActiveQt. So let's get to it!
We'll start with embedding the existing Internet Explorer ActiveX control in a Qt application. If you don't have it already, download the Qt SDK for Windows, install it and, no, don't launch QtCreator just yet. According to the documentation, we first need to build ActiveQt. Assuming you got the latest SDK version at the time of writing and you installed it in the default location, building and installing the neccessary files is done on the command line in the following way:
Now it's time to fire up QtCreator and create a new project. Click File -> New, under Projects choose "Qt4 Gui Application" and give it a name and location. I chose "MultiBrowser" and C:\code. Accept the defaults for everything else. In the tree on the left, displaying the files of your project, double click MultiBrowser.pro and add a line containing:
We don't need slots for forward and back, because those buttons can be hooked up directly to the QAxWidget instance. Our mainwindow.cpp file contains the implementation of the "navigate" slot as well as some calls to connect() in the constructor.
That's all we need for embedding IE in our app. Run it, type in a URL and hit enter. We got signal!
Ok, fine, but originally the plan was to embed the Gecko rendering engine in our Qt application. As the title already gives away, there is in fact an ActiveX control that lets us do that. It was written by former Netscape employee Adam Lock with the goal to be API compatible with the IE control. That means we can use this control exactly like the one we are already using. Download the installer from Adams website and run it. Once that is done, we need to slightly change our GUI to accommodate a second browser control. I chose a tab widget for this task. Open mainwindow.ui, delete the browserWidget and drag a QTabWidget in its place. The tab widget already contains two tabs. In the object tree in the upper left, select the first tab. The type of the objects that are contained in the tabs is QWidget. We can promote them to QAxWidget just like we did with the original QWidget we used above. I called the first one ieTab and the second geckoTab. The code inside mainwindow.cpp has to be adapted as well. Change every occurrence of browserWidget to ieTab and add the same code for geckoTab. Afterwards, mainwindow.cpp should look something like this
If you run the app, you should now have both browsers embedded and displaying whatever URL was entered in the QLineEdit widget. We can check that we have in fact two different browsers by opening a website that displays the HTTP headers that are sent by IE and Gecko.
But what about the other two engines, Internet Explorer and Gecko (used by Firefox)? Windows has a component technology called COM which allows applications to offer and consume object-oriented interfaces in a programming language independent way. One flavor of this technology is called ActiveX, which, if my understanding of this is correct, basically means "COM controls that put something on the screen". I haven't written much code that made use of Windows specific technologies like COM, so I might get some things wrong. But reading that Wikipedia article, I feel like I have a pretty good idea of how this is supposed to work. This should be sufficient to make sense of the Qt documentation on their ActiveQt Framework. All the tedious plumbing details are hopefully hidden inside ActiveQt. So let's get to it!
We'll start with embedding the existing Internet Explorer ActiveX control in a Qt application. If you don't have it already, download the Qt SDK for Windows, install it and, no, don't launch QtCreator just yet. According to the documentation, we first need to build ActiveQt. Assuming you got the latest SDK version at the time of writing and you installed it in the default location, building and installing the neccessary files is done on the command line in the following way:
You should now have the files libQaxContainer.a, libQaxContainerd.a, libQaxServer.a and libQaxServerd.a in C:\Qt\2009.04\qt\lib.C:\Documents and Settings\Your Name> cd \Qt\2009.04\bin C:\Qt\2009.04\bin>qtenv Setting up a MinGW/Qt only environment... -- QTDIR set to C:\Qt\2009.04\qt -- PATH set to C:\Qt\2009.04\qt\bin -- Adding C:\Qt\2009.04\bin to PATH -- Adding C:\WINDOWS\System32 to PATH -- QMAKESPEC set to win32-g++ C:\Qt\2009.04\bin>cd ..\qt\src\activeqt C:\Qt\2009.04\qt\src\activeqt>qmake C:\Qt\2009.04\qt\src\activeqt>mingw32-make ---- Lots of output... ----- C:\Qt\2009.04\qt\src\activeqt>mingw32-make install ---- Some more output... -----
Now it's time to fire up QtCreator and create a new project. Click File -> New, under Projects choose "Qt4 Gui Application" and give it a name and location. I chose "MultiBrowser" and C:\code. Accept the defaults for everything else. In the tree on the left, displaying the files of your project, double click MultiBrowser.pro and add a line containing:
CONFIG += qaxcontainerThe next step is to build the GUI. To do this open the file mainwindow.ui. This will load the GUI designer inside QtCreator. I won't give a step by step explanation of how to use the GUI designer. It's reasonably intuitive if you've ever used a similar tool. The first thing I added was a "File" menu with a "Quit" item that sends the close() signal to the MainWindow instance when clicked. Then I removed the toolbar, added two buttons for navigating back and forward and a QLineEdit for the URL. Below those, I put a QWidget. We need to replace this with a QAxWidget, which can host ActiveX controls. To do this, right click the object on the tree view in the upper left corner and choose "Promote to...". In the dialog enter the information as seen on the screenshot, click "Add" and then "Promote".
After telling QtCreator to apply a grid layout to the widgets, the result looks something like this:
Now open mainwindow.cpp and add the following line to the constructor
Hit Ctrl-R to check whether what you have so far compiles successfully. What's left for a working browser is some event handling. If a user enters a URL in the QLineEdit and hits enter, the browser should open that URL and display pictures of cute kittens. Maybe the forward and back buttons should work too. So what we need for that are some slots that we will connect to the signals emitted by editUrl, backButton and forwardButton. In mainwindow.h add one of those slots, so that the file looks like thisui->browserWidget->setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}");
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui/QMainWindow>
namespace Ui
{
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void navigate();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
We don't need slots for forward and back, because those buttons can be hooked up directly to the QAxWidget instance. Our mainwindow.cpp file contains the implementation of the "navigate" slot as well as some calls to connect() in the constructor.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QAxWidget>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->browserWidget->setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}");
connect(ui->urlEdit,
SIGNAL(returnPressed()),
this,
SLOT(navigate()));
connect(ui->backButton,
SIGNAL(clicked()),
ui->browserWidget,
SLOT(GoBack()));
connect(ui->forwardButton,
SIGNAL(clicked()),
ui->browserWidget,
SLOT(GoForward()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::navigate()
{
ui->browserWidget->dynamicCall("Navigate(const QString&)",
ui->urlEdit->text());
}
That's all we need for embedding IE in our app. Run it, type in a URL and hit enter. We got signal!
Ok, fine, but originally the plan was to embed the Gecko rendering engine in our Qt application. As the title already gives away, there is in fact an ActiveX control that lets us do that. It was written by former Netscape employee Adam Lock with the goal to be API compatible with the IE control. That means we can use this control exactly like the one we are already using. Download the installer from Adams website and run it. Once that is done, we need to slightly change our GUI to accommodate a second browser control. I chose a tab widget for this task. Open mainwindow.ui, delete the browserWidget and drag a QTabWidget in its place. The tab widget already contains two tabs. In the object tree in the upper left, select the first tab. The type of the objects that are contained in the tabs is QWidget. We can promote them to QAxWidget just like we did with the original QWidget we used above. I called the first one ieTab and the second geckoTab. The code inside mainwindow.cpp has to be adapted as well. Change every occurrence of browserWidget to ieTab and add the same code for geckoTab. Afterwards, mainwindow.cpp should look something like this
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QAxWidget>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->ieTab->setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}");
connect(ui->urlEdit,
SIGNAL(returnPressed()),
this,
SLOT(navigate()));
connect(ui->backButton,
SIGNAL(clicked()),
ui->ieTab,
SLOT(GoBack()));
connect(ui->forwardButton,
SIGNAL(clicked()),
ui->ieTab,
SLOT(GoForward()));
ui->geckoTab->setControl("{1339B54C-3453-11D2-93B9-000000000000}");
connect(ui->backButton,
SIGNAL(clicked()),
ui->geckoTab,
SLOT(GoBack()));
connect(ui->forwardButton,
SIGNAL(clicked()),
ui->geckoTab,
SLOT(GoForward()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::navigate()
{
ui->ieTab->dynamicCall("Navigate(const QString&)",
ui->urlEdit->text());
ui->geckoTab->dynamicCall("Navigate(const QString&)",
ui->urlEdit->text());
} If you run the app, you should now have both browsers embedded and displaying whatever URL was entered in the QLineEdit widget. We can check that we have in fact two different browsers by opening a website that displays the HTTP headers that are sent by IE and Gecko.
The headers sent by the embedded Internet Explorer control.
The headers sent by the embedded Mozilla Gecko control.
There's just one little issue. Notice in the second screenshot, the user agent contains the string "Gecko/20050410". That means our Mozilla ActiveX control uses a very old version of the Gecko rendering engine (You might see a slightly different string. I installed version 1.7.7 of the control for some reason, not 1.7.12. But that one is totally out of date as well). You can get a recent version of the control as part of the XULRunner SDK from the Mozilla FTP Server. Depending on how adventurous you feel, you can either choose the latest release or the nightly build from trunk. I get the impression that the Mozilla project does not really advertise the fact that there is a recent version of the ActiveX control inside the XULRunner SDK very much. I stumbled upon its existence via a comment by Mozilla Platform Evangelist Mark Finkle on this entry in Nick Bradburys blog. Grab the SDK, extract it somewhere, uninstall the old Mozilla control and register the new one in Windows. To do this, open a command line window, navigate to the directory where you put the SDK, change to the "bin" directory and enter "regsvr32 mozctlx.dll". To get an idea about how much better the recent Gecko engine is compared to the old one, we can look at the ACID browser test.
old and busted
latest'n'greatest
Pretty sweet, innit?
Subscribe to:
Posts (Atom)






