/* This file is part of the KDE project
   Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "kexitabledesignerview.h"
#include "kexitabledesignerview_p.h"
#include "kexilookupcolumnpage.h"
#include "kexitabledesignercommands.h"

#include <tqlayout.h>
#include <tqlabel.h>
#include <tqsplitter.h>

#include <kiconloader.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <tdeaction.h>
#include <tdepopupmenu.h>
#include <tdemessagebox.h>
#include <kiconeffect.h>

#include <koproperty/set.h>
#include <koproperty/utils.h>

#include <kexidb/cursor.h>
#include <kexidb/tableschema.h>
#include <kexidb/connection.h>
#include <kexidb/utils.h>
#include <kexidb/roweditbuffer.h>
#include <kexidb/error.h>
#include <kexidb/lookupfieldschema.h>
#include <kexiutils/identifier.h>
#include <kexiproject.h>
#include <keximainwindow.h>
#include <widget/tableview/kexidataawarepropertyset.h>
#include <widget/kexicustompropertyfactory.h>
#include <kexiutils/utils.h>
#include <kexidialogbase.h>
#include <kexitableview.h>

//#define MAX_FIELDS 101 //nice prime number

//! used only for BLOBs
#define DEFAULT_OBJECT_TYPE_VALUE "image"

//#define KexiTableDesignerView_DEBUG

//! @todo remove this when BLOBs are implemented
//#define KEXI_NO_BLOB_FIELDS

using namespace KexiTableDesignerCommands;

//! @internal Used in tryCastTQVariant() anf canCastTQVariant()
static bool isIntegerTQVariant(TQVariant::Type t)
{
	return t==TQVariant::LongLong 
		|| t==TQVariant::ULongLong
		|| t==TQVariant::Int
		|| t==TQVariant::UInt;
}

//! @internal Used in tryCastTQVariant()
static bool canCastTQVariant(TQVariant::Type fromType, TQVariant::Type toType)
{
	return (fromType==TQVariant::Int && toType==TQVariant::UInt)
   || (fromType==TQVariant::CString && toType==TQVariant::String)
   || (fromType==TQVariant::LongLong && toType==TQVariant::ULongLong)
   || ((fromType==TQVariant::String || fromType==TQVariant::CString) 
	      && (isIntegerTQVariant(toType) || toType==TQVariant::Double));
}

/*! @internal 
 \return a variant value converted from \a fromVal to \a toType type.
 Null TQVariant is returned if \a fromVal's type and \a toType type 
 are incompatible. */
static TQVariant tryCastTQVariant( const TQVariant& fromVal, TQVariant::Type toType )
{
	const TQVariant::Type fromType = fromVal.type();
	if (fromType == toType)
		return fromVal;
	if (canCastTQVariant(fromType, toType) || canCastTQVariant(toType, fromType)
	    || (isIntegerTQVariant(fromType) && toType==TQVariant::Double))
	{
		TQVariant res( fromVal );
		if (res.cast(toType))
			return res;
	}
	return TQVariant();
}


KexiTableDesignerView::KexiTableDesignerView(KexiMainWindow *win, TQWidget *parent)
 : KexiDataTable(win, parent, "KexiTableDesignerView", false/*not db-aware*/)
 , KexiTableDesignerInterface()
 , d( new KexiTableDesignerViewPrivate(this) )
{
	//needed for custom "identifier" property editor widget
	KexiCustomPropertyFactory::init();

	KexiDB::Connection *conn = mainWin()->project()->dbConnection();
	d->view = dynamic_cast<KexiTableView*>(mainWidget());

	d->data = new KexiTableViewData();
	if (conn->isReadOnly())
		d->data->setReadOnly(true);
	d->data->setInsertingEnabled( false );

	KexiTableViewColumn *col = new KexiTableViewColumn("pk", KexiDB::Field::Text, TQString(),
		i18n("Additional information about the field"));
	col->setIcon( KexiUtils::colorizeIconToTextColor( SmallIcon("application-vnd.tde.info"), d->view->palette() ) );
	col->setHeaderTextVisible(false);
	col->field()->setSubType("TDEIcon");
	col->setReadOnly(true);
	d->data->addColumn( col );

//	col = new KexiTableViewColumn("name", KexiDB::Field::Text, i18n("Field Name"),
	col = new KexiTableViewColumn("caption", KexiDB::Field::Text, i18n("Field Caption"),
		i18n("Describes caption for the field"));
//	KexiUtils::Validator *vd = new KexiUtils::IdentifierValidator();
//	vd->setAcceptsEmptyValue(true);
//	col->setValidator( vd );
	d->data->addColumn( col );

	col = new KexiTableViewColumn("type", KexiDB::Field::Enum, i18n("Data Type"),
		i18n("Describes data type for the field"));
	d->data->addColumn( col );

#ifdef KEXI_NO_BLOB_FIELDS
//! @todo remove this later
	TQValueVector<TQString> types(KexiDB::Field::LastTypeGroup-1); //don't show last type (BLOB)
#else
	TQValueVector<TQString> types(KexiDB::Field::LastTypeGroup);
#endif
	d->maxTypeNameTextWidth = 0;
	TQFontMetrics fm(font());
	for (uint i=1; i<=types.count(); i++) {
		types[i-1] = KexiDB::Field::typeGroupName(i);
		d->maxTypeNameTextWidth = TQMAX(d->maxTypeNameTextWidth, fm.width(types[i-1]));
	}
	col->field()->setEnumHints(types);

	d->data->addColumn( col = new KexiTableViewColumn("comments", KexiDB::Field::Text, i18n("Comments"),
		i18n("Describes additional comments for the field")) );

	d->view->setSpreadSheetMode();

	connect(d->data, TQ_SIGNAL(aboutToChangeCell(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)),
		this, TQ_SLOT(slotBeforeCellChanged(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)));
	connect(d->data, TQ_SIGNAL(rowUpdated(KexiTableItem*)),
		this, TQ_SLOT(slotRowUpdated(KexiTableItem*)));
	//connect(d->data, TQ_SIGNAL(aboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)),
	//	this, TQ_SLOT(slotAboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)));
	connect(d->data, TQ_SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
		this, TQ_SLOT(slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)));

	setMinimumSize(d->view->minimumSizeHint().width(), d->view->minimumSizeHint().height());
	d->view->setFocus();

	d->sets = new KexiDataAwarePropertySet( this, d->view );
	connect(d->sets, TQ_SIGNAL(rowDeleted()), this, TQ_SLOT(updateActions()));
	connect(d->sets, TQ_SIGNAL(rowInserted()), this, TQ_SLOT(slotRowInserted()));

	d->contextMenuTitle = new TDEPopupTitle(d->view->contextMenu());
	d->view->contextMenu()->insertItem(d->contextMenuTitle, -1, 0);
	connect(d->view->contextMenu(), TQ_SIGNAL(aboutToShow()), this, TQ_SLOT(slotAboutToShowContextMenu()));

	plugSharedAction("tablepart_toggle_pkey", this, TQ_SLOT(slotTogglePrimaryKey()));
	d->action_toggle_pkey = static_cast<TDEToggleAction*>( sharedAction("tablepart_toggle_pkey") );
	d->action_toggle_pkey->plug(d->view->contextMenu(), 1); //add at the beginning
	d->view->contextMenu()->insertSeparator(2);
	setAvailable("tablepart_toggle_pkey", !conn->isReadOnly());

#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
	plugSharedAction("edit_undo", this, TQ_SLOT(slotUndo()));
	plugSharedAction("edit_redo", this, TQ_SLOT(slotRedo()));
	setAvailable("edit_undo", false);
	setAvailable("edit_redo", false);
	connect(d->history, TQ_SIGNAL(commandExecuted(KCommand*)), this, TQ_SLOT(slotCommandExecuted(KCommand*)));
#endif

#ifdef KEXI_DEBUG_GUI
	KexiUtils::addAlterTableActionDebug(TQString()); //to create the tab
	KexiUtils::connectPushButtonActionForDebugWindow( 
		"simulateAlterTableExecution", this, TQ_SLOT(slotSimulateAlterTableExecution()));
	KexiUtils::connectPushButtonActionForDebugWindow( 
		"executeRealAlterTable", this, TQ_SLOT(executeRealAlterTable()));
#endif
}

KexiTableDesignerView::~KexiTableDesignerView()
{
//	removeCurrentPropertySet();
	delete d;
}

void KexiTableDesignerView::initData()
{
	//add column data
//	d->data->clear();
	d->data->deleteAllRows();
	int tableFieldCount = 0;
	d->primaryKeyExists = false;

	if (tempData()->table) {
		tableFieldCount = tempData()->table->fieldCount();
//not needed		d->sets->clear(tableFieldCount);

		//recreate table data rows
		for(int i=0; i < tableFieldCount; i++) {
			KexiDB::Field *field = tempData()->table->field(i);
			KexiTableItem *item = d->data->createItem(); //new KexiTableItem(0);
			if (field->isPrimaryKey()) {
				(*item)[COLUMN_ID_ICON] = "key";
				d->primaryKeyExists = true;
			}
			else {
				KexiDB::LookupFieldSchema *lookupFieldSchema
					= field->table() ? field->table()->lookupFieldSchema(*field) : 0;
				if (lookupFieldSchema && lookupFieldSchema->rowSource().type()!=KexiDB::LookupFieldSchema::RowSource::NoType
					&& !lookupFieldSchema->rowSource().name().isEmpty())
				{
					(*item)[COLUMN_ID_ICON] = "combo";
				}
			}
			(*item)[COLUMN_ID_CAPTION] = field->captionOrName();
			(*item)[COLUMN_ID_TYPE] = field->typeGroup()-1; //-1 because type groups are counted from 1
			(*item)[COLUMN_ID_DESC] = field->description();
			d->data->append(item);

//later!			createPropertySet( i, field );
		}
	}
//	else {
//		d->sets->clear();//default size
//	}

	//add empty space
//	const int columnsCount = d->data->columnsCount();
	for (int i=tableFieldCount; i<(int)d->sets->size(); i++) {
//		KexiTableItem *item = new KexiTableItem(columnsCount);//3 empty fields
		d->data->append(d->data->createItem());
	}

	//set data for our spreadsheet: this will clear our sets
	d->view->setData(d->data);

	//now recreate property sets
	if (tempData()->table) {
		for(int i=0; i < tableFieldCount; i++) {
			KexiDB::Field *field = tempData()->table->field(i);
			createPropertySet( i, *field );
		}
	}

	//column widths
	d->view->setColumnWidth(COLUMN_ID_ICON, IconSize( TDEIcon::Small ) + 10);
	d->view->adjustColumnWidthToContents(COLUMN_ID_CAPTION); //adjust column width
	d->view->setColumnWidth(COLUMN_ID_TYPE, d->maxTypeNameTextWidth + 2 * d->view->rowHeight());
	d->view->setColumnStretchEnabled( true, COLUMN_ID_DESC ); //last column occupies the rest of the area
	const int minCaptionColumnWidth = d->view->fontMetrics().width("wwwwwwwwwww");
	if (minCaptionColumnWidth > d->view->columnWidth(COLUMN_ID_CAPTION))
		d->view->setColumnWidth(COLUMN_ID_CAPTION, minCaptionColumnWidth);

	setDirty(false);
	d->view->setCursorPosition(0, COLUMN_ID_CAPTION); //set @ name column
	propertySetSwitched();
}

//! Gets subtype strings and names for type \a fieldType
void
KexiTableDesignerView::getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup, 
	TQStringList& stringsList, TQStringList& namesList)
{
/* disabled - "mime" is moved from subType to "objectType" custom property
	if (fieldTypeGroup==KexiDB::Field::BLOBGroup) {
		// special case: BLOB type uses "mime-based" subtypes
//! @todo hardcoded!
		stringsList << "image";
		namesList << i18n("Image object type", "Image");
	}
	else {*/
	stringsList = KexiDB::typeStringsForGroup(fieldTypeGroup);
	namesList = KexiDB::typeNamesForGroup(fieldTypeGroup);
//	}
	kexipluginsdbg << "KexiTableDesignerView::getSubTypeListData(): subType strings: " << 
		stringsList.join("|") << "\nnames: " << namesList.join("|") << endl;
}

KoProperty::Set *
KexiTableDesignerView::createPropertySet( int row, const KexiDB::Field& field, bool newOne )
{
	TQString typeName = "KexiDB::Field::" + field.typeGroupString();
	KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
	if (mainWin()->project()->dbConnection()->isReadOnly())
		set->setReadOnly( true );
//	connect(buff,TQ_SIGNAL(propertyChanged(KexiPropertyBuffer&,KexiProperty&)),
//		this, TQ_SLOT(slotPropertyChanged(KexiPropertyBuffer&,KexiProperty&)));

	KoProperty::Property *prop;

	set->addProperty(prop = new KoProperty::Property("uid", d->generateUniqueId(), ""));
	prop->setVisible(false);

	//meta-info for property editor
	set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Table field")) );
	prop->setVisible(false);
	set->addProperty(prop = new KoProperty::Property("this:iconName", 
//! \todo add table_field icon 
		"lineedit" //"table_field"
	));
	prop->setVisible(false);
	set->addProperty(prop = new KoProperty::Property("this:useCaptionAsObjectName", 
		TQVariant(true), TQString())); //we want "caption" to be displayed in the header, not name
	prop->setVisible(false);

	//name
	set->addProperty(prop 
		= new KoProperty::Property("name", TQVariant(field.name()), i18n("Name"), 
		TQString(), KexiCustomPropertyFactory::Identifier) );

	//type
	set->addProperty( prop 
		= new KoProperty::Property("type", TQVariant(field.type()), i18n("Type")) );
#ifndef KexiTableDesignerView_DEBUG
	prop->setVisible(false);//always hidden
#endif

	//subtype
	TQStringList typeStringList, typeNameList;
	getSubTypeListData(field.typeGroup(), typeStringList, typeNameList);
/* disabled - "mime" is moved from subType to "objectType" custom property
	TQString subTypeValue;
	if (field.typeGroup()==KexiDB::Field::BLOBGroup) {
// special case: BLOB type uses "mime-based" subtypes
//! @todo this should be retrieved from KexiDB::Field when BLOB supports many different mimetypes
		subTypeValue = slist.first();
	}
	else {*/
	TQString subTypeValue = field.typeString();
	//}
	set->addProperty(prop = new KoProperty::Property("subType", 
		typeStringList, typeNameList, subTypeValue, i18n("Subtype")));

	// objectType
	TQStringList objectTypeStringList, objectTypeNameList;
//! @todo this should be retrieved from KexiDB::Field when BLOB supports many different mimetypes
	objectTypeStringList << "image";
	objectTypeNameList << i18n("Image object type", "Image");
	TQString objectTypeValue( field.customProperty("objectType").toString() );
	if (objectTypeValue.isEmpty())
		objectTypeValue = DEFAULT_OBJECT_TYPE_VALUE;
	set->addProperty(prop = new KoProperty::Property("objectType", 
		objectTypeStringList, objectTypeNameList, objectTypeValue, i18n("Subtype")/*todo other i18n string?*/));
	
	set->addProperty( prop 
		= new KoProperty::Property("caption", TQVariant(field.caption()), i18n("Caption") ) );
	prop->setVisible(false);//always hidden

	set->addProperty( prop 
		= new KoProperty::Property("description", TQVariant(field.description())) );
	prop->setVisible(false);//always hidden

	set->addProperty(prop 
		= new KoProperty::Property("unsigned", TQVariant(field.isUnsigned()), i18n("Unsigned Number")));

	set->addProperty( prop 
		= new KoProperty::Property("length", (int)field.length()/*200?*/, i18n("Length")));

	set->addProperty( prop 
		= new KoProperty::Property("precision", (int)field.precision()/*200?*/, i18n("Precision")));
#ifdef KEXI_NO_UNFINISHED
	prop->setVisible(false);
#endif
	set->addProperty( prop 
		= new KoProperty::Property("visibleDecimalPlaces", field.visibleDecimalPlaces(), i18n("Visible Decimal Places")));
	prop->setOption("min", -1);
	prop->setOption("minValueText", i18n("Auto Decimal Places","Auto"));

//! @todo set reasonable default for column width
	set->addProperty( prop 
		= new KoProperty::Property("width", (int)field.width()/*200?*/, i18n("Column Width")));
#ifdef KEXI_NO_UNFINISHED
	prop->setVisible(false);
#endif

	set->addProperty( prop 
		= new KoProperty::Property("defaultValue", field.defaultValue(), i18n("Default Value"),
			TQString(), 
//! @todo use "Variant" type here when supported by KoProperty
			(KoProperty::PropertyType)field.variantType()) );
	prop->setOption("3rdState", i18n("None"));
//	prop->setVisible(false);

	set->addProperty( prop 
		= new KoProperty::Property("primaryKey", TQVariant(field.isPrimaryKey()), i18n("Primary Key")));
	prop->setIcon("key");

	set->addProperty( prop
		= new KoProperty::Property("unique", TQVariant(field.isUniqueKey()), i18n("Unique")));

	set->addProperty( prop
		= new KoProperty::Property("notNull", TQVariant(field.isNotNull()), i18n("Required")));
	
	set->addProperty( prop
		= new KoProperty::Property("allowEmpty", TQVariant(!field.isNotEmpty()), i18n("Allow Zero\nSize")));

	set->addProperty( prop
		= new KoProperty::Property("autoIncrement", TQVariant(field.isAutoIncrement()), i18n("Autonumber")));
	prop->setIcon("autonumber");

	set->addProperty( prop
		= new KoProperty::Property("indexed", TQVariant(field.isIndexed()), i18n("Indexed")));

	//- properties related to lookup columns (used and set by the "lookup column" tab in the property pane)
	KexiDB::LookupFieldSchema *lookupFieldSchema = field.table() ? field.table()->lookupFieldSchema(field) : 0;
	set->addProperty( prop = new KoProperty::Property("rowSource", 
		lookupFieldSchema ? lookupFieldSchema->rowSource().name() : TQString(), i18n("Row Source")));
	prop->setVisible(false);

	set->addProperty( prop = new KoProperty::Property("rowSourceType", 
		lookupFieldSchema ? lookupFieldSchema->rowSource().typeName() : TQString(), i18n("Row Source\nType")));
	prop->setVisible(false);

	set->addProperty( prop
		= new KoProperty::Property("boundColumn", 
		lookupFieldSchema ? lookupFieldSchema->boundColumn() : -1, i18n("Bound Column")));
	prop->setVisible(false);
	
//! @todo this is backward-compatible code for "single visible column" implementation
//!       for multiple columns, only the first is displayed, so there is a data loss is GUI is used
//!       -- special koproperty editor needed
	int visibleColumn = -1;
	if (lookupFieldSchema && !lookupFieldSchema->visibleColumns().isEmpty())
		visibleColumn = lookupFieldSchema->visibleColumns().first();
	set->addProperty( prop
		= new KoProperty::Property("visibleColumn", visibleColumn, i18n("Visible Column")));
	prop->setVisible(false);

//! @todo support columnWidths(), columnHeadersVisible(), maximumListRows(), limitToList(), displayWidget()

	//----
	d->updatePropertiesVisibility(field.type(), *set);

	connect(set, TQ_SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
		this, TQ_SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));

	d->sets->insert(row, set, newOne);
	return set;
}

void KexiTableDesignerView::updateActions(bool activated)
{
	Q_UNUSED(activated);
/*! \todo check if we can set pkey for this column type (eg. BLOB?) */
	setAvailable("tablepart_toggle_pkey", propertySet()!=0 && !mainWin()->project()->dbConnection()->isReadOnly());
	if (!propertySet())
		return;
	KoProperty::Set &set = *propertySet();
	d->slotTogglePrimaryKeyCalled = true;
	 d->action_toggle_pkey->setChecked(set["primaryKey"].value().toBool());
	d->slotTogglePrimaryKeyCalled = false;
}

void KexiTableDesignerView::slotUpdateRowActions(int row)
{
	KexiDataTable::slotUpdateRowActions(row);
	updateActions();
}

void KexiTableDesignerView::slotTogglePrimaryKey()
{
	if (d->slotTogglePrimaryKeyCalled)
		return;
	d->slotTogglePrimaryKeyCalled = true;
	if (!propertySet())
		return;
	KoProperty::Set &set = *propertySet();
	bool isSet = !set["primaryKey"].value().toBool();
	set.changeProperty("primaryKey", TQVariant(isSet)); //this will update all related properties as well
/*	CommandGroup *setPrimaryKeyCommand;
	if (isSet) {
		setPrimaryKeyCommand = new CommandGroup(i18n("Set primary key for field \"%1\"")
			.arg(set["name"].value().toString()) );
	}
	else {
		setPrimaryKeyCommand = new CommandGroup(i18n("Unset primary key for field \"%1\"")
			.arg(set["name"].value().toString()) );
	}
	switchPrimaryKey(set, isSet, false, setPrimaryKeyCommand);*/
	//addHistoryCommand( setPrimaryKeyCommand, false /* !execute */ );
	d->slotTogglePrimaryKeyCalled = false;
}

void KexiTableDesignerView::switchPrimaryKey(KoProperty::Set &propertySet, 
	bool set, bool aWasPKey, CommandGroup* commandGroup)
{
	const bool was_pkey = aWasPKey || propertySet["primaryKey"].value().toBool();
//	propertySet["primaryKey"] = TQVariant(set);
	d->setPropertyValueIfNeeded( propertySet, "primaryKey", TQVariant(set), commandGroup );
	if (&propertySet==this->propertySet()) {
		//update action and icon @ column 0 (only if we're changing current property set)
		d->action_toggle_pkey->setChecked(set);
		if (d->view->selectedItem()) {
			//show key in the table
			d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_ICON, 
				TQVariant(set ? "key" : ""));
			d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true);
		}
		if (was_pkey || set) //change flag only if we're setting pk or really clearing it
			d->primaryKeyExists = set;
	}

	if (set) {
		//primary key is set, remove old pkey if exists
		KoProperty::Set *s = 0;
		int i;
		const int count = (int)d->sets->size();
		for (i=0; i<count; i++) {
			s = d->sets->at(i);
			if (s && s!=&propertySet && (*s)["primaryKey"].value().toBool() && i!=d->view->currentRow())
				break;
		}
		if (i<count) {//remove
			//(*s)["autoIncrement"] = TQVariant(false);
			d->setPropertyValueIfNeeded( *s, "autoIncrement", TQVariant(false), commandGroup );
			//(*s)["primaryKey"] = TQVariant(false);
			d->setPropertyValueIfNeeded( *s, "primaryKey", TQVariant(false), commandGroup );
			//remove key from table
			d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
			KexiTableItem *item = d->view->itemAt(i);
			if (item) {
				d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
				d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item, true);
			}
		}
		//set unsigned big-integer type
//		d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem());
		d->slotBeforeCellChanged_enabled = false;
			d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
			TQVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
//			TQVariant(KexiDB::Field::typeGroupName(KexiDB::Field::IntegerGroup)));
			d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true);
		//propertySet["subType"] = KexiDB::Field::typeString(KexiDB::Field::BigInteger);
			d->setPropertyValueIfNeeded( propertySet, "subType", KexiDB::Field::typeString(KexiDB::Field::BigInteger), 
				commandGroup );
		//propertySet["unsigned"] = TQVariant(true);
			d->setPropertyValueIfNeeded( propertySet, "unsigned", TQVariant(true), commandGroup );
/*todo*/
		d->slotBeforeCellChanged_enabled = true;
	}
	updateActions();
}

/*void KexiTableDesignerView::slotCellSelected(int, int row)
{
	kdDebug() << "KexiTableDesignerView::slotCellSelected()" << endl;
	if(row == m_row)
		return;
	m_row = row;
	propertyBufferSwitched();
}*/

tristate KexiTableDesignerView::beforeSwitchTo(int mode, bool &dontStore)
{
	if (!d->view->acceptRowEdit())
		return false;
/*	if (mode==Kexi::DesignViewMode) {
		initData();
		return true;
	}
	else */
	tristate res = true;
	if (mode==Kexi::DataViewMode) {
		if (!dirty() && parentDialog()->neverSaved()) {
			KMessageBox::sorry(this, i18n("Cannot switch to data view, because table design is empty.\n"
				"First, please create your design.") );
			return cancelled;
		}
//<temporary>
		else if (dirty() && !parentDialog()->neverSaved()) {
//			cancelled = (KMessageBox::No == KMessageBox::questionYesNo(this, i18n("Saving changes for existing table design is not yet supported.\nDo you want to discard your changes now?")));

//			KexiDB::Connection *conn = mainWin()->project()->dbConnection();
			bool emptyTable;
			int r = KMessageBox::warningYesNoCancel(this,
				i18n("Saving changes for existing table design is now required.")
				+ "\n" + d->messageForSavingChanges(emptyTable, /* skip warning? */!isPhysicalAlteringNeeded()), 
				TQString(),
				KStdGuiItem::save(), KStdGuiItem::discard(), TQString(), 
				KMessageBox::Notify|KMessageBox::Dangerous);
			if (r == KMessageBox::Cancel)
				res = cancelled;
			else
				res = true;
			dontStore = (r!=KMessageBox::Yes);
			if (!dontStore)
				d->dontAskOnStoreData = true;
//			if (dontStore)
//				setDirty(false);
		}
//</temporary>
		//todo
		return res;
	}
	else if (mode==Kexi::TextViewMode) {
		//todo
	}
	return res;
}

tristate KexiTableDesignerView::afterSwitchFrom(int mode)
{
	if (mode==Kexi::NoViewMode || mode==Kexi::DataViewMode) {
		initData();
	}
	return true;
}

KoProperty::Set *KexiTableDesignerView::propertySet()
{
	return d->sets ? d->sets->currentPropertySet() : 0;
}

/*
void KexiTableDesignerView::removeCurrentPropertySet()
{
	const int r = d->view->currentRow();
	KoProperty::Set *buf = d->sets.at(r);
	if (!buf)
		return;
	buf->debug();
//	m_currentBufferCleared = true;
	d->sets.remove(r);
	propertysetswitched();
//	delete buf;
//	m_currentBufferCleared = false;
}
*/

void KexiTableDesignerView::slotBeforeCellChanged(
	KexiTableItem *item, int colnum, TQVariant& newValue, KexiDB::ResultInfo* /*result*/)
{
	if (!d->slotBeforeCellChanged_enabled)
		return;
//	kdDebug() << d->view->selectedItem() << " " << item 
		//<< " " << d->sets->at( d->view->currentRow() ) << " " << propertySet() << endl;
	if (colnum==COLUMN_ID_CAPTION) {//'caption'
//		if (!item->at(1).toString().isEmpty() && item->at(1).isNull()) {
		//if 'type' is not filled yet
		if (item->at(COLUMN_ID_TYPE).isNull()) {
			//auto select 1st row of 'type' column
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant((int)0));
		}

		KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
		if (propertySetForItem) {
			d->addHistoryCommand_in_slotPropertyChanged_enabled = false; //because we'll add the two changes as one KMacroCommand
				TQString oldName( propertySetForItem->property("name").value().toString() );
				TQString oldCaption( propertySetForItem->property("caption").value().toString() );

				//we need to create the action now as set["name"] will be changed soon..
				ChangeFieldPropertyCommand *changeCaptionCommand
					= new ChangeFieldPropertyCommand( this, *propertySetForItem, "caption", oldCaption, newValue);

				//update field caption and name
				propertySetForItem->changeProperty("caption", newValue);
				propertySetForItem->changeProperty("name", newValue); // "name" prop. is of custom type Identifier, so this assignment 
										            // will automatically convert newValue to an valid identifier

				//remember this action containing 2 subactions
				CommandGroup *changeCaptionAndNameCommand = new CommandGroup(
					i18n("Change \"%1\" field's name to \"%2\" and caption from \"%3\" to \"%4\"")
						.arg(oldName).arg(propertySetForItem->property("name").value().toString())
						.arg(oldCaption).arg(newValue.toString() ));
				changeCaptionAndNameCommand->addCommand( changeCaptionCommand );
//					new ChangeFieldPropertyCommand( this, *propertySetForItem,
	//						"caption", oldCaption, newValue)
		//		);
				changeCaptionAndNameCommand->addCommand(
					new ChangeFieldPropertyCommand( this, *propertySetForItem,
							"name", oldName, propertySetForItem->property("name").value().toString())
				);
				addHistoryCommand( changeCaptionAndNameCommand, false /* !execute */ );

			d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
		}
	}
	else if (colnum==COLUMN_ID_TYPE) {//'type'
		if (newValue.isNull()) {
			//'type' col will be cleared: clear all other columns as well
			d->slotBeforeCellChanged_enabled = false;
				d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
				d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, TQVariant(TQString()));
				d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC, TQVariant());
			d->slotBeforeCellChanged_enabled = true;
			return;
		}

		KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
		if (!propertySetForItem)
			return;

		KoProperty::Set &set = *propertySetForItem; //propertySet();

		//'type' col is changed (existed before)
		//-get type group number
		KexiDB::Field::TypeGroup fieldTypeGroup;
		int i_fieldTypeGroup = newValue.toInt()+1/*counting from 1*/;
		if (i_fieldTypeGroup < 1 || i_fieldTypeGroup >
#ifdef KEXI_NO_BLOB_FIELDS
//! @todo remove this later
			(int)KexiDB::Field::LastTypeGroup-1) //don't show last (BLOB) type
#else
			(int)KexiDB::Field::LastTypeGroup)
#endif
			return;
		fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(i_fieldTypeGroup);

		//-get 1st type from this group, and update 'type' property
		KexiDB::Field::Type fieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
		if (fieldType==KexiDB::Field::InvalidType)
			fieldType = KexiDB::Field::Text;
//moved down		set["type"] = (int)fieldType;
//		set["subType"] = KexiDB::Field::typeName(fieldType);
		
		//-get subtypes for this type: keys (slist) and names (nlist)
		TQStringList slist, nlist;
		getSubTypeListData(fieldTypeGroup, slist, nlist);

		TQString subTypeValue;
/* disabled - "mime" is moved from subType to "objectType" custom property
		if (fieldType==KexiDB::Field::BLOB) {
			// special case: BLOB type uses "mime-based" subtypes
			subTypeValue = slist.first();
		}
		else {*/
			subTypeValue = KexiDB::Field::typeString(fieldType);
		//}
		KoProperty::Property *subTypeProperty = &set["subType"];
		kexipluginsdbg << subTypeProperty->value() << endl;
		
		// *** this action contains subactions ***
		CommandGroup *changeDataTypeCommand = new CommandGroup(
			i18n("Change data type for field \"%1\" to \"%2\"")
				.arg(set["name"].value().toString()).arg( KexiDB::Field::typeName( fieldType ) ) );

//kexipluginsdbg << "++++++++++" << slist << nlist << endl;

		//update subtype list and value
		const bool forcePropertySetReload 
			= KexiDB::Field::typeGroup( KexiDB::Field::typeForString(subTypeProperty->value().toString()) )
				!= fieldTypeGroup; //<-- ?????
//		const bool forcePropertySetReload = set["type"].value().toInt() != (int)fieldTypeGroup;
		const bool useListData = slist.count() > 1; //disabled-> || fieldType==KexiDB::Field::BLOB;

		if (!useListData) {
			slist.clear(); //empty list will be passed
			nlist.clear();
		}
		d->setPropertyValueIfNeeded( set, "type", (int)fieldType, changeDataTypeCommand,
			false /*!forceAddCommand*/, true /*rememberOldValue*/);

		// notNull and defaultValue=false is reasonable for boolean type
		if (fieldType == KexiDB::Field::Boolean) {
//! @todo maybe this is good for other data types as well?
			d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true), changeDataTypeCommand,
				false /*!forceAddCommand*/, false /*!rememberOldValue*/);
			d->setPropertyValueIfNeeded( set, "defaultValue", TQVariant(false), changeDataTypeCommand,
				false /*!forceAddCommand*/, false /*!rememberOldValue*/);
		}

/*		if (useListData) {
		{
			subTypeProperty->setListData( slist, nlist );
		}
		else {
			subTypeProperty->setListData( 0 );
		}*/
		if (set["primaryKey"].value().toBool()==true) {
			//primary keys require big int, so if selected type is not integer- remove PK
			if (fieldTypeGroup != KexiDB::Field::IntegerGroup) {
				/*not needed, line below will do the work
				d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
				d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item); */
				//set["primaryKey"] = TQVariant(false);
				d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(false), changeDataTypeCommand );
//! @todo should we display (passive?) dialog informing about cleared pkey?
			}
		}
//		if (useListData)
//		subTypeProperty->setValue( subTypeValue, false/*!rememberOldValue*/ );
		d->setPropertyValueIfNeeded( set, "subType", subTypeValue, 
			changeDataTypeCommand, false, false /*!rememberOldValue*/,
			&slist, &nlist );

		if (d->updatePropertiesVisibility(fieldType, set, changeDataTypeCommand) || forcePropertySetReload) {
			//properties' visiblility changed: refresh prop. set
			propertySetReloaded(true);
		}

		addHistoryCommand( changeDataTypeCommand, false /* !execute */ );
	}
	else if (colnum==COLUMN_ID_DESC) {//'description'
		KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
		if (!propertySetForItem)
			return;
		//update field desc.
		TQVariant oldValue((*propertySetForItem)["description"].value());
		kexipluginsdbg << oldValue << endl;
		propertySetForItem->changeProperty("description", newValue);
		/*moved addHistoryCommand( 
			new ChangeFieldPropertyCommand( this, *propertySetForItem,
				"description", oldValue, newValue ), false);*/
	}
}

void KexiTableDesignerView::slotRowUpdated(KexiTableItem *item)
{
	const int row = d->view->KexiDataAwareObjectInterface::data()->findRef(item);
	if (row < 0)
		return;
	
	setDirty();

	//-check if the row was empty before updating
	//if yes: we want to add a property set for this new row (field)
	TQString fieldCaption( item->at(COLUMN_ID_CAPTION).toString() );
	const bool prop_set_allowed = !item->at(COLUMN_ID_TYPE).isNull();

	if (!prop_set_allowed && d->sets->at(row)/*propertySet()*/) {
		//there is a property set, but it's not allowed - remove it:
		d->sets->remove( row ); //d->sets->removeCurrentPropertySet();

		//clear 'type' column:
		d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
//		d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE, TQVariant());
		d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant());
		d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);

	} else if (prop_set_allowed && !d->sets->at(row)/*propertySet()*/) {
		//-- create a new field:
		KexiDB::Field::TypeGroup fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>( 
			item->at(COLUMN_ID_TYPE).toInt()+1/*counting from 1*/ );
		int intFieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
		if (intFieldType==0)
			return;

		TQString description( item->at(COLUMN_ID_DESC).toString() );

//todo: check uniqueness:
		TQString fieldName( KexiUtils::string2Identifier(fieldCaption) );

		KexiDB::Field::Type fieldType = KexiDB::intToFieldType( intFieldType );
		KexiDB::Field field( //tmp
			fieldName,
			fieldType,
			KexiDB::Field::NoConstraints,
			KexiDB::Field::NoOptions,
			/*length*/0,
			/*precision*/0,
			/*defaultValue*/TQVariant(),
			fieldCaption,
			description,
			/*width*/0);
//		m_newTable->addField( field );

		// reasonable case for boolean type: set notNull flag and "false" as default value
		if (fieldType == KexiDB::Field::Boolean) {
			field.setNotNull( true );
			field.setDefaultValue( TQVariant(false) );
		}

		kexipluginsdbg << "KexiTableDesignerView::slotRowUpdated(): " << field.debugString() << endl;

		//create a new property set:
		KoProperty::Set *newSet = createPropertySet( row, field, true );
//moved
		//add a special property indicating that this is brand new buffer,
		//not just changed
//		KoProperty::Property* prop = new KoProperty::Property("newrow", TQVariant());
//		prop->setVisible(false);
//		newbuff->addProperty( prop );

		//refresh property editor:
		propertySetSwitched();

		if (row>=0) {
			if (d->addHistoryCommand_in_slotRowUpdated_enabled) {
				addHistoryCommand( new InsertFieldCommand( this, row, *newSet /*propertySet()*/ ), //, field /*will be copied*/ 
					false /* !execute */ );
			}
		}
		else {
			kexipluginswarn << "KexiTableDesignerView::slotRowUpdated() row # not found  !" << endl;
		}
	}
}

void KexiTableDesignerView::updateActions()
{
	updateActions(false);
}

void KexiTableDesignerView::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
{
//	if (!d->slotPropertyChanged_enabled)
//		return;
	const TQCString pname = property.name();
	kexipluginsdbg << "KexiTableDesignerView::slotPropertyChanged(): " << pname << " = " << property.value() 
		<< " (oldvalue = " << property.oldValue() << ")" << endl;

	// true is PK should be altered
	bool changePrimaryKey = false;
	// true is PK should be set to true, otherwise unset
	bool setPrimaryKey = false;

	if (pname=="primaryKey" && d->slotPropertyChanged_primaryKey_enabled) {
		changePrimaryKey = true;
		setPrimaryKey = property.value().toBool();
	}

	// update "lookup column" icon
	if (pname=="rowSource" || pname=="rowSourceType") {
//! @todo indicate invalid definitions of lookup columns as well using a special icon 
//!       (e.g. due to missing data source)
		const int row = d->sets->findRowForPropertyValue("uid", set["uid"].value().toInt());
		KexiTableItem *item = d->view->itemAt(row);
		if (item)
			d->updateIconForItem(*item, set);
	}

	//setting autonumber requires setting PK as well
	CommandGroup *setAutonumberCommand = 0;
	CommandGroup *toplevelCommand = 0;
	if (pname=="autoIncrement" && property.value().toBool()==true) {
		if (set["primaryKey"].value().toBool()==false) {//we need PKEY here!
			TQString msg = TQString("<p>")
				+i18n("Setting autonumber requires primary key to be set for current field.")+"</p>";
			if (d->primaryKeyExists)
				msg += (TQString("<p>")+ i18n("Previous primary key will be removed.")+"</p>");
			msg += (TQString("<p>")
				+i18n("Do you want to create primary key for current field? "
				"Click \"Cancel\" to cancel setting autonumber.")+"</p>");

			if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
				i18n("Setting Autonumber Field"),
				KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
			{
				changePrimaryKey = true;
				setPrimaryKey = true;
				//switchPrimaryKey(set, true);
				// this will be toplevel command 
				setAutonumberCommand = new CommandGroup(
					i18n("Assign autonumber for field \"%1\"").arg(set["name"].value().toString()) );
				toplevelCommand = setAutonumberCommand;
				d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(true), setAutonumberCommand );
			}
			else {
				setAutonumberCommand = new CommandGroup(
					i18n("Remove autonumber from field \"%1\"").arg(set["name"].value().toString()) );
				//d->slotPropertyChanged_enabled = false;
//					set["autoIncrement"].setValue( TQVariant(false), false/*don't save old*/);
//					d->slotPropertyChanged_enabled = true;
				d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(false), setAutonumberCommand,
					true /*forceAddCommand*/, false/*rememberOldValue*/ );
				addHistoryCommand( setAutonumberCommand, false /* !execute */ );
				return;
			}
		}
	}

	//clear PK when these properties were set to false:
	if ((pname=="indexed" || pname=="unique" || pname=="notNull") 
		&& set["primaryKey"].value().toBool() && property.value().toBool()==false)
	{
//! @todo perhaps show a hint in help panel telling what happens?
		changePrimaryKey = true;
		setPrimaryKey = false;
		// this will be toplevel command 
		CommandGroup *unsetIndexedOrUniquOrNotNullCommand = new CommandGroup(
			i18n("Set \"%1\" property for field \"%2\"").arg(property.caption()).arg(set["name"].value().toString()) );
		toplevelCommand = unsetIndexedOrUniquOrNotNullCommand;
		d->setPropertyValueIfNeeded( set, pname, TQVariant(false), unsetIndexedOrUniquOrNotNullCommand );
		if (pname=="notNull") {
//?			d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true), unsetIndexedOrUniquOrNotNullCommand );
			d->setPropertyValueIfNeeded( set, "unique", TQVariant(false), unsetIndexedOrUniquOrNotNullCommand );
		}
	}

	if (pname=="defaultValue") {
		KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
		set["defaultValue"].setType((KoProperty::PropertyType)KexiDB::Field::variantType(type));
	}

	if (pname=="subType" && d->slotPropertyChanged_subType_enabled) {
		d->slotPropertyChanged_subType_enabled = false;
		if (set["primaryKey"].value().toBool()==true 
			&& property.value().toString()!=KexiDB::Field::typeString(KexiDB::Field::BigInteger))
		{
			kexipluginsdbg << "INVALID " << property.value().toString() << endl;
//			if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
//				i18n("This field has promary key assigned. Setting autonumber field"),
//				KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))

		}
		KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
		TQString typeName;
/* disabled - "mime" is moved from subType to "objectType" custom property
		if (type==KexiDB::Field::BLOB) { //special case
			//find i18n'd text
			TQStringList stringsList, namesList;
			getSubTypeListData(KexiDB::Field::BLOBGroup, stringsList, namesList);
			const int stringIndex = stringsList.findIndex( property.value().toString() );
			if (-1 == stringIndex || stringIndex>=(int)namesList.count())
				typeName = property.value().toString(); //for sanity
			else
				typeName = namesList[stringIndex];
		}
		else {*/
		typeName = KexiDB::Field::typeName( KexiDB::Field::typeForString(property.value().toString()) );
//		}
//		kdDebug() << property.value().toString() << endl;
//		kdDebug() << set["type"].value() << endl;
//		if (KexiDB::Field::typeGroup( set["type"].value().toInt() ) == (int)KexiDB::Field::TextGroup) {
		CommandGroup* changeFieldTypeCommand = new CommandGroup(
			i18n("Change type for field \"%1\" to \"%2\"").arg(set["name"].value().toString())
			.arg(typeName) );
		d->setPropertyValueIfNeeded( set, "subType", property.value(), property.oldValue(),
			changeFieldTypeCommand );

		kexipluginsdbg << set["type"].value() << endl;
		const KexiDB::Field::Type newType = KexiDB::Field::typeForString(property.value().toString());
		set["type"].setValue( newType );

		// cast "defaultValue" property value to a new type
		TQVariant oldDefVal( set["defaultValue"].value() );
		TQVariant newDefVal( tryCastTQVariant(oldDefVal, KexiDB::Field::variantType(type)) );
		if (oldDefVal.type()!=newDefVal.type())
			set["defaultValue"].setType( newDefVal.type() );
		d->setPropertyValueIfNeeded( set, "defaultValue", newDefVal, newDefVal,
			changeFieldTypeCommand );

		d->updatePropertiesVisibility(newType, set);
		//properties' visiblility changed: refresh prop. set
		propertySetReloaded(true);
		d->slotPropertyChanged_subType_enabled = true;

		addHistoryCommand( changeFieldTypeCommand, false /* !execute */ );
		return;
//		}
//		d->slotPropertyChanged_subType_enabled = true;
//		return;
	}

	if (d->addHistoryCommand_in_slotPropertyChanged_enabled && !changePrimaryKey/*we'll add multiple commands for PK*/) {
		addHistoryCommand( new ChangeFieldPropertyCommand(this, set, 
				property.name(), property.oldValue() /* ??? */, property.value()), 
			false /* !execute */ );
	}

	if (changePrimaryKey) {
	  d->slotPropertyChanged_primaryKey_enabled = false;
		if (setPrimaryKey) {
			//primary key implies some rules
			//const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled = d->addHistoryCommand_in_slotPropertyChanged_enabled;
//			d->addHistoryCommand_in_slotPropertyChanged_enabled = false;

			//this action contains subactions
			CommandGroup *setPrimaryKeyCommand = new CommandGroup(
				i18n("Set primary key for field \"%1\"")
					.arg(set["name"].value().toString()) );
			if (toplevelCommand)
				toplevelCommand->addCommand( setPrimaryKeyCommand );
			else
				toplevelCommand = setPrimaryKeyCommand;

			d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(true), setPrimaryKeyCommand, true /*forceAddCommand*/ );
			d->setPropertyValueIfNeeded( set, "unique", TQVariant(true), setPrimaryKeyCommand );
			d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true), setPrimaryKeyCommand );
			d->setPropertyValueIfNeeded( set, "allowEmpty", TQVariant(false), setPrimaryKeyCommand );
			d->setPropertyValueIfNeeded( set, "indexed", TQVariant(true), setPrimaryKeyCommand );
//! \todo: add setting for this: "Integer PKeys have autonumber set by default"
			d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(true), setPrimaryKeyCommand );

/*				set["unique"] = TQVariant(true);
				set["notNull"] = TQVariant(true);
				set["allowEmpty"] = TQVariant(false);
				set["indexed"] = TQVariant(true);
				set["autoIncrement"] = TQVariant(true);*/
//			d->addHistoryCommand_in_slotPropertyChanged_enabled = prev_addHistoryCommand_in_slotPropertyChanged_enabled;
//down			addHistoryCommand( toplevelCommand, false /* !execute */ );
		}
		else {//! set PK to false
			//remember this action containing 2 subactions
			CommandGroup *setPrimaryKeyCommand = new CommandGroup(
				i18n("Unset primary key for field \"%1\"")
					.arg(set["name"].value().toString()) );
			if (toplevelCommand)
				toplevelCommand->addCommand( setPrimaryKeyCommand );
			else
				toplevelCommand = setPrimaryKeyCommand;

			d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(false), setPrimaryKeyCommand, true /*forceAddCommand*/ );
			d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(false), setPrimaryKeyCommand );
//			set["autoIncrement"] = TQVariant(false,1);

//down			addHistoryCommand( toplevelCommand, false /* !execute */ );
		}
		switchPrimaryKey(set, setPrimaryKey, true/*wasPKey*/, toplevelCommand);
		d->updatePropertiesVisibility(
			KexiDB::Field::typeForString( set["subType"].value().toString() ), set, toplevelCommand);
		addHistoryCommand( toplevelCommand, false /* !execute */ );
		//properties' visiblility changed: refresh prop. set
		propertySetReloaded(true/*preservePrevSelection*/);
	  d->slotPropertyChanged_primaryKey_enabled = true;
	}
}

void KexiTableDesignerView::slotRowInserted()
{
	updateActions();

	if (d->addHistoryCommand_in_slotRowInserted_enabled) {
		const int row = d->view->currentRow();
		if (row>=0) {
			addHistoryCommand( new InsertEmptyRowCommand( this, row ), false /* !execute */ );
		}
	}
	//TODO?
}

void KexiTableDesignerView::slotAboutToDeleteRow(
	KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint)
{
	Q_UNUSED(result)
	Q_UNUSED(repaint)
	if (item[COLUMN_ID_ICON].toString()=="key")
		d->primaryKeyExists = false;

	if (d->addHistoryCommand_in_slotAboutToDeleteRow_enabled) {
		const int row = d->view->KexiDataAwareObjectInterface::data()->findRef(&item);
		KoProperty::Set *set = row >=0 ? d->sets->at(row) : 0;
		//set can be 0 here, what means "removing empty row"
		addHistoryCommand( 
			new RemoveFieldCommand( this, row, set ),
			false /* !execute */
		);
	}
}

KexiDB::Field * KexiTableDesignerView::buildField( const KoProperty::Set &set ) const
{
	//create a map of property values
	kexipluginsdbg << set["type"].value() << endl;
	TQMap<TQCString, TQVariant> values = KoProperty::propertyValues(set);
	//remove internal values, to avoid creating custom field's properties
	TQMap<TQCString, TQVariant>::Iterator it = values.begin();
	KexiDB::Field *field = new KexiDB::Field();

	while (it!=values.end()) {
		const TQString propName( it.key() );
		if (d->internalPropertyNames.find(propName.latin1()) || propName.startsWith("this:")
			|| (/*sanity*/propName=="objectType" && KexiDB::Field::BLOB != KexiDB::intToFieldType( set["type"].value().toInt() )))
		{
			TQMap<TQCString, TQVariant>::Iterator it_tmp = it;
			++it;
			values.remove(it_tmp);
		}
		else
			++it;
	}
	//assign properties to the field
	// (note that "objectType" property will be saved as custom property)
	if (!KexiDB::setFieldProperties( *field, values )) {
		delete field;
		return 0;
	}
	return field;
}

tristate KexiTableDesignerView::buildSchema(KexiDB::TableSchema &schema, bool beSilent)
{
	if (!d->view->acceptRowEdit())
		return cancelled;

	tristate res = true;
	//check for pkey; automatically add a pkey if user wanted
	if (!d->primaryKeyExists) {
		if (beSilent) {
			kexipluginsdbg << "KexiTableDesignerView::buildSchema(): no primay key defined..." << endl;
		}
		else {
			const int questionRes = KMessageBox::questionYesNoCancel(this,
				i18n("<p>Table \"%1\" has no <b>primary key</b> defined.</p>"
				"<p>Although a primary key is not required, it is needed "
				"for creating relations between database tables. "
				"Do you want to add primary key automatically now?</p>"
				"<p>If you want to add a primary key by hand, press \"Cancel\" "
				"to cancel saving table design.</p>").arg(schema.name()),
				TQString(), KGuiItem(i18n("&Add Primary Key"), "key"), KStdGuiItem::no(),
					"autogeneratePrimaryKeysOnTableDesignSaving");
			if (questionRes==KMessageBox::Cancel) {
				return cancelled;
			}
			else if (questionRes==KMessageBox::Yes) {
				//-find unique name, starting with, "id", "id2", ....
				int i=0;
				int idIndex = 1; //means "id"
				TQString pkFieldName("id%1");
				TQString pkFieldCaption(i18n("Identifier%1", "Id%1"));
				while (i<(int)d->sets->size()) {
					KoProperty::Set *set = d->sets->at(i);
					if (set) {
						if ((*set)["name"].value().toString()
							== pkFieldName.arg(idIndex==1?TQString() : TQString::number(idIndex))
						|| (*set)["caption"].value().toString()
							== pkFieldCaption.arg(idIndex==1?TQString() : TQString::number(idIndex)))
						{
							//try next id index
							i = 0;
							idIndex++;
							continue;
						}
					}
					i++;
				}
				pkFieldName = pkFieldName.arg(idIndex==1?TQString() : TQString::number(idIndex));
				pkFieldCaption = pkFieldCaption.arg(idIndex==1?TQString() : TQString::number(idIndex));
				//ok, add PK with such unique name
				d->view->insertEmptyRow(0);
				d->view->setCursorPosition(0, COLUMN_ID_CAPTION);
				d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_CAPTION,
					TQVariant(pkFieldCaption));
				d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
					TQVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
				if (!d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true)) {
					return cancelled;
				}
				slotTogglePrimaryKey();
			}
		}
	}

	//check for duplicates
	KoProperty::Set *b = 0;
	bool no_fields = true;
	int i;
	TQDict<char> names(1009, false);
	char dummy;
	for (i=0;i<(int)d->sets->size();i++) {
		b = d->sets->at(i);
		if (b) {
			no_fields = false;
			const TQString name = (*b)["name"].value().toString();
			if (name.isEmpty()) {
				if (beSilent) {
					kexipluginswarn << 
						TQString("KexiTableDesignerView::buildSchema(): no field caption entered at row %1...")
						.arg(i+1) << endl;
				}
				else {
					d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
					d->view->startEditCurrentCell();
					KMessageBox::information(this, i18n("You should enter field caption.") );
				}
				res = cancelled;
				break;
			}
			if (names[name]) {
				break;
			}
			names.insert( name, &dummy ); //remember
		}
	}
	if (res == true && no_fields) {//no fields added
		if (beSilent) {
			kexipluginswarn << 
				"KexiTableDesignerView::buildSchema(): no field defined..." << endl;
		}
		else {
			KMessageBox::sorry(this,
				i18n("You have added no fields.\nEvery table should have at least one field.") );
		}
		res = cancelled;
	}
	if (res == true && b && i<(int)d->sets->size()) {//found a duplicate
		if (beSilent) {
			kexipluginswarn << 
				TQString("KexiTableDesignerView::buildSchema(): duplicated field name '%1'")
				.arg((*b)["name"].value().toString()) << endl;
		}
		else {
			d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
			d->view->startEditCurrentCell();
//! @todo for "names hidden" mode we won't get this error because user is unable to change names
			KMessageBox::sorry(this,
				i18n("You have added \"%1\" field name twice.\nField names cannot be repeated. "
				"Correct name of the field.")
				.arg((*b)["name"].value().toString()) );
		}
		res = cancelled;
	}
	if (res == true) {
		//for every field, create KexiDB::Field definition
		for (i=0;i<(int)d->sets->size();i++) {
			KoProperty::Set *s = d->sets->at(i);
			if (!s)
				continue;
			KexiDB::Field * f = buildField( *s );
			if (!f)
				continue; //hmm?
			schema.addField(f);
			if (!(*s)["rowSource"].value().toString().isEmpty() && !(*s)["rowSourceType"].value().toString().isEmpty()) {
				//add lookup column
				KexiDB::LookupFieldSchema *lookupFieldSchema = new KexiDB::LookupFieldSchema();
				lookupFieldSchema->rowSource().setTypeByName( (*s)["rowSourceType"].value().toString() );
				lookupFieldSchema->rowSource().setName( (*s)["rowSource"].value().toString() );
				lookupFieldSchema->setBoundColumn( (*s)["boundColumn"].value().toInt() );
//! @todo this is backward-compatible code for "single visible column" implementation
//!       for multiple columns, only the first is displayed, so there is a data loss is GUI is used
//!       -- special koproperty editor needed
				TQValueList<uint> visibleColumns;
				const int visibleColumn = (*s)["visibleColumn"].value().toInt();
				if (visibleColumn >= 0)
					visibleColumns.append( (uint)visibleColumn );
				lookupFieldSchema->setVisibleColumns( visibleColumns );
//! @todo support columnWidths(), columnHeadersVisible(), maximumListRows(), limitToList(), displayWidget()
				if (!schema.setLookupFieldSchema(f->name(), lookupFieldSchema)) {
					kexipluginswarn << 
						"KexiTableDesignerView::buildSchema(): !schema.setLookupFieldSchema()" << endl;
					delete lookupFieldSchema;
					return false;
				}
			}
		}
	}
	return res;
}

//! @internal
//! A recursive function for copying alter table actions from undo/redo commands.
static void copyAlterTableActions(KCommand* command, KexiDB::AlterTableHandler::ActionList &actions)
{
	CommandGroup* cmdGroup = dynamic_cast<CommandGroup*>( command );
	if (cmdGroup) {//command group: flatten it
		for (TQPtrListIterator<KCommand> it(cmdGroup->commands()); it.current(); ++it)
			copyAlterTableActions(it.current(), actions);
		return;
	}
	Command* cmd = dynamic_cast<Command*>( command );
	if (!cmd) {
		kexipluginswarn << "KexiTableDesignerView::copyAlterTableActions(): cmd is not of type 'Command'!" << endl;
		return;
	}
	KexiDB::AlterTableHandler::ActionBase* action = cmd->createAction();
	//some commands can contain null actions, e.g. "set visibility" command
	if (action)
		actions.append( action );
}

tristate KexiTableDesignerView::buildAlterTableActions(KexiDB::AlterTableHandler::ActionList &actions)
{
	actions.clear();
	kexipluginsdbg << "KexiTableDesignerView::buildAlterTableActions(): " << d->history->commands().count()
		<< " top-level command(s) to process..." << endl;
	for (TQPtrListIterator<KCommand> it(d->history->commands()); it.current(); ++it) {
		copyAlterTableActions(it.current(), actions);
	}
	return true;
}

KexiDB::SchemaData* KexiTableDesignerView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
{
	if (tempData()->table || m_dialog->schemaData()) //must not be
		return 0;

	//create table schema definition
	tempData()->table = new KexiDB::TableSchema(sdata.name());
	tempData()->table->setName( sdata.name() );
	tempData()->table->setCaption( sdata.caption() );
	tempData()->table->setDescription( sdata.description() );

	tristate res = buildSchema(*tempData()->table);
	cancel = ~res;

	//FINALLY: create table:
	if (res == true) {
		//todo
		KexiDB::Connection *conn = mainWin()->project()->dbConnection();
		res = conn->createTable(tempData()->table);
		if (res!=true)
			parentDialog()->setStatus(conn, "");
	}

	if (res == true) {
		//we've current schema
		tempData()->tableSchemaChangedInPreviousView = true;
//not needed; KexiProject emits newItemStored signal //let project know the table is created
//		mainWin()->project()->emitTableCreated(*tempData()->table);
	}
	else {
		delete tempData()->table;
		tempData()->table = 0;
	}
	return tempData()->table;
}

tristate KexiTableDesignerView::storeData(bool dontAsk)
{
	if (!tempData()->table || !m_dialog->schemaData()) {
		d->recentResultOfStoreData = false;
		return false;
	}

	KexiDB::Connection *conn = mainWin()->project()->dbConnection();
	KexiDB::AlterTableHandler *alterTableHandler = 0;
	KexiDB::TableSchema *newTable = 0;

	//- create action list for the alter table handler
	KexiDB::AlterTableHandler::ActionList actions;
	tristate res = buildAlterTableActions( actions );
	bool realAlterTableCanBeUsed = false; //!< @todo this is temporary flag before we switch entirely to real alter table
	if (res == true) {
		alterTableHandler = new KexiDB::AlterTableHandler( *conn );
		alterTableHandler->setActions(actions);

		if (!d->tempStoreDataUsingRealAlterTable) {
			//only compute requirements
			KexiDB::AlterTableHandler::ExecutionArguments args;
			args.onlyComputeRequirements = true;
			(void)alterTableHandler->execute(tempData()->table->name(), args);
			res = args.result;
			if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
				realAlterTableCanBeUsed = true;
		}
	}

	if (res == true) {
		res = KexiTablePart::askForClosingObjectsUsingTableSchema(
			this, *conn, *tempData()->table,
			i18n("You are about to change the design of table \"%1\" "
			"but following objects using this table are opened:")
			.arg(tempData()->table->name()));
	}

	if (res == true) {
		if (!d->tempStoreDataUsingRealAlterTable && !realAlterTableCanBeUsed) {
//! @todo temp; remove this case:
			delete alterTableHandler;
			alterTableHandler = 0;
			// - inform about removing the current table and ask for confirmation
			if (!d->dontAskOnStoreData && !dontAsk) {
				bool emptyTable;
				const TQString msg = d->messageForSavingChanges(emptyTable);
				if (!emptyTable) {
					if (KMessageBox::No == KMessageBox::questionYesNo(this, msg))
						res = cancelled;
				}
			}
			d->dontAskOnStoreData = false; //one-time use
			if (~res) {
				d->recentResultOfStoreData = res;
				return res;
			}
			// keep old behaviour:
			newTable = new KexiDB::TableSchema();
			// copy the schema data
			static_cast<KexiDB::SchemaData&>(*newTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
			res = buildSchema(*newTable);
			kexipluginsdbg << "KexiTableDesignerView::storeData() : BUILD SCHEMA:" << endl;
			newTable->debug();

			res = conn->alterTable(*tempData()->table, *newTable);
			if (res != true)
				parentDialog()->setStatus(conn, "");
		}
		else {
			KexiDB::AlterTableHandler::ExecutionArguments args;
			newTable = alterTableHandler->execute(tempData()->table->name(), args);
			res = args.result;
			kexipluginsdbg << "KexiTableDesignerView::storeData() : ALTER TABLE EXECUTE: " 
				<< res.toString() << endl;
			if (true != res) {
				alterTableHandler->debugError();
				parentDialog()->setStatus(alterTableHandler, "");
			}
		}
	}
	if (res == true) {
		//change current schema
		tempData()->table = newTable;
		tempData()->tableSchemaChangedInPreviousView = true;
		d->history->clear();
	}
	else {
		delete newTable;
	}
	delete alterTableHandler;
	d->recentResultOfStoreData = res;
	return res;
}

tristate KexiTableDesignerView::simulateAlterTableExecution(TQString *debugTarget)
{
#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
# ifdef KEXI_DEBUG_GUI
	if (mainWin()->activeWindow() != parentDialog()) //to avoid executing for multiple alter table views
		return false;
	if (!tempData()->table || !m_dialog->schemaData())
		return false;
	KexiDB::Connection *conn = mainWin()->project()->dbConnection();
	KexiDB::AlterTableHandler::ActionList actions;
	tristate res = buildAlterTableActions( actions );
//todo: result?
	KexiDB::AlterTableHandler alterTableHandler( *conn );
	alterTableHandler.setActions(actions);
	KexiDB::AlterTableHandler::ExecutionArguments args;
	if (debugTarget) {
		args.debugString = debugTarget;
	}
	else {
		args.simulate = true;
	}
	(void)alterTableHandler.execute(tempData()->table->name(), args);
	return args.result;
# else
	return false;
# endif
#else
	return false;
#endif
}

void KexiTableDesignerView::slotSimulateAlterTableExecution()
{
	(void)simulateAlterTableExecution(0);
}

tristate KexiTableDesignerView::executeRealAlterTable()
{
	TQSignal signal;
	signal.connect( mainWin(), TQ_SLOT(slotProjectSave()) );
	d->tempStoreDataUsingRealAlterTable = true;
	d->recentResultOfStoreData = false;
	signal.activate(); //will call KexiMainWindowImpl::slotProjectSaveAs() and thus storeData()
	d->tempStoreDataUsingRealAlterTable = false;
	return d->recentResultOfStoreData;
}

KexiTablePart::TempData* KexiTableDesignerView::tempData() const
{
	return static_cast<KexiTablePart::TempData*>(parentDialog()->tempData());
}

/*void KexiTableDesignerView::slotAboutToUpdateRow(
	KexiTableItem* item, KexiDB::RowEditBuffer* buffer, KexiDB::ResultInfo* result)
{
	KexiDB::RowEditBuffer::SimpleMap map = buffer->simpleBuffer();
	buffer->debug();

	TQVariant old_type = item->at(1);
	TQVariant *buf_type = buffer->at( d->view->field(1)->name() );

	//check if there is a type specified
//	if ((old_type.isNull() && !buf_type) || (buf_type && buf_type->isNull())) {
		//kdDebug() << "err" << endl;
	//}
//	allow = true;
//	m_dirty = m_dirty | result->success;
}*/

#ifdef KEXI_DEBUG_GUI
void KexiTableDesignerView::debugCommand( KCommand* command, int nestingLevel )
{
	if (dynamic_cast<Command*>(command))
		KexiUtils::addAlterTableActionDebug(dynamic_cast<Command*>(command)->debugString(), nestingLevel);
	else
		KexiUtils::addAlterTableActionDebug(command->name(), nestingLevel);
	//show subcommands
	if (dynamic_cast<CommandGroup*>(command)) {
		for (TQPtrListIterator<KCommand> it(dynamic_cast<CommandGroup*>(command)->commands()); it.current(); ++it) {
			debugCommand(it.current(), nestingLevel + 1);
		}
	}
}
#endif

void KexiTableDesignerView::addHistoryCommand( KCommand* command, bool execute )
{
#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
# ifdef KEXI_DEBUG_GUI
	debugCommand( command, 0 );
# endif
	d->history->addCommand( command, execute );
	updateUndoRedoActions();
#endif
}

void KexiTableDesignerView::updateUndoRedoActions()
{
#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
	setAvailable("edit_undo", d->historyActionCollection->action("edit_undo")->isEnabled());
	setAvailable("edit_redo", d->historyActionCollection->action("edit_redo")->isEnabled());
#endif
}

void KexiTableDesignerView::slotUndo()
{
#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
# ifdef KEXI_DEBUG_GUI
	KexiUtils::addAlterTableActionDebug(TQString("UNDO:"));
# endif
	d->history->undo();
	updateUndoRedoActions();
#endif
}

void KexiTableDesignerView::slotRedo()
{
#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
# ifdef KEXI_DEBUG_GUI
	KexiUtils::addAlterTableActionDebug(TQString("REDO:"));
# endif
	d->history->redo();
	updateUndoRedoActions();
#endif
}

void KexiTableDesignerView::slotCommandExecuted(KCommand *command)
{
#ifdef KEXI_DEBUG_GUI
	debugCommand( command, 1 );
#endif
}

void KexiTableDesignerView::slotAboutToShowContextMenu()
{
	//update title
	if (propertySet()) {
		const KoProperty::Set &set = *propertySet();
		TQString captionOrName(set["caption"].value().toString());
		if (captionOrName.isEmpty())
			captionOrName = set["name"].value().toString();
//! @todo show "field" icon
		d->contextMenuTitle->setTitle( i18n("Table field \"%1\"").arg(captionOrName) );
	}
	else {
		d->contextMenuTitle->setTitle( i18n("Empty table row", "Empty Row") );
	}
}

TQString KexiTableDesignerView::debugStringForCurrentTableSchema(tristate& result)
{
	KexiDB::TableSchema tempTable;
	//copy schema data
	static_cast<KexiDB::SchemaData&>(tempTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
	result = buildSchema(tempTable, true /*beSilent*/);
	if (true!=result)
		return TQString();
	return tempTable.debugString(false /*without name*/);
}

// -- low-level actions used by undo/redo framework

void KexiTableDesignerView::clearRow(int row, bool addCommand)
{
	if (!d->view->acceptRowEdit())
		return;
	KexiTableItem *item = d->view->itemAt(row);
	if (!item)
		return;
	//remove from prop. set
	d->sets->remove( row );
	//clear row in table view (just clear value in COLUMN_ID_TYPE column)
//	for (int i=0; i < (int)d->view->KexiDataAwareObjectInterface::data()->columnsCount(); i++) {
	if (!addCommand) {
		d->addHistoryCommand_in_slotRowUpdated_enabled = false;
		d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
		d->slotBeforeCellChanged_enabled = false;
	}
	d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant());
	if (!addCommand) {
		d->addHistoryCommand_in_slotRowUpdated_enabled = true;
		d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
		d->slotBeforeCellChanged_enabled = true;
	}
	d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item, true);
}

void KexiTableDesignerView::insertField(int row, const TQString& caption, bool addCommand)
{
	insertFieldInternal(row, 0, caption, addCommand);
}

void KexiTableDesignerView::insertField(int row, KoProperty::Set& set, bool addCommand)
{
	insertFieldInternal(row, &set, TQString(), addCommand);
}

void KexiTableDesignerView::insertFieldInternal(int row, KoProperty::Set* set, //const KexiDB::Field& field, 
	const TQString& caption, bool addCommand)
{
	if (set && (!set->contains("type") || !set->contains("caption"))) {
		kexipluginswarn << "KexiTableDesignerView::insertField(): no 'type' or 'caption' property in set!" << endl;
		return;
	}
	if (!d->view->acceptRowEdit())
		return;
	KexiTableItem *item = d->view->itemAt(row);
	if (!item)
		return;
	if (!addCommand) {
		d->addHistoryCommand_in_slotRowUpdated_enabled = false;
		d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
		d->slotBeforeCellChanged_enabled = false;
	}
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, 
				set ? (*set)["caption"].value() : TQVariant(caption));//field.caption());
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, 
				set ? (int)KexiDB::Field::typeGroup( (*set)["type"].value().toInt() )-1/*counting from 0*/
				: (((int)KexiDB::Field::TextGroup)-1)/*default type, counting from 0*/
				);
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC, 
				set ? (*set)["description"].value() : TQVariant());//field.description());
	if (!addCommand) {
		d->slotBeforeCellChanged_enabled = true;
	}
		//this will create a new property set:
		d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
		if (set) {
			KoProperty::Set *newSet = d->sets->at(row);
			if (newSet) {
				*newSet = *set; //deep copy
			}
			else {
				kexipluginswarn << "KexiTableDesignerView::insertField() !newSet, row==" << row << endl;
			}
		}
	if (!addCommand) {
		d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
		d->addHistoryCommand_in_slotRowUpdated_enabled = true;
	}
	d->view->updateRow( row );
	propertySetReloaded(true);
}

void KexiTableDesignerView::insertEmptyRow( int row, bool addCommand )
{
	if (!addCommand) {
		d->addHistoryCommand_in_slotRowInserted_enabled = false;
	}
		d->view->insertEmptyRow( row );
	if (!addCommand) {
		d->addHistoryCommand_in_slotRowInserted_enabled = true;
	}
}

/*void KexiTableDesignerView::deleteRow( int row )
{
	d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
		d->view->deleteItem( d->view->KexiDataAwareObjectInterface::data()->at(row) );
	d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
}*/

void KexiTableDesignerView::deleteRow( int row, bool addCommand )
{
	KexiTableItem *item = d->view->itemAt( row );
	if (!item)
		return;
	if (!addCommand) {
		d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
	}
	const bool res = d->view->deleteItem(item);
	if (!addCommand) {
		d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
	}
	if (!res)
		return;
}

void KexiTableDesignerView::changeFieldPropertyForRow( int row,
 const TQCString& propertyName, const TQVariant& newValue, 
 KoProperty::Property::ListData* const listData, bool addCommand )
{
#ifdef KEXI_DEBUG_GUI
	KexiUtils::addAlterTableActionDebug(TQString("** changeFieldProperty: \"")
		+ TQString(propertyName) + "\" to \"" + newValue.toString() + "\"", 2/*nestingLevel*/);
#endif
	if (!d->view->acceptRowEdit())
		return;

	KoProperty::Set* set = d->sets->at( row );
	if (!set || !set->contains(propertyName))
		return;
	KoProperty::Property &property = set->property(propertyName);
	if (listData) {
		if (listData->keys.isEmpty())
			property.setListData( 0 );
		else
			property.setListData( new KoProperty::Property::ListData(*listData) );
	}
	if (propertyName != "type") //delayed type update (we need to have subtype set properly)
		property.setValue(newValue);
	KexiTableItem *item = d->view->itemAt(row);
	Q_ASSERT(item);

	if (propertyName == "type") {
	//	d->addHistoryCommand_in_slotRowUpdated_enabled = false;
//		d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
		d->slotPropertyChanged_subType_enabled = false;
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, 
				int( KexiDB::Field::typeGroup( newValue.toInt() ) )-1);
			d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
		d->addHistoryCommand_in_slotRowUpdated_enabled = true;
//		d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
	//	d->slotPropertyChanged_subType_enabled = true;
		property.setValue(newValue); //delayed type update (we needed to have subtype set properly)
	}

	if (!addCommand) {
		d->addHistoryCommand_in_slotRowUpdated_enabled = false;
		d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
		d->slotPropertyChanged_subType_enabled = false;
	}
		//special cases: properties displayed within the data grid:
		if (propertyName == "caption") {
			if (!addCommand) {
				d->slotBeforeCellChanged_enabled = false;
			}
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, newValue);
			d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
			if (!addCommand) {
				d->slotBeforeCellChanged_enabled = true;
			}
		}
		else if (propertyName == "description") {
			if (!addCommand) {
				d->slotBeforeCellChanged_enabled = false;
			}
			d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC, newValue);
			if (!addCommand) {
				d->slotBeforeCellChanged_enabled = true;
			}
			d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
		}
	if (!addCommand) {
		d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
		d->addHistoryCommand_in_slotRowUpdated_enabled = true;
		d->slotPropertyChanged_subType_enabled = true;
	}
	d->view->updateRow( row );
}

void KexiTableDesignerView::changeFieldProperty( int fieldUID,
 const TQCString& propertyName, const TQVariant& newValue, 
 KoProperty::Property::ListData* const listData, bool addCommand )
{
	//find a property by UID
	const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
	if (row<0) {
		kexipluginswarn << "KexiTableDesignerView::changeFieldProperty(): field with uid="<<fieldUID<<" not found!"<<endl;
		return;
	}
	changeFieldPropertyForRow(row, propertyName, newValue, listData, addCommand);
}

void KexiTableDesignerView::changePropertyVisibility(
	int fieldUID, const TQCString& propertyName, bool visible )
{
#ifdef KEXI_DEBUG_GUI
	KexiUtils::addAlterTableActionDebug(TQString("** changePropertyVisibility: \"")
		+ TQString(propertyName) + "\" to \"" + (visible ? "true" : "false") + "\"", 2/*nestingLevel*/);
#endif
	if (!d->view->acceptRowEdit())
		return;

	//find a property by name
	const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
	if (row<0)
		return;
	KoProperty::Set* set = d->sets->at( row );
	if (!set || !set->contains(propertyName))
		return;

	KoProperty::Property &property = set->property(propertyName);
	if (property.isVisible() != visible) {
		property.setVisible(visible);
		propertySetReloaded(true);
	}
}

void KexiTableDesignerView::propertySetSwitched()
{
	KexiDataTable::propertySetSwitched();

	//if (parentDialog()!=parentDialog()->mainWin()->currentDialog())
	//	return; //this is not the current dialog's view
	
	static_cast<KexiTablePart*>(parentDialog()->part())->lookupColumnPage()
		->assignPropertySet(propertySet());
}

bool KexiTableDesignerView::isPhysicalAlteringNeeded()
{
	//- create action list for the alter table handler
	KexiDB::AlterTableHandler::ActionList actions;
	tristate res = buildAlterTableActions( actions );
	if (res != true)
		return true;

	KexiDB::Connection *conn = mainWin()->project()->dbConnection();
	KexiDB::AlterTableHandler *alterTableHandler = new KexiDB::AlterTableHandler( *conn );
	alterTableHandler->setActions(actions);

	//only compute requirements
	KexiDB::AlterTableHandler::ExecutionArguments args;
	args.onlyComputeRequirements = true;
	(void)alterTableHandler->execute(tempData()->table->name(), args);
	res = args.result;
	delete alterTableHandler;
	if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
		return false;
	return true;
}

#include "kexitabledesignerview.moc"
