//=============================================================================
//
//   File : ClassEditorWindow.cpp
//   Creation date : Mon Feb 15 2010 14:35:55 CEST by Carbone Alessandro
//
//   This file is part of the KVIrc irc client distribution
//   Copyright (C) 2010 Alessandro Carbone (elfonol at gmail dot com)
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the linkss of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your opinion) any later version.
//
//   This program is distributed in the HOPE that it will be USEFUL,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//   See the GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program. If not, write to the Free Software Foundation,
//   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================


#include "ClassEditorWindow.h"

#include "KviIconManager.h"
#include "KviOptions.h"
#include "KviLocale.h"
#include "KviImageDialog.h"
#include "KviConfigurationFile.h"
#include "KviFileDialog.h"
#include "KviFileUtils.h"
#include "KviScriptEditor.h"
#include "kvi_debug.h"
#include "KviApplication.h"
#include "KviMainWindow.h"
#include "KviQString.h"
#include "KviKvsKernel.h"
#include "KviKvsObjectClass.h"
#include "KviKvsObjectController.h"
#include "KviFileDialog.h"
#include "KviCommandFormatter.h"
#include "KviModule.h"
#include "KviTalVBox.h"
#include "kvi_fileextensions.h"
#include "KviModuleManager.h"

#include <QList>
#include <QSplitter>
#include <QLayout>
#include <QToolTip>
#include <QPushButton>
#include <QDir>
#include <QMessageBox>
#include <QInputDialog>
#include <QRegExp>
#include <QMouseEvent>
#include <QPainter>
#include <QBrush>
#include <QStringList>
#include <QLabel>

extern ClassEditorWindow * g_pClassEditorWindow;
extern KviModule * g_pClassEditorModule;

ClassEditorTreeWidget::ClassEditorTreeWidget(QWidget * pParent)
: QTreeWidget(pParent)
{
	setColumnCount (1);
	setHeaderLabel(__tr2qs_ctx("Class","editor"));
	setSelectionMode(QAbstractItemView::ExtendedSelection);
	setSortingEnabled(true);
	setRootIsDecorated(true);
	setAnimated(true);
}

ClassEditorTreeWidget::~ClassEditorTreeWidget()
{
	//remove and delete all items
	clear();
}

void ClassEditorTreeWidget::mousePressEvent(QMouseEvent * e)
{
	if(e->button() == Qt::RightButton)
	{
		QTreeWidgetItem * pItem = itemAt(e->pos());
		if(pItem)
			emit rightButtonPressed(pItem,QCursor::pos());
	}
	QTreeWidget::mousePressEvent(e);
}

ClassEditorTreeWidgetItem::ClassEditorTreeWidgetItem(QTreeWidget * pTreeWidget, Type eType, const QString & szName)
: QTreeWidgetItem(pTreeWidget), KviHeapObject(), m_eType(eType)
{
	setName(szName);
	m_szInheritsClassName = "";
	m_iPos                = 0;
	m_bInternal           = false;
	m_bClassModified      = false;
	QPixmap * pIcon       = 0;
	
	if(eType == ClassEditorTreeWidgetItem::Namespace)
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::NameSpace);
	else
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::Class);

	setIcon(0,QIcon(*pIcon));
}

ClassEditorTreeWidgetItem::ClassEditorTreeWidgetItem(ClassEditorTreeWidgetItem * pParentItem, Type eType, const QString & szName)
: QTreeWidgetItem(pParentItem), m_eType(eType)
{
	setFlags(Qt::ItemIsEditable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
	setName(szName);
	m_szInheritsClassName = "";
	m_bInternal           = false;
	m_iPos                = 0;
	m_bClassModified      = false;
	QPixmap * pIcon       = 0;
	
	if(eType == ClassEditorTreeWidgetItem::Namespace)
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::NameSpace);
	else if(eType == ClassEditorTreeWidgetItem::Class)
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::Class);
	else
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::Function);
	
	setIcon(0,QIcon(*pIcon));
}

void ClassEditorTreeWidgetItem::setClassNotBuilt(bool bModified)
{
	m_bClassModified = bModified;
	QPixmap * pIcon = 0;
	
	if(bModified)
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::ClassNotBuilt);
	else
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::Class);
	
	setIcon(0,QIcon(*pIcon));
}

void ClassEditorTreeWidgetItem::setName(const QString & szName)
{
	m_szName = szName;
	setText(0,m_szName);
}

void ClassEditorTreeWidgetItem::setType(Type eType)
{
	m_eType = eType;
	QPixmap * pIcon = 0;
	
	if(eType == ClassEditorTreeWidgetItem::Namespace)
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::NameSpace);
	else if(eType == ClassEditorTreeWidgetItem::Class)
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::Class);
	else
		pIcon = g_pIconManager->getSmallIcon(KviIconManager::Function);
	
	setIcon(0,QIcon(*pIcon));
}

ClassEditorWidget::ClassEditorWidget(QWidget * pParent)
: QWidget(pParent)
{
	m_pClasses = new KviPointerHashTable<QString,ClassEditorTreeWidgetItem>(100,false);
	m_pClasses->setAutoDelete(false);
	m_pLastEditedItem = 0;
	m_pLastClickedItem = 0;
	m_szDir = QDir::homePath();

	QGridLayout * pLayout = new QGridLayout(this);

	m_pSplitter = new QSplitter(Qt::Horizontal,this);
	m_pSplitter->setChildrenCollapsible(false);
	pLayout->addWidget(m_pSplitter,0,0);

	KviTalVBox * pVBox = new KviTalVBox(m_pSplitter);
	pVBox->setSpacing(0);
	pVBox->setMargin(0);
	m_pTreeWidget = new ClassEditorTreeWidget(pVBox);

	pVBox = new KviTalVBox(m_pSplitter);

	KviTalHBox * pHBox = new KviTalHBox(pVBox);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	m_pClassNameLabel = new QLabel(__tr2qs_ctx("No item selected","editor"),pHBox);
	pHBox->setStretchFactor(m_pClassNameLabel,2);
	m_pClassNameLabel->setWordWrap(true);
	m_pClassNameRenameButton = new QPushButton(__tr2qs_ctx("Rename","editor"),pHBox);
	m_pClassNameRenameButton->setToolTip(__tr2qs_ctx("Edit the class or namespace name","editor"));
	m_pClassNameRenameButton->setEnabled(false);
	connect(m_pClassNameRenameButton,SIGNAL(clicked()),this,SLOT(renameItem()));

	pHBox = new KviTalHBox(pVBox);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);

	pHBox = new KviTalHBox(pVBox);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	m_pFunctionNameLabel = new QLabel(__tr2qs_ctx("No item selected","editor"),pHBox);
	pHBox->setStretchFactor(m_pFunctionNameLabel,2);
	m_pFunctionNameRenameButton = new QPushButton(__tr2qs_ctx("Rename","editor"),pHBox);
	m_pFunctionNameRenameButton->setToolTip(__tr2qs_ctx("Edit the function member name","editor"));
	m_pFunctionNameRenameButton->setEnabled(false);
	connect(m_pFunctionNameRenameButton,SIGNAL(clicked()),this,SLOT(renameFunction()));

	m_pReminderLabel = new QLabel(__tr2qs_ctx("No item selected","editor"),pVBox);
	m_pReminderLabel->hide();
	m_pReminderLabel->setWordWrap(true);

	m_pEditor = KviScriptEditor::createInstance(pVBox);
	m_pEditor->setFocus();
	connect(m_pEditor,SIGNAL(find(const QString &)),this,SLOT(slotFindWord(const QString &)));
	connect(m_pEditor,SIGNAL(replaceAll(const QString &,const QString &)),this,SLOT(slotReplaceAll(const QString &,const QString &)));
	m_pContextPopup = new KviTalPopupMenu(this);

	oneTimeSetup();

	currentItemChanged(0,0);
}

ClassEditorWidget::~ClassEditorWidget()
{
	m_pClasses->clear();
	delete m_pClasses;
}

void ClassEditorWidget::buildFullItemPath(ClassEditorTreeWidgetItem * pItem, QString & szBuffer)
{
	if(!pItem)
		return;
	szBuffer.prepend(pItem->name() + "::");
	pItem = (ClassEditorTreeWidgetItem *)pItem->parent();
	while(pItem)
	{
		QString szTmp = pItem->name();
		if(!szTmp.isEmpty())
		{
			szBuffer.prepend("::");
			szBuffer.prepend(szTmp);
		}
		pItem = (ClassEditorTreeWidgetItem *)pItem->parent();
	}
}

QString ClassEditorWidget::buildFullClassName(ClassEditorTreeWidgetItem * pItem)
{
	if(!pItem)
		return QString();
	QString szName = pItem->name();
	ClassEditorTreeWidgetItem * pNItem = (ClassEditorTreeWidgetItem *)pItem->parent();
	while(pNItem)
	{
		QString szTmp = pNItem->name();
		if(!szTmp.isEmpty())
		{
			szName.prepend("::");
			szName.prepend(szTmp);
		}
		pNItem = (ClassEditorTreeWidgetItem *)pNItem->parent();
	}
	return szName;
}

ClassEditorTreeWidgetItem * ClassEditorWidget::findTopLevelItem(const QString & szName)
{
	for(int i=0; i < m_pTreeWidget->topLevelItemCount(); i++)
	{
		if(KviQString::equalCI(m_pTreeWidget->topLevelItem(i)->text(0),szName))
			return (ClassEditorTreeWidgetItem *)m_pTreeWidget->topLevelItem(i);
	}
	return 0;
}

ClassEditorTreeWidgetItem * ClassEditorWidget::findItem(const QString & szFullName)
{
	bool bFound;
	QStringList lNamespaces = szFullName.split("::");
	if(lNamespaces.count() == 0)
		return 0;
	ClassEditorTreeWidgetItem * pItem = findTopLevelItem(lNamespaces.at(0));
	if(!pItem)
		return 0;
	
	for(int i=1; i < lNamespaces.count(); i++)
	{
		bFound = false;
		for(int j=0; j < pItem->childCount(); j++)
		{
			if (KviQString::equalCI(pItem->child(j)->text(0),lNamespaces.at(i)))
			{
				pItem=( ClassEditorTreeWidgetItem *)pItem->child(j);
				bFound = true;
				break;
			}
		}
		if(!bFound)
			return 0;
	}
	return (ClassEditorTreeWidgetItem *)pItem;
}

ClassEditorTreeWidgetItem * ClassEditorWidget::createFullItem(const QString & szFullName)
{
	bool bFound;
	int i;
	
	QStringList lNamespaces = szFullName.split("::");
	if(lNamespaces.count() == 0)
		return 0;
	if(lNamespaces.count() == 1)
	{
		ClassEditorTreeWidgetItem * pItem = findTopLevelItem(lNamespaces.at(0));
		if(pItem)
			return pItem;
		return new ClassEditorTreeWidgetItem(m_pTreeWidget,ClassEditorTreeWidgetItem::Class,lNamespaces.at(0));
	}
	ClassEditorTreeWidgetItem * pItem = findTopLevelItem(lNamespaces.at(0));
	if(!pItem)
		pItem = new ClassEditorTreeWidgetItem(m_pTreeWidget,ClassEditorTreeWidgetItem::Namespace,lNamespaces.at(0));

	for(i=1; i < lNamespaces.count()-1; i++)
	{
		bFound = false;
		for(int j=0; j < pItem->childCount();j++)
		{
			if(KviQString::equalCI(pItem->child(j)->text(0),lNamespaces.at(i)))
			{
				pItem = (ClassEditorTreeWidgetItem *)pItem->child(j);
				bFound = true;
				break;
			}
		}
		if(!bFound)
			pItem = new ClassEditorTreeWidgetItem(pItem,ClassEditorTreeWidgetItem::Namespace,lNamespaces.at(i));
	}
	return new ClassEditorTreeWidgetItem(pItem,ClassEditorTreeWidgetItem::Class,lNamespaces.at(i));
}

ClassEditorTreeWidgetItem * ClassEditorWidget::createFullNamespace(const QString & szFullName)
{
	bool bFound;
	int i;
	
	QStringList lNamespaces = szFullName.split("::");
	if(lNamespaces.count() == 0)
		return 0;
	ClassEditorTreeWidgetItem * pItem = findTopLevelItem(lNamespaces.at(0));
	if(!pItem)
		pItem = new ClassEditorTreeWidgetItem(m_pTreeWidget,ClassEditorTreeWidgetItem::Namespace,lNamespaces.at(0));
	if(lNamespaces.count() == 1)
		return pItem;

	for(i=1; i < lNamespaces.count(); i++)
	{
		bFound = false;
		for(int j=0;j<pItem->childCount();j++)
		{
			if(KviQString::equalCI(pItem->child(j)->text(0),lNamespaces.at(i)) && (( ClassEditorTreeWidgetItem *)pItem->child(j))->isNamespace())
			{
				pItem = (ClassEditorTreeWidgetItem *)pItem->child(j);
				bFound = true;
				break;
			}
		}
		if(!bFound)
			pItem = new ClassEditorTreeWidgetItem(pItem,ClassEditorTreeWidgetItem::Namespace,lNamespaces.at(i));
	}
	return pItem;
}

void ClassEditorWidget::oneTimeSetup()
{
	QStringList sl;
	QString szClassName;
	QString szPath;
	g_pApp->getLocalKvircDirectory(szPath,KviApplication::Classes);
	QDir d(szPath);
	
	QString szExtension = QString("*%1").arg(KVI_FILEEXTENSION_SCRIPT); // *.kvs
	sl = d.entryList(QStringList(szExtension), QDir::Files | QDir::NoDotAndDotDot);
	
	g_pModuleManager->getModule("objects");
	KviPointerHashTableIterator<QString,KviKvsObjectClass> it(*KviKvsKernel::instance()->objectController()->classDict());
	
	KviKvsObjectClass * pClass = 0;
	ClassEditorTreeWidgetItem * pClassItem = 0;
	while(KviKvsObjectClass * pClass = it.current())
	{
		if(pClass->isBuiltin())
			m_pClasses->insert(it.currentKey(),0);
		else
		{
			QString szTmp;
			szTmp = it.currentKey();
			szTmp.replace("::","--");
			szTmp.append(".kvs");
			if(sl.indexOf(szTmp) == -1)
			{
				szClassName = it.currentKey();
				pClassItem = createFullItem(szClassName);
				createFullClass(it.current(),pClassItem,szClassName);
			}
		}
		++it;
	}

	for(int i=0; i < sl.count(); i++)
	{
		szClassName = sl.at(i);
		szClassName.replace("--","::");
		szClassName.chop(4);
		pClassItem = createFullItem(szClassName);
		pClass = KviKvsKernel::instance()->objectController()->lookupClass(szClassName);
		if(pClass)
			createFullClass(pClass, pClassItem, szClassName);
	}
	loadNotBuiltClasses();
	connect(m_pTreeWidget,SIGNAL(currentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)),this,SLOT(currentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)));
	m_pTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(m_pTreeWidget,SIGNAL(customContextMenuRequested(const QPoint &)),this,SLOT(customContextMenuRequested(const QPoint &)));
}

void ClassEditorWidget::createFullClass(KviKvsObjectClass * pClass, ClassEditorTreeWidgetItem * pClassItem, const QString & szClassName)
{
	KviPointerHashTableIterator<QString,KviKvsObjectFunctionHandler>  it(* pClass->getHandlers());
	QStringList szFunctionsList;
	while(it.current())
	{
		szFunctionsList.append(it.currentKey());
		++it;
	}
	szFunctionsList.sort();
	ClassEditorTreeWidgetItem *pFunctionItem;
	m_pClasses->insert(szClassName,pClassItem);
	KviKvsObjectClass * pParentClass = pClass->parentClass();
	pClassItem->setInheritsClass(pParentClass->name());
	for(int i=0;i<szFunctionsList.count();i++)
	{
		QString szCode;
		KviKvsObjectFunctionHandler *pHandler=pClass->lookupFunctionHandler(szFunctionsList.at(i));
		if (pClass->isScriptHandler(szFunctionsList.at(i)) && !pHandler->isClone())
		{
			pFunctionItem=findFunction(szFunctionsList.at(i), pClassItem);
			if(!pFunctionItem) pFunctionItem = new ClassEditorTreeWidgetItem(pClassItem,ClassEditorTreeWidgetItem::Method,szFunctionsList.at(i));
			pClass->getFunctionCode(szCode,*pHandler);
			pFunctionItem->setBuffer(szCode);
			pFunctionItem->setReminder(pClass->reminder(pHandler));
			if(pHandler->flags() & KviKvsObjectFunctionHandler::Internal)
				pFunctionItem->setInternalFunction(true);
		}
		//++it;
	}
}


/*
void ClassEditorWidget::classRefresh(const QString & szName)
{
	if(m_bSaving)
		return;
	ClassEditorTreeWidgetItem * item;
	KviKvsScript * class = KviKvsClassManager::instance()->classDict()->find(szName);
	item = createFullItem(szName);
	if(item!=m_pLastEditedItem)
	{
		item->setBuffer(class->code());
		return;
	}
	if(
		QMessageBox::warning(0,__tr2qs_ctx("OverWrite Current Class","editor"),
				__tr2qs_ctx("An external script has changed the class you are currently editing. Do you want to accept the external changes?","editor"),
				QMessageBox::Yes,QMessageBox::No|QMessageBox::Default|QMessageBox::Escape) != QMessageBox::Yes
		)
		return;
	item->setBuffer(class->code());
	m_pEditor->setText(class->code());
}
*/

bool ClassEditorWidget::hasSelectedItems()
{
	return m_pTreeWidget->selectedItems().count() ? 1 : 0;
}

bool ClassEditorWidget::classExists(QString & szFullItemName)
{
	if(m_pClasses->find(szFullItemName))
		return true;
	else
		return false;
}

void ClassEditorWidget::renameFunction()
{
	if(!m_pLastEditedItem)
		return;
	ClassEditorTreeWidgetItem * pFunction = m_pLastEditedItem;
	QString szClassName = buildFullClassName((ClassEditorTreeWidgetItem *)pFunction->parent());
	QString szFunctionName = pFunction->name();
	QString szReminder = pFunction->reminder();
	QString szNewReminder = szReminder;
	ClassEditorTreeWidgetItem * pParentClass = (ClassEditorTreeWidgetItem *) pFunction->parent();

	QString szNewFunctionName = szFunctionName;
	bool bInternal = pFunction->isInternalFunction();
	if(!askForFunction(szNewFunctionName,szNewReminder,&bInternal,szClassName,true))
		return;
	if(KviQString::equalCS(szFunctionName,szNewFunctionName) && bInternal == pFunction->isInternalFunction())
	{
		if(!KviQString::equalCS(szNewReminder,szReminder))
		{
			pFunction->setReminder(szNewReminder);
			KviKvsObjectClass * pClass = KviKvsKernel::instance()->objectController()->lookupClass(szClassName);
			if(pClass)
			{
				KviKvsObjectFunctionHandler * pHandler = pClass->lookupFunctionHandler(szFunctionName);
				if(pHandler)
				{
					pClass->setReminder(szNewReminder,pHandler);
					QString szPath;
					QString szFileName = szClassName.toLower();
					szFileName += ".kvs";
					szFileName.replace("::","--");
					g_pApp->getLocalKvircDirectory(szPath,KviApplication::Classes,szFileName);
					pClass->save(szPath);
				}
			}
			currentItemChanged(pFunction,pFunction);
		} else {
			g_pClassEditorModule->lock();
			QMessageBox::information(this,
			__tr2qs_ctx("Function already exists","editor"),
			__tr2qs_ctx("This name is already in use. Please choose another one.","editor"),
			__tr2qs_ctx("Ok, Let me try again...","editor"));
			g_pClassEditorModule->unlock();
			return;
		}
	}
	
	pFunction->setName(szNewFunctionName);
	pFunction->setReminder(szNewReminder);
	currentItemChanged(pFunction,pFunction);
	pFunction->setInternalFunction(bInternal);
	pParentClass->setClassNotBuilt(true);
	
	KviPointerList<ClassEditorTreeWidgetItem> lInheritedClasses;
	lInheritedClasses.setAutoDelete(false);
	searchInheritedClasses(szClassName,lInheritedClasses);
	for(unsigned int i=0; i < lInheritedClasses.count(); i++)
		lInheritedClasses.at(i)->setClassNotBuilt(true);
	activateItem(pFunction);
}

ClassEditorTreeWidgetItem * ClassEditorWidget::findFunction(const QString & szFunctionName, ClassEditorTreeWidgetItem * pClass)
{
	for(int i=0; i < pClass->childCount(); i++)
	{
		if(KviQString::equalCI(szFunctionName,((ClassEditorTreeWidgetItem *)pClass->child(i))->name()))
			return (ClassEditorTreeWidgetItem *) pClass->child(i);
	}
	return 0;
}

void ClassEditorWidget::renameItem()
{
	if(!m_pLastEditedItem)
		return;
	if(m_pLastEditedItem->isClass())
		renameClass(m_pLastEditedItem);
	else if(m_pLastEditedItem->isNamespace())
		renameNamespace(m_pLastEditedItem);
	else {
		// is function
		ClassEditorTreeWidgetItem * pParent = (ClassEditorTreeWidgetItem *)m_pLastEditedItem->parent();
		if(pParent->isClass())
			renameClass(pParent);
	}
}

void ClassEditorWidget::renameClass(ClassEditorTreeWidgetItem * pClassItem)
{
	QString szClassName = buildFullClassName(pClassItem);
	QString szNewClassName = szClassName;

	QString szInheritsClassName = pClassItem->inheritsClass();
	QString szNewInheritsClassName = szInheritsClassName;
	bool bOk = askForClassName(szNewClassName,szNewInheritsClassName,true);
	if(!bOk)
		return;
	if(classExists(szNewClassName) && KviQString::equalCS(szClassName,szNewClassName) && KviQString::equalCS(szInheritsClassName,szNewInheritsClassName))
	{
		g_pClassEditorModule->lock();
		QMessageBox::information(this,
			__tr2qs_ctx("Class already exists","editor"),
			__tr2qs_ctx("This name is already in use. Please choose another one.","editor"),
			__tr2qs_ctx("Ok, Let me try again...","editor"));
		g_pClassEditorModule->unlock();
		return;
	}
	/*if(szNewClassName.tmp.indexOf("@@",Qt::CaseInsensitive) != -1)
	{
		g_pClassEditorModule->lock();
		QMessageBox::information(this,
			__tr2qs_ctx("Bad Namespace Name","editor"),
			__tr2qs_ctx("Found an empty namespace in namespace name","editor"),
			__tr2qs_ctx("Ok, Let me try again...","editor"));
		g_pClassEditorModule->unlock();
		szNewName = "";
		continue;
	}*/
	ClassEditorTreeWidgetItem * pNewItem = 0;
	m_pClasses->removeRef(pClassItem);
	cutItem(pClassItem);
	if(szNewClassName.contains("::"))
	{
		pNewItem = createFullNamespace(szNewClassName.left(szNewClassName.lastIndexOf("::")));
		pClassItem->setName(szNewClassName.section("::",-1,-1));
		pNewItem->addChild(pClassItem);
	} else {
		pClassItem->setName(szNewClassName);
		m_pTreeWidget->addTopLevelItem(pClassItem);
	}
	m_pClasses->insert(szNewClassName,pClassItem);
	pClassItem->setInheritsClass(szNewInheritsClassName);
	pClassItem->setClassNotBuilt(true);
	
	KviPointerList<ClassEditorTreeWidgetItem> lInheritedClasses;
	lInheritedClasses.setAutoDelete(false);
	searchInheritedClasses(szClassName,lInheritedClasses);
	for(unsigned int i=0; i < lInheritedClasses.count(); i++)
	{
		lInheritedClasses.at(i)->setClassNotBuilt(true);
		lInheritedClasses.at(i)->setExpanded(true);
		lInheritedClasses.at(i)->setInheritsClass(szNewClassName);
	}
	
	if(pNewItem)
	{
		activateItem(pNewItem);
		pNewItem->setExpanded(true);
	} else {
		activateItem(pClassItem);
		pClassItem->setExpanded(true);
	}
	
	qDebug("delete class %s caused by rename",szClassName.toUtf8().data());
	KviKvsObjectClass * pClass = KviKvsKernel::instance()->objectController()->lookupClass(szClassName);
	if(pClass)
		KviKvsKernel::instance()->objectController()->deleteClass(pClass);
}

void ClassEditorWidget::cutItem(ClassEditorTreeWidgetItem * pItem)
{
	int iIdx = m_pTreeWidget->indexOfTopLevelItem(pItem);
	if(iIdx!=-1)
		m_pTreeWidget->takeTopLevelItem(iIdx);
	else {
		ClassEditorTreeWidgetItem * pParent = (ClassEditorTreeWidgetItem *)pItem->parent();
		pParent->removeChild(pItem);
	}
}

void ClassEditorWidget::renameNamespace(ClassEditorTreeWidgetItem * pOldNamespaceItem)
{
	QString szOldNameSpaceName = buildFullClassName(m_pLastEditedItem);
	QString szNewNameSpaceName;
	if(!askForNamespaceName(__tr2qs_ctx("Rename Namespace","editor"),__tr2qs_ctx("Please enter the new name for the namespace","editor"),szOldNameSpaceName,szNewNameSpaceName))
		return;
	if(KviQString::equalCI(szOldNameSpaceName,szNewNameSpaceName))
		return;
	ClassEditorTreeWidgetItem * pNewItem = findItem(szNewNameSpaceName);
	if(pNewItem)
	{
		g_pClassEditorModule->lock();
		if(pNewItem->isClass())
		{
			QMessageBox::information(this,
				__tr2qs_ctx("Name already exists as Class name","editor"),
				__tr2qs_ctx("This name is already in use as Class name. Please choose another one.","editor"),
				__tr2qs_ctx("Ok, Let me try again...","editor"));
		} else {
			QMessageBox::information(this,
			__tr2qs_ctx("Namespace already exists","editor"),
			__tr2qs_ctx("This name is already in use. Please choose another one.","editor"),
			__tr2qs_ctx("Ok, Let me try again...","editor"));
		}
		g_pClassEditorModule->unlock();
		return;
	}
	KviPointerList<ClassEditorTreeWidgetItem> pList;
	pList.setAutoDelete(false);
	appendAllClassItemsRecursive(&pList,pOldNamespaceItem);
	cutItem(pOldNamespaceItem);
	if(szNewNameSpaceName.contains("::"))
	{
		pNewItem=createFullNamespace(szNewNameSpaceName.left(szNewNameSpaceName.lastIndexOf("::")));
		pOldNamespaceItem->setName(szNewNameSpaceName.section("::",-1,-1));
		pNewItem->addChild(pOldNamespaceItem);
	} else {
		m_pTreeWidget->addTopLevelItem(pOldNamespaceItem);
		pOldNamespaceItem->setName(szNewNameSpaceName);
	}
	for(unsigned int u=0; u < pList.count(); u++)
	{
		KviPointerHashTableEntry<QString,ClassEditorTreeWidgetItem> * pEntry = m_pClasses->findRef(pList.at(u));
		if(pEntry)
		{
			KviPointerList<ClassEditorTreeWidgetItem> lInheritedClasses;
			lInheritedClasses.setAutoDelete(false);

			QString szOldName = pEntry->key();
			QString szNewName = buildFullClassName(pList.at(u));
			searchInheritedClasses(szOldName,lInheritedClasses);
			for(unsigned int v=0; v < lInheritedClasses.count(); v++)
			{
				lInheritedClasses.at(v)->setClassNotBuilt(true);
				lInheritedClasses.at(v)->setExpanded(true);
				lInheritedClasses.at(v)->setInheritsClass(szNewName);
			}
			m_pClasses->removeRef(pList.at(u));
			m_pClasses->insert(szNewName,pList.at(u));
			pList.at(u)->setClassNotBuilt(true);

			KviKvsObjectClass * pClass = KviKvsKernel::instance()->objectController()->lookupClass(szOldName);
			if(pClass)
				KviKvsKernel::instance()->objectController()->deleteClass(pClass);
		}
	}

	if(pNewItem)
	{
		activateItem(pNewItem);
		pNewItem->setExpanded(true);
	} else {
		activateItem(pOldNamespaceItem);
		pOldNamespaceItem->setExpanded(true);
	}
	//searchReplace(szOldNameSpaceName+"::",true,szNewNameSpaceName+"::");
}

void ClassEditorWidget::saveLastEditedItem()
{
	if(!m_pLastEditedItem)
		return;
	if(!m_pEditor->isModified() || m_pLastEditedItem->isNamespace() || m_pLastEditedItem->isClass())
		return;
	((ClassEditorTreeWidgetItem *)m_pLastEditedItem)->setCursorPosition(m_pEditor->getCursor());
	QString newCode;
	m_pEditor->getText(newCode);
	((ClassEditorTreeWidgetItem *)m_pLastEditedItem)->setBuffer(newCode);
	((ClassEditorTreeWidgetItem *)m_pLastEditedItem->parent())->setClassNotBuilt(true);
}

void ClassEditorWidget::currentItemChanged(QTreeWidgetItem * pTree, QTreeWidgetItem *)
{
	saveLastEditedItem();
	m_pLastEditedItem = (ClassEditorTreeWidgetItem *)pTree;
	if(!m_pLastEditedItem)
	{
		m_pClassNameLabel->setText(__tr2qs_ctx("No item selected","editor"));
		m_pClassNameRenameButton->setEnabled(false);
		m_pEditor->setText("");
		m_pEditor->setEnabled(false);
		return;
	}
	
	ClassEditorTreeWidgetItem * pClassItem = 0;
	if(m_pLastEditedItem->isMethod())
		pClassItem = (ClassEditorTreeWidgetItem *)m_pLastEditedItem->parent();
	else
		pClassItem = m_pLastEditedItem;
	
	QString szClassName = buildFullClassName(pClassItem);
	
	if(m_pLastEditedItem->isNamespace())
	{
		QString szLabelText = __tr2qs_ctx("Namespace","editor");
		szLabelText += ": <b>";
		szLabelText += szClassName;
		szLabelText += "</b>";
		m_pClassNameLabel->setText(szLabelText);
		m_pClassNameRenameButton->setEnabled(true);
                m_pFunctionNameRenameButton->setEnabled(false);
		//m_pinheritsClassNameLabel->setText("");
                m_pFunctionNameLabel->setText("");
		m_pEditor->setText("");
		m_pEditor->setEnabled(false);
		m_pTreeWidget->setFocus();
		return;
	}
	
	QString szLabelText = __tr2qs_ctx("Class","editor");
	szLabelText += ": <b>";
	szLabelText += szClassName;
	szLabelText += "</b>, ";
	szLabelText += __tr2qs_ctx("inherits from class ","editor");
	szLabelText += ": <b>";
	szLabelText += pClassItem->inheritsClass();
	szLabelText += "</b>";
	m_pClassNameLabel->setText(szLabelText);

	szLabelText = __tr2qs_ctx("Member Function","editor");
	if(m_pLastEditedItem->isMethod())
	{
		szLabelText += ": <b>";
		szLabelText += m_pLastEditedItem->text(0);
		szLabelText += "</b>";
		m_pFunctionNameRenameButton->setEnabled(true);
		if(m_pLastEditedItem->reminder().isEmpty())
			m_pReminderLabel->hide();
		else {
			QString szReminderText =__tr2qs_ctx("Reminder text.","editor");
			szReminderText += ": <b>";
			szReminderText += m_pLastEditedItem->reminder();
			szReminderText += "</b>";
			m_pReminderLabel->setText(szReminderText);
			m_pReminderLabel->show();
		}
		
		m_pFunctionNameLabel->setText(szLabelText);
		m_pFunctionNameLabel->show();
		m_pFunctionNameRenameButton->show();
	} else {
		m_pReminderLabel->hide();
		m_pFunctionNameLabel->hide();
		m_pClassNameRenameButton->setEnabled(true);
		m_pFunctionNameRenameButton->hide();
	}
	if(m_pLastEditedItem->isClass())
	{
		m_pFunctionNameRenameButton->setEnabled(false);
		m_pEditor->setText("");
		m_pEditor->setEnabled(true);
		m_pTreeWidget->setFocus();
		QString szBuffer;
		QStringList szFunctionsList;
		KviPointerHashTable<QString,ClassEditorTreeWidgetItem> lFunctions;
		lFunctions.setAutoDelete(false);
		
		ClassEditorTreeWidgetItem * pItem = 0;
		for(int i=0; i < pTree->childCount(); i++)
		{
			pItem = ((ClassEditorTreeWidgetItem *)pTree->child(i));
			szFunctionsList.append(pItem->name());
			lFunctions.insert(pItem->name(),pItem);
		}
		szFunctionsList.sort();
		for(int i=0; i < szFunctionsList.count(); i++)
		{
			szBuffer += "Member Function: <b>$" + szFunctionsList.at(i) + "</b><br>";
			if(!lFunctions.find(szFunctionsList.at(i))->reminder().isEmpty())
				szBuffer+="Parameters reminder: "+lFunctions.find(szFunctionsList.at(i))->reminder()+"<br>";
			szBuffer+="<br>";
		}
		m_pEditor->setUnHighlightedText(szBuffer);
		m_pEditor->setReadOnly(true);
		return;
	}
	m_pEditor->setReadOnly(false);
	m_pEditor->setText(((ClassEditorTreeWidgetItem *)pTree)->buffer());
	m_pEditor->setFocus();
	m_pEditor->setCursorPosition(((ClassEditorTreeWidgetItem *)pTree)->cursorPosition());
	m_pEditor->setEnabled(true);
}

void ClassEditorWidget::customContextMenuRequested(QPoint pnt)
{
	m_pContextPopup->clear();

	m_pLastClickedItem = (ClassEditorTreeWidgetItem *)m_pTreeWidget->itemAt(pnt);

	int iId;
	iId = m_pContextPopup->insertItem(
		*(g_pIconManager->getSmallIcon(KviIconManager::NameSpace)),
		__tr2qs_ctx("Add Namespace","editor"),
		this,SLOT(newNamespace()));
	if(!m_pLastClickedItem)
		m_pContextPopup->setItemEnabled(iId,true);
	else
		m_pContextPopup->setItemEnabled(iId,m_pLastClickedItem->isNamespace());

	iId = m_pContextPopup->insertItem(
		*(g_pIconManager->getSmallIcon(KviIconManager::Class)),
		__tr2qs_ctx("Add Class","editor"),
		this,SLOT(newClass()));
	if(!m_pLastClickedItem)
		m_pContextPopup->setItemEnabled(iId,true);
	else
		m_pContextPopup->setItemEnabled(iId,m_pLastClickedItem->isNamespace());

	iId = m_pContextPopup->insertItem(
		*(g_pIconManager->getSmallIcon(KviIconManager::Function)),
		__tr2qs_ctx("Add Member Function","editor"),
		this,SLOT(newMemberFunction()));
	if(!m_pLastClickedItem)
		m_pContextPopup->setItemEnabled(iId,false);
	else
		m_pContextPopup->setItemEnabled(iId,m_pLastClickedItem->isClass()| m_pLastClickedItem->isMethod());

	bool bHasItems = m_pTreeWidget->topLevelItemCount();
	bool bHasSelected = hasSelectedItems();

	m_pContextPopup->insertSeparator();

	iId = m_pContextPopup->insertItem(
		*(g_pIconManager->getSmallIcon(KviIconManager::Quit)),
		__tr2qs_ctx("Remove Selected","editor"),
		this,SLOT(removeSelectedItems()));
	m_pContextPopup->setItemEnabled(iId,bHasSelected);

	m_pContextPopup->insertSeparator();

	m_pContextPopup->insertItem(
		*(g_pIconManager->getSmallIcon(KviIconManager::Folder)),
		__tr2qs_ctx("Export Selected...","editor"),
		this,SLOT(exportSelected()));
	m_pContextPopup->setItemEnabled(iId,bHasSelected);

	m_pContextPopup->insertItem(
		*(g_pIconManager->getSmallIcon(KviIconManager::Folder)),
		__tr2qs_ctx("Export Selected in singles files...","editor"),
		this,SLOT(exportSelectedSepFiles()));

	m_pContextPopup->setItemEnabled(iId,bHasSelected);

	m_pContextPopup->insertItem(
			*(g_pIconManager->getSmallIcon(KviIconManager::Folder)),
			__tr2qs_ctx("Export All...","editor"),
			this,SLOT(exportAll()));
	m_pContextPopup->setItemEnabled(iId,bHasItems);

	m_pContextPopup->insertSeparator();

	m_pContextPopup->insertItem(
			*(g_pIconManager->getSmallIcon(KviIconManager::Search)),
			__tr2qs_ctx("Find In Classes...","editor"),
			this,SLOT(slotFind()));
	m_pContextPopup->setItemEnabled(iId,bHasItems);

	m_pContextPopup->insertItem(
			*(g_pIconManager->getSmallIcon(KviIconManager::NameSpace)),
			__tr2qs_ctx("Collapse All Items","editor"),
			this,SLOT(slotCollapseItems()));

	m_pContextPopup->setItemEnabled(iId,bHasItems);
	m_pContextPopup->popup(m_pTreeWidget->mapToGlobal(pnt));
}

void ClassEditorWidget::searchReplace(const QString & szSearch, bool bReplace, const QString & szReplace)
{
	KviPointerHashTableIterator<QString,ClassEditorTreeWidgetItem> it (*m_pClasses);
	while(it.current())
	{
		ClassEditorTreeWidgetItem * pItem = it.current();
		for(int j=0; j < pItem->childCount(); j++)
		{
			bool bOpened = false;
			if(((ClassEditorTreeWidgetItem *)pItem->child(j))->buffer().indexOf(szSearch,0,Qt::CaseInsensitive) != -1)
			{
				pItem->child(j)->setBackground(0, QColor(255,0,0,128));
				if(bReplace)
				{
					QString &buffer=(QString &)((ClassEditorTreeWidgetItem *)pItem->child(j))->buffer();
					pItem->setClassNotBuilt(true);
					buffer.replace(szSearch,szReplace,Qt::CaseInsensitive);
				}
				if(!bOpened)
				{
					openParentItems(pItem->child(j));
					bOpened = true;
				}
			} else {
				pItem->child(j)->setBackground(0, QColor(255,255,255));
			}
		}
		++it;
	}
}

void ClassEditorWidget::slotFind()
{
	g_pClassEditorModule->lock();
	bool bOk;

	QString szSearch = QInputDialog::getText(this,
		__tr2qs_ctx("Find In Classes","editor"),
		__tr2qs_ctx("Please enter the text to be searched for. The matching function will be highlighted.","editor"),
		QLineEdit::Normal,
		"",
		&bOk);

	g_pClassEditorModule->unlock();
	if(!bOk)
		return;
	if(szSearch.isEmpty())
		return;
	m_pEditor->setFindText(szSearch);
	searchReplace(szSearch);
}

void ClassEditorWidget::slotFindWord(const QString & szSearch)
{
	m_pEditor->setFindText(szSearch);
}

void ClassEditorWidget::recursiveCollapseItems(ClassEditorTreeWidgetItem * pItem)
{
	if(!pItem)
		return;
	for(int i=0; i < pItem->childCount(); i++)
	{
		if(pItem->child(i)->childCount())
		{
			pItem->child(i)->setExpanded(false);
			recursiveCollapseItems((ClassEditorTreeWidgetItem *)pItem->child(i));
		}
	}
}

void ClassEditorWidget::slotCollapseItems()
{
	for(int i=0; i < m_pTreeWidget->topLevelItemCount(); i++)
	{
		if(m_pTreeWidget->topLevelItem(i)->childCount())
		{
			m_pTreeWidget->topLevelItem(i)->setExpanded(false);
			recursiveCollapseItems((ClassEditorTreeWidgetItem *)m_pTreeWidget->topLevelItem(i));
		}
	}
}

void ClassEditorWidget::slotReplaceAll(const QString & szFind, const QString & szReplace)
{
	m_pEditor->setFindText(szReplace);
	searchReplace(szFind,true,szReplace);
	/*
	for (int i=0;i<m_pTreeWidget->topLevelItemCount();i++)
	{
		recursiveSearchReplace(szFind,(ClassEditorTreeWidgetItem *)m_pTreeWidget->topLevelItem(i),true,szReplace);
	}
	*/
}

void ClassEditorWidget::exportClassBuffer(QString & szBuffer, ClassEditorTreeWidgetItem * pItem)
{
	QString szTmp = pItem->buffer();
	KviCommandFormatter::blockFromBuffer(szTmp);
	QString szName = buildFullClassName(pItem);

	szBuffer = "class(\"";
	szBuffer += szName;
	if(!pItem->inheritsClass().isEmpty())
	{
		szBuffer += "\",\"";
		szBuffer += pItem->inheritsClass();
	}
	szBuffer += "\")\n{\n";
	for(int i=0; i < pItem->childCount(); i++)
	{
		ClassEditorTreeWidgetItem * pFunction = (ClassEditorTreeWidgetItem *)pItem->child(i);
		if(pFunction->isMethod())
		{
			szBuffer += "\t";
			if(pFunction->isInternalFunction())
				szBuffer += "internal ";
			szBuffer += "function ";
			szBuffer += pFunction->name();
			szBuffer += "(" + pFunction->reminder() + ")";
			szBuffer += "\n\t{\n";
			szBuffer += pFunction->buffer();
			szBuffer += "\n\t}\n";
		}
	}
	szBuffer += "}\n";
}

void ClassEditorWidget::exportAll()
{
	exportClasses(false);
}

void ClassEditorWidget::exportSelectedSepFiles()
{
	exportClasses(true,true);
}

void ClassEditorWidget::exportSelected()
{
	exportClasses(true);
}

void ClassEditorWidget::exportSelectionInSinglesFiles(KviPointerList<ClassEditorTreeWidgetItem> * pList)
{
	bool bReplaceAll = false;
	
	if(!m_szDir.endsWith(QString(KVI_PATH_SEPARATOR)))
		m_szDir += KVI_PATH_SEPARATOR;
	if(!pList->first())
	{
		g_pClassEditorModule->lock();
		QMessageBox::warning(this,__tr2qs_ctx("Class Export","editor"),__tr2qs_ctx("There is no selection!","editor"),__tr2qs_ctx("OK","editor"));
		g_pClassEditorModule->unlock();
		return;
	}
	g_pClassEditorModule->lock();

	if(!KviFileDialog::askForDirectoryName(m_szDir,__tr2qs_ctx("Choose a Directory - KVIrc","editor"),m_szDir))
	{
		g_pClassEditorModule->unlock();
		return;
	}

	if(!m_szDir.endsWith(QString(KVI_PATH_SEPARATOR)))
		m_szDir += KVI_PATH_SEPARATOR;

	for(ClassEditorTreeWidgetItem * pItem = pList->first(); pItem; pItem = pList->next())
	{
		QString szTmp;
		exportClassBuffer(szTmp,pItem);
		QString szFileName = buildFullClassName(pItem);
		szFileName += ".kvs";
		szFileName.replace("::","_");
		QString szCompletePath = m_szDir+szFileName;
		if(KviFileUtils::fileExists(szCompletePath) && !bReplaceAll)
		{
			QString szMsg = __tr2qs_ctx("The file \"%1\" exists. Do you want to replace it?","editor").arg(szFileName);
			int iRet = QMessageBox::question(this,__tr2qs_ctx("Replace file","editor"),szMsg,__tr2qs_ctx("Yes","editor"),__tr2qs_ctx("Yes to All","editor"),__tr2qs_ctx("No","editor"));
			if(iRet != 2)
			{
				KviFileUtils::writeFile(szCompletePath,szTmp);
				if(iRet == 1)
					bReplaceAll = true;
			}
		}
		else
		KviFileUtils::writeFile(szCompletePath,szTmp);
	}
	g_pClassEditorModule->unlock();
}

void ClassEditorWidget::exportClasses(bool bSelectedOnly, bool bSingleFiles)
{
	QString szOut;
	QString szNameFile;
	QString szFile;
	int iCount = 0;
	
	saveLastEditedItem();

	KviPointerList<ClassEditorTreeWidgetItem> list;
	list.setAutoDelete(false);

	if(bSelectedOnly)
		appendSelectedClassItems(&list);
	else
		appendAllClassItems(&list);
	
	if(bSingleFiles)
	{
		exportSelectionInSinglesFiles(&list);
		return;
        }
	
	ClassEditorTreeWidgetItem * pTempItem = 0;
	for(ClassEditorTreeWidgetItem * pItem = list.first(); pItem; pItem = list.next())
	{
		pTempItem = pItem;
		iCount++;
		QString szTmp;
		exportClassBuffer(szTmp,pItem);
		szOut += szTmp;
		szOut += "\n";
	}
	
	if(szOut.isEmpty())
	{
		g_pClassEditorModule->lock();
		QMessageBox::warning(this,__tr2qs_ctx("Class Export","editor"),__tr2qs_ctx("The exported file would be empty: cowardly refusing to write it","editor"),__tr2qs_ctx("OK","editor"));
		g_pClassEditorModule->unlock();
		return;
	}

	QString szName = m_szDir;

	if(!szName.endsWith(QString(KVI_PATH_SEPARATOR)))
		szName += KVI_PATH_SEPARATOR;
	
	g_pClassEditorModule->lock();

	if(iCount != 1)
		szNameFile = "classes";
	else {
		QString szTmp = buildFullClassName(pTempItem);
		szNameFile = szTmp.replace("::","_");
	}
	
	szName += szNameFile;
	szName += ".kvs";
	if(!KviFileDialog::askForSaveFileName(szFile,__tr2qs_ctx("Choose a Filename - KVIrc","editor"),szName,KVI_FILTER_SCRIPT,false,true,true))
	{
		g_pClassEditorModule->unlock();
		return;
	}
	m_szDir=QFileInfo(szFile).absolutePath();
	g_pClassEditorModule->unlock();

	if(!KviFileUtils::writeFile(szFile,szOut))
	{
		g_pClassEditorModule->lock();
		QMessageBox::warning(this,__tr2qs_ctx("Write Failed - KVIrc","editor"),__tr2qs_ctx("Unable to write to the class file.","editor"),__tr2qs_ctx("OK","editor"));
		g_pClassEditorModule->unlock();
	}
}

void ClassEditorWidget::saveProperties(KviConfigurationFile * pCfg)
{
	pCfg->writeEntry("Sizes",m_pSplitter->sizes());
	QString szName;
	if(m_pLastEditedItem)
		szName = buildFullClassName(m_pLastEditedItem);
	pCfg->writeEntry("LastClass",szName);
}

void ClassEditorWidget::loadProperties(KviConfigurationFile * pCfg)
{
	QList<int> def;
	def.append(20);
	def.append(80);
	m_pSplitter->setSizes(pCfg->readIntListEntry("Sizes",def));
	QString szTmp = pCfg->readEntry("LastClass",QString());

	ClassEditorTreeWidgetItem * pItem = findItem(szTmp);
	activateItem(pItem);
}

void ClassEditorWidget::appendSelectedClassItems(KviPointerList<ClassEditorTreeWidgetItem> * pList)
{
	QList<QTreeWidgetItem *> list = m_pTreeWidget->selectedItems();
	for(int i=0; i < list.count(); i++)
	{
		if(((ClassEditorTreeWidgetItem *)list.at(i))->isClass())
			pList->append((ClassEditorTreeWidgetItem *)list.at(i));
		else appendSelectedClassItemsRecursive(pList,list.at(i));
	}
}

void ClassEditorWidget::appendSelectedClassItemsRecursive(KviPointerList<ClassEditorTreeWidgetItem> * pList, QTreeWidgetItem * pStartFrom)
{
	for(int i=0; i < pStartFrom->childCount(); i++)
	{
		if(((ClassEditorTreeWidgetItem *)pStartFrom->child(i))->isClass())
			pList->append(((ClassEditorTreeWidgetItem *)pStartFrom->child(i)));
		else
			appendSelectedClassItemsRecursive(pList,pStartFrom->child(i));
	}
}

void ClassEditorWidget::appendAllClassItems(KviPointerList<ClassEditorTreeWidgetItem> * pList)
{
	KviPointerHashTableIterator<QString,ClassEditorTreeWidgetItem> it (*m_pClasses);
	while(it.current())
	{
		pList->append(it.current());
		++it;
	}
}

void ClassEditorWidget::appendAllClassItemsRecursive(KviPointerList<ClassEditorTreeWidgetItem> * pList, QTreeWidgetItem * pStartFrom)
{
	for(int i=0; i < pStartFrom->childCount(); i++)
	{
		if(((ClassEditorTreeWidgetItem *)pStartFrom->child(i))->isClass())
		{
			pList->append((ClassEditorTreeWidgetItem *)pStartFrom->child(i));
		} else {
			appendAllClassItemsRecursive(pList,pStartFrom->child(i));
		}
	}
}

void ClassEditorWidget::removeItemChildren(ClassEditorTreeWidgetItem * pItem, KviPointerList <ClassEditorTreeWidgetItem> & lRemovedItems)
{
	if(pItem->isClass())
	{
		KviPointerList<ClassEditorTreeWidgetItem> lInheritedClasses;
		lInheritedClasses.setAutoDelete(false);
		searchInheritedClasses(buildFullClassName(pItem),lInheritedClasses);
		for(unsigned int u=0; u < lInheritedClasses.count(); u++)
		{
			lInheritedClasses.at(u)->setClassNotBuilt(true);
			lInheritedClasses.at(u)->setExpanded(true);
			lInheritedClasses.at(u)->setInheritsClass("object");
		}
	}
	
	while(pItem->childCount() > 0)
	{
		ClassEditorTreeWidgetItem * pChild = (ClassEditorTreeWidgetItem *)(pItem->child(0));
		if(pChild->childCount())
			removeItemChildren(pChild,lRemovedItems);
		if(pChild->isClass())
		{
			m_pClasses->removeRef(pChild);
			KviKvsObjectClass * pClass = KviKvsKernel::instance()->objectController()->lookupClass(buildFullClassName(pChild));
			if(pClass)
				KviKvsKernel::instance()->objectController()->deleteClass(pClass);
			qDebug("removing class %s %p",buildFullClassName(pChild).toUtf8().data(), pClass);
		}
		pItem->removeChild(pChild);
		lRemovedItems.append(pItem);
		delete pChild;
	}
}

bool ClassEditorWidget::removeItem(ClassEditorTreeWidgetItem * pItem, KviPointerList <ClassEditorTreeWidgetItem> & lRemovedItems, bool * pbYesToAll)
{
	if(!pItem)
		return true;
	QString szMsg;
	QString szName = pItem->name();

	if(!*pbYesToAll)
	{
		saveLastEditedItem();
		if(pItem->isClass())
		{
			szMsg = QString(__tr2qs_ctx("Do you really want to remove the class \"%1\"?","editor")).arg(szName);
		} else if(pItem->isNamespace())
		{
			szMsg = QString(__tr2qs_ctx("Do you really want to remove the namespace \"%1\"?","editor")).arg(szName);
			szMsg += "<br>";
			szMsg += __tr2qs_ctx("Please note that all the children classes/functions will be deleted too.","editor");
		} else if(pItem->isMethod())
		{
			szMsg = QString(__tr2qs_ctx("Do you really want to remove the function \"%1\"?","editor")).arg(szName);
		}
		
		g_pClassEditorModule->lock();
		int iRet = QMessageBox::question(this,__tr2qs_ctx("Remove item","editor"),szMsg,__tr2qs_ctx("Yes","editor"),__tr2qs_ctx("Yes to All","editor"),__tr2qs_ctx("No","editor"));
		g_pClassEditorModule->unlock();
		switch(iRet)
		{
			case 0:
				// nothing
			break;
			case 1:
				*pbYesToAll = true;
			break;
			default:
				return false;
			break;
		}
	}

	if(pItem == m_pLastEditedItem)
		m_pLastEditedItem = 0;
	if(pItem == m_pLastClickedItem)
		m_pLastClickedItem = 0;
	if(pItem->childCount())
		removeItemChildren(pItem,lRemovedItems);
	if(pItem->isClass())
	{
		m_pClasses->removeRef(pItem);
		KviKvsObjectClass * pClass = KviKvsKernel::instance()->objectController()->lookupClass(buildFullClassName(pItem));
		qDebug("removing class %s %p",buildFullClassName(pItem).toUtf8().data(), pClass);
		if(pClass)
			KviKvsKernel::instance()->objectController()->deleteClass(pClass);
		else
		{
			QString szFileName = buildFullClassName(pItem);
			szFileName.replace("::","--");
			szFileName.append(KVI_FILEEXTENSION_SCRIPT);
			QString szPath;
			g_pApp->getLocalKvircDirectory(szPath,KviApplication::Classes);
			QDir d(szPath);
			if(d.exists(szFileName))
			{
				qDebug("rimuovo dal disco il file %s",szFileName.toUtf8().data());
				d.remove(szFileName);
			}
		}
	}
	if(pItem->isMethod())
	{
		updateClassHierarchy((ClassEditorTreeWidgetItem *)pItem->parent());
	}
	lRemovedItems.append(pItem);
	delete pItem;
	return true;
}

void ClassEditorWidget::updateClassHierarchy(ClassEditorTreeWidgetItem * pClass)
{
	pClass->setClassNotBuilt(true);
	KviPointerList<ClassEditorTreeWidgetItem> lInheritedClasses;
	lInheritedClasses.setAutoDelete(false);
	searchInheritedClasses(pClass->name(),lInheritedClasses);
	for(unsigned int u=0; u < lInheritedClasses.count(); u++)
	{
		lInheritedClasses.at(u)->setClassNotBuilt(true);
		lInheritedClasses.at(u)->setInheritsClass(pClass->name());
		lInheritedClasses.at(u)->setExpanded(true);
	}
}

void ClassEditorWidget::removeSelectedItems()
{
	KviPointerList<ClassEditorTreeWidgetItem> lRemovedItems;
	lRemovedItems.setAutoDelete(false);
	QList<QTreeWidgetItem *> list = m_pTreeWidget->selectedItems();
	bool bYesToAll = false;
	for(int i=0; i < list.count(); i++)
	{
		if(lRemovedItems.findRef((ClassEditorTreeWidgetItem *) list.at(i)) != -1)
			continue;
		if(!removeItem((ClassEditorTreeWidgetItem *) list.at(i),lRemovedItems,&bYesToAll))
			return;
	}
}

bool ClassEditorWidget::askForClassName(QString & szClassName, QString & szInheritsClassName, bool bEdit)
{
	KviClassEditorDialog * pDialog = new KviClassEditorDialog(this,"classdialog",m_pClasses,szClassName,szInheritsClassName,bEdit);
	szClassName = "";
	g_pClassEditorModule->lock();
	bool bOk = pDialog->exec();
	g_pClassEditorModule->unlock();
	if(bOk)
	{
		szClassName = pDialog->className();
		szInheritsClassName = pDialog->inheritsClassName();
		delete pDialog;
		return true;
	}
	delete pDialog;
	return false;
}

bool ClassEditorWidget::askForFunction(QString & szFunctionName, QString & szReminder, bool * bInternal, const QString & szClassName, bool bRenameMode)
{
	KviClassEditorFunctionDialog * pDialog = new KviClassEditorFunctionDialog(this,"function",szClassName,szFunctionName,szReminder,*bInternal, bRenameMode);
	szFunctionName = "";
	g_pClassEditorModule->lock();
	bool bOk = pDialog->exec();
	g_pClassEditorModule->unlock();
	if(bOk)
	{
		szFunctionName = pDialog->functionName();
		szReminder = pDialog->reminder();
		*bInternal = pDialog->isInternalFunction();
		delete pDialog;
		return true;
	}
	delete pDialog;
	return false;
}

bool ClassEditorWidget::askForNamespaceName(const QString & szAction, const QString & szText, const QString & szInitialText, QString & szNewName)
{
	bool bOk = false;
	while(szNewName.isEmpty())
	{
		g_pClassEditorModule->lock();
		szNewName = QInputDialog::getText(this,
			szAction,
			szText,
			QLineEdit::Normal,
			szInitialText,
			&bOk);
		g_pClassEditorModule->unlock();
		if(!bOk)
			return false;
		if(szNewName.isEmpty())
		{
			g_pClassEditorModule->lock();
			QMessageBox::warning(this,
				__tr2qs_ctx("Missing Namespace Name","editor"),
				__tr2qs_ctx("You must specify a valid name for the namespace","editor"),
				__tr2qs_ctx("Ok, Let me try again...","editor"));
			g_pClassEditorModule->unlock();
			continue;
		}

		// we allow only [\w:]+
		QRegExp re("[\\w:]+");
		if(!re.exactMatch(szNewName))
		{
			g_pClassEditorModule->lock();
			QMessageBox::information(this,
				__tr2qs_ctx("Bad Namespace Name","editor"),
				__tr2qs_ctx("Namespace names can contain only letters, digits, underscores and '::' namespace separators","editor"),
				__tr2qs_ctx("Ok, Let me try again...","editor"));
			g_pClassEditorModule->unlock();
			szNewName = "";
			continue;
		}
		// make sure that we have only doubled "::" and not ":" or ":::..."
		QString szTmp = szNewName;
		szTmp.replace("::","@"); // @ is not allowed by the rule above
		if(szTmp.indexOf(":",Qt::CaseInsensitive) != -1)
		{
			g_pClassEditorModule->lock();
			QMessageBox::information(this,
				__tr2qs_ctx("Bad Namespace Name","editor"),
				__tr2qs_ctx("Stray ':' character in namespace name: did you mean ...<namespace>::<name> ?","editor"),
				__tr2qs_ctx("Ok, Let me try again...","editor"));
			g_pClassEditorModule->unlock();
			szNewName = "";
			continue;
		}
		if(szTmp.indexOf("@@",Qt::CaseInsensitive) != -1)
		{
			g_pClassEditorModule->lock();
			QMessageBox::information(this,
				__tr2qs_ctx("Bad Namespace Name","editor"),
				__tr2qs_ctx("Found an empty namespace in namespace name","editor"),
				__tr2qs_ctx("Ok, Let me try again...","editor"));
			g_pClassEditorModule->unlock();
			szNewName = "";
			continue;
		}
	}
	return true;
}

void ClassEditorWidget::openParentItems(QTreeWidgetItem * pItem)
{
	if(pItem->parent())
	{
		pItem->parent()->setExpanded(true);
		openParentItems(pItem->parent());
	}
}

void ClassEditorWidget::activateItem(QTreeWidgetItem * pItem)
{
	if(!pItem)
		return;
	openParentItems(pItem);
	m_pTreeWidget->setCurrentItem(pItem);
}

void ClassEditorWidget::newClass()
{
	QString szClassName;
	QString szinheritsClassName;
	askForClassName(szClassName,szinheritsClassName,false);
	if(szClassName.isEmpty())
		return;
	ClassEditorTreeWidgetItem * pItem = newItem(szClassName,ClassEditorTreeWidgetItem::Class);
	
	KviQString::escapeKvs(&szClassName, KviQString::EscapeSpace);
	KviQString::escapeKvs(&szinheritsClassName, KviQString::EscapeSpace);

	QString szClass = "class(";
	szClass += szClassName + "," + szinheritsClassName;
	szClass += "){}\n";
	pItem->setInheritsClass(szinheritsClassName);
	activateItem(pItem);
	m_pClasses->insert(szClassName,pItem);
	KviKvsScript::run(szClass,g_pActiveWindow);
}

void ClassEditorWidget::newNamespace()
{
	QString szName;
	if(!askForNamespaceName(__tr2qs_ctx("Add Namespace","editor"),__tr2qs_ctx("Please enter the name for the new namespace","editor"),"mynamespace",szName))
		return;
	if(szName.isEmpty())
		return;
	ClassEditorTreeWidgetItem * pItem = newItem(szName,ClassEditorTreeWidgetItem::Namespace);
	activateItem(pItem);
}

void ClassEditorWidget::newMemberFunction()
{
	QString szFunctionName;
	QString szClassName;
	QString szReminder;
	
	if(m_pLastClickedItem->isMethod())
		m_pLastClickedItem = (ClassEditorTreeWidgetItem*)m_pLastClickedItem->parent();
	szClassName = buildFullClassName(m_pLastClickedItem);
	bool bInternal = false;
	if(!askForFunction(szFunctionName, szReminder,&bInternal,szClassName,false))
		return;
	if(szFunctionName.isEmpty())
		return;
	
	ClassEditorTreeWidgetItem * pItem = newItem(szFunctionName,ClassEditorTreeWidgetItem::Method);
	pItem->setInternalFunction(bInternal);
	if(!szReminder.isEmpty())
		pItem->setReminder(szReminder);
	activateItem(pItem);
	((ClassEditorTreeWidgetItem*)pItem->parent())->setClassNotBuilt(true);
}

ClassEditorTreeWidgetItem * ClassEditorWidget::newItem(QString & szName, ClassEditorTreeWidgetItem::Type eType)
{
	if(m_pLastClickedItem)
		buildFullItemPath(m_pLastClickedItem,szName);
	
	QString szTmp;
	if(findItem(szName))
		szName.append("1");
	
	int iIdx = 2;
	while(findItem(szName))
	{
		szTmp.setNum(iIdx);
		szName.chop(szTmp.length());
		szName.append(szTmp);
		iIdx++;
	}
	ClassEditorTreeWidgetItem * pItem;
	pItem = createFullItem(szName);
	pItem->setType(eType);
	return pItem;
}

void ClassEditorWidget::build()
{
	saveLastEditedItem();
	KviPointerHashTableIterator<QString,ClassEditorTreeWidgetItem> it (*m_pClasses);
	KviPointerList<ClassEditorTreeWidgetItem> linkedClasses;
	linkedClasses.setAutoDelete(false);
	KviPointerList<ClassEditorTreeWidgetItem> skipClasses;
	skipClasses.setAutoDelete(false);
	bool bErrorWhilecompiling = false;
	while(it.current())
	{
		ClassEditorTreeWidgetItem * pClass = it.current();
		if(skipClasses.findRef(it.current()) != -1)
		{
			++it;
			continue;
		}
		if(pClass->classNotBuilt())
		{
			//qDebug("compiling %s",pClass->name().toUtf8().data());
			ClassEditorTreeWidgetItem * pParentClass = m_pClasses->find(pClass->inheritsClass());

			linkedClasses.append(pClass);
			//if (!pParentClass) qDebug("no parent class");
			while(pParentClass)
			{
				if(pParentClass->classNotBuilt())
					linkedClasses.append(pParentClass);
				pParentClass = m_pClasses->find(pParentClass->inheritsClass());
			}
			for(int i = linkedClasses.count()-1; i >= 0; i--)
			{
				QString szFullClassName = buildFullClassName(linkedClasses.at(i));
				KviKvsObjectClass * pClass = KviKvsKernel::instance()->objectController()->lookupClass(szFullClassName);
				if(pClass)
					KviKvsKernel::instance()->objectController()->deleteClass(pClass);
				QString szClass;
				exportClassBuffer(szClass, linkedClasses.at(i));
				KviKvsScript::run(szClass,g_pActiveWindow);
				pClass = KviKvsKernel::instance()->objectController()->lookupClass(szFullClassName);
				if(!pClass)
				{
					bErrorWhilecompiling = true;
					QString szError = __tr2qs_ctx("Unable to compile class: ","editor");
					szError += szFullClassName + "\n";
					KviPointerList<ClassEditorTreeWidgetItem> lInheritedClasses;
					lInheritedClasses.setAutoDelete(false);
					searchInheritedClasses(szFullClassName,lInheritedClasses);
					if(lInheritedClasses.count())
					{
						szError += __tr2qs_ctx("These inherited classes will be not compiled too:","editor");
						szError += "\n";
						for(unsigned int u=0; u < lInheritedClasses.count(); u++)
						{
							szError += buildFullClassName(lInheritedClasses.at(u)) + "\n";
							lInheritedClasses.at(u)->setClassNotBuilt(true);
							skipClasses.append(lInheritedClasses.at(u));
						}
					}
					QMessageBox::critical(this,__tr2qs_ctx("Compilation error - KVIrc","editor"),szError,
					QMessageBox::Ok,QMessageBox::NoButton,QMessageBox::NoButton);
					break;
				}
				//else qDebug("class compiled %s :\n",szClass.toUtf8().data());
				linkedClasses.at(i)->setClassNotBuilt(false);
				m_pEditor->setModified(false);
			}
		}
		++it;
	}
	if(bErrorWhilecompiling)
		saveNotBuiltClasses();
	else {
		QString szFileName = "libkviclasseditortmp.kvc";
		QString szBuffer;
		g_pApp->getLocalKvircDirectory(szBuffer,KviApplication::ConfigPlugins,szFileName);
		KviConfigurationFile cfg(szBuffer,KviConfigurationFile::Write);
		cfg.clear();
		cfg.sync();
	}
	KviKvsKernel::instance()->objectController()->flushUserClasses();
}

void ClassEditorWidget::loadNotBuiltClasses()
{
	QString szFileName = "libkviclasseditortmp.kvc";
	QString szBuffer;
	g_pApp->getLocalKvircDirectory(szBuffer,KviApplication::ConfigPlugins,szFileName);
	KviConfigurationFile cfg(szBuffer,KviConfigurationFile::Read);
	KviConfigurationFileIterator it(*(cfg.dict()));

	KviPointerList<QString> l;
	l.setAutoDelete(true);

	while(it.current())
	{
		l.append(new QString(it.currentKey()));
		++it;
	}

	for(QString * pszTmp = l.first(); pszTmp; pszTmp = l.next())
	{
		cfg.setGroup(*pszTmp);
		ClassEditorTreeWidgetItem * pClassItem = createFullItem(*pszTmp);
		m_pClasses->insert(*pszTmp,pClassItem);
		pClassItem->setClassNotBuilt(true);
		KviConfigurationFileGroup * pDict = cfg.dict()->find(*pszTmp);
		if(pDict)
		{
			KviConfigurationFileGroupIterator it(*pDict);
			KviPointerList<QString> names;
			names.setAutoDelete(true);
			while(it.current())
			{
				names.append(new QString(it.currentKey()));
				++it;
			}
			for(QString * pszTmp = names.first(); pszTmp; pszTmp = names.next())
			{

				if(KviQString::equalCI(*pszTmp,"@Inherits"))
				{
					pClassItem->setInheritsClass(cfg.readEntry(*pszTmp,""));
					continue;
				}
                                if((*pszTmp).left(9) == "@Reminder")
					continue;
                                QString szCode = cfg.readEntry(*pszTmp,"");
				ClassEditorTreeWidgetItem * pFunctionItem = findFunction(*pszTmp, pClassItem);
				if(!pFunctionItem)
					pFunctionItem = new ClassEditorTreeWidgetItem(pClassItem,ClassEditorTreeWidgetItem::Method,*pszTmp);
				pFunctionItem->setBuffer(szCode);
                                QString szEntry = "@Reminder|" + (*pszTmp);
                                QString szReminder = cfg.readEntry(szEntry,"");
                                pFunctionItem->setReminder(szReminder);
			}
		}
	}
}

void ClassEditorWidget::saveNotBuiltClasses()
{
	saveLastEditedItem();
	KviPointerHashTableIterator<QString,ClassEditorTreeWidgetItem> it (*m_pClasses);
	QString szFileName = "libkviclasseditortmp.kvc";
	QString szBuffer;
	g_pApp->getLocalKvircDirectory(szBuffer,KviApplication::ConfigPlugins,szFileName);
	KviConfigurationFile cfg(szBuffer,KviConfigurationFile::Write);
	cfg.clear();

	while(it.current())
	{
		if(it.current()->classNotBuilt())
		{
			KviKvsObjectClass * pClass = KviKvsKernel::instance()->objectController()->lookupClass(it.currentKey());
			if(pClass)
				KviKvsKernel::instance()->objectController()->deleteClass(pClass);

			cfg.setGroup(it.currentKey());
			cfg.writeEntry("@Inherits",it.current()->inheritsClass());
			QString szReminderEntry;
			for(int i=0; i < it.current()->childCount(); i++)
			{
				if(!((ClassEditorTreeWidgetItem *)it.current()->child(i))->reminder().isEmpty())
				{
					szReminderEntry="@Reminder|"+((ClassEditorTreeWidgetItem *)it.current()->child(i))->name();
					cfg.writeEntry(szReminderEntry,((ClassEditorTreeWidgetItem *)it.current()->child(i))->reminder());
				}
				cfg.writeEntry(((ClassEditorTreeWidgetItem *)it.current()->child(i))->name(),((ClassEditorTreeWidgetItem *)it.current()->child(i))->buffer());
			}
		}
		++it;
	}
	cfg.sync();
}

void ClassEditorWidget::searchInheritedClasses(const QString szClass, KviPointerList<ClassEditorTreeWidgetItem> & lInheritedClasses)
{
	KviPointerHashTableIterator<QString,ClassEditorTreeWidgetItem> it (*m_pClasses);
	while(it.current())
	{
		if(KviQString::equalCI(szClass,it.current()->inheritsClass()))
			lInheritedClasses.append(it.current());
		++it;
	}
}

ClassEditorWindow::ClassEditorWindow(KviMainWindow * pFrm)
: KviWindow(KviWindow::ScriptEditor,pFrm,"classeditor",0)
{
	g_pClassEditorWindow = this;

	setFixedCaption(__tr2qs_ctx("Class Editor","editor"));

	QGridLayout * pLayout = new QGridLayout();

	m_pEditor = new ClassEditorWidget(this);
	pLayout->addWidget(m_pEditor,0,0,1,4);

	QPushButton * pBtn = new QPushButton(__tr2qs_ctx("&Build","editor"),this);
	pBtn->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Accept)));
	pLayout->addWidget(pBtn,1,1);
	connect(pBtn,SIGNAL(clicked()),this,SLOT(buildClicked()));

	pBtn = new QPushButton(__tr2qs_ctx("&Save","editor"),this);
	pBtn->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Accept)));
	pLayout->addWidget(pBtn,1,2);
	connect(pBtn,SIGNAL(clicked()),this,SLOT(saveClicked()));

	pBtn = new QPushButton(__tr2qs_ctx("Close","editor"),this);
	pBtn->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Discard)));
	pLayout->addWidget(pBtn,1,3);
	connect(pBtn,SIGNAL(clicked()),this,SLOT(cancelClicked()));

	pLayout->setRowStretch(0,1);
	pLayout->setColumnStretch(0,1);
	setLayout(pLayout);
}

ClassEditorWindow::~ClassEditorWindow()
{
	g_pClassEditorWindow = 0;
}

void ClassEditorWindow::buildClicked()
{
	m_pEditor->build();
}

void ClassEditorWindow::saveClicked()
{
	m_pEditor->saveNotBuiltClasses();
}

void ClassEditorWindow::cancelClicked()
{
	close();
}

QPixmap * ClassEditorWindow::myIconPtr()
{
	return g_pIconManager->getSmallIcon(KviIconManager::ClassEditor);
}

void ClassEditorWindow::configGroupName(QString &szName)
{
	szName = "classeditor";
}

void ClassEditorWindow::saveProperties(KviConfigurationFile * pCfg)
{
	m_pEditor->saveProperties(pCfg);
}

void ClassEditorWindow::loadProperties(KviConfigurationFile * pCfg)
{
	m_pEditor->loadProperties(pCfg);
}

KviClassEditorDialog::KviClassEditorDialog(QWidget * pParent, const QString & szName, KviPointerHashTable<QString,ClassEditorTreeWidgetItem> * pClasses, const QString & szClassName, const QString & szInheritsClassName, bool bRenameMode)
: QDialog(pParent)
{
	setObjectName(szName);

	QGridLayout * pLayout = new QGridLayout(this);

	KviTalHBox * pHBox = new KviTalHBox(this);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	pLayout->addWidget(pHBox,0,0);

	QLabel * pLabel = new QLabel(pHBox);
	pLabel->setObjectName("classnamelabel");
	pLabel->setText(__tr2qs_ctx("Please enter the name for the class:","editor"));

	m_pClassNameLineEdit = new QLineEdit(pHBox);
	m_pClassNameLineEdit->setObjectName("classnameineedit");
	m_pClassNameLineEdit->setText(szClassName);
	pLabel->setBuddy(m_pClassNameLineEdit);
	QRegExp re;
	if(!bRenameMode)
	{
		// we allow only [\w:]+ class name
		re.setPattern("[\\w]+");
		m_pClassNameLineEdit->setToolTip(__tr2qs_ctx("Class names can contain only letters, digits and underscores","editor"));
	} else {
		// in editor mode we allow [\w:]+ and namespace separator
		re.setPattern("[\\w]+(::[\\w]+)+");
		m_pClassNameLineEdit->setToolTip(__tr2qs_ctx("In rename mode class names can contain only letters, digits and underscores and namespaces :: separator","editor"));
	}
	QRegExpValidator * pValidator = new QRegExpValidator(re,this);
	m_pClassNameLineEdit->setValidator(pValidator);
	m_pClassNameLineEdit->setObjectName("functionameineedit");
	
	pHBox = new KviTalHBox(this);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	pLayout->addWidget(pHBox,1,0);

	pLabel = new QLabel(pHBox);
	pLabel->setObjectName("Inheritsclasslabel");
	pLabel->setText(__tr2qs_ctx("Inherits Class:","editor"));

	m_pInheritsClassComboBox = new QComboBox(pHBox);
	pLabel->setBuddy(m_pInheritsClassComboBox);

	KviPointerHashTableIterator<QString,ClassEditorTreeWidgetItem> pClassesIt(*pClasses);

	QStringList szClasses;
	while(pClassesIt.current())
	{
		if(!KviQString::equalCI(pClassesIt.currentKey(),szClassName))
			szClasses.append(pClassesIt.currentKey());
		++pClassesIt;
	}
	KviPointerHashTableIterator<QString,KviKvsObjectClass> pBuiltinClasses(*KviKvsKernel::instance()->objectController()->classDict());
	while(pBuiltinClasses.current())
	{
		if(pBuiltinClasses.current()->isBuiltin())
			szClasses.append(pBuiltinClasses.currentKey());
		++pBuiltinClasses;
	}
	
	int iCurrentIdx;
	szClasses.sort();
	for(int i=0; i < szClasses.count(); i++)
		m_pInheritsClassComboBox->addItem(szClasses.at(i));
	if(szInheritsClassName.isEmpty())
		iCurrentIdx = m_pInheritsClassComboBox->findText("object");
	else {
		iCurrentIdx = m_pInheritsClassComboBox->findText(szInheritsClassName);
		if(iCurrentIdx == -1)
			iCurrentIdx = m_pInheritsClassComboBox->findText("object");
	}
	m_pInheritsClassComboBox->setCurrentIndex(iCurrentIdx);
	m_pClassNameLineEdit->setFocus();
	connect(m_pClassNameLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));

	pHBox = new KviTalHBox(this);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	pLayout->addWidget(pHBox,2,0);

	m_pNewClassButton = new QPushButton(pHBox);
	m_pNewClassButton->setObjectName("newclassbutton");
	if(bRenameMode)
		m_pNewClassButton->setText(__tr2qs_ctx("&Rename Class","editor"));
	else {
		m_pNewClassButton->setText(__tr2qs_ctx("&Create Class","editor"));
		m_pNewClassButton->setEnabled(false);
	}
	connect(m_pNewClassButton, SIGNAL(clicked()), this, SLOT(accept()));

	QPushButton * pCancelButton = new QPushButton(pHBox);
	pCancelButton->setObjectName("cancelButton");
	pCancelButton->setText(__tr2qs_ctx("&Cancel","editor"));
	connect(pCancelButton, SIGNAL(clicked()), this, SLOT(reject()));
	
	setLayout(pLayout);
}

KviClassEditorDialog::~KviClassEditorDialog()
{
}

void KviClassEditorDialog::textChanged(const QString & szText)
{
	m_pNewClassButton->setEnabled(!szText.isEmpty());
}

KviClassEditorFunctionDialog::KviClassEditorFunctionDialog(QWidget * pParent, const QString & szName, const QString & szClassName, const QString & szFunctionName, const QString & szReminder, bool bIsInternal, bool bRenameMode)
: QDialog(pParent)
{
	setObjectName(szName);

	QGridLayout * pLayout = new QGridLayout(this);

	KviTalHBox * pHBox = new KviTalHBox(this);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	pLayout->addWidget(pHBox,0,0);

	QLabel * pLabel = new QLabel(pHBox);
	pLabel->setObjectName("classnamelabel");
	pLabel->setText("Class: <b>"+szClassName+"</b>");

	pHBox = new KviTalHBox(this);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	pLayout->addWidget(pHBox,1,0);

	pLabel = new QLabel(pHBox);
	pLabel->setObjectName("functionnamelabel");
	pLabel->setText(__tr2qs_ctx("Please enter the name for the member function:","editor"));

	m_pFunctionNameLineEdit = new QLineEdit(pHBox);

	// we allow only [\w:]+ function name
	QRegExp re("[\\w]+");
	QRegExpValidator * pValidator = new QRegExpValidator(re,this);
	m_pFunctionNameLineEdit->setValidator(pValidator);
	m_pFunctionNameLineEdit->setObjectName("functionameineedit");
	m_pFunctionNameLineEdit->setToolTip(__tr2qs_ctx("Function names can contain only letters, digits and underscores","editor"));
	m_pFunctionNameLineEdit->setText(szFunctionName);
	pLabel->setBuddy(m_pFunctionNameLineEdit);

	pHBox = new KviTalHBox(this);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	pLayout->addWidget(pHBox,2,0);

	pLabel = new QLabel(pHBox);
	pLabel->setObjectName("reminderlabel");
	pLabel->setWordWrap(1);
	pLabel->setText(__tr2qs_ctx("Please enter the optional reminder string for the member function:","editor"));

	m_pReminderLineEdit = new QLineEdit(pHBox);
	m_pReminderLineEdit->setText(szReminder);
	pLabel->setBuddy(m_pReminderLineEdit);

	pHBox = new KviTalHBox(this);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	pLayout->addWidget(pHBox,3,0);

	pLabel = new QLabel(pHBox);
	pLabel->setObjectName("functionnamelabel");
	pLabel->setText(__tr2qs_ctx("Set as <b>Internal</b> Function: ","editor"));

	m_pInternalCheckBox = new QCheckBox(pHBox);
	m_pInternalCheckBox->setChecked(bIsInternal);
	m_pFunctionNameLineEdit->setFocus();
	pLabel->setBuddy(m_pInternalCheckBox);
	connect(m_pFunctionNameLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
	
	pHBox->setAlignment(m_pInternalCheckBox,Qt::AlignLeft);
	pHBox->setStretchFactor(m_pInternalCheckBox,70);
	pHBox->setStretchFactor(pLabel,30);

	pHBox = new KviTalHBox(this);
	pHBox->setSpacing(0);
	pHBox->setMargin(0);
	pLayout->addWidget(pHBox,4,0);

	m_pNewFunctionButton = new QPushButton(pHBox);
	m_pNewFunctionButton->setObjectName("newfunctionbutton");
	if(!bRenameMode)
		m_pNewFunctionButton->setText(__tr2qs_ctx("&Add","editor"));
	else
		m_pNewFunctionButton->setText(__tr2qs_ctx("&Rename","editor"));
	
	if(szFunctionName.isEmpty())
		m_pNewFunctionButton->setEnabled(false);
	connect(m_pNewFunctionButton, SIGNAL(clicked()), this, SLOT(accept()));

	QPushButton * pCancelButton = new QPushButton(pHBox);
	pCancelButton->setObjectName("cancelButton");
	pCancelButton->setText(__tr2qs_ctx("&Cancel","editor"));
	connect(pCancelButton, SIGNAL(clicked()), this, SLOT(reject()));

	setLayout(pLayout);
}

KviClassEditorFunctionDialog::~KviClassEditorFunctionDialog()
{
}

void KviClassEditorFunctionDialog::textChanged(const QString & szText)
{
	m_pNewFunctionButton->setEnabled(!szText.isEmpty());
}
