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 <algorithm>
23 #include <functional>
24 
25 #include <QBoxLayout>
26 #include <QClipboard>
27 #include <QComboBox>
28 #include <QGraphicsSceneMouseEvent>
29 #include <QMainWindow>
30 #include <QPainter>
31 #include <QPixmap>
32 #include <QShortcut>
33 #include <QSvgGenerator>
34 #include <QToolBar>
35 
36 #include <U2Core/AppContext.h>
37 #include <U2Core/Counter.h>
38 #include <U2Core/GUrlUtils.h>
39 #include <U2Core/IOAdapterUtils.h>
40 #include <U2Core/Log.h>
41 #include <U2Core/ProjectService.h>
42 #include <U2Core/QObjectScopedPointer.h>
43 #include <U2Core/TaskSignalMapper.h>
44 #include <U2Core/U2OpStatusUtils.h>
45 
46 #include <U2Designer/Dashboard.h>
47 #include <U2Designer/DashboardInfoRegistry.h>
48 #include <U2Designer/DesignerUtils.h>
49 #include <U2Designer/EstimationReporter.h>
50 #include <U2Designer/GrouperEditor.h>
51 #include <U2Designer/MarkerEditor.h>
52 #include <U2Designer/RemoveDashboardsTask.h>
53 #include <U2Designer/WizardController.h>
54 
55 #include <U2Gui/DialogUtils.h>
56 #include <U2Gui/ExportImageDialog.h>
57 #include <U2Gui/ScriptEditorDialog.h>
58 #include <U2Gui/U2FileDialog.h>
59 
60 #include <U2Lang/ActorModel.h>
61 #include <U2Lang/BaseActorCategories.h>
62 #include <U2Lang/BaseAttributes.h>
63 #include <U2Lang/GrouperSlotAttribute.h>
64 #include <U2Lang/HRSchemaSerializer.h>
65 #include <U2Lang/IncludedProtoFactory.h>
66 #include <U2Lang/MapDatatypeEditor.h>
67 #include <U2Lang/SchemaEstimationTask.h>
68 #include <U2Lang/WorkflowEnv.h>
69 #include <U2Lang/WorkflowManager.h>
70 #include <U2Lang/WorkflowRunTask.h>
71 #include <U2Lang/WorkflowSettings.h>
72 
73 #include "BreakpointManagerView.h"
74 #include "ChooseItemDialog.h"
75 #include "CreateScriptWorker.h"
76 #include "DashboardsManagerDialog.h"
77 #include "GalaxyConfigConfigurationDialogImpl.h"
78 #include "ImportSchemaDialog.h"
79 #include "ItemViewStyle.h"
80 #include "PortAliasesConfigurationDialog.h"
81 #include "SceneSerializer.h"
82 #include "SchemaAliasesConfigurationDialogImpl.h"
83 #include "StartupDialog.h"
84 #include "WorkflowDesignerPlugin.h"
85 #include "WorkflowDocument.h"
86 #include "WorkflowEditor.h"
87 #include "WorkflowInvestigationWidgetsController.h"
88 #include "WorkflowMetaDialog.h"
89 #include "WorkflowPalette.h"
90 #include "WorkflowSamples.h"
91 #include "WorkflowSceneIOTasks.h"
92 #include "WorkflowTabView.h"
93 #include "WorkflowViewController.h"
94 #include "WorkflowViewItems.h"
95 #include "debug_messages_translation/WorkflowDebugMessageParserImpl.h"
96 #include "library/ExternalProcessWorker.h"
97 #include "library/ScriptWorker.h"
98 #include "library/create_cmdline_based_worker/CreateCmdlineBasedWorkerWizard.h"
99 
100 namespace U2 {
101 
102 // TODO: sync with SETTINGS in WorkflowSettings.cpp
103 #define SETTINGS QString("workflowview/")
104 
105 #define LAST_DIR SETTINGS + "lastdir"
106 #define SPLITTER_STATE SETTINGS + "splitter"
107 #define EDITOR_STATE SETTINGS + "editor"
108 #define PALETTE_STATE SETTINGS + "palette"
109 #define TABS_STATE SETTINGS + "tabs"
110 #define CHECK_R_PACKAGE SETTINGS + "check_r_for_cistrome"
111 
112 enum { ElementsTab,
113        SamplesTab };
114 
115 #define WS 1000
116 #define MAX_FILE_SIZE 1000000
117 
118 static const int ABSENT_WIDGET_TAB_NUMBER = -1;
119 
120 /************************************************************************/
121 /* Utilities */
122 /************************************************************************/
123 class PercentValidator : public QRegExpValidator {
124 public:
PercentValidator(QObject * parent)125     PercentValidator(QObject *parent)
126         : QRegExpValidator(QRegExp("[1-9][0-9]*" + QObject::tr("%")), parent) {
127     }
fixup(QString & input) const128     void fixup(QString &input) const {
129         if (!input.endsWith(QObject::tr("%"))) {
130             input.append(QObject::tr("%"));
131         }
132     }
133 };  // PercentValidator
134 
scaleCombo(WorkflowView * parent)135 static QComboBox *scaleCombo(WorkflowView *parent) {
136     QComboBox *sceneScaleCombo = new QComboBox(parent);
137     sceneScaleCombo->setEditable(true);
138     sceneScaleCombo->setValidator(new PercentValidator(parent));
139     QStringList scales;
140     scales << "25%"
141            << "50%"
142            << "75%"
143            << "100%"
144            << "125%"
145            << "150%"
146            << "200%";
147     sceneScaleCombo->addItems(scales);
148     sceneScaleCombo->setCurrentIndex(3);
149     QObject::connect(sceneScaleCombo, SIGNAL(currentIndexChanged(const QString &)), parent, SLOT(sl_rescaleScene(const QString &)));
150     // Some visual modifications for Mac:
151     sceneScaleCombo->lineEdit()->setStyleSheet("QLineEdit {margin-right: 1px;}");
152     sceneScaleCombo->setObjectName("wdScaleCombo");
153     return sceneScaleCombo;
154 }
155 
addToggleDashboardAction(QToolBar * toolBar,QAction * action)156 static void addToggleDashboardAction(QToolBar *toolBar, QAction *action) {
157     QWidget *spacer = new QWidget();
158     spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
159     toolBar->addWidget(spacer);
160 
161     toolBar->addAction(action);
162     QToolButton *b = dynamic_cast<QToolButton *>(toolBar->widgetForAction(action));
163     CHECK(nullptr != b, );
164     b->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
165     b->setAutoRaise(false);
166 
167 #ifdef Q_OS_DARWIN
168     b->setStyleSheet("QToolButton {"
169                      "font-size: 13px;"
170                      "border: 1px solid gray;"
171                      "border-radius: 6px;"
172                      "margin-right: 5px;"
173                      "height: 25px;"
174                      "padding: 0px 5px;"
175                      "}");
176 #endif
177 }
178 
scriptMenu(WorkflowView * parent,const QList<QAction * > & scriptingActions)179 static QToolButton *scriptMenu(WorkflowView *parent, const QList<QAction *> &scriptingActions) {
180     QToolButton *scriptingModeButton = new QToolButton(parent);
181     QMenu *scriptingModeMenu = new QMenu(QObject::tr("Scripting mode"), parent);
182     foreach (QAction *a, scriptingActions) {
183         scriptingModeMenu->addAction(a);
184     }
185     scriptingModeButton->setDefaultAction(scriptingModeMenu->menuAction());
186     scriptingModeButton->setPopupMode(QToolButton::InstantPopup);
187     return scriptingModeButton;
188 }
189 
DashboardManagerHelper(QAction * _dmAction,WorkflowView * _parent)190 DashboardManagerHelper::DashboardManagerHelper(QAction *_dmAction, WorkflowView *_parent)
191     : QObject(_parent),
192       dmAction(_dmAction),
193       parent(_parent) {
194     connect(dmAction, SIGNAL(triggered()), SLOT(sl_showDashboardsManagerDialog()));
195 
196     DashboardInfoRegistry *dashboardInfoRegistry = AppContext::getDashboardInfoRegistry();
197     connect(dashboardInfoRegistry, SIGNAL(si_scanningStarted()), SLOT(sl_dashboardsScanningStarted()));
198     connect(dashboardInfoRegistry, SIGNAL(si_scanningFinished()), SLOT(sl_dashboardsScanningFinished()));
199 }
200 
sl_result(int result)201 void DashboardManagerHelper::sl_result(int result) {
202     DashboardsManagerDialog *d = qobject_cast<DashboardsManagerDialog *>(sender());
203     if (QDialog::Accepted == result) {
204         DashboardInfoRegistry *dashboardInfoRegistry = AppContext::getDashboardInfoRegistry();
205 
206         const QMap<QString, bool> dashboardsVisibility = d->getDashboardsVisibility();
207         QList<DashboardInfo> newDashboardInfos;
208         foreach (const QString &dashboardId, dashboardsVisibility.keys()) {
209             DashboardInfo newDashboardInfo = dashboardInfoRegistry->getById(dashboardId);
210             newDashboardInfo.opened = dashboardsVisibility[dashboardId];
211             newDashboardInfos << newDashboardInfo;
212         }
213         dashboardInfoRegistry->updateDashboardInfos(newDashboardInfos);
214 
215         const QStringList dashboardsToRemove = d->removedDashboards();
216         if (!dashboardsToRemove.isEmpty()) {
217             dashboardInfoRegistry->removeDashboards(dashboardsToRemove);
218         }
219     }
220 }
221 
sl_showDashboardsManagerDialog()222 void DashboardManagerHelper::sl_showDashboardsManagerDialog() {
223     if (AppContext::getDashboardInfoRegistry()->isEmpty()) {
224         QMessageBox *d = new QMessageBox(QMessageBox::Information, tr("No Dashboards Found"), tr("You do not have any dashboards yet. You need to run some workflow to use Dashboards Manager."), QMessageBox::NoButton, parent);
225         d->show();
226         return;
227     }
228 
229     DashboardsManagerDialog *d = new DashboardsManagerDialog(parent);
230     connect(d, SIGNAL(finished(int)), SLOT(sl_result(int)));
231     d->setWindowModality(Qt::ApplicationModal);
232     d->show();
233 }
234 
sl_dashboardsScanningStarted()235 void DashboardManagerHelper::sl_dashboardsScanningStarted() {
236     dmAction->setEnabled(false);
237 }
238 
sl_dashboardsScanningFinished()239 void DashboardManagerHelper::sl_dashboardsScanningFinished() {
240     dmAction->setEnabled(true);
241 }
242 
243 /********************************
244  * WorkflowView
245  ********************************/
createInstance(WorkflowGObject * go)246 WorkflowView *WorkflowView::createInstance(WorkflowGObject *go) {
247     MWMDIManager *mdiManager = AppContext::getMainWindow()->getMDIManager();
248     SAFE_POINT(nullptr != mdiManager, "NULL MDI manager", nullptr);
249 
250     WorkflowView *view = new WorkflowView(go);
251     view->setWindowIcon(QIcon(":/workflow_designer/images/wd.png"));
252     mdiManager->addMDIWindow(view);
253     mdiManager->activateWindow(view);
254     return view;
255 }
256 
openWD(WorkflowGObject * go)257 WorkflowView *WorkflowView::openWD(WorkflowGObject *go) {
258     if (WorkflowSettings::isOutputDirectorySet()) {
259         return createInstance(go);
260     }
261 
262     QObjectScopedPointer<StartupDialog> d = new StartupDialog(AppContext::getMainWindow()->getQMainWindow());
263     d->exec();
264     CHECK(!d.isNull(), nullptr);
265 
266     if (QDialog::Accepted == d->result()) {
267         return createInstance(go);
268     }
269     return nullptr;
270 }
271 
WorkflowView(WorkflowGObject * go)272 WorkflowView::WorkflowView(WorkflowGObject *go)
273     : MWMDIWindow(tr("Workflow Designer")), running(false), sceneRecreation(false), go(go),
274       schema(QSharedPointer<Schema>::create()), currentProto(nullptr), currentActor(nullptr),
275       pasteCount(0), debugInfo(new WorkflowDebugStatus(this)), debugActions(), loadWorkflowSceneTask(nullptr) {
276     scriptingMode = WorkflowSettings::getScriptingMode();
277     elementsMenu = nullptr;
278     schema->setDeepCopyFlag(true);
279 
280     setupScene();
281     setupPalette();
282     setupPropertyEditor();
283     setupErrorList();
284 
285     infoSplitter = new QSplitter(Qt::Vertical);
286     infoSplitter->addWidget(sceneView);
287     infoSplitter->addWidget(errorList);
288     addBottomWidgetsToInfoSplitter();
289     setupMainSplitter();
290 
291     loadUiSettings();
292 
293     createActions();
294     sl_changeScriptMode();
295 
296     if (go) {
297         loadSceneFromObject();
298     } else {
299         sl_newScene();
300     }
301 
302     propertyEditor->reset();
303 }
304 
~WorkflowView()305 WorkflowView::~WorkflowView() {
306     uiLog.trace("~WorkflowView");
307     if (!loadWorkflowSceneTask.isNull()) {
308         loadWorkflowSceneTask->cancel();
309     }
310     if (AppContext::getProjectService()) {
311         AppContext::getProjectService()->enableSaveAction(true);
312     }
313     WorkflowSettings::setScriptingMode(scriptingMode);
314     delete currentActor;
315     delete scene;
316     delete breakpointView;
317 }
318 
setupScene()319 void WorkflowView::setupScene() {
320     SceneCreator sc(schema.get(), meta);
321     scene = sc.createScene(this);
322 
323     sceneView = new GlassView(scene);
324     sceneView->setObjectName("sceneView");
325     sceneView->setAlignment(Qt::AlignCenter);
326 
327     scene->views().at(0)->setDragMode(QGraphicsView::RubberBandDrag);
328 
329     connect(scene, SIGNAL(processDblClicked()), SLOT(sl_toggleStyle()));
330     connect(scene, SIGNAL(selectionChanged()), SLOT(sl_editItem()));
331     connect(scene, SIGNAL(selectionChanged()), SLOT(sl_onSelectionChanged()));
332     connect(scene, SIGNAL(configurationChanged()), SLOT(sl_refreshActorDocs()));
333     connect(WorkflowSettings::watcher, SIGNAL(changed()), scene, SLOT(update()));
334 }
335 
setupPalette()336 void WorkflowView::setupPalette() {
337     palette = new WorkflowPalette(WorkflowEnv::getProtoRegistry(), this);
338     palette->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored));
339     connect(palette, SIGNAL(processSelected(Workflow::ActorPrototype *, bool)), SLOT(sl_selectPrototype(Workflow::ActorPrototype *, bool)));
340     connect(palette, SIGNAL(si_prototypeIsAboutToBeRemoved(Workflow::ActorPrototype *)), SLOT(sl_prototypeIsAboutToBeRemoved(Workflow::ActorPrototype *)));
341     connect(palette, SIGNAL(si_protoListModified()), SLOT(sl_protoListModified()));
342     connect(palette, SIGNAL(si_protoChanged()), SLOT(sl_editItem()));
343     connect(palette, SIGNAL(si_protoChanged()), scene, SLOT(sl_updateDocs()));
344 
345     tabs = new QTabWidget(this);
346     tabs->setObjectName("tabs");
347     tabs->insertTab(ElementsTab, palette, tr("Elements"));
348     samples = new SamplesWidget(scene);
349     samples->setObjectName("samples");
350     tabs->insertTab(SamplesTab, new SamplesWrapper(samples, this), tr("Samples"));
351     tabs->setTabPosition(QTabWidget::North);
352 
353     connect(samples, SIGNAL(setupGlass(GlassPane *)), sceneView, SLOT(setGlass(GlassPane *)));
354     connect(samples, SIGNAL(sampleSelected(QString)), this, SLOT(sl_pasteSample(QString)));
355     connect(tabs, SIGNAL(currentChanged(int)), samples, SLOT(cancelItem()));
356     connect(tabs, SIGNAL(currentChanged(int)), palette, SLOT(resetSelection()));
357     connect(tabs, SIGNAL(currentChanged(int)), scene, SLOT(setHint(int)));
358 }
359 
setupErrorList()360 void WorkflowView::setupErrorList() {
361     infoList = new QListWidget(this);
362     connect(infoList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), SLOT(sl_pickInfo(QListWidgetItem *)));
363     errorList = new QGroupBox();
364     {
365         errorList->setFlat(true);
366         errorList->setTitle(tr("Error list"));
367         QVBoxLayout *vl = new QVBoxLayout(errorList);
368         vl->setSpacing(0);
369         vl->setMargin(0);
370         vl->setContentsMargins(0, 0, 0, 0);
371         vl->addWidget(infoList);
372     }
373     errorList->hide();
374 }
375 
setupPropertyEditor()376 void WorkflowView::setupPropertyEditor() {
377     propertyEditor = new WorkflowEditor(this);
378 }
379 
loadSceneFromObject()380 void WorkflowView::loadSceneFromObject() {
381     LoadWorkflowTask::FileFormat format = LoadWorkflowTask::detectFormat(go->getSceneRawData());
382     go->setView(this);
383     QString err;
384     if (format == LoadWorkflowTask::HR) {
385         err = HRSchemaSerializer::string2Schema(go->getSceneRawData(), schema.get(), &meta);
386     } else if (format == LoadWorkflowTask::XML) {
387         QDomDocument xml;
388         QMap<ActorId, ActorId> remapping;
389         xml.setContent(go->getSceneRawData().toUtf8());
390         err = SchemaSerializer::xml2schema(xml.documentElement(), schema.get(), remapping);
391         SchemaSerializer::readMeta(&meta, xml.documentElement());
392         scene->setModified(false);
393         if (err.isEmpty()) {
394             QMessageBox::warning(this, tr("Warning!"), QObject::tr("You opened obsolete workflow in XML format. It is strongly recommended"
395                                                                    " to clear working space and create workflow from scratch."));
396         } else {
397             QMessageBox::warning(this, tr("Warning!"), QObject::tr("Sorry! This workflow is obsolete and cannot be opened."));
398         }
399     } else {
400         coreLog.error(tr("Undefined workflow format for %1").arg(go->getDocument() ? go->getDocument()->getURLString() : tr("file")));
401         sl_newScene();
402     }
403     scene->connectConfigurationEditors();
404 
405     if (!err.isEmpty()) {
406         sl_newScene();
407         coreLog.error(err);
408     } else {
409         SceneCreator sc(schema.get(), meta);
410         sc.recreateScene(scene);
411         if (go->getDocument()) {
412             meta.url = go->getDocument()->getURLString();
413         }
414         sl_updateTitle();
415         scene->setModified(false);
416         rescale();
417         sl_refreshActorDocs();
418     }
419 }
420 
loadUiSettings()421 void WorkflowView::loadUiSettings() {
422     Settings *settings = AppContext::getSettings();
423     if (settings->contains(SPLITTER_STATE)) {
424         splitter->restoreState(settings->getValue(SPLITTER_STATE).toByteArray());
425     }
426     if (settings->contains(PALETTE_STATE)) {
427         palette->restoreState(settings->getValue(PALETTE_STATE));
428     }
429     tabs->setCurrentIndex(settings->getValue(TABS_STATE, SamplesTab).toInt());
430 }
431 
setupMainSplitter()432 void WorkflowView::setupMainSplitter() {
433     splitter = new QSplitter(this);
434     splitter->setObjectName("WorkflowViewMainSplitter");
435     {
436         splitter->addWidget(tabs);
437         splitter->addWidget(infoSplitter);
438         splitter->addWidget(propertyEditor);
439 
440         splitter->setStretchFactor(0, 0);
441         splitter->setStretchFactor(1, 1);
442         splitter->setStretchFactor(2, 0);
443     }
444 
445     tabView = new WorkflowTabView(this);
446     tabView->hide();
447     connect(tabView, SIGNAL(si_countChanged()), SLOT(sl_dashboardCountChanged()));
448 
449     QHBoxLayout *layout = new QHBoxLayout();
450     layout->addWidget(tabView);
451     layout->addWidget(splitter);
452     layout->setSpacing(0);
453     layout->setMargin(0);
454     layout->setContentsMargins(0, 0, 0, 0);
455     setLayout(layout);
456 
457     connect(debugInfo, SIGNAL(si_pauseStateChanged(bool)), scene, SLOT(update()));
458     connect(debugInfo, SIGNAL(si_pauseStateChanged(bool)), SLOT(sl_pause(bool)));
459     connect(investigationWidgets,
460             SIGNAL(si_updateCurrentInvestigation(const Workflow::Link *, int)),
461             debugInfo,
462             SIGNAL(si_busInvestigationIsRequested(const Workflow::Link *, int)));
463     connect(investigationWidgets, SIGNAL(si_countOfMessagesRequested(const Workflow::Link *)), debugInfo, SIGNAL(si_busCountOfMessagesIsRequested(const Workflow::Link *)));
464     connect(debugInfo,
465             SIGNAL(si_busInvestigationRespond(const WorkflowInvestigationData &, const Workflow::Link *)),
466             investigationWidgets,
467             SLOT(sl_currentInvestigationUpdateResponse(const WorkflowInvestigationData &, const Workflow::Link *)));
468     connect(debugInfo, SIGNAL(si_busCountOfMessagesResponse(const Workflow::Link *, int)), investigationWidgets, SLOT(sl_countOfMessagesResponse(const Workflow::Link *, int)));
469     connect(investigationWidgets, SIGNAL(si_convertionMessages2DocumentsIsRequested(const Workflow::Link *, const QString &, int)), SLOT(sl_convertMessages2Documents(const Workflow::Link *, const QString &, int)));
470     connect(debugInfo, SIGNAL(si_breakpointAdded(const ActorId &)), SLOT(sl_breakpointAdded(const ActorId &)));
471     connect(debugInfo, SIGNAL(si_breakpointEnabled(const ActorId &)), SLOT(sl_breakpointEnabled(const ActorId &)));
472     connect(debugInfo, SIGNAL(si_breakpointRemoved(const ActorId &)), SLOT(sl_breakpointRemoved(const ActorId &)));
473     connect(debugInfo, SIGNAL(si_breakpointDisabled(const ActorId &)), SLOT(sl_breakpointDisabled(const ActorId &)));
474     connect(debugInfo, SIGNAL(si_breakpointIsReached(const U2::ActorId &)), SLOT(sl_breakpointIsReached(const U2::ActorId &)));
475 }
476 
sl_breakpointIsReached(const U2::ActorId & actor)477 void WorkflowView::sl_breakpointIsReached(const U2::ActorId &actor) {
478     propagateBreakpointToSceneItem(actor);
479     breakpointView->onBreakpointReached(actor);
480 }
481 
addBottomWidgetsToInfoSplitter()482 void WorkflowView::addBottomWidgetsToInfoSplitter() {
483     bottomTabs = new QTabWidget(infoSplitter);
484 
485     infoList = new QListWidget(this);
486     infoList->setObjectName("infoList");
487     connect(infoList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), SLOT(sl_pickInfo(QListWidgetItem *)));
488 
489     QWidget *w = new QWidget(bottomTabs);
490     QVBoxLayout *vl = new QVBoxLayout(w);
491     vl->setSpacing(0);
492     vl->setMargin(0);
493     vl->setContentsMargins(0, 0, 0, 0);
494     vl->addWidget(infoList);
495     w->hide();
496     bottomTabs->addTab(w, tr("Error list"));
497 
498     breakpointView = new BreakpointManagerView(debugInfo, schema, scene);
499     connect(breakpointView, SIGNAL(si_highlightingRequested(const ActorId &)), SLOT(sl_highlightingRequested(const ActorId &)));
500     connect(scene, SIGNAL(si_itemDeleted(const ActorId &)), breakpointView, SLOT(sl_breakpointRemoved(const ActorId &)));
501     if (WorkflowSettings::isDebuggerEnabled()) {
502         bottomTabs->addTab(breakpointView, QObject::tr("Breakpoints"));
503     }
504 
505     investigationWidgets = new WorkflowInvestigationWidgetsController(bottomTabs);
506 
507     infoSplitter->addWidget(bottomTabs);
508     bottomTabs->hide();
509 }
510 
sl_rescaleScene(const QString & scale)511 void WorkflowView::sl_rescaleScene(const QString &scale) {
512     int percentPos = scale.indexOf(QObject::tr("%"));
513     meta.scalePercent = scale.left(percentPos).toInt();
514     rescale(false);
515 }
516 
updateComboBox(QComboBox * scaleComboBox,int scalePercent)517 static void updateComboBox(QComboBox *scaleComboBox, int scalePercent) {
518     QString value = QString("%1%2").arg(scalePercent).arg(QObject::tr("%"));
519     bool isOk = true;
520     for (int i = 0; i < scaleComboBox->count(); i++) {
521         if (scaleComboBox->itemText(i) == value) {
522             scaleComboBox->setCurrentIndex(i);
523             return;
524         } else {
525             QString itemText = scaleComboBox->itemText(i).mid(0, scaleComboBox->itemText(i).size() - QObject::tr("%").size());
526             if (itemText.toInt(&isOk) > scalePercent && isOk) {
527                 scaleComboBox->insertItem(i, value);
528                 scaleComboBox->setCurrentIndex(i);
529                 return;
530             }
531         }
532     }
533     scaleComboBox->addItem(value);
534     scaleComboBox->setCurrentIndex(scaleComboBox->count() - 1);
535 }
536 
rescale(bool updateGui)537 void WorkflowView::rescale(bool updateGui) {
538     double newScale = meta.scalePercent / 100.0;
539     QGraphicsView *elementsView = scene->views().at(0);
540     elementsView->resetMatrix();
541     elementsView->scale(newScale, newScale);
542     if (updateGui) {
543         updateComboBox(scaleComboBox, meta.scalePercent);
544     }
545 }
546 
createActions()547 void WorkflowView::createActions() {
548     runAction = new QAction(tr("&Run workflow"), this);
549     runAction->setObjectName("Run workflow");
550     runAction->setIcon(QIcon(":workflow_designer/images/run.png"));
551     runAction->setShortcut(QKeySequence("Ctrl+R"));
552     connect(runAction, SIGNAL(triggered()), SLOT(sl_launch()));
553     connect(runAction, SIGNAL(triggered()), debugInfo, SLOT(sl_resumeTriggerActivated()));
554 
555     stopAction = new QAction(tr("S&top workflow"), this);
556     stopAction->setObjectName("Stop workflow");
557     stopAction->setIcon(QIcon(":workflow_designer/images/stopTask.png"));
558     stopAction->setEnabled(false);
559     connect(stopAction, SIGNAL(triggered()), debugInfo, SLOT(sl_executionFinished()));
560     connect(stopAction, SIGNAL(triggered()), SLOT(sl_stop()));
561 
562     validateAction = new QAction(tr("&Validate workflow"), this);
563     validateAction->setObjectName("Validate workflow");
564     validateAction->setIcon(QIcon(":workflow_designer/images/ok.png"));
565     validateAction->setShortcut(QKeySequence("Ctrl+E"));
566     connect(validateAction, SIGNAL(triggered()), SLOT(sl_validate()));
567 
568     estimateAction = new QAction(tr("&Estimate workflow"), this);
569     estimateAction->setObjectName("Estimate workflow");
570     estimateAction->setIcon(QIcon(":core/images/sum.png"));
571     estimateAction->setObjectName("Estimate workflow");
572     connect(estimateAction, SIGNAL(triggered()), SLOT(sl_estimate()));
573 
574     pauseAction = new QAction(tr("&Pause workflow"), this);
575     pauseAction->setObjectName("Pause workflow");
576     pauseAction->setIcon(QIcon(":workflow_designer/images/pause.png"));
577     pauseAction->setShortcut(QKeySequence("Ctrl+P"));
578     pauseAction->setEnabled(false);
579     connect(pauseAction, SIGNAL(triggered()), debugInfo, SLOT(sl_pauseTriggerActivated()));
580     debugActions.append(pauseAction);
581 
582     nextStepAction = new QAction(tr("&Next step"), this);
583     nextStepAction->setIcon(QIcon(":workflow_designer/images/next_step.png"));
584     nextStepAction->setShortcut(QKeySequence("F10"));
585     nextStepAction->setEnabled(false);
586     connect(nextStepAction, SIGNAL(triggered()), debugInfo, SLOT(sl_isolatedStepTriggerActivated()));
587     debugActions.append(nextStepAction);
588 
589     toggleBreakpointAction = breakpointView->getNewBreakpointAction();
590     toggleBreakpointAction->setEnabled(false);
591 
592     tickReadyAction = new QAction(tr("Process one &message"), this);
593     tickReadyAction->setIcon(QIcon(":workflow_designer/images/process_one_message.png"));
594     tickReadyAction->setShortcut(QKeySequence("Ctrl+M"));
595     tickReadyAction->setEnabled(false);
596     connect(tickReadyAction, SIGNAL(triggered()), SLOT(sl_processOneMessage()));
597     connect(tickReadyAction, SIGNAL(triggered()), scene, SLOT(update()));
598     connect(tickReadyAction, SIGNAL(triggered()), SLOT(sl_onSelectionChanged()));
599     connect(tickReadyAction, SIGNAL(triggered()), bottomTabs, SLOT(update()));
600     debugActions.append(tickReadyAction);
601 
602     newAction = new QAction(tr("&New workflow..."), this);
603     newAction->setIcon(QIcon(":workflow_designer/images/filenew.png"));
604     newAction->setShortcuts(QKeySequence::New);
605     newAction->setObjectName("New workflow action");
606     connect(newAction, SIGNAL(triggered()), SLOT(sl_newScene()));
607 
608     saveAction = new QAction(tr("&Save workflow"), this);
609     saveAction->setObjectName("Save workflow");
610     saveAction->setIcon(QIcon(":workflow_designer/images/filesave.png"));
611     saveAction->setShortcut(QKeySequence::Save);
612     saveAction->setShortcutContext(Qt::WindowShortcut);
613     connect(saveAction, SIGNAL(triggered()), SLOT(sl_saveScene()));
614 
615     saveAsAction = new QAction(tr("&Save workflow as..."), this);
616     saveAsAction->setIcon(QIcon(":workflow_designer/images/filesaveas.png"));
617     connect(saveAsAction, SIGNAL(triggered()), SLOT(sl_saveSceneAs()));
618     saveAsAction->setObjectName("Save workflow action");
619 
620     showWizard = new QAction(tr("Show wizard"), this);
621     showWizard->setObjectName("Show wizard");
622     QPixmap pm = QPixmap(":workflow_designer/images/wizard.png").scaled(16, 16);
623     showWizard->setIcon(QIcon(pm));
624     connect(showWizard, SIGNAL(triggered()), SLOT(sl_showWizard()));
625 
626     toggleBreakpointManager = new QAction("Show or hide breakpoint manager", this);
627     toggleBreakpointManager->setIcon(QIcon(":workflow_designer/images/show_breakpoint_manager.png"));
628     toggleBreakpointManager->setObjectName("Show or hide breakpoint manager");
629     connect(toggleBreakpointManager, SIGNAL(triggered()), SLOT(sl_toggleBreakpointManager()));
630 
631     {  // toggle dashboard action
632         toggleDashboard = new QAction(this);
633         toggleDashboard->setObjectName("toggleDashboard");
634         connect(toggleDashboard, SIGNAL(triggered()), SLOT(sl_toggleDashboard()));
635     }
636 
637     loadAction = new QAction(tr("&Load workflow"), this);
638     loadAction->setIcon(QIcon(":workflow_designer/images/fileopen.png"));
639     loadAction->setShortcut(QKeySequence("Ctrl+L"));
640     loadAction->setObjectName("Load workflow");
641     connect(loadAction, SIGNAL(triggered()), SLOT(sl_loadScene()));
642 
643     exportAction = new QAction(tr("&Export workflow as image"), this);
644     exportAction->setIcon(QIcon(":workflow_designer/images/export.png"));
645     exportAction->setShortcut(QKeySequence("Ctrl+Shift+S"));
646     connect(exportAction, SIGNAL(triggered()), SLOT(sl_exportScene()));
647 
648     deleteAction = new QAction(tr("Delete"), this);
649     deleteAction->setIcon(QIcon(":workflow_designer/images/delete.png"));
650     connect(deleteAction, SIGNAL(triggered()), scene, SLOT(sl_deleteItem()));
651 
652     dmAction = new QAction(tr("Dashboards manager"), this);
653     dmAction->setIcon(QIcon(":workflow_designer/images/settings.png"));
654     dmAction->setObjectName("Dashboards manager");
655     new DashboardManagerHelper(dmAction, this);
656 
657     {  // Delete shortcut
658         deleteShortcut = new QAction(sceneView);
659         deleteShortcut->setShortcuts(QKeySequence::Delete);
660         deleteShortcut->setShortcutContext(Qt::WidgetShortcut);
661         connect(deleteShortcut, SIGNAL(triggered()), scene, SLOT(sl_deleteItem()));
662         sceneView->addAction(deleteShortcut);
663     }
664 
665     {  // Ctrl+A shortcut
666         QAction *selectShortcut = new QAction(sceneView);
667         selectShortcut->setShortcuts(QKeySequence::SelectAll);
668         selectShortcut->setShortcutContext(Qt::WidgetShortcut);
669         connect(selectShortcut, SIGNAL(triggered()), scene, SLOT(sl_selectAll()));
670         sceneView->addAction(selectShortcut);
671     }
672 
673     configureParameterAliasesAction = new QAction(tr("Set parameter aliases..."), this);
674     configureParameterAliasesAction->setObjectName("Set parameter aliases");
675     configureParameterAliasesAction->setIcon(QIcon(":workflow_designer/images/table_relationship.png"));
676     connect(configureParameterAliasesAction, SIGNAL(triggered()), SLOT(sl_configureParameterAliases()));
677 
678     configurePortAliasesAction = new QAction(tr("Set port and slot aliases..."), this);
679     configurePortAliasesAction->setIcon(QIcon(":workflow_designer/images/port_relationship.png"));
680     connect(configurePortAliasesAction, SIGNAL(triggered()), SLOT(sl_configurePortAliases()));
681 
682     importSchemaToElement = new QAction(tr("Import workflow to element..."), this);
683     importSchemaToElement->setIcon(QIcon(":workflow_designer/images/import.png"));
684     connect(importSchemaToElement, SIGNAL(triggered()), SLOT(sl_importSchemaToElement()));
685 
686     createGalaxyConfigAction = new QAction(tr("Create Galaxy tool config..."), this);
687     createGalaxyConfigAction->setObjectName("Create Galaxy tool config");
688     createGalaxyConfigAction->setIcon(QIcon(":workflow_designer/images/galaxy.png"));
689     connect(createGalaxyConfigAction, SIGNAL(triggered()), SLOT(sl_createGalaxyConfig()));
690 
691     selectAction = new QAction(tr("Select all elements"), this);
692     connect(selectAction, SIGNAL(triggered()), scene, SLOT(sl_selectAll()));
693 
694     copyAction = new QAction(tr("&Copy"), this);
695     copyAction->setIcon(QIcon(":workflow_designer/images/editcopy.png"));
696     copyAction->setShortcut(QKeySequence("Ctrl+C"));
697     copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
698     copyAction->setObjectName("Copy action");
699     connect(copyAction, SIGNAL(triggered()), SLOT(sl_copyItems()));
700     addAction(copyAction);
701 
702     cutAction = new QAction(tr("Cu&t"), sceneView);
703     cutAction->setIcon(QIcon(":workflow_designer/images/editcut.png"));
704     cutAction->setShortcuts(QKeySequence::Cut);
705     cutAction->setShortcutContext(Qt::WidgetShortcut);
706     connect(cutAction, SIGNAL(triggered()), SLOT(sl_cutItems()));
707     addAction(cutAction);
708 
709     pasteAction = new QAction(tr("&Paste"), this);
710     pasteAction->setIcon(QIcon(":workflow_designer/images/editpaste.png"));
711     pasteAction->setShortcuts(QKeySequence::Paste);
712     pasteAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
713     connect(pasteAction, SIGNAL(triggered()), SLOT(sl_pasteItems()));
714     addAction(pasteAction);
715 
716     {  // style
717         QAction *simpleStyle = new QAction(tr("Minimal"), this);
718         simpleStyle->setObjectName("Minimal");
719         simpleStyle->setData(QVariant(ItemStyles::SIMPLE));
720         styleActions << simpleStyle;
721         connect(simpleStyle, SIGNAL(triggered()), SLOT(sl_setStyle()));
722 
723         QAction *extStyle = new QAction(tr("Extended"), this);
724         extStyle->setObjectName("Extended");
725         extStyle->setData(QVariant(ItemStyles::EXTENDED));
726         styleActions << extStyle;
727         connect(extStyle, SIGNAL(triggered()), SLOT(sl_setStyle()));
728     }
729 
730     {  // scripting mode
731         QAction *notShowScriptAction = new QAction(tr("Hide scripting options"), this);
732         notShowScriptAction->setObjectName("Hide scripting options");
733         notShowScriptAction->setCheckable(true);
734         scriptingActions << notShowScriptAction;
735         notShowScriptAction->setChecked(!scriptingMode);
736         connect(notShowScriptAction, SIGNAL(triggered()), SLOT(sl_changeScriptMode()));
737 
738         QAction *showScriptAction = new QAction(tr("Show scripting options"), this);
739         showScriptAction->setObjectName("Show scripting options");
740         showScriptAction->setCheckable(true);
741         showScriptAction->setChecked(scriptingMode);
742         scriptingActions << showScriptAction;
743         connect(showScriptAction, SIGNAL(triggered()), SLOT(sl_changeScriptMode()));
744     }
745 
746     unlockAction = new QAction(tr("Unlock Scene"), this);
747     unlockAction->setCheckable(true);
748     unlockAction->setChecked(true);
749     connect(unlockAction, SIGNAL(toggled(bool)), SLOT(sl_toggleLock(bool)));
750 
751     createScriptAction = new QAction(tr("Create element with script..."), this);
752     createScriptAction->setObjectName("createScriptAction");
753     createScriptAction->setIcon(QIcon(":workflow_designer/images/script.png"));
754     connect(createScriptAction, SIGNAL(triggered()), SLOT(sl_createScript()));
755 
756     editScriptAction = new QAction(tr("Edit script of the element..."), this);
757     editScriptAction->setObjectName("editScriptAction");
758     editScriptAction->setIcon(QIcon(":workflow_designer/images/script_edit.png"));
759     editScriptAction->setEnabled(false);  // because user need to select actor with script to enable it
760     connect(editScriptAction, SIGNAL(triggered()), SLOT(sl_editScript()));
761 
762     createCmdlineBasedWorkerAction = new QAction(tr("Create element with external tool..."), this);
763     createCmdlineBasedWorkerAction->setObjectName("createElementWithCommandLineTool");
764     createCmdlineBasedWorkerAction->setIcon(QIcon(":workflow_designer/images/external_cmd_tool.png"));
765     connect(createCmdlineBasedWorkerAction, SIGNAL(triggered()), SLOT(sl_createCmdlineBasedWorkerAction()));
766 
767     editExternalToolAction = new QAction(tr("Edit configuration..."), this);
768     editExternalToolAction->setObjectName("editConfiguration");
769     editExternalToolAction->setIcon(QIcon(":workflow_designer/images/external_cmd_tool.png"));
770     editExternalToolAction->setEnabled(false);  // because user need to select actor with script to enable it
771     connect(editExternalToolAction, SIGNAL(triggered()), SLOT(sl_editExternalTool()));
772 
773     appendExternalTool = new QAction(tr("Add element with external tool..."), this);
774     appendExternalTool->setObjectName("AddElementWithCommandLineTool");
775     appendExternalTool->setIcon(QIcon(":workflow_designer/images/external_cmd_tool_add.png"));
776     connect(appendExternalTool, SIGNAL(triggered()), SLOT(sl_appendExternalToolWorker()));
777 
778     findPrototypeAction = new QAction(this);
779     findPrototypeAction->setShortcut(QKeySequence::Find);
780     connect(findPrototypeAction, SIGNAL(triggered()), SLOT(sl_findPrototype()));
781     this->addAction(findPrototypeAction);
782 
783     foreach (QAction *action, debugActions) {
784         action->setVisible(false);
785     }
786 
787     scaleComboBox = scaleCombo(this);
788 }
789 
sl_findPrototype()790 void WorkflowView::sl_findPrototype() {
791     tabs->currentWidget()->setFocus();
792     CHECK(tabs->currentWidget() == palette, );
793 
794     static const int MIN_SIZE_FIND = 260;
795     QList<int> sizes = splitter->sizes();
796     int idx = splitter->indexOf(tabs);
797     CHECK(idx >= 0 && idx < sizes.size(), );
798     if (sizes.at(idx) < MIN_SIZE_FIND / 2) {
799         sizes[idx] = MIN_SIZE_FIND;
800         splitter->setSizes(sizes);
801     }
802 }
803 
sl_createScript()804 void WorkflowView::sl_createScript() {
805     QObjectScopedPointer<CreateScriptElementDialog> dlg = new CreateScriptElementDialog(this);
806     dlg->exec();
807     CHECK(!dlg.isNull(), );
808 
809     if (dlg->result() == QDialog::Accepted) {
810         QList<DataTypePtr> input = dlg->getInput();
811         QList<DataTypePtr> output = dlg->getOutput();
812         QList<Attribute *> attrs = dlg->getAttributes();
813         QString name = dlg->getName();
814         QString desc = dlg->getDescription();
815         if (LocalWorkflow::ScriptWorkerFactory::init(input, output, attrs, name, desc, dlg->getActorFilePath())) {
816             ActorPrototype *proto = WorkflowEnv::getProtoRegistry()->getProto(LocalWorkflow::ScriptWorkerFactory::ACTOR_ID + name);
817             QRectF rect = scene->sceneRect();
818             addProcess(createActor(proto, QVariantMap()), rect.center());
819         }
820     }
821 }
822 
sl_createCmdlineBasedWorkerAction()823 void WorkflowView::sl_createCmdlineBasedWorkerAction() {
824     QString id = palette->createPrototype();
825     CHECK(!id.isEmpty(), );
826 
827     ActorPrototype *proto = WorkflowEnv::getProtoRegistry()->getProto(id);
828     QRectF rect = scene->sceneRect();
829     addProcess(createActor(proto, QVariantMap()), rect.center());
830 }
831 
832 namespace {
copyIntoUgene(const QString & url,U2OpStatus & os)833 QString copyIntoUgene(const QString &url, U2OpStatus &os) {
834     QDir dir(WorkflowSettings::getExternalToolDirectory());
835     if (!dir.exists()) {
836         bool created = dir.mkpath(dir.absolutePath());
837         if (!created) {
838             os.setError(QObject::tr("Can not create the folder: ") + dir.absolutePath());
839             return "";
840         }
841     }
842     QString filePath = GUrlUtils::rollFileName(dir.absolutePath() + "/" + QFileInfo(url).fileName(), "_");
843     if (QFile::exists(filePath)) {
844         os.setError(QObject::tr("The file '%1' already exists").arg(filePath));
845         return "";
846     }
847     bool copied = QFile::copy(url, filePath);
848     if (!copied) {
849         os.setError(QObject::tr("Can not copy the file here: ") + filePath);
850         return "";
851     }
852     return filePath;
853 }
854 }  // namespace
855 
sl_appendExternalToolWorker()856 void WorkflowView::sl_appendExternalToolWorker() {
857     QString filter = DialogUtils::prepareFileFilter(WorkflowUtils::tr("UGENE workflow element"), QStringList() << "etc", true);
858     QString url = U2FileDialog::getOpenFileName(this, tr("Add element"), QString(), filter);
859     if (!url.isEmpty()) {
860         IOAdapter *io = AppContext::getIOAdapterRegistry()->getIOAdapterFactoryById(IOAdapterUtils::url2io(GUrl(url)))->createIOAdapter();
861         if (!io->open(url, IOAdapterMode_Read)) {
862             coreLog.error(tr("Can't load element."));
863             return;
864         }
865         QByteArray data;
866         data.resize(MAX_FILE_SIZE);
867         data.fill(0);
868         io->readBlock(data.data(), MAX_FILE_SIZE);
869         io->close();
870 
871         QScopedPointer<ExternalProcessConfig> cfg(HRSchemaSerializer::string2Actor(data.data()));
872         if (cfg.data()) {
873             if (WorkflowEnv::getProtoRegistry()->getProto(cfg->id)) {
874                 coreLog.error(QString("Element with ID '%1' already exists").arg(cfg->id));
875             } else {
876                 U2OpStatus2Log os;
877                 QString internalUrl = copyIntoUgene(url, os);
878                 CHECK_OP(os, );
879                 cfg->filePath = internalUrl;
880                 if (LocalWorkflow::ExternalProcessWorkerFactory::init(cfg.data())) {
881                     ActorPrototype *proto = WorkflowEnv::getProtoRegistry()->getProto(cfg->id);
882                     QRectF rect = scene->sceneRect();
883                     addProcess(createActor(proto, QVariantMap()), rect.center());
884                     cfg.take();
885                 } else {
886                     coreLog.error(tr("Can't register element."));
887                 }
888             }
889         } else {
890             coreLog.error(tr("Can't load element."));
891         }
892     }
893 }
894 
sl_editScript()895 void WorkflowView::sl_editScript() {
896     QList<Actor *> selectedActors = scene->getSelectedActors();
897     if (selectedActors.size() == 1) {
898         Actor *scriptActor = selectedActors.first();
899         AttributeScript *script = scriptActor->getScript();
900         if (script != nullptr) {
901             QObjectScopedPointer<ScriptEditorDialog> scriptDlg = new ScriptEditorDialog(this, AttributeScriptDelegate::createScriptHeader(*script), script->getScriptText());
902             scriptDlg->exec();
903             CHECK(!scriptDlg.isNull(), );
904 
905             if (scriptDlg->result() == QDialog::Accepted) {
906                 script->setScriptText(scriptDlg->getScriptText());
907                 scriptActor->setScript(script);
908             }
909         }
910     }
911 }
912 
sl_editExternalTool()913 void WorkflowView::sl_editExternalTool() {
914     QList<Actor *> selectedActors = scene->getSelectedActors();
915     if (selectedActors.size() == 1) {
916         ActorPrototype *proto = selectedActors.first()->getProto();
917         const bool edited = palette->editPrototype(proto);
918         if (edited) {
919             scene->sl_updateDocs();
920         }
921     }
922 }
923 
sl_prototypeIsAboutToBeRemoved(Workflow::ActorPrototype * proto)924 void WorkflowView::sl_prototypeIsAboutToBeRemoved(Workflow::ActorPrototype *proto) {
925     if (currentProto == proto) {
926         currentProto = nullptr;
927     }
928 
929     QList<WorkflowProcessItem *> deleteList;
930     foreach (QGraphicsItem *i, scene->items()) {
931         if (i->type() == WorkflowProcessItemType) {
932             WorkflowProcessItem *wItem = static_cast<WorkflowProcessItem *>(i);
933             if (wItem->getProcess()->getProto()->getId() == proto->getId()) {
934                 deleteList << wItem;
935             }
936         }
937     }
938 
939     foreach (WorkflowProcessItem *item, deleteList) {
940         removeProcessItem(item);
941     }
942     scene->update();
943 }
944 
sl_protoListModified()945 void WorkflowView::sl_protoListModified() {
946     CHECK(nullptr != elementsMenu, );
947     palette->createMenu(elementsMenu);
948 }
949 
addProcess(Actor * proc,const QPointF & pos)950 void WorkflowView::addProcess(Actor *proc, const QPointF &pos) {
951     schema->addProcess(proc);
952     removeEstimations();
953 
954     WorkflowProcessItem *it = new WorkflowProcessItem(proc);
955     it->setPos(pos);
956     scene->addItem(it);
957     scene->setModified();
958 
959     ConfigurationEditor *editor = proc->getEditor();
960     if (nullptr != editor) {
961         connect(editor, SIGNAL(si_configurationChanged()), scene, SIGNAL(configurationChanged()));
962     }
963     procItemAdded();
964     ActorPrototype *addedProto = it->getProcess()->getProto();
965     uiLog.trace(addedProto->getDisplayName() + " added");
966     if (WorkflowEnv::getExternalCfgRegistry()->getConfigById(addedProto->getId()) != nullptr) {
967         GCOUNTER(cvar, "Element with external tool is added to the scene");
968     }
969 
970     update();
971 }
972 
removeProcessItem(WorkflowProcessItem * item)973 void WorkflowView::removeProcessItem(WorkflowProcessItem *item) {
974     CHECK(nullptr != item, );
975     Actor *actor = item->getProcess();
976     scene->removeItem(item);
977     delete item;
978 
979     scene->setModified();
980     schema->removeProcess(actor);
981     meta.removeActorMeta(actor->getId());
982     delete actor;
983 
984     removeWizards();
985     removeEstimations();
986 }
987 
removeWizards()988 void WorkflowView::removeWizards() {
989     qDeleteAll(schema->takeWizards());
990     sl_updateUi();
991 }
992 
removeEstimations()993 void WorkflowView::removeEstimations() {
994     meta.estimationsCode.clear();
995     sl_updateUi();
996 }
997 
removeBusItem(WorkflowBusItem * item)998 void WorkflowView::removeBusItem(WorkflowBusItem *item) {
999     Link *link = item->getBus();
1000     scene->removeItem(item);
1001     delete item;
1002     removeEstimations();
1003     scene->setModified();
1004     onBusRemoved(link);
1005 }
1006 
onBusRemoved(Link * link)1007 void WorkflowView::onBusRemoved(Link *link) {
1008     if (!sceneRecreation) {
1009         schema->removeFlow(link);
1010         schema->update();
1011     }
1012 }
1013 
sl_toggleLock(bool b)1014 void WorkflowView::sl_toggleLock(bool b) {
1015     running = !b;
1016     if (sender() != unlockAction) {
1017         unlockAction->setChecked(!running);
1018         breakpointView->setEnabled(b);
1019         toggleDebugActionsState(!b);
1020         investigationWidgets->deleteBusInvestigations();
1021         investigationWidgets->resetInvestigations();
1022         return;
1023     }
1024 
1025     if (!running) {
1026         scene->setRunner(nullptr);
1027     }
1028 
1029     newAction->setEnabled(!running);
1030     loadAction->setEnabled(!running);
1031     deleteAction->setEnabled(!running);
1032     deleteShortcut->setEnabled(!running);
1033     selectAction->setEnabled(!running);
1034     copyAction->setEnabled(!running);
1035     pasteAction->setEnabled(!running);
1036     cutAction->setEnabled(!running);
1037 
1038     stopAction->setEnabled(running);
1039     runAction->setEnabled(!running);
1040     validateAction->setEnabled(!running);
1041     estimateAction->setEnabled(!running);
1042     configureParameterAliasesAction->setEnabled(!running);
1043     configurePortAliasesAction->setEnabled(!running);
1044     importSchemaToElement->setEnabled(!running);
1045 
1046     propertyEditor->setEnabled(!running);
1047     propertyEditor->setSpecialPanelEnabled(!running);
1048     palette->setEnabled(!running);
1049     toggleDebugActionsState(!b);
1050     breakpointView->setEnabled(b);
1051     samples->setEnabled(!running);
1052 
1053     setupActions();
1054     scene->setLocked(running);
1055     scene->update();
1056 }
1057 
sl_setStyle()1058 void WorkflowView::sl_setStyle() {
1059     StyleId s = qobject_cast<QAction *>(sender())->data().value<StyleId>();
1060     QList<QGraphicsItem *> lst = scene->selectedItems();
1061     if (lst.isEmpty()) {
1062         lst = scene->items();
1063     }
1064     foreach (QGraphicsItem *it, lst) {
1065         switch (it->type()) {
1066             case WorkflowProcessItemType:
1067             case WorkflowPortItemType:
1068             case WorkflowBusItemType:
1069                 (static_cast<StyledItem *>(it))->setStyle(s);
1070         }
1071     }
1072     scene->update();
1073 }
1074 
sl_changeScriptMode()1075 void WorkflowView::sl_changeScriptMode() {
1076     QAction *a = qobject_cast<QAction *>(sender());
1077     if (a != nullptr) {
1078         if (a == scriptingActions[0]) {
1079             scriptingMode = false;
1080         } else if (a == scriptingActions[1]) {
1081             scriptingMode = true;
1082         }
1083     }  // else invoked from constructor
1084 
1085     scriptingActions[0]->setChecked(!scriptingMode);
1086     scriptingActions[1]->setChecked(scriptingMode);
1087     propertyEditor->changeScriptMode(scriptingMode);
1088 }
1089 
sl_toggleStyle()1090 void WorkflowView::sl_toggleStyle() {
1091     foreach (QGraphicsItem *it, scene->selectedItems()) {
1092         StyleId s = (static_cast<StyledItem *>(it))->getStyle();
1093         if (s == ItemStyles::SIMPLE) {
1094             s = ItemStyles::EXTENDED;
1095         } else {
1096             s = ItemStyles::SIMPLE;
1097         }
1098         (static_cast<StyledItem *>(it))->setStyle(s);
1099     }
1100     scene->update();
1101 }
1102 
sl_refreshActorDocs()1103 void WorkflowView::sl_refreshActorDocs() {
1104     foreach (QGraphicsItem *it, scene->items()) {
1105         if (it->type() == WorkflowProcessItemType) {
1106             Actor *a = qgraphicsitem_cast<WorkflowProcessItem *>(it)->getProcess();
1107             a->getDescription()->update(a->getValues());
1108         }
1109     }
1110 }
1111 
setupMDIToolbar(QToolBar * tb)1112 void WorkflowView::setupMDIToolbar(QToolBar *tb) {
1113     tb->addAction(newAction);
1114     tb->addAction(loadAction);
1115     tb->addAction(saveAction);
1116     tb->addAction(saveAsAction);
1117     loadSep = tb->addSeparator();
1118     tb->addAction(showWizard);
1119     tb->addAction(validateAction);
1120     tb->addAction(estimateAction);
1121     tb->addAction(runAction);
1122     tb->addAction(pauseAction);
1123     tb->addAction(toggleBreakpointAction);
1124     tb->addAction(toggleBreakpointManager);
1125     tb->addAction(nextStepAction);
1126     tb->addAction(tickReadyAction);
1127     tb->addAction(stopAction);
1128     runSep = tb->addSeparator();
1129     tb->addAction(configureParameterAliasesAction);
1130     confSep = tb->addSeparator();
1131     tb->addAction(createCmdlineBasedWorkerAction);
1132     tb->addAction(appendExternalTool);
1133     extSep = tb->addSeparator();
1134     tb->addAction(deleteAction);
1135     scaleSep = tb->addSeparator();
1136     scaleAction = tb->addWidget(scaleComboBox);
1137     scaleSep = tb->addSeparator();
1138     scriptAction = tb->addWidget(scriptMenu(this, scriptingActions));
1139     tb->addAction(dmAction);
1140 
1141     addToggleDashboardAction(tb, toggleDashboard);
1142 
1143     sl_dashboardCountChanged();
1144     setDashboardActionDecoration(tabView->isVisible());
1145     setupActions();
1146 }
1147 
setupActions()1148 void WorkflowView::setupActions() {
1149     bool dashboard = tabView->isVisible();
1150     bool editMode = !running && !dashboard;
1151 
1152     newAction->setVisible(!dashboard);
1153     loadAction->setVisible(!dashboard);
1154     saveAction->setVisible(!dashboard);
1155     saveAsAction->setVisible(!dashboard);
1156     loadSep->setVisible(!dashboard);
1157 
1158     showWizard->setVisible(editMode && !schema->getWizards().isEmpty());
1159     validateAction->setVisible(editMode);
1160     estimateAction->setVisible(editMode && !meta.estimationsCode.isEmpty());
1161     stopAction->setVisible(running);
1162     runSep->setVisible(editMode);
1163 
1164     configureParameterAliasesAction->setVisible(editMode);
1165     confSep->setVisible(editMode);
1166 
1167     createScriptAction->setVisible(editMode);
1168     editScriptAction->setVisible(editMode);
1169 
1170     createCmdlineBasedWorkerAction->setVisible(editMode);
1171     appendExternalTool->setVisible(editMode);
1172     extSep->setVisible(editMode);
1173 
1174     copyAction->setVisible(editMode);
1175     pasteAction->setVisible(editMode);
1176     cutAction->setVisible(editMode);
1177     deleteAction->setVisible(editMode);
1178 
1179     scaleAction->setVisible(!dashboard);
1180     scaleSep->setVisible(!dashboard);
1181 
1182     scriptAction->setVisible(editMode);
1183 }
1184 
setupViewMenu(QMenu * m)1185 void WorkflowView::setupViewMenu(QMenu *m) {
1186     elementsMenu = palette->createMenu(tr("Add element"));
1187     m->addMenu(elementsMenu);
1188     m->addAction(copyAction);
1189     m->addAction(pasteAction);
1190     pasteAction->setEnabled(!lastPaste.isEmpty());
1191     m->addAction(cutAction);
1192     m->addAction(deleteAction);
1193     m->addAction(selectAction);
1194     m->addSeparator();
1195     m->addAction(newAction);
1196     m->addAction(loadAction);
1197     m->addAction(saveAction);
1198     m->addAction(saveAsAction);
1199     m->addAction(exportAction);
1200     m->addSeparator();
1201     m->addAction(validateAction);
1202     m->addAction(estimateAction);
1203     m->addAction(runAction);
1204     m->addAction(stopAction);
1205     m->addSeparator();
1206     m->addAction(configureParameterAliasesAction);
1207     m->addAction(createGalaxyConfigAction);
1208     m->addAction(configurePortAliasesAction);
1209     m->addAction(importSchemaToElement);
1210     m->addSeparator();
1211     m->addAction(createScriptAction);
1212     m->addAction(editScriptAction);
1213     m->addSeparator();
1214     m->addAction(createCmdlineBasedWorkerAction);
1215     m->addAction(appendExternalTool);
1216     m->addSeparator();
1217 
1218     QMenu *ttMenu = new QMenu(tr("Element style"));
1219     foreach (QAction *a, styleActions) {
1220         ttMenu->addAction(a);
1221     }
1222     m->addMenu(ttMenu);
1223 
1224     QMenu *scriptMenu = new QMenu(tr("Scripting mode"));
1225     foreach (QAction *a, scriptingActions) {
1226         scriptMenu->addAction(a);
1227     }
1228     m->addMenu(scriptMenu);
1229 
1230     if (!unlockAction->isChecked()) {
1231         m->addSeparator();
1232         m->addAction(unlockAction);
1233     }
1234     m->addSeparator();
1235     m->addAction(dmAction);
1236 }
1237 
setupContextMenu(QMenu * m)1238 void WorkflowView::setupContextMenu(QMenu *m) {
1239     if (!debugInfo->isPaused()) {
1240         if (!unlockAction->isChecked()) {
1241             return;
1242         }
1243 
1244         if (!lastPaste.isEmpty()) {
1245             m->addAction(pasteAction);
1246         }
1247         QList<QGraphicsItem *> sel = scene->selectedItems();
1248         if (!sel.isEmpty()) {
1249             if (!((sel.size() == 1 && sel.first()->type() == WorkflowBusItemType) || sel.first()->type() == WorkflowPortItemType)) {
1250                 m->addAction(copyAction);
1251                 m->addAction(cutAction);
1252             }
1253             if (!(sel.size() == 1 && sel.first()->type() == WorkflowPortItemType)) {
1254                 m->addAction(deleteAction);
1255             }
1256             m->addSeparator();
1257             if (sel.size() == 1 && sel.first()->type() == WorkflowProcessItemType) {
1258                 WorkflowProcessItem *wit = qgraphicsitem_cast<WorkflowProcessItem *>(sel.first());
1259                 Actor *scriptActor = wit->getProcess();
1260                 AttributeScript *script = scriptActor->getScript();
1261                 if (script) {
1262                     m->addAction(editScriptAction);
1263                 }
1264 
1265                 ActorPrototype *p = scriptActor->getProto();
1266                 if (p->isExternalTool()) {
1267                     m->addAction(editExternalToolAction);
1268                 }
1269 
1270                 m->addSeparator();
1271 
1272                 QMenu *itMenu = new QMenu(tr("Element properties"));
1273                 foreach (QAction *a, wit->getContextMenuActions()) {
1274                     itMenu->addAction(a);
1275                 }
1276                 m->addMenu(itMenu);
1277             }
1278             if (!(sel.size() == 1 && (sel.first()->type() == WorkflowBusItemType || sel.first()->type() == WorkflowPortItemType))) {
1279                 QMenu *ttMenu = new QMenu(tr("Element style"));
1280                 foreach (QAction *a, styleActions) {
1281                     ttMenu->addAction(a);
1282                 }
1283                 m->addMenu(ttMenu);
1284             }
1285         }
1286         m->addSeparator();
1287 
1288         m->addAction(selectAction);
1289         m->addMenu(palette->createMenu(tr("Add element")));
1290     }
1291 
1292     foreach (QGraphicsItem *item, scene->selectedItems()) {
1293         if (WorkflowProcessItemType == item->type()) {
1294             m->addAction(toggleBreakpointAction);
1295             break;
1296         }
1297     }
1298 }
1299 
sl_pickInfo(QListWidgetItem * info)1300 void WorkflowView::sl_pickInfo(QListWidgetItem *info) {
1301     ActorId id = info->data(ACTOR_ID_REF).value<ActorId>();
1302     foreach (QGraphicsItem *it, scene->items()) {
1303         if (it->type() == WorkflowProcessItemType) {
1304             WorkflowProcessItem *proc = static_cast<WorkflowProcessItem *>(it);
1305             if (proc->getProcess()->getId() != id) {
1306                 continue;
1307             }
1308             scene->clearSelection();
1309             QString pid = info->data(PORT_REF).toString();
1310             WorkflowPortItem *port = proc->getPort(pid);
1311             if (port) {
1312                 port->setSelected(true);
1313             } else {
1314                 proc->setSelected(true);
1315             }
1316             return;
1317         }
1318     }
1319 }
1320 
sl_validate(bool notify)1321 bool WorkflowView::sl_validate(bool notify) {
1322     if (schema->getProcesses().isEmpty()) {
1323         QMessageBox::warning(this, tr("Empty workflow!"), tr("Nothing to run: empty workflow"));
1324         return false;
1325     }
1326 
1327     propertyEditor->commit();
1328     infoList->clear();
1329     QList<QListWidgetItem *> lst;
1330     bool good = WorkflowUtils::validate(*schema, lst);
1331 
1332     if (lst.count() != 0) {
1333         foreach (QListWidgetItem *wi, lst) {
1334             infoList->addItem(wi);
1335         }
1336         bottomTabs->show();
1337         bottomTabs->setCurrentWidget(infoList->parentWidget());
1338         infoList->parentWidget()->show();
1339         QList<int> s = infoSplitter->sizes();
1340         if (s[s.size() - 1] == 0) {
1341             s[s.size() - 1] = qMin(infoList->sizeHint().height(), 300);
1342             infoSplitter->setSizes(s);
1343         }
1344     } else if (bottomTabs->currentWidget() == infoList->parentWidget()) {
1345         bottomTabs->hide();
1346     }
1347     if (!good) {
1348         QMessageBox::warning(this, tr("Workflow cannot be executed"), tr("Please fix issues listed in the error list (located under workflow)."));
1349     } else {
1350         if (notify) {
1351             QString message = tr("Workflow is valid. \n");
1352             if (lst.isEmpty()) {
1353                 message += tr("Well done!");
1354             } else {
1355                 message += tr("There are non-critical warnings.");
1356             }
1357             QMessageBox::information(this, tr("Workflow is valid"), message);
1358         }
1359     }
1360     return good;
1361 }
1362 
sl_estimate()1363 void WorkflowView::sl_estimate() {
1364     CHECK(sl_validate(false /*don't notify*/), );
1365     SAFE_POINT(!meta.estimationsCode.isEmpty(), "No estimation code", );
1366     estimateAction->setEnabled(false);
1367 
1368     SchemaEstimationTask *t = new SchemaEstimationTask(schema, &meta);
1369     connect(t, SIGNAL(si_stateChanged()), SLOT(sl_estimationTaskFinished()));
1370     AppContext::getTaskScheduler()->registerTopLevelTask(t);
1371 }
1372 
sl_estimationTaskFinished()1373 void WorkflowView::sl_estimationTaskFinished() {
1374     SchemaEstimationTask *t = dynamic_cast<SchemaEstimationTask *>(sender());
1375     CHECK(nullptr != t, );
1376     CHECK(t->isFinished(), );
1377     estimateAction->setEnabled(true);
1378     CHECK(!t->hasError(), );
1379     QMessageBox *d = EstimationReporter::createTimeMessage(t->result());
1380     QPushButton *rb = d->addButton(QObject::tr("Run workflow"), QMessageBox::AcceptRole);
1381     rb->setObjectName("Run workflow");
1382     connect(rb, SIGNAL(clicked()), SLOT(sl_launch()));
1383     d->setParent(this);
1384     d->setWindowModality(Qt::ApplicationModal);
1385     d->show();
1386 }
1387 
localHostLaunch()1388 void WorkflowView::localHostLaunch() {
1389     if (!sl_validate(false)) {
1390         return;
1391     }
1392 
1393     if (schema->getDomain().isEmpty()) {
1394         // TODO: user choice
1395         schema->setDomain(WorkflowEnv::getDomainRegistry()->getAllIds().value(0));
1396     }
1397 
1398     if (meta.isSample()) {
1399         GCounter::increment(meta.name, "WDSample:run");
1400     }
1401 
1402     foreach (const Actor *actor, schema->getProcesses()) {
1403         if (WorkflowEnv::getExternalCfgRegistry()->getConfigById(actor->getId()) != nullptr) {
1404             GCOUNTER(cvar, "Element(s) with command line tool is present in the launched workflow");
1405             break;
1406         }
1407     }
1408     debugInfo->setMessageParser(new WorkflowDebugMessageParserImpl());
1409     WorkflowAbstractRunner *t = new WorkflowRunTask(*schema, ActorMap(), debugInfo);
1410 
1411     t->setReportingEnabled(true);
1412     if (WorkflowSettings::monitorRun()) {
1413         commitWarningsToMonitor(t);
1414         unlockAction->setChecked(false);
1415         scene->setRunner(t);
1416         connect(t, SIGNAL(si_ticked()), scene, SLOT(update()));
1417         TaskSignalMapper *signalMapper = new TaskSignalMapper(t);
1418         connect(signalMapper, SIGNAL(si_taskFinished(Task *)), debugInfo, SLOT(sl_executionFinished()));
1419         connect(signalMapper, SIGNAL(si_taskFinished(Task *)), SLOT(sl_toggleLock()));
1420     }
1421     AppContext::getTaskScheduler()->registerTopLevelTask(t);
1422     foreach (WorkflowMonitor *m, t->getMonitors()) {
1423         m->setSaveSchema(meta);
1424         tabView->addDashboard(m, meta.name);
1425         showDashboards();
1426     }
1427 }
1428 
sl_launch()1429 void WorkflowView::sl_launch() {
1430     if (!debugInfo->isPaused()) {
1431         localHostLaunch();
1432         if (nullptr != scene->getRunner()) {
1433             stopAction->setEnabled(true);
1434             pauseAction->setEnabled(true);
1435             propertyEditor->setEnabled(false);
1436             toggleDebugActionsState(true);
1437         }
1438     }
1439 }
1440 
sl_pause(bool isPause)1441 void WorkflowView::sl_pause(bool isPause) {
1442     pauseAction->setEnabled(!isPause);
1443     runAction->setEnabled(isPause);
1444     nextStepAction->setEnabled(isPause);
1445     propertyEditor->setEnabled(isPause);
1446     scene->setLocked(!isPause);
1447     breakpointView->setEnabled(isPause);
1448     investigationWidgets->setInvestigationWidgetsVisible(isPause);
1449     WorkflowAbstractRunner *runningWorkflow = scene->getRunner();
1450     if (nullptr != runningWorkflow && runningWorkflow->isRunning()) {
1451         foreach (WorkflowMonitor *m, runningWorkflow->getMonitors()) {
1452             if (isPause) {
1453                 m->pause();
1454             } else {
1455                 m->resume();
1456             }
1457         }
1458     }
1459     if (isPause && tabView->isVisible()) {
1460         hideDashboards();
1461     }
1462 }
1463 
sl_stop()1464 void WorkflowView::sl_stop() {
1465     Task *runningWorkflow = scene->getRunner();
1466     if (nullptr != runningWorkflow) {
1467         runningWorkflow->cancel();
1468     }
1469     investigationWidgets->resetInvestigations();
1470 }
1471 
toggleDebugActionsState(bool enable)1472 void WorkflowView::toggleDebugActionsState(bool enable) {
1473     if (WorkflowSettings::isDebuggerEnabled()) {
1474         foreach (QAction *action, debugActions) {
1475             action->setVisible(enable);
1476         }
1477     }
1478 }
1479 
propagateBreakpointToSceneItem(ActorId actor)1480 void WorkflowView::propagateBreakpointToSceneItem(ActorId actor) {
1481     WorkflowProcessItem *processItem = findItemById(actor);
1482     Q_ASSERT(processItem->isBreakpointInserted());
1483     processItem->highlightItem();
1484 }
1485 
sl_breakpointAdded(const ActorId & actor)1486 void WorkflowView::sl_breakpointAdded(const ActorId &actor) {
1487     changeBreakpointState(actor, true);
1488 }
1489 
sl_breakpointRemoved(const ActorId & actor)1490 void WorkflowView::sl_breakpointRemoved(const ActorId &actor) {
1491     changeBreakpointState(actor, false);
1492 }
1493 
sl_breakpointEnabled(const ActorId & actor)1494 void WorkflowView::sl_breakpointEnabled(const ActorId &actor) {
1495     changeBreakpointState(actor, false, true);
1496 }
1497 
sl_breakpointDisabled(const ActorId & actor)1498 void WorkflowView::sl_breakpointDisabled(const ActorId &actor) {
1499     changeBreakpointState(actor, false, true);
1500 }
1501 
changeBreakpointState(const ActorId & actor,bool isBreakpointBeingAdded,bool isBreakpointStateBeingChanged)1502 void WorkflowView::changeBreakpointState(const ActorId &actor, bool isBreakpointBeingAdded, bool isBreakpointStateBeingChanged) {
1503     WorkflowProcessItem *processItem = findItemById(actor);
1504     Q_ASSERT(nullptr != processItem);
1505 
1506     if (processItem->isBreakpointInserted()) {
1507         if (!isBreakpointBeingAdded) {
1508             if (!isBreakpointStateBeingChanged) {
1509                 processItem->toggleBreakpoint();
1510             } else {
1511                 processItem->toggleBreakpointState();
1512             }
1513         }
1514     } else {
1515         if (isBreakpointBeingAdded) {
1516             if (!isBreakpointStateBeingChanged) {
1517                 processItem->toggleBreakpoint();
1518             } else {
1519                 Q_ASSERT(false);
1520             }
1521         }
1522     }
1523     scene->update();
1524 }
1525 
sl_toggleBreakpointManager()1526 void WorkflowView::sl_toggleBreakpointManager() {
1527     if (!breakpointView->isVisible()) {
1528         bottomTabs->setVisible(true);
1529         bottomTabs->setCurrentWidget(breakpointView);
1530     } else {
1531         bottomTabs->hide();
1532     }
1533 }
1534 
sl_highlightingRequested(const ActorId & actor)1535 void WorkflowView::sl_highlightingRequested(const ActorId &actor) {
1536     findItemById(actor)->highlightItem();
1537 }
1538 
sl_processOneMessage()1539 void WorkflowView::sl_processOneMessage() {
1540     Q_ASSERT(debugInfo->isPaused());
1541     QList<QGraphicsItem *> selectedItems = scene->selectedItems();
1542     Q_ASSERT(1 == selectedItems.size());
1543     WorkflowProcessItem *processItem = qgraphicsitem_cast<WorkflowProcessItem *>(selectedItems.first());
1544     debugInfo->requestForSingleStep(processItem->getProcess()->getId());
1545 }
1546 
sl_convertMessages2Documents(const Workflow::Link * bus,const QString & messageType,int messageNumber)1547 void WorkflowView::sl_convertMessages2Documents(const Workflow::Link *bus,
1548                                                 const QString &messageType,
1549                                                 int messageNumber) {
1550     debugInfo->convertMessagesToDocuments(bus, messageType, messageNumber, meta.name);
1551 }
1552 
findItemById(ActorId actor) const1553 WorkflowProcessItem *WorkflowView::findItemById(ActorId actor) const {
1554     foreach (QGraphicsItem *item, scene->items()) {
1555         if (WorkflowProcessItemType == item->type()) {
1556             WorkflowProcessItem *processItem = qgraphicsitem_cast<WorkflowProcessItem *>(item);
1557             Q_ASSERT(nullptr != processItem);
1558             if (actor == processItem->getProcess()->getId()) {
1559                 return processItem;
1560             }
1561         }
1562     }
1563     return nullptr;
1564 }
1565 
paintEvent(QPaintEvent * event)1566 void WorkflowView::paintEvent(QPaintEvent *event) {
1567     const bool isWorkflowRunning = (nullptr != scene->getRunner());
1568     const bool isDebuggerEnabled = WorkflowSettings::isDebuggerEnabled();
1569     if (isDebuggerEnabled && ABSENT_WIDGET_TAB_NUMBER == bottomTabs->indexOf(breakpointView)) {
1570         bottomTabs->addTab(breakpointView, QObject::tr("Breakpoints"));
1571     } else if (!isDebuggerEnabled && ABSENT_WIDGET_TAB_NUMBER != bottomTabs->indexOf(breakpointView)) {
1572         breakpointView->sl_deleteAllBreakpoints();
1573         bottomTabs->removeTab(bottomTabs->indexOf(breakpointView));
1574     }
1575     foreach (QAction *action, debugActions) {
1576         action->setVisible(WorkflowSettings::isDebuggerEnabled() && isWorkflowRunning);
1577     }
1578     toggleBreakpointAction->setVisible(isDebuggerEnabled);
1579     toggleBreakpointManager->setVisible(isDebuggerEnabled);
1580 
1581     if (isWorkflowRunning) {
1582         if (debugInfo->isPaused()) {
1583             sl_onSelectionChanged();
1584         } else {
1585             tickReadyAction->setEnabled(false);
1586         }
1587     }
1588     MWMDIWindow::paintEvent(event);
1589 }
sl_configureParameterAliases()1590 void WorkflowView::sl_configureParameterAliases() {
1591     QObjectScopedPointer<SchemaAliasesConfigurationDialogImpl> dlg = new SchemaAliasesConfigurationDialogImpl(*schema, this);
1592     int ret = QDialog::Accepted;
1593     do {
1594         ret = dlg->exec();
1595         CHECK(!dlg.isNull(), );
1596         if (ret == QDialog::Accepted) {
1597             if (!dlg->validateModel()) {
1598                 QMessageBox::critical(this, tr("Bad input!"), tr("Aliases for workflow parameters should be different!"));
1599                 continue;
1600             }
1601             // clear aliases before inserting new
1602             foreach (Actor *actor, schema->getProcesses()) {
1603                 actor->getParamAliases().clear();
1604             }
1605             SchemaAliasesCfgDlgModel model = dlg->getModel();
1606             foreach (const ActorId &id, model.aliases.keys()) {
1607                 foreach (const Descriptor &d, model.aliases.value(id).keys()) {
1608                     Actor *actor = schema->actorById(id);
1609                     assert(actor != nullptr);
1610                     QString alias = model.aliases.value(id).value(d);
1611                     assert(!alias.isEmpty());
1612                     actor->getParamAliases().insert(d.getId(), alias);
1613                     QString help = model.help.value(id).value(d);
1614                     if (!help.isEmpty()) {
1615                         actor->getAliasHelp().insert(alias, help);
1616                     }
1617                 }
1618             }
1619             break;
1620         } else if (ret == QDialog::Rejected) {
1621             break;
1622         } else {
1623             assert(false);
1624         }
1625     } while (ret == QDialog::Accepted);
1626 }
1627 
sl_createGalaxyConfig()1628 void WorkflowView::sl_createGalaxyConfig() {
1629     bool schemeContainsAliases = schema->hasParamAliases();
1630     if (!schemeContainsAliases) {
1631         QMessageBox::critical(this, tr("Bad input!"), tr("Workflow does not contain any parameter aliases"));
1632         return;
1633     }
1634     if (meta.url.isEmpty()) {
1635         return;
1636     }
1637 
1638     QObjectScopedPointer<GalaxyConfigConfigurationDialogImpl> dlg = new GalaxyConfigConfigurationDialogImpl(meta.url, this);
1639     dlg->exec();
1640     CHECK(!dlg.isNull(), );
1641 
1642     if (QDialog::Accepted == dlg->result()) {
1643         bool created = dlg->createGalaxyConfigTask();
1644         if (!created) {
1645             QMessageBox::critical(this, tr("Internal error!"), tr("Can not create Galaxy config"));
1646             return;
1647         }
1648     }
1649 }
1650 
sl_configurePortAliases()1651 void WorkflowView::sl_configurePortAliases() {
1652     QObjectScopedPointer<PortAliasesConfigurationDialog> dlg = new PortAliasesConfigurationDialog(*schema, this);
1653     dlg->exec();
1654     CHECK(!dlg.isNull(), );
1655 
1656     if (QDialog::Accepted == dlg->result()) {
1657         PortAliasesCfgDlgModel model = dlg->getModel();
1658 
1659         QList<PortAlias> portAliases;
1660         foreach (Port *port, model.ports.keys()) {
1661             PortAlias portAlias(port, model.ports.value(port).first, model.ports.value(port).second);
1662 
1663             foreach (Descriptor slotDescr, model.aliases.value(port).keys()) {
1664                 QString actorId;
1665                 QString slotId;
1666                 {
1667                     if (port->isInput()) {
1668                         actorId = port->owner()->getId();
1669                         slotId = slotDescr.getId();
1670                     } else {
1671                         QStringList tokens = slotDescr.getId().split(':');
1672                         assert(2 == tokens.size());
1673                         actorId = tokens[0];
1674                         slotId = tokens[1];
1675                     }
1676                 }
1677 
1678                 Port *sourcePort = nullptr;
1679                 foreach (Port *p, schema->actorById(actorId)->getPorts()) {
1680                     DataTypePtr dt = p->Port::getType();
1681                     QList<Descriptor> descs = dt->getAllDescriptors();
1682                     if (descs.contains(slotId)) {
1683                         sourcePort = p;
1684                         break;
1685                     }
1686                 }
1687                 assert(nullptr != sourcePort);
1688 
1689                 portAlias.addSlot(sourcePort, slotId, model.aliases.value(port).value(slotDescr));
1690             }
1691             portAliases.append(portAlias);
1692         }
1693 
1694         schema->setPortAliases(portAliases);
1695     }
1696 }
1697 
sl_importSchemaToElement()1698 void WorkflowView::sl_importSchemaToElement() {
1699     QString error;
1700     if (!schema->getWizards().isEmpty()) {
1701         error = WorkflowView::tr("The workflow contains a wizard. Sorry, but current version of "
1702                                  "UGENE doesn't support of wizards in the includes.");
1703         QMessageBox::critical(this, tr("Error"), error);
1704     } else if (WorkflowUtils::validateSchemaForIncluding(*schema, error)) {
1705         QObjectScopedPointer<ImportSchemaDialog> d = new ImportSchemaDialog(this);
1706         d->exec();
1707         CHECK(!d.isNull(), );
1708 
1709         if (QDialog::Accepted == d->result()) {
1710             Schema *s = new Schema();
1711             U2OpStatusImpl os;
1712             HRSchemaSerializer::deepCopy(*schema, s, os);
1713             SAFE_POINT_OP(os, );
1714             QString typeName = d->getTypeName();
1715 
1716             s->setTypeName(typeName);
1717             QString text = HRSchemaSerializer::schema2String(*s, nullptr);
1718 
1719             QString path = WorkflowSettings::getIncludedElementsDirectory() + typeName + "." + WorkflowUtils::WD_FILE_EXTENSIONS.first();
1720             QFile file(path);
1721             file.open(QIODevice::WriteOnly);
1722             file.write(text.toLatin1());
1723             file.close();
1724 
1725             ActorPrototype *proto = IncludedProtoFactory::getSchemaActorProto(s, typeName, path);
1726             WorkflowEnv::getProtoRegistry()->registerProto(BaseActorCategories::CATEGORY_INCLUDES(), proto);
1727             WorkflowEnv::getSchemaActorsRegistry()->registerSchema(typeName, s);
1728         }
1729     } else {
1730         QMessageBox::critical(this, tr("Error"), error);
1731     }
1732 }
1733 
sl_selectPrototype(Workflow::ActorPrototype * p,bool putToScene)1734 void WorkflowView::sl_selectPrototype(Workflow::ActorPrototype *p, bool putToScene) {
1735     propertyEditor->setEditable(!p);
1736     scene->clearSelection();
1737     currentProto = p;
1738 
1739     propertyEditor->reset();
1740     if (!p) {
1741         scene->views().at(0)->unsetCursor();
1742         propertyEditor->changeScriptMode(scriptingMode);
1743     } else {
1744         delete currentActor;
1745         currentActor = createActor(p, QVariantMap());
1746         if (putToScene) {
1747             addProcess(currentActor, scene->getLastMousePressPoint());
1748         } else {
1749             propertyEditor->setDescriptor(p, tr("Drag an element to the scene to add it to the workflow."));
1750             scene->views().at(0)->setCursor(Qt::CrossCursor);
1751         }
1752     }
1753 }
1754 
sl_copyItems()1755 void WorkflowView::sl_copyItems() {
1756     QList<WorkflowProcessItem *> procs;
1757     foreach (QGraphicsItem *item, scene->selectedItems()) {
1758         if (item->type() == WorkflowProcessItemType) {
1759             procs << qgraphicsitem_cast<WorkflowProcessItem *>(item);
1760         }
1761     }
1762     if (procs.isEmpty()) {
1763         return;
1764     }
1765 
1766     QList<Actor *> actors = scene->getSelectedActors();
1767     Metadata actorMeta = getMeta(procs);
1768     lastPaste = HRSchemaSerializer::items2String(actors, &actorMeta);
1769     pasteAction->setEnabled(true);
1770     QApplication::clipboard()->setText(lastPaste);
1771     pasteCount = 0;
1772 }
1773 
sl_cutItems()1774 void WorkflowView::sl_cutItems() {
1775     sl_copyItems();
1776     scene->sl_deleteItem();
1777 }
1778 
sl_pasteSample(const QString & s)1779 void WorkflowView::sl_pasteSample(const QString &s) {
1780     tabs->setCurrentIndex(ElementsTab);
1781     infoList->clear();
1782     if (scene->items().isEmpty()) {
1783         // fixing bug with pasting same schema 2 times
1784         {
1785             lastPaste.clear();
1786         }
1787         sl_pasteItems(s, true);
1788         sl_updateTitle();
1789         sl_updateUi();
1790         scene->connectConfigurationEditors();
1791         scene->sl_deselectAll();
1792         scene->update();
1793         rescale();
1794         sl_refreshActorDocs();
1795         meta.setSampleMark(true);
1796         GCounter::increment(meta.name, "WDSample:open");
1797         startFirstAutoRunWizard();
1798     } else {
1799         breakpointView->clear();
1800         scene->clearScene();
1801         schema->reset();
1802         sl_pasteSample(s);
1803     }
1804 }
1805 
getUniquePastedActorIds(const QList<Actor * > & pasted,const QList<Actor * > & origin)1806 static QMap<ActorId, ActorId> getUniquePastedActorIds(const QList<Actor *> &pasted, const QList<Actor *> &origin) {
1807     QMap<ActorId, ActorId> result;
1808     QStringList uniqueIds;
1809     foreach (Actor *a, origin) {
1810         uniqueIds << aid2str(a->getId());
1811     }
1812     foreach (Actor *a, pasted) {
1813         QString uniqId = WorkflowUtils::createUniqueString(aid2str(a->getId()), "-", uniqueIds);
1814         uniqueIds << uniqId;
1815         ActorId newId = str2aid(uniqId);
1816         if (newId != a->getId()) {
1817             result[a->getId()] = newId;
1818         }
1819     }
1820     return result;
1821 }
1822 
renamePastedSchemaActors(Schema & pasted,Metadata & meta,Schema * origin)1823 static void renamePastedSchemaActors(Schema &pasted, Metadata &meta, Schema *origin) {
1824     QMap<ActorId, ActorId> mapping = getUniquePastedActorIds(pasted.getProcesses(), origin->getProcesses());
1825     foreach (const ActorId &id, mapping.keys()) {
1826         pasted.renameProcess(id, mapping[id]);
1827     }
1828     meta.renameActors(mapping);
1829 }
1830 
sl_pasteItems(const QString & s,bool updateSchemaInfo)1831 void WorkflowView::sl_pasteItems(const QString &s, bool updateSchemaInfo) {
1832     QString tmp = s.isNull() ? QApplication::clipboard()->text() : s;
1833     if (tmp == lastPaste) {
1834         ++pasteCount;
1835     } else {
1836         pasteCount = 0;
1837         lastPaste = tmp;
1838     }
1839     QByteArray lpt = lastPaste.toLatin1();
1840     DocumentFormat *wf = AppContext::getDocumentFormatRegistry()->getFormatById(WorkflowDocFormat::FORMAT_ID);
1841     if (wf->checkRawData(lpt).score != FormatDetection_Matched) {
1842         return;
1843     }
1844     disconnect(scene, SIGNAL(selectionChanged()), this, SLOT(sl_editItem()));
1845     scene->clearSelection();
1846     connect(scene, SIGNAL(selectionChanged()), SLOT(sl_editItem()));
1847 
1848     Schema pastedS;
1849     pastedS.setDeepCopyFlag(true);
1850     Metadata pastedM;
1851     QString msg = HRSchemaSerializer::string2Schema(lastPaste, &pastedS, &pastedM);
1852     if (!msg.isEmpty()) {
1853         uiLog.error("Paste issues: " + msg);
1854         return;
1855     }
1856     renamePastedSchemaActors(pastedS, pastedM, schema.get());
1857     if (schema->getProcesses().isEmpty()) {
1858         schema->setWizards(pastedS.takeWizards());
1859     }
1860     schema->merge(pastedS);
1861     updateMeta();
1862     meta.mergeVisual(pastedM);
1863     if (updateSchemaInfo) {
1864         meta.name = pastedM.name;
1865         meta.comment = pastedM.comment;
1866         meta.scalePercent = pastedM.scalePercent;
1867         meta.estimationsCode = pastedM.estimationsCode;
1868     }
1869     pastedS.setDeepCopyFlag(false);
1870     recreateScene();
1871     scene->connectConfigurationEditors();
1872 
1873     foreach (QGraphicsItem *it, scene->items()) {
1874         WorkflowProcessItem *proc = qgraphicsitem_cast<WorkflowProcessItem *>(it);
1875         if (nullptr != proc) {
1876             if (nullptr != pastedS.actorById(proc->getProcess()->getId())) {
1877                 it->setSelected(true);
1878             }
1879         }
1880     }
1881 
1882     int shift = GRID_STEP * (pasteCount);
1883     foreach (QGraphicsItem *it, scene->selectedItems()) {
1884         it->moveBy(shift, shift);
1885     }
1886 }
1887 
recreateScene()1888 void WorkflowView::recreateScene() {
1889     sceneRecreation = true;
1890     SceneCreator sc(schema.get(), meta);
1891     sc.recreateScene(scene);
1892     sceneRecreation = false;
1893 }
1894 
sl_showEditor()1895 void WorkflowView::sl_showEditor() {
1896     propertyEditor->show();
1897     QList<int> s = splitter->sizes();
1898     if (s.last() == 0) {
1899         s.last() = propertyEditor->sizeHint().width();
1900         splitter->setSizes(s);
1901     }
1902 }
1903 
sl_editItem()1904 void WorkflowView::sl_editItem() {
1905     QList<QGraphicsItem *> list = scene->selectedItems();
1906     if (list.size() == 1) {
1907         QGraphicsItem *it = list.at(0);
1908         if (it->type() == WorkflowProcessItemType) {
1909             Actor *a = qgraphicsitem_cast<WorkflowProcessItem *>(it)->getProcess();
1910             propertyEditor->editActor(a);
1911             return;
1912         }
1913         Port *p = nullptr;
1914 
1915         if (it->type() == WorkflowBusItemType) {
1916             WorkflowBusItem *busItem = qgraphicsitem_cast<WorkflowBusItem *>(it);
1917 
1918             if (debugInfo->isPaused()) {
1919                 investigationWidgets->setCurrentInvestigation(busItem->getBus());
1920             }
1921             p = busItem->getInPort()->getPort();
1922         } else if (it->type() == WorkflowPortItemType) {
1923             p = qgraphicsitem_cast<WorkflowPortItem *>(it)->getPort();
1924         }
1925         if (p) {
1926             if (qobject_cast<IntegralBusPort *>(p)) {
1927                 BusPortEditor *ed = new BusPortEditor(qobject_cast<IntegralBusPort *>(p));
1928                 ed->setParent(p);
1929                 p->setEditor(ed);
1930             }
1931         }
1932         propertyEditor->editPort(p);
1933     } else {
1934         propertyEditor->reset();
1935     }
1936 }
1937 
sl_onSelectionChanged()1938 void WorkflowView::sl_onSelectionChanged() {
1939     QList<Actor *> actorsSelected = scene->getSelectedActors();
1940     const int actorsCount = actorsSelected.size();
1941     editScriptAction->setEnabled(actorsCount == 1 && actorsSelected.first()->getScript() != nullptr);
1942     editExternalToolAction->setEnabled(actorsCount == 1 && actorsSelected.first()->getProto()->isExternalTool());
1943     toggleBreakpointAction->setEnabled(scene->items().size() != 0);
1944 
1945     WorkflowAbstractRunner *runner = scene->getRunner();
1946     if (nullptr != runner && !actorsSelected.isEmpty()) {
1947         QList<Workflow::WorkerState> workerStates = runner->getState(actorsSelected.first());
1948         tickReadyAction->setEnabled(debugInfo->isPaused() && 1 == actorsCount && workerStates.contains(WorkerReady));
1949     } else {
1950         tickReadyAction->setEnabled(false);
1951     }
1952 }
1953 
sl_exportScene()1954 void WorkflowView::sl_exportScene() {
1955     propertyEditor->commit();
1956     QString fileName = GUrlUtils::fixFileName(meta.name);
1957     QObjectScopedPointer<ExportImageDialog> dialog = new ExportImageDialog(sceneView->viewport(), ExportImageDialog::WD, fileName, ExportImageDialog::SupportScaling, sceneView->viewport());
1958     dialog->exec();
1959 }
1960 
sl_saveScene()1961 void WorkflowView::sl_saveScene() {
1962     if (meta.url.isEmpty()) {
1963         QObjectScopedPointer<WorkflowMetaDialog> md = new WorkflowMetaDialog(this, meta);
1964         const int rc = md->exec();
1965         CHECK(!md.isNull(), );
1966 
1967         if (rc != QDialog::Accepted) {
1968             return;
1969         }
1970         meta = md->meta;
1971         sl_updateTitle();
1972     }
1973     propertyEditor->commit();
1974     Task *t = new SaveWorkflowSceneTask(getSchema(), getMeta());
1975     AppContext::getTaskScheduler()->registerTopLevelTask(t);
1976     connect(t, SIGNAL(si_stateChanged()), SLOT(sl_onSceneSaved()));
1977 }
1978 
sl_saveSceneAs()1979 void WorkflowView::sl_saveSceneAs() {
1980     QObjectScopedPointer<WorkflowMetaDialog> md = new WorkflowMetaDialog(this, meta);
1981     const int rc = md->exec();
1982     CHECK(!md.isNull(), );
1983 
1984     if (rc != QDialog::Accepted) {
1985         return;
1986     }
1987     propertyEditor->commit();
1988     meta = md->meta;
1989     Task *t = new SaveWorkflowSceneTask(getSchema(), getMeta());
1990     AppContext::getTaskScheduler()->registerTopLevelTask(t);
1991     sl_updateTitle();
1992     connect(t, SIGNAL(si_stateChanged()), SLOT(sl_onSceneSaved()));
1993 }
1994 
startWizard(Wizard * wizard)1995 void WorkflowView::startWizard(Wizard *wizard) {
1996     auto viewPointer = new QPointer<WorkflowView>(this);
1997     QTimer::singleShot(100, [this, wizard, viewPointer]() {
1998         // Check that the view is not closed/destroyed before running the wizard. */
1999         if (!viewPointer->isNull()) {
2000             runWizardAndHandleResult(wizard);
2001         }
2002         delete viewPointer;
2003     });
2004 }
2005 
runWizardAndHandleResult(Wizard * wizard)2006 void WorkflowView::runWizardAndHandleResult(Wizard *wizard) {
2007     WizardController controller(schema, wizard);
2008     QWizard *gui = controller.createGui();
2009     if (gui->exec() && !controller.isBroken()) {
2010         QString result = wizard->getResult(controller.getVariables());
2011         if (!result.isEmpty()) {
2012             controller.applyChanges(meta);
2013             loadWizardResult(result);
2014             return;
2015         }
2016 
2017         const bool isSample = meta.isSample();
2018         updateMeta();
2019         meta.setSampleMark(isSample);
2020 
2021         WizardController::ApplyResult res = controller.applyChanges(meta);
2022         if (WizardController::ACTORS_REPLACED == res) {
2023             recreateScene();
2024             schema->setWizards(QList<Wizard *>());
2025         }
2026         scene->sl_updateDocs();
2027         scene->setModified();
2028         propertyEditor->update();
2029         if (controller.isRunAfterApply()) {
2030             sl_launch();
2031         }
2032     } else if (schema->getProcesses().isEmpty()) {
2033         sl_newScene();
2034     }
2035 }
2036 
loadWizardResult(const QString & result)2037 void WorkflowView::loadWizardResult(const QString &result) {
2038     QString url = QDir::searchPaths(PATH_PREFIX_DATA).first() + "/workflow_samples/" + result;
2039     if (!QFile::exists(url)) {
2040         coreLog.error(tr("File is not found: %1").arg(url));
2041         return;
2042     }
2043     breakpointView->clear();
2044     schema->reset();
2045     meta.reset();
2046     U2OpStatus2Log os;
2047     WorkflowUtils::schemaFromFile(url, schema.get(), &meta, os);
2048     recreateScene();
2049     sl_onSceneLoaded();
2050     if (!schema->getWizards().isEmpty() && !schema->getWizards().first()->isAutoRun()) {
2051         startWizard(schema->getWizards().first());
2052     }
2053 }
2054 
procItemAdded()2055 void WorkflowView::procItemAdded() {
2056     currentActor = nullptr;
2057     propertyEditor->setEditable(true);
2058     scene->invalidate(QRectF(), QGraphicsScene::BackgroundLayer);
2059     if (!currentProto) {
2060         return;
2061     }
2062     palette->resetSelection();
2063     currentProto = nullptr;
2064     assert(scene->views().size() == 1);
2065     scene->views().at(0)->unsetCursor();
2066 }
2067 
startFirstAutoRunWizard()2068 void WorkflowView::startFirstAutoRunWizard() {
2069     const QList<Wizard *> wizardList = schema->getWizards();
2070     for (Wizard *wizard : qAsConst(wizardList)) {
2071         if (wizard->isAutoRun()) {
2072             startWizard(wizard);
2073             return;
2074         }
2075     }
2076 }
2077 
sl_showWizard()2078 void WorkflowView::sl_showWizard() {
2079     if (schema->getWizards().size() > 0) {
2080         startWizard(schema->getWizards().first());
2081     }
2082 }
2083 
getToolbarIcon(const QString & srcPath)2084 static QIcon getToolbarIcon(const QString &srcPath) {
2085     QPixmap pm = QPixmap(":workflow_designer/images/" + srcPath).scaled(16, 16);
2086     return QIcon(pm);
2087 }
2088 
hideDashboards()2089 void WorkflowView::hideDashboards() {
2090     setDashboardActionDecoration(false);
2091     tabView->setVisible(false);
2092     splitter->setVisible(true);
2093     setupActions();
2094 }
2095 
showDashboards()2096 void WorkflowView::showDashboards() {
2097     setDashboardActionDecoration(true);
2098     splitter->setVisible(false);
2099     tabView->setVisible(true);
2100     setupActions();
2101 }
2102 
setDashboardActionDecoration(bool isDashboardsViewActive)2103 void WorkflowView::setDashboardActionDecoration(bool isDashboardsViewActive) {
2104     if (isDashboardsViewActive) {
2105         toggleDashboard->setIconText(tr("To Workflow Designer"));
2106         toggleDashboard->setIcon(getToolbarIcon("wd.png"));
2107         toggleDashboard->setToolTip(tr("Show workflow"));
2108     } else {
2109         toggleDashboard->setIconText(tr("Go to Dashboard"));
2110         toggleDashboard->setIcon(getToolbarIcon("dashboard.png"));
2111         toggleDashboard->setToolTip(tr("Show dashboard"));
2112     }
2113 }
2114 
setDashboardActionVisible(bool visible)2115 void WorkflowView::setDashboardActionVisible(bool visible) {
2116     toggleDashboard->setVisible(visible);
2117 }
2118 
commitWarningsToMonitor(WorkflowAbstractRunner * t)2119 void WorkflowView::commitWarningsToMonitor(WorkflowAbstractRunner *t) {
2120     for (int i = 0; i < infoList->count(); i++) {
2121         QListWidgetItem *warning = infoList->item(i);
2122         foreach (WorkflowMonitor *monitor, t->getMonitors()) {
2123             monitor->addError(warning->data(TEXT_REF).toString(),
2124                               warning->data(ACTOR_ID_REF).toString(),
2125                               warning->data(TYPE_REF).toString());
2126         }
2127     }
2128 }
2129 
sl_toggleDashboard()2130 void WorkflowView::sl_toggleDashboard() {
2131     if (tabView->isVisible()) {
2132         hideDashboards();
2133     } else {
2134         showDashboards();
2135     }
2136 }
2137 
sl_dashboardCountChanged()2138 void WorkflowView::sl_dashboardCountChanged() {
2139     setDashboardActionVisible(tabView->hasDashboards());
2140     if (!tabView->hasDashboards()) {
2141         hideDashboards();
2142     }
2143 }
2144 
sl_loadScene()2145 void WorkflowView::sl_loadScene() {
2146     if (!confirmModified()) {
2147         return;
2148     }
2149 
2150     QString dir = AppContext::getSettings()->getValue(LAST_DIR, QString("")).toString();
2151     QString filter = DesignerUtils::getSchemaFileFilter(true, true);
2152     QString url;
2153 #ifdef Q_OS_DARWIN
2154     if (qgetenv(ENV_GUI_TEST).toInt() == 1 && qgetenv(ENV_USE_NATIVE_DIALOGS).toInt() == 0) {
2155         url = U2FileDialog::getOpenFileName(0, tr("Open workflow file"), dir, filter, 0, QFileDialog::DontUseNativeDialog);
2156     } else
2157 #endif
2158         url = U2FileDialog::getOpenFileName(0, tr("Open workflow file"), dir, filter);
2159     if (!url.isEmpty()) {
2160         AppContext::getSettings()->setValue(LAST_DIR, QFileInfo(url).absoluteDir().absolutePath());
2161         sl_loadScene(url, false);
2162     }
2163 }
2164 
sl_loadScene(const QString & url,bool fromDashboard)2165 void WorkflowView::sl_loadScene(const QString &url, bool fromDashboard) {
2166     CHECK(!running, );
2167     if (fromDashboard && !confirmModified()) {
2168         return;
2169     }
2170     loadWorkflowSceneTask = new LoadWorkflowSceneTask(schema, &meta, scene, url, fromDashboard, fromDashboard);  // FIXME unsynchronized meta usage
2171     TaskSignalMapper *m = new TaskSignalMapper(loadWorkflowSceneTask.data());
2172     connect(m, SIGNAL(si_taskFinished(Task *)), SLOT(sl_onSceneLoaded()));
2173     if (LoadWorkflowTask::detectFormat(IOAdapterUtils::readFileHeader(url)) == LoadWorkflowTask::XML) {
2174         connect(m, SIGNAL(si_taskFinished(Task *)), SLOT(sl_xmlSchemaLoaded(Task *)));
2175     }
2176     AppContext::getTaskScheduler()->registerTopLevelTask(loadWorkflowSceneTask);
2177 }
2178 
sl_xmlSchemaLoaded(Task * t)2179 void WorkflowView::sl_xmlSchemaLoaded(Task *t) {
2180     assert(t != nullptr);
2181     if (!t->hasError()) {
2182         QMessageBox::warning(this, tr("Warning!"), QObject::tr("You opened obsolete workflow in XML format. It is strongly recommended"
2183                                                                " to clear working space and create workflow from scratch."));
2184     } else {
2185         QMessageBox::warning(this, tr("Warning!"), QObject::tr("Sorry! This workflow is obsolete and cannot be opened."));
2186     }
2187 }
2188 
sl_newScene()2189 void WorkflowView::sl_newScene() {
2190     if (!confirmModified()) {
2191         return;
2192     }
2193     breakpointView->clear();
2194     bottomTabs->hide();
2195     scene->sl_reset();
2196     meta.reset();
2197     meta.name = tr("New workflow");
2198     schema->reset();
2199     sl_updateTitle();
2200     scene->setModified(false);
2201     rescale();
2202     scene->update();
2203     sl_updateUi();
2204 }
2205 
sl_onSceneLoaded()2206 void WorkflowView::sl_onSceneLoaded() {
2207     breakpointView->clear();
2208     sl_updateTitle();
2209     sl_updateUi();
2210     scene->centerView();
2211 
2212     scene->setModified(false);
2213     rescale();
2214     sl_refreshActorDocs();
2215     hideDashboards();
2216     tabs->setCurrentIndex(ElementsTab);
2217     startFirstAutoRunWizard();
2218 }
2219 
sl_onSceneSaved()2220 void WorkflowView::sl_onSceneSaved() {
2221     Task *t = dynamic_cast<Task *>(sender());
2222     CHECK(nullptr != t, );
2223     if (t->isFinished() && !t->hasError()) {
2224         scene->setModified(false);
2225     }
2226 }
2227 
sl_updateTitle()2228 void WorkflowView::sl_updateTitle() {
2229     setWindowTitle(tr("Workflow Designer - %1").arg(meta.name));
2230 }
2231 
sl_updateUi()2232 void WorkflowView::sl_updateUi() {
2233     scene->setModified(false);
2234     showWizard->setVisible(!schema->getWizards().isEmpty());
2235     estimateAction->setVisible(!meta.estimationsCode.isEmpty());
2236 }
2237 
saveState()2238 void WorkflowView::saveState() {
2239     AppContext::getSettings()->setValue(SPLITTER_STATE, splitter->saveState());
2240     AppContext::getSettings()->setValue(EDITOR_STATE, propertyEditor->saveState());
2241     AppContext::getSettings()->setValue(PALETTE_STATE, palette->saveState());
2242     AppContext::getSettings()->setValue(TABS_STATE, tabs->currentIndex());
2243 }
2244 
onCloseEvent()2245 bool WorkflowView::onCloseEvent() {
2246     saveState();
2247     if (!confirmModified()) {
2248         return false;
2249     }
2250     if (go) {
2251         go->setView(nullptr);
2252     }
2253     return true;
2254 }
2255 
confirmModified()2256 bool WorkflowView::confirmModified() {
2257     propertyEditor->commit();
2258     if (scene->isModified() && !scene->items().isEmpty()) {
2259         AppContext::getMainWindow()->getMDIManager()->activateWindow(this);
2260         int ret = QMessageBox::question(this, tr("Workflow Designer"), tr("The workflow has been modified.\n"
2261                                                                           "Do you want to save changes?"),
2262                                         QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
2263                                         QMessageBox::Save);
2264         if (QMessageBox::Cancel == ret) {
2265             return false;
2266         } else if (QMessageBox::Discard != ret) {
2267             sl_saveScene();
2268         }
2269     }
2270     return true;
2271 }
2272 
newActorLabel(ActorPrototype * proto,const QList<Actor * > & procs)2273 static QString newActorLabel(ActorPrototype *proto, const QList<Actor *> &procs) {
2274     QStringList allLabels;
2275     foreach (Actor *actor, procs) {
2276         allLabels << actor->getLabel();
2277     }
2278     return WorkflowUtils::createUniqueString(proto->getDisplayName(), " ", allLabels);
2279 }
2280 
createActor(ActorPrototype * proto,const QVariantMap & params) const2281 Actor *WorkflowView::createActor(ActorPrototype *proto, const QVariantMap &params) const {
2282     assert(nullptr != proto);
2283     QString pId = proto->getId().replace(QRegExp("\\s"), "-");
2284     ActorId id = Schema::uniqueActorId(pId, schema->getProcesses());
2285     Actor *actor = proto->createInstance(id, nullptr, params);
2286     assert(nullptr != actor);
2287 
2288     actor->setLabel(newActorLabel(proto, schema->getProcesses()));
2289     return actor;
2290 }
2291 
onModified()2292 void WorkflowView::onModified() {
2293     scene->onModified();
2294 }
2295 
tryBind(WorkflowPortItem * from,WorkflowPortItem * to)2296 WorkflowBusItem *WorkflowView::tryBind(WorkflowPortItem *from, WorkflowPortItem *to) {
2297     WorkflowBusItem *dit = nullptr;
2298 
2299     if (from->getPort()->canBind(to->getPort())) {
2300         Port *src = from->getPort();
2301         Port *dest = to->getPort();
2302         if (src->isInput()) {
2303             src = to->getPort();
2304             dest = from->getPort();
2305         }
2306         if (WorkflowUtils::isPathExist(src, dest)) {
2307             return nullptr;
2308         }
2309 
2310         Link *link = new Link(src, dest);
2311         schema->addFlow(link);
2312         dit = scene->addFlow(from, to, link);
2313         removeEstimations();
2314     }
2315     return dit;
2316 }
2317 
sl_updateSchema()2318 void WorkflowView::sl_updateSchema() {
2319     schema->update();
2320 }
2321 
getSchema() const2322 QSharedPointer<Schema> WorkflowView::getSchema() const {
2323     return schema;
2324 }
2325 
getMeta()2326 const Workflow::Metadata &WorkflowView::getMeta() {
2327     return updateMeta();
2328 }
2329 
updateMeta()2330 const Workflow::Metadata &WorkflowView::updateMeta() {
2331     meta.setSampleMark(false);
2332     meta.resetVisual();
2333     foreach (QGraphicsItem *it, scene->items()) {
2334         switch (it->type()) {
2335             case WorkflowProcessItemType: {
2336                 WorkflowProcessItem *proc = qgraphicsitem_cast<WorkflowProcessItem *>(it);
2337                 ActorVisualData visual(proc->getProcess()->getId());
2338                 visual.setPos(proc->pos());
2339                 ItemViewStyle *style = proc->getStyleById(proc->getStyle());
2340                 if (nullptr != style) {
2341                     visual.setStyle(style->getId());
2342                     if (style->getBgColor() != style->defaultColor()) {
2343                         visual.setColor(style->getBgColor());
2344                     }
2345                     if (style->defaultFont() != QFont()) {
2346                         visual.setFont(style->defaultFont());
2347                     }
2348                     if (ItemStyles::EXTENDED == style->getId()) {
2349                         ExtendedProcStyle *eStyle = dynamic_cast<ExtendedProcStyle *>(style);
2350                         if (!eStyle->isAutoResized()) {
2351                             visual.setRect(eStyle->boundingRect());
2352                         }
2353                     }
2354                 }
2355                 foreach (WorkflowPortItem *port, proc->getPortItems()) {
2356                     visual.setPortAngle(port->getPort()->getId(), port->getOrientarion());
2357                 }
2358                 meta.setActorVisualData(visual);
2359             } break;
2360             case WorkflowBusItemType: {
2361                 WorkflowBusItem *bus = qgraphicsitem_cast<WorkflowBusItem *>(it);
2362                 Port *src = bus->getBus()->source();
2363                 Port *dst = bus->getBus()->destination();
2364                 QPointF p = bus->getText()->pos();
2365                 meta.setTextPos(src->owner()->getId(), src->getId(), dst->owner()->getId(), dst->getId(), p);
2366             } break;
2367         }
2368     }
2369     return meta;
2370 }
2371 
getMeta(const QList<WorkflowProcessItem * > & items)2372 Workflow::Metadata WorkflowView::getMeta(const QList<WorkflowProcessItem *> &items) {
2373     const Workflow::Metadata &meta = getMeta();
2374     Workflow::Metadata result;
2375     result.name = meta.name;
2376     result.url = meta.url;
2377     result.comment = meta.comment;
2378 
2379     foreach (WorkflowProcessItem *proc, items) {
2380         bool contains = false;
2381         ActorVisualData visual = meta.getActorVisualData(proc->getProcess()->getId(), contains);
2382         assert(contains);
2383         result.setActorVisualData(visual);
2384         foreach (WorkflowPortItem *port1, proc->getPortItems()) {
2385             foreach (WorkflowBusItem *bus, port1->getDataFlows()) {
2386                 WorkflowPortItem *port2 = (bus->getInPort() == port1) ? bus->getOutPort() : bus->getInPort();
2387                 WorkflowProcessItem *proc2 = port2->getOwner();
2388                 if (!items.contains(proc2)) {
2389                     continue;
2390                 }
2391                 Port *src = bus->getBus()->source();
2392                 Port *dst = bus->getBus()->destination();
2393                 QPointF p = meta.getTextPos(src->owner()->getId(), src->getId(), dst->owner()->getId(), dst->getId(), contains);
2394                 if (contains) {
2395                     result.setTextPos(src->owner()->getId(), src->getId(), dst->owner()->getId(), dst->getId(), p);
2396                 }
2397             }
2398         }
2399     }
2400     return result;
2401 }
2402 
getRFS()2403 RunFileSystem *WorkflowView::getRFS() {
2404     RunFileSystem *result = new RunFileSystem(this);
2405     RFSUtils::initRFS(*result, schema->getProcesses(), this);
2406     return result;
2407 }
2408 
getAttributeValue(const AttributeInfo & info) const2409 QVariant WorkflowView::getAttributeValue(const AttributeInfo &info) const {
2410     Actor *actor = schema->actorById(info.actorId);
2411     CHECK(nullptr != actor, QVariant());
2412     Attribute *attr = actor->getParameter(info.attrId);
2413     CHECK(nullptr != attr, QVariant());
2414     return attr->getAttributePureValue();
2415 }
2416 
setAttributeValue(const AttributeInfo & info,const QVariant & value)2417 void WorkflowView::setAttributeValue(const AttributeInfo &info, const QVariant &value) {
2418     Actor *actor = schema->actorById(info.actorId);
2419     CHECK(nullptr != actor, );
2420     Attribute *attr = actor->getParameter(info.attrId);
2421     CHECK(nullptr != attr, );
2422     attr->setAttributeValue(value);
2423 }
2424 
isShowSamplesHint() const2425 bool WorkflowView::isShowSamplesHint() const {
2426     SAFE_POINT(nullptr != samples, "NULL samples widget", false);
2427     SAFE_POINT(nullptr != schema, "NULL schema", false);
2428     const bool emptySchema = (0 == schema->getProcesses().size());
2429     return samples->isVisible() && emptySchema;
2430 }
2431 
2432 /********************************
2433  * WorkflowScene
2434  ********************************/
canDrop(const QMimeData * m,QList<ActorPrototype * > & lst)2435 static bool canDrop(const QMimeData *m, QList<ActorPrototype *> &lst) {
2436     if (m->hasFormat(WorkflowPalette::MIME_TYPE)) {
2437         QString id(m->data(WorkflowPalette::MIME_TYPE));
2438         ActorPrototype *proto = WorkflowEnv::getProtoRegistry()->getProto(id);
2439         if (proto) {
2440             lst << proto;
2441         }
2442     } else {
2443         foreach (QList<ActorPrototype *> l, WorkflowEnv::getProtoRegistry()->getProtos().values()) {
2444             foreach (ActorPrototype *proto, l) {
2445                 if (proto->isAcceptableDrop(m)) {
2446                     lst << proto;
2447                 }
2448             }
2449         }
2450     }
2451     return !lst.isEmpty();
2452 }
2453 
WorkflowScene(WorkflowView * parent)2454 WorkflowScene::WorkflowScene(WorkflowView *parent)
2455     : QGraphicsScene(parent), controller(parent), modified(false), locked(false), runner(nullptr), hint(0) {
2456     openDocumentsAction = new QAction(tr("Open document(s)"), this);
2457     connect(openDocumentsAction, SIGNAL(triggered()), SLOT(sl_openDocuments()));
2458 }
2459 
~WorkflowScene()2460 WorkflowScene::~WorkflowScene() {
2461     sl_reset();
2462 }
2463 
sl_deleteItem()2464 void WorkflowScene::sl_deleteItem() {
2465     assert(!locked);
2466     QList<WorkflowProcessItem *> items;
2467     foreach (QGraphicsItem *it, selectedItems()) {
2468         WorkflowProcessItem *proc = qgraphicsitem_cast<WorkflowProcessItem *>(it);
2469         WorkflowBusItem *bus = qgraphicsitem_cast<WorkflowBusItem *>(it);
2470         switch (it->type()) {
2471             case WorkflowProcessItemType:
2472                 items << proc;
2473                 break;
2474             case WorkflowBusItemType:
2475                 controller->removeBusItem(bus);
2476                 setModified();
2477                 break;
2478         }
2479     }
2480     foreach (WorkflowProcessItem *it, items) {
2481         if (it->getProcess() != nullptr) {
2482             emit si_itemDeleted(it->getProcess()->getId());
2483         }
2484         controller->removeProcessItem(it);
2485         setModified();
2486     }
2487 
2488     controller->update();
2489     emit configurationChanged();
2490     update();
2491 }
2492 
getSelectedActors() const2493 QList<Actor *> WorkflowScene::getSelectedActors() const {
2494     QList<Actor *> list;
2495     foreach (QGraphicsItem *item, selectedItems()) {
2496         if (item->type() == WorkflowProcessItemType) {
2497             list << static_cast<WorkflowProcessItem *>(item)->getProcess();
2498         }
2499     }
2500     return list;
2501 }
2502 
contextMenuEvent(QGraphicsSceneContextMenuEvent * e)2503 void WorkflowScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *e) {
2504     QGraphicsScene::contextMenuEvent(e);
2505     if (!e->isAccepted()) {
2506         QMenu menu;
2507         controller->setupContextMenu(&menu);
2508         e->accept();
2509         menu.exec(e->screenPos());
2510     }
2511 }
2512 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent)2513 void WorkflowScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent) {
2514     if (!mouseEvent->isAccepted() && (mouseEvent->button() == Qt::LeftButton) && !selectedItems().isEmpty()) {
2515         emit processDblClicked();
2516     }
2517     QGraphicsScene::mousePressEvent(mouseEvent);
2518 }
2519 
dragEnterEvent(QGraphicsSceneDragDropEvent * event)2520 void WorkflowScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) {
2521     QList<ActorPrototype *> lst;
2522     if (!locked && canDrop(event->mimeData(), lst)) {
2523         event->acceptProposedAction();
2524     } else {
2525         QGraphicsScene::dragEnterEvent(event);
2526     }
2527 }
2528 
dragMoveEvent(QGraphicsSceneDragDropEvent * event)2529 void WorkflowScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) {
2530     QList<ActorPrototype *> lst;
2531     if (!locked && canDrop(event->mimeData(), lst)) {
2532         event->acceptProposedAction();
2533     } else {
2534         QGraphicsScene::dragMoveEvent(event);
2535     }
2536 }
2537 
dropEvent(QGraphicsSceneDragDropEvent * event)2538 void WorkflowScene::dropEvent(QGraphicsSceneDragDropEvent *event) {
2539     QList<ActorPrototype *> lst;
2540     if (!locked && canDrop(event->mimeData(), lst)) {
2541         QList<QGraphicsItem *> targets = items(event->scenePos());
2542         bool done = false;
2543         foreach (QGraphicsItem *it, targets) {
2544             WorkflowProcessItem *target = qgraphicsitem_cast<WorkflowProcessItem *>(it);
2545             if (target && lst.contains(target->getProcess()->getProto())) {
2546                 clearSelection();
2547                 QVariantMap params;
2548                 Actor *a = target->getProcess();
2549                 a->getProto()->isAcceptableDrop(event->mimeData(), &params);
2550                 QMapIterator<QString, QVariant> cfg(params);
2551                 while (cfg.hasNext()) {
2552                     cfg.next();
2553                     a->setParameter(cfg.key(), cfg.value());
2554                 }
2555                 target->setSelected(true);
2556                 done = true;
2557                 break;
2558             }
2559         }
2560         if (!done) {
2561             ActorPrototype *proto = lst.size() > 1 ? ChooseItemDialog(controller).select(lst) : lst.first();
2562             if (proto) {
2563                 Actor *a = controller->getActor();
2564                 if (a) {
2565                     controller->addProcess(a, event->scenePos());
2566                 }
2567                 event->setDropAction(Qt::CopyAction);
2568             }
2569         }
2570     }
2571     QGraphicsScene::dropEvent(event);
2572 }
2573 
keyPressEvent(QKeyEvent * event)2574 void WorkflowScene::keyPressEvent(QKeyEvent *event) {
2575     if (event->key() == Qt::Key_Shift) {
2576         views().at(0)->setDragMode(QGraphicsView::ScrollHandDrag);
2577     }
2578     QGraphicsScene::keyPressEvent(event);
2579 }
2580 
keyReleaseEvent(QKeyEvent * event)2581 void WorkflowScene::keyReleaseEvent(QKeyEvent *event) {
2582     QGraphicsView *v = views().at(0);
2583     if (v->dragMode() == QGraphicsView::ScrollHandDrag) {
2584         v->setDragMode(QGraphicsView::RubberBandDrag);
2585     }
2586     QGraphicsScene::keyReleaseEvent(event);
2587 }
2588 
clearScene()2589 void WorkflowScene::clearScene() {
2590     sl_reset();
2591 }
2592 
setupLinkCtxMenu(const QString & href,Actor * actor,const QPoint & pos)2593 void WorkflowScene::setupLinkCtxMenu(const QString &href, Actor *actor, const QPoint &pos) {
2594     const QString attributeId = WorkflowUtils::getParamIdFromHref(href);
2595     bool isInput = attributeId == BaseAttributes::URL_IN_ATTRIBUTE().getId();
2596     bool isOutput = attributeId == BaseAttributes::URL_OUT_ATTRIBUTE().getId();
2597     if (isInput || isOutput) {
2598         Attribute *attribute = actor->getParameter(attributeId);
2599         QString urlStr;
2600         const QStringList urlList = WorkflowUtils::getAttributeUrls(attribute);
2601 
2602         foreach (const QString &url, urlList) {
2603             if (QFileInfo(url).isFile()) {
2604                 urlStr.append(url).append(';');
2605             }
2606         }
2607         urlStr = urlStr.left(urlStr.size() - 1);
2608 
2609         if (!urlStr.isEmpty()) {
2610             QMenu menu;
2611             openDocumentsAction->setData(urlStr);
2612             menu.addAction(openDocumentsAction);
2613             menu.exec(pos);
2614         }
2615     }
2616 }
2617 
sl_openDocuments()2618 void WorkflowScene::sl_openDocuments() {
2619     const QString &urlStr = openDocumentsAction->data().value<QString>();
2620     const QStringList &_urls = WorkflowUtils::expandToUrls(urlStr);
2621     QList<GUrl> urls;
2622     foreach (const QString &url, _urls) {
2623         urls.append(url);
2624     }
2625     Task *t = AppContext::getProjectLoader()->openWithProjectTask(urls);
2626     if (t) {
2627         AppContext::getTaskScheduler()->registerTopLevelTask(t);
2628     } else {
2629         QMessageBox::critical(controller, tr("Workflow Designer"), tr("Unable to open specified documents. Watch log for details."));
2630     }
2631 }
2632 
mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)2633 void WorkflowScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) {
2634     if (!locked && !mouseEvent->isAccepted() && controller->selectedProto() && (mouseEvent->button() == Qt::LeftButton)) {
2635         controller->addProcess(controller->getActor(), mouseEvent->scenePos());
2636     }
2637     lastMousePressPoint = mouseEvent->scenePos();
2638     QGraphicsScene::mousePressEvent(mouseEvent);
2639 }
2640 
sl_selectAll()2641 void WorkflowScene::sl_selectAll() {
2642     foreach (QGraphicsItem *it, items()) {
2643         it->setSelected(true);
2644     }
2645 }
2646 
sl_deselectAll()2647 void WorkflowScene::sl_deselectAll() {
2648     foreach (QGraphicsItem *it, items()) {
2649         it->setSelected(false);
2650     }
2651 }
2652 
sl_reset()2653 void WorkflowScene::sl_reset() {
2654     QList<QGraphicsItem *> list;
2655     QList<QGraphicsItem *> itemss = items();
2656     foreach (QGraphicsItem *it, itemss) {
2657         if (it->type() == WorkflowProcessItemType) {
2658             list << it;
2659         }
2660     }
2661     modified = false;
2662     foreach (QGraphicsItem *it, list) {
2663         removeItem(it);
2664         delete it;
2665     }
2666 }
2667 
setModified(bool b)2668 void WorkflowScene::setModified(bool b) {
2669     modified = b;
2670     update();
2671 }
2672 
setModified()2673 void WorkflowScene::setModified() {
2674     setModified(true);
2675 }
2676 
drawBackground(QPainter * painter,const QRectF & rect)2677 void WorkflowScene::drawBackground(QPainter *painter, const QRectF &rect) {
2678     if (WorkflowSettings::showGrid()) {
2679         int step = GRID_STEP;
2680         painter->setPen(QPen(QColor(200, 200, 255, 125)));
2681         // draw horizontal grid
2682         qreal start = round(rect.top(), step);
2683         if (start > rect.top()) {
2684             start -= step;
2685         }
2686         for (qreal y = start - step; y < rect.bottom();) {
2687             y += step;
2688             painter->drawLine(rect.left(), y, rect.right(), y);
2689         }
2690         // now draw vertical grid
2691         start = round(rect.left(), step);
2692         if (start > rect.left()) {
2693             start -= step;
2694         }
2695         for (qreal x = start - step; x < rect.right();) {
2696             x += step;
2697             painter->drawLine(x, rect.top(), x, rect.bottom());
2698         }
2699     }
2700 
2701     if (items().size() == 0) {
2702         // draw a hint on empty scene
2703         painter->setPen(Qt::darkGray);
2704         QFont f = painter->font();
2705         if (hint == SamplesTab) {
2706         } else {
2707             QTransform trans = painter->combinedTransform();
2708             f.setFamily("Courier New");
2709             f.setPointSizeF(f.pointSizeF() * 2. / trans.m11());
2710             painter->setFont(f);
2711             QRectF res;
2712             painter->drawText(sceneRect(), Qt::AlignCenter, tr("Drop an element from the palette here"), &res);
2713             QPixmap pix(":workflow_designer/images/leftarrow.png");
2714             QPointF pos(res.left(), res.center().y());
2715             pos.rx() -= pix.width() + GRID_STEP;
2716             pos.ry() -= pix.height() / 2;
2717             painter->drawPixmap(pos, pix);
2718         }
2719     }
2720 }
2721 
onModified()2722 void WorkflowScene::onModified() {
2723     assert(!locked);
2724     modified = true;
2725     emit configurationChanged();
2726 }
2727 
centerView()2728 void WorkflowScene::centerView() {
2729     QRectF childRect;
2730     foreach (QGraphicsItem *child, items()) {
2731         QPointF childPos = child->pos();
2732         QTransform matrix = child->transform() * QTransform().translate(childPos.x(), childPos.y());
2733         childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect());
2734     }
2735     update();
2736 }
2737 
addFlow(WorkflowPortItem * from,WorkflowPortItem * to,Link * link)2738 WorkflowBusItem *WorkflowScene::addFlow(WorkflowPortItem *from, WorkflowPortItem *to, Link *link) {
2739     WorkflowBusItem *dit = new WorkflowBusItem(from, to, link);
2740     from->addDataFlow(dit);
2741     to->addDataFlow(dit);
2742 
2743     addItem(dit);
2744     dit->updatePos();
2745     setModified(true);
2746     return dit;
2747 }
2748 
connectConfigurationEditors()2749 void WorkflowScene::connectConfigurationEditors() {
2750     foreach (QGraphicsItem *i, items()) {
2751         if (i->type() == WorkflowProcessItemType) {
2752             Actor *proc = static_cast<WorkflowProcessItem *>(i)->getProcess();
2753             ConfigurationEditor *editor = proc->getEditor();
2754             if (nullptr != editor) {
2755                 connect(editor, SIGNAL(si_configurationChanged()), this, SIGNAL(configurationChanged()));
2756             }
2757             GrouperEditor *g = dynamic_cast<GrouperEditor *>(editor);
2758             MarkerEditor *m = dynamic_cast<MarkerEditor *>(editor);
2759             if (nullptr != g || nullptr != m) {
2760                 connect(editor, SIGNAL(si_configurationChanged()), controller, SLOT(sl_updateSchema()));
2761             }
2762         }
2763     }
2764 }
2765 
2766 /************************************************************************/
2767 /* SceneCreator */
2768 /************************************************************************/
SceneCreator(Schema * _schema,const Workflow::Metadata & _meta)2769 SceneCreator::SceneCreator(Schema *_schema, const Workflow::Metadata &_meta)
2770     : schema(_schema), meta(_meta), scene(nullptr) {
2771 }
2772 
~SceneCreator()2773 SceneCreator::~SceneCreator() {
2774     delete scene;
2775 }
2776 
recreateScene(WorkflowScene * _scene)2777 WorkflowScene *SceneCreator::recreateScene(WorkflowScene *_scene) {
2778     scene = _scene;
2779     scene->sl_reset();
2780     return createScene();
2781 }
2782 
createScene(WorkflowView * controller)2783 WorkflowScene *SceneCreator::createScene(WorkflowView *controller) {
2784     scene = new WorkflowScene(controller);
2785     scene->setSceneRect(QRectF(-3 * WS, -3 * WS, 5 * WS, 5 * WS));
2786     scene->setItemIndexMethod(QGraphicsScene::NoIndex);
2787     scene->setObjectName("scene");
2788     return createScene();
2789 }
2790 
createScene()2791 WorkflowScene *SceneCreator::createScene() {
2792     QMap<Port *, WorkflowPortItem *> ports;
2793     foreach (Actor *actor, schema->getProcesses()) {
2794         WorkflowProcessItem *procItem = createProcess(actor);
2795         scene->addItem(procItem);
2796         foreach (WorkflowPortItem *portItem, procItem->getPortItems()) {
2797             ports[portItem->getPort()] = portItem;
2798         }
2799     }
2800 
2801     foreach (Link *link, schema->getFlows()) {
2802         createBus(ports, link);
2803     }
2804 
2805     WorkflowScene *result = scene;
2806     scene = nullptr;
2807     return result;
2808 }
2809 
createProcess(Actor * actor)2810 WorkflowProcessItem *SceneCreator::createProcess(Actor *actor) {
2811     WorkflowProcessItem *procItem = new WorkflowProcessItem(actor);
2812     bool contains = false;
2813     ActorVisualData visual = meta.getActorVisualData(actor->getId(), contains);
2814     if (!contains) {
2815         return procItem;
2816     }
2817     QPointF p = visual.getPos(contains);
2818     if (contains) {
2819         procItem->setPos(p);
2820     }
2821     QString s = visual.getStyle(contains);
2822     if (contains) {
2823         procItem->setStyle(s);
2824         {
2825             ItemViewStyle *eStyle = procItem->getStyleById(ItemStyles::EXTENDED);
2826             ItemViewStyle *sStyle = procItem->getStyleById(ItemStyles::SIMPLE);
2827             QColor c = visual.getColor(contains);
2828             if (contains) {
2829                 eStyle->setBgColor(c);
2830                 sStyle->setBgColor(c);
2831             }
2832             QFont f = visual.getFont(contains);
2833             if (contains) {
2834                 eStyle->setDefaultFont(f);
2835                 sStyle->setDefaultFont(f);
2836             }
2837             QRectF r = visual.getRect(contains);
2838             if (contains) {
2839                 qobject_cast<ExtendedProcStyle *>(eStyle)->setFixedBounds(r);
2840             }
2841         }
2842     }
2843     foreach (WorkflowPortItem *portItem, procItem->getPortItems()) {
2844         Port *port = portItem->getPort();
2845         qreal a = visual.getPortAngle(port->getId(), contains);
2846         if (contains) {
2847             portItem->setOrientation(a);
2848         }
2849     }
2850     return procItem;
2851 }
2852 
createBus(const QMap<Port *,WorkflowPortItem * > & ports,Link * link)2853 void SceneCreator::createBus(const QMap<Port *, WorkflowPortItem *> &ports, Link *link) {
2854     WorkflowPortItem *src = ports[link->source()];
2855     WorkflowPortItem *dst = ports[link->destination()];
2856     WorkflowBusItem *busItem = scene->addFlow(src, dst, link);
2857     ActorId srcActorId = src->getOwner()->getProcess()->getId();
2858     ActorId dstActorId = dst->getOwner()->getProcess()->getId();
2859 
2860     bool contains = false;
2861     QPointF p = meta.getTextPos(srcActorId, link->source()->getId(), dstActorId, link->destination()->getId(), contains);
2862     if (contains) {
2863         busItem->getText()->setPos(p);
2864     }
2865 }
2866 
2867 }  // namespace U2
2868