From aac749d4fbfdf3508d97f7dfe4d7c2c03597edbb Mon Sep 17 00:00:00 2001 From: xue <> Date: Mon, 26 Jun 2006 02:52:44 +0000 Subject: Merge from 3.0 branch till 1202. --- framework/Web/UI/WebControls/TPager.php | 759 ++++++++++++++++++++++++++++++++ 1 file changed, 759 insertions(+) create mode 100644 framework/Web/UI/WebControls/TPager.php (limited to 'framework/Web/UI/WebControls/TPager.php') diff --git a/framework/Web/UI/WebControls/TPager.php b/framework/Web/UI/WebControls/TPager.php new file mode 100644 index 00000000..f802e8d7 --- /dev/null +++ b/framework/Web/UI/WebControls/TPager.php @@ -0,0 +1,759 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataBoundControl'); +Prado::using('System.Web.UI.WebControls.TPanelStyle'); +Prado::using('System.Collections.TPagedDataSource'); +Prado::using('System.Collections.TDummyDataSource'); + +/** + * TPager class. + * + * TPager creates a pager that controls the paging of the data populated + * to a data-bound control specified by {@link setControlToPaginate ControlToPaginate}. + * To specify the number of data items displayed on each page, set + * the {@link setPageSize PageSize} property, and to specify which + * page of data to be displayed, set {@link setCurrentPageIndex CurrentPageIndex}. + * + * When the size of the original data is too big to be loaded all in the memory, + * one can enable custom paging. In custom paging, the total number of data items + * is specified manually via {@link setVirtualItemCount VirtualItemCount}, and the data source + * only needs to contain the current page of data. To enable custom paging, + * set {@link setAllowCustomPaging AllowCustomPaging} to true. + * + * TPager can be in one of three {@link setMode Mode}: + * - NextPrev: a next page and a previous page button are rendered. + * - Numeric: a list of page index buttons are rendered. + * - List: a dropdown list of page indices are rendered. + * + * TPager raises an {@link onPageIndexChanged OnPageIndexChanged} event when + * the end-user interacts with it and specifies a new page (e.g. clicking + * on a page button that leads to a new page.) The new page index may be obtained + * from the event parameter's property {@link TPagerPageChangedEventParameter::getNewPageIndex NewPageIndex}. + * + * When multiple pagers are associated with the same data-bound control, + * these pagers will do synchronization among each other so that the interaction + * with one pager will automatically update the UI of the other relevant pagers. + * + * The following example shows a typical usage of TPager: + * + * $pager->ControlToPaginate="Path.To.Control"; + * $pager->DataSource=$data; + * $pager->dataBind(); + * + * Note, the data is assigned to the pager and dataBind() is invoked against the pager. + * Without the pager, one has to set datasource for the target control and call + * its dataBind() directly. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Web.UI.WebControls + * @since 3.0.2 + */ +class TPager extends TDataBoundControl implements INamingContainer +{ + /** + * Command name that TPager understands. + */ + const CMD_PAGE='Page'; + const CMD_PAGE_NEXT='Next'; + const CMD_PAGE_PREV='Previous'; + const CMD_PAGE_FIRST='First'; + const CMD_PAGE_LAST='Last'; + + /** + * @var array list of all pagers, used to synchronize their appearance + */ + static private $_pagers=array(); + + /** + * Registers the pager itself to a global list. + * This method overrides the parent implementation and is invoked during + * OnInit control lifecycle. + * @param mixed event parameter + */ + public function onInit($param) + { + parent::onInit($param); + self::$_pagers[]=$this; + } + + /** + * Unregisters the pager from a global list. + * This method overrides the parent implementation and is invoked during + * OnUnload control lifecycle. + * @param mixed event parameter + */ + public function onUnload($param) + { + parent::onUnload($param); + if(($index=array_search($this,self::$_pagers,true))!==false) + unset(self::$_pagers[$index]); + } + + /** + * Restores the pager state. + * This method overrides the parent implementation and is invoked when + * the control is loading persistent state. + */ + public function loadState() + { + parent::loadState(); + if(!$this->getEnableViewState(true)) + return; + if(!$this->getIsDataBound()) + $this->restoreFromViewState(); + } + + /** + * @return string the ID path of the control whose content would be paginated. + */ + public function getControlToPaginate() + { + return $this->getViewState('ControlToPaginate',''); + } + + /** + * Sets the ID path of the control whose content would be paginated. + * The ID path is the dot-connected IDs of the controls reaching from + * the pager's naming container to the target control. + * @param string the ID path + */ + public function setControlToPaginate($value) + { + $this->setViewState('ControlToPaginate',$value,''); + } + + /** + * @return integer the zero-based index of the current page. Defaults to 0. + */ + public function getCurrentPageIndex() + { + return $this->getViewState('CurrentPageIndex',0); + } + + /** + * @param integer the zero-based index of the current page + * @throws TInvalidDataValueException if the value is less than 0 + */ + public function setCurrentPageIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('pager_currentpageindex_invalid'); + $this->setViewState('CurrentPageIndex',$value,0); + } + + /** + * @return integer the number of data items on each page. Defaults to 10. + */ + public function getPageSize() + { + return $this->getViewState('PageSize',10); + } + + /** + * @param integer the number of data items on each page. + * @throws TInvalidDataValueException if the value is less than 1 + */ + public function setPageSize($value) + { + if(($value=TPropertyValue::ensureInteger($value))<1) + throw new TInvalidDataValueException('pager_pagesize_invalid'); + $this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10); + } + + /** + * @return integer number of pages + */ + public function getPageCount() + { + if(($count=$this->getItemCount())<1) + return 1; + else + { + $pageSize=$this->getPageSize(); + return (int)(($count+$pageSize-1)/$pageSize); + } + } + + /** + * @return boolean whether the custom paging is enabled. Defaults to false. + */ + public function getAllowCustomPaging() + { + return $this->getViewState('AllowCustomPaging',false); + } + + /** + * Sets a value indicating whether the custom paging should be enabled. + * When the pager is in custom paging mode, the {@link setVirtualItemCount VirtualItemCount} + * property is used to determine the paging, and the data items in the + * {@link setDataSource DataSource} are considered to be in the current page. + * @param boolean whether the custom paging is enabled + */ + public function setAllowCustomPaging($value) + { + $this->setViewState('AllowCustomPaging',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return integer virtual number of data items in the data source. Defaults to 0. + * @see setAllowCustomPaging + */ + public function getVirtualItemCount() + { + return $this->getViewState('VirtualItemCount',0); + } + + /** + * @param integer virtual number of data items in the data source. + * @throws TInvalidDataValueException if the value is less than 0 + * @see setAllowCustomPaging + */ + public function setVirtualItemCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('pager_virtualitemcount_invalid'); + $this->setViewState('VirtualItemCount',$value,0); + } + + /** + * @return integer total number of items in the datasource. + */ + public function getItemCount() + { + return $this->getViewState('ItemCount',0); + } + + /** + * @return string pager mode. Defaults to 'NextPrev'. + */ + public function getMode() + { + return $this->getViewState('Mode','NextPrev'); + } + + /** + * @param string pager mode. Valid values include 'NextPrev', 'Numeric' and 'List'. + */ + public function setMode($value) + { + $this->setViewState('Mode',TPropertyValue::ensureEnum($value,'NextPrev','Numeric','List'),'NextPrev'); + } + + /** + * @return string the type of command button for paging. Defaults to 'LinkButton'. + */ + public function getButtonType() + { + return $this->getViewState('ButtonType','LinkButton'); + } + + /** + * @param string the type of command button for paging. Valid values include 'LinkButton' and 'PushButton'. + */ + public function setButtonType($value) + { + $this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'LinkButton','PushButton')); + } + + /** + * @return string text for the next page button. Defaults to '>'. + */ + public function getNextPageText() + { + return $this->getViewState('NextPageText','>'); + } + + /** + * @param string text for the next page button. + */ + public function setNextPageText($value) + { + $this->setViewState('NextPageText',$value,'>'); + } + + /** + * @return string text for the previous page button. Defaults to '<'. + */ + public function getPrevPageText() + { + return $this->getViewState('PrevPageText','<'); + } + + /** + * @param string text for the next page button. + */ + public function setPrevPageText($value) + { + $this->setViewState('PrevPageText',$value,'<'); + } + + /** + * @return string text for the first page button. Defaults to '<<'. + */ + public function getFirstPageText() + { + return $this->getViewState('FirstPageText','<<'); + } + + /** + * @param string text for the first page button. If empty, the first page button will not be rendered. + */ + public function setFirstPageText($value) + { + $this->setViewState('FirstPageText',$value,'<<'); + } + + /** + * @return string text for the last page button. Defaults to '>>'. + */ + public function getLastPageText() + { + return $this->getViewState('LastPageText','>>'); + } + + /** + * @param string text for the last page button. If empty, the last page button will not be rendered. + */ + public function setLastPageText($value) + { + $this->setViewState('LastPageText',$value,'>>'); + } + + /** + * @return integer maximum number of pager buttons to be displayed. Defaults to 10. + */ + public function getPageButtonCount() + { + return $this->getViewState('PageButtonCount',10); + } + + /** + * @param integer maximum number of pager buttons to be displayed + * @throws TInvalidDataValueException if the value is less than 1. + */ + public function setPageButtonCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<1) + throw new TInvalidDataValueException('pager_pagebuttoncount_invalid'); + $this->setViewState('PageButtonCount',$value,10); + } + + /** + * @return TPagedDataSource creates a paged data source + */ + private function createPagedDataSource() + { + $ds=new TPagedDataSource; + $ds->setAllowPaging(true); + $customPaging=$this->getAllowCustomPaging(); + $ds->setAllowCustomPaging($customPaging); + $ds->setCurrentPageIndex($this->getCurrentPageIndex()); + $ds->setPageSize($this->getPageSize()); + if($customPaging) + $ds->setVirtualItemCount($this->getVirtualItemCount()); + return $ds; + } + + /** + * Removes the existing child controls. + */ + protected function reset() + { + $this->getControls()->clear(); + } + + /** + * Restores the pager from viewstate. + */ + protected function restoreFromViewState() + { + $this->reset(); + $ds=$this->createPagedDataSource(); + $ds->setDataSource(new TDummyDataSource($this->getItemCount())); + $this->buildPager($ds); + } + + /** + * Performs databinding to populate data 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(); + + $controlID=$this->getControlToPaginate(); + if(($targetControl=$this->getNamingContainer()->findControl($controlID))===null || !($targetControl instanceof TDataBoundControl)) + throw new TConfigurationException('pager_controltopaginate_invalid',$controlID); + + $ds=$this->createPagedDataSource(); + $ds->setDataSource($this->getDataSource()); + $this->setViewState('ItemCount',$ds->getDataSourceCount()); + + $this->buildPager($ds); + $this->synchronizePagers($targetControl,$ds); + + $targetControl->setDataSource($ds); + $targetControl->dataBind(); + } + + /** + * Synchronizes the state of all pagers who have the same {@link getControlToPaginate ControlToPaginate}. + * @param TDataBoundControl the control whose content is to be paginated + * @param TPagedDataSource the paged data source associated with the pager + */ + protected function synchronizePagers($targetControl,$dataSource) + { + foreach(self::$_pagers as $pager) + { + if($pager!==$this && $pager->getNamingContainer()->findControl($pager->getControlToPaginate())===$targetControl) + { + $pager->reset(); + $pager->setCurrentPageIndex($dataSource->getCurrentPageIndex()); + $customPaging=$dataSource->getAllowCustomPaging(); + $pager->setAllowCustomPaging($customPaging); + $pager->setViewState('ItemCount',$dataSource->getDataSourceCount()); + if($customPaging) + $pager->setVirtualItemCount($dataSource->getVirtualItemCount()); + $pager->buildPager($dataSource); + } + } + } + + /** + * Builds the pager content based on the pager mode. + * Current implementation includes building 'NextPrev', 'Numeric' and 'List' pagers. + * Derived classes may override this method to provide additional pagers. + * @param TPagedDataSource data source bound to the target control + */ + protected function buildPager($dataSource) + { + switch($this->getMode()) + { + case 'NextPrev': + $this->buildNextPrevPager($dataSource); + break; + case 'Numeric': + $this->buildNumericPager($dataSource); + break; + case 'List': + $this->buildListPager($dataSource); + 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==='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 TPagedDataSource data source bound to the pager + */ + protected function buildNextPrevPager($dataSource) + { + $buttonType=$this->getButtonType(); + $controls=$this->getControls(); + if($dataSource->getIsFirstPage()) + { + if(($text=$this->getFirstPageText())!=='') + { + $label=$this->createPagerButton($buttonType,false,$text,'',''); + $controls->add($label); + $controls->add("\n"); + } + $label=$this->createPagerButton($buttonType,false,$this->getPrevPageText(),'',''); + $controls->add($label); + } + else + { + if(($text=$this->getFirstPageText())!=='') + { + $button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_FIRST,''); + $controls->add($button); + $controls->add("\n"); + } + $button=$this->createPagerButton($buttonType,true,$this->getPrevPageText(),self::CMD_PAGE_PREV,''); + $controls->add($button); + } + $controls->add("\n"); + if($dataSource->getIsLastPage()) + { + $label=$this->createPagerButton($buttonType,false,$this->getNextPageText(),'',''); + $controls->add($label); + if(($text=$this->getLastPageText())!=='') + { + $controls->add("\n"); + $label=$this->createPagerButton($buttonType,false,$text,'',''); + $controls->add($label); + } + } + else + { + $button=$this->createPagerButton($buttonType,true,$this->getNextPageText(),self::CMD_PAGE_NEXT,''); + $controls->add($button); + if(($text=$this->getLastPageText())!=='') + { + $controls->add("\n"); + $button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_LAST,''); + $controls->add($button); + } + } + } + + /** + * Builds a numeric pager + * @param TPagedDataSource data source bound to the pager + */ + protected function buildNumericPager($dataSource) + { + $buttonType=$this->getButtonType(); + $controls=$this->getControls(); + $pageCount=$dataSource->getPageCount(); + $pageIndex=$dataSource->getCurrentPageIndex()+1; + $maxButtonCount=$this->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) + { + if(($text=$this->getFirstPageText())!=='') + { + $button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_FIRST,''); + $controls->add($button); + $controls->add("\n"); + } + $prevPageIndex=$startPageIndex-1; + $button=$this->createPagerButton($buttonType,true,$this->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,$this->getNextPageText(),self::CMD_PAGE,"$nextPageIndex"); + $controls->add($button); + if(($text=$this->getLastPageText())!=='') + { + $controls->add("\n"); + $button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_LAST,''); + $controls->add($button); + } + } + } + + /** + * Builds a dropdown list pager + * @param TPagedDataSource data source bound to the pager + */ + protected function buildListPager($dataSource) + { + $list=new TDropDownList; + $this->getControls()->add($list); + $list->setDataSource(range(1,$dataSource->getPageCount())); + $list->dataBind(); + $list->setSelectedIndex($dataSource->getCurrentPageIndex()); + $list->setAutoPostBack(true); + $list->attachEventHandler('OnSelectedIndexChanged',array($this,'listIndexChanged')); + } + + /** + * Event handler to the OnSelectedIndexChanged event of the dropdown list. + * This handler will raise {@link onPageIndexChanged OnPageIndexChanged} event. + * @param TDropDownList the dropdown list control raising the event + * @param TEventParameter event parameter + */ + public function listIndexChanged($sender,$param) + { + $pageIndex=$sender->getSelectedIndex(); + $this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex)); + } + + /** + * This event is raised when page index is changed due to a page button click. + * @param TPagerPageChangedEventParameter event parameter + */ + public function onPageIndexChanged($param) + { + $this->raiseEvent('OnPageIndexChanged',$this,$param); + } + + /** + * Processes a bubbled event. + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand 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) + { + $command=$param->getCommandName(); + if(strcasecmp($command,self::CMD_PAGE)===0) + { + $pageIndex=TPropertyValue::ensureInteger($param->getCommandParameter())-1; + $this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex)); + return true; + } + else if(strcasecmp($command,self::CMD_PAGE_NEXT)===0) + { + $pageIndex=$this->getCurrentPageIndex()+1; + $this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex)); + return true; + } + else if(strcasecmp($command,self::CMD_PAGE_PREV)===0) + { + $pageIndex=$this->getCurrentPageIndex()-1; + $this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex)); + return true; + } + else if(strcasecmp($command,self::CMD_PAGE_FIRST)===0) + { + $this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,0)); + return true; + } + else if(strcasecmp($command,self::CMD_PAGE_LAST)===0) + { + $this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$this->getPageCount()-1)); + return true; + } + return false; + } + else + return false; + } +} + +/** + * TPagerPageChangedEventParameter class + * + * TPagerPageChangedEventParameter encapsulates the parameter data for + * {@link TPager::onPageIndexChanged PageIndexChanged} event of {@link TPager} 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 + * @version $Revision: $ $Date: $ + * @package System.Web.UI.WebControls + * @since 3.0.2 + */ +class TPagerPageChangedEventParameter 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 OnCommand event. + * @param integer new page index + */ + public function __construct($source,$newPageIndex) + { + $this->_source=$source; + $this->_newIndex=$newPageIndex; + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } + + /** + * @return integer new page index + */ + public function getNewPageIndex() + { + return $this->_newIndex; + } +} + +?> \ No newline at end of file -- cgit v1.2.3