<?php
// Copyright 2006 Alexei Samoylov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions
// and limitations under the License.
// Description: This file is very similar to gladeToJS except it does not generate
// any Javascript to go along with the html elements.
// Instead, the widgets are rendered statically. The file is used on
// Wordpress pages to show a preview of the Glade layout.
// global vars to keep track of size of the client window
/*$totalX = 0;
$totalY = 0;
$maxX = 600;
$maxY = 480;
$widthRequest = 0;
$heightRequest = 0;
$largestPackingNode;*/
//$oneBigString='';
// --- NEW DOM-BASED PARSER ---
// takes in a widget and returns its title
function getWidgetTitle($widget) {
$nameAttribute = 'blah'; // some default value
// for windows use the following...
if($widget->getAttribute('class') == 'GtkWindow') $nameAttribute = 'title';
// for buttons and labels labels use the following..
else if($widget->getAttribute('class') == 'GtkButton' || $widget->getAttribute('class') == 'GtkLabel'
|| $widget->getAttribute('class') == 'GtkRadioButton' || $widget->getAttribute('class') == 'GtkCheckButton') $nameAttribute = 'label';
// for input fields
if($widget->getAttribute('class') == 'GtkEntry') $nameAttribute = 'text';
$elementList = $widget->childNodes; // extract widget properties
foreach($elementList as $list) {
if($list->hasAttributes()) {
if($list->getAttribute('name') == $nameAttribute) { // find title of widget
return $list->nodeValue;
}
}
}
return "";
}
function getWidgetID($widget) {
return $widget->getAttribute('id');
}
// this function parses the widget
function parseWidget($widget) {
$propertiesArr; // declare a properties array
// get properties
if ($widget->hasChildNodes()) {
foreach ($widget->childNodes as $child) { // each property is a child, so get the children
if($child->nodeName == 'property') { // filter out properties
$propertiesArr[$child->getAttribute('name')] = $child->nodeValue; // save properties in the array
}
else if ($child->nodeName == 'child') { // otherwise handle more children
makeCallBasedOnElement($child);
}
}
return $propertiesArr; // this is a simple parser, return just the properties
}
}
// parses packing information to retrieve position of the widget
function getWidgetPosition($packing) { // former parsePacking
$positionArr;
foreach($packing->childNodes as $node) {
if ($node->tagName == 'property') {
// find out if this label is inside of another element (e.g. button)
if ($node->getAttribute('name') == 'x') {
$positionArr['x']=$node->nodeValue;
}
else if ($node->getAttribute('name') == 'y') {
$positionArr['y']=$node->nodeValue;
}
}
}
return $positionArr;
}
// create a label
function handleGtkLabel($label, $packing) {
global $oneBigString;
$labelClass = '';
$labelPosition='display:inline;';
// get label ID
$labelID = getWidgetID($label);
// get label's position
$positionArr = getWidgetPosition($packing);
if ($positionArr['x'] !== NULL) {
$labelPosition = $labelPosition.'position:absolute; left:'.$positionArr['x'].'px;'.'top:'.$positionArr['y'].'px;';
$labelClass = 'class="labelClass"';
}
// display:inline will make the label appear in the same line as the image
$oneBigString=$oneBigString.'<div '.$labelClass.' id="'.$labelID.'Layer'.'" style="'.$labelPosition.'">
<div style="display:inline; vertical-align: text-top;" id="'.$labelID.'">'."\n";
$oneBigString=$oneBigString.getWidgetTitle($label)."\n";
$oneBigString=$oneBigString.'</div>'."\n".'</div>'."\n";
}
// create a button with that particular button name
function handleGtkButton($id, $packing) {
global $oneBigString;
// get buttonID
$buttonID = getWidgetID($id);
// get button's position
$positionArr = getWidgetPosition($packing);
$buttonStyle = 'position:absolute;white-space:nowrap;';
if ($positionArr['x'] !== NULL) {
$buttonStyle = $buttonStyle.'left:'.$positionArr['x'].'px;'.'top:'.$positionArr['y'].'px;';
}
// create HTML for the button
$oneBigString=$oneBigString.'<button class="buttonClass" style="'.$buttonStyle.'" id="'.$buttonID.'" name="'.$buttonID.'Name'.'" type="button">';
$propertiesArr = parseWidget($id);
$oneBigString=$oneBigString.getWidgetTitle($id);
$oneBigString=$oneBigString.'</button>'; // close the button tag
}
// this could be one of several types of input. Create the appropriate HTML element based on exact type
function handleGtkInput($id, $packing) {
global $oneBigString;
$textInputID = getWidgetID($id);
$inputStyle = 'position:absolute;white-space:nowrap;';
$inputType = 'radio';
// get button's attributes and properties in an array
$propertiesArr = parseWidget($id);
// get button's packing info
$positionArr = getWidgetPosition($packing);
if ($positionArr['x'] !== NULL) {
$inputStyle = $inputStyle.'left:'.$positionArr['x'].'px;'.'top:'.$positionArr['y'].'px;';
}
if ($id->getAttribute('class') == 'GtkCheckButton') {
$inputType = 'checkbox';
}
else if ($id->getAttribute('class') == 'GtkRadioButton') {
$textInputID = 'myRadio'; // to make sure that they are in the same group
}
if ($id->getAttribute('class') == 'GtkEntry') {
$oneBigString=$oneBigString.'<input class="EntryClass" id="'.$textInputID.'" name="'.$textInputID.'" type="text" readonly="readonly" value="'.getWidgetTitle($id).'" style="'.$inputStyle.'"/>'."\n";
}
else { // it is either radio or checkbox type
$oneBigString=$oneBigString.'<div style="'.$inputStyle.'">'."\n";
$oneBigString=$oneBigString.'<input id="'.$textInputID.'" name="'.$textInputID.'" type="'.$inputType.'" />';
$oneBigString=$oneBigString.getWidgetTitle($id);
$oneBigString=$oneBigString.'</div>'."\n";
}
}
// for now we are handling only one level. There could be multiple levels of embedded layouts
// we assume that there is no packing involved
function handleGtkLayout($layout) {
if ($layout->hasChildNodes()) {
foreach($layout->childNodes as $child) {
if ($child->tagName == 'child') { // pick out just the <child> elements
makeCallBasedOnElement($child);
}
}
}
}
// alignment does not have packing
function handleGtkAlignment($alignment) {
if($alignment->hasChildNodes()) {
foreach ($alignment->childNodes as $child) {
if ($child->tagName == 'child') {
makeCallBasedOnElement($child);
}
}
}
}
// images are handled in a similar manner to the function in gladeToJS, note that we do not use <div> wrappers anywhere
function handleGtkImage($image, $packing) {
global $oneBigString;
// get image ID
$imageID = getWidgetID($image);
$propertiesArr = parseWidget($image);
// get image's position info
$positionArr = getWidgetPosition($packing);
$imageBufName = '';
$imageLocation = 'gladeToJS/images/';
$imageType = '';
$imageStyle = '';
if ($positionArr['x'] !== NULL) {
$imageStyle = $imageStyle.'left:'.$positionArr['x'].'px;'.'top:'.$positionArr['y'].'px;';
$imageStyle = $imageStyle.'position:absolute;';
}
// else it is inside a button, leave position to be default
// figure out whether this icon is a stock icon or whether it is custom
if(($propertiesArr['stock'] !== NULL) || ($propertiesArr['icon_name'] !== NULL)) {
$imageLocation = $imageLocation.'stock/'.$propertiesArr['stock'];
$imageType = '.png';
}
else if($propertiesArr['pixbuf'] !== NULL) {
$imageLocation = $imageLocation.'other/'.$propertiesArr['pixbuf'];
}
if ($positionArr['x'] !== NULL) {
$imageStyle = $imageStyle.'left:'.$positionArr['x'].'px;'.'top:'.$positionArr['y'].'px;';
}
else { // else this is an inner image, display inline
$imageStyle = $imageStyle.'display:inline;';
}
// display the image
$oneBigString=$oneBigString.'<img src="'.$imageLocation.$imageType.'" style="'.$imageStyle.'" id="'.$imageID.'" alt="myImage"/>';
}
function handleGtkComboBox($combo, $packing) {
global $oneBigString;
// get comboBox ID
$comboID = getWidgetID($combo);
$propertiesArr = parseWidget($combo);
// get combo's packing info
$positionArr = getWidgetPosition($packing);
$comboLocation = $propertiesArr['items'];
$comboStyle = 'position:absolute;width:30%;';
if ($positionArr['x'] !== NULL) {
$comboStyle = $comboStyle.'left:'.$positionArr['x'].'px;'.'top:'.$positionArr['y'].'px;';
}
// get the items of the combo box
$tok = strtok($comboLocation, "\n");
$oneBigString=$oneBigString.'<select style="'.$comboStyle.'" class="EntryClass" id="'.$comboID.'">'."\n";
while ($tok !== false) {
$oneBigString=$oneBigString.'<option value ="'.$tok.'">'.$tok.'</option>'."\n";
$tok = strtok("\n");
}
$oneBigString=$oneBigString.'</select>'."\n";
}
// TODO: this function does not work for anything but GtkFixed Layout. Extend to other layouts later!!
function makeCallBasedOnElement($child) {
global $oneBigString;
// packing is necessary to figure out widget location on the screen
$packing = null;
$childNodes = $child->childNodes;
foreach ($child->childNodes as $someChild) { // iterate over children of this node to find packing
if($someChild->tagName == 'packing') {
$packing = $someChild;
}
}
foreach ($child->childNodes as $element) { // for every <child> tag get its children (named item)
if (get_class($element) == 'DOMElement') { // once again we get DOMText along with what we really want. Filter DOMText out.
if($element->getAttribute('class') != null) { // precaution to make sure we have a valid element
$elementClass = $element->getAttribute('class');
if ($elementClass == 'GtkButton') {
handleGtkButton($element, $packing);
}
else if ($elementClass == 'GtkVBox') {
handleGtkLayout($element);
}
else if ($elementClass == 'GtkHBox') {
handleGtkLayout($element);
}
else if ($elementClass == 'GtkFixed') {
handleGtkLayout($element);
}
else if ($elementClass == 'GtkLabel') {
handleGtkLabel($element, $packing);
}
else if ($elementClass == 'GtkEntry') {
handleGtkInput($element, $packing);
}
else if ($elementClass == 'GtkCheckButton') {
handleGtkInput($element, $packing);
}
else if ($elementClass == 'GtkRadioButton') {
handleGtkInput($element, $packing);
}
else if ($elementClass == 'GtkImage') {
handleGtkImage($element, $packing);
}
else if ($elementClass == 'GtkAlignment') {
handleGtkAlignment($element);
}
else if ($elementClass == 'GtkComboBoxEntry') {
handleGtkComboBox($element, $packing);
}
else $oneBigString=$oneBigString.'<br /> UNKNOWN tag '.$element->tagName.'<br />';
}
}
}
}
function displayGtkWindow($window) {
global $totalX;
global $totalY;
global $oneBigString;
//<link rel="stylesheet" title="Flower (Default)" href="css/gladeMainCSS.css" type="text/css" media="screen"/>
// the HTML below emulates WalterZorn's implementation. This took a bit of time to tweak to the right values.
// if you need to change them, play around with positions of resizehandle and resizebutton images first.
$oneBigString=$oneBigString.
'<div>
<link rel="stylesheet" title="Flower (Default)" href="gladeToJS/css/gladeMainCSS.css" type="text/css" media="screen"/>
<div id="titlebar" style="position:relative;border:none;background:#4455aa;overflow:hidden; z-index:1000; width:'.($totalX+20).'px;height:16px;">
<span style="position:relative;left:2px;top:2px;bottom:2px;color:white;font-weight:bold;font-size:11px;font-family:Verdana,Geneva,sans-serif;">
'.getWidgetTitle($window).
'</span>
</div>';
$oneBigString=$oneBigString.
'<div id="frame" style="width:'.($totalX+20).'px;height:'.($totalY+5).'px;background:#cccccc;">
<div id="clientarea" style="position:relative;border:2px inset #cccccc;overflow:auto; width:'.($totalX+10).'px; height:'.($totalY-38).'px; left:3px; top:19px;">';
foreach ($window->childNodes as $item) { // scan through the list of childNodes of a window. The list includes all window properties.
if (get_class($item) == 'DOMElement') { // we get DOMText mixed in as well. Filter that out.
if ($item->tagName == 'child') { // find ONLY children. We don't care about window properties for now
makeCallBasedOnElement($item); // we can just have buttons here, not necessarily layouts, so handle it
}
}
}
$oneBigString=$oneBigString.
'</div>
</div>
<img name="resizehandle" style="position:relative; z-index:500; left:'.$totalX.'px; top:-20px;" src="gladeToJS/winresize.gif" width="20" height="20" alt="" />
</div>
<img name="resizebutton" style="position:relative; z-index:1500; left:'.($totalX+4).'px; top:-'.($totalY+40).'px;" src="gladeToJS/button_up_outset.gif" width="16" height="14" alt="" />';
}
/**
* Hold thrown errors statically
*/
function staticerror($errno, $errstr, $errfile, $errline, $errcontext, $ret = false) {
static $errs = array();
if ($ret === true) {
return $errs;
}
$tag = 'DOMDocument::validate(): ';
$errs[] = str_replace($tag, '', $errstr);
}
// Main Document Processing
function mainDocProcess($file) {
global $totalX;
global $totalY;
global $maxX;
global $maxY;
global $widthRequest;
global $heightRequest;
global $largestPackingNode;
global $oneBigString;
// Load a document
$doc = DOMDocument::loadXML($file);
// -- dreamhost implementation to validate the schema -- //
// Set up error handling
set_error_handler('staticerror');
$old = ini_set('html_errors', false);
// Validate
if ($doc->validate()) {
// $oneBigString = $oneBigString.'validation passed';
//$oneBigString = $oneBigString.'actualEncoding: '.$doc->actualEncoding.' '.'doctype name: '.$doc->doctype->name.' '.
// 'doctype publicId: '.$doc->doctype->publicId.' '.'xmlEncoding: '.$doc->xmlEncoding.' ';
}
//else $oneBigString=$oneBigString."NOT VALID \n";
$doc->validateOnParse = true;
// Restore error handling
ini_set('html_errors', $old);
restore_error_handler();
// -- end dreamhost implementation to validate the schema -- //
// find the totalX and totalY positions
$packingNodes = $doc->getElementsByTagName('packing');
$tempX;
$tempY;
foreach($packingNodes as $pChild) { // examine every packing node
foreach ($pChild->getElementsByTagName('property') as $pName) {
if ($pName->getAttribute('name') == 'x') { // extract x
$tempX = $pName->nodeValue;
}
else if ($pName->getAttribute('name') == 'y') { // extract y
$tempY = $pName->nodeValue;
}
}
foreach($pChild->previousSibling->previousSibling->childNodes as $widgetProp) {
if($widgetProp->nodeName == 'property') {
if($widgetProp->getAttribute('name') == 'width_request') { // extract width
$tempX = $tempX + $widgetProp->nodeValue;
}
else if($widgetProp->getAttribute('name') == 'height_request') { // extract height
$tempY = $tempY + $widgetProp->nodeValue;
}
}
}
if (($tempX <= $maxX) && ($tempX > $totalX)) { // set new x value
$totalX = $tempX;
}
if (($tempY <= $maxY) && ($tempY > $totalY)) { // set new y value
$totalY = $tempY;
}
}
// change totalY to reflect title bar, padding etc.
$totalY = $totalY+45;
// render elements
$nodeList = $doc->getElementsByTagName('widget');
// execute first parsing step
foreach ($nodeList as $param) {
if ($param->getAttribute('class') == 'GtkWindow') { // Render windows first
displayGtkWindow($param);
}
}
}
?>