1 /*
2 SPDX-License-Identifier: GPL-2.0-or-later
3 SPDX-FileCopyrightText: 2003-2020 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
4 */
5
6 #include "umlforeignkeyconstraintdialog.h"
7
8 #include "attribute.h"
9 #include "classifier.h"
10 #include "classifierlistitem.h"
11 #include "debug_utils.h"
12 #include "dialog_utils.h"
13 #include "entityattribute.h"
14 #include "enumliteral.h"
15 #include "enum.h"
16 #include "entity.h"
17 #include "foreignkeyconstraint.h"
18 #include "object_factory.h"
19 #include "operation.h"
20 #include "template.h"
21 #include "uml.h"
22 #include "umldoc.h"
23 #include "umlentitylist.h"
24 #include "uniqueconstraint.h"
25 #include "icon_utils.h"
26
27 #include <kcombobox.h>
28 #if QT_VERSION < 0x050000
29 #include <kdialogbuttonbox.h>
30 #endif
31 #include <klineedit.h>
32 #include <KLocalizedString>
33 #include <KMessageBox>
34
35 #include <QApplication>
36 #include <QGridLayout>
37 #include <QGroupBox>
38 #include <QHBoxLayout>
39 #include <QLabel>
40 #include <QPushButton>
41 #include <QTreeWidget>
42 #include <QVBoxLayout>
43
44 /**
45 * Sets up the UMLForeignKeyConstraintDialog
46 *
47 * @param parent The parent to the UMLForeignKeyConstraintDialog.
48 * @param pForeignKeyConstraint The Unique Constraint to show the properties of
49 */
UMLForeignKeyConstraintDialog(QWidget * parent,UMLForeignKeyConstraint * pForeignKeyConstraint)50 UMLForeignKeyConstraintDialog::UMLForeignKeyConstraintDialog(QWidget* parent, UMLForeignKeyConstraint* pForeignKeyConstraint)
51 : MultiPageDialogBase(parent),
52 m_doc(UMLApp::app()->document()),
53 m_pForeignKeyConstraint(pForeignKeyConstraint)
54 {
55 setCaption(i18n("Foreign Key Setup"));
56 setupGeneralPage();
57 setupColumnPage();
58
59 connect(this, SIGNAL(okClicked()), this, SLOT(slotOk()));
60 connect(this, SIGNAL(applyClicked()), this, SLOT(slotApply()));
61 }
62
63 /**
64 * Standard destructor.
65 */
~UMLForeignKeyConstraintDialog()66 UMLForeignKeyConstraintDialog::~UMLForeignKeyConstraintDialog()
67 {
68 }
69
70 /**
71 * Adds pair to the list.
72 */
slotAddPair()73 void UMLForeignKeyConstraintDialog::slotAddPair()
74 {
75 // get the index of the selected local column and referenced column
76 int indexL = m_ColumnWidgets.localColumnCB->currentIndex();
77 int indexR = m_ColumnWidgets.referencedColumnCB->currentIndex();
78
79 if (indexL == -1 || indexR == -1) {
80 return;
81 }
82
83 // local entity attribute
84 UMLEntityAttribute* localColumn = m_pLocalAttributeList.at(indexL);
85 // referenced entity attribute
86 UMLEntityAttribute* referencedColumn = m_pReferencedAttributeList.at(indexR);
87
88 // remove from combo boxes
89 m_ColumnWidgets.localColumnCB->removeItem(indexL);
90 m_ColumnWidgets.referencedColumnCB->removeItem(indexR);
91
92 // remove from local cache
93 m_pLocalAttributeList.removeAt(indexL);
94 m_pReferencedAttributeList.removeAt(indexR);
95
96 // add to local cache of mapping
97 EntityAttributePair pair = qMakePair(localColumn, referencedColumn);
98 m_pAttributeMapList.append(pair);
99 // update mapping view
100
101 QTreeWidgetItem* mapping = new QTreeWidgetItem(m_ColumnWidgets.mappingTW);
102 mapping->setText(0, localColumn->toString(Uml::SignatureType::SigNoVis));
103 mapping->setText(1, referencedColumn->toString(Uml::SignatureType::SigNoVis));
104
105 m_ColumnWidgets.mappingTW->addTopLevelItem(mapping);
106
107 slotResetWidgetState();
108 }
109
110 /**
111 * Deletes a pair from the list.
112 */
slotDeletePair()113 void UMLForeignKeyConstraintDialog::slotDeletePair()
114 {
115 // get the index of the selected pair in the view
116 QTreeWidgetItem* twi = m_ColumnWidgets.mappingTW->currentItem();
117 int indexP = m_ColumnWidgets.mappingTW->indexOfTopLevelItem(twi);
118 if (indexP == -1) {
119 return;
120 }
121
122 //find pair in local cache
123 EntityAttributePair pair = m_pAttributeMapList.at(indexP);
124
125 // remove them from the view and the list
126 m_ColumnWidgets.mappingTW->takeTopLevelItem(indexP);
127 m_pAttributeMapList.removeAt(indexP);
128
129 // add the attributes to the local caches
130 m_pLocalAttributeList.append(pair.first);
131 m_pReferencedAttributeList.append(pair.second);
132
133 // add them to the view (combo boxes)
134 uDebug() << (pair.first) << (pair.second);
135 m_ColumnWidgets.localColumnCB->addItem((pair.first)->toString(Uml::SignatureType::SigNoVis));
136 m_ColumnWidgets.referencedColumnCB->addItem((pair.second)->toString(Uml::SignatureType::SigNoVis));
137
138 foreach(const EntityAttributePair& p, m_pAttributeMapList) {
139 uDebug() << (p.first)->name() << " " << (p.first)->baseType() << " "
140 << (p.second)->name() << " " << (p.second)->baseType();
141 }
142
143 slotResetWidgetState();
144 }
145
146 /**
147 * Checks if changes are valid and applies them if they are,
148 * else returns false.
149 */
apply()150 bool UMLForeignKeyConstraintDialog::apply()
151 {
152 // set the Referenced Entity
153 QString entityName = m_GeneralWidgets.referencedEntityCB->currentText();
154 UMLObject* uo = m_doc->findUMLObjectRecursive(Uml::ModelType::EntityRelationship,
155 entityName,
156 UMLObject::ot_Entity);
157
158 UMLEntity* ue = uo->asUMLEntity();
159
160 if (ue == 0) {
161 uDebug() << " Could not find UML Entity with name " << entityName;
162 return false;
163 }
164
165 m_pForeignKeyConstraint->setReferencedEntity(ue);
166
167 // set all the update and delete actions
168 UMLForeignKeyConstraint::UpdateDeleteAction updateAction, deleteAction;
169 updateAction = (UMLForeignKeyConstraint::UpdateDeleteAction) m_GeneralWidgets.updateActionCB->currentIndex();
170 deleteAction = (UMLForeignKeyConstraint::UpdateDeleteAction) m_GeneralWidgets.deleteActionCB->currentIndex();
171 m_pForeignKeyConstraint->setUpdateAction(updateAction);
172 m_pForeignKeyConstraint->setDeleteAction(deleteAction);
173
174 // remove all existing mappings first
175 m_pForeignKeyConstraint->clearMappings();
176
177 // add all mappings present in local cache
178 foreach(const EntityAttributePair& pair, m_pAttributeMapList) {
179 if (!m_pForeignKeyConstraint->addEntityAttributePair(pair.first, pair.second)) {
180 return false;
181 }
182 }
183
184 // set the name
185 m_pForeignKeyConstraint->setName(m_GeneralWidgets.nameT->text());
186
187 // propagate changes to tree view
188 m_pForeignKeyConstraint->emitModified();
189
190 return true;
191 }
192
193 /**
194 * Setup the General Page.
195 */
setupGeneralPage()196 void UMLForeignKeyConstraintDialog::setupGeneralPage()
197 {
198 //setup General page
199 QWidget* page = new QWidget();
200 QVBoxLayout* topLayout = new QVBoxLayout();
201 page->setLayout(topLayout);
202
203 pageGeneral = createPage(i18nc("general page title", "General"), i18n("General Settings"),
204 Icon_Utils::it_Properties_General, page);
205
206 m_GeneralWidgets.generalGB = new QGroupBox(i18nc("general group title", "General"));
207 topLayout->addWidget(m_GeneralWidgets.generalGB);
208
209 QGridLayout* generalLayout = new QGridLayout(m_GeneralWidgets.generalGB);
210 generalLayout->setSpacing(spacingHint());
211 generalLayout->setMargin(fontMetrics().height());
212
213 Dialog_Utils::makeLabeledEditField(generalLayout, 0,
214 m_GeneralWidgets.nameL, i18nc("label for entering name", "Name"),
215 m_GeneralWidgets.nameT);
216
217 m_GeneralWidgets.referencedEntityL = new QLabel(i18n("Referenced Entity"));
218 generalLayout->addWidget(m_GeneralWidgets.referencedEntityL, 1, 0);
219
220 m_GeneralWidgets.referencedEntityCB = new KComboBox();
221 generalLayout->addWidget(m_GeneralWidgets.referencedEntityCB, 1, 1);
222
223 m_GeneralWidgets.actionGB = new QGroupBox(i18n("Actions"));
224 topLayout->addWidget(m_GeneralWidgets.actionGB);
225
226 QGridLayout* actionLayout = new QGridLayout(m_GeneralWidgets.actionGB);
227 generalLayout->setSpacing(spacingHint());
228 generalLayout->setMargin(fontMetrics().height());
229
230 m_GeneralWidgets.onUpdateL = new QLabel(i18n("On Update"));
231 actionLayout->addWidget(m_GeneralWidgets.onUpdateL, 0, 0);
232
233 m_GeneralWidgets.updateActionCB = new KComboBox(page);
234 actionLayout->addWidget(m_GeneralWidgets.updateActionCB, 0, 1);
235
236 m_GeneralWidgets.onDeleteL = new QLabel(i18n("On Delete"));
237 actionLayout->addWidget(m_GeneralWidgets.onDeleteL, 1, 0);
238
239 m_GeneralWidgets.deleteActionCB = new KComboBox();
240 actionLayout->addWidget(m_GeneralWidgets.deleteActionCB, 1, 1);
241
242 // set the name
243 m_GeneralWidgets.nameT->setText(m_pForeignKeyConstraint->name());
244
245 // fill up the combo boxes
246
247 // reference entity combo box
248 UMLEntityList entList = m_doc->entities();
249
250 foreach(UMLEntity* ent, entList) {
251 m_GeneralWidgets.referencedEntityCB->addItem(ent->name());
252 }
253
254 UMLEntity* referencedEntity = m_pForeignKeyConstraint->getReferencedEntity();
255
256 int index;
257 if (referencedEntity != 0) {
258 index = m_GeneralWidgets.referencedEntityCB->findText(referencedEntity->name());
259 if (index != -1)
260 m_GeneralWidgets.referencedEntityCB->setCurrentIndex(index);
261 }
262 m_pReferencedEntityIndex = m_GeneralWidgets.referencedEntityCB->currentIndex();
263
264 // action combo boxes
265
266 // do not change order. It is according to enum specification in foreignkeyconstraint.h
267 QStringList actions;
268 actions << i18n("No Action") << i18n("Restrict") << i18n("Cascade") << i18n("Set Null")
269 << i18n("Set Default");
270
271 m_GeneralWidgets.updateActionCB->addItems(actions);
272 m_GeneralWidgets.deleteActionCB->addItems(actions);
273
274 m_GeneralWidgets.updateActionCB->setCurrentIndex(m_pForeignKeyConstraint->getUpdateAction());
275 m_GeneralWidgets.deleteActionCB->setCurrentIndex(m_pForeignKeyConstraint->getDeleteAction());
276
277 connect(m_GeneralWidgets.referencedEntityCB, SIGNAL(activated(int)), this, SLOT(slotReferencedEntityChanged(int)));
278 }
279
280 /**
281 * Setup Column Page.
282 */
setupColumnPage()283 void UMLForeignKeyConstraintDialog::setupColumnPage()
284 {
285 //setup Columns page
286 QWidget* page = new QWidget();
287 QVBoxLayout* topLayout = new QVBoxLayout();
288 page->setLayout(topLayout);
289
290 pageColumn = createPage(i18n("Columns"), i18n("Columns"),
291 Icon_Utils::it_Properties_Columns, page);
292
293 m_ColumnWidgets.mappingTW = new QTreeWidget();
294 topLayout->addWidget(m_ColumnWidgets.mappingTW);
295
296 QStringList headers;
297 headers << i18nc("column header local", "Local") << i18nc("column header referenced", "Referenced");
298 m_ColumnWidgets.mappingTW->setHeaderLabels(headers);
299
300 QWidget* columns = new QWidget();
301 topLayout->addWidget(columns);
302 QGridLayout* columnsLayout = new QGridLayout(columns);
303
304 m_ColumnWidgets.localColumnL = new QLabel(i18n("Local Column"));
305 columnsLayout->addWidget(m_ColumnWidgets.localColumnL, 0, 0);
306
307 m_ColumnWidgets.localColumnCB = new KComboBox();
308 columnsLayout->addWidget(m_ColumnWidgets.localColumnCB, 0, 1);
309
310 m_ColumnWidgets.referencedColumnL = new QLabel(i18n("Referenced Column"));
311 columnsLayout->addWidget(m_ColumnWidgets.referencedColumnL, 1, 0);
312
313 m_ColumnWidgets.referencedColumnCB = new KComboBox();
314 columnsLayout->addWidget(m_ColumnWidgets.referencedColumnCB, 1, 1);
315
316 #if QT_VERSION >= 0x050000
317 QDialogButtonBox* buttonBox = new QDialogButtonBox();
318 m_ColumnWidgets.addPB = buttonBox->addButton(i18n("&Add"), QDialogButtonBox::ActionRole);
319 connect(m_ColumnWidgets.addPB, SIGNAL(clicked()), this, SLOT(slotAddPair()));
320 m_ColumnWidgets.removePB = buttonBox->addButton(i18n("&Delete"), QDialogButtonBox::ActionRole);
321 connect(m_ColumnWidgets.removePB, SIGNAL(clicked()), this, SLOT(slotDeletePair()));
322 #else
323 KDialogButtonBox* buttonBox = new KDialogButtonBox(page);
324 m_ColumnWidgets.addPB = buttonBox->addButton(i18n("&Add"), KDialogButtonBox::ActionRole, this,
325 SLOT(slotAddPair()));
326 m_ColumnWidgets.removePB = buttonBox->addButton(i18n("&Delete"), KDialogButtonBox::ActionRole, this,
327 SLOT(slotDeletePair()));
328 #endif
329
330 columnsLayout->addWidget(buttonBox, 2, 1);
331
332 // fill the column boxes and their local cache.
333 refillLocalAttributeCB();
334 refillReferencedAttributeCB();
335
336 QMap<UMLEntityAttribute*, UMLEntityAttribute*>::iterator i;
337 QMap<UMLEntityAttribute*, UMLEntityAttribute*> map = m_pForeignKeyConstraint->getEntityAttributePairs();
338 for (i = map.begin(); i != map.end() ; ++i) {
339
340 UMLEntityAttribute* localColumn, *referencedColumn;
341
342 localColumn = const_cast<UMLEntityAttribute*>(i.key());
343 referencedColumn = const_cast<UMLEntityAttribute*>(i.value());
344
345 // remove these columns from local cache
346 int indexL = m_pLocalAttributeList.indexOf(localColumn);
347 int indexR = m_pReferencedAttributeList.indexOf(referencedColumn);
348
349 m_pLocalAttributeList.removeAt(indexL);
350 m_pReferencedAttributeList.removeAt(indexR);
351
352 // remove them from combo boxes
353 // the conditions may never be violated . Just for safety though
354 if (indexL >= 0 && indexL < (m_ColumnWidgets.localColumnCB)->count())
355 m_ColumnWidgets.localColumnCB->removeItem(indexL);
356 if (indexR >= 0 && indexR < (m_ColumnWidgets.referencedColumnCB)->count())
357 m_ColumnWidgets.referencedColumnCB->removeItem(indexR);
358
359 // add to local cache
360 m_pAttributeMapList.append(qMakePair(localColumn, referencedColumn));
361
362 // add to view
363 QTreeWidgetItem* mapping = new QTreeWidgetItem(m_ColumnWidgets.mappingTW);
364 mapping->setText(0, localColumn->toString(Uml::SignatureType::SigNoVis));
365 mapping->setText(1, referencedColumn->toString(Uml::SignatureType::SigNoVis));
366
367 m_ColumnWidgets.mappingTW->insertTopLevelItem(0, mapping);
368 }
369
370 slotResetWidgetState();
371
372 connect(m_ColumnWidgets.mappingTW, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotResetWidgetState()));
373 }
374
375 /**
376 * Used when the Apply button is clicked.
377 */
slotApply()378 void UMLForeignKeyConstraintDialog::slotApply()
379 {
380 apply();
381 }
382
383 /**
384 * Used when the OK button is clicked. Calls apply().
385 */
slotOk()386 void UMLForeignKeyConstraintDialog::slotOk()
387 {
388 if (apply()) {
389 accept();
390 }
391 }
392
slotReferencedEntityChanged(int index)393 void UMLForeignKeyConstraintDialog::slotReferencedEntityChanged(int index)
394 {
395 if (index == m_pReferencedEntityIndex) {
396 return;
397 }
398
399 if (!m_pAttributeMapList.empty()) {
400 int result = KMessageBox::questionYesNo(this, i18n("You are attempting to change the Referenced Entity of this ForeignKey Constraint. Any unapplied changes to the mappings between local and referenced entities will be lost. Are you sure you want to continue ?"));
401
402 if (result != KMessageBox::Yes) {
403 // revert back to old index
404 m_GeneralWidgets.referencedEntityCB->setCurrentIndex(m_pReferencedEntityIndex);
405 return;
406 }
407 }
408
409 // set the referenced entity index to current index
410 m_pReferencedEntityIndex = index;
411 m_ColumnWidgets.mappingTW->clear();
412 refillReferencedAttributeCB();
413 refillLocalAttributeCB();
414 }
415
refillReferencedAttributeCB()416 void UMLForeignKeyConstraintDialog::refillReferencedAttributeCB()
417 {
418 m_pReferencedAttributeList.clear();
419 m_ColumnWidgets.referencedColumnCB->clear();
420
421 // fill the combo boxes
422
423 UMLObject* uo = m_doc->findUMLObjectRecursive(Uml::ModelType::EntityRelationship,
424 m_GeneralWidgets.referencedEntityCB->currentText(),
425 UMLObject::ot_Entity);
426
427 UMLEntity* ue = uo->asUMLEntity();
428
429 if (ue) {
430 UMLClassifierListItemList ual = ue->getFilteredList(UMLObject::ot_EntityAttribute);
431 foreach(UMLClassifierListItem* att, ual) {
432 m_pReferencedAttributeList.append(att->asUMLEntityAttribute());
433 m_ColumnWidgets.referencedColumnCB->addItem(att->toString(Uml::SignatureType::SigNoVis));
434 }
435 }
436 }
437
refillLocalAttributeCB()438 void UMLForeignKeyConstraintDialog::refillLocalAttributeCB()
439 {
440 m_pLocalAttributeList.clear();
441 m_ColumnWidgets.localColumnCB->clear();
442 // fill the combo boxes
443 UMLEntity* ue = m_pForeignKeyConstraint->umlParent()->asUMLEntity();
444
445 if (ue) {
446 UMLClassifierListItemList ual = ue->getFilteredList(UMLObject::ot_EntityAttribute);
447 foreach(UMLClassifierListItem* att, ual) {
448 m_pLocalAttributeList.append(att->asUMLEntityAttribute());
449 m_ColumnWidgets.localColumnCB->addItem(att->toString(Uml::SignatureType::SigNoVis));
450 }
451 }
452 }
453
454 /**
455 * Enable/Disable the widgets in the Dialog Box.
456 */
slotResetWidgetState()457 void UMLForeignKeyConstraintDialog::slotResetWidgetState()
458 {
459 m_ColumnWidgets.addPB->setEnabled(true);
460 m_ColumnWidgets.removePB->setEnabled(true);
461 m_ColumnWidgets.localColumnCB->setEnabled(true);
462 m_ColumnWidgets.referencedColumnCB->setEnabled(true);
463
464 // If one of the Combo Boxes is empty, then disable the Combo Box
465 if (m_ColumnWidgets.localColumnCB->count() == 0 || m_ColumnWidgets.referencedColumnCB->count() == 0) {
466 m_ColumnWidgets.localColumnCB->setEnabled(false);
467 m_ColumnWidgets.referencedColumnCB->setEnabled(false);
468 m_ColumnWidgets.addPB->setEnabled(false);
469 }
470
471 // get index of selected Attribute in List Box
472 if (m_ColumnWidgets.mappingTW->currentItem() == 0) {
473 m_ColumnWidgets.removePB->setEnabled(false);
474 }
475 }
476
477
478