Sunday, September 5, 2010

Migrating an application to AIR, and Flex unable to resolve resource bundle

Not sure why but migrating from a web application to AIR I ran into a few issues

Flex AIR unable to resolve resource bundle

To do this, simply right click your project > properties > Flex compiler and add "-source-path ../locale/{locale}" to the Additional compiler arguments text field.  Hat tip.

Specifying startup height and width
Code Snippet
  1. <!-- The window's initial width. Optional. -->
  2. <!-- <width></width> -->
  3. <width>1320</width>
  4. <!-- The window's initial height. Optional. -->
  5. <height>920</height>
Asset references

Shame on me for not refering to images, fonts, etc with an absolute path (relative to the flex src directory), e.g…

Code Snippet
  1. <mx:Image source="@Embed(source='/../assets/some_icon.png')"/>

This also has the happy side effect of making the paths of these assets more sensible than the alternative crazy relative path approach, e.g. the above line replaced a line similar to the following…

Code Snippet
  1. <mx:Image
  2. source="@Embed(source='../../../../../../../assets/some_icon

Thursday, January 21, 2010

ItemEditEnd broken for a dateField column

Here is a test case where the itemEditEnd does not fire even though everything seems to be hooked up.  This caused me some pain, until I took a look at the DataGrid.as source code and stepped through it in the debugger.  Even though rendererIsEditor="true",  in red below you still need to set itemEditor="mx.controls.DateField".   Why I needed this event is another story.   Hope that someone finds this useful.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application layout="horizontal"
    xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            import mx.events.DataGridEvent;
            import mx.collections.ArrayCollection;
            [Bindable] private var _data:ArrayCollection = new ArrayCollection([{id: "0", name: "zero"}, {id: "1", name: "one"}, {id: "2", name: "two"}, {id: "3", name: "three"}]);

            private function onItemEditEnd(event:DataGridEvent):void {
                trace("onItemEdit");
            }
        ]]>
    </mx:Script>
    <mx:DataGrid alternatingItemColors="[#F7F7F7, #E9E9E9]"
        borderColor="#DFD9D9"
        borderStyle="solid"
        dataProvider="{_data}"
        editable="true"
        height="100%"
        id="dgProducts"
        itemEditEnd="onItemEditEnd(event)"
        sortableColumns="false"
        themeColor="#67B5E6"
        variableRowHeight="true"
        width="100%"
        wordWrap="true">
        <mx:columns>
            <mx:DataGridColumn dataField="name"
                editable="false"
                headerText="Product"
                width="60"/>
            <mx:DataGridColumn dataField="none"
                editable="true"
                headerText="License Key"
                width="60">
            </mx:DataGridColumn>
            <mx:DataGridColumn dataField="license_expiration_date"
                editorDataField="selectedDate"
                headerText="Expiration Date"
                itemRenderer="mx.controls.DateField"
                rendererIsEditor="true"
                width="60">
            </mx:DataGridColumn>
        </mx:columns>
    </mx:DataGrid>
</mx:Application>

Friday, January 15, 2010

Flex gantt chart project plan v.01

Flex gantt based on the sample code from ilog / elixir. The demo had a bunch of pitfalls, which I’ve tried to overcome

  1. underling data model (collections) are not XML based. Seems to me that adobe relies on the xml data for alot of their proof of concept.
  2. nested set library from pear php to persist the tree in the back end. There was a bit of heavy lifting and I ended up hacking the pear NestedSet.php
  3. support for multiple trees (forest) in the nested set
  4. ilog has bug in the constraints where the constraints do not refresh
  5. highlighting the selected constraint
  6. circular reference cycles in the directed graph using the tarjan algorithm from a previous post
  7. When a parent constraint changes the move.
  8. Database is sync’d for most interactions
  9. supports a templatization, where the dates get bumped to the the current date + 7 for each template, so you can ruse a project.

After a bunch of thought early on, I decided to try to map nodes, constraints and resources with associated reservations to rows in the database. My thinking was to build it this way for a few reasons:

  1. I didn’t want to have a corrupt xml and then have to go through the pain to of debugging it
  2. support for future concurrency. While it’s not in my use case as of now, my thinking was that XML blobs in the database do not lend themselves to collaboration. Of course if I was to go with real time concurrency, I would need to think about a COCOMO option
  3. templates and copying
  4. I didn’t want to get in the situation where a user made bunch of edits and then app failed, or the persistence failed or the javascript saying “you need to save” because you just navigated away.

The real drawback to going away from xml is that there would be a bunch more heavy lifting to import/export to MS Project Plan

As you can tell, I may have chosen poorly.

Wednesday, December 30, 2009

Delete is broken in Hierarchical Data

I have a AdvancedDataGrid where I am deleting the children. When all the children are gone the folder icon does not change. If I walk the collection and then force the children = null, then the folder changes to a sheet (leaf) icon, as you would expect by looking at the HierachicalCollection class. However when I go to add a new leaf to the summary folder the folder reappears and the down arrow opens indicating that it's got a child, but the child does not render.

Repro using this is swf.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application layout="horizontal"
viewSourceURL="srcview/index.html"
xmlns:mx="http://www.adobe.com/2006/mxml%22>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.IHierarchicalCollectionView;
import mx.collections.ICollectionView;
import mx.collections.HierarchicalData;
[Bindable] public var tasks:HierarchicalData = new HierarchicalData([{id: "T1", name: "Task #1", startTime: "1/14/2008 8:0:0", endTime: "1/28/2008 17:0:0"}, {id: "T2", name: "Task #2", startTime: "1/29/2008 8:0:0", endTime: "2/5/2008 17:0:0"},
{id: "T3", name: "Summary Task #1", startTime: "2/5/2008 8:0:0", endTime: "2/17/2008 17:0:0", children: [{id: "T4", name: "Task #3", startTime: "2/5/2008 8:0:0", endTime: "2/12/2008 17:0:0"},
{id: "T5", name: "Task #4", startTime: "2/13/2008 8:0:0", endTime: "2/17/2008 17:0:0"}]},
{id: "T6", name: "Milestone #1", milestone: "true", startTime: "2/17/2008 17:0:0", endTime: "2/17/2008 17:0:0"}]);

private function onDelete():void {
var task:Object = adGrid.selectedItem;
if(task != null) {
var _taskCollection:IHierarchicalCollectionView = IHierarchicalCollectionView(adGrid.dataProvider);
var parentTask:Object = _taskCollection.getParentItem(task);
_taskCollection.removeChild(parentTask, task);
//trace(parentTask.id + " has children = " + tasks.hasChildren(parentTask));
//this.cleanTaskCollection(tasks.source as Array);
_taskCollection.itemUpdated(parentTask);
//_taskCollection.refresh();
} else {
mx.controls.Alert.show("Please select a node");
}
}

private function cleanTaskCollection(collection:Array):void {
for each(var task:Object in collection) {
if(task.children && task.children.length == 0) {
// attempt to force item render to not show folders
task.children = null;
} else {
// recurse
cleanTaskCollection(task.children);
}
}
}
private var count:int = 0;

private function onAdd():void {
var task:Object = adGrid.selectedItem;
if(task != null) {
var child:Object = new Object;
child.id = count++;
child.name = "new node " + count;
if(task.children == null) {
task.children = new Array();
}
var _taskCollection:IHierarchicalCollectionView = IHierarchicalCollectionView(adGrid.dataProvider);
_taskCollection.addChild(task, child);
} else {
mx.controls.Alert.show("Please select a node");
}
}
]]>
</mx:Script>
<mx:AdvancedDataGrid dataProvider="{tasks}"
designViewDataType="tree"
id="adGrid">
</mx:AdvancedDataGrid>
<mx:Button click="onDelete()"
label="delete">
</mx:Button>
<mx:Button click="onAdd()"
label="add">
</mx:Button>
</mx:Application>

Monday, December 28, 2009

Finding circular dependencies in Flex/AS3

I was looking for cycles in a directed graph in a task chart implementation I was doing for a client. My implementation of the Tarjan algorithm is hard coded for a linkage of task objects linked by constraints. I’d be interested to find out if anyone else has dug into this algorithm and gotten it to work.

package model.task {
import ilog.gantt.TaskChart;


public class Tarjan {
private var index:int = 0;
private var stack:Array = new Array();
private var SCC:Array = new Array();
public var taskChart:ilog.gantt.TaskChart;

/**
* N.B. Don't forget to reset the *.index = -1 after checking
*
* DJH 12/24/2009 This will only work for task nodes connected by constraints. It does not traverse
* the entire task hierachical data
*
* I don't fully understand but all nodes are treated as loops. Look through the SCC
* array for child arrays which with more than 1 node and those the the cycles.
*
*/
public function tarjan(task:Object /* node */ /*, adjacencyList:Array*/ ):Array {
trace("tarjan():: push task " + task.id);
task.index = index;
task.lowLink = index;
index++;
stack.push(task);
var constraints:Array = taskChart.getFromConstraints(task);
// Collect the successors
if(constraints) {
for each(var c:Object in constraints) {
var toTask:Object = taskChart.getToTask(c);
if(toTask == null) {
// DJH 12/9/2009, fix the constraint does not chain to a successor, so ignore
continue;
}
if(toTask.index == -1) {
tarjan(toTask /*, adjacencyList*/ );
task.lowLink = Math.min(task.lowLink, toTask.lowLink);
} else if(stack.indexOf(toTask) >= 0) {
task.lowLink = Math.min(task.lowLink, toTask.index);
}
}
}
if(task.lowLink == task.index) {
var arr:Array = new Array();
do {
toTask = stack.pop();
arr.push(toTask);
} while(toTask != task)
SCC.push(arr);
}
return SCC;
}
}
}

Tuesday, June 23, 2009

Desktopless, headless, AIR on Linux Redhat

AIR installed fine on my RedHat RHEL 5.3 box, with the added bonus of it being a AMD-64 sytem, so a few moments of panic, but I was able to yum in the Gtk *.so. In the Gnome it’s easy enough from the desktop AdobeAIRInstaller.bin, which installs
Binaries to /opt/Adobe AIR/

This is where a little fun began, it turns out that you the application will not run and I get the following error: (./YourAIRApplication:14779): Gtk-WARNING **: cannot open display:

One way around this is to us Xvfb, a virtual frame buffer. For RedHat I was able to download the rpm and install. The bonus here is that xvfb-run is actually a script. Running the final command below works.

rpm -Uvh XFree86-Xvfb-3.3.2.3-25.i386.rpm
xvfb-run.sh
cd /opt/YourAIRApplication/bin
xvfb-run.sh ./YourAIRApplication

Attempting to run on a copied Adobe Air install: I couldn’t get Adobe Air to run from just the binaries, so I copied
/opt/Adobe\ Air to a clean box

I attempted to run ./opt/YourAIRApplication/bin/YourAIRApplication

And I get:
Error loading the runtime (libadobecertstore.so: cannot open shared object file: No such file or

Time to stop and go write some Flex code!

Friday, June 12, 2009


width="100%" height="100%">

/**
* bubbles the rollout event up to the lauching button
*/
private function onRollOut(event : MouseEvent) : void
{
this.dispatchEvent(event);
}
]]>

width="30" height="110"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
backgroundColor="#626262"
rollOut="onRollOut(event)"
>
styleName="progressBar"
borderColor="#BDBDBD"
showTrackHighlight="true"
themeColor="#AEDF4F"
y="5"
/>