Subversion-Projekte lars-tiefland.prado

Revision

Blame | Letzte Änderung | Log anzeigen | RSS feed

<?php
/**
 * TPager class file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.pradosoft.com/
 * @copyright Copyright &copy; 2005-2008 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @version $Id: TPager.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 */

/**
 * TPager class.
 *
 * TPager creates a pager that provides UI for end-users to interactively
 * specify which page of data to be rendered in a {@link TDataBoundControl}-derived control,
 * such as {@link TDataList}, {@link TRepeater}, {@link TCheckBoxList}, etc.
 * The target data-bound control is specified by {@link setControlToPaginate ControlToPaginate},
 * which must be the ID path of the target control reaching from the pager's
 * naming container. Note, the target control must have its {@link TDataBoundControl::setAllowPaging AllowPaging}
 * set to true.
 *
 * TPager can display three different UIs, specified via {@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.
 *
 * When the pager mode is either NextPrev or Numeric, the paging buttons may be displayed
 * in three types by setting {@link setButtonType ButtonType}:
 * - LinkButton: a hyperlink button
 * - PushButton: a normal button
 * - ImageButton: an image button (please set XXXPageImageUrl properties accordingly to specify the button images.)
 *
 * 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}.
 * Normally, in the event handler, one can set the {@link TDataBoundControl::getCurrentPageIndex CurrentPageIndex}
 * to this new page index so that the new page of data is rendered.
 *
 * Multiple pagers can be associated with the same data-bound control.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TPager.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0.2
 */
class TPager extends TWebControl 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';

        private $_pageCount=0;

        /**
         * 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))
                {
                        $this->getControls()->clear();
                        $this->buildPager();
                }
        }

        /**
         * @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 TPagerMode pager mode. Defaults to TPagerMode::NextPrev.
         */
        public function getMode()
        {
                return $this->getViewState('Mode',TPagerMode::NextPrev);
        }

        /**
         * @param TPagerMode pager mode.
         */
        public function setMode($value)
        {
                $this->setViewState('Mode',TPropertyValue::ensureEnum($value,'TPagerMode'),TPagerMode::NextPrev);
        }

        /**
         * @return TPagerButtonType the type of command button for paging. Defaults to TPagerButtonType::LinkButton.
         */
        public function getButtonType()
        {
                return $this->getViewState('ButtonType',TPagerButtonType::LinkButton);
        }

        /**
         * @param TPagerButtonType the type of command button for paging.
         */
        public function setButtonType($value)
        {
                $this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'TPagerButtonType'),TPagerButtonType::LinkButton);
        }

        /**
         * @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 string the image URL for the first page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
         * @since 3.1.1
         */
        public function getFirstPageImageUrl()
        {
                return $this->getViewState('FirstPageImageUrl','');
        }

        /**
         * @param string the image URL for the first page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
         * @since 3.1.1
         */
        public function setFirstPageImageUrl($value)
        {
                $this->setViewState('FirstPageImageUrl',$value);
        }

        /**
         * @return string the image URL for the last page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
         * @since 3.1.1
         */
        public function getLastPageImageUrl()
        {
                return $this->getViewState('LastPageImageUrl','');
        }

        /**
         * @param string the image URL for the last page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
         * @since 3.1.1
         */
        public function setLastPageImageUrl($value)
        {
                $this->setViewState('LastPageImageUrl',$value);
        }

        /**
         * @return string the image URL for the next page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
         * @since 3.1.1
         */
        public function getNextPageImageUrl()
        {
                return $this->getViewState('NextPageImageUrl','');
        }

        /**
         * @param string the image URL for the next page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
         * @since 3.1.1
         */
        public function setNextPageImageUrl($value)
        {
                $this->setViewState('NextPageImageUrl',$value);
        }

        /**
         * @return string the image URL for the previous page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
         * @since 3.1.1
         */
        public function getPrevPageImageUrl()
        {
                return $this->getViewState('PrevPageImageUrl','');
        }

        /**
         * @param string the image URL for the previous page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
         * @since 3.1.1
         */
        public function setPrevPageImageUrl($value)
        {
                $this->setViewState('PrevPageImageUrl',$value);
        }

        /**
         * @return string the image URL for the numeric page buttons. This is only used when {@link getButtonType ButtonType} is 'ImageButton' and {@link getMode Mode} is 'Numeric'.
         * @see setNumericPageImageUrl
         * @since 3.1.1
         */
        public function getNumericPageImageUrl()
        {
                return $this->getViewState('NumericPageImageUrl','');
        }

        /**
         * Sets the image URL for the numeric page buttons.
         * This is actually a template for generating a set of URLs corresponding to numeric button 1, 2, 3, .., etc.
         * Use {0} as the placeholder for the numbers.
         * For example, the image URL http://example.com/images/button{0}.gif
         * will be replaced as http://example.com/images/button1.gif, http://example.com/images/button2.gif, etc.
         * @param string the image URL for the numeric page buttons. This is only used when {@link getButtonType ButtonType} is 'ImageButton' and {@link getMode Mode} is 'Numeric'.
         * @since 3.1.1
         */
        public function setNumericPageImageUrl($value)
        {
                $this->setViewState('NumericPageImageUrl',$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 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
         */
        protected function setCurrentPageIndex($value)
        {
                if(($value=TPropertyValue::ensureInteger($value))<0)
                        throw new TInvalidDataValueException('pager_currentpageindex_invalid');
                $this->setViewState('CurrentPageIndex',$value,0);
        }

        /**
         * @return integer number of pages of data items available
         */
        public function getPageCount()
        {
                return $this->getViewState('PageCount',0);
        }

        /**
         * @param integer number of pages of data items available
         * @throws TInvalidDataValueException if the value is less than 0
         */
        protected function setPageCount($value)
        {
                if(($value=TPropertyValue::ensureInteger($value))<0)
                        throw new TInvalidDataValueException('pager_pagecount_invalid');
                $this->setViewState('PageCount',$value,0);
        }

        /**
         * @return boolean whether the current page is the first page Defaults to false.
         */
        public function getIsFirstPage()
        {
                return $this->getCurrentPageIndex()===0;
        }

        /**
         * @return boolean whether the current page is the last page
         */
        public function getIsLastPage()
        {
                return $this->getCurrentPageIndex()===$this->getPageCount()-1;
        }

        /**
         * 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
         */
        public function onPreRender($param)
        {
                parent::onPreRender($param);

                $controlID=$this->getControlToPaginate();
                if(($targetControl=$this->getNamingContainer()->findControl($controlID))===null || !($targetControl instanceof TDataBoundControl))
                        throw new TConfigurationException('pager_controltopaginate_invalid',$controlID);

                if($targetControl->getAllowPaging())
                {
                        $this->_pageCount=$targetControl->getPageCount();
                        $this->getControls()->clear();
                        $this->setPageCount($targetControl->getPageCount());
                        $this->setCurrentPageIndex($targetControl->getCurrentPageIndex());
                        $this->buildPager();
                }
                else
                        $this->_pageCount=0;
        }

        /**
         * Renders the control.
         * The method overrides the parent implementation by rendering
         * the pager only when there are two or more pages.
         * @param THtmlWriter the writer
         */
        public function render($writer)
        {
                if($this->_pageCount>1)
                        parent::render($writer);
        }

        /**
         * Builds the pager content based on the pager mode.
         * Current implementation includes building 'NextPrev', 'Numeric' and 'DropDownList' pagers.
         * Derived classes may override this method to provide additional pagers.
         */
        protected function buildPager()
        {
                switch($this->getMode())
                {
                        case TPagerMode::NextPrev:
                                $this->buildNextPrevPager();
                                break;
                        case TPagerMode::Numeric:
                                $this->buildNumericPager();
                                break;
                        case TPagerMode::DropDownList:
                                $this->buildListPager();
                                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===TPagerButtonType::LinkButton)
                {
                        if($enabled)
                                $button=new TLinkButton;
                        else
                        {
                                $button=new TLabel;
                                $button->setText($text);
                                return $button;
                        }
                }
                else
                {
                        if($buttonType===TPagerButtonType::ImageButton)
                        {
                                $button=new TImageButton;
                                $button->setImageUrl($this->getPageImageUrl($text,$commandName));
                        }
                        else
                                $button=new TButton;
                        if(!$enabled)
                                $button->setEnabled(false);
                }
                $button->setText($text);
                $button->setCommandName($commandName);
                $button->setCommandParameter($commandParameter);
                $button->setCausesValidation(false);
                return $button;
        }

        /**
         * @param string the caption of the image button
         * @param string the command name associated with the image button
         * @since 3.1.1
         */
        protected function getPageImageUrl($text,$commandName)
        {
                switch($commandName)
                {
                        case self::CMD_PAGE:
                                $url=$this->getNumericPageImageUrl();
                                return str_replace('{0}',$text,$url);
                        case self::CMD_PAGE_NEXT:
                                return $this->getNextPageImageUrl();
                        case self::CMD_PAGE_PREV:
                                return $this->getPrevPageImageUrl();
                        case self::CMD_PAGE_FIRST:
                                return $this->getFirstPageImageUrl();
                        case self::CMD_PAGE_LAST:
                                return $this->getLastPageImageUrl();
                        default:
                                return '';
                }
        }

        /**
         * Builds a next-prev pager
         */
        protected function buildNextPrevPager()
        {
                $buttonType=$this->getButtonType();
                $controls=$this->getControls();
                if($this->getIsFirstPage())
                {
                        if(($text=$this->getFirstPageText())!=='')
                        {
                                $label=$this->createPagerButton($buttonType,false,$text,self::CMD_PAGE_FIRST,'');
                                $controls->add($label);
                                $controls->add("\n");
                        }
                        $label=$this->createPagerButton($buttonType,false,$this->getPrevPageText(),self::CMD_PAGE_PREV,'');
                        $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($this->getIsLastPage())
                {
                        $label=$this->createPagerButton($buttonType,false,$this->getNextPageText(),self::CMD_PAGE_NEXT,'');
                        $controls->add($label);
                        if(($text=$this->getLastPageText())!=='')
                        {
                                $controls->add("\n");
                                $label=$this->createPagerButton($buttonType,false,$text,self::CMD_PAGE_LAST,'');
                                $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
         */
        protected function buildNumericPager()
        {
                $buttonType=$this->getButtonType();
                $controls=$this->getControls();
                $pageCount=$this->getPageCount();
                $pageIndex=$this->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",self::CMD_PAGE,'');
                                $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
         */
        protected function buildListPager()
        {
                $list=new TDropDownList;
                $this->getControls()->add($list);
                $list->setDataSource(range(1,$this->getPageCount()));
                $list->dataBind();
                $list->setSelectedIndex($this->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 <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)
                {
                        $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 <qiang.xue@gmail.com>
 * @version $Id: TPager.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @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 <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;
        }
}


/**
 * TPagerMode class.
 * TPagerMode defines the enumerable type for the possible modes that a {@link TPager} control 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
 * - DropDownList: a dropdown list is used to select pages
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TPager.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0.4
 */
class TPagerMode extends TEnumerable
{
        const NextPrev='NextPrev';
        const Numeric='Numeric';
        const DropDownList='DropDownList';
}


/**
 * TPagerButtonType class.
 * TPagerButtonType defines the enumerable type for the possible types of pager buttons.
 *
 * The following enumerable values are defined:
 * - LinkButton: link buttons
 * - PushButton: form submit buttons
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TPager.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0.4
 */
class TPagerButtonType extends TEnumerable
{
        const LinkButton='LinkButton';
        const PushButton='PushButton';
        const ImageButton='ImageButton';
}