/**********************************************************************
** Copyright (C) 2000 Trolltech AS.  All rights reserved.
** Copyright (c) 2004 Phil Thompson <phil@riverbankcomputing.co.uk>
**
** This file is part of Qt Designer.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

#include "uic.h"
#include "parser.h"
#include "widgetdatabase.h"
#include "domtool.h"
#include <qfile.h>
#include <qstringlist.h>
#include <qdatetime.h>
#define NO_STATIC_COLORS
#include <globaldefs.h>
#include <qregexp.h>
#include <stdio.h>
#include <stdlib.h>

bool Uic::isMainWindow = FALSE;
PyIndent Uic::indent;
PyIndent Uic::trindent;


// Re-calculate the indent string based on the current number and nature of the
// indent.
void PyIndent::calc()
{
	indstr.truncate(0);

	for (uint i = current; i > 0; --i)
		if (tabStop == 0)
			indstr += '\t';
		else
			for (uint t = 0; t < tabStop; ++t)
				indstr += ' ';
}


QString Uic::getComment( const QDomNode& n )
{
    QDomNode child = n.firstChild();
    while ( !child.isNull() ) {
	if ( child.toElement().tagName() == "comment" )
	    return child.toElement().firstChild().toText().data();
	child = child.nextSibling();
    }
    return QString::null;
}

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

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

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

QString Uic::fixString( const QString &str, bool encode )
{
#if QT_VERSION >= 0x030100
    QString s;
    if ( !encode ) {
	s = str;
	s.replace( "\\", "\\\\" );
	s.replace( "\"", "\\\"" );
	s.replace( "\r", "" );
	s.replace( "\n", "\\n\"\n\"" );
    } else {
	QCString utf8 = str.utf8();
	const int l = utf8.length();
	for ( int i = 0; i < l; ++i ) {
	    uchar ch = utf8[i];

	    s += "\\x";

	    if (ch <= 0x0f)
		s += "0";

	    s += QString::number( ch, 16 );
	}
    }
#else
    QString s( str );
    s.replace( QRegExp( "\\\\" ), "\\\\" );
    s.replace( QRegExp( "\"" ), "\\\"" );
    s.replace( QRegExp( "\r?\n" ), "\\n\"\n\"" );
#endif
    return "\"" + s + "\"";
}

QString Uic::trcall( const QString& sourceText, const QString& comment )
{
    if ( sourceText.isEmpty() && comment.isEmpty() )
        return "QString.null";

    QString t = trmacro;
    bool encode = FALSE;
    if ( t.isNull() ) {
        t = "self.__tr";
        for ( int i = 0; i < (int) sourceText.length(); i++ ) {
            if ( sourceText[i].unicode() >= 0x80 ) {
                t = "self.__trUtf8";
		encode = TRUE;
                break;
            }
        }

	if (encode)
	    pyNeedTrUtf8 = TRUE;
	else
	    pyNeedTr = TRUE;
    }

    if ( comment.isEmpty() ) {
        return t + "(" + fixString( sourceText, encode ) + ")";
    } else {
        return t + "(" + fixString( sourceText, encode ) + "," +
               fixString( comment, encode ) + ")";
    }
}

QString Uic::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).
 */
Uic::Uic( const QString &fn, const char *outputFn, QTextStream &outStream,
	  QDomDocument doc, bool subcl, const QString &trm,
	  const QString& subClass, QString &uicClass )
    : out( outStream ), trout (&languageChangeBody ),
      outputFileName( outputFn ), trmacro( trm ),
      pyNeedTr(FALSE), pyNeedTrUtf8(FALSE)
{
    fileName = fn;
    writeFunctImpl = TRUE;
    defMargin = BOXLAYOUT_DEFAULT_MARGIN;
    defSpacing = BOXLAYOUT_DEFAULT_SPACING;
    externPixmaps = FALSE;

    trindent.setIndent(2);

    item_used = cg_used = pal_used = 0;

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

    pixmapLoaderFunction = getPixmapLoaderFunction( doc.firstChild().toElement() );
    nameOfClass = getFormClassName( doc.firstChild().toElement() );

#if QT_VERSION >= 0x030300
    uiFileVersion = doc.firstChild().toElement().attribute("version");
#endif
    stdsetdef = toBool( doc.firstChild().toElement().attribute("stdsetdef") );

    if ( doc.firstChild().isNull() || doc.firstChild().firstChild().isNull() )
        return;

    // Extract any Python code in the comments.
    QStringList comm = QStringList::split('\n',getComment(doc.firstChild()));

    for (QStringList::Iterator it = comm.begin(); it != comm.end(); ++it)
	if ((*it).startsWith("Python:"))
	    pyCode += (*it).mid(7) + "\n";

    QDomElement e = doc.firstChild().firstChild().toElement();
    QDomElement widget;
    while ( !e.isNull() ) {
	if ( e.tagName() == "widget" ) {
	    widget = e;
	} else if ( e.tagName() == "pixmapinproject" ) {
	    externPixmaps = TRUE;
	} else if ( e.tagName() == "layoutdefaults" ) {
            defSpacing = e.attribute( "spacing", defSpacing.toString() );
            defMargin = e.attribute( "margin", defMargin.toString() );
        } else if ( e.tagName() == "layoutfunctions" ) {
	    defSpacing = e.attribute( "spacing", defSpacing.toString() );

            bool ok;
            defSpacing.toInt( &ok );
            if ( !ok ) {
                QString buf = defSpacing.toString();
                defSpacing = buf.append( "()" );
            }
            defMargin = e.attribute( "margin", defMargin.toString() );
            defMargin.toInt( &ok );
            if ( !ok ) {
                QString buf = defMargin.toString();
                defMargin = buf.append( "()" );
            }
	}
	e = e.nextSibling().toElement();
    }
    e = widget;

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

    uicClass = nameOfClass;

    namespaces = QStringList::split( "::", nameOfClass );
    bareNameOfClass = namespaces.last();
    namespaces.remove( namespaces.fromLast() );

    if ( subcl ) {
	createSubImpl( e, subClass );
    } else {
	createFormImpl( e );
    }

    ++indent;

    if (pyNeedTr)
    {
	out << "\n";
	out << indent << "def __tr(self,s,c = None):\n";
	++indent;
	out << indent << "return qApp.translate(\"" << nameOfClass << "\",s,c)\n";
	--indent;
    }

    if (pyNeedTrUtf8)
    {
	out << "\n";
	out << indent << "def __trUtf8(self,s,c = None):\n";
	++indent;
	out << indent << "return qApp.translate(\"" << nameOfClass << "\",s,c,QApplication.UnicodeUTF8)\n";
	--indent;
    }

    --indent;
}

/*! Extracts a pixmap loader function from \a e
 */
QString Uic::getPixmapLoaderFunction( const QDomElement& e )
{
    QDomElement n;
    for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName() == "pixmapfunction" )
	    return n.firstChild().toText().data();
    }
    return QString::null;
}


/*! Extracts the forms class name from \a e
 */
QString Uic::getFormClassName( 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;
	}
    }
    return cn;
}

/*! Extracts a class name from \a e.
 */
QString Uic::getClassName( const QDomElement& e )
{
    QString s = e.attribute( "class" );
    if ( s.isEmpty() && e.tagName() == "toolbar" )
	s = "QToolBar";
    else if ( s.isEmpty() && e.tagName() == "menubar" )
	s = "QMenuBar";
    return s;
}

/*! Returns TRUE if database framework code is generated, else FALSE.
*/

bool Uic::isFrameworkCodeGenerated( const QDomElement& e )
{
    QDomElement n = getObjectProperty( e, "frameworkCode" );
    if ( n.attribute("name") == "frameworkCode" &&
	 !DomTool::elementToVariant( n.firstChild().toElement(), QVariant( TRUE, 0 ) ).toBool() )
	return FALSE;
    return TRUE;
}

/*! Extracts an object name from \a e. It's stored in the 'name'
 property.
 */
QString Uic::getObjectName( const QDomElement& e )
{
    QDomElement n = getObjectProperty( e, "name" );
    if ( n.firstChild().toElement().tagName() == "cstring" )
	return n.firstChild().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 name;

    if ( getClassName(p) != "QLayoutWidget" )
	name = "Layout";

    QDomElement n = getObjectProperty( p, "name" );
    if ( n.firstChild().toElement().tagName() == "cstring" ) {
        name.prepend( n.firstChild().toElement().firstChild().toText().data() );
        return QStringList::split( "::", name ).last();
    }
    return e.tagName();
}


QString Uic::getDatabaseInfo( const QDomElement& e, const QString& tag )
{
    QDomElement n;
    QDomElement n1;
    int child = 0;
    // database info is a stringlist stored in this order
    if ( tag == "connection" )
	child = 0;
    else if ( tag == "table" )
	child = 1;
    else if ( tag == "field" )
	child = 2;
    else
	return QString::null;
    n = getObjectProperty( e, "database" );
    if ( n.firstChild().toElement().tagName() == "stringlist" ) {
	    // find correct stringlist entry
	    QDomElement n1 = n.firstChild().firstChild().toElement();
	    for ( int i = 0; i < child && !n1.isNull(); ++i )
		n1 = n1.nextSibling().toElement();
	    if ( n1.isNull() )
		return QString::null;
	    return n1.firstChild().toText().data();
    }
    return QString::null;
}


/*!
  Returns include file for class \a className or a null string.
 */
QString Uic::getInclude( const QString& className )
{
    int wid = WidgetDatabase::idFromClassName( className );
    if ( wid != -1 )
	return WidgetDatabase::includeFile( wid );
    return QString::null;
}


void Uic::createActionImpl( const QDomElement &n, const QString &parent )
{
    for ( QDomElement ae = n; !ae.isNull(); ae = ae.nextSibling().toElement() ) {
	QString objName = registerObject( getObjectName( ae ) );
	if ( ae.tagName() == "action" )
	    out << indent << "self." << objName << " = QAction(" << parent << ",\"" << objName << "\")" << endl;
	else if ( ae.tagName() == "actiongroup" )
	    out << indent << "self." << objName << " = QActionGroup(" << parent << ",\"" << objName << "\")" << endl;
	else
	    continue;
	bool subActionsDone = FALSE;
#if QT_VERSION >= 0x030300
	bool hasMenuText = FALSE;
	QString actionText;
#endif
	for ( QDomElement n2 = ae.firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) {
	    if ( n2.tagName() == "property" ) {
		bool stdset = stdsetdef;
		if ( n2.hasAttribute( "stdset" ) )
		    stdset = toBool( n2.attribute( "stdset" ) );
		QString prop = n2.attribute("name");
		if ( prop == "name" )
		    continue;
		QString value = setObjectProperty( "QAction", objName, prop, n2.firstChild().toElement(), stdset );
		if ( value.isEmpty() )
		    continue;

		QString call = "self." + objName + ".";
		if ( stdset )
		    call += mkStdSet( prop ) + "(" + value + ")";
		else
		    call += "setProperty(\"" + prop + "\",QVariant(" + value + "))";

#if QT_VERSION >= 0x030300
		if (prop == "menuText")
		    hasMenuText = TRUE;
		else if (prop == "text")
		    actionText = value;
#endif

                if ( n2.firstChild().toElement().tagName() == "string" ) {
                    trout << trindent << call << endl;
                } else {
                    out << indent << call << endl;
                }
	    } else if ( !subActionsDone && ( n2.tagName() == "actiongroup" || n2.tagName() == "action" ) ) {
		createActionImpl( n2, "self." + objName );
		subActionsDone = TRUE;
	    }
	}
#if QT_VERSION >= 0x030300
	// workaround for loading pre-3.3 files expecting bogus QAction behavior
	if (!hasMenuText && !actionText.isEmpty() && uiFileVersion < "3.3")
	    trout << indent << "self." << objName << ".setMenuText(" << actionText << ")" << endl;
#endif
    }
}

QString get_dock( const QString &d )
{
    if ( d == "0" )
	return "Qt.DockUnmanaged";
    if ( d == "1" )
	return "Qt.DockTornOff";
    if ( d == "2" )
	return "Qt.DockTop";
    if ( d == "3" )
	return "Qt.DockBottom";
    if ( d == "4" )
	return "Qt.DockRight";
    if ( d == "5" )
	return "Qt.DockLeft";
    if ( d == "6" )
	return "Qt.DockMinimized";
    return "";
}

void Uic::createToolbarImpl( const QDomElement &n, const QString &parentClass, const QString &parent )
{
    QDomNodeList nl = n.elementsByTagName( "toolbar" );
    for ( int i = 0; i < (int) nl.length(); i++ ) {
	QDomElement ae = nl.item( i ).toElement();
	QString dock = get_dock( ae.attribute( "dock" ) );
	QString objName = getObjectName( ae );
 	out << indent << "self." << objName << " = QToolBar(QString(\"\"),self," << dock << ")" << endl;
	createObjectImpl( ae, parentClass, parent );
	for ( QDomElement n2 = ae.firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) {
	    if ( n2.tagName() == "action" ) {
		out << indent << "self." << n2.attribute( "name" ) << ".addTo(self." << objName << ")" << endl;
	    } else if ( n2.tagName() == "separator" ) {
		out << indent << "self." << objName << ".addSeparator()" << endl;
	    } else if ( n2.tagName() == "widget" ) {
		if ( n2.attribute( "class" ) != "Spacer" ) {
			createObjectImpl( n2, "QToolBar", "self." + objName );
		} else {
		    QString child = createSpacerImpl( n2, parentClass, parent, objName );
		    out << indent << "QApplication.sendPostedEvents(self." << objName
			<< ",QEvent.ChildInserted)" << endl;
		    out << indent << "self." << objName << ".boxLayout().addItem(" << child << ")" << endl;
		}
	    }
	}
    }
}

void Uic::createMenuBarImpl( const QDomElement &n, const QString &parentClass, const QString &parent )
{
    QString objName = getObjectName( n );
    out << indent << "self." << objName << " = QMenuBar(self,\"" << objName << "\")" << endl;
    createObjectImpl( n, parentClass, parent );

    int i = 0;
    QDomElement c = n.firstChild().toElement();
    while ( !c.isNull() ) {
	if ( c.tagName() == "item" ) {
	    QString itemName = "self." + c.attribute( "name" );
	    out << endl;
	    out << indent << itemName << " = QPopupMenu(self)" << endl;
	    createPopupMenuImpl( c, parentClass, itemName );
	    out << indent << "self." << objName << ".insertItem(QString(\"\")," << itemName << "," << i << ")" << endl;
	    QString findItem("self." + objName + ".findItem(%1)");
	    findItem = findItem.arg(i);
	    trout << indent << "if " << findItem << ":" << endl;
	    ++indent;
	    trout << indent << findItem << ".setText(" << trcall( c.attribute( "text" ) ) << ")" << endl;
	    --indent;
	} else if ( c.tagName() == "separator" ) {
	    out << endl;
	    out << indent << "self." << objName << ".insertSeparator(" << i << ")" << endl;
	}
	c = c.nextSibling().toElement();
	i++;
    }
}

void Uic::createPopupMenuImpl( const QDomElement &e, const QString &parentClass, const QString &parent )
{
    int i = 0;
    for ( QDomElement n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) {
	if ( n.tagName() == "action" || n.tagName() == "actiongroup" ) {
	    QDomElement n2 = n.nextSibling().toElement();
	    if ( n2.tagName() == "item" ) { // the action has a sub menu
		QString itemName = "self." + n2.attribute( "name" );
		QString itemText = n2.attribute( "text" );
		out << indent << itemName << " = QPopupMenu(self)" << endl;
		out << indent << parent << ".setAccel(" << trcall( n2.attribute( "accel" ) ) << ",";
		out << parent << ".insertItem(" << "self." << n.attribute( "name" ) << ".iconSet(),";
		out << trcall( itemText ) << "," << itemName << "))" << endl;
		trout << indent << parent << ".changeItem(" << parent << ".idAt(" << i << "),";
		trout << trcall( itemText ) << ")" << endl;
		createPopupMenuImpl( n2, parentClass, itemName );
		n = n2;
	    } else {
		out << indent << "self." << n.attribute( "name" ) << ".addTo(" << parent << ")" << endl;
	    }
	} else if ( n.tagName() == "separator" ) {
	    out << indent << parent << ".insertSeparator()" << endl;
	}
	++i;
    }
}

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

QString Uic::createListBoxItemImpl( const QDomElement &e, const QString &parent ,
				    QString *value )
{
    QDomElement n = e.firstChild().toElement();
    QString txt;
    QString com;
    QString pix;
    while ( !n.isNull() ) {
	if ( n.tagName() == "property" ) {
	    QString attrib = n.attribute( "name" );
	    QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() );
	    if ( attrib == "text" ) {
		txt = v.toString();
		com = getComment( n );
	    } else if ( attrib == "pixmap" ) {
		pix = v.toString();
		if ( !pix.isEmpty() ) {
		    if ( pixmapLoaderFunction.isEmpty() ) {
		        pix.prepend( "self." );
		    } else {
		        pix.prepend( pixmapLoaderFunction + "( " + QString( externPixmaps ? "\"" : "self." ) );
		        pix.append(  QString( externPixmaps ? "\"" : "" ) + ")" );
		    }
		}
	    }
	}
	n = n.nextSibling().toElement();
    }

    if ( value )
        *value = trcall( txt, com );

    if ( pix.isEmpty() )
	return parent + ".insertItem(" + trcall( txt, com ) + ")";

    return parent + ".insertItem(" + pix + "," + trcall( txt, com ) + ")";
}

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

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

    if ( pix.isEmpty() )
	return "QIconViewItem(" + parent + "," + trcall( txt, com ) + ")";
    else
	return "QIconViewItem(" + parent + "," + trcall( txt, com ) + "," + 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" );
    } else {
	item = "item";
	item_used = TRUE;
    }
    s = trindent + item + " = ";

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

    QStringList texts;
    QStringList pixmaps;
    while ( !n.isNull() ) {
	if ( n.tagName() == "property" ) {
	    QString attrib = n.attribute("name");
	    QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() );
	    if ( attrib == "text" )
		texts << v.toString();
	    else if ( attrib == "pixmap" ) {
		QString pix = v.toString();
		if ( !pix.isEmpty() ) {
		    if ( pixmapLoaderFunction.isEmpty() ) {
		        pix.prepend( "self." );
		    } else {
		        pix.prepend( pixmapLoaderFunction + "( " + QString( externPixmaps ? "\"" : "self." ) );
		        pix.append(  QString( externPixmaps ? "\"" : "" ) + ")" );
		    }
		}
		pixmaps << pix;
	    }
	} else if ( n.tagName() == "item" ) {
	    s += trindent + item + ".setOpen(1)\n";
	    s += createListViewItemImpl( n, parent, item );
	}
	n = n.nextSibling().toElement();
    }

    for ( int i = 0; i < (int)texts.count(); ++i ) {
	if ( !texts[ i ].isEmpty() )
	    s += trindent + item + ".setText(" + QString::number( i ) + "," + trcall( texts[ i ] ) + ")\n";
	if ( !pixmaps[ i ].isEmpty() )
	    s += trindent + 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,
				       QString *value )
{
    QDomElement n = e.firstChild().toElement();
    QString txt;
    QString com;
    QString pix;
    bool clickable = FALSE, resizable = FALSE;
    while ( !n.isNull() ) {
	if ( n.tagName() == "property" ) {
	    QString attrib = n.attribute("name");
	    QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() );
	    if ( attrib == "text" ) {
		txt = v.toString();
		com = getComment( n );
	    } else if ( attrib == "pixmap" ) {
		pix = v.toString();
		if ( !pix.isEmpty() ) {
		    if ( pixmapLoaderFunction.isEmpty() ) {
		        pix.prepend( "self." );
		    } else {
		        pix.prepend( pixmapLoaderFunction + "( " + QString( externPixmaps ? "\"" : "self." ) );
		        pix.append(  QString( externPixmaps ? "\"" : "" ) + ")" );
		    }
		}
	    } else if ( attrib == "clickable" )
		clickable = v.toBool();
            else if ( attrib == "resizable" || attrib == "resizeable" )
		resizable = v.toBool();
	}
	n = n.nextSibling().toElement();
    }

    if ( value )
        *value = trcall( txt, com );

    QString s;
    s = indent + parent + ".addColumn(" + trcall( txt, com ) + ")\n";
    if ( !pix.isEmpty() )
	s += indent + parent + ".header().setLabel(" + parent + ".header().count() - 1," + pix + "," + trcall( txt, com ) + ")\n";
    if ( !clickable )
	s += indent + parent + ".header().setClickEnabled(0," + parent + ".header().count() - 1)\n";
    if ( !resizable )
	s += indent + parent + ".header().setResizeEnabled(0," + parent + ".header().count() - 1)\n";
    return s;
}

QString Uic::createTableRowColumnImpl( const QDomElement &e, const QString &parent,
				       QString *value )
{
    QString objClass = getClassName( e.parentNode().toElement() );
    QDomElement n = e.firstChild().toElement();
    QString txt;
    QString com;
    QString pix;
    QString field;
    bool isRow = e.tagName() == "row";
    while ( !n.isNull() ) {
	if ( n.tagName() == "property" ) {
	    QString attrib = n.attribute("name");
	    QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() );
	    if ( attrib == "text" ) {
		txt = v.toString();
		com = getComment( n );
	    } else if ( attrib == "pixmap" ) {
		pix = v.toString();
		if ( !pix.isEmpty() ) {
		    if ( pixmapLoaderFunction.isEmpty() ) {
		        pix.prepend( "self." );
		    } else {
		        pix.prepend( pixmapLoaderFunction + "( " + QString( externPixmaps ? "\"" : "self." ) );
		        pix.append(  QString( externPixmaps ? "\"" : "" ) + ")" );
		    }
		}
	    } else if ( attrib == "field" )
		field = v.toString();
	}
	n = n.nextSibling().toElement();
    }

    if ( value )
        *value = trcall( txt, com );

    // ### This generated code sucks! We have to set the number of
    // rows/cols before and then only do setLabel/()
    // ### careful, though, since QDataTable has an API which makes this code pretty good

    QString s;
    if ( isRow ) {
	s = indent + parent + ".setNumRows(" + parent + ".numRows() + 1)\n";
	if ( pix.isEmpty() )
	    s += indent + parent + ".verticalHeader().setLabel(" + parent + ".numRows() - 1,"
		 + trcall( txt, com ) + ")\n";
	else
	    s += indent + parent + ".verticalHeader().setLabel(" + parent + ".numRows() - 1,QIconSet("
		 + pix + ")," + trcall( txt, com ) + ")\n";
    } else {
	if ( objClass == "QTable" ) {
	    s = indent + parent + ".setNumCols(" + parent + ".numCols() + 1)\n";
	    if ( pix.isEmpty() )
		s += indent + parent + ".horizontalHeader().setLabel(" + parent + ".numCols() - 1,"
		     + trcall( txt, com ) + ")\n";
	    else
		s += indent + parent + ".horizontalHeader().setLabel(" + parent + ".numCols() - 1,QIconSet("
		     + pix + ")," + trcall( txt, com ) + ")\n";
	} else if ( objClass == "QDataTable" ) {
	    if ( !txt.isEmpty() && !field.isEmpty() ) {
		if ( pix.isEmpty() )
		    out << indent << parent << ".addColumn(" << fixString( field ) << "," << trcall( txt, com ) << ")" << endl;
		else
		    out << indent << parent << ".addColumn(" << fixString( field ) << "," << trcall( txt, com ) << ",QIconSet(" << pix << "))" << endl;
	    }
	}
    }
    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;

    QString margin = DomTool::readProperty( e, "margin", defMargin ).toString();
    QString spacing = DomTool::readProperty( e, "spacing", defSpacing ).toString();
    QString resizeMode = DomTool::readProperty( e, "resizeMode", QString::null ).toString();

    QString optcells;
    if ( isGrid )
	optcells = "1,1,";
    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(" << spacing << ")" << endl;
	out << indent << parent << ".layout().setMargin(" << margin << ")" << endl;
	out << indent << objName << " = " << qlayout << "(" << parent << ".layout())" << endl;
	out << indent << objName << ".setAlignment(Qt.AlignTop)" << endl;
    } else {
	out << indent << objName << " = " << qlayout << "(";
	if ( layout.isEmpty() )
	    out << parent;
	else {
	    out << "None";
            if ( !DomTool::hasProperty( e, "margin" ) )
                margin = "0";
	}
	out << "," << optcells << margin << "," << spacing << ",\"" << objName << "\")" << endl;
    }
    if ( !resizeMode.isEmpty() )
	out << indent << objName << ".setResizeMode(QLayout." << resizeMode << ")" << 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( getObjectName( e ) );

    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;
}

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


/*!
  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( "self." );
	    } else {
		pixmap.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "self." ) );
		pixmap.append( QString( externPixmaps ? "\"" : "" ) + ")" );
	    }
	    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;
}

/*!  Returns TRUE if the widget properties specify that it belongs to
  the database \a connection and \a table.
*/

bool Uic::isWidgetInTable( const QDomElement& e, const QString& connection, const QString& table )
{
    QString conn = getDatabaseInfo( e, "connection" );
    QString tab = getDatabaseInfo( e, "table" );
    if ( conn == connection && tab == table )
	return TRUE;
    return FALSE;
}

/*!
  Registers all database connections, cursors and forms.
*/

void Uic::registerDatabases( const QDomElement& e )
{
    QDomElement n;
    QDomNodeList nl;
    int i;
    nl = e.parentNode().toElement().elementsByTagName( "widget" );
    for ( i = 0; i < (int) nl.length(); ++i ) {
	n = nl.item(i).toElement();
	QString conn = getDatabaseInfo( n, "connection"  );
	QString tab = getDatabaseInfo( n, "table"  );
	QString fld = getDatabaseInfo( n, "field"  );
	if ( !conn.isNull() ) {
	    dbConnections += conn;
	    if ( !tab.isNull() ) {
		dbCursors[conn] += tab;
		if ( !fld.isNull() )
		    dbForms[conn] += tab;
	    }
	}
    }
}

/*!
  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 )
{
    if ( list.isEmpty() )
	return list;

    QStringList result;
    for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) {
	if ( !result.contains(*it) )
	    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 );
}
