

Today I took the Squeak 3.9 development image for a spin and decided make the switch from Squeak 3.7. This made me wonder: how do I switch images? I've made customizations to my old Squeak world and naturally I want to bring all of that with me. (I'm not a standard-environment kind of guy by any means.)



My overall experience with Smalltalk has been that things have to be done differently in an image-based environment. To my surprise most things I've expected to be awkward and difficult have turned out to be comfortable and easy. SqueakMap and its package loader, Monticello, and SqueakSource have all been breaths of fresh air. Unfortunately, I haven't found enough nice techniques for transporting customizations between images so in this case I've had to improvise.



Transporting my own code (SawfishCompat and PacketStudio) was a snap: I just used Monticello to checkout the latest versions from SqueakSource. Transporting my data is proving much more difficult: I haven't found a nice way to transport small things like my background colour and font choices or big things like my keyboard layout.



Keyboard layout is a "big thing" because it took me a maddeningly time long to click everything into the KeyBinder application that I downloaded from SqueakMap. And so today's adventure with Squeak is all about configuring global key bindings.



As an Emacs user I expect defining a global key binding to be roughly as simple as typing:

(global-set-key "\C-cs" 'show-outline-structure)

whereas with KeyBinder I actually click around and fill in a form like this each time:





When I submit this form the code is then squirreled away in one of KeyBinder's data structures and out of my reach. So how can I transport this? First I clicked around in the halo menu and found the command save morph in file which actually did produce a KeyBinder.morph on disk. I crossed my fingers and loaded this into the new image via the file browser and eureka! There's the comforting little keyboard icon of the KeyBinder morph. But alas, the morph didn't respond to mouse clicks as it had in the old image. Somehow it seemed to have lost its essense in transportation. Drat!



So next I dug into the KeyBinder application and found it pretty incomprehensible. The program is nearly a thousand lines of code in five classes (KeyBinder, KeyBinderInstaller, KeyBinding, KeyBindingConfigMorph, WindowSwitcherMorph) made up of lots of itty-bitty methods that feel like decoys to prevent me from finding how it actually works. The only useful thing I learned from this escapade was how to count the lines in a Squeak program:

((PackageInfo named: 'KeyBinder') classes collect: #linesOfCode) sum => 819

So now it's sunday night and I want a hack to blog about so I decide to write my own KeyBinder. Mine has the simple anatomy that an Emacs guy would expect (a key->function dictionary and a keypress hook), includes my bindings in the source (so I can easily transport them between images), and inherits from SimpleSwitchMorph so that I can toggle it off using the mouse if it breaks keyboard input somehow :-).



Hm.. how to present a Squeak program on a blog? This should be easy, since the volume of code can fit comfortably on the screen, but I've not managed to make a screenshot-worthy setup. There's a copy html menu item that will HTML'ize individual methods but that doesn't seem to play nicely with my stylesheet. I'll file this away as a problem for later and just post a tidied up change set:

SimpleSwitchMorph subclass: #SimpleKeyBinder instanceVariableNames: 'bindings' classVariableNames: '' poolDictionaries: '' category: 'SimpleKeyBinder' "Simple morph for adding global keybindings. The bindings are defined in initializeBindings and clicking the morph toggles active/inactive." initialize super initializeWithLabel: 'SimpleKeyBinder'. self activeHand addAdditionalKeyboardFocus: self. self initializeBindings. initializeBindings bindings := Dictionary newFrom: { '<Opt-m>' -> [ SystemWindow topWindowDo: [:w | w expandBoxHit]]. '<Opt-p>' -> [ SystemWindow moveViewport: #up ]. '<Opt-n>' -> [ SystemWindow moveViewport: #down ]. '<Opt-b>' -> [ SystemWindow moveViewport: #left ]. '<Opt-f>' -> [ SystemWindow moveViewport: #right ]. } handlesKeyboard: event ^self isOn and: [event isKeystroke and: [bindings includesKey: event keyString]]. keyStroke: event (bindings at: event keyString) value



So in the end I'm happy that I solved my key bindings problem. I'm curious to know if there actually is a good way to transport objects between images, because it would be a bummer if direct manipulation is a dead-end and immortality belongs only to code. For now I'm mystified but optimistic.



(This post can reasonably be seen as a cry for help to real Smalltalk hackers.)