kexi

kexitabledesignerview.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This program is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this program; see the file COPYING.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kexitabledesignerview.h"
00021 #include "kexitabledesignerview_p.h"
00022 #include "kexilookupcolumnpage.h"
00023 #include "kexitabledesignercommands.h"
00024 
00025 #include <qlayout.h>
00026 #include <qlabel.h>
00027 #include <qsplitter.h>
00028 
00029 #include <kiconloader.h>
00030 #include <kdebug.h>
00031 #include <klocale.h>
00032 #include <kaction.h>
00033 #include <kpopupmenu.h>
00034 #include <kmessagebox.h>
00035 #include <kiconeffect.h>
00036 
00037 #include <koproperty/set.h>
00038 #include <koproperty/utils.h>
00039 
00040 #include <kexidb/cursor.h>
00041 #include <kexidb/tableschema.h>
00042 #include <kexidb/connection.h>
00043 #include <kexidb/utils.h>
00044 #include <kexidb/roweditbuffer.h>
00045 #include <kexidb/error.h>
00046 #include <kexidb/lookupfieldschema.h>
00047 #include <kexiutils/identifier.h>
00048 #include <kexiproject.h>
00049 #include <keximainwindow.h>
00050 #include <widget/tableview/kexidataawarepropertyset.h>
00051 #include <widget/kexicustompropertyfactory.h>
00052 #include <kexiutils/utils.h>
00053 #include <kexidialogbase.h>
00054 #include <kexitableview.h>
00055 
00056 //#define MAX_FIELDS 101 //nice prime number
00057 
00059 #define DEFAULT_OBJECT_TYPE_VALUE "image"
00060 
00061 //#define KexiTableDesignerView_DEBUG
00062 
00064 //#define KEXI_NO_BLOB_FIELDS
00065 
00066 using namespace KexiTableDesignerCommands;
00067 
00069 static bool isIntegerQVariant(QVariant::Type t)
00070 {
00071     return t==QVariant::LongLong 
00072         || t==QVariant::ULongLong
00073         || t==QVariant::Int
00074         || t==QVariant::UInt;
00075 }
00076 
00078 static bool canCastQVariant(QVariant::Type fromType, QVariant::Type toType)
00079 {
00080     return (fromType==QVariant::Int && toType==QVariant::UInt)
00081    || (fromType==QVariant::CString && toType==QVariant::String)
00082    || (fromType==QVariant::LongLong && toType==QVariant::ULongLong)
00083    || ((fromType==QVariant::String || fromType==QVariant::CString) 
00084           && (isIntegerQVariant(toType) || toType==QVariant::Double));
00085 }
00086 
00091 static QVariant tryCastQVariant( const QVariant& fromVal, QVariant::Type toType )
00092 {
00093     const QVariant::Type fromType = fromVal.type();
00094     if (fromType == toType)
00095         return fromVal;
00096     if (canCastQVariant(fromType, toType) || canCastQVariant(toType, fromType)
00097         || (isIntegerQVariant(fromType) && toType==QVariant::Double))
00098     {
00099         QVariant res( fromVal );
00100         if (res.cast(toType))
00101             return res;
00102     }
00103     return QVariant();
00104 }
00105 
00106 
00107 KexiTableDesignerView::KexiTableDesignerView(KexiMainWindow *win, QWidget *parent)
00108  : KexiDataTable(win, parent, "KexiTableDesignerView", false/*not db-aware*/)
00109  , KexiTableDesignerInterface()
00110  , d( new KexiTableDesignerViewPrivate(this) )
00111 {
00112     //needed for custom "identifier" property editor widget
00113     KexiCustomPropertyFactory::init();
00114 
00115     KexiDB::Connection *conn = mainWin()->project()->dbConnection();
00116     d->view = dynamic_cast<KexiTableView*>(mainWidget());
00117 
00118     d->data = new KexiTableViewData();
00119     if (conn->isReadOnly())
00120         d->data->setReadOnly(true);
00121     d->data->setInsertingEnabled( false );
00122 
00123     KexiTableViewColumn *col = new KexiTableViewColumn("pk", KexiDB::Field::Text, QString::null,
00124         i18n("Additional information about the field"));
00125     col->setIcon( KexiUtils::colorizeIconToTextColor( SmallIcon("info"), d->view->palette() ) );
00126     col->setHeaderTextVisible(false);
00127     col->field()->setSubType("KIcon");
00128     col->setReadOnly(true);
00129     d->data->addColumn( col );
00130 
00131 //  col = new KexiTableViewColumn("name", KexiDB::Field::Text, i18n("Field Name"),
00132     col = new KexiTableViewColumn("caption", KexiDB::Field::Text, i18n("Field Caption"),
00133         i18n("Describes caption for the field"));
00134 //  KexiUtils::Validator *vd = new KexiUtils::IdentifierValidator();
00135 //  vd->setAcceptsEmptyValue(true);
00136 //  col->setValidator( vd );
00137     d->data->addColumn( col );
00138 
00139     col = new KexiTableViewColumn("type", KexiDB::Field::Enum, i18n("Data Type"),
00140         i18n("Describes data type for the field"));
00141     d->data->addColumn( col );
00142 
00143 #ifdef KEXI_NO_BLOB_FIELDS
00145     QValueVector<QString> types(KexiDB::Field::LastTypeGroup-1); //don't show last type (BLOB)
00146 #else
00147     QValueVector<QString> types(KexiDB::Field::LastTypeGroup);
00148 #endif
00149     d->maxTypeNameTextWidth = 0;
00150     QFontMetrics fm(font());
00151     for (uint i=1; i<=types.count(); i++) {
00152         types[i-1] = KexiDB::Field::typeGroupName(i);
00153         d->maxTypeNameTextWidth = QMAX(d->maxTypeNameTextWidth, fm.width(types[i-1]));
00154     }
00155     col->field()->setEnumHints(types);
00156 
00157     d->data->addColumn( col = new KexiTableViewColumn("comments", KexiDB::Field::Text, i18n("Comments"),
00158         i18n("Describes additional comments for the field")) );
00159 
00160     d->view->setSpreadSheetMode();
00161 
00162     connect(d->data, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)),
00163         this, SLOT(slotBeforeCellChanged(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)));
00164     connect(d->data, SIGNAL(rowUpdated(KexiTableItem*)),
00165         this, SLOT(slotRowUpdated(KexiTableItem*)));
00166     //connect(d->data, SIGNAL(aboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)),
00167     //  this, SLOT(slotAboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)));
00168     connect(d->data, SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
00169         this, SLOT(slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)));
00170 
00171     setMinimumSize(d->view->minimumSizeHint().width(), d->view->minimumSizeHint().height());
00172     d->view->setFocus();
00173 
00174     d->sets = new KexiDataAwarePropertySet( this, d->view );
00175     connect(d->sets, SIGNAL(rowDeleted()), this, SLOT(updateActions()));
00176     connect(d->sets, SIGNAL(rowInserted()), this, SLOT(slotRowInserted()));
00177 
00178     d->contextMenuTitle = new KPopupTitle(d->view->contextMenu());
00179     d->view->contextMenu()->insertItem(d->contextMenuTitle, -1, 0);
00180     connect(d->view->contextMenu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowContextMenu()));
00181 
00182     plugSharedAction("tablepart_toggle_pkey", this, SLOT(slotTogglePrimaryKey()));
00183     d->action_toggle_pkey = static_cast<KToggleAction*>( sharedAction("tablepart_toggle_pkey") );
00184     d->action_toggle_pkey->plug(d->view->contextMenu(), 1); //add at the beginning
00185     setAvailable("tablepart_toggle_pkey", !conn->isReadOnly());
00186 
00187 #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
00188     plugSharedAction("edit_undo", this, SLOT(slotUndo()));
00189     plugSharedAction("edit_redo", this, SLOT(slotRedo()));
00190     setAvailable("edit_undo", false);
00191     setAvailable("edit_redo", false);
00192     connect(d->history, SIGNAL(commandExecuted(KCommand*)), this, SLOT(slotCommandExecuted(KCommand*)));
00193 #endif
00194 
00195 #ifdef KEXI_DEBUG_GUI
00196     KexiUtils::addAlterTableActionDebug(QString::null); //to create the tab
00197     KexiUtils::connectPushButtonActionForDebugWindow( 
00198         "simulateAlterTableExecution", this, SLOT(slotSimulateAlterTableExecution()));
00199     KexiUtils::connectPushButtonActionForDebugWindow( 
00200         "executeRealAlterTable", this, SLOT(executeRealAlterTable()));
00201 #endif
00202 }
00203 
00204 KexiTableDesignerView::~KexiTableDesignerView()
00205 {
00206 //  removeCurrentPropertySet();
00207     delete d;
00208 }
00209 
00210 void KexiTableDesignerView::initData()
00211 {
00212     //add column data
00213 //  d->data->clear();
00214     d->data->deleteAllRows();
00215     int tableFieldCount = 0;
00216     d->primaryKeyExists = false;
00217 
00218     if (tempData()->table) {
00219         tableFieldCount = tempData()->table->fieldCount();
00220 //not needed        d->sets->clear(tableFieldCount);
00221 
00222         //recreate table data rows
00223         for(int i=0; i < tableFieldCount; i++) {
00224             KexiDB::Field *field = tempData()->table->field(i);
00225             KexiTableItem *item = d->data->createItem(); //new KexiTableItem(0);
00226             if (field->isPrimaryKey()) {
00227                 (*item)[COLUMN_ID_ICON] = "key";
00228                 d->primaryKeyExists = true;
00229             }
00230             else {
00231                 KexiDB::LookupFieldSchema *lookupFieldSchema
00232                     = field->table() ? field->table()->lookupFieldSchema(*field) : 0;
00233                 if (lookupFieldSchema && lookupFieldSchema->rowSource().type()!=KexiDB::LookupFieldSchema::RowSource::NoType
00234                     && !lookupFieldSchema->rowSource().name().isEmpty())
00235                 {
00236                     (*item)[COLUMN_ID_ICON] = "combo";
00237                 }
00238             }
00239             (*item)[COLUMN_ID_CAPTION] = field->captionOrName();
00240             (*item)[COLUMN_ID_TYPE] = field->typeGroup()-1; //-1 because type groups are counted from 1
00241             (*item)[COLUMN_ID_DESC] = field->description();
00242             d->data->append(item);
00243 
00244 //later!            createPropertySet( i, field );
00245         }
00246     }
00247 //  else {
00248 //      d->sets->clear();//default size
00249 //  }
00250 
00251     //add empty space
00252 //  const int columnsCount = d->data->columnsCount();
00253     for (int i=tableFieldCount; i<(int)d->sets->size(); i++) {
00254 //      KexiTableItem *item = new KexiTableItem(columnsCount);//3 empty fields
00255         d->data->append(d->data->createItem());
00256     }
00257 
00258     //set data for our spreadsheet: this will clear our sets
00259     d->view->setData(d->data);
00260 
00261     //now recreate property sets
00262     if (tempData()->table) {
00263         for(int i=0; i < tableFieldCount; i++) {
00264             KexiDB::Field *field = tempData()->table->field(i);
00265             createPropertySet( i, *field );
00266         }
00267     }
00268 
00269     //column widths
00270     d->view->setColumnWidth(COLUMN_ID_ICON, IconSize( KIcon::Small ) + 10);
00271     d->view->adjustColumnWidthToContents(COLUMN_ID_CAPTION); //adjust column width
00272     d->view->setColumnWidth(COLUMN_ID_TYPE, d->maxTypeNameTextWidth + 2 * d->view->rowHeight());
00273     d->view->setColumnStretchEnabled( true, COLUMN_ID_DESC ); //last column occupies the rest of the area
00274     const int minCaptionColumnWidth = d->view->fontMetrics().width("wwwwwwwwwww");
00275     if (minCaptionColumnWidth > d->view->columnWidth(COLUMN_ID_CAPTION))
00276         d->view->setColumnWidth(COLUMN_ID_CAPTION, minCaptionColumnWidth);
00277 
00278     setDirty(false);
00279     d->view->setCursorPosition(0, COLUMN_ID_CAPTION); //set @ name column
00280     propertySetSwitched();
00281 }
00282 
00284 void
00285 KexiTableDesignerView::getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup, 
00286     QStringList& stringsList, QStringList& namesList)
00287 {
00288 /* disabled - "mime" is moved from subType to "objectType" custom property
00289     if (fieldTypeGroup==KexiDB::Field::BLOBGroup) {
00290         // special case: BLOB type uses "mime-based" subtypes
00292         stringsList << "image";
00293         namesList << i18n("Image object type", "Image");
00294     }
00295     else {*/
00296     stringsList = KexiDB::typeStringsForGroup(fieldTypeGroup);
00297     namesList = KexiDB::typeNamesForGroup(fieldTypeGroup);
00298 //  }
00299     kexipluginsdbg << "KexiTableDesignerView::getSubTypeListData(): subType strings: " << 
00300         stringsList.join("|") << "\nnames: " << namesList.join("|") << endl;
00301 }
00302 
00303 KoProperty::Set *
00304 KexiTableDesignerView::createPropertySet( int row, const KexiDB::Field& field, bool newOne )
00305 {
00306     QString typeName = "KexiDB::Field::" + field.typeGroupString();
00307     KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
00308     if (mainWin()->project()->dbConnection()->isReadOnly())
00309         set->setReadOnly( true );
00310 //  connect(buff,SIGNAL(propertyChanged(KexiPropertyBuffer&,KexiProperty&)),
00311 //      this, SLOT(slotPropertyChanged(KexiPropertyBuffer&,KexiProperty&)));
00312 
00313     KoProperty::Property *prop;
00314 
00315     set->addProperty(prop = new KoProperty::Property("uid", d->generateUniqueId(), ""));
00316     prop->setVisible(false);
00317 
00318     //meta-info for property editor
00319     set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Table field")) );
00320     prop->setVisible(false);
00321     set->addProperty(prop = new KoProperty::Property("this:iconName", 
00323         "lineedit" //"table_field"
00324     ));
00325     prop->setVisible(false);
00326     set->addProperty(prop = new KoProperty::Property("this:useCaptionAsObjectName", 
00327         QVariant(true, 1), QString::null)); //we want "caption" to be displayed in the header, not name
00328     prop->setVisible(false);
00329 
00330     //name
00331     set->addProperty(prop 
00332         = new KoProperty::Property("name", QVariant(field.name()), i18n("Name"), 
00333         QString::null, KexiCustomPropertyFactory::Identifier) );
00334 
00335     //type
00336     set->addProperty( prop 
00337         = new KoProperty::Property("type", QVariant(field.type()), i18n("Type")) );
00338 #ifndef KexiTableDesignerView_DEBUG
00339     prop->setVisible(false);//always hidden
00340 #endif
00341 
00342     //subtype
00343     QStringList typeStringList, typeNameList;
00344     getSubTypeListData(field.typeGroup(), typeStringList, typeNameList);
00345 /* disabled - "mime" is moved from subType to "objectType" custom property
00346     QString subTypeValue;
00347     if (field.typeGroup()==KexiDB::Field::BLOBGroup) {
00348 // special case: BLOB type uses "mime-based" subtypes
00350         subTypeValue = slist.first();
00351     }
00352     else {*/
00353     QString subTypeValue = field.typeString();
00354     //}
00355     set->addProperty(prop = new KoProperty::Property("subType", 
00356         typeStringList, typeNameList, subTypeValue, i18n("Subtype")));
00357 
00358     // objectType
00359     QStringList objectTypeStringList, objectTypeNameList;
00361     objectTypeStringList << "image";
00362     objectTypeNameList << i18n("Image object type", "Image");
00363     QString objectTypeValue( field.customProperty("objectType").toString() );
00364     if (objectTypeValue.isEmpty())
00365         objectTypeValue = DEFAULT_OBJECT_TYPE_VALUE;
00366     set->addProperty(prop = new KoProperty::Property("objectType", 
00367         objectTypeStringList, objectTypeNameList, objectTypeValue, i18n("Subtype")/*todo other i18n string?*/));
00368     
00369     set->addProperty( prop 
00370         = new KoProperty::Property("caption", QVariant(field.caption()), i18n("Caption") ) );
00371     prop->setVisible(false);//always hidden
00372 
00373     set->addProperty( prop 
00374         = new KoProperty::Property("description", QVariant(field.description())) );
00375     prop->setVisible(false);//always hidden
00376 
00377     set->addProperty(prop 
00378         = new KoProperty::Property("unsigned", QVariant(field.isUnsigned(), 4), i18n("Unsigned Number")));
00379 
00380     set->addProperty( prop 
00381         = new KoProperty::Property("length", (int)field.length()/*200?*/, i18n("Length")));
00382 
00383     set->addProperty( prop 
00384         = new KoProperty::Property("precision", (int)field.precision()/*200?*/, i18n("Precision")));
00385 #ifdef KEXI_NO_UNFINISHED
00386     prop->setVisible(false);
00387 #endif
00388     set->addProperty( prop 
00389         = new KoProperty::Property("visibleDecimalPlaces", field.visibleDecimalPlaces(), i18n("Visible Decimal Places")));
00390     prop->setOption("min", -1);
00391     prop->setOption("minValueText", i18n("Auto Decimal Places","Auto"));
00392 
00394     set->addProperty( prop 
00395         = new KoProperty::Property("width", (int)field.width()/*200?*/, i18n("Column Width")));
00396 #ifdef KEXI_NO_UNFINISHED
00397     prop->setVisible(false);
00398 #endif
00399 
00400     set->addProperty( prop 
00401         = new KoProperty::Property("defaultValue", field.defaultValue(), i18n("Default Value"),
00402             QString::null, 
00404             (KoProperty::PropertyType)field.variantType()) );
00405     prop->setOption("3rdState", i18n("None"));
00406 //  prop->setVisible(false);
00407 
00408     set->addProperty( prop 
00409         = new KoProperty::Property("primaryKey", QVariant(field.isPrimaryKey(), 4), i18n("Primary Key")));
00410     prop->setIcon("key");
00411 
00412     set->addProperty( prop
00413         = new KoProperty::Property("unique", QVariant(field.isUniqueKey(), 4), i18n("Unique")));
00414 
00415     set->addProperty( prop
00416         = new KoProperty::Property("notNull", QVariant(field.isNotNull(), 4), i18n("Required")));
00417     
00418     set->addProperty( prop
00419         = new KoProperty::Property("allowEmpty", QVariant(!field.isNotEmpty(), 4), i18n("Allow Zero\nSize")));
00420 
00421     set->addProperty( prop
00422         = new KoProperty::Property("autoIncrement", QVariant(field.isAutoIncrement(), 4), i18n("Autonumber")));
00423     prop->setIcon("autonumber");
00424 
00425     set->addProperty( prop
00426         = new KoProperty::Property("indexed", QVariant(field.isIndexed(), 4), i18n("Indexed")));
00427 
00428     //- properties related to lookup columns (used and set by the "lookup column" tab in the property pane)
00429     KexiDB::LookupFieldSchema *lookupFieldSchema = field.table() ? field.table()->lookupFieldSchema(field) : 0;
00430     set->addProperty( prop = new KoProperty::Property("rowSource", 
00431         lookupFieldSchema ? lookupFieldSchema->rowSource().name() : QString::null, i18n("Row Source")));
00432     prop->setVisible(false);
00433 
00434     set->addProperty( prop = new KoProperty::Property("rowSourceType", 
00435         lookupFieldSchema ? lookupFieldSchema->rowSource().typeName() : QString::null, i18n("Row Source\nType")));
00436     prop->setVisible(false);
00437 
00438     set->addProperty( prop
00439         = new KoProperty::Property("boundColumn", 
00440         lookupFieldSchema ? lookupFieldSchema->boundColumn() : -1, i18n("Bound Column")));
00441     prop->setVisible(false);
00442     
00446     int visibleColumn = -1;
00447     if (lookupFieldSchema && !lookupFieldSchema->visibleColumns().isEmpty())
00448         visibleColumn = lookupFieldSchema->visibleColumns().first();
00449     set->addProperty( prop
00450         = new KoProperty::Property("visibleColumn", visibleColumn, i18n("Visible Column")));
00451     prop->setVisible(false);
00452 
00454 
00455     //----
00456     d->updatePropertiesVisibility(field.type(), *set);
00457 
00458     connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
00459         this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
00460 
00461     d->sets->insert(row, set, newOne);
00462     return set;
00463 }
00464 
00465 void KexiTableDesignerView::updateActions(bool activated)
00466 {
00467     Q_UNUSED(activated);
00469     setAvailable("tablepart_toggle_pkey", propertySet()!=0 && !mainWin()->project()->dbConnection()->isReadOnly());
00470     if (!propertySet())
00471         return;
00472     KoProperty::Set &set = *propertySet();
00473     d->slotTogglePrimaryKeyCalled = true;
00474      d->action_toggle_pkey->setChecked(set["primaryKey"].value().toBool());
00475     d->slotTogglePrimaryKeyCalled = false;
00476 }
00477 
00478 void KexiTableDesignerView::slotUpdateRowActions(int row)
00479 {
00480     KexiDataTable::slotUpdateRowActions(row);
00481     updateActions();
00482 }
00483 
00484 void KexiTableDesignerView::slotTogglePrimaryKey()
00485 {
00486     if (d->slotTogglePrimaryKeyCalled)
00487         return;
00488     d->slotTogglePrimaryKeyCalled = true;
00489     if (!propertySet())
00490         return;
00491     KoProperty::Set &set = *propertySet();
00492     bool isSet = !set["primaryKey"].value().toBool();
00493     set.changeProperty("primaryKey", QVariant(isSet,1)); //this will update all related properties as well
00494 /*  CommandGroup *setPrimaryKeyCommand;
00495     if (isSet) {
00496         setPrimaryKeyCommand = new CommandGroup(i18n("Set primary key for field \"%1\"")
00497             .arg(set["name"].value().toString()) );
00498     }
00499     else {
00500         setPrimaryKeyCommand = new CommandGroup(i18n("Unset primary key for field \"%1\"")
00501             .arg(set["name"].value().toString()) );
00502     }
00503     switchPrimaryKey(set, isSet, false, setPrimaryKeyCommand);*/
00504     //addHistoryCommand( setPrimaryKeyCommand, false /* !execute */ );
00505     d->slotTogglePrimaryKeyCalled = false;
00506 }
00507 
00508 void KexiTableDesignerView::switchPrimaryKey(KoProperty::Set &propertySet, 
00509     bool set, bool aWasPKey, CommandGroup* commandGroup)
00510 {
00511     const bool was_pkey = aWasPKey || propertySet["primaryKey"].value().toBool();
00512 //  propertySet["primaryKey"] = QVariant(set, 1);
00513     d->setPropertyValueIfNeeded( propertySet, "primaryKey", QVariant(set,1), commandGroup );
00514     if (&propertySet==this->propertySet()) {
00515         //update action and icon @ column 0 (only if we're changing current property set)
00516         d->action_toggle_pkey->setChecked(set);
00517         if (d->view->selectedItem()) {
00518             //show key in the table
00519             d->view->data()->clearRowEditBuffer();
00520             d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_ICON, 
00521                 QVariant(set ? "key" : ""));
00522             d->view->data()->saveRowChanges(*d->view->selectedItem(), true);
00523         }
00524         if (was_pkey || set) //change flag only if we're setting pk or really clearing it
00525             d->primaryKeyExists = set;
00526     }
00527 
00528     if (set) {
00529         //primary key is set, remove old pkey if exists
00530         KoProperty::Set *s = 0;
00531         int i;
00532         const int count = (int)d->sets->size();
00533         for (i=0; i<count; i++) {
00534             s = d->sets->at(i);
00535             if (s && s!=&propertySet && (*s)["primaryKey"].value().toBool() && i!=d->view->currentRow())
00536                 break;
00537         }
00538         if (i<count) {//remove
00539             //(*s)["autoIncrement"] = QVariant(false, 0);
00540             d->setPropertyValueIfNeeded( *s, "autoIncrement", QVariant(false,0), commandGroup );
00541             //(*s)["primaryKey"] = QVariant(false, 0);
00542             d->setPropertyValueIfNeeded( *s, "primaryKey", QVariant(false,0), commandGroup );
00543             //remove key from table
00544             d->view->data()->clearRowEditBuffer();
00545             KexiTableItem *item = d->view->itemAt(i);
00546             if (item) {
00547                 d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
00548                 d->view->data()->saveRowChanges(*item, true);
00549             }
00550         }
00551         //set unsigned big-integer type
00552 //      d->view->data()->saveRowChanges(*d->view->selectedItem());
00553         d->slotBeforeCellChanged_enabled = false;
00554             d->view->data()->clearRowEditBuffer();
00555             d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
00556             QVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
00557 //          QVariant(KexiDB::Field::typeGroupName(KexiDB::Field::IntegerGroup)));
00558             d->view->data()->saveRowChanges(*d->view->selectedItem(), true);
00559         //propertySet["subType"] = KexiDB::Field::typeString(KexiDB::Field::BigInteger);
00560             d->setPropertyValueIfNeeded( propertySet, "subType", KexiDB::Field::typeString(KexiDB::Field::BigInteger), 
00561                 commandGroup );
00562         //propertySet["unsigned"] = QVariant(true,4);
00563             d->setPropertyValueIfNeeded( propertySet, "unsigned", QVariant(true,4), commandGroup );
00564 /*todo*/
00565         d->slotBeforeCellChanged_enabled = true;
00566     }
00567     updateActions();
00568 }
00569 
00570 /*void KexiTableDesignerView::slotCellSelected(int, int row)
00571 {
00572     kdDebug() << "KexiTableDesignerView::slotCellSelected()" << endl;
00573     if(row == m_row)
00574         return;
00575     m_row = row;
00576     propertyBufferSwitched();
00577 }*/
00578 
00579 tristate KexiTableDesignerView::beforeSwitchTo(int mode, bool &dontStore)
00580 {
00581     if (!d->view->acceptRowEdit())
00582         return false;
00583 /*  if (mode==Kexi::DesignViewMode) {
00584         initData();
00585         return true;
00586     }
00587     else */
00588     tristate res = true;
00589     if (mode==Kexi::DataViewMode) {
00590         if (!dirty() && parentDialog()->neverSaved()) {
00591             KMessageBox::sorry(this, i18n("Cannot switch to data view, because table design is empty.\n"
00592                 "First, please create your design.") );
00593             return cancelled;
00594         }
00595 //<temporary>
00596         else if (dirty() && !parentDialog()->neverSaved()) {
00597 //          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?")));
00598 
00599 //          KexiDB::Connection *conn = mainWin()->project()->dbConnection();
00600             bool emptyTable;
00601             int r = KMessageBox::warningYesNoCancel(this,
00602                 i18n("Saving changes for existing table design is now required.")
00603                 + "\n" + d->messageForSavingChanges(emptyTable), QString::null,
00604                 KStdGuiItem::save(), KStdGuiItem::discard(), QString::null, 
00605                 KMessageBox::Notify|KMessageBox::Dangerous);
00606             if (r == KMessageBox::Cancel)
00607                 res = cancelled;
00608             else
00609                 res = true;
00610             dontStore = (r!=KMessageBox::Yes);
00611             if (!dontStore)
00612                 d->dontAskOnStoreData = true;
00613 //          if (dontStore)
00614 //              setDirty(false);
00615         }
00616 //</temporary>
00617         //todo
00618         return res;
00619     }
00620     else if (mode==Kexi::TextViewMode) {
00621         //todo
00622     }
00623     return res;
00624 }
00625 
00626 tristate KexiTableDesignerView::afterSwitchFrom(int mode)
00627 {
00628     if (mode==Kexi::NoViewMode || mode==Kexi::DataViewMode) {
00629         initData();
00630     }
00631     return true;
00632 }
00633 
00634 KoProperty::Set *KexiTableDesignerView::propertySet()
00635 {
00636     return d->sets ? d->sets->currentPropertySet() : 0;
00637 }
00638 
00639 /*
00640 void KexiTableDesignerView::removeCurrentPropertySet()
00641 {
00642     const int r = d->view->currentRow();
00643     KoProperty::Set *buf = d->sets.at(r);
00644     if (!buf)
00645         return;
00646     buf->debug();
00647 //  m_currentBufferCleared = true;
00648     d->sets.remove(r);
00649     propertysetswitched();
00650 //  delete buf;
00651 //  m_currentBufferCleared = false;
00652 }
00653 */
00654 
00655 void KexiTableDesignerView::slotBeforeCellChanged(
00656     KexiTableItem *item, int colnum, QVariant& newValue, KexiDB::ResultInfo* /*result*/)
00657 {
00658     if (!d->slotBeforeCellChanged_enabled)
00659         return;
00660 //  kdDebug() << d->view->selectedItem() << " " << item 
00661         //<< " " << d->sets->at( d->view->currentRow() ) << " " << propertySet() << endl;
00662     if (colnum==COLUMN_ID_CAPTION) {//'caption'
00663 //      if (!item->at(1).toString().isEmpty() && item->at(1).isNull()) {
00664         //if 'type' is not filled yet
00665         if (item->at(COLUMN_ID_TYPE).isNull()) {
00666             //auto select 1st row of 'type' column
00667             d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant((int)0));
00668         }
00669 
00670         KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
00671         if (propertySetForItem) {
00672             d->addHistoryCommand_in_slotPropertyChanged_enabled = false; //because we'll add the two changes as one KMacroCommand
00673                 QString oldName( propertySetForItem->property("name").value().toString() );
00674                 QString oldCaption( propertySetForItem->property("caption").value().toString() );
00675 
00676                 //we need to create the action now as set["name"] will be changed soon..
00677                 ChangeFieldPropertyCommand *changeCaptionCommand
00678                     = new ChangeFieldPropertyCommand( this, *propertySetForItem, "caption", oldCaption, newValue);
00679 
00680                 //update field caption and name
00681                 propertySetForItem->changeProperty("caption", newValue);
00682                 propertySetForItem->changeProperty("name", newValue); // "name" prop. is of custom type Identifier, so this assignment 
00683                                                     // will automatically convert newValue to an valid identifier
00684 
00685                 //remember this action containing 2 subactions
00686                 CommandGroup *changeCaptionAndNameCommand = new CommandGroup(
00687                     i18n("Change \"%1\" field's name to \"%2\" and caption from \"%3\" to \"%4\"")
00688                         .arg(oldName).arg(propertySetForItem->property("name").value().toString())
00689                         .arg(oldCaption).arg(newValue.toString() ));
00690                 changeCaptionAndNameCommand->addCommand( changeCaptionCommand );
00691 //                  new ChangeFieldPropertyCommand( this, *propertySetForItem,
00692     //                      "caption", oldCaption, newValue)
00693         //      );
00694                 changeCaptionAndNameCommand->addCommand(
00695                     new ChangeFieldPropertyCommand( this, *propertySetForItem,
00696                             "name", oldName, propertySetForItem->property("name").value().toString())
00697                 );
00698                 addHistoryCommand( changeCaptionAndNameCommand, false /* !execute */ );
00699 
00700             d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
00701         }
00702     }
00703     else if (colnum==COLUMN_ID_TYPE) {//'type'
00704         if (newValue.isNull()) {
00705             //'type' col will be cleared: clear all other columns as well
00706             d->slotBeforeCellChanged_enabled = false;
00707                 d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
00708                 d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, QVariant(QString::null));
00709                 d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC, QVariant());
00710             d->slotBeforeCellChanged_enabled = true;
00711             return;
00712         }
00713 
00714         KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
00715         if (!propertySetForItem)
00716             return;
00717 
00718         KoProperty::Set &set = *propertySetForItem; //propertySet();
00719 
00720         //'type' col is changed (existed before)
00721         //-get type group number
00722         KexiDB::Field::TypeGroup fieldTypeGroup;
00723         int i_fieldTypeGroup = newValue.toInt()+1/*counting from 1*/;
00724         if (i_fieldTypeGroup < 1 || i_fieldTypeGroup >
00725 #ifdef KEXI_NO_BLOB_FIELDS
00727             (int)KexiDB::Field::LastTypeGroup-1) //don't show last (BLOB) type
00728 #else
00729             (int)KexiDB::Field::LastTypeGroup)
00730 #endif
00731             return;
00732         fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(i_fieldTypeGroup);
00733 
00734         //-get 1st type from this group, and update 'type' property
00735         KexiDB::Field::Type fieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
00736         if (fieldType==KexiDB::Field::InvalidType)
00737             fieldType = KexiDB::Field::Text;
00738 //moved down        set["type"] = (int)fieldType;
00739 //      set["subType"] = KexiDB::Field::typeName(fieldType);
00740         
00741         //-get subtypes for this type: keys (slist) and names (nlist)
00742         QStringList slist, nlist;
00743         getSubTypeListData(fieldTypeGroup, slist, nlist);
00744 
00745         QString subTypeValue;
00746 /* disabled - "mime" is moved from subType to "objectType" custom property
00747         if (fieldType==KexiDB::Field::BLOB) {
00748             // special case: BLOB type uses "mime-based" subtypes
00749             subTypeValue = slist.first();
00750         }
00751         else {*/
00752             subTypeValue = KexiDB::Field::typeString(fieldType);
00753         //}
00754         KoProperty::Property *subTypeProperty = &set["subType"];
00755         kexipluginsdbg << subTypeProperty->value() << endl;
00756         
00757         // *** this action contains subactions ***
00758         CommandGroup *changeDataTypeCommand = new CommandGroup(
00759             i18n("Change data type for field \"%1\" to \"%2\"")
00760                 .arg(set["name"].value().toString()).arg( KexiDB::Field::typeName( fieldType ) ) );
00761 
00762 //kexipluginsdbg << "++++++++++" << slist << nlist << endl;
00763 
00764         //update subtype list and value
00765         const bool forcePropertySetReload 
00766             = KexiDB::Field::typeGroup( KexiDB::Field::typeForString(subTypeProperty->value().toString()) )
00767                 != fieldTypeGroup; //<-- ?????
00768 //      const bool forcePropertySetReload = set["type"].value().toInt() != (int)fieldTypeGroup;
00769         const bool useListData = slist.count() > 1; //disabled-> || fieldType==KexiDB::Field::BLOB;
00770 
00771         if (!useListData) {
00772             slist.clear(); //empty list will be passed
00773             nlist.clear();
00774         }
00775         d->setPropertyValueIfNeeded( set, "type", (int)fieldType, changeDataTypeCommand,
00776             false , true /*rememberOldValue*/);
00777 
00778         // notNull and defaultValue=false is reasonable for boolean type
00779         if (fieldType == KexiDB::Field::Boolean) {
00781             d->setPropertyValueIfNeeded( set, "notNull", QVariant(true, 1), changeDataTypeCommand,
00782                 false , false );
00783             d->setPropertyValueIfNeeded( set, "defaultValue", QVariant(false, 1), changeDataTypeCommand,
00784                 false , false );
00785         }
00786 
00787 /*      if (useListData) {
00788         {
00789             subTypeProperty->setListData( slist, nlist );
00790         }
00791         else {
00792             subTypeProperty->setListData( 0 );
00793         }*/
00794         if (set["primaryKey"].value().toBool()==true) {
00795             //primary keys require big int, so if selected type is not integer- remove PK
00796             if (fieldTypeGroup != KexiDB::Field::IntegerGroup) {
00797                 /*not needed, line below will do the work
00798                 d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
00799                 d->view->data()->saveRowChanges(*item); */
00800                 //set["primaryKey"] = QVariant(false, 1);
00801                 d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(false, 1), changeDataTypeCommand );
00803             }
00804         }
00805 //      if (useListData)
00806 //      subTypeProperty->setValue( subTypeValue, false/*!rememberOldValue*/ );
00807         d->setPropertyValueIfNeeded( set, "subType", subTypeValue, 
00808             changeDataTypeCommand, false, false ,
00809             &slist, &nlist );
00810 
00811         if (d->updatePropertiesVisibility(fieldType, set, changeDataTypeCommand) || forcePropertySetReload) {
00812             //properties' visiblility changed: refresh prop. set
00813             propertySetReloaded(true);
00814         }
00815 
00816         addHistoryCommand( changeDataTypeCommand, false /* !execute */ );
00817     }
00818     else if (colnum==COLUMN_ID_DESC) {//'description'
00819         KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
00820         if (!propertySetForItem)
00821             return;
00822         //update field desc.
00823         QVariant oldValue((*propertySetForItem)["description"].value());
00824         kexipluginsdbg << oldValue << endl;
00825         propertySetForItem->changeProperty("description", newValue);
00826         /*moved addHistoryCommand( 
00827             new ChangeFieldPropertyCommand( this, *propertySetForItem,
00828                 "description", oldValue, newValue ), false);*/
00829     }
00830 }
00831 
00832 void KexiTableDesignerView::slotRowUpdated(KexiTableItem *item)
00833 {
00834     const int row = d->view->data()->findRef(item);
00835     if (row < 0)
00836         return;
00837     
00838     setDirty();
00839 
00840     //-check if the row was empty before updating
00841     //if yes: we want to add a property set for this new row (field)
00842     QString fieldCaption( item->at(COLUMN_ID_CAPTION).toString() );
00843     const bool prop_set_allowed = !item->at(COLUMN_ID_TYPE).isNull();
00844 
00845     if (!prop_set_allowed && d->sets->at(row)/*propertySet()*/) {
00846         //there is a property set, but it's not allowed - remove it:
00847         d->sets->remove( row ); //d->sets->removeCurrentPropertySet();
00848 
00849         //clear 'type' column:
00850         d->view->data()->clearRowEditBuffer();
00851 //      d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE, QVariant());
00852         d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant());
00853         d->view->data()->saveRowChanges(*item);
00854 
00855     } else if (prop_set_allowed && !d->sets->at(row)/*propertySet()*/) {
00856         //-- create a new field:
00857         KexiDB::Field::TypeGroup fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>( 
00858             item->at(COLUMN_ID_TYPE).toInt()+1/*counting from 1*/ );
00859         int intFieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
00860         if (intFieldType==0)
00861             return;
00862 
00863         QString description( item->at(COLUMN_ID_DESC).toString() );
00864 
00865 //todo: check uniqueness:
00866         QString fieldName( KexiUtils::string2Identifier(fieldCaption) );
00867 
00868         KexiDB::Field::Type fieldType = KexiDB::intToFieldType( intFieldType );
00869         KexiDB::Field field( //tmp
00870             fieldName,
00871             fieldType,
00872             KexiDB::Field::NoConstraints,
00873             KexiDB::Field::NoOptions,
00874             /*length*/0,
00875             /*precision*/0,
00876             /*defaultValue*/QVariant(),
00877             fieldCaption,
00878             description,
00879             /*width*/0);
00880 //      m_newTable->addField( field );
00881 
00882         // reasonable case for boolean type: set notNull flag and "false" as default value
00883         if (fieldType == KexiDB::Field::Boolean) {
00884             field.setNotNull( true );
00885             field.setDefaultValue( QVariant(false, 0) );
00886         }
00887 
00888         kexipluginsdbg << "KexiTableDesignerView::slotRowUpdated(): " << field.debugString() << endl;
00889 
00890         //create a new property set:
00891         KoProperty::Set *newSet = createPropertySet( row, field, true );
00892 //moved
00893         //add a special property indicating that this is brand new buffer,
00894         //not just changed
00895 //      KoProperty::Property* prop = new KoProperty::Property("newrow", QVariant());
00896 //      prop->setVisible(false);
00897 //      newbuff->addProperty( prop );
00898 
00899         //refresh property editor:
00900         propertySetSwitched();
00901 
00902         if (row>=0) {
00903             if (d->addHistoryCommand_in_slotRowUpdated_enabled) {
00904                 addHistoryCommand( new InsertFieldCommand( this, row, *newSet /*propertySet()*/ ), //, field /*will be copied*/ 
00905                     false /* !execute */ );
00906             }
00907         }
00908         else {
00909             kexipluginswarn << "KexiTableDesignerView::slotRowUpdated() row # not found  !" << endl;
00910         }
00911     }
00912 }
00913 
00914 void KexiTableDesignerView::updateActions()
00915 {
00916     updateActions(false);
00917 }
00918 
00919 void KexiTableDesignerView::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
00920 {
00921 //  if (!d->slotPropertyChanged_enabled)
00922 //      return;
00923     const QCString pname = property.name();
00924     kexipluginsdbg << "KexiTableDesignerView::slotPropertyChanged(): " << pname << " = " << property.value() 
00925         << " (oldvalue = " << property.oldValue() << ")" << endl;
00926 
00927     // true is PK should be altered
00928     bool changePrimaryKey = false;
00929     // true is PK should be set to true, otherwise unset
00930     bool setPrimaryKey = false;
00931 
00932     if (pname=="primaryKey" && d->slotPropertyChanged_primaryKey_enabled) {
00933         changePrimaryKey = true;
00934         setPrimaryKey = property.value().toBool();
00935     }
00936 
00937     // update "lookup column" icon
00938     if (pname=="rowSource" || pname=="rowSourceType") {
00941         const int row = d->sets->findRowForPropertyValue("uid", set["uid"].value().toInt());
00942         KexiTableItem *item = d->view->itemAt(row);
00943         if (item)
00944             d->updateIconForItem(*item, set);
00945     }
00946 
00947     //setting autonumber requires setting PK as well
00948     CommandGroup *setAutonumberCommand = 0;
00949     CommandGroup *toplevelCommand = 0;
00950     if (pname=="autoIncrement" && property.value().toBool()==true) {
00951         if (set["primaryKey"].value().toBool()==false) {//we need PKEY here!
00952             QString msg = QString("<p>")
00953                 +i18n("Setting autonumber requires primary key to be set for current field.")+"</p>";
00954             if (d->primaryKeyExists)
00955                 msg += (QString("<p>")+ i18n("Previous primary key will be removed.")+"</p>");
00956             msg += (QString("<p>")
00957                 +i18n("Do you want to create primary key for current field? "
00958                 "Click \"Cancel\" to cancel setting autonumber.")+"</p>");
00959 
00960             if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
00961                 i18n("Setting Autonumber Field"),
00962                 KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
00963             {
00964                 changePrimaryKey = true;
00965                 setPrimaryKey = true;
00966                 //switchPrimaryKey(set, true);
00967                 // this will be toplevel command 
00968                 setAutonumberCommand = new CommandGroup(
00969                     i18n("Assign autonumber for field \"%1\"").arg(set["name"].value().toString()) );
00970                 toplevelCommand = setAutonumberCommand;
00971                 d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(true,1), setAutonumberCommand );
00972             }
00973             else {
00974                 setAutonumberCommand = new CommandGroup(
00975                     i18n("Remove autonumber from field \"%1\"").arg(set["name"].value().toString()) );
00976                 //d->slotPropertyChanged_enabled = false;
00977 //                  set["autoIncrement"].setValue( QVariant(false,1), false/*don't save old*/);
00978 //                  d->slotPropertyChanged_enabled = true;
00979                 d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(false,1), setAutonumberCommand, 
00980                     true /*forceAddCommand*/, false/*rememberOldValue*/ );
00981                 addHistoryCommand( setAutonumberCommand, false /* !execute */ );
00982                 return;
00983             }
00984         }
00985     }
00986 
00987     //clear PK when these properties were set to false:
00988     if ((pname=="indexed" || pname=="unique" || pname=="notNull") 
00989         && set["primaryKey"].value().toBool() && property.value().toBool()==false)
00990     {
00992         changePrimaryKey = true;
00993         setPrimaryKey = false;
00994         // this will be toplevel command 
00995         CommandGroup *unsetIndexedOrUniquOrNotNullCommand = new CommandGroup(
00996             i18n("Set \"%1\" property for field \"%2\"").arg(property.caption()).arg(set["name"].value().toString()) );
00997         toplevelCommand = unsetIndexedOrUniquOrNotNullCommand;
00998         d->setPropertyValueIfNeeded( set, pname, QVariant(false,1), unsetIndexedOrUniquOrNotNullCommand );
00999         if (pname=="notNull") {
01000 //?         d->setPropertyValueIfNeeded( set, "notNull", QVariant(true,1), unsetIndexedOrUniquOrNotNullCommand );
01001             d->setPropertyValueIfNeeded( set, "unique", QVariant(false,1), unsetIndexedOrUniquOrNotNullCommand );
01002         }
01003     }
01004 
01005     if (pname=="defaultValue") {
01006         KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
01007         set["defaultValue"].setType((KoProperty::PropertyType)KexiDB::Field::variantType(type));
01008     }
01009 
01010     if (pname=="subType" && d->slotPropertyChanged_subType_enabled) {
01011         d->slotPropertyChanged_subType_enabled = false;
01012         if (set["primaryKey"].value().toBool()==true 
01013             && property.value().toString()!=KexiDB::Field::typeString(KexiDB::Field::BigInteger))
01014         {
01015             kexipluginsdbg << "INVALID " << property.value().toString() << endl;
01016 //          if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
01017 //              i18n("This field has promary key assigned. Setting autonumber field"),
01018 //              KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
01019 
01020         }
01021         KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
01022         QString typeName;
01023 /* disabled - "mime" is moved from subType to "objectType" custom property
01024         if (type==KexiDB::Field::BLOB) { //special case
01025             //find i18n'd text
01026             QStringList stringsList, namesList;
01027             getSubTypeListData(KexiDB::Field::BLOBGroup, stringsList, namesList);
01028             const int stringIndex = stringsList.findIndex( property.value().toString() );
01029             if (-1 == stringIndex || stringIndex>=(int)namesList.count())
01030                 typeName = property.value().toString(); //for sanity
01031             else
01032                 typeName = namesList[stringIndex];
01033         }
01034         else {*/
01035         typeName = KexiDB::Field::typeName( KexiDB::Field::typeForString(property.value().toString()) );
01036 //      }
01037 //      kdDebug() << property.value().toString() << endl;
01038 //      kdDebug() << set["type"].value() << endl;
01039 //      if (KexiDB::Field::typeGroup( set["type"].value().toInt() ) == (int)KexiDB::Field::TextGroup) {
01040         CommandGroup* changeFieldTypeCommand = new CommandGroup(
01041             i18n("Change type for field \"%1\" to \"%2\"").arg(set["name"].value().toString())
01042             .arg(typeName) );
01043         d->setPropertyValueIfNeeded( set, "subType", property.value(), property.oldValue(),
01044             changeFieldTypeCommand );
01045 
01046         kexipluginsdbg << set["type"].value() << endl;
01047         const KexiDB::Field::Type newType = KexiDB::Field::typeForString(property.value().toString());
01048         set["type"].setValue( newType );
01049 
01050         // cast "defaultValue" property value to a new type
01051         QVariant oldDefVal( set["defaultValue"].value() );
01052         QVariant newDefVal( tryCastQVariant(oldDefVal, KexiDB::Field::variantType(type)) );
01053         if (oldDefVal.type()!=newDefVal.type())
01054             set["defaultValue"].setType( newDefVal.type() );
01055         d->setPropertyValueIfNeeded( set, "defaultValue", newDefVal, newDefVal,
01056             changeFieldTypeCommand );
01057 
01058         d->updatePropertiesVisibility(newType, set);
01059         //properties' visiblility changed: refresh prop. set
01060         propertySetReloaded(true);
01061         d->slotPropertyChanged_subType_enabled = true;
01062 
01063         addHistoryCommand( changeFieldTypeCommand, false /* !execute */ );
01064         return;
01065 //      }
01066 //      d->slotPropertyChanged_subType_enabled = true;
01067 //      return;
01068     }
01069 
01070     if (d->addHistoryCommand_in_slotPropertyChanged_enabled && !changePrimaryKey/*we'll add multiple commands for PK*/) {
01071         addHistoryCommand( new ChangeFieldPropertyCommand(this, set, 
01072                 property.name(), property.oldValue() /* ??? */, property.value()), 
01073             false /* !execute */ );
01074     }
01075 
01076     if (changePrimaryKey) {
01077       d->slotPropertyChanged_primaryKey_enabled = false;
01078         if (setPrimaryKey) {
01079             //primary key implies some rules
01080             //const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled = d->addHistoryCommand_in_slotPropertyChanged_enabled;
01081 //          d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
01082 
01083             //this action contains subactions
01084             CommandGroup *setPrimaryKeyCommand = new CommandGroup(
01085                 i18n("Set primary key for field \"%1\"")
01086                     .arg(set["name"].value().toString()) );
01087             if (toplevelCommand)
01088                 toplevelCommand->addCommand( setPrimaryKeyCommand );
01089             else
01090                 toplevelCommand = setPrimaryKeyCommand;
01091 
01092             d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(true,1), setPrimaryKeyCommand, true /*forceAddCommand*/ );
01093             d->setPropertyValueIfNeeded( set, "unique", QVariant(true,1), setPrimaryKeyCommand );
01094             d->setPropertyValueIfNeeded( set, "notNull", QVariant(true,1), setPrimaryKeyCommand );
01095             d->setPropertyValueIfNeeded( set, "allowEmpty", QVariant(false,1), setPrimaryKeyCommand );
01096             d->setPropertyValueIfNeeded( set, "indexed", QVariant(true,1), setPrimaryKeyCommand );
01098             d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(true,1), setPrimaryKeyCommand );
01099 
01100 /*              set["unique"] = QVariant(true,1);
01101                 set["notNull"] = QVariant(true,1);
01102                 set["allowEmpty"] = QVariant(false,1);
01103                 set["indexed"] = QVariant(true,1);
01104                 set["autoIncrement"] = QVariant(true,1);*/
01105 //          d->addHistoryCommand_in_slotPropertyChanged_enabled = prev_addHistoryCommand_in_slotPropertyChanged_enabled;
01106 //down          addHistoryCommand( toplevelCommand, false /* !execute */ );
01107         }
01108         else {
01109             //remember this action containing 2 subactions
01110             CommandGroup *setPrimaryKeyCommand = new CommandGroup(
01111                 i18n("Unset primary key for field \"%1\"")
01112                     .arg(set["name"].value().toString()) );
01113             if (toplevelCommand)
01114                 toplevelCommand->addCommand( setPrimaryKeyCommand );
01115             else
01116                 toplevelCommand = setPrimaryKeyCommand;
01117 
01118             d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(false,1), setPrimaryKeyCommand, true /*forceAddCommand*/ );
01119             d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(false,1), setPrimaryKeyCommand );
01120 //          set["autoIncrement"] = QVariant(false,1);
01121 
01122 //down          addHistoryCommand( toplevelCommand, false /* !execute */ );
01123         }
01124         switchPrimaryKey(set, setPrimaryKey, true/*wasPKey*/, toplevelCommand);
01125         d->updatePropertiesVisibility(
01126             KexiDB::Field::typeForString( set["subType"].value().toString() ), set, toplevelCommand);
01127         addHistoryCommand( toplevelCommand, false /* !execute */ );
01128         //properties' visiblility changed: refresh prop. set
01129         propertySetReloaded(true/*preservePrevSelection*/);
01130       d->slotPropertyChanged_primaryKey_enabled = true;
01131     }
01132 }
01133 
01134 void KexiTableDesignerView::slotRowInserted()
01135 {
01136     updateActions();
01137 
01138     if (d->addHistoryCommand_in_slotRowInserted_enabled) {
01139         const int row = d->view->currentRow();
01140         if (row>=0) {
01141             addHistoryCommand( new InsertEmptyRowCommand( this, row ), false /* !execute */ );
01142         }
01143     }
01144     //TODO?
01145 }
01146 
01147 void KexiTableDesignerView::slotAboutToDeleteRow(
01148     KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint)
01149 {
01150     Q_UNUSED(result)
01151     Q_UNUSED(repaint)
01152     if (item[COLUMN_ID_ICON].toString()=="key")
01153         d->primaryKeyExists = false;
01154 
01155     if (d->addHistoryCommand_in_slotAboutToDeleteRow_enabled) {
01156         const int row = d->view->data()->findRef(&item);
01157         KoProperty::Set *set = row >=0 ? d->sets->at(row) : 0;
01158         //set can be 0 here, what means "removing empty row"
01159         addHistoryCommand( 
01160             new RemoveFieldCommand( this, row, set ),
01161             false /* !execute */
01162         );
01163     }
01164 }
01165 
01166 KexiDB::Field * KexiTableDesignerView::buildField( const KoProperty::Set &set ) const
01167 {
01168     //create a map of property values
01169     kexipluginsdbg << set["type"].value() << endl;
01170     QMap<QCString, QVariant> values = KoProperty::propertyValues(set);
01171     //remove internal values, to avoid creating custom field's properties
01172     QMap<QCString, QVariant>::Iterator it = values.begin();
01173     KexiDB::Field *field = new KexiDB::Field();
01174 
01175     while (it!=values.end()) {
01176         const QString propName( it.key() );
01177         if (d->internalPropertyNames.find(propName.latin1()) || propName.startsWith("this:")
01178             || (/*sanity*/propName=="objectType" && KexiDB::Field::BLOB != KexiDB::intToFieldType( set["type"].value().toInt() )))
01179         {
01180             QMap<QCString, QVariant>::Iterator it_tmp = it;
01181             ++it;
01182             values.remove(it_tmp);
01183         }
01184         else
01185             ++it;
01186     }
01187     //assign properties to the field
01188     // (note that "objectType" property will be saved as custom property)
01189     if (!KexiDB::setFieldProperties( *field, values )) {
01190         delete field;
01191         return 0;
01192     }
01193     return field;
01194 }
01195 
01196 tristate KexiTableDesignerView::buildSchema(KexiDB::TableSchema &schema, bool beSilent)
01197 {
01198     if (!d->view->acceptRowEdit())
01199         return cancelled;
01200 
01201     tristate res = true;
01202     //check for pkey; automatically add a pkey if user wanted
01203     if (!d->primaryKeyExists) {
01204         if (beSilent) {
01205             kexipluginsdbg << "KexiTableDesignerView::buildSchema(): no primay key defined..." << endl;
01206         }
01207         else {
01208             const int questionRes = KMessageBox::questionYesNoCancel(this,
01209                 i18n("<p>Table \"%1\" has no <b>primary key</b> defined.</p>"
01210                 "<p>Although a primary key is not required, it is needed "
01211                 "for creating relations between database tables. "
01212                 "Do you want to add primary key automatically now?</p>"
01213                 "<p>If you want to add a primary key by hand, press \"Cancel\" "
01214                 "to cancel saving table design.</p>").arg(schema.name()),
01215                 QString::null, KGuiItem(i18n("&Add Primary Key"), "key"), KStdGuiItem::no(),
01216                     "autogeneratePrimaryKeysOnTableDesignSaving");
01217             if (questionRes==KMessageBox::Cancel) {
01218                 return cancelled;
01219             }
01220             else if (questionRes==KMessageBox::Yes) {
01221                 //-find unique name, starting with, "id", "id2", ....
01222                 int i=0;
01223                 int idIndex = 1; //means "id"
01224                 QString pkFieldName("id%1");
01225                 QString pkFieldCaption(i18n("Identifier%1", "Id%1"));
01226                 while (i<(int)d->sets->size()) {
01227                     KoProperty::Set *set = d->sets->at(i);
01228                     if (set) {
01229                         if ((*set)["name"].value().toString()
01230                             == pkFieldName.arg(idIndex==1?QString::null : QString::number(idIndex))
01231                         || (*set)["caption"].value().toString()
01232                             == pkFieldCaption.arg(idIndex==1?QString::null : QString::number(idIndex)))
01233                         {
01234                             //try next id index
01235                             i = 0;
01236                             idIndex++;
01237                             continue;
01238                         }
01239                     }
01240                     i++;
01241                 }
01242                 pkFieldName = pkFieldName.arg(idIndex==1?QString::null : QString::number(idIndex));
01243                 pkFieldCaption = pkFieldCaption.arg(idIndex==1?QString::null : QString::number(idIndex));
01244                 //ok, add PK with such unique name
01245                 d->view->insertEmptyRow(0);
01246                 d->view->setCursorPosition(0, COLUMN_ID_CAPTION);
01247                 d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_CAPTION,
01248                     QVariant(pkFieldCaption));
01249                 d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
01250                     QVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
01251                 if (!d->view->data()->saveRowChanges(*d->view->selectedItem(), true)) {
01252                     return cancelled;
01253                 }
01254                 slotTogglePrimaryKey();
01255             }
01256         }
01257     }
01258 
01259     //check for duplicates
01260     KoProperty::Set *b = 0;
01261     bool no_fields = true;
01262     int i;
01263     QDict<char> names(101, false);
01264     char dummy;
01265     for (i=0;i<(int)d->sets->size();i++) {
01266         b = d->sets->at(i);
01267         if (b) {
01268             no_fields = false;
01269             const QString name = (*b)["name"].value().toString();
01270             if (name.isEmpty()) {
01271                 if (beSilent) {
01272                     kexipluginswarn << 
01273                         QString("KexiTableDesignerView::buildSchema(): no field caption entered at row %1...")
01274                         .arg(i+1) << endl;
01275                 }
01276                 else {
01277                     d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
01278                     d->view->startEditCurrentCell();
01279                     KMessageBox::information(this, i18n("You should enter field caption.") );
01280                 }
01281                 res = cancelled;
01282                 break;
01283             }
01284             if (names[name]) {
01285                 break;
01286             }
01287             names.insert( name, &dummy ); //remember
01288         }
01289     }
01290     if (res == true && no_fields) {//no fields added
01291         if (beSilent) {
01292             kexipluginswarn << 
01293                 "KexiTableDesignerView::buildSchema(): no field defined..." << endl;
01294         }
01295         else {
01296             KMessageBox::sorry(this,
01297                 i18n("You have added no fields.\nEvery table should have at least one field.") );
01298         }
01299         res = cancelled;
01300     }
01301     if (res == true && b && i<(int)d->sets->size()) {//found a duplicate
01302         if (beSilent) {
01303             kexipluginswarn << 
01304                 QString("KexiTableDesignerView::buildSchema(): duplicated field name '%1'")
01305                 .arg((*b)["name"].value().toString()) << endl;
01306         }
01307         else {
01308             d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
01309             d->view->startEditCurrentCell();
01311             KMessageBox::sorry(this,
01312                 i18n("You have added \"%1\" field name twice.\nField names cannot be repeated. "
01313                 "Correct name of the field.")
01314                 .arg((*b)["name"].value().toString()) );
01315         }
01316         res = cancelled;
01317     }
01318     if (res == true) {
01319         //for every field, create KexiDB::Field definition
01320         for (i=0;i<(int)d->sets->size();i++) {
01321             KoProperty::Set *s = d->sets->at(i);
01322             if (!s)
01323                 continue;
01324             KexiDB::Field * f = buildField( *s );
01325             if (!f)
01326                 continue; //hmm?
01327             schema.addField(f);
01328             if (!(*s)["rowSource"].value().toString().isEmpty() && !(*s)["rowSourceType"].value().toString().isEmpty()) {
01329                 //add lookup column
01330                 KexiDB::LookupFieldSchema *lookupFieldSchema = new KexiDB::LookupFieldSchema();
01331                 lookupFieldSchema->rowSource().setTypeByName( (*s)["rowSourceType"].value().toString() );
01332                 lookupFieldSchema->rowSource().setName( (*s)["rowSource"].value().toString() );
01333                 lookupFieldSchema->setBoundColumn( (*s)["boundColumn"].value().toInt() );
01337                 QValueList<uint> visibleColumns;
01338                 const int visibleColumn = (*s)["visibleColumn"].value().toInt();
01339                 if (visibleColumn >= 0)
01340                     visibleColumns.append( (uint)visibleColumn );
01341                 lookupFieldSchema->setVisibleColumns( visibleColumns );
01343                 if (!schema.setLookupFieldSchema(f->name(), lookupFieldSchema)) {
01344                     kexipluginswarn << 
01345                         "KexiTableDesignerView::buildSchema(): !schema.setLookupFieldSchema()" << endl;
01346                     delete lookupFieldSchema;
01347                     return false;
01348                 }
01349             }
01350         }
01351     }
01352     return res;
01353 }
01354 
01357 static void copyAlterTableActions(KCommand* command, KexiDB::AlterTableHandler::ActionList &actions)
01358 {
01359     CommandGroup* cmdGroup = dynamic_cast<CommandGroup*>( command );
01360     if (cmdGroup) {//command group: flatten it
01361         for (QPtrListIterator<KCommand> it(cmdGroup->commands()); it.current(); ++it)
01362             copyAlterTableActions(it.current(), actions);
01363         return;
01364     }
01365     Command* cmd = dynamic_cast<Command*>( command );
01366     if (!cmd) {
01367         kexipluginswarn << "KexiTableDesignerView::copyAlterTableActions(): cmd is not of type 'Command'!" << endl;
01368         return;
01369     }
01370     KexiDB::AlterTableHandler::ActionBase* action = cmd->createAction();
01371     //some commands can contain null actions, e.g. "set visibility" command
01372     if (action)
01373         actions.append( action );
01374 }
01375 
01376 tristate KexiTableDesignerView::buildAlterTableActions(KexiDB::AlterTableHandler::ActionList &actions)
01377 {
01378     actions.clear();
01379     kexipluginsdbg << "KexiTableDesignerView::buildAlterTableActions(): " << d->history->commands().count()
01380         << " top-level command(s) to process..." << endl;
01381     for (QPtrListIterator<KCommand> it(d->history->commands()); it.current(); ++it) {
01382         copyAlterTableActions(it.current(), actions);
01383     }
01384     return true;
01385 }
01386 
01387 KexiDB::SchemaData* KexiTableDesignerView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
01388 {
01389     if (tempData()->table || m_dialog->schemaData()) //must not be
01390         return 0;
01391 
01392     //create table schema definition
01393     tempData()->table = new KexiDB::TableSchema(sdata.name());
01394     tempData()->table->setName( sdata.name() );
01395     tempData()->table->setCaption( sdata.caption() );
01396     tempData()->table->setDescription( sdata.description() );
01397 
01398     tristate res = buildSchema(*tempData()->table);
01399     cancel = ~res;
01400 
01401     //FINALLY: create table:
01402     if (res == true) {
01403         //todo
01404         KexiDB::Connection *conn = mainWin()->project()->dbConnection();
01405         res = conn->createTable(tempData()->table);
01406         if (res!=true)
01407             parentDialog()->setStatus(conn, "");
01408     }
01409 
01410     if (res == true) {
01411         //we've current schema
01412         tempData()->tableSchemaChangedInPreviousView = true;
01413 //not needed; KexiProject emits newItemStored signal //let project know the table is created
01414 //      mainWin()->project()->emitTableCreated(*tempData()->table);
01415     }
01416     else {
01417         delete tempData()->table;
01418         tempData()->table = 0;
01419     }
01420     return tempData()->table;
01421 }
01422 
01423 tristate KexiTableDesignerView::storeData(bool dontAsk)
01424 {
01425     if (!tempData()->table || !m_dialog->schemaData()) {
01426         d->recentResultOfStoreData = false;
01427         return false;
01428     }
01429 
01430     KexiDB::Connection *conn = mainWin()->project()->dbConnection();
01431     KexiDB::AlterTableHandler *alterTableHandler = 0;
01432     KexiDB::TableSchema *newTable = 0;
01433 
01434     //- create action list for the alter table handler
01435     KexiDB::AlterTableHandler::ActionList actions;
01436     tristate res = buildAlterTableActions( actions );
01437     bool realAlterTableCanBeUsed = false; 
01438     if (res == true) {
01439         alterTableHandler = new KexiDB::AlterTableHandler( *conn );
01440         alterTableHandler->setActions(actions);
01441 
01442         if (!d->tempStoreDataUsingRealAlterTable) {
01443             //only compute requirements
01444             KexiDB::AlterTableHandler::ExecutionArguments args;
01445             args.onlyComputeRequirements = true;
01446             (void)alterTableHandler->execute(tempData()->table->name(), args);
01447             res = args.result;
01448             if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
01449                 realAlterTableCanBeUsed = true;
01450         }
01451     }
01452 
01453     if (res == true) {
01454         res = KexiTablePart::askForClosingObjectsUsingTableSchema(
01455             this, *conn, *tempData()->table,
01456             i18n("You are about to change the design of table \"%1\" "
01457             "but following objects using this table are opened:")
01458             .arg(tempData()->table->name()));
01459     }
01460 
01461     if (res == true) {
01462         if (!d->tempStoreDataUsingRealAlterTable && !realAlterTableCanBeUsed) {
01464             delete alterTableHandler;
01465             alterTableHandler = 0;
01466             // - inform about removing the current table and ask for confimation
01467             if (!d->dontAskOnStoreData && !dontAsk) {
01468                 bool emptyTable;
01469                 const QString msg = d->messageForSavingChanges(emptyTable);
01470                 if (!emptyTable) {
01471                     if (KMessageBox::No == KMessageBox::questionYesNo(this, msg))
01472                         res = cancelled;
01473                 }
01474             }
01475             d->dontAskOnStoreData = false; //one-time use
01476             if (~res) {
01477                 d->recentResultOfStoreData = res;
01478                 return res;
01479             }
01480             // keep old behaviour:
01481             newTable = new KexiDB::TableSchema();
01482             // copy the schema data
01483             static_cast<KexiDB::SchemaData&>(*newTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
01484             res = buildSchema(*newTable);
01485             kexipluginsdbg << "KexiTableDesignerView::storeData() : BUILD SCHEMA:" << endl;
01486             newTable->debug();
01487 
01488             res = conn->alterTable(*tempData()->table, *newTable);
01489             if (res != true)
01490                 parentDialog()->setStatus(conn, "");
01491         }
01492         else {
01493             KexiDB::AlterTableHandler::ExecutionArguments args;
01494             newTable = alterTableHandler->execute(tempData()->table->name(), args);
01495             res = args.result;
01496             kexipluginsdbg << "KexiTableDesignerView::storeData() : ALTER TABLE EXECUTE: " 
01497                 << res.toString() << endl;
01498             if (true != res) {
01499                 alterTableHandler->debugError();
01500                 parentDialog()->setStatus(alterTableHandler, "");
01501             }
01502         }
01503     }
01504     if (res == true) {
01505         //change current schema
01506         tempData()->table = newTable;
01507         tempData()->tableSchemaChangedInPreviousView = true;
01508     }
01509     else {
01510         delete newTable;
01511     }
01512     delete alterTableHandler;
01513     d->recentResultOfStoreData = res;
01514     return res;
01515 }
01516 
01517 tristate KexiTableDesignerView::simulateAlterTableExecution(QString *debugTarget)
01518 {
01519 #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
01520 # ifdef KEXI_DEBUG_GUI
01521     if (mainWin()->activeWindow() != parentDialog()) //to avoid executing for multiple alter table views
01522         return false;
01523     if (!tempData()->table || !m_dialog->schemaData())
01524         return false;
01525     KexiDB::Connection *conn = mainWin()->project()->dbConnection();
01526     KexiDB::AlterTableHandler::ActionList actions;
01527     tristate res = buildAlterTableActions( actions );
01528 //todo: result?
01529     KexiDB::AlterTableHandler alterTableHandler( *conn );
01530     alterTableHandler.setActions(actions);
01531     KexiDB::AlterTableHandler::ExecutionArguments args;
01532     if (debugTarget) {
01533         args.debugString = debugTarget;
01534     }
01535     else {
01536         args.simulate = true;
01537     }
01538     (void)alterTableHandler.execute(tempData()->table->name(), args);
01539     return args.result;
01540 # else
01541     return false;
01542 # endif
01543 #else
01544     return false;
01545 #endif
01546 }
01547 
01548 void KexiTableDesignerView::slotSimulateAlterTableExecution()
01549 {
01550     (void)simulateAlterTableExecution(0);
01551 }
01552 
01553 tristate KexiTableDesignerView::executeRealAlterTable()
01554 {
01555     QSignal signal;
01556     signal.connect( mainWin(), SLOT(slotProjectSave()) );
01557     d->tempStoreDataUsingRealAlterTable = true;
01558     d->recentResultOfStoreData = false;
01559     signal.activate(); //will call KexiMainWindowImpl::slotProjectSaveAs() and thus storeData()
01560     d->tempStoreDataUsingRealAlterTable = false;
01561     return d->recentResultOfStoreData;
01562 }
01563 
01564 KexiTablePart::TempData* KexiTableDesignerView::tempData() const
01565 {
01566     return static_cast<KexiTablePart::TempData*>(parentDialog()->tempData());
01567 }
01568 
01569 /*void KexiTableDesignerView::slotAboutToUpdateRow(
01570     KexiTableItem* item, KexiDB::RowEditBuffer* buffer, KexiDB::ResultInfo* result)
01571 {
01572     KexiDB::RowEditBuffer::SimpleMap map = buffer->simpleBuffer();
01573     buffer->debug();
01574 
01575     QVariant old_type = item->at(1);
01576     QVariant *buf_type = buffer->at( d->view->field(1)->name() );
01577 
01578     //check if there is a type specified
01579 //  if ((old_type.isNull() && !buf_type) || (buf_type && buf_type->isNull())) {
01580         //kdDebug() << "err" << endl;
01581     //}
01582 //  allow = true;
01583 //  m_dirty = m_dirty | result->success;
01584 }*/
01585 
01586 #ifdef KEXI_DEBUG_GUI
01587 void KexiTableDesignerView::debugCommand( KCommand* command, int nestingLevel )
01588 {
01589     if (dynamic_cast<Command*>(command))
01590         KexiUtils::addAlterTableActionDebug(dynamic_cast<Command*>(command)->debugString(), nestingLevel);
01591     else
01592         KexiUtils::addAlterTableActionDebug(command->name(), nestingLevel);
01593     //show subcommands
01594     if (dynamic_cast<CommandGroup*>(command)) {
01595         for (QPtrListIterator<KCommand> it(dynamic_cast<CommandGroup*>(command)->commands()); it.current(); ++it) {
01596             debugCommand(it.current(), nestingLevel + 1);
01597         }
01598     }
01599 }
01600 #endif
01601 
01602 void KexiTableDesignerView::addHistoryCommand( KCommand* command, bool execute )
01603 {
01604 #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
01605 # ifdef KEXI_DEBUG_GUI
01606     debugCommand( command, 0 );
01607 # endif
01608     d->history->addCommand( command, execute );
01609     updateUndoRedoActions();
01610 #endif
01611 }
01612 
01613 void KexiTableDesignerView::updateUndoRedoActions()
01614 {
01615 #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
01616     setAvailable("edit_undo", d->historyActionCollection->action("edit_undo")->isEnabled());
01617     setAvailable("edit_redo", d->historyActionCollection->action("edit_redo")->isEnabled());
01618 #endif
01619 }
01620 
01621 void KexiTableDesignerView::slotUndo()
01622 {
01623 #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
01624 # ifdef KEXI_DEBUG_GUI
01625     KexiUtils::addAlterTableActionDebug(QString("UNDO:"));
01626 # endif
01627     d->history->undo();
01628     updateUndoRedoActions();
01629 #endif
01630 }
01631 
01632 void KexiTableDesignerView::slotRedo()
01633 {
01634 #ifndef KEXI_NO_UNDOREDO_ALTERTABLE
01635 # ifdef KEXI_DEBUG_GUI
01636     KexiUtils::addAlterTableActionDebug(QString("REDO:"));
01637 # endif
01638     d->history->redo();
01639     updateUndoRedoActions();
01640 #endif
01641 }
01642 
01643 void KexiTableDesignerView::slotCommandExecuted(KCommand *command)
01644 {
01645 #ifdef KEXI_DEBUG_GUI
01646     debugCommand( command, 1 );
01647 #endif
01648 }
01649 
01650 void KexiTableDesignerView::slotAboutToShowContextMenu()
01651 {
01652     //update title
01653     if (propertySet()) {
01654         const KoProperty::Set &set = *propertySet();
01655         QString captionOrName(set["caption"].value().toString());
01656         if (captionOrName.isEmpty())
01657             captionOrName = set["name"].value().toString();
01659         d->contextMenuTitle->setTitle( i18n("Table field \"%1\"").arg(captionOrName) );
01660     }
01661     else {
01662         d->contextMenuTitle->setTitle( i18n("Empty table row", "Empty Row") );
01663     }
01664 }
01665 
01666 QString KexiTableDesignerView::debugStringForCurrentTableSchema(tristate& result)
01667 {
01668     KexiDB::TableSchema tempTable;
01669     //copy schema data
01670     static_cast<KexiDB::SchemaData&>(tempTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
01671     result = buildSchema(tempTable, true /*beSilent*/);
01672     if (true!=result)
01673         return QString::null;
01674     return tempTable.debugString(false /*without name*/);
01675 }
01676 
01677 // -- low-level actions used by undo/redo framework
01678 
01679 void KexiTableDesignerView::clearRow(int row, bool addCommand)
01680 {
01681     if (!d->view->acceptRowEdit())
01682         return;
01683     KexiTableItem *item = d->view->itemAt(row);
01684     if (!item)
01685         return;
01686     //remove from prop. set
01687     d->sets->remove( row );
01688     //clear row in table view (just clear value in COLUMN_ID_TYPE column)
01689 //  for (int i=0; i < (int)d->view->data()->columnsCount(); i++) {
01690     if (!addCommand) {
01691         d->addHistoryCommand_in_slotRowUpdated_enabled = false;
01692         d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
01693         d->slotBeforeCellChanged_enabled = false;
01694     }
01695     d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant());
01696     if (!addCommand) {
01697         d->addHistoryCommand_in_slotRowUpdated_enabled = true;
01698         d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
01699         d->slotBeforeCellChanged_enabled = true;
01700     }
01701     d->view->data()->saveRowChanges(*item, true);
01702 }
01703 
01704 void KexiTableDesignerView::insertField(int row, const QString& caption, bool addCommand)
01705 {
01706     insertFieldInternal(row, 0, caption, addCommand);
01707 }
01708 
01709 void KexiTableDesignerView::insertField(int row, KoProperty::Set& set, bool addCommand)
01710 {
01711     insertFieldInternal(row, &set, QString::null, addCommand);
01712 }
01713 
01714 void KexiTableDesignerView::insertFieldInternal(int row, KoProperty::Set* set, //const KexiDB::Field& field, 
01715     const QString& caption, bool addCommand)
01716 {
01717     if (set && (!set->contains("type") || !set->contains("caption"))) {
01718         kexipluginswarn << "KexiTableDesignerView::insertField(): no 'type' or 'caption' property in set!" << endl;
01719         return;
01720     }
01721     if (!d->view->acceptRowEdit())
01722         return;
01723     KexiTableItem *item = d->view->itemAt(row);
01724     if (!item)
01725         return;
01726     if (!addCommand) {
01727         d->addHistoryCommand_in_slotRowUpdated_enabled = false;
01728         d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
01729         d->slotBeforeCellChanged_enabled = false;
01730     }
01731             d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, 
01732                 set ? (*set)["caption"].value() : QVariant(caption));//field.caption());
01733             d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, 
01734                 set ? (int)KexiDB::Field::typeGroup( (*set)["type"].value().toInt() )-1/*counting from 0*/
01735                 : (((int)KexiDB::Field::TextGroup)-1)/*default type, counting from 0*/
01736                 );
01737             d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC, 
01738                 set ? (*set)["description"].value() : QVariant());//field.description());
01739     if (!addCommand) {
01740         d->slotBeforeCellChanged_enabled = true;
01741     }
01742         //this will create a new property set:
01743         d->view->data()->saveRowChanges(*item);
01744         if (set) {
01745             KoProperty::Set *newSet = d->sets->at(row);
01746             if (newSet) {
01747                 *newSet = *set; //deep copy
01748             }
01749             else {
01750                 kexipluginswarn << "KexiTableDesignerView::insertField() !newSet, row==" << row << endl;
01751             }
01752         }
01753     if (!addCommand) {
01754         d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
01755         d->addHistoryCommand_in_slotRowUpdated_enabled = true;
01756     }
01757     d->view->updateRow( row );
01758     propertySetReloaded(true);
01759 }
01760 
01761 void KexiTableDesignerView::insertEmptyRow( int row, bool addCommand )
01762 {
01763     if (!addCommand) {
01764         d->addHistoryCommand_in_slotRowInserted_enabled = false;
01765     }
01766         d->view->insertEmptyRow( row );
01767     if (!addCommand) {
01768         d->addHistoryCommand_in_slotRowInserted_enabled = true;
01769     }
01770 }
01771 
01772 /*void KexiTableDesignerView::deleteRow( int row )
01773 {
01774     d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
01775         d->view->deleteItem( d->view->data()->at(row) );
01776     d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
01777 }*/
01778 
01779 void KexiTableDesignerView::deleteRow( int row, bool addCommand )
01780 {
01781     KexiTableItem *item = d->view->itemAt( row );
01782     if (!item)
01783         return;
01784     if (!addCommand) {
01785         d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
01786     }
01787     const bool res = d->view->deleteItem(item);
01788     if (!addCommand) {
01789         d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
01790     }
01791     if (!res)
01792         return;
01793 }
01794 
01795 void KexiTableDesignerView::changeFieldPropertyForRow( int row,
01796  const QCString& propertyName, const QVariant& newValue, 
01797  KoProperty::Property::ListData* const listData, bool addCommand )
01798 {
01799 #ifdef KEXI_DEBUG_GUI
01800     KexiUtils::addAlterTableActionDebug(QString("** changeFieldProperty: \"")
01801         + QString(propertyName) + "\" to \"" + newValue.toString() + "\"", 2/*nestingLevel*/);
01802 #endif
01803     if (!d->view->acceptRowEdit())
01804         return;
01805 
01806     KoProperty::Set* set = d->sets->at( row );
01807     if (!set || !set->contains(propertyName))
01808         return;
01809     KoProperty::Property &property = set->property(propertyName);
01810     if (listData) {
01811         if (listData->keys.isEmpty())
01812             property.setListData( 0 );
01813         else
01814             property.setListData( new KoProperty::Property::ListData(*listData) );
01815     }
01816     if (propertyName != "type") //delayed type update (we need to have subtype set properly)
01817         property.setValue(newValue);
01818     KexiTableItem *item = d->view->itemAt(row);
01819     Q_ASSERT(item);
01820 
01821     if (propertyName == "type") {
01822     //  d->addHistoryCommand_in_slotRowUpdated_enabled = false;
01823 //      d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
01824         d->slotPropertyChanged_subType_enabled = false;
01825             d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, 
01826                 int( KexiDB::Field::typeGroup( newValue.toInt() ) )-1);
01827             d->view->data()->saveRowChanges(*item);
01828         d->addHistoryCommand_in_slotRowUpdated_enabled = true;
01829 //      d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
01830     //  d->slotPropertyChanged_subType_enabled = true;
01831         property.setValue(newValue); //delayed type update (we needed to have subtype set properly)
01832     }
01833 
01834     if (!addCommand) {
01835         d->addHistoryCommand_in_slotRowUpdated_enabled = false;
01836         d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
01837         d->slotPropertyChanged_subType_enabled = false;
01838     }
01839         //special cases: properties displayed within the data grid:
01840         if (propertyName == "caption") {
01841             if (!addCommand) {
01842                 d->slotBeforeCellChanged_enabled = false;
01843             }
01844             d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, newValue);
01845             d->view->data()->saveRowChanges(*item);
01846             if (!addCommand) {
01847                 d->slotBeforeCellChanged_enabled = true;
01848             }
01849         }
01850         else if (propertyName == "description") {
01851             if (!addCommand) {
01852                 d->slotBeforeCellChanged_enabled = false;
01853             }
01854             d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC, newValue);
01855             if (!addCommand) {
01856                 d->slotBeforeCellChanged_enabled = true;
01857             }
01858             d->view->data()->saveRowChanges(*item);
01859         }
01860     if (!addCommand) {
01861         d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
01862         d->addHistoryCommand_in_slotRowUpdated_enabled = true;
01863         d->slotPropertyChanged_subType_enabled = true;
01864     }
01865     d->view->updateRow( row );
01866 }
01867 
01868 void KexiTableDesignerView::changeFieldProperty( int fieldUID,
01869  const QCString& propertyName, const QVariant& newValue, 
01870  KoProperty::Property::ListData* const listData, bool addCommand )
01871 {
01872     //find a property by UID
01873     const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
01874     if (row<0) {
01875         kexipluginswarn << "KexiTableDesignerView::changeFieldProperty(): field with uid="<<fieldUID<<" not found!"<<endl;
01876         return;
01877     }
01878     changeFieldPropertyForRow(row, propertyName, newValue, listData, addCommand);
01879 }
01880 
01881 void KexiTableDesignerView::changePropertyVisibility(
01882     int fieldUID, const QCString& propertyName, bool visible )
01883 {
01884 #ifdef KEXI_DEBUG_GUI
01885     KexiUtils::addAlterTableActionDebug(QString("** changePropertyVisibility: \"")
01886         + QString(propertyName) + "\" to \"" + (visible ? "true" : "false") + "\"", 2/*nestingLevel*/);
01887 #endif
01888     if (!d->view->acceptRowEdit())
01889         return;
01890 
01891     //find a property by name
01892     const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
01893     if (row<0)
01894         return;
01895     KoProperty::Set* set = d->sets->at( row );
01896     if (!set || !set->contains(propertyName))
01897         return;
01898 
01899     KoProperty::Property &property = set->property(propertyName);
01900     if (property.isVisible() != visible) {
01901         property.setVisible(visible);
01902         propertySetReloaded(true);
01903     }
01904 }
01905 
01906 void KexiTableDesignerView::propertySetSwitched()
01907 {
01908     KexiDataTable::propertySetSwitched();
01909 
01910     //if (parentDialog()!=parentDialog()->mainWin()->currentDialog())
01911     //  return; //this is not the current dialog's view
01912     
01913     static_cast<KexiTablePart*>(parentDialog()->part())->lookupColumnPage()
01914         ->assignPropertySet(propertySet());
01915 }
01916 
01917 #include "kexitabledesignerview.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys