kexitabledesignerview_p.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_p.h"
21#include "kexitabledesignerview.h"
22
23#include <tqlayout.h>
24#include <tqlabel.h>
25#include <tqsplitter.h>
26
27#include <kiconloader.h>
28#include <kdebug.h>
29#include <tdelocale.h>
30#include <tdeaction.h>
31#include <tdepopupmenu.h>
32#include <tdemessagebox.h>
33
34#include <koproperty/set.h>
35
36#include <kexidb/cursor.h>
37#include <kexidb/tableschema.h>
38#include <kexidb/connection.h>
39#include <kexidb/utils.h>
40#include <kexidb/roweditbuffer.h>
41#include <kexidb/error.h>
42#include <kexiutils/identifier.h>
43#include <kexiproject.h>
44#include <keximainwindow.h>
45#include <widget/tableview/kexidataawarepropertyset.h>
46#include <widget/kexicustompropertyfactory.h>
47#include <kexiutils/utils.h>
48#include <kexidialogbase.h>
49#include <kexitableview.h>
50#include "kexitabledesignercommands.h"
51
52using namespace KexiTableDesignerCommands;
53
54//----------------------------------------------
55
56CommandHistory::CommandHistory(TDEActionCollection *actionCollection, bool withMenus)
57 : KCommandHistory(actionCollection, withMenus)
58{
59 // We need ALL the commands because we'll collect reuse their
60 // data before performing alter table, so set that to the maximum,
61 // as KCommandHistory has default = 50.
62 setUndoLimit(INT_MAX);
63 setRedoLimit(INT_MAX);
64}
65
66void CommandHistory::addCommand(KCommand *command, bool execute)
67{
68 KCommandHistory::addCommand(command, execute);
69 m_commandsToUndo.append(command);
70}
71
72void CommandHistory::undo()
73{
74 if (!m_commandsToUndo.isEmpty()) {
75 KCommand * cmd = m_commandsToUndo.take( m_commandsToUndo.count()-1 );
76 m_commandsToRedo.append( cmd );
77 }
78 KCommandHistory::undo();
79}
80
81void CommandHistory::redo()
82{
83 if (!m_commandsToRedo.isEmpty()) {
84 KCommand * cmd = m_commandsToRedo.take( m_commandsToRedo.count()-1 );
85 m_commandsToUndo.append( cmd );
86 }
87 KCommandHistory::redo();
88}
89
90void CommandHistory::clear() {
91 KCommandHistory::clear(); m_commandsToUndo.clear();
92}
93
94//----------------------------------------------
95
96KexiTableDesignerViewPrivate::KexiTableDesignerViewPrivate(KexiTableDesignerView* aDesignerView)
97 : designerView(aDesignerView)
98 , sets(0)
99 , uniqueIdCounter(0)
100 , dontAskOnStoreData(false)
101 , slotTogglePrimaryKeyCalled(false)
102 , primaryKeyExists(false)
103 , slotPropertyChanged_primaryKey_enabled(true)
104 , slotPropertyChanged_subType_enabled(true)
105 , addHistoryCommand_in_slotPropertyChanged_enabled(true)
106 , addHistoryCommand_in_slotRowUpdated_enabled(true)
107 , addHistoryCommand_in_slotAboutToDeleteRow_enabled(true)
108 , addHistoryCommand_in_slotRowInserted_enabled(true)
109 , slotBeforeCellChanged_enabled(true)
110 , tempStoreDataUsingRealAlterTable(false)
111{
112 historyActionCollection = new TDEActionCollection((TQWidget*)0,"");
113 history = new CommandHistory(historyActionCollection, true);
114
115 internalPropertyNames.insert("subType",(char*)1);
116 internalPropertyNames.insert("uid",(char*)1);
117 internalPropertyNames.insert("newrow",(char*)1);
118 internalPropertyNames.insert("rowSource",(char*)1);
119 internalPropertyNames.insert("rowSourceType",(char*)1);
120 internalPropertyNames.insert("boundColumn",(char*)1);
121 internalPropertyNames.insert("visibleColumn",(char*)1);
122}
123
124KexiTableDesignerViewPrivate::~KexiTableDesignerViewPrivate() {
125 delete sets;
126 delete historyActionCollection;
127 delete history;
128}
129
130int KexiTableDesignerViewPrivate::generateUniqueId()
131{
132 return ++uniqueIdCounter;
133}
134
135void KexiTableDesignerViewPrivate::setPropertyValueIfNeeded(
136 const KoProperty::Set& set, const TQCString& propertyName,
137 const TQVariant& newValue, const TQVariant& oldValue, CommandGroup* commandGroup,
138 bool forceAddCommand, bool rememberOldValue,
139 TQStringList* const slist, TQStringList* const nlist)
140{
141 KoProperty::Property& property = set[propertyName];
142
143 KoProperty::Property::ListData *oldListData = property.listData() ?
144 new KoProperty::Property::ListData(*property.listData()) : 0; //remember because we'll change list data soon
145 if (slist && nlist) {
146 if (slist->isEmpty() || nlist->isEmpty()) {
147 property.setListData(0);
148 }
149 else {
150 property.setListData(*slist, *nlist);
151 }
152 }
153 if (oldValue.type() == newValue.type()
154 && (oldValue == newValue || (!oldValue.isValid() && !newValue.isValid()))
155 && !forceAddCommand)
156 {
157 return;
158 }
159
160 const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled
161 = addHistoryCommand_in_slotPropertyChanged_enabled; //remember
162 addHistoryCommand_in_slotPropertyChanged_enabled = false;
163 if (property.value() != newValue)
164 property.setValue( newValue, rememberOldValue );
165 if (commandGroup) {
166 commandGroup->addCommand(
167 new ChangeFieldPropertyCommand( designerView, set, propertyName, oldValue, newValue,
168 oldListData, property.listData()) );
169 }
170 delete oldListData;
171 addHistoryCommand_in_slotPropertyChanged_enabled
172 = prev_addHistoryCommand_in_slotPropertyChanged_enabled; //restore
173}
174
175void KexiTableDesignerViewPrivate::setPropertyValueIfNeeded(
176 const KoProperty::Set& set, const TQCString& propertyName,
177 const TQVariant& newValue, CommandGroup* commandGroup,
178 bool forceAddCommand, bool rememberOldValue,
179 TQStringList* const slist, TQStringList* const nlist)
180{
181 KoProperty::Property& property = set[propertyName];
182 TQVariant oldValue( property.value() );
183 setPropertyValueIfNeeded( set, propertyName, newValue, property.value(),
184 commandGroup, forceAddCommand, rememberOldValue, slist, nlist);
185}
186
187void KexiTableDesignerViewPrivate::setVisibilityIfNeeded( const KoProperty::Set& set, KoProperty::Property* prop,
188 bool visible, bool &changed, CommandGroup *commandGroup )
189{
190 if (prop->isVisible() != visible) {
191 if (commandGroup) {
192 commandGroup->addCommand(
193 new ChangePropertyVisibilityCommand( designerView, set, prop->name(), visible ) );
194 }
195 prop->setVisible( visible );
196 changed = true;
197 }
198}
199
200bool KexiTableDesignerViewPrivate::updatePropertiesVisibility(KexiDB::Field::Type fieldType, KoProperty::Set &set,
201 CommandGroup *commandGroup)
202{
203 bool changed = false;
204 KoProperty::Property *prop;
205 bool visible;
206
207 prop = &set["subType"];
208 kexipluginsdbg << "subType=" << prop->value().toInt() << " type=" << set["type"].value().toInt()<< endl;
209
210 //if there is no more than 1 subType name or it's a PK: hide the property
211 visible = (prop->listData() && prop->listData()->keys.count() > 1 /*disabled || isObjectTypeGroup*/)
212 && set["primaryKey"].value().toBool()==false;
213 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
214
215 prop = &set["objectType"];
216 const bool isObjectTypeGroup = set["type"].value().toInt() == (int)KexiDB::Field::BLOB; // used only for BLOBs
217 visible = isObjectTypeGroup;
218 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
219
220 prop = &set["unsigned"];
221 visible = KexiDB::Field::isNumericType(fieldType);
222 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
223
224 prop = &set["length"];
225 visible = (fieldType == KexiDB::Field::Text);
226 if (prop->isVisible()!=visible) {
227// prop->setVisible( visible );
228 //update the length when it makes sense
229 const int lengthToSet = visible ? KexiDB::Field::defaultTextLength() : 0;
230 setPropertyValueIfNeeded( set, "length", lengthToSet,
231 commandGroup, false, false );
232// if (lengthToSet != prop->value().toInt())
233// prop->setValue( lengthToSet, false );
234// changed = true;
235 }
236 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
237#ifndef KEXI_NO_UNFINISHED
238 prop = &set["precision"];
239 visible = KexiDB::Field::isFPNumericType(fieldType);
240 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
241#endif
242 prop = &set["visibleDecimalPlaces"];
244 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
245
246 prop = &set["unique"];
247 visible = fieldType != KexiDB::Field::BLOB;
248 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
249
250 prop = &set["indexed"];
251 visible = fieldType != KexiDB::Field::BLOB;
252 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
253
254 prop = &set["allowEmpty"];
255 visible = KexiDB::Field::hasEmptyProperty(fieldType);
256 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
257
258 prop = &set["autoIncrement"];
259 visible = KexiDB::Field::isAutoIncrementAllowed(fieldType);
260 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
261
263#ifdef KEXI_NO_UNFINISHED
264 prop = &set["defaultValue"];
265 visible = !isObjectTypeGroup;
266 setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
267#endif
268
269 return changed;
270}
271
272TQString KexiTableDesignerViewPrivate::messageForSavingChanges(bool &emptyTable, bool skipWarning)
273{
274 KexiDB::Connection *conn = designerView->mainWin()->project()->dbConnection();
275 bool ok;
276 emptyTable = conn->isEmpty( *designerView->tempData()->table, ok ) && ok;
277 return i18n("Do you want to save the design now?")
278 + ( (emptyTable || skipWarning) ? TQString() :
279 (TQString("\n\n") + designerView->part()->i18nMessage(":additional message before saving design",
280 designerView->parentDialog())) );
281}
282
283void KexiTableDesignerViewPrivate::updateIconForItem(KexiTableItem &item, KoProperty::Set& set)
284{
285 TQVariant icon;
286 if (!set["rowSource"].value().toString().isEmpty() && !set["rowSourceType"].value().toString().isEmpty())
287 icon = "combo";
288 //show/hide icon in the table
289 view->KexiDataAwareObjectInterface::data()->clearRowEditBuffer();
290 view->KexiDataAwareObjectInterface::data()->updateRowEditBuffer(&item, COLUMN_ID_ICON, icon);
291 view->KexiDataAwareObjectInterface::data()->saveRowChanges(item, true);
292}
293
294#include "kexitabledesignerview_p.moc"
Provides database connection, allowing queries and data modification.
Definition connection.h:62
bool isEmpty(TableSchema &table, bool &success)
bool isAutoIncrementAllowed() const
Definition field.h:396
bool hasEmptyProperty() const
Definition field.h:388
bool isFPNumericType() const
Definition field.h:266
bool isNumericType() const
Definition field.h:252
Undo/redo command used for when changing a property for a table field.
Undo/redo command used when property visibility is changed.
Design view of the Table Designer.
KEXI_DB_EXPORT bool supportsVisibleDecimalPlacesProperty(Field::Type type)