// Copyright (c) 2000 - 2001 Phil Thompson <phil@river-bank.demon.co.uk>

/**********************************************************************
**   Copyright (C) 2000 Troll Tech AS.  All rights reserved.
**
**   This file is part of Qt GUI Designer.
**
**   This file may be distributed under the terms of the GNU General
**   Public License version 2 as published by the Free Software
**   Foundation and appearing in the file COPYING included in the
**   packaging of this file. If you did not get the file, send email
**   to info@trolltech.com
**
**   The file is provided AS IS with NO WARRANTY OF ANY KIND,
**   INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
**   A PARTICULAR PURPOSE.
**
**********************************************************************/

#include "uic.h"
#include <qapplication.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <qstringlist.h>
#include <qstrlist.h>
#include <qdatetime.h>
#include <widgetdatabase.h>
#include <domtool.h>
#include <globaldefs.h>
#include <qregexp.h>
#include <zlib.h>

static QString mkBool( bool b )
{
    return b? "1" : "0";
}

static QString mkBool( const QString& s )
{
    return mkBool( s == "true" || s == "1" );
}

static bool toBool( const QString& s )
{
    return s == "true" || s.toInt() != 0;
}


// fixString is only used in conjunction with tr(). We need to write out the
// string in utf8 and make sure it's converted from utf8 when created.
static QString fixString( const QString &str )
{
    QString s( str );
    s.replace( QRegExp( "\\\\" ), "\\\\" );
    s.replace( QRegExp( "\"" ), "\\\"" );
    s.replace( QRegExp( "\n" ), "\\n\"\n\"" );
    s.replace( QRegExp( "\r" ), "\\r" );

    bool onlyAscii = TRUE;
    unsigned int i;
    for ( i = 0; i < s.length(); i++ ) {
        if ( s.at(i).unicode() >= 0x80 ) {
            onlyAscii = FALSE;
            break;
        }
    }
    if ( onlyAscii )
        s = "\"" + s + "\"";
    else
        s = "QString.fromUtf8(\"" + s + "\")";
    return s;
}

static QString mkStdSet( const QString& prop )
{
    return QString( "set" ) + prop[0].upper() + prop.mid(1);
}


/*!
  \class Uic uic.h
  \brief User Interface Compiler

  The class Uic encapsulates the user interface compiler (uic).
 */

static QString className;

Uic::Uic( QTextStream &outStream, QDomDocument doc, const QString &trm )
    : out( outStream ), trmacro( trm )
{
    item_used = cg_used = pal_used = 0;

    initIndent();

    layouts << "hbox" << "vbox" << "grid";
    tags = layouts;
    tags << "widget";

    nameOfClass = getClassName( doc.firstChild().toElement() );

    QDomElement firstWidget = doc.firstChild().firstChild().toElement();
    while ( firstWidget.tagName() != "widget" )
	firstWidget = firstWidget.nextSibling().toElement();

    if ( nameOfClass.isEmpty() )
	nameOfClass = getObjectName( firstWidget );

    // This is a hack.
    className = nameOfClass;

    createFormImpl( firstWidget );
}


/*! Extracts a class name from \a e
 */
QString Uic::getClassName( const QDomElement& e )
{
    QDomElement n;
    QString cn;
    for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName() == "class" ) {
	    QString s = n.firstChild().toText().data();
	    int i;
	    while ( ( i = s.find(' ' )) != -1  )
		s[i] = '_';
	    cn = s;
	} else if ( n.tagName() == "pixmapfunction" ) {
	    pixmapLoaderFunction = n.firstChild().toText().data();
	}
    }
    return cn;
}

/*! Extracts an object name from \a e. It's stored in the 'name'
 property.
 */
QString Uic::getObjectName( const QDomElement& e )
{
    QDomElement n;
    for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName() == "property" ) {
	    QDomElement n2 = n.firstChild().toElement();
	    if ( n2.tagName() == "name" && n2.firstChild().toText().data() == "name" ) {
		return n2.nextSibling().toElement().firstChild().toText().data();
	    }
	}
    }
    return QString::null;
}

/*! Extracts an layout name from \a e. It's stored in the 'name'
 property of the preceeding sibling (the first child of a QLayoutWidget).
 */
QString Uic::getLayoutName( const QDomElement& e )
{
    QDomElement p = e.parentNode().toElement();
    QString tail = QString::null;
 
    if (getClassName(p) != "QLayoutWidget")
        tail = "Layout";
 
    QDomElement n;
    for ( n = p.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
        if ( n.tagName() == "property" ) {
            QDomElement n2 = n.firstChild().toElement();
            if ( n2.tagName() == "name" && n2.firstChild().toText().data() == "name" ) {
                return n2.nextSibling().toElement().firstChild().toText().data() + tail;
            }
        }
    }
    return e.tagName();
}

QByteArray unzipXPM( QString data, ulong& length )
{
    char *ba = new char[ data.length() / 2 ];
    for ( int i = 0; i < (int)data.length() / 2; ++i ) {
	char h = data[ 2 * i ].latin1();
	char l = data[ 2 * i  + 1 ].latin1();
	uchar r = 0;
	if ( h <= '9' )
	    r += h - '0';
	else
	    r += h - 'a' + 10;
	r = r << 4;
	if ( l <= '9' )
	    r += l - '0';
	else
	    r += l - 'a' + 10;
	ba[ i ] = r;
    }
    if ( length <  data.length() * 5 )
	length = data.length() * 5;
    QByteArray baunzip( length );
    ::uncompress( (uchar*) baunzip.data(), &length, (uchar*) ba, data.length()/2 );
    return baunzip;
}


/*!
  Creates an implementation ( cpp-file ) for the form given in \a e

  \sa createFormDecl(), createObjectImpl()
 */
void Uic::createFormImpl( const QDomElement &e )
{
    QStringList::Iterator it;
    QDomElement n;
    QDomNodeList nl;
    int i;
    QString objClass = getClassName( e );
    if ( objClass.isEmpty() )
	return;
    QString objName = getObjectName( e );

    // Handle custom widgets.
    for (n = e; !n.isNull(); n = n.nextSibling().toElement())
    {
	if (n.tagName() != "customwidgets")
		continue;

	QDomElement n2 = n.firstChild().toElement();

	while (!n2.isNull())
	{
		if (n2.tagName() == "customwidget")
		{
			QDomElement n3 = n2.firstChild().toElement();
			QString cname, header;

			while (!n3.isNull())
			{
				if (n3.tagName() == "class")
					cname = n3.firstChild().toText().data();
				else if (n3.tagName() == "header")
					header = n3.firstChild().toText().data();

				n3 = n3.nextSibling().toElement();
			}

			if (cname.isEmpty())
				cname = "UnnamedCustomClass";

			int ext = header.findRev('.');

			if (ext >= 0)
				header.truncate(ext);

			if (header.isEmpty())
				header = cname.lower();

			out << "from " << header << " import " << cname << endl;
		}

		n2 = n2.nextSibling().toElement();
	}
    }

    // find out what images are required
    QStringList requiredImages;
    nl = e.elementsByTagName( "pixmap" );
    for ( int j = 0; j < (int) nl.length(); j++ ) {
	requiredImages += nl.item(j).firstChild().toText().data();
    }
 
    QStringList images;
    QStringList xpmImages;
    if ( pixmapLoaderFunction.isEmpty() ) {
	// create images
	for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) {
	    if ( n.tagName()  == "images" ) {
		nl = n.elementsByTagName( "image" );
		for ( i = 0; i < (int) nl.length(); i++ ) {
		    QDomElement tmp = nl.item(i).firstChild().toElement();
		    QString img = registerObject( tmp.firstChild().toText().data() );
		    if ( !requiredImages.contains( img ) )
			continue;
		    tmp = tmp.nextSibling().toElement();
		    QString format = tmp.attribute("format", "PNG" );
		    QString data = tmp.firstChild().toText().data();

		    out << endl;
		    out << endl;

		    if ( format == "XPM.GZ" ) {
			xpmImages += img;
			ulong length = tmp.attribute("length").toULong();
			QByteArray baunzip = unzipXPM( data, length );
			int a = 0;
			out << indent << img << "_data = [" << endl;
			while ( baunzip[a] != '\"' )
			    a++;
			for ( ; a < (int) length; a++ )
			{
			    char ch;

			    if ((ch = baunzip[a]) == '}')
			    {
				out << endl << "]" << endl;
				break;
			    }

			    if (ch == '\"')
				ch = '\'';

			    out << ch;
			}
		    } else {
			images += img;
			out << indent << img << "_data = \\" << endl;
			pushIndent();
			out << indent << "'";
			int a ;
			for ( a = 0; a < (int) (data.length()/2)-1; a++ ) {
			    out << "\\x" << QString(data[2*a]) << QString(data[2*a+1]);
			    if ( a % 12 == 11 )
				out << "' \\" << endl << indent << "'";
			}
			out << "\\x" << QString(data[2*a]) << QString(data[2*a+1]) << "'" << endl;
			popIndent();
		    }
		}
	    }
	}
    }

    // register the object and unify its name
    objName = registerObject( objName );


    // constructor

    out << endl;
    out << endl;
    out << indent << "class " << nameOfClass << "(" << objClass << "):" << endl;
    pushIndent();

    if ( objClass == "QDialog" || objClass == "QWizard" ) {
	out << indent << "def __init__(self,parent = None,name = None,modal = 0,fl = 0):" << endl;
	pushIndent();
	out << indent << objClass << ".__init__(self,parent,name,modal,fl)" << endl;
    } else if ( objClass == "QWidget" )  { // standard QWidget
	out << indent << "def __init__(self,parent = None,name = None,fl = 0):" << endl;
	pushIndent();
	out << indent << objClass << ".__init__(self,parent,name,fl)" << endl;
    } else {
	out << indent << "def __init__(self,parent = None,name = None):" << endl;
	pushIndent();
	out << indent << objClass << ".__init__(self,parent,name)" << endl;
    }

    out << endl;

    // create pixmaps for all images
    if ( !images.isEmpty() ) {
	QStringList::Iterator it;
	for ( it = images.begin(); it != images.end(); ++it ) {
	    out << indent << (*it) << " = QPixmap()" << endl;
	    out << indent << (*it) << ".loadFromData(" << (*it) << "_data,'PNG')" << endl;
	}

	out << endl;
    }
    // create pixmaps for all images
    if ( !xpmImages.isEmpty() ) {
	for ( it = xpmImages.begin(); it != xpmImages.end(); ++it ) {
	    out << indent << (*it) << " = QPixmap(" << (*it) << "_data)" << endl;
	}

	out << endl;
    }


    // set the properties
    for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName() == "property" ) {
	    bool stdset = toBool( n.attribute( "stdset" ) );
	    QDomElement n2 = n.firstChild().toElement();

	    if ( n2.tagName() == "name" ) {
		QString prop = n2.firstChild().toText().data();
		QString value = setObjectProperty( objClass, QString::null, prop, n2.nextSibling().toElement(), stdset );

		if ( value.isEmpty() )
		    continue;

		if ( prop == "name" ) {
		    out << indent << "if name == None:" << endl;
		    pushIndent();
		}

		out << indent;

		if ( prop == "geometry" && n2.nextSibling().toElement().tagName() == "rect") {
		    QDomElement n3 = n2.nextSibling().toElement().firstChild().toElement();
		    int w = 0, h = 0;

		    while ( !n3.isNull() ) {
			if ( n3.tagName() == "width" )
			    w = n3.firstChild().toText().data().toInt();
			else if ( n3.tagName() == "height" )
			    h = n3.firstChild().toText().data().toInt();
			n3 = n3.nextSibling().toElement();
		    }

		    out << "self.resize(" << w << "," << h << ")" << endl;
		} else {
		    if ( stdset )
			out << "self." << mkStdSet(prop) << "(" << value << ")" << endl;
		    else
			out << "self.setProperty('" << prop << "',QVariant(" << value << "))" << endl;
		}

		if (prop == "name") {
		    popIndent();
		    out << endl;
		}
	    }
	}
    }


    // create all children, some forms have special requirements

    if ( objClass == "QWizard" ) {
	for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	    if ( tags.contains( n.tagName()  ) ) {
		QString page = createObjectImpl( n, objClass, "self" );
		QString label = DomTool::readAttribute( n, "title", "" ).toString();
		out << indent << "self.addPage(" << page << ","<< trmacro << "(" << fixString( label ) << "))" << endl;
		QVariant def( FALSE, 0 );
		if ( DomTool::hasAttribute( n, "backEnabled" ) )
		    out << indent << "self.setBackEnabled(" << page << "," << mkBool( DomTool::readAttribute( n, "backEnabled", def).toBool() ) << ")" << endl;
		if ( DomTool::hasAttribute( n, "nextEnabled" ) )
		    out << indent << "self.setNextEnabled(" << page << "," << mkBool( DomTool::readAttribute( n, "nextEnabled", def).toBool() ) << ")" << endl;
		if ( DomTool::hasAttribute( n, "finishEnabled" ) )
		    out << indent << "self.setFinishEnabled(" << page << "," << mkBool( DomTool::readAttribute( n, "finishEnabled", def).toBool() ) << ")" << endl;
		if ( DomTool::hasAttribute( n, "helpEnabled" ) )
		    out << indent << "self.setHelpEnabled(" << page << "," << mkBool( DomTool::readAttribute( n, "helpEnabled", def).toBool() ) << ")" << endl;
		if ( DomTool::hasAttribute( n, "finish" ) )
		    out << indent << "self.setFinish(" << page << "," << mkBool( DomTool::readAttribute( n, "finish", def).toBool() ) << ")" << endl;
	    }
	}
     } else { // standard widgets
	 for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	     if ( tags.contains( n.tagName()  ) )
		 createObjectImpl( n, objName, "self" );
	 }
     }

    // Get the list of any user defined slots.

    QStringList userSlots;

    for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName()  == "connections" ) {
	    for ( QDomElement n2 = n.firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) {
		if ( n2.tagName() == "slot" ) {
		    userSlots += n2.firstChild().toText().data();
		}
	    }
	}
    }

    for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName()  == "connections" ) {
	    // setup signals and slots connections
	    out << endl;
	    nl = n.elementsByTagName( "connection" );
	    for ( i = 0; i < (int) nl.length(); i++ ) {
		QString sender, receiver, signal, slot;
		for ( QDomElement n2 = nl.item(i).firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) {
		    if ( n2.tagName() == "sender" )
			sender = n2.firstChild().toText().data();
		    else if ( n2.tagName() == "receiver" )
			receiver = n2.firstChild().toText().data();
		    else if ( n2.tagName() == "signal" )
			signal = n2.firstChild().toText().data();
		    else if ( n2.tagName() == "slot" )
			slot = n2.firstChild().toText().data();
		}
		if ( sender.isEmpty() || receiver.isEmpty() || signal.isEmpty() || slot.isEmpty() )
		    continue;

		sender = registeredName( sender );
		receiver = registeredName( receiver );

		 // translate formwindow name to "self"
		if ( sender == objName )
		    sender = "self";
		else
		    sender = "self." + sender;
		if ( receiver == objName )
		    receiver = "self";
		else
		    receiver = "self." + receiver;

		// Note that we never use the SLOT() macro in case we have
		// custom widgets implemented in Python.
		out << indent << "self.connect(" << sender
		    << ",SIGNAL('" << signal << "'),self."
		    << slot.left(slot.find('(')) << ")" << endl;
	    }
	} else if ( n.tagName()  == "tabstops" ) {
	    // setup tab order
	    out << endl;
	    QString lastName;
	    QDomElement n2 = n.firstChild().toElement();
	    while ( !n2.isNull() ) {
		if ( n2.tagName() == "tabstop" ) {
		    QString name = n2.firstChild().toText().data();
		    name = registeredName( name );
		    if ( !lastName.isEmpty() )
			out << indent << "self.setTabOrder(self." << lastName << ",self." << name << ")" << endl;
		    lastName = name;
		}
		n2 = n2.nextSibling().toElement();
	    }
	}
    }


    // buddies
    bool firstBuddy = TRUE;
    for ( QValueList<Buddy>::Iterator buddy = buddies.begin(); buddy != buddies.end(); ++buddy ) {
 	if ( isObjectRegistered( (*buddy).buddy ) ) {
	    if ( firstBuddy ) {
		out << endl;
	    }
	    out << indent << "self." << (*buddy).key << ".setBuddy(self." << registeredName( (*buddy).buddy ) << ")" << endl;
	    firstBuddy = FALSE;
	}

    }

    // end of constructor
    popIndent();

    // handle application font changes if required
    nl = e.elementsByTagName( "widget" );
    bool needEventHandler = FALSE;
    for ( i = 0; i < (int) nl.length(); i++ ) {
	if ( DomTool::hasProperty( nl.item(i).toElement() , "font" ) ) {
	    needEventHandler = TRUE;
	    break;
	}
    }
    if ( needEventHandler ) {
	out << endl;
	out << indent << "def event(self,ev):" << endl;
	pushIndent();
	out << indent << "ret = " << objClass << ".event(self,ev)" << endl;
	out << endl;
	out << indent << "if ev.type() == QEvent.ApplicationFontChange:" << endl;
	pushIndent();

	for ( i = 0; i < (int) nl.length(); i++ ) {
	    n = nl.item(i).toElement();
	    if ( DomTool::hasProperty( n, "font" ) )
		createExclusiveProperty( n, "font" );
	}

	out << endl;
	popIndent();
	out << indent << "return ret" << endl;
	popIndent();
    }


    // Generate user defined slot hooks.

    for (it = userSlots.begin(); it != userSlots.end(); ++it) {
	int astart = (*it).find('(');

	out << endl;
	out << indent << "def " << (*it).left(astart) << "(self";

	// We don't reproduce the argument names (if any) because we would have
	// to remove the types - too complicated for the moment, so we just
	// count them and give them names based on their position.

	QString args = (*it).mid(astart + 1,(*it).find(')') - astart - 1).stripWhiteSpace();

	if (!args.isEmpty()) {
	    int nrargs = args.contains(',') + 1;

	    for (int i = 0; i < nrargs; ++i)
		out << ",a" << i;
	}

	out << "):" << endl;
	pushIndent();
	out << indent << "print '" << nameOfClass << "." << *it << ": not implemented yet'" << endl;
	popIndent();
    }

    popIndent();
}


/*!
  Creates an implementation for the object given in \a e.

  Traverses recursively over all children.

  Returns the name of the generated child object.

  \sa createObjectDecl()
 */
QString Uic::createObjectImpl( const QDomElement &e, const QString& parentClass, const QString& parent, const QString& layout )
{

    QDomElement n;
    QString objClass, objName, fullObjName;

    if ( layouts.contains( e.tagName() ) )
	return createLayoutImpl( e, parentClass, parent, layout );

    objClass = getClassName( e );
    if ( objClass.isEmpty() )
	return objName;
    objName = getObjectName( e );

    QString definedName = objName;
    bool isTmpObject = objName.isEmpty() || objClass == "QLayoutWidget";
    if ( isTmpObject ) {
	if ( objClass[0] == 'Q' )
	    objName = objClass.mid(1);
	else
	    objName = objClass.lower();
    }

    bool isLine = objClass == "Line";
    if ( isLine )
	objClass = "QFrame";

    out << endl;
    if ( objClass == "QLayoutWidget" ) {
	if ( layout.isEmpty() ) {
            // register the object and unify its name
            objName = registerObject( objName );
	    out << indent << objName << " = QWidget(" << parent << ",'" << definedName << "')" << endl;
	} else {
	    // the layout widget is not necessary, hide it by creating its child in the parent
	    QString result;
	    for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
		if (tags.contains( n.tagName()  ) )
		    result = createObjectImpl( n, parentClass, parent, layout );
	    }
	    return result;
	}

	// Layouts don't go into the class instance dictionary.
	fullObjName = objName;
    } else {
        // register the object and unify its name
        objName = registerObject( objName );

	// Temporary objects don't go into the class instance dictionary.
	fullObjName = isTmpObject ? objName : "self." + objName;

	out << indent << fullObjName << " = " << createObjectInstance( objClass, parent, objName ) << endl;
    }

    lastItem = "None";
    // set the properties and insert items
    for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName() == "property" ) {
	    bool stdset = toBool( n.attribute( "stdset" ) );
	    QDomElement n2 = n.firstChild().toElement();

	    if ( n2.tagName() == "name" ) {
		QString prop = n2.firstChild().toText().data();
		QString value = setObjectProperty( objClass, objName, prop, n2.nextSibling().toElement(), stdset );

		if ( value.isEmpty() )
		    continue;
		if ( prop == "name" )
		    continue;
		if ( prop == "buddy" && value[0] == '\'' && value[(int)value.length()-1] == '\'' ) {
		    buddies << Buddy( objName, value.mid(1, value.length() - 2 ) );
		    continue;
		}
		if ( isLine && prop == "orientation" ) {
		    prop = "frameStyle";
		    if ( value.right(10)  == "Horizontal" )
			value = "QFrame.HLine | QFrame.Sunken";
		    else
			value = "QFrame.VLine | QFrame.Sunken";
		}
		if ( prop == "buttonGroupId" ) {
		    if ( parentClass == "QButtonGroup" )
			out << indent << parent << ".insert(" << fullObjName << "," << value << ")" << endl;
		    continue;
		}
		if ( prop == "geometry") {
			out << indent << fullObjName << ".setGeometry(" << value << ")" << endl;
		} else {
		    if ( stdset )
			out << indent << fullObjName << "." << mkStdSet(prop) << "(" << value << ")" << endl;
		    else
			out << indent << fullObjName << ".setProperty('" << prop << "',QVariant(" << value << "))" << endl;
		}

	    }
	} else if ( n.tagName() == "item" ) {
	    if ( objClass.mid(1) == "ListBox" ) {
		QString s = createListBoxItemImpl( n, fullObjName );
		if ( !s.isEmpty() )
		    out << indent << s << endl;
	    } else if ( objClass.mid(1) == "ComboBox" ) {
		QString s = createListBoxItemImpl( n, fullObjName );
		if ( !s.isEmpty() )
		    out << indent << s << endl;
	    } else if ( objClass.mid(1) == "IconView" ) {
		QString s = createIconViewItemImpl( n, fullObjName );
		if ( !s.isEmpty() )
		    out << indent << s << endl;
	    } else if ( objClass.mid(1) == "ListView" ) {
		QString s = createListViewItemImpl( n, fullObjName, QString::null );
		if ( !s.isEmpty() )
		    out << s << endl;
	    }
	} else if ( n.tagName() == "column" ) {
	    if ( objClass.mid(1) == "ListView" ) {
		QString s = createListViewColumnImpl( n, fullObjName );
		if ( !s.isEmpty() )
		    out << s;
	    }
	}
    }

    // create all children, some widgets have special requirements

    if ( objClass == "QTabWidget" ) {
	for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	    if ( tags.contains( n.tagName()  ) ) {
		QString page = createObjectImpl( n, objClass, fullObjName );
		QString label = DomTool::readAttribute( n, "title", "" ).toString();
		out << indent << fullObjName << ".insertTab(" << page << "," << trmacro << "(" << fixString( label ) << "))" << endl;
	    }
	}
     } else { // standard widgets
	for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	    if ( tags.contains( n.tagName()  ) )
		createObjectImpl( n, objClass, fullObjName );
	}
    }

    return fullObjName;
}

/*!
  Creates implementation of an listbox item tag.
*/

QString Uic::createListBoxItemImpl( const QDomElement &e, const QString &parent )
{
    QDomElement n = e.firstChild().toElement();
    QString txt;
    QString pix;
    while ( !n.isNull() ) {
	if ( n.tagName() == "property" ) {
	    QDomElement n2 = n.firstChild().toElement();
	    if ( n2.tagName() == "name" ) {
		QString attrib = n2.firstChild().toText().data();
		QVariant v = DomTool::elementToVariant( n2.nextSibling().toElement(), QVariant() );
		if ( attrib == "text" )
		    txt = v.toString();
		else if ( attrib == "pixmap" ) {
		    pix = v.toString();
		    if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) {
			pix.prepend( pixmapLoaderFunction + "(" );
			pix.append( ")" );
		    }
		}
	    }
	}
	n = n.nextSibling().toElement();
    }

    return pix.isEmpty() ?
      parent + ".insertItem(" + trmacro + "(" + fixString( txt ) + "))":
      parent + ".insertItem(" + pix + "," + trmacro + "(" + fixString( txt ) + "))";
}

/*!
  Creates implementation of an iconview item tag.
*/

QString Uic::createIconViewItemImpl( const QDomElement &e, const QString &parent )
{
    QDomElement n = e.firstChild().toElement();
    QString txt;
    QString pix;
    while ( !n.isNull() ) {
	if ( n.tagName() == "property" ) {
	    QDomElement n2 = n.firstChild().toElement();
	    if ( n2.tagName() == "name" ) {
		QString attrib = n2.firstChild().toText().data();
		QVariant v = DomTool::elementToVariant( n2.nextSibling().toElement(), QVariant() );
		if ( attrib == "text" )
		    txt = v.toString();
		else if ( attrib == "pixmap" ) {
		    pix = v.toString();
		    if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) {
			pix.prepend( pixmapLoaderFunction + "( " );
			pix.append( " )" );
		    }
		}
	    }
	}
	n = n.nextSibling().toElement();
    }

    if ( pix.isEmpty() )
	return "QIconViewItem(" + parent + "," + trmacro + "(" + fixString( txt ) + "))";
    return "QIconViewItem(" + parent + "," + trmacro + "(" + fixString( txt ) + ")," + pix + ")";

}

/*!
  Creates implementation of an listview item tag.
*/

QString Uic::createListViewItemImpl( const QDomElement &e, const QString &parent,
				     const QString &parentItem )
{
    QString s;

    QDomElement n = e.firstChild().toElement();

    bool hasChildren = e.elementsByTagName( "item" ).count() > 0;
    QString item;

    if ( hasChildren ) {
	item = registerObject( "item" );
	s = indent + item + " = ";
    } else {
	item = "item";
	if ( item_used )
	    s = indent + item + " = ";
	else
	    s = indent + item + " = ";
	item_used = TRUE;
    }

    if ( !parentItem.isEmpty() )
	s += "QListViewItem(" + parentItem + "," + lastItem + ")\n";
    else
	s += "QListViewItem(" + parent + "," + lastItem + ")\n";

    QStringList textes;
    QStringList pixmaps;
    while ( !n.isNull() ) {
	if ( n.tagName() == "property" ) {
	    QDomElement n2 = n.firstChild().toElement();
	    if ( n2.tagName() == "name" ) {
		QString attrib = n2.firstChild().toText().data();
		QVariant v = DomTool::elementToVariant( n2.nextSibling().toElement(), QVariant() );
		if ( attrib == "text" )
		    textes << v.toString();
		else if ( attrib == "pixmap" ) {
		    QString pix = v.toString();
		    if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) {
			pix.prepend( pixmapLoaderFunction + "( " );
			pix.append( " )" );
		    }
		    pixmaps << pix;
		}
	    }
	} else if ( n.tagName() == "item" ) {
	    s += indent + item + ".setOpen(1)\n";
	    s += createListViewItemImpl( n, parent, item );
	}
	n = n.nextSibling().toElement();
    }

    for ( int i = 0; i < (int)textes.count(); ++i ) {
	if ( !textes[ i ].isEmpty() )
	    s += indent + item + ".setText(" + QString::number( i ) + "," + trmacro + "(" + fixString( textes[ i ] ) + "))\n";
	if ( !pixmaps[ i ].isEmpty() )
	    s += indent + item + ".setPixmap(" + QString::number( i ) + "," + pixmaps[ i ] + ")\n";
    }

    lastItem = item;
    return s;
}

/*!
  Creates implementation of an listview column tag.
*/

QString Uic::createListViewColumnImpl( const QDomElement &e, const QString &parent )
{
    QDomElement n = e.firstChild().toElement();
    QString txt;
    QString pix;
    bool clickable = FALSE, resizeable = FALSE;
    while ( !n.isNull() ) {
	if ( n.tagName() == "property" ) {
	    QDomElement n2 = n.firstChild().toElement();
	    if ( n2.tagName() == "name" ) {
		QString attrib = n2.firstChild().toText().data();
		QVariant v = DomTool::elementToVariant( n2.nextSibling().toElement(), QVariant() );
		if ( attrib == "text" )
		    txt = v.toString();
		else if ( attrib == "pixmap" ) {
		    pix = v.toString();
		    if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) {
			pix.prepend( pixmapLoaderFunction + "( " );
			pix.append( " )" );
		    }
		} else if ( attrib == "clickable" )
		    clickable = v.toBool();
		else if ( attrib == "resizeable" )
		    resizeable = v.toBool();
	    }
	}
	n = n.nextSibling().toElement();
    }

    QString s;
    s = indent + parent + ".addColumn(" + trmacro + "(" + fixString( txt ) + "))\n";
    if ( !pix.isEmpty() )
	s += indent + parent + ".header().setLabel(" + parent + ".header().count() - 1," + pix + "," + trmacro + "(" + fixString( txt ) + "))\n";
    if ( !clickable )
	s += indent + parent + ".header().setClickEnabled(0," + parent + ".header().count() - 1)\n";
    if ( !resizeable )
	s += indent + parent + ".header().setResizeEnabled(0," + parent + ".header().count() - 1)\n";

    return s;
}

/*!
  Creates the implementation of a layout tag. Called from createObjectImpl().
 */
QString Uic::createLayoutImpl( const QDomElement &e, const QString& parentClass, const QString& parent, const QString& layout )
{
    QDomElement n;
    QString objClass, objName;
    objClass = e.tagName();

    QString qlayout = "QVBoxLayout";
    if ( objClass == "hbox" )
	qlayout = "QHBoxLayout";
    else if ( objClass == "grid" )
	qlayout = "QGridLayout";

    bool isGrid = e.tagName() == "grid" ;
    objName = registerObject( getLayoutName( e ) );
    layoutObjects += objName;
    int margin = DomTool::readProperty( e, "margin", BOXLAYOUT_DEFAULT_MARGIN ).toInt();
    int spacing = DomTool::readProperty( e, "spacing", BOXLAYOUT_DEFAULT_SPACING ).toInt();

    if ( (parentClass == "QGroupBox" || parentClass == "QButtonGroup") && layout.isEmpty() ) {
	// special case for group box
	out << indent << parent << ".setColumnLayout(0,Qt.Vertical)" << endl;
	out << indent << parent << ".layout().setSpacing(0)" << endl;
	out << indent << parent << ".layout().setMargin(0)" << endl;
	out << indent << objName << " = " << qlayout << "(" << parent << ".layout())" << endl;
	out << indent << objName << ".setAlignment(Qt.AlignTop)" << endl;
    } else {
	if ( layout.isEmpty() )
	    out << indent << objName << " = " << qlayout << "(" << parent << ")" << endl;
	else
	    out << indent << objName << " = " << qlayout << "()" << endl;
    }

    out << indent << objName << ".setSpacing(" << spacing << ")" << endl;
    out << indent << objName << ".setMargin(" << margin << ")" << endl;

    if ( !isGrid ) {
	for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	    if ( n.tagName() == "spacer" ) {
		QString child = createSpacerImpl( n, parentClass, parent, objName );
		out << indent << objName << ".addItem(" << child << ")" << endl;
	    } else if ( tags.contains( n.tagName() ) ) {
		QString child = createObjectImpl( n, parentClass, parent, objName );
		if ( isLayout( child ) )
		    out << indent << objName << ".addLayout(" << child << ")" << endl;
		else
		    out << indent << objName << ".addWidget(" << child << ")" << endl;
	    }
	}
    } else {
	for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	    QDomElement ae = n;
	    int row = ae.attribute( "row" ).toInt();
	    int col = ae.attribute( "column" ).toInt();
	    int rowspan = ae.attribute( "rowspan" ).toInt();
	    int colspan = ae.attribute( "colspan" ).toInt();
	    if ( rowspan < 1 )
		rowspan = 1;
	    if ( colspan < 1 )
		colspan = 1;
	    if ( n.tagName() == "spacer" ) {
		QString child = createSpacerImpl( n, parentClass, parent, objName );
		if ( rowspan * colspan != 1 )
		    out << indent << objName << ".addMultiCell(" << child << ","
			<< row << "," << row + rowspan - 1 << "," << col << ","
			<< col + colspan - 1 << ")" << endl;
		else
		    out << indent << objName << ".addItem(" << child << ","
			<< row << "," << col << ")" << endl;
	    } else if ( tags.contains( n.tagName() ) ) {
		QString child = createObjectImpl( n, parentClass, parent, objName );
		out << endl;
		QString o = "Widget";
		if ( isLayout( child ) )
		    o = "Layout";
		if ( rowspan * colspan != 1 )
		    out << indent << objName << ".addMultiCell" << o << "(" << child << ","
			<< row << "," << row + rowspan - 1 << "," << col << "," << col + colspan - 1 << ")" << endl;
		else
		    out << indent << objName << ".add" << o << "(" << child << ","
			<< row << "," << col << ")" << endl;
	    }
	}
    }

    return objName;
}



QString Uic::createSpacerImpl( const QDomElement &e, const QString& /*parentClass*/, const QString& /*parent*/, const QString& /*layout*/)
{
    QDomElement n;
    QString objClass, objName;
    objClass = e.tagName();
    objName = registerObject( "spacer" );

    QSize size = DomTool::readProperty( e, "sizeHint", QSize(0,0) ).toSize();
    QString sizeType = DomTool::readProperty( e, "sizeType", "Expanding" ).toString();
    bool isVspacer = DomTool::readProperty( e, "orientation", "Horizontal" ) == "Vertical";

    if ( sizeType != "Expanding" && sizeType != "MinimumExpanding" &&
         DomTool::hasProperty( e, "geometry" ) ) { // compatibility Qt 2.2
        QRect geom = DomTool::readProperty( e, "geometry", QRect(0,0,0,0) ).toRect();
        size = geom.size();
    }

    if ( isVspacer )
	out << indent << objName << " = QSpacerItem("
	    << size.width() << "," << size.height()
	    << ",QSizePolicy.Minimum,QSizePolicy." << sizeType << ")" << endl;
    else
	out << indent << objName << " = QSpacerItem("
	    << size.width() << "," << size.height()
	    << ",QSizePolicy." << sizeType << ",QSizePolicy.Minimum)" << endl;

    return objName;
}

/*!
  Creates a set-call for property \a exclusiveProp of the object
  given in \a e.

  If the object does not have this property, the function does nothing.

  Exclusive properties are used to generate the implementation of
  application font or palette change handlers in createFormImpl().

 */
void Uic::createExclusiveProperty( const QDomElement & e, const QString& exclusiveProp )
{
    QDomElement n;
    QString objClass = getClassName( e );
    if ( objClass.isEmpty() )
	return;
    QString objName = getObjectName( e );
    if ( objClass.isEmpty() )
	return;
    for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName() == "property" ) {
	    bool stdset = toBool( n.attribute( "stdset" ) );
	    QDomElement n2 = n.firstChild().toElement();
	    if ( n2.tagName() == "name" ) {
		QString prop = n2.firstChild().toText().data();
		if ( prop != exclusiveProp )
		    continue;
		QString value = setObjectProperty( objClass, objName, prop, n2.nextSibling().toElement(), stdset );
		if ( value.isEmpty() )
		    continue;
		out << indent << objName << ".setProperty('" << prop << "',QVariant(" << value << "))" << endl;
	    }
	}
    }

}


const char* const ColorRole[] = {
    "Foreground", "Button", "Light", "Midlight", "Dark", "Mid",
    "Text", "BrightText", "ButtonText", "Base", "Background", "Shadow",
    "Highlight", "HighlightedText", 0
};


/*!
  Attention: this function has to be in sync with Resource::setWidgetProperty(). If you change one, change both.
 */
QString Uic::setObjectProperty( const QString& objClass, const QString& obj, const QString &prop, const QDomElement &e, bool stdset )
{
    QString v;
    if ( e.tagName() == "rect" ) {
	QDomElement n3 = e.firstChild().toElement();
	int x = 0, y = 0, w = 0, h = 0;
	while ( !n3.isNull() ) {
	    if ( n3.tagName() == "x" )
		x = n3.firstChild().toText().data().toInt();
	    else if ( n3.tagName() == "y" )
		y = n3.firstChild().toText().data().toInt();
	    else if ( n3.tagName() == "width" )
		w = n3.firstChild().toText().data().toInt();
	    else if ( n3.tagName() == "height" )
		h = n3.firstChild().toText().data().toInt();
	    n3 = n3.nextSibling().toElement();
	}
	v = "QRect(%1,%2,%3,%4)";
	v = v.arg(x).arg(y).arg(w).arg(h);

    } else if ( e.tagName() == "point" ) {
	QDomElement n3 = e.firstChild().toElement();
	int x = 0, y = 0;
	while ( !n3.isNull() ) {
	    if ( n3.tagName() == "x" )
		x = n3.firstChild().toText().data().toInt();
	    else if ( n3.tagName() == "y" )
		y = n3.firstChild().toText().data().toInt();
	    n3 = n3.nextSibling().toElement();
	}
	v = "QPoint(%1,%2)";
	v = v.arg(x).arg(y);
    } else if ( e.tagName() == "size" ) {
	QDomElement n3 = e.firstChild().toElement();
	int w = 0, h = 0;
	while ( !n3.isNull() ) {
	    if ( n3.tagName() == "width" )
		w = n3.firstChild().toText().data().toInt();
	    else if ( n3.tagName() == "height" )
		h = n3.firstChild().toText().data().toInt();
	    n3 = n3.nextSibling().toElement();
	}
	v = "QSize(%1,%2)";
	v = v.arg(w).arg(h);
    } else if ( e.tagName() == "color" ) {
	QDomElement n3 = e.firstChild().toElement();
	int r= 0, g = 0, b = 0;
	while ( !n3.isNull() ) {
	    if ( n3.tagName() == "red" )
		r = n3.firstChild().toText().data().toInt();
	    else if ( n3.tagName() == "green" )
		g = n3.firstChild().toText().data().toInt();
	    else if ( n3.tagName() == "blue" )
		b = n3.firstChild().toText().data().toInt();
	    n3 = n3.nextSibling().toElement();
	}
	v = "QColor(%1,%2,%3)";
	v = v.arg(r).arg(g).arg(b);
    } else if ( e.tagName() == "font" ) {
	QDomElement n3 = e.firstChild().toElement();
	QString fontname = "f";
	if ( !obj.isEmpty() ) {
	    fontname = obj + "_font";
	    out << indent << fontname << " = QFont(self." << obj << ".font())" << endl;
	} else {
	    out << indent << fontname << " = QFont(self.font())" << endl;
	}
	while ( !n3.isNull() ) {
	    if ( n3.tagName() == "family" )
		out << indent << fontname << ".setFamily('" << n3.firstChild().toText().data() << "')" << endl;
	    else if ( n3.tagName() == "pointsize" )
		out << indent << fontname << ".setPointSize(" << n3.firstChild().toText().data() << ")" << endl;
	    else if ( n3.tagName() == "bold" )
		out << indent << fontname << ".setBold(" << mkBool( n3.firstChild().toText().data() ) << ")" << endl;
	    else if ( n3.tagName() == "italic" )
		out << indent << fontname << ".setItalic(" << mkBool( n3.firstChild().toText().data() ) << ")" << endl;
	    else if ( n3.tagName() == "underline" )
		out << indent << fontname << ".setUnderline(" << mkBool( n3.firstChild().toText().data() ) << ")" << endl;
	    else if ( n3.tagName() == "strikeout" )
		out << indent << fontname << ".setStrikeOut(" << mkBool( n3.firstChild().toText().data() ) << ")" << endl;
	    n3 = n3.nextSibling().toElement();
	}

	if ( prop == "font" ) {
	    if ( !obj.isEmpty() )
		out << indent << "self." << obj << ".setFont(" << fontname << ")" << endl;
	    else
		out << indent << "self.setFont(" << fontname << ")" << endl;
	} else {
	    v = fontname;
	}
    } else if ( e.tagName() == "string" ) {
	if ( prop == "toolTip" ) {
	    if ( !obj.isEmpty() )
		out << indent << "QToolTip.add(self." << obj << "," + trmacro + "(" << fixString( e.firstChild().toText().data() ) << "))" << endl;
	    else
		out << indent << "QToolTip.add(self," + trmacro + "(" << fixString( e.firstChild().toText().data() ) << "))" << endl;
	} else if ( prop == "whatsThis" ) {
	    if ( !obj.isEmpty() )
		out << indent << "QWhatsThis.add(self." << obj << "," << trmacro << "(" << fixString( e.firstChild().toText().data() ) << "))" << endl;
	    else
		out << indent << "QWhatsThis.add(self," << trmacro << "(" << fixString( e.firstChild().toText().data() ) << "))" << endl;
	} else {
	    v = trmacro + "(%1)";
	    v = v.arg( fixString( e.firstChild().toText().data() ) );
	}
    } else if ( e.tagName() == "cstring" ) {
	    v = "'%1'";
	    v = v.arg( e.firstChild().toText().data() );
    } else if ( e.tagName() == "number" ) {
	v = "%1";
	v = v.arg( e.firstChild().toText().data() );
    } else if ( e.tagName() == "bool" ) {
	if ( stdset )
	    v = "%1";
	else
	    v = "%1,0";
	v = v.arg( mkBool( e.firstChild().toText().data() ) );
    } else if ( e.tagName() == "pixmap" ) {
	v = e.firstChild().toText().data();
	if ( !pixmapLoaderFunction.isEmpty() ) {
	    v.prepend( pixmapLoaderFunction + "( " );
	    v.append( " )" );
	}
    } else if ( e.tagName() == "iconset" ) {
	v = "QIconSet(%1)";
	v = v.arg( e.firstChild().toText().data() );
    } else if ( e.tagName() == "image" ) {
	v = e.firstChild().toText().data() + ".convertToImage()";
    } else if ( e.tagName() == "enum" ) {
	v = "%1.%2";
        QString oc = objClass;
        QString ev = e.firstChild().toText().data();
        if ( oc == "QListView" && ev == "Manual" ) // #### workaround, rename QListView::Manual of WithMode enum in 3.0
            oc = "QScrollView";
        v = v.arg( oc ).arg( ev );
    } else if ( e.tagName() == "set" ) {
	QString keys( e.firstChild().toText().data() );
	QStringList lst = QStringList::split( '|', keys );
	v = "";
	for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
	    v += objClass + "." + *it;
	    if ( it != lst.fromLast() )
		v += " | ";
	}
    } else if ( e.tagName() == "sizepolicy" ) {
	QDomElement n3 = e.firstChild().toElement();
	QSizePolicy sp;
	while ( !n3.isNull() ) {
	    if ( n3.tagName() == "hsizetype" )
		sp.setHorData( (QSizePolicy::SizeType)n3.firstChild().toText().data().toInt() );
	    else if ( n3.tagName() == "vsizetype" )
		sp.setVerData( (QSizePolicy::SizeType)n3.firstChild().toText().data().toInt() );
	    n3 = n3.nextSibling().toElement();
	}
        QString tmp;
        if ( !obj.isEmpty() )
            tmp = "self." + obj;
	else
	    tmp = "self";
	v = "QSizePolicy(%1,%2," + tmp + ".sizePolicy().hasHeightForWidth())";
	v = v.arg( (int)sp.horData() ).arg( (int)sp.verData() );
    } else if ( e.tagName() == "palette" ) {
	QPalette pal;
	bool no_pixmaps = e.elementsByTagName( "pixmap" ).count() == 0;
	QDomElement n;
	if ( no_pixmaps ) {
	    n = e.firstChild().toElement();
	    while ( !n.isNull() ) {
		QColorGroup cg;
		if ( n.tagName() == "active" ) {
		    cg = loadColorGroup( n );
		    pal.setActive( cg );
		} else if ( n.tagName() == "inactive" ) {
		    cg = loadColorGroup( n );
		    pal.setInactive( cg );
		} else if ( n.tagName() == "disabled" ) {
		    cg = loadColorGroup( n );
		    pal.setDisabled( cg );
		}
		n = n.nextSibling().toElement();
	    }
	}
	if ( no_pixmaps && pal == QPalette( pal.active().button(), pal.active().background() ) ) {
	    v = "QPalette(QColor(%1,%2,%3),QColor(%1,%2,%3))";
	    v = v.arg( pal.active().button().red() ).arg( pal.active().button().green() ).arg( pal.active().button().blue() );
	    v = v.arg( pal.active().background().red() ).arg( pal.active().background().green() ).arg( pal.active().background().blue() );
	} else {
	    QString palette = "pal";
	    if ( !pal_used ) {
		out << indent << palette << " = QPalette()" << endl;
		pal_used = TRUE;
	    }
	    QString cg = "cg";
	    if ( !cg_used ) {
		out << indent << cg << " = QColorGroup()" << endl;
		cg_used = TRUE;
	    }
	    n = e.firstChild().toElement();
	    while ( !n.isNull() && n.tagName() != "active")
		n = n.nextSibling().toElement();
	    createColorGroupImpl( cg, n );
	    out << indent << palette << ".setActive(" << cg << ")" << endl;

	    n = e.firstChild().toElement();
	    while ( !n.isNull() && n.tagName() != "inactive")
		n = n.nextSibling().toElement();
	    createColorGroupImpl( cg, n );
	    out << indent << palette << ".setInactive(" << cg << ")" << endl;

	    n = e.firstChild().toElement();
	    while ( !n.isNull() && n.tagName() != "disabled")
		n = n.nextSibling().toElement();
	    createColorGroupImpl( cg, n );
	    out << indent << palette << ".setDisabled(" << cg << ")" << endl;
	    v = palette;
	}
    } else if ( e.tagName() == "cursor" ) {
	v = "QCursor(%1)";
	v = v.arg( e.firstChild().toText().data() );
    }
    return v;
}



/*!
  Creates a colorgroup with name \a name from the color group \a cg
 */
void Uic::createColorGroupImpl( const QString& name, const QDomElement& e )
{
    QColorGroup cg;
    int r = -1;
    QDomElement n = e.firstChild().toElement();
    QString color;
    while ( !n.isNull() ) {
	if ( n.tagName() == "color" ) {
	    r++;
	    QColor col = DomTool::readColor( n );
	    color = "QColor(%1,%2,%3)";
	    color = color.arg( col.red() ).arg( col.green() ).arg( col.blue() );
	    if ( col == white )
		color = "Qt.white";
	    else if ( col == black )
	    color = "Qt.black";
	    if ( n.nextSibling().toElement().tagName() != "pixmap" ) {
		out << indent << name << ".setColor(QColorGroup." << ColorRole[r] << "," << color << ")" << endl;
	    }
	} else if ( n.tagName() == "pixmap" ) {
	    QString pixmap = n.firstChild().toText().data();
	    if ( !pixmapLoaderFunction.isEmpty() ) {
		pixmap.prepend( pixmapLoaderFunction + "(" );
		pixmap.append( ")" );
	    }
	    out << indent << name << ".setBrush(QColorGroup."
		<< ColorRole[r] << ",QBrush(" << color << "," << pixmap << "))" << endl;
	}
	n = n.nextSibling().toElement();
    }
}

/*!
  Auxiliary function to load a color group. The colorgroup must not
  contain pixmaps.
 */
QColorGroup Uic::loadColorGroup( const QDomElement &e )
{
    QColorGroup cg;
    int r = -1;
    QDomElement n = e.firstChild().toElement();
    QColor col;
    while ( !n.isNull() ) {
	if ( n.tagName() == "color" ) {
	    r++;
	    cg.setColor( (QColorGroup::ColorRole)r, (col = DomTool::readColor( n ) ) );
	}
	n = n.nextSibling().toElement();
    }
    return cg;
}



/*!
  Registers an object with name \a name.

  The returned name is a valid variable identifier, as similar to \a
  name as possible and guaranteed to be unique within the form.

  \sa registeredName(), isObjectRegistered()
 */
QString Uic::registerObject( const QString& name )
{
    if ( objectNames.isEmpty() ) {
	// some temporary variables we need
	objectNames += "img";
	objectNames += "item";
	objectNames += "cg";
	objectNames += "pal";
    }

    QString result = name;
    int i;
    while ( ( i = result.find(' ' )) != -1  ) {
	result[i] = '_';
    }

    if ( objectNames.contains( result ) ) {
	int i = 2;
	while ( objectNames.contains( result + "_" + QString::number(i) ) )
		i++;
	result += "_";
	result += QString::number(i);
    }
    objectNames += result;
    objectMapper.insert( name, result );
    return result;
}

/*!
  Returns the registered name for the original name \a name
  or \a name if \a name  wasn't registered.

  \sa registerObject(), isObjectRegistered()
 */
QString Uic::registeredName( const QString& name )
{
    if ( !objectMapper.contains( name ) )
	return name;
    return objectMapper[name];
}

/*!
  Returns whether the object \a name was registered yet or not.
 */
bool Uic::isObjectRegistered( const QString& name )
{
    return objectMapper.contains( name );
}


/*!
  Unifies the entries in stringlist \a list. Should really be a QStringList feature.
 */
QStringList Uic::unique( const QStringList& list )
{
    QStringList result;
    if (list.isEmpty() )
	return result;
    QStringList l = list;
    l.sort();
    result += l.first();
    for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
	if ( *it != result.last() )
	    result += *it;
    }
    return result;
}



/*!
  Creates an instance of class \a objClass, with parent \a parent and name \a objName
 */
QString Uic::createObjectInstance( const QString& objClass, const QString& parent, const QString& objName )
{

    if ( objClass.mid(1) == "ComboBox" ) {
	return objClass + "(0," + parent + ",'" + objName + "')";
    }
    return objClass + "(" + parent + ",'" + objName + "')";
}

bool Uic::isLayout( const QString& name ) const
{
    return layoutObjects.contains( name );
}


#if defined(BLACKADDER)
#include "ba.h"

int BA::compilePython(const QString &fileName,const QString &outputFile,
		      const QString &trmacro,bool execCode)
{
#else
int main( int argc, char * argv[] )
{
    const char *error = 0;
    QString fileName;
    QString outputFile;
    QString trmacro;
    bool execCode = FALSE;

    for ( int n = 1; n < argc && error == 0; n++ ) {
	QCString arg = argv[n];
	if ( arg[0] == '-' ) {			// option
	    QCString opt = &arg[1];
	    if ( opt[0] == 'o' ) {		// output redirection
		if ( opt[1] == '\0' ) {
		    if ( !(n < argc-1) ) {
			error = "Missing output-file name";
			break;
		    }
		    outputFile = argv[++n];
		} else
		    outputFile = &opt[1];
	    } else if ( opt == "tr" ) {
		if ( opt == "tr" || opt[1] == '\0' ) {
		    if ( !(n < argc-1) ) {
			error = "Missing tr macro.";
			break;
		    }
		    trmacro = argv[++n];
		} else {
		    trmacro = &opt[1];
		}
	    } else if ( opt == "x" ) {
		execCode = TRUE;
	    }
	} else {
	    if (!fileName.isEmpty())		// can handle only one file
		error	 = "Too many input files specified";
	    else
		fileName = argv[n];
	}
    }

    if ( argc < 2 || error || fileName.isEmpty() ) {
	fprintf( stderr, "PyQt user interface compiler\n" );
	if ( error )
	    fprintf( stderr, "pyuic: %s\n", error );

	fprintf( stderr, "Usage: %s [options] <uifile>\n"
		 "\nGenerate PyQt implementation:\n"
		 "   %s [options] <uifile>\n"
		 "Options:\n"
		 "\t-o file 	Write output to file rather than stdout\n"
		 "\t-tr func	Use func(...) rather than tr(...) for i18n\n"
		 "\t-x		Generate extra code to test the class\n"
		 , argv[0], argv[0]);
	exit( 1 );
    }
#endif

    QFile file( fileName );
    if ( !file.open( IO_ReadOnly ) )
	qFatal( "pyuic: Could not open file '%s' ", (const char *)fileName );

    QFile fileOut;
    if (!outputFile.isEmpty()) {
	fileOut.setName( outputFile );
	if (!fileOut.open( IO_WriteOnly ) )
	    qFatal( "pyuic: Could not open output file '%s'", (const char *)outputFile );
    } else {
	fileOut.open( IO_WriteOnly, stdout );
    }
    QTextStream out( &fileOut );
    out.setEncoding( QTextStream::UnicodeUTF8 );

    QDomDocument doc;
    if ( !doc.setContent( &file ) )
	qFatal( "pyuic: Failed to parse %s\n", (const char *)fileName );

    out << "# Form implementation generated from reading ui file '" << fileName << "'" << endl;
    out << "#" << endl;
    out << "# Created: " << QDateTime::currentDateTime().toString() << endl;
    out << "#      by: The Python User Interface Compiler (pyuic) " << PYQT_VERSION << endl;
    out << "#" << endl;
    out << "# WARNING! All changes made in this file will be lost!" << endl;
    out << endl;
    out << endl;

    if (execCode)
	out << "import sys" << endl;

    out << "from qt import *" << endl;

    Uic( out, doc, trmacro.isEmpty() ? QString("self.tr") : trmacro );

    if (execCode) {
	out << endl;
	out << endl;
	out << "if __name__ == '__main__':" << endl;
	out << "    a = QApplication(sys.argv)" << endl;
	out << "    QObject.connect(a,SIGNAL('lastWindowClosed()'),a,SLOT('quit()'))" << endl;
	out << "    w = " << className << "()" << endl;
	out << "    a.setMainWidget(w)" << endl;
	out << "    w.show()" << endl;
	out << "    a.exec_loop()" << endl;
    }

    return 0;
}
