kexi

kexitabledesignerview.cpp

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