//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2016 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#include "CudaExport.hpp"
#include "Version.hpp"

#ifndef WIN32
#  include <dlfcn.h>
#endif

#include <QPointer>
#include <QMessageBox>
#include <QToolButton>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QUrl>
#include <QList>
#include <QFileInfo>
#include <QSettings>
#include <QPushButton>
#include <QMutexLocker>
#include <QFileSystemWatcher>
#include <QTimer>
#include <QDesktopServices>
#include <QFileDialog>
#include <QInputDialog>
#include <SystemProcess.hpp>
#include <Progress.hpp>
#include <QShortcut>

#include <Process.hpp>
#include <PrivateProcess.hpp>

#include <Colors.hpp>
#include <ColorEditorDlg.hpp>
#include <LabelEditorDlg.hpp>
#include <MorphoGraphX.hpp>
#include <Forall.hpp>
#include <ProcessParmsModel.hpp>
#include <ProcessThread.hpp>
#include <ProcessDocsDlg.hpp>
#include <Dir.hpp>
#include <DebugDlg.hpp>
#include <Library.hpp>
#include <PathEditorDlg.hpp>
#include <Geometry.hpp>
#include <About.hpp>
#include <Information.hpp>

#include <memory>
#include <typeinfo>

#include <omp.h>
#include <sched.h>

#ifdef WIN32
// ensure nvidia recognises this is a 3D program
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
#endif

using namespace mgx;

enum TasksTreeRole { TaskNameRole = Qt::UserRole, ProcessPositionRole = Qt::UserRole + 1 };

QString MorphoGraphX::AppDir = QString();
QLabel* MorphoGraphX::ActiveStack;

MorphoGraphX::~MorphoGraphX() {}

MorphoGraphX::MorphoGraphX(QString appdir,QStringList addLibraries)
  : QMainWindow(), stack1(0, this), stack2(1, this), needSaving(false),
    currentProcess(0), currentSetup(0), processThread(0), editPathsDlg(0)
{
  setAcceptDrops(true);
  Information::out << introText() << endl;
  AppDir = appdir;
  ui.setupUi(this);

  createSetupProcess();

  stack1.init(currentSetup->stack(0), currentSetup->mesh(0));
  stack2.init(currentSetup->stack(1), currentSetup->mesh(1));

  Information::init(this);

  // Create progress widget and setup progress class
  mgx::Progress::instance().setupProgress(this, ui.progressToolBar);
  progressStart("", 0, false);

  // EditParms dialog
  editParmsDlog = new QDialog(this);
  editParmsDialog = new Ui_EditParmsDialog;
  editParmsDialog->setupUi(editParmsDlog);
  QPushButton* saveAs = editParmsDialog->OKCancel->addButton("Save As", 
                                                  QDialogButtonBox::ActionRole);
  saveAs->setText("Save As");
  connect(saveAs, SIGNAL(clicked()), this, SLOT(EditParmsSaveAsSlot()));

  // Connect control In slots
  connectControlsIn();

  parmsModel = new ProcessParmsModel(this);
  ui.ProcessParameters->setModel(parmsModel);
  ui.ProcessParameters->setItemDelegateForColumn(1, new FreeFloatDelegate(this));
  ui.ProcessParameters->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
  ui.ProcessParameters->resizeColumnToContents(0);
  connect(parmsModel, SIGNAL(valuesChanged()), this, SLOT(storeParameters()));

  resetParametersAct = new QAction("Reset to default", this);
  connect(resetParametersAct, SIGNAL(triggered()), this, SLOT(resetDefaultParameters()));

  ui.ProcessParameters->addAction(resetParametersAct);

  editTasksAct = new QAction("Edit tasks", this);
  connect(editTasksAct, SIGNAL(triggered()), this, SLOT(editTasks()));

  ui.ProcessTasksCommand->addAction(editTasksAct);

  // Create system processes
  // loadSystemProcesses();

  // First, make sure the factory lists is created here
  processFactories();

#ifdef WATCH_PROCESS_FOLDERS
  // Keep an eye on the process directory
  QString process = processesDir().absolutePath();
  processWatcher = new QFileSystemWatcher(this);
  processWatcher->addPath(process);
  processReloadTimer = new QTimer(this);
  processReloadTimer->setSingleShot(true);
  processReloadTimer->setInterval(100);
  connect(processWatcher, SIGNAL(directoryChanged(const QString &)), processReloadTimer, SLOT(start()));
  connect(processWatcher, SIGNAL(fileChanged(const QString &)), processReloadTimer, SLOT(start()));
  connect(processReloadTimer, SIGNAL(timeout()), this, SLOT(ReloadProcesses()));
#endif

  // Initialize cuda
  initGPU();

  // Set affinity
  #ifdef WIN32
    omp_set_dynamic(1);
  #else
    cpu_set_t my_set;
    CPU_ZERO(&my_set);
    int numCPUs = sysconf(_SC_NPROCESSORS_ONLN);   // Linux only?
    for(int i = 0; i < numCPUs; i++)
      CPU_SET(i, &my_set);     /* set the bit that represents core i. */
    sched_setaffinity(0, sizeof(cpu_set_t), &my_set);

    // Ask OpenMP to use all processors
    omp_set_dynamic(0);
    omp_set_num_threads(numCPUs);
  #endif

  Information::out << "OpenMP Processors: " << omp_get_num_procs() << endl;
  Information::out << "OpenMP Max threads: " << omp_get_max_threads() << endl << endl;

  // Added process libraries
  {
    QList<QDir> dirs = mgx::processDirs();
    for(int i = 0 ; i < addLibraries.size() ; i++) {
      QFileInfo qf(addLibraries.at(i));
      if(addedLibFiles.contains(qf)) 
        continue;
      else if(!(qf.exists())) {
        mgx::Information::out << "Library file " << addLibraries.at(i) << " does not exist." << endl;
      } else {
        for(int d = 0 ; d < dirs.size() ; d++) {
        if(qf.canonicalPath() == dirs.at(d).canonicalPath()) {
          mgx::Information::out << "Library file " << addLibraries.at(i)
                 << " is already included in process directory "
                 << dirs.at(d).path() << endl;
          goto libFileAlreadyIncluded;
        }
      }
      addedLibFiles << qf;
libFileAlreadyIncluded:
      continue;
      }
    }

    if(addedLibFiles.size() > 0) {
      mgx::Information::out << "Libraries added from command line:";
      for(int i = 0 ; i < addedLibFiles.size() ; i++)
        mgx::Information::out << " " << addedLibFiles.at(i).filePath();
      mgx::Information::out << endl << endl;
    }
  }

  ReloadProcesses();
  SaveProcessesParameters();

  // Restore geometry and settings
  {
    QSettings settings;
    settings.beginGroup("MainWindow");
    restoreGeometry(settings.value("Geometry").toByteArray());
    restoreState(settings.value("WindowState").toByteArray(), 1);
    settings.endGroup();
  }

  // Set up ActiveStack widget;
  ActiveStack = new QLabel;
  ui.statusBar->addPermanentWidget(ActiveStack);
  ActiveStack->setText("Main Stack 1 Active");

  // Load data into controls (before connecting)
  loadControls();

  // Connect control In slots
  connectControlsOut();

  // connect the different controls
  connect(&stack1, SIGNAL(changeSize(const Point3u &, const Point3f &, const Point3f &)), this,
          SLOT(changeStack1SizeSlot(const Point3u &, const Point3f &, const Point3f &)));
  connect(&stack2, SIGNAL(changeSize(const Point3u &, const Point3f &, const Point3f &)), this,
          SLOT(changeStack2SizeSlot(const Point3u &, const Point3f &, const Point3f &)));
  connect(&stack1, SIGNAL(stackUnloaded()), this, SLOT(stack1UnloadedSlot()));
  connect(&stack2, SIGNAL(stackUnloaded()), this, SLOT(stack2UnloadedSlot()));

  connect(ui.Viewer, SIGNAL(modified()), this, SLOT(modified()));

  stack1.initControls(ui.Viewer);
  stack2.initControls(ui.Viewer);

  connect(&stack1, SIGNAL(changedInterface()), this, SLOT(modified()));
  connect(&stack2, SIGNAL(changedInterface()), this, SLOT(modified()));

  // send stack and plane pointers to Viewer
  ui.Viewer->stack1 = &stack1;
  ui.Viewer->stack2 = &stack2;
  ui.Viewer->cutSurf = &cutSurf;

  // Start with clipping plane as manipulated frame
  // ui.Clip1Rotate->setChecked(true);
  RotateSlot(true);

  // Connect debug dialog
  connect(ui.actionDebug_Dialog, SIGNAL(triggered()), this, SLOT(DebugDialogSlot()));

  // Setup the shortcuts
  QAction *altQ = new QAction(this);
  altQ->setShortcut(Qt::Key_Q | Qt::ALT);

  QAction *altW = new QAction(this);
  altW->setShortcut(Qt::Key_W | Qt::ALT);

  QAction *altE = new QAction(this);
  altE->setShortcut(Qt::Key_E | Qt::ALT);

  QAction *altD = new QAction(this);
  altD->setShortcut(Qt::Key_D | Qt::ALT);

  QAction *altS = new QAction(this);
  altS->setShortcut(Qt::Key_S | Qt::ALT);


  QAction *ctrlAltZ = new QAction(this);
  ctrlAltZ->setShortcut(Qt::Key_Z | Qt::ALT | Qt::CTRL);

  QAction *ctrlAltX = new QAction(this);
  ctrlAltX->setShortcut(Qt::Key_X | Qt::ALT | Qt::CTRL);

  connect(altQ, SIGNAL(triggered()), this, SLOT(toggleControlsTab()));
  this->addAction(altQ);
  connect(altW, SIGNAL(triggered()), this, SLOT(toggleStackTabs()));
  this->addAction(altW);
  connect(altE, SIGNAL(triggered()), this, SLOT(toggleControlKeyInteraction()));
  this->addAction(altE);
  connect(altD, SIGNAL(triggered()), this, SLOT(toggleWorkCheckBox()));
  this->addAction(altD);
  connect(altS, SIGNAL(triggered()), this, SLOT(toggleMainCheckBox()));
  this->addAction(altS);

  connect(ctrlAltZ, SIGNAL(triggered()), this, SLOT(toggleMeshSurfaceView()));
  this->addAction(ctrlAltZ);
  connect(ctrlAltX, SIGNAL(triggered()), this, SLOT(toggleMeshViewDropdown()));
  this->addAction(ctrlAltX);
}

void MorphoGraphX::createSetupProcess()
{
  currentSetup = new SetupProcess(this);
  Stack* s1 = currentSetup->addStack();
  s1->main()->setTransferFct(TransferFunction::scale_green());
  s1->work()->setTransferFct(TransferFunction::scale_cyan());
  Stack* s2 = currentSetup->addStack();
  s2->main()->setTransferFct(TransferFunction::scale_red());
  s2->work()->setTransferFct(TransferFunction::scale_yellow());
  Mesh* m1 = currentSetup->addMesh(s1);
  m1->setSurfFct(TransferFunction::scale_gray());
  m1->setHeatFct(TransferFunction::jet());
  Mesh* m2 = currentSetup->addMesh(s2);
  m2->setSurfFct(TransferFunction::scale_gray());
  m2->setHeatFct(TransferFunction::jet());
  currentSetup->p->camera = ui.Viewer->_camera;

  ui.Viewer->clip1.setClip(currentSetup->clip1());
  ui.Viewer->clip2.setClip(currentSetup->clip2());
  ui.Viewer->clip3.setClip(currentSetup->clip3());
  ui.Viewer->c1 = currentSetup->clip1();
  ui.Viewer->c2 = currentSetup->clip2();
  ui.Viewer->c3 = currentSetup->clip3();
  cutSurf.cut = currentSetup->cuttingSurface();
}

void MorphoGraphX::setDebug(bool debug)
{
  if(!debug)
    ui.actionDebug_Dialog->setVisible(false);
  ui.menuFileEditPaths->setVisible(false);
}

void MorphoGraphX::loadControls()
{
  // RSS A lot of this code is duplicated in updateStateFromProcess
  // Load clipplane info into controls
  ui.Clip1Enable->setChecked(ui.Viewer->c1->enabled());
  ui.Clip1Grid->setChecked(ui.Viewer->c1->grid());
  ui.Clip1Width->setSliderPosition(int(2000 * log(ui.Viewer->c1->width() * 2.0 / ui.Viewer->getSceneRadius())));
  ui.Clip2Enable->setChecked(ui.Viewer->c2->enabled());
  ui.Clip2Grid->setChecked(ui.Viewer->c2->grid());
  ui.Clip2Width->setSliderPosition(int(2000 * log(ui.Viewer->c2->width() * 2.0 / ui.Viewer->getSceneRadius())));
  ui.Clip3Enable->setChecked(ui.Viewer->c3->enabled());
  ui.Clip3Grid->setChecked(ui.Viewer->c3->grid());
  ui.Clip3Width->setSliderPosition(int(2000 * log(ui.Viewer->c3->width() * 2.0 / ui.Viewer->getSceneRadius())));

  ui.Clip1Width->setDefaultValue(0);
  ui.Clip2Width->setDefaultValue(0);
  ui.Clip3Width->setDefaultValue(0);

  ui.GlobalContrast->setValue(int(ui.Viewer->GlobalContrast * 5000.0f));
  ui.GlobalBrightness->setValue(int((ui.Viewer->GlobalBrightness + 1.0f) * 5000.0f));
  ui.GlobalShininess->setValue(int(ui.Viewer->GlobalShininess/128.0 * 5000.0f));
  ui.GlobalSpecular->setValue(int(ui.Viewer->GlobalSpecular * 5000.0f));

  ui.GlobalBrightness->setDefaultValue(5000);
  ui.GlobalContrast->setDefaultValue(5000);
  ui.GlobalShininess->setDefaultValue(160);
  ui.GlobalSpecular->setDefaultValue(1000);

  // Load controls for Stack 1
  ui.Stack1MainShow->setChecked(stack1.stack->main()->isVisible());
  ui.Stack1MainBright->setValue(int(stack1.stack->main()->brightness() * 10000.0));
  ui.Stack1MainOpacity->setValue(int(stack1.stack->main()->opacity() * 10000.0));
  ui.Stack1MainLabels->setChecked(stack1.stack->main()->labels());
  ui.Stack1Main16Bit->setChecked(stack1.Main16Bit);

  ui.Stack1WorkShow->setChecked(stack1.stack->work()->isVisible());
  ui.Stack1WorkBright->setValue(int(stack1.stack->work()->brightness() * 10000.0));
  ui.Stack1WorkOpacity->setValue(int(stack1.stack->work()->opacity() * 10000.0));
  ui.Stack1WorkLabels->setChecked(stack1.stack->work()->labels());
  ui.Stack1Work16Bit->setChecked(stack1.Work16Bit);

  ui.Stack1SurfShow->setChecked(stack1.mesh->showSurface());
  ui.Stack1SurfBright->setValue(int(stack1.mesh->brightness() * 10000.0));
  ui.Stack1SurfOpacity->setValue(int(stack1.mesh->opacity() * 10000.0));
  ui.Stack1SurfBlend->setChecked(stack1.mesh->blending());
  ui.Stack1SurfCull->setChecked(stack1.mesh->culling());
  ui.Stack1SurfParent->setChecked(stack1.mesh->useParents());

  switch(stack1.mesh->surfView()) {
  case Mesh::SURF_VERTEX:
    ui.Stack1SurfVertex->setChecked(true);
    break;
  case Mesh::SURF_TRIANGLE:
    ui.Stack1SurfTriangle->setChecked(true);
    break;
  case Mesh::SURF_LABEL:
    ui.Stack1SurfLabel->setChecked(true);
    break;
  }
  int index = 0;
  if((index = ui.Stack1VertexView->findText(stack1.mesh->surfVertexView())) >= 0)
    ui.Stack1VertexView->setCurrentIndex(index);
  if((index = ui.Stack1TriangleView->findText(stack1.mesh->surfTriangleView())) >= 0)
    ui.Stack1TriangleView->setCurrentIndex(index);
  if((index = ui.Stack1LabelView->findText(stack1.mesh->surfLabelView())) >= 0)
    ui.Stack1LabelView->setCurrentIndex(index);

  ui.Stack1MeshShow->setChecked(stack1.mesh->showMesh());
  ui.Stack1AxisShow->setChecked(stack1.mesh->showAxis());
  if((index = ui.Stack1MeshView->findText(stack1.mesh->meshView())) >= 0)
    ui.Stack1MeshView->setCurrentIndex(index);
  if((index = ui.Stack1AxisView->findText(stack1.mesh->axisView())) >= 0)
    ui.Stack1AxisView->setCurrentIndex(index);
  ui.Stack1MeshLines->setChecked(stack1.mesh->showMeshLines());
  ui.Stack1MeshPoints->setChecked(stack1.mesh->showMeshPoints());

  ui.Stack1CellMap->setChecked(stack1.mesh->showMeshCellMap());
  ui.Stack1ShowTrans->setChecked(stack1.stack->showTrans());
  ui.Stack1ShowBBox->setChecked(stack1.stack->showBBox());
  ui.Stack1ShowScale->setChecked(stack1.stack->showScale());
  ui.Stack1TieScales->setChecked(stack1.stack->tieScales());
  ui.Stack1Scale_X->setValue(stack1.stack->scale().x());
  ui.Stack1Scale_Y->setValue(stack1.stack->scale().y());
  ui.Stack1Scale_Z->setValue(stack1.stack->scale().z());

  ui.Stack1Scale_X->setDefaultValue(0);
  ui.Stack1Scale_Y->setDefaultValue(0);
  ui.Stack1Scale_Z->setDefaultValue(0);

  // Load controls for Stack 2
  ui.Stack2MainShow->setChecked(stack2.stack->main()->isVisible());
  ui.Stack2MainBright->setValue(int(stack2.stack->main()->brightness() * 10000.0f));
  ui.Stack2MainOpacity->setValue(int(stack2.stack->main()->opacity() * 10000.0f));
  ui.Stack2MainLabels->setChecked(stack2.stack->main()->labels());
  ui.Stack2Main16Bit->setChecked(stack2.Main16Bit);

  ui.Stack2WorkShow->setChecked(stack2.stack->work()->isVisible());
  ui.Stack2WorkBright->setValue(int(stack2.stack->work()->brightness() * 10000.0f));
  ui.Stack2WorkOpacity->setValue(int(stack2.stack->work()->opacity() * 10000.0f));
  ui.Stack2WorkLabels->setChecked(stack2.stack->work()->labels());
  ui.Stack2Work16Bit->setChecked(stack2.Work16Bit);

  ui.Stack2SurfShow->setChecked(stack2.mesh->showSurface());
  ui.Stack2SurfBright->setValue(int(stack2.mesh->brightness() * 10000.0f));
  ui.Stack2SurfOpacity->setValue(int(stack2.mesh->opacity() * 10000.0f));
  ui.Stack2SurfBlend->setChecked(stack2.mesh->blending());
  ui.Stack2SurfCull->setChecked(stack2.mesh->culling());
  ui.Stack2SurfParent->setChecked(stack2.mesh->useParents());

  switch(stack2.mesh->surfView()) {
  case Mesh::SURF_VERTEX:
    ui.Stack2SurfVertex->setChecked(true);
    break;
  case Mesh::SURF_TRIANGLE:
    ui.Stack2SurfTriangle->setChecked(true);
    break;
  case Mesh::SURF_LABEL:
    ui.Stack2SurfLabel->setChecked(true);
    break;
  }
  if((index = ui.Stack2VertexView->findText(stack2.mesh->surfVertexView())) >= 0)
    ui.Stack2VertexView->setCurrentIndex(index);
  if((index = ui.Stack2TriangleView->findText(stack2.mesh->surfTriangleView())) >= 0)
    ui.Stack2TriangleView->setCurrentIndex(index);
  if((index = ui.Stack2LabelView->findText(stack2.mesh->surfLabelView())) >= 0)
    ui.Stack2LabelView->setCurrentIndex(index);

  ui.Stack2MeshShow->setChecked(stack2.mesh->showMesh());
  ui.Stack2AxisShow->setChecked(stack2.mesh->showAxis());
  if((index = ui.Stack2MeshView->findText(stack2.mesh->meshView())) >= 0)
    ui.Stack2MeshView->setCurrentIndex(index);
  if((index = ui.Stack2AxisView->findText(stack2.mesh->axisView())) >= 0)
    ui.Stack2AxisView->setCurrentIndex(index);
  ui.Stack2MeshLines->setChecked(stack2.mesh->showMeshLines());
  ui.Stack2MeshPoints->setChecked(stack2.mesh->showMeshPoints());

  ui.Stack2CellMap->setChecked(stack2.mesh->showMeshCellMap());
  ui.Stack2ShowTrans->setChecked(stack2.stack->showTrans());
  ui.Stack2ShowBBox->setChecked(stack2.stack->showBBox());
  ui.Stack2ShowScale->setChecked(stack2.stack->showScale());
  ui.Stack2TieScales->setChecked(stack2.stack->tieScales());
  ui.Stack2Scale_X->setValue(stack2.stack->scale().x());
  ui.Stack2Scale_Y->setValue(stack2.stack->scale().y());
  ui.Stack2Scale_Z->setValue(stack2.stack->scale().z());

  ui.Stack2Scale_X->setDefaultValue(0);
  ui.Stack2Scale_Y->setDefaultValue(0);
  ui.Stack2Scale_Z->setDefaultValue(0);

  // Load global settings
  ui.VoxelEditRadius->setValue(ImgData::VoxelEditRadius);
  ui.Slices->setValue(ImgData::Slices);
  ui.ScreenSampling->setValue(int(ui.Viewer->sampling * 10) - 10);

  ui.Slices->setDefaultValue(250);
  ui.ScreenSampling->setDefaultValue(10);
  ui.VoxelEditRadius->setDefaultValue(25);

  ui.FillWorkData->setChecked(ImgData::FillWorkData);
  ui.SeedStack->setChecked(ImgData::SeedStack);
  ui.DrawCutSurf->setChecked(currentSetup->cuttingSurface()->isVisible());
  CuttingSurface* cut = currentSetup->cuttingSurface();
  switch(cut->mode()) {
  case CuttingSurface::THREE_AXIS:
    ui.ThreeAxis->setChecked(true);
    break;
  case CuttingSurface::PLANE:
    ui.CutSurfPlane->setChecked(true);
    break;
  case CuttingSurface::BEZIER:
    ui.CutSurfBezier->setChecked(true);
    break;
  }
  ui.CutSurfGrid->setChecked(cut->drawGrid());
  SetCutSurfControl();

  // ui.CutSurfSizeX->setDefaultValue(0);
  // ui.CutSurfSizeY->setDefaultValue(0);
  // ui.CutSurfSizeZ->setDefaultValue(0);
}

void MorphoGraphX::connectControlsIn()
{
  // Connect incoming signals to widgets
  connect(&cutSurf, SIGNAL(DrawCutSurfSignal(bool)), ui.DrawCutSurf, SLOT(setChecked(bool)));
  connect(&cutSurf, SIGNAL(ThreeAxisSignal(bool)), ui.ThreeAxis, SLOT(setChecked(bool)));
  connect(&cutSurf, SIGNAL(CutSurfGridSignal(bool)), ui.CutSurfGrid, SLOT(setChecked(bool)));
  connect(&cutSurf, SIGNAL(CutSurfPlaneSignal(bool)), ui.CutSurfPlane, SLOT(setChecked(bool)));
  connect(&cutSurf, SIGNAL(CutSurfBezierSignal(bool)), ui.CutSurfBezier, SLOT(setChecked(bool)));
  connect(&cutSurf, SIGNAL(SizeXSignal(int)), ui.CutSurfSizeX, SLOT(setValue(int)));
  connect(&cutSurf, SIGNAL(SizeYSignal(int)), ui.CutSurfSizeY, SLOT(setValue(int)));
  connect(&cutSurf, SIGNAL(SizeZSignal(int)), ui.CutSurfSizeZ, SLOT(setValue(int)));
  connect(&cutSurf, SIGNAL(ViewerUpdateSignal()), ui.Viewer, SLOT(ViewerUpdateSlot()));

  // Create action group for select mode
  QActionGroup* selectActionGroup = new QActionGroup(this);
  selectActionGroup->addAction(ui.actionAddNewSeed);
  selectActionGroup->addAction(ui.actionAddCurrentSeed);
  selectActionGroup->addAction(ui.actionDrawSignal);
  selectActionGroup->addAction(ui.actionPickLabel);
  selectActionGroup->addAction(ui.actionGrabSeed);
  selectActionGroup->addAction(ui.actionFillLabel);
  selectActionGroup->addAction(ui.actionPickFillLabel);
  selectActionGroup->addAction(ui.actionPickFillParent);  
  selectActionGroup->addAction(ui.actionMeshSelect);
  selectActionGroup->addAction(ui.actionLassoSelect);
  selectActionGroup->addAction(ui.actionLabelSelect);
  selectActionGroup->addAction(ui.actionConnectedSelect);
  selectActionGroup->addAction(ui.actionTriangleSelect);

  selectActionGroup->addAction(ui.actionPickVolLabel);
  selectActionGroup->addAction(ui.actionPickFillVolumeLabel);
  selectActionGroup->addAction(ui.actionFillVolumeLabel);
  selectActionGroup->addAction(ui.actionDeleteVolumeLabel);
  selectActionGroup->addAction(ui.actionVoxelEdit);
}

void MorphoGraphX::connectControlsOut()
{
  // Connect outgoing signals from widgets
  connect(ui.Stack1Rotate, SIGNAL(toggled(bool)), this, SLOT(RotateSlot(bool)));
  connect(ui.Stack2Rotate, SIGNAL(toggled(bool)), this, SLOT(RotateSlot(bool)));
  connect(ui.Clip1Rotate, SIGNAL(toggled(bool)), this, SLOT(RotateSlot(bool)));
  connect(ui.Clip2Rotate, SIGNAL(toggled(bool)), this, SLOT(RotateSlot(bool)));
  connect(ui.Clip3Rotate, SIGNAL(toggled(bool)), this, SLOT(RotateSlot(bool)));
  connect(ui.CutSurfRotate, SIGNAL(toggled(bool)), this, SLOT(RotateSlot(bool)));

  connect(ui.GlobalBrightness, SIGNAL(valueChanged(int)), this, SLOT(GlobalBrightnessSlot()));
  connect(ui.GlobalContrast, SIGNAL(valueChanged(int)), this, SLOT(GlobalContrastSlot()));
  connect(ui.GlobalShininess, SIGNAL(valueChanged(int)), this, SLOT(GlobalShininessSlot()));
  connect(ui.GlobalSpecular, SIGNAL(valueChanged(int)), this, SLOT(GlobalSpecularSlot()));

  // Connect controls for Stack 1
  connect(ui.Stack1MainShow, SIGNAL(toggled(bool)), &stack1, SLOT(MainShowSlot(bool)));
  connect(ui.Stack1MainShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));
  connect(ui.Stack1WorkShow, SIGNAL(toggled(bool)), &stack1, SLOT(WorkShowSlot(bool)));
  connect(ui.Stack1WorkShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));

  connect(ui.Stack1MainBright, SIGNAL(valueChanged(int)), &stack1, SLOT(MainBrightSlot(int)));
  connect(ui.Stack1MainOpacity, SIGNAL(valueChanged(int)), &stack1, SLOT(MainOpacitySlot(int)));
  connect(ui.Stack1MainLabels, SIGNAL(toggled(bool)), &stack1, SLOT(MainLabelsSlot(bool)));
  connect(ui.Stack1Main16Bit, SIGNAL(toggled(bool)), &stack1, SLOT(Main16BitSlot(bool)));
  connect(ui.Stack1MainColorMap, SIGNAL(clicked()), &stack1, SLOT(MainColorMapSlot()));

  connect(ui.Stack1WorkBright, SIGNAL(valueChanged(int)), &stack1, SLOT(WorkBrightSlot(int)));
  connect(ui.Stack1WorkOpacity, SIGNAL(valueChanged(int)), &stack1, SLOT(WorkOpacitySlot(int)));
  connect(ui.Stack1WorkLabels, SIGNAL(toggled(bool)), &stack1, SLOT(WorkLabelsSlot(bool)));
  connect(ui.Stack1Work16Bit, SIGNAL(toggled(bool)), &stack1, SLOT(Work16BitSlot(bool)));
  connect(ui.Stack1WorkColorMap, SIGNAL(clicked()), &stack1, SLOT(WorkColorMapSlot()));

  connect(ui.Stack1SurfShow, SIGNAL(toggled(bool)), &stack1, SLOT(SurfShowSlot(bool)));
  connect(ui.Stack1SurfShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));
  connect(ui.Stack1SurfBright, SIGNAL(valueChanged(int)), &stack1, SLOT(SurfBrightSlot(int)));
  connect(ui.Stack1SurfOpacity, SIGNAL(valueChanged(int)), &stack1, SLOT(SurfOpacitySlot(int)));
  connect(ui.Stack1SurfBlend, SIGNAL(toggled(bool)), &stack1, SLOT(SurfBlendSlot(bool)));
  connect(ui.Stack1SurfCull, SIGNAL(toggled(bool)), &stack1, SLOT(SurfCullSlot(bool)));

  connect(ui.Stack1SurfVertex, SIGNAL(toggled(bool)), &stack1, SLOT(SurfVertexSlot(bool)));
  connect(ui.Stack1SurfTriangle, SIGNAL(toggled(bool)), &stack1, SLOT(SurfTriangleSlot(bool)));
  connect(ui.Stack1SurfLabel, SIGNAL(toggled(bool)), &stack1, SLOT(SurfLabelSlot(bool)));

  connect(ui.Stack1VertexView, SIGNAL(currentIndexChanged(QString)), &stack1, SLOT(VertexViewSlot(QString)));
  connect(ui.Stack1TriangleView, SIGNAL(currentIndexChanged(QString)), &stack1, SLOT(TriangleViewSlot(QString)));
  connect(ui.Stack1LabelView, SIGNAL(currentIndexChanged(QString)), &stack1, SLOT(LabelViewSlot(QString)));

  connect(ui.Stack1VertexColorMap, SIGNAL(clicked()), &stack1, SLOT(VertexColorMapSlot()));
  connect(ui.Stack1TriangleColorMap, SIGNAL(clicked()), &stack1, SLOT(TriangleColorMapSlot()));
  connect(ui.Stack1LabelColorMap, SIGNAL(clicked()), &stack1, SLOT(LabelColorMapSlot()));

  connect(ui.Stack1SurfParent, SIGNAL(toggled(bool)), &stack1, SLOT(SurfParentSlot(bool)));

  connect(ui.Stack1MeshShow, SIGNAL(toggled(bool)), &stack1, SLOT(MeshShowSlot(bool)));
  connect(ui.Stack1MeshShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));
  connect(ui.Stack1MeshView, SIGNAL(currentIndexChanged(QString)), &stack1, SLOT(ChangeMeshViewModeSlot(QString)));
  connect(ui.Stack1AxisShow, SIGNAL(toggled(bool)), &stack1, SLOT(AxisShowSlot(bool)));
  connect(ui.Stack1AxisShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));
  connect(ui.Stack1AxisView, SIGNAL(currentIndexChanged(QString)), &stack1, SLOT(ChangeAxisViewModeSlot(QString)));
  connect(ui.Stack1MeshLines, SIGNAL(toggled(bool)), &stack1, SLOT(MeshLinesSlot(bool)));
  connect(ui.Stack1MeshPoints, SIGNAL(toggled(bool)), &stack1, SLOT(MeshPointsSlot(bool)));

  connect(ui.Stack1CellMap, SIGNAL(toggled(bool)), &stack1, SLOT(CellMapSlot(bool)));
  connect(ui.Stack1ShowTrans, SIGNAL(toggled(bool)), &stack1, SLOT(ShowTransSlot(bool)));
  connect(ui.Stack1ShowTrans, SIGNAL(toggled(bool)), this, SLOT(RotateSlot(bool)));
  connect(ui.Stack1ShowBBox, SIGNAL(toggled(bool)), &stack1, SLOT(ShowBBoxSlot(bool)));
  connect(ui.Stack1ShowScale, SIGNAL(toggled(bool)), &stack1, SLOT(ShowScaleSlot(bool)));
  connect(ui.Stack1TieScales, SIGNAL(toggled(bool)), &stack1, SLOT(TieScalesSlot(bool)));
  connect(ui.Stack1Scale_X, SIGNAL(valueChanged(int)), &stack1, SLOT(ScaleSlotX(int)));
  connect(ui.Stack1Scale_Y, SIGNAL(valueChanged(int)), &stack1, SLOT(ScaleSlotY(int)));
  connect(ui.Stack1Scale_Z, SIGNAL(valueChanged(int)), &stack1, SLOT(ScaleSlotZ(int)));

  // Connect controls for Stack 2
  connect(ui.Stack2MainShow, SIGNAL(toggled(bool)), &stack2, SLOT(MainShowSlot(bool)));
  connect(ui.Stack2MainShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));
  connect(ui.Stack2WorkShow, SIGNAL(toggled(bool)), &stack2, SLOT(WorkShowSlot(bool)));
  connect(ui.Stack2WorkShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));

  connect(ui.Stack2MainBright, SIGNAL(valueChanged(int)), &stack2, SLOT(MainBrightSlot(int)));
  connect(ui.Stack2MainOpacity, SIGNAL(valueChanged(int)), &stack2, SLOT(MainOpacitySlot(int)));
  connect(ui.Stack2MainLabels, SIGNAL(toggled(bool)), &stack2, SLOT(MainLabelsSlot(bool)));
  connect(ui.Stack2Main16Bit, SIGNAL(toggled(bool)), &stack2, SLOT(Main16BitSlot(bool)));
  connect(ui.Stack2MainColorMap, SIGNAL(clicked()), &stack2, SLOT(MainColorMapSlot()));

  connect(ui.Stack2WorkBright, SIGNAL(valueChanged(int)), &stack2, SLOT(WorkBrightSlot(int)));
  connect(ui.Stack2WorkOpacity, SIGNAL(valueChanged(int)), &stack2, SLOT(WorkOpacitySlot(int)));
  connect(ui.Stack2WorkLabels, SIGNAL(toggled(bool)), &stack2, SLOT(WorkLabelsSlot(bool)));
  connect(ui.Stack2Work16Bit, SIGNAL(toggled(bool)), &stack2, SLOT(Work16BitSlot(bool)));
  connect(ui.Stack2WorkColorMap, SIGNAL(clicked()), &stack2, SLOT(WorkColorMapSlot()));

  connect(ui.Stack2SurfShow, SIGNAL(toggled(bool)), &stack2, SLOT(SurfShowSlot(bool)));
  connect(ui.Stack2SurfShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));
  connect(ui.Stack2SurfBright, SIGNAL(valueChanged(int)), &stack2, SLOT(SurfBrightSlot(int)));
  connect(ui.Stack2SurfOpacity, SIGNAL(valueChanged(int)), &stack2, SLOT(SurfOpacitySlot(int)));
  connect(ui.Stack2SurfBlend, SIGNAL(toggled(bool)), &stack2, SLOT(SurfBlendSlot(bool)));
  connect(ui.Stack2SurfCull, SIGNAL(toggled(bool)), &stack2, SLOT(SurfCullSlot(bool)));

  connect(ui.Stack2SurfVertex, SIGNAL(toggled(bool)), &stack2, SLOT(SurfVertexSlot(bool)));
  connect(ui.Stack2SurfTriangle, SIGNAL(toggled(bool)), &stack2, SLOT(SurfTriangleSlot(bool)));
  connect(ui.Stack2SurfLabel, SIGNAL(toggled(bool)), &stack2, SLOT(SurfLabelSlot(bool)));

  connect(ui.Stack2VertexView, SIGNAL(currentIndexChanged(QString)), &stack2, SLOT(VertexViewSlot(QString)));
  connect(ui.Stack2TriangleView, SIGNAL(currentIndexChanged(QString)), &stack2, SLOT(TriangleViewSlot(QString)));
  connect(ui.Stack2LabelView, SIGNAL(currentIndexChanged(QString)), &stack2, SLOT(LabelViewSlot(QString)));

  connect(ui.Stack2VertexColorMap, SIGNAL(clicked()), &stack2, SLOT(VertexColorMapSlot()));
  connect(ui.Stack2TriangleColorMap, SIGNAL(clicked()), &stack2, SLOT(TriangleColorMapSlot()));
  connect(ui.Stack2LabelColorMap, SIGNAL(clicked()), &stack2, SLOT(LabelColorMapSlot()));;

  connect(ui.Stack2SurfParent, SIGNAL(toggled(bool)), &stack2, SLOT(SurfParentSlot(bool)));

  connect(ui.Stack2MeshShow, SIGNAL(toggled(bool)), &stack2, SLOT(MeshShowSlot(bool)));
  connect(ui.Stack2MeshShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));
  connect(ui.Stack2MeshView, SIGNAL(currentIndexChanged(QString)), &stack2, SLOT(ChangeMeshViewModeSlot(QString)));
  connect(ui.Stack2AxisShow, SIGNAL(toggled(bool)), &stack2, SLOT(AxisShowSlot(bool)));
  connect(ui.Stack2AxisShow, SIGNAL(toggled(bool)), this, SLOT(updateCurrentStack()));
  connect(ui.Stack2AxisView, SIGNAL(currentIndexChanged(QString)), &stack2, SLOT(ChangeAxisViewModeSlot(QString)));
  connect(ui.Stack2MeshLines, SIGNAL(toggled(bool)), &stack2, SLOT(MeshLinesSlot(bool)));
  connect(ui.Stack2MeshPoints, SIGNAL(toggled(bool)), &stack2, SLOT(MeshPointsSlot(bool)));

  connect(ui.Stack2CellMap, SIGNAL(toggled(bool)), &stack2, SLOT(CellMapSlot(bool)));
  connect(ui.Stack2ShowTrans, SIGNAL(toggled(bool)), &stack2, SLOT(ShowTransSlot(bool)));
  connect(ui.Stack2ShowTrans, SIGNAL(toggled(bool)), this, SLOT(RotateSlot(bool)));
  connect(ui.Stack2ShowScale, SIGNAL(toggled(bool)), &stack2, SLOT(ShowScaleSlot(bool)));
  connect(ui.Stack2TieScales, SIGNAL(toggled(bool)), &stack2, SLOT(TieScalesSlot(bool)));
  connect(ui.Stack2ShowBBox, SIGNAL(toggled(bool)), &stack2, SLOT(ShowBBoxSlot(bool)));
  connect(ui.Stack2Scale_X, SIGNAL(valueChanged(int)), &stack2, SLOT(ScaleSlotX(int)));
  connect(ui.Stack2Scale_Y, SIGNAL(valueChanged(int)), &stack2, SLOT(ScaleSlotY(int)));
  connect(ui.Stack2Scale_Z, SIGNAL(valueChanged(int)), &stack2, SLOT(ScaleSlotZ(int)));

  // Clipping controls
  connect(ui.Clip1Enable, SIGNAL(toggled(bool)), ui.Viewer, SLOT(Clip1EnableSlot(bool)));
  connect(ui.Clip1Grid, SIGNAL(toggled(bool)), ui.Viewer, SLOT(Clip1GridSlot(bool)));
  connect(ui.Clip1Width, SIGNAL(valueChanged(int)), ui.Viewer, SLOT(Clip1WidthSlot(int)));
  connect(ui.Clip2Enable, SIGNAL(toggled(bool)), ui.Viewer, SLOT(Clip2EnableSlot(bool)));
  connect(ui.Clip2Grid, SIGNAL(toggled(bool)), ui.Viewer, SLOT(Clip2GridSlot(bool)));
  connect(ui.Clip2Width, SIGNAL(valueChanged(int)), ui.Viewer, SLOT(Clip2WidthSlot(int)));
  connect(ui.Clip3Enable, SIGNAL(toggled(bool)), ui.Viewer, SLOT(Clip3EnableSlot(bool)));
  connect(ui.Clip3Grid, SIGNAL(toggled(bool)), ui.Viewer, SLOT(Clip3GridSlot(bool)));
  connect(ui.Clip3Width, SIGNAL(valueChanged(int)), ui.Viewer, SLOT(Clip3WidthSlot(int)));

  connect(ui.Viewer, SIGNAL(changeSceneRadius(float)), this, SLOT(ResetClipControl(float)));

  connect(ui.Slices, SIGNAL(valueChanged(int)), ui.Viewer, SLOT(SlicesSlot(int)));
  connect(ui.ScreenSampling, SIGNAL(valueChanged(int)), ui.Viewer, SLOT(ScreenSamplingSlot(int)));
  connect(ui.VoxelEditRadius, SIGNAL(valueChanged(int)), this, SLOT(VoxelEditRadiusSlot(int)));

  connect(ui.ProcessStackCommand, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this,
          SLOT(ProcessCommandSlot(QTreeWidgetItem*, QTreeWidgetItem*)));
  connect(ui.ProcessMeshCommand, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this,
          SLOT(ProcessCommandSlot(QTreeWidgetItem*, QTreeWidgetItem*)));
  connect(ui.ProcessMiscCommand, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this,
          SLOT(ProcessCommandSlot(QTreeWidgetItem*, QTreeWidgetItem*)));
  connect(ui.ProcessTasksCommand, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this,
          SLOT(ProcessTasksCommandSlot(QTreeWidgetItem*, QTreeWidgetItem*)));

  connect(ui.ProcessTabs, SIGNAL(currentChanged(int)), this, SLOT(ProcessCommandTabSlot(int)));
  connect(ui.StackTabs, SIGNAL(currentChanged(int)), this, SLOT(updateCurrentStack()));
  connect(ui.FillWorkData, SIGNAL(toggled(bool)), this, SLOT(FillWorkDataSlot(bool)));
  connect(ui.SeedStack, SIGNAL(toggled(bool)), this, SLOT(SeedStackSlot(bool)));

  // File menu
  connect(ui.menuFileOpen, SIGNAL(triggered()), this, SLOT(FileOpenSlot()));
  connect(ui.menuFileSave, SIGNAL(triggered()), this, SLOT(FileSaveSlot()));
  connect(ui.menuFileSaveAs, SIGNAL(triggered()), this, SLOT(FileSaveAsSlot()));
  connect(ui.menuFileQuickSave, SIGNAL(triggered()), this, SLOT(FileQuickSaveSlot()));
  connect(ui.menuFileEditPaths, SIGNAL(triggered()), this, SLOT(FileEditPathsSlot()));
  connect(ui.menuExit, SIGNAL(triggered()), this, SLOT(ExitSlot()));

  // Stack menu
  connect(ui.menuStack1Open, SIGNAL(triggered()), this, SLOT(Stack1OpenSlot()));
  connect(ui.menuStack1Save, SIGNAL(triggered()), this, SLOT(Stack1SaveSlot()));
  connect(ui.menuStack1Import, SIGNAL(triggered()), this, SLOT(Stack1ImportSlot()));
  connect(ui.menuStack1Load, SIGNAL(triggered()), this, SLOT(Stack1ImportSeriesSlot()));
  connect(ui.menuStack1Export, SIGNAL(triggered()), this, SLOT(Stack1ExportSlot()));
  connect(ui.menuWorkStack1Open, SIGNAL(triggered()), this, SLOT(Stack1WorkOpenSlot()));
  connect(ui.menuWorkStack1Save, SIGNAL(triggered()), this, SLOT(Stack1WorkSaveSlot()));
  connect(ui.menuWorkStack1Import, SIGNAL(triggered()), this, SLOT(Stack1WorkImportSlot()));
  connect(ui.menuWorkStack1Load, SIGNAL(triggered()), this, SLOT(Stack1WorkImportSeriesSlot()));
  connect(ui.menuWorkStack1Export, SIGNAL(triggered()), this, SLOT(Stack1WorkExportSlot()));
  connect(ui.menuStack1Reset, SIGNAL(triggered()), this, SLOT(Stack1ResetSlot()));

  connect(ui.menuStack2Open, SIGNAL(triggered()), this, SLOT(Stack2OpenSlot()));
  connect(ui.menuStack2Save, SIGNAL(triggered()), this, SLOT(Stack2SaveSlot()));
  connect(ui.menuStack2Import, SIGNAL(triggered()), this, SLOT(Stack2ImportSlot()));
  connect(ui.menuStack2Load, SIGNAL(triggered()), this, SLOT(Stack2ImportSeriesSlot()));
  connect(ui.menuStack2Export, SIGNAL(triggered()), this, SLOT(Stack2ExportSlot()));
  connect(ui.menuWorkStack2Open, SIGNAL(triggered()), this, SLOT(Stack2WorkOpenSlot()));
  connect(ui.menuWorkStack2Save, SIGNAL(triggered()), this, SLOT(Stack2WorkSaveSlot()));
  connect(ui.menuWorkStack2Import, SIGNAL(triggered()), this, SLOT(Stack2WorkImportSlot()));
  connect(ui.menuWorkStack2Load, SIGNAL(triggered()), this, SLOT(Stack2WorkImportSeriesSlot()));
  connect(ui.menuWorkStack2Export, SIGNAL(triggered()), this, SLOT(Stack2WorkExportSlot()));
  connect(ui.menuStack2Reset, SIGNAL(triggered()), this, SLOT(Stack2ResetSlot()));

  // Mesh menu
  connect(ui.menuMesh1Import, SIGNAL(triggered()), this, SLOT(Mesh1ImportSlot()));
  connect(ui.menuMesh1Export, SIGNAL(triggered()), this, SLOT(Mesh1ExportSlot()));
  connect(ui.menuMesh1Load, SIGNAL(triggered()), this, SLOT(Mesh1LoadSlot()));
  connect(ui.menuMesh1Save, SIGNAL(triggered()), this, SLOT(Mesh1SaveSlot()));
  connect(ui.menuMesh1Reset, SIGNAL(triggered()), this, SLOT(Mesh1ResetSlot()));
  connect(ui.menuMesh2Import, SIGNAL(triggered()), this, SLOT(Mesh2ImportSlot()));
  connect(ui.menuMesh2Export, SIGNAL(triggered()), this, SLOT(Mesh2ExportSlot()));
  connect(ui.menuMesh2Load, SIGNAL(triggered()), this, SLOT(Mesh2LoadSlot()));
  connect(ui.menuMesh2Save, SIGNAL(triggered()), this, SLOT(Mesh2SaveSlot()));
  connect(ui.menuMesh2Reset, SIGNAL(triggered()), this, SLOT(Mesh2ResetSlot()));

  //Attributes Menu
  connect(ui.menuMesh1Attributes, SIGNAL(triggered()), this, SLOT(Mesh1ManageAttributes()));
  connect(ui.menuMesh2Attributes, SIGNAL(triggered()), this, SLOT(Mesh2ManageAttributes()));

  // Selection menu
  connect(ui.actionSelectAll, SIGNAL(triggered()), this, SLOT(MeshSelectAll()));
  connect(ui.actionUnselect, SIGNAL(triggered()), this, SLOT(MeshUnselect()));
  connect(ui.actionInvertSelection, SIGNAL(triggered()), this, SLOT(MeshInvertSelection()));
  connect(ui.actionAddCurrentLabel, SIGNAL(triggered()), this, SLOT(MeshAddCurrentLabel()));
  connect(ui.actionSelectUnlabled, SIGNAL(triggered()), this, SLOT(MeshAddUnlabeled()));
  connect(ui.actionRemoveCurrentLabel, SIGNAL(triggered()), this, SLOT(MeshRemoveCurrentLabel()));
  connect(ui.actionChangeSeed, SIGNAL(triggered()), this, SLOT(ChangeCurrentSeed()));

  // Help menu
  connect(ui.actionUserManual, SIGNAL(triggered()), this, SLOT(UserManualSlot()));
  connect(ui.actionDoxygen, SIGNAL(triggered()), this, SLOT(DoxygenSlot()));
  connect(ui.actionQGLViewerHelp, SIGNAL(triggered()), this, SLOT(QGLViewerHelpSlot()));
  connect(ui.actionAbout, SIGNAL(triggered()), this, SLOT(AboutSlot()));

  // Edit toolbar
  connect(ui.actionLabelColor, SIGNAL(triggered()), ui.Viewer, SLOT(LabelColorSlot()));
  connect(ui.Viewer, SIGNAL(setLabelColor(QIcon&)), this, SLOT(LabelColorSlot(QIcon&)));
  connect(ui.actionEraseSelection, SIGNAL(triggered()), this, SLOT(EraseSelectSlot()));
  connect(ui.actionFillSelection, SIGNAL(triggered()), this, SLOT(FillSelectSlot()));
  connect(ui.actionDeleteSelection, SIGNAL(triggered()), this, SLOT(DeleteSelectionSlot()));
  connect(ui.actionDeleteSelection, SIGNAL(triggered()), this, SLOT(DeleteSelectionSlot()));
  connect(ui.Viewer, SIGNAL(deleteSelection()), this, SLOT(DeleteSelectionSlot()));

  // View toolbar
  connect(ui.actionResetView, SIGNAL(triggered()), ui.Viewer, SLOT(ResetViewSlot()));
  connect(ui.actionEditParms, SIGNAL(triggered()), this, SLOT(EditParmsSlot()));
  connect(ui.actionProcessDocs, SIGNAL(triggered()), this, SLOT(ProcessDocsSlot()));
  connect(ui.actionColorPalette, SIGNAL(triggered()), this, SLOT(ColorPaletteSlot()));
  connect(ui.actionSwapSurfaces, SIGNAL(triggered()), this, SLOT(SwapSurfacesSlot()));
  connect(Colors::instance(), SIGNAL(colorsChanged()), &stack1, SLOT(updateLineColor()));
  connect(Colors::instance(), SIGNAL(colorsChanged()), &stack2, SLOT(updateLineColor()));
  connect(Colors::instance(), SIGNAL(colorsChanged()), ui.Viewer, SLOT(UpdateSlot()));
  connect(ui.actionEditLabels, SIGNAL(toggled(bool)), this, SLOT(LabelsEditSlot(bool)));

  connect(ui.actionSaveScreenshot, SIGNAL(triggered()), ui.Viewer, SLOT(saveScreenshot()));
  connect(ui.Viewer, SIGNAL(recordingMovie(bool)), ui.actionRecordMovie, SLOT(setChecked(bool)));
  connect(ui.actionRecordMovie, SIGNAL(toggled(bool)), ui.Viewer, SLOT(recordMovie(bool)));
  connect(ui.actionReload_shaders, SIGNAL(triggered()), ui.Viewer, SLOT(ReloadShaders()));

  // Object communication
  connect(&stack1, SIGNAL(viewerUpdate()), ui.Viewer, SLOT(ViewerUpdateSlot()));
  connect(&stack2, SIGNAL(viewerUpdate()), ui.Viewer, SLOT(ViewerUpdateSlot()));
  connect(&stack1, SIGNAL(updateSliderScale()), this, SLOT(UpdateSliderScaleSlot()));
  connect(&stack2, SIGNAL(updateSliderScale()), this, SLOT(UpdateSliderScaleSlot()));
  //connect(&stack1, SIGNAL(forceSurfHeat()), this, SLOT(forceStack1SurfHeat()));
  //connect(&stack2, SIGNAL(forceSurfHeat()), this, SLOT(forceStack2SurfHeat()));
  connect(&stack1, SIGNAL(toggleEditLabels()), ui.actionEditLabels, SLOT(trigger()));
  connect(&stack2, SIGNAL(toggleEditLabels()), ui.actionEditLabels, SLOT(trigger()));

  // Cutting surface controls
  connect(ui.DrawCutSurf, SIGNAL(toggled(bool)), &cutSurf, SLOT(DrawCutSurfSlot(bool)));
  connect(ui.ThreeAxis, SIGNAL(toggled(bool)), &cutSurf, SLOT(ThreeAxisSlot(bool)));
  connect(ui.CutSurfGrid, SIGNAL(toggled(bool)), &cutSurf, SLOT(CutSurfGridSlot(bool)));
  connect(ui.CutSurfReset, SIGNAL(clicked()), this, SLOT(ResetCutSurf()));
  connect(ui.CutSurfPlane, SIGNAL(toggled(bool)), &cutSurf, SLOT(CutSurfPlaneSlot(bool)));
  connect(ui.CutSurfBezier, SIGNAL(toggled(bool)), &cutSurf, SLOT(CutSurfBezierSlot(bool)));
  connect(ui.CutSurfSizeX, SIGNAL(valueChanged(int)), &cutSurf, SLOT(SizeXSlot(int)));
  connect(ui.CutSurfSizeY, SIGNAL(valueChanged(int)), &cutSurf, SLOT(SizeYSlot(int)));
  connect(ui.CutSurfSizeZ, SIGNAL(valueChanged(int)), &cutSurf, SLOT(SizeZSlot(int)));

  // Changing label
  connect(ui.Viewer, SIGNAL(selectLabelChanged(int)), this, SLOT(changeSelectLabel(int)));
}

// Update sliders according to scale values. Useful when scales are tied to each other.
void MorphoGraphX::UpdateSliderScaleSlot()
{
  QWidget *currentTab = ui.StackTabs->currentWidget();
  if(currentTab == ui.Stack1Tab) {
    ui.Stack1Scale_X->setValue(stack1.toSliderScale(stack1.stack->scale().x()));
    ui.Stack1Scale_Y->setValue(stack1.toSliderScale(stack1.stack->scale().y()));
    ui.Stack1Scale_Z->setValue(stack1.toSliderScale(stack1.stack->scale().z()));
  }
  if(currentTab == ui.Stack2Tab) {
    ui.Stack2Scale_X->setValue(stack2.toSliderScale(stack2.stack->scale().x()));
    ui.Stack2Scale_Y->setValue(stack2.toSliderScale(stack2.stack->scale().y()));
    ui.Stack2Scale_Z->setValue(stack2.toSliderScale(stack2.stack->scale().z()));
  }
};

void MorphoGraphX::readParms()
{
  // Get filename
  QString filename = projectFile();
  modified(false);

  if(!filename.isEmpty()) {
    QFileInfo fi(filename);
    if(!fi.exists()) {
      if(!filename.endsWith(".mgxv", Qt::CaseInsensitive))
        filename += ".mgxv";
      if(!QFileInfo(filename).exists()) {
        QFile f(filename);
        f.open(QIODevice::WriteOnly);
        f.close();
      }
    }
    filename = QDir(filename).absolutePath();

    filename = stripCurrentDir(filename);
    setWindowTitle(QString("MorphoGraphX - ") + filename);
    filename = QDir::currentPath() + "/" + filename;

    // Check ending, add suffix if required
    QString suffix = ".mgxv";
    if(filename.right(suffix.size()) != suffix)
      filename.append(suffix);

    // Check that file can be read
    QFile file(projectFile());
    if(!file.exists()) {
      if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        SETSTATUS("readParms::Cannot create parms file-" << projectFile());
        return;
      }
    } else {
      if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        SETSTATUS("readParms::Cannot open parms file for reading-" << projectFile());
        return;
      }
    }
    file.close();
  }

  // Create parms object
  Parms parms(projectFile());

  // Read model parms
  parms("Main", "SurfOffset", ImgData::SurfOffset, .01f);

  parms("Main", "Slices", ImgData::Slices, 500u);
  parms("Main", "ScreenSampling", ui.Viewer->sampling, 0.0f);
  parms("Main", "TileCount", ImgData::TileCount, 20u);
  parms("Main", "MaxTexSize", ImgData::MaxTexSize, Point3u(1024u, 1024u, 1024u));
  parms("Main", "DrawNormals", ImgData::DrawNormals, 0.0f);
  parms("Main", "DrawOffset", ImgData::DrawOffset, 1.0f);
  if(ImgData::DrawOffset < 1.0f)
    ImgData::DrawOffset = 1.0f;
  parms("Main", "DrawZeroLabels", ImgData::DrawZeroLabels, 0.0f);
  parms("Main", "DrawNhbds", ImgData::DrawNhbds, 0.0f);
  parms("Main", "DeleteBadVertex", ImgData::DeleteBadVertex, false);
  parms("Main", "CudaHoldMem", CudaHoldMem, size_t(0));

  ImgData::scaleBar.readParms(parms, "ScaleBar");
  ImgData::colorBar.readParms(parms, "ColorBar");

  parms("Main", "VoxelEditRadius", ImgData::VoxelEditRadius, 25);
  parms("Main", "VoxelEditMaxPix", ImgData::VoxelEditMaxPix, 1024000);

  parms("Main", "MeshPointSize", ImgData::MeshPointSize, 3.0f);
  parms("Main", "MeshLineWidth", ImgData::MeshLineWidth, 1.0f);
  parms("Main", "FillWorkData", ImgData::FillWorkData, false);
  parms("Main", "SeedStack", ImgData::SeedStack, false);

  Colors::instance()->readParms(parms, "Colors");

  // Read the viewer and stack parms
  ui.Viewer->readParms(parms, "Main");
  if(currentSetup) {
    currentSetup->setGlobalContrast(ui.Viewer->GlobalContrast);
    currentSetup->setGlobalBrightness(ui.Viewer->GlobalBrightness);
    currentSetup->setGlobalShininess(ui.Viewer->GlobalShininess);
    currentSetup->setGlobalSpecular(ui.Viewer->GlobalSpecular);
  }
  stack1.readParms(parms, "Stack1");
  stack2.readParms(parms, "Stack2");
  cutSurf.readParms(parms, "Plane");

  readProcessParms();

  parms.all("Labels", "Color", ImgData::LabelColors);
  if(ImgData::LabelColors.empty()) {
    ImgData::LabelColors.resize(16);
    ImgData::LabelColors[0] = Colorf(1, 0, 0, 1);
    ImgData::LabelColors[1] = Colorf(0, 1, 0, 1);
    ImgData::LabelColors[2] = Colorf(0, 0, 1, 1);
    ImgData::LabelColors[3] = Colorf(1, 1, 0, 1);
    ImgData::LabelColors[4] = Colorf(1, 0, 1, 1);
    ImgData::LabelColors[5] = Colorf(0, 1, 1, 1);
    ImgData::LabelColors[6] = Colorf(.5, 0, 0, 1);
    ImgData::LabelColors[7] = Colorf(0, .5, 0, 1);
    ImgData::LabelColors[8] = Colorf(0, 0, .5, 1);
    ImgData::LabelColors[9] = Colorf(.5, 0, .5, 1);
    ImgData::LabelColors[10] = Colorf(.5, .5, 0, 1);
    ImgData::LabelColors[11] = Colorf(1, 0, .5, 1);
    ImgData::LabelColors[12] = Colorf(.5, .5, 1, 1);
    ImgData::LabelColors[13] = Colorf(0, .5, 1, 1);
    ImgData::LabelColors[14] = Colorf(.5, 0, 1, 1);
    ImgData::LabelColors[15] = Colorf(1, .5, 0, 1);
  }
  stack1.initTex();
  stack2.initTex();

  // Set cuda memory
  setHoldMemGPU(CudaHoldMem);
}

void MorphoGraphX::readProcessParms()
{
  Parms parms(projectFile());

  Information::out << "Reading Process Parameters" << endl;

  QString mgxVersion;
  parms("Main", "MGXVersion", mgxVersion, QString("0.0"));

  std::vector<QString> procNames;
  std::vector<uint> numParms;
  std::vector<QString> parmNames;
  std::vector<QString> parmStrings;

  QStringList sections;
  if(mgxVersion >= QString("1.1"))
    sections << "Process";
  else
    sections <<"StackProcess" << "MeshProcess" << "GlobalProcess";

  // Read process parms into arrays
  forall(QString &section, sections) {
    if(mgxVersion >= QString("1.0")) {
      parms.all(section, "ProcessName", procNames);
      parms.all(section, "NumParms", numParms);
      parms.all(section, "ParmName", parmNames);
      parms.all(section, "ParmString", parmStrings);
    } else {
      // Older versions had both "strings" and "values", as well as "Named" vs "UnNamed" parameters
      // Now all parameters are stored as strings, and all parameters have names
      std::vector<int> nbStrings;
      std::vector<int> nbValues;
      std::vector<int> nbNamedStrings;
      std::vector<int> nbNamedValues;
      std::vector<QString> strings;
      std::vector<QString> values;
  
      parms.all(section, "ProcessName", procNames);
      parms.all(section, "NbProcessStrings", nbStrings);
      parms.all(section, "NbProcessValues", nbValues);
      parms.all(section, "NbProcessNamedStrings", nbNamedStrings);
      parms.all(section, "NbProcessNamedValues", nbNamedValues);
      parms.all(section, "ProcessParameter", parmNames);
      parms.all(section, "ProcessString", strings);
      parms.all(section, "ProcessValue", values);
  
      int valuesIdx = 0;
      int stringsIdx = 0;
      for(size_t i = 0; i < procNames.size(); ++i) {
        if(i >= nbStrings.size() or i >= nbValues.size() or i >= nbNamedStrings.size()
           or i >= nbNamedValues.size()) {
          Information::out << "Error reading Process definition for process " 
              << procNames[i] << endl;
          return;
        }
        numParms.push_back(nbNamedStrings[i] + nbNamedValues[i]);
        // Get named strings
        for(int j = 0; j < nbNamedStrings[i]; ++j) {
          if(stringsIdx >= (int)strings.size()) {
            Information::out << "Error reading String parms for process " << procNames[i] << endl;
            return;
          }
          parmStrings.push_back(strings[stringsIdx++]);
        }
        // Toss "unnamed strings"
        stringsIdx += nbStrings[i] - nbNamedStrings[i];
  
        // Get named values
        for(int j = 0; j < nbNamedValues[i]; ++j) {
          if(valuesIdx >= (int)values.size()) {
            Information::out << "Error reading Value parms for process " << procNames[i] << endl;
            return;
          }
          parmStrings.push_back(values[valuesIdx++]);
        }
        // Toss "unnamed values"
        valuesIdx += nbValues[i] - nbNamedValues[i];
      }
    }
    // If before version 1.1, add the path to the process names
    if(mgxVersion < QString("1.1")) {
      for(uint i = 0; i < procNames.size(); ++i) {
        QMap<QString, mgx::ProcessDefinition>::const_iterator it = savedProc.constBegin();
        while (it != savedProc.constEnd()) {
          if(it.key().endsWith(procNames[i])) {
            procNames[i] = it.key();
          }
          ++it;
        }      
      }
    }
  
    // Now process parameters
    size_t parmIdx = 0;
    for(size_t i = 0; i < procNames.size(); ++i) {
      // Check process definition
      if(i >= numParms.size()) {
        Information::out << "Error reading Process definition for process " << procNames[i] << endl;
        return;
      }
  
      // Get parm names and their string values and put in a map
      std::map<QString, QString> parmMap;
      for(size_t j = 0; j < numParms[i]; ++j) {
        if(parmIdx + j >= parmNames.size() or parmIdx + j >= parmStrings.size()) {
          Information::out << "Error reading Parms for process " << procNames[i] << endl;
          return;
        }
        parmMap[parmNames[parmIdx + j]] = parmStrings[parmIdx + j];
      }
  
      // Get process definition and fill in the parms
      if(savedProc.count(procNames[i]) == 1) {
        ProcessDefinition& def = savedProc[procNames[i]];
        for(int j = 0; j < def.parmNames.size(); ++j)
          if(parmMap.count(def.parmNames[j]) == 1)
            def.parms[j] = parmMap[def.parmNames[j]];
          else
            Information::out << "Parameter '" << def.parmNames[j] << "' not found for process '" 
                          << procNames[i] << "'" << endl;
      } else
        Information::out << "Process '" << procNames[i] << "' not found." << endl;
      parmIdx += numParms[i];
    }
  }

  Information::out << "Reading Tasks" << endl;

  TaskEditDlg::tasks_t newTasks;
  TaskEditDlg::readTasks(projectFile(), savedProc, newTasks);
  updateCurrentTasks(newTasks);
}

void MorphoGraphX::updateCurrentTasks(const TaskEditDlg::tasks_t& saved_tasks)
{
  // Now, update the tasks with the saved ones, unless empty
  for(TaskEditDlg::tasks_t::const_iterator it = saved_tasks.begin(); 
                                                                 it != saved_tasks.end(); ++it) {
    QString name = it.key();
    QList<ProcessDefinition> sprocs = it.value();
    bool task_exists = tasks.contains(name);
    std::vector<bool> used;
    forall(ProcessDefinition& sdef, sprocs) {
      // Check the parameters are valid
      ProcessDefinition* real_def = getProcessDefinition(sdef.name);
      if(real_def) {
        sdef.description = real_def->description;
        sdef.parmNames = real_def->parmNames;
        sdef.parmDescs = real_def->parmDescs;
        sdef.icon = real_def->icon;
        sdef.parmChoice = real_def->parmChoice;
      }

      int old_proc_id = -1;
      if(task_exists) {
        const QList<ProcessDefinition>& procs = tasks[name];
        used.resize(procs.size(), false);
        for(int i = 0; i < procs.size(); ++i) {
          const ProcessDefinition& def = procs[i];
          if(not used[i] and def.name == sdef.name) {
            used[i] = true;
            old_proc_id = i;
            break;
          }
        }
      }
      if(not checkProcessParms(sdef.name, sdef.parms)) {
        if(old_proc_id < 0)
          getLastParms(sdef.name, sdef.parms);
        else {
          const ProcessDefinition& def = tasks[name][old_proc_id];
          sdef.parms = def.parms;
        }
      }
    }
    tasks[name] = sprocs;
  }
}

void MorphoGraphX::writeProcessParms(const ProcessDefinition& def, QTextStream& pout)
{
  pout << "ProcessName: " << def.name << endl;
  pout << "NumParms: " << def.parmNames.size() << endl;
  for(int i = 0; i < def.parmNames.size(); ++i) {
    pout << "ParmName: " << def.parmNames[i] << endl;
    pout << "ParmString: " << def.parms[i] << endl;
  }
}

void MorphoGraphX::writeParms()
{
  // Open the file for write
  QFile file(projectFile());
  if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
    SETSTATUS("writeParms::Cannot open parms file for writing-" << projectFile());
    return;
  }
  QTextStream pout(&file);
  pout.setCodec("UTF-8");

  pout << "// Parameters for MorphoGraphX" << endl;
  pout << "[Main]" << endl;

  // Write out model parms
  pout << "MGXVersion: " << VERSION << endl;
  pout << "MGXRevision: " << REVISION << endl;
  pout << "ClearColor: " << ImgData::ClearColor << endl;
  pout << "SurfOffset: " << ImgData::SurfOffset << endl;
  pout << "Slices: " << ImgData::Slices << endl;
  pout << "ScreenSampling: " << ui.Viewer->sampling << endl;
  pout << "TileCount: " << ImgData::TileCount << endl;
  pout << "MaxTexSize: " << ImgData::MaxTexSize << endl;
  pout << endl;

  pout << "DrawNormals: " << ImgData::DrawNormals << endl;
  pout << "DrawOffset: " << ImgData::DrawOffset << endl;

  pout << "DrawZeroLabels: " << ImgData::DrawZeroLabels << endl;
  pout << "DrawNhbds: " << ImgData::DrawNhbds << endl;
  pout << "DeleteBadVertex: " << (ImgData::DeleteBadVertex ? "true" : "false") << endl;
  pout << "CudaHoldMem: " << CudaHoldMem << endl;
  pout << endl;

  pout << "MeshPointSize: " << ImgData::MeshPointSize << endl;
  pout << "MeshLineWidth: " << ImgData::MeshLineWidth << endl;
  pout << "SeedStack: " << (ImgData::SeedStack ? "true" : "false") << endl;

  pout << "VoxelEditRadius: " << ImgData::VoxelEditRadius << endl;
  pout << "VoxelEditMaxPix: " << ImgData::VoxelEditMaxPix << endl;
  pout << endl;

  pout << "[Labels]" << endl;
  for(size_t i = 0; i < ImgData::LabelColors.size(); ++i)
    pout << "Color: " << ImgData::LabelColors[i] << endl;
  pout << endl;

  Colors::instance()->writeParms(pout, "Colors");

  // Write the viewer and stack parms
  ui.Viewer->writeParms(pout, "Main");
  stack1.writeParms(pout, "Stack1");
  stack2.writeParms(pout, "Stack2");
  cutSurf.writeParms(pout, "Plane");

  ImgData::scaleBar.writeParms(pout, "ScaleBar");
  ImgData::colorBar.writeParms(pout, "ColorBar");

  pout << endl << "[Process]" << endl;
  forall(const ProcessDefinition& def, processes)
    writeProcessParms(def, pout);

  // Write out tasks
  TaskEditDlg::writeTasks(pout, tasks);

  needSaving = false;
}

void MorphoGraphX::SaveView(const QString& filename)
{
  QStringList parms;
  parms << filename;
  LaunchProcess("Misc/System/Save View", parms, Process::PROCESS_RUN, false);
}

void MorphoGraphX::LoadView(const QString& filename)
{
  QStringList parms;
  parms << filename;
  LaunchProcess("Misc/System/Load View", parms, Process::PROCESS_RUN, false);
}

void MorphoGraphX::clearMeshSelect()
{
  stack1.lineId = 0;
  stack2.lineId = 0;
  stack1.clearMeshSelect();
  stack2.clearMeshSelect();
}

// Slots
void MorphoGraphX::RotateSlot(bool)
{
  if(ui.Stack1Rotate->isChecked())
    ui.Viewer->setManipulatedFrame(&stack1.getFrame());
  else if(ui.Stack2Rotate->isChecked())
    ui.Viewer->setManipulatedFrame(&stack2.getFrame());
  else if(ui.Clip1Rotate->isChecked())
    ui.Viewer->setManipulatedFrame(&ui.Viewer->c1->frame());
  else if(ui.Clip2Rotate->isChecked())
    ui.Viewer->setManipulatedFrame(&ui.Viewer->c2->frame());
  else if(ui.Clip3Rotate->isChecked())
    ui.Viewer->setManipulatedFrame(&ui.Viewer->c3->frame());
  else if(ui.CutSurfRotate->isChecked())
    ui.Viewer->setManipulatedFrame(cutSurf.getFrame());
  else
    ui.Viewer->setManipulatedFrame(&ui.Viewer->c1->frame());

  ui.Viewer->manipulatedFrame()->setSpinningSensitivity(ui.Viewer->Spinning);
  ui.Viewer->updateAll();
}

void MorphoGraphX::FileOpenSlot()
{
  QStringList parms;
  parms << projectFile();
  LaunchProcess("Misc/System/Load View", parms, Process::PROCESS_RUN,  true);
}

void MorphoGraphX::FileOpen(const QString& filename) 
{
  LoadView(filename);
}

void MorphoGraphX::setModelPath(const QString& modelPath) 
{
  if(modelPath.size() == 0)
    return;

  // Set the main tab
  ui.ControlsTab->setCurrentIndex(2);

  // Look through tree widgets and make the matching one current
  QList<QTreeWidget *> trees;
  trees << ui.ProcessStackCommand << ui.ProcessMeshCommand
        << ui.ProcessMiscCommand << ui.ProcessTasksCommand;
  for(int i = 0; i < trees.size(); i++) {
    if(i == 3) // Skip tasks
      continue; 
    QTreeWidgetItemIterator it(trees[i]);
    while (*it) {
      if ((*it)->text(1) == modelPath) {
        ui.ProcessTabs->setCurrentIndex(i);
        trees[i]->setCurrentItem(*it);
        return;
      }
      ++it;
    }
  }
}

void MorphoGraphX::FileEditPathsSlot()
{
  if(editPathsDlg) {
    if(editPathsDlg->isVisible())
      editPathsDlg->close();
    else
      editPathsDlg->show();
  } else {
    SETSTATUS("Create new edit paths dialog");
    editPathsDlg = new PathEditorDlg(this, dynamic_cast<MGXCamera*>(ui.Viewer->camera()));
    editPathsDlg->show();
  }
}

void MorphoGraphX::FileSaveSlot()
{
  if(projectFile().isEmpty())
    FileSaveAsSlot();
  else
    SaveView(projectFile());
}

void MorphoGraphX::FileSaveAsSlot()
{
  QStringList parms;
  parms << projectFile();
  LaunchProcess("Misc/System/Save View", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::FileQuickSaveSlot()
{
  QStringList parms;
//  if(!getProcessParms<QuickSaveFile>(globalProcess(), parms)) throw(QString("Unable to get QuickSaveFile Parameters"));
  //parms << "Yes" <<projectFile();
  LaunchProcess("Misc/System/Quick Save", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::ExitSlot()
{
  close();
}

QString MorphoGraphX::getMeshName(int num)
{
  ImgData* data = (num == 0) ? &stack1 : &stack2;
  return data->mesh->file();
}

QString MorphoGraphX::getStackName(bool main, int num)
{
  ImgData* data = (num == 0) ? &stack1 : &stack2;
  if(main)
    return data->stack->main()->file();
  else
    return data->stack->work()->file();
}

void MorphoGraphX::ImportMesh(int stack)
{
  QStringList parms;
  if(!getLastParms("Mesh/System/Import", parms)) {
    QMessageBox::critical(this, "Error importing mesh", "There is no Mesh process named 'Import'");
    return;
  }
  QString fn = getMeshName(stack);
  //if(!fn.isEmpty())
    parms[0] = fn;
  parms[5] = QString::number(stack);
  LaunchProcess("Mesh/System/Import", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::ResetMesh(int stack)
{
  QMessageBox::StandardButton a = QMessageBox::question(this, "Reset Mesh", "Are you sure you want to reset the mesh?",
                                                        QMessageBox::No | QMessageBox::Yes);
  if(a == QMessageBox::No)
    return;

  QStringList parms;
  if(!getLastParms("Mesh/System/Reset", parms)) {
    QMessageBox::critical(this, "Error reseting mesh", "There is no Mesh process named 'Reset'");
    return;
  }
  parms[0] = QString::number(stack);
  LaunchProcess("Mesh/System/Reset", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::ExportMesh(int stack)
{
  QStringList parms;
  if(!getLastParms("Mesh/System/Export", parms)) {
    QMessageBox::critical(this, "Error exporting mesh", "There is no Mesh process named 'Export'");
    return;
  }
  QString fn = getMeshName(stack);
  //if(!fn.isEmpty())
    parms[0] = fn;
  parms[3] = QString::number(stack);
  LaunchProcess("Mesh/System/Export", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::LoadMesh(int stack)
{
  QStringList parms;
  if(!getLastParms("Mesh/System/Load", parms)) {
    QMessageBox::critical(this, "Error loading mesh", "There is no Mesh process named 'Load'");
    return;
  }
  QString fn = getMeshName(stack);
  //if(!fn.isEmpty())
    parms[0] = fn;
  parms[3] = QString::number(stack);
  LaunchProcess("Mesh/System/Load", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::SaveMesh(int stack)
{
  QStringList parms;
  if(!getLastParms("Mesh/System/Save", parms)) {
    QMessageBox::critical(this, "Error saving mesh", "There is no Mesh process named 'Save'");
    return;
  }
  QString fn = getMeshName(stack);
  //if(!fn.isEmpty())
    parms[0] = fn;
  parms[2] = QString::number(stack);
  LaunchProcess("Mesh/System/Save", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::ImportStackSeries(int stack, bool main)
{
  QStringList parms;
  if(!getLastParms("Stack/System/Import", parms)) {
    QMessageBox::critical(this, "Error loading stack", "There is no Stack process named 'Import Stack'");
    return;
  }
  parms[0] = QString::number(stack);
  parms[1] = (main ? "Main" : "Work");
  LaunchProcess("Stack/System/Import", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::ImportStack(int stack, bool main)
{
  QStringList parms;
  if(!getLastParms("Stack/ITK/System/ITK Image Reader", parms)) {
    QMessageBox::critical(this, "Error loading stack", "There is no Stack process named 'ITK Image Reader'");
    return;
  }
  parms[1] = (main ? "Main" : "Work");
  parms[2] = QString::number(stack);
  LaunchProcess("Stack/ITK/System/ITK Image Reader", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::OpenStack(int stack, bool main)
{
  QStringList parms;
  if(!getLastParms("Stack/System/Open", parms)) {
    QMessageBox::critical(this, "Error opening stack", "There is no Stack process named 'Open Stack'");
    return;
  }
  QString fn = getStackName(main, stack);
  //if(!fn.isEmpty())
    parms[0] = fn;
  parms[1] = (main ? "Main" : "Work");
  parms[2] = QString::number(stack);
  LaunchProcess("Stack/System/Open", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::SaveStack(int stack, bool main)
{
  // Check if stack is scaled before saving
  Stack* s = currentSetup->stack(stack);
  if(s->showScale()) {
    QMessageBox::StandardButton a = QMessageBox::question(
        this, "Save Stack", QString("Stack %1 is scaled. Do you want to save it?").arg(stack + 1),
        QMessageBox::No | QMessageBox::Yes);
    if(a == QMessageBox::No)
      return;
  }

  QStringList parms;
  if(!getLastParms("Stack/System/Save", parms)) {
    QMessageBox::critical(this, "Error saving stack", "There is no Stack process named 'Save'");
    return;
  }
  QString fn = getStackName(main, stack);
  //if(!fn.isEmpty())
    parms[0] = fn;
  parms[1] = (main ? "Main" : "Work");
  parms[2] = QString::number(stack);
  LaunchProcess("Stack/System/Save", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::ExportStack(int stack, bool main)
{
  QStringList parms;
  if(!getLastParms("Stack/System/Export", parms)) {
    QMessageBox::critical(this, "Error exporting stack", "There is no Stack process named 'Export'");
    return;
  }
  parms[1] = (main ? "Main" : "Work");
  parms[4] = QString::number(stack);
  LaunchProcess("Stack/System/Export", parms, Process::PROCESS_RUN, true, true);
}
void MorphoGraphX::ManageAttributesMesh(int mesh)
{
  QStringList parms;
  if(!getLastParms("Mesh/Attributes/Manage Attributes", parms)) {
    QMessageBox::critical(this, "Error loading mesh", "There is no Mesh process named 'ManageAttributes'");
    return;
  }
  parms[0] = QString::number(mesh);
  LaunchProcess("Mesh/Attributes/Manage Attributes", parms, Process::PROCESS_RUN, true, true);
}

// Slots from the GUI
void MorphoGraphX::Stack1ImportSlot() { ImportStack(0, true); }
void MorphoGraphX::Stack1ImportSeriesSlot() { ImportStackSeries(0, true); }
void MorphoGraphX::Stack1SaveSlot() { SaveStack(0, true); }
void MorphoGraphX::Stack1ExportSlot() { ExportStack(0, true); }
void MorphoGraphX::Stack1OpenSlot() { OpenStack(0, true); }
void MorphoGraphX::Stack1ResetSlot() { stack1.resetStack(); ui.Viewer->updateAll(); }
void MorphoGraphX::Stack2ImportSlot() { ImportStack(1, true); }
void MorphoGraphX::Stack2ImportSeriesSlot() { ImportStackSeries(1, true); }
void MorphoGraphX::Stack2SaveSlot() { SaveStack(1, true); }
void MorphoGraphX::Stack2ExportSlot() { ExportStack(1, true); }
void MorphoGraphX::Stack2OpenSlot() { OpenStack(1, true); }
void MorphoGraphX::Stack2ResetSlot() { stack2.resetStack(); ui.Viewer->updateAll(); }
void MorphoGraphX::Stack1WorkImportSlot() { ImportStack(0, false); }
void MorphoGraphX::Stack1WorkImportSeriesSlot() { ImportStackSeries(0, false); }
void MorphoGraphX::Stack1WorkSaveSlot() { SaveStack(0, false); }
void MorphoGraphX::Stack1WorkExportSlot() { ExportStack(0, false); }
void MorphoGraphX::Stack1WorkOpenSlot() { OpenStack(0, false); }
void MorphoGraphX::Stack2WorkImportSlot() { ImportStack(1, false); }
void MorphoGraphX::Stack2WorkImportSeriesSlot() { ImportStackSeries(1, false); }
void MorphoGraphX::Stack2WorkSaveSlot() { SaveStack(1, false); }
void MorphoGraphX::Stack2WorkExportSlot() { ExportStack(1, false); }
void MorphoGraphX::Stack2WorkOpenSlot() { OpenStack(1, false); }
void MorphoGraphX::Mesh1LoadSlot() { LoadMesh(0); }
void MorphoGraphX::Mesh1SaveSlot() { SaveMesh(0); }
void MorphoGraphX::Mesh2ImportSlot() { ImportMesh(1); }
void MorphoGraphX::Mesh2ExportSlot() { ExportMesh(1); }
void MorphoGraphX::Mesh1ImportSlot() { ImportMesh(0); }
void MorphoGraphX::Mesh1ExportSlot() { ExportMesh(0); }
void MorphoGraphX::Mesh1ResetSlot() { ResetMesh(0); }
void MorphoGraphX::Mesh2LoadSlot() { LoadMesh(1); }
void MorphoGraphX::Mesh2SaveSlot() { SaveMesh(1); }
void MorphoGraphX::Mesh2ResetSlot() { ResetMesh(1); }
void MorphoGraphX::Mesh1ManageAttributes() { ManageAttributesMesh(0); }
void MorphoGraphX::Mesh2ManageAttributes() { ManageAttributesMesh(1); }
void MorphoGraphX::ProgressStopSlot() { progressCancel(); }

void MorphoGraphX::toggleControlsTab()
{
 int currentTab = ui.ControlsTab->currentIndex();
 currentTab++;
 if(currentTab>=ui.ControlsTab->count()) currentTab = 0;
 ui.ControlsTab->setCurrentIndex(currentTab);
}

void MorphoGraphX::toggleStackTabs()
{
 if(ui.ControlsTab->currentIndex() == 0){
   int currentTab = ui.StackTabs->currentIndex();
   currentTab++;
   if(currentTab>=ui.StackTabs->count()) currentTab = 0;
   ui.StackTabs->setCurrentIndex(currentTab);
 }
}

void MorphoGraphX::toggleMainCheckBox()
{
  if(ui.StackTabs->currentWidget() == ui.Stack1Tab) {
    ui.Stack1MainShow->setChecked(!ui.Stack1MainShow->isChecked());
  } else if(ui.StackTabs->currentWidget() == ui.Stack2Tab) {
    ui.Stack2MainShow->setChecked(!ui.Stack2MainShow->isChecked());
  }
}

void MorphoGraphX::toggleWorkCheckBox()
{
  if(ui.StackTabs->currentWidget() == ui.Stack1Tab) {
    ui.Stack1WorkShow->setChecked(!ui.Stack1WorkShow->isChecked());
  } else if(ui.StackTabs->currentWidget() == ui.Stack2Tab) {
    ui.Stack2WorkShow->setChecked(!ui.Stack2WorkShow->isChecked());
  }
}


void MorphoGraphX::toggleControlKeyInteraction()
{
 if(ui.Clip1Rotate->isChecked()) ui.Clip2Rotate->setChecked(true);
 else if(ui.Clip2Rotate->isChecked()) ui.Clip3Rotate->setChecked(true);
 else if(ui.Clip3Rotate->isChecked()) ui.Stack1Rotate->setChecked(true);
 else if(ui.Stack1Rotate->isChecked()) ui.Stack2Rotate->setChecked(true);
 else if(ui.Stack2Rotate->isChecked()) ui.CutSurfRotate->setChecked(true);
 else if(ui.CutSurfRotate->isChecked()) ui.Clip1Rotate->setChecked(true);
}

void MorphoGraphX::toggleMeshViewDropdown()
{
if(ui.ControlsTab->currentIndex() == 0){
  if(ui.StackTabs->currentIndex() == 0){
    int currentTab = ui.Stack1MeshView->currentIndex();
    currentTab++;
    if(currentTab>=ui.Stack1MeshView->count()) currentTab = 0;
    ui.Stack1MeshView->setCurrentIndex(currentTab);
  } else {

    int currentTab = ui.Stack2MeshView->currentIndex();
    currentTab++;
    if(currentTab>=ui.Stack2MeshView->count()) currentTab = 0;
    ui.Stack2MeshView->setCurrentIndex(currentTab);
  }
}
}

void MorphoGraphX::toggleMeshSurfaceView()
{
if(ui.ControlsTab->currentIndex() == 0){
  if(ui.StackTabs->currentIndex() == 0){
    int currentTab = ui.Stack1LabelView->currentIndex();
    currentTab++;
    if(currentTab>=ui.Stack1LabelView->count()) currentTab = 0;
    ui.Stack1LabelView->setCurrentIndex(currentTab);
  } else {

    int currentTab = ui.Stack2LabelView->currentIndex();
    currentTab++;
    if(currentTab>=ui.Stack2LabelView->count()) currentTab = 0;
    ui.Stack2LabelView->setCurrentIndex(currentTab);
  }
}
}



void MorphoGraphX::MeshSelectAll()
{
  QStringList parms;
  if(!getLastParms("Mesh/Selection/Select All", parms)) {
    QMessageBox::critical(this, "Error select all", "There is no Mesh process named 'Select All'");
    return;
  }
  LaunchProcess("Mesh/Selection/Select All", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::MeshUnselect()
{
  QStringList parms;
  if(!getLastParms("Mesh/Selection/Unselect", parms)) {
    QMessageBox::critical(this, "Error unselect", "There is no Mesh process named 'Unselect'");
    return;
  }
  LaunchProcess("Mesh/Selection/Unselect", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::MeshInvertSelection()
{
  QStringList parms;
  if(!getLastParms("Mesh/Selection/Invert Selection", parms)) {
    QMessageBox::critical(this, "Error invert selection", "There is no Mesh process named 'Invert Selection'");
    return;
  }
  LaunchProcess("Mesh/Selection/Invert Selection", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::MeshAddCurrentLabel()
{
  QStringList parms;
  if(!getLastParms("Mesh/Selection/Select Label", parms)) {
    QMessageBox::critical(this, "Error add current label", "There is no Mesh process named 'Select Label'");
    return;
  }
  parms[0] = "No";
  parms[1] = QString::number(ui.Viewer->selectedLabel);
  LaunchProcess("Mesh/Selection/Select Label", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::MeshAddUnlabeled()
{
  QStringList parms;
  if(!getLastParms("Mesh/Selection/Select Unlabeled", parms)) {
    QMessageBox::critical(this, "Error add unlabeled", "There is no Mesh process named 'Select Unlabeled'");
    return;
  }
  parms[0] = "No";
  LaunchProcess("Mesh/Selection/Select Unlabeled", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::MeshRemoveCurrentLabel()
{
  QStringList parms;
  if(!getLastParms("Mesh/Selection/Unselect Label", parms)) {
    QMessageBox::critical(this, "Error remove current label", "There is no Mesh process named 'Unselect Label'");
    return;
  }
  parms[0] = "0";
  LaunchProcess("Mesh/Selection/Unselect Label", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::ChangeCurrentSeed()
{
  bool ok;
  int value = QInputDialog::getInt(this, "Change Current Seed", "New value for the seed:", ui.Viewer->selectedLabel, 0,
                                   65535, 1, &ok);
  if(ok)
    ui.Viewer->setLabel(value);
}

void MorphoGraphX::UserManualSlot()
{
  QString link = "file://" + docDir().canonicalPath() + "/MGXUserManual.pdf";
  #ifdef WIN32
    QDesktopServices::openUrl(QUrl::fromLocalFile(link));
  #else
    QDesktopServices::openUrl(QUrl(link));
  #endif
}

void MorphoGraphX::DoxygenSlot()
{
  QString link = "file://" + docDir().canonicalPath() + "/html/index.html";
  Information::out << "Doxygen link:" << link << endl;
  #ifdef WIN32
    QDesktopServices::openUrl(QUrl::fromLocalFile(link));
  #else
    QDesktopServices::openUrl(QUrl(link));
  #endif
}

void MorphoGraphX::QGLViewerHelpSlot() 
{
  ui.Viewer->aboutQGLViewer();
}

void MorphoGraphX::AboutSlot()
{
  QString about(aboutText());

  QMessageBox dlg(tr("MorphoGraphX - A tool for 4D Image Processing (and More!)"), about, QMessageBox::Information, QMessageBox::Ok, QMessageBox::NoButton,
                  QMessageBox::NoButton, this);
  // Set icon
  dlg.setIconPixmap(QPixmap(":/images/Icon.png"));
  dlg.setTextFormat(Qt::RichText);

  // Force minimum width
  QSpacerItem* horizontalSpacer = new QSpacerItem(600, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
  QGridLayout* layout = (QGridLayout*)dlg.layout();
  layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());

  dlg.exec();
}

void MorphoGraphX::LabelColorSlot()
{
  QIcon icon;
  ui.actionLabelColor->setIcon(icon);
}

void MorphoGraphX::LabelColorSlot(QIcon& icon)
{
  ui.actionLabelColor->setIcon(icon);
}

void MorphoGraphX::on_actionAddNewSeed_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_ADD_SEED;
}

void MorphoGraphX::on_actionAddCurrentSeed_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_CURR_SEED;
}

void MorphoGraphX::on_actionDrawSignal_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_DRAW_SIGNAL;
}

void MorphoGraphX::on_actionPickLabel_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_PICK_LABEL;
}

void MorphoGraphX::on_actionGrabSeed_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_GRAB_SEED;
}

void MorphoGraphX::on_actionFillLabel_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_FILL_LABEL;
}

void MorphoGraphX::on_actionPickFillLabel_triggered(bool on)
{
  if(on) {
    ui.Viewer->guiAction = MorphoViewer::MESH_PICK_FILL_LABEL;
    ui.Viewer->meshPickFill = true;
  }
}

void MorphoGraphX::on_actionPickFillParent_triggered(bool on)
{
  if(on) {
    ui.Viewer->guiAction = MorphoViewer::MESH_PICK_FILL_PARENT;
  }
}

void MorphoGraphX::on_actionMeshSelect_toggled(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_SEL_POINTS;
  ImgData::MeshSelect = on;
}

void MorphoGraphX::on_actionLassoSelect_toggled(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_SEL_LASSO;
  ImgData::MeshSelect = on;
}



void MorphoGraphX::on_actionLabelSelect_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_SEL_LABEL;
}

void MorphoGraphX::on_actionConnectedSelect_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_SEL_CONN;
}

void MorphoGraphX::on_actionTriangleSelect_toggled(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::MESH_SEL_TRIS;
}

void MorphoGraphX::on_actionVoxelEdit_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::STACK_VOXEL_EDIT;
}

void MorphoGraphX::on_actionPickVolLabel_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::STACK_PICK_LABEL;
}

void MorphoGraphX::on_actionDeleteVolumeLabel_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::STACK_DEL_LABEL;
}

void MorphoGraphX::on_actionFillVolumeLabel_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::STACK_FILL_LABEL;
}

void MorphoGraphX::on_actionPickFillVolumeLabel_triggered(bool on)
{
  if(on)
    ui.Viewer->guiAction = MorphoViewer::STACK_PICK_FILL_LABEL;
}

void MorphoGraphX::on_actionRewind_triggered(bool on)
{
  // RSS This reloads the dll, maybe not so useful
  //ProcessDefinition* def = getProcessDefinition(currentProcessName);
  //if(def and !ui.Viewer->processRunning())
  //  def->factory->reload();
  if(ui.Viewer->processRunning()) {
    progressCancel();
    return;
  }

  if(currentProcessName.isEmpty()) {
    QMessageBox::warning(this, 
        "Process Control", "Please select a process before trying to run it");
    return;
  }

  ui.ProcessParameters->setFocus();
  QStringList parms = parmsModel->parms();
  if(LaunchProcess(currentProcessName, parms, Process::PROCESS_REWIND)) {
    parmsModel->setParms(parms);
  }
}

void MorphoGraphX::on_actionStop_triggered(bool on)
{
  progressCancel();
}

void MorphoGraphX::on_actionStep_triggered(bool on)
{
  if(ui.Viewer->processRunning()) {
    progressCancel();
    return;
  }

  if(currentProcessName.isEmpty()) {
    QMessageBox::warning(this, 
        "Process Control", "Please select a process before trying to run it");
    return;
  }
  ui.ProcessParameters->setFocus();
  QStringList parms = parmsModel->parms();
  if(LaunchProcess(currentProcessName, parms, Process::PROCESS_STEP)) {
    parmsModel->setParms(parms);
  }
}

void MorphoGraphX::on_actionRun_triggered(bool on)
{
  if(ui.Viewer->processRunning())
    return;

  if(currentProcessName.isEmpty()) {
    QMessageBox::warning(this, 
        "Process Control", "Please select a process before trying to run it");
    return;
  }
  ui.ProcessParameters->setFocus();
  QStringList parms = parmsModel->parms();
  if(LaunchProcess(currentProcessName, parms, Process::PROCESS_RUN)) {
    parmsModel->setParms(parms);
  }
}

void MorphoGraphX::EraseSelectSlot()
{
  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
  if(activeMesh() == 0)
    stack1.fillSelect(0);
  else if(activeMesh() == 1)
    stack2.fillSelect(0);
  ui.Viewer->updateAll();
  QApplication::restoreOverrideCursor();
}

void MorphoGraphX::FillSelectSlot()
{
  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
  if(activeMesh() == 0)
    stack1.fillSelect(ui.Viewer->selectedLabel);
  else if(activeMesh() == 1)
    stack2.fillSelect(ui.Viewer->selectedLabel);
  ui.Viewer->updateAll();
  QApplication::restoreOverrideCursor();
}

void MorphoGraphX::DeleteSelectionSlot()
{
  ImgData &stk = (activeMesh() == 0 ? stack1 : stack2);
  QStringList parms;
  if(!getLastParms("Mesh/Structure/Delete Selection", parms)) {
    QMessageBox::critical(this, "Error deleting mesh selection", 
                          "Cannot find process Mesh/Structure/Delete Selection");
    return;
  }
  LaunchProcess("Mesh/Structure/Delete Selection", parms, Process::PROCESS_RUN, true, true);
}

void MorphoGraphX::ColorPaletteSlot()
{
  ColorEditorDlg dlg(Colors::instance(), this);
  dlg.exec();
}

void MorphoGraphX::LabelsEditSlot(bool on)
{
  if(labelEditorDlg.isNull()) {
    labelEditorDlg = new LabelEditorDlg(&ImgData::LabelColors, this);
    labelEditorDlg->setCurrentLabel(ui.Viewer->selectedLabel);
    connect(ui.Viewer, SIGNAL(selectLabelChanged(int)), labelEditorDlg, SLOT(setCurrentLabel(int)));
    connect(labelEditorDlg, SIGNAL(update()), ui.Viewer, SLOT(UpdateLabels()));
    connect(labelEditorDlg, SIGNAL(finished(int)), ui.actionEditLabels, SLOT(toggle()));
    connect(labelEditorDlg, SIGNAL(selectLabel(int, int, bool)), ui.Viewer, SLOT(selectMeshLabel(int, int, bool)));
    connect(labelEditorDlg, SIGNAL(makeLabelCurrent(int)), ui.Viewer, SLOT(setLabel(int)));
  }
  labelEditorDlg->setVisible(on);
}

void MorphoGraphX::SwapSurfacesSlot()
{
  if((ui.Stack1SurfShow->isChecked() and !ui.Stack2SurfShow->isChecked())
     or (!ui.Stack1SurfShow->isChecked() and ui.Stack2SurfShow->isChecked())) {
    ui.Stack1SurfShow->toggle();
    ui.Stack2SurfShow->toggle();
    if(ui.Stack1SurfShow->isChecked())
      ui.StackTabs->setCurrentIndex(0);
    else if(ui.Stack2SurfShow->isChecked())
      ui.StackTabs->setCurrentIndex(1);
  } else if((ui.Stack1MeshShow->isChecked() and !ui.Stack2MeshShow->isChecked())
            or (!ui.Stack1MeshShow->isChecked() and ui.Stack2MeshShow->isChecked())) {
    ui.Stack1MeshShow->toggle();
    ui.Stack2MeshShow->toggle();
    if(ui.Stack1MeshShow->isChecked())
      ui.StackTabs->setCurrentIndex(0);
    else if(ui.Stack2MeshShow->isChecked())
      ui.StackTabs->setCurrentIndex(1);
  }
  ui.Viewer->updateAll();
}

void MorphoGraphX::EditParmsSlot()
{
  if(projectFile().isEmpty()) {
    QMessageBox::information(
      this, "Cannot edit parameters",
      "To edit the parameter file, you first need to create one. Save the current parameters in File->Save");
    return;
  }
  writeParms();
  QFile file(projectFile());
  if(file.open(QIODevice::ReadOnly)) {
    QTextStream stream(&file);
    stream.setCodec("UTF-8");
    editParmsDialog->Parms->setPlainText(stream.readAll());
  }
  file.close();

  if(editParmsDlog->exec() == QDialog::Accepted) {
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
      SETSTATUS("EditParms::Error:Cannot open output file:" << projectFile());
      return;
    }
    QTextStream stream(&file);
    stream.setCodec("UTF-8");
    stream << editParmsDialog->Parms->toPlainText();
    file.close();
    // editParmsDialog->Parms->setModified(false);
    SETSTATUS("Saved parameters to file:" << projectFile());
  }
  SaveProcessesParameters();
  readParms();
  loadControls();
  RecallProcessesParameters();
  ReloadTasks();
  ui.Viewer->updateAll();
}

void MorphoGraphX::EditParmsSaveAsSlot()
{
  // Be sure to grab current process parameters
  // processCommandGetParmValues(ImgData::ProcessCommandIndex);

  // Write to file
  QString filename
    = QFileDialog::getSaveFileName(this, QString("Select file to save parmaters to "), projectFile(),
                                   QString("Parameter files *.mgxv"), 0, QFileDialog::DontUseNativeDialog);
  if(filename == "")
    return;

  QFile file(filename);
  if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
    SETSTATUS("EditParmsSaveAs::Error:Cannot open output file:" << filename);
    return;
  }
  QTextStream stream(&file);
  stream << editParmsDialog->Parms->toPlainText();
  file.close();
  SETSTATUS("Saved parameters to file:" << projectFile());
}

// Display automatically generated documentation for processes, takes from tooltips
void docEntry(const ProcessDefinition& def, QTextStream& doc)
{
  doc << "<frame><b>" << def.name << "</b>: " << def.description;
  if(def.parmNames.size() > 0) {
    doc << "<br><b>Parameters</b>";
    doc << "<table width='100%' border=1 cell-spacing='1' style='border-width: 1px;border-style: solid'>";
    for(int i = 0; i < def.parmNames.size(); ++i) {
      doc << "<tr><td width='20%'>" << def.parmNames[i] << "</td>";
      if(i < def.parmDescs.size())
        doc << "<td>" << def.parmDescs[i] << "</td>";
      doc << "</tr>";
    }
  }
  doc << "</table></frame><br><br>";
}

void MorphoGraphX::ProcessDocsSlot()
{
  ProcessDocsDialog processDocsDlg(this);
  processDocsDlg.exec();
}

void MorphoGraphX::ProcessCommandTabSlot(int tab)
{
  QWidget *activeTab = ui.ProcessTabs->currentWidget();
  if(activeTab == ui.ProcessStack) {
    if(ui.ProcessStackCommand->topLevelItemCount() == 0)
      ProcessCommandSlot(0, 0);
    else
      ProcessCommandSlot(ui.ProcessStackCommand->currentItem(), 0);
  } else if(activeTab == ui.ProcessMesh) {
    if(ui.ProcessMeshCommand->topLevelItemCount() == 0)
      ProcessCommandSlot(0, 0);
    else
      ProcessCommandSlot(ui.ProcessMeshCommand->currentItem(), 0);
  } else if(activeTab == ui.ProcessMisc) {
    if(ui.ProcessMiscCommand->topLevelItemCount() == 0)
      ProcessCommandSlot(0, 0);
    else
      ProcessCommandSlot(ui.ProcessMiscCommand->currentItem(), 0);
  } else if(activeTab == ui.ProcessTasks) {
    if(ui.ProcessTasksCommand->topLevelItemCount() == 0)
      ProcessTasksCommandSlot(0, 0);
    else
      ProcessTasksCommandSlot(ui.ProcessTasksCommand->currentItem(), 0);
  }
  ui.ProcessParameters->resizeColumnToContents(0);
}

void MorphoGraphX::updateCurrentStack()
{
  QWidget *activeTab = ui.StackTabs->currentWidget();
  currentSetup->setCurrentMeshId(-1);
  currentSetup->setCurrentStackId(-1);
  Stack* s1 = currentSetup->stack(0);
  s1->setMainAsCurrent();
  Stack* s2 = currentSetup->stack(1);
  s2->setMainAsCurrent();
  if(ui.Stack1WorkShow->isChecked())
    s1->setWorkAsCurrent();
  if(ui.Stack2WorkShow->isChecked())
    s2->setWorkAsCurrent();

  QString activeText, meshType;
  if(activeTab == ui.Stack1Tab) {
    currentSetup->setCurrentStackId(0);
    if(ui.Stack1WorkShow->isChecked()) {
      currentSetup->setCurrentMeshId(0);
      activeText = "Work Stack 1";
    } else if(ui.Stack1MainShow->isChecked()) {
      currentSetup->setCurrentMeshId(0);
      activeText = "Main Stack 1";
    } else if(ui.Stack1SurfShow->isChecked() or ui.Stack1MeshShow->isChecked()) {
      currentSetup->setCurrentMeshId(0);
      activeText = "Stack 1";
    } else 
      activeText = "No Stack";
    meshType = stack1.mesh->meshType();
  } else if(activeTab == ui.Stack2Tab) {
    if(ui.Stack2WorkShow->isChecked()) {
      currentSetup->setCurrentStackId(1);
      currentSetup->setCurrentMeshId(1);
      activeText = "Work Stack 2";
    } else if(ui.Stack2MainShow->isChecked()) {
      currentSetup->setCurrentStackId(1);
      currentSetup->setCurrentMeshId(1);
      activeText = "Main Stack 2";
    } else if(ui.Stack2SurfShow->isChecked() or ui.Stack2MeshShow->isChecked()) {
      currentSetup->setCurrentMeshId(1);
      activeText = "Stack 2";
    } else 
      activeText = "No Stack";
    meshType = stack2.mesh->meshType();
  } else {
    activeText = "No Stack";
    meshType = "No";
  }
  ActiveStack->setText(QString("%1 Active - %2 Mesh").arg(activeText).arg(meshType));
}

void MorphoGraphX::ProcessCommandSlot(QTreeWidgetItem* current, QTreeWidgetItem*)
{
  if(current == 0 or current->childCount() > 0) {
    parmsModel->clear();
    currentProcessName.clear();
  } else {
    currentProcessInTasks = false;
    QString name = current->text(1);
    currentProcessName = name;
    parmsModel->setParms(processes[name]);
  }
}

void MorphoGraphX::ProcessTasksCommandSlot(QTreeWidgetItem* current, QTreeWidgetItem*)
{
  if(current == 0) {
    parmsModel->clear();
    currentProcessName.clear();
  } else {
    bool found = false;
    if(current->flags() & Qt::ItemIsEnabled) {
      currentProcessName = current->text(0);
      currentProcessInTasks = true;
      QString task = current->data(0, TaskNameRole).toString();
      int proc_num = current->data(0, ProcessPositionRole).toInt();
      if(tasks.contains(task)) {
        const QList<ProcessDefinition>& defs = tasks[task];
        if(proc_num < defs.size()) {
          const ProcessDefinition& def = defs[proc_num];
          parmsModel->setParms(def);
          found = true;
        }
      }
    }
    if(!found) {
      parmsModel->clear();
      currentProcessName.clear();
    }
  }
}

void MorphoGraphX::SeedStackSlot(bool val)
{
  ImgData::SeedStack = val;
  ui.Viewer->updateAll();
}

bool MorphoGraphX::LaunchProcess(const QString& processName, QStringList& parms, Process::ProcessAction processAction, 
                                                         bool useGUI, bool saveParms)
{
  if(processThread) {
    // QMessageBox::critical(this, QString("Error starting process %1").arg(processName),
    //      QString("The process '%1' is already running. Please wait for the process to finish " 
    //      "before starting another one").arg(currentProcess->name()));
    return false;
  }
  size_t expectedParms = size_t(-1);
  if(!checkProcessParms(processName, parms, &expectedParms)) {
    if(expectedParms == size_t(-1))
      QMessageBox::critical(this, "Error starting process",
         QString("There is no process named '%1'").arg(processName));
    else
      QMessageBox::critical(this, "Error starting process",
        QString("Expected %1 arguments for process %2, but %3 were provided")
                                                .arg(expectedParms).arg(processName).arg(parms.size()));
    return false;
  }

  updateCurrentStack();
  currentSetup->resetModified();

  currentProcess = currentSetup->makeProcess(processName);
  if(not currentProcess) {
    QMessageBox::critical(this, "Error finding process",
                          QString("There is no process named '%1'").arg(processName));
    return false;
  }

  // Disconnect any slots
  disconnect(currentProcess, 0, 0, 0);
  // Connect Gui slot for system commands
  connect(currentProcess, SIGNAL(systemCommandGui(mgx::Process *, int, QStringList)), 
            this, SLOT(systemCommand(mgx::Process *, int, QStringList)));

  // Call the process initialize()
  // RSS Fix: processes should look in parent to see if they are to use the GUI
  try {
    currentProcess->setParms(parms);
    if(not currentProcess->initialize(useGUI ? this : 0))
      return false;
    bool valid;
    if(saveParms) {
      valid = saveDefaultParms(processName, parms);
      // If the running process is selected in the GUI, update the parms there too.
      if(processName == currentProcessName)
        parmsModel->setParms(parms);
    } else
      valid = checkProcessParms(processName, parms);
    if(not valid) {
      QMessageBox::critical(this, "Error during process initialization", "Parameters invalid");
      return false;
    }
  } catch(const QString& str) {
    QMessageBox::critical(this, "Error during process initialization: ", str);
    return false;
  } catch(const std::exception& ex) {
    QMessageBox::critical(this, "Error during process initialization: ", 
                                                  QString::fromLocal8Bit(ex.what()));
    return false;
  } catch(...) {
    QMessageBox::critical(this, "Error during process initialization", "Unknown exception");
    return false;
  }

  setStatus(QString());

  // Save the current command in MorphoGraphX.py, but only if it already exists
  if(QFile::exists("MorphoGraphX.py")) {
    QFile file("MorphoGraphX.py");
    if(file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
      QTextStream ss(&file);
      currentProcess->p->currentPythonCall = currentProcess->pythonCall(parms);
      ss << currentProcess->p->currentPythonCall << endl;
      file.close();
    }
  }

  progressStart(QString("Running process %1").arg(processName), 0, false);

  processThread = new ProcessThread(currentProcess, processAction, this);
  // Connect process slot for system commands
  connect(currentProcess, SIGNAL(systemCommandProcess(mgx::Process *, int, QStringList)), 
            this, SLOT(systemCommand(mgx::Process *, int, QStringList)), Qt::BlockingQueuedConnection);
  // Connect thread finish slots
  connect(processThread, SIGNAL(finished()), this, SLOT(ProcessCommandFinished()));
  connect(processThread, SIGNAL(finished()), processThread, SLOT(deleteLater()));

  currentProcessTime.start();
  //ui.ControlsTab->setDisabled(true); // RSS Is it dangerous to leave this open?
  ui.menubar->setDisabled(true);
  ui.volumeToolbar->setDisabled(true);
  ui.miscToolbar->setDisabled(true);
  ui.meshToolbar->setDisabled(true);
  ui.actionRewind->setDisabled(true);
  ui.actionRun->setDisabled(true);
  ui.actionStep->setDisabled(true);
  ui.Viewer->processRunning(true);

  // These can cause mesh access
  ui.Stack1VertexView->setDisabled(true);
  ui.Stack2VertexView->setDisabled(true);
  ui.Stack1TriangleView->setDisabled(true);
  ui.Stack2TriangleView->setDisabled(true);
  ui.Stack1LabelView->setDisabled(true);
  ui.Stack2LabelView->setDisabled(true);

  processThread->start();
  return true;
}

void MorphoGraphX::ProcessCommandFinished()
{
  QString name = currentProcess->name();
  name.replace(" ", "_");
  int msec = currentProcessTime.elapsed();
  Information::out << name << " Process running time: " << (msec / 1000.0) << " s." << endl;

  // Call the process finalize()
  bool useGUI = true;
  try {
    if(!currentProcess->finalize(useGUI ? this : 0))
      QMessageBox::critical(this, "Error during process finalize: ", 
                                                               "Process returned error status");
  } catch(const QString& str) {
    QMessageBox::critical(this, "Error during process finalize: ", str);
  } catch(const std::exception& ex) {
    QMessageBox::critical(this, "Error during process finalize: ", 
                                                             QString::fromLocal8Bit(ex.what()));
  } catch(...) {
    QMessageBox::critical(this, "Error during process finalize: ", "Unknown exception");
  }

  //ui.ControlsTab->setEnabled(true);
  ui.menubar->setEnabled(true);
  ui.volumeToolbar->setEnabled(true);
  ui.miscToolbar->setEnabled(true);
  ui.meshToolbar->setEnabled(true);
  ui.actionRewind->setEnabled(true);
  ui.actionRun->setEnabled(true);
  ui.actionStep->setEnabled(true);
  ui.Viewer->processRunning(false);

  // These can cause mesh access
  ui.Stack1VertexView->setEnabled(true);
  ui.Stack2VertexView->setEnabled(true);
  ui.Stack1TriangleView->setEnabled(true);
  ui.Stack2TriangleView->setEnabled(true);
  ui.Stack1LabelView->setEnabled(true);
  ui.Stack2LabelView->setEnabled(true);

  // RSS step() returns false when finished and run() when an error occurred
  // We'll therefore use error messages to determine success
  // bool success = processThread->exitStatus();
  bool success = true;
  if(!processThread->warningMessage().isEmpty())
    QMessageBox::information(this, QString("Warning running process %1")
                     .arg(currentProcess->name()), processThread->warningMessage());
  else if(!processThread->errorMessage().isEmpty()) {
    QMessageBox::critical(this, QString("Error running process %1").arg(currentProcess->name()),
                          processThread->errorMessage());
    success = false;
  }

  // If a process partially completes, we still may need to update things.
  updateStateFromProcess(currentProcess);
  ui.Viewer->updateAll();
  processThread = 0;
  currentProcess = 0;
  progressStop();
  currentSetup->p->currentPythonCall.clear();

  if(success)
    emit processFinished();
  else
    emit processFailed();
}

bool MorphoGraphX::setFrameVis(const QString &name, ulong iFlags, const Point3d &pos, const Quaternion &orient)
{
  if(name.isEmpty()) {
    Information::out << "Empty frame name passed to setFrameVis" << endl;
    return false;
  }
  if(!(iFlags == iFlags)) {
    Information::out << "Bad flags passed to setFrameVis" << endl;
    return false;
  }
  if(!(pos == pos)) {
    Information::out << "Bad position passed to setFrameVis" << endl;
    return false;
  }
  if(!(orient == orient)) {
    Information::out << "Bad orientation passed to setFrameVis" << endl;
    return false;
  }

  VisFlags flags;
  flags.iFlags = iFlags;
  qglviewer::ManipulatedFrame *frame = 0;

  if(name == "Clip1") {
    bool enabled = flags.clipFlags.enabled;
    if(enabled != currentSetup->clip1()->enabled()) {
      if(enabled)
        currentSetup->clip1()->enable();
      else
        currentSetup->clip1()->disable();
      ui.Clip1Enable->setChecked(enabled);
    }
    bool grid = flags.clipFlags.grid;
    if(grid != currentSetup->clip1()->grid()) {
      if(grid)
        currentSetup->clip1()->showGrid();
      else
        currentSetup->clip1()->hideGrid();
      ui.Clip1Grid->setChecked(grid);
    }
    frame = &currentSetup->clip1()->frame();
  } else if(name == "Clip2") {
    bool enabled = flags.clipFlags.enabled;
    if(enabled != currentSetup->clip2()->enabled()) {
      if(enabled)
        currentSetup->clip2()->enable();
      else
        currentSetup->clip2()->disable();
      ui.Clip2Enable->setChecked(enabled);
    }
    bool grid = flags.clipFlags.grid;
    if(grid != currentSetup->clip2()->grid()) {
      if(grid)
        currentSetup->clip2()->showGrid();
      else
        currentSetup->clip2()->hideGrid();
      ui.Clip2Grid->setChecked(grid);
    }
    frame = &currentSetup->clip2()->frame();
  } else if(name == "Clip3") {
    bool enabled = flags.clipFlags.enabled;
    if(enabled != currentSetup->clip3()->enabled()) {
      if(enabled)
        currentSetup->clip3()->enable();
      else
        currentSetup->clip3()->disable();
      ui.Clip3Enable->setChecked(enabled);
    }
    bool grid = flags.clipFlags.grid;
    if(grid != currentSetup->clip3()->grid()) {
      if(grid)
        currentSetup->clip3()->showGrid();
      else
        currentSetup->clip3()->hideGrid();
      ui.Clip3Grid->setChecked(grid);
    }
    frame = &currentSetup->clip3()->frame();
  } else if(name == "CutSurf") {
    bool enabled = flags.cutSurfFlags.enabled;
    if(enabled != currentSetup->cuttingSurface()->isVisible()) {
      if(enabled)
        currentSetup->cuttingSurface()->show();
      else
        currentSetup->cuttingSurface()->hide();
      ui.DrawCutSurf->setChecked(enabled);
    }
    bool grid = flags.cutSurfFlags.grid;
    if(grid != currentSetup->cuttingSurface()->drawGrid()) {
      if(grid)
        currentSetup->cuttingSurface()->showGrid();
      else
        currentSetup->cuttingSurface()->hideGrid();
      ui.CutSurfGrid->setChecked(grid);
    }
    frame = &currentSetup->cuttingSurface()->frame();
  } else if(name == "Stack1") {
    bool main = flags.stackFlags.main;
    if(main != currentSetup->stack(0)->main()->isVisible()) {
      if(main)
        currentSetup->stack(0)->main()->show();
      else
        currentSetup->stack(0)->main()->hide();
      ui.Stack1MainShow->setChecked(main);
    }
    bool work = flags.stackFlags.work;
    if(work != currentSetup->stack(0)->work()->isVisible()) {
      if(work)
        currentSetup->stack(0)->work()->show();
      else
        currentSetup->stack(0)->work()->hide();
      ui.Stack1WorkShow->setChecked(work);
    }
    frame = &currentSetup->stack(0)->frame();
    } else if(name == "Stack2") {
    bool main = flags.stackFlags.main;
    if(main != currentSetup->stack(1)->main()->isVisible()) {
      if(main)
        currentSetup->stack(1)->main()->show();
      else
        currentSetup->stack(1)->main()->hide();
      ui.Stack1MainShow->setChecked(main);
    }
    bool work = flags.stackFlags.work;
    if(work != currentSetup->stack(1)->work()->isVisible()) {
      if(work)
        currentSetup->stack(1)->work()->show();
      else
        currentSetup->stack(1)->work()->hide();
      ui.Stack1WorkShow->setChecked(work);
    }
    frame = &currentSetup->stack(1)->frame();
  }
  if(!frame)
    return false;

  // Set position and orientation
  if(pos != Point3d(frame->position()) or orient != Point4d(frame->orientation()))
    frame->setPositionAndOrientation(qglviewer::Vec(pos), qglviewer::Quaternion(orient.x(), orient.y(), orient.z(), orient.w()));

  return true;
}

bool MorphoGraphX::setCameraVis(const Point3d &pos, const Quaternion &orient, const Point3d &center, double zoom)
{
  MGXCamera *camera = dynamic_cast<MGXCamera *>(ui.Viewer->camera());
  if(!camera) {
    Information::out << "Bad camera passed to setCameraVis" << endl;
    return false;
  }
  if(!(pos == pos)) {
    Information::out << "Bad position passed to setCameraVis" << endl;
    return false;
  }
  if(!(orient == orient)) {
    Information::out << "Bad orientation passed to setCameraVis" << endl;
    return false;
  }
  if(!(center == center)) {
    Information::out << "Bad center passed to setCameraVis" << endl;
    return false;
  }
  if(!(zoom == zoom)) {
    Information::out << "Bad zoom passed to setCameraVis" << endl;
    return false;
  }

  if(center != Point3d(camera->sceneCenter()))
    camera->setSceneCenter(qglviewer::Vec(center));
  if(zoom != camera->zoom())
    camera->setZoom(zoom);

  if(pos != Point3d(camera->frame()->position()) or orient != Point4d(camera->frame()->orientation()))
    camera->frame()->setPositionAndOrientation(qglviewer::Vec(pos), qglviewer::Quaternion(orient.x(), orient.y(), orient.z(), orient.w()));

  return true;
} 

void MorphoGraphX::systemCommand(mgx::Process *proc, int command, QStringList parms)
{
  if(command == UPDATE_VIEWER) {
    updateViewer();
  } else if(command == UPDATE_STATE) {
    if(!proc)
      Information::out << "Bad parameters passed to Update State" << endl;
    else
      updateStateFromProcess(proc);
  } else if(command == SET_STATUS) {
    if(parms.size() < 2)
      Information::out << "Bad parameters passed to Set Status" << endl;
    else
      setStatus(parms[0], stringToBool(parms[1]));
  } else if(command == LOAD_VIEW) {
    if(!proc or parms.size() < 1)
      Information::out << "Bad parameters passed to Load View" << endl;
    else
      loadView(proc, parms[0]);
  } else if(command == SAVE_VIEW) {
    if(!proc or parms.size() < 1)
      Information::out << "Bad parameters passed to Save View" << endl;
    else
      saveView(proc, parms[0]);
  } else if(command == SET_CURRENT_STACK) {
    if(!proc or parms.size() < 2)
      Information::out << "Bad parameters passed to Set Current Stack" << endl;
    else
      setCurrentStack(proc, parms[0], parms[1].toInt());
  } else if(command == TAKE_SNAPSHOT) {
    if(!proc or parms.size() < 6)
      Information::out << "Bad parameters passed to Take Snapshot" << endl;
    else
      takeSnapshot(proc, parms[0], parms[1].toFloat(), parms[2].toInt(), parms[3].toInt(), 
        parms[4].toInt(), stringToBool(parms[5]));
  } else if(command == SET_FRAME_VIS) {
    if(!proc or parms.size() < 4)
      Information::out << "Bad parameters passed to Set Frame Vis" << endl;
    else
      setFrameVis(parms[0], parms[1].toULong(), stringToPoint3d(parms[2]), stringToPoint4d(parms[3]));
  } else if(command == SET_CAMERA_VIS) {
    if(!proc or parms.size() < 4)
      Information::out << "Bad parameters passed to Set Camera Vis" << endl;
    else
      setCameraVis(stringToPoint3d(parms[0]), stringToPoint4d(parms[1]), stringToPoint3d(parms[2]), parms[3].toDouble());
  }
}

void MorphoGraphX::updateViewer()
{
  ui.Viewer->updateAll();
}

void MorphoGraphX::loadView(Process *proc, QString fileName)
{
  setProjectFile(fileName);

  // Save the process parameters
  SaveProcessesParameters();

  // Load the project
  readParms();

  RecallProcessesParameters();
  loadControls();
  ReloadTasks();

  ui.Viewer->updateAll();
}

void MorphoGraphX::saveView(Process *proc, QString fileName)
{
  setProjectFile(fileName);
  saveSettings();

  updateStateFromProcess(proc);

  // Save the process parameters
  SaveProcessesParameters();

  // Load the project
  writeParms();

  ui.Viewer->updateAll();
}

void MorphoGraphX::setCurrentStack(mgx::Process *proc, const QString &store, int id)
{
  if(!proc) {
    std::cout << "setCurrentStack Null process pointer" << std::endl;
    return;
  }
  if(id < ui.StackTabs->count()) {
    proc->p->current_stack = id;
    proc->p->current_mesh = id;
    if(store == "Main")
      proc->p->_stacks[id]->setMainAsCurrent();
    else
      proc->p->_stacks[id]->setWorkAsCurrent();
    ui.StackTabs->setCurrentIndex(id);
    bool showMain = false;
    bool showWork = false;
    if(store == "Main") {
      proc->p->_stacks[id]->main()->show();
      proc->p->_stacks[id]->work()->hide();
      showMain = true;
    } else if(store == "Work") {
      proc->p->_stacks[id]->main()->hide();
      proc->p->_stacks[id]->work()->show();
      showWork = true;
    } else if(store == "Both") {
      proc->p->_stacks[id]->work()->show();
      proc->p->_stacks[id]->main()->show();
      showMain = true;
      showWork = true;
    } else if(store == "None") {
      proc->p->_stacks[id]->work()->hide();
      proc->p->_stacks[id]->main()->hide();
    } else {
      std::cout << QString("%1: Invalid store: %2").arg(proc->name()).arg(store).toStdString() << endl;
      return;
    }
    switch(id) {
    case 0:
      ui.Stack1WorkShow->setChecked(showMain);
      ui.Stack1WorkShow->setChecked(showWork);
      break;
    case 1:
      ui.Stack2MainShow->setChecked(showMain);
      ui.Stack2WorkShow->setChecked(showWork);
      break;
    }
  }
}

void MorphoGraphX::takeSnapshot(mgx::Process *proc, QString fileName, 
        float overSampling, int width, int height, int quality, bool expandFrustum)
{
  updateStateFromProcess(proc);
  ui.Viewer->updateAll();
  // Force event processing
  QCoreApplication::flush();

  if(width == 0)
    width = ui.Viewer->width();
  if(height == 0)
    height = ui.Viewer->height();

  QString error;
  proc->p->success = ui.Viewer->saveImageSnapshot(fileName, QSize(width, height), overSampling, expandFrustum, false, &error);
  if(!proc->p->success)
    proc->setErrorMessage(error);
}

void MorphoGraphX::setStatus(QString text, bool alsoPrint)
{
  Information::setStatus(text, alsoPrint);
}

void MorphoGraphX::updateStateFromProcess(Process* proc)
{
  QMutexLocker locker(&proc->p->lock);
  // First, check the stores and stacks
  Store* mainStore1 = 0, *mainStore2 = 0, *workStore1 = 0, *workStore2 = 0;
  Mesh* mesh1 = 0, *mesh2 = 0;
  Stack* _stack1 = 0, *_stack2 = 0;
  for(int i = 0; i < proc->stackCount(); ++i) {
    Stack* stack = proc->stack(i);
    if(stack->id() == 0) {
      _stack1 = stack;
      mainStore1 = stack->main();
      workStore1 = stack->work();
    } else if(stack->id() == 1) {
      _stack2 = stack;
      mainStore2 = stack->main();
      workStore2 = stack->work();
    }
  }
  for(int i = 0; i < proc->meshCount(); ++i) {
    Mesh* mesh = proc->mesh(i);
    if(mesh->id() == 0)
      mesh1 = mesh;
    else if(mesh->id() == 1)
      mesh2 = mesh;
  }
  if(_stack1) {
    stack1.stack = _stack1;
    stack1.updateStackSize();
    ui.Stack1Scale_X->setValue(stack1.toSliderScale(_stack1->scale().x()));
    ui.Stack1Scale_Y->setValue(stack1.toSliderScale(_stack1->scale().y()));
    ui.Stack1Scale_Z->setValue(stack1.toSliderScale(_stack1->scale().z()));

    if(mainStore1->data().size() != _stack1->storeSize()) {
      mainStore1->reset();
      mainStore1->changed();
    }
    if(mainStore1->wasChanged())
      stack1.reloadMainTex(mainStore1->changedBBox());
    if(mainStore1->transferFunctionChanged())
      stack1.updateMainColorMap();

    if(workStore1->data().size() != _stack1->storeSize()) {
      SETSTATUS("Warning, work store 1 is not of the right size: deleting");
      workStore1->reset();
      workStore1->changed();
    }
    if(workStore1->wasChanged())
      stack1.reloadWorkTex(workStore1->changedBBox());
    if(workStore1->transferFunctionChanged())
      stack1.updateWorkColorMap();

    ui.Stack1MainShow->setChecked(mainStore1->isVisible());
    ui.Stack1WorkShow->setChecked(workStore1->isVisible());

    ui.Stack1ShowTrans->setChecked(_stack1->showTrans());
    ui.Stack1ShowBBox->setChecked(_stack1->showBBox());
    ui.Stack1ShowScale->setChecked(_stack1->showScale());
    ui.Stack1TieScales->setChecked(_stack1->tieScales());

    ui.Stack1MainLabels->setChecked(mainStore1->labels());
    ui.Stack1WorkLabels->setChecked(workStore1->labels());

    ui.Stack1MainBright->setValue(mainStore1->brightness() * 10000);
    ui.Stack1MainOpacity->setValue(mainStore1->opacity() * 10000);
    ui.Stack1WorkBright->setValue(workStore1->brightness() * 10000);
    ui.Stack1WorkOpacity->setValue(workStore1->opacity() * 10000);
  }

  if(_stack2) {
    stack2.stack = _stack2;
    stack2.updateStackSize();
    ui.Stack2Scale_X->setValue(stack2.toSliderScale(_stack2->scale().x()));
    ui.Stack2Scale_Y->setValue(stack1.toSliderScale(_stack2->scale().y()));
    ui.Stack2Scale_Z->setValue(stack1.toSliderScale(_stack2->scale().z()));

    if(mainStore2->data().size() != _stack2->storeSize()) {
      SETSTATUS("Warning, main store 2 is not of the right size: deleting");
      mainStore2->reset();
      mainStore2->changed();
    }

    if(workStore2->data().size() != _stack2->storeSize()) {
      SETSTATUS("Warning, work store 2 is not of the right size: deleting");
      workStore2->reset();
      workStore2->changed();
    }

    if(mainStore2->wasChanged())
      stack2.reloadMainTex(mainStore2->changedBBox());
    if(mainStore2->transferFunctionChanged())
      stack2.updateMainColorMap();

    if(workStore2->wasChanged())
      stack2.reloadWorkTex(workStore2->changedBBox());
    if(workStore2->transferFunctionChanged())
      stack2.updateWorkColorMap();

    ui.Stack2MainShow->setChecked(mainStore2->isVisible());
    ui.Stack2WorkShow->setChecked(workStore2->isVisible());

    ui.Stack2ShowTrans->setChecked(_stack2->showTrans());
    ui.Stack2ShowBBox->setChecked(_stack2->showBBox());
    ui.Stack2ShowScale->setChecked(_stack2->showScale());
    ui.Stack2TieScales->setChecked(_stack2->tieScales());

    ui.Stack2MainLabels->setChecked(mainStore2->labels());
    ui.Stack2WorkLabels->setChecked(workStore2->labels());

    ui.Stack2MainBright->setValue(mainStore2->brightness() * 10000);
    ui.Stack2MainOpacity->setValue(mainStore2->opacity() * 10000);
    ui.Stack2WorkBright->setValue(workStore2->brightness() * 10000);
    ui.Stack2WorkOpacity->setValue(workStore2->opacity() * 10000);
  }

  // Then the meshes
  if(mesh1) {
    stack1.mesh = mesh1;
    if(mesh1->changes() & ImgData::RELOAD_VBO)
      stack1.fillVBOs();
    else {
      if(mesh1->changes() & ImgData::RELOAD_POS) { 
        stack1.updatePos();
        stack1.updateTriPos();
      }
      if(mesh1->changes() & ImgData::RELOAD_LINES)
        stack1.updateLines();
      if(mesh1->changes() & ImgData::RELOAD_TRIS)
        stack1.updateTriColor();
      if((mesh1->changes() & ImgData::UPDATE_SELECTION) and 
                                    not (mesh1->changes() & ImgData::RELOAD_LINES))
        stack1.updateSelection();
      // Always do these, maybe we can speed this up?
      stack1.updateCellGraph();
      stack1.updateAxis();
    }
    if(mesh1->hasImgTex() and not mesh1->imgTex().isNull())
      stack1.loadImgTex(mesh1->imgTex());
    if(mesh1->surfFctChanged())
      stack1.updateSurfColorMap(mesh1->surfFct());
    if(mesh1->heatFctChanged())
      stack1.updateHeatColorMap(mesh1->heatFct());
    ui.Stack1SurfCull->setChecked(mesh1->culling());
    ui.Stack1SurfBlend->setChecked(mesh1->blending());

    ui.Stack1MeshShow->setChecked(mesh1->showMesh());
    ui.Stack1AxisShow->setChecked(mesh1->showAxis());
    ui.Stack1SurfShow->setChecked(mesh1->showSurface());
    ui.Stack1SurfParent->setChecked(mesh1->useParents());
    switch(mesh1->surfView()) {
      case Mesh::SURF_VERTEX:
        ui.Stack1SurfVertex->setChecked(true);
        break;
      case Mesh::SURF_TRIANGLE:
        ui.Stack1SurfTriangle->setChecked(true);
        break;
      case Mesh::SURF_LABEL:
        ui.Stack1SurfLabel->setChecked(true);
        break;
    }
    int index = 0;
    if((index = ui.Stack1VertexView->findText(mesh1->surfVertexView())) >= 0)
      ui.Stack1VertexView->setCurrentIndex(index);
    if((index = ui.Stack1TriangleView->findText(mesh1->surfTriangleView())) >= 0)
      ui.Stack1TriangleView->setCurrentIndex(index);
    if((index = ui.Stack1LabelView->findText(mesh1->surfLabelView())) >= 0)
      ui.Stack1LabelView->setCurrentIndex(index);

    if((index = ui.Stack1MeshView->findText(mesh1->meshView())) >= 0)
      ui.Stack1MeshView->setCurrentIndex(index);
    if((index = ui.Stack1AxisView->findText(mesh1->axisView())) >= 0)
      ui.Stack1AxisView->setCurrentIndex(index);

    ui.Stack1MeshLines->setChecked(mesh1->showMeshLines());
    ui.Stack1MeshPoints->setChecked(mesh1->showMeshPoints());
    ui.Stack1CellMap->setChecked(mesh1->showMeshCellMap());

    ui.Stack1SurfBright->setValue(mesh1->brightness() * 10000);
    ui.Stack1SurfOpacity->setValue(mesh1->opacity() * 10000);
  }

  if(mesh2) {
    stack2.mesh = mesh2;
    if(mesh2->changes() & ImgData::RELOAD_VBO)
      stack2.fillVBOs();
    else {
      if(mesh2->changes() & ImgData::RELOAD_POS) {
        stack2.updatePos();
        stack2.updateTriPos();
      }
      if(mesh2->changes() & ImgData::RELOAD_LINES)
        stack2.updateLines();
      if(mesh2->changes() & ImgData::RELOAD_TRIS)
        stack2.updateTriColor();
      if((mesh2->changes() & ImgData::UPDATE_SELECTION) and 
                                    not (mesh2->changes() & ImgData::RELOAD_LINES))
        stack2.updateSelection();
      stack2.updateCellGraph();
      stack2.updateAxis();
    }

    if(mesh2->hasImgTex() and not mesh2->imgTex().isNull())
      stack2.loadImgTex(mesh2->imgTex());
    if(mesh2->surfFctChanged())
      stack2.updateSurfColorMap(mesh2->surfFct());
    if(mesh2->heatFctChanged())
      stack2.updateHeatColorMap(mesh2->heatFct());
    ui.Stack2SurfCull->setChecked(mesh2->culling());
    ui.Stack2SurfBlend->setChecked(mesh2->blending());

    ui.Stack2MeshShow->setChecked(mesh2->showMesh());
    ui.Stack2AxisShow->setChecked(mesh2->showAxis());
    ui.Stack2SurfShow->setChecked(mesh2->showSurface());
    ui.Stack2SurfParent->setChecked(mesh2->useParents());
    switch(mesh2->surfView()) {
    case Mesh::SURF_VERTEX:
      ui.Stack2SurfVertex->setChecked(true);
      break;
    case Mesh::SURF_TRIANGLE:
      ui.Stack2SurfTriangle->setChecked(true);
      break;
    case Mesh::SURF_LABEL:
      ui.Stack2SurfLabel->setChecked(true);
      break;
    }
    int index = 0;
    if((index = ui.Stack2VertexView->findText(stack2.mesh->surfVertexView())) >= 0)
      ui.Stack2VertexView->setCurrentIndex(index);
    if((index = ui.Stack2TriangleView->findText(stack2.mesh->surfTriangleView())) >= 0)
      ui.Stack2TriangleView->setCurrentIndex(index);
    if((index = ui.Stack2LabelView->findText(stack2.mesh->surfLabelView())) >= 0)
      ui.Stack2LabelView->setCurrentIndex(index);

    if((index = ui.Stack2MeshView->findText(mesh2->meshView())) >= 0)
      ui.Stack2MeshView->setCurrentIndex(index);
    if((index = ui.Stack1AxisView->findText(mesh2->axisView())) >= 0)
      ui.Stack2AxisView->setCurrentIndex(index);

    ui.Stack2MeshLines->setChecked(mesh2->showMeshLines());
    ui.Stack2MeshPoints->setChecked(mesh2->showMeshPoints());
    ui.Stack2CellMap->setChecked(mesh2->showMeshCellMap());

    ui.Stack2SurfBright->setValue(mesh2->brightness() * 10000);
    ui.Stack2SurfOpacity->setValue(mesh2->opacity() * 10000);
  }

  // Update from cutting surface
  CuttingSurface* cut = currentSetup->cuttingSurface();
  ui.DrawCutSurf->setChecked(cut->isVisible());
  switch(cut->mode()) {
  case CuttingSurface::THREE_AXIS:
    ui.ThreeAxis->setChecked(true);
    break;
  case CuttingSurface::PLANE:
    ui.CutSurfPlane->setChecked(true);
    break;
  case CuttingSurface::BEZIER:
    ui.CutSurfBezier->setChecked(true);
    break;
  }

  // Find the bounding box of all images
  BoundingBox3f bbox(Point3f(FLT_MAX, FLT_MAX, FLT_MAX), Point3f(-FLT_MAX, -FLT_MAX, -FLT_MAX));
  forall(Stack* stack, proc->stacks()) {
    Point3f worldSize = multiply(Point3f(stack->size()), stack->step());
    bbox |= worldSize;
  }
  forall(Mesh* mesh, proc->meshes()) {
    if(!mesh->empty()) {
      const BoundingBox3f& b = mesh->boundingBox();
      bbox |= b;
    }
  }
  Point3f rbox;
  if(bbox[0].x() > bbox[1].x())
    rbox = Point3f(0.f);
  else
    rbox = max(fabs(bbox[0]), fabs(bbox[1]));
  ui.Viewer->setSceneBoundingBox(rbox);
  cutSurf.setSceneBoundingBox(rbox);
  ui.CutSurfGrid->setChecked(cut->drawGrid());
  SetCutSurfControl();

  // Update global brightness and contrast
  int intBright = int((proc->globalBrightness() + 1.0) * 5000.0);
  int intContrast = int((proc->globalContrast() * 5000.0));
  int intShininess = int((proc->globalShininess() / 128.0 * 5000.0));
  int intSpecular = int((proc->globalSpecular() * 5000.0));

  ui.GlobalBrightness->setValue(intBright);
  ui.GlobalContrast->setValue(intContrast);
  ui.GlobalShininess->setValue(intShininess);
  ui.GlobalSpecular->setValue(intSpecular);

  ResetClipControl(ui.Viewer->getSceneRadius());

  setWindowTitle(currentSetup->actingFile());

  currentSetup->resetModified();
  updateCurrentStack();
  ui.Viewer->setLabel(proc->selectedLabel());
}

void MorphoGraphX::setProjectFile(const QString &f)
{
  QString filename = f;
  if(!filename.isEmpty())
    filename = absoluteFilePath(filename);
  currentSetup->setFile(filename);
}

const QString& MorphoGraphX::projectFile() const { return currentSetup->file(); }

void MorphoGraphX::dragEnterEvent(QDragEnterEvent* event)
{
  static QStringList recognisedSuffix;
  if(recognisedSuffix.empty()) {
    recognisedSuffix << "mgxs" << "mgxv" << "mgxm" << "fct" << "inr" << "jpg" << "jpeg" << "png" 
      << "tif" << "tiff" << "ply" << "mesh" << "vtu" << "lif" << "lsm" << "py" << "h5" << "hdf" << "hdf5";
  }

  if(event->keyboardModifiers() == Qt::AltModifier)
    event->setDropAction(Qt::MoveAction);
  else
    event->setDropAction(Qt::CopyAction);

  const QMimeData* mime = event->mimeData();
  if(mime->hasUrls()) {
    QList<QUrl> urls = mime->urls();
    if(urls.size() == 1) {
      QUrl url = urls[0];
      QFileInfo fi(url.toLocalFile());
      QString ext = fi.suffix().toLower();
      if(fi.exists() and fi.isReadable() and recognisedSuffix.contains(ext)) {
        event->accept();
        return;
      }
    }
  }
  event->ignore();
}

void MorphoGraphX::AutoRunSlot()
{
  disconnect(this, SIGNAL(processFinished()), this, SLOT(AutoRunSlot()));
  ui.actionRun->trigger();
}

void MorphoGraphX::AutoOpen(const QString& filename, int stack, bool main)
{
  disconnect(this, SIGNAL(processFinished()), this, SLOT(AutoOpen()));
  QString pth = (filename.isEmpty() ? _loadFilename : filename);
  QFileInfo fi(pth);
  QString ext = fi.suffix().toLower();
  if(ext == "mgxv") {
    FileOpen(pth);
  } else {
    QStringList parms;
    if(ext == "vtu") {
      parms << pth << "VTK Mesh" << "Yes" << "no" << "no" << QString::number(stack);
      LaunchProcess("Mesh/System/Import", parms, Process::PROCESS_RUN, false);
    } else if(ext == "mgxs" or ext == "inr" or ext == "tif" or ext == "tiff") {
      parms << pth << (main ? "Main" : "Work") << QString::number(stack) << "";
      LaunchProcess("Stack/System/Open", parms, Process::PROCESS_RUN, false);
    } else if(ext == "lif" or ext == "lsm" or ext == "jpg" or ext == "jpeg" or ext == "png") {
      parms << pth << (main ? "Main" : "Work") << QString::number(stack);
      LaunchProcess("Stack/ITK/System/ITK Image Reader", parms, Process::PROCESS_RUN, true, true);
    } else if(ext == "mgxm") {
      parms << pth << "no" << "no" << QString::number(stack);
      LaunchProcess("Mesh/System/Load", parms, Process::PROCESS_RUN, false);
    } else if(ext == "mesh") {
      parms << pth << "MeshEdit" << "Yes" << "no" << "no" << QString::number(stack);
      LaunchProcess("Mesh/System/Import", parms, Process::PROCESS_RUN, false);
    } else if(ext == "ply") {
      parms << pth << "PLY" << "Yes" << "no" << "no" << QString::number(stack);
      LaunchProcess("Mesh/System/Import", parms, Process::PROCESS_RUN, false);
    } else if(ext == "fct") {
      ui.Viewer->loadFile(pth, (stack == 1), not main);
    } else if(ext == "py") {
      parms << pth;
      LaunchProcess("Misc/Python/Python Script", parms, Process::PROCESS_RUN, false);
    } else if(ext == "h5" or ext == "hdf5" or ext == "hdf") {
      parms << pth << (main ? "Main" : "Work") << QString::number(stack) << "";
      LaunchProcess("Stack/System/Open HDF5", parms, Process::PROCESS_RUN, true);
    }
  }
}

void MorphoGraphX::dropEvent(QDropEvent* event)
{
  QString pth = event->mimeData()->urls()[0].toLocalFile();

  int stack = 0;
  bool main = true;
  Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers();
  if(mods & Qt::AltModifier)
    stack++;
  if(mods & Qt::ShiftModifier)
    main = false;

  AutoOpen(pth, stack, main);
}

void MorphoGraphX::closeEvent(QCloseEvent* e)
{
  if(needSaving) {
    QMessageBox::StandardButton a = QMessageBox::question(
        this, "Closing with unsaved information",
        "You haven't saved the current state of MorphoGraphX. Do you want to quit without saving?",
        QMessageBox::No | QMessageBox::Yes);
    if(a == QMessageBox::No) {
      e->ignore();
      return;
    }
  }
  ui.Viewer->quitting = true;
  saveSettings();

  QApplication::quit();

//  // Unload the processes by hand
//
//  ui.ProcessStackCommand->clear();
//  ui.ProcessMeshCommand->clear();
//  ui.ProcessMiscCommand->clear();
//  ui.ProcessDynamXCommand->clear();
//
//  processes.clear();
//
//  forall(Library* l, loadedLibs) {
//    if(l->isLoaded()) {
//      if(DEBUG)
//        Information::out << "Unloading library '" << l->fileName() << "'" << endl;
//      l->unload();
//      if(l->isLoaded())
//        SETSTATUS("Failed to unload library '" << l->fileName() << "': " << l->errorString());
//    }
//    delete l;
//  }
//  loadedLibs.clear();
//  unregisterSystemProcesses();
//
//  delete currentSetup;
//
//  QMainWindow::closeEvent(e);
}

void MorphoGraphX::saveSettings()
{
  QSettings settings;
  settings.beginGroup("MainWindow");
  settings.setValue("Geometry", saveGeometry());
  settings.setValue("WindowState", saveState(1));
  settings.endGroup();
}

void MorphoGraphX::stack1UnloadedSlot() {
  ui.Stack1Size->setText("Not loaded");
}

void MorphoGraphX::stack2UnloadedSlot() {
  ui.Stack2Size->setText("Not loaded");
}

void MorphoGraphX::changeStack1SizeSlot(const Point3u& size, const Point3f& step, const Point3f& )
{
  QString msg;
  if(size_t(size.x()) * size.y() * size.z() != 0) {
    msg = QString("%1x%2x%3    %4x%5x%6").arg(size.x()).arg(size.y()).arg(size.z()).arg(step.x()).arg(step.y()).arg(
        step.z());
    msg += UM;
    ui.Stack1Size->setText(msg);
  } else
    stack1UnloadedSlot();
}

void MorphoGraphX::changeStack2SizeSlot(const Point3u& size, const Point3f& step, const Point3f& )
{
  QString msg;
  if(size_t(size.x()) * size.y() * size.z() != 0) {
    msg = QString("%1x%2x%3    %4x%5x%6").arg(size.x()).arg(size.y()).arg(size.z()).arg(step.x()).arg(step.y()).arg(
        step.z());
    msg += UM;
    ui.Stack2Size->setText(msg);
  } else
    stack2UnloadedSlot();
}

void MorphoGraphX::modified(bool on)
{
  if(on != needSaving)
    needSaving = on;
}

QTreeWidgetItem* MorphoGraphX::getFolder(QString name, QHash<QString, QTreeWidgetItem*>& folders, QTreeWidget* tree)
{
  if(folders.contains(name))
    return folders[name];
  int idx = name.lastIndexOf('/');
  QTreeWidgetItem* item = 0;
  if(idx == -1) {
    item = new QTreeWidgetItem(QStringList() << name);
    item->setExpanded(false);
    item->setFlags(Qt::ItemIsEnabled);
    tree->addTopLevelItem(item);
  } else {
    QString newname = name.left(idx);
    QTreeWidgetItem* parent = getFolder(newname, folders, tree);
    QString fn = name.mid(idx + 1);
    item = new QTreeWidgetItem(QStringList() << fn);
    item->setExpanded(false);
    item->setFlags(Qt::ItemIsEnabled);
    parent->addChild(item);
  }
  folders[name] = item;
  return item;
}

// Load the processes into the tree view
void MorphoGraphX::createItem(const ProcessDefinition& def)
{
  // Extract the tab folder and name from the process name
  QString tab, folder, name;
  if(!getProcessText(def.name, tab, folder, name))
    return;

  QTreeWidget *tree;
  QHash<QString, QTreeWidgetItem*> *folders;
  if(tab == "Stack") {
    tree = ui.ProcessStackCommand;
    folders = &stackFolders;
  } else if(tab == "Mesh") {
    tree = ui.ProcessMeshCommand;
    folders = &meshFolders;
  } else if(tab == "Misc") {
    tree = ui.ProcessMiscCommand;
    folders = &miscFolders;
  } else {
    Information::out 
        << QString("Create item cannot find correct tab (%1)for process").arg(tab) << endl;
    return;
  }
    
  QStringList desc;
  // The full name goes in column 2
  desc << name << def.name;

  QTreeWidgetItem* item = new QTreeWidgetItem(desc);
  item->setToolTip(0, def.description);
  item->setIcon(0, def.icon);
  if(folder.isEmpty())
    tree->addTopLevelItem(item);
  else {
    // getFolder, recursively creates the tree
    QTreeWidgetItem* folderWidget = getFolder(folder, *folders, tree);
    folderWidget->addChild(item);
  }
}

void MorphoGraphX::updateDefinitions(QMap<QString, ProcessDefinition>& procDefs,
  ProcessFactoryPtr factory, const QMap<QString, ProcessDefinition>& oldProcDefs)
{
  Process* proc = (*factory)(*currentSetup);
  ProcessDefinition def;
  def.name = proc->name();
  def.name.replace('_', " ");
  def.description = proc->description();
  def.parmNames = proc->parmNames();
  def.parmDescs = proc->parmDescs();
  def.icon = proc->icon();
  ProcessDefinition oldDef = oldProcDefs[def.name];
  if(oldProcDefs.contains(def.name) and oldDef.parmNames.size() == def.parmNames.size())
    def.parms = oldDef.parms;
  else
    def.parms = proc->parmDefaults();
  def.parmChoice = proc->parmChoice();
  def.factory = factory;
  int defined = def.parmNames.size();
  int provided = proc->parmDefaults().size();
  int available = def.parms.size();
  if(defined != provided or defined != available)
    Information::err << "Error for process " << def.name
                     << ": parameters doesn't match the number of defaults. This process won't be used." << endl;
  else if(defined > available)
    Information::err << "Error for process " << def.name
                     << ": the number of defined parameters is greater than the number of parameters available."
       "This process won't be used." << endl;
  else {
    if(!def.factory)
      Information::err << "Error, creating a process with an empty factory" << endl;
    procDefs[def.name] = def;
  }
}

// Perform a deep copy of the strings, and don't copy the pointers
void duplicateDefinitions(const QMap<QString, ProcessDefinition>& procDefs,
                                QMap<QString, ProcessDefinition>& savedProcDefs)
{
  forall(const ProcessDefinition& def, procDefs) {
    ProcessDefinition newDef;
    newDef.name = def.name;
    newDef.parmNames = def.parmNames;
    newDef.parms = def.parms;
    savedProcDefs[newDef.name] = newDef;
  }
}

void MorphoGraphX::SaveProcessesParameters()
{
  duplicateDefinitions(processes, savedProc);
}

void MorphoGraphX::RecallProcessesParameters()
{
  int cnt = 0;
  for(processesMap_t::iterator it = processes.begin();
                                     it != processes.end(); ++it, ++cnt) {
    if(it->factory)
      updateDefinitions(processes, it->factory, savedProc);
    else if(DEBUG)
      Information::out << "Warning, process " << cnt << " (" << it->name << ") with empty factory" << endl;
  }

  ProcessDefinition* def = getProcessDefinition(currentProcessName);
  if(def)
    parmsModel->setParms(*def);
}

void MorphoGraphX::ReloadProcesses()
{
#ifdef WATCH_PROCESS_FOLDERS
  if(currentProcess) {
    processReloadTimer->setSingleShot(true);
    processReloadTimer->start();
    return;
  }
#endif
  if(!currentProcessName.isEmpty()) {
    currentProcessName.clear();
    parmsModel->clear();
  }

  ui.ProcessStackCommand->clear();
  ui.ProcessMeshCommand->clear();
  ui.ProcessMiscCommand->clear();

  // Save the current state of things, duplicating everything to never
  // reference a constant of the dll
  SaveProcessesParameters();

  processes.clear();

  unregisterSystemProcesses();

  uint totalProcesses = processFactories().size();
  Information::out << "Base processes" << endl;
  Information::out << " processes: " << totalProcesses << endl << endl;

  Information::out << "Loading system processes" << endl;
  registerSystemProcesses();
  uint procCount = processFactories().size() - totalProcesses;
  Information::out << " added " << procCount << " processes, total: " << processFactories().size() << endl;
  totalProcesses = processFactories().size();

  // SETSTATUS("Unloading libraries");
  // First, unload loaded libraries
  forall(Library* l, loadedLibs) {
    if(l->isLoaded())
      l->unload();
    delete l;
  }
  loadedLibs.clear();

  // Then, find and load libraries
  QList<QDir> dirs = processDirs();
  forall(const QDir& dir, dirs) {
    Information::out << "\nLoading libraries from '" << dir.absolutePath() << "'" << endl;
    QStringList files = dir.entryList(QDir::Files);
    files.sort();
#ifdef WATCH_PROCESS_FOLDERS
    QStringList watched = processWatcher->files();
#endif
    forall(const QString& f, files) {
      QString lib_path = dir.absoluteFilePath(f);
      if(Library::isLibrary(lib_path)) {
        Library* lib = new Library(lib_path);
        lib->load();
        if(lib->isLoaded()) {
#ifdef WATCH_PROCESS_FOLDERS
          if(!watched.contains(lib_path))
            processWatcher->addPath(lib_path);
#endif
          loadedLibs << lib;
          Information::out << "Loaded library " << f << endl;
          procCount = processFactories().size() - totalProcesses;
          Information::out << " added " << procCount << " processes, total: " << processFactories().size() << endl;
          totalProcesses = processFactories().size();
        } else {
          SETSTATUS("Failed to load library file " << f << ": \n" << lib->errorString());
          delete lib;
        }
      }
    }
  }

  if(addedLibFiles.size() > 0) Information::out << endl;
  forall(const QFileInfo& qf, addedLibFiles) {
    QString lib_path = qf.absoluteFilePath();
    if(Library::isLibrary(lib_path)) {
      Library* lib = new Library(lib_path);
      lib->load();
      if(lib->isLoaded()) {
        loadedLibs << lib;
        Information::out << "Loaded library " << qf.fileName() << endl;
        procCount = processFactories().size() - totalProcesses;
        Information::out << " added " << procCount << " processes, total: " << processFactories().size() << endl;
        totalProcesses = processFactories().size();
      } else {
        SETSTATUS("Failed to load library file " << qf.fileName() << ": \n" << lib->errorString());
        delete lib;
      }
    }
  }

  /*
     SETSTATUS("Now watching:");
     watched = processWatcher->files();
     forall(const QString& s, watched)
     SETSTATUS("  file: " << s);
     watched = processWatcher->directories();
     forall(const QString& s, watched)
     SETSTATUS("  path: " << s);
   */

  // then create processes
  forall(ProcessFactoryPtr factory, processFactories())
    updateDefinitions(processes, factory, savedProc);

  // list of folders

  //SETSTATUS("Generated " << processes.size() << " processes from "
  //                       << processFactories().size() << " registered");
  QString msg = QString("Created %1 processes").arg(processes.size());
  if(processes.size() < processFactories().size())
    msg += QString(", Warning, %1 registered").arg(processFactories().size());
  setStatus(msg);

  stackFolders.clear();
  meshFolders.clear();
  ccFolders.clear();
  miscFolders.clear();
  dynamXFolders.clear();
  forall(const ProcessDefinition& def, processes)
    createItem(def);

  ui.ProcessStackCommand->sortItems(0, Qt::AscendingOrder);
  ui.ProcessStackCommand->resizeColumnToContents(0);
  QTreeWidgetItem* item = ui.ProcessStackCommand->topLevelItem(0);
  if(item)
    ui.ProcessStackCommand->setCurrentItem(item);
  if(ui.ProcessStackCommand->topLevelItemCount() > 0 and ui.ProcessStackCommand->topLevelItem(0)->childCount() == 0)
    ProcessCommandSlot(ui.ProcessStackCommand->topLevelItem(0), 0);

  ui.ProcessMeshCommand->sortItems(0, Qt::AscendingOrder);
  ui.ProcessMeshCommand->resizeColumnToContents(0);
  item = ui.ProcessMeshCommand->topLevelItem(0);
  if(item)
    ui.ProcessMeshCommand->setCurrentItem(item);
  if(ui.ProcessMeshCommand->topLevelItemCount() > 0 and ui.ProcessMeshCommand->topLevelItem(0)->childCount() == 0)
    ProcessCommandSlot(ui.ProcessMeshCommand->topLevelItem(0), 0);

  ui.ProcessMiscCommand->sortItems(0, Qt::AscendingOrder);
  ui.ProcessMiscCommand->resizeColumnToContents(0);
  item = ui.ProcessMiscCommand->topLevelItem(0);
  if(item)
    ui.ProcessMiscCommand->setCurrentItem(item);
  if(ui.ProcessMiscCommand->topLevelItemCount() > 0 and ui.ProcessMiscCommand->topLevelItem(0)->childCount() == 0)
    ProcessCommandSlot(ui.ProcessMiscCommand->topLevelItem(0), 0);

  ReloadTasks();
}

void MorphoGraphX::storeParameters()
{
  if(currentProcessName.isEmpty())
    return;
  ProcessDefinition* def = 0;
  if(currentProcessInTasks) {
    QTreeWidgetItem* current = ui.ProcessTasksCommand->currentItem();
    if(current and current->flags() & Qt::ItemIsEnabled) {
      QString task = current->data(0, TaskNameRole).toString();
      int proc_num = current->data(0, ProcessPositionRole).toInt();
      if(tasks.contains(task)) {
        QList<ProcessDefinition>& defs = tasks[task];
        if(proc_num < defs.size())
          def = &defs[proc_num];
      }
    }
  } else {
    if(!currentProcessName.isEmpty()) 
      def = &processes[currentProcessName];
  }
  if(def)
    def->parms = parmsModel->parms();
}

int MorphoGraphX::activeStack() const
{
  int active = ui.StackTabs->currentIndex();
  if((active == 0 and (ui.Stack1MainShow->isChecked() or ui.Stack1WorkShow->isChecked()))
     or (active == 1 and (ui.Stack2MainShow->isChecked() or ui.Stack2WorkShow->isChecked())))
    return active;
  return -1;
}

int MorphoGraphX::activeMesh() const
{
  int active = ui.StackTabs->currentIndex();
  if((active == 0 and (ui.Stack1MeshShow->isChecked() or ui.Stack1SurfShow->isChecked()))
     or (active == 1 and (ui.Stack2MeshShow->isChecked() or ui.Stack2SurfShow->isChecked())))
    return active;
  return -1;
}

void MorphoGraphX::resetDefaultParameters()
{
  if(!currentProcessName.isEmpty()) {
    QStringList parms;
    QList<float> values;
    if(getDefaultParms(currentProcessName, parms)) {
      parmsModel->setParms(parms);
    }
  }
}

void MorphoGraphX::ReloadTasks()
{
  ui.ProcessTasksCommand->clear();
  QStringList ts = tasks.keys();
  ts.sort();
  forall(const QString& t, ts) {
    QList<ProcessDefinition>& defs = tasks[t];
    QTreeWidgetItem* task_item = new QTreeWidgetItem(QStringList() << t);
    int proc_num = 0;
    forall(ProcessDefinition& def, defs) {
      QTreeWidgetItem* item = new QTreeWidgetItem(QStringList() << def.name);
      item->setToolTip(0, def.description);
      item->setIcon(0, def.icon);
      item->setData(0, TaskNameRole, t);
      item->setData(0, ProcessPositionRole, proc_num);
      if(validProcessName(def.name)) {
        if(!checkProcessParms(def.name, def.parms)) {
          getLastParms(def.name, def.parms);
        }
      } else
        item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
      task_item->addChild(item);
      ++proc_num;
    }
    ui.ProcessTasksCommand->addTopLevelItem(task_item);
    ui.ProcessTasksCommand->expandItem(task_item);
  }
}

void MorphoGraphX::editTasks()
{
  TaskEditDlg* dlg = new TaskEditDlg(tasks, savedProc, this);
  if(dlg->exec() == QDialog::Accepted) {
    // Deep copy to duplicate all strings
    const TaskEditDlg::tasks_t& ts = dlg->tasks();
    tasks.clear();
    updateCurrentTasks(ts);
    ReloadTasks();
  }
}

void MorphoGraphX::GlobalContrastSlot()
{
  ui.Viewer->GlobalContrast = float(ui.GlobalContrast->value()) / 5000.0f;
  currentSetup->setGlobalContrast(ui.Viewer->GlobalContrast);
  ui.Viewer->updateAll();
}

void MorphoGraphX::GlobalBrightnessSlot()
{
  ui.Viewer->GlobalBrightness = float(ui.GlobalBrightness->value() - 5000) / 5000.0;
  currentSetup->setGlobalBrightness(ui.Viewer->GlobalBrightness);
  ui.Viewer->updateAll();
}

void MorphoGraphX::GlobalShininessSlot()
{
  ui.Viewer->GlobalShininess = float(ui.GlobalShininess->value() / 5000.0 * 128.0);
  currentSetup->setGlobalShininess(ui.Viewer->GlobalShininess);
  ui.Viewer->updateAll();
}

void MorphoGraphX::GlobalSpecularSlot()
{
  ui.Viewer->GlobalSpecular = float(ui.GlobalSpecular->value() / 5000.0);
  currentSetup->setGlobalSpecular(ui.Viewer->GlobalSpecular);
  ui.Viewer->updateAll();
}

void MorphoGraphX::DebugDialogSlot()
{
  static QPointer<DebugDlg> dlg = 0;
  if(dlg.isNull())
    dlg = new DebugDlg(ui.Viewer, this);
  dlg->show();
}

void MorphoGraphX::SetCutSurfControl()
{
  float sceneRadius = cutSurf.getSceneRadius();
  if(sceneRadius == 0.0f)
    sceneRadius = 1.0f;
  ui.CutSurfSizeX->setSliderPosition(int(log(cutSurf.cut->size().x() * 2.0 / sceneRadius) * 2000));
  ui.CutSurfSizeY->setSliderPosition(int(log(cutSurf.cut->size().y() * 2.0 / sceneRadius) * 2000));
  ui.CutSurfSizeZ->setSliderPosition(int(log(cutSurf.cut->size().z() * 2.0 / sceneRadius) * 2000));
}

void MorphoGraphX::ResetClipControl(float sceneRadius)
{
  if(sceneRadius == 0.0f)
    sceneRadius = 1.0f;
  ui.Clip1Width->setSliderPosition(int(log(currentSetup->clip1()->width() * 2.0 / sceneRadius) * 2000));
  ui.Clip2Width->setSliderPosition(int(log(currentSetup->clip2()->width() * 2.0 / sceneRadius) * 2000));
  ui.Clip3Width->setSliderPosition(int(log(currentSetup->clip3()->width() * 2.0 / sceneRadius) * 2000));
}

void MorphoGraphX::ResetCutSurf()
{
  cutSurf.Reset(ui.Viewer->getSceneRadius());
  SetCutSurfControl();
  ResetClipControl(ui.Viewer->getSceneRadius());
}

const Process* MorphoGraphX::globalProcess() const {
  return currentSetup;
}

Process* MorphoGraphX::globalProcess() {
  return currentSetup;
}

void MorphoGraphX::changeSelectLabel(int lab) {
  currentSetup->setSelectedLabel(lab);
}
