kexi

kexitabledesignerview.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "kexitabledesignerview.h"
21#include "kexitabledesignerview_p.h"
22#include "kexilookupcolumnpage.h"
23#include "kexitabledesignercommands.h"
24
25#include <tqlayout.h>
26#include <tqlabel.h>
27#include <tqsplitter.h>
28
29#include <kiconloader.h>
30#include <kdebug.h>
31#include <tdelocale.h>
32#include <tdeaction.h>
33#include <tdepopupmenu.h>
34#include <tdemessagebox.h>
35#include <kiconeffect.h>
36
37#include <koproperty/set.h>
38#include <koproperty/utils.h>
39
40#include <kexidb/cursor.h>
41#include <kexidb/tableschema.h>
42#include <kexidb/connection.h>
43#include <kexidb/utils.h>
44#include <kexidb/roweditbuffer.h>
45#include <kexidb/error.h>
46#include <kexidb/lookupfieldschema.h>
47#include <kexiutils/identifier.h>
48#include <kexiproject.h>
49#include <keximainwindow.h>
50#include <widget/tableview/kexidataawarepropertyset.h>
51#include <widget/kexicustompropertyfactory.h>
52#include <kexiutils/utils.h>
53#include <kexidialogbase.h>
54#include <kexitableview.h>
55
56//#define MAX_FIELDS 101 //nice prime number
57
59#define DEFAULT_OBJECT_TYPE_VALUE "image"
60
61//#define KexiTableDesignerView_DEBUG
62
64//#define KEXI_NO_BLOB_FIELDS
65
66using namespace KexiTableDesignerCommands;
67
69static bool isIntegerTQVariant(TQVariant::Type t)
70{
71 return t==TQVariant::LongLong
72 || t==TQVariant::ULongLong
73 || t==TQVariant::Int
74 || t==TQVariant::UInt;
75}
76
78static bool canCastTQVariant(TQVariant::Type fromType, TQVariant::Type toType)
79{
80 return (fromType==TQVariant::Int && toType==TQVariant::UInt)
81 || (fromType==TQVariant::CString && toType==TQVariant::String)
82 || (fromType==TQVariant::LongLong && toType==TQVariant::ULongLong)
83 || ((fromType==TQVariant::String || fromType==TQVariant::CString)
84 && (isIntegerTQVariant(toType) || toType==TQVariant::Double));
85}
86
91static TQVariant tryCastTQVariant( const TQVariant& fromVal, TQVariant::Type toType )
92{
93 const TQVariant::Type fromType = fromVal.type();
94 if (fromType == toType)
95 return fromVal;
96 if (canCastTQVariant(fromType, toType) || canCastTQVariant(toType, fromType)
97 || (isIntegerTQVariant(fromType) && toType==TQVariant::Double))
98 {
99 TQVariant res( fromVal );
100 if (res.cast(toType))
101 return res;
102 }
103 return TQVariant();
104}
105
106
108 : KexiDataTable(win, parent, "KexiTableDesignerView", false/*not db-aware*/)
109 , KexiTableDesignerInterface()
110 , d( new KexiTableDesignerViewPrivate(this) )
111{
112 //needed for custom "identifier" property editor widget
114
116 d->view = dynamic_cast<KexiTableView*>(mainWidget());
117
118 d->data = new KexiTableViewData();
119 if (conn->isReadOnly())
120 d->data->setReadOnly(true);
121 d->data->setInsertingEnabled( false );
122
124 i18n("Additional information about the field"));
125 col->setIcon( KexiUtils::colorizeIconToTextColor( SmallIcon("application-vnd.tde.info"), d->view->palette() ) );
126 col->setHeaderTextVisible(false);
127 col->field()->setSubType("TDEIcon");
128 col->setReadOnly(true);
129 d->data->addColumn( col );
130
131// col = new KexiTableViewColumn("name", KexiDB::Field::Text, i18n("Field Name"),
132 col = new KexiTableViewColumn("caption", KexiDB::Field::Text, i18n("Field Caption"),
133 i18n("Describes caption for the field"));
134// KexiUtils::Validator *vd = new KexiUtils::IdentifierValidator();
135// vd->setAcceptsEmptyValue(true);
136// col->setValidator( vd );
137 d->data->addColumn( col );
138
139 col = new KexiTableViewColumn("type", KexiDB::Field::Enum, i18n("Data Type"),
140 i18n("Describes data type for the field"));
141 d->data->addColumn( col );
142
143#ifdef KEXI_NO_BLOB_FIELDS
145 TQValueVector<TQString> types(KexiDB::Field::LastTypeGroup-1); //don't show last type (BLOB)
146#else
147 TQValueVector<TQString> types(KexiDB::Field::LastTypeGroup);
148#endif
149 d->maxTypeNameTextWidth = 0;
150 TQFontMetrics fm(font());
151 for (uint i=1; i<=types.count(); i++) {
152 types[i-1] = KexiDB::Field::typeGroupName(i);
153 d->maxTypeNameTextWidth = TQMAX(d->maxTypeNameTextWidth, fm.width(types[i-1]));
154 }
155 col->field()->setEnumHints(types);
156
157 d->data->addColumn( col = new KexiTableViewColumn("comments", KexiDB::Field::Text, i18n("Comments"),
158 i18n("Describes additional comments for the field")) );
159
160 d->view->setSpreadSheetMode();
161
162 connect(d->data, TQ_SIGNAL(aboutToChangeCell(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)),
163 this, TQ_SLOT(slotBeforeCellChanged(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)));
164 connect(d->data, TQ_SIGNAL(rowUpdated(KexiTableItem*)),
165 this, TQ_SLOT(slotRowUpdated(KexiTableItem*)));
166 //connect(d->data, TQ_SIGNAL(aboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)),
167 // this, TQ_SLOT(slotAboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)));
168 connect(d->data, TQ_SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
169 this, TQ_SLOT(slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)));
170
171 setMinimumSize(d->view->minimumSizeHint().width(), d->view->minimumSizeHint().height());
172 d->view->setFocus();
173
174 d->sets = new KexiDataAwarePropertySet( this, d->view );
175 connect(d->sets, TQ_SIGNAL(rowDeleted()), this, TQ_SLOT(updateActions()));
176 connect(d->sets, TQ_SIGNAL(rowInserted()), this, TQ_SLOT(slotRowInserted()));
177
178 d->contextMenuTitle = new TDEPopupTitle(d->view->contextMenu());
179 d->view->contextMenu()->insertItem(d->contextMenuTitle, -1, 0);
180 connect(d->view->contextMenu(), TQ_SIGNAL(aboutToShow()), this, TQ_SLOT(slotAboutToShowContextMenu()));
181
182 plugSharedAction("tablepart_toggle_pkey", this, TQ_SLOT(slotTogglePrimaryKey()));
183 d->action_toggle_pkey = static_cast<TDEToggleAction*>( sharedAction("tablepart_toggle_pkey") );
184 d->action_toggle_pkey->plug(d->view->contextMenu(), 1); //add at the beginning
185 d->view->contextMenu()->insertSeparator(2);
186 setAvailable("tablepart_toggle_pkey", !conn->isReadOnly());
187
188#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
189 plugSharedAction("edit_undo", this, TQ_SLOT(slotUndo()));
190 plugSharedAction("edit_redo", this, TQ_SLOT(slotRedo()));
191 setAvailable("edit_undo", false);
192 setAvailable("edit_redo", false);
193 connect(d->history, TQ_SIGNAL(commandExecuted(KCommand*)), this, TQ_SLOT(slotCommandExecuted(KCommand*)));
194#endif
195
196#ifdef KEXI_DEBUG_GUI
197 KexiUtils::addAlterTableActionDebug(TQString()); //to create the tab
198 KexiUtils::connectPushButtonActionForDebugWindow(
199 "simulateAlterTableExecution", this, TQ_SLOT(slotSimulateAlterTableExecution()));
200 KexiUtils::connectPushButtonActionForDebugWindow(
201 "executeRealAlterTable", this, TQ_SLOT(executeRealAlterTable()));
202#endif
203}
204
205KexiTableDesignerView::~KexiTableDesignerView()
206{
207// removeCurrentPropertySet();
208 delete d;
209}
210
212{
213 //add column data
214// d->data->clear();
215 d->data->deleteAllRows();
216 int tableFieldCount = 0;
217 d->primaryKeyExists = false;
218
219 if (tempData()->table) {
220 tableFieldCount = tempData()->table->fieldCount();
221//not needed d->sets->clear(tableFieldCount);
222
223 //recreate table data rows
224 for(int i=0; i < tableFieldCount; i++) {
225 KexiDB::Field *field = tempData()->table->field(i);
226 KexiTableItem *item = d->data->createItem(); //new KexiTableItem(0);
227 if (field->isPrimaryKey()) {
228 (*item)[COLUMN_ID_ICON] = "key";
229 d->primaryKeyExists = true;
230 }
231 else {
232 KexiDB::LookupFieldSchema *lookupFieldSchema
233 = field->table() ? field->table()->lookupFieldSchema(*field) : 0;
234 if (lookupFieldSchema && lookupFieldSchema->rowSource().type()!=KexiDB::LookupFieldSchema::RowSource::NoType
235 && !lookupFieldSchema->rowSource().name().isEmpty())
236 {
237 (*item)[COLUMN_ID_ICON] = "combo";
238 }
239 }
240 (*item)[COLUMN_ID_CAPTION] = field->captionOrName();
241 (*item)[COLUMN_ID_TYPE] = field->typeGroup()-1; //-1 because type groups are counted from 1
242 (*item)[COLUMN_ID_DESC] = field->description();
243 d->data->append(item);
244
245//later! createPropertySet( i, field );
246 }
247 }
248// else {
249// d->sets->clear();//default size
250// }
251
252 //add empty space
253// const int columnsCount = d->data->columnsCount();
254 for (int i=tableFieldCount; i<(int)d->sets->size(); i++) {
255// KexiTableItem *item = new KexiTableItem(columnsCount);//3 empty fields
256 d->data->append(d->data->createItem());
257 }
258
259 //set data for our spreadsheet: this will clear our sets
260 d->view->setData(d->data);
261
262 //now recreate property sets
263 if (tempData()->table) {
264 for(int i=0; i < tableFieldCount; i++) {
265 KexiDB::Field *field = tempData()->table->field(i);
266 createPropertySet( i, *field );
267 }
268 }
269
270 //column widths
271 d->view->setColumnWidth(COLUMN_ID_ICON, IconSize( TDEIcon::Small ) + 10);
272 d->view->adjustColumnWidthToContents(COLUMN_ID_CAPTION); //adjust column width
273 d->view->setColumnWidth(COLUMN_ID_TYPE, d->maxTypeNameTextWidth + 2 * d->view->rowHeight());
274 d->view->setColumnStretchEnabled( true, COLUMN_ID_DESC ); //last column occupies the rest of the area
275 const int minCaptionColumnWidth = d->view->fontMetrics().width("wwwwwwwwwww");
276 if (minCaptionColumnWidth > d->view->columnWidth(COLUMN_ID_CAPTION))
277 d->view->setColumnWidth(COLUMN_ID_CAPTION, minCaptionColumnWidth);
278
279 setDirty(false);
280 d->view->setCursorPosition(0, COLUMN_ID_CAPTION); //set @ name column
282}
283
285void
287 TQStringList& stringsList, TQStringList& namesList)
288{
289/* disabled - "mime" is moved from subType to "objectType" custom property
290 if (fieldTypeGroup==KexiDB::Field::BLOBGroup) {
291 // special case: BLOB type uses "mime-based" subtypes
293 stringsList << "image";
294 namesList << i18n("Image object type", "Image");
295 }
296 else {*/
297 stringsList = KexiDB::typeStringsForGroup(fieldTypeGroup);
298 namesList = KexiDB::typeNamesForGroup(fieldTypeGroup);
299// }
300 kexipluginsdbg << "KexiTableDesignerView::getSubTypeListData(): subType strings: " <<
301 stringsList.join("|") << "\nnames: " << namesList.join("|") << endl;
302}
303
304KoProperty::Set *
305KexiTableDesignerView::createPropertySet( int row, const KexiDB::Field& field, bool newOne )
306{
307 TQString typeName = "KexiDB::Field::" + field.typeGroupString();
308 KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
309 if (mainWin()->project()->dbConnection()->isReadOnly())
310 set->setReadOnly( true );
311// connect(buff,TQ_SIGNAL(propertyChanged(KexiPropertyBuffer&,KexiProperty&)),
312// this, TQ_SLOT(slotPropertyChanged(KexiPropertyBuffer&,KexiProperty&)));
313
314 KoProperty::Property *prop;
315
316 set->addProperty(prop = new KoProperty::Property("uid", d->generateUniqueId(), ""));
317 prop->setVisible(false);
318
319 //meta-info for property editor
320 set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Table field")) );
321 prop->setVisible(false);
322 set->addProperty(prop = new KoProperty::Property("this:iconName",
324 "lineedit" //"table_field"
325 ));
326 prop->setVisible(false);
327 set->addProperty(prop = new KoProperty::Property("this:useCaptionAsObjectName",
328 TQVariant(true), TQString())); //we want "caption" to be displayed in the header, not name
329 prop->setVisible(false);
330
331 //name
332 set->addProperty(prop
333 = new KoProperty::Property("name", TQVariant(field.name()), i18n("Name"),
335
336 //type
337 set->addProperty( prop
338 = new KoProperty::Property("type", TQVariant(field.type()), i18n("Type")) );
339#ifndef KexiTableDesignerView_DEBUG
340 prop->setVisible(false);//always hidden
341#endif
342
343 //subtype
344 TQStringList typeStringList, typeNameList;
345 getSubTypeListData(field.typeGroup(), typeStringList, typeNameList);
346/* disabled - "mime" is moved from subType to "objectType" custom property
347 TQString subTypeValue;
348 if (field.typeGroup()==KexiDB::Field::BLOBGroup) {
349// special case: BLOB type uses "mime-based" subtypes
351 subTypeValue = slist.first();
352 }
353 else {*/
354 TQString subTypeValue = field.typeString();
355 //}
356 set->addProperty(prop = new KoProperty::Property("subType",
357 typeStringList, typeNameList, subTypeValue, i18n("Subtype")));
358
359 // objectType
360 TQStringList objectTypeStringList, objectTypeNameList;
362 objectTypeStringList << "image";
363 objectTypeNameList << i18n("Image object type", "Image");
364 TQString objectTypeValue( field.customProperty("objectType").toString() );
365 if (objectTypeValue.isEmpty())
366 objectTypeValue = DEFAULT_OBJECT_TYPE_VALUE;
367 set->addProperty(prop = new KoProperty::Property("objectType",
368 objectTypeStringList, objectTypeNameList, objectTypeValue, i18n("Subtype")/*todo other i18n string?*/));
369
370 set->addProperty( prop
371 = new KoProperty::Property("caption", TQVariant(field.caption()), i18n("Caption") ) );
372 prop->setVisible(false);//always hidden
373
374 set->addProperty( prop
375 = new KoProperty::Property("description", TQVariant(field.description())) );
376 prop->setVisible(false);//always hidden
377
378 set->addProperty(prop
379 = new KoProperty::Property("unsigned", TQVariant(field.isUnsigned()), i18n("Unsigned Number")));
380
381 set->addProperty( prop
382 = new KoProperty::Property("length", (int)field.length()/*200?*/, i18n("Length")));
383
384 set->addProperty( prop
385 = new KoProperty::Property("precision", (int)field.precision()/*200?*/, i18n("Precision")));
386#ifdef KEXI_NO_UNFINISHED
387 prop->setVisible(false);
388#endif
389 set->addProperty( prop
390 = new KoProperty::Property("visibleDecimalPlaces", field.visibleDecimalPlaces(), i18n("Visible Decimal Places")));
391 prop->setOption("min", -1);
392 prop->setOption("minValueText", i18n("Auto Decimal Places","Auto"));
393
395 set->addProperty( prop
396 = new KoProperty::Property("width", (int)field.width()/*200?*/, i18n("Column Width")));
397#ifdef KEXI_NO_UNFINISHED
398 prop->setVisible(false);
399#endif
400
401 set->addProperty( prop
402 = new KoProperty::Property("defaultValue", field.defaultValue(), i18n("Default Value"),
403 TQString(),
405 (KoProperty::PropertyType)field.variantType()) );
406 prop->setOption("3rdState", i18n("None"));
407// prop->setVisible(false);
408
409 set->addProperty( prop
410 = new KoProperty::Property("primaryKey", TQVariant(field.isPrimaryKey()), i18n("Primary Key")));
411 prop->setIcon("key");
412
413 set->addProperty( prop
414 = new KoProperty::Property("unique", TQVariant(field.isUniqueKey()), i18n("Unique")));
415
416 set->addProperty( prop
417 = new KoProperty::Property("notNull", TQVariant(field.isNotNull()), i18n("Required")));
418
419 set->addProperty( prop
420 = new KoProperty::Property("allowEmpty", TQVariant(!field.isNotEmpty()), i18n("Allow Zero\nSize")));
421
422 set->addProperty( prop
423 = new KoProperty::Property("autoIncrement", TQVariant(field.isAutoIncrement()), i18n("Autonumber")));
424 prop->setIcon("autonumber");
425
426 set->addProperty( prop
427 = new KoProperty::Property("indexed", TQVariant(field.isIndexed()), i18n("Indexed")));
428
429 //- properties related to lookup columns (used and set by the "lookup column" tab in the property pane)
430 KexiDB::LookupFieldSchema *lookupFieldSchema = field.table() ? field.table()->lookupFieldSchema(field) : 0;
431 set->addProperty( prop = new KoProperty::Property("rowSource",
432 lookupFieldSchema ? lookupFieldSchema->rowSource().name() : TQString(), i18n("Row Source")));
433 prop->setVisible(false);
434
435 set->addProperty( prop = new KoProperty::Property("rowSourceType",
436 lookupFieldSchema ? lookupFieldSchema->rowSource().typeName() : TQString(), i18n("Row Source\nType")));
437 prop->setVisible(false);
438
439 set->addProperty( prop
440 = new KoProperty::Property("boundColumn",
441 lookupFieldSchema ? lookupFieldSchema->boundColumn() : -1, i18n("Bound Column")));
442 prop->setVisible(false);
443
447 int visibleColumn = -1;
448 if (lookupFieldSchema && !lookupFieldSchema->visibleColumns().isEmpty())
449 visibleColumn = lookupFieldSchema->visibleColumns().first();
450 set->addProperty( prop
451 = new KoProperty::Property("visibleColumn", visibleColumn, i18n("Visible Column")));
452 prop->setVisible(false);
453
455
456 //----
457 d->updatePropertiesVisibility(field.type(), *set);
458
459 connect(set, TQ_SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
460 this, TQ_SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
461
462 d->sets->insert(row, set, newOne);
463 return set;
464}
465
467{
468 Q_UNUSED(activated);
470 setAvailable("tablepart_toggle_pkey", propertySet()!=0 && !mainWin()->project()->dbConnection()->isReadOnly());
471 if (!propertySet())
472 return;
473 KoProperty::Set &set = *propertySet();
474 d->slotTogglePrimaryKeyCalled = true;
475 d->action_toggle_pkey->setChecked(set["primaryKey"].value().toBool());
476 d->slotTogglePrimaryKeyCalled = false;
477}
478
479void KexiTableDesignerView::slotUpdateRowActions(int row)
480{
481 KexiDataTable::slotUpdateRowActions(row);
483}
484
486{
487 if (d->slotTogglePrimaryKeyCalled)
488 return;
489 d->slotTogglePrimaryKeyCalled = true;
490 if (!propertySet())
491 return;
492 KoProperty::Set &set = *propertySet();
493 bool isSet = !set["primaryKey"].value().toBool();
494 set.changeProperty("primaryKey", TQVariant(isSet)); //this will update all related properties as well
495/* CommandGroup *setPrimaryKeyCommand;
496 if (isSet) {
497 setPrimaryKeyCommand = new CommandGroup(i18n("Set primary key for field \"%1\"")
498 .arg(set["name"].value().toString()) );
499 }
500 else {
501 setPrimaryKeyCommand = new CommandGroup(i18n("Unset primary key for field \"%1\"")
502 .arg(set["name"].value().toString()) );
503 }
504 switchPrimaryKey(set, isSet, false, setPrimaryKeyCommand);*/
505 //addHistoryCommand( setPrimaryKeyCommand, false /* !execute */ );
506 d->slotTogglePrimaryKeyCalled = false;
507}
508
510 bool set, bool aWasPKey, CommandGroup* commandGroup)
511{
512 const bool was_pkey = aWasPKey || propertySet["primaryKey"].value().toBool();
513// propertySet["primaryKey"] = TQVariant(set);
514 d->setPropertyValueIfNeeded( propertySet, "primaryKey", TQVariant(set), commandGroup );
515 if (&propertySet==this->propertySet()) {
516 //update action and icon @ column 0 (only if we're changing current property set)
517 d->action_toggle_pkey->setChecked(set);
518 if (d->view->selectedItem()) {
519 //show key in the table
520 d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
521 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_ICON,
522 TQVariant(set ? "key" : ""));
523 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true);
524 }
525 if (was_pkey || set) //change flag only if we're setting pk or really clearing it
526 d->primaryKeyExists = set;
527 }
528
529 if (set) {
530 //primary key is set, remove old pkey if exists
531 KoProperty::Set *s = 0;
532 int i;
533 const int count = (int)d->sets->size();
534 for (i=0; i<count; i++) {
535 s = d->sets->at(i);
536 if (s && s!=&propertySet && (*s)["primaryKey"].value().toBool() && i!=d->view->currentRow())
537 break;
538 }
539 if (i<count) {//remove
540 //(*s)["autoIncrement"] = TQVariant(false);
541 d->setPropertyValueIfNeeded( *s, "autoIncrement", TQVariant(false), commandGroup );
542 //(*s)["primaryKey"] = TQVariant(false);
543 d->setPropertyValueIfNeeded( *s, "primaryKey", TQVariant(false), commandGroup );
544 //remove key from table
545 d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
546 KexiTableItem *item = d->view->itemAt(i);
547 if (item) {
548 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
549 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item, true);
550 }
551 }
552 //set unsigned big-integer type
553// d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem());
554 d->slotBeforeCellChanged_enabled = false;
555 d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
556 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
557 TQVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
558// TQVariant(KexiDB::Field::typeGroupName(KexiDB::Field::IntegerGroup)));
559 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true);
560 //propertySet["subType"] = KexiDB::Field::typeString(KexiDB::Field::BigInteger);
561 d->setPropertyValueIfNeeded( propertySet, "subType", KexiDB::Field::typeString(KexiDB::Field::BigInteger),
562 commandGroup );
563 //propertySet["unsigned"] = TQVariant(true);
564 d->setPropertyValueIfNeeded( propertySet, "unsigned", TQVariant(true), commandGroup );
565/*todo*/
566 d->slotBeforeCellChanged_enabled = true;
567 }
569}
570
571/*void KexiTableDesignerView::slotCellSelected(int, int row)
572{
573 kdDebug() << "KexiTableDesignerView::slotCellSelected()" << endl;
574 if(row == m_row)
575 return;
576 m_row = row;
577 propertyBufferSwitched();
578}*/
579
581{
582 if (!d->view->acceptRowEdit())
583 return false;
584/* if (mode==Kexi::DesignViewMode) {
585 initData();
586 return true;
587 }
588 else */
589 tristate res = true;
590 if (mode==Kexi::DataViewMode) {
591 if (!dirty() && parentDialog()->neverSaved()) {
592 KMessageBox::sorry(this, i18n("Cannot switch to data view, because table design is empty.\n"
593 "First, please create your design.") );
594 return cancelled;
595 }
596//<temporary>
597 else if (dirty() && !parentDialog()->neverSaved()) {
598// 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?")));
599
600// KexiDB::Connection *conn = mainWin()->project()->dbConnection();
601 bool emptyTable;
602 int r = KMessageBox::warningYesNoCancel(this,
603 i18n("Saving changes for existing table design is now required.")
604 + "\n" + d->messageForSavingChanges(emptyTable, /* skip warning? */!isPhysicalAlteringNeeded()),
605 TQString(),
606 KStdGuiItem::save(), KStdGuiItem::discard(), TQString(),
607 KMessageBox::Notify|KMessageBox::Dangerous);
608 if (r == KMessageBox::Cancel)
609 res = cancelled;
610 else
611 res = true;
612 dontStore = (r!=KMessageBox::Yes);
613 if (!dontStore)
614 d->dontAskOnStoreData = true;
615// if (dontStore)
616// setDirty(false);
617 }
618//</temporary>
619 //todo
620 return res;
621 }
622 else if (mode==Kexi::TextViewMode) {
623 //todo
624 }
625 return res;
626}
627
629{
630 if (mode==Kexi::NoViewMode || mode==Kexi::DataViewMode) {
631 initData();
632 }
633 return true;
634}
635
637{
638 return d->sets ? d->sets->currentPropertySet() : 0;
639}
640
641/*
642void KexiTableDesignerView::removeCurrentPropertySet()
643{
644 const int r = d->view->currentRow();
645 KoProperty::Set *buf = d->sets.at(r);
646 if (!buf)
647 return;
648 buf->debug();
649// m_currentBufferCleared = true;
650 d->sets.remove(r);
651 propertysetswitched();
652// delete buf;
653// m_currentBufferCleared = false;
654}
655*/
656
658 KexiTableItem *item, int colnum, TQVariant& newValue, KexiDB::ResultInfo* /*result*/)
659{
660 if (!d->slotBeforeCellChanged_enabled)
661 return;
662// kdDebug() << d->view->selectedItem() << " " << item
663 //<< " " << d->sets->at( d->view->currentRow() ) << " " << propertySet() << endl;
664 if (colnum==COLUMN_ID_CAPTION) {//'caption'
665// if (!item->at(1).toString().isEmpty() && item->at(1).isNull()) {
666 //if 'type' is not filled yet
667 if (item->at(COLUMN_ID_TYPE).isNull()) {
668 //auto select 1st row of 'type' column
669 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant((int)0));
670 }
671
672 KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
673 if (propertySetForItem) {
674 d->addHistoryCommand_in_slotPropertyChanged_enabled = false; //because we'll add the two changes as one KMacroCommand
675 TQString oldName( propertySetForItem->property("name").value().toString() );
676 TQString oldCaption( propertySetForItem->property("caption").value().toString() );
677
678 //we need to create the action now as set["name"] will be changed soon..
679 ChangeFieldPropertyCommand *changeCaptionCommand
680 = new ChangeFieldPropertyCommand( this, *propertySetForItem, "caption", oldCaption, newValue);
681
682 //update field caption and name
683 propertySetForItem->changeProperty("caption", newValue);
684 propertySetForItem->changeProperty("name", newValue); // "name" prop. is of custom type Identifier, so this assignment
685 // will automatically convert newValue to an valid identifier
686
687 //remember this action containing 2 subactions
688 CommandGroup *changeCaptionAndNameCommand = new CommandGroup(
689 i18n("Change \"%1\" field's name to \"%2\" and caption from \"%3\" to \"%4\"")
690 .arg(oldName).arg(propertySetForItem->property("name").value().toString())
691 .arg(oldCaption).arg(newValue.toString() ));
692 changeCaptionAndNameCommand->addCommand( changeCaptionCommand );
693// new ChangeFieldPropertyCommand( this, *propertySetForItem,
694 // "caption", oldCaption, newValue)
695 // );
696 changeCaptionAndNameCommand->addCommand(
697 new ChangeFieldPropertyCommand( this, *propertySetForItem,
698 "name", oldName, propertySetForItem->property("name").value().toString())
699 );
700 addHistoryCommand( changeCaptionAndNameCommand, false /* !execute */ );
701
702 d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
703 }
704 }
705 else if (colnum==COLUMN_ID_TYPE) {//'type'
706 if (newValue.isNull()) {
707 //'type' col will be cleared: clear all other columns as well
708 d->slotBeforeCellChanged_enabled = false;
709 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
710 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, TQVariant(TQString()));
711 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC, TQVariant());
712 d->slotBeforeCellChanged_enabled = true;
713 return;
714 }
715
716 KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
717 if (!propertySetForItem)
718 return;
719
720 KoProperty::Set &set = *propertySetForItem; //propertySet();
721
722 //'type' col is changed (existed before)
723 //-get type group number
724 KexiDB::Field::TypeGroup fieldTypeGroup;
725 int i_fieldTypeGroup = newValue.toInt()+1/*counting from 1*/;
726 if (i_fieldTypeGroup < 1 || i_fieldTypeGroup >
727#ifdef KEXI_NO_BLOB_FIELDS
729 (int)KexiDB::Field::LastTypeGroup-1) //don't show last (BLOB) type
730#else
731 (int)KexiDB::Field::LastTypeGroup)
732#endif
733 return;
734 fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(i_fieldTypeGroup);
735
736 //-get 1st type from this group, and update 'type' property
737 KexiDB::Field::Type fieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
738 if (fieldType==KexiDB::Field::InvalidType)
739 fieldType = KexiDB::Field::Text;
740//moved down set["type"] = (int)fieldType;
741// set["subType"] = KexiDB::Field::typeName(fieldType);
742
743 //-get subtypes for this type: keys (slist) and names (nlist)
744 TQStringList slist, nlist;
745 getSubTypeListData(fieldTypeGroup, slist, nlist);
746
747 TQString subTypeValue;
748/* disabled - "mime" is moved from subType to "objectType" custom property
749 if (fieldType==KexiDB::Field::BLOB) {
750 // special case: BLOB type uses "mime-based" subtypes
751 subTypeValue = slist.first();
752 }
753 else {*/
754 subTypeValue = KexiDB::Field::typeString(fieldType);
755 //}
756 KoProperty::Property *subTypeProperty = &set["subType"];
757 kexipluginsdbg << subTypeProperty->value() << endl;
758
759 // *** this action contains subactions ***
760 CommandGroup *changeDataTypeCommand = new CommandGroup(
761 i18n("Change data type for field \"%1\" to \"%2\"")
762 .arg(set["name"].value().toString()).arg( KexiDB::Field::typeName( fieldType ) ) );
763
764//kexipluginsdbg << "++++++++++" << slist << nlist << endl;
765
766 //update subtype list and value
767 const bool forcePropertySetReload
768 = KexiDB::Field::typeGroup( KexiDB::Field::typeForString(subTypeProperty->value().toString()) )
769 != fieldTypeGroup; //<-- ?????
770// const bool forcePropertySetReload = set["type"].value().toInt() != (int)fieldTypeGroup;
771 const bool useListData = slist.count() > 1; //disabled-> || fieldType==KexiDB::Field::BLOB;
772
773 if (!useListData) {
774 slist.clear(); //empty list will be passed
775 nlist.clear();
776 }
777 d->setPropertyValueIfNeeded( set, "type", (int)fieldType, changeDataTypeCommand,
778 false , true /*rememberOldValue*/);
779
780 // notNull and defaultValue=false is reasonable for boolean type
781 if (fieldType == KexiDB::Field::Boolean) {
783 d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true), changeDataTypeCommand,
784 false , false );
785 d->setPropertyValueIfNeeded( set, "defaultValue", TQVariant(false), changeDataTypeCommand,
786 false , false );
787 }
788
789/* if (useListData) {
790 {
791 subTypeProperty->setListData( slist, nlist );
792 }
793 else {
794 subTypeProperty->setListData( 0 );
795 }*/
796 if (set["primaryKey"].value().toBool()==true) {
797 //primary keys require big int, so if selected type is not integer- remove PK
798 if (fieldTypeGroup != KexiDB::Field::IntegerGroup) {
799 /*not needed, line below will do the work
800 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_ICON, TQVariant());
801 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item); */
802 //set["primaryKey"] = TQVariant(false);
803 d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(false), changeDataTypeCommand );
805 }
806 }
807// if (useListData)
808// subTypeProperty->setValue( subTypeValue, false/*!rememberOldValue*/ );
809 d->setPropertyValueIfNeeded( set, "subType", subTypeValue,
810 changeDataTypeCommand, false, false ,
811 &slist, &nlist );
812
813 if (d->updatePropertiesVisibility(fieldType, set, changeDataTypeCommand) || forcePropertySetReload) {
814 //properties' visiblility changed: refresh prop. set
816 }
817
818 addHistoryCommand( changeDataTypeCommand, false /* !execute */ );
819 }
820 else if (colnum==COLUMN_ID_DESC) {//'description'
821 KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
822 if (!propertySetForItem)
823 return;
824 //update field desc.
825 TQVariant oldValue((*propertySetForItem)["description"].value());
826 kexipluginsdbg << oldValue << endl;
827 propertySetForItem->changeProperty("description", newValue);
828 /*moved addHistoryCommand(
829 new ChangeFieldPropertyCommand( this, *propertySetForItem,
830 "description", oldValue, newValue ), false);*/
831 }
832}
833
835{
836 const int row = d->view->KexiDataAwareObjectInterface::data()->findRef(item);
837 if (row < 0)
838 return;
839
840 setDirty();
841
842 //-check if the row was empty before updating
843 //if yes: we want to add a property set for this new row (field)
844 TQString fieldCaption( item->at(COLUMN_ID_CAPTION).toString() );
845 const bool prop_set_allowed = !item->at(COLUMN_ID_TYPE).isNull();
846
847 if (!prop_set_allowed && d->sets->at(row)/*propertySet()*/) {
848 //there is a property set, but it's not allowed - remove it:
849 d->sets->remove( row ); //d->sets->removeCurrentPropertySet();
850
851 //clear 'type' column:
852 d->view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
853// d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE, TQVariant());
854 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant());
855 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
856
857 } else if (prop_set_allowed && !d->sets->at(row)/*propertySet()*/) {
858 //-- create a new field:
859 KexiDB::Field::TypeGroup fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(
860 item->at(COLUMN_ID_TYPE).toInt()+1/*counting from 1*/ );
861 int intFieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
862 if (intFieldType==0)
863 return;
864
865 TQString description( item->at(COLUMN_ID_DESC).toString() );
866
867//todo: check uniqueness:
868 TQString fieldName( KexiUtils::string2Identifier(fieldCaption) );
869
870 KexiDB::Field::Type fieldType = KexiDB::intToFieldType( intFieldType );
871 KexiDB::Field field( //tmp
872 fieldName,
873 fieldType,
874 KexiDB::Field::NoConstraints,
875 KexiDB::Field::NoOptions,
876 /*length*/0,
877 /*precision*/0,
878 /*defaultValue*/TQVariant(),
879 fieldCaption,
880 description,
881 /*width*/0);
882// m_newTable->addField( field );
883
884 // reasonable case for boolean type: set notNull flag and "false" as default value
885 if (fieldType == KexiDB::Field::Boolean) {
886 field.setNotNull( true );
887 field.setDefaultValue( TQVariant(false) );
888 }
889
890 kexipluginsdbg << "KexiTableDesignerView::slotRowUpdated(): " << field.debugString() << endl;
891
892 //create a new property set:
893 KoProperty::Set *newSet = createPropertySet( row, field, true );
894//moved
895 //add a special property indicating that this is brand new buffer,
896 //not just changed
897// KoProperty::Property* prop = new KoProperty::Property("newrow", TQVariant());
898// prop->setVisible(false);
899// newbuff->addProperty( prop );
900
901 //refresh property editor:
903
904 if (row>=0) {
905 if (d->addHistoryCommand_in_slotRowUpdated_enabled) {
906 addHistoryCommand( new InsertFieldCommand( this, row, *newSet /*propertySet()*/ ), //, field /*will be copied*/
907 false /* !execute */ );
908 }
909 }
910 else {
911 kexipluginswarn << "KexiTableDesignerView::slotRowUpdated() row # not found !" << endl;
912 }
913 }
914}
915
920
921void KexiTableDesignerView::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
922{
923// if (!d->slotPropertyChanged_enabled)
924// return;
925 const TQCString pname = property.name();
926 kexipluginsdbg << "KexiTableDesignerView::slotPropertyChanged(): " << pname << " = " << property.value()
927 << " (oldvalue = " << property.oldValue() << ")" << endl;
928
929 // true is PK should be altered
930 bool changePrimaryKey = false;
931 // true is PK should be set to true, otherwise unset
932 bool setPrimaryKey = false;
933
934 if (pname=="primaryKey" && d->slotPropertyChanged_primaryKey_enabled) {
935 changePrimaryKey = true;
936 setPrimaryKey = property.value().toBool();
937 }
938
939 // update "lookup column" icon
940 if (pname=="rowSource" || pname=="rowSourceType") {
943 const int row = d->sets->findRowForPropertyValue("uid", set["uid"].value().toInt());
944 KexiTableItem *item = d->view->itemAt(row);
945 if (item)
946 d->updateIconForItem(*item, set);
947 }
948
949 //setting autonumber requires setting PK as well
950 CommandGroup *setAutonumberCommand = 0;
951 CommandGroup *toplevelCommand = 0;
952 if (pname=="autoIncrement" && property.value().toBool()==true) {
953 if (set["primaryKey"].value().toBool()==false) {//we need PKEY here!
954 TQString msg = TQString("<p>")
955 +i18n("Setting autonumber requires primary key to be set for current field.")+"</p>";
956 if (d->primaryKeyExists)
957 msg += (TQString("<p>")+ i18n("Previous primary key will be removed.")+"</p>");
958 msg += (TQString("<p>")
959 +i18n("Do you want to create primary key for current field? "
960 "Click \"Cancel\" to cancel setting autonumber.")+"</p>");
961
962 if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
963 i18n("Setting Autonumber Field"),
964 KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
965 {
966 changePrimaryKey = true;
967 setPrimaryKey = true;
968 //switchPrimaryKey(set, true);
969 // this will be toplevel command
970 setAutonumberCommand = new CommandGroup(
971 i18n("Assign autonumber for field \"%1\"").arg(set["name"].value().toString()) );
972 toplevelCommand = setAutonumberCommand;
973 d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(true), setAutonumberCommand );
974 }
975 else {
976 setAutonumberCommand = new CommandGroup(
977 i18n("Remove autonumber from field \"%1\"").arg(set["name"].value().toString()) );
978 //d->slotPropertyChanged_enabled = false;
979// set["autoIncrement"].setValue( TQVariant(false), false/*don't save old*/);
980// d->slotPropertyChanged_enabled = true;
981 d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(false), setAutonumberCommand,
982 true /*forceAddCommand*/, false/*rememberOldValue*/ );
983 addHistoryCommand( setAutonumberCommand, false /* !execute */ );
984 return;
985 }
986 }
987 }
988
989 //clear PK when these properties were set to false:
990 if ((pname=="indexed" || pname=="unique" || pname=="notNull")
991 && set["primaryKey"].value().toBool() && property.value().toBool()==false)
992 {
994 changePrimaryKey = true;
995 setPrimaryKey = false;
996 // this will be toplevel command
997 CommandGroup *unsetIndexedOrUniquOrNotNullCommand = new CommandGroup(
998 i18n("Set \"%1\" property for field \"%2\"").arg(property.caption()).arg(set["name"].value().toString()) );
999 toplevelCommand = unsetIndexedOrUniquOrNotNullCommand;
1000 d->setPropertyValueIfNeeded( set, pname, TQVariant(false), unsetIndexedOrUniquOrNotNullCommand );
1001 if (pname=="notNull") {
1002//? d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true), unsetIndexedOrUniquOrNotNullCommand );
1003 d->setPropertyValueIfNeeded( set, "unique", TQVariant(false), unsetIndexedOrUniquOrNotNullCommand );
1004 }
1005 }
1006
1007 if (pname=="defaultValue") {
1008 KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
1009 set["defaultValue"].setType((KoProperty::PropertyType)KexiDB::Field::variantType(type));
1010 }
1011
1012 if (pname=="subType" && d->slotPropertyChanged_subType_enabled) {
1013 d->slotPropertyChanged_subType_enabled = false;
1014 if (set["primaryKey"].value().toBool()==true
1015 && property.value().toString()!=KexiDB::Field::typeString(KexiDB::Field::BigInteger))
1016 {
1017 kexipluginsdbg << "INVALID " << property.value().toString() << endl;
1018// if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
1019// i18n("This field has promary key assigned. Setting autonumber field"),
1020// KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
1021
1022 }
1023 KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
1024 TQString typeName;
1025/* disabled - "mime" is moved from subType to "objectType" custom property
1026 if (type==KexiDB::Field::BLOB) { //special case
1027 //find i18n'd text
1028 TQStringList stringsList, namesList;
1029 getSubTypeListData(KexiDB::Field::BLOBGroup, stringsList, namesList);
1030 const int stringIndex = stringsList.findIndex( property.value().toString() );
1031 if (-1 == stringIndex || stringIndex>=(int)namesList.count())
1032 typeName = property.value().toString(); //for sanity
1033 else
1034 typeName = namesList[stringIndex];
1035 }
1036 else {*/
1037 typeName = KexiDB::Field::typeName( KexiDB::Field::typeForString(property.value().toString()) );
1038// }
1039// kdDebug() << property.value().toString() << endl;
1040// kdDebug() << set["type"].value() << endl;
1041// if (KexiDB::Field::typeGroup( set["type"].value().toInt() ) == (int)KexiDB::Field::TextGroup) {
1042 CommandGroup* changeFieldTypeCommand = new CommandGroup(
1043 i18n("Change type for field \"%1\" to \"%2\"").arg(set["name"].value().toString())
1044 .arg(typeName) );
1045 d->setPropertyValueIfNeeded( set, "subType", property.value(), property.oldValue(),
1046 changeFieldTypeCommand );
1047
1048 kexipluginsdbg << set["type"].value() << endl;
1049 const KexiDB::Field::Type newType = KexiDB::Field::typeForString(property.value().toString());
1050 set["type"].setValue( newType );
1051
1052 // cast "defaultValue" property value to a new type
1053 TQVariant oldDefVal( set["defaultValue"].value() );
1054 TQVariant newDefVal( tryCastTQVariant(oldDefVal, KexiDB::Field::variantType(type)) );
1055 if (oldDefVal.type()!=newDefVal.type())
1056 set["defaultValue"].setType( newDefVal.type() );
1057 d->setPropertyValueIfNeeded( set, "defaultValue", newDefVal, newDefVal,
1058 changeFieldTypeCommand );
1059
1060 d->updatePropertiesVisibility(newType, set);
1061 //properties' visiblility changed: refresh prop. set
1062 propertySetReloaded(true);
1063 d->slotPropertyChanged_subType_enabled = true;
1064
1065 addHistoryCommand( changeFieldTypeCommand, false /* !execute */ );
1066 return;
1067// }
1068// d->slotPropertyChanged_subType_enabled = true;
1069// return;
1070 }
1071
1072 if (d->addHistoryCommand_in_slotPropertyChanged_enabled && !changePrimaryKey/*we'll add multiple commands for PK*/) {
1074 property.name(), property.oldValue() /* ??? */, property.value()),
1075 false /* !execute */ );
1076 }
1077
1078 if (changePrimaryKey) {
1079 d->slotPropertyChanged_primaryKey_enabled = false;
1080 if (setPrimaryKey) {
1081 //primary key implies some rules
1082 //const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled = d->addHistoryCommand_in_slotPropertyChanged_enabled;
1083// d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
1084
1085 //this action contains subactions
1086 CommandGroup *setPrimaryKeyCommand = new CommandGroup(
1087 i18n("Set primary key for field \"%1\"")
1088 .arg(set["name"].value().toString()) );
1089 if (toplevelCommand)
1090 toplevelCommand->addCommand( setPrimaryKeyCommand );
1091 else
1092 toplevelCommand = setPrimaryKeyCommand;
1093
1094 d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(true), setPrimaryKeyCommand, true /*forceAddCommand*/ );
1095 d->setPropertyValueIfNeeded( set, "unique", TQVariant(true), setPrimaryKeyCommand );
1096 d->setPropertyValueIfNeeded( set, "notNull", TQVariant(true), setPrimaryKeyCommand );
1097 d->setPropertyValueIfNeeded( set, "allowEmpty", TQVariant(false), setPrimaryKeyCommand );
1098 d->setPropertyValueIfNeeded( set, "indexed", TQVariant(true), setPrimaryKeyCommand );
1100 d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(true), setPrimaryKeyCommand );
1101
1102/* set["unique"] = TQVariant(true);
1103 set["notNull"] = TQVariant(true);
1104 set["allowEmpty"] = TQVariant(false);
1105 set["indexed"] = TQVariant(true);
1106 set["autoIncrement"] = TQVariant(true);*/
1107// d->addHistoryCommand_in_slotPropertyChanged_enabled = prev_addHistoryCommand_in_slotPropertyChanged_enabled;
1108//down addHistoryCommand( toplevelCommand, false /* !execute */ );
1109 }
1110 else {
1111 //remember this action containing 2 subactions
1112 CommandGroup *setPrimaryKeyCommand = new CommandGroup(
1113 i18n("Unset primary key for field \"%1\"")
1114 .arg(set["name"].value().toString()) );
1115 if (toplevelCommand)
1116 toplevelCommand->addCommand( setPrimaryKeyCommand );
1117 else
1118 toplevelCommand = setPrimaryKeyCommand;
1119
1120 d->setPropertyValueIfNeeded( set, "primaryKey", TQVariant(false), setPrimaryKeyCommand, true /*forceAddCommand*/ );
1121 d->setPropertyValueIfNeeded( set, "autoIncrement", TQVariant(false), setPrimaryKeyCommand );
1122// set["autoIncrement"] = TQVariant(false,1);
1123
1124//down addHistoryCommand( toplevelCommand, false /* !execute */ );
1125 }
1126 switchPrimaryKey(set, setPrimaryKey, true/*wasPKey*/, toplevelCommand);
1127 d->updatePropertiesVisibility(
1128 KexiDB::Field::typeForString( set["subType"].value().toString() ), set, toplevelCommand);
1129 addHistoryCommand( toplevelCommand, false /* !execute */ );
1130 //properties' visiblility changed: refresh prop. set
1131 propertySetReloaded(true/*preservePrevSelection*/);
1132 d->slotPropertyChanged_primaryKey_enabled = true;
1133 }
1134}
1135
1137{
1138 updateActions();
1139
1140 if (d->addHistoryCommand_in_slotRowInserted_enabled) {
1141 const int row = d->view->currentRow();
1142 if (row>=0) {
1143 addHistoryCommand( new InsertEmptyRowCommand( this, row ), false /* !execute */ );
1144 }
1145 }
1146 //TODO?
1147}
1148
1150 KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint)
1151{
1152 Q_UNUSED(result)
1153 Q_UNUSED(repaint)
1154 if (item[COLUMN_ID_ICON].toString()=="key")
1155 d->primaryKeyExists = false;
1156
1157 if (d->addHistoryCommand_in_slotAboutToDeleteRow_enabled) {
1158 const int row = d->view->KexiDataAwareObjectInterface::data()->findRef(&item);
1159 KoProperty::Set *set = row >=0 ? d->sets->at(row) : 0;
1160 //set can be 0 here, what means "removing empty row"
1162 new RemoveFieldCommand( this, row, set ),
1163 false /* !execute */
1164 );
1165 }
1166}
1167
1168KexiDB::Field * KexiTableDesignerView::buildField( const KoProperty::Set &set ) const
1169{
1170 //create a map of property values
1171 kexipluginsdbg << set["type"].value() << endl;
1172 TQMap<TQCString, TQVariant> values = KoProperty::propertyValues(set);
1173 //remove internal values, to avoid creating custom field's properties
1174 TQMap<TQCString, TQVariant>::Iterator it = values.begin();
1175 KexiDB::Field *field = new KexiDB::Field();
1176
1177 while (it!=values.end()) {
1178 const TQString propName( it.key() );
1179 if (d->internalPropertyNames.find(propName.latin1()) || propName.startsWith("this:")
1180 || (/*sanity*/propName=="objectType" && KexiDB::Field::BLOB != KexiDB::intToFieldType( set["type"].value().toInt() )))
1181 {
1182 TQMap<TQCString, TQVariant>::Iterator it_tmp = it;
1183 ++it;
1184 values.remove(it_tmp);
1185 }
1186 else
1187 ++it;
1188 }
1189 //assign properties to the field
1190 // (note that "objectType" property will be saved as custom property)
1191 if (!KexiDB::setFieldProperties( *field, values )) {
1192 delete field;
1193 return 0;
1194 }
1195 return field;
1196}
1197
1199{
1200 if (!d->view->acceptRowEdit())
1201 return cancelled;
1202
1203 tristate res = true;
1204 //check for pkey; automatically add a pkey if user wanted
1205 if (!d->primaryKeyExists) {
1206 if (beSilent) {
1207 kexipluginsdbg << "KexiTableDesignerView::buildSchema(): no primay key defined..." << endl;
1208 }
1209 else {
1210 const int questionRes = KMessageBox::questionYesNoCancel(this,
1211 i18n("<p>Table \"%1\" has no <b>primary key</b> defined.</p>"
1212 "<p>Although a primary key is not required, it is needed "
1213 "for creating relations between database tables. "
1214 "Do you want to add primary key automatically now?</p>"
1215 "<p>If you want to add a primary key by hand, press \"Cancel\" "
1216 "to cancel saving table design.</p>").arg(schema.name()),
1217 TQString(), KGuiItem(i18n("&Add Primary Key"), "key"), KStdGuiItem::no(),
1218 "autogeneratePrimaryKeysOnTableDesignSaving");
1219 if (questionRes==KMessageBox::Cancel) {
1220 return cancelled;
1221 }
1222 else if (questionRes==KMessageBox::Yes) {
1223 //-find unique name, starting with, "id", "id2", ....
1224 int i=0;
1225 int idIndex = 1; //means "id"
1226 TQString pkFieldName("id%1");
1227 TQString pkFieldCaption(i18n("Identifier%1", "Id%1"));
1228 while (i<(int)d->sets->size()) {
1229 KoProperty::Set *set = d->sets->at(i);
1230 if (set) {
1231 if ((*set)["name"].value().toString()
1232 == pkFieldName.arg(idIndex==1?TQString() : TQString::number(idIndex))
1233 || (*set)["caption"].value().toString()
1234 == pkFieldCaption.arg(idIndex==1?TQString() : TQString::number(idIndex)))
1235 {
1236 //try next id index
1237 i = 0;
1238 idIndex++;
1239 continue;
1240 }
1241 }
1242 i++;
1243 }
1244 pkFieldName = pkFieldName.arg(idIndex==1?TQString() : TQString::number(idIndex));
1245 pkFieldCaption = pkFieldCaption.arg(idIndex==1?TQString() : TQString::number(idIndex));
1246 //ok, add PK with such unique name
1247 d->view->insertEmptyRow(0);
1248 d->view->setCursorPosition(0, COLUMN_ID_CAPTION);
1249 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_CAPTION,
1250 TQVariant(pkFieldCaption));
1251 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
1252 TQVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
1253 if (!d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*d->view->selectedItem(), true)) {
1254 return cancelled;
1255 }
1257 }
1258 }
1259 }
1260
1261 //check for duplicates
1262 KoProperty::Set *b = 0;
1263 bool no_fields = true;
1264 int i;
1265 TQDict<char> names(1009, false);
1266 char dummy;
1267 for (i=0;i<(int)d->sets->size();i++) {
1268 b = d->sets->at(i);
1269 if (b) {
1270 no_fields = false;
1271 const TQString name = (*b)["name"].value().toString();
1272 if (name.isEmpty()) {
1273 if (beSilent) {
1274 kexipluginswarn <<
1275 TQString("KexiTableDesignerView::buildSchema(): no field caption entered at row %1...")
1276 .arg(i+1) << endl;
1277 }
1278 else {
1279 d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
1280 d->view->startEditCurrentCell();
1281 KMessageBox::information(this, i18n("You should enter field caption.") );
1282 }
1283 res = cancelled;
1284 break;
1285 }
1286 if (names[name]) {
1287 break;
1288 }
1289 names.insert( name, &dummy ); //remember
1290 }
1291 }
1292 if (res == true && no_fields) {//no fields added
1293 if (beSilent) {
1294 kexipluginswarn <<
1295 "KexiTableDesignerView::buildSchema(): no field defined..." << endl;
1296 }
1297 else {
1298 KMessageBox::sorry(this,
1299 i18n("You have added no fields.\nEvery table should have at least one field.") );
1300 }
1301 res = cancelled;
1302 }
1303 if (res == true && b && i<(int)d->sets->size()) {//found a duplicate
1304 if (beSilent) {
1305 kexipluginswarn <<
1306 TQString("KexiTableDesignerView::buildSchema(): duplicated field name '%1'")
1307 .arg((*b)["name"].value().toString()) << endl;
1308 }
1309 else {
1310 d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
1311 d->view->startEditCurrentCell();
1313 KMessageBox::sorry(this,
1314 i18n("You have added \"%1\" field name twice.\nField names cannot be repeated. "
1315 "Correct name of the field.")
1316 .arg((*b)["name"].value().toString()) );
1317 }
1318 res = cancelled;
1319 }
1320 if (res == true) {
1321 //for every field, create KexiDB::Field definition
1322 for (i=0;i<(int)d->sets->size();i++) {
1323 KoProperty::Set *s = d->sets->at(i);
1324 if (!s)
1325 continue;
1326 KexiDB::Field * f = buildField( *s );
1327 if (!f)
1328 continue; //hmm?
1329 schema.addField(f);
1330 if (!(*s)["rowSource"].value().toString().isEmpty() && !(*s)["rowSourceType"].value().toString().isEmpty()) {
1331 //add lookup column
1332 KexiDB::LookupFieldSchema *lookupFieldSchema = new KexiDB::LookupFieldSchema();
1333 lookupFieldSchema->rowSource().setTypeByName( (*s)["rowSourceType"].value().toString() );
1334 lookupFieldSchema->rowSource().setName( (*s)["rowSource"].value().toString() );
1335 lookupFieldSchema->setBoundColumn( (*s)["boundColumn"].value().toInt() );
1339 TQValueList<uint> visibleColumns;
1340 const int visibleColumn = (*s)["visibleColumn"].value().toInt();
1341 if (visibleColumn >= 0)
1342 visibleColumns.append( (uint)visibleColumn );
1343 lookupFieldSchema->setVisibleColumns( visibleColumns );
1345 if (!schema.setLookupFieldSchema(f->name(), lookupFieldSchema)) {
1346 kexipluginswarn <<
1347 "KexiTableDesignerView::buildSchema(): !schema.setLookupFieldSchema()" << endl;
1348 delete lookupFieldSchema;
1349 return false;
1350 }
1351 }
1352 }
1353 }
1354 return res;
1355}
1356
1359static void copyAlterTableActions(KCommand* command, KexiDB::AlterTableHandler::ActionList &actions)
1360{
1361 CommandGroup* cmdGroup = dynamic_cast<CommandGroup*>( command );
1362 if (cmdGroup) {//command group: flatten it
1363 for (TQPtrListIterator<KCommand> it(cmdGroup->commands()); it.current(); ++it)
1364 copyAlterTableActions(it.current(), actions);
1365 return;
1366 }
1367 Command* cmd = dynamic_cast<Command*>( command );
1368 if (!cmd) {
1369 kexipluginswarn << "KexiTableDesignerView::copyAlterTableActions(): cmd is not of type 'Command'!" << endl;
1370 return;
1371 }
1373 //some commands can contain null actions, e.g. "set visibility" command
1374 if (action)
1375 actions.append( action );
1376}
1377
1379{
1380 actions.clear();
1381 kexipluginsdbg << "KexiTableDesignerView::buildAlterTableActions(): " << d->history->commands().count()
1382 << " top-level command(s) to process..." << endl;
1383 for (TQPtrListIterator<KCommand> it(d->history->commands()); it.current(); ++it) {
1384 copyAlterTableActions(it.current(), actions);
1385 }
1386 return true;
1387}
1388
1390{
1391 if (tempData()->table || m_dialog->schemaData()) //must not be
1392 return 0;
1393
1394 //create table schema definition
1395 tempData()->table = new KexiDB::TableSchema(sdata.name());
1396 tempData()->table->setName( sdata.name() );
1397 tempData()->table->setCaption( sdata.caption() );
1398 tempData()->table->setDescription( sdata.description() );
1399
1400 tristate res = buildSchema(*tempData()->table);
1401 cancel = ~res;
1402
1403 //FINALLY: create table:
1404 if (res == true) {
1405 //todo
1407 res = conn->createTable(tempData()->table);
1408 if (res!=true)
1409 parentDialog()->setStatus(conn, "");
1410 }
1411
1412 if (res == true) {
1413 //we've current schema
1414 tempData()->tableSchemaChangedInPreviousView = true;
1415//not needed; KexiProject emits newItemStored signal //let project know the table is created
1416// mainWin()->project()->emitTableCreated(*tempData()->table);
1417 }
1418 else {
1419 delete tempData()->table;
1420 tempData()->table = 0;
1421 }
1422 return tempData()->table;
1423}
1424
1426{
1427 if (!tempData()->table || !m_dialog->schemaData()) {
1428 d->recentResultOfStoreData = false;
1429 return false;
1430 }
1431
1433 KexiDB::AlterTableHandler *alterTableHandler = 0;
1434 KexiDB::TableSchema *newTable = 0;
1435
1436 //- create action list for the alter table handler
1438 tristate res = buildAlterTableActions( actions );
1439 bool realAlterTableCanBeUsed = false;
1440 if (res == true) {
1441 alterTableHandler = new KexiDB::AlterTableHandler( *conn );
1442 alterTableHandler->setActions(actions);
1443
1444 if (!d->tempStoreDataUsingRealAlterTable) {
1445 //only compute requirements
1447 args.onlyComputeRequirements = true;
1448 (void)alterTableHandler->execute(tempData()->table->name(), args);
1449 res = args.result;
1450 if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
1451 realAlterTableCanBeUsed = true;
1452 }
1453 }
1454
1455 if (res == true) {
1456 res = KexiTablePart::askForClosingObjectsUsingTableSchema(
1457 this, *conn, *tempData()->table,
1458 i18n("You are about to change the design of table \"%1\" "
1459 "but following objects using this table are opened:")
1460 .arg(tempData()->table->name()));
1461 }
1462
1463 if (res == true) {
1464 if (!d->tempStoreDataUsingRealAlterTable && !realAlterTableCanBeUsed) {
1466 delete alterTableHandler;
1467 alterTableHandler = 0;
1468 // - inform about removing the current table and ask for confirmation
1469 if (!d->dontAskOnStoreData && !dontAsk) {
1470 bool emptyTable;
1471 const TQString msg = d->messageForSavingChanges(emptyTable);
1472 if (!emptyTable) {
1473 if (KMessageBox::No == KMessageBox::questionYesNo(this, msg))
1474 res = cancelled;
1475 }
1476 }
1477 d->dontAskOnStoreData = false; //one-time use
1478 if (~res) {
1479 d->recentResultOfStoreData = res;
1480 return res;
1481 }
1482 // keep old behaviour:
1483 newTable = new KexiDB::TableSchema();
1484 // copy the schema data
1485 static_cast<KexiDB::SchemaData&>(*newTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
1486 res = buildSchema(*newTable);
1487 kexipluginsdbg << "KexiTableDesignerView::storeData() : BUILD SCHEMA:" << endl;
1488 newTable->debug();
1489
1490 res = conn->alterTable(*tempData()->table, *newTable);
1491 if (res != true)
1492 parentDialog()->setStatus(conn, "");
1493 }
1494 else {
1496 newTable = alterTableHandler->execute(tempData()->table->name(), args);
1497 res = args.result;
1498 kexipluginsdbg << "KexiTableDesignerView::storeData() : ALTER TABLE EXECUTE: "
1499 << res.toString() << endl;
1500 if (true != res) {
1501 alterTableHandler->debugError();
1502 parentDialog()->setStatus(alterTableHandler, "");
1503 }
1504 }
1505 }
1506 if (res == true) {
1507 //change current schema
1508 tempData()->table = newTable;
1509 tempData()->tableSchemaChangedInPreviousView = true;
1510 d->history->clear();
1511 }
1512 else {
1513 delete newTable;
1514 }
1515 delete alterTableHandler;
1516 d->recentResultOfStoreData = res;
1517 return res;
1518}
1519
1521{
1522#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
1523# ifdef KEXI_DEBUG_GUI
1524 if (mainWin()->activeWindow() != parentDialog()) //to avoid executing for multiple alter table views
1525 return false;
1526 if (!tempData()->table || !m_dialog->schemaData())
1527 return false;
1530 tristate res = buildAlterTableActions( actions );
1531//todo: result?
1532 KexiDB::AlterTableHandler alterTableHandler( *conn );
1533 alterTableHandler.setActions(actions);
1535 if (debugTarget) {
1536 args.debugString = debugTarget;
1537 }
1538 else {
1539 args.simulate = true;
1540 }
1541 (void)alterTableHandler.execute(tempData()->table->name(), args);
1542 return args.result;
1543# else
1544 return false;
1545# endif
1546#else
1547 return false;
1548#endif
1549}
1550
1555
1557{
1558 TQSignal signal;
1559 signal.connect( mainWin(), TQ_SLOT(slotProjectSave()) );
1560 d->tempStoreDataUsingRealAlterTable = true;
1561 d->recentResultOfStoreData = false;
1562 signal.activate(); //will call KexiMainWindowImpl::slotProjectSaveAs() and thus storeData()
1563 d->tempStoreDataUsingRealAlterTable = false;
1564 return d->recentResultOfStoreData;
1565}
1566
1567KexiTablePart::TempData* KexiTableDesignerView::tempData() const
1568{
1569 return static_cast<KexiTablePart::TempData*>(parentDialog()->tempData());
1570}
1571
1572/*void KexiTableDesignerView::slotAboutToUpdateRow(
1573 KexiTableItem* item, KexiDB::RowEditBuffer* buffer, KexiDB::ResultInfo* result)
1574{
1575 KexiDB::RowEditBuffer::SimpleMap map = buffer->simpleBuffer();
1576 buffer->debug();
1577
1578 TQVariant old_type = item->at(1);
1579 TQVariant *buf_type = buffer->at( d->view->field(1)->name() );
1580
1581 //check if there is a type specified
1582// if ((old_type.isNull() && !buf_type) || (buf_type && buf_type->isNull())) {
1583 //kdDebug() << "err" << endl;
1584 //}
1585// allow = true;
1586// m_dirty = m_dirty | result->success;
1587}*/
1588
1589#ifdef KEXI_DEBUG_GUI
1590void KexiTableDesignerView::debugCommand( KCommand* command, int nestingLevel )
1591{
1592 if (dynamic_cast<Command*>(command))
1593 KexiUtils::addAlterTableActionDebug(dynamic_cast<Command*>(command)->debugString(), nestingLevel);
1594 else
1595 KexiUtils::addAlterTableActionDebug(command->name(), nestingLevel);
1596 //show subcommands
1597 if (dynamic_cast<CommandGroup*>(command)) {
1598 for (TQPtrListIterator<KCommand> it(dynamic_cast<CommandGroup*>(command)->commands()); it.current(); ++it) {
1599 debugCommand(it.current(), nestingLevel + 1);
1600 }
1601 }
1602}
1603#endif
1604
1605void KexiTableDesignerView::addHistoryCommand( KCommand* command, bool execute )
1606{
1607#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
1608# ifdef KEXI_DEBUG_GUI
1609 debugCommand( command, 0 );
1610# endif
1611 d->history->addCommand( command, execute );
1613#endif
1614}
1615
1617{
1618#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
1619 setAvailable("edit_undo", d->historyActionCollection->action("edit_undo")->isEnabled());
1620 setAvailable("edit_redo", d->historyActionCollection->action("edit_redo")->isEnabled());
1621#endif
1622}
1623
1625{
1626#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
1627# ifdef KEXI_DEBUG_GUI
1628 KexiUtils::addAlterTableActionDebug(TQString("UNDO:"));
1629# endif
1630 d->history->undo();
1632#endif
1633}
1634
1636{
1637#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
1638# ifdef KEXI_DEBUG_GUI
1639 KexiUtils::addAlterTableActionDebug(TQString("REDO:"));
1640# endif
1641 d->history->redo();
1643#endif
1644}
1645
1647{
1648#ifdef KEXI_DEBUG_GUI
1649 debugCommand( command, 1 );
1650#endif
1651}
1652
1654{
1655 //update title
1656 if (propertySet()) {
1657 const KoProperty::Set &set = *propertySet();
1658 TQString captionOrName(set["caption"].value().toString());
1659 if (captionOrName.isEmpty())
1660 captionOrName = set["name"].value().toString();
1662 d->contextMenuTitle->setTitle( i18n("Table field \"%1\"").arg(captionOrName) );
1663 }
1664 else {
1665 d->contextMenuTitle->setTitle( i18n("Empty table row", "Empty Row") );
1666 }
1667}
1668
1670{
1671 KexiDB::TableSchema tempTable;
1672 //copy schema data
1673 static_cast<KexiDB::SchemaData&>(tempTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
1674 result = buildSchema(tempTable, true /*beSilent*/);
1675 if (true!=result)
1676 return TQString();
1677 return tempTable.debugString(false /*without name*/);
1678}
1679
1680// -- low-level actions used by undo/redo framework
1681
1682void KexiTableDesignerView::clearRow(int row, bool addCommand)
1683{
1684 if (!d->view->acceptRowEdit())
1685 return;
1686 KexiTableItem *item = d->view->itemAt(row);
1687 if (!item)
1688 return;
1689 //remove from prop. set
1690 d->sets->remove( row );
1691 //clear row in table view (just clear value in COLUMN_ID_TYPE column)
1692// for (int i=0; i < (int)d->view->KexiDataAwareObjectInterface::data()->columnsCount(); i++) {
1693 if (!addCommand) {
1694 d->addHistoryCommand_in_slotRowUpdated_enabled = false;
1695 d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
1696 d->slotBeforeCellChanged_enabled = false;
1697 }
1698 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, TQVariant());
1699 if (!addCommand) {
1700 d->addHistoryCommand_in_slotRowUpdated_enabled = true;
1701 d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
1702 d->slotBeforeCellChanged_enabled = true;
1703 }
1704 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item, true);
1705}
1706
1707void KexiTableDesignerView::insertField(int row, const TQString& caption, bool addCommand)
1708{
1709 insertFieldInternal(row, 0, caption, addCommand);
1710}
1711
1712void KexiTableDesignerView::insertField(int row, KoProperty::Set& set, bool addCommand)
1713{
1714 insertFieldInternal(row, &set, TQString(), addCommand);
1715}
1716
1717void KexiTableDesignerView::insertFieldInternal(int row, KoProperty::Set* set, //const KexiDB::Field& field,
1718 const TQString& caption, bool addCommand)
1719{
1720 if (set && (!set->contains("type") || !set->contains("caption"))) {
1721 kexipluginswarn << "KexiTableDesignerView::insertField(): no 'type' or 'caption' property in set!" << endl;
1722 return;
1723 }
1724 if (!d->view->acceptRowEdit())
1725 return;
1726 KexiTableItem *item = d->view->itemAt(row);
1727 if (!item)
1728 return;
1729 if (!addCommand) {
1730 d->addHistoryCommand_in_slotRowUpdated_enabled = false;
1731 d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
1732 d->slotBeforeCellChanged_enabled = false;
1733 }
1734 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION,
1735 set ? (*set)["caption"].value() : TQVariant(caption));//field.caption());
1736 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
1737 set ? (int)KexiDB::Field::typeGroup( (*set)["type"].value().toInt() )-1/*counting from 0*/
1738 : (((int)KexiDB::Field::TextGroup)-1)/*default type, counting from 0*/
1739 );
1740 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC,
1741 set ? (*set)["description"].value() : TQVariant());//field.description());
1742 if (!addCommand) {
1743 d->slotBeforeCellChanged_enabled = true;
1744 }
1745 //this will create a new property set:
1746 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
1747 if (set) {
1748 KoProperty::Set *newSet = d->sets->at(row);
1749 if (newSet) {
1750 *newSet = *set; //deep copy
1751 }
1752 else {
1753 kexipluginswarn << "KexiTableDesignerView::insertField() !newSet, row==" << row << endl;
1754 }
1755 }
1756 if (!addCommand) {
1757 d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
1758 d->addHistoryCommand_in_slotRowUpdated_enabled = true;
1759 }
1760 d->view->updateRow( row );
1761 propertySetReloaded(true);
1762}
1763
1764void KexiTableDesignerView::insertEmptyRow( int row, bool addCommand )
1765{
1766 if (!addCommand) {
1767 d->addHistoryCommand_in_slotRowInserted_enabled = false;
1768 }
1769 d->view->insertEmptyRow( row );
1770 if (!addCommand) {
1771 d->addHistoryCommand_in_slotRowInserted_enabled = true;
1772 }
1773}
1774
1775/*void KexiTableDesignerView::deleteRow( int row )
1776{
1777 d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
1778 d->view->deleteItem( d->view->KexiDataAwareObjectInterface::data()->at(row) );
1779 d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
1780}*/
1781
1782void KexiTableDesignerView::deleteRow( int row, bool addCommand )
1783{
1784 KexiTableItem *item = d->view->itemAt( row );
1785 if (!item)
1786 return;
1787 if (!addCommand) {
1788 d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
1789 }
1790 const bool res = d->view->deleteItem(item);
1791 if (!addCommand) {
1792 d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
1793 }
1794 if (!res)
1795 return;
1796}
1797
1799 const TQCString& propertyName, const TQVariant& newValue,
1800 KoProperty::Property::ListData* const listData, bool addCommand )
1801{
1802#ifdef KEXI_DEBUG_GUI
1803 KexiUtils::addAlterTableActionDebug(TQString("** changeFieldProperty: \"")
1804 + TQString(propertyName) + "\" to \"" + newValue.toString() + "\"", 2/*nestingLevel*/);
1805#endif
1806 if (!d->view->acceptRowEdit())
1807 return;
1808
1809 KoProperty::Set* set = d->sets->at( row );
1810 if (!set || !set->contains(propertyName))
1811 return;
1812 KoProperty::Property &property = set->property(propertyName);
1813 if (listData) {
1814 if (listData->keys.isEmpty())
1815 property.setListData( 0 );
1816 else
1817 property.setListData( new KoProperty::Property::ListData(*listData) );
1818 }
1819 if (propertyName != "type") //delayed type update (we need to have subtype set properly)
1820 property.setValue(newValue);
1821 KexiTableItem *item = d->view->itemAt(row);
1822 Q_ASSERT(item);
1823
1824 if (propertyName == "type") {
1825 // d->addHistoryCommand_in_slotRowUpdated_enabled = false;
1826// d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
1827 d->slotPropertyChanged_subType_enabled = false;
1828 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
1829 int( KexiDB::Field::typeGroup( newValue.toInt() ) )-1);
1830 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
1831 d->addHistoryCommand_in_slotRowUpdated_enabled = true;
1832// d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
1833 // d->slotPropertyChanged_subType_enabled = true;
1834 property.setValue(newValue); //delayed type update (we needed to have subtype set properly)
1835 }
1836
1837 if (!addCommand) {
1838 d->addHistoryCommand_in_slotRowUpdated_enabled = false;
1839 d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
1840 d->slotPropertyChanged_subType_enabled = false;
1841 }
1842 //special cases: properties displayed within the data grid:
1843 if (propertyName == "caption") {
1844 if (!addCommand) {
1845 d->slotBeforeCellChanged_enabled = false;
1846 }
1847 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, newValue);
1848 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
1849 if (!addCommand) {
1850 d->slotBeforeCellChanged_enabled = true;
1851 }
1852 }
1853 else if (propertyName == "description") {
1854 if (!addCommand) {
1855 d->slotBeforeCellChanged_enabled = false;
1856 }
1857 d->view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(item, COLUMN_ID_DESC, newValue);
1858 if (!addCommand) {
1859 d->slotBeforeCellChanged_enabled = true;
1860 }
1861 d->view->KexiDataAwareObjectInterface::data()->saveRowChanges(*item);
1862 }
1863 if (!addCommand) {
1864 d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
1865 d->addHistoryCommand_in_slotRowUpdated_enabled = true;
1866 d->slotPropertyChanged_subType_enabled = true;
1867 }
1868 d->view->updateRow( row );
1869}
1870
1872 const TQCString& propertyName, const TQVariant& newValue,
1873 KoProperty::Property::ListData* const listData, bool addCommand )
1874{
1875 //find a property by UID
1876 const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
1877 if (row<0) {
1878 kexipluginswarn << "KexiTableDesignerView::changeFieldProperty(): field with uid="<<fieldUID<<" not found!"<<endl;
1879 return;
1880 }
1881 changeFieldPropertyForRow(row, propertyName, newValue, listData, addCommand);
1882}
1883
1885 int fieldUID, const TQCString& propertyName, bool visible )
1886{
1887#ifdef KEXI_DEBUG_GUI
1888 KexiUtils::addAlterTableActionDebug(TQString("** changePropertyVisibility: \"")
1889 + TQString(propertyName) + "\" to \"" + (visible ? "true" : "false") + "\"", 2/*nestingLevel*/);
1890#endif
1891 if (!d->view->acceptRowEdit())
1892 return;
1893
1894 //find a property by name
1895 const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
1896 if (row<0)
1897 return;
1898 KoProperty::Set* set = d->sets->at( row );
1899 if (!set || !set->contains(propertyName))
1900 return;
1901
1902 KoProperty::Property &property = set->property(propertyName);
1903 if (property.isVisible() != visible) {
1904 property.setVisible(visible);
1905 propertySetReloaded(true);
1906 }
1907}
1908
1910{
1912
1913 //if (parentDialog()!=parentDialog()->mainWin()->currentDialog())
1914 // return; //this is not the current dialog's view
1915
1916 static_cast<KexiTablePart*>(parentDialog()->part())->lookupColumnPage()
1917 ->assignPropertySet(propertySet());
1918}
1919
1921{
1922 //- create action list for the alter table handler
1924 tristate res = buildAlterTableActions( actions );
1925 if (res != true)
1926 return true;
1927
1929 KexiDB::AlterTableHandler *alterTableHandler = new KexiDB::AlterTableHandler( *conn );
1930 alterTableHandler->setActions(actions);
1931
1932 //only compute requirements
1934 args.onlyComputeRequirements = true;
1935 (void)alterTableHandler->execute(tempData()->table->name(), args);
1936 res = args.result;
1937 delete alterTableHandler;
1938 if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
1939 return false;
1940 return true;
1941}
1942
1943#include "kexitabledesignerview.moc"
void plugSharedAction(const char *action_name, TQObject *receiver, const char *slot)
@ Identifier
string allowing nonempty identifiers
static void init()
Called once to register all propert and editor types provided by this factory.
Abstract base class used for implementing all the AlterTable actions.
Definition alter.h:157
Arguments for AlterTableHandler::execute().
Definition alter.h:400
A tool for handling altering database table schema.
Definition alter.h:112
void setActions(const ActionList &actions)
Definition alter.cpp:773
TQPtrList< ActionBase > ActionList
Defines a type for action list.
Definition alter.h:151
TableSchema * execute(const TQString &tableName, ExecutionArguments &args)
Definition alter.cpp:785
Provides database connection, allowing queries and data modification.
Definition connection.h:62
bool createTable(TableSchema *tableSchema, bool replaceExisting=false)
Creates a table according to the given schema.
virtual bool isReadOnly() const
tristate alterTable(TableSchema &tableSchema, TableSchema &newTableSchema)
FieldList & addField(Field *field)
Field * field(uint id)
Definition fieldlist.h:76
Meta-data for a field.
Definition field.h:73
TQString typeName() const
Definition field.h:298
@ InvalidType
Definition field.h:84
TQString captionOrName() const
Definition field.h:374
TQString typeString() const
Definition field.h:308
static TQVariant::Type variantType(uint type)
Converts type type to TQVariant equivalent as accurate as possible.
Definition field.cpp:141
void setEnumHints(const TQValueVector< TQString > &l)
Definition field.h:544
void setSubType(const TQString &subType)
Definition field.h:322
static Type typeForString(const TQString &typeString)
Definition field.cpp:197
static TQString typeName(uint type)
Definition field.cpp:173
TypeGroup typeGroup() const
Definition field.h:301
TQString typeGroupName() const
Definition field.h:304
static TypeGroup typeGroup(uint type)
Definition field.cpp:281
virtual TableSchema * table() const
Definition field.cpp:300
static TQString typeString(uint type)
Definition field.cpp:179
void setTypeByName(const TQString &typeName)
Provides information about lookup field's setup.
void setVisibleColumns(const TQValueList< uint > &list)
TQValueList< uint > visibleColumns() const
void debugError()
Definition object.cpp:159
virtual void clear()
TQString debugString(bool includeTableName)
bool setLookupFieldSchema(const TQString &fieldName, LookupFieldSchema *lookupFieldSchema)
KexiTableViewData * data() const
KexiDataTable(KexiMainWindow *mainWin, TQWidget *parent, const char *name=0, bool dbAware=true)
KexiDialogTempData * tempData() const
KexiPart::Part * part() const
Kexi's main window interface This interface is implemented by KexiMainWindowImpl class.
virtual KexiProject * project()=0
Project data of currently opened project or NULL if no project here yet.
KexiDB::Connection * dbConnection() const
Undo/redo command used for when changing a property for a table field.
Base class for all Table Designer's commands.
virtual KexiDB::AlterTableHandler::ActionBase * createAction()
Undo/redo command used when property visibility is changed.
Undo/redo command used when a new field is inserted into a table.
Undo/redo command used when a field is removed from a table.
void switchPrimaryKey(KoProperty::Set &propertySet, bool set, bool aWasPKey=false, CommandGroup *commandGroup=0)
virtual KoProperty::Set * propertySet()
void getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup, TQStringList &stringsList, TQStringList &namesList)
Gets subtype strings and names for type fieldType.
virtual KexiDB::SchemaData * storeNewData(const KexiDB::SchemaData &sdata, bool &cancel)
void slotAboutToDeleteRow(KexiTableItem &item, KexiDB::ResultInfo *result, bool repaint)
Called before row deleting in tableview.
virtual tristate afterSwitchFrom(int mode)
virtual void deleteRow(int row, bool addCommand=false)
void insertFieldInternal(int row, KoProperty::Set *set, const TQString &caption, bool addCommand)
void addHistoryCommand(KCommand *command, bool execute)
virtual void propertySetSwitched()
Reimplemented to pass the information also to the "Lookup" tab.
KexiTableDesignerView(KexiMainWindow *win, TQWidget *parent)
virtual void changeFieldPropertyForRow(int row, const TQCString &propertyName, const TQVariant &newValue, KoProperty::Property::ListData *const listData, bool addCommand)
tristate buildSchema(KexiDB::TableSchema &schema, bool beSilent=false)
virtual void insertField(int row, const TQString &caption, bool addCommand=false)
virtual TQString debugStringForCurrentTableSchema(tristate &result)
void initData()
called whenever data should be reloaded (on switching to this view mode)
void slotRowInserted()
Called before row inserting in tableview.
void slotBeforeCellChanged(KexiTableItem *item, int colnum, TQVariant &newValue, KexiDB::ResultInfo *result)
Called before cell change in tableview.
virtual tristate storeData(bool dontAsk=false)
virtual void clearRow(int row, bool addCommand=false)
virtual tristate executeRealAlterTable()
void slotCommandExecuted(KCommand *command)
void slotPropertyChanged(KoProperty::Set &set, KoProperty::Property &property)
void updateUndoRedoActions()
Updates undo/redo shared actions availability by looking at command history's action.
KoProperty::Set * createPropertySet(int row, const KexiDB::Field &field, bool newOne=false)
void changePropertyVisibility(int fieldUID, const TQCString &propertyName, bool visible)
KexiDB::Field * buildField(const KoProperty::Set &set) const
tristate buildAlterTableActions(KexiDB::AlterTableHandler::ActionList &actions)
virtual tristate simulateAlterTableExecution(TQString *debugTarget)
virtual tristate beforeSwitchTo(int mode, bool &dontStore)
void slotRowUpdated(KexiTableItem *item)
Called on row change in a tableview.
virtual void insertEmptyRow(int row, bool addCommand=false)
void changeFieldProperty(int fieldUID, const TQCString &propertyName, const TQVariant &newValue, KoProperty::Property::ListData *const listData=0, bool addCommand=false)
KexiDB::Field * field() const
void setHeaderTextVisible(bool visible)
void setIcon(const TQIconSet &icon)
void setReadOnly(bool ro)
forces readOnly flag to be set to ro
KexiTableView class provides a widget for displaying data in a tabular view.
virtual TDEAction * sharedAction(const char *action_name)
virtual bool dirty() const
virtual void setAvailable(const char *action_name, bool set)
virtual void propertySetSwitched()
void propertySetReloaded(bool preservePrevSelection=false, const TQCString &propertyToSelect=TQCString())
KexiDialogBase * parentDialog() const
KexiMainWindow * mainWin() const
3-state logical type with three values: true, false and cancelled and convenient operators.
Definition tristate.h:100
TQString toString() const
Definition tristate.h:166
KEXI_DB_EXPORT TQStringList typeStringsForGroup(Field::TypeGroup typeGroup)
KEXI_DB_EXPORT Field::Type intToFieldType(int type)
KEXI_DB_EXPORT TQStringList typeNamesForGroup(Field::TypeGroup typeGroup)
KEXI_DB_EXPORT bool setFieldProperties(Field &field, const TQMap< TQCString, TQVariant > &values)
KEXI_DB_EXPORT Field::Type defaultTypeForGroup(Field::TypeGroup typeGroup)
KEXIUTILS_EXPORT TQString string2Identifier(const TQString &s)
KEXIUTILS_EXPORT TQIconSet colorizeIconToTextColor(const TQPixmap &icon, const TQPalette &palette)
@ TextViewMode
Also known as SQL View Mode.
Definition kexi.h:48
@ NoViewMode
Definition kexi.h:44