/*
 * Dialogs for QFitsView which are extensions to UI dialogs
 * or completely standalone
 */

#include <QSpinBox>
#include <QLineEdit>
#include <QSlider>
#include <QRadioButton>
#include <QCheckBox>
#include <QButtonGroup>
#include <QUrl>
#include <QDesktopServices>
#include <QMessageBox>
#include <QScrollArea>
#include <QScrollBar>
#include <QResizeEvent>
#include <QImageWriter>
#include <QDirIterator>
#include <QSizePolicy>
#include <QDockWidget>

#include <float.h>
#include <cmath>

#include "dialogs.h"
#include "QFitsMainWindow.h"
#include "QFitsMainView.h"
#include "QFitsSingleBuffer.h"
#include "QFitsGlobal.h"
#include "QFitsHeaderView.h"
#include "fits.h"
#include "qtdpuser.h"
#include "dpuser.procs.h"
#include "fitting.h"
#include "../utils/cmpfit/mpfit.h"
#include "../dpuser/mpfit/mpfitAST.h"

#include <rapidxml.hpp>

extern "C" {
#include "resources/splash_about.xpm"
// #include "resources/mpe.xpm"
#include "resources/fileopen.xpm"
// #include "resources/telescope.xpm"
}

QString getSaveImageFilename(QString *selectedFilter) {
    QString filename;
    if (selectedFilter != NULL) {
        // extract available image formats into a QStringList
        QList<QByteArray>formats1(QImageWriter::supportedImageFormats());
        QStringList formatsStringList;
        for (int i = 0; i < formats1.size(); ++i) {
            formatsStringList << formats1.at(i);
        }

        // put png at front, if present
        if (formatsStringList.contains("png")) {
            formatsStringList.removeAt(formatsStringList.indexOf("png"));
            QStringList aa;
            aa << "png";
            for (int i = 0; i < formatsStringList.size(); i++) {
                aa << formatsStringList.at(i);
            }
            formatsStringList = aa;
        }

        // put QStringList into a QString, and add 'any'-filter at end
        QString formatString;
        for (int i = 0; i < formatsStringList.size(); i++) {
            formatString += "*." + formatsStringList.at(i) + ";;";
        }
        formatString += "*";

        filename = QFileDialog::getSaveFileName(NULL,
                                                "QFitsView - Save as image",
                                                settings.lastSavePath,
                                                formatString,
                                                selectedFilter);
    }
    return filename;
}

void alignLabels(QLabel *label1, QLabel *label2, QLabel *label3) {
    int maxwidth, minheight;

    label1->adjustSize();
    maxwidth = label1->width();
    minheight = (int)(label1->height() * 1.5);
    if (label2 != NULL) {
        label2->adjustSize();
        if (label2->width() > maxwidth) {
            maxwidth = label2->width();
        }
        if ((int)(label2->height() * 1.5) > minheight) {
            minheight = (int)(label2->height() * 1.5);
        }
    }
    if (label3 != NULL) {
        label3->adjustSize();
        if (label3->width() > maxwidth) {
            maxwidth = label3->width();
        }
        if ((int)(label3->height() * 1.5) > minheight) {
            minheight = (int)(label3->height() * 1.5);
        }
    }

    label1->setAlignment(Qt::AlignRight);
    label1->resize(maxwidth, minheight);
    if (label2 != NULL) {
        label2->setAlignment(Qt::AlignRight);
        label2->setGeometry(label1->x(),
                            label1->y() + minheight + 5,
                            maxwidth,
                            minheight);
    }
    if (label3 != NULL) {
        label3->setAlignment(Qt::AlignRight);
        label3->setGeometry(label1->x(),
                            label2->y() + minheight + 5,
                            maxwidth,
                            minheight);
    }
}

About::About(QWidget *parent) : QDialog(parent,
                                        Qt::WindowTitleHint | Qt::WindowSystemMenuHint) {
    setWindowTitle("About QFitsView");
//    setIcon(QPixmap((const char **)telescope_xpm));

    QPixmap pm(splash_about);
    PixmapLabel = new QFitsSimplestButton(pm, this);
//    PixmapLabel->setPixmap((const char **)splashxpm);
    PixmapLabel->setGeometry(20, 20, pm.width(), pm.height());

    QLabel *MpeLabel = new QLabel(this);
    MpeLabel->setPixmap(QIcon(":/logo-MPE-bw.svg").pixmap(100, 100));
    MpeLabel->setGeometry(410,
                          10,
                          100,
                          100);

    TextLabel1 = new QLabel(this);
    TextLabel1->setTextFormat(Qt::RichText);
    TextLabel1->setWordWrap(true);
    QString txt =   "<b><font size=\"+1\">Version " + QString(DP_VERSION) +
                    "</font></b>\n"
                    "<br><br>\n"
                    "This image shows the central parsec of the centre of our galaxy. The \"yellow\" "
                    "emission is called \"mini-spiral\" and shows gas streamers which can be seen in "
                    "the emission line of hydrogen. This image has been taken at the 3.5m telescope "
                    "of the Calar Alto observatory in Spain in the infrared K-Band at 2.2 microns with "
                    "the adaptive optics system ALFA.";
    TextLabel1->setText(txt);
    TextLabel1->setMinimumSize(310, 10);
    TextLabel1->adjustSize();
    TextLabel1->move(PixmapLabel->width() + 40, 100 + 20);

    PixmapLabel->move(20,(TextLabel1->y()+TextLabel1->height()-PixmapLabel->height())/2+10);

    TextLabel2 = new QLabel(this);
    TextLabel2->setTextFormat(Qt::RichText);
    TextLabel2->setWordWrap(true);
    TextLabel2->setText(
        "QFitsView is a FITS file viewer which was written using the QT widget library (v" + QString(qVersion()) +
        ") in order to be portable between various flavours of UNIX, MAC OS-X, and the "
        "Windows operating system. It is developed at the Max-Planck-Institute for "
        "Extraterrestrial Physics by Thomas Ott and Alex Agudo Berbel");
    TextLabel2->setMinimumSize(PixmapLabel->width() + TextLabel1->width(), 10);
    TextLabel2->adjustSize();
    TextLabel2->move(10, TextLabel1->y() + TextLabel1->height() + 10);

    OKButton = new QPushButton(this);
    OKButton->setText("OK");
    OKButton->setGeometry(TextLabel2->x() + TextLabel2->width() / 2 - 75,
                          TextLabel2->y() + TextLabel2->height() + 10,
                          150,
                          30);
    connect( OKButton, SIGNAL( clicked() ),
             this, SLOT( accept() ) );

    adjustSize();

    setFixedSize(size());
}

//DpHelp::DpHelp(QWidget *parent) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint) {
DpHelp::DpHelp(QWidget *parent) : QDialog(parent) {
    setWindowTitle("Help");
//    setIcon(QPixmap((const char **)telescope_xpm));

    leftPanel = new QTextBrowser(this);
    leftPanel->setStyleSheet("background: #C0C0FF; font-weight: bold; text-decoration: none;");
//    leftPanel->setStyleSheet("background: #C0C0FF; text-align: center; font-weight: bold; list-style-type: none; a { text-decoration: none; }");
    HtmlStart = QString("<html><head>") +
            "<style type=\"text/css\" title=\"currentStyle\">"+
            "body { padding: 10px; border-style: solid; border-width: 10px; border-color: #A0A0FF;  border-right-width: 0px;  text-align: justify; background: #FFFFFF; }" +
            "h1 { background: #FFFF99; }" +
//            "body { margin: 0; background: #C0C0FF; }" +
            "#menu { text-align: center; font-weight: bold; ; background: #C0C0FF;}" +
        "#menu a { text-decoration: none; font-size: 125%; display: block; }" +
        "#menu a:hover { color: #FFFFFF; }" +
            "h1 { background: #FFFF99; }" +
            "</style>"+
"</head><body><font size=+1>";

//            QString navigationText = HtmlStart +
    QString navigationText = QString("<html><body>") +
            "<center><font size=+2>" +
"<a href=\"index.html\">Introduction</a><br>"+
"<a href=\"history.html\">History</a><br>"+
"<a href=\"syntax.html\">Syntax</a><br>"+
"<a href=\"operators.html\">Operators</a><br>"+
"<a href=\"ifandloop.html\">Structural commands</a><br>"+
"<a href=\"variables.html\">Data types</a><br>"+
"<a href=\"plotting.html\">Graphics</a><br>"+
"<a href=\"fitsfiles.html\">Fits files</a><br>"+
"<a href=\"bridges.html\">Calling PYTHON/GDL</a><br>"+
"<a href=\"category.html\">Category index</a><br>"+
"<a href=\"functions.html\">Function index</a><br>"+
"<a href=\"procedures.html\">Procedure index</a><br>"+
"<a href=\"pgplot.html\">Pgplot index</a><br>"+
"<a href=\"examples.html\">Examples</a><br>"+
"<hr>"+
"<a href=\"qfitsview.html\">QFitsView documentation</a><br>"+
            "</font></center>" +
"</body></html>";


    leftPanel->setText(navigationText);
    leftPanel->adjustSize();
    leftPanel->move(0, 0);

    searchButton = new QPushButton("Search", this);
    searchButton->setGeometry(width() - 100, 0, 100, 30);
    searchPanel = new QLineEdit(this);
    searchPanel->setGeometry(searchButton->x() - 150, 0, 150, 30);
    backButton = new QPushButton("Back", this);
    backButton->setGeometry(leftPanel->width(), 0, 100, 30);
    connect(searchButton, SIGNAL(clicked()), SLOT(findDocu()));
    connect(searchPanel, SIGNAL(returnPressed()), SLOT(findDocu()));
    connect(backButton, SIGNAL(clicked()), SLOT(backClicked()));

    std::vector<std::string> entry = functionHelp["sin"];
    mainText = new QTextBrowser(this);
    mainText->setStyleSheet("border-style: solid; border-width: 10px; border-right-width: 0px; border-color: #A0A0FF; text-align: justify;");

//    mainText->setTextFormat(Qt::RichText);
//    mainText->setWordWrap(true);
    QString txt = "<html>";
            txt = txt +
"<head>"+
"   <meta name=\"Author\" content=\"Thomas Ott\">"+
"   <title>DPUSER - The Next Generation: Function ymax</title>"+

" <style type=\"text/css\" title=\"currentStyle\">"+
//                    "body { padding: 10px; border-style: solid; border-width: 10px; border-color: #A0A0FF;  border-right-width: 0px;  text-align: justify; background: #FFFFFF; }" +
                    "h1 { background: #FFFF99; }" +
" </style>"+
"<link rel=\"shortcut icon\" href=\"dpuser.ico\" type=\"image/xicon\">"+
"</head>"+

"<body>";
            for (int i = 0; i < entry.size(); i++) txt += entry.at(i).c_str();

            txt = txt +
"</div>"+
"<div id=\"copyright\">"+
"Copyright &copy; Thomas Ott ---- DPUSER - The Next Generation 3.3 (Rev. 1167)"+
"</div>"+
"</body>"+
"</html>";
    mainText->setText(txt);
    leftPanel->setGeometry(0, 0, leftPanel->width(), 500);

    leftPanel->setOpenLinks(false);
    connect(leftPanel, SIGNAL(anchorClicked(QUrl)), this, SLOT(setMainUrl(QUrl)));
    mainText->setOpenLinks(false);
    connect(mainText, SIGNAL(anchorClicked(QUrl)), this, SLOT(setMainUrl(QUrl)));
    mainText->setGeometry(leftPanel->width(), searchPanel->height(), 800, 470);
    adjustSize();
    currentPage = "index.html";
    setMainUrl(QUrl("index.html"));
}

void DpHelp::resizeEvent(QResizeEvent *e) {
    searchButton->move(e->size().width() - searchButton->width(), 0);
    searchPanel->move(searchButton->x() - searchPanel->width(), 0);
    leftPanel->resize(leftPanel->width(), e->size().height());
    mainText->resize(e->size().width() - leftPanel->width(), e->size().height() - searchPanel->height());
}

void DpHelp::findDocu() {
    QString txt = HtmlStart;
    QString searchTerm = searchPanel->text();
    QRegularExpression searchExp("\\b" + searchTerm.toLower() + "\\b");
    txt += "<h1>Search results for " + searchTerm + "</h1>";
    bool found = false;

    // search functions
    try {
        for(std::map<std::string, std::vector<std::string> >::iterator it = functionHelp.begin(); it != functionHelp.end(); ++it) {
            bool foundfunc = false;
            QString description = it->second.at(0).c_str();

            description = description.toLower();
            if (description.contains(searchExp)) foundfunc = true;
            description = it->first.c_str();
            if (description.contains(searchExp)) foundfunc = true;
            if (foundfunc) txt += "<a href=\"function_" + description + ".html\">function " + description + "</a><br>";
            if (foundfunc) found = true;
        }
        // search functions
        for(std::map<std::string, std::vector<std::string> >::iterator it = procedureHelp.begin(); it != procedureHelp.end(); ++it) {
            bool foundproc = false;
            QString description = it->second.at(0).c_str();
            description = description.toLower();
            if (description.contains(searchExp)) foundproc = true;
            description = it->first.c_str();
            if (description.contains(searchExp)) foundproc = true;
            if (foundproc) txt += "<a href=\"procedure_" + description + ".html\">procedure " + description + "</a><br>";
            if (foundproc) found = true;
        }
    } catch (std::exception &e) {
// just ignore
        txt += e.what();
    }

    // search other html files
    QDirIterator it(":", QDirIterator::Subdirectories);
    while (it.hasNext()) {
        bool foundother = false;
        QString name = it.next();
        if (name.endsWith(".html")) {
            QFile file(name);
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                while (!file.atEnd()) {
                    QString line = QString(file.readLine()).toLower();
                    if (line.contains(searchExp)) foundother = true;
                }
            }
            if (foundother) {
                txt += "<a href=\"" + name.right(name.length() - 2) + "\">" + name.right(name.length() - 2) + "</a><br>";
                found = true;
            }
        }
    }
    if (!found) txt += "no matches";
    mainText->setText(txt);
}

void DpHelp::backClicked() {
     setMainUrl(QUrl(previousPage));
}

//std::string DpHelp2HTML(const char *what) {
QString DpHelp2HTML(std::vector<std::string>what) {
    QString rv;
    QString line;
    bool bigfont = false;
    bool link = false;
//    for (int i = 0; i < functionHelp[what].size(); i++) {
//        line = functionHelp[what].at(i);
    for (int i = 0; i < what.size(); i++) {
        line = what.at(i).c_str();
        if ((line == "Syntax") ||
            (line == "Arguments") ||
            (line == "Arguments:") ||
            (line == "Argument:") ||
            (line == "Examples") ||
            (line == "Returns") ||
            (line == "Switches") ||
            (line == "Notes") ||
            (line == "Note:") ||
            (line == "See also")) {
            bigfont = true;
        } else {
            bigfont = false;
            line.replace(" > ", " &gt; ");
            line.replace(" < ", " &lt; ");
        }
        if (bigfont) {
            rv += "<br><br><b><font size=+2>";
        }
        if ((line.left(9) == "function_") || (line.left(10) == "procedure_")) {
            link = true;
            rv += "<a href=\"";
            rv += line;
            rv += ".html\">";
        } else if (line.left(9) == "category_") {
            link = true;
            rv += "<a href=\"category.html#";
            rv += line.right(line.length() - 9);
            rv += "\">";
        }
        rv += line.replace("_", " ");
        if (bigfont) {
            rv += "</font></b><br>";
        }
        if (line.left(6) == "<code>") {
            rv += "</code>";
        }
        if (link) {
            rv += "</a>";
        }
        rv += "<br>";
    }
    return rv;
}

QString createSummary(const QStringList summary, const QString prefix) {
    QString current = "3";
    QString txt;
    QString p = prefix;
    int index = 0;

    if (prefix.left(2) != "pg") {
        txt += "<font size=+2><b>3D</b></font><br>";
        txt += "<table width=\"100%\" bgcolor=\"#FFFF99\" cellpadding=0 cellspacing=0 border=0>";
    } else {
        p = prefix.right(prefix.length() - 2);
        index = 2;
        current = summary[0][index];
        txt += "<font size=+2><b>" + current.toUpper() + "</b></font><br>";
        txt += "<table width=\"100%\" bgcolor=\"#FFFF99\" cellpadding=0 cellspacing=0 border=0>";
    }
    int count = 0;

    for (int i=0; i < summary.size(); i++) {
      if (summary[i][index] != current) {
        for (int j=count; j < 5; j++) txt += "<td width=\"20%\">&nbsp;</td>";
        current = summary[i][index];
        txt += "</tr></table><br>";
        txt += "<font size=+2><b>" + current.toUpper() + "</b></font><br>";
        txt += "<table width=\"100%\" bgcolor=\"#FFFF99\" cellpadding=0 cellspacing=0 border=0>";
        count = 0;
      }
      count++;
      if (count > 5) {
        txt += "</tr>";
        count = 1;
      }
      if (count == 1) txt += "<tr>";

      // test if function name is obsolete ("function_name (obsolete)"). When true the " (obsolete)"-string will be removed for the file-name
      QString filename = summary[i];
      if (filename.contains("obsolete")) {
        filename = filename.left(filename.length() - 11);
      }

      txt += "<td width=\"20%\"><a href=\"" + p + "_" + filename + ".html\">" + filename + "</a></td>";

      if (i == summary.size()-1) {
        for (int j=count; j < 5; j++) txt += "<td width=\"20%\">&nbsp;</td>";
        txt += "</tr></table>";
      }
    }
    return txt;
}

void DpHelp::setMainUrl(const QUrl &name) {
    QString txt = HtmlStart;
    QString what = name.toString();
//    if (what.startsWith("function_pg")) what = "pgplot" + what.right(what.length() - 8);
//    if (what.startsWith("procedure_pg")) what = "pgplot" + what.right(what.length() - 9);
    QStringList anchor = what.split('#');
    previousPage = currentPage;
    currentPage = what;
    std::vector<std::string> func;

    if (what.startsWith("#")) {
        mainText->scrollToAnchor(anchor.at(1));
        return;
    } else if (what.startsWith("function_")) {
        what = what.mid(9, what.length() - 5 - 9);
        txt += "<h1>function " + what + "</h1>";
//        txt += DpHelp2HTML(what.toStdString().c_str()).c_str();
        if (QFile::exists(":/" + what + ".png")) txt += "<p><img src=\":/" + what + ".png\" align=\"right\">";
        if (functionHelp.count(what.toStdString()) == 1) {
            txt += DpHelp2HTML(functionHelp[what.toStdString()]);
        }
//        std::vector<std::string> entry = functionHelp[what.toStdString()];
//        for (int i = 0; i < entry.size(); i++) txt += entry.at(i).c_str();
    } else if (what.startsWith("procedure_")) {
        what = what.mid(10, what.length() - 5 - 10);
        txt += "<h1>procedure " + what + "</h1>";
        if (procedureHelp.count(what.toStdString()) == 1) {
            txt += DpHelp2HTML(procedureHelp[what.toStdString()]);
        }
    } else if (what == "functions.html") {
        txt += "<h1>Alphabetical function index</h1>";
        QStringList functionList;
        for(std::map<std::string, std::vector<std::string> >::iterator it = functionHelp.begin(); it != functionHelp.end(); ++it) {
          functionList.append(it->first.c_str());
        }
        functionList.sort();
        txt += createSummary(functionList, "function");
    } else if (what == "procedures.html") {
        txt += "<h1>Alphabetical procedure index</h1>";
        QStringList procedureList;
        for(std::map<std::string, std::vector<std::string> >::iterator it = procedureHelp.begin(); it != procedureHelp.end(); ++it) {
          procedureList.append(it->first.c_str());
        }
        procedureList.sort();
        txt += createSummary(procedureList, "procedure");
    } else if (what == "pgplot.html") {
        txt += "<h1>Alphabetical pgplot function index</h1>";
        QStringList pgplotList;
        for(std::map<std::string, std::vector<std::string> >::iterator it = functionHelp.begin(); it != functionHelp.end(); ++it) {
          if (it->first.substr(0, 2) == "pg") {
            pgplotList.append(it->first.c_str());
          }
        }
        pgplotList.sort();
        txt += createSummary(pgplotList, "pgfunction");
        txt += "<h1>Alphabetical pgplot procedure index</h1>";
        pgplotList.clear();
        for(std::map<std::string, std::vector<std::string> >::iterator it = procedureHelp.begin(); it != procedureHelp.end(); ++it) {
          if (it->first.substr(0, 2) == "pg") {
            pgplotList.append(it->first.c_str());
          }
        }
        pgplotList.sort();
        txt += createSummary(pgplotList, "pgprocedure");
    } else {
        QString fname = anchor.at(0);
        fname.prepend(":/");
        QFile file(fname);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            txt += "No help available for " + what;
        }

        while (!file.atEnd()) {
            QByteArray line = file.readLine();
            if (!line.contains("</body>") && !line.contains("</html>")) {
                txt += line.replace("src=\"", "src=\":/");
            }
        }
    }
    txt += "<br><center>" + QString(DPUSERVERSION) + DP_VERSION + "</center>";
    txt += "</body></html>";
    mainText->setText(txt);
    if (anchor.count() > 1) {
        mainText->scrollToAnchor(anchor.at(1));
    }
    raise();
}

//checkForUpdates::checkForUpdates(QWidget *parent) : QDialog(parent) {
//    setWindowTitle("Software update");

//    buffer = new QBuffer();

//    QVBoxLayout *mainLayout = new QVBoxLayout();
//    setLayout(mainLayout);

//    label = new QLabel("Checking for latest version...");
//    label->setGeometry(10, 10, 300, 70);
//    label->setAlignment(Qt::AlignCenter);
//    label->setWordWrap(true);
//    progress = new QProgressBar();
//    progress->setMinimum(0);
//    progress->setMaximum(0);
//    progress->setGeometry(50, 50, 200, 10);
//    button = new QPushButton("Close");
//    button->setGeometry(110, 80, 100, 25);
//    link = new QPushButton("go to the QFitsView home page");
//    link->setGeometry(10, 80, 300, 25);
//    link->hide();

//    mainLayout->addWidget(label);
//    mainLayout->addWidget(progress);
//    mainLayout->addWidget(button);
//    mainLayout->addWidget(link);

//    http = new QHttp(this);
//    connect(http, SIGNAL(requestFinished(int, bool)),
//            this, SLOT(httpRequestFinished(int, bool)));
//    connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
//            this, SLOT(readResponseHeader(const QHttpResponseHeader &)));
//    connect(button, SIGNAL(clicked()),
//            this, SLOT(accept()));
//    connect(link, SIGNAL(clicked()),
//            this, SLOT(linkClicked()));

//    adjustSize();
//    setFixedSize(size());
//    checkUpdate();
//}

//checkForUpdates::~checkForUpdates() {
//    if (buffer) {
//        delete buffer;
//    }
//}

//void checkForUpdates::checkUpdate(void) {
//    QUrl url("http://www.mpe.mpg.de/~ott/QFitsView/revision.txt");
//    http->setHost(url.host(), url.port(80));
//    httpRequestAborted = false;
//    httpGetId = http->get(url.path(), buffer);
//}

//void checkForUpdates::linkClicked() {
//    accept();
//    QDesktopServices::openUrl(QUrl("http://www.mpe.mpg.de/~ott/QFitsView"));
//}

//void checkForUpdates::httpRequestFinished(int requestId, bool error) {
//    if (requestId != httpGetId) {
//        return;
//    }
//    progress->hide();
//    if (httpRequestAborted) {
//        return;
//    }
//    if (error) {
//        label->setText("Error: Could not connect to update server");
//    } else {
//        if (QString(buffer->data()).toInt() > QString(GetRevString()).mid(5).toInt())
//        {
//            label->setText("A new version is available!");
//            button->hide();
//            link->show();
//        } else {
//            label->setText("You are running the latest version of QFitsView");
//        }
//    }
//}

//void checkForUpdates::readResponseHeader(const QHttpResponseHeader &responseHeader) {
//    if (responseHeader.statusCode() != 200) {
//        label->setText(QString("Download failed: ") + responseHeader.reasonPhrase());
//        httpRequestAborted = true;
//        http->abort();
//        return;
//    }
//}

moreColourmaps::moreColourmaps(QWidget *parent) : QDialog(parent) {
    setWindowTitle("QFitsView - More colourmaps...");

    QPushButton testButton("imroolrgb");
    testButton.adjustSize();
    int w = testButton.size().width();
    int h = testButton.size().height();
    int d = h / 10;
    int x = d, y = d;
    int column = 0, row = 0;
    QList<QAction *> actions = ((QFitsMainWindow*)parent)->mapActions->actions();
    for (QList<QAction *>::iterator iter = actions.begin(); iter != actions.end(); iter++) {
        QToolButton *button = new QToolButton(this);
        button->setGeometry(x, y, w, h);
        x += w + d;
        column++;
        if (column > 4) {
            column = 0;
            x = d;
            y += h + d;
            row++;
        }
        button->setDefaultAction(*iter);
        button->setToolButtonStyle(Qt::ToolButtonTextOnly);
    }

    QPushButton *closebutton = new QPushButton("Close", this);
    closebutton->setGeometry(2 * (w+d) + d, y + h + d, w, h);
    connect(closebutton, SIGNAL(clicked()), this, SLOT(hide()));
    setFixedSize(5 * (w+d) + d, (row + 2) * (h+d) + d);
}

CubeDisplayDialog::CubeDisplayDialog(QFitsMainWindow *parent) :
                                   QDialog(dynamic_cast<QWidget*>(parent)) {
    myParent = parent;
    n3 = 9999;
    crpix = 1.0;
    crval = 1.0;
    cdelt = 1.0;
    interactiveUpdates = true;

    setWindowTitle("CubeDisplay");

    QVBoxLayout *mainLayout = new QVBoxLayout();
    setLayout(mainLayout);

    //////////////////////////////////////////////////////
    // CUBE display & CUBE movie
    //
    QHBoxLayout *hBoxLayout = new QHBoxLayout();
    mainLayout->addLayout(hBoxLayout);
    //
    // CUBE display
    //
    buttonDisplaySingle = new QRadioButton("Single");
    buttonDisplaySingle->setChecked(true);
    buttonDisplayAverage = new QRadioButton("Average");
    buttonDisplayMedian = new QRadioButton("Median");
    buttonDisplayLinemap = new QRadioButton("Linemap");

    QButtonGroup *buttonGroupDisplay = new QButtonGroup();
    buttonGroupDisplay->addButton(buttonDisplaySingle, 1);
    buttonGroupDisplay->addButton(buttonDisplayAverage, 2);
    buttonGroupDisplay->addButton(buttonDisplayMedian, 3);
    buttonGroupDisplay->addButton(buttonDisplayLinemap, 4);

    QGroupBox *groupCubeDisplay = new QGroupBox();
    groupCubeDisplay->setTitle("CUBE display");
    QVBoxLayout *vboxCubeDisplay = new QVBoxLayout(groupCubeDisplay);
    vboxCubeDisplay->addWidget(buttonDisplaySingle);
    vboxCubeDisplay->addWidget(buttonDisplayAverage);
    vboxCubeDisplay->addWidget(buttonDisplayMedian);
    vboxCubeDisplay->addWidget(buttonDisplayLinemap);
    hBoxLayout->addWidget(groupCubeDisplay);
    //
    // CUBE movie
    //
    spinMovieSpeed = new QSpinBox();
    spinMovieSpeed->setMaximum(9999);
    spinMovieSpeed->setSingleStep(100);
    spinMovieSpeed->setValue(200);
    checkAutoScale = new QCheckBox("rescale each image");
    checkAutoScale->setChecked(true);

    QGroupBox *groupCubeMovie = new QGroupBox();
    groupCubeMovie->setTitle("CUBE movie");
    QVBoxLayout *vboxCubeMovie = new QVBoxLayout(groupCubeMovie);
    vboxCubeMovie->addWidget(new QLabel("Movie Speed"));
    vboxCubeMovie->addWidget(spinMovieSpeed);
    vboxCubeMovie->addWidget(checkAutoScale);
    hBoxLayout->addWidget(groupCubeMovie);

    ////////////////////////////////////////////////////
    // Channels, Wavelength
    //
    hBoxLayout = new QHBoxLayout();
    mainLayout->addLayout(hBoxLayout);
    //
    // --> Channels
    //
    spinLinemapCenter = new QSpinBox();
    spinLinemapCenter->setMinimum(1);
    spinLinemapCenter->setMaximum(9999);
    spinLinemapCenter->setValue(1);
    spinLinemapWidth = new QSpinBox();
    spinLinemapWidth->setMinimum(1);
    spinLinemapWidth->setMaximum(9999);
    spinLinemapWidth->setValue(1);
    checkLinemapDoCont1 = new QCheckBox();
    spinLinemapCont1 = new QSpinBox();
    spinLinemapCont1->setMinimum(-9999);
    spinLinemapCont1->setMaximum(9999);
    spinLinemapCont1->setValue(-10);
    spinLinemapWidth1 = new QSpinBox();
    spinLinemapWidth1->setMinimum(1);
    spinLinemapWidth1->setMaximum(9999);
    spinLinemapWidth1->setValue(1);
    checkLinemapDoCont2 = new QCheckBox();
    spinLinemapCont2 = new QSpinBox();
    spinLinemapCont2->setMinimum(-9999);
    spinLinemapCont2->setMaximum(9999);
    spinLinemapCont2->setValue(10);
    spinLinemapWidth2 = new QSpinBox();
    spinLinemapWidth2->setMinimum(1);
    spinLinemapWidth2->setMaximum(9999);
    spinLinemapWidth2->setValue(1);

    QGridLayout *gridLayout = new QGridLayout();
    gridLayout->addWidget(new QLabel("Channels"), 0, 1);
    gridLayout->addWidget(new QLabel("Central Frame"), 1, 0);
    gridLayout->addWidget(spinLinemapCenter, 1, 1);
    gridLayout->addWidget(new QLabel("Width"), 1, 2);
    gridLayout->addWidget(spinLinemapWidth, 1, 3);
    gridLayout->addWidget(checkLinemapDoCont1, 2, 0);
    gridLayout->addWidget(spinLinemapCont1, 2, 1);
    gridLayout->addWidget(new QLabel("Width"), 2, 2);
    gridLayout->addWidget(spinLinemapWidth1, 2, 3);
    gridLayout->addWidget(checkLinemapDoCont2, 3, 0);
    gridLayout->addWidget(spinLinemapCont2, 3, 1);
    gridLayout->addWidget(new QLabel("Width"), 3, 2);
    gridLayout->addWidget(spinLinemapWidth2, 3, 3);
    hBoxLayout->addLayout(gridLayout);
    hBoxLayout->addItem(new QSpacerItem(50, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));
    //
    // --> Wavelength
    //
    lineeditLinemapCenterW = new QLineEdit();
    lineeditLinemapWidthW = new QLineEdit();
    lineeditLinemapCont1W = new QLineEdit();
    lineeditLinemapWidth1W = new QLineEdit();
    lineeditLinemapCont2W = new QLineEdit();
    lineeditLinemapWidth2W = new QLineEdit();

    gridLayout = new QGridLayout();
    gridLayout->addWidget(new QLabel("Wavelength"), 0, 1);
    gridLayout->addWidget(lineeditLinemapCenterW, 1, 0);
    gridLayout->addWidget(new QLabel("Width"), 1, 1);
    gridLayout->addWidget(lineeditLinemapWidthW, 1, 2);
    gridLayout->addWidget(lineeditLinemapCont1W, 2, 0);
    gridLayout->addWidget(new QLabel("Width"), 2, 1);
    gridLayout->addWidget(lineeditLinemapWidth1W, 2, 2);
    gridLayout->addWidget(lineeditLinemapCont2W, 3, 0);
    gridLayout->addWidget(new QLabel("Width"), 3, 1);
    gridLayout->addWidget(lineeditLinemapWidth2W, 3, 2);
    hBoxLayout->addLayout(gridLayout);

    ////////////////////////////////////////////////////
    // Spacer
    //
    hBoxLayout = new QHBoxLayout();
    mainLayout->addLayout(hBoxLayout);
    hBoxLayout->addItem(new QSpacerItem(1, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));

    ////////////////////////////////////////////////////
    // Slider
    //
    hBoxLayout = new QHBoxLayout();
    mainLayout->addLayout(hBoxLayout);
    labelWavelengthSliderLabel1 = new QLabel("1");
    labelWavelengthSliderLabel1->setAlignment(Qt::AlignLeft);
    labelLinemapInfo = new QLabel("No wavelength information available");
    labelLinemapInfo->setAlignment(Qt::AlignHCenter);
    labelWavelengthSliderLabel2 = new QLabel("9999");
    labelWavelengthSliderLabel2->setAlignment(Qt::AlignRight);
    hBoxLayout->addWidget(labelWavelengthSliderLabel1);
    hBoxLayout->addWidget(labelLinemapInfo);
    hBoxLayout->addWidget(labelWavelengthSliderLabel2);

    hBoxLayout = new QHBoxLayout();
    sliderWavelength = new QSlider();
    sliderWavelength->setMinimum(1);
    sliderWavelength->setMaximum(9999);
    sliderWavelength->setValue(500);
    sliderWavelength->setTracking(true);
    sliderWavelength->setOrientation(Qt::Horizontal);
    sliderWavelength->setTickInterval(1000);
    hBoxLayout->addWidget(sliderWavelength);
    mainLayout->addLayout(hBoxLayout);

    ////////////////////////////////////////////////////
    // OK button
    //
    hBoxLayout = new QHBoxLayout();
    mainLayout->addLayout(hBoxLayout);

    buttonOk = new QPushButton("Ok");
    hBoxLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));
    hBoxLayout->addWidget(buttonOk);
    hBoxLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));

    ////////////////////////////////////////////////////
    // tab order & connects
    //
    setTabOrder(buttonDisplaySingle, buttonDisplayAverage);
    setTabOrder(buttonDisplayAverage, buttonDisplayMedian);
    setTabOrder(buttonDisplayMedian, buttonDisplayLinemap);
    setTabOrder(buttonDisplayLinemap, spinMovieSpeed);
    setTabOrder(spinMovieSpeed, checkAutoScale);
    setTabOrder(checkAutoScale, spinLinemapCenter);
    setTabOrder(spinLinemapCenter, spinLinemapWidth);
    setTabOrder(spinLinemapWidth, lineeditLinemapCenterW);
    setTabOrder(lineeditLinemapCenterW, lineeditLinemapWidthW);
    setTabOrder(lineeditLinemapWidthW, checkLinemapDoCont1);
    setTabOrder(checkLinemapDoCont1, spinLinemapCont1);
    setTabOrder(spinLinemapCont1, spinLinemapWidth1);
    setTabOrder(spinLinemapWidth1, lineeditLinemapCont1W);
    setTabOrder(lineeditLinemapCont1W, lineeditLinemapWidth1W);
    setTabOrder(lineeditLinemapWidth1W, checkLinemapDoCont2);
    setTabOrder(checkLinemapDoCont2, spinLinemapCont2);
    setTabOrder(spinLinemapCont2, spinLinemapWidth2);
    setTabOrder(spinLinemapWidth2, lineeditLinemapCont2W);
    setTabOrder(lineeditLinemapCont2W, lineeditLinemapWidth2W);
    setTabOrder(lineeditLinemapWidth2W, sliderWavelength);
    setTabOrder(sliderWavelength, buttonOk);

    connect(buttonOk, SIGNAL(clicked()),
            this, SLOT(accept()));
    connect(sliderWavelength, SIGNAL(valueChanged(int)),
            spinLinemapCenter, SLOT(setValue(int)));
    connect(spinLinemapCenter, SIGNAL(valueChanged(int)),
            this, SLOT(setLinemapCenterW(int)));
    connect(lineeditLinemapCenterW, SIGNAL(textChanged(const QString &)),
            this, SLOT(setLinemapCenterC(const QString &)));
    connect(spinLinemapWidth, SIGNAL(valueChanged(int)),
            this, SLOT(setLinemapWidthW(int)));
    connect(lineeditLinemapWidthW, SIGNAL(textChanged(const QString &)),
            this, SLOT(setLinemapWidthC(const QString &)));
    connect(spinLinemapCont1, SIGNAL(valueChanged(int)),
            this, SLOT(setLinemapCont1W(int)));
    connect(lineeditLinemapCont1W, SIGNAL(textChanged(const QString &)),
            this, SLOT(setLinemapCont1C(const QString &)));
    connect(spinLinemapWidth1, SIGNAL(valueChanged(int)),
            this, SLOT(setLinemapWidth1W(int)));
    connect(lineeditLinemapWidth1W, SIGNAL(textChanged(const QString &)),
            this, SLOT(setLinemapWidth1C(const QString &)));
    connect(spinLinemapCont2, SIGNAL(valueChanged(int)),
            this, SLOT(setLinemapCont2W(int)));
    connect(lineeditLinemapCont2W, SIGNAL(textChanged(const QString &)),
            this, SLOT(setLinemapCont2C(const QString &)));
    connect(spinLinemapWidth2, SIGNAL(valueChanged(int)),
            this, SLOT(setLinemapWidth2W(int)));
    connect(lineeditLinemapWidth2W, SIGNAL(textChanged(const QString &)),
            this, SLOT(setLinemapWidth2C(const QString &)));
    connect(checkAutoScale, SIGNAL(toggled(bool)),
            myParent->main_view, SLOT(setAutoScale(bool)));
    connect(spinMovieSpeed, SIGNAL(valueChanged(int)),
            myParent->main_view, SLOT(setMovieSpeed(int)));
    connect(buttonOk, SIGNAL(clicked()),
            myParent, SLOT(updateCubeOptions()));
    connect(spinLinemapCenter, SIGNAL(valueChanged(int)),
            myParent, SLOT(updateLinemapInfo(int)));
    connect(spinLinemapWidth, SIGNAL(valueChanged(int)),
            myParent, SLOT(updateLinemapInfo(int)));

}

void CubeDisplayDialog::show(void) {
    QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer*>(getCurrentBuffer());
    if ((sb != NULL) &&
        (sb->getDpData()->type == typeFits) &&
        (sb->getDpData()->fvalue != NULL))
    {
        interactiveUpdates = false;

        buffersLock.lockForRead();
        Fits *f = sb->getDpData()->fvalue;
        double crpix = f->getCRPIX(3);
        double cdelt = f->getCDELT(3);
        double crval = f->getCRVAL(3);
        long n3 = f->Naxis(3);
        buffersLock.unlock();

        setDisplayMode(sb->getCubeMode());
        spinLinemapCenter->setMaximum(n3);
        spinLinemapCont1->setMaximum(n3);
        spinLinemapCont2->setMaximum(n3);
        spinLinemapCont1->setMinimum(-n3);
        spinLinemapCont2->setMinimum(-n3);
        sliderWavelength->setMaximum(n3);

        lineeditLinemapCenterW->setText(
                QString::number(crval + (sb->getCubeCenter(QFV::Wavelength) - crpix) * cdelt));
        spinLinemapCenter->setValue(
                (int)(sb->getCubeCenter(QFV::Wavelength) + 0.5));
        lineeditLinemapWidthW->setText(
                QString::number(sb->getLineWidth(QFV::Wavelength) * cdelt));
        spinLinemapWidth->setValue(
                (int)(sb->getLineWidth(QFV::Wavelength) + 0.5));
        lineeditLinemapCont1W->setText(
                QString::number((double)(sb->getLineCont1Center()) * cdelt));
        spinLinemapCont1->setValue(sb->getLineCont1Center());
        lineeditLinemapWidth1W->setText(
                QString::number((double)(sb->getLineCont1Width()) * cdelt));
        spinLinemapWidth1->setValue(sb->getLineCont1Width());
        lineeditLinemapCont2W->setText(
                QString::number((double)(sb->getLineCont2Center()) * cdelt));
        spinLinemapCont2->setValue(sb->getLineCont2Center());
        lineeditLinemapWidth2W->setText(
                QString::number((double)(sb->getLineCont2Width()) * cdelt));
        spinLinemapWidth2->setValue(sb->getLineCont2Width());

        checkLinemapDoCont1->setChecked(sb->getLineCont1());
        checkLinemapDoCont2->setChecked(sb->getLineCont2());

        interactiveUpdates = true;

        QDialog::show();
    }
}

QFitsBaseBuffer* CubeDisplayDialog::getCurrentBuffer() {
    return myParent->getCurrentBuffer();
}

void CubeDisplayDialog::setDisplayMode(dpCubeMode newmode) {
    buttonDisplaySingle->setChecked(false);
    buttonDisplayAverage->setChecked(false);
    buttonDisplayMedian->setChecked(false);
    buttonDisplayLinemap->setChecked(false);
    switch (newmode) {
        case DisplayCubeSingle:
            buttonDisplaySingle->setChecked(true);
            break;
        case DisplayCubeAverage:
            buttonDisplayAverage->setChecked(true);
            break;
        case DisplayCubeMedian:
            buttonDisplayMedian->setChecked(true);
            break;
        case DisplayCubeLinemap:
            buttonDisplayLinemap->setChecked(true);
            break;
        default:
            break;
    }
}

void CubeDisplayDialog::setCubeDisplayOptions(double cr, double cd,
                                              double cv, int n) {
    crpix = cr;
    cdelt = cd;
    crval = cv;
    n3 = n;
}

void CubeDisplayDialog::setLinemapCenterW(int newvalue) {
    if (interactiveUpdates && !lineeditLinemapCenterW->hasFocus())
        lineeditLinemapCenterW->setText(
                QString::number(crval + ((double)(newvalue) - crpix) * cdelt));
}

void CubeDisplayDialog::setLinemapCenterC(const QString &newvalue) {
    if (interactiveUpdates && !spinLinemapCenter->hasFocus())
        spinLinemapCenter->setValue(
                (int)(crpix + (newvalue.toDouble() - crval) / cdelt + 0.5));
}

void CubeDisplayDialog::setLinemapWidthW(int newvalue) {
    if (interactiveUpdates && !lineeditLinemapWidthW->hasFocus())
        lineeditLinemapWidthW->setText(QString::number((double)newvalue * cdelt));
}

void CubeDisplayDialog::setLinemapWidthC(const QString &newvalue) {
    if (interactiveUpdates && !spinLinemapWidth->hasFocus())
        spinLinemapWidth->setValue((int)(newvalue.toDouble() / cdelt + 0.5));
}

void CubeDisplayDialog::setLinemapCont1W(int newvalue) {
    if (interactiveUpdates && !lineeditLinemapCont1W->hasFocus())
        lineeditLinemapCont1W->setText(QString::number((double)(newvalue) * cdelt));
}

void CubeDisplayDialog::setLinemapCont1C(const QString &newvalue) {
    if (interactiveUpdates && !spinLinemapCont1->hasFocus())
        spinLinemapCont1->setValue((int)(newvalue.toDouble() / cdelt + 0.5));
}

void CubeDisplayDialog::setLinemapWidth1W(int newvalue) {
    if (interactiveUpdates && !lineeditLinemapWidth1W->hasFocus())
        lineeditLinemapWidth1W->setText(QString::number((double)(newvalue) * cdelt));
}

void CubeDisplayDialog::setLinemapWidth1C(const QString &newvalue) {
    if (interactiveUpdates && !spinLinemapWidth1->hasFocus())
        spinLinemapWidth1->setValue((int)(newvalue.toDouble() / cdelt + 0.5));
}

void CubeDisplayDialog::setLinemapCont2W(int newvalue) {
    if (interactiveUpdates && !lineeditLinemapCont2W->hasFocus())
        lineeditLinemapCont2W->setText(QString::number((double)(newvalue) * cdelt));
}

void CubeDisplayDialog::setLinemapCont2C(const QString &newvalue) {
    if (interactiveUpdates && !spinLinemapCont2->hasFocus())
            spinLinemapCont2->setValue((int)(newvalue.toDouble() / cdelt + 0.5));
}

void CubeDisplayDialog::setLinemapWidth2W(int newvalue) {
    if (interactiveUpdates && !lineeditLinemapWidth2W->hasFocus())
        lineeditLinemapWidth2W->setText(QString::number((double)(newvalue) * cdelt));
}

void CubeDisplayDialog::setLinemapWidth2C(const QString &newvalue) {
    if (interactiveUpdates && !spinLinemapWidth2->hasFocus())
        spinLinemapWidth2->setValue((int)(newvalue.toDouble() / cdelt + 0.5));
}

void CubeDisplayDialog::setLinemapCenterMaxValue(int val) {
    spinLinemapCenter->setMaximum(val);
}

void CubeDisplayDialog::setLinemapCont1MaxValue(int val) {
    spinLinemapCont1->setMaximum(val);
}

void CubeDisplayDialog::setLinemapCont2MaxValue(int val) {
    spinLinemapCont2->setMaximum(val);
}

void CubeDisplayDialog::setLinemapSliderWavelength(int val) {
    sliderWavelength->setMaximum(val);
}

void CubeDisplayDialog::setLinemapCenterValue(int val) {
    spinLinemapCenter->setValue(val);
}

void CubeDisplayDialog::setLinemapWavelengthSliderInformation(QString *label1, QString *info, QString *label2) {
    if (label1 != NULL) {
        labelWavelengthSliderLabel1->setText(*label1);
    }
    labelLinemapInfo->setText(*info);
    if (label2 != NULL) {
        labelWavelengthSliderLabel2->setText(*label2);
    }
}

int CubeDisplayDialog::getLinemapCenterValue() {
    return spinLinemapCenter->value();
}
int CubeDisplayDialog::getLinemapWidthValue() {
    return spinLinemapWidth->value();
}
int CubeDisplayDialog::getLinemapCont1Value() {
    return spinLinemapCont1->value();
}
int CubeDisplayDialog::getLinemapCont2Value() {
    return spinLinemapCont2->value();
}
int CubeDisplayDialog::getLinemapWidth1Value() {
    return spinLinemapWidth1->value();
}
int CubeDisplayDialog::getLinemapWidth2Value() {
    return spinLinemapWidth2->value();
}

bool CubeDisplayDialog::getButtonDisplaySingleChecked() {
    return buttonDisplaySingle->isChecked();
}
bool CubeDisplayDialog::getButtonDisplayAverageChecked() {
    return buttonDisplayAverage->isChecked();
}
bool CubeDisplayDialog::getButtonDisplayMedianChecked() {
    return buttonDisplayMedian->isChecked();
}
bool CubeDisplayDialog::getButtonDisplayLinemapChecked() {
    return buttonDisplayLinemap->isChecked();
}
bool CubeDisplayDialog::getCheckLinemapDoCont1Checked() {
    return checkLinemapDoCont1->isChecked();
}
bool CubeDisplayDialog::getCheckLinemapDoCont2Checked() {
    return checkLinemapDoCont2->isChecked();
}








ImageDisplay::ImageDisplay(QWidget* parent) : QDialog(parent) {
    setWindowTitle("QFitsView - Image Display");

    QLabel *labelBright = new QLabel();
    labelBright->setText("Brightness");
    QLabel *labelContrast= new QLabel();
    labelContrast->setText("Contrast");

    sliderBrightness = new QSlider();
    sliderBrightness->setMaximum(999);
    sliderBrightness->setValue(500);
    sliderBrightness->setOrientation(Qt::Horizontal);

    spinboxBrightness = new QSpinBox();
    spinboxBrightness->setMaximum(999);
    spinboxBrightness->setValue(500);

    sliderContrast = new QSlider();
    sliderContrast->setMaximum(999);
    sliderContrast->setValue(99);
    sliderContrast->setOrientation(Qt::Horizontal);

    spinboxContrast = new QSpinBox();
    spinboxContrast->setMaximum(999);
    spinboxContrast->setValue(99);

    checkboxIgnoreValue = new QCheckBox();
    checkboxIgnoreValue->setChecked(true);
    checkboxIgnoreValue->setText("Ignore value of:");

    lineeditIgnoreValue = new QLineEdit();
    lineeditIgnoreValue->setText("-1e10");


    buttonClose = new QPushButton();
    buttonClose->setText("Close");

    QGridLayout *gridLayout = new QGridLayout();
    gridLayout->addWidget(labelBright, 0, 0);
    gridLayout->addWidget(sliderBrightness, 0, 1);
    gridLayout->addWidget(spinboxBrightness, 0, 2);
    gridLayout->addWidget(labelContrast, 1, 0);
    gridLayout->addWidget(sliderContrast, 1, 1);
    gridLayout->addWidget(spinboxContrast, 1, 2);
    gridLayout->addWidget(checkboxIgnoreValue, 2, 1);
    gridLayout->addWidget(lineeditIgnoreValue, 2, 2);

    QGroupBox *groupBox = new QGroupBox();
    groupBox->setTitle("Image display");
    groupBox->setLayout(gridLayout);

    QHBoxLayout *hLayout = new QHBoxLayout();
    hLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));
    hLayout->addWidget(buttonClose);
    hLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addWidget(groupBox);
    mainLayout->addLayout(hLayout);
    setLayout(mainLayout);

    // signals and slots connections
    connect( sliderBrightness, SIGNAL( valueChanged(int) ),
             spinboxBrightness, SLOT( setValue(int) ) );
    connect( sliderContrast, SIGNAL( valueChanged(int) ),
             spinboxContrast, SLOT( setValue(int) ) );
    connect( spinboxBrightness, SIGNAL( valueChanged(int) ),
             sliderBrightness, SLOT( setValue(int) ) );
    connect( spinboxContrast, SIGNAL( valueChanged(int) ),
             sliderContrast, SLOT( setValue(int) ) );
    connect( checkboxIgnoreValue, SIGNAL( toggled(bool) ),
             lineeditIgnoreValue, SLOT( setEnabled(bool) ) );
    connect( buttonClose, SIGNAL( clicked() ),
             this, SLOT( accept() ) );
}

/*
 *  Destroys the object and frees any allocated resources
 */
ImageDisplay::~ImageDisplay()
{
    // no need to delete child widgets, Qt does it all for us
}

BlinkDialog::BlinkDialog(QWidget *parent): QDialog(parent) {
    setWindowTitle("Blink buffers");
    list = new QListWidget(this);

    QPushButton *selectAllButton = new QPushButton("Select All", this);
    connect(selectAllButton, SIGNAL(clicked()), this, SLOT(selectAll()));
    QPushButton *deselectAllButton = new QPushButton("Deselect All", this);
    connect(deselectAllButton, SIGNAL(clicked()), this, SLOT(deselectAll()));

    QLabel *timeoutLabel = new QLabel("Time step", this);
    timeoutLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

    timeout = new QSpinBox(this);
    timeout->setMinimum(0);
    timeout->setMaximum(10000);
    timeout->setSuffix("ms");
    timeout->setSingleStep(100);
    timeout->setValue(200);
    timeout->setSpecialValueText("fastest");

    cancelButton = new QPushButton("Cancel", this);
    connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));

    okButton = new QPushButton("Ok", this);
    connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));

    QGridLayout *layout = new QGridLayout(this);
    layout->addWidget(list, 0, 0, 10, 4);
    layout->addWidget(selectAllButton, 10, 0);
    layout->addWidget(deselectAllButton, 10, 1);
    layout->addWidget(timeoutLabel, 10, 2);
    layout->addWidget(timeout, 10, 3);
    layout->addWidget(cancelButton, 11, 0, 1, 2);
    layout->addWidget(okButton, 11, 2, 1, 2);

    adjustSize();
}

void BlinkDialog::selectAll() {
    for (int row = 0; row < list->count(); row++) {
        list->item(row)->setCheckState(Qt::Checked);
    }
}

void BlinkDialog::deselectAll() {
    for (int row = 0; row < list->count(); row++) {
        list->item(row)->setCheckState(Qt::Unchecked);
    }
}

PlotOptionsDialog::PlotOptionsDialog(QWidget *parent) : QDialog(parent) {
//    setIcon(QPixmap((const char **)telescope_xpm));
    setWindowTitle("QFitsView - Cuts Plot");

    CutsStyle = new QComboBox(this);
    CutsStyle->addItem("horizontal");
    CutsStyle->addItem("vertical");
    CutsStyle->addItem("radial average");

    CutsWidth = new QSpinBox(this);
    CutsWidth->setSuffix(" Pixels");
    CutsWidth->setRange(1, 300);
    CutsWidth->setValue(30);
    CutsWidth->setSingleStep(5);

    PlotTakeLimits = new QCheckBox("Plot limits from display", this);

    QFormLayout *layout = new QFormLayout(this);
    layout->addRow("Style", CutsStyle);
    layout->addRow("Width", CutsWidth);
    layout->addWidget(PlotTakeLimits);

    adjustSize();
    closeButton = new QPushButton("Close", this);
    closeButton->adjustSize();
    closeButton->move(width() / 2 - closeButton->width() / 2, height());
    connect(closeButton, SIGNAL(clicked()), this, SLOT(accept()));

//    adjustSize();
    setFixedSize(width(), closeButton->y() + closeButton->height() + 10);
}

dpImportDialog::dpImportDialog(QWidget *parent, const QString &fname) : QDialog(parent) {
    setWindowTitle("Import File");

    highlighter = NULL;
    filename = fname;

    importText = new QRadioButton("Import as text", this);
    importText->move(20, 20);
    importText->adjustSize();
    QLabel *importTextExplanation = new QLabel("Each text line in the file will be\nimported as an element of a string list", this);
    importTextExplanation->move(20, importText->y() + importText->height() + 5);
    importTextExplanation->adjustSize();

    importNumber = new QRadioButton("Import as Numbers", this);
    importNumber->move(20, importTextExplanation->y() + importTextExplanation->height() + 10);
    importNumber->adjustSize();
    importNumber->setChecked(true);
    QLabel *importNumberExplanation = new QLabel("The content of the file will be interpreted\naccording to these options:", this);
    importNumberExplanation->move(20, importNumber->y() + importNumber->height() + 5);
    importNumberExplanation->adjustSize();

    QGroupBox *optionsBox = new QGroupBox(this);
    optionsBox->move(20, importNumberExplanation->y() + importNumberExplanation->height() + 10);
    QGridLayout *layout = new QGridLayout(optionsBox);

    QLabel *l1 = new QLabel("Number of columns", optionsBox);
    layout->addWidget(l1, 0, 0);
    columns = new QSpinBox(optionsBox);
    columns->setRange(0, 9999);
    columns->setSpecialValueText("Auto");
    layout->addWidget(columns, 0, 1);

    QLabel *l2 = new QLabel("Column separator", optionsBox);
    layout->addWidget(l2, 1, 0);
    delimiter = new QComboBox(optionsBox);
    delimiter->addItem("  (Whitespace [Space, Tab])");
    delimiter->addItem(", (Comma)");
    delimiter->addItem("; (Semicolon)");
    delimiter->setEditable(true);
    layout->addWidget(delimiter, 1, 1);

    useComment = new QCheckBox("Don't import Lines Starting with ", optionsBox);
    useComment->setChecked(false);
    layout->addWidget(useComment, 2, 0);
    comment = new QLineEdit(optionsBox);
    comment->setMaxLength(1);
    comment->setText("#");
    comment->setEnabled(false);
    layout->addWidget(comment, 2, 1);
    connect(useComment, SIGNAL(toggled(bool)), comment, SLOT(setEnabled(bool)));

    QLabel *l3 = new QLabel("Number of lines to skip at beginning: ", optionsBox);
    layout->addWidget(l3, 3, 0);
    skiplines = new QSpinBox(optionsBox);
    skiplines->setRange(0, 9999);
    skiplines->setSpecialValueText("None");
    layout->addWidget(skiplines, 3, 1);

    optionsBox->adjustSize();

    preview = new QTextEdit(this);
    preview->setGeometry(20, optionsBox->y() + optionsBox->height() + 10, optionsBox->width(), optionsBox->height());
    preview->setLineWrapMode(QTextEdit::NoWrap);
    preview->setStyleSheet("font-family: monospace; font-size: 10pt");
    preview->setReadOnly(true);
    QFile file(fname);
    file.open(QIODevice::ReadOnly | QIODevice::Text);
//    preview->setText(file.readAll());
    preview->setText(file.read(10000));
    file.close();

    updatePreview();

    cancelButton = new QPushButton("Cancel", this);
    cancelButton->adjustSize();
    cancelButton->move(preview->x() + preview->width() / 2 - cancelButton->width() - 30, preview->y() + preview->height() + 20);
//    cancelButton->setGeometry(preview->x() + preview->width() / 2 - 130, preview->y() + preview->height() + 20, 100, 30);
    connect(cancelButton, SIGNAL(clicked()),
            this, SLOT(reject()));

    okButton = new QPushButton("Ok", this);
    okButton->setGeometry(preview->x() + preview->width() / 2 + 30, preview->y() + preview->height() + 20, cancelButton->width(), cancelButton->height());
    connect(okButton, SIGNAL(clicked()),
            this, SLOT(accept()));

    connect(importText, SIGNAL(clicked()), this, SLOT(updatePreview()));
    connect(importNumber, SIGNAL(clicked()), this, SLOT(updatePreview()));
    connect(delimiter, SIGNAL(editTextChanged(const QString &)), this, SLOT(updatePreview2(const QString &)));
    connect(useComment, SIGNAL(clicked()), this, SLOT(updatePreview()));
    connect(comment, SIGNAL(textEdited(const QString &)), this, SLOT(updatePreview2(const QString &)));
    connect(skiplines, SIGNAL(valueChanged(int)), this, SLOT(updatePreview3(int)));
    adjustSize();
    setFixedSize(size());
}

void dpImportDialog::updatePreview(void) {
    QString delimiterText, commentText;

    delimiterText = delimiter->currentText().left(1);

    if (useComment->isChecked()) commentText = comment->text();
    else commentText = "";

    if (highlighter != NULL) {
        highlighter->setDocument(0);
        delete highlighter;
        highlighter = NULL;
    }

    QTextCharFormat singleLineCommentFormat;
    singleLineCommentFormat.setForeground(Qt::black);
    QTextCursor *lineselector = new QTextCursor(preview->document());
    lineselector->setPosition(0);
    lineselector->movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
    lineselector->setCharFormat(singleLineCommentFormat);

    if (importNumber->isChecked()) {
        highlighter = new PreviewHighlighter(preview->document(), commentText, delimiterText);

        if (skiplines->value() > 0) {
            singleLineCommentFormat.setForeground(Qt::gray);
            lineselector->setPosition(0);
            lineselector->movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, skiplines->value());
            lineselector->setCharFormat(singleLineCommentFormat);

        }
        highlighter->rehighlight();
    }
}

void dpImportDialog::updatePreview2(const QString &bla) {
    updatePreview();
}

void dpImportDialog::updatePreview3(int bla) {
    updatePreview();
}

dpPushButton::dpPushButton(const QString &text, QWidget *parent, int row, int col, int maxWidth) : QPushButton(text, parent) {
    r = row;
    c = col;
    connect(this, SIGNAL(clicked()), this, SLOT(wasclicked()));
    if (maxWidth > 0) {
//        setMaximumWidth(maxWidth);
    }
}

void dpPushButton::wasclicked() {
    emit dpPushButtonClicked(r, c);
}

dpFitsExtensionDialog::dpFitsExtensionDialog(QWidget *parent, const QString &fname, bool newBuffer, bool fromCmdLine)
    : QDialog(parent, Qt::WindowCloseButtonHint | Qt::WindowMaximizeButtonHint)
{
    filename = fname;
    extension = column = 0;
    isNewBuffer = newBuffer;

    headerView = new QFitsHeaderViewExt(this);
    headerView->setWindowFlags(headerView->windowFlags() | Qt::WindowStaysOnTopHint);

    setWindowTitle("Select FITS file extension - " + filename);

    mainWidget = new QWidget(this);
    mainWidget->move(0, 0);

    scrollareaMain = new QScrollArea(this);
    scrollareaMain->setWidget(mainWidget);
    scrollareaMain->setFrameShape(QFrame::NoFrame);

    extensionFrame = new QFrame(mainWidget);
    extensionFrame->setFrameShape(QFrame::StyledPanel);
    QGridLayout *extensionLayout = new QGridLayout(extensionFrame);
    extensionLayout->setHorizontalSpacing(20);
    extensionLayout->setVerticalSpacing(0);

    columnFrame = new QFrame(mainWidget);
    columnFrame->setFrameShape(QFrame::StyledPanel);

    scrollareaColumn = new QScrollArea(mainWidget);
    scrollareaColumn->setFrameShape(QFrame::NoFrame);
    scrollareaColumn->setWidget(columnFrame);

    QGridLayout *columnLayout = new QGridLayout(columnFrame);
    columnLayout->setHorizontalSpacing(0);
    columnLayout->setVerticalSpacing(0);

    Fits _tmp;
    int  labelHeight = 0;
    if (_tmp.OpenFITS(filename.toLocal8Bit())) {
        int          columns = 0,
                     ext = 1;
        QLabel       *label = NULL;
        dpPushButton *button = NULL;
        QString      sizeString,
                     s;
        char         keyValue[81],
                     columnKey[9];

        // write titles in extension frame
        label = new QLabel("<b>Extension</b>");
        label->adjustSize();
        extensionLayout->addWidget(label, 0, 1, Qt::AlignCenter);
        label = new QLabel("<b>Header</b>");
        label->adjustSize();
        extensionLayout->addWidget(label, 0, 2, Qt::AlignCenter);
        tlabel = new QLabel("<b>Type</b>");
        tlabel->adjustSize();
        extensionLayout->addWidget(tlabel, 0, 3, Qt::AlignCenter);
        label = new QLabel("<b>Size</b>");
        label->adjustSize();
        extensionLayout->addWidget(label, 0, 4, Qt::AlignCenter);

        //
        // write primary in extension frame
        //

        // read primary header
        _tmp.ReadFitsHeader();
        _tmp.getHeaderInformation();

        button = new dpPushButton("PRIMARY", this, 0, -1);
        extensionLayout->addWidget(button, 1, 1);
        connect(button, SIGNAL(dpPushButtonClicked(int&, int&)),
                this, SLOT(extensionButtonClicked(int&, int&)));

        QString H = "...";
        int hWidth = 20;
        button = new dpPushButton(H.trimmed(), this, 0, -1, hWidth);
        extensionLayout->addWidget(button, 1, 2);
        connect(button, SIGNAL(dpPushButtonClicked(int&, int&)),
                this, SLOT(headerButtonClicked(int&, int&)));

        label = new QLabel("IMAGE");
        label->adjustSize();
        extensionLayout->addWidget(label, 1, 3, Qt::AlignCenter);

        if (_tmp.Naxis(1) == 0) {
            sizeString = "empty";
        } else {
            sizeString = QString::number(_tmp.Naxis(1));
            for (int i = 2; i <= _tmp.Naxis(0); i++) {
                sizeString += " x " + QString::number(_tmp.Naxis(i));
            }
        }
        label = new QLabel(sizeString);
        extensionLayout->addWidget(label, 1, 4, Qt::AlignCenter);

        button = new dpPushButton("load", this, 0, -1);
        columnLayout->addWidget(button, 1, 0);
        connect(button, SIGNAL(dpPushButtonClicked(int&, int&)),
                this, SLOT(extensionButtonClicked(int&, int&)));

        // loop through extensions
        int nrColHeadersAdded = 0;
        while (_tmp.ReadFitsExtensionHeader(ext, TRUE) > 0) {
            QLabel *labeli = new QLabel(QString::number(ext), this);
            extensionLayout->addWidget(labeli, ext+1, 0);

            _tmp.getHeaderInformation();
            if (_tmp.GetStringKey("EXTNAME", keyValue)) {
                s = keyValue;
            } else {
                s = QString("ext ") + QString::number(ext);
            }

            // create a button to open all columns in a fitsarr
            int row = ext;
            int col = 0;
            if (_tmp.extensionType == IMAGE) {
                col = -1;
            }
            dpPushButton *cbutton = new dpPushButton(s.trimmed(), this, row, col);
            extensionLayout->addWidget(cbutton, ext+1, 1);
            connect(cbutton, SIGNAL(dpPushButtonClicked(int&, int&)),
                    this, SLOT(extensionButtonClicked(int&, int&)));

            button = new dpPushButton(H.trimmed(), this, row, col, hWidth);
            extensionLayout->addWidget(button, ext+1, 2);
            connect(button, SIGNAL(dpPushButtonClicked(int&, int&)),
                    this, SLOT(headerButtonClicked(int&, int&)));

            switch (_tmp.extensionType) {
                case IMAGE:     s = "IMAGE";        break;
                case TABLE:     s = "TABLE";        break;
                case BINTABLE:  s = "BINARY TABLE"; break;
                case UNKNOWN:   s = "UNKNOWN";      break;
                default:        s = "PRIMARY";      break;
            }

            QLabel *labeltype = new QLabel(s, this);
            extensionLayout->addWidget(labeltype, ext+1, 3, Qt::AlignCenter);
            if (_tmp.Naxis(1) == 0) sizeString = "empty";
            else {
                switch (_tmp.extensionType) {
                    case IMAGE:
                        sizeString = QString::number(_tmp.Naxis(1));
                        for (int i = 2; i <= _tmp.Naxis(0); i++) {
                            sizeString += " x " + QString::number(_tmp.Naxis(i));
                        }
                        break;
                    case BINTABLE:
                    case TABLE:
                        _tmp.GetIntKey("TFIELDS", &columns);
                        sizeString = QString::number(columns);
                        sizeString += " x " + QString::number(_tmp.Naxis(2));
                        break;
                    default:
                        sizeString = "";
                        break;
                }
            }
            QLabel *labelsize = new QLabel(sizeString);
            extensionLayout->addWidget(labelsize, ext+1, 4, Qt::AlignCenter);

            if (_tmp.extensionType == BINTABLE || _tmp.extensionType == TABLE) {
                QString s, t;
                int width = 0, repeat = 0;
                char fieldtype;

                for (column = 1; column <= columns; column++) {
                    if (nrColHeadersAdded < column) {
                        label = new QLabel("<b>#" + QString::number(column) + "</b>");
                        columnLayout->addWidget(label, 0, column-1, Qt::AlignCenter);
                        nrColHeadersAdded++;
                    }

                    sprintf(columnKey, "TTYPE%i", column);
                    if (_tmp.GetStringKey(columnKey, keyValue)) {
                        s = keyValue;
                    } else {
                        s = QString::number(column);
                    }
                    if (_tmp.tfieldWidth(column, &width, &repeat, &fieldtype)) {
                        t = QString::number(repeat);
                        t += " column";
                        if (repeat > 1) {
                            t += "s";
                        }
                        sprintf(columnKey, "TDIM%i", column);
                        if (_tmp.GetStringKey(columnKey, keyValue)) {
                            t += " ";
                            t += QString(keyValue).trimmed();
                        }
                        switch (fieldtype) {
                            case 'A': t += ", ASCII string";    break;
                            case 'B': t += ", B";               break;
                            case 'L': t += ", 1 byte integers"; break;
                            case 'I': t += ", 2 byte integers"; break;
                            case 'J': t += ", 4 byte integers"; break;
                            case 'K': t += ", 8 byte integers"; break;
                            case 'E':
                            case 'F': t += ", 4 byte floats";   break;
                            case 'D': t += ", 8 byte doubles";  break;
                            case 'C': t += ", 8 byte float complex";  break;
                            case 'M': t += ", 16 byte double complex";  break;
                            default:                            break;
                        }
                    } else {
                        t = "";
                    }

                    dpPushButton *cbutton = new dpPushButton(s.trimmed(), this, ext, column);
                    if (labelHeight == 0) {
                        cbutton->adjustSize();
                        labelHeight = cbutton->height();
                    }
                    if (t.length() > 0) {
                        cbutton->setToolTip(t);
                    }
                    columnLayout->addWidget(cbutton, ext+1, column-1);
                    connect(cbutton, SIGNAL(dpPushButtonClicked(int&, int&)),
                            this, SLOT(extensionButtonClicked(int&, int&)));
                }
            } else if (_tmp.extensionType == IMAGE) {
                if (nrColHeadersAdded < 1) {
                    label = new QLabel("<b>#1</b>");
                    columnLayout->addWidget(label, 0, 0, Qt::AlignCenter);
                    nrColHeadersAdded++;
                }
                column = -1;
                dpPushButton *cbutton = new dpPushButton("load", this, ext, column);
                if (labelHeight == 0) {
                    cbutton->adjustSize();
                    labelHeight = cbutton->height();
                }
                columnLayout->addWidget(cbutton, ext+1, 0);
                connect(cbutton, SIGNAL(dpPushButtonClicked(int&, int&)),
                        this, SLOT(extensionButtonClicked(int&, int&)));
            }
            ext++;
        }
        _tmp.CloseFITS();
    }

    int height = labelHeight + 2,
        extCnt = extensionLayout->rowCount(),
        colCnt = columnLayout->rowCount(),
        shiftScrollareaColumn = 20;
#ifdef Q_OS_MACX
    height = labelHeight-5,
    extCnt = 1,
    colCnt = 1;
#endif
    for (int i = 0; i < extCnt; i++) {
        extensionLayout->setRowMinimumHeight(i, height);
    }
    for (int i = 0; i < colCnt; i++) {
        columnLayout->setRowMinimumHeight(i, height);
    }

    extensionFrame->adjustSize();
    columnFrame->adjustSize();

    int dlgWidth = 500;
    if (columnFrame->width() < dlgWidth) {
        dlgWidth = columnFrame->width()+1;
    }
    scrollareaColumn->setGeometry(extensionFrame->x() + extensionFrame->width() + 5, extensionFrame->y(),
                                  dlgWidth, extensionFrame->height()+20);

    int dlgHeight = 400;
    mainWidget->adjustSize();
    if (mainWidget->height() < dlgHeight) {
        dlgHeight = mainWidget->height();
        shiftScrollareaColumn = 0;
    }
    scrollareaMain->setGeometry(10, 10, mainWidget->width()+20, dlgHeight);

    scrollareaColumn->horizontalScrollBar()->setParent(this);
    scrollareaColumn->horizontalScrollBar()->setFixedWidth(scrollareaColumn->width());
    scrollareaColumn->horizontalScrollBar()->move(scrollareaColumn->x() + mainWidget->x() + 10,
                                                  scrollareaMain->height() + shiftScrollareaColumn);

    if (columnFrame->width() < scrollareaColumn->width()) {
        scrollareaColumn->horizontalScrollBar()->hide();
    }

    if (fromCmdLine && (!isNewBuffer)) {
        exitButton = new QPushButton("Exit", this);
        connect(exitButton, SIGNAL(clicked()), this, SLOT(exitClicked()));
        exitButton->setGeometry(10, scrollareaMain->height() + 10 + shiftScrollareaColumn, 100, 30);
        exitButton->setDefault(true);
        cancelButton = NULL;
    } else {
        cancelButton = new QPushButton("Cancel", this);
        connect(cancelButton, SIGNAL(clicked()), this, SLOT(closeDialog()));
        cancelButton->setGeometry(10, scrollareaMain->height() + 10 + shiftScrollareaColumn, 100, 30);
        cancelButton->setDefault(true);
        exitButton = NULL;
    }

    okButton = new QPushButton("Read All", this);
    okButton->setGeometry(10+200, scrollareaMain->height() + 10 + shiftScrollareaColumn, 100, 30);
    okButton->setDefault(true);
    connect(okButton, SIGNAL(clicked()),
            this, SLOT(readAllExtensionsClicked()));

    connect(this, SIGNAL(rejected()),
            this, SLOT(rejectDialog()));

    if (settings.loadExtMaximized) showMaximized();
    else if (settings.loadExtHeight != -1 && settings.loadExtWidth != -1) resize(settings.loadExtWidth, settings.loadExtHeight);
    else adjustSize();
}

void dpFitsExtensionDialog::resizeEvent(QResizeEvent *e) {
    int maxwidth = extensionFrame->x() + extensionFrame->width() + 5 + columnFrame->width();

    if (maxwidth > e->size().width() - 30) maxwidth = e->size().width() - 30;
    mainWidget->resize(maxwidth, mainWidget->height());

    if (isMaximized()) settings.loadExtMaximized = true;
    else {
        settings.loadExtMaximized = false;
        settings.loadExtWidth = e->size().width();
        settings.loadExtHeight = e->size().height();
    }
    int shiftScrollareaColumn = 20;
    int dlgWidth = size().width() - 30 - (extensionFrame->x() + extensionFrame->width() + 5);

    if (columnFrame->width() < dlgWidth) {
        dlgWidth = columnFrame->width()+1;
    }
    scrollareaColumn->setGeometry(extensionFrame->x() + extensionFrame->width() + 5, extensionFrame->y(),
                                  dlgWidth, extensionFrame->height()+20);

    int dlgHeight = size().height() - 70;
    if (mainWidget->height() < dlgHeight) {
        dlgHeight = mainWidget->height();
        shiftScrollareaColumn = 0;
    }
    scrollareaMain->setGeometry(10, 10, mainWidget->width()+20, dlgHeight);
    scrollareaColumn->horizontalScrollBar()->setFixedWidth(scrollareaColumn->width());
    scrollareaColumn->horizontalScrollBar()->move(scrollareaColumn->x() + mainWidget->x() + 10,
                                                  scrollareaMain->height() + shiftScrollareaColumn);
    if (columnFrame->width() < scrollareaColumn->width()) {
        scrollareaColumn->horizontalScrollBar()->hide();
    } else {
        scrollareaColumn->horizontalScrollBar()->show();
    }
    if (exitButton) exitButton->setGeometry(10, scrollareaMain->height() + 10 + shiftScrollareaColumn, 100, 30);
    if (cancelButton) cancelButton->setGeometry(10, scrollareaMain->height() + 10 + shiftScrollareaColumn, 100, 30);
    okButton->setGeometry(10+200, scrollareaMain->height() + 10 + shiftScrollareaColumn, 100, 30);
}

dpFitsExtensionDialog::~dpFitsExtensionDialog() {
    rejectDialog();
}

void dpFitsExtensionDialog::updateExtensionNumber(const int &ext) {
	extension = ext;
}

void dpFitsExtensionDialog::updateColumnNumber(const int &col) {
	column = col;
}

void dpFitsExtensionDialog::extensionButtonClicked(int &ext, int &col) {
    QString cmd;

    if (isNewBuffer) {
        cmd = freeBufferName().c_str();
    } else {
        cmd = QString(fitsMainWindow->getCurrentBufferIndex().c_str());
    }
    if (col >= 0) {
        cmd += " = ";
        cmd += "readfitsbintable(\"";
        cmd += filename;
        cmd += "\", ";
        cmd += QString::number(ext);
        cmd += ", ";
        cmd += QString::number(col);
        cmd += ")";
    } else {
        cmd += " = ";
        if (ext == 0) {
            cmd += "readfits(\"";
            cmd += filename;
            cmd += "\")";
        } else  {
            cmd += "readfitsextension(\"";
            cmd += filename;
            cmd += "\", ";
            cmd += QString::number(ext);
            cmd += ")";
        }
    }
    dpuser_widget->executeCommand(cmd);

    QFileInfo finfo(filename);
    settings.lastOpenPath = finfo.absoluteDir().path();
    settings.lastOpenPath.replace("\\", "/");

    closeDialog();
}

void dpFitsExtensionDialog::headerButtonClicked(int &ext, int&) {
    bool ok = false;
    Fits _tmp;
    if (_tmp.OpenFITS(filename.toLocal8Bit(), false)) {
        if (ext == 0) {
            if (_tmp.ReadFitsHeader() > 0) {
                ok = true;
            }
        } else if (ext > 0) {
            if (_tmp.ReadFitsExtensionHeader(ext, TRUE, TRUE) > 0) {
                ok = true;
            }
        }
        if (!ok) _tmp.CloseFITS();
    }
    if (ok) {
        if (!headerView->isVisible()) {
            headerView->move(mapToGlobal(tlabel->pos()));
        }
        headerView->showUp(&_tmp, filename);
        _tmp.CloseFITS();
    }
}

void dpFitsExtensionDialog::readAllExtensionsClicked() {
    QString cmd;
    if (isNewBuffer) {
        cmd = freeBufferName().c_str();
    } else {
        cmd = QString(fitsMainWindow->getCurrentBufferIndex().c_str());
    }
    cmd += " = readfitsall(\"" + filename + "\")";
    dpuser_widget->executeCommand(cmd);

    QFileInfo finfo(filename);
    settings.lastOpenPath = finfo.absoluteDir().path();
    settings.lastOpenPath.replace("\\", "/");

    closeDialog();
}

void dpFitsExtensionDialog::rejectDialog() {
    delete headerView; headerView = NULL;
}

void dpFitsExtensionDialog::closeDialog() {
    // done() emits a rejected() SIGNAL
    // which in return calls closeDialog() again
    // and thus deletes headerView
    done(0);
}

void dpFitsExtensionDialog::exitClicked() {
    // done() emits a rejected() SIGNAL
    // which in return calls closeDialog() again
    // and thus deletes headerView
    dpuser_widget->executeCommand(QString("exit"));
    dpuserthread.wait(1000);

    ::exit(0);
    done(0);
}

dpMpfitDataSelectWidget::dpMpfitDataSelectWidget(QString label, QWidget *parent) : QWidget(parent) {
    QLabel *l = new QLabel(label, this);
    l->adjustSize();
    data = new dpPopup(this);
    data->addItem("<none>", -1);
    data->adjustSize();
    column = new QRadioButton("column", this);
    column->adjustSize();
    row = new QRadioButton("row", this);
    row->adjustSize();
    column->setChecked(true);
    roworcolumn = new QSpinBox(this);
    roworcolumn->setMinimum(1);
    roworcolumn->setMaximum(100000);
    roworcolumn->adjustSize();
    enableSelectors(false);

    int w = column->width() + row->width() + roworcolumn->width();
    int h = data->height();
    l->setGeometry(0, 0, l->width(), h);
    data->setGeometry(l->width(), 0, w - l->width(), h);
    column->setGeometry(0, h, column->width(), h);
    row->setGeometry(column->width(), h, row->width(), h);
    roworcolumn->setGeometry(row->width() + column->width(), h, roworcolumn->width(), h);

    connect(data, SIGNAL(activated(int)), SIGNAL(somethingChanged()));
    connect(row, SIGNAL(clicked(bool)), SIGNAL(somethingChanged()));
    connect(column, SIGNAL(clicked(bool)), SIGNAL(somethingChanged()));
    connect(roworcolumn, SIGNAL(valueChanged(int)), SIGNAL(somethingChanged()));

    adjustSize();
}

void dpMpfitDataSelectWidget::enableSelectors(bool enable) {
    row->setEnabled(enable);
    column->setEnabled(enable);
    roworcolumn->setEnabled(enable);
}

dpMpfitParameterWidget::dpMpfitParameterWidget(QString label, QWidget *parent) : QWidget(parent), parameterName(label) {
//    estimate = new dpDoubleEdit("1.0", this);
    estimate = new dpFitEstimate("1.0", this);
    estimate->adjustSize();
    int theHeight = estimate->height();
    estimate->setGeometry(100, 0, estimate->width(), theHeight);
    la = new QLabel("<b>&nbsp;Variable</b>", this);
    la->adjustSize();
    la->setText("<b>&nbsp;" + label + ":&nbsp;</b>");
    la->setGeometry(0, 0, la->width(), theHeight);
    estimate->move(la->x() + la->width(), 0);
//    unconstrained = new QRadioButton("unconstrained", this);
//    unconstrained->adjustSize();
//    unconstrained->setGeometry(estimate->x() + estimate->width() + 5, 0, unconstrained->width(), theHeight);
//    constrained = new QRadioButton("constrained", this);
//    constrained->adjustSize();
//    constrained->setGeometry(unconstrained->x() + unconstrained->width() + 5, 0, constrained->width(), theHeight);
//    fixed = new QRadioButton("fixed", this);
//    fixed->adjustSize();
//    unconstrained->setChecked(true);
//    lowerBound = new dpDoubleEdit("-1.0", this);
//    lowerBound->setGeometry(constrained->x() + constrained->width() + 5, 0, estimate->width(), theHeight);
//    upperBound = new dpDoubleEdit("1.0", this);
//    upperBound->setGeometry(lowerBound->x() + lowerBound->width(), 0, estimate->width(), theHeight);
//    lowerBound->setEnabled(false);
//    upperBound->setEnabled(false);
//    fixed->setGeometry(upperBound->x() + upperBound->width() + 5, 0, fixed->width(), theHeight);
    fitresult = new dpFitResult(this);
    fitresult->adjustSize();
    fitresult->setGeometry(estimate->x() + estimate->width() + 5, 0, fitresult->width(), theHeight);



//    result = new QLabel(this);
//    result->setGeometry(fixed->x() + fixed->width() + 5, 0, 100, theHeight);
//    result->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
//    resulterr = new QLabel(this);
//    resulterr->setGeometry(result->x() + result->width(), 0, 150, theHeight);
//    resulterr->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);

//    connect(constrained, SIGNAL(toggled(bool)), lowerBound, SLOT(setEnabled(bool)));
//    connect(constrained, SIGNAL(toggled(bool)), upperBound, SLOT(setEnabled(bool)));

    adjustSize();
}

dpMpfitPopupWidget::dpMpfitPopupWidget(QWidget *parent) : QWidget(parent) {
    QLabel * fitlabel = new QLabel("<font size=+1><b>&nbsp;f(x)=</b></font>", this);
    fitlabel->adjustSize();
    loadParams = new QPushButton("Load...", this);
    loadParams->adjustSize();
    int theHeight = loadParams->height();
    loadParams->move(0, loadParams->height() * 2);
    saveParams = new QPushButton("Save...", this);
    saveParams->adjustSize();
    saveParams->move(loadParams->width(), loadParams->y());
    leftWidth = saveParams->x() + saveParams->width();
    fitfunction = new QTextEdit(this);
    fitfunction->setGeometry(leftWidth, 0, 300, loadParams->height() * 3);
    fitfunction->setAcceptRichText(false);
    getVariables = new QPushButton("Evaluate", this);
    getVariables->adjustSize();
    getVariables->move(leftWidth + 300, 0);
    dofit = new QPushButton("FIT!", this);
    dofit->setGeometry(leftWidth + 300, loadParams->height(), getVariables->width(), loadParams->height());
    configureMpfit = new QPushButton("MPFIT...", this);
    configureMpfit->setGeometry(leftWidth + 300, loadParams->height() * 2, getVariables->width(), loadParams->height());

    QLabel *l1 = new QLabel("<b>&nbsp;Variable</b>", this);
    QLabel *l2 = new QLabel("<b>&nbsp;Estimate</b>", this);
    constrainX = new QCheckBox("Constrain x:", this);
    constrainX->setChecked(false);
    constrainX->adjustSize();
    minx = new dpDoubleEdit("", this);
    minx->setEnabled(false);

    maxx = new dpDoubleEdit("", this);
    maxx->setEnabled(false);
    l1->adjustSize();
    l2->adjustSize();
    l1->setGeometry(0, fitfunction->height(), l1->width(), theHeight);
    l2->setGeometry(l1->width(), fitfunction->height(), l2->width(), theHeight);
    constrainX->setGeometry(l2->x() + l2->width() + 5, fitfunction->height(), constrainX->width(), theHeight);
    minx->setGeometry(constrainX->x() + constrainX->width() + 5, fitfunction->height(), l1->width(), theHeight);
    maxx->setGeometry(minx->x() + minx->width() + 5, fitfunction->height(), l1->width(), theHeight);
    fitresult = new QLabel("<b><center>&nbsp;Fit result</center></b>", this);
    fitresult->adjustSize();
    fitresult->setGeometry(maxx->x() + maxx->width() + 20, fitfunction->height(), fitresult->width(), theHeight);
    copyButton = new QPushButton("copy to clipboard", this);
    copyButton->adjustSize();
    copyButton->setGeometry(500-copyButton->width(), 99, copyButton->width(), theHeight);
    parametersWidget = new QWidget(this);
    parametersWidget->setGeometry(0, fitfunction->height() + l1->height(), 300, 0);

    connect(constrainX, SIGNAL(clicked(bool)), minx, SLOT(setEnabled(bool)));
    connect(constrainX, SIGNAL(clicked(bool)), maxx, SLOT(setEnabled(bool)));

//    connect(getVariables, SIGNAL(clicked(bool)), SLOT(setParameters()));

    adjustSize();
}

void dpMpfitPopupWidget::resizeEvent(QResizeEvent *r) {
    int w = r->size().width();
    fitfunction->resize(r->size().width() - dofit->width() - leftWidth, fitfunction->height());
    getVariables->move(fitfunction->width() + leftWidth, 0);
    dofit->move(fitfunction->width() + leftWidth, dofit->y());
    configureMpfit->move(fitfunction->width() + leftWidth, configureMpfit->y());
    copyButton->move(r->size().width() - copyButton->width(), configureMpfit->y() + configureMpfit->height());
}

void dpMpfitPopupWidget::setParameters(void) {
    int i, nparameters, theHeight;
    dpStringList variables = mpfit_sorted_variables;
//    dpStringList variables = mpfit_find_variables(fitfunction->toPlainText().toStdString());
    parameterNames.clear();
    nparameters = variables.size();

    for (QMap<QString, dpMpfitParameterWidget *>::iterator it=parameters.begin(); it != parameters.end(); ++it) {
        it.value()->hide();
    }

    if (nparameters > 0) {
        for (i = 0; i < nparameters; i++) {
            if (!parameters.contains(variables.at(i).c_str())) {
                parameters.insert(variables.at(i).c_str(), new dpMpfitParameterWidget(variables.at(i).c_str(), parametersWidget));
                connect (parameters.value(variables.at(i).c_str())->estimate, SIGNAL(valueChanged(double)), this, SLOT(parameterChanged(double)));
            }
            parameterNames.append(variables.at(i).c_str());
        }

        if (parameterNames.size() > 0) {
            theHeight = parameters.value(parameterNames.at(0))->height();
            for (i = 0; i < parameterNames.size(); i++) {
//                parameters.value(parameterNames.at(i))->showChildren();
                parameters.value(parameterNames.at(i))->move(0, i * theHeight);
                parameters.value(parameterNames.at(i))->show();
            }
        }
    }
    parametersWidget->adjustSize();
    adjustSize();
    parentWidget()->adjustSize();
//    parentWidget()->resize(parentWidget()->size());
}

void dpMpfitPopupWidget::parameterChanged(double v) {
    emit somethingChanged();
}

dpMpfitDialog::dpMpfitDialog(QWidget *parent) : QDialog(parent) {
    setWindowTitle("QFitsView - Mpfit");

    mpfitConfiguration = new dpMpfitConfiguration(this);
    mpfitConfiguration->hide();

    lineStyle = QCPGraph::lsLine;
    scatterStyle = QCPScatterStyle::ssCross;

    plotarea = new QCustomPlot(this);
    plotarea->setGeometry(0, 100, 800, 500);
    plotarea->addGraph();

    connect(plotarea, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseClicked(QMouseEvent*)));
    plotStyleLinesAct = new QAction("Plot Style: Lines", this);
    connect(plotStyleLinesAct, SIGNAL(triggered()), this, SLOT(setPlotStyleLines()));

    plotStyleCrossAct = new QAction("Plot Style: Crosses", this);
    connect(plotStyleCrossAct, SIGNAL(triggered()), this, SLOT(setPlotStyleCross()));

    rightPopupMenu = new QMenu(this);
    rightPopupMenu->addAction(plotStyleLinesAct);
    rightPopupMenu->addAction(plotStyleCrossAct);

    rightwidget = new QWidget(this);
    rightwidget->setGeometry(plotarea->x() + plotarea->width(), 30, 200, 50);
    y = new dpMpfitDataSelectWidget("Y-Axis:", rightwidget);
    x = new dpMpfitDataSelectWidget("X-Axis:", rightwidget);
    x->move(0, y->y() + y->height() + 5);
    yerr = new dpMpfitDataSelectWidget("Err in Y:", rightwidget);
    yerr->move(0, x->y() + x->height() + 5);
    xerr = new dpMpfitDataSelectWidget("Err in X:", rightwidget);
    xerr->move(0, yerr->y() + yerr->height() + 5);

    connect(x, SIGNAL(somethingChanged()), this, SLOT(somethingChanged()));
    connect(y, SIGNAL(somethingChanged()), this, SLOT(somethingChanged()));
    connect(yerr, SIGNAL(somethingChanged()), this, SLOT(somethingChanged()));
    connect(xerr, SIGNAL(somethingChanged()), this, SLOT(somethingChanged()));

    mpfitParameters = new dpMpfitPopupWidget(this);

    connect(mpfitParameters->fitfunction, SIGNAL(textChanged()), this, SLOT(fittextChanged()));
    connect(mpfitParameters->getVariables, SIGNAL(clicked(bool)), this, SLOT(fitfunctionChanged()));
    connect(mpfitParameters->dofit, SIGNAL(clicked(bool)), this, SLOT(dofit()));
    connect(mpfitParameters->configureMpfit, SIGNAL(clicked()), mpfitConfiguration, SLOT(show()));
    connect(mpfitParameters->copyButton, SIGNAL(clicked(bool)), this, SLOT(copyfit()));
    connect(mpfitParameters->saveParams, SIGNAL(clicked()), this, SLOT(saveParams()));
    connect(mpfitParameters->loadParams, SIGNAL(clicked()), this, SLOT(loadParams()));
    connect(mpfitParameters, SIGNAL(somethingChanged()), this, SLOT(fitestimateChanged()));

    rightwidget->adjustSize();
    adjustSize();
//    setMinimumSize(size());
}

void dpMpfitDialog::resizeEvent(QResizeEvent *r) {
    mpfitParameters->resize(r->size().width(), mpfitParameters->height());
    plotarea->setGeometry(0, mpfitParameters->height(), r->size().width() - rightwidget->width(), r->size().height() - mpfitParameters->height());
    rightwidget->move(plotarea->x() + plotarea->width(), mpfitParameters->height());
}

void dpMpfitDialog::fitestimateChanged() {
    evaluateFitFunction();
}

void dpMpfitDialog::fittextChanged() {
    dpString function;
    dpString funct = mpfitParameters->fitfunction->toPlainText().toStdString();
    for (int i = 0; i < funct.size(); i++) if (!isspace(funct[i])) function.push_back(funct[i]);
    function.push_back(';');
    mpfitASTNode *oldAST = mpfitAST;
    try {
        main_mpfit(function);
        delete oldAST;
        mpfitParameters->fitfunction->setStyleSheet("color: black;");
        mpfitParameters->setParameters();
        evaluateFitFunction();
    } catch (dpuserTypeException e) {
        mpfitAST = oldAST;
        mpfitParameters->setParameters();
        mpfitParameters->fitfunction->setStyleSheet("color: red;");
    }


    plotarea->setGeometry(0, mpfitParameters->height(), size().width() - rightwidget->width(), size().height() - mpfitParameters->height());
    rightwidget->move(plotarea->x() + plotarea->width(), mpfitParameters->height());
}

void dpMpfitDialog::fitfunctionChanged() {
    mpfitParameters->setParameters();

    plotarea->setGeometry(0, mpfitParameters->height(), size().width() - rightwidget->width(), size().height() - mpfitParameters->height());
    rightwidget->move(plotarea->x() + plotarea->width(), mpfitParameters->height());

    evaluateFitFunction();
}

void dpMpfitDialog::evaluateFitFunction(Fits *parameters) {
    dpString funct = mpfitParameters->fitfunction->toPlainText().toStdString();
    if (funct.isEmpty()) {
        plotarea->removeGraph(1);
        plotarea->yAxis->rescale();
        plotarea->replot();
        return;
    }

    if (plotarea->graphCount() < 2) plotarea->addGraph();
    if (plotarea->graphCount() < 2) plotarea->addGraph();

//    dpString fff = mpfit_parse_dpuser(funct);

    // evaluate which variables are used
    dpStringList variableList = mpfit_sorted_variables;
//    dpStringList variableList = mpfit_find_variables(fff);
//    dpString variables = mpfit_build_variable_declarations(variableList);

    // write c code
//    dpString fname = mpfit_write_userfunction(fff, variables);
//    if (fname.size() == 0) {
//        QMessageBox::critical(this, QString("Compilation failed"), mpfit_compile_output.c_str());
//        return;
//    }

    Fits x, y, result, estimate;
    if (parameters == NULL) {
        estimate.create(variableList.size(), 1, R8);
        for (int i = 0; i < variableList.size(); i++) {
            estimate.r8data[i] = mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->value();            
            if (mpfitParameters->parameterNames.at(i).toStdString().size() == 1) {
                mpfit_quick_vars[mpfitParameters->parameterNames.at(i).toStdString()[0]] = estimate.r8data[i];
            } else {
                mpfit_vars[mpfitParameters->parameterNames.at(i).toStdString()] = estimate.r8data[i];
            }
        }

// adjust x constrain Labels & entry fields
        if (variableList.size() > 0) {
            mpfitParameters->fitresult->setGeometry(mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(0))->fitresult->x(),
                                                    mpfitParameters->fitresult->y(), mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(0))->fitresult->width(),
                                                    mpfitParameters->fitresult->height());
            mpfitParameters->constrainX->move(mpfitParameters->fitresult->x() + mpfitParameters->fitresult->width(), mpfitParameters->constrainX->y());
            mpfitParameters->minx->move(mpfitParameters->constrainX->x() + mpfitParameters->constrainX->width(), mpfitParameters->minx->y());
            mpfitParameters->maxx->move(mpfitParameters->minx->x() + mpfitParameters->minx->width(), mpfitParameters->maxx->y());
        }
    } else {
        estimate.copy(*parameters);
    }
    x.create(xd.size(), 1, R8);
    if (mpfitParameters->constrainX->isChecked()) {
        double xmin = mpfitParameters->minx->value();
        double xmax = mpfitParameters->maxx->value();
        if (xmin > xmax) {
            double tempd;
            tempd = xmax;
            xmax = xmin;
            xmin = tempd;
        }
        int j = 0;
        for (int i = 0; i < xd.size(); i++) {
            if (xd.at(i) >= xmin && xd.at(i) <= xmax) {
                x.r8data[j] = xd.at(i);
                j++;
            }
        }
        if (j == 0) return;
        x.resize(j);
    } else {
        for (int i = 0; i < xd.size(); i++) {
            x.r8data[i] = xd.at(i);
        }
    }
//        if (mpfit_evaluate_userfunction(result, x, estimate, fname) == -1) {
    if (mpfit_evaluate_userstring(result, x, estimate, funct) == -1) {
        QMessageBox::critical(this, QString("Compilation failed"), mpfit_compile_output.c_str());
        return;
    }
    QVector<double>data, xx;
    data.resize(result.Nelements());
    xx.resize(result.Nelements());
    for (int i = 0; i < result.Nelements(); i++) {
        xx[i] = x.ValueAt(i);
        data[i] = result.ValueAt(i);
    }
    plotarea->graph(1)->setData(xx, data);
    plotarea->graph(1)->setAntialiased(false);
    QPen pen;
    pen.setWidth(3);
    if (parameters == NULL) {
        pen.setColor(QColor(255,0,0, 100));
    } else {
        pen.setColor(QColor(0, 255, 0, 200));
    }
    plotarea->graph(1)->setPen(pen);
    plotarea->yAxis->rescale();
    plotarea->replot();
}

void dpMpfitDialog::dofit() {
    if (mpfit_sorted_variables.size() == 0) {
        QMessageBox::warning(this, "QFitsView MPFIT", "No variables to fit!");
        return;
    }
    if (yd.size() < 1) {
        QMessageBox::warning(this, "QFitsView MPFIT", "No data to fit!");
        return;
    }
    dpString funct = mpfitParameters->fitfunction->toPlainText().toStdString();
//    dpString fff = mpfit_parse_dpuser(funct);

    // evaluate which variables are used
    dpStringList variableList = mpfit_sorted_variables;
//    dpStringList variableList = mpfit_find_variables(fff);
//    dpString variables = mpfit_build_variable_declarations(variableList);
    Fits estimate, x, y, err, result;
    estimate.create(variableList.size(), 4, R8);
    for (int i = 0; i < variableList.size(); i++) {
        estimate.r8data[estimate.C_I(i, 0)] = mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->value();
        if (mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->fixed->isChecked()) {
            estimate.r8data[estimate.C_I(i, 1)] = -1.0;
        } else if (mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->constrained->isChecked()) {
            estimate.r8data[estimate.C_I(i, 1)] = 3.0;
            estimate.r8data[estimate.C_I(i, 2)] = mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->lowerBound->value();
            estimate.r8data[estimate.C_I(i, 3)] = mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->upperBound->value();
        }
    }
    x.create(xd.size(), 1, R8);
    y.create(xd.size(), 1, R8);
    err.create(xd.size(), 1, R8);
    if (mpfitParameters->constrainX->isChecked()) {
        double xmin = mpfitParameters->minx->value();
        double xmax = mpfitParameters->maxx->value();
        if (xmin > xmax) {
            double tempd;
            tempd = xmax;
            xmax = xmin;
            xmin = tempd;
        }
        int j = 0;
        for (int i = 0; i < xd.size(); i++) {
            if (xd.at(i) >= xmin && xd.at(i) <= xmax) {
                x.r8data[j] = xd.at(i);
                y.r8data[j] = yd.at(i);
                if (xerrd.size() > 0 && yerrd.size() > 0) {
                    err.r8data[j] = sqrt(xerrd.at(i)*xerrd.at(i) + yerrd.at(i)*yerrd.at(i));
                } else if (xerrd.size() > 0) {
                    err.r8data[j] = fabs(xerrd.at(i));
                } else if (yerrd.size() > 0) {
                    err.r8data[j] = fabs(yerrd.at(i));
                } else {
                    err.r8data[j] = 1.0;
                }
                j++;
            }
        }
        if (j == 0) return;
        x.resize(j);
        y.resize(j);
        err.resize(j);
    } else {
        for (int i = 0; i < xd.size(); i++) {
            x.r8data[i] = xd.at(i);
            y.r8data[i] = yd.at(i);
            if (xerrd.size() > 0 && yerrd.size() > 0) {
                err.r8data[i] = sqrt(xerrd.at(i)*xerrd.at(i) + yerrd.at(i)*yerrd.at(i));
            } else if (xerrd.size() > 0) {
                err.r8data[i] = fabs(xerrd.at(i));
            } else if (yerrd.size() > 0) {
                err.r8data[i] = fabs(yerrd.at(i));
            } else {
                err.r8data[i] = 1.0;
            }
        }
    }
//    mpfit_functionstring(result, x, y, err, estimate, funct);
    if (mpfit_fit_userstring(result, x, y, err, estimate, funct) == -1) {
        QMessageBox::critical(this, QString("Compilation failed"), mpfit_compile_output.c_str());
        return;
    }

    for (int i = 0; i < variableList.size(); i++) {
        mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->fitresult->setResult(result.ValueAt(i), result.ValueAt(result.C_I(i, 1)));
//        QString s = QString::number(result.ValueAt(i));
//        mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->result->setText(s);
//        s = " +- " + QString::number(result.ValueAt(result.C_I(i, 1)));
//        mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->resulterr->setText(s);
    }
    // write c code
//    dpString fname = mpfit_write_userfunction(fff, variables);

//    mpfit_evaluate_userfunction(estimate, x, result, fname);
    for (int i = 0; i < variableList.size(); i++) {
        if (mpfitParameters->parameterNames.at(i).toStdString().size() == 1) {
            mpfit_quick_vars[mpfitParameters->parameterNames.at(i).toStdString()[0]] = result.r8data[i];
        } else {
            mpfit_vars[mpfitParameters->parameterNames.at(i).toStdString()] = result.r8data[i];
        }
    }
    evaluateFitFunction(&result);
//    if (mpfit_evaluate_userstring(estimate, x, funct) == -1) {
//        QMessageBox::critical(this, QString("Compilation failed"), mpfit_compile_output.c_str());
//        return;
//    }
//    QVector<double>data;
//    data.resize(estimate.Nelements());
//    for (int i = 0; i < estimate.Nelements(); i++) data[i] = estimate.ValueAt(i);
//    if (plotarea->graphCount() < 2) plotarea->addGraph();
//    plotarea->graph(1)->setData(xd, data);
//    plotarea->graph(1)->setAntialiased(false);
//    QPen pen;
//    pen.setColor(QColor(255,0,0));
//    plotarea->graph(1)->setPen(pen);
//    plotarea->yAxis->rescale();
//    plotarea->replot();
}

void dpMpfitDialog::saveParams() {
    QString filename = QFileDialog::getSaveFileName(this, "Save Fit Parameters as...", settings.lastSavePath, "*.xml;;*");

    if (!filename.isEmpty()) {
        QFile file(filename);
        file.open(QIODevice::WriteOnly);

        QXmlStreamWriter xmlWriter(&file);
        xmlWriter.setAutoFormatting(true);
        xmlWriter.writeStartDocument();

        xmlWriter.writeStartElement("MPFIT");

        xmlWriter.writeTextElement("FitFunction", mpfitParameters->fitfunction->toPlainText());

        xmlWriter.writeStartElement("ConstrainX");
        xmlWriter.writeTextElement("Active", mpfitParameters->constrainX->isChecked() ? "1" : "0");
        xmlWriter.writeTextElement("Minimum", mpfitParameters->minx->text());
        xmlWriter.writeTextElement("Maximum", mpfitParameters->maxx->text());
        xmlWriter.writeEndElement();

        dpStringList variableList = mpfit_sorted_variables;
        for (int i = 0; i < variableList.size(); i++) {
            xmlWriter.writeStartElement("Parameter");
            xmlWriter.writeAttribute("Name", mpfitParameters->parameterNames.at(i));
            xmlWriter.writeTextElement("Initial", mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->text());
            xmlWriter.writeTextElement("Fixed", mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->fixed->isChecked() ? "1": "0");
            xmlWriter.writeTextElement("Constrained", mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->constrained->isChecked() ? "1": "0");
            xmlWriter.writeTextElement("ConstrainMinimum", mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->lowerBound->text());
            xmlWriter.writeTextElement("ConstrainMaximum", mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->estimate->upperBound->text());
            xmlWriter.writeEndElement();
        }

        xmlWriter.writeStartElement("Configuration");
        xmlWriter.writeTextElement("covtol", QString::number(dpMpfitConfig.covtol));
        xmlWriter.writeTextElement("epsfcn", QString::number(dpMpfitConfig.epsfcn));
        xmlWriter.writeTextElement("ftol", QString::number(dpMpfitConfig.ftol));
        xmlWriter.writeTextElement("gtol", QString::number(dpMpfitConfig.gtol));
        xmlWriter.writeTextElement("maxfev", QString::number(dpMpfitConfig.maxfev));
        xmlWriter.writeTextElement("maxiter", QString::number(dpMpfitConfig.maxiter));
        xmlWriter.writeTextElement("stepfactor", QString::number(dpMpfitConfig.stepfactor));
        xmlWriter.writeTextElement("xtol", QString::number(dpMpfitConfig.xtol));
        xmlWriter.writeEndElement();

        xmlWriter.writeEndDocument();

        file.close();
    }
    QFileInfo finfo(filename);
    settings.lastSavePath = finfo.absoluteDir().path();
    settings.lastSavePath.replace("\\", "/");
}

using namespace rapidxml;

char *getXMLValue(xml_node<> *node, const char *name) {
    if (node == NULL || name == NULL) return NULL;
    xml_node<> *vnode = node->first_node(name);
    if (vnode == NULL) return NULL;
    return vnode->value();
}

void dpMpfitDialog::loadParams() {
    QString filename = QFileDialog::getOpenFileName(this, "Load Fit Parameters", settings.lastOpenPath, "*.xml;;*");
    if (filename.isEmpty()) return;

    FILE *fd;
    char *newinput;
    if ((fd = fopen(filename.toStdString().c_str(), "rb")) == NULL) {
        dp_output("Could not open file %s for reading\n", filename.toStdString().c_str());
        return;
    }
    fseek(fd, 0L, SEEK_END);
    long flength = ftell(fd);
    rewind(fd);
    if ((newinput = (char *)calloc((flength+2), sizeof(char))) == NULL) {
        fclose(fd);
        return;
    }
    fread(newinput, sizeof(char), flength, fd);
    fclose(fd);

    xml_document<> doc;
    doc.parse<0>(newinput);
    xml_node<> *root_node = doc.first_node("MPFIT");
    if (root_node == NULL) return;

    char *value;
    if ((value = getXMLValue(root_node, "FitFunction")) == NULL) return;

    mpfitParameters->fitfunction->setText(value);

    xml_node<> *constrain = root_node->first_node("ConstrainX");
    if (constrain != NULL) {
        if ((value = getXMLValue(constrain, "Active")) != NULL) {
            if (value[0] == '1') mpfitParameters->constrainX->setChecked(false);
            if (value[0] == '0') mpfitParameters->constrainX->setChecked(true);
            mpfitParameters->constrainX->click(); // 2 lines above inverse logic. This line (de-)activates the input fields
        }
        if ((value = getXMLValue(constrain, "Minimum")) != NULL) mpfitParameters->minx->setText(value);
        if ((value = getXMLValue(constrain, "Maximum")) != NULL) mpfitParameters->maxx->setText(value);
    }

    xml_node<> *configuration = root_node->first_node("Configuration");
    if (configuration != NULL) {
        if ((value = getXMLValue(configuration, "covtol")) != NULL) dpMpfitConfig.covtol = atof(value);
        if ((value = getXMLValue(configuration, "epsfcn")) != NULL) dpMpfitConfig.epsfcn = atof(value);;
        if ((value = getXMLValue(configuration, "ftol")) != NULL) dpMpfitConfig.ftol = atof(value);
        if ((value = getXMLValue(configuration, "gtol")) != NULL) dpMpfitConfig.gtol = atof(value);
        if ((value = getXMLValue(configuration, "maxfev")) != NULL) dpMpfitConfig.maxfev = atoi(value);
        if ((value = getXMLValue(configuration, "maxiter")) != NULL) dpMpfitConfig.maxiter = atoi(value);
        if ((value = getXMLValue(configuration, "stepfactor")) != NULL) dpMpfitConfig.stepfactor = atof(value);
        if ((value = getXMLValue(configuration, "xtol")) != NULL) dpMpfitConfig.xtol = atof(value);
    }

    for (xml_node<> *parameter = root_node->first_node("Parameter"); parameter; parameter = parameter->next_sibling()) {
        if (parameter != NULL) {
            QString parameterName = "";

            xml_attribute<> *attr = parameter->first_attribute("Name");
            if (attr != NULL) value = attr->value();
            if (value != NULL) parameterName = value;
            if (parameterName != "" && mpfitParameters->parameters.count(parameterName) != 0) {
                if ((value = getXMLValue(parameter, "Initial")) != NULL) mpfitParameters->parameters[parameterName]->estimate->setText(value);
                if ((value = getXMLValue(parameter, "Constrained")) != NULL) if (value[0] == '1') mpfitParameters->parameters[parameterName]->estimate->constrained->click();
                if ((value = getXMLValue(parameter, "ConstrainMinimum")) != NULL) mpfitParameters->parameters[parameterName]->estimate->lowerBound->setText(value);
                if ((value = getXMLValue(parameter, "ConstrainMaximum")) != NULL) mpfitParameters->parameters[parameterName]->estimate->upperBound->setText(value);
                if ((value = getXMLValue(parameter, "Fixed")) != NULL) if (value[0] == '1') mpfitParameters->parameters[parameterName]->estimate->fixed->click();
            }
        }
    }

    free(newinput);

    QFileInfo finfo(filename);
    settings.lastOpenPath = finfo.absoluteDir().path();
    settings.lastOpenPath.replace("\\", "/");
}

int dpMpfitDialog::getBufferData(dpMpfitDataSelectWidget &which, QVector<double> &data) {
    int axis = which.data->currentText() != "<none>" && which.data->currentText() != "";
    dpint64 datasize = 0;
    Fits *fdata = NULL;
    bool isY = ((dpMpfitDataSelectWidget *)&which == y);

    QReadLocker locker(&buffersLock);

    if (axis) {
        std::string index = which.data->currentText().toStdString();
        QFitsSingleBuffer *b = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(index));
        fdata = b->getDpData()->fvalue;
        if (fdata->Naxis(0) == 1 || (fdata->Naxis(0) == 2 && fdata->Naxis(1) == 1)) {
            which.enableSelectors(false);
            datasize = fdata->Nelements();
            axis = fdata->Naxis(0);
        } else if (fdata->Naxis(0) == 2) {
            which.enableSelectors(true);
            if (which.row->isChecked()) {
                which.roworcolumn->setMaximum(fdata->Naxis(2));
                datasize = fdata->Naxis(1);
                axis = 1;
            } else {
                which.roworcolumn->setMaximum(fdata->Naxis(1));
                datasize = fdata->Naxis(2);
                axis = 2;
            }
        } else {
            axis = 0;
        }
    } else {
        which.enableSelectors(false);
    }
    if (datasize > 0) {
        data.resize(datasize);
        for (dpint64 xx = 0; xx < datasize; xx++) {
            dpint64 ii = xx;
            if (fdata->Naxis(0) == 2) {
                if (axis == 1) {
                    ii = fdata->C_I(xx, which.roworcolumn->value()-1);
                } else {
                    ii = fdata->C_I(which.roworcolumn->value()-1, xx);
                }
            }
            data[xx] = fdata->ValueAt(ii);
            if (isY && (!std::isfinite(data[xx]))) hasNAN = true;
        }
        if ((dpMpfitDataSelectWidget *)&which == y) {
            crval = fdata->getCRVAL(axis);
            crpix = fdata->getCRPIX(axis);
            cdelt = fdata->getCDELT(axis);
            logaxis = fdata->isAxisLog(axis);
        }
    } else if ((dpMpfitDataSelectWidget *)&which == x) {
        for (dpint64 xx = 0; xx < data.size(); xx++) {
            data[xx] = pixwave(xx+1, crpix, crval, cdelt, logaxis);
//            data[xx] = crval + ((double)(xx+1) - crpix) * cdelt;
        }
        axis = 1;
    } else {
        data.clear();
    }
    return axis;
}

void dpMpfitDialog::removeBlanks() {
    bool haserrx, haserry;
    haserrx = (xerrd.size() > 0);
    haserry = (yerrd.size() > 0);

    QVector<double>newx, newy, newerrx, newerry;

    int j = 0;
    newx.resize(yd.size());
    newy.resize(yd.size());
    if (haserry) newerry.resize(yd.size());
    if (haserrx) newerrx.resize(yd.size());
    for (int i = 0; i < yd.size(); i++) {
        if (std::isfinite(yd[i])) {
            newy[j] = yd[i];
            newx[j] = xd[i];
            if (haserry) newerry[j] = yerrd[i];
            if (haserrx) newerrx[j] = xerrd[i];
            j++;
        }
    }
    newy.resize(j);
    yd = newy;
    newx.resize(j);
    xd = newx;
    if (haserry) {
        newerry.resize(j);
        yerrd = newerry;
    }
    if (haserrx) {
        newerrx.resize(j);
        xerrd = newerrx;
    }
}

void dpMpfitDialog::somethingChanged() {
    hasNAN = false;
    plotarea->clearGraphs();

    int yaxis = getBufferData(*y, yd);

    if (yaxis < 1) return;

    xd.resize(yd.size());
    getBufferData(*x, xd);

    int yerrsize = getBufferData(*yerr, yerrd);
    int xerrsize = getBufferData(*xerr, xerrd);

    if (hasNAN) removeBlanks();

    plotarea->addGraph();
    plotarea->graph(0)->setData(xd, yd);
    plotarea->graph(0)->setAntialiased(false);
//    plotarea->graph(0)->setLineStyle(QCPGraph::lsStepCenter);
    plotarea->graph(0)->setLineStyle(lineStyle);
    if (lineStyle == QCPGraph::lsNone)
        plotarea->graph(0)->setScatterStyle(QCPScatterStyle(scatterStyle, 4));
    if (yerrsize > 0) {
        QCPErrorBars *yerrorBars = new QCPErrorBars(plotarea->xAxis, plotarea->yAxis);
        yerrorBars->removeFromLegend();
        yerrorBars->setAntialiased(false);
        yerrorBars->setDataPlottable(plotarea->graph(0));
        yerrorBars->setPen(QPen(QColor(180,180,180)));
        yerrorBars->setData(yerrd);
    }
    if (xerrsize > 0) {
        QCPErrorBars *xerrorBars = new QCPErrorBars(plotarea->xAxis, plotarea->yAxis);
        xerrorBars->setErrorType(QCPErrorBars::etKeyError);
        xerrorBars->removeFromLegend();
        xerrorBars->setAntialiased(false);
        xerrorBars->setDataPlottable(plotarea->graph(0));
        xerrorBars->setPen(QPen(QColor(180,180,180)));
        xerrorBars->setData(xerrd);
    }
    evaluateFitFunction();
    plotarea->xAxis->rescale();
    plotarea->yAxis->rescale();
    plotarea->replot();
}

void dpMpfitDialog::copyfit() {
    double flux;
    double fluxerr;
    double a, fwhm, da, dfwhm;
    QString text;

    text = "QFitsView MPFIT\n===============\n";
    text += "Fit function: " + mpfitParameters->fitfunction->toPlainText() + "\n";
    text += "Constrain in x: ";
    if (mpfitParameters->constrainX->isChecked()) text += mpfitParameters->minx->text() + "..." + mpfitParameters->maxx->text() + "\n";
    else text += "no\n";

    dpStringList variableList = mpfit_sorted_variables;
    text += "Variable,value,error\n";
    for (int i = 0; i < variableList.size(); i++) {
//        QString error = mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->resulterr->text();
//        error = error.right(error.size() - 4);
//        text += mpfitParameters->parameterNames.at(i) + "," + mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->result->text() + "," + error + "\n";
        text += mpfitParameters->parameterNames.at(i) + "," + mpfitParameters->parameters.value(mpfitParameters->parameterNames.at(i))->fitresult->text() + "\n";
    }

    QApplication::clipboard()->setText(text);
}

void dpMpfitDialog::mouseClicked(QMouseEvent *m) {
    if (m->button() == Qt::RightButton) {
        rightPopupMenu->exec(m->globalPos());
    }
}

void dpMpfitDialog::setPlotStyleLines() {
    lineStyle = QCPGraph::lsLine;
    somethingChanged();
}

void dpMpfitDialog::setPlotStyleCross() {
    lineStyle = QCPGraph::lsNone;
    somethingChanged();
}

dpMpfitConfiguration::dpMpfitConfiguration(QWidget *parent) : QDialog(parent) {
    mainLayout = new QVBoxLayout;

    setLayout(mainLayout);

    QLabel *l_ftol = new QLabel("Relative chi-square convergence criterium", this);
    ftol = new dpDoubleEdit("1e-10", this);
    QLabel *l_xtol = new QLabel("Relative parameter convergence criterium", this);
    xtol = new dpDoubleEdit("1e-10", this);
    QLabel *l_gtol = new QLabel("Orthogonality convergence criterium", this);
    gtol = new dpDoubleEdit("1e-10", this);
    QLabel *l_epsfcn = new QLabel("Finite derivative step size", this);
    epsfcn = new dpDoubleEdit("", this);
    epsfcn->setText(QString::number(MP_MACHEP0));
    QLabel *l_stepfactor = new QLabel("Initial step bound", this);
    stepfactor = new dpDoubleEdit("100.0", this);
    QLabel *l_covtol = new QLabel("Range tolerance for covariance calculation", this);
    covtol = new dpDoubleEdit("1e-14", this);
    QLabel *l_maxiter = new QLabel("Maximum number of iterations", this);
    maxiter = new QSpinBox(this);
    maxiter->setMinimum(1);
    maxiter->setMaximum(INT_MAX);
    maxiter->setValue(200);
    QLabel *l_maxfev = new QLabel("Maximum number of function evaluations, or 0 for no limit", this);
    maxfev = new QSpinBox(this);
    maxfev->setMinimum(0);
    maxfev->setMaximum(INT_MAX);
    maxfev->setValue(0);

    QGridLayout *gridLayout = new QGridLayout();
    gridLayout->addWidget(l_ftol, 0, 0);
    gridLayout->addWidget(ftol, 0, 1);
    gridLayout->addWidget(l_xtol, 1, 0);
    gridLayout->addWidget(xtol, 1, 1);
    gridLayout->addWidget(l_gtol, 2, 0);
    gridLayout->addWidget(gtol, 2, 1);
    gridLayout->addWidget(l_epsfcn, 3, 0);
    gridLayout->addWidget(epsfcn, 3, 1);
    gridLayout->addWidget(l_stepfactor, 4, 0);
    gridLayout->addWidget(stepfactor, 4, 1);
    gridLayout->addWidget(l_covtol, 5, 0);
    gridLayout->addWidget(covtol, 5, 1);
    gridLayout->addWidget(l_maxiter, 6, 0);
    gridLayout->addWidget(maxiter, 6, 1);
    gridLayout->addWidget(l_maxfev, 7, 0);
    gridLayout->addWidget(maxfev, 7, 1);

    mainLayout->addLayout(gridLayout);

    QPushButton *buttonOk = new QPushButton("Ok");
    buttonOk->setDefault(true);
    QPushButton *buttonCancel = new QPushButton("Cancel");
    QPushButton *buttonHelp = new QPushButton("Help");

    QGridLayout *buttonLayout = new QGridLayout();
    buttonLayout->addWidget(buttonHelp, 0, 0);
    buttonLayout->addWidget(buttonCancel, 0, 2);
    buttonLayout->addWidget(buttonOk, 0, 3);

    mainLayout->addLayout(buttonLayout);

    connect(buttonOk, SIGNAL(clicked()),
            this, SLOT(accepted()));
    connect(buttonCancel, SIGNAL(clicked()),
            this, SLOT(reject()));
    connect(buttonHelp, SIGNAL(clicked()),
            this, SLOT(showHelp()));

    adjustSize();
    setFixedSize(size());
}

void dpMpfitConfiguration::accepted() {
    dpMpfitConfig.covtol = covtol->value();
    dpMpfitConfig.epsfcn = epsfcn->value();
    dpMpfitConfig.ftol = ftol->value();
    dpMpfitConfig.gtol = gtol->value();
    dpMpfitConfig.maxfev = maxfev->value();
    dpMpfitConfig.maxiter = maxiter->value();
    dpMpfitConfig.stepfactor = stepfactor->value();
    dpMpfitConfig.xtol = xtol->value();

    accept();
}

void dpMpfitConfiguration::showHelp() {
    QString helpText = "For general information on MPFIT, please visit <a href=\"https://www.physics.wisc.edu/~craigm/idl/cmpfit.html\">the mpfit webpage</a>.<br>";
    helpText += "<br><i>Available operators</i>: <b>+&nbsp;-&nbsp;*&nbsp;/&nbsp;^&nbsp;(&nbsp;)</b><br>";
    helpText += "<br><i>Available functions</i>:<br>sin(x) cos(x) tan(x) sinh(x) cosh(x) tanh(x) ";
    helpText += "asin(x) acos(x) atan(x) atan(x1,x2) asinh(x) acosh(x) atanh(x) ";
    helpText += "exp(x) log(x) ln(x) sqrt(x) erf(x) bessel(x1,kind,order) ";
    helpText += "int(x) round(x) frac(x) abs(x) gauss(x,fwhm)";

    QMessageBox::information(this, "MPFIT help", helpText);
}

dpWatchdirDialog::dpWatchdirDialog(QWidget *parent) : QDialog(parent) {
    setWindowTitle("Watch directory");

    QLabel *commandLabel = new QLabel("DPUSER command to execute ($$ denotes the filename as string)", this);
    commandLabel->adjustSize();
    int w = commandLabel->width();

    QLabel *pathLabel = new QLabel("Path to monitor (leave empty to disable the watcher)", this);
    pathLabel->adjustSize();
    pathLabel->move(10, 10);
    path = new QLineEdit(this);
    path->adjustSize();
    path->setGeometry(10, pathLabel->y() + pathLabel->height() + 10, w-path->height(), path->height());
    pathButton = new QPushButton(QPixmap(fileopen), "", this);
    pathButton->setGeometry(w-path->height()+10, path->y(), path->height(), path->height());

    QLabel *filterLabel = new QLabel("Filename filter", this);
    filterLabel->adjustSize();
    filterLabel->move(10, path->y() + path->height() + 10);
    filter = new QLineEdit(this);
    filter->setText("*.fits");
    filter->setGeometry(10, filterLabel->y() + filterLabel->height() + 10, w, path->height());

    commandLabel->move(10, filter->y() + filter->height() + 10);
    command = new QLineEdit(this);
    command->setText("hhh = readfits($$)");
    command->setGeometry(10, commandLabel->y() + commandLabel->height() + 10, w, path->height());

    sleepButton = new QCheckBox("Sleep [seconds] before executing script", this);
    sleepButton->move(10, command->y() + command->height() + 10);
    sleepButton->adjustSize();
    sleepButton->setChecked(true);
    sleepSeconds = new QSpinBox(this);
    sleepSeconds->setMinimum(1);
    sleepSeconds->setMaximum(60);
    sleepSeconds->adjustSize();
    sleepSeconds->move(sleepButton->x() + sleepButton->width() + 10, sleepButton->y());

    adjustSize();

    QPushButton *buttonOk = new QPushButton("Ok", this);
    buttonOk->adjustSize();
    buttonOk->setDefault(true);
    QPushButton *buttonCancel = new QPushButton("Cancel", this);
    buttonCancel->adjustSize();
    QPushButton *buttonHelp = new QPushButton("Help", this);
    buttonHelp->adjustSize();

    buttonOk->setGeometry(10, height() + 10, width() / 4, buttonOk->height());
    buttonCancel->setGeometry(buttonOk->x() + buttonOk->width() + 10, height() + 10, width() / 4, buttonCancel->height());
    buttonHelp->setGeometry(buttonCancel->x() + buttonCancel->width() + 10, height() + 10, width() / 4, buttonHelp->height());

    connect(sleepButton, SIGNAL(clicked(bool)), sleepSeconds, SLOT(setEnabled(bool)));
    connect(pathButton, SIGNAL(clicked()), this, SLOT(pathButtonClicked()));
    connect(buttonOk, SIGNAL(clicked()), this, SLOT(accepted()));
    connect(buttonCancel, SIGNAL(clicked()), this, SLOT(close()));
    connect(buttonHelp, SIGNAL(clicked()), this, SLOT(helpClicked()));

    setTabOrder(path, filter);
    setTabOrder(filter, command);

    adjustSize();
    setFixedSize(size());
}

void dpWatchdirDialog::updateandshow() {
    QFitsMainWindow *myparent = static_cast<QFitsMainWindow *>(parent());
    if (myparent->fsWatcher.directories().isEmpty()) {
        path->setText("");
        filter->setText("");
        command->setText("");
    } else {
        path->setText(myparent->fsWatcher.directories().first());
        filter->setText(myparent->FileSystemChangedPattern.pattern());
        QString cmd = myparent->FileSystemChangedAction;
        if (cmd.left(6) == "sleep ") cmd.remove(0, cmd.indexOf(";")+1);
        command->setText(cmd);
    }
    show();
}

void dpWatchdirDialog::accepted() {
    QFitsMainWindow *myparent = static_cast<QFitsMainWindow *>(parent());

    QString cmd;
    if (sleepButton->isChecked()) cmd += "sleep " + sleepSeconds->text() + ";";
    cmd += command->text();
    myparent->setDirToWatch(path->text().toStdString(), filter->text().toStdString(), cmd.toStdString());
    hide();
}

void dpWatchdirDialog::pathButtonClicked() {
    QString ppp = path->text();
    if (ppp.isEmpty()) ppp = settings.lastOpenPath;
    QString pathname = QFileDialog::getExistingDirectory(this, "Select Path to watch", ppp);
    if (!pathname.isEmpty()) path->setText(QDir::toNativeSeparators(pathname));
}

void dpWatchdirDialog::helpClicked() {
    QFitsMainWindow *myparent = static_cast<QFitsMainWindow *>(parent());
    myparent->dpuserHelp("procedure_watchdir.html");
}

#ifdef HAS_PLOTTING

QtCategoryButton::QtCategoryButton(const QString& a_Text, QWidget *a_pParent, QWidget *a_pItem)
    : QPushButton(a_Text, a_pParent)
    , m_pItem(a_pItem)
{
    connect(this, SIGNAL(pressed()), this, SLOT(ButtonPressed()));
}

void QtCategoryButton::ButtonPressed()
{
    if (m_pItem->isHidden()) m_pItem->show();
    else m_pItem->hide();
}

dpDataSelectWidget::dpDataSelectWidget(QString label, QWidget *parent) : QWidget(parent) {
    QLabel *l = new QLabel(label, this);
    data = new dpPopup(this);
    data->addItem("<none>", -1);
    data->adjustSize();
    int h = data->height();
    l->setGeometry(0, 0, 50, h);
    data->setGeometry(50, 0, 150, h);
    rangeLabel = new QLabel(" [", this);
    rangeLabel->adjustSize();
    rangeLabel->setGeometry(data->x() + data->width() + 10, 0, rangeLabel->width(), h);
    range = new QLineEdit(this);
    range->setGeometry(rangeLabel->x() + rangeLabel->width() + 5, 0, 100, h);
    rangeExample = new QLabel(this);
    rangeExample->setGeometry(range->x() + range->width() + 5, 0, 100, h);
    x = new QSpinBox(this);
    x->setPrefix("[");
    x->setSpecialValueText("*");
    x->setRange(0, INT_MAX);
    x->setFrame(false);
    x->setGeometry(200, 0, 50, h);
    y = new QSpinBox(this);
    y->setSpecialValueText("*");
    y->setRange(0, INT_MAX);
    y->setFrame(false);
    y->setGeometry(250, 0, 50, h);
    z = new QSpinBox(this);
    z->setSpecialValueText("*");
    z->setRange(0, INT_MAX);
    z->setFrame(false);
    z->setGeometry(300, 0, 50, h);

    connect(data, SIGNAL(activated(int)), SLOT(dataSelected()));
    connect(range, SIGNAL(textChanged(QString)), SIGNAL(somethingChanged()));
    connect(x, SIGNAL(valueChanged(int)), SLOT(updateRange()));
    connect(y, SIGNAL(valueChanged(int)), SLOT(updateRange()));
    connect(z, SIGNAL(valueChanged(int)), SLOT(updateRange()));

    adjustSize();

    setFixedSize(size());
    dataSelected();
}

void dpDataSelectWidget::dataSelected() {
    std::string index = data->currentText().toStdString();
    rangeLabel->hide();
    range->hide();
    rangeExample->hide();
    QRegularExpression rx;
    x->hide();
    y->hide();
    z->hide();
    n1 = n2 = n3 = 1;
    if (index == "<none>" || index == "") return;
    if (dpuser_vars.count(index) && (dpuser_vars.at(index).type == typeFits)) {
        Fits *f = dpuser_vars.at(index).fvalue;
        naxis = f->Naxis(0);
        switch (f->Naxis(0)) {
            case 3:
                n3 = f->Naxis(3);
                n2 = f->Naxis(2);
                n1 = f->Naxis(1);
                rx.setPattern("\\*,[0-9]+,[0-9]+|[0-9]+,[0-9]+,\\*|[0-9]+,\\*,[0-9]+");
                range->setValidator(new QRegularExpressionValidator(rx));
                range->setEnabled(true);
                range->show();
                range->setToolTip("Enter data range, e.g. " + QString::number(f->Naxis(1) / 2) + "," + QString::number(f->Naxis(2)/2) + ",*");
                rangeExample->setText("] e.g. " + QString::number(f->Naxis(1) / 2) + "," + QString::number(f->Naxis(2)/2) + ",*");
                rangeExample->show();
                rangeLabel->show();
//                z->setMaximum(f->Naxis(3));
//                y->setMaximum(f->Naxis(2));
//                x->setMaximum(f->Naxis(1));
//                z->setValue(0);
//                y->setValue(1);
//                x->setValue(1);
//                y->setPrefix(",");
//                y->setSuffix("");
//                z->setPrefix(",");
//                z->setSuffix("]");
//                x->show();
//                y->show();
//                z->show();
            break;
            case 2:
                n2 = f->Naxis(2);
                n1 = f->Naxis(1);
                rx.setPattern("\\*,[0-9]+|[0-9]+,\\*");
                range->setValidator(new QRegularExpressionValidator(rx));
                range->setEnabled(true);
                range->show();
                range->setToolTip("Enter data range, e.g. " + QString::number(f->Naxis(1) / 2) + ",*");
                rangeExample->setText("] e.g. " + QString::number(f->Naxis(1) / 2) + ",*");
                rangeExample->show();
                rangeLabel->show();
//                y->setMaximum(f->Naxis(2));
//                x->setMaximum(f->Naxis(1));
//                y->setValue(0);
//                x->setValue(1);
//                y->setPrefix(",");
//                y->setSuffix("]");
//                x->show();
//                y->show();
            break;
            case 1:
                rx.setPattern("\\*");
                range->setText("*");
                range->setEnabled(false);
                range->setValidator(new QRegularExpressionValidator(rx));
                range->show();
                range->setToolTip("");
                rangeExample->setText("]");
                rangeExample->show();
                rangeLabel->show();
//                x->setValue(0);
//                x->setMaximum(f->Naxis(1));
//                x->setSuffix("]");
//                x->show();
            break;
            default:
            break;
        }
    }
    emit somethingChanged();
}

bool dpDataSelectWidget::selectedRange(int *xv, int *yv, int *zv) {
    if (!range->hasAcceptableInput()) return false;
    *xv = *yv = *zv = -1;
    QStringList axes = range->text().split(',');
    switch (naxis) {
        case 3:
            if (axes[2] != "*") *zv = axes[2].toInt();
        case 2:
            if (axes[1] != "*") *yv = axes[1].toInt();
            if (axes[0] != "*") *xv = axes[0].toInt();
        break;
        default:
        break;
    }
    if (*xv > n1 || *yv > n2 || *zv > n3) return false;
    return true;
}

void dpDataSelectWidget::updateRange() {
    emit somethingChanged();
}

// utility function to draw scatter point. Same order as plplot symbols
#ifdef HAS_PLPLOT

void drawShape2(int shape, PLINT x, PLINT y, double w) {
    std::vector<PLINT> xpts, ypts;
    switch (shape) {
        case 1: // open rectangle
            xpts = { x-w, x+w, x+w, x-w, x-w };
            ypts = { y-w, y-w, y+w, y+w, y-w };
            plP_draphy_poly(xpts.data(), ypts.data(), 5);
            break;
        case 2: // filled rectangle
            xpts = { x-w, x+w, x+w, x-w, x-w };
            ypts = { y-w, y-w, y+w, y+w, y-w };
            plP_plfclp(xpts.data(), ypts.data(), 5, plsc->clpxmi, plsc->clpxma,
                       plsc->clpymi, plsc->clpyma, plP_fill);
            break;
/*        case 1: // point
            plarc(x, y, w*a, w, 0., 360., 0., true);
            break;
        case 2: // plus sign
            xpts = { x, x };
            ypts = { y-w, y+w };
            plline(2, xpts.data(), ypts.data());
            xpts = { x-w*a, x+w*a };
            ypts = { y, y };
            plline(2, xpts.data(), ypts.data());
            break;
        case 3:
            break;
        case 4: // open circle
            plarc(x, y, w*a, w, 0., 360., 0., false);
            break; */
        default:
            break;
    }

}

double global_plplot_aspect_ratio;

void drawShape(int shape, double x, double y, double w, double a) {
    std::vector<PLFLT> xpts, ypts;
    switch (shape) {
        case 0: // open rectangle
            xpts = { x-w*a, x+w*a, x+w*a, x-w*a, x-w*a };
            ypts = { y-w, y-w, y+w, y+w, y-w };
            plline(5, xpts.data(), ypts.data());
            break;
        case 1: // point
            plarc(x, y, w*a, w, 0., 360., 0., true);
            break;
        case 2: // plus sign
            xpts = { x, x };
            ypts = { y-w, y+w };
            plline(2, xpts.data(), ypts.data());
            xpts = { x-w*a, x+w*a };
            ypts = { y, y };
            plline(2, xpts.data(), ypts.data());
            break;
        case 3:
            break;
        case 4: // open circle
            plarc(x, y, w*a, w, 0., 360., 0., false);
            break;
        default:
            break;
    }
}

#endif /* HAS_PLPLOT */

dpLinePlotDialog::dpLinePlotDialog(QString title, QWidget *parent) : QWidget(parent) {
    xdata = ydata = xerrdata = yerrdata = NULL;
    series = new QLineEdit(title, this);
    x = new dpDataSelectWidget("x-axis", this);
    y = new dpDataSelectWidget("y-axis", this);
    xerr = new dpDataSelectWidget("x error", this);
    yerr = new dpDataSelectWidget("y error", this);

    connect(x, SIGNAL(somethingChanged()), this, SLOT(xchanged()));
    connect(y, SIGNAL(somethingChanged()), this, SLOT(ychanged()));
    connect(xerr, SIGNAL(somethingChanged()), this, SIGNAL(somethingChanged()));
    connect(yerr, SIGNAL(somethingChanged()), this, SLOT(yerrchanged()));

    QGridLayout *gridLayout = new QGridLayout();
    gridLayout->setVerticalSpacing(0);
    gridLayout->addWidget(new QLabel("Name:"), 0, 0);
    gridLayout->addWidget(series, 0, 1, 1, 7);
    gridLayout->addWidget(x, 1, 0, 1, 7);
    gridLayout->addWidget(y, 2, 0, 1, 7);
    gridLayout->addWidget(xerr, 3, 0, 1, 7);
    gridLayout->addWidget(yerr, 4, 0, 1, 7);

    linestyle = new QComboBox(this);
    int h = linestyle->iconSize().height();
    linestyle->setIconSize(QSize(100, h));
    linestyle->addItem("<none>");
//    linestyle->addItems({"<none>", "solid", "dashed", "dots", "dash-dot", "dash-dot-dot"});

    QVector<Qt::PenStyle> lines;
    lines << Qt::SolidLine;
    lines << Qt::DashLine;
    lines << Qt::DotLine;
    lines << Qt::DashDotLine;
    lines << Qt::DashDotDotLine;

    for(int i=0;i<lines.count();i++)
    {
        // fill QComboBox
        Qt::PenStyle ps = lines.at(i);
        QPixmap pm(100,h);
        QPainter qp(&pm);
        qp.fillRect(0,0,100,h,linestyle->palette().background());
        qp.setPen(QPen(Qt::SolidPattern, 4, ps));
        qp.drawLine(0, h/2-2, 100, h/2-2);
        QIcon   icon = QIcon(pm);
        linestyle->addItem(icon, "");
    }

    linestyle->setCurrentIndex(1);
    linewidth = new QDoubleSpinBox(this);
    linewidth->setValue(1);
    linewidth->setRange(0, 1000);
    linecolor.setRgb(0, 0, 0);
    linecolorButton = new QPushButton("color", this);
    linecolorButton->setStyleSheet("color : black");
    gridLayout->addWidget(new QLabel("Line:"), 5, 0);
    gridLayout->addWidget(linestyle, 5, 1);
    gridLayout->addWidget(new QLabel("Width:"), 5, 2);
    gridLayout->addWidget(linewidth, 5, 3);
    gridLayout->addWidget(linecolorButton, 5, 4);

    symbolstyle = new QComboBox(this);
    symbolstyle->addItem("<none>");

    // from the qcustomplot forum
    QVector<QCPScatterStyle::ScatterShape> shapes;

//    shapes << QCPScatterStyle::ssNone;
    shapes << QCPScatterStyle::ssDot;
    shapes << QCPScatterStyle::ssCross;
    shapes << QCPScatterStyle::ssPlus;
    shapes << QCPScatterStyle::ssCircle;
    shapes << QCPScatterStyle::ssDisc;
    shapes << QCPScatterStyle::ssSquare;
    shapes << QCPScatterStyle::ssDiamond;
    shapes << QCPScatterStyle::ssStar;
    shapes << QCPScatterStyle::ssTriangle;
    shapes << QCPScatterStyle::ssTriangleInverted;
    shapes << QCPScatterStyle::ssCrossSquare;
    shapes << QCPScatterStyle::ssPlusSquare;
    shapes << QCPScatterStyle::ssCrossCircle;
    shapes << QCPScatterStyle::ssPlusCircle;
    shapes << QCPScatterStyle::ssPeace;

    for(int i=0;i<shapes.count();i++)
    {
        // fill QComboBox
        QCPScatterStyle ss = shapes.at(i);
        ss.setSize(20);
        QPixmap pm(25,25);
        QCPPainter qp(&pm);
        qp.fillRect(0,0,25,25,symbolstyle->palette().background());
        ss.applyTo(&qp, QPen(Qt::black, 2));
        ss.drawShape(&qp,12,12);
        QIcon   icon = QIcon(pm);
        symbolstyle->addItem(icon, "");
    }

//    symbolstyle->addItems({"<none>", "H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"});
    symbolsize = new QDoubleSpinBox(this);
    symbolsize->setValue(4);
    symbolsize->setRange(1, 1000);
    symbolwidth = new QDoubleSpinBox(this);
    symbolwidth->setValue(2);
    symbolwidth->setRange(0, 1000);
    symbolcolor.setRgb(0, 0, 0);
    symbolcolorButton = new QPushButton("color", this);
    symbolcolorButton->setStyleSheet("color : black");

    gridLayout->addWidget(new QLabel("Symbol:"), 6, 0);
    gridLayout->addWidget(symbolstyle, 6, 1);
    gridLayout->addWidget(new QLabel("Size:"), 6, 2);
    gridLayout->addWidget(symbolsize, 6, 3);
    gridLayout->addWidget(symbolcolorButton, 6, 4);
    gridLayout->addWidget(new QLabel("Width:"), 7, 2);
    gridLayout->addWidget(symbolwidth, 7, 3);

    connect(linestyle, SIGNAL(activated(int)), SIGNAL(somethingChanged()));
    connect(linewidth, SIGNAL(valueChanged(double)), SIGNAL(somethingChanged()));
    connect(linecolorButton, SIGNAL(clicked(bool)), SLOT(linecolorClicked()));
    connect(symbolcolorButton, SIGNAL(clicked(bool)), SLOT(symbolcolorClicked()));
    connect(symbolstyle, SIGNAL(activated(int)), SIGNAL(somethingChanged()));
    connect(symbolsize, SIGNAL(valueChanged(double)), SIGNAL(somethingChanged()));
    connect(symbolwidth, SIGNAL(valueChanged(double)), SIGNAL(somethingChanged()));

    setLayout(gridLayout);

    y->data->populateAndSelect(fitsMainWindow->getCurrentBufferIndex().c_str());
    y->dataSelected();

    adjustSize();

    setFixedSize(size());
}

void dpLinePlotDialog::addBufferData(QString name, int row, int column) {
    y->data->setCurrentText(name);
    if (row < 1 && column < 1) y->range->setText("*");
    else if (row < 1) y->range->setText("*," + QString::number(column));
    else if (column < 1) y->range->setText(QString::number(row) + ",*");
    else y->range->setText(QString::number(row) + "," + QString::number(column) + ",*");
    ychanged();
}

void dpLinePlotDialog::xchanged() {
    bool validVariable = true;
    std::string index = x->data->currentText().toStdString();
    if (index == "<none>" || index == "") validVariable = false;
    if (validVariable) if (!dpuser_vars.count(index)) validVariable = false;
    if (validVariable) if (dpuser_vars.at(index).type != typeFits) validVariable = false;
    if (!validVariable) {
        if (!ydata) return;
        if (!xdata) xdata = new Fits();
        xdata->create(ydata->Nelements(), 1, R8);

        double crval = ydata->getCRVAL(1);
        double crpix = ydata->getCRPIX(1);
        double cdelt = ydata->getCDELT(1);
        bool logwave = ydata->isAxisLog(1);
        for (int i = 0; i < xdata->Nelements(); i++) xdata->r8data[i] = pixwave(i+1, crpix, crval, cdelt, logwave);
        return;
    }

    if (!xdata) xdata = new Fits();
    Fits *f = dpuser_vars.at(index).fvalue;
    int xr = 1, yr = 1, zr = 1;
    if (x->x->isVisible()) xr = x->x->value();
    if (x->y->isVisible()) yr = x->y->value();
    if (x->z->isVisible()) zr = x->z->value();
    if (xr == 0) xr = -1;
    if (yr == 0) yr = -1;
    if (zr == 0) zr = -1;
    f->extractRange(*xdata, xr, xr, yr, yr, zr, zr);
    xdata->setType(R8);

    emit somethingChanged();
}

void dpLinePlotDialog::ychanged() {
    bool validVariable = true;
    std::string index = y->data->currentText().toStdString();
    if (index == "<none>" || index == "") validVariable = false;
    if (validVariable) if (!dpuser_vars.count(index)) validVariable = false;
    if (validVariable) if (dpuser_vars.at(index).type != typeFits) validVariable = false;
    if (!validVariable) {
        if (ydata) delete ydata;
        ydata = NULL;
        return;
    }

    if (!ydata) ydata = new Fits();
    Fits *f = dpuser_vars.at(index).fvalue;
    int xr = 1, yr = 1, zr = 1;
    if (y->selectedRange(&xr, &yr, &zr)) {
//    if (y->x->isVisible()) xr = y->x->value();
//    if (y->y->isVisible()) yr = y->y->value();
//    if (y->z->isVisible()) zr = y->z->value();
//    if (xr == 0) xr = -1;
//    if (yr == 0) yr = -1;
//    if (zr == 0) zr = -1;
        f->extractRange(*ydata, xr, xr, yr, yr, zr, zr);
        ydata->setType(R8);
        xchanged();

        emit somethingChanged();
    }
}

void dpLinePlotDialog::yerrchanged() {
    bool validVariable = true;
    std::string index = yerr->data->currentText().toStdString();
    if (index == "<none>" || index == "") validVariable = false;
    if (validVariable) if (!dpuser_vars.count(index)) validVariable = false;
    if (validVariable) if (dpuser_vars.at(index).type != typeFits) validVariable = false;
    if (!validVariable) {
        if (yerrdata) delete yerrdata;
        yerrdata = NULL;
        return;
    }

    if (!yerrdata) yerrdata = new Fits();
    Fits *f = dpuser_vars.at(index).fvalue;
    int xr = 1, yr = 1, zr = 1;
    if (yerr->selectedRange(&xr, &yr, &zr)) {
        f->extractRange(*yerrdata, xr, xr, yr, yr, zr, zr);
        yerrdata->setType(R8);
        xchanged();

        emit somethingChanged();
    }
}

void dpLinePlotDialog::linecolorClicked() {
    QColor newlinecolor = QColorDialog::getColor(linecolor, this, "Select Colour", QColorDialog::ShowAlphaChannel);
    if (newlinecolor.isValid()) {
        linecolor = newlinecolor;
        QVariant variant= linecolor;
        QString colcode = variant.toString();
        linecolorButton->setStyleSheet("color :"+colcode);
        emit somethingChanged();
    }
}

void dpLinePlotDialog::symbolcolorClicked() {
    QColor newlinecolor = QColorDialog::getColor(symbolcolor, this, "Select Colour", QColorDialog::ShowAlphaChannel);
    if (newlinecolor.isValid()) {
        symbolcolor = newlinecolor;
        QVariant variant= symbolcolor;
        QString colcode = variant.toString();
        symbolcolorButton->setStyleSheet("color :"+colcode);
        emit somethingChanged();
    }
}

/*
void dpLinePlotDialog::executeCommands() {
    if (!ydata) return;
    std::string yindex = y->data->currentText().toStdString();
    if (yindex == "<none>" || yindex == "") return;

    QString xindex = x->data->currentText();
    if (xindex == "<none>" || xindex == "") {
        if (!xdata) xdata = new Fits();
        xdata->create(ydata->Nelements(), 1, R8);

        double crval = ydata->getCRVAL(1);
        double crpix = ydata->getCRPIX(1);
        double cdelt = ydata->getCDELT(1);
        bool logwave = ydata->isAxisLog(1);
        for (int i = 0; i < xdata->Nelements(); i++) xdata->r8data[i] = pixwave(i+1, crpix, crval, cdelt, logwave);
    }
    if (!xdata) return;
    plscol0a(1, linecolor.red(), linecolor.green(), linecolor.blue(), (double)linecolor.alpha() / 255.);
    plcol0(1);
    if (linestyle->currentIndex() > 0) {
        plwidth(linewidth->value());
        pllsty(linestyle->currentIndex());
        plline(xdata->Nelements(), xdata->r8data, ydata->r8data);
    }
    if (symbolstyle->currentIndex() > 0) {
        plwidth(linewidth->value());
        plschr(0, symbolsize->value());
        double xmin, xmax, ymin, ymax, aspect, scale;
        plgvpd(&xmin, &xmax, &ymin, &ymax);
        aspect = global_plplot_aspect_ratio;
//        aspect = (xmax - xmin) / (ymax - ymin);
        plgvpw(&xmin, &xmax, &ymin, &ymax);
        scale = (xmax - xmin) / (ymax - ymin) * aspect;

//        for (int i = 0; i < xdata->Nelements(); i++) {
//            PLFLT x, y;
//            TRANSFORM(xdata->r8data[i], ydata->r8data[i], &x, &y);
//            dp_output("%i %i\n", plP_wcpcx(x), plP_wcpcy( y ));
////            plsc->dev_hrshsym = 1;
//            drawShape2(symbolstyle->currentIndex(), plP_wcpcx(x), plP_wcpcy( y ), symbolsize->value());
////            plhrsh2(linestyle->currentIndex() - 9, plP_wcpcx(x), plP_wcpcy(y));
////            plsc->dev_hrshsym = 0;
//        }

//        plsc->dev_hrshsym = 1;
//        c_plsym(xdata->Nelements(), xdata->r8data, ydata->r8data, symbolstyle->currentIndex());
        plpoin(xdata->Nelements(), xdata->r8data, ydata->r8data, symbolstyle->currentIndex());
//          plstring(xdata->Nelements(), xdata->r8data, ydata->r8data, series->text().toStdString().c_str()); //linestyle->currentIndex() - 8);
//          plstring(1, xdata->r8data, ydata->r8data, "H");
 //               for (int i = 0; i < xdata->Nelements(); i++) drawShape(symbolstyle->currentIndex(), xdata->r8data[i], ydata->r8data[i], symbolsize->value() / 100., scale);
//        plsc->dev_hrshsym = 0;
    }
}
*/

void dpLinePlotDialog::executeCommands(QCustomPlot *plot) {
    if (!ydata) return;
    std::string yindex = y->data->currentText().toStdString();
    if (yindex == "<none>" || yindex == "") return;

    QString xindex = x->data->currentText();
    if (xindex == "<none>" || xindex == "") {
        if (!xdata) xdata = new Fits();
        xdata->create(ydata->Nelements(), 1, R8);

        double crval = ydata->getCRVAL(1);
        double crpix = ydata->getCRPIX(1);
        double cdelt = ydata->getCDELT(1);
        bool logwave = ydata->isAxisLog(1);
        for (int i = 0; i < xdata->Nelements(); i++) xdata->r8data[i] = pixwave(i+1, crpix, crval, cdelt, logwave);
    }
    if (!xdata) return;

    QVector<double>xx(ydata->Nelements()), yy(ydata->Nelements());
    for (int i = 0; i < ydata->Nelements(); i++) {
        xx[i] = xdata->r8data[i];
        yy[i] = ydata->r8data[i];
    }
    int n;

    if (linestyle->currentIndex() > 0) {
        QPen pen;
        pen.setWidthF(linewidth->value());
        pen.setColor(linecolor);
        pen.setStyle((Qt::PenStyle)linestyle->currentIndex());
        plot->addGraph();
        n = plot->graphCount() - 1;
        plot->graph(n)->setLineStyle(QCPGraph::lsLine);
        plot->graph(n)->setPen(pen);
        plot->graph(n)->setAntialiased(true);
        plot->graph(n)->setData(xx, yy, true);
    }
    if (symbolstyle->currentIndex() > 0) {
        QPen pen;
        pen.setWidthF(symbolwidth->value());
        pen.setColor(symbolcolor);
        plot->addGraph();
        n = plot->graphCount() - 1;
        plot->graph(n)->setLineStyle(QCPGraph::lsNone);
        if (false) {
//        if (symbolstyle->currentIndex() == 3) {
            double x = 0., y = 0.;
            double w = symbolsize->value() / 6.;
            double pw = symbolwidth->value() / 4.;
            QPointF lineArray[12] = {QPointF(x-w,   y-pw),
                                    QPointF(x-pw, y-pw),
                                    QPointF(x-pw, y-w),
                                    QPointF(x+pw, y-w),
                                    QPointF(x+pw, y-pw),
                                    QPointF(x+w, y-pw),
                                    QPointF(x+w, y+pw),
                                    QPointF(x+pw, y+pw),
                                    QPointF(x+pw, y+w),
                                    QPointF(x-pw, y+w),
                                    QPointF(x-pw, y+pw),
                                    QPointF(x-w, y+pw)};

            QPainterPath pa(lineArray[0]);
            for (int i = 1; i < 12; i++) pa.lineTo(lineArray[i]);
//            pa.lineTo(lineArray[0]);

            QCPScatterStyle st(pa, Qt::NoPen, QBrush(pen.color()), symbolsize->value() * 6.);
            plot->graph(n)->setPen(Qt::NoPen);
            plot->graph(n)->setScatterStyle(st);
        } else {
            plot->graph(n)->setPen(pen);
            plot->graph(n)->setScatterStyle(QCPScatterStyle((QCPScatterStyle::ScatterShape)symbolstyle->currentIndex(), symbolsize->value()*2.));
        }
        plot->graph(n)->setAntialiased(true);
        plot->graph(n)->setData(xx, yy, true);
    }

    if (yerrdata) {
        QVector<double>yerr(yerrdata->Nelements());
        for (int i = 0; i < yerrdata->Nelements(); i++) yerr[i] = yerrdata->r8data[i];
        QCPErrorBars *yerrorBars = new QCPErrorBars(plot->xAxis, plot->yAxis);
        yerrorBars->setErrorType(QCPErrorBars::etValueError);
        yerrorBars->removeFromLegend();
        yerrorBars->setAntialiased(false);
        yerrorBars->setDataPlottable(plot->graph(n));
        yerrorBars->setPen(QPen(QColor(180,180,180)));
        yerrorBars->setData(yerr);
    }
}

QStringList dpLinePlotDialog::plotCommands() {
    QStringList rv;
    QString cmd;

    if (!ydata) return rv;
    QString yindex = y->data->currentText();
    if (yindex == "<none>" || yindex == "") return rv;

    if (linestyle->currentIndex() > 0) {
        rv += "plwidth " + linewidth->text();
        rv += "plscol0 1," + QString::number(linecolor.red()) + ", " + QString::number(linecolor.green()) + ", " + QString::number(linecolor.blue()) + ", " + QString::number(linecolor.alpha() / 255.);
        rv += "plcol0, 1";
        rv += "pllsty " + QString::number(linestyle->currentIndex());
        cmd = "plline " + QString::number(ydata->Nelements()) + ", ";
        QString xindex = x->data->currentText();
        if (xindex == "<none>" || xindex == "") {
            cmd += "[1:" + QString::number(ydata->Nelements()) + "], ";
        } else {
            cmd += xindex + "[" + x->range->text() + "], ";
        }
        cmd += yindex + "[" + y->range->text() + "]";
        rv += cmd;
    }
    if (symbolstyle->currentIndex() > 0) {
        rv += "plwidth " + symbolwidth->text();
        rv += "plscol0 1," + QString::number(symbolcolor.red()) + ", " + QString::number(symbolcolor.green()) + ", " + QString::number(symbolcolor.blue()) + ", " + QString::number(symbolcolor.alpha() / 255.);
        rv += "plcol0, 1";
        rv += "pllsty 1";
        cmd = "plpoin " + QString::number(ydata->Nelements()) + ", ";
        QString xindex = x->data->currentText();
        if (xindex == "<none>" || xindex == "") {
            cmd += "[1:" + QString::number(ydata->Nelements()) + "], ";
        } else {
            cmd += xindex + "[" + x->range->text() + "], ";
        }
        cmd += yindex + "[" + y->range->text() + "], ";
        cmd += QString::number(symbolstyle->currentIndex());
        rv += cmd;
    }

    return rv;
}

dpPlotConfiguration::dpPlotConfiguration(QWidget *parent) : QWidget(parent) {
    QGridLayout *layout = new QGridLayout();
    layout->setVerticalSpacing(0);

    title = new QLineEdit(this);
    titleFont = new QFontComboBox(this);
    xtitle = new QLineEdit(this);
    ytitle = new QLineEdit(this);
    xmin = new QDoubleSpinBox(this);
    xmin->setFixedWidth(xmin->width());
    xmin->setRange(-DBL_MAX, DBL_MAX);
    xmin->setValue(0.0);
    xmin->setDecimals(20);
    xmin->setEnabled(false);
    xmax = new QDoubleSpinBox(this);
    xmax->setFixedWidth(xmin->width());
    xmax->setRange(-DBL_MAX, DBL_MAX);
    xmax->setValue(1.0);
    xmax->setDecimals(20);
    xmax->setEnabled(false);
    ymin = new QDoubleSpinBox(this);
    ymin->setFixedWidth(xmin->width());
    ymin->setRange(-DBL_MAX, DBL_MAX);
    ymin->setValue(0.0);
    ymin->setDecimals(20);
    ymin->setEnabled(false);
    ymax = new QDoubleSpinBox(this);
    ymax->setFixedWidth(xmin->width());
    ymax->setRange(-DBL_MAX, DBL_MAX);
    ymax->setValue(1.0);
    ymax->setDecimals(20);
    ymax->setEnabled(false);
    xauto = new QCheckBox("auto", this);
    xauto->setChecked(true);
    yauto = new QCheckBox("auto", this);
    yauto->setChecked(true);
    aspect = new QDoubleSpinBox(this);
    aspect->setMinimum(0.05);
    aspect->setMaximum(5.0);
    aspect->setValue(0.75);
    aspect->setSingleStep(0.05);

    layout->addWidget(new QLabel("Title:", this), 0, 0);
    layout->addWidget(title, 0, 1, 1, 8);
    layout->addWidget(new QLabel("Font:", this), 1, 1);
    layout->addWidget(titleFont, 1, 2, 1, 7);
    layout->addWidget(new QLabel("X Label:", this), 2, 0);
    layout->addWidget(xtitle, 2, 1, 1, 10);
    layout->addWidget(new QLabel("Limits:", this), 3, 0);
    layout->addWidget(xmin, 3, 1);
    layout->addWidget(xmax, 3, 2);
    layout->addWidget(xauto, 3, 3);
    layout->addWidget(new QLabel("Y Label", this), 4, 0);
    layout->addWidget(ytitle, 4, 1, 1, 10);
    layout->addWidget(new QLabel("Limits:", this), 5, 0);
    layout->addWidget(ymin, 5, 1);
    layout->addWidget(ymax, 5, 2);
    layout->addWidget(yauto, 5, 3);
    layout->addWidget(new QLabel("Aspect Ratio", this), 6, 0);
    layout->addWidget(aspect, 6, 1);

    connect(title, SIGNAL(textChanged(QString)), SIGNAL(somethingChanged()));
    connect(xtitle, SIGNAL(textChanged(QString)), SIGNAL(somethingChanged()));
    connect(ytitle, SIGNAL(textChanged(QString)), SIGNAL(somethingChanged()));
    connect(xmin, SIGNAL(valueChanged(double)), SIGNAL(somethingChanged()));
    connect(xmax, SIGNAL(valueChanged(double)), SIGNAL(somethingChanged()));
    connect(xauto, SIGNAL(clicked(bool)), xmin, SLOT(setDisabled(bool)));
    connect(xauto, SIGNAL(clicked(bool)), xmax, SLOT(setDisabled(bool)));
    connect(xauto, SIGNAL(clicked(bool)), SIGNAL(somethingChanged()));
    connect(ymin, SIGNAL(valueChanged(double)), SIGNAL(somethingChanged()));
    connect(ymax, SIGNAL(valueChanged(double)), SIGNAL(somethingChanged()));
    connect(yauto, SIGNAL(clicked(bool)), ymin, SLOT(setDisabled(bool)));
    connect(yauto, SIGNAL(clicked(bool)), ymax, SLOT(setDisabled(bool)));
    connect(yauto, SIGNAL(clicked(bool)), SIGNAL(somethingChanged()));
//    connect(aspect, SIGNAL(valueChanged(double)), SIGNAL(somethingChanged()));

    connect(titleFont, SIGNAL(currentFontChanged(QFont)), SIGNAL(somethingChanged()));

    setLayout(layout);

    adjustSize();
    setFixedSize(size());
};

QStringList dpPlotConfiguration::plotCommands() {
    QStringList cmds;
    cmds += "plstart \"\", 1, 1";
    cmds += "plscolbg 255, 255, 255";
    cmds += "plscol0 0, 255, 255, 255";
    cmds += "pladv 0";
    cmds += "plscol0 1, 0, 0, 0";
    cmds += "plcol0 1";
    cmds += "plclear";
    cmds += "plsvpa 0., 1., 0., 1.";
    cmds += "plvasp " + QString::number(aspect->value());
    cmds += "plwind " + QString::number(xmin->value()) + ", " + QString::number(xmax->value()) + ", " + QString::number(ymin->value()) + ", " + QString::number(ymax->value());
    cmds += "plwidth 0";
    cmds += "pllsty 1";
    cmds += "plbox \"bcfnst\", 0, 0, \"bcfnst\", 0, 0";
    cmds += "pllab \"" + xtitle->text() + "\", \"" + ytitle->text() + "\", \"" + title->text() + "\"";

    return cmds;
}

/*@
void dpPlotConfiguration::executeCommands() {
    plscolbg(255, 255, 255);
    plscol0(0, 255, 255, 255);
    pladv(0);
    plscol0(1, 0, 0, 0);
    plcol0(1);
    plsvpa(0., 1., 0., 1.);
    plvasp(aspect->value());
    plwind(xmin->value(), xmax->value(), ymin->value(), ymax->value());
    plwidth(0);
    pllsty(1);
    plbox("bcfnst", 0, 0, "bcfnst", 0, 0);
    pllab(xtitle->text().toStdString().c_str(), ytitle->text().toStdString().c_str(), title->text().toStdString().c_str());
    global_plplot_aspect_ratio = aspect->value();
//    plenv(xmin->value(), xmax->value(), ymin->value(), ymax->value(), -1, 0);
}
*/

void dpPlotConfiguration::executeCommands(QCustomPlot *plot) {
    plot->clearGraphs();

    if (QCPTextElement *plotTitle = dynamic_cast<QCPTextElement *>(plot->plotLayout()->elementAt(0))) {
        plotTitle->setText(title->text().toUtf8());
    }

    plot->xAxis->grid()->setVisible(false);
    plot->yAxis->grid()->setVisible(false);
    plot->xAxis2->setVisible(true);
    plot->xAxis2->setTickLabels(false);
    plot->yAxis2->setVisible(true);
    plot->yAxis2->setTickLabels(false);
    plot->xAxis->setRange(xmin->value(), xmax->value());
    plot->yAxis->setRange(ymin->value(), ymax->value());
    plot->xAxis2->setRange(xmin->value(), xmax->value());
    plot->yAxis2->setRange(ymin->value(), ymax->value());
    plot->xAxis->setLabel(xtitle->text());
    plot->yAxis->setLabel(ytitle->text());
//    QPen pen;
//    pen.setWidth(5);
//    plot->xAxis->setBasePen(pen);
}


dpPlplotWindow::dpPlplotWindow(QWidget *parent) : QMainWindow(parent) {
//    setWindowIcon(QPixmap((const char **)telescope_xpm));
    setWindowTitle("QFitsView - PlPlot");

//    menuBar = new QMenuBar(this);
    QMenu * plotMenu = menuBar()->addMenu( "Plot" );
    plotMenu->addAction("Add", this, SLOT(add()));
    plotMenu->addAction( "Copy", this, SLOT(copy()) );
    plotMenu->addAction( "Save as...", this, SLOT(saveAs()));
    plotMenu->addAction("Generate plplot commands", this, SLOT(generatePlplotCommands()));

    layoutWidget = new QWidget(this);
    configlayout = new QGridLayout();
    configlayout->setVerticalSpacing(0);
    configlayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);

    config = new dpPlotConfiguration(layoutWidget);

    configlayout->addWidget(new QtCategoryButton("Axis Configuration", this, config), 0, 0);
    configlayout->addWidget(config, 1, 0);

    layoutWidget->setLayout(configlayout);
    layoutWidget->adjustSize();

    rightWidget = new QScrollArea(this);
    rightWidget->setWidget(layoutWidget);
    rightWidget->resize(layoutWidget->size().width() + rightWidget->verticalScrollBar()->sizeHint().width(), layoutWidget->size().height());

    /*@
    plot = new QtExtWidget(QT_DEFAULT_X, QT_DEFAULT_Y, this);
    plsetopt("bg", "FFFFFF");
    plmkstrm( &strm );
    plsdev( "extqt" );

    plsetqtdev( plot );


    plinit();
    */
    plot = new QCustomPlot(this);
    plot->plotLayout()->insertRow(0);

    plotTitle = new QCPTextElement(plot, "", config->titleFont->currentFont());

    plot->plotLayout()->addElement(0, 0, plotTitle);
    resize(1000, 600);

//@    replot();

    connect(config, SIGNAL(somethingChanged()), this, SLOT(dataChanged()));
    connect(config->aspect, SIGNAL(valueChanged(double)), this, SLOT(setAspectRatio(double)));
    connect(config->titleFont, SIGNAL(currentFontChanged(QFont)), this, SLOT(setTitleFont(QFont)));

    plot->setAntialiasedElements(QCP::aeAll);
}

dpPlplotWindow::~dpPlplotWindow() {
//@    plend();
}

void dpPlplotWindow::add() {
    QString title = "Series ";
    title += QString::number(plots.size() + 1);
    dpLinePlotDialog *newdialog = new dpLinePlotDialog(title, this);
    configlayout->addWidget(new QtCategoryButton(title, this, newdialog), configlayout->rowCount(), 0);
    configlayout->addWidget(newdialog, configlayout->rowCount(), 0);
    layoutWidget->resize(layoutWidget->width(), layoutWidget->height()
                         + configlayout->itemAt(configlayout->rowCount() - 1)->geometry().height()
                        + configlayout->itemAt(configlayout->rowCount() - 2)->geometry().height());
    rightWidget->verticalScrollBar()->setValue(rightWidget->verticalScrollBar()->maximum());
    plots.push_back(newdialog);
    connect(newdialog, SIGNAL(somethingChanged()), this, SLOT(dataChanged()));
    newdialog->show();
    newdialog->ychanged();
//    dataChanged();
}

void dpPlplotWindow::addBufferData(QString buffer, int row, int column) {
    add();
    dpLinePlotDialog *p = plots.at(plots.size()-1);
    p->addBufferData(buffer, row, column);
    show();
    raise();
}

void dpPlplotWindow::resizeEvent(QResizeEvent *r) {
    setAspectRatio(config->aspect->value());

//    plot->setGeometry(0, menuBar()->height(), width() - rightWidget->width(), height() - menuBar()->height());
    rightWidget->setGeometry(width() - rightWidget->width(), menuBar()->height(), rightWidget->width(), height() - menuBar()->height());
}

void dpPlplotWindow::replot() {
//@    plot->clearWidget();
//@    plsstrm(strm);
    config->executeCommands(plot);
    for (auto series:plots) series->executeCommands(plot);
    plot->replot();
//@    update();
//    QStringList script;
//    script += config->plotCommands();
//    for (auto series:plots) script += series->plotCommands();
//    script += "plreplot";
//    if (dpuser_widget) dpuser_widget->executeScript(script);
}

void dpPlplotWindow::dataChanged() {
    if (config->xauto->isChecked()) {
        double xmin, xmax;
        int i = 0;
        for (auto series:plots) {
            double xmi, xma;
            if (series->xdata) series->xdata->get_minmax(&xmi, &xma);
            if (i == 0) {
                xmin = xmi;
                xmax = xma;
            } else {
                if (xmi < xmin) xmin = xmi;
                if (xma > xmax) xmax = xma;
            }
            i++;
        }
        config->xmin->setValue(xmin);
        config->xmax->setValue(xmax);
    }
    if (config->yauto->isChecked()) {
        double ymin, ymax;
        int i = 0;
        for (auto series:plots) {
            double ymi, yma;
            if (series->ydata) series->ydata->get_minmax(&ymi, &yma);
            if (i == 0) {
                ymin = ymi;
                ymax = yma;
            } else {
                if (ymi < ymin) ymin = ymi;
                if (yma > ymax) ymax = yma;
            }
            i++;
        }
        config->ymin->setValue(ymin);
        config->ymax->setValue(ymax);
    }
    setAspectRatio(0.0);
    setAspectRatio(0.0); // twice so aspect ratio gets updated correctly...
}

void dpPlplotWindow::setAspectRatio(double ratio) {
    int xmargin = plot->plotLayout()->outerRect().width() - plot->yAxis->axisRect()->width();
    int ymargin = plot->plotLayout()->outerRect().height() - plot->xAxis->axisRect()->height();
    int maxw = width() - rightWidget->width() - xmargin;
    int maxh = height() - menuBar()->height() - ymargin;
    int h = maxw * config->aspect->value();
    int w;
    if (h <= maxh) w = maxw;
    else {
        w = maxh / config->aspect->value();
        h = maxh;
    }
    plot->setGeometry((maxw-w)/2, (maxh-h)/2 + menuBar()->height(), w + xmargin, h + ymargin);

    replot();
}

void dpPlplotWindow::setTitleFont(QFont newFont) {
    plotTitle->setFont(newFont);
    setAspectRatio(0.0);
    setAspectRatio(0.0); // twice so aspect ratio gets updated correctly...
}

static QString selectedFilter;

void dpPlplotWindow::saveAs() {
#ifdef HAS_PLPLOT
    QString filters = "*.pdf;;*.png;;*.jpg;;*.bmp";

    QString filename = QFileDialog::getSaveFileName(this,
                                                    "QFitsView - Save plot",
                                                    settings.lastSavePath,
                                                    filters,
                                                    &selectedFilter);

//    QString fname = getSaveImageFilename(&selectedFilter);
    if (!filename.isEmpty()) {
        if (selectedFilter == "*.pdf") plot->savePdf(filename);
        else plot->saveRastered(filename, 0, 0, 1., NULL);
    }
    return;
 //   QString filters = "*.gif;;

    const char **menustr, **devname;
    menustr = (const char **)malloc(100 * sizeof(char *));
    devname = (const char **)malloc(100 * sizeof(char *));
    int n = 100;
//    for (int i = 0; i < n; i++) {
//        menustr[i] = NULL;
//        devname[i] = NULL;
//    }
    plgFileDevs(&menustr, &devname, &n);

    filters = "";

    int i;
    for (i = 0; i < n-1; i++) {
        filters += QString(menustr[i]) + " (" + QString(devname[i]) + ");;";
    }
    filters += QString(menustr[i]) + " (" + QString(devname[i]) + ")";

    filename = QFileDialog::getSaveFileName(this,
                                                    "QFitsView - Save plot",
                                                    settings.lastSavePath,
                                                    filters,
                                                    &selectedFilter);

    if (!filename.isEmpty()) {
        QString device = selectedFilter.mid(selectedFilter.lastIndexOf('(')+1, selectedFilter.lastIndexOf(')')-selectedFilter.lastIndexOf('(')-1);

        PLINT cur_strm, new_strm;
        plgstrm( &cur_strm );    // get current stream
        plmkstrm( &new_strm );   // create a new one

        plsfnam(filename.toStdString().c_str());       // file name
        plsdev(device.toStdString().c_str());         // device type
        plsetopt("-geometry", (QString("1024x") + QString::number(int(1024.0 * config->aspect->value()))).toStdString().c_str());
        plscol0(0, 255, 255, 255);
        plinit();
//        plcpstrm(cur_strm, false);
//        plreplot();
        config->executeCommands(plot);
        for (auto series:plots) series->executeCommands(plot);
        pleop();

        plsstrm( cur_strm );     // return to previous stream
    }
/*
    PLINT strm2;
    plmkstrm(&strm2);
    plsfnam("/tmp/hhh.bmp");
    plsdev("jpgqt");
    plinit();
    pladv(0);
    plcpstrm(strm, false);
    plsstrm(strm2);
    plreplot();
//    pleop();
    plend1();
    plsstrm(strm);
    */

    free(menustr);
    free(devname);
#endif /* HAS_PLPLOT */
}

void dpPlplotWindow::generatePlplotCommands() {
    QStringList commands;
    commands += config->plotCommands();
    for (auto series:plots) commands += series->plotCommands();

    QApplication::clipboard()->setText(commands.join("\n"));
}

void dpPlplotWindow::copy() {

}

#endif /* HAS_PLOTTING */

#ifdef HAS_PLPLOT

dpPg2PlplotWindow::dpPg2PlplotWindow(int pgid) : QMainWindow() {
    setWindowIcon(QPixmap((const char **)telescope_xpm));
    setWindowTitle("QFitsView - PgPlot " + QString::number(pgid + 1));

    QMenu * plotMenu = menuBar()->addMenu( "Plot" );
    plotMenu->addAction( "Copy", this, SLOT(copy()) );
    plotMenu->addAction( "Save as...", this, SLOT(saveAs()));

    plot = new QtExtWidget(QT_DEFAULT_X, QT_DEFAULT_Y, this);
    plmkstrm( &strm );
    plsdev( "extqt" );

    plsetqtdev( plot );

    plinit();

    resize(800, 600);
}

dpPg2PlplotWindow::~dpPg2PlplotWindow() {
    plsstrm(strm);
    plend1();
}

void dpPg2PlplotWindow::resizeEvent(QResizeEvent *r) {
    plot->setGeometry(0, menuBar()->height(), width(), height() - menuBar()->height());
}

void dpPg2PlplotWindow::saveAs() {
    PLINT cur_strm, new_strm;
    plgstrm( &cur_strm );    // get current stream
    plmkstrm( &new_strm );   // create a new one

    plsfnam("/tmp/hhh.jpg");       // file name
    plsdev( "jpgqt" );         // device type

    plstar(1, 1);
    plcpstrm( cur_strm, 0 ); // copy old stream parameters to new stream
    plreplot();              // do the save by replaying the plot buffer
//    plenv(0., 10., 0., 10., 0, 0);
//    plend1();                // finish the device
    pleop();

    plsstrm( cur_strm );     // return to previous stream
}

void dpPg2PlplotWindow::copy() {
    QApplication::clipboard()->setPixmap(plot->grab());
}

#endif /* HAS_PLPLOT */
