Joining the Adobe Community Professionals for 2010

Formerly the Adobe Community Experts program, this is a collective of awesome individuals who exhibit expertise in various Adobe product areas from Acrobat to Photoshop, Flash, Cold Fusion, Flex, AIR and many, many more. Comprised of community leaders, this group has a worldwide presence and expounds the virtues of Adobe to the masses through the sharing of their time and expertise.

I’m truly honored to have been chosen as a member of this select group and look forward to expanding my community efforts throughout this new year. Having missed FlashCamp Brasil this past weekend, this revelation makes the trip to FITC Amsterdam that much more rewarding. See you there!!

GitX Tweaks: Commit View

So, after thinking about my most recent updates, I decided I wanted to change the way that the commit view looks but just slightly. This is my 1st pass on some things I’d like to have available via the toolbar.

Would also like to tweak the view selector buttons, revise remote selection and add toolbar buttons for all the contextual menu items, which aren’t many.

GitX Update: Remote Selector

Previously, I implemented toolbar buttons where each button was tied to a specific remote repository. This was/is fine if you’ll only be working with one or two hosts but becomes cumbersome when dealing with multiple hosts.

Enter the NSPopUpButton. By switching to this UI element I’m able to allow a user to select a remote from a configurable list of remote repositories. I like this UX slightly better than having a button for each remote and the functionality behind the scenes still reaches the same result.

Now with that out of the way, I can move on to some other items on my TODO list…:)

Introducing Opticon Runtime Debugger

ol_opticon_adm_banner_495x250

Opticon is a runtime debugging console useful for Flash/Flex developers. It’s simple to start using in your projects. To use, follow these steps after installing the application:

1. Launch the application
2. Click the Settings button
3. Click on the SWC icon to add the OpticonConnector to your project’s build path
4. Replace your trace statements with one of the following:

  • a) Opticon.log(‘message to display’)
  • b) Opticon.warning(‘warning to display’)
  • c) Opticon.error(‘error to display’)

Download available from:
Adobe AIR Marketplace
RIAForge

Some might ask why? This idea first appeared when AIR was Apollo and was born out of necessity (I always forget some trace statements during cleanup). Could I use global find and replace? Sure, when I’m developing with Eclipse-based IDEs but that’s not always the case.

Why not use one of the other great tools out there? After 2 years in the shadows, it’s ready to be shared with the world and, because I have a vision of where I’d like to take this so hopefully you’ll come along for the ride.

Where can I submit any bugs I find? I’ll have the bug reporting mechanism in place shortly. You’ll be notified via the application or you can subscribe to this post to be notified once that goes live.

My version doesn’t include the connector. What can I do? This happened as a result of a build error. Visit this link to download the connector Get Opticon Connector.

Double-headed Arrows with GraphicsUtil

I recently ran across this awesome utility class, GraphicsUtil, created by Noel Billig. It allows the ability to draw lines with an arrow on one end using the drawing API in AS3. This proved useful for a project I was working on. In addition to its default behavior however, I needed the utility class to provide the ability to draw lines with arrows on both ends. Well thanks to Noel’s clean code, I was able to easily modify the base class to do just that by adding the following static method:

/**
 * Draws a double-headed arrow. Pass in ArrowStyle
 * objects for both arrows to override the default settings.
 * @param graphics
 * @param start
 * @param end
 * @param startStyle
 * @param endStyle
 */
public static function drawArrows( graphics:Graphics,
        start:Point,end:Point,
        startStyle:Object=null,
        endStyle:Object=null ):void
{
	// variables used for arrow 1
	var startArrowStyle:ArrowStyle;
	var startHalfWidth:Number;
	var vect1:Point;
	var startNorm:Point;
	var start1:Point;
	var start2:Point;
	var end1:Point;
	var end2:Point;
	var startHeadPnt:Point;
	var startHeadPntNorm:Point;
	var startEdge1:Point;
	var startEdge2:Point;
	var startShaftCenter:Point;
	var startInter1:Point;
	var startInter2:Point;
	var startEdgeCenter:Point;
	var startEdgeNorm:Point;
	var startEdgeCntrl1:Point;
	var startEdgeCntrl2:Point;
 
	// variables used for arrow 2
	var endArrowStyle:ArrowStyle;
	var endHalfWidth:Number;
	var vect2:Point;
	var endNorm:Point;
	var end3:Point;
	var end4:Point;
	var start3:Point;
	var start4:Point;
	var endHeadPnt:Point;
	var endHeadPntNorm:Point;
	var endEdge1:Point;
	var endEdge2:Point;
	var endShaftCenter:Point;
	var endInter1:Point;
	var endInter2:Point;
	var endEdgeCenter:Point;
	var endEdgeNorm:Point;
	var endEdgeCntrl1:Point;
	var endEdgeCntrl2:Point;
 
	if (start.equals(end)) return;
 
	///////////////////////////////// start arrow config \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
	if (startStyle == null)
	{
		startArrowStyle = new ArrowStyle();
	}
	else if (startStyle is ArrowStyle)
	{
		startArrowStyle = startStyle as ArrowStyle;
	}
	else
	{
		startArrowStyle = new ArrowStyle( startStyle );
	}
 
	vect1 = end.subtract( start );
	startHalfWidth = (startArrowStyle.headWidth != -1) ? startArrowStyle.headWidth / 2 : startArrowStyle.headLength / 2;
 
	//Figure out the line start/end points
	startNorm = new Point( vect1.y, -vect1.x );
	startNorm.normalize( startArrowStyle.shaftThickness/ 2 );
	start1 = start.add( startNorm );
	start2 = start.subtract( startNorm );
	end1 = end.add( startNorm );
	end2 = end.subtract( startNorm );
 
	//figure out where the arrow head starts
	startHeadPnt = vect1.clone();
	startHeadPnt.normalize( startHeadPnt.length - startArrowStyle.headLength );
	startHeadPnt = startHeadPnt.add( start );
 
	//calculate the arrowhead corners
	startHeadPntNorm = startNorm.clone();
	startHeadPntNorm.normalize( startHalfWidth );
	startEdge1 = startHeadPnt.add( startHeadPntNorm );
	startEdge2 = startHeadPnt.subtract( startHeadPntNorm );
 
	//Figure out where the arrow connects the the shaft, then calc the intersections
	startShaftCenter = Point.interpolate( end, startHeadPnt, startArrowStyle.shaftPosition );
	startInter1 = GeomUtil.getLineIntersection( start1, end1, startShaftCenter, startEdge1 );
	startInter2 = GeomUtil.getLineIntersection( start2, end2, startShaftCenter, startEdge2 );
 
	//Figure out the control points
	startEdgeCenter = Point.interpolate( end, startHeadPnt, startArrowStyle.edgeControlPosition );
	startEdgeNorm = startNorm.clone();
	startEdgeNorm.normalize( startHalfWidth * startArrowStyle.edgeControlSize );
	startEdgeCntrl1 = startEdgeCenter.add( startEdgeNorm );
	startEdgeCntrl2 = startEdgeCenter.subtract( startEdgeNorm );
 
	///////////////////////////////// end arrow config \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
	if (endStyle == null)
	{
		endArrowStyle = new ArrowStyle();
	}
	else if (endStyle is ArrowStyle)
	{
		endArrowStyle = endStyle as ArrowStyle;
	}
	else
	{
		endArrowStyle = new ArrowStyle( endStyle );
	}
 
	vect2 = start.subtract( end );
	endHalfWidth = (endArrowStyle.headWidth != -1) ? endArrowStyle.headWidth / 2 : endArrowStyle.headLength / 2;
 
	//Figure out the line start/end points
	endNorm = new Point( vect2.y, -vect2.x );
	endNorm.normalize( endArrowStyle.shaftThickness / 2 );
	start3 = start.add( endNorm );
	start4 = start.subtract( endNorm );
	end3 = end.add( endNorm );
	end4 = end.subtract( endNorm );
 
	//figure out where the arrow head starts
	endHeadPnt = vect2.clone();
	endHeadPnt.normalize( endHeadPnt.length - endArrowStyle.headLength );
	endHeadPnt = endHeadPnt.add( end );
 
	//calculate the arrowhead corners
	endHeadPntNorm = endNorm.clone();
	endHeadPntNorm.normalize( endHalfWidth );
	endEdge1 = endHeadPnt.add( endHeadPntNorm );
	endEdge2 = endHeadPnt.subtract( endHeadPntNorm );
 
	//Figure out where the arrow connects the the shaft, then calc the intersections
	endShaftCenter = Point.interpolate( start, endHeadPnt, endArrowStyle.shaftPosition );
	endInter1 = GeomUtil.getLineIntersection( end3, start3, endShaftCenter, endEdge1 );
	endInter2 = GeomUtil.getLineIntersection( end4, start4, endShaftCenter, endEdge2 );
 
	//Figure out the control points
	endEdgeCenter = Point.interpolate( start, endHeadPnt, endArrowStyle.edgeControlPosition );
	endEdgeNorm = endNorm.clone();
	endEdgeNorm.normalize( endHalfWidth * endArrowStyle.edgeControlSize );
	endEdgeCntrl1 = endEdgeCenter.add( endEdgeNorm );
	endEdgeCntrl2 = endEdgeCenter.subtract( endEdgeNorm );
 
 
	///////////////////////////////// draw the graphics \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
	// draw 1st arrow
	graphics.moveTo( start1.x, start2.y );
	graphics.lineTo( startInter1.x, startInter1.y );
	graphics.lineTo( startEdge1.x, startEdge1.y );
	graphics.curveTo( startEdgeCntrl1.x, startEdgeCntrl1.y, end.x, end.y );
	graphics.curveTo( startEdgeCntrl2.x, startEdgeCntrl2.y, startEdge2.x, startEdge2.y );
	graphics.lineTo( startInter2.x, startInter2.y );
	graphics.lineTo( start2.x, start2.y );
	graphics.lineTo( start1.x, start1.y );
 
	// draw 2nd arrow
	graphics.moveTo( end3.x, end4.y );
	graphics.lineTo( endInter1.x, endInter1.y );
	graphics.lineTo( endEdge1.x, endEdge1.y );
	graphics.curveTo( endEdgeCntrl1.x, endEdgeCntrl1.y, start.x, start.y );
	graphics.curveTo( endEdgeCntrl2.x, endEdgeCntrl2.y, endEdge2.x, endEdge2.y );
	graphics.lineTo( endInter2.x, endInter2.y );
	graphics.lineTo( end4.x, end4.y );
	graphics.lineTo( end3.x, end3.y );
}

Those are the basics. It can be tweaked to taste, etc. Kudos to Noel for the such a useful class… :)

GitX: A git GUI for OSX

GitX is a git GUI made for OSX. It was created by Pieter de Bie and is a really solid tool for working with git on OSX. I’ve been using it for about 9 months now. I liked it so much when I first began using it that I immediately added the ability to perform remote pushes to Unfuddle.

I was pushing some commits earlier this morning and realized that I never made the changes to GitX public. It was always my goal to share the updates with the community but I always kept getting sidetracked. Well, today I forked the master project and even added GitHub integration.

olgx_github_screenshot

You’ll need the latest version of Xcode to compile from source. If you just want access to the customized application, you can download a ZIP of the forked project and you’ll find the application in /path/to/download/build/Debug/GitX.app. Hope others find this as useful as I have…

olgx_gitx_screenshot

Overlaying the Flex SDK with the AIR 2.0 SDK on OSX

Since I develop with multiple IDEs, I maintain the latest releases of the Flex and AIR SDKs in a centralized location outside of their default install locations. This made enabling the AIR 2.0 SDK simple for me as all I had to do was the following:

1. Duplicate my Flex 3 SDK path
2. Rename the duplicate to flex_sdk_3.x_air_sdk_2.x
3. Download the latest AIR 2.0 SDK (currently AIR20_mac_sdk_120209.tbz2)
4. Place the AIR SDK zip in the root of the new Flex SDK directory

/flex_sdk_3.x_air_sdk_2.x/AIR20_mac_sdk_120209.tbz2

5. Open up Terminal in the new Flex SDK directory and execute the following command:

tar -xjf AIR20_mac_sdk_120209.tbz2

6. Open Flex Builder and add the new SDK -> Flex Builder > Preferences > Installed Flex SDKs > Add

Now, when I want to create an AIR app that targets the 2.0 SDK all I have to do is

1. Update the project compiler settings to use the new SDK -> Project > Properties > Flex Compiler > Use a Specific SDK
2. Update the application descriptor file to use the AIR 2.0 namespace

<application xmlns="http://ns.adobe.com/air/application/2.0beta">

All of the above steps can be repeated for the Flex 4 SDK as well.

For those interested, these are the items that were updated and added to the SDK for 2.0:
/bin/adl
/bin/adt
/frameworks/libs/air
/frameworks/projects/air
/lib/nai
/lib/adt.jar
/runtimes/air
/samples
/templates/air
/AIR SDK Readme.txt
/SDK license.pdf

CAVEATS:

  1. There’s a new certificate process started in AIR 1.5.3 that may disrupt your workflow if you’re creating signed applications.
  2. If you want to utilize the new native process features, you’ll have to implement multi-platform builds (native installers for Win, Mac and Linux) and these must occur on the target platform (ie, can only create DMGs on Mac, etc)

Working with MySQL Doesn’t Have to Suck

That’s the tag line for Querious, an awesome MySQL database management tool from Araelium Group. It’s like MySQL Administrator combined with Sequel Pro.

Prior to Querious, I would have to use both of those other apps plus some CLI to manage all the data systems I’m responsible for. Now, with Querious I can use one GUI and resort to the CLI only when absolutely necessary. It has a lightweight feel like Sequel Pro plus you get the full administrative capabilities of MySQL Administrator + phpMyAdmin. If you have to import a massive SQL dump, either split it up or prepare for the app to crash (unless you modify max_allowed_packet).

All in all, Querious is a must-have for MySQL DBAs that work exclusively on a Mac and is definitely worth the $25 price tag…

Installing New Snow Leopard Server

During a super busy week this week the main server on the internal network died. This was bad, really bad. As the Open Directory Master, this machine was responsible for everyone being able to enjoy a smooth workflow. Promoting another server to OD Master just wasn’t an option as there were other critical services running on the now deceased machine. No, for this catastrophe we had to invest in something that we could expect to be more reliable under the constant strain.

Pre-Installation

Pre-Installation

Surprisingly, the New Mac Mini Server from Apple couldn’t have been released at a better time. With a little under 12 hours until the next day of business, we would have to acquire a server, setup and restore the system data from our latest backup. Based on those requirements, I knew that only one box could rise to the occasion.

So, after receiving purchase approval, I made my way to the nearest Apple Store to snag one of those precious jewels. I knew things would work out too when I made it to Santa Monica and lucked up on a parking space about 40 feet from the Apple Store entrance.

Once in the store, I didn’t waste anytime. I needed to get my server and split. Of course I ended up browsing and didn’t leave until about 20 minutes later. But, I had our new server in hand ready to be welcomed into the fold.

Once I made it back to the office around 9pm, I had to prepare the rack for a new family member. Finally, we could have all of our servers consolidated in 1 area instead of scattered around thanks to the form factor of the Mac Mini. It really takes up very minimal real estate and makes me wonder if I’ll ever deploy Xserve now that the Mini is a viable option. The cost-to-value ratio here is excellent. I mean you get a terabyte server with 4GB of RAM and an unlimited client license to Snow Leopard Server for around $1200 (including Apple Care). To do the same with a new Xserve could easily set you back $3000+. It might not work for a video shop but at these prices it’s a no brainer for powering an expanding network.

Anyways, it’s now time to put this puppy into action. So I roll up my sleeves and get to crackin’.

The Arrival

The Arrival

The box that it comes in adds an additional 1/3 to the dimensions. So you know the footprint on this bad boy is going to be small.

Installation Preparation

Installation Preparation

After inspecting the package contents, it was time to get to work. About 9 hours later, the new machine was installed into its new home ready to provide core and directory services to the team with overall workflow only slightly bruised…:D

Home, Sweet Home!

Home, Sweet Home!

AS3: LinearGradientUtil Class

Once again, I found myself needing a utility class that would let me stop writing the same code block over and over again. And so, the LinearGradientUtil class was born.

Previously, I’d have something like the following in a view class:

/**
 * Draws the gradient background for
 * this component on initialize and resize.
 */
private function _createGradientBackground():void
{
   var fillType:String      = GradientType.LINEAR;
   var colors:Array         = [ 0xe9e9e9, 0xc7c7c7 ];
   var alphas:Array         = [ 1, 1 ];
   var ratios:Array         = [ 0, 255 ];
   var matrix:Matrix        = new Matrix();
       matrix.createGradientBox( width, 34, Math.PI/2, 0, 0 );
   var spreadMethod:String 	= SpreadMethod.PAD;
   var g:Graphics           = this.graphics;
       g.clear();
       g.beginGradientFill( fillType, colors, alphas, ratios, matrix, spreadMethod );
       g.drawRect( 0, 0, width, height );
       g.endFill();
}

This would be called after INITIALIZE and RESIZE events. Writing those lines over and over again prompted me to devise a more elegant solution:

package labs.otuome.fl.graphics
{
	import flash.display.GradientType;
	import flash.display.Graphics;
	import flash.display.SpreadMethod;
	import flash.display.Sprite;
	import flash.geom.Matrix;
 
	/**
	 * Util class to create linear gradient fills.
	 * @author Hasan Otuome
	 */
	public class LinearGradientUtil
	{
		///////////////////////////////////////////////////////////////////////////////////////////////
		// PRIVATE PROPERTIES
		///////////////////////////////////////////////////////////////////////////////////////////////
 
		///////////////////////////////////////////////////////////////////////////////////////////////
		// PUBLIC PROPERTIES
		///////////////////////////////////////////////////////////////////////////////////////////////
		public static const HORIZONTAL:String 		= 'horizontalLinearGradient';
		public static const VERTICAL:String 		= 'verticalLinearGradient';
		///////////////////////////////////////////////////////////////////////////////////////////////
		// PUBLIC METHODS
		///////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 * Draws the linear gradient to the .graphics
		 * property of the specified component. Defaults
		 * to a Web 2.0 style gray gradient.
		 *
		 * @param target The display object to apply the gradient to.
		 * @param size For a vertical gradient this is the
		 * height of the gradient. For a horizontal gradient,
		 * this is the width of the gradient.
		 * @param primaryColor The top or left-most color depending on orientation.
		 * @param secondaryColor The bottom or right-most color depending on orientation.
		 * @param primaryAlpha The top or left-most color depending on orientation.
		 * @param secondaryAlpha The bottom or right-most alpha depending on orientation.
		 * @param primaryRatio The ratio of the top or left-most color depending on orientation.
		 * @param secondaryRatio The ratio of the bottom or right-most color depending on orientation.
		 * @param orientation either horizontal or vertical. defaults to vertical.
		 * @param invert Toggle that switches what the method considers primary and secondary.
		 */
		public static function createGradientBackground( target:Sprite,
						size:uint=34,
						cornerRadius:uint=0,
						primaryColor:uint=0xe9e9e9,
						secondaryColor:uint=0xc7c7c7,
						primaryAlpha:uint=1,
						secondaryAlpha:uint=1,
						primaryRatio:uint=0,
						secondaryRatio:uint=255,
						orientation:String=LinearGradientUtil.HORIZONTAL,
						invert:Boolean=false ):void
		{
			var fillType:String;
			var colors:Array;
			var alphas:Array;
			var ratios:Array;
			var matrix:Matrix;
			var spreadMethod:String;
			var g:Graphics;
 
			fillType 			= GradientType.LINEAR;
			colors 				= _createColorsArray( primaryColor, secondaryColor, invert );
			alphas 				= _createAlphasArray( primaryAlpha, secondaryAlpha, invert );
			ratios 				= _createRatiosArray( primaryRatio, secondaryRatio, invert );
			matrix 				= new Matrix();
 
			switch (orientation)
			{
				case LinearGradientUtil.HORIZONTAL:
					matrix.createGradientBox( size, target.height, Math.PI/2, 0, 0 );
					break;
				case LinearGradientUtil.VERTICAL:
					matrix.createGradientBox( target.width, size, Math.PI/2, 0, 0 );
					break;
			}
 
			spreadMethod 		= SpreadMethod.PAD;
 
			g 					= target.graphics;
			g.clear();
			g.beginGradientFill( fillType, colors, alphas, ratios, matrix, spreadMethod );
 
			if (cornerRadius > 0)
			{
				g.drawRoundRect( 0, 0, target.width, target.height, cornerRadius );
			}
			else
			{
				g.drawRect( 0, 0, target.width, target.height );
			}
 
			g.endFill();
		}
		///////////////////////////////////////////////////////////////////////////////////////////////
		// OVERRIDES
		///////////////////////////////////////////////////////////////////////////////////////////////
 
		///////////////////////////////////////////////////////////////////////////////////////////////
		// PRIVATE METHODS
		///////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 * Creates the colors array required by the main method.
		 * @param colorOne
		 * @param colorTwo
		 * @param inverted
		 * @return The gradient colors array.
		 */
		private static function _createColorsArray( colorOne:uint,
						colorTwo:uint,
						inverted:Boolean=false ):Array
		{
			var colors:Array;
			if (!inverted)
			{
				colors = [ colorOne, colorTwo ];
			}
			else
			{
				colors = [ colorTwo, colorOne ];
			}
			return colors;
		}
		/**
		 * Creates the alphas array required by the main method.
		 * @param colorOneAlpha
		 * @param colorTwoAlpha
		 * @param inverted
		 * @return The gradient alphas array.
		 */
		private static function _createAlphasArray( colorOneAlpha:uint,
						colorTwoAlpha:uint,
						inverted:Boolean=false ):Array
		{
			var alphas:Array;
			if (!inverted)
			{
				alphas = [ colorOneAlpha, colorTwoAlpha ];
			}
			else
			{
				alphas = [ colorTwoAlpha, colorOneAlpha ];
			}
			return alphas;
		}
		/**
		 * Creates the ratios array required by the main method.
		 * @param colorOneRatio
		 * @param colorTwoRatio
		 * @param inverted
		 * @return The gradient ratios array.
		 */
		private static function _createRatiosArray( colorOneRatio:uint,
						colorTwoRatio:uint,
						inverted:Boolean=false ):Array
		{
			var ratios:Array;
			if (!inverted)
			{
				ratios = [ colorOneRatio, colorTwoRatio ];
			}
			else
			{
				ratios = [ colorTwoRatio, colorOneRatio ];
			}
			return ratios;
		}
	}
}

Now, I can just issue the command like so:

/**
 * Draws the gradient background for
 * this component on initialize and resize.
 */
private function _createGradientBackground():void
{
//	LinearGradientUtil.createGradientBackground( this, this.height, getStyle('cornerRadius'), 0xfdfeff, 0xe9e9e9 ); // changing color scheme
//	LinearGradientUtil.createGradientBackground( this ); // using the default color scheme
}

Less code + rich functionality = happy developer!!

:D

Get Adobe Flash playerPlugin by wpburn.com wordpress themes