Current File : //opt/RZphp73/includes/Gtk/Styled/ScrollBar.php
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 2005 Scott Mattocks                                    |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available through the world-wide-web at the following url:           |
// | http://www.php.net/license/3_0.txt.                                  |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Author: Scott Mattocks <scottmattocks@php.net>                       |
// +----------------------------------------------------------------------+
//
// $Id: ScrollBar.php,v 1.2 2005/01/11 13:55:04 scottmattocks Exp $

// Define the error code if it isn't already.
if (!defined('GTK_STYLED_ERROR')) {
    define('GTK_STYLED_ERROR', 1);
}

/**
 * Class for creating a pseudo-scrollbar whose style can be controlled
 * more easily than a regular scrollbar.
 *
 * While it is possible to control some style elements of a GtkScrollBar,
 * other elements cannot be controlled so easily. Items such as the images
 * at the begining and end (usually arrows) and the scroll bar that is 
 * dragged to scroll the element cannot be changed. This leads to 
 * applications that either must conform to the windowing systems look
 * and feel or appear incomplete. The goal of this family of PHP-GTK 
 * classes is to provide all the same functionality as a normal scroll
 * bar but allow the user to have better control over the look and feel.
 *
 * There are several steps to undertake inorder to make this family of
 * classes mimic a GtkScrollBar:
 * Done: Define what needs to be done.
 * Done: Create a pseudo widget that looks like a scroll bar.
 * Done: Make the pseudo widget act like a scroll bar (move up and down)
 * Done: Make the pseudo widget control another widget
 * Done: Make the bar dragable within the track only.
 * Done: Make the pseudo widget "styleable"
 *
 * @author     Scott Mattocks <scottmattocks@php.net>
 * @version    @VER@
 * @category   Gtk
 * @package    Gtk_Styled
 * @license    PHP version 3.0
 * @copyright  Copyright &copy; 2005 Scott Mattocks
 */
class Gtk_Styled_ScrollBar {
  
    /**
     * The useable widget
     * @var object
     */
    var $widget;
    /**
     * The widget that the scroll bar will control.
     * @var object
     */
    var $widgetToScroll;
    /**
     * The pseudo widget that defines the basic properties of the scroll bar.
     * @var object
     */
    var $styleAdjustment;
    /**
     * The the button that makes the value of the scroll less.
     * @var object
     */
    var $lessButton;
    /**
     * The the button that makes the value of the scroll more.
     * @var object
     */
    var $moreButton;
    /**
     * The time out tag that indicates the user is pressing and holding
     * a part of the scroll bar.
     * @var integer
     */
    var $pressing;
    /**
     * The number of microseconds to wait before incrementing the value
     * again while the user is holding a button.
     * @var integer
     */
    var $delay = 200;
	/**
	 * The width of a button.
	 * @var integer
	 */
	var $width = 15;
	/**
	 * The height of a button.
	 * @var integer
	 */
	var $height = 15;

    /**
     * Constructor. Check parameters an call helper methods.
     * 
     * A Gtk_Styled_ScrollBar requires a Gtk_StyleAdjustment on construction.
     * The style adjustment can be either an H or V style adjustment but
     * Whichever is passed controls the look and behavior of the scroll
     * bar.
     *
     * The widget to be scrolled is added later.
     *
     * @access public
     * @param  object &$styleAdjustment
     * @return void
     */
    function Gtk_Styled_ScrollBar(&$styleAdjustment)
    {
        // Check that a StyleAdjustment was passed.
        if (!is_a($styleAdjustment, 'Gtk_Styled_Adjustment')) {
            $this->_handleError('Gtk_Styled_ScrollBar expects a Gtk_Styled_Adjustment. Given a ' . get_class($styleAdjustment) . '.');
        }
        
        $this->styleAdjustment =& $styleAdjustment;

        // Create the scroll bar look and feel.
        $this->_createScrollBar();
    }

    /**
     * Create the scroll bar parts and put them together.
     *
     * A scroll bar consists of the adjustment and two buttons that
     * control the value of the adjustment. Depending on what type of
     * adjustment is passed, the scroll bar will be either a 
     * horizontal or vertical scoll bar.
     *
     * @access private
     * @param  none
     * @return void
     */
    function _createScrollBar()
    {
        // Create a container to hold the scroll.
        if (is_a($this->styleAdjustment, 'Gtk_Styled_HAdjustment')) {
            $this->widget =& new GtkHBox;
        } else {
            $this->widget =& new GtkVBox;
        }

        // Create the controls.
        $this->lessButton =& new GtkButton(NULL);
        $this->moreButton =& new GtkButton(NULL);

        $this->lessButton->set_usize($this->width, $this->height);
        $this->moreButton->set_usize($this->width, $this->height);
        
        // Add the default images to the buttons.
        require 'data/Gtk_Styled/Buttons.php';
        if (is_a($this->styleAdjustment, 'Gtk_Styled_HAdjustment')) {
            $this->setButtonContents($this->lessButton, $this->_createPixmap($buttons['horizontal_less']));
            $this->setButtonContents($this->moreButton, $this->_createPixmap($buttons['horizontal_more']));
        } else {
            $this->setButtonContents($this->lessButton, $this->_createPixmap($buttons['vertical_less']));
            $this->setButtonContents($this->moreButton, $this->_createPixmap($buttons['vertical_more']));
        }

        // Add everything to the container.
        $this->widget->pack_start($this->lessButton, false, false, 0);
        $this->widget->pack_start($this->styleAdjustment->getWidget(), true, true, 0);
        $this->widget->pack_start($this->moreButton, false, false, 0);
    }

    /**
     * Create a pixmap from an array.
     *
     * Turn an array into a pixmap. This method is used to turn the arrays
     * in the buttons data file into images. The images are then used for
     * the scroll buttons. 
     *
     * @access private
     * @param  array   &$imgArray The image array.
     * @return &array             The new GtkPixmap.
     */    
    function &_createPixmap(&$imgArray)
    {
        $tmpWindow =& new GtkWindow;
        $tmpWindow->realize();
        $transparentColor = new GdkColor('#FFFFFF');
        $pieces =& gdk::pixmap_create_from_xpm_d($tmpWindow->window, $transparentColor, $imgArray);
        $pxm =& new GtkPixmap($pieces[0], $pieces[1]);
        unset($tmpWindow);

        return $pxm;
    }

    /**
     * Set the pixmap image of a button.
     *
     * The method allows the user to set the contents of the button.
     * Each button should have an image visually indicating what
     * affect it will have on the value of the adjustment. By default
     * these image are triangles pointing in the direction that they
     * will move the scroll bar. The user may supply any image they
     * desire and may also change the shape of the button using the
     * setButtonMask() method.
     *
     * @access public
     * @param  object &$button The button to set the image for.
     * @param  object &$widget The new contents of the button.
     * @return widget          The previous widget in the button.
     */    
    function setButtonContents(&$button, &$widget)
    {
        $prevChild = $button->child;
        $button->remove($prevChild);
        $button->add($widget);

        return $prevChild;
    }

    /**
     * Set the style of one of the buttons.
     *
     * The whole purpose of this class is to allow you to set the
     * style of a the widget. The track and bar styles are set in
     * the Adjustment. The button styles can be set here.
     *
     * @access public
     * @param  object &$button
     * @param  object &$style
     * @return void
     */
    function setButtonStyle(&$button, &$style)
    {
        $button->set_style($style);
    }

    /**
     * Set the pix mask of one of the buttons.
     *
     * The user may control the shape of the scrollbar buttons by
     * adding a pix mask. By default the buttons are grey squares
     * but by using this method and setButtonContents it is possible
     * to make the button any shape and/or color desired.
     *
     * The button that passed to this method should be the return
     * value from one of getLessButton() or getMoreButton().The x
     * and y parameters will offset the mask from the upper left
     * corner.
     *
     * @access public
     * @param  object  &$button The button whose shape is to be changed.
     * @param  object  &$mask   The mask to apply to the button.
     * @param  integer $x       The offset from the left edge.
     * @param  integer $y       The offset from the top edge.
     */
    function setButtonMask(&$button, &$mask, $x = 0, $y = 0)
    {
        $button->shape_combine_mask($mask, $x, $y);
    }
 
    /**
     * Return the 'more' button.
     *
     * @access public
     * @param  none
     * @return &object
     */
    function &getMoreButton()
    {
        return $this->moreButton;
    }

    /**
     * Return the 'less' button.
     *
     * @access public
     * @param  none
     * @return &object
     */
    function &getLessButton()
    {
        return $this->lessButton;
    }

    /**
     * Add a widget to be controlled by the Gtk_Styled_ScrollBar.
     *
     * It doesn't make much sense to have a scroll bar with nothing to
     * scroll. This method puts the Gtk_Styled_ScrollBar incharge of the
     * $widgetToScroll. The widget to scroll should obviously be 
     * scrollable. The Gtk_Styled_ScrollBar will only control scrolling in
     * one direction. Which direction depends on what type of 
     * Gtk_StyleAdjustment was passed on construction. To fully control
     * all directions of scrolling, you must add the widget to scroll 
     * to two different Gtk_Styled_ScrollBars.
     *
     * @access public
     * @param  object  &$widgetToScroll
     * @return void
     */
    function addWidgetToScroll(&$widgetToScroll)
    {
        // Set the member variable.
        $this->widgetToScroll = $widgetToScroll;

        // Set the values for the adjustment based off of the
        // widget to scroll.
        $this->setAdjFromAdj($this->widgetToScroll);
        
        // Connect the style adjustment pieces to the right signals.
        $this->_connectStyleAdjustment();

        // We need to keep the styleAdjustment values current with
        // the "real" adjustment values. The values can be changed
        // by the scrollable widget or other objects. It is important
        // to keep things consistent and up-to-date.
        $this->widgetToScroll->connect('value-changed', array(&$this, 'setAdjValueFromAdj'));
        $this->widgetToScroll->connect('changed', array(&$this, 'setAdjFromAdj'));
    }

    /**
     * Connect the styleAdjustment pieces to value setting methods.
     *
     * To have control over the scrollable widget, the user must be
     * able to change the value of the styleAdjustment. The track
     * and the buttons must be clickable.
     * 
     * Eventually, the bar must also be dragable.
     *
     * @access private
     * @param  none
     * @return void
     */
    function _connectStyleAdjustment()
    {
        // Connect the pre and post tracks to change the value by one page.
        $this->styleAdjustment->preTrack->connect_object('button-press-event',
                                                         array(&$this, 'setAdjValue'),
                                                         (0 - $this->styleAdjustment->pageSize), true);
        $this->styleAdjustment->preTrack->connect_object('button-release-event',
                                                         array(&$this, 'stopValue'));
        $this->styleAdjustment->preTrack->connect_object('leave-notify-event',
                                                         array(&$this, 'stopValue'));

        $this->styleAdjustment->bar->connect_object('pressed', 
                                                    array(&$this, 'setAdjValueMouse'));
        $this->styleAdjustment->bar->connect_object('released',
                                                    array(&$this, 'stopValue'));

        $this->styleAdjustment->postTrack->connect_object('button-press-event',
                                                          array(&$this, 'setAdjValue'),
                                                          $this->styleAdjustment->pageSize, true);
        $this->styleAdjustment->postTrack->connect_object('button-release-event',
                                                          array(&$this, 'stopValue'));
        $this->styleAdjustment->postTrack->connect_object('leave-notify-event',
                                                          array(&$this, 'stopValue'));

        // Make the controls have an effect on the adjustment value.
        $this->lessButton->connect_object('pressed', 
                                          array(&$this, 'setAdjValue'),
                                          (0 - $this->styleAdjustment->stepIncrement), true);
        $this->lessButton->connect_object('released', 
                                          array(&$this, 'stopValue'));

        $this->moreButton->connect_object('pressed',
                                          array(&$this, 'setAdjValue'),
                                          $this->styleAdjustment->stepIncrement, true);
        $this->moreButton->connect_object('released', 
                                          array(&$this, 'stopValue'));
    }

    /**
     * Set the adjustment value based on the mouse position.
     *
     * When the scrollbar is being dragged to change its value 
     * the change in mouse position should influence an equal 
     * change in bar position. In other words one pixel change
     * in mouse position does not necessarily translate to one
     * pixel change in bar position.
     *
     * @access public
     * @param  none
     * @return void
     */
    function setAdjValueMouse()
    {
        // Figure out which coordinate to use.
        if (is_a($this->styleAdjustment, 'Gtk_Styled_HAdjustment')) {
            $coord = 0;
        } else {
            $coord = 1;
        }

        // Get the current mouse position.
        $newPos = $this->styleAdjustment->widget->window->pointer[$coord];

        // Make sure the mouse is still within the bar.
        if(!$this->styleAdjustment->checkMouseBounds()) {
            return true;
        }


        // Set the adjustment value based on the difference between the
        // old and new mouse position.
        if (isset($this->mPosition)) {
            if (isset($this->widgetToScroll)) {
                $this->widgetToScroll->set_value($this->styleAdjustment->setValue($this->styleAdjustment->getValueFromPosition($newPos - $this->mPosition)));
            } else {
                $this->styleAdjustment->setValue($this->styleAdjustment->getValueFromPosition($newPos - $this->mPosition));
            }
            //$this->setAdjValue($this->styleAdjustment->value + ($newPos - $this->mPosition), false);
        }

        // Make the current position the old position for next time.
        $this->mPosition = $newPos;

        // Keep calling the method to update the value.
        $this->delay = 50;
        $this->pressing = gtk::timeout_add($this->delay, array(&$this, 'setAdjValueMouse'));
    }

    /**
     * Set the value of the adjustment and styleAdjustment.
     *
     * Sets the value of the "real" adjustment and the style adjustment.
     * By setting the value of the adjustment, the scrolling widget will
     * move as if it has been scrolled. This makes it easier to fake the
     * scrolling action as if it were done by the style adjustment.
     *
     * @access public
     * @param  object $event The GdkEvent that occured to make the value change
     * @param  double $value The new value for the adjustments.
     * @return void
     */
    function setAdjValue($event, $value, $continue = false)
    {
        // Adjust for the fact that some events pass the event also.
        if (is_numeric($event)) {
            $continue = $value;
            $value    = $event;
        }

        // Set the value of the widget to scroll also.
        // This is a cheat for making the scrollable widget scroll.
        if (isset($this->widgetToScroll)) {
            // Because the widget to scroll is connected to other methods,
            // it will change the value of the style adjustment when its
            // value is changed.
            $this->widgetToScroll->set_value($this->styleAdjustment->setValue($this->styleAdjustment->value + $value));
        } else {
            $this->styleAdjustment->setValue($this->styleAdjustment->value + $value);
        }

        // Keep going if the user is holding down the button.
        if ($continue) {
            $this->pressing = gtk::timeout_add($this->delay, array(&$this, 'setAdjValue'), $value, $continue);
            // Reduce the delay.
            $this->delay = 100;
        }
    }

    /**
     * Stop incrementing the value.
     *
     * This method kills the timeout that is calling the setAdjValue 
     * method. This will stop the scrollbar from moving after the 
     * user has released the button or moved the mouse outside of the
     * track.
     * 
     * @access public
     * @param  none
     * @return void
     */
    function stopValue()
    {
        // Kill the timeout.
        if (isset($this->pressing)) {
            gtk::timeout_remove($this->pressing);
        }
        // The user must hold down the button for a little while
        // before the scrolling starts.
        $this->delay = 200;

        if (isset($this->mPosition)) {
            unset($this->mPosition);
        }
    }

    /**
     * Set the styleAdjustment value based on the value of a 
     * GtkAdjustment object.
     *
     * In order to keep the styleAdjustment in sync with the scrolling
     * widget, the value must be taken from the GtkAdjustment that 
     * controls the GtkScrollbar.
     *
     * @access public
     * @param  object &$adj The GtkAdjustment to get the value from.
     * @return void
     */
    function setAdjValueFromAdj(&$adj)
    {
        $this->styleAdjustment->setValue($adj->value);
    }

    /**
     * Set the styleAdjustment values based off of a GtkAdjustment.
     *
     * @access public
     * @param  object &$adj The GtkAdjustment to get the values from.
     * @return void
     */
    function setAdjFromAdj(&$adj)
    {
        $this->styleAdjustment->setUpper($adj->upper);
        $this->styleAdjustment->setLower($adj->lower);
        $this->styleAdjustment->setPageSize($adj->page_size);
        $this->styleAdjustment->setPageIncrement($adj->page_increment);
        $this->styleAdjustment->setStepIncrement($adj->step_increment);        
        $this->styleAdjustment->setValue($adj->value);
    }

    /**
     * Get the final product that the user can use.
     *
     * To make things easier for the user, and to keep some 
     * consistency with other PEAR Gtk classes, the final usable
     * object is called widget and is returned from the getWidget
     * method. 
     * 
     * @access public
     * @param  none
     * @return &object
     */
    function &getWidget()
    {
        return $this->widget;
    }

    /**
     * Error handling method.
     *
     * Errors should be handled with PEAR::Error_Stack
     *
     * @access private
     * @param  string  $message
     * @param  integer $level
     * @return mixed
     */
    function _handleError($msg, $code = GTK_STYLED_ERROR, $pearMode = PEAR_ERROR_PRINT)
    {
        // Require the pear class so that we can use its error functionality.
        require_once ('PEAR.php');
        
        // Check whether or not we should print the error.
        PEAR::raiseError($msg . "\n", $code, $pearMode);
    }
}
/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 */
?>