diff --git a/Sources/MainWindow.cpp b/Sources/MainWindow.cpp
index be4825ac0aab4950c6edb4594a489ad6ebd7bcbc..ff6bb640665bb1fe0691beb6ca1afa1d6e515fb0 100644
--- a/Sources/MainWindow.cpp
+++ b/Sources/MainWindow.cpp
@@ -20,7 +20,7 @@
 #include <QMessageBox>
 #include <QDoubleValidator>
 #include <QIntValidator>
-#include <QSignalMapper>
+#include <QRegExpValidator>
 
 MainWindow::MainWindow(QWidget *parent) :
     QMainWindow(parent),
@@ -44,6 +44,7 @@ MainWindow::MainWindow(QWidget *parent) :
     // quit worker thread and clear plot data immediately when any settings are changed
     connect(ui->openProject, &QPushButton::clicked, this, &MainWindow::handleOpenProj);
     connect(ui->openFileButton, &QPushButton::clicked, this, &MainWindow::onOpenFileClicked);
+    ui->runIDInput->setValidator(new QRegExpValidator( QRegExp("[A-Za-z0-9_]+"), ui->runIDInput));
     connect(ui->runIDInput, &QLineEdit::editingFinished, this, &MainWindow::onRunIDInput);
     ui->maxEventNumInput->setValidator(new QIntValidator(1, 10000000,ui->maxEventNumInput));
     connect(ui->maxEventNumInput, &QLineEdit::editingFinished, this, &MainWindow::onMaxEventNumInput);
@@ -137,9 +138,12 @@ MainWindow::MainWindow(QWidget *parent) :
 //    connect(ui->applySettingsButton, &QPushButton::clicked, this, &MainWindow::onConfigApplied);
     setWindowTitle(tr("Back projection"));
 
-    while(!handleOpenProj())
+    qDebug() << "Attempting to open a project";
+    if(!handleOpenProj())
     {
-        qDebug() << "Attempting to open a project";
+        qDebug() << "Cannot open project. Exit application.";
+//        QCoreApplication::exit(1);
+        exit(EXIT_FAILURE);
     }
     ui->statusBar->showMessage("Ready.");
 }
@@ -216,7 +220,8 @@ bool MainWindow::handleOpenProj()
     if (config)
     {
         // save current settings
-        config->writeSetup(config->outputDir.currentPath() + "settings.ini");
+        config->writeSetup("settings.ini");
+        // stop current run
         onConfigChanged();
     }
     // select project dir
@@ -224,37 +229,126 @@ bool MainWindow::handleOpenProj()
     dialog.setWindowTitle("Open a new or existing project");
     dialog.setFileMode(QFileDialog::DirectoryOnly);
     dialog.setOption(QFileDialog::ShowDirsOnly, false);
-    if(dialog.exec())
+    if(!dialog.exec())
+        return false;
+
+    QFileInfo proj_dir(dialog.directory().absolutePath());
+    if (!proj_dir.isDir())
+    {
+        QMessageBox::critical(this, "Failed to open direcory", QString("%1 is not a directory.").arg(proj_dir.absolutePath()));
+        return false;
+    }
+    if (!proj_dir.isWritable())
     {
-        QDir::setCurrent(dialog.directory().path());
+        QMessageBox::critical(this, "Permission denied", QString("%1 is not writable.").arg(proj_dir.absolutePath()));
+        return false;
+    }
     //    qDebug() << QDir::current();
 
-        // if settings.ini does not exist, copy the template settings.ini file to current dir
-        if (!QDir::current().exists("settings.ini"))
-        {
-            QFile::copy(":/Data/Data/settings.ini", "settings.ini");
-        }
-        // read .ini file in current dir
-        if (!config) // start up
-            config = new Setup("settings.ini");
-        else {
-            config->readSetup("settings.ini");
+    // if settings.ini does not exist, copy the template settings.ini file to current dir
+    QString settingsPath = dialog.directory().absolutePath() + "/settings.ini";
+    if (!dialog.directory().exists("settings.ini"))
+    {
+        if(!QFile::copy(":/Data/Data/settings.ini", settingsPath)){
+            QMessageBox::critical(this, "Cannot create file", QString("Cannot create settings.ini in %1").arg(proj_dir.absolutePath()));
+            return false;
         }
-        showConfig();
-        return true;
     }
-    return false;
+    // check read permission
+    if(!QFileInfo(settingsPath).isReadable() && !QFile::setPermissions(settingsPath, QFileDevice::ReadOwner))
+    {
+        QMessageBox::critical(this, "Permission denied", QString("Cannot read settings.ini in %1").arg(proj_dir.absolutePath()));
+        return false;
+    }
+    // check write permission
+    if(!QFileInfo(settingsPath).isWritable() && !QFile::setPermissions(settingsPath, QFileDevice::WriteOwner))
+    {
+        QMessageBox::critical(this, "Permission denied", QString("Cannot write to settings.ini in %1").arg(proj_dir.absolutePath()));
+        return false;
+    }
+
+    // set current path to this dir
+    QDir::setCurrent(dialog.directory().absolutePath());
+    // read .ini file in current dir
+    if (!config) // start up
+        config = new Setup("settings.ini");
+    else {
+        config->readSetup("settings.ini");
+    }
+    showConfig();
+    return true;
 }
 
 bool MainWindow::handleSave()
 {
     qDebug() << "Save clicked";
-    return true;
+    if (config){
+        config->writeSetup("settings.ini");
+        ui->statusBar->showMessage("Project settings saved.");
+        return true;
+    }
+    return false;
 }
 
 bool MainWindow::handleSaveAs()
 {
     qDebug() << "Save as clicked";
+    if (!config)
+        return false;
+
+    // save current settings
+    config->writeSetup("settings.ini");
+    // stop current run
+    onConfigChanged();
+    // select new dir
+    QFileDialog dialog;
+    dialog.setWindowTitle("Save as");
+    dialog.setFileMode(QFileDialog::DirectoryOnly);
+    dialog.setOption(QFileDialog::ShowDirsOnly, false);
+    if(!dialog.exec())
+        return false;
+
+    QFileInfo proj_dir(dialog.directory().absolutePath());
+    if (!proj_dir.isDir())
+    {
+        QMessageBox::critical(this, "Failed to open direcory", QString("%1 is not a directory.").arg(proj_dir.absolutePath()));
+        return false;
+    }
+    if (!proj_dir.isWritable())
+    {
+        QMessageBox::critical(this, "Permission denied", QString("%1 is not writable.").arg(proj_dir.absolutePath()));
+        return false;
+    }
+    //    qDebug() << QDir::current();
+
+    // if settings.ini does not exist, copy the template settings.ini file to current dir
+    QString settingsPath = dialog.directory().absolutePath() + "/settings.ini";
+    if (!dialog.directory().exists("settings.ini"))
+    {
+        if(!QFile::copy("settings.ini", settingsPath)){
+            QMessageBox::critical(this, "Cannot create file", QString("Cannot create settings.ini in %1").arg(proj_dir.absolutePath()));
+            return false;
+        }
+    }
+    // check read permission
+    if(!QFileInfo(settingsPath).isReadable() && !QFile::setPermissions(settingsPath, QFileDevice::ReadOwner))
+    {
+        QMessageBox::critical(this, "Permission denied", QString("Cannot read settings.ini in %1").arg(proj_dir.absolutePath()));
+        return false;
+    }
+    // check write permission
+    if(!QFileInfo(settingsPath).isWritable() && !QFile::setPermissions(settingsPath, QFileDevice::WriteOwner))
+    {
+        QMessageBox::critical(this, "Permission denied", QString("Cannot write to settings.ini in %1").arg(proj_dir.absolutePath()));
+        return false;
+    }
+
+    // set current path to this dir
+    QDir::setCurrent(dialog.directory().absolutePath());
+    // read .ini file in current dir
+    config->readSetup("settings.ini");
+    showConfig();
+    ui->statusBar->showMessage(QString("Project saved to %1").arg(QDir::currentPath()));
     return true;
 }
 
@@ -273,6 +367,7 @@ void MainWindow::handleAbout()
 
 void MainWindow::notifyThreadFinished()
 {
+    ui->statusBar->showMessage("Stopped");
     // pop up a message box indicating processing is finished.
 //    if (!aborted)
 //    {
@@ -293,11 +388,11 @@ void MainWindow::handleStart()
     if (!workerThread.isNull())
     {
         emit workerStarted();
+        ui->statusBar->showMessage("Running");
     }
     else{
         onConfigApplied();
     }
-    ui->statusBar->showMessage("Running");
 }
 
 void MainWindow::handlePause()
@@ -364,8 +459,19 @@ void MainWindow::showConfig()
 
 bool MainWindow::checkConfig()
 {
+    // check if file exists
+    if (!QFile(config->filePath).exists()){
+        QMessageBox::critical(this, "File not exist", QString("Cannot open file %1").arg(config->filePath));
+        return false;
+    }
+    // check if runID already exists
     if (config->outputDir.exists()) {
-        // check if runID already exists
+        // check if it's writable
+        if (!QFileInfo(config->outputDir.absolutePath()).isWritable())
+        {
+            QMessageBox::critical(this, "Permission denied", QString("Cannot write to directory %1").arg(config->outputDir.absolutePath()));
+            return false;
+        }
         QMessageBox::StandardButton resBtn = QMessageBox::question(this, "Overwrite",
                                                                    QString("Run-%1 already exists. Are you sure you want to overwrite?\n").arg(config->runID),
                                                                    QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
@@ -375,8 +481,24 @@ bool MainWindow::checkConfig()
         }
     }
     else {
-        // create a new dir
-       config->outputDir.mkpath(".");
+        // create a new dir for current ID
+       if(!config->outputDir.mkpath(".")){
+           QMessageBox::critical(this, "Permission denied", "Cannot create output directory for run "+config->runID);
+           return false;
+       }
+       // check read permission
+       QString outdirpath = config->outputDir.absolutePath();
+       if(!QFileInfo(outdirpath).isReadable() && !QFile::setPermissions(outdirpath, QFileDevice::ReadOwner))
+       {
+           QMessageBox::critical(this, "Permission denied", QString("Cannot read settings.ini in %1").arg(outdirpath));
+           return false;
+       }
+       // check write permission
+       if(!QFileInfo(outdirpath).isWritable() && !QFile::setPermissions(outdirpath, QFileDevice::WriteOwner))
+       {
+           QMessageBox::critical(this, "Permission denied", QString("Cannot write to settings.ini in %1").arg(outdirpath));
+           return false;
+       }
     }
 
     if (config->phiMax <= config->phiMin)
@@ -405,7 +527,8 @@ bool MainWindow::checkConfig()
     for (int i=0; i<config->channelSettings.size(); i++) {
         for (int j=i+1; j<config->channelSettings.size(); j++) {
             if (config->channelSettings[j].chNum == config->channelSettings[i].chNum){
-                QMessageBox::warning(this, "Duplicated channel number.", QString("Channel-%1 and channel-%2 have same channel number.").arg(QString::number(i+1), QString::number(j+1)));
+                QMessageBox::warning(this, "Duplicated channel number.",
+                                     QString("Channel-%1 and channel-%2 have same channel number.").arg(QString::number(i+1), QString::number(j+1)));
                 return false;
             }
         }
diff --git a/Sources/setup.cpp b/Sources/setup.cpp
index b2c4fab93dbb073982f9dfd09b58914983cf0735..51184b9814d152c613a2ecedceb6d36f165dbd50 100644
--- a/Sources/setup.cpp
+++ b/Sources/setup.cpp
@@ -14,7 +14,7 @@ void Setup::readSetup(const QString path)
 //    filePath = ":/Data/Data/pulses.txt";
 //    filePath = "/media/ming/DATA/projects/Postprocessing/simulation_2/pulses_with_pos_sort_by_time.txt";
     // filePath = "/media/ming/DATA/projects/Postprocessing/simulation_3/pulses_with_pos_sort_by_time.txt";
-    filePath = "/home/mingf2/Downloads/pulses_with_pos_sort_by_time.txt";
+//    filePath = "/home/mingf2/Downloads/pulses_with_pos_sort_by_time.txt";
     runID = settings.value("RunID").toString();
     outputDir = QDir(runID);
     maxN = settings.value("MaxEventNum").toInt();
diff --git a/imagerQt.pro b/imagerQt.pro
index f334b74a6290821555bd4f4ebc48118d65438c89..c85d236e134b82459a1d536d2c8e3c72eea0dad3 100644
--- a/imagerQt.pro
+++ b/imagerQt.pro
@@ -57,14 +57,14 @@ QMAKE_CXXFLAGS += -fopenmp
 #QMAKE_LFLAGS += -fsanitize=address
 LIBS += -fopenmp
 
-# ROOTSYS=/home/ming/root
+ ROOTSYS=/home/ming/root
 
 INCLUDEPATH += \
-    $$(ROOTSYS)/include \
+    $$ROOTSYS/include \
     $$PWD/Headers
 
 LIBS += \
-    -L$$(ROOTSYS)/lib -lCore -lImt -lRIO -lNet -lHist -lGraf \
+    -L$$ROOTSYS/lib -lCore -lImt -lRIO -lNet -lHist -lGraf \
     -lGraf3d -lGpad -lTree -lTreePlayer -lRint -lPostscript -lMatrix \
     -lPhysics -lMathCore -lThread -lMultiProc -pthread -lm -ldl -rdynamic