Logo Search packages:      
Sourcecode: ugene version File versions

WorkflowViewController.cpp

/*****************************************************************
* Unipro UGENE - Integrated Bioinformatics Suite
* Copyright (C) 2008 Unipro, Russia (http://ugene.unipro.ru)
* All Rights Reserved
* 
*     This source code is distributed under the terms of the
*     GNU General Public License. See the files COPYING and LICENSE
*     for details.
*****************************************************************/

#include "WorkflowDesignerPlugin.h"
#include "WorkflowViewController.h"
#include "WorkflowViewItems.h"
#include "WorkflowDocument.h"
#include "SceneSerializer.h"
#include "WorkflowIOTasks.h"
#include "WorkflowPalette.h"
#include "WorkflowEditor.h"
#include "WorkflowRunTask.h"
#include "WorkflowMetaDialog.h"
#include "ChooseItemDialog.h"
#include "SchemaConfigurationDialog.h"

#include <workflow/WorkflowModel.h>
#include <workflow/WorkflowRegistry.h>
#include <workflow/IntegralBusModel.h>
#include <workflow/WorkflowEnv.h>
#include <workflow/WorkflowManager.h>
#include <workflow_support/TypeMapEditor.h>
#include <workflow_support/WorkflowUtils.h>

#include <core_api/AppContext.h>
#include <core_api/MainWindow.h>
#include <core_api/Log.h>
#include <core_api/Settings.h>

#include <util_tasks/TaskSignalMapper.h>

#include <QtGui/QMenu>
#include <QtGui/QBoxLayout>
#include <QtGui/QSplitter>
#include <QtGui/QToolButton>

#include <QtSvg/QSvgGenerator>
#include <QtGui/QPrinter>
#include <QtGui/QPixmap>
#include <QtGui/QPainter>

#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtGui/QGraphicsView>
#include <QtGui/QComboBox>
#include <QtGui/QFileDialog>
#include <QtCore/QFileInfo>
#include <QtGui/QMessageBox>
#include <QtGui/QCloseEvent>
#include <QtGui/QToolBar>

#include <QtXml/qdom.h>

Q_DECLARE_METATYPE(GB2::StyleId);

/* TRANSLATOR GB2::LocalWorkflow::WorkflowView*/

namespace GB2 {

static LogCategory log(ULOG_CAT_WD);

#define SETTINGS QString("workflowview/")
#define LAST_DIR SETTINGS + "lastdir"
#define SPLITTER_STATE SETTINGS + "splitter"
#define EDITOR_STATE SETTINGS + "editor"
#define PALETTE_STATE SETTINGS + "palette"

#define WS 1000
WorkflowView::WorkflowView(WorkflowGObject* go) : MWMDIWindow(tr("Workflow Designer")), go(go),
         currentProc(NULL), clipbrd(NULL)
{
    scene = new WorkflowScene(this);
    scene->setSceneRect(QRectF(-WS, -WS, WS, WS));
    connect(scene, SIGNAL(processItemAdded()), SLOT(sl_procItemAdded()));
    connect(scene, SIGNAL(processDblClicked()), SLOT(sl_toggleStyle()));

    palette = new WorkflowPalette(WorkflowEnv::getProtoRegistry(), this);
    palette->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored));
    connect(palette, SIGNAL(processSelected(Workflow::ActorPrototype*)), 
        SLOT(sl_selectProcess(Workflow::ActorPrototype*)));

    infoList = new QListWidget(this);
    connect(infoList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(sl_pickInfo(QListWidgetItem*)));
    splitter = new QSplitter(this);
    splitter->addWidget(palette);

    QGraphicsView* sceneView = new QGraphicsView(scene);
    infoSplitter = new QSplitter(Qt::Vertical, splitter);
    infoSplitter->addWidget(sceneView);
    {
        QGroupBox* w = new QGroupBox(infoSplitter);
        w->setFlat(true);
        w->setTitle(tr("Error list"));
        QVBoxLayout* vl = new QVBoxLayout(w);
        vl->setSpacing(0);
        vl->setMargin(0);
        vl->addWidget(infoList);
        w->hide();
        infoSplitter->addWidget(w);
    }
    splitter->addWidget(infoSplitter);

    propertyEditor = new WorkflowEditor(this);
    //connect(scene, SIGNAL(selectionChanged()), propertyEditor, SLOT(clearContents()));
    //connect(scene, SIGNAL(selectionChanged()), propertyEditor, SLOT(hide()));
    connect(scene, SIGNAL(selectionChanged()), SLOT(sl_editItem()));
    splitter->addWidget(propertyEditor);

    Settings* settings = AppContext::getSettings();
    if (settings->contains(SPLITTER_STATE)) {
        splitter->restoreState(settings->getValue(SPLITTER_STATE).toByteArray());
    }
    if (settings->contains(EDITOR_STATE)) {
        propertyEditor->restoreState(settings->getValue(EDITOR_STATE));
    }
    if (settings->contains(PALETTE_STATE)) {
        palette->restoreState(settings->getValue(PALETTE_STATE));
    }
    splitter->setChildrenCollapsible(false);
    scene->views().at(0)->setDragMode(QGraphicsView::RubberBandDrag);
    scene->views().at(0)->setRubberBandSelectionMode(Qt::ContainsItemShape);
    
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(splitter);
    setLayout(layout);

    createActions();
    
    connect(scene, SIGNAL(configurationChanged()), SLOT(sl_refreshActorDocs()));
    connect(propertyEditor, SIGNAL(iterationSelected()), SLOT(sl_refreshActorDocs()));

    if (go) {
        //FIXME
        go->setView(this);
        QMap<ActorId, ActorId> remapping;
        QString err = SceneSerializer::xml2scene(go->getXML().documentElement(), scene, remapping);
        if (!err.isEmpty()) {
            scene->sl_reset();
            log.error(err);
        } else {
            QList<Iteration> lst;
            SchemaSerializer::readIterations(lst, go->getXML().documentElement(), remapping);
            scene->setIterations(lst);
            SchemaSerializer::readMeta(&meta, go->getXML().documentElement());
            meta.url = go->getDocument()->getURL();
            scene->setModified(false);
        }
    } else {
        sl_newScene();
    }
}

WorkflowView::~WorkflowView() {
      delete clipbrd;
}

static QString percentStr = WorkflowView::tr("%");

class PercentValidator : public QRegExpValidator {
public:
      PercentValidator(QObject* parent) : QRegExpValidator(QRegExp("[1-9][0-9]*"+percentStr), parent) {}
      void fixup(QString& input) const {
            if (!input.endsWith(percentStr)) {
                  input.append(percentStr);
            }
      }
};

void WorkflowView::sl_rescaleScene(const QString &scale)
{
      int percentPos = scale.indexOf(percentStr);
      double newScale = scale.left(percentPos).toDouble() / 100.0;
      QMatrix oldMatrix = scene->views().at(0)->matrix();
      scene->views().at(0)->resetMatrix();
      scene->views().at(0)->translate(oldMatrix.dx(), oldMatrix.dy());
      scene->views().at(0)->scale(newScale, newScale);
}


void WorkflowView::createActions() {
    runAction = new QAction(tr("&Run schema"), this);
    runAction->setIcon(QIcon(":workflow_designer/images/run.png"));
    runAction->setShortcut(QKeySequence("Ctrl+R"));
    connect(runAction, SIGNAL(triggered()), SLOT(sl_launch()));

    validateAction = new QAction(tr("&Validate schema"), this);
    validateAction->setIcon(QIcon(":workflow_designer/images/ok.png"));
    validateAction->setShortcut(QKeySequence("Ctrl+E"));
    connect(validateAction, SIGNAL(triggered()), SLOT(sl_validate()));

    newAction = new QAction(tr("&New schema"), this);
    newAction->setIcon(QIcon(":workflow_designer/images/filenew.png"));
      newAction->setShortcuts(QKeySequence::New);
    connect(newAction, SIGNAL(triggered()), SLOT(sl_newScene()));

    saveAction = new QAction(tr("&Save schema"), this);
    saveAction->setIcon(QIcon(":workflow_designer/images/filesave.png"));
      saveAction->setShortcuts(QKeySequence::Save);//("Ctrl+S"));
    connect(saveAction, SIGNAL(triggered()), SLOT(sl_saveScene()));

    saveAsAction = new QAction(tr("&Save schema as..."), this);
    connect(saveAsAction, SIGNAL(triggered()), SLOT(sl_saveSceneAs()));

    loadAction = new QAction(tr("&Load schema"), this);
    loadAction->setIcon(QIcon(":workflow_designer/images/fileopen.png"));
      loadAction->setShortcuts(QKeySequence::Open);//("Ctrl+L"));
    connect(loadAction, SIGNAL(triggered()), SLOT(sl_loadScene()));

    exportAction = new QAction(tr("&Export schema"), this);
    exportAction->setIcon(QIcon(":workflow_designer/images/export.png"));
    exportAction->setShortcut(QKeySequence("Ctrl+Shift+S"));
    connect(exportAction, SIGNAL(triggered()), SLOT(sl_exportScene()));

    deleteAction = new QAction(tr("Delete item"), this);
    deleteAction->setIcon(QIcon(":workflow_designer/images/delete.png"));
    deleteAction->setShortcuts(QKeySequence::Delete);
    connect(deleteAction, SIGNAL(triggered()), scene, SLOT(sl_deleteItem()));

    configureAction = new QAction(tr("Configure iterations"), this);
    configureAction->setIcon(QIcon(":workflow_designer/images/tag.png"));
    //configureAction ->setShortcut(QKeySequence::Delete);
    connect(configureAction , SIGNAL(triggered()), SLOT(sl_configure()));

    selectAction = new QAction(tr("Select all items"), this);
      selectAction->setShortcuts(QKeySequence::SelectAll);
    connect(selectAction, SIGNAL(triggered()), scene, SLOT(sl_selectAll()));

    bringToFrontAction = new QAction(tr("Bring to front"), this);
    bringToFrontAction->setIcon(QIcon(":workflow_designer/images/bringtofront.png"));
    connect(bringToFrontAction, SIGNAL(triggered()), scene, SLOT(sl_bringToFront()));

    sendToBackAction = new QAction(tr("Send to back"), this);
    sendToBackAction->setIcon(QIcon(":workflow_designer/images/sendtoback.png"));
    connect(sendToBackAction, SIGNAL(triggered()), scene, SLOT(sl_sendToBack()));

      copyAction = new QAction(tr("&Copy"), this);
      copyAction->setIcon(QIcon(":workflow_designer/images/editcopy.png"));
      copyAction->setShortcuts(QKeySequence::Copy);
      connect(copyAction, SIGNAL(triggered()), SLOT(sl_copyItems()));
      
      cutAction = new QAction(tr("Cu&t"), this);
      cutAction->setIcon(QIcon(":workflow_designer/images/editcut.png"));
      cutAction->setShortcuts(QKeySequence::Cut);
      connect(cutAction, SIGNAL(triggered()), SLOT(sl_cutItems()));

      pasteAction = new QAction(tr("&Paste"), this);
      pasteAction->setIcon(QIcon(":workflow_designer/images/editpaste.png"));
      pasteAction->setShortcuts(QKeySequence::Paste);
      connect(pasteAction, SIGNAL(triggered()), SLOT(sl_pasteItems()));

    sceneScaleCombo = new QComboBox(this);
      sceneScaleCombo->setEditable(true);
      sceneScaleCombo->setValidator(new PercentValidator(this));
    QStringList scales;
    scales << tr("25%") << tr("50%") << tr("75%") << tr("100%") << tr("125%") << tr("150%") << tr("200%");
    sceneScaleCombo->addItems(scales);
    sceneScaleCombo->setCurrentIndex(3);
    connect(sceneScaleCombo, SIGNAL(currentIndexChanged(const QString &)), SLOT(sl_rescaleScene(const QString &)));

    QAction* simpleStyle = new QAction(tr("Minimal"), this);
    simpleStyle->setData(QVariant(ItemStyles::SIMPLE));
    connect(simpleStyle, SIGNAL(triggered()), SLOT(sl_setStyle()));
    QAction* extStyle = new QAction(tr("Extended"), this);
    extStyle->setData(QVariant(ItemStyles::EXTENDED));
    connect(extStyle, SIGNAL(triggered()), SLOT(sl_setStyle()));
    styleActions << simpleStyle << extStyle;
}

void WorkflowView::sl_setStyle() {
    StyleId s = qobject_cast<QAction* >(sender())->data().value<StyleId>();
    QList<QGraphicsItem*> lst = scene->selectedItems();
    if (lst.isEmpty()) {
        lst = scene->items();
    }
    foreach(QGraphicsItem* it, lst) {
        switch (it->type()) {
            case WorkflowProcessItemType:
            case WorkflowPortItemType:
            case WorkflowBusItemType:
            ((StyledItem*)it)->setStyle(s);
        }
    }
    //update();
}

void WorkflowView::sl_toggleStyle() {
    foreach(QGraphicsItem* it, scene->selectedItems()) {
        StyleId s = ((StyledItem*)it)->getStyle();
        if (s == ItemStyles::SIMPLE) {
            s = ItemStyles::EXTENDED;
        } else {
            s = ItemStyles::SIMPLE;
        }
        ((StyledItem*)it)->setStyle(s);
    }
    //update();
}

void WorkflowView::sl_refreshActorDocs() {
    foreach(QGraphicsItem* it, scene->items()) {
        if (it->type() == WorkflowProcessItemType) {
            Actor* a = qgraphicsitem_cast<WorkflowProcessItem*>(it)->getProcess();
            a->getDescription()->update(propertyEditor->getCurrentIteration().getParameters(a->getId()));
        }
    }
}


void WorkflowView::setupMDIToolbar(QToolBar* tb) {
    tb->addAction(newAction);
    tb->addAction(loadAction);
    tb->addAction(saveAction);
    tb->addSeparator();
    tb->addAction(validateAction);
    tb->addAction(runAction);
    tb->addAction(configureAction);
    tb->addSeparator();
      tb->addAction(copyAction);
      tb->addAction(pasteAction);
      tb->addAction(cutAction);
    tb->addAction(deleteAction);
    tb->addSeparator();
    tb->addAction(bringToFrontAction);
    tb->addAction(sendToBackAction);
    tb->addSeparator();
    tb->addWidget(sceneScaleCombo);

    QToolButton* tt = new QToolButton(tb);
    QMenu* ttMenu = new QMenu(tr("Item style"), this);
    foreach(QAction* a, styleActions) {
        ttMenu->addAction(a);
    }
    tt->setDefaultAction(ttMenu->menuAction());
    tt->setPopupMode(QToolButton::InstantPopup);
    tb->addWidget(tt);
}

void WorkflowView::setupViewMenu(QMenu* m) {
    m->addMenu(palette->createMenu(tr("Add item")));
      m->addAction(copyAction);
      m->addAction(pasteAction);
      m->addAction(cutAction);
    m->addAction(deleteAction);
    m->addAction(selectAction);
    m->addSeparator();
    m->addAction(bringToFrontAction);
    m->addAction(sendToBackAction);
    m->addSeparator();
    m->addAction(newAction);
    m->addAction(loadAction);
    m->addAction(saveAction);
    m->addAction(saveAsAction);
    m->addAction(exportAction);
    m->addSeparator();
    m->addAction(validateAction);
    m->addAction(configureAction);
    m->addAction(runAction);
    m->addSeparator();

    QMenu* ttMenu = new QMenu(tr("Item style"));
    foreach(QAction* a, styleActions) {
        ttMenu->addAction(a);
    }
    m->addMenu(ttMenu);
}

void WorkflowView::setupContextMenu(QMenu* m, bool itemSelected) {
    if (itemSelected) {
            m->addAction(copyAction);
            m->addAction(cutAction);
        m->addAction(deleteAction);
        m->addSeparator();
        m->addAction(bringToFrontAction);
        m->addAction(sendToBackAction);
        m->addSeparator();
        QMenu* ttMenu = new QMenu(tr("Item style"));
        foreach(QAction* a, styleActions) {
            ttMenu->addAction(a);
        }
        m->addMenu(ttMenu);
    }
      if (clipbrd) {
            m->addAction(pasteAction);
      }
    m->addAction(selectAction);
    m->addMenu(palette->createMenu(tr("Add item")));
}

#define ACTOR_REF Qt::UserRole
#define PORT_REF Qt::UserRole + 1
#define ITERATION_REF Qt::UserRole + 2

void WorkflowView::sl_pickInfo(QListWidgetItem* info) {
    ActorId id = info->data(ACTOR_REF).value<ActorId>();
    foreach(QGraphicsItem* it, scene->items()) {
        if (it->type() == WorkflowProcessItemType)
        {
            WorkflowProcessItem* proc = (WorkflowProcessItem*)it;
            if (proc->getProcess()->getId() != id) {
                continue;
            } 
            scene->clearSelection();
            QString pid = info->data(PORT_REF).toString();
            WorkflowPortItem* port = proc->getPort(pid);
            if (port) {
                port->setSelected(true);
            } else {
                proc->setSelected(true);
                int itid = info->data(ITERATION_REF).toInt();
                propertyEditor->selectIteration(itid);
            }
            return;
        }
    }
}

bool WorkflowView::sl_validate(bool notify) {
    propertyEditor->commit();
    infoList->clear();
    Schema schema = scene->getSchema();
    bool good = true;
    foreach (Actor* a, schema.procs) {
        foreach(Port* p, a->getPorts()) {
            QStringList l;
            bool ag = p->validate(l);
            good &= ag;
            if (!l.isEmpty()) {
                foreach(QString s, l) {
                    QListWidgetItem* item = new QListWidgetItem(a->getProto()->icon, 
                        QString("%1 : %2").arg(a->getLabel()).arg(s));
                    item->setData(PORT_REF, p->getId());
                    item->setData(ACTOR_REF, a->getId());
                    infoList->addItem(item);
                }
            }
        }
    }

    foreach (Iteration it, scene->getIterations()) {
        Schema sh;
        QMap<ActorId, ActorId> map = SchemaSerializer::deepCopy(schema, &sh);
        sh.applyConfiguration(it, map);

        foreach (Actor* a, sh.procs) {
            QStringList l;
            bool ag = a->validate(l);
            good &= ag;
            if (!l.isEmpty()) {
                foreach(QString s, l) {
                    QListWidgetItem* item = new QListWidgetItem(a->getProto()->icon, 
                        tr("Iteration '%3', %1 : %2").arg(a->getLabel()).arg(s).arg(it.name));
                    item->setData(ACTOR_REF, map.key(a->getId()));
                    item->setData(ITERATION_REF, it.id);
                    //item->setToolTip(a->getDocumentation());
                    infoList->addItem(item);
                }
            }
        }
    }

    if (infoList->count() != 0) {
        infoList->parentWidget()->show();
        QList<int> s = infoSplitter->sizes();
        if (s.last() == 0) {
            s.last() = qMin(infoList->sizeHint().height(), 300);
            infoSplitter->setSizes(s);
        }
    } else {
        infoList->parentWidget()->hide();
    }
    if (!good) {

        QMessageBox::warning(this, tr("Schema cannot be executed"), 
            tr("Please fix issues listed in the error list (located under schema)."));
    } else {
        if (notify) {
            QMessageBox::information(this, tr("Schema is valid"), 
                tr("Schema is valid.\nWell done!"));
        }
    }
    return good;
}

void WorkflowView::sl_launch() {
    if (!sl_validate(false)) {
        return;
    }
    Schema sh = scene->getSchema();
    if (sh.domain.isEmpty()) {
        //|TODO user choice
        sh.domain = WorkflowEnv::getDomainRegistry()->getAllIds().value(0);
    }
    Task* t = new WorkflowRunTask(sh, scene->getIterations());
    t->setReportingEnabled(true);
    AppContext::getTaskScheduler()->registerTopLevelTask(t);
}

void WorkflowView::sl_configure() {
    propertyEditor->commit();
    SchemaConfigurationDialog d(scene->getSchema(), scene->getIterations(), this);
    int ret = d.exec();
    if (d.hasModifications()) {
        scene->setIterations(d.getIterations());
        propertyEditor->resetIterations();
    }
    if (QDialog::Accepted == ret) {
        sl_launch();
    }
}

void WorkflowView::sl_selectProcess(Workflow::ActorPrototype* p) {
    currentProc = p;
    propertyEditor->reset();
    if (!p) {
        //log.debug("deselected");
        scene->views().at(0)->unsetCursor();
    } else {
        propertyEditor->setDescriptor(p, tr("Drag the palette item to scene or just click on the scene to create a new object."));
        //log.debug(p->getDisplayName() + " selected");
        scene->views().at(0)->setCursor(Qt::CrossCursor);
    }
}

void WorkflowView::sl_copyItems() {
      QList<QGraphicsItem*> items = scene->selectedItems();
      if (items.isEmpty()) return;
      delete clipbrd;
      clipbrd = new QDomDocument();
      QDomElement root = clipbrd->createElement("root");
      clipbrd->appendChild(root);
      SceneSerializer::saveItems(items, root);
    //FIXME copy iteration data
    pasteCount = 0;
}

void WorkflowView::sl_cutItems() {
      sl_copyItems();
      scene->sl_deleteItem();
}

void WorkflowView::sl_pasteItems() {
      if (!clipbrd) return;
      scene->clearSelection();
    //FIXME copy iteration data

    QMap<ActorId, ActorId> stub;
    QString msg = SceneSerializer::xml2scene(clipbrd->documentElement(), scene, stub, true, true);
    if (!msg.isEmpty()) {
        log.trace("Paste issues: " + msg);
    }
      QGraphicsItemGroup* g = scene->createItemGroup(scene->selectedItems());
    int shift = 10*(++pasteCount);
      g->moveBy(shift, shift);
      scene->destroyItemGroup(g);
}

void WorkflowView::sl_procItemAdded() {
    if (!currentProc) return;
    log.trace(currentProc->getDisplayName() + " added");
    palette->resetSelection();
    currentProc = NULL;
    assert(scene->views().size() == 1);
    scene->views().at(0)->unsetCursor();
}

void WorkflowView::sl_showEditor() {
    propertyEditor->show();
    QList<int> s = splitter->sizes();
    if (s.last() == 0) {
        s.last() = propertyEditor->sizeHint().width();
        splitter->setSizes(s);
    }
}

void WorkflowView::sl_editItem() {
    //QList<Actor*> list = scene->getSelectedProcItems();
    QList<QGraphicsItem*> list = scene->selectedItems();
    if (list.size() == 1) {
        QGraphicsItem* it = list.at(0);
        if (it->type() == WorkflowProcessItemType) {
            propertyEditor->editActor(qgraphicsitem_cast<WorkflowProcessItem*>(it)->getProcess());
            return;
        }
        Port* p = NULL;
        if (it->type() == WorkflowBusItemType) {
            p = qgraphicsitem_cast<WBusItem*>(it)->getInPort()->getPort();
        } else if (it->type() == WorkflowPortItemType) {
            p = qgraphicsitem_cast<WorkflowPortItem*>(it)->getPort();
        }
        if (p) {
            if (qobject_cast<BusPort*>(p)) 
            {
                BusPortEditor* ed = new BusPortEditor(qobject_cast<BusPort*>(p));
                ed->setParent(p);
                p->setEditor(ed);
            }
        }
        propertyEditor->editPort(p);
    } else {
        propertyEditor->reset();
    }
}

void WorkflowView::sl_exportScene() {
    propertyEditor->commit();

    // TODO more export options and features
    QString dir = AppContext::getSettings()->getValue(LAST_DIR, QString("")).toString();
    QString filter = tr("Raster image (*.png *.bmp *.jpg *.jpeg *.ppm *.xbm *.xpm)");
    filter += "\n" + tr("Vector image (*.svg)");
    filter += "\n"+ tr("Portable document (*.pdf *.ps)");
    QString selectedFilter = "";//AppContext::getSettings()->getValue(LAST_FILE_FILTER, QString("")).toString();
    QString url = QFileDialog::getSaveFileName(0, tr("Export workflow schema to image"), dir, filter,&selectedFilter);
    if (!url.isEmpty()) {
        AppContext::getSettings()->setValue(LAST_DIR, QFileInfo(url).absoluteDir().absolutePath());
        log.details(tr("Saving scene image to file: %1").arg(url));
        bool result = false;
        QRectF bounds = scene->itemsBoundingRect();
        if (url.endsWith(".svg", Qt::CaseInsensitive)) {
            QSvgGenerator svg;
            svg.setFileName(url);
            svg.setSize(bounds.size().toSize());
            QPainter painter(&svg);
            painter.setRenderHint(QPainter::Antialiasing);
            scene->render(&painter, QRectF(), bounds);
            result = painter.end();
        } else if (url.endsWith(".pdf", Qt::CaseInsensitive) || url.endsWith(".ps", Qt::CaseInsensitive)) {
            QPrinter printer;
            printer.setOutputFileName(url);
            QPainter painter(&printer);
            painter.setRenderHint(QPainter::Antialiasing);
            scene->render(&painter, QRectF(), bounds);
            result = painter.end();
        } else {
            QPixmap pixmap(bounds.size().toSize());
            QPainter painter(&pixmap);
            painter.setRenderHint(QPainter::Antialiasing);
            scene->render(&painter, QRectF(), bounds);
            result = painter.end() & pixmap.save(url);
        }
        if (!result) {
            log.error(WorkflowView::tr("failed to save image to %1").arg(url));
        }
    }
}

void WorkflowView::sl_saveScene() {
    if (meta.url.isEmpty()) {
        WorkflowMetaDialog md(meta);
        int rc = md.exec();
        if (rc != QDialog::Accepted) {
            return;
        }
        meta = md.meta;
        sl_updateTitle();
    }
    propertyEditor->commit();
    Task* t = new SaveWorkflowSchemaTask(scene, meta); 
    AppContext::getTaskScheduler()->registerTopLevelTask(t);
}

void WorkflowView::sl_saveSceneAs() {
    WorkflowMetaDialog md(meta);
    int rc = md.exec();
    if (rc != QDialog::Accepted) {
        return;
    }
    propertyEditor->commit();
    meta = md.meta;
    Task* t = new SaveWorkflowSchemaTask(scene, meta);
    AppContext::getTaskScheduler()->registerTopLevelTask(t);
    sl_updateTitle();
}

void WorkflowView::sl_loadScene() {
    if (!confirmModified()) {
        return;
    }

    QString dir = AppContext::getSettings()->getValue(LAST_DIR, QString("")).toString();
    QString filter = DesignerUtils::getSchemaFileFilter(true);
    QString url = QFileDialog::getOpenFileName(0, tr("Open workflow schema file"), dir, filter);
    if (!url.isEmpty()) {
        AppContext::getSettings()->setValue(LAST_DIR, QFileInfo(url).absoluteDir().absolutePath());
        Task* t = new LoadWorkflowSchemaTask(scene, &meta, url); //FIXME unsynchronized meta usage
        TaskSignalMapper* m = new TaskSignalMapper(t);
        connect(m, SIGNAL(si_taskFinished()), SLOT(sl_updateTitle()));
        connect(m, SIGNAL(si_taskFinished()), propertyEditor, SLOT(resetIterations()));
        AppContext::getTaskScheduler()->registerTopLevelTask(t);
    }
}

void WorkflowView::sl_newScene() {
    if (!confirmModified()) {
        return;
    }
    infoList->parentWidget()->hide();
    scene->sl_reset();
    meta.reset();
    meta.name = tr("New schema");
    sl_updateTitle();
    propertyEditor->resetIterations();
    scene->setModified(false);
}

void WorkflowView::sl_updateTitle() {
    setWindowName(tr("Workflow Designer - %1").arg(meta.name));    
}

bool WorkflowView::onCloseEvent() {
    AppContext::getSettings()->setValue(SPLITTER_STATE, splitter->saveState());
    AppContext::getSettings()->setValue(EDITOR_STATE, propertyEditor->saveState());
    AppContext::getSettings()->setValue(PALETTE_STATE, palette->saveState());
    if (!confirmModified()) {
        return false;
    }
    if (go) {
        go->setView(NULL);
    }
    return true;
}

bool WorkflowView::confirmModified() {
    propertyEditor->commit();
    if (scene->isModified()) {
        AppContext::getMainWindow()->getMDIManager()->activateWindow(this);
        int ret = QMessageBox::question(this, tr("Workflow Designer"),
            tr("The schema has been modified.\n"
            "Do you want to save changes?"),
            QMessageBox::Save | QMessageBox::Discard
            | QMessageBox::Cancel,
            QMessageBox::Save);
        if (QMessageBox::Cancel == ret) {
            return false;
        } else if (QMessageBox::Discard == ret) {
            //scene->setModified(false);
        } else {
            sl_saveScene();
        }
    }
    return true;
}


WorkflowScene::WorkflowScene(WorkflowView *parent) 
: QGraphicsScene(parent), controller(parent), modified(false) {
}

Schema WorkflowScene::getSchema() const {
    Schema schema;
    foreach(QGraphicsItem* it, items()) {
        if (it->type() == WorkflowProcessItemType) 
        {
            schema.procs << ((WorkflowProcessItem*)it)->getProcess();
        } 
        else if (it->type() == WorkflowBusItemType) 
        {
            schema.flows << ((WBusItem*)it)->getBus();
        }
    }

    return schema;
}

void WorkflowScene::sl_deleteItem() {
    //FIXME cleanup iterations as well
    QList<QGraphicsItem*> items;
    QList<ActorId> ids;
    foreach(QGraphicsItem* it, selectedItems()) {
        if (it->type() == WorkflowPortItemType) {
            continue;
        }
        items << it;
        if (it->type() == WorkflowProcessItemType) {
            ids << qgraphicsitem_cast<WorkflowProcessItem*>(it)->getProcess()->getId();
        }
    }
    modified |= !items.isEmpty();
    foreach(QGraphicsItem* it, items) {
        removeItem(it);
        delete it;
    }
    bool cfgModified = false;
    for (int i = 0; i<iterations.size(); i++) {
        Iteration& it = iterations[i];
        foreach(IterationCfgKey key, it.cfg.uniqueKeys()) {
            if (ids.contains(key.first)) {
                it.cfg.remove(key);
                cfgModified = true;
            }
        }
    }
    if (cfgModified) {
        controller->propertyEditor->resetIterations();
        emit configurationChanged();
    }
    update();
}

QList<Actor*> WorkflowScene::getSelectedProcItems() const {
    QList<Actor*> list;
    foreach (QGraphicsItem *item, selectedItems()) {
        if (item->type() == WorkflowProcessItemType) {
            list << static_cast<WorkflowProcessItem*>(item)->getProcess();
        }
    }
    return list;
}

void WorkflowScene::contextMenuEvent(QGraphicsSceneContextMenuEvent * e) {
    QMenu menu;
    controller->setupContextMenu(&menu, !selectedItems().isEmpty());
    e->accept();
    menu.exec(e->screenPos());
}

void WorkflowScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent) {
    if (!mouseEvent->isAccepted() && (mouseEvent->button() == Qt::LeftButton) && !selectedItems().isEmpty()) {
        emit processDblClicked();
    }
    QGraphicsScene::mousePressEvent(mouseEvent);
}

static bool canDrop(const QMimeData* m, QList<ActorPrototype*>& lst) {
    if (m->hasFormat(WorkflowPalette::MIME_TYPE)) {
        QString id(m->data(WorkflowPalette::MIME_TYPE));
        ActorPrototype* proto = WorkflowEnv::getProtoRegistry()->getProto(id);
        if (proto) {
            lst << proto;
        }
    } else {
        foreach(QList<ActorPrototype*> l, WorkflowEnv::getProtoRegistry()->getProtos().values()) {
            foreach(ActorPrototype* proto, l) {
                if (proto->isAcceptableDrop(m)) {
                    lst << proto;
                }
            }
        }
    }
    //foreach(ActorPrototype* a, lst) {log.debug("drop acceptable: " + a->getId());}
    return !lst.isEmpty();
}

void WorkflowScene::dragEnterEvent(QGraphicsSceneDragDropEvent * event) {
    QList<ActorPrototype*> lst;
    if (canDrop(event->mimeData(), lst)) {
        event->acceptProposedAction();
    } 
    else
    {
        QGraphicsScene::dragEnterEvent(event);
    }
}

void WorkflowScene::dragMoveEvent(QGraphicsSceneDragDropEvent * event) {
    QList<ActorPrototype*> lst;
        if (canDrop(event->mimeData(), lst)) {
        event->acceptProposedAction();
    } 
    else 
    {
        QGraphicsScene::dragMoveEvent(event);
    }
}

void WorkflowScene::dropEvent(QGraphicsSceneDragDropEvent * event) {
    QList<ActorPrototype*> lst;
    if (canDrop(event->mimeData(), lst))
    {
        QList<QGraphicsItem *> targets = items(event->scenePos());
        bool done = false;
        foreach(QGraphicsItem* it, targets) {
            WorkflowProcessItem* target = qgraphicsitem_cast<WorkflowProcessItem*>(it);
            if (target && lst.contains(target->getProcess()->getProto())) {
                clearSelection();
                QVariantMap params;
                Actor* a = target->getProcess();
                a->getProto()->isAcceptableDrop(event->mimeData(), &params);
                QMapIterator<QString, QVariant> cfg(params);
                while (cfg.hasNext())
                {
                    cfg.next();
                    a->setParameter(cfg.key(),cfg.value());
                }
                target->setSelected(true);
                done = true;
                break;
            }
        }
        if (!done) {
            ActorPrototype* proto = lst.size() > 1 ? ChooseItemDialog(controller).select(lst) : lst.first();
            if (proto) {
                QVariantMap params;
                proto->isAcceptableDrop(event->mimeData(), &params);
                addProcess(proto->createInstance(params), event->scenePos());
                event->setDropAction(Qt::CopyAction);
            }
        }
    } 
    QGraphicsScene::dropEvent(event);
}


static int count = 0;
void WorkflowScene::addProcess(Actor* proc, const QPointF& pos) {
    WorkflowProcessItem* it = new WorkflowProcessItem(proc);
    addItem(it);
    it->setPos(pos);
    it->createPorts();
    Actor* a = it->getProcess();
    a->setLabel(proc->getProto()->getDisplayName() + QString(" %1").arg(++count));
    modified = true;
    emit processItemAdded();
}

void WorkflowScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) {
    if (!mouseEvent->isAccepted() && controller->selectedProto() && (mouseEvent->button() == Qt::LeftButton)) {
        addProcess(controller->selectedProto()->createInstance(), mouseEvent->scenePos());
    }
    QGraphicsScene::mousePressEvent(mouseEvent);
}

void WorkflowScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) {
    QGraphicsScene::mouseMoveEvent(mouseEvent);
}
void WorkflowScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) {
    QGraphicsScene::mouseReleaseEvent(mouseEvent);
}

void WorkflowScene::sl_bringToFront()
{
    if (selectedItems().isEmpty())
        return;

    QGraphicsItem *selectedItem = selectedItems().first();
    qreal zValue = 0;
    foreach (QGraphicsItem *item, selectedItem->collidingItems()) {
        if (item->zValue() >= zValue /*&& item->type() == WorkflowProcessItemType*/)
            zValue = item->zValue() + 1;
    }
    selectedItem->setZValue(zValue);
    modified = true;
}

void WorkflowScene::sl_sendToBack()
{
    if (selectedItems().isEmpty())
        return;

    QGraphicsItem *selectedItem = selectedItems().first();
    qreal zValue = 0;
    foreach (QGraphicsItem *item, selectedItem->collidingItems()) {
        if (item->zValue() <= zValue /*&& item->type() == WorkflowProcessItemType*/)
            zValue = item->zValue() - 1;
    }
    selectedItem->setZValue(zValue);
    modified = true;
}

void WorkflowScene::sl_selectAll() {
    foreach(QGraphicsItem* it, items()) {
        //if (it->type() == WorkflowProcessItemType) 
        {
            it->setSelected(true);
        }
    }
}

void WorkflowScene::sl_reset() {
    QList<QGraphicsItem*> list;
    foreach(QGraphicsItem* it, items()) {
        if (it->type() == WorkflowProcessItemType) {
            list << it;
        } //else if (it->type() == WorkflowBusItemType)
    }
    modified = false;
    foreach(QGraphicsItem* it, list) {
        removeItem(it);
        delete it;
    }
    iterations.clear();
}

WorkflowScene::~WorkflowScene()
{
    sl_reset();
}
}//namespace

Generated by  Doxygen 1.6.0   Back to index