mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 06:19:52 +08:00
Notebook: support scan and import external files
This commit is contained in:
parent
8973f40020
commit
dc0c0ca849
@ -378,3 +378,8 @@ QList<QSharedPointer<File>> Notebook::collectFiles()
|
|||||||
|
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList Notebook::scanAndImportExternalFiles()
|
||||||
|
{
|
||||||
|
return m_configMgr->scanAndImportExternalFiles(getRootNode().data());
|
||||||
|
}
|
||||||
|
@ -144,6 +144,8 @@ namespace vnotex
|
|||||||
// Get content files recursively.
|
// Get content files recursively.
|
||||||
QList<QSharedPointer<File>> collectFiles();
|
QList<QSharedPointer<File>> collectFiles();
|
||||||
|
|
||||||
|
QStringList scanAndImportExternalFiles();
|
||||||
|
|
||||||
static const QString c_defaultAttachmentFolder;
|
static const QString c_defaultAttachmentFolder;
|
||||||
|
|
||||||
static const QString c_defaultImageFolder;
|
static const QString c_defaultImageFolder;
|
||||||
|
@ -80,6 +80,8 @@ namespace vnotex
|
|||||||
|
|
||||||
virtual bool checkNodeExists(Node *p_node) = 0;
|
virtual bool checkNodeExists(Node *p_node) = 0;
|
||||||
|
|
||||||
|
virtual QStringList scanAndImportExternalFiles(Node *p_node) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Version of the config processing code.
|
// Version of the config processing code.
|
||||||
virtual QString getCodeVersion() const;
|
virtual QString getCodeVersion() const;
|
||||||
|
@ -996,3 +996,59 @@ bool VXNotebookConfigMgr::checkNodeExists(Node *p_node)
|
|||||||
p_node->setExists(exists);
|
p_node->setExists(exists);
|
||||||
return exists;
|
return exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList VXNotebookConfigMgr::scanAndImportExternalFiles(Node *p_node)
|
||||||
|
{
|
||||||
|
QStringList files;
|
||||||
|
if (!p_node->isContainer() || p_node->getUse() == Node::Use::RecycleBin) {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
// External nodes.
|
||||||
|
auto dir = p_node->toDir();
|
||||||
|
auto externalNodes = fetchExternalChildren(p_node);
|
||||||
|
for (const auto &node : externalNodes) {
|
||||||
|
Node::Flags flags = Node::Flag::Content;
|
||||||
|
if (node->isFolder()) {
|
||||||
|
if (isLikelyImageFolder(dir.filePath(node->getName()))) {
|
||||||
|
qWarning() << "skip importing folder containing only images" << node->getName();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
flags = Node::Flag::Container;
|
||||||
|
}
|
||||||
|
|
||||||
|
addAsNode(p_node, flags, node->getName(), NodeParameters());
|
||||||
|
files << dir.filePath(node->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Children folders (including newly-added external nodes).
|
||||||
|
for (const auto &child : p_node->getChildrenRef()) {
|
||||||
|
if (child->isContainer()) {
|
||||||
|
files << scanAndImportExternalFiles(child.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VXNotebookConfigMgr::isLikelyImageFolder(const QString &p_dirPath)
|
||||||
|
{
|
||||||
|
QDir dir(p_dirPath);
|
||||||
|
const auto folders = dir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
|
||||||
|
if (!folders.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto files = dir.entryList(QDir::Files);
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &file : files) {
|
||||||
|
if (!FileUtils::isImage(dir.filePath(file))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -74,6 +74,8 @@ namespace vnotex
|
|||||||
|
|
||||||
bool checkNodeExists(Node *p_node) Q_DECL_OVERRIDE;
|
bool checkNodeExists(Node *p_node) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
QStringList scanAndImportExternalFiles(Node *p_node) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Config of a file child.
|
// Config of a file child.
|
||||||
struct NodeFileConfig
|
struct NodeFileConfig
|
||||||
@ -195,6 +197,8 @@ namespace vnotex
|
|||||||
|
|
||||||
bool isExcludedFromExternalNode(const QString &p_name) const;
|
bool isExcludedFromExternalNode(const QString &p_name) const;
|
||||||
|
|
||||||
|
static bool isLikelyImageFolder(const QString &p_dirPath);
|
||||||
|
|
||||||
Info m_info;
|
Info m_info;
|
||||||
|
|
||||||
static bool s_initialized;
|
static bool s_initialized;
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
<file>icons/up_parent_node.svg</file>
|
<file>icons/up_parent_node.svg</file>
|
||||||
<file>icons/properties.svg</file>
|
<file>icons/properties.svg</file>
|
||||||
<file>icons/recycle_bin.svg</file>
|
<file>icons/recycle_bin.svg</file>
|
||||||
|
<file>icons/scan_import.svg</file>
|
||||||
<file>icons/search_location_list.svg</file>
|
<file>icons/search_location_list.svg</file>
|
||||||
<file>icons/save_editor.svg</file>
|
<file>icons/save_editor.svg</file>
|
||||||
<file>icons/buffer.svg</file>
|
<file>icons/buffer.svg</file>
|
||||||
|
1
src/data/core/icons/scan_import.svg
Normal file
1
src/data/core/icons/scan_import.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1630643879525" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4410" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M810.666667 512V384h-170.666667a85.333333 85.333333 0 0 1-85.333333-85.333333V128H213.333333v384h597.333334zM128 512V128a85.333333 85.333333 0 0 1 85.333333-85.333333h444.330667L896 281.002667V512h85.333333v85.333333H42.666667v-85.333333h85.333333z m682.666667 170.666667h85.333333v213.333333a85.333333 85.333333 0 0 1-85.333333 85.333333H213.333333a85.333333 85.333333 0 0 1-85.333333-85.333333v-213.333333h85.333333v213.333333h597.333334v-213.333333zM640 145.664V298.666667h153.002667L640 145.664z" p-id="4411" fill="#000000"></path></svg>
|
After Width: | Height: | Size: 918 B |
@ -261,6 +261,16 @@ bool FileUtils::isText(const QString &p_filePath)
|
|||||||
return mimeType.inherits(QStringLiteral("text/plain"));
|
return mimeType.inherits(QStringLiteral("text/plain"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileUtils::isImage(const QString &p_filePath)
|
||||||
|
{
|
||||||
|
QMimeDatabase mimeDatabase;
|
||||||
|
auto mimeType = mimeDatabase.mimeTypeForFile(p_filePath);
|
||||||
|
if (mimeType.name().startsWith(QStringLiteral("image/"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QImage FileUtils::imageFromFile(const QString &p_filePath)
|
QImage FileUtils::imageFromFile(const QString &p_filePath)
|
||||||
{
|
{
|
||||||
QImage img(p_filePath);
|
QImage img(p_filePath);
|
||||||
|
@ -56,6 +56,8 @@ namespace vnotex
|
|||||||
|
|
||||||
static bool isText(const QString &p_filePath);
|
static bool isText(const QString &p_filePath);
|
||||||
|
|
||||||
|
static bool isImage(const QString &p_filePath);
|
||||||
|
|
||||||
static QImage imageFromFile(const QString &p_filePath);
|
static QImage imageFromFile(const QString &p_filePath);
|
||||||
|
|
||||||
static QPixmap pixmapFromFile(const QString &p_filePath);
|
static QPixmap pixmapFromFile(const QString &p_filePath);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
#include <QCheckBox>
|
||||||
|
|
||||||
#include <vtextedit/markdownutils.h>
|
#include <vtextedit/markdownutils.h>
|
||||||
#include <vtextedit/networkutils.h>
|
#include <vtextedit/networkutils.h>
|
||||||
@ -28,6 +29,10 @@ using namespace vnotex;
|
|||||||
|
|
||||||
int ImageInsertDialog::s_lastScaleSliderValue = 10;
|
int ImageInsertDialog::s_lastScaleSliderValue = 10;
|
||||||
|
|
||||||
|
int ImageInsertDialog::s_lastScaleWidth = -1;
|
||||||
|
|
||||||
|
bool ImageInsertDialog::s_fixedScaleWidth = false;
|
||||||
|
|
||||||
ImageInsertDialog::ImageInsertDialog(const QString &p_title,
|
ImageInsertDialog::ImageInsertDialog(const QString &p_title,
|
||||||
const QString &p_imageTitle,
|
const QString &p_imageTitle,
|
||||||
const QString &p_imageAlt,
|
const QString &p_imageAlt,
|
||||||
@ -108,12 +113,14 @@ void ImageInsertDialog::setupUI(const QString &p_title,
|
|||||||
|
|
||||||
int height = m_image.height() * (1.0 * p_val / m_image.width());
|
int height = m_image.height() * (1.0 * p_val / m_image.width());
|
||||||
m_imageLabel->resize(p_val, height);
|
m_imageLabel->resize(p_val, height);
|
||||||
|
|
||||||
|
s_lastScaleWidth = p_val;
|
||||||
});
|
});
|
||||||
// 0.1 to 2.0 -> 1 to 20.
|
// 0.1 to 2.0 -> 1 to 20.
|
||||||
m_scaleSlider = new QSlider(mainWidget);
|
m_scaleSlider = new QSlider(mainWidget);
|
||||||
m_scaleSlider->setOrientation(Qt::Horizontal);
|
m_scaleSlider->setOrientation(Qt::Horizontal);
|
||||||
m_scaleSlider->setMinimum(1);
|
m_scaleSlider->setMinimum(1);
|
||||||
m_scaleSlider->setMaximum(20);
|
m_scaleSlider->setMaximum(50);
|
||||||
m_scaleSlider->setValue(s_lastScaleSliderValue);
|
m_scaleSlider->setValue(s_lastScaleSliderValue);
|
||||||
m_scaleSlider->setSingleStep(1);
|
m_scaleSlider->setSingleStep(1);
|
||||||
m_scaleSlider->setPageStep(5);
|
m_scaleSlider->setPageStep(5);
|
||||||
@ -125,6 +132,16 @@ void ImageInsertDialog::setupUI(const QString &p_title,
|
|||||||
gridLayout->addWidget(m_scaleSlider, 3, 2, 1, 2);
|
gridLayout->addWidget(m_scaleSlider, 3, 2, 1, 2);
|
||||||
gridLayout->addWidget(m_sliderLabel, 3, 4, 1, 1);
|
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.
|
// Preview area.
|
||||||
m_imageLabel = new QLabel(mainWidget);
|
m_imageLabel = new QLabel(mainWidget);
|
||||||
m_imageLabel->setScaledContents(true);
|
m_imageLabel->setScaledContents(true);
|
||||||
@ -132,7 +149,7 @@ void ImageInsertDialog::setupUI(const QString &p_title,
|
|||||||
m_previewArea->setBackgroundRole(QPalette::Dark);
|
m_previewArea->setBackgroundRole(QPalette::Dark);
|
||||||
m_previewArea->setWidget(m_imageLabel);
|
m_previewArea->setWidget(m_imageLabel);
|
||||||
m_previewArea->setMinimumSize(256, 256);
|
m_previewArea->setMinimumSize(256, 256);
|
||||||
gridLayout->addWidget(m_previewArea, 4, 0, 1, 5);
|
gridLayout->addWidget(m_previewArea, 5, 0, 1, 5);
|
||||||
|
|
||||||
setImageControlsVisible(false);
|
setImageControlsVisible(false);
|
||||||
|
|
||||||
@ -271,11 +288,16 @@ void ImageInsertDialog::setImage(const QImage &p_image)
|
|||||||
|
|
||||||
m_widthSpin->setMaximum(m_image.width() * 5);
|
m_widthSpin->setMaximum(m_image.width() * 5);
|
||||||
|
|
||||||
// Set the scaling widgets.
|
if (s_fixedScaleWidth) {
|
||||||
if (m_scaleSlider->value() == s_lastScaleSliderValue) {
|
m_widthSpin->setValue(s_lastScaleWidth);
|
||||||
handleScaleSliderValueChanged(s_lastScaleSliderValue);
|
|
||||||
} else {
|
} else {
|
||||||
m_scaleSlider->setValue(s_lastScaleSliderValue);
|
// Set the scaling widgets.
|
||||||
|
if (m_scaleSlider->value() == s_lastScaleSliderValue) {
|
||||||
|
// Trigger it manually.
|
||||||
|
handleScaleSliderValueChanged(s_lastScaleSliderValue);
|
||||||
|
} else {
|
||||||
|
m_scaleSlider->setValue(s_lastScaleSliderValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setImageControlsVisible(true);
|
setImageControlsVisible(true);
|
||||||
|
@ -112,6 +112,10 @@ namespace vnotex
|
|||||||
QSharedPointer<QTemporaryFile> m_tempFile;
|
QSharedPointer<QTemporaryFile> m_tempFile;
|
||||||
|
|
||||||
static int s_lastScaleSliderValue;
|
static int s_lastScaleSliderValue;
|
||||||
|
|
||||||
|
static int s_lastScaleWidth;
|
||||||
|
|
||||||
|
static bool s_fixedScaleWidth;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +132,38 @@ TitleBar *NotebookExplorer::setupTitleBar(QWidget *p_parent)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto btn = titleBar->addActionButton(QStringLiteral("scan_import.svg"), tr("Scan and Import"));
|
||||||
|
connect(btn, &QToolButton::clicked,
|
||||||
|
this, [this]() {
|
||||||
|
if (!m_currentNotebook) {
|
||||||
|
MessageBoxHelper::notify(MessageBoxHelper::Warning,
|
||||||
|
tr("Please select one notebook first."),
|
||||||
|
VNoteX::getInst().getMainWindow());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Warning,
|
||||||
|
tr("Scan the whole notebook (%1) and import external files automatically.").arg(m_currentNotebook->getName()),
|
||||||
|
tr("This operation helps importing external files that are added outside VNote. "
|
||||||
|
"It may import unexpected files."),
|
||||||
|
tr("It is recommended to always manage files within VNote."),
|
||||||
|
VNoteX::getInst().getMainWindow());
|
||||||
|
if (ret != QMessageBox::Ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto importedFiles = m_currentNotebook->scanAndImportExternalFiles();
|
||||||
|
MessageBoxHelper::notify(MessageBoxHelper::Information,
|
||||||
|
tr("Imported %n file(s).", "", importedFiles.size()),
|
||||||
|
QString(),
|
||||||
|
importedFiles.join('\n'),
|
||||||
|
VNoteX::getInst().getMainWindow());
|
||||||
|
if (!importedFiles.isEmpty()) {
|
||||||
|
m_nodeExplorer->reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
titleBar->addMenuAction(QStringLiteral("manage_notebooks.svg"),
|
titleBar->addMenuAction(QStringLiteral("manage_notebooks.svg"),
|
||||||
tr("&Manage Notebooks"),
|
tr("&Manage Notebooks"),
|
||||||
titleBar,
|
titleBar,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user