Thursday, April 19, 2012

Google Chrome in Flash Builder on Mac

Wanted to share and easy way to add Google Crome as a browser in Flash Builder.  Just open finder and go to your Applications folder.  Right-Click the Google Crome icon and select "Show Package Contents".  Once opened click on, open the MacOs folder and theres your location. Copy that and go back to Flash Builder and go to Preferences -> General -> Web Browsers and click "New".  From there add a name such as Chrome and then paste in the location. Enjoy!

Here's my result:

/Applications/Google Chrome.app/Contents/MacOS/Google Chrome

Wednesday, February 22, 2012

Spark Spreadsheet Datagrid, locked columns

I was recently tasked with providing spreadsheet like functionality in a Flex 4 application where you can lock columns on the left side of the grid to keep them in view while scrolling as well as allowing single cell editing while highlighting entire rows on mouse overs.  This is somewhat the same as you would expect in an Excel spreadsheet.  Anyway, I was able to put his together really quick to satisfy our needs based on a blog I came across giving me the idea to put 2 grids side by side. Here's a link LockedGrid.
Anyway, I needed a lot more functionality so I expanded on this idea and created a custom component.  It's pretty rough and not everything is wired in yet but you can feel free to use it and call me on any hiccups.  I put this together really quickly but wanted to share.


	[SkinState("lockedMode")]
	[SkinState("nonLockedMode")]

	[Event(name = "gridDoubleClick", type = "spark.events.GridEvent")]
	[Event(name = "gridClick", type = "spark.events.GridEvent")]
	[Event(name = "gridItemEditorSessionStarting", type = "spark.events.GridItemEditorEvent")]
	[Event(name = "gridItemEditorSessionStart", type = "spark.events.GridItemEditorEvent")]
	[Event(name = "gridItemEditorSessionSave", type = "spark.events.GridItemEditorEvent")]
	[Event(name = "gridItemEditorSessionCancel", type = "spark.events.GridItemEditorEvent")]
	[Event(name = "selectionChange", type = "spark.events.GridSelectionEvent")]


	public class SparkLockedColumnGrid extends SkinnableComponent
	{
		public static const LOCKEDMODE:String = "lockedMode";
		public static const NONLOCKEDMODE:String = "nonLockedMode";
		private static const MIN_ROWHEIGHT:Number = 30;

		[SkinPart(required = "true")]
		public var lockedGrid:DataGrid;

		[SkinPart(required = "true")]
		public var dataGrid:DataGrid;

		private var _columnsChanged:Boolean;
		private var _gridsLocked:Boolean;
		private var _dataProviderChanged:Boolean;
		private var _selectionModeChanged:Boolean;
		private var lockedCols:ArrayList = new ArrayList();
		private var gridCols:ArrayList = new ArrayList();
		private var _rowHeightChanged:Boolean;
		private var _selectedCellsChanged:Boolean;
		private var _selectedItemChanged:Boolean;
		private var _selectedIndexChanged:Boolean;
		private var _selectedItemsChanged:Boolean;
		private var _selectedIndicesChanged:Boolean;
		private var _selectedCellChanged:Boolean;
		private var _doubleClickEnabledChanged:Boolean;
		private var _spreadsheetModeChanged:Boolean;
		private var _lockedGridSelectionMode:String;
		private var selectedChildDatagrid:DataGrid;
		private var _editableChanged:Boolean;
		private var freezeGrid:Boolean;

		//----------------------------------
		//  selectedItem
		//----------------------------------
		private var _selectedItem:Object;

		public function get selectedItem():Object
		{
			return _selectedItem;
		}

		public function set selectedItem(value:Object):void
		{
			_selectedItem = value;
			_selectedItemChanged = true;
			invalidateProperties();
		}

		//----------------------------------
		//  selectedIndex
		//----------------------------------
		private var _selectedIndex:int;

		public function get selectedIndex():int
		{
			return _selectedIndex;
		}

		public function set selectedIndex(value:int):void
		{
			_selectedIndex = value;
			_selectedIndexChanged = true;
			invalidateProperties();
		}

		//----------------------------------
		//  selectedItems
		//----------------------------------
		private var _selectedItems:Vector.(Object);

		public function get selectedItems():Vector.(Object){
			return _selectedItems;
		}

		public function set selectedItems(value:Vector.(Object)):void
		{
			_selectedItems = value;
			_selectedItemsChanged = true;
			invalidateProperties();
		}


		//----------------------------------
		//  selectedIndices
		//----------------------------------
		private var _selectedIndices:Vector.(int);

		public function get selectedIndices():Vector.(int)
		{
			return _selectedIndices;
		}

		public function set selectedIndices(value:Vector.(int)):void
		{
			_selectedIndices = value;
			_selectedIndicesChanged = true;
			invalidateProperties();
		}


		//----------------------------------
		//  selectedCell
		//----------------------------------
		private var _selectedCell:CellPosition;

		public function get selectedCell():CellPosition
		{
			return _selectedCell;
		}

		public function set selectedCell(value:CellPosition):void
		{
			_selectedCell = value;
			_selectedCellChanged = true;
			invalidateProperties();
		}


		//----------------------------------
		//  selectedCells
		//----------------------------------
		private var _selectedCells:Vector.(cellposition);

		public function get selectedCells():Vector.(cellposition)
		{
			return _selectedCells;
		}

		public function set selectedCells(value:Vector.(cellposition)):void
		{
			_selectedCells = value;
			_selectedCellsChanged = true;
			invalidateProperties();
		}

		//----------------------------------
		//  Editable
		//----------------------------------
		private var _editable:Boolean;

		public function get editable():Boolean
		{
			return _editable;
		}

		/**
		 *  @private
		 */
		public function set editable(value:Boolean):void
		{
			if (_editable != value)
			{
				_editable = value;
				_editableChanged = true;
				invalidateProperties();
			}
		}

		/**
		 *  SPREADSHEET MODE
		 * this feature will change the selection mode on mouseover of the
		 * two grids to select a row on the locked grid and cell mode on the regular
		 * grid. On double click of a locked row will change to cell mode and an edit mode.
		 *
		**/
		private var _spreadsheetMode:Boolean;

		public function get spreadsheetMode():Boolean
		{
			return _spreadsheetMode;
		}

		public function set spreadsheetMode(value:Boolean):void
		{
			_spreadsheetMode = value;
			_spreadsheetModeChanged = true;
			invalidateProperties();
		}

		//----------------------------------
		//  Columns
		//----------------------------------
		private var _columns:IList;

		public function get columns():IList
		{
			return _columns;
		}

		/**
		 *  @private
		 */
		public function set columns(value:IList):void
		{
			if (_columns != value)
			{
				_columns = value;
				_columnsChanged = true;
				invalidateProperties();
			}
		}

		//----------------------------------
		//  dataProvider
		//----------------------------------

		//----------------------------------
		//  dataProvider
		//----------------------------------

		private var _dataProvider:IList;

		[Bindable("dataProviderChanged")]
		[Inspectable(category = "Data")]
		public function get dataProvider():IList
		{
			return _dataProvider;
		}

		/**
		 *  @private
		 */
		public function set dataProvider(value:IList):void
		{
			if (_dataProvider != value)
			{
				if (_dataProvider && _dataProvider.hasEventListener(CollectionEvent.COLLECTION_CHANGE))
					_dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, updateDataProvider);

				_dataProvider = value;

				dispatchChangeEvent("dataProviderChanged");
				_dataProviderChanged = true;
				invalidateDisplayList();
				invalidateProperties();

				if (dataProvider)
					_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, updateDataProvider, false, 0, true);
			}
		}

		private function updateDataProvider(event:CollectionEvent):void
		{
			_dataProviderChanged = true;
			invalidateDisplayList();
		}

		//----------------------------------
		//  selectionMode
		//----------------------------------

		private var _selectionMode:String;

		public function get selectionMode():String
		{
			return _selectionMode;
		}

		public function set selectionMode(value:String):void
		{
			_selectionMode = value;
			_selectionModeChanged = true;
			invalidateProperties();
		}

		//----------------------------------
		//  Row Height
		//----------------------------------
		private var _rowHeight:Number = 20;

		public function get rowHeight():Number
		{
			return _rowHeight;
		}

		public function set rowHeight(value:Number):void
		{
			_rowHeight = value;
			_rowHeightChanged = true;
			invalidateProperties();
		}

		//----------------------------------
		//  doubleClickEnabled
		//----------------------------------

		override public function set doubleClickEnabled(value:Boolean):void
		{
			super.doubleClickEnabled = value;

			_doubleClickEnabledChanged = true;
			invalidateProperties();
		}


		//----------------------------------
		//  commitProperties
		//----------------------------------

		override protected function commitProperties():void
		{
			if (dataGrid && _columnsChanged)
			{
				updateGridColumns();
				_columnsChanged = false;
			}

			if (_dataProviderChanged)
			{
				if (lockedGrid)
					lockedGrid.dataProvider = dataProvider;

				if (dataGrid)
					dataGrid.dataProvider = dataProvider;

				_gridsLocked = false;
				_dataProviderChanged = false;
			}

			if (_rowHeightChanged)
			{
				if (isNaN(rowHeight))
				{
					_rowHeight = MIN_ROWHEIGHT;
				}

				if (lockedGrid)
				{
					lockedGrid.rowHeight = _rowHeight;
				}

				if (dataGrid)
				{
					dataGrid.rowHeight = _rowHeight;
				}

				_rowHeightChanged = false;
			}

			if (dataGrid && lockedGrid && !_gridsLocked)
			{
				if (selectedChildDatagrid == dataGrid)
				{
					lockedGrid.grid.hoverRowIndex = dataGrid.grid.hoverRowIndex;
				}
				else
				{
					dataGrid.grid.hoverRowIndex = lockedGrid.grid.hoverRowIndex;
				}

				_gridsLocked = true;
			}

			if (_selectedCellsChanged)
			{

				if (dataGrid == selectedChildDatagrid)
				{
					dataGrid.selectedCells = selectedCells;

					if (lockedGrid)
						lockedGrid.selectedCells = null;
				}
				else if (lockedGrid == selectedChildDatagrid)
				{
					lockedGrid.selectedCells = selectedCells;

					if (dataGrid)
						dataGrid.selectedCells = null;
				}

				_selectedCellsChanged = false;
			}

			if (_selectedItemChanged)
			{
				if (lockedGrid)
					lockedGrid.selectedItem = selectedItem;

				if (dataGrid)
					dataGrid.selectedItem = selectedItem;

				_selectedItemChanged = false;
			}

			if (_selectedIndexChanged)
			{
				if (lockedGrid)
					lockedGrid.selectedIndex = selectedIndex;

				if (dataGrid)
					dataGrid.selectedIndex = selectedIndex;

				_selectedIndexChanged = false;
			}

			if (_selectedItemsChanged)
			{
				if (lockedGrid)
					lockedGrid.selectedItems = selectedItems;

				if (dataGrid)
					dataGrid.selectedItems = selectedItems;

				_selectedItemsChanged = false;
			}

			if (_selectedIndicesChanged)
			{
				if (lockedGrid)
					lockedGrid.selectedIndices = selectedIndices;

				if (dataGrid)
					dataGrid.selectedIndices = selectedIndices;

				_selectedIndicesChanged = false;
			}

			if (_selectedCellChanged)
			{
				if (dataGrid == selectedChildDatagrid)
				{
					dataGrid.selectedCell = selectedCell;

					if (lockedGrid)
						lockedGrid.selectedCell = null;
				}
				else if (lockedGrid == selectedChildDatagrid)
				{
					lockedGrid.selectedCell = selectedCell;

					if (dataGrid)
						if (spreadsheetMode)
						{
							dataGrid.selectedIndex = selectedCell.rowIndex;
						}
						else
						{
							dataGrid.selectedCell = null;
						}
				}
				_selectedCellChanged = false;
			}

			if (_doubleClickEnabledChanged)
			{
				if (lockedGrid)
					lockedGrid.doubleClickEnabled = doubleClickEnabled;

				if (dataGrid)
					dataGrid.doubleClickEnabled = doubleClickEnabled;

				_doubleClickEnabledChanged = false;
			}

			if (_editableChanged)
			{
				if (lockedGrid)
					lockedGrid.editable = editable;

				if (dataGrid)
					dataGrid.editable = editable;

				_editableChanged = false;
			}

			if (_spreadsheetModeChanged)
			{
				if (lockedGrid)
				{
					if (spreadsheetMode)
					{
						lockedGrid.editable = true;
					}
					else
					{
						lockedGrid.editable = editable;
					}
				}

				if (dataGrid)
				{
					if (spreadsheetMode)
					{
						dataGrid.editable = true;
					}
					else
					{
						dataGrid.editable = editable;
					}
				}
			}

			super.commitProperties();

		}

		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(unscaledWidth, unscaledHeight);

			if (_selectionModeChanged)
			{
				if (lockedGrid)
				{
					if (selectionMode == GridSelectionMode.SINGLE_CELL || selectionMode == GridSelectionMode.MULTIPLE_CELLS)
					{
						_lockedGridSelectionMode = lockedGrid.selectionMode = GridSelectionMode.NONE;
					}
					else
					{
						_lockedGridSelectionMode = lockedGrid.selectionMode = selectionMode;
					}
				}

				if (dataGrid)
				{
					dataGrid.selectionMode = selectionMode;
				}
			}

		}

		private function updateGridColumns():void
		{
			if (!columns)
				return;

			lockedCols = new ArrayList();
			gridCols = new ArrayList();

			for (var x:int = 0; x < columns.length; x++)
			{
				if (columns.getItemAt(x) is LockedGridColumn && (columns.getItemAt(x) as LockedGridColumn).lockColumn)
				{
					lockedCols.addItem(columns.getItemAt(x));
				}
				else
				{
					gridCols.addItem(columns.getItemAt(x));
				}
			}

			lockedGrid.columns = lockedCols;
			dataGrid.columns = gridCols;

			invalidateSkinState();
		}

		override protected function getCurrentSkinState():String
		{
			var returnState:String = NONLOCKEDMODE;

			// Use information in the class to determine the new view state of the skin class. 
			if (lockedCols.length > 0)
			{
				returnState = LOCKEDMODE;
			}

			return returnState;
		}

		override protected function partAdded(partName:String, instance:Object):void
		{
			super.partAdded(partName, instance);

			if (instance == lockedGrid)
			{
				lockedGrid.addEventListener(GridEvent.GRID_ROLL_OVER, lockedGridRollOverEvent, false, 0, true);
				lockedGrid.addEventListener(GridEvent.GRID_ROLL_OUT, lockedGridRollOutEvent, false, 0, true);
				lockedGrid.addEventListener(GridSelectionEvent.SELECTION_CHANGE, lockedGridSelectEvent, false, 0, true);
				lockedGrid.addEventListener(GridEvent.GRID_DOUBLE_CLICK, dataGrid_itemDoubleClickHandler, false, 0, true);
				lockedGrid.addEventListener(GridEvent.GRID_CLICK, dataGrid_itemClickHandler, false, 0, true);
				lockedGrid.addEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, gridItemEditorSaveHandler, false, 0, true);
				lockedGrid.addEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START, gridItemEditorStartHandler, false, 0, true);
				lockedGrid.addEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_CANCEL, gridItemEditorCancelHandler, false, 0, true);
				lockedGrid.setStyle("horizontalScrollPolicy", "off");
				lockedGrid.setStyle("verticalScrollPolicy", "off");
				_gridsLocked = false;
				lockedGrid.hasFocusableChildren = false;
				lockedGrid.rowHeight = MIN_ROWHEIGHT;
				lockedGrid.columns = new ArrayList();
				lockedGrid.sortableColumns = false;
			}
			else if (instance == dataGrid)
			{
				dataGrid.addEventListener(GridEvent.GRID_ROLL_OVER, gridRollOverEvent, false, 0, true);
				dataGrid.addEventListener(GridEvent.GRID_ROLL_OUT, gridRollOutEvent, false, 0, true);
				dataGrid.addEventListener(GridSelectionEvent.SELECTION_CHANGE, gridSelectEvent, false, 0, true);
				dataGrid.scroller.verticalScrollBar.addEventListener(Event.CHANGE, verticalScrollChange, false, 0, true);
				dataGrid.addEventListener(GridEvent.GRID_DOUBLE_CLICK, dataGrid_itemDoubleClickHandler, false, 0, true);
				dataGrid.addEventListener(GridEvent.GRID_CLICK, dataGrid_itemClickHandler, false, 0, true);
				dataGrid.scroller.verticalScrollBar.addEventListener(ResizeEvent.RESIZE, onAddHandler, false, 0, true);
				dataGrid.scroller.horizontalScrollBar.addEventListener(ResizeEvent.RESIZE, onAddHandler, false, 0, true);
				dataGrid.addEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START, gridItemEditorStartHandler, false, 0, true);
				dataGrid.addEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, gridItemEditorSaveHandler, false, 0, true);
				dataGrid.addEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_CANCEL, gridItemEditorCancelHandler, false, 0, true);
				_gridsLocked = false;
				setNonLockedGridAsSelected();
				dataGrid.hasFocusableChildren = false;
				dataGrid.rowHeight = MIN_ROWHEIGHT;
				dataGrid.columns = new ArrayList();
				dataGrid.sortableColumns = false;
			}
		}

		override protected function partRemoved(partName:String, instance:Object):void
		{
			super.partRemoved(partName, instance);

			if (instance == lockedGrid)
			{
				lockedGrid.removeEventListener(GridEvent.GRID_ROLL_OVER, lockedGridRollOverEvent);
				lockedGrid.removeEventListener(GridEvent.GRID_ROLL_OUT, lockedGridRollOutEvent);
				lockedGrid.removeEventListener(GridSelectionEvent.SELECTION_CHANGE, lockedGridSelectEvent);
				lockedGrid.removeEventListener(GridEvent.GRID_DOUBLE_CLICK, dataGrid_itemDoubleClickHandler);
				lockedGrid.removeEventListener(GridEvent.GRID_CLICK, dataGrid_itemClickHandler);
				lockedGrid.removeEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, gridItemEditorSaveHandler);
				lockedGrid.removeEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START, gridItemEditorStartHandler);
			}
			else if (instance == dataGrid)
			{
				dataGrid.removeEventListener(GridEvent.GRID_ROLL_OVER, gridRollOverEvent);
				dataGrid.removeEventListener(GridEvent.GRID_ROLL_OUT, gridRollOutEvent);
				dataGrid.removeEventListener(GridSelectionEvent.SELECTION_CHANGE, gridSelectEvent);
				dataGrid.scroller.verticalScrollBar.removeEventListener(Event.CHANGE, verticalScrollChange);
				dataGrid.scroller.verticalScrollBar.removeEventListener(ResizeEvent.RESIZE, onAddHandler);
				dataGrid.scroller.horizontalScrollBar.removeEventListener(ResizeEvent.RESIZE, onAddHandler);
				dataGrid.removeEventListener(GridEvent.GRID_DOUBLE_CLICK, dataGrid_itemDoubleClickHandler);
				dataGrid.removeEventListener(GridEvent.GRID_CLICK, dataGrid_itemClickHandler);
				dataGrid.removeEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, gridItemEditorSaveHandler);
				dataGrid.removeEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START, gridItemEditorStartHandler);
			}
		}


		private function updateDatagridSpreadsheetSelectionMode():void
		{
			if (spreadsheetMode)
			{
				if (lockedGrid)
					lockedGrid.selectionMode = GridSelectionMode.NONE;

				if (dataGrid)
					dataGrid.selectionMode = GridSelectionMode.SINGLE_CELL;

				dispatchEvent(new GridSelectionEvent(GridSelectionEvent.SELECTION_CHANGE));
			}
			else
			{
				if (lockedGrid)
					lockedGrid.selectionMode = _lockedGridSelectionMode;

				if (dataGrid)
					dataGrid.selectionMode = selectionMode;
			}
		}

		private function updateLockedgridSpreadsheetSelectionMode():void
		{
			if (spreadsheetMode)
			{
				if (lockedGrid)
					lockedGrid.selectionMode = GridSelectionMode.SINGLE_CELL;

				if (dataGrid)
					dataGrid.selectionMode = GridSelectionMode.SINGLE_ROW;

				dispatchEvent(new GridSelectionEvent(GridSelectionEvent.SELECTION_CHANGE));
			}
			else
			{
				if (lockedGrid)
					lockedGrid.selectionMode = _lockedGridSelectionMode;

				if (dataGrid)
					dataGrid.selectionMode = selectionMode;
			}
		}

		//Scrolls the locked datagrid when the scrollable datagrid scrolls vertically
		private function verticalScrollChange(evt:Event):void
		{
			dataGrid.validateNow();
			lockedGrid.grid.verticalScrollPosition = dataGrid.grid.verticalScrollPosition;
		}

		private function gridRollOutEvent(event:GridEvent):void
		{
			if (dataGrid.editorRowIndex != -1 || dataGrid.editorColumnIndex != -1)
				dataGrid.dispatchEvent(new GridItemEditorEvent(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE));

			dataGrid.grid.hoverRowIndex = -1;

		}

		private function gridRollOverEvent(event:GridEvent):void
		{
			if (freezeGrid)
				return;

			updateDatagridSpreadsheetSelectionMode();
			lockedGrid.validateNow();
			setNonLockedGridAsSelected();
			_gridsLocked = false;
			invalidateProperties();
		}

		private function lockedGridRollOutEvent(event:GridEvent):void
		{
			if (lockedGrid.editorRowIndex != -1 || lockedGrid.editorColumnIndex != -1)
				lockedGrid.dispatchEvent(new GridItemEditorEvent(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE));

			lockedGrid.grid.hoverRowIndex = -1;
		}

		private function lockedGridRollOverEvent(event:GridEvent):void
		{
			if (freezeGrid)
				return;

			updateLockedgridSpreadsheetSelectionMode();
			dataGrid.validateNow();
			setLockedGridAsSelected();
			_gridsLocked = false;
			invalidateProperties();
		}

		protected function dataGrid_itemDoubleClickHandler(event:GridEvent):void
		{
			if ((event.currentTarget as DataGrid).selectionMode == GridSelectionMode.NONE)
			{
				event.stopPropagation();
			}
			else if ((event.currentTarget as DataGrid).selectionMode == GridSelectionMode.SINGLE_CELL && (event.currentTarget as DataGrid).editable)
			{
				(event.currentTarget as DataGrid).startItemEditorSession(event.rowIndex, event.columnIndex);
			}
		}

		protected function dataGrid_itemClickHandler(event:GridEvent):void
		{
			if ((event.currentTarget as DataGrid).selectionMode == GridSelectionMode.NONE)
			{
				event.stopPropagation();
			}
		}

		protected function lockedGridSelectEvent(event:GridSelectionEvent):void
		{
			event.stopPropagation();

			if (!lockedGrid)
			{
				return;
			}

			if (lockedGrid.selectionMode == GridSelectionMode.MULTIPLE_CELLS || lockedGrid.selectionMode == GridSelectionMode.SINGLE_CELL || spreadsheetMode)
			{
				setLockedGridAsSelected();
				selectedCell = (event.currentTarget as DataGrid).selectedCell;
				selectedCells = (event.currentTarget as DataGrid).selectedCells;

				if (dataGrid)
				{
					dataGrid.selectedCell = null;
					dataGrid.selectedCells = null;
				}
			}
			else
			{
				selectedItem = lockedGrid.selectedItem = (event.currentTarget as DataGrid).selectedItem;
				selectedIndex = lockedGrid.selectedIndex = (event.currentTarget as DataGrid).selectedIndex;
				selectedItems = lockedGrid.selectedItems = (event.currentTarget as DataGrid).selectedItems;
				selectedIndices = lockedGrid.selectedIndices = (event.currentTarget as DataGrid).selectedIndices;
			}

			var e:GridSelectionEvent = event.clone() as GridSelectionEvent;
			dispatchEvent(e);
		}

		protected function gridSelectEvent(event:GridSelectionEvent):void
		{
			event.stopPropagation();

			if (!dataGrid)
			{
				return;
			}

			if (dataGrid.selectionMode == GridSelectionMode.MULTIPLE_CELLS || dataGrid.selectionMode == GridSelectionMode.SINGLE_CELL || spreadsheetMode)
			{
				setNonLockedGridAsSelected();
				selectedCell = (event.currentTarget as DataGrid).selectedCell;
				selectedCells = (event.currentTarget as DataGrid).selectedCells;

				if (lockedGrid)
				{
					lockedGrid.selectedCell = null;
					lockedGrid.selectedCells = null;
				}
			}
			else
			{
				selectedItem = dataGrid.selectedItem = (event.currentTarget as DataGrid).selectedItem;
				selectedIndex = dataGrid.selectedIndex = (event.currentTarget as DataGrid).selectedIndex;
				selectedItems = dataGrid.selectedItems = (event.currentTarget as DataGrid).selectedItems;
				selectedIndices = dataGrid.selectedIndices = (event.currentTarget as DataGrid).selectedIndices;
			}

			var e:GridSelectionEvent = event.clone() as GridSelectionEvent;
			dispatchEvent(e);
		}

		private function gridItemEditorStartHandler(event:GridItemEditorEvent):void
		{
			freezeGrid = true;
			event.stopPropagation();

			var e:GridItemEditorEvent = event.clone() as GridItemEditorEvent;
			dispatchEvent(e);
		}

		private function gridItemEditorSaveHandler(event:GridItemEditorEvent):void
		{
			freezeGrid = false;
			event.stopPropagation();

			var e:GridItemEditorEvent = event.clone() as GridItemEditorEvent;
			dispatchEvent(e);
		}

		protected function gridItemEditorCancelHandler(event:GridItemEditorEvent):void
		{
			freezeGrid = false;
			event.stopPropagation();

			var e:GridItemEditorEvent = event.clone() as GridItemEditorEvent;
			dispatchEvent(e);
		}

		private function onAddHandler(event:ResizeEvent):void
		{
			if (lockedGrid)
			{
				if (dataGrid && dataGrid.scroller.horizontalScrollBar.visible)
				{
					lockedGrid.height = dataGrid.height - dataGrid.scroller.horizontalScrollBar.height;
				}
				else
				{
					lockedGrid.height = dataGrid.height;
				}

				lockedGrid.invalidateSize();
				lockedGrid.invalidateProperties();
				invalidateSize();
				invalidateProperties();
				invalidateDisplayList();
			}
		}

		public function SparkLockedColumnGrid()
		{
			this.addEventListener(MouseEvent.ROLL_OUT, handleComponentMouseOut);
		}

		private function handleComponentMouseOut(event:MouseEvent):void
		{
			if (event.currentTarget != lockedGrid && event.currentTarget != dataGrid)
			{
				if (lockedGrid)
				{
					lockedGrid.grid.hoverRowIndex = -1;
					lockedGrid.invalidateProperties();
					lockedGrid.invalidateDisplayList();
				}

				if (dataGrid)
				{
					dataGrid.grid.hoverRowIndex = -1;
					dataGrid.invalidateProperties();
					dataGrid.invalidateDisplayList();
				}
			}
		}

		public function setLockedGridAsSelected():void
		{
			selectedChildDatagrid = lockedGrid;
		}

		public function setNonLockedGridAsSelected():void
		{
			selectedChildDatagrid = dataGrid;
		}

		public function selectedGridIsLocked():Boolean
		{
			return selectedChildDatagrid == lockedGrid ? true : false;
		}

		public function startItemEditorSession(rowIndex:int, columnIndex:int):Boolean
		{
			var ret:Boolean;

			if (selectedGridIsLocked())
			{
				ret = lockedGrid.startItemEditorSession(rowIndex, columnIndex);
				lockedGrid.validateNow();
				dataGrid.grid.verticalScrollPosition = lockedGrid.grid.verticalScrollPosition;
			}
			else
			{
				ret = dataGrid.startItemEditorSession(rowIndex, columnIndex);
				dataGrid.validateNow();
				lockedGrid.grid.verticalScrollPosition = dataGrid.grid.verticalScrollPosition;
			}

			return ret;
		}

		//----------------------------------
		//  dataProviderLength
		//---------------------------------- 
		public function get dataProviderLength():int
		{
			const dataProvider:IList = dataProvider;
			return (dataProvider) ? dataProvider.length : 0;
		}

		private function dispatchChangeEvent(type:String):void
		{
			if (hasEventListener(type))
				dispatchEvent(new Event(type));
		}
	}

And an example skin:





	
		[HostComponent("components.datagrid.SparkLockedColumnGrid")]
	

	
		
		
		
		
	

	
		
			
			
		
	
	
	
		
		
	


Spark Skinnable GridItemRenderer

I am currently working on a project with a requirement to have skinnable grids with changeable skins. Now working with itemrenderers in Spark, I've found it to be painfully similar to the Halo days. I was thinking to myself it would have been nice if GridItemRenderer extended SkinnableComponent instead of a Group but whatever, I'm sure there is a good explanation. Anyhow, I took it upon myself to create a quick SkinnableGridItemRenderer to fill my void.

I'm not too sure if this scales and i'm not too sure if tooltips will work perfectly but it's a good start and i don't have those requirements so it works great for what I need it for. By the way, I didn't include all the states so you'll have to add the others if you need them.

package com.datagrid
{ 
 import flash.events.Event;
 import flash.events.TextEvent;
 
 import mx.events.FlexEvent;
 
 import spark.components.Grid;
 import spark.components.gridClasses.GridColumn;
 import spark.components.gridClasses.IGridItemRenderer;
 import spark.components.supportClasses.SkinnableComponent;
 import spark.components.supportClasses.TextBase;

 [Event(name="dataChange", type="mx.events.FlexEvent")]

 //--------------------------------------
 //  Excluded APIs
 //--------------------------------------

 [Exclude(name="transitions", kind="property")]

 [SkinState("disabled")]
 [SkinState("selected")]
 [SkinState("hovered")]
 [SkinState("normal")]
 [SkinState("down")]

 public class SkinnableGridItemRenderer extends SkinnableComponent implements IGridItemRenderer
 {
  [SkinPart(required="false")]
  public var labelDisplay:TextBase;

  //----------------------------------
  //  column
  //----------------------------------

  private var _column:GridColumn=null;

  [Bindable("columnChanged")]

  /**
   *  @inheritDoc
   *
   *  

The Grid's updateDisplayList() method sets this property * before calling preprare()

. * * @default null * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get column():GridColumn { return _column; } /** * @private */ public function set column(value:GridColumn):void { if (_column == value) return; _column=value; dispatchChangeEvent("columnChanged"); } //---------------------------------- // rowIndex //---------------------------------- private var _rowIndex:int=-1; [Bindable("rowIndexChanged")] public function get rowIndex():int { return _rowIndex; } /** * @private */ public function set rowIndex(value:int):void { if (_rowIndex == value) return; _rowIndex=value; dispatchChangeEvent("rowIndexChanged"); } //---------------------------------- // dragging //---------------------------------- private var _dragging:Boolean; public function get dragging():Boolean { return _dragging; } public function set dragging(value:Boolean):void { if (_dragging == value) return; _dragging=value; } //---------------------------------- // label //---------------------------------- private var _label:String; public function get label():String { return _label; } public function set label(value:String):void { if (_label == value) return; _label=value; if (labelDisplay) labelDisplay.text=label; } //---------------------------------- // columnIndex //---------------------------------- public function get columnIndex():int { return (column) ? column.columnIndex : -1; } //---------------------------------- // selected //---------------------------------- private var _selected:Boolean; public function get selected():Boolean { return _selected; } public function set selected(value:Boolean):void { if (_selected == value) return; _selected=value; invalidateDisplayList(); invalidateSkinState(); } //---------------------------------- // showsCaret //---------------------------------- private var _showsCaret:Boolean; public function get showsCaret():Boolean { return _showsCaret; } public function set showsCaret(value:Boolean):void { if (_showsCaret == value) return; _showsCaret=value; } //---------------------------------- // down //---------------------------------- /** * @private * storage for the down property */ private var _down:Boolean=false; /** * @inheritDoc * * @default false * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get down():Boolean { return _down; } /** * @private */ public function set down(value:Boolean):void { if (value == _down) return; _down=value; invalidateDisplayList(); invalidateSkinState(); } //---------------------------------- // grid //---------------------------------- /** * Returns the Grid associated with this item renderer. * This is the same value as column.grid. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get grid():Grid { return (column) ? column.grid : null; } //---------------------------------- // hovered //---------------------------------- private var _hovered:Boolean=false; /** * @inheritDoc * * @default false * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get hovered():Boolean { return _hovered; } /** * @private */ public function set hovered(value:Boolean):void { if (value == _hovered) return; _hovered=value; invalidateDisplayList(); invalidateSkinState(); } //---------------------------------- // data //---------------------------------- private var _data:Object; public function get data():Object { return _data; } public function set data(value:Object):void { if (_data == value) return; _data=value; } override protected function getCurrentSkinState():String { var retState:String="normal"; if (!enabled) { retState="disabled"; } else if (_down) { retState="down"; } else if (_hovered) { retState="hovered"; } else if (_selected) { retState="selected"; } return retState; } //---------------------------------- // skin parts //---------------------------------- override protected function partAdded(partName:String, instance:Object):void { super.partAdded(partName, instance); if (instance == labelDisplay) { labelDisplay.text=label; } } private function dispatchChangeEvent(type:String):void { if (hasEventListener(type)) dispatchEvent(new Event(type)); } public function prepare(hasBeenRecycled:Boolean):void { } public function discard(willBeRecycled:Boolean):void { } } }

Here's an example skin:


 
 
  [HostComponent("com.datagrid.SkinnableGridItemRenderer")]
 

 
  
  
  
  
  
 

 


Where to go from here? Well you can easily override this base class and add additional functionality and skin states to accomplish pretty much anything. All this isn't really necessary depending on your project setup but we use themes and it was easier with this approach and the styling was much easier.

Flex 4 Spark Auto Scrolling Form

I had an interesting request today to make sure when tabbing through all of our forms, that the vertical scroll bar adjusts to make sure the focused field is in view. I went back and forth on this and it's not built in to flex so some reason, but a colleague came across a piece of code from flex 3 and converted it over to flex 4. This worked well for tabbing down a page but not up so we doctored it up a little and it now scrolls up and down. I found it so easy and helpful that I thought i'd share

public class ScrollUtil
 {
  // this method is specifically written for the Scroller component
  // to use this on other scroller components will need modification or
  // another method
  public static function autoScroll(event:FocusEvent):void
  {
   var scroller:Scroller = event.currentTarget as Scroller;
   var focusItem:DisplayObject = DisplayObject(scroller.focusManager.getFocus());

   // if no scrollbar or focused item, nothing to do
   if (scroller.verticalScrollBar && focusItem)
   {
    // is the current focus item inside our container?
    if (focusItem == scroller || !scroller.contains(focusItem))
    {
     return;
    }

    // find the focusItem’s y coord in the scrolled container
    // by summing the y offsets of the item and all parent containers in the tree
    // between the target and the scrolled container
    ensureFocusItemIsVisible(focusItem, scroller);
   }
  }

  public static function ensureFocusItemIsVisible(focusItem:DisplayObject, scroller:Scroller):void
  {
   var focusTopEdge:int = focusItem.y;
   var thisItem:DisplayObjectContainer = focusItem.parent;

   while (thisItem != scroller)
   {
    focusTopEdge += thisItem.y;
    thisItem = thisItem.parent;
   }

   var focusBottomEdge:int = focusTopEdge + focusItem.height;
   var scrollbarRange:int = scroller.verticalScrollBar.maxHeight;
   var visibleWindowHeight:int = scroller.height;
   var firstVisibleY:int = scroller.viewport.verticalScrollPosition;
   var lastVisibleY:int = visibleWindowHeight + scroller.viewport.verticalScrollPosition;
   var newPos:int;

   if (scroller.horizontalScrollBar)
   {
    // remove the horiz scrollbar height from lastVisibleY
    lastVisibleY -= scroller.horizontalScrollBar.height;
   }

   if (focusTopEdge == scroller.viewport.verticalScrollPosition)
   {
    trace("Bar not moved, already at top edge of focus item.");
   }
   else if (focusTopEdge > lastVisibleY)
   {
    newPos = Math.min(scrollbarRange, scroller.viewport.verticalScrollPosition + (focusBottomEdge - lastVisibleY));
    scroller.viewport.verticalScrollPosition = newPos;
    trace("Moved bar down to " + newPos);
   }
   else if (focusTopEdge < firstVisibleY)
   {
    scroller.viewport.verticalScrollPosition = focusTopEdge;
    trace("Moved bar up to " + focusTopEdge);
   }
   else
   {
    trace("Bar not moved.");
    ;
   }
  }
 }