mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
support file associations and open with built-in editors
This commit is contained in:
parent
737f9e51d8
commit
b3a385693c
@ -5,6 +5,8 @@
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include "buffer.h"
|
||||
#include <core/configmgr.h>
|
||||
#include <core/coreconfig.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -20,24 +22,38 @@ bool FileType::isMarkdown() const
|
||||
|
||||
FileTypeHelper::FileTypeHelper()
|
||||
{
|
||||
setupBuiltInTypes();
|
||||
reload();
|
||||
}
|
||||
|
||||
// TODO: read configuration file.
|
||||
void FileTypeHelper::reload()
|
||||
{
|
||||
setupBuiltInTypes();
|
||||
|
||||
setupSuffixTypeMap();
|
||||
}
|
||||
|
||||
void FileTypeHelper::setupBuiltInTypes()
|
||||
{
|
||||
m_fileTypes.clear();
|
||||
|
||||
const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
|
||||
|
||||
{
|
||||
FileType type;
|
||||
type.m_type = FileType::Markdown;
|
||||
type.m_displayName = Buffer::tr("Markdown");
|
||||
type.m_typeName = QStringLiteral("Markdown");
|
||||
type.m_suffixes << QStringLiteral("md")
|
||||
<< QStringLiteral("mkd")
|
||||
<< QStringLiteral("rmd")
|
||||
<< QStringLiteral("markdown");
|
||||
type.m_displayName = Buffer::tr("Markdown");
|
||||
|
||||
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
|
||||
if (suffixes && !suffixes->isEmpty()) {
|
||||
type.m_suffixes = *suffixes;
|
||||
} else {
|
||||
type.m_suffixes << QStringLiteral("md")
|
||||
<< QStringLiteral("mkd")
|
||||
<< QStringLiteral("rmd")
|
||||
<< QStringLiteral("markdown");
|
||||
}
|
||||
|
||||
m_fileTypes.push_back(type);
|
||||
}
|
||||
|
||||
@ -46,7 +62,14 @@ void FileTypeHelper::setupBuiltInTypes()
|
||||
type.m_type = FileType::Text;
|
||||
type.m_typeName = QStringLiteral("Text");
|
||||
type.m_displayName = Buffer::tr("Text");
|
||||
type.m_suffixes << QStringLiteral("txt") << QStringLiteral("text") << QStringLiteral("log");
|
||||
|
||||
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
|
||||
if (suffixes && !suffixes->isEmpty()) {
|
||||
type.m_suffixes = *suffixes;
|
||||
} else {
|
||||
type.m_suffixes << QStringLiteral("txt") << QStringLiteral("text") << QStringLiteral("log");
|
||||
}
|
||||
|
||||
m_fileTypes.push_back(type);
|
||||
}
|
||||
|
||||
@ -88,10 +111,10 @@ const FileType &FileTypeHelper::getFileTypeBySuffix(const QString &p_suffix) con
|
||||
}
|
||||
}
|
||||
|
||||
#define ADD(x, y) m_suffixTypeMap.insert((x), (y))
|
||||
|
||||
void FileTypeHelper::setupSuffixTypeMap()
|
||||
{
|
||||
m_suffixTypeMap.clear();
|
||||
|
||||
for (int i = 0; i < m_fileTypes.size(); ++i) {
|
||||
for (const auto &suffix : m_fileTypes[i].m_suffixes) {
|
||||
if (m_suffixTypeMap.contains(suffix)) {
|
||||
@ -113,7 +136,7 @@ const FileType &FileTypeHelper::getFileType(int p_type) const
|
||||
return m_fileTypes[p_type];
|
||||
}
|
||||
|
||||
const FileTypeHelper &FileTypeHelper::getInst()
|
||||
FileTypeHelper &FileTypeHelper::getInst()
|
||||
{
|
||||
static FileTypeHelper helper;
|
||||
return helper;
|
||||
|
@ -32,6 +32,7 @@ namespace vnotex
|
||||
bool isMarkdown() const;
|
||||
};
|
||||
|
||||
// Only handle built-in editors.
|
||||
class FileTypeHelper
|
||||
{
|
||||
public:
|
||||
@ -47,7 +48,9 @@ namespace vnotex
|
||||
|
||||
bool checkFileType(const QString &p_filePath, int p_type) const;
|
||||
|
||||
static const FileTypeHelper &getInst();
|
||||
void reload();
|
||||
|
||||
static FileTypeHelper &getInst();
|
||||
|
||||
private:
|
||||
FileTypeHelper();
|
||||
|
@ -18,6 +18,8 @@ namespace vnotex
|
||||
|
||||
virtual Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) = 0;
|
||||
|
||||
virtual bool isBufferCreatedByFactory(const Buffer *p_buffer) const = 0;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -9,3 +9,8 @@ Buffer *MarkdownBufferFactory::createBuffer(const BufferParameters &p_parameters
|
||||
{
|
||||
return new MarkdownBuffer(p_parameters, p_parent);
|
||||
}
|
||||
|
||||
bool MarkdownBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
|
||||
{
|
||||
return dynamic_cast<const MarkdownBuffer *>(p_buffer) != nullptr;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ namespace vnotex
|
||||
public:
|
||||
Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
|
||||
};
|
||||
} // vnotex
|
||||
|
||||
|
@ -9,3 +9,8 @@ Buffer *TextBufferFactory::createBuffer(const BufferParameters &p_parameters,
|
||||
{
|
||||
return new TextBuffer(p_parameters, p_parent);
|
||||
}
|
||||
|
||||
bool TextBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
|
||||
{
|
||||
return dynamic_cast<const TextBuffer *>(p_buffer) != nullptr;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ namespace vnotex
|
||||
public:
|
||||
Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -11,14 +11,19 @@
|
||||
#include <buffer/nodebufferprovider.h>
|
||||
#include <buffer/filebufferprovider.h>
|
||||
#include <utils/widgetutils.h>
|
||||
#include <utils/processutils.h>
|
||||
#include "notebookmgr.h"
|
||||
#include "vnotex.h"
|
||||
#include "externalfile.h"
|
||||
#include "sessionconfig.h"
|
||||
#include "configmgr.h"
|
||||
|
||||
#include "fileopenparameters.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
QMap<QString, QString> BufferMgr::s_suffixToFileType;
|
||||
|
||||
BufferMgr::BufferMgr(QObject *p_parent)
|
||||
: QObject(p_parent)
|
||||
{
|
||||
@ -66,12 +71,27 @@ void BufferMgr::open(Node *p_node, const QSharedPointer<FileOpenParameters> &p_p
|
||||
return;
|
||||
}
|
||||
|
||||
const auto nodePath = p_node->fetchAbsolutePath();
|
||||
|
||||
auto fileType = p_paras->m_fileType;
|
||||
if (fileType.isEmpty()) {
|
||||
// Check if we need to open it with external program by default according to the suffix.
|
||||
fileType = findFileTypeByFile(nodePath);
|
||||
if (openWithExternalProgram(nodePath, fileType)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto buffer = findBuffer(p_node);
|
||||
if (!buffer) {
|
||||
auto nodePath = p_node->fetchAbsolutePath();
|
||||
if (!buffer || !isSameTypeBuffer(buffer, fileType)) {
|
||||
auto nodeFile = p_node->getContentFile();
|
||||
Q_ASSERT(nodeFile);
|
||||
auto fileType = nodeFile->getContentType().m_typeName;
|
||||
if (fileType.isEmpty()) {
|
||||
fileType = nodeFile->getContentType().m_typeName;
|
||||
} else if (fileType != nodeFile->getContentType().m_typeName) {
|
||||
nodeFile->setContentType(FileTypeHelper::getInst().getFileTypeByName(fileType).m_type);
|
||||
}
|
||||
|
||||
auto factory = m_bufferServer->getItem(fileType);
|
||||
if (!factory) {
|
||||
// No factory to open this file type.
|
||||
@ -96,6 +116,11 @@ void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenPar
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it is requested to open with external program.
|
||||
if (openWithExternalProgram(p_filePath, p_paras->m_fileType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfo finfo(p_filePath);
|
||||
if (!finfo.exists()) {
|
||||
auto msg = QString("Failed to open file that does not exist (%1)").arg(p_filePath);
|
||||
@ -123,11 +148,25 @@ void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenPar
|
||||
return;
|
||||
}
|
||||
|
||||
auto fileType = p_paras->m_fileType;
|
||||
if (fileType.isEmpty()) {
|
||||
// Check if we need to open it with external program by default according to the suffix.
|
||||
fileType = findFileTypeByFile(p_filePath);
|
||||
if (openWithExternalProgram(p_filePath, fileType)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto buffer = findBuffer(p_filePath);
|
||||
if (!buffer) {
|
||||
if (!buffer || !isSameTypeBuffer(buffer, fileType)) {
|
||||
// Open it as external file.
|
||||
auto externalFile = QSharedPointer<ExternalFile>::create(p_filePath);
|
||||
auto fileType = externalFile->getContentType().m_typeName;
|
||||
if (fileType.isEmpty()) {
|
||||
fileType = externalFile->getContentType().m_typeName;
|
||||
} else if (fileType != externalFile->getContentType().m_typeName) {
|
||||
externalFile->setContentType(FileTypeHelper::getInst().getFileTypeByName(fileType).m_type);
|
||||
}
|
||||
|
||||
auto factory = m_bufferServer->getItem(fileType);
|
||||
if (!factory) {
|
||||
// No factory to open this file type.
|
||||
@ -188,3 +227,64 @@ void BufferMgr::addBuffer(Buffer *p_buffer)
|
||||
p_buffer->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
bool BufferMgr::openWithExternalProgram(const QString &p_filePath, const QString &p_name) const
|
||||
{
|
||||
if (p_name.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto pro = ConfigMgr::getInst().getSessionConfig().findExternalProgram(p_name)) {
|
||||
const auto command = pro->fetchCommand(p_filePath);
|
||||
if (!command.isEmpty()) {
|
||||
ProcessUtils::startDetached(command);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BufferMgr::isSameTypeBuffer(const Buffer *p_buffer, const QString &p_typeName) const
|
||||
{
|
||||
if (p_typeName.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto factory = m_bufferServer->getItem(p_typeName);
|
||||
Q_ASSERT(factory);
|
||||
if (factory) {
|
||||
return factory->isBufferCreatedByFactory(p_buffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BufferMgr::updateSuffixToFileType(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes)
|
||||
{
|
||||
s_suffixToFileType.clear();
|
||||
|
||||
for (const auto &fts : p_fileTypeSuffixes) {
|
||||
for (const auto &suf : fts.m_suffixes) {
|
||||
auto it = s_suffixToFileType.find(suf);
|
||||
if (it != s_suffixToFileType.end()) {
|
||||
qWarning() << "suffix conflicts for file types" << fts.m_name << it.value();
|
||||
it.value() = fts.m_name;
|
||||
} else {
|
||||
s_suffixToFileType.insert(suf, fts.m_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString BufferMgr::findFileTypeByFile(const QString &p_filePath)
|
||||
{
|
||||
QFileInfo fi(p_filePath);
|
||||
auto suffix = fi.suffix().toLower();
|
||||
auto it = s_suffixToFileType.find(suffix);
|
||||
if (it != s_suffixToFileType.end()) {
|
||||
return it.value();
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,10 @@
|
||||
#include <QScopedPointer>
|
||||
#include <QSharedPointer>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
|
||||
#include "namebasedserver.h"
|
||||
#include "coreconfig.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
@ -30,6 +32,8 @@ namespace vnotex
|
||||
|
||||
void open(const QString &p_filePath, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||
|
||||
static void updateSuffixToFileType(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes);
|
||||
|
||||
signals:
|
||||
void bufferRequested(Buffer *p_buffer, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||
|
||||
@ -42,10 +46,19 @@ namespace vnotex
|
||||
|
||||
void addBuffer(Buffer *p_buffer);
|
||||
|
||||
bool openWithExternalProgram(const QString &p_filePath, const QString &p_name) const;
|
||||
|
||||
bool isSameTypeBuffer(const Buffer *p_buffer, const QString &p_typeName) const;
|
||||
|
||||
static QString findFileTypeByFile(const QString &p_filePath);
|
||||
|
||||
QSharedPointer<NameBasedServer<IBufferFactory>> m_bufferServer;
|
||||
|
||||
// Managed by QObject.
|
||||
QVector<Buffer *> m_buffers;
|
||||
|
||||
// Mapping from suffix to file type or external program name.
|
||||
static QMap<QString, QString> s_suffixToFileType;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include <QMetaEnum>
|
||||
#include <QLocale>
|
||||
|
||||
#include <utils/utils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
#define READSTR(key) readString(appObj, userObj, (key))
|
||||
@ -10,6 +12,17 @@ using namespace vnotex;
|
||||
#define READBOOL(key) readBool(appObj, userObj, (key))
|
||||
#define READSTRLIST(key) readStringList(appObj, userObj, (key))
|
||||
|
||||
CoreConfig::FileTypeSuffix::FileTypeSuffix(const QString &p_name, const QStringList &p_suffixes)
|
||||
: m_name(p_name),
|
||||
m_suffixes(p_suffixes)
|
||||
{
|
||||
}
|
||||
|
||||
bool CoreConfig::FileTypeSuffix::operator==(const FileTypeSuffix &p_other) const
|
||||
{
|
||||
return m_name == p_other.m_name && m_suffixes == p_other.m_suffixes;
|
||||
}
|
||||
|
||||
QStringList CoreConfig::s_availableLocales;
|
||||
|
||||
CoreConfig::CoreConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
|
||||
@ -73,6 +86,8 @@ void CoreConfig::init(const QJsonObject &p_app,
|
||||
auto lineEnding = READSTR(QStringLiteral("line_ending"));
|
||||
m_lineEnding = stringToLineEndingPolicy(lineEnding);
|
||||
}
|
||||
|
||||
loadFileTypeSuffixes(appObj, userObj);
|
||||
}
|
||||
|
||||
QJsonObject CoreConfig::toJson() const
|
||||
@ -89,6 +104,7 @@ QJsonObject CoreConfig::toJson() const
|
||||
obj[QStringLiteral("history_max_count")] = m_historyMaxCount;
|
||||
obj[QStringLiteral("per_notebook_history")] = m_perNotebookHistoryEnabled;
|
||||
obj[QStringLiteral("line_ending")] = lineEndingPolicyToString(m_lineEnding);
|
||||
obj[QStringLiteral("file_type_suffixes")] = saveFileTypeSuffixes();
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -241,3 +257,67 @@ void CoreConfig::setLineEndingPolicy(LineEndingPolicy p_ending)
|
||||
{
|
||||
updateConfig(m_lineEnding, p_ending, this);
|
||||
}
|
||||
|
||||
void CoreConfig::loadFileTypeSuffixes(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
m_fileTypeSuffixes.clear();
|
||||
|
||||
QJsonArray arr;
|
||||
if (p_user.contains(QStringLiteral("file_type_suffixes"))) {
|
||||
arr = p_user[QStringLiteral("file_type_suffixes")].toArray();
|
||||
} else {
|
||||
arr = p_app[QStringLiteral("file_type_suffixes")].toArray();
|
||||
}
|
||||
|
||||
m_fileTypeSuffixes.reserve(arr.size());
|
||||
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
const auto obj = arr[i].toObject();
|
||||
const auto name = obj[QStringLiteral("name")].toString();
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
const auto suffixes = readStringList(obj, QStringLiteral("suffixes"));
|
||||
if (suffixes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
m_fileTypeSuffixes.push_back(FileTypeSuffix(name, Utils::toLower(suffixes)));
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray CoreConfig::saveFileTypeSuffixes() const
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const auto &fts : m_fileTypeSuffixes) {
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("name")] = fts.m_name;
|
||||
writeStringList(obj, QStringLiteral("suffixes"), fts.m_suffixes);
|
||||
arr.push_back(obj);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
const QVector<CoreConfig::FileTypeSuffix> &CoreConfig::getFileTypeSuffixes() const
|
||||
{
|
||||
return m_fileTypeSuffixes;
|
||||
}
|
||||
|
||||
void CoreConfig::setFileTypeSuffixes(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes)
|
||||
{
|
||||
updateConfig(m_fileTypeSuffixes, p_fileTypeSuffixes, this);
|
||||
}
|
||||
|
||||
const QStringList *CoreConfig::findFileTypeSuffix(const QString &p_name) const
|
||||
{
|
||||
if (p_name.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto &fts : m_fileTypeSuffixes) {
|
||||
if (fts.m_name == p_name) {
|
||||
return &fts.m_suffixes;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -72,6 +72,19 @@ namespace vnotex
|
||||
};
|
||||
Q_ENUM(Shortcut)
|
||||
|
||||
struct FileTypeSuffix
|
||||
{
|
||||
FileTypeSuffix() = default;
|
||||
|
||||
FileTypeSuffix(const QString &p_name, const QStringList &p_suffixes);
|
||||
|
||||
bool operator==(const FileTypeSuffix &p_other) const;
|
||||
|
||||
QString m_name;
|
||||
|
||||
QStringList m_suffixes;
|
||||
};
|
||||
|
||||
CoreConfig(ConfigMgr *p_mgr, IConfig *p_topConfig);
|
||||
|
||||
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
|
||||
@ -115,6 +128,11 @@ namespace vnotex
|
||||
LineEndingPolicy getLineEndingPolicy() const;
|
||||
void setLineEndingPolicy(LineEndingPolicy p_ending);
|
||||
|
||||
const QVector<FileTypeSuffix> &getFileTypeSuffixes() const;
|
||||
void setFileTypeSuffixes(const QVector<FileTypeSuffix> &p_fileTypeSuffixes);
|
||||
|
||||
const QStringList *findFileTypeSuffix(const QString &p_name) const;
|
||||
|
||||
private:
|
||||
friend class MainConfig;
|
||||
|
||||
@ -124,6 +142,10 @@ namespace vnotex
|
||||
|
||||
QJsonObject saveShortcuts() const;
|
||||
|
||||
void loadFileTypeSuffixes(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
|
||||
QJsonArray saveFileTypeSuffixes() const;
|
||||
|
||||
// Theme name.
|
||||
QString m_theme;
|
||||
|
||||
@ -157,6 +179,8 @@ namespace vnotex
|
||||
|
||||
LineEndingPolicy m_lineEnding = LineEndingPolicy::LF;
|
||||
|
||||
QVector<FileTypeSuffix> m_fileTypeSuffixes;
|
||||
|
||||
static QStringList s_availableLocales;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
@ -59,7 +59,6 @@ namespace vnotex
|
||||
|
||||
const FileType &getContentType() const;
|
||||
|
||||
protected:
|
||||
void setContentType(int p_type);
|
||||
|
||||
private:
|
||||
|
@ -50,6 +50,9 @@ namespace vnotex
|
||||
// Whether should save this file into session.
|
||||
bool m_sessionEnabled = true;
|
||||
|
||||
// Whether specify the built-in file type to open as or the external program to open with.
|
||||
QString m_fileType;
|
||||
|
||||
std::function<void()> m_hooks[Hook::MaxHook];
|
||||
};
|
||||
}
|
||||
|
@ -56,6 +56,13 @@ QJsonObject SessionConfig::ExternalProgram::toJson() const
|
||||
return jobj;
|
||||
}
|
||||
|
||||
QString SessionConfig::ExternalProgram::fetchCommand(const QString &p_file) const
|
||||
{
|
||||
auto command(m_command);
|
||||
command.replace(QStringLiteral("%1"), QString("\"%1\"").arg(p_file));
|
||||
return command;
|
||||
}
|
||||
|
||||
SessionConfig::SessionConfig(ConfigMgr *p_mgr)
|
||||
: IConfig(p_mgr, nullptr)
|
||||
{
|
||||
@ -438,6 +445,16 @@ const QVector<SessionConfig::ExternalProgram> &SessionConfig::getExternalProgram
|
||||
return m_externalPrograms;
|
||||
}
|
||||
|
||||
const SessionConfig::ExternalProgram *SessionConfig::findExternalProgram(const QString &p_name) const
|
||||
{
|
||||
for (const auto &pro : m_externalPrograms) {
|
||||
if (pro.m_name == p_name) {
|
||||
return &pro;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const QVector<HistoryItem> &SessionConfig::getHistory() const
|
||||
{
|
||||
return m_history;
|
||||
|
@ -66,6 +66,8 @@ namespace vnotex
|
||||
|
||||
QJsonObject toJson() const;
|
||||
|
||||
QString fetchCommand(const QString &p_file) const;
|
||||
|
||||
QString m_name;
|
||||
|
||||
// %1: the file paths to open.
|
||||
@ -134,6 +136,7 @@ namespace vnotex
|
||||
void removeQuickAccessFile(const QString &p_file);
|
||||
|
||||
const QVector<ExternalProgram> &getExternalPrograms() const;
|
||||
const ExternalProgram *findExternalProgram(const QString &p_name) const;
|
||||
|
||||
const QVector<HistoryItem> &getHistory() const;
|
||||
void addHistory(const HistoryItem &p_item);
|
||||
|
@ -99,6 +99,8 @@ void VNoteX::initNotebookMgr()
|
||||
|
||||
void VNoteX::initBufferMgr()
|
||||
{
|
||||
BufferMgr::updateSuffixToFileType(ConfigMgr::getInst().getCoreConfig().getFileTypeSuffixes());
|
||||
|
||||
Q_ASSERT(!m_bufferMgr);
|
||||
m_bufferMgr = new BufferMgr(this);
|
||||
m_bufferMgr->init();
|
||||
|
@ -6,7 +6,7 @@
|
||||
"version" : "3.12.0"
|
||||
},
|
||||
"core" : {
|
||||
"theme" : "moonlight",
|
||||
"theme" : "pure",
|
||||
"locale" : "",
|
||||
"shortcuts" : {
|
||||
"FullScreen" : "F11",
|
||||
@ -61,6 +61,25 @@
|
||||
"MoveOneSplitRight" : "Ctrl+G, Shift+L",
|
||||
"OpenLastClosedFile" : "Ctrl+Shift+T"
|
||||
},
|
||||
"file_type_suffixes" : [
|
||||
{
|
||||
"name" : "Markdown",
|
||||
"suffixes" : [
|
||||
"md",
|
||||
"mkd",
|
||||
"rmd",
|
||||
"markdown"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "Text",
|
||||
"suffixes" : [
|
||||
"txt",
|
||||
"text",
|
||||
"log"
|
||||
]
|
||||
}
|
||||
],
|
||||
"shortcut_leader_key" : "Ctrl+G",
|
||||
"toolbar_icon_size" : 18,
|
||||
"docks_tabbar_icon_size" : 24,
|
||||
|
@ -204,3 +204,12 @@ QColor Utils::toColor(const QString &p_color)
|
||||
|
||||
return QColor(p_color);
|
||||
}
|
||||
|
||||
QStringList Utils::toLower(const QStringList &p_list)
|
||||
{
|
||||
QStringList lowerList;
|
||||
for (const auto &ele : p_list) {
|
||||
lowerList << ele.toLower();
|
||||
}
|
||||
return lowerList;
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ namespace vnotex
|
||||
static QJsonValue parseAndReadJson(const QJsonObject &p_obj, const QString &p_exp);
|
||||
|
||||
static QColor toColor(const QString &p_color);
|
||||
|
||||
static QStringList toLower(const QStringList &p_list);
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <QLayout>
|
||||
#include <QPushButton>
|
||||
#include <QSplitter>
|
||||
#include <QFormLayout>
|
||||
|
||||
#include <core/global.h>
|
||||
|
||||
@ -460,3 +461,10 @@ bool WidgetUtils::distributeWidgetsOfSplitter(QSplitter *p_splitter)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WidgetUtils::clearLayout(QFormLayout *p_layout)
|
||||
{
|
||||
for (int i = p_layout->rowCount() - 1; i >= 0; --i) {
|
||||
p_layout->removeRow(i);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ class QLayout;
|
||||
class QPushButton;
|
||||
class QSplitter;
|
||||
class QScreen;
|
||||
class QFormLayout;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
@ -90,6 +91,8 @@ namespace vnotex
|
||||
|
||||
static bool distributeWidgetsOfSplitter(QSplitter *p_splitter);
|
||||
|
||||
static void clearLayout(QFormLayout *p_layout);
|
||||
|
||||
private:
|
||||
static void resizeToHideScrollBar(QScrollArea *p_scroll, bool p_vertical, bool p_horizontal);
|
||||
};
|
||||
|
132
src/widgets/dialogs/settings/fileassociationpage.cpp
Normal file
132
src/widgets/dialogs/settings/fileassociationpage.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
#include "fileassociationpage.h"
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMap>
|
||||
|
||||
#include <widgets/widgetsfactory.h>
|
||||
#include <widgets/lineedit.h>
|
||||
#include <core/coreconfig.h>
|
||||
#include <core/sessionconfig.h>
|
||||
#include <core/configmgr.h>
|
||||
#include <core/buffermgr.h>
|
||||
#include <utils/widgetutils.h>
|
||||
#include <utils/utils.h>
|
||||
#include <core/vnotex.h>
|
||||
#include <buffer/filetypehelper.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const char *FileAssociationPage::c_nameProperty = "name";
|
||||
|
||||
const QChar FileAssociationPage::c_suffixSeparator = QLatin1Char(';');
|
||||
|
||||
FileAssociationPage::FileAssociationPage(QWidget *p_parent)
|
||||
: SettingsPage(p_parent)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void FileAssociationPage::setupUI()
|
||||
{
|
||||
auto mainLayout = new QVBoxLayout(this);
|
||||
|
||||
m_builtInFileTypesBox = new QGroupBox(tr("Built-In File Types"), this);
|
||||
WidgetsFactory::createFormLayout(m_builtInFileTypesBox);
|
||||
mainLayout->addWidget(m_builtInFileTypesBox);
|
||||
|
||||
m_externalProgramsBox = new QGroupBox(tr("External Programs"), this);
|
||||
WidgetsFactory::createFormLayout(m_externalProgramsBox);
|
||||
mainLayout->addWidget(m_externalProgramsBox);
|
||||
}
|
||||
|
||||
void FileAssociationPage::loadInternal()
|
||||
{
|
||||
loadBuiltInTypesGroup(m_builtInFileTypesBox);
|
||||
|
||||
loadExternalProgramsGroup(m_externalProgramsBox);
|
||||
}
|
||||
|
||||
bool FileAssociationPage::saveInternal()
|
||||
{
|
||||
auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
|
||||
|
||||
QVector<CoreConfig::FileTypeSuffix> fileTypeSuffixes;
|
||||
|
||||
auto lineEdits = m_builtInFileTypesBox->findChildren<QLineEdit *>(QString());
|
||||
lineEdits << m_externalProgramsBox->findChildren<QLineEdit *>(QString());
|
||||
fileTypeSuffixes.reserve(lineEdits.size());
|
||||
for (const auto lineEdit : lineEdits) {
|
||||
auto name = lineEdit->property(c_nameProperty).toString();
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
auto suffixes = lineEdit->text().split(c_suffixSeparator, Qt::SkipEmptyParts);
|
||||
if (suffixes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
fileTypeSuffixes.push_back(CoreConfig::FileTypeSuffix(name, Utils::toLower(suffixes)));
|
||||
}
|
||||
|
||||
coreConfig.setFileTypeSuffixes(fileTypeSuffixes);
|
||||
|
||||
FileTypeHelper::getInst().reload();
|
||||
|
||||
BufferMgr::updateSuffixToFileType(coreConfig.getFileTypeSuffixes());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString FileAssociationPage::title() const
|
||||
{
|
||||
return tr("File Associations");
|
||||
}
|
||||
|
||||
void FileAssociationPage::loadBuiltInTypesGroup(QGroupBox *p_box)
|
||||
{
|
||||
auto layout = static_cast<QFormLayout *>(p_box->layout());
|
||||
WidgetUtils::clearLayout(layout);
|
||||
|
||||
const auto &types = FileTypeHelper::getInst().getAllFileTypes();
|
||||
|
||||
for (const auto &ft : types) {
|
||||
if (ft.m_type == FileType::Others) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto lineEdit = WidgetsFactory::createLineEdit(p_box);
|
||||
layout->addRow(ft.m_displayName, lineEdit);
|
||||
connect(lineEdit, &QLineEdit::textChanged,
|
||||
this, &FileAssociationPage::pageIsChanged);
|
||||
|
||||
lineEdit->setPlaceholderText(tr("Suffixes separated by ;"));
|
||||
lineEdit->setToolTip(tr("List of suffixes for this file type"));
|
||||
lineEdit->setProperty(c_nameProperty, ft.m_typeName);
|
||||
lineEdit->setText(ft.m_suffixes.join(c_suffixSeparator));
|
||||
}
|
||||
}
|
||||
|
||||
void FileAssociationPage::loadExternalProgramsGroup(QGroupBox *p_box)
|
||||
{
|
||||
auto layout = static_cast<QFormLayout *>(p_box->layout());
|
||||
WidgetUtils::clearLayout(layout);
|
||||
|
||||
const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
|
||||
const auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||
for (const auto &pro : sessionConfig.getExternalPrograms()) {
|
||||
auto lineEdit = WidgetsFactory::createLineEdit(p_box);
|
||||
layout->addRow(pro.m_name, lineEdit);
|
||||
connect(lineEdit, &QLineEdit::textChanged,
|
||||
this, &FileAssociationPage::pageIsChanged);
|
||||
|
||||
lineEdit->setPlaceholderText(tr("Suffixes separated by ;"));
|
||||
lineEdit->setToolTip(tr("List of suffixes to open with external program"));
|
||||
lineEdit->setProperty(c_nameProperty, pro.m_name);
|
||||
|
||||
auto suffixes = coreConfig.findFileTypeSuffix(pro.m_name);
|
||||
if (suffixes) {
|
||||
lineEdit->setText(suffixes->join(c_suffixSeparator));
|
||||
}
|
||||
}
|
||||
}
|
40
src/widgets/dialogs/settings/fileassociationpage.h
Normal file
40
src/widgets/dialogs/settings/fileassociationpage.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef FILEASSOCIATIONPAGE_H
|
||||
#define FILEASSOCIATIONPAGE_H
|
||||
|
||||
#include "settingspage.h"
|
||||
|
||||
class QGroupBox;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class FileAssociationPage : public SettingsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FileAssociationPage(QWidget *p_parent = nullptr);
|
||||
|
||||
QString title() const Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void loadInternal() Q_DECL_OVERRIDE;
|
||||
|
||||
bool saveInternal() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void loadBuiltInTypesGroup(QGroupBox *p_box);
|
||||
|
||||
void loadExternalProgramsGroup(QGroupBox *p_box);
|
||||
|
||||
QGroupBox *m_builtInFileTypesBox = nullptr;
|
||||
|
||||
QGroupBox *m_externalProgramsBox = nullptr;
|
||||
|
||||
static const char *c_nameProperty;
|
||||
|
||||
static const QChar c_suffixSeparator;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FILEASSOCIATIONPAGE_H
|
@ -27,6 +27,7 @@
|
||||
#include "imagehostpage.h"
|
||||
#include "vipage.h"
|
||||
#include "notemanagementpage.h"
|
||||
#include "fileassociationpage.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -173,6 +174,12 @@ void SettingsDialog::setupPages()
|
||||
*/
|
||||
}
|
||||
|
||||
// File Association.
|
||||
{
|
||||
auto page = new FileAssociationPage(this);
|
||||
addPage(page);
|
||||
}
|
||||
|
||||
setChangesUnsaved(false);
|
||||
m_pageExplorer->setCurrentItem(m_pageExplorer->topLevelItem(0), 0, QItemSelectionModel::ClearAndSelect);
|
||||
m_pageExplorer->expandAll();
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "mainwindow.h"
|
||||
#include <utils/iconutils.h>
|
||||
#include <utils/docsutils.h>
|
||||
#include <utils/processutils.h>
|
||||
#include "treewidget.h"
|
||||
#include "listwidget.h"
|
||||
#include "dialogs/notepropertiesdialog.h"
|
||||
@ -38,6 +37,7 @@
|
||||
#include <core/coreconfig.h>
|
||||
#include <core/sessionconfig.h>
|
||||
#include <core/widgetconfig.h>
|
||||
#include <buffer/filetypehelper.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -2106,15 +2106,32 @@ void NotebookNodeExplorer::addOpenWithMenu(QMenu *p_menu, bool p_master)
|
||||
{
|
||||
auto subMenu = p_menu->addMenu(tr("Open &With"));
|
||||
|
||||
const auto &types = FileTypeHelper::getInst().getAllFileTypes();
|
||||
|
||||
for (const auto &ft : types) {
|
||||
if (ft.m_type == FileType::Others) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QAction *act = subMenu->addAction(ft.m_displayName);
|
||||
connect(act, &QAction::triggered,
|
||||
this, [this, act, p_master]() {
|
||||
openSelectedNodesWithProgram(act->data().toString(), p_master);
|
||||
});
|
||||
act->setData(ft.m_typeName);
|
||||
}
|
||||
|
||||
subMenu->addSeparator();
|
||||
|
||||
{
|
||||
const auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||
for (const auto &pro : sessionConfig.getExternalPrograms()) {
|
||||
QAction *act = subMenu->addAction(pro.m_name);
|
||||
connect(act, &QAction::triggered,
|
||||
this, [this, act, p_master]() {
|
||||
openSelectedNodesWithCommand(act->data().toString(), p_master);
|
||||
openSelectedNodesWithProgram(act->data().toString(), p_master);
|
||||
});
|
||||
act->setData(pro.m_command);
|
||||
act->setData(pro.m_name);
|
||||
WidgetUtils::addActionShortcutText(act, pro.m_shortcut);
|
||||
}
|
||||
}
|
||||
@ -2125,7 +2142,7 @@ void NotebookNodeExplorer::addOpenWithMenu(QMenu *p_menu, bool p_master)
|
||||
auto defaultAct = subMenu->addAction(tr("System Default Program"));
|
||||
connect(defaultAct, &QAction::triggered,
|
||||
this, [this, defaultAct, p_master]() {
|
||||
openSelectedNodesWithCommand(QString(), p_master);
|
||||
openSelectedNodesWithProgram(QString(), p_master);
|
||||
});
|
||||
const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
|
||||
WidgetUtils::addActionShortcutText(defaultAct, coreConfig.getShortcut(CoreConfig::OpenWithDefaultProgram));
|
||||
@ -2157,7 +2174,7 @@ void NotebookNodeExplorer::setupShortcuts()
|
||||
if (!isCombinedExploreMode()) {
|
||||
isMaster = m_masterExplorer->hasFocus();
|
||||
}
|
||||
openSelectedNodesWithCommand(QString(), isMaster);
|
||||
openSelectedNodesWithProgram(QString(), isMaster);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -2165,21 +2182,21 @@ void NotebookNodeExplorer::setupShortcuts()
|
||||
const auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||
for (const auto &pro : sessionConfig.getExternalPrograms()) {
|
||||
auto shortcut = WidgetUtils::createShortcut(pro.m_shortcut, this);
|
||||
const auto &command = pro.m_command;
|
||||
const auto &name = pro.m_name;
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this, command]() {
|
||||
this, [this, name]() {
|
||||
bool isMaster = true;
|
||||
if (!isCombinedExploreMode()) {
|
||||
isMaster = m_masterExplorer->hasFocus();
|
||||
}
|
||||
openSelectedNodesWithCommand(command, isMaster);
|
||||
openSelectedNodesWithProgram(name, isMaster);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotebookNodeExplorer::openSelectedNodesWithCommand(const QString &p_command, bool p_master)
|
||||
void NotebookNodeExplorer::openSelectedNodesWithProgram(const QString &p_name, bool p_master)
|
||||
{
|
||||
const bool closeBefore = ConfigMgr::getInst().getWidgetConfig().getNodeExplorerCloseBeforeOpenWithEnabled();
|
||||
const auto files = getSelectedNodesPath(p_master);
|
||||
@ -2196,12 +2213,12 @@ void NotebookNodeExplorer::openSelectedNodesWithCommand(const QString &p_command
|
||||
}
|
||||
}
|
||||
|
||||
if (p_command.isEmpty()) {
|
||||
if (p_name.isEmpty()) {
|
||||
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(file));
|
||||
} else {
|
||||
auto command = p_command;
|
||||
command.replace(QStringLiteral("%1"), QString("\"%1\"").arg(file));
|
||||
ProcessUtils::startDetached(command);
|
||||
auto paras = QSharedPointer<FileOpenParameters>::create();
|
||||
paras->m_fileType = p_name;
|
||||
emit VNoteX::getInst().openFileRequested(file, paras);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ namespace vnotex
|
||||
|
||||
QStringList getSelectedNodesPath(bool p_master) const;
|
||||
|
||||
void openSelectedNodesWithCommand(const QString &p_command, bool p_master);
|
||||
void openSelectedNodesWithProgram(const QString &p_name, bool p_master);
|
||||
|
||||
bool belongsToMasterExplorer(const Node *p_node) const;
|
||||
|
||||
|
@ -9,7 +9,6 @@ using namespace vnotex;
|
||||
WebPage::WebPage(QWidget *p_parent)
|
||||
: QWebEnginePage(p_parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool WebPage::acceptNavigationRequest(const QUrl &p_url,
|
||||
@ -29,7 +28,7 @@ bool WebPage::acceptNavigationRequest(const QUrl &p_url,
|
||||
if (scheme == QStringLiteral("data")) {
|
||||
// Qt 5.12 and above will trigger this when calling QWebEngineView::setHtml().
|
||||
return true;
|
||||
} else if (scheme == QStringLiteral("chrome-devtools")) {
|
||||
} else if (scheme == QStringLiteral("chrome-devtools") || scheme == QStringLiteral("devtools")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ SOURCES += \
|
||||
$$PWD/dialogs/selectionitemwidget.cpp \
|
||||
$$PWD/dialogs/settings/appearancepage.cpp \
|
||||
$$PWD/dialogs/settings/editorpage.cpp \
|
||||
$$PWD/dialogs/settings/fileassociationpage.cpp \
|
||||
$$PWD/dialogs/settings/generalpage.cpp \
|
||||
$$PWD/dialogs/settings/imagehostpage.cpp \
|
||||
$$PWD/dialogs/settings/markdowneditorpage.cpp \
|
||||
@ -150,6 +151,7 @@ HEADERS += \
|
||||
$$PWD/dialogs/selectionitemwidget.h \
|
||||
$$PWD/dialogs/settings/appearancepage.h \
|
||||
$$PWD/dialogs/settings/editorpage.h \
|
||||
$$PWD/dialogs/settings/fileassociationpage.h \
|
||||
$$PWD/dialogs/settings/generalpage.h \
|
||||
$$PWD/dialogs/settings/imagehostpage.h \
|
||||
$$PWD/dialogs/settings/markdowneditorpage.h \
|
||||
|
Loading…
x
Reference in New Issue
Block a user