1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3     SPDX-FileCopyrightText: 2002-2021 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
4 */
5 
6 // own header
7 #include "selectoperationdialog.h"
8 
9 // local includes
10 #include "attribute.h"
11 #include "classifier.h"
12 #include "debug_utils.h"
13 #include "linkwidget.h"
14 #include "operation.h"
15 #include "umlclassifierlistitemlist.h"
16 #include "umlscene.h"
17 #include "umlview.h"
18 #include "dialog_utils.h"
19 
20 // kde includes
21 #include <klineedit.h>
22 #include <kcombobox.h>
23 #include <KLocalizedString>
24 
25 // qt includes
26 #include <QCheckBox>
27 #include <QGridLayout>
28 #include <QGroupBox>
29 #include <QLabel>
30 #include <QPushButton>
31 #include <QRadioButton>
32 #include <QVBoxLayout>
33 
caseInsensitiveLessThan(const UMLOperation * s1,const UMLOperation * s2)34 bool caseInsensitiveLessThan(const UMLOperation *s1, const UMLOperation *s2)
35 {
36     return s1->name().toLower() < s2->name().toLower();
37 }
38 
39 /**
40  *  Constructs a SelectOperationDialog instance.
41  *
42  *  @param  parent  The parent to this instance.
43  *  @param  c       The concept to get the operations from.
44  *  @param  enableAutoIncrement Flag to enable auto increment checkbox
45  */
SelectOperationDialog(UMLView * parent,UMLClassifier * c,LinkWidget * widget,bool enableAutoIncrement)46 SelectOperationDialog::SelectOperationDialog(UMLView *parent, UMLClassifier * c, LinkWidget *widget, bool enableAutoIncrement)
47   : SinglePageDialogBase(parent),
48     m_id(CUSTOM),
49     m_pView(parent),
50     m_classifier(c),
51     m_widget(widget),
52     m_enableAutoIncrement(enableAutoIncrement)
53 {
54     setCaption(i18n("Select Operation"));
55 
56     QFrame *frame = new QFrame(this);
57     setMainWidget(frame);
58 
59     QVBoxLayout * topLayout = new QVBoxLayout(frame);
60 
61     m_pOpGB = new QGroupBox(i18n("Select Operation"));
62     topLayout->addWidget(m_pOpGB);
63 
64     QGridLayout * mainLayout = new QGridLayout(m_pOpGB);
65     Dialog_Utils::makeLabeledEditField(mainLayout, 0,
66                                     m_pSeqL, i18n("Sequence number:"),
67                                     m_pSeqLE);
68 
69     m_pOpAS = new QCheckBox(i18n("Auto increment:"), m_pOpGB);
70     mainLayout->addWidget(m_pOpAS, 0, 2);
71     connect(m_pOpAS, SIGNAL(toggled(bool)), this, SLOT(slotAutoIncrementChecked(bool)));
72     m_pOpAS->setEnabled(enableAutoIncrement);
73 
74     m_pOpRB = new QLabel(i18n("Class operation:"), m_pOpGB);
75     mainLayout->addWidget(m_pOpRB, 1, 0);
76 
77     m_pOpCB = new KComboBox(m_pOpGB);
78 #if QT_VERSION < 0x050000
79     m_pOpCB->setCompletionMode(KGlobalSettings::CompletionPopup);
80 #endif
81     m_pOpCB->setDuplicatesEnabled(false); // only allow one of each type in box
82     connect(m_pOpCB, SIGNAL(currentIndexChanged(int)), this, SLOT(slotIndexChanged(int)));
83     mainLayout->addWidget(m_pOpCB, 1, 1, 1, 2);
84 
85     m_newOperationButton = new QPushButton(i18n("New Operation..."), m_pOpGB);
86     connect(m_newOperationButton, SIGNAL(clicked()), this, SLOT(slotNewOperation()));
87     mainLayout->addWidget(m_newOperationButton, 1, 3);
88 
89     m_pCustomRB = new QLabel(i18n("Custom operation:"), m_pOpGB);
90     mainLayout->addWidget(m_pCustomRB, 2, 0);
91 
92     m_pOpLE = new KLineEdit(m_pOpGB);
93     connect(m_pOpLE, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString)));
94     mainLayout->addWidget(m_pOpLE, 2, 1, 1, 2);
95     setupOperationsList();
96     enableButtonOk(false);
97     setupDialog();
98 }
99 
100 /**
101  *  Standard destructor.
102  */
~SelectOperationDialog()103 SelectOperationDialog::~SelectOperationDialog()
104 {
105 }
106 
107 /**
108  *  Returns the operation to display.
109  *
110  *  @return The operation to display.
111  */
getOpText()112 QString SelectOperationDialog::getOpText()
113 {
114     if (m_pOpLE->text().isEmpty())
115         return m_pOpCB->currentText();
116     else
117         return m_pOpLE->text();
118 }
119 
120 /**
121  * Return whether the user selected a class operation
122  * or a custom operation.
123  *
124  * @return  True if user selected a class operation,
125  *          false if user selected a custom operation
126  */
isClassOp() const127 bool SelectOperationDialog::isClassOp() const
128 {
129     return (m_id == OP);
130 }
131 
132 /**
133  * Set the custom operation text.
134  *
135  *  @param op The operation to set as the custom operation.
136  */
setCustomOp(const QString & op)137 void SelectOperationDialog::setCustomOp(const QString &op)
138 {
139     m_pOpLE->setText(op);
140     slotTextChanged(op);
141 }
142 
143 /**
144  * Handle auto increment checkbox click.
145  */
slotAutoIncrementChecked(bool state)146 void SelectOperationDialog::slotAutoIncrementChecked(bool state)
147 {
148     m_enableAutoIncrement = state;
149     if (state && m_pSeqLE->text().isEmpty())
150         m_pSeqLE->setText(m_pView->umlScene()->autoIncrementSequenceValue());
151 }
152 
153 /**
154  * Handle new operation button click.
155  */
slotNewOperation()156 void SelectOperationDialog::slotNewOperation()
157 {
158     UMLOperation *op = m_classifier->createOperation();
159     if (!op)
160         return;
161     setupOperationsList();
162     setClassOp(op->toString(Uml::SignatureType::SigNoVis));
163     enableButtonOk(true);
164 }
165 
166 /**
167  * Handle combox box changes.
168  */
slotIndexChanged(int index)169 void SelectOperationDialog::slotIndexChanged(int index)
170 {
171     if (index != -1) {
172         m_pOpLE->setText(QString());
173         m_id = OP;
174         enableButtonOk(true);
175     }
176     if (m_pOpCB->currentText().isEmpty()) {
177         enableButtonOk(false);
178     }
179 }
180 
181 /**
182  * Handle custom line edit changes.
183  */
slotTextChanged(const QString & text)184 void SelectOperationDialog::slotTextChanged(const QString &text)
185 {
186     if (text.isEmpty()) {
187         enableButtonOk(false);
188     }
189     else {
190         m_pOpCB->setCurrentIndex(-1);
191         m_id = CUSTOM;
192         enableButtonOk(true);
193     }
194 }
195 
196 /**
197  * Set the class operation text.
198  *
199  *  @param op The operation to set as the class operation.
200  * @return false if no such operation exists.
201  */
setClassOp(const QString & op)202 bool SelectOperationDialog::setClassOp(const QString &op)
203 {
204     for (int i = 1; i != m_pOpCB->count(); ++i) {
205         if (m_pOpCB->itemText(i) == op) {
206             m_pOpCB->setCurrentIndex(i);
207             slotIndexChanged(i);
208             return true;
209         }
210     }
211     return false;
212 }
213 
214 /**
215  * Setup dialog operations list.
216  */
setupOperationsList()217 void SelectOperationDialog::setupOperationsList()
218 {
219     m_pOpCB->clear();
220     UMLOperationList list = m_classifier->getOpList(true);
221     if (list.count() > 0)
222         m_pOpCB->insertItem(0, QString());
223     qSort(list.begin(), list.end(), caseInsensitiveLessThan);
224     foreach(UMLOperation * obj, list) {
225         QString s = obj->toString(Uml::SignatureType::SigNoVis);
226         m_pOpCB->insertItem(list.count(), s);
227         m_pOpCB->completionObject()->addItem(s);
228     }
229     m_nOpCount = m_classifier->operations();
230 }
231 
232 /**
233  *  Returns the sequence number for the operation.
234  *
235  *  @return Returns the sequence number for the operation.
236  */
getSeqNumber()237 QString SelectOperationDialog::getSeqNumber()
238 {
239     return m_pSeqLE->text();
240 }
241 
242 /**
243  * Set the sequence number text.
244  *
245  *  @param  num     The number to set the sequence to.
246  */
setSeqNumber(const QString & num)247 void SelectOperationDialog::setSeqNumber(const QString &num)
248 {
249     m_pSeqLE->setText(num);
250 }
251 
252 /**
253  * Set the flag for auto increment sequence numbering.
254  * @param state   the state of the flag
255  */
setAutoIncrementSequence(bool state)256 void SelectOperationDialog::setAutoIncrementSequence(bool state)
257 {
258    m_pOpAS->setChecked(state);
259 }
260 
261 /**
262  * Return the flag for auto increment sequence numbering.
263  */
autoIncrementSequence()264 bool SelectOperationDialog::autoIncrementSequence()
265 {
266    return m_pOpAS->isChecked();
267 }
268 
269 /**
270  * internal setup function
271  */
setupDialog()272 void SelectOperationDialog::setupDialog()
273 {
274     if (m_enableAutoIncrement && m_pView->umlScene()->autoIncrementSequence()) {
275         setAutoIncrementSequence(true);
276         setSeqNumber(m_pView->umlScene()->autoIncrementSequenceValue());
277    } else
278         setSeqNumber(m_widget->sequenceNumber());
279 
280     if (m_widget->operation() == 0) {
281         setCustomOp(m_widget->lwOperationText());
282     } else {
283         setClassOp(m_widget->lwOperationText());
284     }
285 }
286 
287 /**
288  * apply changes to the related instamces
289  * @return true - success
290  * @return false - failure
291  */
apply()292 bool SelectOperationDialog::apply()
293 {
294     QString opText = getOpText();
295     if (isClassOp()) {
296         Model_Utils::OpDescriptor od;
297         Model_Utils::Parse_Status st = Model_Utils::parseOperation(opText, od, m_classifier);
298         if (st == Model_Utils::PS_OK) {
299             UMLClassifierList selfAndAncestors = m_classifier->findSuperClassConcepts();
300             selfAndAncestors.prepend(m_classifier);
301             UMLOperation *op = 0;
302             foreach (UMLClassifier *cl, selfAndAncestors) {
303                 op = cl->findOperation(od.m_name, od.m_args);
304                 if (op) {
305                     break;
306                 }
307             }
308             if (!op) {
309                 // The op does not yet exist. Create a new one.
310                 UMLObject *o = m_classifier->createOperation(od.m_name, 0, &od.m_args);
311                 op = o->asUMLOperation();
312             }
313             if (od.m_pReturnType) {
314                 op->setType(od.m_pReturnType);
315             }
316 
317             m_widget->setOperation(op);
318             opText.clear();
319         } else {
320             m_widget->setOperation(0);
321         }
322     } else {
323         m_widget->setOperation(0);
324     }
325     m_widget->setSequenceNumber(getSeqNumber());
326     m_widget->setOperationText(opText);
327     if (m_enableAutoIncrement) {
328         m_pView->umlScene()->setAutoIncrementSequence(autoIncrementSequence());
329     }
330     return true;
331 }
332