Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ Install the corresponding Tesseract language data before adding a language to
| `Ctrl+Shift+Z`, `Ctrl+Y` | Redo |
| `Ctrl+C` | Copy PNG only |
| `Ctrl+S` | Save PNG only |
| `Ctrl+Shift+S` | Save As: pick a destination in a file dialog; the editor stays open, and the chosen folder is remembered for the next Save As |
| `Enter` | Copy and save |
| `P` | Pin the capture on screen and close the editor |
| `Esc` | Return to Select; press again to close |
Expand Down
21 changes: 16 additions & 5 deletions src/capture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,7 @@ QString runtimePath(const QString &name) {
}

QString screenshotTargetPath(QString &error) {
QString root = qEnvironmentVariable("OMASNAP_SCREENSHOT_DIR");
if (root.isEmpty())
root =
QDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation))
.filePath(QStringLiteral("Screenshots"));
const QString root = screenshotRootDir();
if (!QDir().mkpath(root)) {
error =
QStringLiteral("Could not create screenshot directory: %1").arg(root);
Expand Down Expand Up @@ -1044,6 +1040,21 @@ bool quickOutput(const QImage &image, QuickOutputMode mode, QString &error) {
return true;
}

QString screenshotRootDir() {
QString root = qEnvironmentVariable("OMASNAP_SCREENSHOT_DIR");
if (root.isEmpty())
root =
QDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation))
.filePath(QStringLiteral("Screenshots"));
return root;
}

QString defaultScreenshotFileName() {
return QStringLiteral("screenshot-%1.png")
.arg(QDateTime::currentDateTime().toString(
QStringLiteral("yyyy-MM-dd_HH-mm-ss")));
}

QString moveSnapshotToScreenshots(const QString &sourcePath, QString &error) {
const QString targetPath = screenshotTargetPath(error);
if (targetPath.isEmpty())
Expand Down
4 changes: 4 additions & 0 deletions src/capture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ QImage applyRedactionsScaled(QImage image, const QVector<Annotation> &redactions
[[nodiscard]] bool ensurePrivateDirectory(const QString &path);
/** Returns Omasnap's private runtime directory, or empty on failure. */
[[nodiscard]] QString secureRuntimeDirectory();
/** Returns the screenshot output directory without creating it. */
[[nodiscard]] QString screenshotRootDir();
/** Returns a timestamped `screenshot-….png` file name. */
[[nodiscard]] QString defaultScreenshotFileName();
[[nodiscard]] QString moveSnapshotToScreenshots(const QString &sourcePath,
QString &error);
[[nodiscard]] QString temporarySnapshotPath();
Expand Down
71 changes: 71 additions & 0 deletions src/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <QDir>
#include <QEvent>
#include <QFile>
#include <QFileDialog>
#include <QFileInfo>
#include <QFontDatabase>
#include <QFontMetrics>
Expand All @@ -27,6 +28,7 @@
#include <QRandomGenerator>
#include <QScreen>
#include <QScrollBar>
#include <QSettings>
#include <QTextDocument>
#include <QThread>
#include <QTimer>
Expand Down Expand Up @@ -2614,6 +2616,68 @@ void CaptureEditor::finish(OutputMode mode) {
close();
}

void CaptureEditor::saveAs() {
// Ctrl+Shift+S: pick a destination, write a copy there, and keep the
// editor open so Enter/Ctrl+C/Ctrl+S still produce the default outputs.
if (busy_ || selection_.isEmpty())
return;
busy_ = true;
setStatus(QStringLiteral("Preparing screenshot…"));
snapshotOutputRequested_ = true;
scheduleSnapshot();
const bool snapshotOk = waitForSnapshot();
// Later mid-edit writes go back to the fast crash-recovery compression.
snapshotOutputRequested_ = false;
const QFileInfo snapshotFile(snapshotPath_);
if (!snapshotOk || snapshotPath_.isEmpty() || !snapshotFile.exists() ||
snapshotFile.size() <= 0) {
busy_ = false;
setStatus(QStringLiteral("Could not prepare screenshot snapshot"));
return;
}

QSettings settings(defaultPaletteConfigPath(), QSettings::IniFormat);
QString startDir =
settings.value(QStringLiteral("output/save_as_dir")).toString();
if (startDir.isEmpty() || !QDir(startDir).exists())
startDir = screenshotRootDir();
const QString suggested =
QDir(startDir).filePath(defaultScreenshotFileName());
// The editor is a fullscreen stays-on-top surface, so a dialog opened on
// top of it ends up buried behind it with no way to reach either window.
// Hide the overlay for the dialog's lifetime (parent nullptr keeps the
// dialog mapped while the editor is hidden), then restore it.
hide();
QString target = QFileDialog::getSaveFileName(
nullptr, QStringLiteral("Save screenshot as"), suggested,
QStringLiteral("PNG image (*.png)"));
show();
raise();
activateWindow();
if (target.isEmpty()) {
busy_ = false;
setStatus(QStringLiteral("Save As cancelled"));
return;
}
if (!target.endsWith(QStringLiteral(".png"), Qt::CaseInsensitive))
target += QStringLiteral(".png");
if (QFile::exists(target) && !QFile::remove(target)) {
busy_ = false;
setStatus(QStringLiteral("Could not overwrite: %1").arg(target));
return;
}
if (!QFile::copy(snapshotPath_, target)) {
busy_ = false;
setStatus(QStringLiteral("Could not save to: %1").arg(target));
return;
}
settings.setValue(QStringLiteral("output/save_as_dir"),
QFileInfo(target).absolutePath());
busy_ = false;
setStatus(QStringLiteral("Saved to %1").arg(target));
sendCaptureNotification(QStringLiteral("Screenshot saved"), target);
}

void CaptureEditor::handleToolbar(const QString &action) {
const Tool toolBefore = tool_;
const QString statusBefore = status_;
Expand Down Expand Up @@ -2860,6 +2924,12 @@ void CaptureEditor::keyPressEvent(QKeyEvent *event) {
} else if (event->matches(QKeySequence::Copy)) {
finish(OutputMode::Copy);
return;
} else if (event->matches(QKeySequence::SaveAs) ||
(event->key() == Qt::Key_S &&
event->modifiers().testFlag(Qt::ControlModifier) &&
event->modifiers().testFlag(Qt::ShiftModifier))) {
saveAs();
return;
} else if (event->matches(QKeySequence::Save)) {
finish(OutputMode::Save);
return;
Expand Down Expand Up @@ -4663,6 +4733,7 @@ void CaptureEditor::paintEdit(QPainter &painter) {
{QStringLiteral("Enter"), QStringLiteral("Copy + save")},
{QStringLiteral("Ctrl+C"), QStringLiteral("Copy only")},
{QStringLiteral("Ctrl+S"), QStringLiteral("Save only")},
{QStringLiteral("Ctrl+Shift+S"), QStringLiteral("Save As…")},
{QStringLiteral("Esc"), QStringLiteral("Arrow / twice close")}},
keepVisible);
if (hoveredButton) {
Expand Down
1 change: 1 addition & 0 deletions src/editor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ class CaptureEditor final : public QWidget {
void redoEdit();
void selectWindowInDirection(int key);
void finish(OutputMode mode);
void saveAs();
void handleEscape();
void handleToolbar(const QString &action);
void paintEdit(QPainter &painter);
Expand Down
Loading