714 lines
25 KiB
C++
714 lines
25 KiB
C++
#include "mainwindow.h"
|
|
#include <QScrollArea>
|
|
#include <QDir>
|
|
#include <QKeyEvent>
|
|
#include <QMenu>
|
|
#include <QMenuBar>
|
|
#include <QFileDialog>
|
|
#include <QStandardPaths>
|
|
#include <QMessageBox>
|
|
#include <QProgressDialog>
|
|
#include <QDebug>
|
|
#include <QDockWidget>
|
|
#include <QActionGroup>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <QSettings>
|
|
#include <QGuiApplication>
|
|
#include <QThreadPool>
|
|
#include <QStatusBar>
|
|
#include <QImageReader>
|
|
#include <QMimeDatabase>
|
|
#include "loadrunable.h"
|
|
#include "markedfiles.h"
|
|
#include "about.h"
|
|
#include "statusbar.h"
|
|
#include "settingsdialog.h"
|
|
#include "histogram.h"
|
|
#include "batchprocessing.h"
|
|
|
|
#ifdef __linux__
|
|
#include <sys/ioctl.h>
|
|
#include <linux/btrfs.h>
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
bool moveToTrash(const QString &path);
|
|
|
|
int MainWindow::socketPair[2] = {0, 0};
|
|
|
|
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|
{
|
|
qRegisterMetaType<ImageInfoData>("ImageInfoData");
|
|
qRegisterMetaType<std::shared_ptr<RawImage>>("std::shared_ptr<RawImage>");
|
|
|
|
SettingsDialog::loadSettings();
|
|
|
|
QStringList nameFilter;
|
|
_saveFilter = tr("FITS (*.fits *.fit);;XISF (*.xisf);;");
|
|
_openFilter = tr("Images (");
|
|
QMimeDatabase db;
|
|
auto supportedFormats = QImageReader::supportedMimeTypes();
|
|
QStringList filters;
|
|
for(auto format : supportedFormats)
|
|
{
|
|
QMimeType mimeType = db.mimeTypeForName(format);
|
|
_saveFilter.append(mimeType.filterString() + ";;");
|
|
_openFilter.append("*.");
|
|
_openFilter.append(mimeType.suffixes().join(" *."));
|
|
_openFilter.append(" ");
|
|
nameFilter.append(mimeType.suffixes());
|
|
}
|
|
_openFilter.append("*.fit *.fits *.xisf *.cr2 *.cr3 *.nef *.dng)");
|
|
_openFilter.append(tr(";;All files (*)"));
|
|
nameFilter.append({"fit", "fits", "xisf", "cr2", "cr3", "nef", "dng"});
|
|
QImageReader::setAllocationLimit(0);
|
|
|
|
m_info = new ImageInfo(this);
|
|
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
|
|
infoDock->setWidget(m_info);
|
|
infoDock->setObjectName("infoDock");
|
|
addDockWidget(Qt::LeftDockWidgetArea, infoDock);
|
|
resize(800, 600);
|
|
setStatusBar(new QStatusBar(this));
|
|
|
|
m_database = new Database(this);
|
|
if(!m_database->init())
|
|
QMessageBox::critical(this, tr("Can't open DB"), tr("Can't open SQLITE database"));
|
|
|
|
m_imageGL = new ImageScrollAreaGL(m_database, this);
|
|
setCentralWidget(m_imageGL);
|
|
|
|
StatusBar *statusBar = new StatusBar(this);
|
|
setStatusBar(statusBar);
|
|
connect(m_imageGL->imageWidget(), &ImageWidget::status, statusBar, &StatusBar::newStatus);
|
|
|
|
m_stretchPanel = new StretchToolbar(this);
|
|
connect(m_stretchPanel, &StretchToolbar::paramChanged, m_imageGL->imageWidget(), &ImageWidget::setMTFParams);
|
|
connect(m_stretchPanel, &StretchToolbar::autoStretch, [&](){ m_stretchPanel->stretchImage(m_ringList->currentImage().get()); });
|
|
connect(m_stretchPanel, &StretchToolbar::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
|
|
connect(m_stretchPanel, &StretchToolbar::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
|
|
connect(m_stretchPanel, &StretchToolbar::falseColor, m_imageGL->imageWidget(), &ImageWidget::falseColor);
|
|
|
|
m_ringList = new ImageRingList(m_database, nameFilter, this);
|
|
m_filesystem = new FilesystemWidget(m_ringList, this);
|
|
connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int)));
|
|
connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort);
|
|
connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort);
|
|
|
|
m_filetree = new Filetree(this);
|
|
connect(m_filetree, &Filetree::fileSelected, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
|
connect(m_filetree, &Filetree::copyFiles, [this](const QString &path){ copyOrMove(true, path); });
|
|
connect(m_filetree, &Filetree::moveFiles, [this](const QString &path){ copyOrMove(false, path); });
|
|
connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir));
|
|
|
|
m_databaseView = new DataBaseView(m_database, this);
|
|
connect(m_databaseView, SIGNAL(loadFile(QString)), this, SLOT(loadFile(QString)));
|
|
|
|
addToolBar(Qt::TopToolBarArea, m_stretchPanel);
|
|
|
|
QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this);
|
|
filesystemDock->setWidget(m_filesystem);
|
|
filesystemDock->setObjectName("filesystemDock");
|
|
addDockWidget(Qt::LeftDockWidgetArea, filesystemDock);
|
|
|
|
QDockWidget *databaseViewDock = new QDockWidget(tr("FITS/XISF files database"), this);
|
|
databaseViewDock->setWidget(m_databaseView);
|
|
databaseViewDock->setObjectName("databaseViewDock");
|
|
databaseViewDock->hide();
|
|
addDockWidget(Qt::BottomDockWidgetArea, databaseViewDock);
|
|
|
|
QDockWidget *filetreeDock = new QDockWidget(tr("File tree"), this);
|
|
filetreeDock->setWidget(m_filetree);
|
|
filetreeDock->setObjectName("filetreeDock");
|
|
databaseViewDock->hide();
|
|
addDockWidget(Qt::LeftDockWidgetArea, filetreeDock);
|
|
|
|
Histogram *histogram = new Histogram(this);
|
|
QDockWidget *histogramDock = new QDockWidget(tr("Histogram"), this);
|
|
histogramDock->setWidget(histogram);
|
|
histogramDock->setObjectName("histogramDock");
|
|
histogramDock->hide();
|
|
addDockWidget(Qt::LeftDockWidgetArea, histogramDock);
|
|
|
|
setWindowTitle(tr("Tenmon"));
|
|
|
|
connect(m_ringList, SIGNAL(pixmapLoaded(Image*)), this, SLOT(pixmapLoaded(Image*)));
|
|
connect(m_ringList, SIGNAL(currentImageChanged(int)), this, SLOT(updateWindowTitle()));
|
|
connect(m_ringList, SIGNAL(infoLoaded(ImageInfoData)), m_info, SLOT(setInfo(const ImageInfoData&)));
|
|
connect(m_ringList, SIGNAL(currentImageChanged(int)), m_filesystem, SLOT(selectFile(int)));
|
|
connect(m_ringList, &ImageRingList::thumbnailLoaded, m_imageGL->imageWidget(), &ImageWidget::thumbnailLoaded);
|
|
connect(m_ringList, &ImageRingList::pixmapLoaded, m_stretchPanel, &StretchToolbar::imageLoaded);
|
|
connect(m_ringList, &ImageRingList::pixmapLoaded, histogram, &Histogram::imageLoaded);
|
|
connect(m_imageGL->imageWidget(), &ImageWidget::fileDropped, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
|
|
|
QMenu *fileMenu = new QMenu(tr("File"), this);
|
|
fileMenu->addAction(tr("Open"), this, SLOT(loadFile()), QKeySequence::Open);
|
|
fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir);
|
|
fileMenu->addAction(tr("Save as"), this, SLOT(saveAs()), QKeySequence::Save);
|
|
fileMenu->addSeparator();
|
|
fileMenu->addAction(tr("Copy marked files"), this, SLOT(copyMarked()), Qt::Key_F5);
|
|
fileMenu->addAction(tr("Move marked files"), this, SLOT(moveMarked()), Qt::Key_F6);
|
|
fileMenu->addAction(tr("Move marked files to trash"), this, &MainWindow::deleteMarked, QKeySequence::Delete);
|
|
fileMenu->addSeparator();
|
|
fileMenu->addAction(tr("Index directory"), this, SLOT(indexDir()));
|
|
fileMenu->addAction(tr("Reindex files"), this, SLOT(reindex()));
|
|
fileMenu->addAction(tr("Export database to CSV"), this, &MainWindow::exportCSV);
|
|
fileMenu->addAction(tr("Batch processing"), [this](){
|
|
BatchProcessing *batchProcessing = new BatchProcessing(this);
|
|
batchProcessing->exec();
|
|
delete batchProcessing;
|
|
}, Qt::Key_B | Qt::CTRL);
|
|
fileMenu->addSeparator();
|
|
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool)));
|
|
liveModeAction->setCheckable(true);
|
|
QAction *exitAction = fileMenu->addAction(tr("Exit"), this, SLOT(close()));
|
|
exitAction->setShortcut(QKeySequence::Quit);
|
|
menuBar()->addMenu(fileMenu);
|
|
|
|
QMenu *editMenu = new QMenu(tr("Edit"), this);
|
|
editMenu->addAction(tr("Settings"), this, &MainWindow::showSettingsDialog);
|
|
menuBar()->addMenu(editMenu);
|
|
|
|
QMenu *viewMenu = new QMenu(tr("View"), this);
|
|
viewMenu->addAction(tr("Zoom In"), m_imageGL, SLOT(zoomIn()), QKeySequence::ZoomIn);
|
|
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
|
|
viewMenu->addAction(tr("Best Fit"), m_imageGL, SLOT(bestFit()), QKeySequence("Ctrl+1"));
|
|
viewMenu->addAction(tr("100%"), m_imageGL, SLOT(oneToOne()));
|
|
viewMenu->addSeparator();
|
|
QMenu *bayerMenu = viewMenu->addMenu(tr("Bayer mask"));
|
|
QActionGroup *bayerActionGroup = new QActionGroup(this);
|
|
QAction *rggbAction = bayerActionGroup->addAction(tr("RGGB"));//0 0
|
|
QAction *grbgAction = bayerActionGroup->addAction(tr("GRBG"));//1 0
|
|
QAction *gbrgAction = bayerActionGroup->addAction(tr("GBRG"));//0 1
|
|
QAction *bggrAction = bayerActionGroup->addAction(tr("BGGR"));//1 1
|
|
rggbAction->setCheckable(true); rggbAction->setData(0); rggbAction->setIcon(QIcon(":/bayer.png"));
|
|
grbgAction->setCheckable(true); grbgAction->setData(1); grbgAction->setIcon(QIcon(":/grbg.png"));
|
|
gbrgAction->setCheckable(true); gbrgAction->setData(2); gbrgAction->setIcon(QIcon(":/gbrg.png"));
|
|
bggrAction->setCheckable(true); bggrAction->setData(3); bggrAction->setIcon(QIcon(":/bggr.png"));
|
|
bayerMenu->addActions({rggbAction, grbgAction, gbrgAction, bggrAction});
|
|
viewMenu->addMenu(bayerMenu);
|
|
connect(bayerActionGroup, &QActionGroup::triggered, [this](QAction *action){
|
|
int data = action->data().toInt();
|
|
m_imageGL->imageWidget()->setBayerMask(data);
|
|
QSettings settings;
|
|
settings.setValue("mainwindow/bayermask", data);
|
|
});
|
|
|
|
QAction *thumbnailsAction = viewMenu->addAction(tr("Thumbnails"), [this](bool checked){
|
|
if(SettingsDialog::loadThumbsizes())m_ringList->clearThumbnails();
|
|
m_imageGL->imageWidget()->allocateThumbnails(m_ringList->imageNames());
|
|
m_imageGL->imageWidget()->showThumbnail(checked);
|
|
if(checked)m_ringList->loadThumbnails();
|
|
else m_ringList->stopLoading();
|
|
}, Qt::Key_F2);
|
|
thumbnailsAction->setCheckable(true);
|
|
QAction *slideshowAction = viewMenu->addAction(tr("Slideshow"), m_ringList, &ImageRingList::toggleSlideshow, Qt::Key_F3);
|
|
slideshowAction->setCheckable(true);
|
|
menuBar()->addMenu(viewMenu);
|
|
|
|
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
|
selectMenu->addAction(tr("Mark"), this, SLOT(markImage()), Qt::Key_F7);
|
|
selectMenu->addAction(tr("Unmark"), this, SLOT(unmarkImage()), Qt::Key_F8);
|
|
selectMenu->addSeparator();
|
|
selectMenu->addAction(tr("Mark and next"), this, SLOT(markAndNext()), Qt::Key_M);
|
|
selectMenu->addAction(tr("Unmark and next"), this, SLOT(unmarkAndNext()), Qt::Key_X);
|
|
selectMenu->addAction(tr("Show marked"), this, &MainWindow::showMarkFilesDialog);
|
|
menuBar()->addMenu(selectMenu);
|
|
|
|
QMenu *analyzeMenu = new QMenu(tr("Analyze"), this);
|
|
QActionGroup *analyzeGroup = new QActionGroup(this);
|
|
connect(analyzeGroup, &QActionGroup::triggered, [](QAction* action) {
|
|
static QAction* lastAction = nullptr;
|
|
if(action == lastAction)
|
|
{
|
|
action->setChecked(false);
|
|
lastAction = nullptr;
|
|
}
|
|
else
|
|
lastAction = action;
|
|
});
|
|
|
|
QAction *statsAction = analyzeGroup->addAction(tr("Image statistics"));
|
|
QAction *peakAction = analyzeGroup->addAction(tr("Peak finder"));
|
|
QAction *starAction = analyzeGroup->addAction(tr("Star finder"));
|
|
statsAction->setCheckable(true);
|
|
peakAction->setCheckable(true);
|
|
starAction->setCheckable(true);
|
|
connect(statsAction, SIGNAL(toggled(bool)), this, SLOT(imageStats(bool)));
|
|
connect(peakAction, SIGNAL(toggled(bool)), this, SLOT(peakFinder(bool)));
|
|
connect(starAction, SIGNAL(toggled(bool)), this, SLOT(starFinder(bool)));
|
|
analyzeMenu->addActions({statsAction, peakAction, starAction});
|
|
//menuBar()->addMenu(analyzeMenu);
|
|
|
|
QMenu *dockMenu = new QMenu(tr("Docks"), this);
|
|
dockMenu->addAction(infoDock->toggleViewAction());
|
|
dockMenu->addAction(m_stretchPanel->toggleViewAction());
|
|
dockMenu->addAction(filesystemDock->toggleViewAction());
|
|
dockMenu->addAction(databaseViewDock->toggleViewAction());
|
|
dockMenu->addAction(filetreeDock->toggleViewAction());
|
|
dockMenu->addAction(histogramDock->toggleViewAction());
|
|
menuBar()->addMenu(dockMenu);
|
|
|
|
QMenu *helpMenu = menuBar()->addMenu(tr("Help"));
|
|
helpMenu->addAction(tr("Help"), [this]{ HelpDialog help(this); help.exec(); }, QKeySequence::HelpContents);
|
|
helpMenu->addAction(tr("About Tenmon"), [this]{ About about(this); about.exec(); });
|
|
helpMenu->addAction(tr("About Qt"), [this](){ QMessageBox::aboutQt(this); });
|
|
|
|
setupSigterm();
|
|
QSettings settings;
|
|
restoreGeometry(settings.value("mainwindow/geometry").toByteArray());
|
|
restoreState(settings.value("mainwindow/state").toByteArray());
|
|
int bayermask = settings.value("mainwindow/bayermask", 0).toInt();
|
|
switch(bayermask)
|
|
{
|
|
default:
|
|
case 0:
|
|
rggbAction->setChecked(true); break;
|
|
case 1:
|
|
grbgAction->setChecked(true); break;
|
|
case 2:
|
|
gbrgAction->setChecked(true); break;
|
|
case 3:
|
|
bggrAction->setChecked(true); break;
|
|
}
|
|
|
|
QStringList standardLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
|
|
if(standardLocations.size())
|
|
_lastDir = standardLocations.first();
|
|
|
|
_lastDir = settings.value("mainwindow/lastdir", _lastDir).toString();
|
|
|
|
QStringList args = QCoreApplication::arguments();
|
|
args.removeFirst();
|
|
for(auto &arg : args)
|
|
{
|
|
QFileInfo info(arg);
|
|
if(info.exists())
|
|
{
|
|
m_ringList->setFile(info.canonicalFilePath());
|
|
updateWindowTitle();
|
|
_lastDir = info.absoluteDir().absolutePath();
|
|
settings.setValue("mainwindow/lastdir", _lastDir);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_imageGL->setFocus();
|
|
|
|
// workaround for nasty wayland backend bug https://bugreports.qt.io/browse/QTBUG-87332
|
|
if(static_cast<QGuiApplication*>(QCoreApplication::instance())->platformName() == "wayland")
|
|
{
|
|
infoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
|
filesystemDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
|
databaseViewDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
|
filetreeDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
|
m_stretchPanel->setFloatable(false);
|
|
}
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete m_database;
|
|
}
|
|
|
|
void MainWindow::keyPressEvent(QKeyEvent *event)
|
|
{
|
|
switch (event->key())
|
|
{
|
|
case Qt::Key_Left:
|
|
case Qt::Key_Up:
|
|
m_ringList->decrement();
|
|
break;
|
|
case Qt::Key_Right:
|
|
case Qt::Key_Down:
|
|
m_ringList->increment();
|
|
break;
|
|
default:
|
|
event->ignore();
|
|
break;
|
|
}
|
|
|
|
if(event->isAccepted())
|
|
updateWindowTitle();
|
|
}
|
|
|
|
void MainWindow::keyReleaseEvent(QKeyEvent *event)
|
|
{
|
|
event->ignore();
|
|
}
|
|
|
|
void MainWindow::setupSigterm()
|
|
{
|
|
#ifdef __linux__
|
|
struct sigaction signal;
|
|
|
|
signal.sa_handler = MainWindow::signalHandler;
|
|
sigemptyset(&signal.sa_mask);
|
|
signal.sa_flags = 0;
|
|
signal.sa_flags |= SA_RESTART;
|
|
|
|
sigaction(SIGHUP, &signal, 0);
|
|
sigaction(SIGTERM, &signal, 0);
|
|
sigaction(SIGINT, &signal, 0);
|
|
|
|
::socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair);
|
|
socketNotifier = new QSocketNotifier(socketPair[1], QSocketNotifier::Read, this);
|
|
connect(socketNotifier, SIGNAL(activated(int)), this, SLOT(socketNotify()));
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::signalHandler(int)
|
|
{
|
|
char a = 1;
|
|
::write(socketPair[0], &a, sizeof(a));
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
{
|
|
QSettings settings;
|
|
settings.setValue("mainwindow/geometry", saveGeometry());
|
|
settings.setValue("mainwindow/state", saveState());
|
|
QMainWindow::closeEvent(event);
|
|
}
|
|
|
|
void MainWindow::copyOrMove(bool copy)
|
|
{
|
|
QString dest = QFileDialog::getExistingDirectory(this,
|
|
tr("Select destination"),
|
|
_lastDir,
|
|
QFileDialog::ShowDirsOnly);
|
|
copyOrMove(copy, dest);
|
|
}
|
|
|
|
void MainWindow::copyOrMove(bool copy, const QString &dest)
|
|
{
|
|
QDir dir(dest);
|
|
if(!dest.isEmpty() && dir.exists())
|
|
{
|
|
int i = 0;
|
|
QStringList files = m_database->getMarkedFiles();
|
|
QProgressDialog progress(copy ? tr("Copying") : tr("Moving"), tr("Cancel"), 0, files.size(), this);
|
|
progress.setWindowModality(Qt::WindowModal);
|
|
progress.show();
|
|
for(const QString &file : files)
|
|
{
|
|
bool result = false;
|
|
QFileInfo info(file);
|
|
QFile srcFile(file);
|
|
QFile dstFile(dir.absoluteFilePath(info.fileName()));
|
|
|
|
if(dstFile.exists())
|
|
continue;
|
|
|
|
if(progress.wasCanceled())
|
|
return;
|
|
#ifdef __linux__
|
|
if(copy)
|
|
{
|
|
srcFile.open(QIODevice::ReadOnly);
|
|
dstFile.open(QIODevice::WriteOnly);
|
|
if(ioctl(dstFile.handle(), BTRFS_IOC_CLONE, srcFile.handle()) < 0)
|
|
{
|
|
dstFile.remove();
|
|
dstFile.close();
|
|
result = srcFile.copy(dstFile.fileName());
|
|
}
|
|
else result = true;
|
|
}
|
|
else
|
|
{
|
|
result = srcFile.rename(dstFile.fileName());
|
|
}
|
|
#else
|
|
if(copy)
|
|
result = srcFile.copy(dstFile.fileName());
|
|
else
|
|
result = srcFile.rename(dstFile.fileName());
|
|
#endif
|
|
if(!result)
|
|
{
|
|
QString t = copy ? tr("Failed to copy") : tr("Failed to move");
|
|
QString m = copy ? tr("Failed to copy from %1 to %2") : tr("Failed to move from %1 to %2");
|
|
QMessageBox::StandardButton button = QMessageBox::warning(this, t,
|
|
m.arg(srcFile.fileName()).arg(dir.absolutePath()),
|
|
QMessageBox::Ignore | QMessageBox::Abort);
|
|
if(button == QMessageBox::Abort)return;
|
|
}
|
|
progress.setValue(i++);
|
|
}
|
|
m_database->clearMarkedFiles();
|
|
}
|
|
}
|
|
|
|
void MainWindow::socketNotify()
|
|
{
|
|
socketNotifier->setEnabled(false);
|
|
char tmp;
|
|
read(socketPair[1], &tmp, sizeof(tmp));
|
|
close();
|
|
socketNotifier->setEnabled(true);
|
|
}
|
|
|
|
void MainWindow::pixmapLoaded(Image *image)
|
|
{
|
|
if(image->rawImage())
|
|
{
|
|
m_imageGL->setImage(image);
|
|
}
|
|
}
|
|
|
|
void MainWindow::loadFile()
|
|
{
|
|
QString file = QFileDialog::getOpenFileName(this,
|
|
tr("Open file"),
|
|
_lastDir,
|
|
_openFilter);
|
|
loadFile(file);
|
|
}
|
|
|
|
void MainWindow::loadFile(const QString &path)
|
|
{
|
|
if(!path.isEmpty())
|
|
{
|
|
QFileInfo info(path);
|
|
m_ringList->setFile(info.canonicalFilePath());
|
|
updateWindowTitle();
|
|
if(info.isDir())
|
|
_lastDir = info.absolutePath();
|
|
else
|
|
_lastDir = info.canonicalPath();
|
|
QSettings settings;
|
|
settings.setValue("mainwindow/lastdir", _lastDir);
|
|
}
|
|
}
|
|
|
|
void MainWindow::loadFile(int row)
|
|
{
|
|
m_ringList->loadFile(row);
|
|
}
|
|
|
|
void MainWindow::loadDir()
|
|
{
|
|
QString dir = QFileDialog::getExistingDirectory(this,
|
|
tr("Open directory recursively"),
|
|
_lastDir);
|
|
if(!dir.isEmpty())
|
|
{
|
|
_lastDir = dir;
|
|
m_ringList->setDir(dir, QString(), true);
|
|
QSettings settings;
|
|
settings.setValue("mainwindow/lastdir", _lastDir);
|
|
}
|
|
}
|
|
|
|
void MainWindow::indexDir()
|
|
{
|
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir, QFileDialog::ShowDirsOnly);
|
|
indexDir(dir);
|
|
}
|
|
|
|
void MainWindow::indexDir(const QString &dir)
|
|
{
|
|
if(!dir.isEmpty())
|
|
{
|
|
QProgressDialog progressDialog(tr("Indexing FITS files"), tr("Cancel"), 0, 1, this);
|
|
progressDialog.setModal(true);
|
|
m_database->indexDir(dir, &progressDialog);
|
|
}
|
|
}
|
|
|
|
void MainWindow::reindex()
|
|
{
|
|
QProgressDialog progressDialog(tr("Indexing FITS files"), tr("Cancel"), 0, 1, this);
|
|
progressDialog.setModal(true);
|
|
m_database->reindex(&progressDialog);
|
|
}
|
|
|
|
void MainWindow::saveAs()
|
|
{
|
|
QString selectedFilter;
|
|
QString file = QFileDialog::getSaveFileName(this,
|
|
tr("Save as"),
|
|
_lastDir,
|
|
_saveFilter,
|
|
&selectedFilter);
|
|
auto filterToFormat = [](const QString &file, const QString &filter) -> const char*
|
|
{
|
|
QString suffix = QFileInfo(file).suffix();
|
|
if(!suffix.compare("jpg", Qt::CaseInsensitive) || !suffix.compare("jpeg", Qt::CaseInsensitive))return "JPEG";
|
|
if(!suffix.compare("png", Qt::CaseInsensitive))return "PNG";
|
|
if(!suffix.compare("fits", Qt::CaseInsensitive) || !suffix.compare("fit", Qt::CaseInsensitive))return "FITS";
|
|
if(!suffix.compare("xisf", Qt::CaseInsensitive))return "XISF";
|
|
if(filter.contains("png"))return "PNG";
|
|
if(filter.contains("fits"))return "FITS";
|
|
if(filter.contains("xisf"))return "XISF";
|
|
return "JPEG";
|
|
};
|
|
|
|
if(!file.isEmpty())
|
|
{
|
|
QString format = filterToFormat(file, selectedFilter);
|
|
|
|
if(format == "FITS" || format == "XISF")
|
|
{
|
|
convert(file, format);
|
|
}
|
|
else
|
|
{
|
|
QImage img = m_imageGL->imageWidget()->renderToImage();
|
|
if(!img.isNull())
|
|
img.save(file, filterToFormat(file, selectedFilter));
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::convert(const QString &outfile, const QString &format)
|
|
{
|
|
QString file = m_ringList->currentImage()->name();
|
|
QThreadPool::globalInstance()->start(new ConvertRunable(file, outfile, format));
|
|
}
|
|
|
|
void MainWindow::markImage()
|
|
{
|
|
ImagePtr ptr = m_ringList->currentImage();
|
|
if(ptr)
|
|
{
|
|
QString file = ptr->name();
|
|
if(!file.isEmpty())
|
|
{
|
|
m_database->mark(file);
|
|
m_ringList->updateMark();
|
|
}
|
|
|
|
updateWindowTitle();
|
|
}
|
|
}
|
|
|
|
void MainWindow::unmarkImage()
|
|
{
|
|
ImagePtr ptr = m_ringList->currentImage();
|
|
if(ptr)
|
|
{
|
|
QString file = ptr->name();
|
|
if(!file.isEmpty())
|
|
{
|
|
m_database->unmark(file);
|
|
m_ringList->updateMark();
|
|
}
|
|
|
|
updateWindowTitle();
|
|
}
|
|
}
|
|
|
|
void MainWindow::markAndNext()
|
|
{
|
|
markImage();
|
|
m_ringList->increment();
|
|
updateWindowTitle();
|
|
}
|
|
|
|
void MainWindow::unmarkAndNext()
|
|
{
|
|
unmarkImage();
|
|
m_ringList->increment();
|
|
updateWindowTitle();
|
|
}
|
|
|
|
void MainWindow::copyMarked()
|
|
{
|
|
copyOrMove(true);
|
|
}
|
|
|
|
void MainWindow::moveMarked()
|
|
{
|
|
copyOrMove(false);
|
|
}
|
|
|
|
void MainWindow::deleteMarked()
|
|
{
|
|
QStringList files = m_database->getMarkedFiles();
|
|
if(QMessageBox::question(this, tr("Move files to trash?"), tr("Do you want to move %1 files to trash?").arg(files.size())) != QMessageBox::Yes)
|
|
return;
|
|
|
|
QProgressDialog progress(tr("Moving marked files to trash"), tr("Cancel"), 0, files.size(), this);
|
|
progress.setWindowModality(Qt::WindowModal);
|
|
progress.show();
|
|
int i = 0;
|
|
for(const QString &file : files)
|
|
{
|
|
if(!QFile::exists(file))
|
|
continue;
|
|
|
|
if(progress.wasCanceled())
|
|
return;
|
|
|
|
if(!moveToTrash(file))
|
|
{
|
|
QMessageBox::warning(this, tr("Failed to move file to trash"), tr("Failed to move file to trash %1").arg(file));
|
|
return;
|
|
}
|
|
progress.setValue(i++);
|
|
}
|
|
m_database->clearMarkedFiles();
|
|
}
|
|
|
|
void MainWindow::liveMode(bool active)
|
|
{
|
|
m_ringList->setLiveMode(active);
|
|
}
|
|
|
|
void MainWindow::imageStats(bool imageStats)
|
|
{
|
|
m_ringList->setCalculateStats(imageStats);
|
|
}
|
|
|
|
void MainWindow::peakFinder(bool findPeaks)
|
|
{
|
|
m_ringList->setFindPeaks(findPeaks);
|
|
}
|
|
|
|
void MainWindow::starFinder(bool findStars)
|
|
{
|
|
m_ringList->setFindStars(findStars);
|
|
}
|
|
|
|
void MainWindow::showMarkFilesDialog()
|
|
{
|
|
MarkedFiles markedFiles(this);
|
|
markedFiles.exec();
|
|
}
|
|
|
|
void MainWindow::showSettingsDialog()
|
|
{
|
|
SettingsDialog settingsDialog(this);
|
|
connect(&settingsDialog, &SettingsDialog::preloadChanged, m_ringList, &ImageRingList::setPreload);
|
|
settingsDialog.exec();
|
|
}
|
|
|
|
void MainWindow::exportCSV()
|
|
{
|
|
QStringList documentDir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
|
|
if(documentDir.empty())documentDir.append("");
|
|
QString file = QFileDialog::getSaveFileName(this,
|
|
tr("Save as"),
|
|
documentDir.first(),
|
|
tr("CSV file (*.csv)"));
|
|
|
|
if(!file.isEmpty())
|
|
m_databaseView->exportCSV(file);
|
|
}
|
|
|
|
void MainWindow::updateWindowTitle()
|
|
{
|
|
ImagePtr ptr = m_ringList->currentImage();
|
|
if(ptr)
|
|
{
|
|
QFileInfo info(ptr->name());
|
|
QString title = info.fileName();
|
|
if(m_database->isMarked(ptr->name()))
|
|
title += " *";
|
|
setWindowTitle(title);
|
|
}
|
|
}
|