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 "CreateScriptWorker.h"
23 
24 #include <QAbstractItemModel>
25 #include <QFileDialog>
26 #include <QHeaderView>
27 #include <QMessageBox>
28 #include <QPushButton>
29 #include <QTableView>
30 
31 #include <U2Core/Log.h>
32 
33 #include <U2Gui/HelpButton.h>
34 
35 #include <U2Lang/ActorPrototypeRegistry.h>
36 #include <U2Lang/BaseSlots.h>
37 #include <U2Lang/BaseTypes.h>
38 #include <U2Lang/WorkflowSettings.h>
39 
40 #include "WorkflowEditorDelegates.h"
41 #include "library/ScriptWorker.h"
42 #include "util/WorkerNameValidator.h"
43 
44 namespace U2 {
45 
46 #define WORKFLOW_DOC "GB2WORKFLOW"
47 #define ACTOR_ELEMENT "Actor"
48 #define INPUT_PORT_ELEMENT "Input-port"
49 #define OUTPUT_PORT_ELEMENT "Output-port"
50 #define ATTRIBUTE_ELEMENT "Attributes"
51 #define IN_SLOT_ELEMENT "In-Slots"
52 #define OUT_SLOT_ELEMENT "Out-Slots"
53 #define SLOT_ID "Slot"
54 #define ATTR_ELEMENT "Attribute"
55 #define NAME_ID "Name"
56 #define TYPE_ID "Type"
57 #define NAME_ELEMENT "Element-name"
58 #define DESCR_ELEMENT "Element-description"
59 #define DESCR_ID "Description"
60 
61 // Q_DECLARE_METATYPE(DataTypePtr);
62 
63 class MapForTypesDelegate {
64 public:
MapForTypesDelegate()65     MapForTypesDelegate() {
66     }
67 
getAttrTypes()68     static QVariantMap getAttrTypes() {
69         QVariantMap res;
70         DataTypePtr ptr = BaseTypes::BOOL_TYPE();
71         res[ptr->getDisplayName()] = BaseTypes::BOOL_TYPE()->getId();
72 
73         ptr = BaseTypes::NUM_TYPE();
74         res[ptr->getDisplayName()] = BaseTypes::NUM_TYPE()->getId();
75 
76         ptr = BaseTypes::STRING_TYPE();
77         res[ptr->getDisplayName()] = BaseTypes::STRING_TYPE()->getId();
78 
79         return res;
80     }
81 
getPortTypes()82     static QVariantMap getPortTypes() {
83         QVariantMap res;
84         DataTypePtr ptr = BaseTypes::DNA_SEQUENCE_TYPE();
85         res[ptr->getDisplayName()] = BaseTypes::DNA_SEQUENCE_TYPE()->getId();
86 
87         ptr = BaseTypes::ANNOTATION_TABLE_TYPE();
88         res[ptr->getDisplayName()] = BaseTypes::ANNOTATION_TABLE_TYPE()->getId();
89 
90         ptr = BaseTypes::MULTIPLE_ALIGNMENT_TYPE();
91         res[ptr->getDisplayName()] = BaseTypes::MULTIPLE_ALIGNMENT_TYPE()->getId();
92 
93         ptr = BaseTypes::STRING_TYPE();
94         res[ptr->getDisplayName()] = BaseTypes::STRING_TYPE()->getId();
95         /*Descriptor ptr = BaseSlots::DNA_SEQUENCE_SLOT();
96         res[ptr.getDisplayName()] = BaseSlots::DNA_SEQUENCE_SLOT().getId();
97 
98         ptr = BaseSlots::ANNOTATION_TABLE_SLOT();
99         res[ptr.getDisplayName()] = BaseSlots::ANNOTATION_TABLE_SLOT().getId();
100 
101         ptr = BaseSlots::MULTIPLE_ALIGNMENT_SLOT();
102         res[ptr.getDisplayName()] = BaseSlots::MULTIPLE_ALIGNMENT_SLOT().getId();
103 
104         ptr = BaseSlots::TEXT_SLOT();
105         res[ptr.getDisplayName()] = BaseSlots::TEXT_SLOT().getId();*/
106         return res;
107     }
108 
109     static QVariantMap portMap;
110     static QVariantMap attrMap;
111 };
112 
113 QVariantMap MapForTypesDelegate::attrMap = MapForTypesDelegate::getAttrTypes();
114 QVariantMap MapForTypesDelegate::portMap = MapForTypesDelegate::getPortTypes();
115 
116 enum DelegateType {
117     DelegateForPort,
118     DelegateForAttribute
119 };
120 
121 class CfgListItem {
122     Q_DISABLE_COPY(CfgListItem)
123 public:
CfgListItem(DelegateType t)124     CfgListItem(DelegateType t) {
125         if (t == DelegateForPort) {
126             delegate = new ComboBoxDelegate(MapForTypesDelegate::portMap);
127             dataTypeId = BaseTypes::DNA_SEQUENCE_TYPE()->getId();
128         } else if (t == DelegateForAttribute) {
129             delegate = new ComboBoxDelegate(MapForTypesDelegate::attrMap);
130             dataTypeId = BaseTypes::STRING_TYPE()->getId();
131         } else {
132             assert(0);
133         }
134     }
~CfgListItem()135     ~CfgListItem() {
136         delete delegate;
137     }
getDelegate() const138     PropertyDelegate *getDelegate() const {
139         return delegate;
140     }
getDataType() const141     QString getDataType() const {
142         return dataTypeId;
143     }
setDataType(const QString & id)144     void setDataType(const QString &id) {
145         dataTypeId = id;
146     }
147 
getName() const148     QString getName() const {
149         return name;
150     }
setName(const QString & _name)151     void setName(const QString &_name) {
152         name = _name;
153     }
154 
155 private:
156     PropertyDelegate *delegate;
157     QString dataTypeId;
158     QString name;
159     // DataTypePtr dataType;
160 };
161 
162 class CfgListModel : public QAbstractListModel {
163 public:
164     // Hint for row height. We use non-default row to make combo-boxes fit.
165     int itemHeight;
CfgListModel(int rowHeightHint,QObject * obj=nullptr)166     CfgListModel(int rowHeightHint, QObject *obj = nullptr)
167         : QAbstractListModel(obj), itemHeight(rowHeightHint) {
168         items.append(new CfgListItem(DelegateForPort));
169     }
170 
~CfgListModel()171     ~CfgListModel() {
172         foreach (CfgListItem *item, items) {
173             delete item;
174         }
175     }
176 
getItems() const177     QStringList getItems() const {
178         QStringList result;
179         foreach (CfgListItem *item, items) {
180             result.append(item->getDataType());
181         }
182         return result;
183     }
184 
rowCount(const QModelIndex & parent=QModelIndex ()) const185     int rowCount(const QModelIndex &parent = QModelIndex()) const {
186         Q_UNUSED(parent);
187         return items.count();
188     }
189 
flags(const QModelIndex &) const190     Qt::ItemFlags flags(const QModelIndex &) const {
191         return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
192     }
193 
getItem(const QModelIndex & index) const194     CfgListItem *getItem(const QModelIndex &index) const {
195         // CfgListItem *item  = static_cast<CfgListItem*>(index.internalPointer());
196         return items.at(index.row());
197     }
198 
parent(const QModelIndex &) const199     QModelIndex parent(const QModelIndex &) const {
200         return QModelIndex();
201     }
202 
data(const QModelIndex & index,int role) const203     QVariant data(const QModelIndex &index, int role /* = Qt::DisplayRole */) const {
204         CfgListItem *item = getItem(index);
205         PropertyDelegate *dg = item->getDelegate();
206         switch (role) {
207             case Qt::DisplayRole:
208             case Qt::ToolTipRole:
209                 return dg->getDisplayValue(item->getDataType());
210             case DelegateRole:
211                 return qVariantFromValue<PropertyDelegate *>(dg);
212             case Qt::EditRole:
213             case ConfigurationEditor::ItemValueRole:
214                 return item->getDataType();
215             case Qt::SizeHintRole:
216                 return QSize(0, itemHeight);
217             default:
218                 return QVariant();
219         }
220     }
221 
setData(const QModelIndex & index,const QVariant & value,int role)222     bool setData(const QModelIndex &index, const QVariant &value, int role /* = Qt::EditRole */) {
223         switch (role) {
224             case Qt::EditRole:
225             case ConfigurationEditor::ItemValueRole:
226                 CfgListItem *item = getItem(index);
227                 if (item->getDataType() != value.toString()) {
228                     if (!value.toString().isEmpty()) {
229                         item->setDataType(value.toString());
230                     }
231                 }
232                 emit dataChanged(index, index);
233         }
234         return true;
235     }
236 
insertRows(int row,int count=0,const QModelIndex & parent=QModelIndex ())237     bool insertRows(int row, int count = 0, const QModelIndex &parent = QModelIndex()) {
238         Q_UNUSED(row);
239         beginInsertRows(parent, items.size(), items.size() + count);
240         for (int i = 0; i < count; i++) {
241             items.append(new CfgListItem(DelegateForPort));
242         }
243         endInsertRows();
244         return true;
245     }
246 
removeRows(int row,int count,const QModelIndex & parent)247     bool removeRows(int row, int count, const QModelIndex &parent /* = QModelIndex */) {
248         Q_UNUSED(count);
249         if (rowCount() == 0 || row < 0 || row > rowCount()) {
250             return false;
251         }
252 
253         beginRemoveRows(parent, row, row);
254         items.removeAt(row);
255         endRemoveRows();
256         return true;
257     }
258 
259 private:
260     QList<CfgListItem *> items;
261 };
262 
263 class CfgTableModel : public QAbstractTableModel {
264 public:
CfgTableModel(QObject * obj=nullptr)265     CfgTableModel(QObject *obj = nullptr)
266         : QAbstractTableModel(obj) {
267         // attrs.append(new CfgListItem());
268     }
269 
rowCount(const QModelIndex &) const270     int rowCount(const QModelIndex & /* = QModelIndex */) const {
271         return attrs.size();
272     }
273 
columnCount(const QModelIndex &) const274     int columnCount(const QModelIndex & /* = QModelIndex */) const {
275         return 2;
276     }
277 
flags(const QModelIndex &) const278     Qt::ItemFlags flags(const QModelIndex &) const {
279         return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
280     }
281 
getItem(const QModelIndex & index) const282     CfgListItem *getItem(const QModelIndex &index) const {
283         // CfgListItem *item  = static_cast<CfgListItem*>(index.internalPointer());
284         return attrs.at(index.row());
285     }
286 
getItems() const287     QList<CfgListItem *> getItems() const {
288         return attrs;
289     }
290 
data(const QModelIndex & index,int role) const291     QVariant data(const QModelIndex &index, int role /* = Qt::DisplayRole */) const {
292         CfgListItem *item = getItem(index);
293         int col = index.column();
294         PropertyDelegate *dg = item->getDelegate();
295 
296         switch (role) {
297             case Qt::DisplayRole:
298                 if (col == 0)
299                     return item->getName();
300                 else
301                     return dg->getDisplayValue(item->getDataType());
302             case DelegateRole:
303                 if (col == 1)
304                     return qVariantFromValue<PropertyDelegate *>(dg);
305                 else
306                     return QVariant();
307             case Qt::EditRole:
308             case ConfigurationEditor::ItemValueRole:
309                 if (col == 1)
310                     return item->getDataType();
311                 else
312                     return item->getName();
313             default:
314                 return QVariant();
315         }
316     }
317 
headerData(int section,Qt::Orientation orientation,int role=Qt::DisplayRole) const318     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const {
319         if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
320             switch (section) {
321                 case 0:
322                     return CreateScriptElementDialog::tr("Name");
323                 case 1:
324                     return CreateScriptElementDialog::tr("Type");
325                 default:
326                     return QVariant();
327             }
328         }
329         return QVariant();
330     }
331 
setData(const QModelIndex & index,const QVariant & value,int role)332     bool setData(const QModelIndex &index, const QVariant &value, int role) {
333         int col = index.column();
334         CfgListItem *item = getItem(index);
335         switch (role) {
336             case Qt::EditRole:
337             case ConfigurationEditor::ItemValueRole:
338                 if (col == 1) {
339                     if (item->getDataType() != value.toString()) {
340                         if (!value.toString().isEmpty()) {
341                             item->setDataType(value.toString());
342                         }
343                     }
344                 } else {
345                     if (item->getName() != value.toString()) {
346                         item->setName(value.toString());
347                     }
348                 }
349                 emit dataChanged(index, index);
350         }
351         return true;
352     }
353 
insertRows(int row,int count=0,const QModelIndex & parent=QModelIndex ())354     bool insertRows(int row, int count = 0, const QModelIndex &parent = QModelIndex()) {
355         Q_UNUSED(row);
356         Q_UNUSED(count);
357         beginInsertRows(parent, attrs.size(), attrs.size());
358         attrs.append(new CfgListItem(DelegateForAttribute));
359         endInsertRows();
360         return true;
361     }
362 
removeRows(int row,int count=0,const QModelIndex & parent=QModelIndex ())363     bool removeRows(int row, int count = 0, const QModelIndex &parent = QModelIndex()) {
364         Q_UNUSED(count);
365         if (row >= 0 && row < attrs.size()) {
366             beginRemoveRows(parent, row, row);
367             attrs.removeAt(row);
368             endRemoveRows();
369             return true;
370         } else {
371             return false;
372         }
373     }
374 
375 private:
376     QList<CfgListItem *> attrs;
377 };
378 
CreateScriptElementDialog(QWidget * p,ActorPrototype * proto)379 CreateScriptElementDialog::CreateScriptElementDialog(QWidget *p, ActorPrototype *proto)
380     : QDialog(p), editing(false) {
381     setupUi(this);
382     new HelpButton(this, buttonBox, "65929977");
383     buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
384     buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
385 
386     // make list rows have the same size as buttons -> this must be enought to fit combo-boxes
387     int comboboxHeightHint = addInputButton->sizeHint().height();
388     inputList->setModel(new CfgListModel(comboboxHeightHint, this));
389     inputList->setItemDelegate(new ProxyDelegate());
390     outputList->setModel(new CfgListModel(comboboxHeightHint, this));
391     outputList->setItemDelegate(new ProxyDelegate());
392 
393     attributeTable->setModel(new CfgTableModel());
394     attributeTable->setItemDelegate(new ProxyDelegate());
395 
396     errorBox->hide();
397 
398     connect(addInputButton, SIGNAL(clicked()), SLOT(sl_addInputClicked()));
399     connect(addOutputButton, SIGNAL(clicked()), SLOT(sl_addOutputClicked()));
400     connect(addAttributeButton, SIGNAL(clicked()), SLOT(sl_addAttribute()));
401 
402     connect(deleteInputButton, SIGNAL(clicked()), SLOT(sl_deleteInputClicked()));
403     connect(deleteOutputButton, SIGNAL(clicked()), SLOT(sl_deleteOutputClicked()));
404     connect(deleteAttributeButton, SIGNAL(clicked()), SLOT(sl_deleteAttributeClicked()));
405 
406     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
407     QPushButton *cancelButton = buttonBox->button(QDialogButtonBox::Cancel);
408     connect(fileButton, SIGNAL(clicked()), SLOT(sl_getDirectory()));
409     connect(okButton, SIGNAL(clicked()), SLOT(sl_okClicked()));
410     connect(cancelButton, SIGNAL(clicked()), SLOT(sl_cancelClicked()));
411 
412     attributeTable->horizontalHeader()->setStretchLastSection(true);
413     attributeTable->verticalHeader()->hide();
414 
415     nameEdit->setValidator(new DeprecatedWorkerNameValidator(this));
416 
417     if (proto) {
418         fillFields(proto);
419         editing = true;
420     }
421 }
422 
getDatatypeOfSlotDesc(const Descriptor & dt)423 static DataTypePtr getDatatypeOfSlotDesc(const Descriptor &dt) {
424     QString dtId = dt.getId();
425     if (dtId == BaseSlots::DNA_SEQUENCE_SLOT().getId()) {
426         return BaseTypes::DNA_SEQUENCE_TYPE();
427     }
428     if (dtId == BaseSlots::ANNOTATION_TABLE_SLOT().getId()) {
429         return BaseTypes::ANNOTATION_TABLE_TYPE();
430     }
431     if (dtId == BaseSlots::MULTIPLE_ALIGNMENT_SLOT().getId()) {
432         return BaseTypes::MULTIPLE_ALIGNMENT_TYPE();
433     }
434     if (dtId == BaseSlots::TEXT_SLOT().getId()) {
435         return BaseTypes::STRING_TYPE();
436     }
437     assert(false);
438     return DataTypePtr();
439 }
440 
fillFields(ActorPrototype * proto)441 void CreateScriptElementDialog::fillFields(ActorPrototype *proto) {
442     int inputInd = 0;
443     int outputInd = 0;
444     QList<PortDescriptor *> portDescriptors = proto->getPortDesciptors();
445     for (const PortDescriptor *desc : qAsConst(portDescriptors)) {
446         if (desc->isInput()) {
447             inputList->model()->insertRows(0, desc->getType()->getAllDescriptors().size() - 1, QModelIndex());
448             foreach (const Descriptor &d, desc->getType()->getAllDescriptors()) {
449                 QModelIndex mi = inputList->model()->index(inputInd, 0);
450                 inputList->model()->setData(mi, getDatatypeOfSlotDesc(d)->getId());
451                 inputInd++;
452             }
453 
454         } else {
455             outputList->model()->insertRows(0, desc->getType()->getAllDescriptors().size() - 1, QModelIndex());
456             foreach (const Descriptor &d, desc->getType()->getAllDescriptors()) {
457                 QModelIndex mi = outputList->model()->index(outputInd, 0);
458                 outputList->model()->setData(mi, getDatatypeOfSlotDesc(d)->getId());
459                 outputInd++;
460             }
461         }
462     }
463 
464     int ind = 0;
465     foreach (const Attribute *attr, proto->getAttributes()) {
466         attributeTable->model()->insertRow(1, QModelIndex());
467         QModelIndex mi1 = attributeTable->model()->index(ind, 0);
468         QModelIndex mi2 = attributeTable->model()->index(ind, 1);
469         attributeTable->model()->setData(mi1, attr->getId());
470         attributeTable->model()->setData(mi2, attr->getAttributeType()->getId());
471         ind++;
472     }
473 
474     nameEdit->setText(proto->getDisplayName());
475     descriptionEdit->setText(proto->getDocumentation());
476 }
477 
sl_getDirectory()478 void CreateScriptElementDialog::sl_getDirectory() {
479     QString url = WorkflowSettings::getUserDirectory();
480 
481     QFileDialog dialog(this);
482     dialog.setFileMode(QFileDialog::Directory);
483     dialog.setViewMode(QFileDialog::List);
484     dialog.setDirectory(url);
485     if (dialog.exec() == QDialog::Accepted) {
486         QString dir = dialog.selectedFiles().first();
487         fileEdit->setText(dir);
488     }
489 }
490 
sl_addInputClicked()491 void CreateScriptElementDialog::sl_addInputClicked() {
492     inputList->model()->insertRow(0, QModelIndex());
493 }
494 
sl_addOutputClicked()495 void CreateScriptElementDialog::sl_addOutputClicked() {
496     outputList->model()->insertRow(0, QModelIndex());
497 }
498 
sl_addAttribute()499 void CreateScriptElementDialog::sl_addAttribute() {
500     attributeTable->model()->insertRow(0, QModelIndex());
501 }
502 
sl_deleteInputClicked()503 void CreateScriptElementDialog::sl_deleteInputClicked() {
504     QModelIndex index = inputList->currentIndex();
505     inputList->model()->removeRow(index.row());
506 }
507 
sl_deleteOutputClicked()508 void CreateScriptElementDialog::sl_deleteOutputClicked() {
509     QModelIndex index = outputList->currentIndex();
510     outputList->model()->removeRow(index.row());
511 }
512 
sl_deleteAttributeClicked()513 void CreateScriptElementDialog::sl_deleteAttributeClicked() {
514     QModelIndex index = attributeTable->currentIndex();
515     attributeTable->model()->removeRow(index.row());
516 }
517 
sl_cancelClicked()518 void CreateScriptElementDialog::sl_cancelClicked() {
519     reject();
520 }
521 
sl_okClicked()522 void CreateScriptElementDialog::sl_okClicked() {
523     CfgListModel *inputPorts = static_cast<CfgListModel *>(inputList->model());
524     QList<QString> typeIds = inputPorts->getItems();
525     DataTypeRegistry *dtr = WorkflowEnv::getDataTypeRegistry();
526     assert(dtr);
527     input.clear();
528     foreach (const QString &id, typeIds) {
529         DataTypePtr ptr = dtr->getById(id);
530         if (input.contains(ptr)) {
531             QMessageBox::critical(this, tr("error"), tr("Two identical types for input port"));
532             coreLog.error(tr("two identical types for input port"));
533             return;
534         }
535         input << ptr;
536     }
537 
538     CfgListModel *outputPorts = static_cast<CfgListModel *>(outputList->model());
539     typeIds = outputPorts->getItems();
540     assert(dtr);
541     output.clear();
542     foreach (const QString &id, typeIds) {
543         DataTypePtr ptr = dtr->getById(id);
544         if (output.contains(ptr)) {
545             QMessageBox::critical(this, tr("error"), tr("Two identical types for output port"));
546             coreLog.error(tr("two identical types for output port"));
547             return;
548         }
549         output << ptr;
550     }
551 
552     CfgTableModel *attrTableModel = static_cast<CfgTableModel *>(attributeTable->model());
553     QList<CfgListItem *> attributes = attrTableModel->getItems();
554     attrs.clear();
555     for (CfgListItem *item : qAsConst(attributes)) {
556         QString itemName = item->getName();
557         if (itemName.isEmpty()) {
558             QMessageBox::critical(this, tr("error"), tr("Name for some attributes is empty"));
559             coreLog.error(tr("Name for some attributes is empty"));
560             return;
561         }
562         for (const Attribute *attr : qAsConst(attrs)) {
563             if (attr->getId() == itemName) {
564                 QMessageBox::critical(this, tr("error"), tr("Two attributes with name %1").arg(itemName));
565                 coreLog.error(tr("Two attributes with name %1").arg(itemName));
566                 return;
567             }
568         }
569 
570         DataTypePtr ptr = dtr->getById(item->getDataType());
571         Descriptor desc(itemName, itemName, ptr->getDisplayName());
572         if (ptr == BaseTypes::BOOL_TYPE()) {
573             attrs << new Attribute(desc, ptr, false, QVariant(false));
574         } else {
575             attrs << new Attribute(desc, ptr);
576         }
577     }
578 
579     name = nameEdit->text();
580     if (name.isEmpty()) {
581         QMessageBox::critical(this, tr("error"), tr("Name for block is empty"));
582         coreLog.error(tr("Name for block is empty"));
583         return;
584     }
585 
586     ActorPrototypeRegistry *pr = WorkflowEnv::getProtoRegistry();
587     if (pr) {
588         if (pr->getProto(LocalWorkflow::ScriptWorkerFactory::ACTOR_ID + name) && !editing) {
589             QMessageBox::critical(this, tr("error"), tr("Actor with this name already registered"));
590             coreLog.error(tr("Actor with this name already registered"));
591             return;
592         }
593     }
594 
595     description = descriptionEdit->toPlainText();
596     if (description.isEmpty()) {
597         QMessageBox::critical(this, tr("error"), tr("Description for block is empty"));
598         coreLog.error(tr("Description for block is empty"));
599         return;
600     }
601 
602     if (!fileEdit->text().isEmpty()) {
603         changeDirectoryForActors();
604     }
605 
606     if (!saveParams()) {
607         errorBox->show();
608         return;
609     }
610 
611     accept();
612 }
613 
changeDirectoryForActors()614 void CreateScriptElementDialog::changeDirectoryForActors() {
615     QString url = WorkflowSettings::getUserDirectory();
616     QString newUrl = fileEdit->text() + "/";
617 
618     if (url != newUrl) {
619         WorkflowSettings::setUserDirectory(newUrl);
620 
621         QDir dir(url);
622         if (!dir.exists()) {
623             // coreLog.info(tr("There isn't folder with users workflow elements"));
624             return;
625         }
626         dir.setNameFilters(QStringList() << "*.usa");
627         QFileInfoList fileList = dir.entryInfoList();
628         foreach (const QFileInfo &fileInfo, fileList) {
629             QString newFileUrl = newUrl + fileInfo.fileName();
630             QFile::copy(fileInfo.filePath(), newFileUrl);
631         }
632     }
633 }
634 
saveParams()635 bool CreateScriptElementDialog::saveParams() {
636     QDomDocument doc = saveXml();
637     QString url = WorkflowSettings::getUserDirectory();
638     QDir dir(url);
639     if (!dir.exists()) {
640         dir.mkpath(url);
641     }
642 
643     IOAdapterFactory *iof = AppContext::getIOAdapterRegistry()->getIOAdapterFactoryById(BaseIOAdapters::LOCAL_FILE);
644     IOAdapter *io = iof->createIOAdapter();
645     actorFilePath = url + name + ".usa";
646     if (io->open(actorFilePath, IOAdapterMode_Write)) {
647         io->writeBlock(doc.toByteArray());
648         io->close();
649         return true;
650     } else {
651         coreLog.error(tr("Can't save user's workflow element"));
652         return false;
653     }
654 }
655 
saveXml()656 QDomDocument CreateScriptElementDialog::saveXml() {
657     QDomDocument xml(WORKFLOW_DOC);
658     QDomElement actor = xml.createElement(ACTOR_ELEMENT);
659     xml.appendChild(actor);
660 
661     CfgListModel *inputPorts = static_cast<CfgListModel *>(inputList->model());
662     QList<QString> typeIds = inputPorts->getItems();
663     QDomElement inputPort = xml.createElement(INPUT_PORT_ELEMENT);
664     actor.appendChild(inputPort);
665     foreach (const QString &str, typeIds) {
666         QDomElement slot = xml.createElement(IN_SLOT_ELEMENT);
667         slot.setAttribute(SLOT_ID, str);
668         inputPort.appendChild(slot);
669     }
670 
671     CfgListModel *outputPorts = static_cast<CfgListModel *>(outputList->model());
672     typeIds = outputPorts->getItems();
673     QDomElement outputPort = xml.createElement(OUTPUT_PORT_ELEMENT);
674     actor.appendChild(outputPort);
675     foreach (const QString &str, typeIds) {
676         QDomElement slot = xml.createElement(OUT_SLOT_ELEMENT);
677         slot.setAttribute(SLOT_ID, str);
678         outputPort.appendChild(slot);
679     }
680 
681     CfgTableModel *attrTableModel = static_cast<CfgTableModel *>(attributeTable->model());
682     QList<CfgListItem *> attributes = attrTableModel->getItems();
683     QDomElement attribute = xml.createElement(ATTRIBUTE_ELEMENT);
684     actor.appendChild(attribute);
685     foreach (CfgListItem *item, attributes) {
686         QString itemName = item->getName();
687         QString itemId = item->getDataType();
688         QDomElement attr = xml.createElement(ATTR_ELEMENT);
689         attr.setAttribute(NAME_ID, itemName);
690         attr.setAttribute(TYPE_ID, itemId);
691         attribute.appendChild(attr);
692     }
693 
694     QDomElement nameEl = xml.createElement(NAME_ELEMENT);
695     nameEl.setAttribute(NAME_ID, name);
696     actor.appendChild(nameEl);
697 
698     QDomElement descriptionEl = xml.createElement(DESCR_ELEMENT);
699     descriptionEl.setAttribute(DESCR_ID, description);
700     actor.appendChild(descriptionEl);
701 
702     return xml;
703 }
704 
getInput() const705 QList<DataTypePtr> CreateScriptElementDialog::getInput() const {
706     return input;
707 }
getOutput() const708 QList<DataTypePtr> CreateScriptElementDialog::getOutput() const {
709     return output;
710 }
getAttributes() const711 QList<Attribute *> CreateScriptElementDialog::getAttributes() const {
712     return attrs;
713 }
getName() const714 const QString CreateScriptElementDialog::getName() const {
715     return name;
716 }
717 
getDescription() const718 const QString CreateScriptElementDialog::getDescription() const {
719     return description;
720 }
721 
getActorFilePath() const722 const QString CreateScriptElementDialog::getActorFilePath() const {
723     return actorFilePath;
724 }
725 
726 }  // namespace U2
727