Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: trunk
Fetching contributors…

Cannot retrieve contributors at this time

913 lines (827 sloc) 31.137 kB
<?php
/**
* Gtk2_ScrollingLabel
*
* This is a class to encapsulate the functionality
* needed for a scrolling gtk label. This class
* provides a simple, easy to understand API for
* setting up and controlling the label. It allows
* for the ability to scroll in either direction,
* start and stop the scroll, pause and unpause the
* scroll, get and set the text, and set display
* properites of the text.
*
* Reloaded for PHP-Gtk2!
*
* @author Anant Narayanan
* @author Scott Mattocks
* @category Gtk2
* @package ScrollingLabel
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
*
*/
/**
* Class for creating a label with the ability to scroll
* text.
*/
class Gtk2_ScrollingLabel {
/**
* A constant for representing srolls that move from left to right
* @const GTK2_SCROLLINGLABEL_LEFT
*/
const GTK2_SCROLLINGLABEL_LEFT = 1;
/**
* A constant for representing srolls that move from right to left
* @const GTK2_SCROLLINGLABEL_RIGHT
*/
const GTK2_SCROLLINGLABEL_RIGHT = 2;
/**
* A SrollingLabel error code.
* @const GTK2_SCROLLINGLABEL_DEFAULT_ERROR
*/
const GTK2_SCROLLINGLABEL_DEFAULT_ERROR = 1;
/**
* The event box which will contain the label and listen for events.
* @var GtkEventBox
* @access protected
*/
protected $widget;
/**
* The label which will display the text.
* @var GtkLabel
* @access protected
*/
protected $label;
/**
* The full text that will be displayed over time.
* @var string
* @access protected
*/
protected $labelText;
/**
* The tag returned to indicate the call to gtk timeout.
* @var integer
* @access protected
*/
protected $gtkTimeoutTag;
/**
* The number of characters that should be visible
* @var integer
* @access protected
*/
protected $visibleLength = 70;
/**
* The number of milliseconds between calls to the method passed
* to gtk timeout.
* @var integer
* @access protected
*/
protected $speed = 70;
/**
* The first character to show when direction is self::GTK2_SCROLLINGLABEL_LEFT
* The last character to show when direction is self::GTK2_SCROLLINGLABEL_RIGHT
* @var integer
* @access protected
*/
protected $currentCharPosition = 0;
/**
* The direction the text should be moving.
* @var integer
* @access protected
*/
protected $direction = self::GTK2_SCROLLINGLABEL_LEFT;
/**
* Whether the text should change direction when it hits an edge.
* @var boolean
* @access protected
*/
protected $bounce = true;
/**
* Constructor.
* Builds needed widgets.
*
* @access private
* @param string $startingText The text to display upon creation.
* @return void
*/
function Gtk2_ScrollingLabel($startingText = '')
{
// Create a GTK_eventbox.
$this->widget = new GtkEventBox;
// We need to add an event mask for the event box.
$this->widget->add_events(Gdk::BUTTON_PRESS_MASK);
// Next we need to create a label.
$this->label = new GtkLabel($startingText);
$this->labelText = $startingText;
// We need to set up some label stuff
$this->label->set_alignment(0, 1.0);
$this->label->set_size_request(-1, 1);
$this->label->set_justify(Gtk::JUSTIFY_LEFT);
// We need to set the direction of the scroll.
$this->setDirection($this->direction);
// Then we can cram the label into the event box.
$this->widget->add($this->label);
}
/**
* Display the visible text and de/increment the current position.
* Needs to check if we are at the end of the string and which
* direction the text is moving.
*
* This is the brains of the class. scrollLabel figures out what
* to do next. If we are scrolling in one direction we need to
* know when the text has all passed through the visible "window"
* so that we can start all over again. If we are bouncing the
* text between the two edges of the visible window we need to
* know when we have reached an edge so that the text can go in
* the opposite direction next time.
*
* Note: If the text is set to bounce and the text is larger than
* the visible window can display, the text will not bounce the
* way one would expect. Larger than visible window text bouncing
* may be supported later but that is not guaranteed.
*
* @access private
* @param none
* @return boolean False if the scrolling should stop.
*/
function scrollLabel()
{
// If the timeout is dead we return false.
if (!isset($this->gtkTimeoutTag)) {
return false;
}
// Get the visible text.
$visible = $this->getVisibleText();
// Set the text.
$this->label->set_text($visible);
// We need to figure out which way to move the text.
if ($this->direction == self::GTK2_SCROLLINGLABEL_LEFT) {
++$this->currentCharPosition;
} else {
--$this->currentCharPosition;
}
// Check to see if we need to do change direction.
if ($this->bounce) {
// We are going to change direction eventually.
if ($this->direction == self::GTK2_SCROLLINGLABEL_LEFT && $visible == $this->labelText) {
// If we are scrolling right to left and displaying the last character,
// we need to turn around and move left to right next time.
$this->setDirection(self::GTK2_SCROLLINGLABEL_RIGHT);
// We need to set the current position to the end.
$this->currentCharPosition = strlen($this->padText()) - strlen($this->labelText);
} elseif ($this->direction == self::GTK2_SCROLLINGLABEL_RIGHT && $this->currentCharPosition <= 0) {
// If we are scrolling left to right and we are showing the first
// character, we need to turn around and go right to left next time.
$this->setDirection(self::GTK2_SCROLLINGLABEL_LEFT);
// We need to start the scroll back at the begining.
$this->currentCharPosition = 0;
}
}
// Check to see if we need to start over.
if ($this->direction == self::GTK2_SCROLLINGLABEL_LEFT &&
$this->currentCharPosition >= strlen($this->padText())) {
// We need to start back at the beginning.
$this->currentCharPosition = 0;
} elseif ($this->direction == self::GTK2_SCROLLINGLABEL_RIGHT &&
$this->currentCharPosition < 0) {
// We need to start back at the end.
$this->currentCharPosition = strlen($this->labelText) + $this->visibleLength - 1;
}
return true;
}
/**
* Begin scrolling the text from the start of the string.
* Usually connected to an event.
*
* startScroll() will make the text begin scrolling in the
* direction given by calling setDirection(). If the text
* is scrolling left to right (GTK_self::GTK2_SCROLLINGLABEL_RIGHT), the
* current character position is set to the end of the text.
* This means that the last character of the label will be
* the first character shown. If the text is scrolling
* right to left (self::GTK2_SCROLLINGLABEL_LEFT), the current character
* position is set to zero. This means that first character
* shown will be a blank space.
*
* This method is best used when connected to an event.
*
* @access public
* @param none
* @return boolean true if the scrolling has started.
* @uses unPause
* @see setStartSignal
*/
public function startScroll()
{
// First reset the current character position.
$this->currentCharPosition = 0;
// Then return the value of a call to the unpuase method.
return $this->unPause();
}
/**
* Begin scrolling the text from the current character position.
* Work horse for startScroll. Usually connected to an event.
*
* unPause() does pretty much the same thing as startScroll
* except that it does not manipulate the current character
* position. When you unpause the scroll, the text continues
* from where it left off when it was paused.
*
* This method is best used when conneted to an event.
*
* @access public
* @param none
* @return boolean true if the scrolling has started.
* @see setUnPauseSignal
*/
public function unPause()
{
// First check to see if there is a timeout already started.
if (isset($this->gtkTimeoutTag)) {
// Throw error about scroll already started.
$this->error('Cannot unpause. Text not scrolling.',
self::GTK2_SCROLLINGLABEL_DEFAULT_ERROR, PEAR_ERROR_PRINT);
return false;
} elseif (!isset($this->speed)) {
// Throw error about no value for speed.
return $this->error('Cannot unpause. No speed set.');
} else {
// Start the scrolling.
$this->gtkTimeoutTag = Gtk::timeout_add($this->speed, array($this, 'scrollLabel'));
}
// Give back the timeout tag.
return $this->gtkTimeoutTag;
}
/**
* Stop scrolling the text and return the string to the beginning.
* Usually connected to an event.
*
* stopScroll will stop the text from scrolling and set the
* current character position to zero. When the text is
* started in motion again, it will be shown as it would if
* it had been started for the first time.
*
* This method is best used when connected to an event.
*
* @access public
* @param none
* @return boolean true if the scrolling has stopped.
* @uses pause
* @see setStopSignal
*/
public function stopScroll()
{
// First pause the scroll.
if ($this->pause()) {
// Then, if that worked, reset the current position.
$this->currentCharPosition = 0;
// Finally call the method to change the display returning
// the value from that method to indicate success.
return $this->scrollLabel();
}
return false;
}
/**
* Stop the scroll where it is and do not change the current position.
* Usually connected to an event.
*
* pause() will stop the text in its tracks. The scrolling
* will stop but the visible text will not change. This is
* different from stopScroll() in that stopScroll() will
* clear the display back to its starting state.
*
* This method is best used when connected to an event.
*
* @access public
* @param none
* @return boolean true if the scrolling has stopped.
* @see setPauseSignal
*/
public function pause()
{
// Pretty much like stop but without reseting the position.
if (empty ($this->gtkTimeoutTag)) {
// Throw an error about no scroll in progress.
$this->_error('Cannot pause. No timeout found.',
self::GTK2_SCROLLINGLABEL_DEFAULT_ERROR, PEAR_ERROR_PRINT);
return false;
} else {
// All we need to do is unset the tag member because it is a reference.
unset ($this->gtkTimeoutTag);
// Indicate success by returning true.
return true;
}
}
/**
* Connect a signal to a method.
*
* This private method will connect the event $signal to the
* method $methodName. If the optional parameter $after is
* set to true, the event will be connected using connect
* after. All methods connected to an event with connect
* after will be called after methods connected with plain
* old connect. The GTK label does not listen for signals so
* all events must be connected to the event box.
*
* @access private
* @param object $signal The Gtk::Signal to listen for.
* @param string $methodName The method to connect.
* @param boolean $after Should we make this one of the last calls.
* @return boolean
*/
private function connectSignal($signal, $methodName, $after = false)
{
// Check to see if we are using connect after
if ($after) {
$method = 'connect_after';
} else {
$method = 'connect';
}
// Connect the event box to the signal with the method.
$this->widget->$method($signal, array($this, $methodName));
return true;
}
/**
* Connect a signal to a method and act on another widget.
*
* This private method connects an event to a method and
* passes that method a widget from which to get data or
* to act on in some way. If the optional parameter $after
* is set to true, the event will be connected using connect
* after. All methods connected to an event with connect
* after will be called after methods connected with plain
* old connect. The GTK label does not listen for signals
* so all events must be connected to the event box.
*
* @access private
* @param object $signal The GTK_Signal to listen for.
* @param string $methodName The method to connect.
* @param &object $object The widget to act on.
* @param boolean $after Should we make this one of the last calls.
* @return boolean
*/
private function connectSignalSimple($signal, $methodName, &$object, $after = false)
{
// Check that signal is accepted by eventbox.
/**
* @todo Check the above
**/
// Check to see if we are using connect after
if ($after) {
$method = 'connect_simple_after';
} else {
$method = 'connect_simple';
}
// Connect the event box to the signal with the method.
$this->widget->$method($signal, array(&$this, $methodName), $object);
}
/**
* Connect the pause method to an event.
*
* setPauseSignal() will make the event box listen for the
* signal $signal and will envoke pause() when ever it is
* heard. The signal should be a signal that GTKEventBox
* listens for normally or a button press event.
*
* @access public
* @param string $signal The GTK signal to connect to the method.
* @return integer
* @uses connectSignal
*/
public function setPauseSignal($signal)
{
// Call the private connectSignal method.
return $this->connectSignal($signal, 'pause');
}
/**
* Connect the unpause method to an event.
*
* setUnPauseSignal() will make the event box listen for the
* signal $signal and will envoke unPause() when ever it is
* heard. The signal should be a signal that GTKEventBox
* listens for normally or a button press event.
*
* @access public
* @param string $signal The GTK signal to connect to the method.
* @return integer
* @uses connectSignal
*/
public function setUnPauseSignal($signal)
{
// Call the private connectSignal method.
return $this->connectSignal($signal, 'unPause');
}
/**
* Connect the start method to an event.
*
* setStartSignal() will make the event box listen for the
* signal $signal and will envoke startScroll() when ever it is
* heard. The signal should be a signal that GTKEventBox
* listens for normally or a button press event.
*
*
* @access public
* @param string $signal The GTK signal to connect to the method.
* @return integer
* @uses connectSignal
*/
public function setStartSignal($signal)
{
// Call the private connectSignal method.
return $this->connectSignal($signal, 'startScroll');
}
/**
* Connect the stop method to an event.
*
* setStopSignal() will make the event box listen for the
* signal $signal and will envoke stopScroll() when ever it is
* heard. The signal should be a signal that GTKEventBox
* listens for normally or a button press event.
*
*
* @access public
* @param string $signal The GTK signal to connect to the method.
* @return integer
* @uses connectSignal
*/
public function setStopSignal($signal)
{
// Call the private connectSignal method.
return $this->connectSignal($signal, 'stopScroll');
}
/**
* Set the speed of the scrolling text.
*
* setSpeed() will change how quickly the text scrolls
* across the visible label area. $speed is the number
* of milliseconds to wait between calls to _scrollLabel.
* $speed is passes to gtk::timeout_add(). If $restart
* is set to true, the speed of the text will be changed
* immediately. Otherwise the speed will not be changed
* until _scrollLabel is called again. This effectively
* means that the speed changes one letter after you set
* the new speed if $restart is false.
*
* @access public
* @param string $speed The speed the text should scroll.
* @param boolean $restart Whether or not the speed should be changed before the next call to _scrollLabel
* @return integer
*/
public function setSpeed($speed, $restart = false)
{
// Check the speed.
if (!is_numeric($speed) || $speed < 1 || $speed > 1000) {
// Throw an error about illegal speed value
$this->error('Cannot set speed. Invalid value: ' . $speed,
self::GTK2_SCROLLINGLABEL_DEFAULT_ERROR, PEAR_ERROR_PRINT);
} else {
// Speed is the number of milliseconds between timeouts.
$this->speed = $speed;
}
// If the scroll is currently moving we may need to
// pause and unpause the scroll.
if ($restart && isset ($this->gtkTimeoutTag)) {
// Pausing and unpausing the scroll will affectively change the speed.
$this->pause();
$this->unPause();
}
return $this->speed;
}
/**
* Set the diretion that the text should scroll.
*
* Set the direction that the text will move across the visible
* area of the label. The only accepted directions are the two
* constants self::GTK2_SCROLLINGLABEL_LEFT and self::GTK2_SCROLLINGLABEL_RIGHT which move the
* from right to left and left to right respectively. The return
* value is the diretion the text will move on the next call to
* scrollLabel().
*
* @access public
* @param integer $direction An integer specifying the direction.
* @return integer
*/
public function setDirection($direction)
{
// Check direction.
if ($direction != self::GTK2_SCROLLINGLABEL_LEFT &&
$direction != self::GTK2_SCROLLINGLABEL_RIGHT) {
// Throw an error about illegal direction choice.
$this->error('Cannot set direction. Illegal value: ' . $direction,
self::GTK2_SCROLLINGLABEL_DEFAULT_ERROR, PEAR_ERROR_PRINT);
} else {
$this->direction = $direction;
}
return $this->direction;
}
/**
* Set whether or not the text should change directions when it hits an edge
*
* If bounce is true, the text will scroll to the left until the
* first character hits the left edge of the visible window then
* it will scroll to the right. When the last character hits the
* right edge of the visible window the text will scroll back to
* the left.
*
* Note: I don't know what will happen if bounce is true and the
* text is larger than what can be viewed at one time in the
* window. Therefore it is suggested that the length of the text
* always be at least one less than the size of the window.
*
* @access public
* @param boolean $bounce true means change directions.
* @return boolean The current state of $this->bounce
*/
public function setBounce($bounce)
{
$this->bounce = $bounce;
return $this->bounce;
}
/**
* Deprecated method to set bouncing.
*
* @deprecated
* @access public
* @param boolean $bounce true means change directions.
* @return boolean The current state of $this->bounce
* @see setBounce
*/
public function bounce($bounce)
{
return $this->setBounce($bounce);
}
/**
* Set the style in which the text will be displayed.
*
* setStyle() lets you define the look of the text that is
* scrolling as well as the background of the widget.
*
* This method has not been implemented yet.
*
* @access public
* @param &object $style The GTK_Style object to use.
* @return &object
*/
public function setStyle(&$style)
{
$this->label->set_style($style);
}
/**
* Get the label's style widget.
*
* getStyle() returns the label's style widget. This lets you
* make changes to the style to alter the look and feel of the
* widget. You can change the font of the display, the color of
* the background, etc.
*
* @access public
* @param none
* @return &object
*/
public function &getStyle()
{
return $this->label->get_style();
}
/**
* Return the portion of the label text that should be visible
*
* getVisibleText() is used internally to get the text that
* should be shown everytime the text scrolls. It can also
* be called publically to get the text that is currently
* displayed in the visible window area. The part of the text
* that is visible depends on the current character position
* and the direction the text is moving.
*
* @access public
* @param none
* @return string The visible text.
* @uses padText()
*/
public function getVisibleText()
{
// Pad the text.
$text = $this->padText();
// We need to show from the current character position to
// the character at currentCharPosition + visibleLength.
if ($this->direction == self::GTK2_SCROLLINGLABEL_LEFT) {
$text = substr($text, $this->currentCharPosition, $this->visibleLength);
} else {
// I want to start at the end and get the text
// $visibleLength in front of that.
$text = substr($text, $this->currentCharPosition - $this->visibleLength,
$this->visibleLength);
}
return $text;
}
/**
* Pad the text with enough space to fill the visible area.
*
* padText adds the needed amount of white space to the
* text so that it can scroll properly. When the text is
* set to bounce the padding should only be what is needed
* to make the string as long as the visible window area.
* If the text is not bouncing, the text must be padded with
* as many spaces as the length of the visible area.
*
* @access public
* @param none
* @return string The padded text.
*/
public function padText()
{
// Get the full label text.
$text = $this->labelText;
// We need to pad the text on the left unless we are bouncing the text.
if (!$this->bounce) {
$text = str_pad($text, strlen($text) + $this->visibleLength, ' ', STR_PAD_LEFT);
} elseif (strlen($this->labelText) < $this->visibleLength) {
$text = str_pad($text, $this->visibleLength, ' ', STR_PAD_LEFT);
}
return $text;
}
/**
* Return all of the text that can ever be shown by this label.
*
* @access public
* @param none
* @return string
*/
public function getFullText()
{
return $this->labelText;
}
/**
* Return all of the text that is not currently visible.
*
* @access public
* @param none
* @return string
*/
public function getHiddenText()
{
// Pad the text.
$text = $this->padText();
// We need to get the text minus the visible portion.
// I don't know if this will ever be useful.
$firstPart = substr($text, 0, $this->currentCharPosition);
$secondPart = substr($text, $this->currentCharPosition + $this->visibleLength);
return $firstPart . $secondPart;
}
/**
* Set the full text of the label.
*
* setFullText() will change the text of the scrolling label.
* The return value is what the text of the label will be
* when _scrollLabel is next called.
*
* @access public
* @param string $text The new text for the label.
* @return string The label's text.
*/
public function setFullText($text)
{
// Set the label text to the supplied text.
$this->labelText = $text;
return $this->labelText;
}
/**
* Change the current character position.
*
* jumpToChar() allows you to go directly to a character
* position on the next call to scrollLabel. The new
* position must be between zero (the first character)
* and the length of the label minus one. The character
* position should be calculated using the string from
* padText(). When the text is scrolling to the left the
* $charPosition should be the left-most character to
* display. When text is scrolling to the right,
* $charPosition should be the right-most character to
* display.
*
* The visible label will not change until scrollLabel
* is called again.
*
* @access public
* @param integer $charPosition Depending on the direction either the left or right most character to display.
* @return integer The new current character position. (PEAR_Error when there is a problem.)
*/
public function jumpToChar($charPosition)
{
// Check that the charPosition exists.
if (!is_numeric ($charPosition) || $charPosition < 0 ||
$charPosition >= strlen($this->labelText)) {
// Throw an error about illegal char position.
$this->_error('Cannot jump to character. Invalid value.
Character position must be a number between zero and
the length of the string (' . strlen($this->padText()) . ')');
} else {
// Set the current character position.
$this->currentCharPosition = $charPosition;
}
return $this->currentCharPosition;
}
/**
* Change how many characters should be visible at any given time.
*
* setVisibleLength() will set the number of characters that are
* visible at any given time. While it is ik to set the visible
* length smaller than the width of the displaying widget, you
* probably don't want to make it larger then the width of the
* displaying widget. It will make the padded lenght longer and
* add additional time before the text begins to appear on screen.
*
* @access public
* @param integer $length The new size of the visible area in characters.
* @return integer The new size of the visible area in characters.
*/
public function setVisibleLength($length)
{
// First check the length.
if (!is_numeric($length) || $length < 0) {
// Throw an error about illegal visible length.
$this->_error('Cannot set visible length. Invalid value.
Length must be a number greater than zero.');
} else {
$this->visibleLength = $length;
}
return $this->visibleLength;
}
/**
* Return the visible area length in characters
*
* @access public
* @param none
* @return integer
*/
public function getVisibleLength()
{
return $this->visibleLength;
}
/**
* Show the GTK components.
*
* @access public
* @param none
* @return void
*/
public function show()
{
// Show the eventbox and the label.
$this->widget->show_all();
}
/**
* Hide the GTK components.
*
* @access public
* @param none
* @return void
*/
public function hide()
{
// Hide the label and the eventbox.
$this->widget->hide_all();
}
/**
* Return the GTKEventBox with the GTKLabel inside.
*
* @access public
* @param none
* @return &object
*/
public function getScrollingLabel()
{
return $this->widget;
}
/**
* Raise a pear error.
*
* error will raise a PEAR error with $msg as the message.
* If the optional parameter $code is set, that error code
* will be used. Otherwise the default error code will be
* used. If the optional $pearMode is passed that mode will
* be used to display/return the error.
*
* @access private
* @param string $msg The human readable error message.
* @param integer $code The error code.
* @param integer $pearMode The PEAR mode for the error.
* @return mixed A PEAR_Error or void.
*/
public function error($msg, $code = self::GTK2_SCROLLINGLABEL_DEFAULT_ERROR, $pearMode = PEAR_ERROR_RETURN)
{
// 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.
if ($pearMode == PEAR_ERROR_PRINT) {
PEAR::setErrorHandling(PEAR_ERROR_PRINT, "Gtk_ScrollingLabel error: %s<BR />\n");
}
PEAR::raiseError($msg, $code, $pearMode);
}
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
?>
Jump to Line
Something went wrong with that request. Please try again.