Refactoring UI, Skinning, Translating - was: Re: [sldev] sljirastats.com Linden Metrics Report

Felix Duesenburg kfa at gmx.net
Thu Apr 24 18:45:19 PDT 2008


Callum Lerwick wrote:
> On Thu, 2008-04-24 at 06:20 -0400, Felix Duesenburg wrote:
>> IMHO the xml files are in dire need of refactoring. Should we not do this:
>>
>> In the XUI files, separate the structure from the skin. Only colors,
>> text labels, border styles etc. go into the skin overlay. Even that
>> could again be separated into a style pack and a language pack only
>> containing the labels and tooltips.
> 
> Can you say... CSS?
> 

Very good... but will have to be implemented from scratch...

Here's a little tool attached that I had lying around somewhere and
could adapt. It rips the xui files apart and separates function, skin
and language parts as suggested.

It scans the skins/xui/en-us for all xml files that contain floaters,
panels or menus. Then it filters by attributes, and distributes the
elements into 3 different document trees. This is just quick fix, the
filter may need some fine-tuning (in start_element in the handler package).

The output is formatted in a patch-friendly format. The resulting folder
structure is just a proposal, too: ./xui for the functional part,
./skins/default for the styles, and ./lang/en-us for the text.

Simply place it in the source tree under the newview folder and run from
the command line. (Sorry no Python, my mother tongue is Perl). It
doesn't overwrite any existing files.

Not sure if this makes sense to anybody, but at least it can create you
a clean set of files for skinning or translation work.

Cheers,
Felix
-------------- next part --------------
#!/usr/bin/perl

use XML::Parser::PerlSAX;
use File::Path;

$handler = MyXMLHandler->new();
$parser = XML::Parser::PerlSAX->new(Handler => $handler);

$lang = 'en-us';
mkpath(["./xui", "./skin/default", "./lang/$lang"], 1);
$dir = "./skins/xui/$lang";
opendir(DIR, $dir) or die "can't opendir $dir: $!\n";

while (my $file = readdir(DIR)) {
	next unless $file =~ /\.xml$/;
	print "parsing $dir/$file\n";
	$handler->set_outfiles("./xui/$file", "./skin/default/$file", "./lang/$lang/$file");
	$parser->parse(Source => {SystemId => "$dir/$file"});
}
closedir DIR;
exit;

package MyXMLHandler;

use XML::DOM;
use Data::Dumper;

my ($root, $process, $level, $text);
my ($xui_doc, $skin_doc, $lang_doc);
my ($xui_n, $skin_n, $lang_n);
my ($xui_file, $skin_file, $lang_file);
my ($xui_node, $skin_node, $lang_node);
my ($xui_parent, $skin_parent, $lang_parent);

sub set_outfiles {
    my ($self, $f1, $f2, $f3) = @_;
	$xui_file = $f1;
	$skin_file = $f2;
	$lang_file = $f3;
}

sub new {
    my $type = shift;
    $filter = {};
    return bless {}, $type;
}

sub start_document {
    my ($self) = @_;
    $level = 0;
    $xui_doc = XML::DOM::Document->new;
    $xui_n = 0;
    $skin_doc = XML::DOM::Document->new;
    $skin_n = 0;
    $lang_doc = XML::DOM::Document->new;
    $lang_n = 0;
}

sub start_element {
    my ($self, $element) = @_;
    my $tag = lc($element->{Name});
    my $attrs = $element->{Attributes};
    if ($level == 0) {
		$root = $tag;
		$process = ($root =~ /^(.*floater.*|.*panel.*|menu)$/);
    }
	if ($process) {
		$xui_parent = $level ? $xui_node : $xui_doc;
		$xui_node = $xui_doc->createElement($tag);
		$xui_parent->appendChild($xui_node);
		$skin_parent = $level ? $skin_node : $skin_doc;
		$skin_node = $skin_doc->createElement($tag);
		$skin_parent->appendChild($skin_node);
		$lang_parent = $level ? $lang_node : $lang_doc;
		$lang_node = $lang_doc->createElement($tag);
		$lang_parent->appendChild($lang_node);
		foreach my $attr (sort keys %$attrs) {
			if ($attr =~ /^(name)$/i) {
				$xui_node->setAttribute($attr, $attrs->{$attr});
				$lang_node->setAttribute($attr, $attrs->{$attr});
				$skin_node->setAttribute($attr, $attrs->{$attr});
			}
			elsif ($attr =~ /^(label|tool_tip)$/i) {
				$lang_node->setAttribute($attr, $attrs->{$attr});
				$lang_n++;
			}
			elsif ($attr =~ /^(.*style|.*color|border.*)$/i) {
				$skin_node->setAttribute($attr, $attrs->{$attr});
				$skin_n++;
			}
			else {
				$xui_node->setAttribute($attr, $attrs->{$attr});
				$xui_n++;
			}
		}
		$text =~ s/^\s+//s;
		$text =~ s/\s+$//s;
		if ($text ne '') {
			$lang_node->addText($text);
			$lang_n++;
		    $text = '';
  		}
	}
    $level++;
}

sub characters {
	my ($self, $characters) = @_;
	if ($process) {
		$text .= $characters->{Data};
	}
}

sub end_element {
	my ($self, $element) = @_;
    my $tag = lc($element->{Name});
    $level--;
	if ($process) {
		$text =~ s/^\s+//s;
		$text =~ s/\s+$//s;
		if ($text ne '') {
			$lang_node->addText($text);
			$lang_n++;
			$text = '';
		}
		$xui_node = $xui_parent;
		$xui_parent = $xui_parent->getParentNode() if $level > 0;
		$skin_node = $skin_parent;
		$skin_parent = $skin_parent->getParentNode() if $level > 0;
		$lang_node = $lang_parent;
		$lang_parent = $lang_parent->getParentNode() if $level > 0;
    }
}

sub end_document {
    my ($self) = @_;
    if ($process && $xui_n) {
		if (open FILE, ">$xui_file") {
			print "writing $xui_file\n";
			traverse(FILE, $xui_doc);
			close FILE;
		}
		else {
			print "can't write to $xui_file: $!\n";
		}
	}
    if ($process && $lang_n) {
		if (open FILE, ">$lang_file") {
			print "writing $lang_file\n";
			traverse(FILE, $lang_doc);
			close FILE;
		}
		else {
			print "can't write to $lang_file: $!\n";
		}
	}
    if ($process && $skin_n) {
		if (open FILE, ">$skin_file") {
			print "writing $skin_file\n";
			traverse(FILE, $skin_doc);
			close FILE;
		}
		else {
			print "can't write to $skin_file: $!\n";
		}
	}
}

sub traverse {
	my($fp, $node, $indent)= @_;
	my $ind = "\t" x $indent;
	my $inda = "$ind\t";
	if ($node->getNodeType == DOCUMENT_NODE) {
		print $fp qq~<?xml version="1.0" encoding="utf-8" standalone="yes" ?>\n~;
		foreach my $child ($node->getChildNodes) {
			traverse($fp, $child, 0);
		}
	}
	elsif ($node->getNodeType == ELEMENT_NODE) {
		my $name = $node->getNodeName;
		my $attrs = $node->getAttributes;
		print $fp qq~$ind<$name\n~;
		for my $attr (sort attr_sort $attrs->getValues) {
			my $key = $attr->getNodeName;
			my $val = $attr->getNodeValue;
			print $fp qq~$inda$key="$val"\n~;
		}
		print $fp qq~$ind>\n~;
		foreach my $child ($node->getChildNodes) {
			traverse($fp, $child, $indent + 1);
		}
		print $fp qq~$ind</$name>\n~;
	}
	elsif ($node->getNodeType == TEXT_NODE) {
		my $text = $node->getData;
		print $fp qq~$text\n~;
	}
}

sub attr_sort {
	return -1 if $a->getNodeName eq 'name';
	return 1 if $b->getNodeName eq 'name';
	return $a->getNodeName cmp $b->getNodeName;
}

1; #Ye Olde 'Return True' for the in-line package...


More information about the SLDev mailing list