#include <QPainter>
#include <QClipboard>
#include <QMessageBox>
#include <QDir>
#include <QPushButton>
#include <QButtonGroup>
#include <QCursor>
#include <QFileInfo>
#include <QApplication>
#include <QDragEnterEvent>
#include <QtPrintSupport/QPrinter>
#include <QtPrintSupport/QPrintDialog>

#include "QFitsMainWindow.h"
#include "QFitsMainView.h"
#include "QFitsSingleBuffer.h"
#include "QFitsMultiBuffer.h"
#include "QFitsWidget2D.h"
#include "QFitsScroller.h"
#include "QFitsCubeSpectrum.h"
#include "QFitsTools.h"
#include "QFitsWidget2D.h"
#include "QFitsView2D.h"
#include "QFitsMarkers.h"
#include "QFitsViewingTools.h"
#include "imred.h"
#include "fits.h"
#include "qtdpuser.h"
#include "dpuser_utils.h"
#include "fitting.h"
#include "QFitsToolBar.h"
#include "dpstringlist.h"
#include "events.h"

extern "C" {
#include "resources/cursor_rotate.xpm"
#include "resources/cursor_select.xpm"
}

QFitsView2D::QFitsView2D(QFitsScroller *parent, QFitsWidget2D* myWidget)
                        : QFitsBaseView(parent), myParent(myWidget)
{
    drawMode = Simple;
    initialZoom = true;

    rightPopupMenu = new QMenu();
    createPopupMenu();

    setFocusPolicy(Qt::StrongFocus);
    setMouseTracking(true);

    mousemode = None;
    distancemode = Pixels;
    setAcceptDrops(true);
//    rightButtonMenuVisible = false;

    connect(this, SIGNAL(statusbartext(const QString &)),
            fitsMainWindow->statusBar(), SLOT(showMessage(const QString &)));

    // SolidLine, DashLine
    // Qt::Dense1Pattern is almost Qt::SolidPattern
    // Qt::Dense7Pattern is almost Qt::NoBrush
    sourceContinuumMarkers = new QFitsMarkers(QColor(0, 200, 0), QColor(0, 255, 0, 40),
                                              QColor(200, 0, 0), QColor(255, 0, 0, 40),
                                              Qt::SolidLine,
                                              Qt::SolidPattern);
}

QFitsView2D::~QFitsView2D() {
    if (sourceContinuumMarkers) {
        delete sourceContinuumMarkers;
        sourceContinuumMarkers = NULL;
    }
    if (rightPopupMenu) {
        delete rightPopupMenu;
        rightPopupMenu = NULL;
    }
}

void QFitsView2D::createPopupMenu() {
    // create actions
    exportSpecAsciiAct = new QAction("Save spectrum as ASCII...", this);
    connect(exportSpecAsciiAct, SIGNAL(triggered()), this, SLOT(exportSpecAsciiSlot()));

    exportSpecFitsAct = new QAction("Save spectrum as FITS...", this);
    connect(exportSpecFitsAct, SIGNAL(triggered()), this, SLOT(exportSpecFitsSlot()));

    copySpecBufferAct = new QAction("Copy spectrum to new buffer", this);
    connect(copySpecBufferAct, SIGNAL(triggered()), this, SLOT(copySpecBufferSlot()));

#ifdef HAS_PLOTTING
    plotSpecAct = new QAction("Send single spectrum to plotting tool", this);
    connect(plotSpecAct, SIGNAL(triggered()), this, SLOT(plotSpecSlot()));
#endif /* HAS_PLOTTING */

    copyImageBufferAct = new QAction("Copy image to new buffer", this);
    copyImageBufferAct->setStatusTip(tr("Copies image as it appears on screen"));
    connect(copyImageBufferAct, SIGNAL(triggered()), this, SLOT(copyImageBufferSlot()));

    saveImageFitsAct = new QAction("Save image as FITS...", this);
    saveImageFitsAct->setStatusTip(tr("Saves image without any modifications"));
    connect(saveImageFitsAct, SIGNAL(triggered()), this, SLOT(saveImageFitsSlot()));

    saveMBbufferAct = new QAction("Save extension as FITS...", this);
    saveMBbufferAct->setStatusTip(tr("Saves this extension without any modifications"));
    connect(saveMBbufferAct, SIGNAL(triggered()), this, SLOT(saveMBbufferSlot()));

    copyMBbufferAct = new QAction("Copy extension to new buffer", this);
    copyMBbufferAct->setStatusTip(tr("Copies this extension to a new buffer"));
    connect(copyMBbufferAct, SIGNAL(triggered()), this, SLOT(copyMBbufferSlot()));

    saveMarkersAct = new QAction("Save source/continuum mask...", this);
    connect(saveMarkersAct, SIGNAL(triggered()), this, SLOT(saveMarkersSlot()));

    exportMarkersAct = new QAction("New 2xn buffer from source/continuum mask...", this);
    connect(exportMarkersAct, SIGNAL(triggered()), this, SLOT(exportMarkersSlot()));

    loadMarkersAct = new QAction("Load source/continuum mask...", this);
    connect(loadMarkersAct, SIGNAL(triggered()), this, SLOT(loadMarkersSlot()));

    radialProfileAct = new QAction("Radial profile from this position", this);
    connect(radialProfileAct, SIGNAL(triggered()), this, SLOT(radialProfileSlot()));

#ifdef HAS_PLOTTING
    plotHorizontalAct = new QAction("Send horizontal cut to plotting tool", this);
    connect(plotHorizontalAct, SIGNAL(triggered()), this, SLOT(plotHorizontalSlot()));

    plotVerticalAct = new QAction("Send vertical cut to plotting tool", this);
    connect(plotVerticalAct, SIGNAL(triggered()), this, SLOT(plotVerticalSlot()));
#endif /* HAS_PLOTTING */

    lockPosAct = new QAction("Lock position", this);
    connect(lockPosAct, SIGNAL(triggered()), this, SLOT(lockPosSlot()));

    useStdStarAct = new QAction("Use as standard star", this);
    connect(useStdStarAct, SIGNAL(triggered()), this, SLOT(useStdStarSlot()));

    // fill popup menu
    rightPopupMenu->addAction(exportSpecAsciiAct);
    rightPopupMenu->addAction(exportSpecFitsAct);
    rightPopupMenu->addAction(copySpecBufferAct);
#ifdef HAS_PLOTTING
    rightPopupMenu->addAction(plotSpecAct);
#endif /* HAS_PLOTTING */
    rightPopupMenu->addSeparator();
    rightPopupMenu->addAction(saveImageFitsAct);
    rightPopupMenu->addAction(copyImageBufferAct);
    rightPopupMenu->addSeparator();
    rightPopupMenu->addAction(saveMBbufferAct);
    rightPopupMenu->addAction(copyMBbufferAct);
    rightPopupMenu->addSeparator();
    rightPopupMenu->addAction(saveMarkersAct);
    rightPopupMenu->addAction(exportMarkersAct);
    rightPopupMenu->addAction(loadMarkersAct);
    rightPopupMenu->addSeparator();
    rightPopupMenu->addAction(radialProfileAct);
#ifdef HAS_PLOTTING
    rightPopupMenu->addAction(plotHorizontalAct);
    rightPopupMenu->addAction(plotVerticalAct);
#endif /* HAS_PLOTTING */
    rightPopupMenu->addSeparator();
    rightPopupMenu->addAction(lockPosAct);
    rightPopupMenu->addAction(useStdStarAct);
}

void QFitsView2D::exportSpecAsciiSlot() {
    setMouseTracking(false);
    fitsMainWindow->spectrum->saveAscii(getMyBuffer());
    setMouseTracking(true);
}

void QFitsView2D::exportSpecFitsSlot() {
    setMouseTracking(false);
    fitsMainWindow->spectrum->saveFitsSpectrum(getMyBuffer());
    setMouseTracking(true);
}

void QFitsView2D::copySpecBufferSlot() {
    setMouseTracking(false);
    fitsMainWindow->spectrum->newBufferSpectrum(getMyBuffer());
    setMouseTracking(true);
}

void QFitsView2D::plotSpecSlot() {
#ifdef HAS_PLOTTING
    fitsMainWindow->plplotWindow->addBufferData(QString(fitsMainWindow->main_view->getCurrentBufferIndex().c_str()), fitsMainWindow->spectrum->getCenX(), fitsMainWindow->spectrum->getCenY());
#endif /* HAS_PLOTTING */
}

void QFitsView2D::copyImageBufferSlot() {
    QString cmd = QString(freeBufferName().to_c_str()) + " = ";

    QFitsSingleBuffer *sb = getMyBuffer();
    QReadLocker locker(&buffersLock);
    if ((sb != NULL) &&
        (sb->getDpData()->type == typeFits) &&
        (sb->getDpData()->fvalue->Naxis(3) > 1))
    {
        Fits *f = sb->getDpData()->fvalue;
        switch (sb->getCubeMode()) {
            case DisplayCubeAverage:
                cmd += "cubeavg(" + QString(getBufferIndex().c_str()) +
                       sb->getExtensionIndicesString();
                if (sb->getIgnoreInCube()) {
                    cmd += ", " + QString::number(sb->getIgnoreValue());
                }
                cmd += ")";
                break;
            case DisplayCubeMedian:
                if (sb->getIgnoreInCube()) {
                    sb->getCubeDisplay2D()->CubeMedian(*f, sb->getIgnoreValue());
                }
                cmd += "cubemedian(" + QString(getBufferIndex().c_str()) +
                       sb->getExtensionIndicesString();
                if (sb->getIgnoreInCube()) {
                    cmd += ", " + QString::number(sb->getIgnoreValue());
                }
                cmd += ")";
                break;
            case DisplayCubeLinemap: {
                int c1 = (int)(sb->getCubeCenter(QFV::Wavelength) - sb->getLineWidth(QFV::Wavelength) + 0.5),
                    c2 = (int)(sb->getCubeCenter(QFV::Wavelength) + sb->getLineWidth(QFV::Wavelength) + 0.5);

                if (c1 > c2) {
                    int tmp = c1;
                    c1 = c2;
                    c2 = tmp;
                }

                if (c1 < 1) {
                    c1 = 1;
                }
                if (c2 > f->Naxis(3)) {
                    c2 = f->Naxis(3);
                }
                if (sb->getLineCont1() && sb->getLineCont2()) {
                    cmd += "2 * ";
                }
                cmd += "cubeavg(" + QString(getBufferIndex().c_str()) +
                       sb->getExtensionIndicesString() +
                       "[*,*," + QString::number(c1) + ":" + QString::number(c2) + "]";
                if (sb->getIgnoreInCube()) {
                    cmd += ", " + QString::number(sb->getIgnoreValue());
                }
                cmd += ")";

                if (sb->getLineCont1()) {
                    c1 = (int)(sb->getCubeCenter(QFV::Wavelength) + sb->getLineCont1Center() - sb->getLineCont1Width() + 0.5);
                    c2 = (int)(sb->getCubeCenter(QFV::Wavelength) + sb->getLineCont1Center() + sb->getLineCont1Width() + 0.5);
                    if (c1 < 1) {
                        c1 = 1;
                    }
                    if (c2 > f->Naxis(3)) {
                        c2 = f->Naxis(3);
                    }
                    cmd += " - cubeavg(" + QString(getBufferIndex().c_str()) +
                           sb->getExtensionIndicesString() +
                           "[*,*," + QString::number(c1) + ":" + QString::number(c2) + "]";
                    if (sb->getIgnoreInCube()) {
                        cmd += ", " + QString::number(sb->getIgnoreValue());
                    }
                    cmd += ")";
                }
                if (sb->getLineCont2()) {
                    c1 = (int)(sb->getCubeCenter(QFV::Wavelength) + sb->getLineCont2Center() - sb->getLineCont2Width() + 0.5);
                    c2 = (int)(sb->getCubeCenter(QFV::Wavelength) + sb->getLineCont2Center() + sb->getLineCont2Width() + 0.5);
                    if (c1 < 1) {
                        c1 = 1;
                    }
                    if (c2 > f->Naxis(3)) {
                        c2 = f->Naxis(3);
                    }
                    cmd += " - cubeavg(" + QString(getBufferIndex().c_str()) +
                           sb->getExtensionIndicesString() +
                           "[*,*," + QString::number(c1) + ":" + QString::number(c2) + "]";
                    if (sb->getIgnoreInCube()) {
                        cmd += ", " + QString::number(sb->getIgnoreValue());
                    }
                    cmd += ")";
                }
                }
                break;
            default:
                cmd += QString(getBufferIndex().c_str()) +
                       sb->getExtensionIndicesString() +
                       "[*,*," + QString::number((int)(sb->getCubeCenter(QFV::Wavelength) + 0.5)) + "]";
                break;
        }
    }
    dpuser_widget->executeCommand(cmd);
}

void QFitsView2D::copyCubeBufferSlot() {
    QFitsSingleBuffer *sb = getMyBuffer();

    QString cmd = QString(freeBufferName().c_str()) + " = " +
                  QString(getBufferIndex().c_str()) +
                  sb->getExtensionIndicesString();

    dpuser_widget->executeCommand(cmd);
}

void QFitsView2D::saveImageFitsSlot() {
    QFitsSingleBuffer *sb = getMyBuffer();
    sb->saveFitsImage();
}

void QFitsView2D::saveMBbufferSlot() {
    QFitsSingleBuffer *sb = getMyBuffer();
    sb->saveFits();
}
void QFitsView2D::copyMBbufferSlot() {
    QString cmd = QString(freeBufferName().c_str()) + " = " +
                  QString(getBufferIndex().c_str()) +
                  getMyBuffer()->getExtensionIndicesString();

    dpuser_widget->executeCommand(cmd);
}

void QFitsView2D::saveMarkersSlot() {
    QFitsBaseBuffer   *bb = fitsMainWindow->getCurrentBuffer();
    QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer*>(bb);
    QFitsMultiBuffer  *mb = dynamic_cast<QFitsMultiBuffer*>(bb);
    dpuserType        *dp = bb->getDpData();

    QString filename;
    if (sb != NULL) {
        if ((dp->type != typeFits) ||
            (dp->fvalue->Naxis(0) < 2))
        {
            QMessageBox::warning(this, "QFitsView", "Data must be an image or cube!");
            return;
        }

        filename = dp->fvalue->getFileName();
    } else if (mb != NULL) {
        if (dp->type != typeDpArr) {
            QMessageBox::warning(this, "QFitsView", "Data seems to be wrong!?! (MB and no typeDpArr)");
            return;
        }
        filename = dp->dparrvalue->at(0)->getFileName();
    }

    // add in "_mask" in original filename
    QFileInfo filenameInfo(filename);
    QString proposedFilename = settings.lastSavePath + "/" + filenameInfo.baseName() + "_mask." + filenameInfo.completeSuffix();

    QString selectedFilter;
    filename = QFileDialog::getSaveFileName(this,
                                            "QFitsView - Save Mask as FITS",
                                            proposedFilename,
                                            "*.fits",
                                            &selectedFilter);
    if (!filename.isNull() &&
        buffersLock.tryLockForRead())
    {
        selectedFilter.remove(0,1);
        if (!filename.endsWith(selectedFilter)) {
            filename += selectedFilter;
        }

        dpuserType *dp = bb->getDpData();
        if (sb != NULL){
            // SB

            // create mask
            Fits *data = dp->fvalue,
                 *mask = new Fits;
            mask->create(data->Naxis(1), data->Naxis(2), I2);

            // set source markers as 1 in mask
            // set continuum markers as -1 in mask
            long x = 0,
                 y = 0;

            // save source/continuum markers
            for (int i = 0; i < sourceContinuumMarkers->getSizeSource(); i++) {
                sourceContinuumMarkers->getMarkerSource(i, &x, &y);
                mask->i2data[mask->F_I(x, y)] = 1;
            }
            for (int i = 0; i < sourceContinuumMarkers->getSizeContinuum(); i++) {
                sourceContinuumMarkers->getMarkerContinuum(i, &x, &y);
                mask->i2data[mask->F_I(x, y)] = -1;
            }

//            // save cursor markers
//            QFitsMarkers *markers = sb->getCursorMarkers();
//            for (int i = 0; i < markers->getSizeSource(); i++) {
//                markers->getMarkerSource(i, &x, &y);
//                mask->i2data[mask->F_I(x, y)] = 1;
//            }
//            for (int i = 0; i < markers->getSizeContinuum(); i++) {
//                markers->getMarkerContinuum(i, &x, &y);
//                mask->i2data[mask->F_I(x, y)] = -1;
//            }

            // save mask
            mask->WriteFITS(filename.toStdString().c_str());
            delete mask; mask = NULL;
        } else {
            // MB

            // create mask
            dpuserType *mask = new dpuserType();
            mask->type = typeDpArr;
            mask->dparrvalue = new dpuserTypeList();
            for (int i = 0; i < dp->dparrvalue->size(); i++) {
                dpuserType *dpD = dp->dparrvalue->at(i);
                if (dpD->type == typeFits) {
                    dpuserType* dpM = new dpuserType;
                    dpM->type = typeFits;
                    dpM->fvalue = new Fits;
                    dpM->fvalue->create(dpD->fvalue->Naxis(1), dpD->fvalue->Naxis(2), I2);
                    mask->dparrvalue->push_back(dpM);

                }
            }

            for (int i = 0; i < mask->dparrvalue->size(); i++) {

                // set source markers as 1 in mask
                // set continuum markers as -1 in mask
                long x = 0,
                     y = 0;

                Fits *f = mask->dparrvalue->at(i)->fvalue;

                // save source/continuum markers
                sb = mb->getSB(i);
                QFitsMarkers* markers = sb->getSourceMarkers();
                for (int i = 0; i < markers->getSizeSource(); i++) {
                    markers->getMarkerSource(i, &x, &y);
                    f->i2data[f->F_I(x, y)] = 1;
                }
                for (int i = 0; i < markers->getSizeContinuum(); i++) {
                    markers->getMarkerContinuum(i, &x, &y);
                    f->i2data[f->F_I(x, y)] = -1;
                }

//                // save cursor markers
//                QFitsMarkers *markers = sb->getCursorMarkers();
//                for (int i = 0; i < markers->getSizeSource(); i++) {
//                    markers->getMarkerSource(i, &x, &y);
//                    f->i2data[f->F_I(x, y)] = 1;
//                }
//                for (int i = 0; i < markers->getSizeContinuum(); i++) {
//                    markers->getMarkerContinuum(i, &x, &y);
//                    f->i2data[f->F_I(x, y)] = -1;
//                }
            }

            // save mask
            if (mask->dparrvalue->at(0)->type == typeFits) {
                mask->dparrvalue->at(0)->fvalue->WriteFITS(filename.toStdString().c_str());
                for (int i = 1; i < mask->dparrvalue->size(); i++) {
                    if (mask->dparrvalue->at(i)->type == typeFits) {
                        mask->dparrvalue->at(i)->fvalue->WriteFITSExtension(filename.toStdString().c_str());
                    }
                }
            }
//            dpuserProcedure_writefits(dpuserType(filename.toStdString().c_str()), *mask);
            delete mask; mask = NULL;
dp_output("SAVE MARKERS FROM MULTIBUFFER: Can't do this right now :-(");
        }

        buffersLock.unlock();

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

void QFitsView2D::exportMarkersSlot() {
    QFitsBaseBuffer   *bb = fitsMainWindow->getCurrentBuffer();
    QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer*>(bb);
    QFitsMultiBuffer  *mb = dynamic_cast<QFitsMultiBuffer*>(bb);
    dpuserType        *dp = bb->getDpData();

    if (sb != NULL) {
        if ((dp->type != typeFits) ||
            (dp->fvalue->Naxis(0) < 2))
        {
            QMessageBox::warning(this, "QFitsView", "Data must be an image or cube!");
            return;
        }
    } else {
        QMessageBox::warning(this, "QFitsView", "Data must be an image or cube!");
        return;
    }

    if (buffersLock.tryLockForRead()) {
        dpuserType *dp = bb->getDpData();
            // create mask
        Fits mask;
        mask.create(2, sourceContinuumMarkers->getSizeSource() + sourceContinuumMarkers->getSizeContinuum(), I2);

        int counter = 0;
        long x, y;

        // save source/continuum markers
        for (int i = 0; i < sourceContinuumMarkers->getSizeSource(); i++) {
            sourceContinuumMarkers->getMarkerSource(i, &x, &y);
            mask.i2data[counter] = x;
            counter++;
            mask.i2data[counter] = y;
            counter++;
        }
        for (int i = 0; i < sourceContinuumMarkers->getSizeContinuum(); i++) {
            sourceContinuumMarkers->getMarkerContinuum(i, &x, &y);
            mask.i2data[counter] = x;
            counter++;
            mask.i2data[counter] = y;
            counter++;
        }

        buffersLock.unlock();

        mask.extensionType = BINTABLE;
        QString bufferName  = freeBufferName().c_str();
        injectVariable(bufferName, mask);

        QString cmd = "view " + bufferName;
        dpuser_widget->executeCommand(cmd);
    }
}

void QFitsView2D::loadMarkersSlot() {
    QString fname = QFileDialog::getOpenFileName(this, "QFitsView - Open Source/Continuum Mask", settings.lastOpenPath,
                                                 "Fits files (*.fits *.fts *.fits.gz *.fts.gz);;"
                                                 "All files (*)");

    if (fname.size() > 0) {
        QFileInfo finfo(fname);
        settings.lastOpenPath = finfo.absoluteDir().path();
        settings.lastOpenPath.replace("\\", "/");
        fname.replace("\\", "/");
        QByteArray s = fname.toLocal8Bit();
        const char *fnameChar = s.constData();

        // check for presence of typeFits in data
        QFitsBaseBuffer   *bb     = fitsMainWindow->getCurrentBuffer();
        QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer*>(bb);
        QFitsMultiBuffer  *mb = dynamic_cast<QFitsMultiBuffer*>(bb);
        dpuserType      *dpData     = bb->getDpData();
        QVector<bool>   validExtData;
        Fits            *dataSB     = NULL;
        dpuserTypeList  *dataMB     = NULL;
        if (sb != NULL) {
            // data is SB
            if (dpData->type != typeFits) {
                QMessageBox::warning(this, "QFitsView", "Data must be an image or cube!");
                return;
            }
            dataSB = dpData->fvalue;
        } else {
            // data is MB

            // check if MB is typeDpArr
            if (dpData->type != typeDpArr) {
                QMessageBox::warning(this, "QFitsView", "Data must contain image data!");
                return;
            }
            int foundValid = 0;
            for (int i = 0; i < dpData->dparrvalue->size(); i++) {
                dpuserType *dp = dpData->dparrvalue->at(i);
                if (dp->type == typeFits) {
                    validExtData.push_back(true);
                    foundValid++;
                } else {
                    validExtData.push_back(false);
                }
            }
            if (foundValid == 0) {
                QMessageBox::warning(this, "QFitsView", "At least one extension of the data "
                                                        "must be an image or cube!");
                return;
            }
            dataMB = dpData->dparrvalue;
        }

        // count extensions in mask
        Fits       *maskSB    = new Fits;
        int        nrExtMask  = maskSB->CountExtensions(fnameChar),
                   nrExtData  = validExtData.size();
        delete maskSB; maskSB = NULL;

        // check if either data and mask are both SB or MB
        if (dataSB) {
            // data is SB
            if (nrExtData != nrExtMask) {
                QMessageBox::warning(this, "QFitsView", "Data and mask don't match!\n\n"
                                                        "Data is a plain FITS file without extensions, "
                                                        "mask is a FITS with extensions.");
                return;
            }
        } else {
            // data is MB
            if (nrExtMask == 0) {
                QMessageBox::warning(this, "QFitsView", "Data and mask don't match!\n\n"
                                                        "Data is a FITS with extensions, the mask "
                                                        "is a plain FITS file without extensions.");
                return;
            } else if ((nrExtData-1) != nrExtMask) {
                QString str = "Data and mask don't match!\n\nData has " + QString::number(nrExtData-1) +
                              " extensions,\nmask has " + QString::number(nrExtMask) + " extensions.";
                QMessageBox::warning(this, "QFitsView", str);
                return;
            }
        }

        //
        // load mask
        //
        dpuserTypeList *maskMB = NULL;
        if (nrExtMask == 0) {
            maskSB = new Fits;
            maskSB->ReadFITS(fnameChar);
        } else {
            maskMB = new dpuserTypeList;
            maskMB->ReadFITS(fnameChar);
        }

        //
        // check if buffers match (SB/MB, size)
        //
        try {
            if (dataMB) {
                QVector<bool>   validExtMask;
                for (int i = 0; i < maskMB->size(); i++) {
                    dpuserType *dp = maskMB->at(i);
                    if (dp->type == typeFits) {
                        validExtMask.push_back(true);
                    } else {
                        validExtMask.push_back(false);
                    }
                }
                for (int i = 0; i < validExtData.size(); i++) {
                    if (validExtData.at(i) != validExtMask.at(i)) {
                        QString str = "Data and mask extensions don't match!\n(Extension: " + QString::number(i) + ")";
                        throw dpuserTypeException(const_cast<char*>(str.toStdString().c_str()));
                    }
                }
            }

            if (dataSB) {
                // check if mask is an image
                if (maskSB->Naxis(0) != 2) {
                    throw dpuserTypeException("Mask must be an image!");
                }

                // check if SBs have the same dimensions
                if ((dataSB->Naxis(0) < maskSB->Naxis(0)) ||
                    (dataSB->Naxis(1) != maskSB->Naxis(1)) ||
                    (dataSB->Naxis(2) != maskSB->Naxis(2)))
                {
                    throw dpuserTypeException("Data and mask are of different dimensions!");
                }
            } else {
                // check child SBs if they have the same dimensions
                for (int i = 0; i < dataMB->size(); i++) {
                    dpuserType *dpD = dataMB->at(i),
                               *dpM = maskMB->at(i);

                    // check if data SB is typeFits, ignore other extensions
                    if (dpD->type == typeFits) {
                        // check if mask SB is typeFits
                        if (dpM->type != typeFits) {
                            QString str = "Mask must be a vector/image/cube!\n(Extension: " + QString::number(i) + ")";
                            throw dpuserTypeException(const_cast<char*>(str.toStdString().c_str()));
                        }

                        // check if mask is an image
                        if (dpM->fvalue->Naxis(0) != 2) {
                            throw dpuserTypeException("Mask must be an image!");
                        }

                        // check if SB has same dimensions
                        if ((dpD->fvalue->Naxis(0) < dpM->fvalue->Naxis(0)) ||
                            (dpD->fvalue->Naxis(1) != dpM->fvalue->Naxis(1)) ||
                            (dpD->fvalue->Naxis(2) != dpM->fvalue->Naxis(2)))
                        {
                            QString str = "Data and mask are of different dimensions!\n(Extension: " + QString::number(i) + ")";
                            throw dpuserTypeException(const_cast<char*>(str.toStdString().c_str()));
                        }
                    }
                }
            }

            //
            // apply mask
            //
            if (dataSB != NULL) {
                // data and mask are SB
                for (int x = 1; x <= dataSB->Naxis(1); x++) {
                    for (int y = 1; y <= dataSB->Naxis(2); y++) {
                        if (maskSB->ValueAt(maskSB->F_I(x,y)) > 0.) {
                            sourceContinuumMarkers->addMarkerSource(x, y);
                        } else if (maskSB->ValueAt(maskSB->F_I(x,y)) < 0.) {
                            sourceContinuumMarkers->addMarkerContinuum(x, y);
                        }
                    }
                }
                // paint markers in View2D
                update();
            } else {
                // data and mask are MB
                for (int i = 0; i < dataMB->size(); i++) {
                    dpuserType *dpD = dataMB->at(i),
                               *dpM = maskMB->at(i);
                    if (dpD != NULL) {
                        // data and mask are SB
                        QFitsMarkers* markers = mb->getSB(i)->getSourceMarkers();
                        for (int x = 1; x <= dpD->fvalue->Naxis(1); x++) {
                            for (int y = 1; y <= dpD->fvalue->Naxis(2); y++) {
                                if (dpM->fvalue->ValueAt(dpM->fvalue->F_I(x,y)) > 0.) {
                                    markers->addMarkerSource(x, y);
                                } else if (dpM->fvalue->ValueAt(dpM->fvalue->F_I(x,y)) < 0.) {
                                    markers->addMarkerContinuum(x, y);
                                }
                            }
                        }
                    }

                }
                // paint markers in the View2D of all SBs
                mb->update();
            }

            // paint markers CubeSpectrum
            fitsMainWindow->getCurrentBuffer()->createManualSpectrum();
        } catch (dpuserTypeException e) {
            QMessageBox::warning(this, "QFitsView", e.reason());
        }

        delete maskSB; maskSB = NULL;
        delete maskMB; maskMB = NULL;
    }
}

void QFitsView2D::radialProfileSlot() {
    double  imageX = 0,
            imageY = 0;
    QFitsSingleBuffer *sb = getMyBuffer();

    widgetToImageNoSignal(mouseX, mouseY, &imageX, &imageY);

    QString cmd = QString(freeBufferName().c_str()) + " = radialprofile(" +
                  QString(getBufferIndex().c_str()) +
                  sb->getExtensionIndicesString() +
                  ", " + QString::number((int)imageX) + ", " + QString::number((int)imageY) + ")";

    dpuser_widget->executeCommand(cmd);
}

void QFitsView2D::plotHorizontalSlot() {
#ifdef HAS_PLOTTING
    double  imageX = 0,
            imageY = 0;

    widgetToImageNoSignal(mouseX, mouseY, &imageX, &imageY);
    fitsMainWindow->plplotWindow->addBufferData(QString(getBufferIndex().c_str()), -1, (int)imageY);
#endif /* HAS_PLOTTING */
}

void QFitsView2D::plotVerticalSlot() {
#ifdef HAS_PLOTTING
    double  imageX = 0,
            imageY = 0;

    widgetToImageNoSignal(mouseX, mouseY, &imageX, &imageY);
    fitsMainWindow->plplotWindow->addBufferData(QString(getBufferIndex().c_str()), (int)imageX, -1);
#endif /* HAS_PLOTTING */
}

void QFitsView2D::lockPosSlot() {
    lockUnlockSpectrum(mousePos);
}

void QFitsView2D::useStdStarSlot() {
    fitsMainWindow->spectrum->saveStandardStar(getMyBuffer(), sourceContinuumMarkers);
}

QFitsMarkers* QFitsView2D::getSourceMarkers() {
    return sourceContinuumMarkers;
}

void QFitsView2D::paintEvent(QPaintEvent *p) {
    QFitsSingleBuffer *sb = getMyBuffer();
    QImage *sbImg = sb->getImage();

    if (!sbImg->isNull()) {
//printf("========= QFitsView2D::paintEvent() ===========    (%d)\n", setXXX++);fflush(stdout);
        double  zoomFactor  = sb->getZoomFactor(),
                imgWzoomed  = sbImg->width() * zoomFactor,
                imgHzoomed  = sbImg->height() * zoomFactor;

        xoffset = yoffset = 0;
        int visibleW = visibleRegion().boundingRect().width(),
            visibleH = visibleRegion().boundingRect().height();
        if (visibleW > imgWzoomed) {
            xoffset = (int)((visibleW - imgWzoomed) / 2.);
        }
        if (visibleH > imgHzoomed) {
            yoffset = (int)((visibleH - imgHzoomed) / 2.);
        }
//printf("   === zoomFactor:       %g\n", zoomFactor);fflush(stdout);
//printf("   === sb        DIM  w: %d, h: %d\n", sbImg->width(), sbImg->height());fflush(stdout);
//printf("   ===           DIMz w: %g, h: %g\n", imgWzoomed, imgHzoomed);fflush(stdout);
//printf("   ===           c/w  x: %g, y: %g, w: %g, h: %g\n", sb->getXcenter(), sb->getYcenter(),sb->getWidthVisible(), sb->getHeightVisible());fflush(stdout);
//printf("   ===           off  x: %d, y: %d\n", xoffset, yoffset);fflush(stdout);
//printf("   === widget    dim  w: %d, h: %d\n", width(), height());fflush(stdout);
//printf("   ===           vis  l: %d, t: %d, w: %d, h: %d\n", visibleRegion().boundingRect().left(), visibleRegion().boundingRect().top(), visibleW, visibleH);fflush(stdout);
//printf("   === scroll    vis  h: %d, v: %d\n",myParent->getScr()->horizontalScrollBar()->isVisible(), myParent->getScr()->verticalScrollBar()->isVisible());fflush(stdout);
//printf("   ===           pos  h: %d, v: %d\n",myParent->getScr()->horizontalScrollBar()->sliderPosition(), myParent->getScr()->verticalScrollBar()->sliderPosition());fflush(stdout);
//printf("   ===           val  h: %d, v: %d\n",myParent->getScr()->horizontalScrollBar()->value(), myParent->getScr()->verticalScrollBar()->value());fflush(stdout);
//printf("   ===           min  h: %d, v: %d\n",myParent->getScr()->horizontalScrollBar()->minimum(), myParent->getScr()->verticalScrollBar()->minimum());fflush(stdout);
//printf("   ===           max  h: %d, v: %d\n",myParent->getScr()->horizontalScrollBar()->maximum(), myParent->getScr()->verticalScrollBar()->maximum());fflush(stdout);
//printf("   ===           stp  h: %d, v: %d\n",myParent->getScr()->horizontalScrollBar()->pageStep(), myParent->getScr()->verticalScrollBar()->pageStep());fflush(stdout);
        QPainter pa(this);
        pa.setRenderHint(QPainter::SmoothPixmapTransform, false);
        pa.save();
        pa.translate(xoffset, yoffset);

        QFitsSingleBuffer *sb = myParent->getMyBuffer();
        if (zoomFactor >= 1.) {
            // >= 100%
            pa.scale(zoomFactor, zoomFactor);
            pa.drawImage(0, 0, *(sbImg));
        } else {
            QImage subImage(sbImg->width() * zoomFactor,
                            sbImg->height() * zoomFactor,
                            QImage::Format_Indexed8);
            subImage.setColorTable(sbImg->colorTable());

            int inc = 1 / zoomFactor;
            for (int y = sbImg->height() - 1, yy = subImage.height() - 1; y >= 0, yy >= 0; y-=inc, yy--) {
                if (y >= 0 && yy >= 0 && y < sbImg->height() && yy < subImage.height()) {
                    uchar *p1 = sbImg->scanLine(y),
                          *pp = subImage.scanLine(yy);
                    for (int x = 0, xx = 0; x < sbImg->width(), xx < subImage.width(); x+=inc, xx++) {
                        if (x >= 0 && xx >= 0 && x < sbImg->width() && xx < subImage.width()) {
                            *(pp++) = *(p1++);
                            for (int j = 0; j < inc-1; j++) {
                                *p1++;
                            }
                        }
                    }
                }
            }
            pa.drawImage(0, 0, subImage);
        }

        pa.restore();
        if (mousemode == drawDistMarker) {
            drawDistanceMarker(pa);
        } else if (mousemode == drawRectGreen) {
            drawRectMarker(pa, QColor(0, 255, 0));
        } else if (mousemode == drawRectRed) {
            drawRectMarker(pa, QColor(255, 0, 0));
        } else if (mousemode == drawRectWhite) {
            drawRectMarker(pa, QColor(255, 255, 255));
        }
        if (drawMode == longslitMarker) {
            pa.save();
            pa.translate(xoffset, yoffset);
            drawLongslitMarker(pa, extra_int[0], extra_int[1], extra_double[0], extra_int[2], extra_double[1]);
            pa.restore();
        } else if (drawMode == ellipticalProfileMarker) {
            pa.save();
            pa.translate(xoffset, yoffset);
            drawEllipticalProfileMarker(pa, extra_int[0], extra_int[1], extra_double[0], extra_double[1], extra_int[2]);
            pa.restore();
        } else if (drawMode == markposMarker) {
            pa.save();
            pa.translate(xoffset, yoffset);
            drawMarkposMarker(pa);
            pa.restore();
        }

        // paint source & continuum points
        if (sourceContinuumMarkers->hasAnyMarkers()) {
            pa.save();
            sourceContinuumMarkers->setCenter(sb, 0, 0);
            sourceContinuumMarkers->drawAllMarkers(&pa, xoffset, yoffset, sb);
            pa.restore();
        }

        // paint cursor/spectrum points
        pa.save();
        sb->getCursorMarkers()->drawAllMarkers(&pa, xoffset, yoffset, sb);
        pa.restore();

        if ((sb != NULL) && (sb->isMultiBufferChild())) {
            QFitsSingleBuffer *sb_marked = sb->getMyMultiBuffer()->getMarkedSingleBuffer();
            if ((sb_marked != NULL) && (sb == sb_marked)) {
                // draw cyan bounding box
                QPen pen(Qt::cyan, 3);
                pa.setPen(pen);
                pa.drawRect(0, 0, width()-1, height()-1);
            }
        }

    }
    sb->notifyCombiner();
//printf("===============================================\n");fflush(stdout);
}

void QFitsView2D::mouseMoveEvent(QMouseEvent *m) {
    if (!hasFocus()) {
        setFocus();
    }

    QFitsSingleBuffer *sb = getMyBuffer();
    if ((sb != NULL) && (sb->getDpData()->type == typeFits)) {
        Fits    *f          = sb->getDpData()->fvalue;
        int     imageX      = 0,
                imageY      = 0;
        double  imageXDbl   = 0.,
                imageYDbl   = 0.;
        sb->getCursorMarkers()->setBufferToPaint(sb);

        // keep track of this all the time: Must be able to check if cursor is over
        // image when spectrum-lock is removed with Key_L (mouse could have moved in
        // between)
        currentpos = m->pos();

        if (drawMode == longslitMarker || drawMode == ellipticalProfileMarker) {
            widgetToImageNoSignal(currentpos.x(), currentpos.y(), &imageXDbl, &imageYDbl);
            imageX = (int)imageXDbl;
            imageY = (int)imageYDbl;
            if (m->buttons() == Qt::NoButton) {
                int distance = (int)sqrt((float)((extra_int[0] - imageX) * (extra_int[0] - imageX) +
                                                 (extra_int[1] - imageY) * (extra_int[1] - imageY)));
                if (distance < 10) {
                    setCursor(Qt::PointingHandCursor);
                } else {
                    double angle;

                    angle = -atan2((double)(imageX - extra_int[0]),
                                   (double)(extra_int[1]-imageY)) + M_PI - extra_double[0] * M_PI / 180.;
                    if (drawMode == ellipticalProfileMarker) {
                        angle = -atan2((double)(imageX - extra_int[0]),
                                       (double)(extra_int[1]-imageY)) + M_PI + extra_double[0] * M_PI / 180.;
                    }
                    if (distance * fabs(sin(angle)) < 8) {
                        QPixmap pm(cursor_rotate);
                        setCursor(QCursor(pm));
//@                    } else if (drawMode == longslitMarker && distance * fabs(sin(angle)) < 16) {
//@                        setCursor(Qt::SizeAllCursor);
                    } else {
                        setCursor(Qt::ArrowCursor);
                    }
                }
            }
        } else if (drawMode == markposMarker) {
            QPixmap pm(cursor_select);
            setCursor(QCursor(pm, 16, 18));
        } else if ((mousemode == None) && (cursor().shape() != Qt::ArrowCursor)) {
            setCursor(Qt::ArrowCursor);
        }

        if (m->buttons() == Qt::NoButton) {
            if (buffersLock.tryLockForRead()) {
                if (f != NULL) {
                    if (sb->getLockedSingleBuffer() == NULL) {
                        widgetToImage(currentpos.x(), currentpos.y());
                    }
                }
                buffersLock.unlock();
            }
            if ((mousemode == drawDistMarker) ||
                (mousemode == drawRectGreen) || (mousemode == drawRectRed) || (mousemode == drawRectWhite))
            {
                update();
            }

            // draw points for spectrum
            if (sb->getCursorMarkers()->hasAnyMarkers()) {
                widgetToImageNoSignal(currentpos.x(), currentpos.y(), &imageXDbl, &imageYDbl);
                imageX = (int)imageXDbl;
                imageY = (int)imageYDbl;
                dp_debug("%i %i, %i %i", currentpos.x(), currentpos.y(), imageX, imageY);
                sb->getCursorMarkers()->setCenter(sb, imageX, imageY);
                update();
            }

        } else if (m->buttons() == Qt::LeftButton) {
            if (((drawMode == longslitMarker ||
                  drawMode == ellipticalProfileMarker)) &&
                (cursor().shape() != Qt::ArrowCursor))
            {
                double angle;

                if (cursor().shape() == Qt::PointingHandCursor) {
                    fitsMainWindow->longslitChangedCenter(imageX, imageY);
                } else if (cursor().shape() == Qt::BitmapCursor) {
                    angle = -atan2((double)(imageX - extra_int[0]),
                                   (double)(extra_int[1]-imageY)) * 180. / M_PI + 180.;
                    fitsMainWindow->longslitChangedAngle(angle);
                }
            } else {
                QPoint pos = mapToParent(m->pos());
                emit mouseClicked(pos.x(), pos.y());
            }
        }
    }
}

void QFitsView2D::mousePressEvent(QMouseEvent *m) {
    QFitsSingleBuffer *sb = getMyBuffer();

    if (m->button() == Qt::LeftButton) {
        if (sb->isMultiBufferChild()) {
            setMousePressedPos(m->pos().x(), m->pos().y());
        } else {
            if (fitsMainWindow->main_view->getCurrentBuffer() != getMyBuffer())
                fitsMainWindow->main_view->setCurrentBufferFromWidget(getMyBuffer());
        }
    } else if (m->button() == Qt::MiddleButton) {
        double  imageX = 0,
                imageY = 0;

//printf("2)      w2i  xIn:  %d, yIn:  %d\n", m->x(), m->y()); fflush(stdout);
        widgetToImageNoSignal(m->x(), m->y(), &imageX, &imageY);
//printf("        w2i  xOut: %g, yOut: %g\n", imageX, imageY); fflush(stdout);
        imageX -= 1;
        imageY -= 1;
        sb->setXcenter(imageX);
        sb->setYcenter(imageY);
        setScrollerImageCenter(imageX, imageY);

        fitsMainWindow->updateTotalVisibleRect();
    } else if (m->button() == Qt::RightButton) {
        bool ok = true;

        // sb is a pure QFitsSingleBuffer
        if (sb->Naxis(0) != 3) {
            // we have an image
            exportSpecAsciiAct->setVisible(false);
            exportSpecFitsAct->setVisible(false);
            copySpecBufferAct->setVisible(false);
#ifdef HAS_PLOTTING
            plotSpecAct->setVisible(false);
#endif /* HAS_PLOTTING */
            copyImageBufferAct->setVisible(false);
            saveImageFitsAct->setVisible(false);
            lockPosAct->setVisible(false);
            useStdStarAct->setVisible(false);
#ifdef HAS_PLOTTING
            plotHorizontalAct->setVisible(true);
            plotVerticalAct->setVisible(true);
#endif /* HAS_PLOTTING */
        } else {
            // we have a cube
            exportSpecAsciiAct->setVisible(true);
            exportSpecFitsAct->setVisible(true);
            copySpecBufferAct->setVisible(true);
#ifdef HAS_PLOTTING
            plotSpecAct->setVisible(true);
#endif /* HAS_PLOTTING */
            copyImageBufferAct->setVisible(true);
            saveImageFitsAct->setVisible(true);
            lockPosAct->setVisible(true);
            useStdStarAct->setVisible(true);
#ifdef HAS_PLOTTING
            plotHorizontalAct->setVisible(false);
            plotVerticalAct->setVisible(false);
#endif /* HAS_PLOTTING */
        }

        if (sourceContinuumMarkers->hasAnyMarkers() ||
            sb->getCursorMarkers()->hasAnyMarkers())
        {
            saveMarkersAct->setEnabled(true);
            exportMarkersAct->setEnabled(true);
        } else {
            saveMarkersAct->setEnabled(false);
            exportMarkersAct->setEnabled(false);
        }

        if (sb->isMultiBufferChild()) {
            // MultiBuffer
            saveMBbufferAct->setVisible(true);
            copyMBbufferAct->setVisible(true);
        } else {
            // SingleBuffer
            saveMBbufferAct->setVisible(false);
            copyMBbufferAct->setVisible(false);
        }

        if (ok) {
            // store mouse position
            mouseX = m->x();
            mouseY = m->y();
            mousePos = mapToGlobal(m->pos());

            // show popup menu
            rightPopupMenu->exec(mousePos);
        }
    }
}

void QFitsView2D::mouseReleaseEvent(QMouseEvent *m) {
    if (m->button() == Qt::LeftButton) {
        QFitsSingleBuffer *sb = getMyBuffer();
        if (sb->isMultiBufferChild()) {
            int x = 0, y = 0;
            getMousePressedPos(&x, &y);
            // check for single click (check if mouse has moved since it has
            // been pressed, otherwise it it a colormap change)
            if ((m->pos().x() == x) && (m->pos().y() == y)) {
                sb->setMarkedSingleBuffer(sb);
            }
        }
    }
}

void QFitsView2D::keyPressEvent( QKeyEvent *e ) {
    QFitsSingleBuffer *sb = getMyBuffer();
    bool locked = (sb->getLockedSingleBuffer() != NULL);

    if (cursor().shape() != Qt::WaitCursor) {
        if (!(e->modifiers() & Qt::CTRL) &&
            !(e->modifiers() & Qt::ALT) &&
            !(e->modifiers() & Qt::SHIFT))
        {
            QPoint pos = cursor().pos();
            QCursor mycursor = cursor();

            switch (e->key()) {
                case Qt::Key_Up:
                    pos -= QPoint(0, 1);
                    mycursor.setPos(pos);
    #ifdef Q_OS_MACX
                    pos = mapFromGlobal(pos);
                    widgetToImage(pos.x(), pos.y());
    #endif
                    break;
                case Qt::Key_Down:
                    pos += QPoint(0, 1);
                    mycursor.setPos(pos);
    #ifdef Q_OS_MACX
                    pos = mapFromGlobal(pos);
                    widgetToImage(pos.x(), pos.y());
    #endif
                    break;
                case Qt::Key_Left:
                    pos -= QPoint(1, 0);
                    mycursor.setPos(pos);
    #ifdef Q_OS_MACX
                    pos = mapFromGlobal(pos);
                    widgetToImage(pos.x(), pos.y());
    #endif
                    break;
                case Qt::Key_Right:
                    pos += QPoint(1, 0);
                    mycursor.setPos(pos);
    #ifdef Q_OS_MACX
                    pos = mapFromGlobal(pos);
                    widgetToImage(pos.x(), pos.y());
    #endif
                    break;
                case Qt::Key_A:
                    setMouseTracking(false);
                    fitsMainWindow->spectrum->saveAscii(sb);
                    setMouseTracking(true);
                    break;
                case Qt::Key_F:
                    setMouseTracking(false);
                    fitsMainWindow->spectrum->saveFitsSpectrum(sb);
                    setMouseTracking(true);
                    break;
                case Qt::Key_G:
                    sb->setVisibleTool(Tools_2Dfit);
                    fitsMainWindow->toolsWidget->setCurrentWidget(fitsMainWindow->fitter2d);
                    fitsMainWindow->fitter2d->fitGauss();
//                    fitsMainWindow->toolsWidget->hideButton->raise();
//                    activateWindow();
                    break;
                case Qt::Key_D:
                case Qt::Key_P:
                    if ((mousemode == None) && !locked) {
                        initialpos = mapFromGlobal(pos);
                        currentpos = mapFromGlobal(pos);
                        setCursor(Qt::CrossCursor);
                        mousemode = drawDistMarker;
                    }
                    if (e->key() == Qt::Key_P) distancemode = Pixels;
                    else distancemode = Arcsecs;
                    break;
                case Qt::Key_L:
                    lockUnlockSpectrum(pos);
                    break;
                case Qt::Key_S:
                    if ((mousemode == None) && !locked) {
                        initialpos = mapFromGlobal(pos);
                        currentpos = mapFromGlobal(pos);
                        setCursor(Qt::CrossCursor);
                        mousemode = drawRectGreen;
                    }
                    break;
                case Qt::Key_C:
                    if ((mousemode == None) && !locked) {
                        initialpos = mapFromGlobal(pos);
                        currentpos = mapFromGlobal(pos);
                        setCursor(Qt::CrossCursor);
                        mousemode = drawRectRed;
                    }
                    break;
                case Qt::Key_X:
                    if ((mousemode == None) && !locked) {
                        initialpos = mapFromGlobal(pos);
                        currentpos = mapFromGlobal(pos);
                        setCursor(Qt::CrossCursor);
                        mousemode = drawRectWhite;
                    }
                    break;
                case Qt::Key_R:
                    sourceContinuumMarkers->deleteAllMarkers();
                    update();
                    getMyBuffer()->createManualSpectrum();
                    fitsMainWindow->viewingtools->updateRegionInfo();
                    break;
                case Qt::Key_M: {
                        setMarkposDrawMode(true);
                        sb->setVisibleTool(Tools_Markpos);
                        fitsMainWindow->toolsWidget->setCurrentWidget(fitsMainWindow->markposDialog);
                        fitsMainWindow->toolsWidget->show();
//                        if (!fitsMainWindow->markposDialog->isVisible()) {
//                            fitsMainWindow->markposDialog->showDialog();
//                            fitsMainWindow->activateWindow();
//                        }
                        double  imageXDbl   = 0.,
                                imageYDbl   = 0.;
                        widgetToImageNoSignal(mapFromGlobal(pos).x(), mapFromGlobal(pos).y(), &imageXDbl, &imageYDbl);
                        int imageX = (int)imageXDbl;
                        int imageY = (int)imageYDbl;
                        fitsMainWindow->markposNewPosition(imageX, imageY);
                    }
                    break;
                case Qt::Key_Escape: {
                        if (drawMode == markposMarker) {
                            fitsMainWindow->markposDialog->clearValues();
                            if (dpusermutex->tryLock()) {
                                setMarkposDrawMode(false);
                                fitsMainWindow->markposDialog->hide();
                                dpusermutex->unlock();
                            }
                            markpos_Positions.clear();
                            update(visibleRegion());
                        }
                    }
                    break;
                default:
                    fitsMainWindow->main_view->keyPressEvent(e);
                    break;
            }
            fitsMainWindow->main_view->keyPressEvent(e);
        }
    } else {
        fitsMainWindow->main_view->keyPressEvent(e);
    }
}

void QFitsView2D::keyReleaseEvent(QKeyEvent *e) {
    QFitsSingleBuffer *sb = getMyBuffer();
    bool locked = (sb->getLockedSingleBuffer() != NULL);

    switch (e->key()) {
        case Qt::Key_D:
        case Qt::Key_P:
            if (!e->isAutoRepeat() && !locked) {
                mousemode = None;
                update(visibleRegion());
                setCursor(Qt::ArrowCursor);
                fitsMainWindow->viewingtools->setDistanceInfo(0., 0., 0, 0, false);
            }
            break;
        case Qt::Key_S:
            if (!e->isAutoRepeat() && mousemode == drawRectGreen && !locked) {
                double  x1Dbl = 0.,
                        x2Dbl = 0.,
                        y1Dbl = 0.,
                        y2Dbl = 0.;
                widgetToImageNoSignal(initialpos.x(), initialpos.y(), &x1Dbl, &y1Dbl);
                widgetToImageNoSignal(currentpos.x(), currentpos.y(), &x2Dbl, &y2Dbl);
                int x1 = (int)x1Dbl,
                    x2 = (int)x2Dbl,
                    y1 = (int)y1Dbl,
                    y2 = (int)y2Dbl;

                if (x1 > x2) {
                    int tmp = x1;
                    x1 = x2;
                    x2 = tmp;
                }
                if (y1 > y2) {
                    int tmp = y1;
                    y1 = y2;
                    y2 = tmp;
                }

                for (int x = x1; x <= x2; x++) {
                    for (int y = y1; y <= y2; y++) {
                        if ((x > 0) && (y > 0) &&
                            (x <= sb->Naxis(1)) &&
                            (y <= sb->Naxis(2)))
                        {
                            sourceContinuumMarkers->addMarkerSource(x,y);
                        }
                    }
                }

                mousemode = None;
                update();
                getMyBuffer()->createManualSpectrum();
                fitsMainWindow->viewingtools->updateRegionInfo();
            }
            break;
        case Qt::Key_C:
            if (!e->isAutoRepeat() && mousemode == drawRectRed && !locked) {
                double x1Dbl = 0., x2Dbl = 0., y1Dbl = 0., y2Dbl = 0.;
                widgetToImageNoSignal(initialpos.x(), initialpos.y(), &x1Dbl, &y1Dbl);
                widgetToImageNoSignal(currentpos.x(), currentpos.y(), &x2Dbl, &y2Dbl);
                int x1 = (int)x1Dbl,
                    x2 = (int)x2Dbl,
                    y1 = (int)y1Dbl,
                    y2 = (int)y2Dbl;

                if (x1 > x2) {
                    int tmp = x1;
                    x1 = x2;
                    x2 = tmp;
                }
                if (y1 > y2) {
                    int tmp = y1;
                    y1 = y2;
                    y2 = tmp;
                }

                for (int x = x1; x <= x2; x++) {
                    for (int y = y1; y <= y2; y++) {
                        if ((x > 0) && (y > 0) &&
                            (x <= sb->Naxis(1)) &&
                            (y <= sb->Naxis(2)))
                        {
                            sourceContinuumMarkers->addMarkerContinuum(x, y);
                        }
                    }
                }

                mousemode = None;
                update();
                getMyBuffer()->createManualSpectrum();
            }
            break;
        case Qt::Key_X:
            if (!e->isAutoRepeat() && !locked) {
                double x1Dbl = 0., x2Dbl = 0., y1Dbl = 0., y2Dbl = 0.;
                widgetToImageNoSignal(initialpos.x(), initialpos.y(), &x1Dbl, &y1Dbl);
                widgetToImageNoSignal(currentpos.x(), currentpos.y(), &x2Dbl, &y2Dbl);
                int x1 = (int)x1Dbl,
                    x2 = (int)x2Dbl,
                    y1 = (int)y1Dbl,
                    y2 = (int)y2Dbl;

                if (x1 > x2) {
                    int tmp = x1;
                    x1 = x2;
                    x2 = tmp;
                }
                if (y1 > y2) {
                    int tmp = y1;
                    y1 = y2;
                    y2 = tmp;
                }

                for (int x = x1; x <= x2; x++) {
                    for (int y = y1; y <= y2; y++) {
                        if ((x > 0) && (y > 0) &&
                            (x <= sb->Naxis(1)) &&
                            (y <= sb->Naxis(2)))
                        {
                            sourceContinuumMarkers->deleteMarker(x,y);
                        }
                    }
                }

                mousemode = None;
                update();
                getMyBuffer()->createManualSpectrum();
                fitsMainWindow->viewingtools->updateRegionInfo();
            }
            break;
        default:
            break;
    }
}

void QFitsView2D::enterEvent(QEvent *e) {
    setFocus();
    myParent->enterBuffer();

    QString txt = "Mouse-left: change colormap";
    if (!completelyVisibleX() || !completelyVisibleY()) {
        txt += " | Mouse-right: center image";
    }

    if (fabs(getMyBuffer()->getCubeCenter(QFV::Default)) < 1e-10) { // if (center == 0)
        txt += " | Keys: d/p=measure distances, g=Gaussfit : "
               "l=(un)lock position";
    } else {
        txt += " | Keys: d/p=measure distances : g=Gaussfit : "
               "l=(un)lock position : "
               "s/c/x=source/continuum/delete spectral point : "
               "r=remove spectrum";
    }
    emit statusbartext(txt);

    if (myParent->getMyBuffer()->isMultiBufferChild()) {
        if (myParent->getMyBuffer()->isEmptyPrimary()) {
            fitsMainWindow->mytoolbar->enableControlsView1D(false);
        } else {
            fitsMainWindow->mytoolbar->enableControlsView1D(true);
        }
    } else {
        // no need to do anything here, we have just SB, this one is always active
    }
}

void QFitsView2D::leaveEvent (QEvent*) {
    myParent->leaveBuffer();
}

void QFitsView2D::widgetToImage(int nx, int ny) {
    QFitsSingleBuffer *sb = getMyBuffer();
    if ((sb != NULL) &&
        (sb->getDpData()->type == typeFits))
    {
        double  imageX = 0.,
                imageY = 0.,
                imageXnotFlipRot = 0.,
                imageYnotFlipRot = 0.;

        widgetToImageNoSignal(nx, ny,
                              &imageX, &imageY,
                              &imageXnotFlipRot, &imageYnotFlipRot);

        if (buffersLock.tryLockForRead()) {
            fitsMainWindow->updateTools((int)imageX,
                                        (int)imageY,
                                        (int)imageXnotFlipRot,
                                        (int)imageYnotFlipRot);
            buffersLock.unlock();
        }
    }
}

// widgetX/Y:          widget coordinates
// imageX/Y:           Fits coordinates (flipped, rotated etc.)
// imageX/YnotRotFlip: Fits coordinates (NOT flipped, NOT rotated etc.)
void QFitsView2D::widgetToImageNoSignal(int    widgetX, int    widgetY,
                                        double *imageX, double *imageY,
                                        double *imageXnotRotFlip, double *imageYnotRotFlip) {
    QFitsSingleBuffer *sb = getMyBuffer();
    if ((sb != NULL) &&
        (sb->getDpData()->type == typeFits) &&
        (sb->getDpData()->fvalue != NULL))
    {
        if (!(buffersLock.tryLockForRead())) {
            return;
        }

        double  xDbl      = widgetX - xoffset,
                yDbl      = widgetY - yoffset,
                pixCenter =0;// 0.5;    // correction: shift origin of pixel from center to lower-left

        // remember transformed (from widget to Fits coordinate space) values
        // without rotation and flip
        if ((imageXnotRotFlip != NULL) || (imageYnotRotFlip != NULL)) {
            double xorigDbl = xDbl,
                   yorigDbl = yDbl;

            sb->transCoordWidget2Image(&xorigDbl, &yorigDbl, sb->getZoomFactor(), height() - 2 * yoffset, false);
            if (imageXnotRotFlip != NULL) {
                *imageXnotRotFlip = xorigDbl + pixCenter;
            }
            if (imageYnotRotFlip != NULL) {
                *imageYnotRotFlip = yorigDbl + pixCenter;
            }
        }

        sb->transCoordWidget2Image(&xDbl, &yDbl, sb->getZoomFactor(), height() - 2 * yoffset);

        *imageX = xDbl + pixCenter;
        *imageY = yDbl + pixCenter;

        buffersLock.unlock();
    } else {
        *imageX = -1.;
        *imageY = -1.;
        if (imageXnotRotFlip != NULL) {
            *imageXnotRotFlip = -1.;
        }
        if (imageYnotRotFlip != NULL) {
            *imageYnotRotFlip = -1.;
        }
    }
}

void QFitsView2D::drawDistanceMarker(QPainter &p) {
    QFitsSingleBuffer *sb = getMyBuffer();
    bool inPixels = true;
    if ((sb != NULL) &&
        (sb->getDpData()->type == typeFits))
    {
        QReadLocker locker(&buffersLock);
        Fits *sbFits = sb->getDpData()->fvalue;
        if (sbFits != NULL) {
            double  x = 0., y = 0.,
                    xpos = 0., ypos = 0.,
                    ixpos = 0., iypos = 0.,
                    dx = 0., dy = 0.;
            int dix = 0, diy = 0;

/*            if ((mousemode == drawDistMarker) && (sbFits->hasRefPix())) { */
                inPixels = false;
                widgetToImageNoSignal(initialpos.x(), initialpos.y(), &x, &y);
                x = (int)x;
                y = (int)y;

                double cd1_1 = 0.0, cd2_2 = 0.0, cd1_2 = 0.0, cd2_1 = 0.0, crota2 = 0.0;
                sbFits->GetFloatKey("CROTA2", &crota2),
                sbFits->GetFloatKey("CD1_1", &cd1_1),
                sbFits->GetFloatKey("CD2_1", &cd2_1),
                sbFits->GetFloatKey("CD1_2", &cd1_2),
                sbFits->GetFloatKey("CD2_2", &cd2_2),

                worldpos(x, y,
                         sbFits->getCRVAL(1),
                         sbFits->getCRVAL(2),
                         sbFits->getCRPIX(1),
                         sbFits->getCRPIX(2),
                         sbFits->getCDELT(1),
                         sbFits->getCDELT(2),
                         crota2, cd1_1, cd2_1, cd1_2, cd2_2,
                         sbFits->crtype,
                         &xpos, &ypos);

                inPixels = (x == xpos && y == ypos);
                ixpos = x;
                iypos = y;
                dix = -x;
                diy = -y;
/*                ixpos = xpos;
                iypos = ypos; */
                widgetToImageNoSignal(currentpos.x(), currentpos.y(), &x, &y);
                x = (int)x;
                y = (int)y;

                sbFits->GetFloatKey("CROTA2", &crota2),
                sbFits->GetFloatKey("CD1_1", &cd1_1),
                sbFits->GetFloatKey("CD2_1", &cd2_1),
                sbFits->GetFloatKey("CD1_2", &cd1_2),
                sbFits->GetFloatKey("CD2_2", &cd2_2),
                worldpos(x, y, sbFits->getCRVAL(1),
                         sbFits->getCRVAL(2),
                         sbFits->getCRPIX(1),
                         sbFits->getCRPIX(2),
                         sbFits->getCDELT(1),
                         sbFits->getCDELT(2),
                         crota2, cd1_1, cd2_1, cd1_2, cd2_2,
                         sbFits->crtype,
                         &xpos, &ypos);

                inPixels = (inPixels && x == xpos && y == ypos);

/*                dx = (xpos - ixpos) * 3600.;
                dy = (ypos - iypos) * 3600.; */
                dix += x;
                diy += y;

                xpos = x;
                ypos = y;
                dp_debug("DRAW MARKER --------------------------------------");
                dp_debug("DRAW MARKER xpos: %g, ypos: %g", xpos, ypos);
                dp_debug("DRAW MARKER ixpos: %g, iypos: %g", ixpos, iypos);

/*                dix = (int)(xpos - ixpos);
                diy = (int)(ypos - iypos);

                if (sbFits->hasRefPix()) { */
                    dx = (xpos - ixpos) * sbFits->getCDELT(1) * 3600.;
                    dy = (ypos - iypos) * sbFits->getCDELT(2) * 3600.;
/*                } else {
                    dx = dix;
                    dy = diy;
                    inPixels = true;
                }
*/
                if ((sb->getRotation() == 90) || (sb->getRotation() == 270)) {
                    std::swap(dx, dy);
                    std::swap(dix, diy);
                }

                fitsMainWindow->viewingtools->setDistanceInfo(dx, dy, dix, diy, inPixels);
/*
            } else {
                widgetToImageNoSignal(initialpos.x(), initialpos.y(), &x, &y);
                ixpos = (int)x;
                iypos = (int)y;
                widgetToImageNoSignal(currentpos.x(), currentpos.y(), &x, &y);
                xpos = (int)x;
                ypos = (int)y;
                dx = xpos - ixpos;
                dy = ypos - iypos;
            }
            if ((sb->getRotation() == 90) ||
                (sb->getRotation() == 270))
            {
                double _tmp;
                _tmp = dx;
                dx = dy;
                dy = _tmp;
            }
*/
            p.setPen(QColor(255, 255, 255));
            p.setBrush(QColor(0, 0, 0));
            p.drawLine(initialpos, currentpos);
            QString text;
            if (inPixels || distancemode == Pixels) {
                text = QString::number(sqrt(dix*dix+diy*diy), 'g', 4);
            } else {
                text = QString::number(sqrt(dx*dx+dy*dy), 'g', 4);
                text += "''"; // unit is arcsec
            }

            QFontMetrics metrics(font());
            int w = metrics.boundingRect(text).width(),
                h = metrics.boundingRect(text).height();
            QPoint point = (currentpos + initialpos) / 2;
            if (currentpos.x() > initialpos.x()) {
                point.setX(point.x() - w);
            }
            if (currentpos.y() > initialpos.y()) {
                point.setY(point.y() + h/2);
            }

            p.setPen(QColor(0, 0, 0));
            p.drawRoundedRect(point.x()-2, point.y()+2, w+4, -h, 50, 99);
            p.setPen(QColor(255, 255, 255));
            p.drawText(point, text);

            p.drawLine(currentpos.x(), initialpos.y(), currentpos.x(), currentpos.y());
            if (inPixels || distancemode == Pixels) {
                text = QString::number(diy, 'g', 4);
            } else {
                text = QString::number(dy, 'g', 4);
                text += "''"; // unit is arcsec
            }
            w = metrics.boundingRect(text).width();
            if (currentpos.x() > initialpos.x()) {
                point.setX(currentpos.x() + 2);
            } else {
                point.setX(currentpos.x() - w);
            }

            p.setPen(QColor(0, 0, 0));
            p.drawRoundedRect(point.x()-2, point.y()+2, w+4, -h, 50, 99);
            p.setPen(QColor(255, 255, 255));
            p.drawText(point, text);

            p.drawLine(initialpos.x(), initialpos.y(), currentpos.x(), initialpos.y());
            if (inPixels || distancemode == Pixels) {
                text = QString::number(dix, 'g', 4);
            } else {
                text = QString::number(dx, 'g', 4);
                text += "''"; // unit is arcsec
            }
            w = metrics.boundingRect(text).width();
            point.setX((currentpos.x() + initialpos.x() - w) / 2);
            if (currentpos.y() > initialpos.y()) {
                point.setY(initialpos.y() - 2);
            } else {
                point.setY(initialpos.y() + h);
            }

            p.setPen(QColor(0, 0, 0));
            p.drawRoundedRect(point.x()-2, point.y()+2, w+4, -h, 50, 99);
            p.setPen(QColor(255, 255, 255));
            p.drawText(point, text);
        }
    }
}

void QFitsView2D::drawRectMarker(QPainter &p, QColor color) {
    p.setPen(QPen(color));
    p.setBrush(QColor(color.red(), color.green(), color.blue(), 50));
    p.drawRect(initialpos.x(), initialpos.y(),
               currentpos.x() - initialpos.x(), currentpos.y() - initialpos.y());
}

std::string QFitsView2D::getBufferIndex() {
    return myParent->getBufferIndex();
}

QFitsSingleBuffer* QFitsView2D::getMyBuffer() {
    return myParent->getMyBuffer();
}

void QFitsView2D::lockUnlockSpectrum(QPoint &pos) {
    QFitsSingleBuffer   *sb_underMouse  = getMyBuffer(),
                        *sb_locked      = sb_underMouse->getLockedSingleBuffer();

    if (!sb_underMouse->getHasCube() ||
        ((sb_locked != NULL) && (sb_underMouse != sb_locked)))
    {
        // when there is a locked SB and cursor moved to another SB skip the rest
        return;
    }

    // get pixel position in Fits
    QPoint spectrumpos = mapFromGlobal(pos);
    if (sb_locked == NULL) {
        double x = 0., y = 0.;
        widgetToImageNoSignal(spectrumpos.x(), spectrumpos.y(), &x, &y);

        if ((x >= 1) && (x <= sb_underMouse->Naxis(1)) &&
            (y >= 1) && (y <= sb_underMouse->Naxis(2)))
        {
            lockPosAct->setText("Unlock position");

            sb_underMouse->lockSB(true, (int)x, (int)y);
        }
    } else {
        // update spectrum immediately after unlocking
        // before a mouse move was needed
        widgetToImage(spectrumpos.x(), spectrumpos.y());

        lockPosAct->setText("Lock position");

        sb_locked->lockSB(false);

        // cursor could have moved to another QFitsSingleBuffer
        fitsMainWindow->setActualSB(sb_underMouse);
    }
    update();
}

bool QFitsView2D::completelyVisibleX() {
    QFitsSingleBuffer   *sb = getMyBuffer();
    int     widgetVisW      = visibleRegion().boundingRect().width();
    double  imageWzoom      = sb->getImage()->width() * sb->getZoomFactor();

    if (widgetVisW < imageWzoom) {
        return false;
    } else {
        return true;
    }
}

bool QFitsView2D::completelyVisibleY() {
    QFitsSingleBuffer   *sb = getMyBuffer();
    int     widgetVisH      = visibleRegion().boundingRect().height();
    double  imageHzoom      = sb->getImage()->height() * sb->getZoomFactor();

    if (widgetVisH < imageHzoom) {
        return false;
    } else {
        return true;
    }
}

void QFitsView2D::updateLongslitMarker(int xcenter, int ycenter, double angle,
                                     int slitwidth, double opening_angle) {
    extra_int[0] = xcenter;
    extra_int[1] = ycenter;
    extra_double[0] = angle;
    extra_int[2] = slitwidth;
    extra_double[1] = opening_angle;
    if (slitwidth < 0)
        drawMode = Simple;
    else
        drawMode = longslitMarker;

    update(visibleRegion());
}

void QFitsView2D::updateEllipticalProfileMarker(int xcenter, int ycenter,
                                               double angle, double ratio,
                                               int slitwidth) {
    extra_int[0] = xcenter;
    extra_int[1] = ycenter;
    extra_double[0] = angle;
    extra_double[1] = ratio;
    extra_int[2] = slitwidth;
    if (slitwidth < 0) {
        drawMode = Simple;
    } else {
        drawMode = ellipticalProfileMarker;
    }

    update(visibleRegion());
}

void QFitsView2D::updateMarkposMarker(QVector<int> centers) {
    markpos_Positions = centers;
    if (centers.size() == 0) {
//        drawMode = Simple;
    } else {
//        drawMode = markposMarker;
    }

    update(visibleRegion());
}

void QFitsView2D::setMarkposDrawMode(bool enable) {
    if (enable) {
        drawMode = markposMarker;
        QPixmap pm(cursor_select);
        setCursor(QCursor(pm, 16, 18));
    } else {
        drawMode = Simple;
        setCursor(Qt::ArrowCursor);
    }
}

void QFitsView2D::printImage() {
    QPrinter printer;
    QPrintDialog printerDialog(&printer, this);

    if (getMyBuffer()->getImage() != NULL) {
        if (printerDialog.exec()) {
            QPainter pa;
            float ratio;

            if (!pa.begin(&printer)) {
                return;
            }
            QApplication::setOverrideCursor(Qt::WaitCursor);
            ratio = (float)printer.width() / (float)printer.height();
            if (getMyBuffer()->getImage()->width() > getMyBuffer()->getImage()->height()) {
                 pa.setWindow(0, 0, getMyBuffer()->getImage()->width(),
                                    getMyBuffer()->getImage()->width()/ratio);
            } else {
                pa.setWindow(0, 0, getMyBuffer()->getImage()->height()*ratio,
                                   getMyBuffer()->getImage()->height());
            }
            pa.drawImage(0, 0, *(getMyBuffer()->getImage()));
            pa.end();
            QApplication::restoreOverrideCursor();
        }
    }
}

void QFitsView2D::drawLongslitMarker(QPainter &p, int xcenter, int ycenter,
                                     double angle, int slitwidth, double opening_angle)
{
    int x = xcenter,
        y = ycenter;

    getMyBuffer()->imageToWidget(&x, &y);

    if (getMyBuffer()->getFlipX()) {
        angle = -angle;
    }
    if (getMyBuffer()->getFlipY()) {
        angle = 180. - angle;
    }

    double zoomFactor = getMyBuffer()->getZoomFactor();

    p.translate(x+(int)(zoomFactor/2.), y+(int)(zoomFactor/2.));
    p.rotate(angle - getMyBuffer()->getRotation());
    p.setPen(QColor(255, 255, 255));
//    p.drawRect(-(double)slitwidth/2.*zoomFactor, -height(),
//               (double)slitwidth*zoomFactor, height()*2);
    p.drawEllipse(-(double)slitwidth/2.*zoomFactor-2.,
                  -(double)slitwidth/2.*zoomFactor-2.,
                  (double)slitwidth*zoomFactor+5.,
                  (double)slitwidth*zoomFactor+5.);
    p.drawLine(0, 0, 0, -20);
    p.drawLine(0, -20, -5, -15);
    p.drawLine(0, -20, 5, -15);

    double opening_height = height() * tan(opening_angle * M_PI / 180.);
    p.drawLine(-(double)slitwidth/2.*zoomFactor, 0, -(double)slitwidth/2.*zoomFactor - opening_height, height());
    p.drawLine(-(double)slitwidth/2.*zoomFactor, 0, -(double)slitwidth/2.*zoomFactor - opening_height, -height());
    double fullwidth = (double)slitwidth/2.*zoomFactor;
    if (slitwidth % 2 == 1) fullwidth++;
    p.drawLine(fullwidth, 0, fullwidth + opening_height, -height());
    p.drawLine(fullwidth, 0, fullwidth + opening_height, height());
}

void QFitsView2D::drawEllipticalProfileMarker(QPainter &p, int xcenter, int ycenter,
                                            double angle, double ratio, int slitwidth) {
    int x = xcenter,
        y = ycenter;

    getMyBuffer()->imageToWidget(&x, &y);

    if (getMyBuffer()->getFlipX()) {
        angle = -angle;
    }
    if (getMyBuffer()->getFlipY()) {
        angle = 180. - angle;
    }

    double zoomFactor = getMyBuffer()->getZoomFactor();

    p.translate(x+(int)(zoomFactor/2.), y+(int)(zoomFactor/2.));
    p.rotate(-angle - getMyBuffer()->getRotation());
    p.setPen(QPen(QColor(255, 255, 255, 200),
                  (int)(slitwidth*zoomFactor)<1 ? 1 : (int)(slitwidth*zoomFactor)));
    p.drawEllipse(-(double)slitwidth*zoomFactor*1.5,
                  -(double)slitwidth*zoomFactor*1.5/ratio,
                  (double)slitwidth*zoomFactor*3.,
                  (double)(slitwidth*zoomFactor/ratio*3.));
    p.setPen(QPen(QColor(255, 255, 255), 1));
    p.drawLine(0, 0, 0, -20);
    p.drawLine(0, -20, -5, -15);
    p.drawLine(0, -20, 5, -15);
}

void QFitsView2D::drawMarkposMarker(QPainter &p) {
    int x, y, xs, ys;
    double zoomFactor = getMyBuffer()->getZoomFactor();
    p.setPen(QPen(QColor(255, 255, 255, 200), 3));
    for (int i = 0; i < markpos_Positions.size(); i+=2) {

        xs = x = markpos_Positions.at(i);
        ys = y = markpos_Positions.at(i+1);

        getMyBuffer()->imageToWidget(&x, &y);

//        p.drawEllipse(QPoint(x, y), 5, 5);

        x += (int)(zoomFactor/2.);
        y += (int)(zoomFactor/2.);

        p.drawLine(x-10, y, x-5, y);
        p.drawLine(x+5, y, x+10, y);
        p.drawLine(x, y-10, x, y-5);
        p.drawLine(x, y+5, x, y+10);
        p.drawText(x+7, y+10, "(" + QString::number(xs) + "," + QString::number(ys) + ")");
//        p.translate(x+(int)(zoomFactor/2.), y+(int)(zoomFactor/2.));

//        p.setPen(QPen(QColor(255, 255, 255, 200), 2));

//        p.drawEllipse(-(double)slitwidth*zoomFactor*1.5,
//                  -(double)slitwidth*zoomFactor*1.5/ratio,
//                  (double)slitwidth*zoomFactor*3.,
//                  (double)(slitwidth*zoomFactor/ratio*3.));
    }
}

void QFitsView2D::setRotation(int newRotation) {
    QFitsSingleBuffer *sb = getMyBuffer();
    if (newRotation == sb->getRotation()) {
        return;
    }

    if (newRotation < 4) {
        newRotation *= 90;
    }

    Fits _tmpimage;
    _tmpimage.create(sb->getImage()->width(),
                     sb->getImage()->height(), I1);

    // set image pixels
    unsigned char *i1data = _tmpimage.i1data;
    for (int y = sb->getImage()->height() - 1; y >= 0; y--) {
        uchar *p = sb->getImage()->scanLine(y);
        for (int x = 0; x < sb->getImage()->width(); x++) {
            *(i1data++) = *p++;
        }
    }

    if (newRotation <= 270) {
        int angle = newRotation - sb->getRotation();
        if (angle < 0)
            angle += 360;
        if (angle >= 360)
            angle -= 360;
        _tmpimage.rot90(angle);
        sb->setRotationVal(newRotation);
        if ((_tmpimage.Naxis(1) != sb->getImage()->width()) ||
            (_tmpimage.Naxis(2) != sb->getImage()->height()))
        {
            if (sb->getImage() != NULL) {
                delete sb->getImage();
            }
            sb->setImage(new QImage(_tmpimage.Naxis(1),
                                         _tmpimage.Naxis(2),
                                         QImage::Format_Indexed8));
            sb->getImage()->setColorCount(NCOLORS);

        }
        if ((angle == 90) || (angle == 270)) {
            bool flip;
            flip = sb->getFlipX();
            sb->setFlipX(sb->getFlipY());
            sb->setFlipY(flip);
        }
    } else {
        if (newRotation == 301) {
            _tmpimage.flip(1);
            sb->setFlipX(!sb->getFlipX());
        } else if (newRotation == 302) {
            _tmpimage.flip(2);
            sb->setFlipY(!sb->getFlipY());
        }
    }

    i1data = _tmpimage.i1data;

    // set image pixels
    for (int y = sb->getImage()->height() - 1; y >= 0; y--) {
        uchar *p = sb->getImage()->scanLine(y);
        for (int x = 0; x < sb->getImage()->width(); x++) {
            *p++ = *(i1data++);
        }
    }

    sb->updateScaling();

    myParent->setImageCenter(sb->getXcenter(), sb->getYcenter());
}

void QFitsView2D::setFlipX(bool newflip) {
    if (newflip != getMyBuffer()->getFlipX()) {
        setRotation(301);
    }
}

void QFitsView2D::setFlipY(bool newflip) {
    if (newflip != getMyBuffer()->getFlipY()) {
        setRotation(302);
    }
}

void QFitsView2D::setScrollerWidgetCenter(double Wx, double Wy) {
    Wx -= (double)visibleRegion().boundingRect().width() / 2.,
    Wy -= (double)visibleRegion().boundingRect().height() / 2.;

    doScrollerUpdate = false;
    myParent->getScr()->horizontalScrollBar()->setSliderPosition((int)Wx);
    myParent->getScr()->verticalScrollBar()->setSliderPosition((int)Wy);
    doScrollerUpdate = true;
}

void QFitsView2D::setScrollerImageCenter(double x, double y) {
    QFitsSingleBuffer *sb = getMyBuffer();

    sb->transCoordImage2Widget(&x, &y, sb->getZoomFactor());

    setScrollerWidgetCenter(x, y);
}

void QFitsView2D::applyZoom() {
//printf("*** QFitsView2D::applyZoom() *****zoomFactor: %g *************\n", getMyBuffer()->getZoomFactor());fflush(stdout);
    QFitsSingleBuffer *sb = getMyBuffer();

    double  zoomFactor      = sb->getZoomFactor();
    int     scrollerWidth   = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent),
            widgetVisW      = visibleRegion().boundingRect().width(),
            widgetVisH      = visibleRegion().boundingRect().height(),
            imageW          = sb->getImage()->width(),
            imageH          = sb->getImage()->height();
    double  imageWzoom      = imageW * zoomFactor,
            imageHzoom      = imageH * zoomFactor;

    if (completelyVisibleX()) {
        // completely visible in x
        sb->setWidthVisible(imageW);
        sb->setXcenter((double)imageW / 2.);
    } else {
        // partially visible in x
        double tmp = 0.;
        if (imageHzoom <= widgetVisH) {
            tmp = imageW * widgetVisW / imageWzoom;
        } else {
            // vertical scrollbar is about to show
            tmp = imageW * (widgetVisW - scrollerWidth) / imageWzoom;
        }
        sb->setWidthVisible(tmp);
    }

    if (completelyVisibleY()) {
        // completely visible in y
        sb->setHeightVisible(imageH);
        sb->setYcenter((double)imageH / 2.);
    } else {
        // partially visible in y
        double tmp = 0.;
        if (imageWzoom <= widgetVisW) {
            tmp = imageH * widgetVisH / imageHzoom;
        } else {
            // horizontal scrollbar is about to show
            tmp = imageH * (widgetVisH - scrollerWidth) / imageHzoom;
        }
        sb->setHeightVisible(tmp);
    }

//printf("*** View2D    widget     w: %d, h: %d\n", width(), height()); fflush(stdout);
//printf("***           visRegion  w: %d, h: %d, l: %d, t: %d\n", visibleRegion().boundingRect().width(), visibleRegion().boundingRect().height(), visibleRegion().boundingRect().left(), visibleRegion().boundingRect().top()); fflush(stdout);
//printf("*** Widget2D  widget     w: %d, h: %d\n", myParent->width(), myParent->height()); fflush(stdout);
//printf("***           visRegion  w: %d, h: %d, l: %d, t: %d\n", myParent->visibleRegion().boundingRect().width(), myParent->visibleRegion().boundingRect().height(), myParent->visibleRegion().boundingRect().left(), myParent->visibleRegion().boundingRect().top()); fflush(stdout);
//printf("*** scroller  wid/hei    w: %d, h: %d\n", myParent->getScr()->width(), myParent->getScr()->height()); fflush(stdout);
//printf("***           visRegion  w: %d, h: %d, l: %d, t: %d\n", myParent->getScr()->visibleRegion().boundingRect().width(), myParent->getScr()->visibleRegion().boundingRect().height(), myParent->getScr()->visibleRegion().boundingRect().left(),myParent->getScr()->visibleRegion().boundingRect().top());fflush(stdout);
//printf("***           isVis      h: %d, v: %d\n", (int)myParent->getScr()->horizontalScrollBar()->isVisible(), (int)myParent->getScr()->verticalScrollBar()->isVisible()); fflush(stdout);
//printf("***           val (pos)  h: %d, v: %d (h: %d, v: %d)\n", myParent->getScr()->horizontalScrollBar()->value(), myParent->getScr()->verticalScrollBar()->value(), myParent->getScr()->horizontalScrollBar()->sliderPosition(), myParent->getScr()->verticalScrollBar()->sliderPosition()); fflush(stdout);
//printf("***           PageStep   h: %d, v: %d\n", myParent->getScr()->horizontalScrollBar()->pageStep(), myParent->getScr()->verticalScrollBar()->pageStep()); fflush(stdout);
//printf("***           minimum    h: %d, v: %d\n", myParent->getScr()->horizontalScrollBar()->minimum(), myParent->getScr()->verticalScrollBar()->minimum()); fflush(stdout);
//printf("***           maximum    h: %d, v: %d\n", myParent->getScr()->horizontalScrollBar()->maximum(), myParent->getScr()->verticalScrollBar()->maximum()); fflush(stdout);
//printf("*** SB  x/yCenter        x: %g, y: %g\n", sb->getXcenter(), sb->getYcenter()); fflush(stdout);
//printf("*** SB  Height/WidthVis  w: %g, h: %g\n", sb->getWidthVisible(), sb->getHeightVisible()); fflush(stdout);
//printf("*** View2D::x/yoffset    x: %d, y: %d\n", xoffset, yoffset); fflush(stdout);

    // important for notifying the scroller when to show up
    setMinimumSize(imageWzoom, imageHzoom);

    // notify the scroller where to position
    setScrollerImageCenter(sb->getXcenter(), sb->getYcenter());

    fitsMainWindow->updateTotalVisibleRect();
    update();
//printf("*** (after)\n");
//printf("*** View2D    widget     w: %d, h: %d\n", width(), height()); fflush(stdout);
//printf("***           visRegion  w: %d, h: %d, l: %d, t: %d\n", visibleRegion().boundingRect().width(), visibleRegion().boundingRect().height(), visibleRegion().boundingRect().left(), visibleRegion().boundingRect().top()); fflush(stdout);
//printf("*** Widget2D  widget     w: %d, h: %d\n", myParent->width(), myParent->height()); fflush(stdout);
//printf("***           visRegion  w: %d, h: %d, l: %d, t: %d\n", myParent->visibleRegion().boundingRect().width(), myParent->visibleRegion().boundingRect().height(), myParent->visibleRegion().boundingRect().left(), myParent->visibleRegion().boundingRect().top()); fflush(stdout);
//printf("*** scroller  wid/hei    w: %d, h: %d\n", myParent->getScr()->width(), myParent->getScr()->height()); fflush(stdout);
//printf("***           visRegion  w: %d, h: %d, l: %d, t: %d\n", myParent->getScr()->visibleRegion().boundingRect().width(), myParent->getScr()->visibleRegion().boundingRect().height(), myParent->getScr()->visibleRegion().boundingRect().left(),myParent->getScr()->visibleRegion().boundingRect().top());fflush(stdout);
//printf("***           isVis      h: %d, v: %d\n", (int)myParent->getScr()->horizontalScrollBar()->isVisible(), (int)myParent->getScr()->verticalScrollBar()->isVisible()); fflush(stdout);
//printf("***           val (pos)  h: %d, v: %d (h: %d, v: %d)\n", myParent->getScr()->horizontalScrollBar()->value(), myParent->getScr()->verticalScrollBar()->value(), myParent->getScr()->horizontalScrollBar()->sliderPosition(), myParent->getScr()->verticalScrollBar()->sliderPosition()); fflush(stdout);
//printf("***           PageStep   h: %d, v: %d\n", myParent->getScr()->horizontalScrollBar()->pageStep(), myParent->getScr()->verticalScrollBar()->pageStep()); fflush(stdout);
//printf("***           minimum    h: %d, v: %d\n", myParent->getScr()->horizontalScrollBar()->minimum(), myParent->getScr()->verticalScrollBar()->minimum()); fflush(stdout);
//printf("***           maximum    h: %d, v: %d\n", myParent->getScr()->horizontalScrollBar()->maximum(), myParent->getScr()->verticalScrollBar()->maximum()); fflush(stdout);
//printf("*** SB  x/yCenter        x: %g, y: %g\n", sb->getXcenter(), sb->getYcenter()); fflush(stdout);
//printf("*** SB  Height/WidthVis  w: %g, h: %g\n", sb->getWidthVisible(), sb->getHeightVisible()); fflush(stdout);
//printf("*** View2D::x/yoffset    x: %d, y: %d\n", xoffset, yoffset); fflush(stdout);
//printf("***********************************************\n");fflush(stdout);
}

void QFitsView2D::scrollerMoved(int x, int y) {
    if (doScrollerUpdate) {
        QFitsSingleBuffer *sb = getMyBuffer();

        double  Sx      = x,
                Sy      = y,
                Sx_max  = myParent->getScr()->horizontalScrollBar()->maximum(),
                Sy_max  = myParent->getScr()->verticalScrollBar()->maximum(),
                Wx_max  = width(),
                Wy_max  = height(),
                vRx     = visibleRegion().boundingRect().width(),
                vRy     = visibleRegion().boundingRect().height();
        int     Wx      = 0,
                Wy      = 0;

        // translate coordinates from scroller space to widget space
        Wx = (int)(floor(Sx * (Wx_max - vRx) / Sx_max + vRx / 2.));
        Wy = (int)(floor(Sy * (Wy_max - vRy) / Sy_max + vRy / 2.));

        double  imageX = 0,
                imageY = 0;
//printf("4)      w2i  xIn:  %d, yIn:  %d\n", Wx, Wy); fflush(stdout);
        widgetToImageNoSignal(Wx, Wy, &imageX, &imageY);
//printf("        w2i  xOut: %g, yOut: %g\n", imageX, imageY); fflush(stdout);

        imageX -= 0.5;//1.;
        imageY -= 0.5;//1.;

        if ((imageX > 0) && (imageY > 0)) {
            sb->setXcenter(imageX);
            sb->setYcenter(imageY);
        }
        fitsMainWindow->updateTotalVisibleRect();
    }
}
