<?php
/**
 * TDataGrid related class files.
 * This file contains the definition of the following classes:
 * TDataGrid, TDataGridItem, TDataGridItemCollection, TDataGridColumnCollection,
 * TDataGridPagerStyle, TDataGridItemEventParameter,
 * TDataGridCommandEventParameter, TDataGridSortCommandEventParameter,
 * TDataGridPageChangedEventParameter
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.pradosoft.com/
 * @copyright Copyright &copy; 2005-2007 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 */

/**
 * Includes TBaseList, TPagedDataSource, TDummyDataSource and TTable classes
 */
Prado::using('System.Web.UI.WebControls.TBaseDataList');
Prado::using('System.Collections.TPagedDataSource');
Prado::using('System.Collections.TDummyDataSource');
Prado::using('System.Web.UI.WebControls.TTable');
Prado::using('System.Web.UI.WebControls.TPanel');
Prado::using('System.Web.UI.WebControls.TDataGridPagerStyle');

/**
 * TDataGrid class
 *
 * TDataGrid represents a data bound and updatable grid control.
 *
 * To populate data into the datagrid, sets its {@link setDataSource DataSource}
 * to a tabular data source and call {@link dataBind()}.
 * Each row of data will be represented by an item in the {@link getItems Items}
 * collection of the datagrid.
 *
 * An item can be at one of three states: browsing, selected and edit.
 * The state determines how the item will be displayed. For example, if an item
 * is in edit state, it may be displayed as a table row with input text boxes
 * if the columns are of type {@link TBoundColumn}; and if in browsing state,
 * they are displayed as static text.
 *
 * To change the state of an item, set {@link setEditItemIndex EditItemIndex}
 * or {@link setSelectedItemIndex SelectedItemIndex} property.
 *
 * Each datagrid item has a {@link TDataGridItem::getItemType type}
 * which tells the position and state of the item in the datalist. An item in the header
 * of the repeater is of type Header. A body item may be of either
 * Item, AlternatingItem, SelectedItem or EditItem, depending whether the item
 * index is odd or even, whether it is being selected or edited.
 *
 * A datagrid is specified with a list of columns. Each column specifies how the corresponding
 * table column will be displayed. For example, the header/footer text of that column,
 * the cells in that column, and so on. The following column types are currently
 * provided by the framework,
 * - {@link TBoundColumn}, associated with a specific field in datasource and displays the corresponding data.
 * - {@link TEditCommandColumn}, displaying edit/update/cancel command buttons
 * - {@link TButtonColumn}, displaying generic command buttons that may be bound to specific field in datasource.
 * - {@link TDropDownListColumn}, displaying a dropdown list when the item is in edit state
 * - {@link THyperLinkColumn}, displaying a hyperlink that may be bound to specific field in datasource.
 * - {@link TCheckBoxColumn}, displaying a checkbox that may be bound to specific field in datasource.
 * - {@link TTemplateColumn}, displaying content based on templates.
 *
 * There are three ways to specify columns for a datagrid.
 * <ul>
 *  <li>Automatically generated based on data source.
 *  By setting {@link setAutoGenerateColumns AutoGenerateColumns} to true,
 *  a list of columns will be automatically generated based on the schema of the data source.
 *  Each column corresponds to a column of the data.</li>
 *  <li>Specified in template. For example,
 *    <code>
 *     <com:TDataGrid ...>
 *        <com:TBoundColumn .../>
 *        <com:TEditCommandColumn .../>
 *     </com:TDataGrid>
 *    </code>
 *  </li>
 *  <li>Manually created in code. Columns can be manipulated via
 *  the {@link setColumns Columns} property of the datagrid. For example,
 *  <code>
 *    $column=new TBoundColumn;
 *    $datagrid->Columns[]=$column;
 *  </code>
 *  </li>
 * </ul>
 * Note, automatically generated columns cannot be accessed via
 * the {@link getColumns Columns} property.
 *
 * TDataGrid supports sorting. If the {@link setAllowSorting AllowSorting}
 * is set to true, a column with nonempty {@link setSortExpression SortExpression}
 * will have its header text displayed as a clickable link button.
 * Clicking on the link button will raise {@link onSortCommand OnSortCommand}
 * event. You can respond to this event, sort the data source according
 * to the event parameter, and then invoke {@link databind()} on the datagrid
 * to show to end users the sorted data.
 *
 * TDataGrid supports paging. If the {@link setAllowPaging AllowPaging}
 * is set to true, a pager will be displayed on top and/or bottom of the table.
 * How the pager will be displayed is determined by the {@link getPagerStyle PagerStyle}
 * property. Clicking on a pager button will raise an {@link onPageIndexChanged OnPageIndexChanged}
 * event. You can respond to this event, specify the page to be displayed by
 * setting {@link setCurrentPageIndex CurrentPageIndex}</b> property,
 * and then invoke {@link databind()} on the datagrid to show to end users
 * a new page of data.
 *
 * TDataGrid supports two kinds of paging. The first one is based on the number of data items in
 * datasource. The number of pages {@link getPageCount PageCount} is calculated based
 * the item number and the {@link setPageSize PageSize} property.
 * The datagrid will manage which section of the data source to be displayed
 * based on the {@link setCurrentPageIndex CurrentPageIndex} property.
 * The second approach calculates the page number based on the
 * {@link setVirtualItemCount VirtualItemCount} property and
 * the {@link setPageSize PageSize} property. The datagrid will always
 * display from the beginning of the datasource up to the number of
 * {@link setPageSize PageSize} data items. This approach is especially
 * useful when the datasource may contain too many data items to be managed by
 * the datagrid efficiently.
 *
 * When the datagrid contains a button control that raises an {@link onCommand OnCommand}
 * event, the event will be bubbled up to the datagrid control.
 * If the event's command name is recognizable by the datagrid control,
 * a corresponding item event will be raised. The following item events will be
 * raised upon a specific command:
 * - OnEditCommand, if CommandName=edit
 * - OnCancelCommand, if CommandName=cancel
 * - OnSelectCommand, if CommandName=select
 * - OnDeleteCommand, if CommandName=delete
 * - OnUpdateCommand, if CommandName=update
 * - onPageIndexChanged, if CommandName=page
 * - OnSortCommand, if CommandName=sort
 * Note, an {@link onItemCommand OnItemCommand} event is raised in addition to
 * the above specific command events.
 *
 * TDataGrid also raises an {@link onItemCreated OnItemCreated} event for
 * every newly created datagrid item. You can respond to this event to customize
 * the content or style of the newly created item.
 *
 * Note, the data bound to the datagrid are reset to null after databinding.
 * There are several ways to access the data associated with a datagrid row:
 * - Access the data in {@link onItemDataBound OnItemDataBound} event
 * - Use {@link getDataKeys DataKeys} to obtain the data key associated with
 * the specified datagrid row and use the key to fetch the corresponding data
 * from some persistent storage such as DB.
 * - Save the data in viewstate and get it back during postbacks.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGrid extends TBaseDataList implements INamingContainer
{
	/**
	 * datagrid item types
	 * @deprecated deprecated since version 3.0.4. Use TListItemType constants instead.
	 */
	const IT_HEADER='Header';
	const IT_FOOTER='Footer';
	const IT_ITEM='Item';
	const IT_SEPARATOR='Separator';
	const IT_ALTERNATINGITEM='AlternatingItem';
	const IT_EDITITEM='EditItem';
	const IT_SELECTEDITEM='SelectedItem';
	const IT_PAGER='Pager';

	/**
	 * Command name that TDataGrid understands.
	 */
	const CMD_SELECT='Select';
	const CMD_EDIT='Edit';
	const CMD_UPDATE='Update';
	const CMD_DELETE='Delete';
	const CMD_CANCEL='Cancel';
	const CMD_SORT='Sort';
	const CMD_PAGE='Page';
	const CMD_PAGE_NEXT='Next';
	const CMD_PAGE_PREV='Previous';

	/**
	 * @var TDataGridColumnCollection manually created column collection
	 */
	private $_columns=null;
	/**
	 * @var TDataGridColumnCollection automatically created column collection
	 */
	private $_autoColumns=null;
	/**
	 * @var TList all columns including both manually and automatically created columns
	 */
	private $_allColumns=null;
	/**
	 * @var TDataGridItemCollection datagrid item collection
	 */
	private $_items=null;
	/**
	 * @var TDataGridItem header item
	 */
	private $_header=null;
	/**
	 * @var TDataGridItem footer item
	 */
	private $_footer=null;
	/**
	 * @var TPagedDataSource paged data source object
	 */
	private $_pagedDataSource=null;
	private $_topPager=null;
	private $_bottomPager=null;
	/**
	 * @var ITemplate template used when empty data is bounded
	 */
	private $_emptyTemplate=null;
	/**
	 * @var boolean whether empty template is effective
	 */
	private $_useEmptyTemplate=false;

	/**
	 * @return string tag name (table) of the datagrid
	 */
	protected function getTagName()
	{
		return 'table';
	}

	/**
	 * Adds objects parsed in template to datagrid.
	 * Datagrid columns are added into {@link getColumns Columns} collection.
	 * @param mixed object parsed in template
	 */
	public function addParsedObject($object)
	{
		if($object instanceof TDataGridColumn)
			$this->getColumns()->add($object);
		else
			parent::addParsedObject($object);  // this is needed by EmptyTemplate
	}

	/**
	 * @return TDataGridColumnCollection manually specified datagrid columns
	 */
	public function getColumns()
	{
		if(!$this->_columns)
			$this->_columns=new TDataGridColumnCollection($this);
		return $this->_columns;
	}

	/**
	 * @return TDataGridColumnCollection automatically generated datagrid columns
	 */
	public function getAutoColumns()
	{
		if(!$this->_autoColumns)
			$this->_autoColumns=new TDataGridColumnCollection($this);
		return $this->_autoColumns;
	}

	/**
	 * @return TDataGridItemCollection datagrid item collection
	 */
	public function getItems()
	{
		if(!$this->_items)
			$this->_items=new TDataGridItemCollection;
		return $this->_items;
	}

	/**
	 * @return integer number of items
	 */
	public function getItemCount()
	{
		return $this->_items?$this->_items->getCount():0;
	}

	/**
	 * Creates a style object for the control.
	 * This method creates a {@link TTableStyle} to be used by datagrid.
	 * @return TTableStyle control style to be used
	 */
	protected function createStyle()
	{
		return new TTableStyle;
	}

	/**
	 * @return string the URL of the background image for the datagrid
	 */
	public function getBackImageUrl()
	{
		return $this->getStyle()->getBackImageUrl();
	}

	/**
	 * @param string the URL of the background image for the datagrid
	 */
	public function setBackImageUrl($value)
	{
		$this->getStyle()->setBackImageUrl($value);
	}

	/**
	 * @return TTableItemStyle the style for every item
	 */
	public function getItemStyle()
	{
		if(($style=$this->getViewState('ItemStyle',null))===null)
		{
			$style=new TTableItemStyle;
			$this->setViewState('ItemStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TTableItemStyle the style for each alternating item
	 */
	public function getAlternatingItemStyle()
	{
		if(($style=$this->getViewState('AlternatingItemStyle',null))===null)
		{
			$style=new TTableItemStyle;
			$this->setViewState('AlternatingItemStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TTableItemStyle the style for selected item
	 */
	public function getSelectedItemStyle()
	{
		if(($style=$this->getViewState('SelectedItemStyle',null))===null)
		{
			$style=new TTableItemStyle;
			$this->setViewState('SelectedItemStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TTableItemStyle the style for edit item
	 */
	public function getEditItemStyle()
	{
		if(($style=$this->getViewState('EditItemStyle',null))===null)
		{
			$style=new TTableItemStyle;
			$this->setViewState('EditItemStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TTableItemStyle the style for header
	 */
	public function getHeaderStyle()
	{
		if(($style=$this->getViewState('HeaderStyle',null))===null)
		{
			$style=new TTableItemStyle;
			$this->setViewState('HeaderStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TTableItemStyle the style for footer
	 */
	public function getFooterStyle()
	{
		if(($style=$this->getViewState('FooterStyle',null))===null)
		{
			$style=new TTableItemStyle;
			$this->setViewState('FooterStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TDataGridPagerStyle the style for pager
	 */
	public function getPagerStyle()
	{
		if(($style=$this->getViewState('PagerStyle',null))===null)
		{
			$style=new TDataGridPagerStyle;
			$this->setViewState('PagerStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TStyle the style for thead element, if any
	 * @since 3.1.1
	 */
	public function getTableHeadStyle()
	{
		if(($style=$this->getViewState('TableHeadStyle',null))===null)
		{
			$style=new TStyle;
			$this->setViewState('TableHeadStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TStyle the style for tbody element, if any
	 * @since 3.1.1
	 */
	public function getTableBodyStyle()
	{
		if(($style=$this->getViewState('TableBodyStyle',null))===null)
		{
			$style=new TStyle;
			$this->setViewState('TableBodyStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return TStyle the style for tfoot element, if any
	 * @since 3.1.1
	 */
	public function getTableFootStyle()
	{
		if(($style=$this->getViewState('TableFootStyle',null))===null)
		{
			$style=new TStyle;
			$this->setViewState('TableFootStyle',$style,null);
		}
		return $style;
	}

	/**
	 * @return string caption for the datagrid
	 */
	public function getCaption()
	{
		return $this->getViewState('Caption','');
	}

	/**
	 * @param string caption for the datagrid
	 */
	public function setCaption($value)
	{
		$this->setViewState('Caption',$value,'');
	}

	/**
	 * @return TTableCaptionAlign datagrid caption alignment. Defaults to TTableCaptionAlign::NotSet.
	 */
	public function getCaptionAlign()
	{
		return $this->getViewState('CaptionAlign',TTableCaptionAlign::NotSet);
	}

	/**
	 * @param TTableCaptionAlign datagrid caption alignment. Valid values include
	 */
	public function setCaptionAlign($value)
	{
		$this->setViewState('CaptionAlign',TPropertyValue::ensureEnum($value,'TTableCaptionAlign'),TTableCaptionAlign::NotSet);
	}

	/**
	 * @return TDataGridItem the header item
	 */
	public function getHeader()
	{
		return $this->_header;
	}

	/**
	 * @return TDataGridItem the footer item
	 */
	public function getFooter()
	{
		return $this->_footer;
	}

	/**
	 * @return TDataGridPager the pager displayed at the top of datagrid. It could be null if paging is disabled.
	 */
	public function getTopPager()
	{
		return $this->_topPager;
	}

	/**
	 * @return TDataGridPager the pager displayed at the bottom of datagrid. It could be null if paging is disabled.
	 */
	public function getBottomPager()
	{
		return $this->_bottomPager;
	}

	/**
	 * @return TDataGridItem the selected item, null if no item is selected.
	 */
	public function getSelectedItem()
	{
		$index=$this->getSelectedItemIndex();
		$items=$this->getItems();
		if($index>=0 && $index<$items->getCount())
			return $items->itemAt($index);
		else
			return null;
	}

	/**
	 * @return integer the zero-based index of the selected item in {@link getItems Items}.
	 * A value -1 means no item selected.
	 */
	public function getSelectedItemIndex()
	{
		return $this->getViewState('SelectedItemIndex',-1);
	}

	/**
	 * Selects an item by its index in {@link getItems Items}.
	 * Previously selected item will be un-selected.
	 * If the item to be selected is already in edit mode, it will remain in edit mode.
	 * If the index is less than 0, any existing selection will be cleared up.
	 * @param integer the selected item index
	 */
	public function setSelectedItemIndex($value)
	{
		if(($value=TPropertyValue::ensureInteger($value))<0)
			$value=-1;
		if(($current=$this->getSelectedItemIndex())!==$value)
		{
			$this->setViewState('SelectedItemIndex',$value,-1);
			$items=$this->getItems();
			$itemCount=$items->getCount();
			if($current>=0 && $current<$itemCount)
			{
				$item=$items->itemAt($current);
				if($item->getItemType()!==TListItemType::EditItem)
					$item->setItemType($current%2?TListItemType::AlternatingItem:TListItemType::Item);
			}
			if($value>=0 && $value<$itemCount)
			{
				$item=$items->itemAt($value);
				if($item->getItemType()!==TListItemType::EditItem)
					$item->setItemType(TListItemType::SelectedItem);
			}
		}
	}

	/**
	 * @return TDataGridItem the edit item
	 */
	public function getEditItem()
	{
		$index=$this->getEditItemIndex();
		$items=$this->getItems();
		if($index>=0 && $index<$items->getCount())
			return $items->itemAt($index);
		else
			return null;
	}

	/**
	 * @return integer the zero-based index of the edit item in {@link getItems Items}.
	 * A value -1 means no item is in edit mode.
	 */
	public function getEditItemIndex()
	{
		return $this->getViewState('EditItemIndex',-1);
	}

	/**
	 * Edits an item by its index in {@link getItems Items}.
	 * Previously editting item will change to normal item state.
	 * If the index is less than 0, any existing edit item will be cleared up.
	 * @param integer the edit item index
	 */
	public function setEditItemIndex($value)
	{
		if(($value=TPropertyValue::ensureInteger($value))<0)
			$value=-1;
		if(($current=$this->getEditItemIndex())!==$value)
		{
			$this->setViewState('EditItemIndex',$value,-1);
			$items=$this->getItems();
			$itemCount=$items->getCount();
			if($current>=0 && $current<$itemCount)
				$items->itemAt($current)->setItemType($current%2?TListItemType::AlternatingItem:TListItemType::Item);
			if($value>=0 && $value<$itemCount)
				$items->itemAt($value)->setItemType(TListItemType::EditItem);
		}
	}

	/**
	 * @return boolean whether sorting is enabled. Defaults to false.
	 */
	public function getAllowSorting()
	{
		return $this->getViewState('AllowSorting',false);
	}

	/**
	 * @param boolean whether sorting is enabled
	 */
	public function setAllowSorting($value)
	{
		$this->setViewState('AllowSorting',TPropertyValue::ensureBoolean($value),false);
	}

	/**
	 * @return boolean whether datagrid columns should be automatically generated. Defaults to true.
	 */
	public function getAutoGenerateColumns()
	{
		return $this->getViewState('AutoGenerateColumns',true);
	}

	/**
	 * @param boolean whether datagrid columns should be automatically generated
	 */
	public function setAutoGenerateColumns($value)
	{
		$this->setViewState('AutoGenerateColumns',TPropertyValue::ensureBoolean($value),true);
	}

	/**
	 * @return boolean whether the header should be displayed. Defaults to true.
	 */
	public function getShowHeader()
	{
		return $this->getViewState('ShowHeader',true);
	}

	/**
	 * @param boolean whether the header should be displayed
	 */
	public function setShowHeader($value)
	{
		$this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true);
	}

	/**
	 * @return boolean whether the footer should be displayed. Defaults to false.
	 */
	public function getShowFooter()
	{
		return $this->getViewState('ShowFooter',false);
	}

	/**
	 * @param boolean whether the footer should be displayed
	 */
	public function setShowFooter($value)
	{
		$this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),false);
	}

	/**
	 * @return ITemplate the template applied when no data is bound to the datagrid
	 */
	public function getEmptyTemplate()
	{
		return $this->_emptyTemplate;
	}

	/**
	 * @param ITemplate the template applied when no data is bound to the datagrid
	 * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
	 */
	public function setEmptyTemplate($value)
	{
		if($value instanceof ITemplate || $value===null)
			$this->_emptyTemplate=$value;
		else
			throw new TInvalidDataTypeException('datagrid_template_required','EmptyTemplate');
	}

	/**
	 * This method overrides parent's implementation to handle
	 * {@link onItemCommand OnItemCommand} event which is bubbled from
	 * {@link TDataGridItem} child controls.
	 * If the event parameter is {@link TDataGridCommandEventParameter} and
	 * the command name is a recognized one, which includes 'select', 'edit',
	 * 'delete', 'update', and 'cancel' (case-insensitive), then a
	 * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}).
	 * This method should only be used by control developers.
	 * @param TControl the sender of the event
	 * @param TEventParameter event parameter
	 * @return boolean whether the event bubbling should stop here.
	 */
	public function bubbleEvent($sender,$param)
	{
		if($param instanceof TDataGridCommandEventParameter)
		{
			$this->onItemCommand($param);
			$command=$param->getCommandName();
			if(strcasecmp($command,self::CMD_SELECT)===0)
			{
				$this->setSelectedItemIndex($param->getItem()->getItemIndex());
				$this->onSelectedIndexChanged($param);
				return true;
			}
			else if(strcasecmp($command,self::CMD_EDIT)===0)
			{
				$this->onEditCommand($param);
				return true;
			}
			else if(strcasecmp($command,self::CMD_DELETE)===0)
			{
				$this->onDeleteCommand($param);
				return true;
			}
			else if(strcasecmp($command,self::CMD_UPDATE)===0)
			{
				$this->onUpdateCommand($param);
				return true;
			}
			else if(strcasecmp($command,self::CMD_CANCEL)===0)
			{
				$this->onCancelCommand($param);
				return true;
			}
			else if(strcasecmp($command,self::CMD_SORT)===0)
			{
				$this->onSortCommand(new TDataGridSortCommandEventParameter($sender,$param));
				return true;
			}
			else if(strcasecmp($command,self::CMD_PAGE)===0)
			{
				$p=$param->getCommandParameter();
				if(strcasecmp($p,self::CMD_PAGE_NEXT)===0)
					$pageIndex=$this->getCurrentPageIndex()+1;
				else if(strcasecmp($p,self::CMD_PAGE_PREV)===0)
					$pageIndex=$this->getCurrentPageIndex()-1;
				else
					$pageIndex=TPropertyValue::ensureInteger($p)-1;
				$this->onPageIndexChanged(new TDataGridPageChangedEventParameter($sender,$pageIndex));
				return true;
			}
		}
		return false;
	}

	/**
	 * Raises <b>OnCancelCommand</b> event.
	 * This method is invoked when a button control raises <b>OnCommand</b> event
	 * with <b>cancel</b> command name.
	 * @param TDataGridCommandEventParameter event parameter
	 */
	public function onCancelCommand($param)
	{
		$this->raiseEvent('OnCancelCommand',$this,$param);
	}

	/**
	 * Raises <b>OnDeleteCommand</b> event.
	 * This method is invoked when a button control raises <b>OnCommand</b> event
	 * with <b>delete</b> command name.
	 * @param TDataGridCommandEventParameter event parameter
	 */
	public function onDeleteCommand($param)
	{
		$this->raiseEvent('OnDeleteCommand',$this,$param);
	}

	/**
	 * Raises <b>OnEditCommand</b> event.
	 * This method is invoked when a button control raises <b>OnCommand</b> event
	 * with <b>edit</b> command name.
	 * @param TDataGridCommandEventParameter event parameter
	 */
	public function onEditCommand($param)
	{
		$this->raiseEvent('OnEditCommand',$this,$param);
	}

	/**
	 * Raises <b>OnItemCommand</b> event.
	 * This method is invoked when a button control raises <b>OnCommand</b> event.
	 * @param TDataGridCommandEventParameter event parameter
	 */
	public function onItemCommand($param)
	{
		$this->raiseEvent('OnItemCommand',$this,$param);
	}

	/**
	 * Raises <b>OnSortCommand</b> event.
	 * This method is invoked when a button control raises <b>OnCommand</b> event
	 * with <b>sort</b> command name.
	 * @param TDataGridSortCommandEventParameter event parameter
	 */
	public function onSortCommand($param)
	{
		$this->raiseEvent('OnSortCommand',$this,$param);
	}

	/**
	 * Raises <b>OnUpdateCommand</b> event.
	 * This method is invoked when a button control raises <b>OnCommand</b> event
	 * with <b>update</b> command name.
	 * @param TDataGridCommandEventParameter event parameter
	 */
	public function onUpdateCommand($param)
	{
		$this->raiseEvent('OnUpdateCommand',$this,$param);
	}

	/**
	 * Raises <b>OnItemCreated</b> event.
	 * This method is invoked right after a datagrid item is created and before
	 * added to page hierarchy.
	 * @param TDataGridItemEventParameter event parameter
	 */
	public function onItemCreated($param)
	{
		$this->raiseEvent('OnItemCreated',$this,$param);
	}

	/**
	 * Raises <b>OnPagerCreated</b> event.
	 * This method is invoked right after a datagrid pager is created and before
	 * added to page hierarchy.
	 * @param TDataGridPagerEventParameter event parameter
	 */
	public function onPagerCreated($param)
	{
		$this->raiseEvent('OnPagerCreated',$this,$param);
	}

	/**
	 * Raises <b>OnItemDataBound</b> event.
	 * This method is invoked for each datagrid item after it performs
	 * databinding.
	 * @param TDataGridItemEventParameter event parameter
	 */
	public function onItemDataBound($param)
	{
		$this->raiseEvent('OnItemDataBound',$this,$param);
	}

	/**
	 * Raises <b>OnPageIndexChanged</b> event.
	 * This method is invoked when current page is changed.
	 * @param TDataGridPageChangedEventParameter event parameter
	 */
	public function onPageIndexChanged($param)
	{
		$this->raiseEvent('OnPageIndexChanged',$this,$param);
	}

	/**
	 * Saves item count in viewstate.
	 * This method is invoked right before control state is to be saved.
	 */
	public function saveState()
	{
		parent::saveState();
		if(!$this->getEnableViewState(true))
			return;
		if($this->_items)
			$this->setViewState('ItemCount',$this->_items->getCount(),0);
		else
			$this->clearViewState('ItemCount');
		if($this->_autoColumns)
		{
			$state=array();
			foreach($this->_autoColumns as $column)
				$state[]=$column->saveState();
			$this->setViewState('AutoColumns',$state,array());
		}
		else
			$this->clearViewState('AutoColumns');
		if($this->_columns)
		{
			$state=array();
			foreach($this->_columns as $column)
				$state[]=$column->saveState();
			$this->setViewState('Columns',$state,array());
		}
		else
			$this->clearViewState('Columns');
	}

	/**
	 * Loads item count information from viewstate.
	 * This method is invoked right after control state is loaded.
	 */
	public function loadState()
	{
		parent::loadState();
		if(!$this->getEnableViewState(true))
			return;
		if(!$this->getIsDataBound())
		{
			$state=$this->getViewState('AutoColumns',array());
			if(!empty($state))
			{
				$this->_autoColumns=new TDataGridColumnCollection($this);
				foreach($state as $st)
				{
					$column=new TBoundColumn;
					$column->loadState($st);
					$this->_autoColumns->add($column);
				}
			}
			else
				$this->_autoColumns=null;
			$state=$this->getViewState('Columns',array());
			if($this->_columns && $this->_columns->getCount()===count($state))
			{
				$i=0;
				foreach($this->_columns as $column)
				{
					$column->loadState($state[$i]);
					$i++;
				}
			}
			$this->restoreGridFromViewState();
		}
	}

	/**
	 * Clears up all items in the datagrid.
	 */
	public function reset()
	{
		$this->getControls()->clear();
		$this->getItems()->clear();
		$this->_header=null;
		$this->_footer=null;
		$this->_topPager=null;
		$this->_bottomPager=null;
		$this->_useEmptyTemplate=false;
	}

	/**
	 * Restores datagrid content from viewstate.
	 */
	protected function restoreGridFromViewState()
	{
		$this->reset();

		$allowPaging=$this->getAllowPaging();

		$itemCount=$this->getViewState('ItemCount',0);
		$dsIndex=$this->getViewState('DataSourceIndex',0);

		$columns=new TList($this->getColumns());
		$columns->mergeWith($this->_autoColumns);
		$this->_allColumns=$columns;

		$items=$this->getItems();

		if($columns->getCount())
		{
			foreach($columns as $column)
				$column->initialize();
			$selectedIndex=$this->getSelectedItemIndex();
			$editIndex=$this->getEditItemIndex();
			for($index=0;$index<$itemCount;++$index)
			{
				if($index===0)
				{
					if($allowPaging)
						$this->_topPager=$this->createPager();
					$this->_header=$this->createItemInternal(-1,-1,TListItemType::Header,false,null,$columns);
				}
				if($index===$editIndex)
					$itemType=TListItemType::EditItem;
				else if($index===$selectedIndex)
					$itemType=TListItemType::SelectedItem;
				else if($index % 2)
					$itemType=TListItemType::AlternatingItem;
				else
					$itemType=TListItemType::Item;
				$items->add($this->createItemInternal($index,$dsIndex,$itemType,false,null,$columns));
				$dsIndex++;
			}
			if($index>0)
			{
				$this->_footer=$this->createItemInternal(-1,-1,TListItemType::Footer,false,null,$columns);
				if($allowPaging)
					$this->_bottomPager=$this->createPager();
			}
		}
		if(!$dsIndex && $this->_emptyTemplate!==null)
		{
			$this->_useEmptyTemplate=true;
			$this->_emptyTemplate->instantiateIn($this);
		}
	}

	/**
	 * Performs databinding to populate datagrid items from data source.
	 * This method is invoked by {@link dataBind()}.
	 * You may override this function to provide your own way of data population.
	 * @param Traversable the bound data
	 */
	protected function performDataBinding($data)
	{
		$this->reset();
		$keys=$this->getDataKeys();
		$keys->clear();
		$keyField=$this->getDataKeyField();

		// get all columns
		if($this->getAutoGenerateColumns())
		{
			$columns=new TList($this->getColumns());
			$autoColumns=$this->createAutoColumns($data);
			$columns->mergeWith($autoColumns);
		}
		else
			$columns=$this->getColumns();
		$this->_allColumns=$columns;

		$items=$this->getItems();

		$index=0;
		$allowPaging=$this->getAllowPaging() && ($data instanceof TPagedDataSource);
		$dsIndex=$allowPaging?$data->getFirstIndexInPage():0;
		$this->setViewState('DataSourceIndex',$dsIndex,0);
		if($columns->getCount())
		{
			foreach($columns as $column)
				$column->initialize();

			$selectedIndex=$this->getSelectedItemIndex();
			$editIndex=$this->getEditItemIndex();
			foreach($data as $key=>$row)
			{
				if($keyField!=='')
					$keys->add($this->getDataFieldValue($row,$keyField));
				else
					$keys->add($key);
				if($index===0)
				{
					if($allowPaging)
						$this->_topPager=$this->createPager();
					$this->_header=$this->createItemInternal(-1,-1,TListItemType::Header,true,null,$columns);
				}
				if($index===$editIndex)
					$itemType=TListItemType::EditItem;
				else if($index===$selectedIndex)
					$itemType=TListItemType::SelectedItem;
				else if($index % 2)
					$itemType=TListItemType::AlternatingItem;
				else
					$itemType=TListItemType::Item;
				$items->add($this->createItemInternal($index,$dsIndex,$itemType,true,$row,$columns));
				$index++;
				$dsIndex++;
			}
			if($index>0)
			{
				$this->_footer=$this->createItemInternal(-1,-1,TListItemType::Footer,true,null,$columns);
				if($allowPaging)
					$this->_bottomPager=$this->createPager();
			}
		}
		$this->setViewState('ItemCount',$index,0);
		if(!$dsIndex && $this->_emptyTemplate!==null)
		{
			$this->_useEmptyTemplate=true;
			$this->_emptyTemplate->instantiateIn($this);
			$this->dataBindChildren();
		}
	}

	/**
	 * Merges consecutive cells who have the same text.
	 * @since 3.1.1
	 */
	private function groupCells()
	{
		if(($columns=$this->_allColumns)===null)
			return;
		$items=$this->getItems();
		foreach($columns as $id=>$column)
		{
			if(!$column->getEnableCellGrouping())
				continue;
			$prevCell=null;
			$prevCellText=null;
			foreach($items as $item)
			{
				$itemType=$item->getItemType();
				$cell=$item->getCells()->itemAt($id);
				if(!$cell->getVisible())
					continue;
				if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem)
				{
					if(($cellText=$this->getCellText($cell))==='')
					{
						$prevCell=null;
						$prevCellText=null;
						continue;
					}
					if($prevCell===null || $prevCellText!==$cellText)
					{
						$prevCell=$cell;
						$prevCellText=$cellText;
					}
					else
					{
						if(($rowSpan=$prevCell->getRowSpan())===0)
							$rowSpan=1;
						$prevCell->setRowSpan($rowSpan+1);
						$cell->setVisible(false);
					}
				}
			}
		}
	}

	private function getCellText($cell)
	{
		if(($data=$cell->getText())==='' && $cell->getHasControls())
		{
			$controls=$cell->getControls();
			foreach($controls as $control)
			{
				if($control instanceof IDataRenderer)
					return $control->getData();
			}
		}
		return $data;
	}

	/**
	 * Creates a datagrid item instance based on the item type and index.
	 * @param integer zero-based item index
	 * @param TListItemType item type
	 * @return TDataGridItem created data list item
	 */
	protected function createItem($itemIndex,$dataSourceIndex,$itemType)
	{
		return new TDataGridItem($itemIndex,$dataSourceIndex,$itemType);
	}

	private function createItemInternal($itemIndex,$dataSourceIndex,$itemType,$dataBind,$dataItem,$columns)
	{
		$item=$this->createItem($itemIndex,$dataSourceIndex,$itemType);
		$this->initializeItem($item,$columns);
		$param=new TDataGridItemEventParameter($item);
		if($dataBind)
		{
			$item->setDataItem($dataItem);
			$this->onItemCreated($param);
			$this->getControls()->add($item);
			$item->dataBind();
			$this->onItemDataBound($param);
		}
		else
		{
			$this->onItemCreated($param);
			$this->getControls()->add($item);
		}
		return $item;
	}

	/**
	 * Initializes a datagrid item and cells inside it
	 * @param TDataGrid datagrid item to be initialized
	 * @param TDataGridColumnCollection datagrid columns to be used to initialize the cells in the item
	 */
	protected function initializeItem($item,$columns)
	{
		$cells=$item->getCells();
		$itemType=$item->getItemType();
		$index=0;
		foreach($columns as $column)
		{
			if($itemType===TListItemType::Header)
				$cell=new TTableHeaderCell;
			else
				$cell=new TTableCell;
			if(($id=$column->getID())!=='')
				$item->registerObject($id,$cell);
			$cells->add($cell);
			$column->initializeCell($cell,$index,$itemType);
			$index++;
		}
	}

	private function createPager()
	{
		$pager=new TDataGridPager($this);
		$this->buildPager($pager);
		$this->onPagerCreated(new TDataGridPagerEventParameter($pager));
		$this->getControls()->add($pager);
		return $pager;
	}

	/**
	 * Builds the pager content based on pager style.
	 * @param TDataGridPager the container for the pager
	 */
	protected function buildPager($pager)
	{
		switch($this->getPagerStyle()->getMode())
		{
			case TDataGridPagerMode::NextPrev:
				$this->buildNextPrevPager($pager);
				break;
			case TDataGridPagerMode::Numeric:
				$this->buildNumericPager($pager);
				break;
		}
	}

	/**
	 * Creates a pager button.
	 * Depending on the button type, a TLinkButton or a TButton may be created.
	 * If it is enabled (clickable), its command name and parameter will also be set.
	 * Derived classes may override this method to create additional types of buttons, such as TImageButton.
	 * @param string button type, either LinkButton or PushButton
	 * @param boolean whether the button should be enabled
	 * @param string caption of the button
	 * @param string CommandName corresponding to the OnCommand event of the button
	 * @param string CommandParameter corresponding to the OnCommand event of the button
	 * @return mixed the button instance
	 */
	protected function createPagerButton($buttonType,$enabled,$text,$commandName,$commandParameter)
	{
		if($buttonType===TDataGridPagerButtonType::LinkButton)
		{
			if($enabled)
				$button=new TLinkButton;
			else
			{
				$button=new TLabel;
				$button->setText($text);
				return $button;
			}
		}
		else
		{
			$button=new TButton;
			if(!$enabled)
				$button->setEnabled(false);
		}
		$button->setText($text);
		$button->setCommandName($commandName);
		$button->setCommandParameter($commandParameter);
		$button->setCausesValidation(false);
		return $button;
	}

	/**
	 * Builds a next-prev pager
	 * @param TDataGridPager the container for the pager
	 */
	protected function buildNextPrevPager($pager)
	{
		$style=$this->getPagerStyle();
		$buttonType=$style->getButtonType();
		$controls=$pager->getControls();
		$currentPageIndex=$this->getCurrentPageIndex();
		if($currentPageIndex===0)
		{
			$label=$this->createPagerButton($buttonType,false,$style->getPrevPageText(),'','');
			$controls->add($label);
		}
		else
		{
			$button=$this->createPagerButton($buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,self::CMD_PAGE_PREV);
			$controls->add($button);
		}
		$controls->add("\n");
		if($currentPageIndex===$this->getPageCount()-1)
		{
			$label=$this->createPagerButton($buttonType,false,$style->getNextPageText(),'','');
			$controls->add($label);
		}
		else
		{
			$button=$this->createPagerButton($buttonType,true,$style->getNextPageText(),self::CMD_PAGE,self::CMD_PAGE_NEXT);
			$controls->add($button);
		}
	}

	/**
	 * Builds a numeric pager
	 * @param TDataGridPager the container for the pager
	 */
	protected function buildNumericPager($pager)
	{
		$style=$this->getPagerStyle();
		$buttonType=$style->getButtonType();
		$controls=$pager->getControls();
		$pageCount=$this->getPageCount();
		$pageIndex=$this->getCurrentPageIndex()+1;
		$maxButtonCount=$style->getPageButtonCount();
		$buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount;
		$startPageIndex=1;
		$endPageIndex=$buttonCount;
		if($pageIndex>$endPageIndex)
		{
			$startPageIndex=((int)(($pageIndex-1)/$maxButtonCount))*$maxButtonCount+1;
			if(($endPageIndex=$startPageIndex+$maxButtonCount-1)>$pageCount)
				$endPageIndex=$pageCount;
			if($endPageIndex-$startPageIndex+1<$maxButtonCount)
			{
				if(($startPageIndex=$endPageIndex-$maxButtonCount+1)<1)
					$startPageIndex=1;
			}
		}

		if($startPageIndex>1)
		{
			$prevPageIndex=$startPageIndex-1;
			$button=$this->createPagerButton($buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,"$prevPageIndex");
			$controls->add($button);
			$controls->add("\n");
		}

		for($i=$startPageIndex;$i<=$endPageIndex;++$i)
		{
			if($i===$pageIndex)
			{
				$label=$this->createPagerButton($buttonType,false,"$i",'','');
				$controls->add($label);
			}
			else
			{
				$button=$this->createPagerButton($buttonType,true,"$i",self::CMD_PAGE,"$i");
				$controls->add($button);
			}
			if($i<$endPageIndex)
				$controls->add("\n");
		}

		if($pageCount>$endPageIndex)
		{
			$controls->add("\n");
			$nextPageIndex=$endPageIndex+1;
			$button=$this->createPagerButton($buttonType,true,$style->getNextPageText(),self::CMD_PAGE,"$nextPageIndex");
			$controls->add($button);
		}
	}

	/**
	 * Automatically generates datagrid columns based on datasource schema
	 * @param Traversable data source bound to the datagrid
	 * @return TDataGridColumnCollection
	 */
	protected function createAutoColumns($dataSource)
	{
		if(!$dataSource)
			return null;
		$autoColumns=$this->getAutoColumns();
		$autoColumns->clear();
		foreach($dataSource as $row)
		{
			foreach($row as $key=>$value)
			{
				$column=new TBoundColumn;
				if(is_string($key))
				{
					$column->setHeaderText($key);
					$column->setDataField($key);
					$column->setSortExpression($key);
					$autoColumns->add($column);
				}
				else
				{
					$column->setHeaderText(TListItemType::Item);
					$column->setDataField($key);
					$column->setSortExpression(TListItemType::Item);
					$autoColumns->add($column);
				}
			}
			break;
		}
		return $autoColumns;
	}

	/**
	 * Applies styles to items, header, footer and separators.
	 * Item styles are applied in a hierarchical way. Style in higher hierarchy
	 * will inherit from styles in lower hierarchy.
	 * Starting from the lowest hierarchy, the item styles include
	 * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle},
	 * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}.
	 * Therefore, if background color is set as red in {@link getItemStyle ItemStyle},
	 * {@link getEditItemStyle EditItemStyle} will also have red background color
	 * unless it is set to a different value explicitly.
	 */
	protected function applyItemStyles()
	{
		$itemStyle=$this->getViewState('ItemStyle',null);

		$alternatingItemStyle=$this->getViewState('AlternatingItemStyle',null);
		if($itemStyle!==null)
		{
			if($alternatingItemStyle===null)
				$alternatingItemStyle=$itemStyle;
			else
				$alternatingItemStyle->mergeWith($itemStyle);
		}

		$selectedItemStyle=$this->getViewState('SelectedItemStyle',null);

		$editItemStyle=$this->getViewState('EditItemStyle',null);
		if($selectedItemStyle!==null)
		{
			if($editItemStyle===null)
				$editItemStyle=$selectedItemStyle;
			else
				$editItemStyle->mergeWith($selectedItemStyle);
		}

		$headerStyle=$this->getViewState('HeaderStyle',null);
		$footerStyle=$this->getViewState('FooterStyle',null);
		$pagerStyle=$this->getViewState('PagerStyle',null);
		$separatorStyle=$this->getViewState('SeparatorStyle',null);

		foreach($this->getControls() as $index=>$item)
		{
			if(!($item instanceof TDataGridItem) && !($item instanceof TDataGridPager))
				continue;
			$itemType=$item->getItemType();
			switch($itemType)
			{
				case TListItemType::Header:
					if($headerStyle)
						$item->getStyle()->mergeWith($headerStyle);
					if(!$this->getShowHeader())
						$item->setVisible(false);
					break;
				case TListItemType::Footer:
					if($footerStyle)
						$item->getStyle()->mergeWith($footerStyle);
					if(!$this->getShowFooter())
						$item->setVisible(false);
					break;
				case TListItemType::Separator:
					if($separatorStyle)
						$item->getStyle()->mergeWith($separatorStyle);
					break;
				case TListItemType::Item:
					if($itemStyle)
						$item->getStyle()->mergeWith($itemStyle);
					break;
				case TListItemType::AlternatingItem:
					if($alternatingItemStyle)
						$item->getStyle()->mergeWith($alternatingItemStyle);
					break;
				case TListItemType::SelectedItem:
					if($selectedItemStyle)
						$item->getStyle()->mergeWith($selectedItemStyle);
					if($index % 2==1)
					{
						if($itemStyle)
							$item->getStyle()->mergeWith($itemStyle);
					}
					else
					{
						if($alternatingItemStyle)
							$item->getStyle()->mergeWith($alternatingItemStyle);
					}
					break;
				case TListItemType::EditItem:
					if($editItemStyle)
						$item->getStyle()->mergeWith($editItemStyle);
					if($index % 2==1)
					{
						if($itemStyle)
							$item->getStyle()->mergeWith($itemStyle);
					}
					else
					{
						if($alternatingItemStyle)
							$item->getStyle()->mergeWith($alternatingItemStyle);
					}
					break;
				case TListItemType::Pager:
					if($pagerStyle)
					{
						$item->getStyle()->mergeWith($pagerStyle);
						if($index===0)
						{
							if($pagerStyle->getPosition()===TDataGridPagerPosition::Bottom || !$pagerStyle->getVisible())
								$item->setVisible(false);
						}
						else
						{
							if($pagerStyle->getPosition()===TDataGridPagerPosition::Top || !$pagerStyle->getVisible())
								$item->setVisible(false);
						}
					}
					break;
				default:
					break;
			}
			if($this->_columns && $itemType!==TListItemType::Pager)
			{
				$n=$this->_columns->getCount();
				$cells=$item->getCells();
				for($i=0;$i<$n;++$i)
				{
					$cell=$cells->itemAt($i);
					$column=$this->_columns->itemAt($i);
					if(!$column->getVisible())
						$cell->setVisible(false);
					else
					{
						if($itemType===TListItemType::Header)
							$style=$column->getHeaderStyle(false);
						else if($itemType===TListItemType::Footer)
							$style=$column->getFooterStyle(false);
						else
							$style=$column->getItemStyle(false);
						if($style!==null)
							$cell->getStyle()->mergeWith($style);
					}
				}
			}
		}
	}

	/**
	 * Renders the openning tag for the datagrid control which will render table caption if present.
	 * @param THtmlWriter the writer used for the rendering purpose
	 */
	public function renderBeginTag($writer)
	{
		parent::renderBeginTag($writer);
		if(($caption=$this->getCaption())!=='')
		{
			if(($align=$this->getCaptionAlign())!==TTableCaptionAlign::NotSet)
				$writer->addAttribute('align',strtolower($align));
			$writer->renderBeginTag('caption');
			$writer->write($caption);
			$writer->renderEndTag();
		}
	}

	/**
	 * Renders the datagrid.
	 * @param THtmlWriter writer for the rendering purpose
	 */
	public function render($writer)
	{
		if($this->getHasControls())
		{
			$this->groupCells();
			if($this->_useEmptyTemplate)
			{
				$control=new TWebControl;
				$control->setID($this->getClientID());
				$control->copyBaseAttributes($this);
				if($this->getHasStyle())
					$control->getStyle()->copyFrom($this->getStyle());
				$control->renderBeginTag($writer);
				$this->renderContents($writer);
				$control->renderEndTag($writer);
			}
			else if($this->getViewState('ItemCount',0)>0)
			{
				$this->applyItemStyles();
				if($this->_topPager)
				{
					$this->_topPager->renderControl($writer);
					$writer->writeLine();
				}
				$this->renderTable($writer);
				if($this->_bottomPager)
				{
					$writer->writeLine();
					$this->_bottomPager->renderControl($writer);
				}
			}
		}
	}

	/**
	 * Renders the tabular data.
	 * @param THtmlWriter writer
	 */
	protected function renderTable($writer)
	{
		$this->renderBeginTag($writer);
		if($this->_header && $this->_header->getVisible())
		{
			$writer->writeLine();
			if($style=$this->getViewState('TableHeadStyle',null))
				$style->addAttributesToRender($writer);
			$writer->renderBeginTag('thead');
			$this->_header->render($writer);
			$writer->renderEndTag();
		}
		$writer->writeLine();
		if($style=$this->getViewState('TableBodyStyle',null))
			$style->addAttributesToRender($writer);
		$writer->renderBeginTag('tbody');
		foreach($this->getItems() as $item)
			$item->renderControl($writer);
		$writer->renderEndTag();

		if($this->_footer && $this->_footer->getVisible())
		{
			$writer->writeLine();
			if($style=$this->getViewState('TableFootStyle',null))
				$style->addAttributesToRender($writer);
			$writer->renderBeginTag('tfoot');
			$this->_footer->render($writer);
			$writer->renderEndTag();
		}

		$writer->writeLine();
		$this->renderEndTag($writer);
	}
}

/**
 * TDataGridItemEventParameter class
 *
 * TDataGridItemEventParameter encapsulates the parameter data for
 * {@link TDataGrid::onItemCreated OnItemCreated} event of {@link TDataGrid} controls.
 * The {@link getItem Item} property indicates the datagrid item related with the event.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridItemEventParameter extends TEventParameter
{
	/**
	 * The TDataGridItem control responsible for the event.
	 * @var TDataGridItem
	 */
	private $_item=null;

	/**
	 * Constructor.
	 * @param TDataGridItem datagrid item related with the corresponding event
	 */
	public function __construct(TDataGridItem $item)
	{
		$this->_item=$item;
	}

	/**
	 * @return TDataGridItem datagrid item related with the corresponding event
	 */
	public function getItem()
	{
		return $this->_item;
	}
}

/**
 * TDataGridPagerEventParameter class
 *
 * TDataGridPagerEventParameter encapsulates the parameter data for
 * {@link TDataGrid::onPagerCreated OnPagerCreated} event of {@link TDataGrid} controls.
 * The {@link getPager Pager} property indicates the datagrid pager related with the event.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridPagerEventParameter extends TEventParameter
{
	/**
	 * The TDataGridPager control responsible for the event.
	 * @var TDataGridPager
	 */
	private $_pager=null;

	/**
	 * Constructor.
	 * @param TDataGridPager datagrid pager related with the corresponding event
	 */
	public function __construct(TDataGridPager $pager)
	{
		$this->_pager=$pager;
	}

	/**
	 * @return TDataGridPager datagrid pager related with the corresponding event
	 */
	public function getPager()
	{
		return $this->_pager;
	}
}

/**
 * TDataGridCommandEventParameter class
 *
 * TDataGridCommandEventParameter encapsulates the parameter data for
 * {@link TDataGrid::onItemCommand ItemCommand} event of {@link TDataGrid} controls.
 *
 * The {@link getItem Item} property indicates the datagrid item related with the event.
 * The {@link getCommandSource CommandSource} refers to the control that originally
 * raises the Command event.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridCommandEventParameter extends TCommandEventParameter
{
	/**
	 * @var TDataGridItem the TDataGridItem control responsible for the event.
	 */
	private $_item=null;
	/**
	 * @var TControl the control originally raises the <b>Command</b> event.
	 */
	private $_source=null;

	/**
	 * Constructor.
	 * @param TDataGridItem datagrid item responsible for the event
	 * @param TControl original event sender
	 * @param TCommandEventParameter original event parameter
	 */
	public function __construct($item,$source,TCommandEventParameter $param)
	{
		$this->_item=$item;
		$this->_source=$source;
		parent::__construct($param->getCommandName(),$param->getCommandParameter());
	}

	/**
	 * @return TDataGridItem the TDataGridItem control responsible for the event.
	 */
	public function getItem()
	{
		return $this->_item;
	}

	/**
	 * @return TControl the control originally raises the <b>Command</b> event.
	 */
	public function getCommandSource()
	{
		return $this->_source;
	}
}

/**
 * TDataGridSortCommandEventParameter class
 *
 * TDataGridSortCommandEventParameter encapsulates the parameter data for
 * {@link TDataGrid::onSortCommand SortCommand} event of {@link TDataGrid} controls.
 *
 * The {@link getCommandSource CommandSource} property refers to the control
 * that originally raises the OnCommand event, while {@link getSortExpression SortExpression}
 * gives the sort expression carried with the sort command.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridSortCommandEventParameter extends TEventParameter
{
	/**
	 * @var string sort expression
	 */
	private $_sortExpression='';
	/**
	 * @var TControl original event sender
	 */
	private $_source=null;

	/**
	 * Constructor.
	 * @param TControl the control originally raises the <b>OnCommand</b> event.
	 * @param TDataGridCommandEventParameter command event parameter
	 */
	public function __construct($source,TDataGridCommandEventParameter $param)
	{
		$this->_source=$source;
		$this->_sortExpression=$param->getCommandParameter();
	}

	/**
	 * @return TControl the control originally raises the <b>OnCommand</b> event.
	 */
	public function getCommandSource()
	{
		return $this->_source;
	}

	/**
	 * @return string sort expression
	 */
	public function getSortExpression()
	{
		return $this->_sortExpression;
	}
}

/**
 * TDataGridPageChangedEventParameter class
 *
 * TDataGridPageChangedEventParameter encapsulates the parameter data for
 * {@link TDataGrid::onPageIndexChanged PageIndexChanged} event of {@link TDataGrid} controls.
 *
 * The {@link getCommandSource CommandSource} property refers to the control
 * that originally raises the OnCommand event, while {@link getNewPageIndex NewPageIndex}
 * returns the new page index carried with the page command.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridPageChangedEventParameter extends TEventParameter
{
	/**
	 * @var integer new page index
	 */
	private $_newIndex;
	/**
	 * @var TControl original event sender
	 */
	private $_source=null;

	/**
	 * Constructor.
	 * @param TControl the control originally raises the <b>OnCommand</b> event.
	 * @param integer new page index
	 */
	public function __construct($source,$newPageIndex)
	{
		$this->_source=$source;
		$this->_newIndex=$newPageIndex;
	}

	/**
	 * @return TControl the control originally raises the <b>OnCommand</b> event.
	 */
	public function getCommandSource()
	{
		return $this->_source;
	}

	/**
	 * @return integer new page index
	 */
	public function getNewPageIndex()
	{
		return $this->_newIndex;
	}
}

/**
 * TDataGridItem class
 *
 * A TDataGridItem control represents an item in the {@link TDataGrid} control,
 * such as heading section, footer section, or a data item.
 * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}>
 * and {@link getDataItem DataItem} properties, respectively. The type of the item
 * is given by {@link getItemType ItemType} property. Property {@link getDataSourceIndex DataSourceIndex}
 * gives the index of the item from the bound data source.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridItem extends TTableRow implements INamingContainer
{
	/**
	 * @var integer index of the data item in the Items collection of datagrid
	 */
	private $_itemIndex='';
	/**
	 * @var integer index of the item from the bound data source
	 */
	private $_dataSourceIndex=0;
	/**
	 * type of the TDataGridItem
	 * @var string
	 */
	private $_itemType='';
	/**
	 * value of the data item
	 * @var mixed
	 */
	private $_data=null;

	/**
	 * Constructor.
	 * @param integer zero-based index of the item in the item collection of datagrid
	 * @param TListItemType item type
	 */
	public function __construct($itemIndex,$dataSourceIndex,$itemType)
	{
		$this->_itemIndex=$itemIndex;
		$this->_dataSourceIndex=$dataSourceIndex;
		$this->setItemType($itemType);
		if($itemType===TListItemType::Header)
			$this->setTableSection(TTableRowSection::Header);
		else if($itemType===TListItemType::Footer)
			$this->setTableSection(TTableRowSection::Footer);
	}

	/**
	 * @return TListItemType item type.
	 */
	public function getItemType()
	{
		return $this->_itemType;
	}

	/**
	 * @param TListItemType item type
	 */
	public function setItemType($value)
	{
		$this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType');
	}

	/**
	 * @return integer zero-based index of the item in the item collection of datagrid
	 */
	public function getItemIndex()
	{
		return $this->_itemIndex;
	}

	/**
	 * @return integer the index of the datagrid item from the bound data source
	 */
	public function getDataSourceIndex()
	{
		return $this->_dataSourceIndex;
	}

	/**
	 * @return mixed data associated with the item
	 * @since 3.1.0
	 */
	public function getData()
	{
		return $this->_data;
	}

	/**
	 * @param mixed data to be associated with the item
	 * @since 3.1.0
	 */
	public function setData($value)
	{
		$this->_data=$value;
	}

	/**
	 * This property is deprecated since v3.1.0.
	 * @return mixed data associated with the item
	 * @deprecated deprecated since v3.1.0. Use {@link getData} instead.
	 */
	public function getDataItem()
	{
		return $this->getData();
	}

	/**
	 * This property is deprecated since v3.1.0.
	 * @param mixed data to be associated with the item
	 * @deprecated deprecated since version 3.1.0. Use {@link setData} instead.
	 */
	public function setDataItem($value)
	{
		return $this->setData($value);
	}

	/**
	 * This method overrides parent's implementation by wrapping event parameter
	 * for <b>OnCommand</b> event with item information.
	 * @param TControl the sender of the event
	 * @param TEventParameter event parameter
	 * @return boolean whether the event bubbling should stop here.
	 */
	public function bubbleEvent($sender,$param)
	{
		if($param instanceof TCommandEventParameter)
		{
			$this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param));
			return true;
		}
		else
			return false;
	}
}


/**
 * TDataGridPager class.
 *
 * TDataGridPager represents a datagrid pager.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridPager extends TPanel implements INamingContainer
{
	private $_dataGrid;

	/**
	 * Constructor.
	 * @param TDataGrid datagrid object
	 */
	public function __construct($dataGrid)
	{
		$this->_dataGrid=$dataGrid;
	}

	/**
	 * This method overrides parent's implementation by wrapping event parameter
	 * for <b>OnCommand</b> event with item information.
	 * @param TControl the sender of the event
	 * @param TEventParameter event parameter
	 * @return boolean whether the event bubbling should stop here.
	 */
	public function bubbleEvent($sender,$param)
	{
		if($param instanceof TCommandEventParameter)
		{
			$this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param));
			return true;
		}
		else
			return false;
	}

	/**
	 * @return TDataGrid the datagrid owning this pager
	 */
	public function getDataGrid()
	{
		return $this->_dataGrid;
	}

	/**
	 * @return string item type.
	 */
	public function getItemType()
	{
		return TListItemType::Pager;
	}
}


/**
 * TDataGridItemCollection class.
 *
 * TDataGridItemCollection represents a collection of data grid items.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridItemCollection extends TList
{
	/**
	 * Inserts an item at the specified position.
	 * This overrides the parent implementation by inserting only TDataGridItem.
	 * @param integer the speicified position.
	 * @param mixed new item
	 * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridItem.
	 */
	public function insertAt($index,$item)
	{
		if($item instanceof TDataGridItem)
			parent::insertAt($index,$item);
		else
			throw new TInvalidDataTypeException('datagriditemcollection_datagriditem_required');
	}
}

/**
 * TDataGridColumnCollection class.
 *
 * TDataGridColumnCollection represents a collection of data grid columns.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataGridColumnCollection extends TList
{
	/**
	 * the control that owns this collection.
	 * @var TControl
	 */
	private $_o;

	/**
	 * Constructor.
	 * @param TDataGrid the control that owns this collection.
	 */
	public function __construct(TDataGrid $owner)
	{
		$this->_o=$owner;
	}

	/**
	 * @return TDataGrid the control that owns this collection.
	 */
	protected function getOwner()
	{
		return $this->_o;
	}

	/**
	 * Inserts an item at the specified position.
	 * This overrides the parent implementation by inserting only TDataGridColumn.
	 * @param integer the speicified position.
	 * @param mixed new item
	 * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridColumn.
	 */
	public function insertAt($index,$item)
	{
		if($item instanceof TDataGridColumn)
		{
			$item->setOwner($this->_o);
			parent::insertAt($index,$item);
		}
		else
			throw new TInvalidDataTypeException('datagridcolumncollection_datagridcolumn_required');
	}
}

/**
 * TDataGridPagerMode class.
 * TDataGridPagerMode defines the enumerable type for the possible modes that a datagrid pager can take.
 *
 * The following enumerable values are defined:
 * - NextPrev: pager buttons are displayed as next and previous pages
 * - Numeric: pager buttons are displayed as numeric page numbers
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0.4
 */
class TDataGridPagerMode extends TEnumerable
{
	const NextPrev='NextPrev';
	const Numeric='Numeric';
}


/**
 * TDataGridPagerButtonType class.
 * TDataGridPagerButtonType defines the enumerable type for the possible types of datagrid pager buttons.
 *
 * The following enumerable values are defined:
 * - LinkButton: link buttons
 * - PushButton: form submit buttons
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0.4
 */
class TDataGridPagerButtonType extends TEnumerable
{
	const LinkButton='LinkButton';
	const PushButton='PushButton';
}


/**
 * TDataGridPagerPosition class.
 * TDataGridPagerPosition defines the enumerable type for the possible positions that a datagrid pager can be located at.
 *
 * The following enumerable values are defined:
 * - Bottom: pager appears only at the bottom of the data grid.
 * - Top: pager appears only at the top of the data grid.
 * - TopAndBottom: pager appears on both top and bottom of the data grid.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataGrid.php 2211 2007-09-07 13:34:10Z xue $
 * @package System.Web.UI.WebControls
 * @since 3.0.4
 */
class TDataGridPagerPosition extends TEnumerable
{
	const Bottom='Bottom';
	const Top='Top';
	const TopAndBottom='TopAndBottom';
}

?>