1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "PortAliasesConfigurationDialog.h"
23 
24 #include <QMessageBox>
25 #include <QPushButton>
26 
27 #include <U2Gui/HelpButton.h>
28 
29 #include <U2Lang/ActorModel.h>
30 #include <U2Lang/WorkflowUtils.h>
31 
32 namespace U2 {
33 namespace Workflow {
34 
PortAliasesConfigurationDialog(const Schema & schema,QWidget * p)35 PortAliasesConfigurationDialog::PortAliasesConfigurationDialog(const Schema &schema, QWidget *p)
36     : QDialog(p), currentRow(-1) {
37     setupUi(this);
38     new HelpButton(this, buttonBox, "65930020");
39 
40     buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
41     buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
42     QPushButton *cancelPushButton = buttonBox->button(QDialogButtonBox::Cancel);
43     QPushButton *okPushButton = buttonBox->button(QDialogButtonBox::Ok);
44 
45     connect(cancelPushButton, SIGNAL(clicked()), SLOT(reject()));
46     connect(okPushButton, SIGNAL(clicked()), SLOT(accept()));
47     connect(portAliasEdit, SIGNAL(textChanged(const QString &)), SLOT(sl_portAliasChanged(const QString &)));
48     connect(portDescriptionEdit, SIGNAL(textChanged(const QString &)), SLOT(sl_portDescriptionChanged(const QString &)));
49 
50     okPushButton->setDefault(true);
51     portAliasesTableWidget->verticalHeader()->hide();
52 
53     portAliasesTableWidget->horizontalHeader()->setSectionsClickable(false);
54     portAliasesTableWidget->horizontalHeader()->setStretchLastSection(true);
55 
56     foreach (Actor *actor, schema.getProcesses()) {
57         assert(nullptr != actor);
58         foreach (Port *port, actor->getPorts()) {
59             assert(nullptr != port);
60             // show only output and free input ports
61             if (port->isInput()) {
62                 if (!port->getLinks().isEmpty()) {
63                     continue;
64                 }
65             }
66             int pos = portListWidget->count();
67             QString itemName = actor->getLabel() + "." + port->getDisplayName();
68             QListWidgetItem *item = new QListWidgetItem(itemName);
69             portListWidget->insertItem(pos, item);
70             portListMap.insert(pos, port);
71         }
72     }
73     if (portListMap.isEmpty()) {
74         portInfoGroupBox->setVisible(false);
75     }
76 
77     connect(portListWidget, SIGNAL(currentRowChanged(int)), SLOT(sl_portSelected(int)));
78     connect(portAliasesTableWidget, SIGNAL(cellChanged(int, int)), SLOT(sl_onDataChange(int, int)));
79 
80     initializeModel(schema);
81 }
82 
clearAliasTable()83 void PortAliasesConfigurationDialog::clearAliasTable() {
84     portAliasesTableWidget->clearContents();
85     portAliasesTableWidget->setRowCount(0);
86 }
87 
sl_portSelected(int row)88 void PortAliasesConfigurationDialog::sl_portSelected(int row) {
89     if (row == -1) {
90         return;
91     }
92     currentRow = row;
93     clearAliasTable();
94 
95     assert(row >= 0 && row < portListWidget->count());
96     Port *currentPort = portListMap.value(row);
97     assert(nullptr != currentPort);
98 
99     int rowInd = 0;
100     QMap<Descriptor, QString> aliasMap = model.aliases.value(currentPort);
101     QMap<Descriptor, QString>::const_iterator it = aliasMap.constBegin();
102     while (it != aliasMap.constEnd()) {
103         portAliasesTableWidget->insertRow(rowInd);
104 
105         QTableWidgetItem *portNameItem = new QTableWidgetItem(it.key().getDisplayName());
106         portAliasesTableWidget->setItem(rowInd, 0, portNameItem);
107         portNameItem->setData(Qt::UserRole, QVariant::fromValue(it.key()));
108         portNameItem->setFlags(portNameItem->flags() ^ Qt::ItemIsSelectable ^ Qt::ItemIsEditable);
109 
110         QTableWidgetItem *aliasItem = new QTableWidgetItem(it.value());
111         portAliasesTableWidget->setItem(rowInd, 1, aliasItem);
112 
113         rowInd++;
114         ++it;
115     }
116 
117     if (currentPort->isInput()) {
118         portTypeLabel->setText(tr("Input"));
119     } else {
120         portTypeLabel->setText(tr("Output"));
121     }
122 
123     portAliasEdit->setText(model.ports.value(currentPort).first);
124     portDescriptionEdit->setText(model.ports.value(currentPort).second);
125 }
126 
sl_onDataChange(int row,int col)127 void PortAliasesConfigurationDialog::sl_onDataChange(int row, int col) {
128     assert(row >= 0 && row < portAliasesTableWidget->rowCount());
129     if (0 == col) {
130         return;
131     }
132 
133     Port *port = portListMap.value(portListWidget->currentRow());
134     assert(nullptr != port);
135 
136     Descriptor desc = portAliasesTableWidget->item(row, 0)->data(Qt::UserRole).value<Descriptor>();
137     assert(model.aliases.value(port).contains(desc));
138 
139     model.aliases[port][desc] = portAliasesTableWidget->item(row, 1)->text();
140 }
141 
sl_portAliasChanged(const QString & newStr)142 void PortAliasesConfigurationDialog::sl_portAliasChanged(const QString &newStr) {
143     Port *currentPort = portListMap.value(currentRow);
144     PortInfo info = model.ports.value(currentPort);
145     model.ports.insert(currentPort, PortInfo(newStr, info.second));
146 }
147 
sl_portDescriptionChanged(const QString & newStr)148 void PortAliasesConfigurationDialog::sl_portDescriptionChanged(const QString &newStr) {
149     Port *currentPort = portListMap.value(currentRow);
150     PortInfo info = model.ports.value(currentPort);
151     model.ports.insert(currentPort, PortInfo(info.first, newStr));
152 }
153 
initializeModel(const Schema & schema)154 void PortAliasesConfigurationDialog::initializeModel(const Schema &schema) {
155     foreach (Actor *actor, schema.getProcesses()) {
156         assert(nullptr != actor);
157         foreach (Port *port, actor->getPorts()) {
158             assert(nullptr != port);
159             if (port->isInput()) {
160                 if (!port->getLinks().isEmpty()) {
161                     continue;
162                 }
163             }
164             model.ports.insert(port, PortInfo("", ""));
165             QList<Descriptor> slotList;
166             {
167                 DataTypePtr to = WorkflowUtils::getToDatatypeForBusport(qobject_cast<IntegralBusPort *>(port));
168                 if (port->isInput()) {
169                     slotList = to->getDatatypesMap().keys();
170                 } else {
171                     DataTypePtr from = WorkflowUtils::getFromDatatypeForBusport(qobject_cast<IntegralBusPort *>(port), to);
172                     slotList = from->getDatatypesMap().keys();
173                 }
174             }
175 
176             QList<SlotAlias> slotAliases;
177             foreach (const PortAlias &portAlias, schema.getPortAliases()) {
178                 if (portAlias.getSourcePort() == port) {
179                     slotAliases = portAlias.getSlotAliases();
180                     model.ports.insert(port, PortInfo(portAlias.getAlias(), portAlias.getDescription()));
181                     break;
182                 }
183             }
184 
185             QMap<Descriptor, QString> aliasMap;
186             foreach (Descriptor slotDescr, slotList) {
187                 QString actorId;
188                 QString slotId;
189                 {
190                     if (port->isInput()) {
191                         actorId = port->owner()->getId();
192                         slotId = slotDescr.getId();
193                     } else {
194                         QStringList tokens = slotDescr.getId().split(':');
195                         assert(2 == tokens.size());
196                         actorId = tokens[0];
197                         slotId = tokens[1];
198                     }
199                 }
200 
201                 QString alias;
202                 foreach (const SlotAlias &slotAlias, slotAliases) {
203                     if (slotAlias.getSourceSlotId() == slotId) {
204                         if (slotAlias.getSourcePort()->owner()->getId() == actorId) {
205                             alias = slotAlias.getAlias();
206                             break;
207                         }
208                     }
209                 }
210                 aliasMap.insert(slotDescr, alias);
211             }
212             model.aliases.insert(port, aliasMap);
213         }
214     }
215 }
216 
accept()217 void PortAliasesConfigurationDialog::accept() {
218     foreach (Port *port, model.aliases.keys()) {
219         QStringList slotAliases = model.aliases.value(port).values();
220 
221         bool slotsNotAliased = true;
222         foreach (const QString &alias, slotAliases) {
223             slotsNotAliased = slotsNotAliased && alias.isEmpty();
224         }
225 
226         bool portNotAliases = model.ports.value(port).first.isEmpty();
227         if (!slotsNotAliased && portNotAliases) {
228             QString portStr = port->owner()->getLabel() + "." + port->getId();
229             QMessageBox::information(this, tr("Workflow Designer"), tr("There is a port with some aliased slots but without alias name:\n%1").arg(portStr));
230             return;
231         }
232 
233         slotAliases.removeAll("");
234         if (0 != slotAliases.removeDuplicates()) {
235             QMessageBox::information(this, tr("Workflow Designer"), tr("Slot aliases of one port must be different!"));
236             return;
237         }
238 
239         foreach (Port *otherPort, model.ports.keys()) {
240             if (otherPort == port) {
241                 continue;
242             }
243             if (model.ports.value(port).first.isEmpty()) {
244                 continue;
245             }
246             if (model.ports.value(port).first == model.ports.value(otherPort).first) {
247                 QString portStr1 = port->owner()->getLabel() + "." + port->getId();
248                 QString portStr2 = otherPort->owner()->getLabel() + "." + otherPort->getId();
249 
250                 QMessageBox::information(this, tr("Workflow Designer"), tr("Port aliases must be different! Rename one of ports:\n%1 or %2").arg(portStr1).arg(portStr2));
251                 return;
252             }
253         }
254     }
255 
256     QDialog::accept();
257 }
258 
getModel() const259 PortAliasesCfgDlgModel PortAliasesConfigurationDialog::getModel() const {
260     PortAliasesCfgDlgModel ret;
261     foreach (Port *port, model.ports.keys()) {
262         QString newPortName = model.ports.value(port).first;
263         if (newPortName.isEmpty()) {
264             continue;
265         }
266         ret.ports.insert(port, model.ports.value(port));
267 
268         QMap<Descriptor, QString> aliases = model.aliases.value(port);
269         foreach (const Descriptor &slotDescr, aliases.keys()) {
270             if (aliases.value(slotDescr).isEmpty()) {
271                 aliases.remove(slotDescr);
272             }
273         }
274         ret.aliases.insert(port, aliases);
275     }
276     return ret;
277 }
278 
279 }    // namespace Workflow
280 }    // namespace U2
281