00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00057
00059 #define DEFAULT_OBJECT_TYPE_VALUE "image"
00060
00061
00062
00064
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)
00109 , KexiTableDesignerInterface()
00110 , d( new KexiTableDesignerViewPrivate(this) )
00111 {
00112
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
00132 col = new KexiTableViewColumn("caption", KexiDB::Field::Text, i18n("Field Caption"),
00133 i18n("Describes caption for the field"));
00134
00135
00136
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
00167
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);
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);
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
00208 delete d;
00209 }
00210
00211 void KexiTableDesignerView::initData()
00212 {
00213
00214
00215 d->data->deleteAllRows();
00216 int tableFieldCount = 0;
00217 d->primaryKeyExists = false;
00218
00219 if (tempData()->table) {
00220 tableFieldCount = tempData()->table->fieldCount();
00221
00222
00223
00224 for(int i=0; i < tableFieldCount; i++) {
00225 KexiDB::Field *field = tempData()->table->field(i);
00226 KexiTableItem *item = d->data->createItem();
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;
00242 (*item)[COLUMN_ID_DESC] = field->description();
00243 d->data->append(item);
00244
00245
00246 }
00247 }
00248
00249
00250
00251
00252
00253
00254 for (int i=tableFieldCount; i<(int)d->sets->size(); i++) {
00255
00256 d->data->append(d->data->createItem());
00257 }
00258
00259
00260 d->view->setData(d->data);
00261
00262
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
00271 d->view->setColumnWidth(COLUMN_ID_ICON, IconSize( KIcon::Small ) + 10);
00272 d->view->adjustColumnWidthToContents(COLUMN_ID_CAPTION);
00273 d->view->setColumnWidth(COLUMN_ID_TYPE, d->maxTypeNameTextWidth + 2 * d->view->rowHeight());
00274 d->view->setColumnStretchEnabled( true, COLUMN_ID_DESC );
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);
00281 propertySetSwitched();
00282 }
00283
00285 void
00286 KexiTableDesignerView::getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup,
00287 QStringList& stringsList, QStringList& namesList)
00288 {
00289
00290
00291
00293
00294
00295
00296
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
00312
00313
00314 KoProperty::Property *prop;
00315
00316 set->addProperty(prop = new KoProperty::Property("uid", d->generateUniqueId(), ""));
00317 prop->setVisible(false);
00318
00319
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"
00325 ));
00326 prop->setVisible(false);
00327 set->addProperty(prop = new KoProperty::Property("this:useCaptionAsObjectName",
00328 QVariant(true, 1), QString::null));
00329 prop->setVisible(false);
00330
00331
00332 set->addProperty(prop
00333 = new KoProperty::Property("name", QVariant(field.name()), i18n("Name"),
00334 QString::null, KexiCustomPropertyFactory::Identifier) );
00335
00336
00337 set->addProperty( prop
00338 = new KoProperty::Property("type", QVariant(field.type()), i18n("Type")) );
00339 #ifndef KexiTableDesignerView_DEBUG
00340 prop->setVisible(false);
00341 #endif
00342
00343
00344 QStringList typeStringList, typeNameList;
00345 getSubTypeListData(field.typeGroup(), typeStringList, typeNameList);
00346
00347
00348
00349
00351
00352
00353
00354 QString subTypeValue = field.typeString();
00355
00356 set->addProperty(prop = new KoProperty::Property("subType",
00357 typeStringList, typeNameList, subTypeValue, i18n("Subtype")));
00358
00359
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")));
00369
00370 set->addProperty( prop
00371 = new KoProperty::Property("caption", QVariant(field.caption()), i18n("Caption") ) );
00372 prop->setVisible(false);
00373
00374 set->addProperty( prop
00375 = new KoProperty::Property("description", QVariant(field.description())) );
00376 prop->setVisible(false);
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(), i18n("Length")));
00383
00384 set->addProperty( prop
00385 = new KoProperty::Property("precision", (int)field.precision(), 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(), 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
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
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));
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
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
00514 d->setPropertyValueIfNeeded( propertySet, "primaryKey", QVariant(set,1), commandGroup );
00515 if (&propertySet==this->propertySet()) {
00516
00517 d->action_toggle_pkey->setChecked(set);
00518 if (d->view->selectedItem()) {
00519
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)
00526 d->primaryKeyExists = set;
00527 }
00528
00529 if (set) {
00530
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) {
00540
00541 d->setPropertyValueIfNeeded( *s, "autoIncrement", QVariant(false,0), commandGroup );
00542
00543 d->setPropertyValueIfNeeded( *s, "primaryKey", QVariant(false,0), commandGroup );
00544
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
00553
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));
00558
00559 d->view->data()->saveRowChanges(*d->view->selectedItem(), true);
00560
00561 d->setPropertyValueIfNeeded( propertySet, "subType", KexiDB::Field::typeString(KexiDB::Field::BigInteger),
00562 commandGroup );
00563
00564 d->setPropertyValueIfNeeded( propertySet, "unsigned", QVariant(true,4), commandGroup );
00565
00566 d->slotBeforeCellChanged_enabled = true;
00567 }
00568 updateActions();
00569 }
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580 tristate KexiTableDesignerView::beforeSwitchTo(int mode, bool &dontStore)
00581 {
00582 if (!d->view->acceptRowEdit())
00583 return false;
00584
00585
00586
00587
00588
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
00597 else if (dirty() && !parentDialog()->neverSaved()) {
00598
00599
00600
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, !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
00616
00617 }
00618
00619
00620 return res;
00621 }
00622 else if (mode==Kexi::TextViewMode) {
00623
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
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657 void KexiTableDesignerView::slotBeforeCellChanged(
00658 KexiTableItem *item, int colnum, QVariant& newValue, KexiDB::ResultInfo* )
00659 {
00660 if (!d->slotBeforeCellChanged_enabled)
00661 return;
00662
00663
00664 if (colnum==COLUMN_ID_CAPTION) {
00665
00666
00667 if (item->at(COLUMN_ID_TYPE).isNull()) {
00668
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;
00675 QString oldName( propertySetForItem->property("name").value().toString() );
00676 QString oldCaption( propertySetForItem->property("caption").value().toString() );
00677
00678
00679 ChangeFieldPropertyCommand *changeCaptionCommand
00680 = new ChangeFieldPropertyCommand( this, *propertySetForItem, "caption", oldCaption, newValue);
00681
00682
00683 propertySetForItem->changeProperty("caption", newValue);
00684 propertySetForItem->changeProperty("name", newValue);
00685
00686
00687
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
00694
00695
00696 changeCaptionAndNameCommand->addCommand(
00697 new ChangeFieldPropertyCommand( this, *propertySetForItem,
00698 "name", oldName, propertySetForItem->property("name").value().toString())
00699 );
00700 addHistoryCommand( changeCaptionAndNameCommand, false );
00701
00702 d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
00703 }
00704 }
00705 else if (colnum==COLUMN_ID_TYPE) {
00706 if (newValue.isNull()) {
00707
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;
00721
00722
00723
00724 KexiDB::Field::TypeGroup fieldTypeGroup;
00725 int i_fieldTypeGroup = newValue.toInt()+1;
00726 if (i_fieldTypeGroup < 1 || i_fieldTypeGroup >
00727 #ifdef KEXI_NO_BLOB_FIELDS
00729 (int)KexiDB::Field::LastTypeGroup-1)
00730 #else
00731 (int)KexiDB::Field::LastTypeGroup)
00732 #endif
00733 return;
00734 fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(i_fieldTypeGroup);
00735
00736
00737 KexiDB::Field::Type fieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
00738 if (fieldType==KexiDB::Field::InvalidType)
00739 fieldType = KexiDB::Field::Text;
00740
00741
00742
00743
00744 QStringList slist, nlist;
00745 getSubTypeListData(fieldTypeGroup, slist, nlist);
00746
00747 QString subTypeValue;
00748
00749
00750
00751
00752
00753
00754 subTypeValue = KexiDB::Field::typeString(fieldType);
00755
00756 KoProperty::Property *subTypeProperty = &set["subType"];
00757 kexipluginsdbg << subTypeProperty->value() << endl;
00758
00759
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
00765
00766
00767 const bool forcePropertySetReload
00768 = KexiDB::Field::typeGroup( KexiDB::Field::typeForString(subTypeProperty->value().toString()) )
00769 != fieldTypeGroup;
00770
00771 const bool useListData = slist.count() > 1;
00772
00773 if (!useListData) {
00774 slist.clear();
00775 nlist.clear();
00776 }
00777 d->setPropertyValueIfNeeded( set, "type", (int)fieldType, changeDataTypeCommand,
00778 false , true );
00779
00780
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
00790
00791
00792
00793
00794
00795
00796 if (set["primaryKey"].value().toBool()==true) {
00797
00798 if (fieldTypeGroup != KexiDB::Field::IntegerGroup) {
00799
00800
00801
00802
00803 d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(false, 1), changeDataTypeCommand );
00805 }
00806 }
00807
00808
00809 d->setPropertyValueIfNeeded( set, "subType", subTypeValue,
00810 changeDataTypeCommand, false, false ,
00811 &slist, &nlist );
00812
00813 if (d->updatePropertiesVisibility(fieldType, set, changeDataTypeCommand) || forcePropertySetReload) {
00814
00815 propertySetReloaded(true);
00816 }
00817
00818 addHistoryCommand( changeDataTypeCommand, false );
00819 }
00820 else if (colnum==COLUMN_ID_DESC) {
00821 KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
00822 if (!propertySetForItem)
00823 return;
00824
00825 QVariant oldValue((*propertySetForItem)["description"].value());
00826 kexipluginsdbg << oldValue << endl;
00827 propertySetForItem->changeProperty("description", newValue);
00828
00829
00830
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
00843
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)) {
00848
00849 d->sets->remove( row );
00850
00851
00852 d->view->data()->clearRowEditBuffer();
00853
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)) {
00858
00859 KexiDB::Field::TypeGroup fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(
00860 item->at(COLUMN_ID_TYPE).toInt()+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
00868 QString fieldName( KexiUtils::string2Identifier(fieldCaption) );
00869
00870 KexiDB::Field::Type fieldType = KexiDB::intToFieldType( intFieldType );
00871 KexiDB::Field field(
00872 fieldName,
00873 fieldType,
00874 KexiDB::Field::NoConstraints,
00875 KexiDB::Field::NoOptions,
00876 0,
00877 0,
00878 QVariant(),
00879 fieldCaption,
00880 description,
00881 0);
00882
00883
00884
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
00893 KoProperty::Set *newSet = createPropertySet( row, field, true );
00894
00895
00896
00897
00898
00899
00900
00901
00902 propertySetSwitched();
00903
00904 if (row>=0) {
00905 if (d->addHistoryCommand_in_slotRowUpdated_enabled) {
00906 addHistoryCommand( new InsertFieldCommand( this, row, *newSet ),
00907 false );
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
00924
00925 const QCString pname = property.name();
00926 kexipluginsdbg << "KexiTableDesignerView::slotPropertyChanged(): " << pname << " = " << property.value()
00927 << " (oldvalue = " << property.oldValue() << ")" << endl;
00928
00929
00930 bool changePrimaryKey = false;
00931
00932 bool setPrimaryKey = false;
00933
00934 if (pname=="primaryKey" && d->slotPropertyChanged_primaryKey_enabled) {
00935 changePrimaryKey = true;
00936 setPrimaryKey = property.value().toBool();
00937 }
00938
00939
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
00950 CommandGroup *setAutonumberCommand = 0;
00951 CommandGroup *toplevelCommand = 0;
00952 if (pname=="autoIncrement" && property.value().toBool()==true) {
00953 if (set["primaryKey"].value().toBool()==false) {
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
00969
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
00979
00980
00981 d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(false,1), setAutonumberCommand,
00982 true , false );
00983 addHistoryCommand( setAutonumberCommand, false );
00984 return;
00985 }
00986 }
00987 }
00988
00989
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
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
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
01019
01020
01021
01022 }
01023 KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
01024 QString typeName;
01025
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037 typeName = KexiDB::Field::typeName( KexiDB::Field::typeForString(property.value().toString()) );
01038
01039
01040
01041
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
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
01062 propertySetReloaded(true);
01063 d->slotPropertyChanged_subType_enabled = true;
01064
01065 addHistoryCommand( changeFieldTypeCommand, false );
01066 return;
01067
01068
01069
01070 }
01071
01072 if (d->addHistoryCommand_in_slotPropertyChanged_enabled && !changePrimaryKey) {
01073 addHistoryCommand( new ChangeFieldPropertyCommand(this, set,
01074 property.name(), property.oldValue() , property.value()),
01075 false );
01076 }
01077
01078 if (changePrimaryKey) {
01079 d->slotPropertyChanged_primaryKey_enabled = false;
01080 if (setPrimaryKey) {
01081
01082
01083
01084
01085
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 );
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
01103
01104
01105
01106
01107
01108
01109 }
01110 else {
01111
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 );
01121 d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(false,1), setPrimaryKeyCommand );
01122
01123
01124
01125 }
01126 switchPrimaryKey(set, setPrimaryKey, true, toplevelCommand);
01127 d->updatePropertiesVisibility(
01128 KexiDB::Field::typeForString( set["subType"].value().toString() ), set, toplevelCommand);
01129 addHistoryCommand( toplevelCommand, false );
01130
01131 propertySetReloaded(true);
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 );
01144 }
01145 }
01146
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
01161 addHistoryCommand(
01162 new RemoveFieldCommand( this, row, set ),
01163 false
01164 );
01165 }
01166 }
01167
01168 KexiDB::Field * KexiTableDesignerView::buildField( const KoProperty::Set &set ) const
01169 {
01170
01171 kexipluginsdbg << set["type"].value() << endl;
01172 QMap<QCString, QVariant> values = KoProperty::propertyValues(set);
01173
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 || (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
01190
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
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
01224 int i=0;
01225 int idIndex = 1;
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
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
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));
01253 if (!d->view->data()->saveRowChanges(*d->view->selectedItem(), true)) {
01254 return cancelled;
01255 }
01256 slotTogglePrimaryKey();
01257 }
01258 }
01259 }
01260
01261
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 );
01290 }
01291 }
01292 if (res == true && no_fields) {
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()) {
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
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;
01329 schema.addField(f);
01330 if (!(*s)["rowSource"].value().toString().isEmpty() && !(*s)["rowSourceType"].value().toString().isEmpty()) {
01331
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) {
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
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())
01392 return 0;
01393
01394
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
01404 if (res == true) {
01405
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
01414 tempData()->tableSchemaChangedInPreviousView = true;
01415
01416
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
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
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
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;
01478 if (~res) {
01479 d->recentResultOfStoreData = res;
01480 return res;
01481 }
01482
01483 newTable = new KexiDB::TableSchema();
01484
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
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())
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
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();
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
01573
01574
01575
01576
01577
01578
01579
01580
01581
01582
01583
01584
01585
01586
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
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
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
01673 static_cast<KexiDB::SchemaData&>(tempTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
01674 result = buildSchema(tempTable, true );
01675 if (true!=result)
01676 return QString::null;
01677 return tempTable.debugString(false );
01678 }
01679
01680
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
01690 d->sets->remove( row );
01691
01692
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,
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));
01736 d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
01737 set ? (int)KexiDB::Field::typeGroup( (*set)["type"].value().toInt() )-1
01738 : (((int)KexiDB::Field::TextGroup)-1)
01739 );
01740 d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC,
01741 set ? (*set)["description"].value() : QVariant());
01742 if (!addCommand) {
01743 d->slotBeforeCellChanged_enabled = true;
01744 }
01745
01746 d->view->data()->saveRowChanges(*item);
01747 if (set) {
01748 KoProperty::Set *newSet = d->sets->at(row);
01749 if (newSet) {
01750 *newSet = *set;
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
01776
01777
01778
01779
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);
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")
01820 property.setValue(newValue);
01821 KexiTableItem *item = d->view->itemAt(row);
01822 Q_ASSERT(item);
01823
01824 if (propertyName == "type") {
01825
01826
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
01833
01834 property.setValue(newValue);
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
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
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);
01890 #endif
01891 if (!d->view->acceptRowEdit())
01892 return;
01893
01894
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
01914
01915
01916 static_cast<KexiTablePart*>(parentDialog()->part())->lookupColumnPage()
01917 ->assignPropertySet(propertySet());
01918 }
01919
01920 bool KexiTableDesignerView::isPhysicalAlteringNeeded()
01921 {
01922
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
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"