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 "ActorCfgModel.h"
23
24 #include <QDebug>
25
26 #include <U2Core/Log.h>
27 #include <U2Core/U2SafePoints.h>
28
29 #include <U2Lang/BaseTypes.h>
30 #include <U2Lang/Datatype.h>
31 #include <U2Lang/IntegralBusType.h>
32 #include <U2Lang/URLAttribute.h>
33 #include <U2Lang/WorkflowSettings.h>
34 #include <U2Lang/WorkflowUtils.h>
35
36 #include "WorkflowEditor.h"
37 #include "WorkflowEditorDelegates.h"
38
39 namespace U2 {
40
41 /*****************************
42 * ActorCfgModel
43 *****************************/
44 static const int KEY_COLUMN = 0;
45 static const int VALUE_COLUMN = 1;
46 static const int SCRIPT_COLUMN = 2;
47
ActorCfgModel(QObject * parent,SchemaConfig * _schemaConfig)48 ActorCfgModel::ActorCfgModel(QObject *parent, SchemaConfig *_schemaConfig)
49 : QAbstractTableModel(parent), schemaConfig(_schemaConfig), subject(nullptr), scriptMode(false) {
50 scriptDelegate = new AttributeScriptDelegate();
51 }
52
~ActorCfgModel()53 ActorCfgModel::~ActorCfgModel() {
54 delete scriptDelegate;
55 }
56
setActor(Actor * cfg)57 void ActorCfgModel::setActor(Actor *cfg) {
58 listValues.clear();
59 attrs.clear();
60 subject = cfg;
61 if (nullptr != cfg) {
62 attrs = cfg->getAttributes();
63 setupAttributesScripts();
64
65 ConfigurationEditor *editor = subject->getEditor();
66 if (nullptr != editor) {
67 foreach (Attribute *attr, attrs) {
68 PropertyDelegate *delegate = editor->getDelegate(attr->getId());
69 if (nullptr != delegate) {
70 delegate->setSchemaConfig(schemaConfig);
71 }
72 }
73 }
74 }
75 beginResetModel();
76 endResetModel();
77 }
78
dumpDescriptors(const QList<Descriptor> & descriptors)79 void dumpDescriptors(const QList<Descriptor> &descriptors) {
80 foreach (const Descriptor &d, descriptors) {
81 qDebug() << d.getId() << d.getDisplayName();
82 }
83 }
84
setupAttributesScripts()85 void ActorCfgModel::setupAttributesScripts() {
86 foreach (Attribute *attribute, attrs) {
87 assert(attribute != nullptr);
88 attribute->getAttributeScript().clearScriptVars();
89
90 DataTypePtr attributeType = attribute->getAttributeType();
91 // FIXME: add support for all types in scripting
92 if (attributeType != BaseTypes::STRING_TYPE() && attributeType != BaseTypes::NUM_TYPE()) {
93 continue;
94 }
95
96 foreach (const PortDescriptor *descr, subject->getProto()->getPortDesciptors()) {
97 if (descr->isInput()) {
98 DataTypePtr dataTypePtr = descr->getType();
99 if (dataTypePtr->isMap()) {
100 QMap<Descriptor, DataTypePtr> map = dataTypePtr->getDatatypesMap();
101 foreach (const Descriptor &desc, map.keys()) {
102 QString id = desc.getId().replace(QRegExp("[^a-zA-Z0-9_]"), "_");
103 if (id.at(0).isDigit()) {
104 id.prepend("_");
105 }
106 Descriptor d(id, desc.getDisplayName(), desc.getDocumentation());
107 attribute->getAttributeScript().setScriptVar(d, QVariant());
108 }
109 } else if (dataTypePtr->isList()) {
110 foreach (const Descriptor &typeDescr, dataTypePtr->getAllDescriptors()) {
111 QString id = typeDescr.getId().replace(QRegExp("[^a-zA-Z0-9_]"), "_");
112 if (id.at(0).isDigit()) {
113 id.prepend("_");
114 }
115 Descriptor d(id, typeDescr.getDisplayName(), typeDescr.getDocumentation());
116 attribute->getAttributeScript().setScriptVar(d, QVariant());
117 }
118 } else {
119 QString id = dataTypePtr->getId().replace(QRegExp("[^a-zA-Z0-9_]"), "_");
120 if (id.at(0).isDigit()) {
121 id.prepend("_");
122 }
123 QString displayName = dataTypePtr->getDisplayName();
124 QString doc = dataTypePtr->getDocumentation();
125 attribute->getAttributeScript().setScriptVar(Descriptor(id, displayName, doc), QVariant());
126 }
127 }
128 }
129
130 QString attrVarName = attribute->getDisplayName();
131 QString id = attribute->getId().replace(QRegExp("[^a-zA-Z0-9_]"), "_");
132 if (id.at(0).isDigit()) {
133 id.prepend("_");
134 }
135 attribute->getAttributeScript().setScriptVar(Descriptor(id, attrVarName, attribute->getDocumentation()), QVariant());
136 }
137 }
138
update()139 void ActorCfgModel::update() {
140 beginResetModel();
141 endResetModel();
142 }
143
columnCount(const QModelIndex &) const144 int ActorCfgModel::columnCount(const QModelIndex &) const {
145 if (scriptMode) {
146 return 3; // key, value and script
147 } else {
148 return 2;
149 }
150 }
151
rowCount(const QModelIndex & parent) const152 int ActorCfgModel::rowCount(const QModelIndex &parent) const {
153 if (parent.isValid()) {
154 return 0;
155 }
156
157 return attrs.isEmpty() || parent.isValid() ? 0 : attrs.size() /*rows*/;
158 }
159
isVisible(Attribute * a) const160 bool ActorCfgModel::isVisible(Attribute *a) const {
161 CHECK(nullptr != subject, true);
162 if (nullptr != dynamic_cast<URLAttribute *>(a)) {
163 return false;
164 }
165 return subject->isAttributeVisible(a);
166 }
167
flags(const QModelIndex & index) const168 Qt::ItemFlags ActorCfgModel::flags(const QModelIndex &index) const {
169 int col = index.column();
170 int row = index.row();
171
172 Attribute *currentAttribute = getAttributeByRow(row);
173 SAFE_POINT(nullptr != currentAttribute, "Unexpected attribute", Qt::NoItemFlags);
174 if (!isVisible(currentAttribute)) {
175 return Qt::NoItemFlags;
176 }
177
178 switch (col) {
179 case KEY_COLUMN:
180 return Qt::ItemIsEnabled;
181 case VALUE_COLUMN:
182 return row < attrs.size() ? Qt::ItemIsEditable | Qt::ItemIsEnabled : Qt::ItemIsEnabled;
183 case SCRIPT_COLUMN: {
184 if (row < attrs.size()) {
185 // FIXME: add support for all types in scripting
186 if (currentAttribute->getAttributeType() != BaseTypes::STRING_TYPE() && currentAttribute->getAttributeType() != BaseTypes::NUM_TYPE()) {
187 return Qt::ItemIsEnabled;
188 } else {
189 return Qt::ItemIsEditable | Qt::ItemIsEnabled;
190 }
191 } else {
192 return Qt::ItemIsEnabled;
193 }
194 }
195 default:
196 assert(false);
197 }
198 // unreachable code
199 return Qt::NoItemFlags;
200 }
201
headerData(int section,Qt::Orientation orientation,int role) const202 QVariant ActorCfgModel::headerData(int section, Qt::Orientation orientation, int role) const {
203 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
204 switch (section) {
205 case KEY_COLUMN:
206 return WorkflowEditor::tr("Name");
207 case VALUE_COLUMN:
208 return WorkflowEditor::tr("Value");
209 case SCRIPT_COLUMN:
210 return WorkflowEditor::tr("Script");
211 default:
212 assert(false);
213 }
214 }
215 // unreachable code
216 return QVariant();
217 }
218
setAttributeValue(const Attribute * attr,QVariant & attrValue) const219 bool ActorCfgModel::setAttributeValue(const Attribute *attr, QVariant &attrValue) const {
220 assert(attr != nullptr);
221 bool isDefaultVal = attr->isDefaultValue();
222
223 attrValue = attr->getAttributePureValue();
224 return isDefaultVal;
225 }
226
getAttributeByRow(int row) const227 Attribute *ActorCfgModel::getAttributeByRow(int row) const {
228 SAFE_POINT(row < attrs.size(), "Unexpected row requested", nullptr);
229 return attrs.at(row);
230 }
231
modelIndexById(const QString & id) const232 QModelIndex ActorCfgModel::modelIndexById(const QString &id) const {
233 for (int i = 0; i < attrs.size(); i++) {
234 Attribute *a = getAttributeByRow(i);
235 if (nullptr != a && a->getId() == id) {
236 QModelIndex modelIndex = index(i, 1);
237 return modelIndex;
238 }
239 }
240 return QModelIndex();
241 }
242
data(const QModelIndex & index,int role) const243 QVariant ActorCfgModel::data(const QModelIndex &index, int role) const {
244 const Attribute *currentAttribute = getAttributeByRow(index.row());
245 SAFE_POINT(nullptr != currentAttribute, "Invalid attribute", QVariant());
246 if (role == DescriptorRole) { // descriptor that will be shown in under editor. 'propDoc' in WorkflowEditor
247 return qVariantFromValue<Descriptor>(*currentAttribute);
248 }
249
250 int col = index.column();
251 switch (col) {
252 case KEY_COLUMN: {
253 switch (role) {
254 case Qt::DisplayRole:
255 return currentAttribute->getDisplayName();
256 case Qt::ToolTipRole:
257 return currentAttribute->getDocumentation();
258 case Qt::FontRole:
259 if (currentAttribute->isRequiredAttribute()) {
260 QFont fnt;
261 fnt.setBold(true);
262 return QVariant(fnt);
263 }
264 return QVariant();
265 default:
266 return QVariant();
267 }
268 }
269 case VALUE_COLUMN: {
270 if (role == ConfigurationEditor::ItemListValueRole) {
271 return listValues.value(currentAttribute->getId());
272 }
273
274 QVariant attributeValue;
275 bool isDefaultVal = setAttributeValue(currentAttribute, attributeValue);
276 ConfigurationEditor *confEditor = subject->getEditor();
277 PropertyDelegate *propertyDelegate = confEditor ? confEditor->getDelegate(currentAttribute->getId()) : nullptr;
278 switch (role) {
279 case Qt::DisplayRole:
280 case Qt::ToolTipRole: {
281 if (propertyDelegate) {
282 return propertyDelegate->getDisplayValue(attributeValue);
283 } else {
284 QString valueStr = WorkflowUtils::getStringForParameterDisplayRole(attributeValue);
285 return !valueStr.isEmpty() ? valueStr : attributeValue;
286 }
287 }
288 case Qt::ForegroundRole:
289 return isDefaultVal ? QVariant(QColor(Qt::gray)) : QVariant();
290 case DelegateRole:
291 return qVariantFromValue<PropertyDelegate *>(propertyDelegate);
292 case Qt::EditRole:
293 case ConfigurationEditor::ItemValueRole:
294 return attributeValue;
295 default:
296 return QVariant();
297 }
298 }
299 case SCRIPT_COLUMN: {
300 // FIXME: add support for all types in scripting
301 if (currentAttribute->getAttributeType() != BaseTypes::STRING_TYPE() && currentAttribute->getAttributeType() != BaseTypes::NUM_TYPE()) {
302 if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
303 return QVariant(tr("N/A"));
304 } else {
305 return QVariant();
306 }
307 }
308
309 // for STRING type
310 switch (role) {
311 case Qt::DisplayRole:
312 case Qt::ToolTipRole:
313 return scriptDelegate ? scriptDelegate->getDisplayValue(qVariantFromValue<AttributeScript>(currentAttribute->getAttributeScript())) : QVariant();
314 case Qt::ForegroundRole:
315 return currentAttribute->getAttributeScript().isEmpty() ? QVariant(QColor(Qt::gray)) : QVariant();
316 case DelegateRole:
317 assert(scriptDelegate != nullptr);
318 return qVariantFromValue<PropertyDelegate *>(scriptDelegate);
319 case Qt::EditRole:
320 case ConfigurationEditor::ItemValueRole:
321 return qVariantFromValue<AttributeScript>(currentAttribute->getAttributeScript());
322 default:
323 return QVariant();
324 }
325 }
326 default:
327 assert(false);
328 }
329 // unreachable code
330 return QVariant();
331 }
332
canSetData(Attribute * attr,const QVariant & value)333 bool ActorCfgModel::canSetData(Attribute *attr, const QVariant &value) {
334 bool dir = false;
335 bool isOutUrlAttr = RFSUtils::isOutUrlAttribute(attr, subject, dir);
336 CHECK(isOutUrlAttr, true);
337
338 RunFileSystem *rfs = schemaConfig->getRFS();
339 return rfs->canAdd(value.toString(), dir);
340 }
341
342 namespace {
343
getTags(Actor * subject,const QString & attrId)344 DelegateTags *getTags(Actor *subject, const QString &attrId) {
345 ConfigurationEditor *editor = subject->getEditor();
346 CHECK(nullptr != editor, nullptr);
347 PropertyDelegate *delegate = editor->getDelegate(attrId);
348 CHECK(nullptr != delegate, nullptr);
349 return delegate->tags();
350 }
351
352 } // namespace
353
getAttributeRelatedVisibility(Attribute * changedAttr,const QMap<Attribute *,bool> & foundRelatedAttrs) const354 QMap<Attribute *, bool> ActorCfgModel::getAttributeRelatedVisibility(Attribute *changedAttr, const QMap<Attribute *, bool> &foundRelatedAttrs) const {
355 QMap<Attribute *, bool> relatedAttributesVisibility = foundRelatedAttrs;
356 foreach (Attribute *a, attrs) {
357 if (a != changedAttr && !relatedAttributesVisibility.contains(a)) {
358 foreach (const AttributeRelation *rel, a->getRelations()) {
359 if (rel->getRelatedAttrId() == changedAttr->getId()) {
360 relatedAttributesVisibility.insert(a, isVisible(a));
361 const QMap<Attribute *, bool> dependentAttributeVisibility = getAttributeRelatedVisibility(a, relatedAttributesVisibility);
362 foreach (Attribute *dependentAttr, dependentAttributeVisibility.keys()) {
363 relatedAttributesVisibility[dependentAttr] = dependentAttributeVisibility[dependentAttr];
364 }
365 }
366 }
367 }
368 }
369 return relatedAttributesVisibility;
370 }
371
checkIfAttributeVisibilityChanged(const QMap<Attribute *,bool> & attributeVisibility)372 void ActorCfgModel::checkIfAttributeVisibilityChanged(const QMap<Attribute *, bool> &attributeVisibility) {
373 foreach (Attribute *a, attributeVisibility.keys()) {
374 if (attributeVisibility[a] != isVisible(a)) {
375 const QModelIndex affectedIndex = modelIndexById(a->getId());
376 emit dataChanged(affectedIndex, affectedIndex);
377 }
378 }
379 }
380
setData(const QModelIndex & index,const QVariant & value,int role)381 bool ActorCfgModel::setData(const QModelIndex &index, const QVariant &value, int role) {
382 int col = index.column();
383 Attribute *editingAttribute = getAttributeByRow(index.row());
384 SAFE_POINT(editingAttribute != nullptr, "Invalid attribute detected", false);
385
386 switch (col) {
387 case VALUE_COLUMN: {
388 switch (role) {
389 case ConfigurationEditor::ItemListValueRole: {
390 listValues.insert(editingAttribute->getId(), value);
391 return true;
392 }
393 case Qt::EditRole:
394 case ConfigurationEditor::ItemValueRole: {
395 QMap<Attribute *, bool> relatedAttributesVisibility = getAttributeRelatedVisibility(editingAttribute);
396
397 const QString &key = editingAttribute->getId();
398 if (editingAttribute->getAttributePureValue() != value) {
399 subject->setParameter(key, value);
400 emit dataChanged(index, index);
401 uiLog.trace("committed property change");
402 }
403 foreach (const AttributeRelation *relation, editingAttribute->getRelations()) {
404 if (relation->valueChangingRelation()) {
405 DelegateTags *inf = getTags(subject, editingAttribute->getId());
406 DelegateTags *dep = getTags(subject, relation->getRelatedAttrId());
407 Attribute *depAttr = subject->getParameter(relation->getRelatedAttrId());
408 QVariant newValue = relation->getAffectResult(value, depAttr->getAttributePureValue(), inf, dep);
409
410 if (canSetData(depAttr, newValue)) {
411 QModelIndex idx = modelIndexById(relation->getRelatedAttrId());
412 setData(idx, newValue);
413 }
414 }
415 }
416 checkIfAttributeVisibilityChanged(relatedAttributesVisibility);
417 subject->updateItemsAvailability(editingAttribute);
418
419 return true;
420 }
421 default:
422 return false;
423 }
424 }
425 case SCRIPT_COLUMN: {
426 switch (role) {
427 case Qt::EditRole:
428 case ConfigurationEditor::ItemValueRole: {
429 AttributeScript attrScript = value.value<AttributeScript>();
430 editingAttribute->getAttributeScript().setScriptText(attrScript.getScriptText());
431 emit dataChanged(index, index);
432 uiLog.trace(QString("user script for '%1' attribute updated").arg(editingAttribute->getDisplayName()));
433 return true;
434 }
435 default:
436 return false;
437 }
438 }
439 default:
440 assert(false);
441 }
442
443 // unreachable code
444 return false;
445 }
446
changeScriptMode(bool _mode)447 void ActorCfgModel::changeScriptMode(bool _mode) {
448 scriptMode = _mode;
449 update();
450 }
451
getScriptMode() const452 bool ActorCfgModel::getScriptMode() const {
453 return scriptMode;
454 }
455
456 } // namespace U2
457