Twitterified Client now fully Open-Source

Twitterified Client - Open Sourced!Well, this title says it all!

Find the announcement here.

And, of course, find the source code at GitHub: here.

If you enjoyed this post, make sure you subscribe to my RSS feed!


Flex: A hack to give instant focus to a form field

Day 79 - f o c u sI was working on a “quick add” tool for Toodledo — which I am liking more and more — and I assumed that my biggest challenge would be wiring a hotkey to my application — a can of worms in its own right. My mini-application’s main requirement is to be fast and not get in the user’s way. Part of this requirement is that when the “Add” window is displayed, the “Task description” field should be automatically selected, an inviting cursor happily blinking. Easier said than done!

Flex will let me display the native window and select the description field using setFocus() but no cursor for me. I googled the issue and found that when a Flex application runs in a browser, the solution is to give the application itself focus using Javascript.
But I’m not running in a browser!

And now, my silly workaround: since displaying an other panel container — such as an alert message — and dismissing it will return proper focus to my original field, I will simply “display” an invisible container and immediately get rid of it.
There is no guarantee that, on the odd instance, a race condition will not occur.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.voilaweb.addtdtask
{
    import flash.events.TimerEvent;
 
    public class Main
    {
        import mx.core.Application;
        import mx.containers.TitleWindow;
        import flash.display.DisplayObject;
        import flash.utils.Timer;
        import flash.events.TimerEvent;
        import mx.managers.PopUpManager;
 
        private var _tw:TitleWindow;
 
        public function Main()
        {
            Application.application.task_description.setFocus();
            Application.application.task_description.drawFocus(true);
            _tw = new TitleWindow();
            _tw.width = _tw.height = 0;
            var t:Timer = new Timer(100, 1);
            t.addEventListener(TimerEvent.TIMER, showCursor);
            t.start();
            mx.managers.PopUpManager.addPopUp(_tw, Application.application as DisplayObject, true);
        }
 
        public function showCursor(event:TimerEvent):void
        {
            mx.managers.PopUpManager.removePopUp(_tw);
        }
    }
}

If you enjoyed this post, make sure you subscribe to my RSS feed!


Adobe Alchemy: Passing a ByteArray from Flex to C++

fxlogoI started playing with Alchemy a couple days ago, with the intent of finally writing OpensIFRr3 — you know, the one working with sIFR3. Since the previous release was in Java, I was planning on creating this one in Flex.
I am not quite there yet, due to the nightmare that porting libxml would be. But I’ve learnt that reading Alchemy’s currently meager documentation could have saved me a few headaches. For instance, do not assume that you will find here some of the magic that other “gateways” offer. More specifically, do not assume that a ByteArray will automatically be passed from ActionScript to C++ as a char *.
Nope, it’s passed as a ByteArray structure, with all sorts of fun overhead that will drive you crazy if you assume a “StringType” object.

If this is gibberish to you, keep reading:
Yes, Alchemy is verbose.
No, you should not fear its complexity.

Here is how I read a compressed flash file in a ByteArray, in Flex, then pass it to C++ for parsing. Obviously it is the start of OpensIFRr3 (jump to the end of the listing if you are only curious about seeing an Alchemy call in action):

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package
{
	import flash.display.Sprite;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.ByteArray;
	import flash.utils.CompressionAlgorithm;
	import cmodule.swfmill.CLibInit; // My C code
 
	public class EchoTest extends Sprite
	{
		private var length:uint;
		private var compressed:Boolean;
		private var version:uint
		private var sig:String;
		private var data:ByteArray;
 
		private function main_fread_uint(fs:FileStream):uint
		{
			return fs.readUnsignedByte();	
		}
 
		private function main_fread_char(fs:FileStream):String
		{
			return String.fromCharCode(fs.readUnsignedByte());	
		}
 
		private function main_fread_str(fs:FileStream, offset:uint, length:uint):String
		{
			var ba:ByteArray = new ByteArray();
			fs.readBytes(ba, offset, length);
			return ba.toString();
		}
 
		private function main_load(name:String):Boolean
		{
			try
			{
				var file:File = new File("/Users/Chris/Projects/sIFRr3/rockwell.swf");
				var fs:FileStream = new FileStream();
				if(!file.exists)
				{
					trace("File does not exist");
					return false;
				}
				fs.open(file, FileMode.READ);
				sig = main_fread_str(fs, 0, 3);
				version = main_fread_uint(fs);
				if(sig != "CWS" && sig != "FWS")
				{
					trace("ERROR: input is no SWF");
					return false;
				}
				length = main_fread_uint(fs);
				length += main_fread_uint(fs) << 8;
				length += main_fread_uint(fs) << 16;
				length += main_fread_uint(fs) << 24;
				length -= 8;
				compressed = sig.charAt(0) == 'C';
				if(length != file.size - 8)
				{
					if( length > file.size - 8 && !compressed)
					{
						trace("WARNING: size specified in SWF (" + length + ") != file.size (" + file.size + "), using filesize-8.");
						length = file.size - 8;
					}
				}
				data = new ByteArray();
				fs.readBytes(data);
				if(compressed)
				{
					data.uncompress(CompressionAlgorithm.ZLIB);					
				}
				fs.close();
			}
			catch(e:Error)
			{
				trace("Error: " + e.message);
				return false;
			}				
			return true;
		}
 
		public function SWFTest()
		{
			var loader:CLibInit = new CLibInit;
			var lib:Object = loader.init();
 
			if(main_load("/Users/Chris/Projects/sIFRr3/rockwell.swf"))
			{
				trace("ASLength = " + data.length);
				lib.swfmill_swf2xml(version, data.length, data); // Exported method
			}
		}
	}
}

The magic happens here: lib.swfmill_swf2xml(…)

Let’s have a look a this little guy in his natural habitat — in swmill.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main()
{
	//define the methods exposed to ActionScript
	//typed as an ActionScript Function instance
	AS3_Val swfmill_swf2xml_method = AS3_Function( NULL, swfmill_swf2xml);
 
	// construct an object that holds references to the functions
	AS3_Val result = AS3_Object( "swfmill_swf2xml: AS3ValType", swfmill_swf2xml_method );
 
	// Release
	AS3_Release( swfmill_swf2xml_method );
 
	// notify that we initialized -- THIS DOES NOT RETURN!
	AS3_LibInit( result );
 
	// should never get here!
	return 0;
}

So, main() will be invoked when the program instance comes up and Flex will be informed that there is a method of interest (swfmill_swf2xml).

Digression:
I know, this is a bit hard to read, but so far most of Alchemy is. After all, the whole bridge relies on the C++ code being compiled to bytecode that is linked indirectly against Flash libraries, the result being turned into ActionScript that will then be compiled to asm-asc. If you look at the work files, you will see ActionScript mixed directly with what looks like assembly code. To quote Adobe, the end-result is “basically one huge finite state machine.”
This means that your C++ code will run “somewhat” in parallel with ActionScript. Beware!

How does this swfmill_swf2xml method manage with our ByteArray? After all, C++ doesn’t know anything about that type of structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static AS3_Val swfmill_swf2xml( void *self, AS3_Val args )
{
	bool success = false;
	unsigned int e_version;
	unsigned int e_length;
	AS3_Val		 e_data = AS3_Undefined();
 
	AS3_ArrayValue( args, "IntType, IntType, AS3ValType", &e_version, &e_length, &e_data );
	fprintf(stderr, "Clength: %u", e_length);
	unsigned char * data = (unsigned char *)malloc(sizeof(unsigned char) * (e_length + 1));
	for(int runner=0; runner < e_length; runner++)
	{
		*(data + runner) = AS3_IntValue(AS3_Get(e_data, AS3_Int(runner))) & 0xFF;
	}
 
	...
 
	return AS3_Int(success ? 0 : -1);
}

Did you see it? Did you see the awesomely hackish way we have to retrieve our arguments using AS3_ArrayValue?
Use of AS3ValType means “You, C++, will store this object as a blob because you do not know better. However, if you use the correct AS3 calls, you will be able to ask ActionScript to manipulate this object on your behalf.”
Therefore, to convert that object, which, I happen to know, is a ByteArray, I will ask ActionScript to read its content and I will store it in an array of chars. Note that I am masking the returned value with 0xFF, because there is no function to read a byte, therefore I need to get rid of the irrelevant bits after reading an integer. And, yes, AS3_IntValue() is a function that maps AS3 integers to C integers. More info here.

If you read the page I just provided a link to, you may wonder why I am passing AS3_Int(runner) to AS3_Get(…)
After all, the second parameter is supposed to be a method name. And this is the case – in a way. By passing an integer, I am asking ActionScript to use the ‘[]‘ form to access our ByteArray.
Note that I had no joy trying to use readUnsignedByte() instead. That’s to be expected; after all, Alchemy is far from stable — or complete.

But it’s certainly promising.

If you enjoyed this post, make sure you subscribe to my RSS feed!


Flex: Render your tree nodes with a line through

So…how hard can it be, right? After all, Flex offers to change your text style — using setStyle — to bold, italics, underlined…oh, but no strikethrough.

But I really needed that for my tree nodes, so here is how I did it.

First, my tree, as defined in a MXML file — well, a simplified version:

?View Code ACTIONSCRIPT
1
2
3
4
<mx:Tree id="outlinerTree" 
    height="100%" width="100%"
    itemRenderer="com.voilaweb.tfd.OutlinerRenderer"
    creationComplete="main_initOutliner();"/>

As you can see, I explicitely reference my renderer. Let’s have a look at OutlinerRenderer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.voilaweb.tfd
{
	import mx.collections.*;
	import mx.controls.treeClasses.*;
 
	public class OutlinerRenderer extends TreeItemRenderer
	{
		override public function set data(value:Object):void
		{
			super.data = value;
		}
 
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			var startx:Number = data ? TreeListData(listData).indent : 0;
			if(disclosureIcon)
				startx += disclosureIcon.measuredWidth;
			if(icon)
				startx += icon.measuredWidth;
			graphics.clear();
			graphics.lineStyle(1, getStyle("color"));
			var y:Number = label.y + label.measuredHeight / 2;
			graphics.moveTo(startx, y);
			graphics.lineTo(startx + label.measuredWidth, y);
		}
	}
}

In a nuthsell, I let the parent paint the label, then add my own line to my graphic context. I use the label’s measurements to size my line.

For those of you new to ActionScript but familiar with Java: yes, it’s just like overriding the paint() method. In fact, ActionScript offers so many similarities with Java — renderers, editors, listeners, annotations… — that if you’ve ever written front-end code, you should give AS3 a try.

If you enjoyed this post, make sure you subscribe to my RSS feed!


Flex: How I worked around MOUSE_OUT’s inefficiencies

In Twitterified Client v1, one of the features I really wanted available right off the bat, was that when a user moves their mouse pointer over a twitterer’s avatar, an overlay image is displayed. This image is a menu of sort, allowing the user to send a direct message, a reply or access a more advanced menu. Of course, when the mouse pointer exists the avatar’s region, the overlay image needs to quietly go. Being the Flex newbie that I am, I thought that a straightforward implementation would work:

?View Code ACTIONSCRIPT
_imageOverlayPanel.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);

In onMouseOut(), I would detach _imageOverLayPanel, remove the listener, and move foward. Unfortunately, when moving my mouse around at a reasonably fast speed, I found out that the overlay panel did not go away, and after a while I had several avatars “polluted” by that overlay panel. I started “tracing” the MOUSE_OUT event and found out that, at least in AIR applications, there is no guarantee that such an event will be triggered. Ouch.

I experimented with, instead, using a timer that would kick in every 500ms and check whether the mouse pointer was still in the avatar’s region. It worked very well but felt a bit clunky and there was a risk of race condition with mouse events.

In the end, I settled for an approach using MOUSE_MOVE, which feels “cleaner” and works almost as well as the timer method:

?View Code ACTIONSCRIPT
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMovePossiblyOut);
?View Code ACTIONSCRIPT
private function onMouseMovePossiblyOut(event:MouseEvent):void
{
    if(!_imageOverlayPanel.parent) return;
    var pt:Point = new Point(event.stageX, event.stageY);
    pt = _imageOverlayPanel.globalToLocal(pt);
    if(pt.x < 0 || pt.y < 0 || pt.x > _imageOverlayPanel.width || pt.y > _imageOverlayPanel.height)
    {
        _imageOverlayPanel.parent.addEventListener(MouseEvent.MOUSE_OVER, onImageMouseOver); // innerWrapper resumes listening to mouse_over events
        _imageOverlayPanel.parent.removeChild(_imageOverlayPanel); // overlay panel is detached from innerWrapper
    }
}

Note that the trick, here, is that I add my mouse listener to the stage rather than the image overlay itself. This allows me to receive mouse events that happen outside the overlay’s region; they are the ones I am interested in.

If you enjoyed this post, make sure you subscribe to my RSS feed!