mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 14:29:54 +08:00
364 lines
11 KiB
C++
364 lines
11 KiB
C++
#include "imageinsertdialog.h"
|
|
|
|
#include <QGridLayout>
|
|
#include <QVBoxLayout>
|
|
#include <QUrl>
|
|
#include <QRegularExpression>
|
|
#include <QLabel>
|
|
#include <QPushButton>
|
|
#include <QRegExpValidator>
|
|
#include <QSpinBox>
|
|
#include <QSlider>
|
|
#include <QScrollArea>
|
|
#include <QFileInfo>
|
|
#include <QDir>
|
|
#include <QFileDialog>
|
|
#include <QTimer>
|
|
#include <QTemporaryFile>
|
|
#include <QCheckBox>
|
|
|
|
#include <vtextedit/markdownutils.h>
|
|
#include <vtextedit/networkutils.h>
|
|
|
|
#include <widgets/widgetsfactory.h>
|
|
#include <widgets/lineedit.h>
|
|
#include <utils/fileutils.h>
|
|
#include <utils/pathutils.h>
|
|
|
|
using namespace vnotex;
|
|
|
|
int ImageInsertDialog::s_lastScaleSliderValue = 10;
|
|
|
|
int ImageInsertDialog::s_lastScaleWidth = -1;
|
|
|
|
bool ImageInsertDialog::s_fixedScaleWidth = false;
|
|
|
|
ImageInsertDialog::ImageInsertDialog(const QString &p_title,
|
|
const QString &p_imageTitle,
|
|
const QString &p_imageAlt,
|
|
const QString &p_imagePath,
|
|
bool p_browserEnabled,
|
|
QWidget *p_parent)
|
|
: Dialog(p_parent),
|
|
m_browserEnabled(p_browserEnabled)
|
|
{
|
|
m_imagePathCheckTimer = new QTimer(this);
|
|
m_imagePathCheckTimer->setSingleShot(true);
|
|
m_imagePathCheckTimer->setInterval(500);
|
|
connect(m_imagePathCheckTimer, &QTimer::timeout,
|
|
this, &ImageInsertDialog::checkImagePathInput);
|
|
|
|
setupUI(p_title, p_imageTitle, p_imageAlt, p_imagePath);
|
|
|
|
checkInput();
|
|
}
|
|
|
|
void ImageInsertDialog::setupUI(const QString &p_title,
|
|
const QString &p_imageTitle,
|
|
const QString &p_imageAlt,
|
|
const QString &p_imagePath)
|
|
{
|
|
auto mainWidget = new QWidget(this);
|
|
setCentralWidget(mainWidget);
|
|
|
|
auto mainLayout = new QVBoxLayout(mainWidget);
|
|
|
|
auto gridLayout = new QGridLayout();
|
|
mainLayout->addLayout(gridLayout);
|
|
|
|
mainLayout->addStretch();
|
|
|
|
// Image Path.
|
|
m_imagePathEdit = WidgetsFactory::createLineEdit(p_imagePath, mainWidget);
|
|
m_imagePathEdit->setReadOnly(!m_browserEnabled);
|
|
gridLayout->addWidget(new QLabel(tr("From:"), mainWidget), 0, 0, 1, 1);
|
|
gridLayout->addWidget(m_imagePathEdit, 0, 1, 1, 3);
|
|
connect(m_imagePathEdit, &QLineEdit::textChanged,
|
|
this, [this]() {
|
|
m_imagePathCheckTimer->start();
|
|
});
|
|
|
|
m_browseBtn = new QPushButton(tr("&Browse"), mainWidget);
|
|
m_browseBtn->setEnabled(m_browserEnabled);
|
|
gridLayout->addWidget(m_browseBtn, 0, 4, 1, 1);
|
|
connect(m_browseBtn, &QPushButton::clicked,
|
|
this, &ImageInsertDialog::browseFile);
|
|
|
|
// Image Title.
|
|
m_imageTitleEdit = WidgetsFactory::createLineEdit(p_imageTitle, mainWidget);
|
|
auto titleValidator = new QRegExpValidator(QRegExp(vte::MarkdownUtils::c_imageTitleRegExp), m_imageTitleEdit);
|
|
m_imageTitleEdit->setValidator(titleValidator);
|
|
gridLayout->addWidget(new QLabel(tr("Title:"), mainWidget), 1, 0, 1, 1);
|
|
gridLayout->addWidget(m_imageTitleEdit, 1, 1, 1, 3);
|
|
connect(m_imageTitleEdit, &QLineEdit::textChanged,
|
|
this, &ImageInsertDialog::checkInput);
|
|
|
|
// Image Alt.
|
|
m_imageAltEdit = WidgetsFactory::createLineEdit(p_imageAlt, mainWidget);
|
|
auto altValidator = new QRegExpValidator(QRegExp(vte::MarkdownUtils::c_imageAltRegExp), m_imageAltEdit);
|
|
m_imageAltEdit->setValidator(altValidator);
|
|
gridLayout->addWidget(new QLabel(tr("Alt text:"), mainWidget), 2, 0, 1, 1);
|
|
gridLayout->addWidget(m_imageAltEdit, 2, 1, 1, 3);
|
|
|
|
// Scale.
|
|
m_widthSpin = WidgetsFactory::createSpinBox(mainWidget);
|
|
m_widthSpin->setMinimum(1);
|
|
m_widthSpin->setSingleStep(10);
|
|
m_widthSpin->setSuffix(" px");
|
|
connect(m_widthSpin, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
|
this, [this](int p_val) {
|
|
if (m_image.isNull()) {
|
|
return;
|
|
}
|
|
|
|
int height = m_image.height() * (1.0 * p_val / m_image.width());
|
|
m_imageLabel->resize(p_val, height);
|
|
|
|
s_lastScaleWidth = p_val;
|
|
});
|
|
// 0.1 to 2.0 -> 1 to 20.
|
|
m_scaleSlider = new QSlider(mainWidget);
|
|
m_scaleSlider->setOrientation(Qt::Horizontal);
|
|
m_scaleSlider->setMinimum(1);
|
|
m_scaleSlider->setMaximum(50);
|
|
m_scaleSlider->setValue(s_lastScaleSliderValue);
|
|
m_scaleSlider->setSingleStep(1);
|
|
m_scaleSlider->setPageStep(5);
|
|
connect(m_scaleSlider, &QSlider::valueChanged,
|
|
this, &ImageInsertDialog::handleScaleSliderValueChanged);
|
|
m_sliderLabel = new QLabel("1x", mainWidget);
|
|
gridLayout->addWidget(new QLabel(tr("Scaling width:"), mainWidget), 3, 0, 1, 1);
|
|
gridLayout->addWidget(m_widthSpin, 3, 1, 1, 1);
|
|
gridLayout->addWidget(m_scaleSlider, 3, 2, 1, 2);
|
|
gridLayout->addWidget(m_sliderLabel, 3, 4, 1, 1);
|
|
|
|
{
|
|
auto fixedWidthCheckBox = WidgetsFactory::createCheckBox(tr("Fixed scaling width"), mainWidget);
|
|
fixedWidthCheckBox->setChecked(s_fixedScaleWidth);
|
|
connect(fixedWidthCheckBox, &QCheckBox::stateChanged,
|
|
this, [this](int p_state) {
|
|
s_fixedScaleWidth = p_state == Qt::Checked;
|
|
});
|
|
gridLayout->addWidget(fixedWidthCheckBox, 4, 1, 1, 1);
|
|
}
|
|
|
|
// Preview area.
|
|
m_imageLabel = new QLabel(mainWidget);
|
|
m_imageLabel->setScaledContents(true);
|
|
m_previewArea = new QScrollArea(mainWidget);
|
|
m_previewArea->setBackgroundRole(QPalette::Dark);
|
|
m_previewArea->setWidget(m_imageLabel);
|
|
m_previewArea->setMinimumSize(256, 256);
|
|
gridLayout->addWidget(m_previewArea, 5, 0, 1, 5);
|
|
|
|
setImageControlsVisible(false);
|
|
|
|
gridLayout->setColumnStretch(0, 0);
|
|
gridLayout->setColumnStretch(1, 0);
|
|
gridLayout->setColumnStretch(2, 1);
|
|
gridLayout->setColumnStretch(3, 1);
|
|
gridLayout->setColumnStretch(4, 0);
|
|
|
|
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
|
|
setWindowTitle(p_title);
|
|
}
|
|
|
|
void ImageInsertDialog::setImageControlsVisible(bool p_visible)
|
|
{
|
|
m_widthSpin->setEnabled(p_visible);
|
|
m_scaleSlider->setEnabled(p_visible);
|
|
m_sliderLabel->setEnabled(p_visible);
|
|
|
|
m_previewArea->setVisible(p_visible);
|
|
}
|
|
|
|
void ImageInsertDialog::showEvent(QShowEvent *p_event)
|
|
{
|
|
Dialog::showEvent(p_event);
|
|
|
|
m_imageTitleEdit->selectAll();
|
|
m_imageTitleEdit->setFocus();
|
|
}
|
|
|
|
void ImageInsertDialog::checkImagePathInput()
|
|
{
|
|
const QString text = m_imagePathEdit->text();
|
|
QUrl url = QUrl::fromUserInput(text);
|
|
if (text.isEmpty() || !url.isValid()) {
|
|
setImage(QImage());
|
|
return;
|
|
}
|
|
|
|
if (url.isLocalFile()) {
|
|
const auto localFile = url.toLocalFile();
|
|
if (QFileInfo::exists(localFile)) {
|
|
setImage(FileUtils::imageFromFile(localFile));
|
|
} else {
|
|
setImage(QImage());
|
|
}
|
|
|
|
m_source = Source::LocalFile;
|
|
} else {
|
|
setImage(QImage());
|
|
m_source = Source::ImageData;
|
|
|
|
if (!m_downloader) {
|
|
m_downloader = new vte::NetworkAccess(this);
|
|
connect(m_downloader, &vte::NetworkAccess::requestFinished,
|
|
this, &ImageInsertDialog::handleImageDownloaded);
|
|
}
|
|
|
|
m_downloader->requestAsync(url);
|
|
}
|
|
|
|
m_imageTitleEdit->setText(QFileInfo(text).baseName());
|
|
|
|
checkInput();
|
|
}
|
|
|
|
void ImageInsertDialog::checkInput()
|
|
{
|
|
setButtonEnabled(QDialogButtonBox::Ok, !m_image.isNull());
|
|
}
|
|
|
|
void ImageInsertDialog::browseFile()
|
|
{
|
|
QString bpath(QDir::homePath());
|
|
QString filePath = QFileDialog::getOpenFileName(this,
|
|
tr("Select Image To Insert"),
|
|
bpath,
|
|
tr("Images (*.png *.xpm *.jpg *.bmp *.gif *.svg *.webp);;All (*.*)"));
|
|
if (filePath.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
m_source = Source::LocalFile;
|
|
|
|
setImagePath(filePath);
|
|
|
|
m_imageTitleEdit->selectAll();
|
|
m_imageTitleEdit->setFocus();
|
|
}
|
|
|
|
QString ImageInsertDialog::getImageTitle() const
|
|
{
|
|
return m_imageTitleEdit->text();
|
|
}
|
|
|
|
QString ImageInsertDialog::getImageAltText() const
|
|
{
|
|
return m_imageAltEdit->text();
|
|
}
|
|
|
|
QString ImageInsertDialog::getImagePath() const
|
|
{
|
|
if (m_tempFile.isNull()) {
|
|
return m_imagePathEdit->text();
|
|
} else {
|
|
return m_tempFile->fileName();
|
|
}
|
|
}
|
|
|
|
ImageInsertDialog::Source ImageInsertDialog::getImageSource() const
|
|
{
|
|
return m_source;
|
|
}
|
|
|
|
void ImageInsertDialog::setImageSource(ImageInsertDialog::Source p_source)
|
|
{
|
|
m_source = p_source;
|
|
}
|
|
|
|
const QImage &ImageInsertDialog::getImage() const
|
|
{
|
|
return m_image;
|
|
}
|
|
|
|
void ImageInsertDialog::setImage(const QImage &p_image)
|
|
{
|
|
m_image = p_image;
|
|
if (m_image.isNull()) {
|
|
m_imageLabel->clear();
|
|
setImageControlsVisible(false);
|
|
} else {
|
|
m_imageLabel->setPixmap(QPixmap::fromImage(m_image));
|
|
|
|
m_imageLabel->adjustSize();
|
|
|
|
m_widthSpin->setMaximum(m_image.width() * 5);
|
|
|
|
if (s_fixedScaleWidth) {
|
|
m_widthSpin->setValue(s_lastScaleWidth);
|
|
} else {
|
|
// Set the scaling widgets.
|
|
if (m_scaleSlider->value() == s_lastScaleSliderValue) {
|
|
// Trigger it manually.
|
|
handleScaleSliderValueChanged(s_lastScaleSliderValue);
|
|
} else {
|
|
m_scaleSlider->setValue(s_lastScaleSliderValue);
|
|
}
|
|
}
|
|
|
|
setImageControlsVisible(true);
|
|
}
|
|
|
|
checkInput();
|
|
}
|
|
|
|
void ImageInsertDialog::setImagePath(const QString &p_path)
|
|
{
|
|
m_tempFile.reset();
|
|
m_imagePathEdit->setText(p_path);
|
|
}
|
|
|
|
int ImageInsertDialog::getScaledWidth() const
|
|
{
|
|
if (m_image.isNull()) {
|
|
return 0;
|
|
}
|
|
|
|
int val = m_widthSpin->value();
|
|
return val == m_image.width() ? 0 : val;
|
|
}
|
|
|
|
void ImageInsertDialog::handleImageDownloaded(const vte::NetworkReply &p_data, const QString &p_url)
|
|
{
|
|
setImage(QImage::fromData(p_data.m_data));
|
|
|
|
// Save it to a temp file to avoid potential data loss via QImage.
|
|
bool savedToFile = false;
|
|
if (!p_data.m_data.isEmpty()) {
|
|
auto format = QFileInfo(PathUtils::removeUrlParameters(p_url)).suffix();
|
|
m_tempFile.reset(FileUtils::createTemporaryFile(format));
|
|
if (m_tempFile->open()) {
|
|
savedToFile = -1 != m_tempFile->write(p_data.m_data);
|
|
m_tempFile->close();
|
|
}
|
|
}
|
|
|
|
m_source = savedToFile ? Source::LocalFile : Source::ImageData;
|
|
if (!savedToFile) {
|
|
m_tempFile.reset();
|
|
}
|
|
}
|
|
|
|
void ImageInsertDialog::handleScaleSliderValueChanged(int p_val)
|
|
{
|
|
if (m_image.isNull()) {
|
|
return;
|
|
}
|
|
|
|
int width = m_image.width();
|
|
qreal factor = 1.0;
|
|
if (p_val != 10) {
|
|
factor = p_val / 10.0;
|
|
width = m_image.width() * factor;
|
|
}
|
|
|
|
m_widthSpin->setValue(width);
|
|
m_sliderLabel->setText(QString::number(factor) + "x");
|
|
|
|
s_lastScaleSliderValue = p_val;
|
|
}
|