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