mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-04 21:39:52 +08:00
suport snippet
This commit is contained in:
parent
ebd65b26be
commit
d1d8fabb60
@ -1 +1 @@
|
||||
Subproject commit ace5699b65e61dfaca1e252364a8a735048e115b
|
||||
Subproject commit 98274148a0e1ad371f29abe072fac35bf5d7b6df
|
@ -24,7 +24,7 @@
|
||||
using namespace vnotex;
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
#define VX_DEBUG_WEB
|
||||
// #define VX_DEBUG_WEB
|
||||
#endif
|
||||
|
||||
const QString ConfigMgr::c_orgName = "VNote";
|
||||
@ -388,6 +388,13 @@ QString ConfigMgr::getUserTemplateFolder() const
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserSnippetFolder() const
|
||||
{
|
||||
auto folderPath = PathUtils::concatenateFilePath(m_userConfigFolderPath, QStringLiteral("snippets"));
|
||||
QDir().mkpath(folderPath);
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserOrAppFile(const QString &p_filePath) const
|
||||
{
|
||||
QFileInfo fi(p_filePath);
|
||||
|
@ -93,6 +93,8 @@ namespace vnotex
|
||||
|
||||
QString getUserTemplateFolder() const;
|
||||
|
||||
QString getUserSnippetFolder() const;
|
||||
|
||||
// If @p_filePath is absolute, just return it.
|
||||
// Otherwise, first try to find it in user folder, then in app folder.
|
||||
QString getUserOrAppFile(const QString &p_filePath) const;
|
||||
|
@ -24,6 +24,7 @@ namespace vnotex
|
||||
NavigationDock,
|
||||
OutlineDock,
|
||||
SearchDock,
|
||||
SnippetDock,
|
||||
LocationListDock,
|
||||
Search,
|
||||
NavigationMode,
|
||||
|
@ -216,7 +216,7 @@ QJsonObject SessionConfig::saveStateAndGeometry() const
|
||||
QJsonObject obj;
|
||||
writeByteArray(obj, QStringLiteral("main_window_state"), m_mainWindowStateGeometry.m_mainState);
|
||||
writeByteArray(obj, QStringLiteral("main_window_geometry"), m_mainWindowStateGeometry.m_mainGeometry);
|
||||
writeBitArray(obj, QStringLiteral("docks_visibility_before_expand"), m_mainWindowStateGeometry.m_docksVisibilityBeforeExpand);
|
||||
writeStringList(obj, QStringLiteral("visible_docks_before_expand"), m_mainWindowStateGeometry.m_visibleDocksBeforeExpand);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -336,7 +336,7 @@ void SessionConfig::loadStateAndGeometry(const QJsonObject &p_session)
|
||||
const auto obj = p_session.value(QStringLiteral("state_geometry")).toObject();
|
||||
m_mainWindowStateGeometry.m_mainState = readByteArray(obj, QStringLiteral("main_window_state"));
|
||||
m_mainWindowStateGeometry.m_mainGeometry = readByteArray(obj, QStringLiteral("main_window_geometry"));
|
||||
m_mainWindowStateGeometry.m_docksVisibilityBeforeExpand = readBitArray(obj, QStringLiteral("docks_visibility_before_expand"));
|
||||
m_mainWindowStateGeometry.m_visibleDocksBeforeExpand = readStringList(obj, QStringLiteral("visible_docks_before_expand"));
|
||||
}
|
||||
|
||||
QByteArray SessionConfig::getViewAreaSessionAndClear()
|
||||
|
@ -35,14 +35,14 @@ namespace vnotex
|
||||
{
|
||||
return m_mainState == p_other.m_mainState
|
||||
&& m_mainGeometry == p_other.m_mainGeometry
|
||||
&& m_docksVisibilityBeforeExpand == p_other.m_docksVisibilityBeforeExpand;
|
||||
&& m_visibleDocksBeforeExpand == p_other.m_visibleDocksBeforeExpand;
|
||||
}
|
||||
|
||||
QByteArray m_mainState;
|
||||
|
||||
QByteArray m_mainGeometry;
|
||||
|
||||
QBitArray m_docksVisibilityBeforeExpand;
|
||||
QStringList m_visibleDocksBeforeExpand;
|
||||
};
|
||||
|
||||
enum OpenGL
|
||||
|
@ -18,6 +18,7 @@
|
||||
"NavigationDock" : "Ctrl+G, A",
|
||||
"OutlineDock" : "Ctrl+G, U",
|
||||
"SearchDock" : "Ctrl+G, S",
|
||||
"SnippetDock" : "",
|
||||
"LocationListDock" : "Ctrl+G, L",
|
||||
"Search" : "Ctrl+Alt+F",
|
||||
"NavigationMode" : "Ctrl+G, W",
|
||||
|
@ -1,7 +1,7 @@
|
||||
# External Programs
|
||||
VNote allows user to open notes with **external programs** via the `Open With` in the context menu of the node explorer.
|
||||
|
||||
To add custom external programs, user needs to edit the session configuration. A sample may look like this:
|
||||
To add custom external programs, user needs to edit the session configuration (the `session.json` file in user configuration folder). A sample may look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -27,4 +27,4 @@ An external program could have 3 properties:
|
||||
1. Use `%1` as a placeholder which will be replaced by the real file paths (automatically wrapped by double quotes);
|
||||
3. `shortcut`: the shortcut assigned to this external program;
|
||||
|
||||
Close VNote before editting the session configuration.
|
||||
**Close VNote** before editting the session configuration.
|
||||
|
@ -1,7 +1,7 @@
|
||||
# 外部程序
|
||||
VNote 支持通过在节点浏览器上下文菜单中的 `打开方式` 来调用 **外部程序** 打开笔记。
|
||||
|
||||
用户需要编辑会话配置来添加自定义外部程序。一个例子如下:
|
||||
用户需要编辑会话配置(用户配置文件夹下的 `session.json` 文件)来添加自定义外部程序。一个例子如下:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -27,4 +27,4 @@ VNote 支持通过在节点浏览器上下文菜单中的 `打开方式` 来调
|
||||
1. 使用 `%1` 占位符,会被替换为真实的文件路径(自动加上双引号包裹);
|
||||
3. `shortcut`: 分配给该外部程序的快捷键;
|
||||
|
||||
修改配置前请关闭 VNote。
|
||||
修改配置前请 **关闭 VNote** 。
|
||||
|
30
src/snippet/dynamicsnippet.cpp
Normal file
30
src/snippet/dynamicsnippet.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "dynamicsnippet.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
DynamicSnippet::DynamicSnippet(const QString &p_name,
|
||||
const QString &p_description,
|
||||
const Callback &p_callback)
|
||||
: Snippet(p_name,
|
||||
p_description,
|
||||
Snippet::InvalidShortcut,
|
||||
false,
|
||||
QString(),
|
||||
QString()),
|
||||
m_callback(p_callback)
|
||||
{
|
||||
setType(Type::Dynamic);
|
||||
setReadOnly(true);
|
||||
}
|
||||
|
||||
QString DynamicSnippet::apply(const QString &p_selectedText,
|
||||
const QString &p_indentationSpaces,
|
||||
int &p_cursorOffset)
|
||||
{
|
||||
Q_UNUSED(p_indentationSpaces);
|
||||
auto text = m_callback(p_selectedText);
|
||||
p_cursorOffset = text.size();
|
||||
return text;
|
||||
}
|
30
src/snippet/dynamicsnippet.h
Normal file
30
src/snippet/dynamicsnippet.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef DYNAMICSNIPPET_H
|
||||
#define DYNAMICSNIPPET_H
|
||||
|
||||
#include "snippet.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Snippet based on function.
|
||||
// To replace the legacy Magic Word.
|
||||
class DynamicSnippet : public Snippet
|
||||
{
|
||||
public:
|
||||
typedef std::function<QString(const QString &)> Callback;
|
||||
|
||||
DynamicSnippet(const QString &p_name,
|
||||
const QString &p_description,
|
||||
const Callback &p_callback);
|
||||
|
||||
QString apply(const QString &p_selectedText,
|
||||
const QString &p_indentationSpaces,
|
||||
int &p_cursorOffset) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
Callback m_callback;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // DYNAMICSNIPPET_H
|
174
src/snippet/snippet.cpp
Normal file
174
src/snippet/snippet.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
#include "snippet.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <utils/utils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const QString Snippet::c_defaultCursorMark = QStringLiteral("@@");
|
||||
|
||||
const QString Snippet::c_defaultSelectionMark = QStringLiteral("$$");
|
||||
|
||||
Snippet::Snippet(const QString &p_name)
|
||||
: m_name(p_name)
|
||||
{
|
||||
}
|
||||
|
||||
Snippet::Snippet(const QString &p_name,
|
||||
const QString &p_content,
|
||||
int p_shortcut,
|
||||
bool p_indentAsFirstLine,
|
||||
const QString &p_cursorMark,
|
||||
const QString &p_selectionMark)
|
||||
: m_type(Type::Text),
|
||||
m_name(p_name),
|
||||
m_content(p_content),
|
||||
m_shortcut(p_shortcut),
|
||||
m_indentAsFirstLine(p_indentAsFirstLine),
|
||||
m_cursorMark(p_cursorMark),
|
||||
m_selectionMark(p_selectionMark)
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject Snippet::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
|
||||
obj[QStringLiteral("type")] = static_cast<int>(m_type);
|
||||
obj[QStringLiteral("content")] = m_content;
|
||||
obj[QStringLiteral("shortcut")] = m_shortcut;
|
||||
obj[QStringLiteral("indent_as_first_line")] = m_indentAsFirstLine;
|
||||
obj[QStringLiteral("cursor_mark")] = m_cursorMark;
|
||||
obj[QStringLiteral("selection_mark")] = m_selectionMark;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Snippet::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
m_type = static_cast<Type>(p_jobj[QStringLiteral("type")].toInt());
|
||||
m_content = p_jobj[QStringLiteral("content")].toString();
|
||||
m_shortcut = p_jobj[QStringLiteral("shortcut")].toInt();
|
||||
m_indentAsFirstLine = p_jobj[QStringLiteral("indent_as_first_line")].toBool();
|
||||
m_cursorMark = p_jobj[QStringLiteral("cursor_mark")].toString();
|
||||
m_selectionMark = p_jobj[QStringLiteral("selection_mark")].toString();
|
||||
}
|
||||
|
||||
bool Snippet::isValid() const
|
||||
{
|
||||
return !m_name.isEmpty() && m_type != Type::Invalid;
|
||||
}
|
||||
|
||||
const QString &Snippet::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
int Snippet::getShortcut() const
|
||||
{
|
||||
return m_shortcut;
|
||||
}
|
||||
|
||||
QString Snippet::getShortcutString() const
|
||||
{
|
||||
if (m_shortcut == InvalidShortcut) {
|
||||
return QString();
|
||||
} else {
|
||||
return Utils::intToString(m_shortcut, 2);
|
||||
}
|
||||
}
|
||||
|
||||
Snippet::Type Snippet::getType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
const QString &Snippet::getCursorMark() const
|
||||
{
|
||||
return m_cursorMark;
|
||||
}
|
||||
|
||||
const QString &Snippet::getSelectionMark() const
|
||||
{
|
||||
return m_selectionMark;
|
||||
}
|
||||
|
||||
bool Snippet::isIndentAsFirstLineEnabled() const
|
||||
{
|
||||
return m_indentAsFirstLine;
|
||||
}
|
||||
|
||||
const QString &Snippet::getContent() const
|
||||
{
|
||||
return m_content;
|
||||
}
|
||||
|
||||
QString Snippet::apply(const QString &p_selectedText,
|
||||
const QString &p_indentationSpaces,
|
||||
int &p_cursorOffset)
|
||||
{
|
||||
QString appliedText;
|
||||
p_cursorOffset = 0;
|
||||
if (!isValid() || m_content.isEmpty()) {
|
||||
qWarning() << "failed to apply an invalid snippet" << m_name;
|
||||
return appliedText;
|
||||
}
|
||||
|
||||
// Indent each line after the first line.
|
||||
if (m_indentAsFirstLine && !p_indentationSpaces.isEmpty()) {
|
||||
auto lines = m_content.split(QLatin1Char('\n'));
|
||||
Q_ASSERT(!lines.isEmpty());
|
||||
appliedText = lines[0];
|
||||
for (int i = 1; i < lines.size(); ++i) {
|
||||
appliedText += QLatin1Char('\n') + p_indentationSpaces + lines[i];
|
||||
}
|
||||
} else {
|
||||
appliedText = m_content;
|
||||
}
|
||||
|
||||
// Find the cursor mark and break the content.
|
||||
QString secondPart;
|
||||
if (!m_cursorMark.isEmpty()) {
|
||||
QStringList parts = appliedText.split(m_cursorMark);
|
||||
Q_ASSERT(!parts.isEmpty());
|
||||
if (parts.size() > 2) {
|
||||
qWarning() << "failed to apply snippet with multiple cursor marks" << m_name;
|
||||
return QString();
|
||||
}
|
||||
|
||||
appliedText = parts[0];
|
||||
if (parts.size() == 2) {
|
||||
secondPart = parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the selection mark.
|
||||
if (!m_selectionMark.isEmpty()) {
|
||||
if (!appliedText.isEmpty()) {
|
||||
appliedText.replace(m_selectionMark, p_selectedText);
|
||||
}
|
||||
|
||||
if (!secondPart.isEmpty()) {
|
||||
secondPart.replace(m_selectionMark, p_selectedText);
|
||||
}
|
||||
}
|
||||
|
||||
p_cursorOffset = appliedText.size();
|
||||
return appliedText + secondPart;
|
||||
}
|
||||
|
||||
bool Snippet::isReadOnly() const
|
||||
{
|
||||
return m_readOnly;
|
||||
}
|
||||
|
||||
void Snippet::setReadOnly(bool p_readOnly)
|
||||
{
|
||||
m_readOnly = p_readOnly;
|
||||
}
|
||||
|
||||
void Snippet::setType(Type p_type)
|
||||
{
|
||||
m_type = p_type;
|
||||
}
|
98
src/snippet/snippet.h
Normal file
98
src/snippet/snippet.h
Normal file
@ -0,0 +1,98 @@
|
||||
#ifndef SNIPPET_H
|
||||
#define SNIPPET_H
|
||||
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Snippet
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
Invalid,
|
||||
Text,
|
||||
Script,
|
||||
Dynamic
|
||||
};
|
||||
|
||||
enum { InvalidShortcut = -1 };
|
||||
|
||||
Snippet() = default;
|
||||
|
||||
explicit Snippet(const QString &p_name);
|
||||
|
||||
Snippet(const QString &p_name,
|
||||
const QString &p_content,
|
||||
int p_shortcut,
|
||||
bool p_indentAsFirstLine,
|
||||
const QString &p_cursorMark,
|
||||
const QString &p_selectionMark);
|
||||
|
||||
virtual ~Snippet() = default;
|
||||
|
||||
QJsonObject toJson() const;
|
||||
void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
bool isReadOnly() const;
|
||||
|
||||
void setReadOnly(bool p_readOnly);
|
||||
|
||||
const QString &getName() const;
|
||||
|
||||
Type getType() const;
|
||||
|
||||
int getShortcut() const;
|
||||
|
||||
QString getShortcutString() const;
|
||||
|
||||
const QString &getCursorMark() const;
|
||||
|
||||
const QString &getSelectionMark() const;
|
||||
|
||||
bool isIndentAsFirstLineEnabled() const;
|
||||
|
||||
const QString &getContent() const;
|
||||
|
||||
// Apply the snippet to generate result text.
|
||||
virtual QString apply(const QString &p_selectedText,
|
||||
const QString &p_indentationSpaces,
|
||||
int &p_cursorOffset);
|
||||
|
||||
static const QString c_defaultCursorMark;
|
||||
|
||||
static const QString c_defaultSelectionMark;
|
||||
|
||||
protected:
|
||||
void setType(Type p_type);
|
||||
|
||||
private:
|
||||
bool m_readOnly = false;
|
||||
|
||||
Type m_type = Type::Invalid;
|
||||
|
||||
// Name (and file name) of the snippet.
|
||||
// To avoid mixed with shortcut, the name should not contain digits.
|
||||
QString m_name;
|
||||
|
||||
// Content of the snippet if it is Text.
|
||||
// Embedded snippet is supported.
|
||||
QString m_content;
|
||||
|
||||
// Shortcut digits of this snippet.
|
||||
int m_shortcut = InvalidShortcut;
|
||||
|
||||
bool m_indentAsFirstLine = false;
|
||||
|
||||
// CursorMark is a mark string to indicate the cursor position after applying the snippet.
|
||||
QString m_cursorMark;
|
||||
|
||||
// SelectionMark is a mark string which will be replaced by the selected text before applying the snippet after a snippet is applied.
|
||||
QString m_selectionMark;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SNIPPET_H
|
12
src/snippet/snippet.pri
Normal file
12
src/snippet/snippet.pri
Normal file
@ -0,0 +1,12 @@
|
||||
QT += widgets
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/dynamicsnippet.h \
|
||||
$$PWD/snippet.h \
|
||||
$$PWD/snippetmgr.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/dynamicsnippet.cpp \
|
||||
$$PWD/snippet.cpp \
|
||||
$$PWD/snippetmgr.cpp
|
||||
|
433
src/snippet/snippetmgr.cpp
Normal file
433
src/snippet/snippetmgr.cpp
Normal file
@ -0,0 +1,433 @@
|
||||
#include "snippetmgr.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QSet>
|
||||
#include <QTextCursor>
|
||||
#include <QRegularExpression>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <core/configmgr.h>
|
||||
#include <buffer/buffer.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <vtextedit/vtextedit.h>
|
||||
#include <vtextedit/texteditutils.h>
|
||||
#include <vtextedit/textutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const QString SnippetMgr::c_snippetSymbolRegExp = QString("%([^%]+)%");
|
||||
|
||||
SnippetMgr::SnippetMgr()
|
||||
{
|
||||
loadSnippets();
|
||||
}
|
||||
|
||||
QString SnippetMgr::getSnippetFolder() const
|
||||
{
|
||||
return ConfigMgr::getInst().getUserSnippetFolder();
|
||||
}
|
||||
|
||||
const QVector<QSharedPointer<Snippet>> &SnippetMgr::getSnippets() const
|
||||
{
|
||||
return m_snippets;
|
||||
}
|
||||
|
||||
void SnippetMgr::loadSnippets()
|
||||
{
|
||||
Q_ASSERT(m_snippets.isEmpty());
|
||||
|
||||
auto builtInSnippets = loadBuiltInSnippets();
|
||||
|
||||
QSet<QString> names;
|
||||
for (const auto &snippet : builtInSnippets) {
|
||||
Q_ASSERT(!names.contains(snippet->getName()));
|
||||
names.insert(snippet->getName());
|
||||
}
|
||||
|
||||
// Look for all the *.json files.
|
||||
QDir dir(getSnippetFolder());
|
||||
dir.setFilter(QDir::Files | QDir::NoSymLinks);
|
||||
const auto jsonFiles = dir.entryList(QStringList() << "*.json");
|
||||
for (const auto &jsonFile : jsonFiles) {
|
||||
auto snip = loadSnippet(dir.filePath(jsonFile));
|
||||
if (snip->isValid()) {
|
||||
if (names.contains(snip->getName())) {
|
||||
qWarning() << "skip loading snippet with name conflict" << snip->getName() << jsonFile;
|
||||
continue;
|
||||
}
|
||||
|
||||
names.insert(snip->getName());
|
||||
|
||||
addOneSnippet(snip);
|
||||
}
|
||||
}
|
||||
|
||||
m_snippets.append(builtInSnippets);
|
||||
|
||||
qDebug() << "loaded" << m_snippets.size() << "snippets";
|
||||
}
|
||||
|
||||
QVector<int> SnippetMgr::getAvailableShortcuts(int p_exemption) const
|
||||
{
|
||||
QVector<int> shortcuts;
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
if (!m_shortcutToSnippet.contains(i) || i == p_exemption) {
|
||||
shortcuts.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
return shortcuts;
|
||||
}
|
||||
|
||||
QSharedPointer<Snippet> SnippetMgr::find(const QString &p_name, Qt::CaseSensitivity p_cs) const
|
||||
{
|
||||
if (p_cs == Qt::CaseInsensitive) {
|
||||
const auto lowerName = p_name.toLower();
|
||||
for (const auto &snip : m_snippets) {
|
||||
if (snip->getName().toLower() == lowerName) {
|
||||
return snip;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto &snip : m_snippets) {
|
||||
if (snip->getName() == p_name) {
|
||||
return snip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SnippetMgr::addSnippet(const QSharedPointer<Snippet> &p_snippet)
|
||||
{
|
||||
Q_ASSERT(!find(p_snippet->getName(), Qt::CaseInsensitive));
|
||||
saveSnippet(p_snippet);
|
||||
addOneSnippet(p_snippet);
|
||||
}
|
||||
|
||||
void SnippetMgr::addOneSnippet(const QSharedPointer<Snippet> &p_snippet)
|
||||
{
|
||||
m_snippets.push_back(p_snippet);
|
||||
addSnippetToShortcutMap(p_snippet);
|
||||
}
|
||||
|
||||
QSharedPointer<Snippet> SnippetMgr::loadSnippet(const QString &p_snippetFile) const
|
||||
{
|
||||
const auto obj = FileUtils::readJsonFile(p_snippetFile);
|
||||
auto snip = QSharedPointer<Snippet>::create(QFileInfo(p_snippetFile).completeBaseName());
|
||||
snip->fromJson(obj);
|
||||
return snip;
|
||||
}
|
||||
|
||||
void SnippetMgr::saveSnippet(const QSharedPointer<Snippet> &p_snippet)
|
||||
{
|
||||
Q_ASSERT(p_snippet->isValid()
|
||||
&& !p_snippet->isReadOnly()
|
||||
&& p_snippet->getType() != Snippet::Type::Dynamic);
|
||||
FileUtils::writeFile(getSnippetFile(p_snippet), p_snippet->toJson());
|
||||
}
|
||||
|
||||
void SnippetMgr::removeSnippet(const QString &p_name)
|
||||
{
|
||||
auto snippet = find(p_name);
|
||||
if (!snippet || snippet->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeSnippetFromShortcutMap(snippet);
|
||||
m_snippets.removeAll(snippet);
|
||||
FileUtils::removeFile(getSnippetFile(snippet));
|
||||
}
|
||||
|
||||
QString SnippetMgr::getSnippetFile(const QSharedPointer<Snippet> &p_snippet) const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(getSnippetFolder(), p_snippet->getName() + QStringLiteral(".json"));
|
||||
}
|
||||
|
||||
void SnippetMgr::updateSnippet(const QString &p_name, const QSharedPointer<Snippet> &p_snippet)
|
||||
{
|
||||
auto snippet = find(p_name);
|
||||
Q_ASSERT(snippet);
|
||||
|
||||
// If renamed, remove the old file first.
|
||||
if (p_name != p_snippet->getName()) {
|
||||
FileUtils::removeFile(getSnippetFile(snippet));
|
||||
}
|
||||
|
||||
removeSnippetFromShortcutMap(snippet);
|
||||
|
||||
*snippet = *p_snippet;
|
||||
saveSnippet(snippet);
|
||||
|
||||
addSnippetToShortcutMap(snippet);
|
||||
}
|
||||
|
||||
void SnippetMgr::removeSnippetFromShortcutMap(const QSharedPointer<Snippet> &p_snippet)
|
||||
{
|
||||
if (p_snippet->getShortcut() != Snippet::InvalidShortcut) {
|
||||
auto iter = m_shortcutToSnippet.find(p_snippet->getShortcut());
|
||||
Q_ASSERT(iter != m_shortcutToSnippet.end());
|
||||
if (iter.value() == p_snippet) {
|
||||
// There may exist conflict in shortcut.
|
||||
m_shortcutToSnippet.erase(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnippetMgr::addSnippetToShortcutMap(const QSharedPointer<Snippet> &p_snippet)
|
||||
{
|
||||
if (p_snippet->getShortcut() != Snippet::InvalidShortcut) {
|
||||
m_shortcutToSnippet.insert(p_snippet->getShortcut(), p_snippet);
|
||||
}
|
||||
}
|
||||
|
||||
void SnippetMgr::applySnippet(const QString &p_name,
|
||||
vte::VTextEdit *p_textEdit,
|
||||
const OverrideMap &p_overrides) const
|
||||
{
|
||||
auto snippet = find(p_name);
|
||||
if (!snippet) {
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(snippet->isValid());
|
||||
|
||||
auto cursor = p_textEdit->textCursor();
|
||||
cursor.beginEditBlock();
|
||||
|
||||
// Get selected text.
|
||||
const auto selectedText = p_textEdit->selectedText();
|
||||
p_textEdit->removeSelectedText();
|
||||
|
||||
QString appliedText;
|
||||
int cursorOffset = 0;
|
||||
|
||||
auto it = p_overrides.find(p_name);
|
||||
if (it != p_overrides.end()) {
|
||||
appliedText = it.value();
|
||||
cursorOffset = appliedText.size();
|
||||
} else {
|
||||
// Fetch indentation of first line.
|
||||
QString indentationSpaces;
|
||||
if (snippet->isIndentAsFirstLineEnabled()) {
|
||||
indentationSpaces = vte::TextEditUtils::fetchIndentationSpaces(cursor.block());
|
||||
}
|
||||
|
||||
appliedText = snippet->apply(selectedText, indentationSpaces, cursorOffset);
|
||||
appliedText = applySnippetBySymbol(appliedText, selectedText, cursorOffset, p_overrides);
|
||||
}
|
||||
|
||||
const int beforePos = cursor.position();
|
||||
cursor.insertText(appliedText);
|
||||
cursor.setPosition(beforePos + cursorOffset);
|
||||
|
||||
cursor.endEditBlock();
|
||||
p_textEdit->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
QString SnippetMgr::applySnippetBySymbol(const QString &p_content) const
|
||||
{
|
||||
int offset = 0;
|
||||
return applySnippetBySymbol(p_content, QString(), offset);
|
||||
}
|
||||
|
||||
QString SnippetMgr::applySnippetBySymbol(const QString &p_content,
|
||||
const QString &p_selectedText,
|
||||
int &p_cursorOffset,
|
||||
const OverrideMap &p_overrides) const
|
||||
{
|
||||
QString content(p_content);
|
||||
|
||||
int maxTimes = 100;
|
||||
|
||||
QRegularExpression regExp(c_snippetSymbolRegExp);
|
||||
int pos = 0;
|
||||
while (pos < content.size() && maxTimes-- > 0) {
|
||||
QRegularExpressionMatch match;
|
||||
int idx = content.indexOf(regExp, pos, &match);
|
||||
if (idx == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto snippetName = match.captured(1);
|
||||
auto snippet = find(snippetName);
|
||||
if (!snippet) {
|
||||
// Skip it.
|
||||
pos = idx + match.capturedLength(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
QString afterText;
|
||||
|
||||
auto it = p_overrides.find(snippetName);
|
||||
if (it != p_overrides.end()) {
|
||||
afterText = it.value();
|
||||
} else {
|
||||
const auto indentationSpaces = vte::TextUtils::fetchIndentationSpacesInMultiLines(content, idx);
|
||||
|
||||
// Ignore the cursor mark.
|
||||
int ignoredCursorOffset = 0;
|
||||
afterText = snippet->apply(p_selectedText, indentationSpaces, ignoredCursorOffset);
|
||||
}
|
||||
|
||||
content.replace(idx, match.capturedLength(0), afterText);
|
||||
|
||||
// Maintain the cursor offset.
|
||||
if (p_cursorOffset > idx) {
|
||||
if (p_cursorOffset < idx + match.capturedLength(0)) {
|
||||
p_cursorOffset = idx;
|
||||
} else {
|
||||
p_cursorOffset += (afterText.size() - match.capturedLength(0));
|
||||
}
|
||||
}
|
||||
|
||||
// @afterText may still contains snippet symbol.
|
||||
pos = idx;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
// Used as the function template for some date/time related dynamic snippets.
|
||||
static QString formattedDateTime(const QString &p_format)
|
||||
{
|
||||
return QDateTime::currentDateTime().toString(p_format);
|
||||
}
|
||||
|
||||
QVector<QSharedPointer<Snippet>> SnippetMgr::loadBuiltInSnippets() const
|
||||
{
|
||||
QVector<QSharedPointer<Snippet>> snippets;
|
||||
|
||||
addDynamicSnippet(snippets,
|
||||
"d",
|
||||
tr("the day as number without a leading zero (`1` to `31`)"),
|
||||
std::bind(formattedDateTime, "d"));
|
||||
addDynamicSnippet(snippets,
|
||||
"dd",
|
||||
tr("the day as number with a leading zero (`01` to `31`)"),
|
||||
std::bind(formattedDateTime, "dd"));
|
||||
addDynamicSnippet(snippets,
|
||||
"ddd",
|
||||
tr("the abbreviated localized day name (e.g. `Mon` to `Sun`)"),
|
||||
std::bind(formattedDateTime, "ddd"));
|
||||
addDynamicSnippet(snippets,
|
||||
"dddd",
|
||||
tr("the long localized day name (e.g. `Monday` to `Sunday`)"),
|
||||
std::bind(formattedDateTime, "dddd"));
|
||||
addDynamicSnippet(snippets,
|
||||
"M",
|
||||
tr("the month as number without a leading zero (`1` to `12`)"),
|
||||
std::bind(formattedDateTime, "M"));
|
||||
addDynamicSnippet(snippets,
|
||||
"MM",
|
||||
tr("the month as number with a leading zero (`01` to `12`)"),
|
||||
std::bind(formattedDateTime, "MM"));
|
||||
addDynamicSnippet(snippets,
|
||||
"MMM",
|
||||
tr("the abbreviated localized month name (e.g. `Jan` to `Dec`)"),
|
||||
std::bind(formattedDateTime, "MMM"));
|
||||
addDynamicSnippet(snippets,
|
||||
"MMMM",
|
||||
tr("the long localized month name (e.g. `January` to `December`)"),
|
||||
std::bind(formattedDateTime, "MMMM"));
|
||||
addDynamicSnippet(snippets,
|
||||
"yy",
|
||||
tr("the year as two digit numbers (`00` to `99`)"),
|
||||
std::bind(formattedDateTime, "yy"));
|
||||
addDynamicSnippet(snippets,
|
||||
"yyyy",
|
||||
tr("the year as four digit numbers"),
|
||||
std::bind(formattedDateTime, "yyyy"));
|
||||
addDynamicSnippet(snippets,
|
||||
"w",
|
||||
tr("the week number (`1` to `53`)"),
|
||||
[](const QString &) {
|
||||
return QString::number(QDate::currentDate().weekNumber());
|
||||
});
|
||||
addDynamicSnippet(snippets,
|
||||
"H",
|
||||
tr("the hour without a leading zero (`0` to `23` even with AM/PM display)"),
|
||||
std::bind(formattedDateTime, "H"));
|
||||
addDynamicSnippet(snippets,
|
||||
"HH",
|
||||
tr("the hour with a leading zero (`00` to `23` even with AM/PM display)"),
|
||||
std::bind(formattedDateTime, "HH"));
|
||||
addDynamicSnippet(snippets,
|
||||
"m",
|
||||
tr("the minute without a leading zero (`0` to `59`)"),
|
||||
std::bind(formattedDateTime, "m"));
|
||||
addDynamicSnippet(snippets,
|
||||
"mm",
|
||||
tr("the minute with a leading zero (`00` to `59`)"),
|
||||
std::bind(formattedDateTime, "mm"));
|
||||
addDynamicSnippet(snippets,
|
||||
"s",
|
||||
tr("the second without a leading zero (`0` to `59`)"),
|
||||
std::bind(formattedDateTime, "s"));
|
||||
addDynamicSnippet(snippets,
|
||||
"ss",
|
||||
tr("the second with a leading zero (`00` to `59`)"),
|
||||
std::bind(formattedDateTime, "ss"));
|
||||
addDynamicSnippet(snippets,
|
||||
"date",
|
||||
tr("date (`2021-02-24`)"),
|
||||
std::bind(formattedDateTime, "yyyy-MM-dd"));
|
||||
addDynamicSnippet(snippets,
|
||||
"da",
|
||||
tr("the abbreviated date (`20210224`)"),
|
||||
std::bind(formattedDateTime, "yyyyMMdd"));
|
||||
addDynamicSnippet(snippets,
|
||||
"time",
|
||||
tr("time (`16:51:02`)"),
|
||||
std::bind(formattedDateTime, "hh:mm:ss"));
|
||||
addDynamicSnippet(snippets,
|
||||
"datetime",
|
||||
tr("date and time (`2021-02-24_16:51:02`)"),
|
||||
std::bind(formattedDateTime, "yyyy-MM-dd_hh:mm:ss"));
|
||||
|
||||
// These snippets need override to fill the real value.
|
||||
// Check generateOverrides().
|
||||
addDynamicSnippet(snippets,
|
||||
QStringLiteral("note"),
|
||||
tr("name of current note"),
|
||||
[](const QString &) {
|
||||
return tr("[Value Not Available]");
|
||||
});
|
||||
addDynamicSnippet(snippets,
|
||||
QStringLiteral("no"),
|
||||
tr("complete base name of current note"),
|
||||
[](const QString &) {
|
||||
return tr("[Value Not Available]");
|
||||
});
|
||||
return snippets;
|
||||
}
|
||||
|
||||
void SnippetMgr::addDynamicSnippet(QVector<QSharedPointer<Snippet>> &p_snippets,
|
||||
const QString &p_name,
|
||||
const QString &p_description,
|
||||
const DynamicSnippet::Callback &p_callback)
|
||||
{
|
||||
auto snippet = QSharedPointer<DynamicSnippet>::create(p_name, p_description, p_callback);
|
||||
p_snippets.push_back(snippet);
|
||||
}
|
||||
|
||||
SnippetMgr::OverrideMap SnippetMgr::generateOverrides(const Buffer *p_buffer)
|
||||
{
|
||||
OverrideMap overrides;
|
||||
overrides.insert(QStringLiteral("note"), p_buffer->getName());
|
||||
overrides.insert(QStringLiteral("no"), QFileInfo(p_buffer->getName()).completeBaseName());
|
||||
return overrides;
|
||||
}
|
||||
|
||||
SnippetMgr::OverrideMap SnippetMgr::generateOverrides(const QString &p_fileName)
|
||||
{
|
||||
OverrideMap overrides;
|
||||
overrides.insert(QStringLiteral("note"), p_fileName);
|
||||
overrides.insert(QStringLiteral("no"), QFileInfo(p_fileName).completeBaseName());
|
||||
return overrides;
|
||||
}
|
106
src/snippet/snippetmgr.h
Normal file
106
src/snippet/snippetmgr.h
Normal file
@ -0,0 +1,106 @@
|
||||
#ifndef SNIPPETMGR_H
|
||||
#define SNIPPETMGR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <core/noncopyable.h>
|
||||
|
||||
#include "dynamicsnippet.h"
|
||||
|
||||
namespace vte
|
||||
{
|
||||
class VTextEdit;
|
||||
}
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Buffer;
|
||||
|
||||
class SnippetMgr : public QObject, private Noncopyable
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef QMap<QString, QString> OverrideMap;
|
||||
|
||||
static SnippetMgr &getInst()
|
||||
{
|
||||
static SnippetMgr inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
QString getSnippetFolder() const;
|
||||
|
||||
const QVector<QSharedPointer<Snippet>> &getSnippets() const;
|
||||
|
||||
// @p_exemption: include it even it is occupied by one snippet.
|
||||
QVector<int> getAvailableShortcuts(int p_exemption = Snippet::InvalidShortcut) const;
|
||||
|
||||
QSharedPointer<Snippet> find(const QString &p_name, Qt::CaseSensitivity p_cs = Qt::CaseSensitive) const;
|
||||
|
||||
void addSnippet(const QSharedPointer<Snippet> &p_snippet);
|
||||
|
||||
void removeSnippet(const QString &p_name);
|
||||
|
||||
void updateSnippet(const QString &p_name, const QSharedPointer<Snippet> &p_snippet);
|
||||
|
||||
// Apply snippet @p_name directly in current cursor position.
|
||||
// For snippets in @p_overrides, we just provide simple contents without nested snippets.
|
||||
void applySnippet(const QString &p_name,
|
||||
vte::VTextEdit *p_textEdit,
|
||||
const OverrideMap &p_overrides = OverrideMap()) const;
|
||||
|
||||
// Resolve %snippet_name% as snippet and apply recursively.
|
||||
// Will update @p_cursorOffset if needed.
|
||||
// For snippets in @p_overrides, we just provide simple contents without nested snippets.
|
||||
QString applySnippetBySymbol(const QString &p_content,
|
||||
const QString &p_selectedText,
|
||||
int &p_cursorOffset,
|
||||
const OverrideMap &p_overrides = OverrideMap()) const;
|
||||
|
||||
QString applySnippetBySymbol(const QString &p_content) const;
|
||||
|
||||
// Generate standard overrides for given buffer.
|
||||
static OverrideMap generateOverrides(const Buffer *p_buffer);
|
||||
|
||||
// Generate standard overrides.
|
||||
static OverrideMap generateOverrides(const QString &p_fileName);
|
||||
|
||||
// %name%.
|
||||
// Captured texts:
|
||||
// 1 - The name of the snippet.
|
||||
static const QString c_snippetSymbolRegExp;
|
||||
|
||||
private:
|
||||
SnippetMgr();
|
||||
|
||||
void loadSnippets();
|
||||
|
||||
QSharedPointer<Snippet> loadSnippet(const QString &p_snippetFile) const;
|
||||
|
||||
void saveSnippet(const QSharedPointer<Snippet> &p_snippet);
|
||||
|
||||
QString getSnippetFile(const QSharedPointer<Snippet> &p_snippet) const;
|
||||
|
||||
void addOneSnippet(const QSharedPointer<Snippet> &p_snippet);
|
||||
|
||||
void removeSnippetFromShortcutMap(const QSharedPointer<Snippet> &p_snippet);
|
||||
|
||||
void addSnippetToShortcutMap(const QSharedPointer<Snippet> &p_snippet);
|
||||
|
||||
QVector<QSharedPointer<Snippet>> loadBuiltInSnippets() const;
|
||||
|
||||
static void addDynamicSnippet(QVector<QSharedPointer<Snippet>> &p_snippets,
|
||||
const QString &p_name,
|
||||
const QString &p_description,
|
||||
const DynamicSnippet::Callback &p_callback);
|
||||
|
||||
QVector<QSharedPointer<Snippet>> m_snippets;
|
||||
|
||||
QMap<int, QSharedPointer<Snippet>> m_shortcutToSnippet;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SNIPPETMGR_H
|
@ -49,6 +49,8 @@ include($$PWD/export/export.pri)
|
||||
|
||||
include($$PWD/search/search.pri)
|
||||
|
||||
include($$PWD/snippet/snippet.pri)
|
||||
|
||||
include($$PWD/core/core.pri)
|
||||
|
||||
include($$PWD/widgets/widgets.pri)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QMimeDatabase>
|
||||
#include <QDateTime>
|
||||
#include <QTemporaryFile>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "../core/exception.h"
|
||||
#include "pathutils.h"
|
||||
@ -34,6 +35,11 @@ QString FileUtils::readTextFile(const QString &p_filePath)
|
||||
return text;
|
||||
}
|
||||
|
||||
QJsonObject FileUtils::readJsonFile(const QString &p_filePath)
|
||||
{
|
||||
return QJsonDocument::fromJson(readFile(p_filePath)).object();
|
||||
}
|
||||
|
||||
void FileUtils::writeFile(const QString &p_filePath, const QByteArray &p_data)
|
||||
{
|
||||
QFile file(p_filePath);
|
||||
@ -59,6 +65,11 @@ void FileUtils::writeFile(const QString &p_filePath, const QString &p_text)
|
||||
file.close();
|
||||
}
|
||||
|
||||
void FileUtils::writeFile(const QString &p_filePath, const QJsonObject &p_jobj)
|
||||
{
|
||||
writeFile(p_filePath, QJsonDocument(p_jobj).toJson());
|
||||
}
|
||||
|
||||
void FileUtils::renameFile(const QString &p_path, const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(PathUtils::isLegalFileName(p_name));
|
||||
@ -194,7 +205,7 @@ QString FileUtils::renameIfExistsCaseInsensitive(const QString &p_path)
|
||||
|
||||
void FileUtils::removeFile(const QString &p_filePath)
|
||||
{
|
||||
Q_ASSERT(QFileInfo(p_filePath).isFile());
|
||||
Q_ASSERT(!QFileInfo::exists(p_filePath) || QFileInfo(p_filePath).isFile());
|
||||
QFile file(p_filePath);
|
||||
if (!file.remove()) {
|
||||
Exception::throwOne(Exception::Type::FailToRemoveFile,
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QJsonObject>
|
||||
|
||||
class QTemporaryFile;
|
||||
|
||||
@ -19,10 +20,14 @@ namespace vnotex
|
||||
|
||||
static QString readTextFile(const QString &p_filePath);
|
||||
|
||||
static QJsonObject readJsonFile(const QString &p_filePath);
|
||||
|
||||
static void writeFile(const QString &p_filePath, const QByteArray &p_data);
|
||||
|
||||
static void writeFile(const QString &p_filePath, const QString &p_text);
|
||||
|
||||
static void writeFile(const QString &p_filePath, const QJsonObject &p_jobj);
|
||||
|
||||
// Rename file or dir.
|
||||
static void renameFile(const QString &p_path, const QString &p_name);
|
||||
|
||||
|
@ -117,3 +117,12 @@ QString Utils::boolToString(bool p_val)
|
||||
{
|
||||
return p_val ? QStringLiteral("true") : QStringLiteral("false");
|
||||
}
|
||||
|
||||
QString Utils::intToString(int p_val, int p_width)
|
||||
{
|
||||
auto str = QString::number(p_val);
|
||||
if (str.size() < p_width) {
|
||||
str.prepend(QString(p_width - str.size(), QLatin1Char('0')));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
@ -50,6 +50,8 @@ namespace vnotex
|
||||
static bool fuzzyEqual(qreal p_a, qreal p_b);
|
||||
|
||||
static QString boolToString(bool p_val);
|
||||
|
||||
static QString intToString(int p_val, int p_width = 0);
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -30,7 +30,6 @@ void FolderPropertiesDialog::setupUI()
|
||||
setCentralWidget(m_infoWidget);
|
||||
|
||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
||||
|
||||
setWindowTitle(m_node->getName() + QStringLiteral(" ") + tr("Properties"));
|
||||
}
|
||||
@ -38,11 +37,9 @@ void FolderPropertiesDialog::setupUI()
|
||||
void FolderPropertiesDialog::setupNodeInfoWidget(QWidget *p_parent)
|
||||
{
|
||||
m_infoWidget = new NodeInfoWidget(m_node, p_parent);
|
||||
connect(m_infoWidget, &NodeInfoWidget::inputEdited,
|
||||
this, &FolderPropertiesDialog::validateInputs);
|
||||
}
|
||||
|
||||
void FolderPropertiesDialog::validateInputs()
|
||||
bool FolderPropertiesDialog::validateInputs()
|
||||
{
|
||||
bool valid = true;
|
||||
QString msg;
|
||||
@ -50,7 +47,7 @@ void FolderPropertiesDialog::validateInputs()
|
||||
valid = valid && validateNameInput(msg);
|
||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||
: ScrollDialog::InformationLevel::Error);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool FolderPropertiesDialog::validateNameInput(QString &p_msg)
|
||||
@ -74,7 +71,7 @@ bool FolderPropertiesDialog::validateNameInput(QString &p_msg)
|
||||
|
||||
void FolderPropertiesDialog::acceptedButtonClicked()
|
||||
{
|
||||
if (saveFolderProperties()) {
|
||||
if (validateInputs() && saveFolderProperties()) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,6 @@ namespace vnotex
|
||||
protected:
|
||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void validateInputs();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
@ -29,6 +26,8 @@ namespace vnotex
|
||||
|
||||
bool saveFolderProperties();
|
||||
|
||||
bool validateInputs();
|
||||
|
||||
NodeInfoWidget *m_infoWidget = nullptr;
|
||||
|
||||
Node *m_node = nullptr;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "notebookmgr.h"
|
||||
#include "../messageboxhelper.h"
|
||||
#include <utils/iconutils.h>
|
||||
#include <utils/utils.h>
|
||||
#include <utils/widgetutils.h>
|
||||
#include "../widgetsfactory.h"
|
||||
#include "exception.h"
|
||||
@ -212,12 +213,38 @@ void ManageNotebooksDialog::setChangesUnsaved(bool p_unsaved)
|
||||
setButtonEnabled(QDialogButtonBox::Reset, m_changesUnsaved);
|
||||
}
|
||||
|
||||
bool ManageNotebooksDialog::validateInputs()
|
||||
{
|
||||
bool valid = true;
|
||||
QString msg;
|
||||
|
||||
valid = valid && validateNameInput(msg);
|
||||
|
||||
setInformationText(msg, valid ? Dialog::InformationLevel::Info
|
||||
: Dialog::InformationLevel::Error);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool ManageNotebooksDialog::validateNameInput(QString &p_msg)
|
||||
{
|
||||
if (m_notebookInfoWidget->getName().isEmpty()) {
|
||||
Utils::appendMsg(p_msg, tr("Please specify a name for the notebook."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ManageNotebooksDialog::saveChangesToNotebook()
|
||||
{
|
||||
if (!m_changesUnsaved || !m_notebook) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!validateInputs()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_notebook->updateName(m_notebookInfoWidget->getName());
|
||||
m_notebook->updateDescription(m_notebookInfoWidget->getDescription());
|
||||
return true;
|
||||
|
@ -51,6 +51,10 @@ namespace vnotex
|
||||
|
||||
bool checkUnsavedChanges();
|
||||
|
||||
bool validateInputs();
|
||||
|
||||
bool validateNameInput(QString &p_msg);
|
||||
|
||||
QListWidget *m_notebookList = nullptr;
|
||||
|
||||
NotebookInfoWidget *m_notebookInfoWidget = nullptr;
|
||||
|
@ -26,7 +26,6 @@ void NewFolderDialog::setupUI(const Node *p_node)
|
||||
setCentralWidget(m_infoWidget);
|
||||
|
||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
||||
|
||||
setWindowTitle(tr("New Folder"));
|
||||
}
|
||||
@ -34,11 +33,9 @@ void NewFolderDialog::setupUI(const Node *p_node)
|
||||
void NewFolderDialog::setupNodeInfoWidget(const Node *p_node, QWidget *p_parent)
|
||||
{
|
||||
m_infoWidget = new NodeInfoWidget(p_node, Node::Flag::Container, p_parent);
|
||||
connect(m_infoWidget, &NodeInfoWidget::inputEdited,
|
||||
this, &NewFolderDialog::validateInputs);
|
||||
}
|
||||
|
||||
void NewFolderDialog::validateInputs()
|
||||
bool NewFolderDialog::validateInputs()
|
||||
{
|
||||
bool valid = true;
|
||||
QString msg;
|
||||
@ -46,7 +43,7 @@ void NewFolderDialog::validateInputs()
|
||||
valid = valid && validateNameInput(msg);
|
||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||
: ScrollDialog::InformationLevel::Error);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool NewFolderDialog::validateNameInput(QString &p_msg)
|
||||
@ -69,7 +66,7 @@ bool NewFolderDialog::validateNameInput(QString &p_msg)
|
||||
|
||||
void NewFolderDialog::acceptedButtonClicked()
|
||||
{
|
||||
if (newFolder()) {
|
||||
if (validateInputs() && newFolder()) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,6 @@ namespace vnotex
|
||||
protected:
|
||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void validateInputs();
|
||||
|
||||
private:
|
||||
void setupUI(const Node *p_node);
|
||||
|
||||
@ -32,6 +29,8 @@ namespace vnotex
|
||||
|
||||
bool newFolder();
|
||||
|
||||
bool validateInputs();
|
||||
|
||||
NodeInfoWidget *m_infoWidget = nullptr;
|
||||
|
||||
QSharedPointer<Node> m_newNode;
|
||||
|
@ -29,7 +29,6 @@ void NewNotebookDialog::setupUI()
|
||||
setCentralWidget(m_infoWidget);
|
||||
|
||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
||||
|
||||
setWindowTitle(tr("New Notebook"));
|
||||
}
|
||||
@ -39,8 +38,6 @@ void NewNotebookDialog::setupNotebookInfoWidget(QWidget *p_parent)
|
||||
m_infoWidget = new NotebookInfoWidget(NotebookInfoWidget::Create, p_parent);
|
||||
connect(m_infoWidget, &NotebookInfoWidget::rootFolderEdited,
|
||||
this, &NewNotebookDialog::handleRootFolderPathChanged);
|
||||
connect(m_infoWidget, &NotebookInfoWidget::basicInfoEdited,
|
||||
this, &NewNotebookDialog::validateInputs);
|
||||
|
||||
{
|
||||
auto whatsThis = tr("<br/>Both absolute and relative paths are supported. ~ and environment variable are not supported now.");
|
||||
@ -49,7 +46,7 @@ void NewNotebookDialog::setupNotebookInfoWidget(QWidget *p_parent)
|
||||
}
|
||||
}
|
||||
|
||||
void NewNotebookDialog::validateInputs()
|
||||
bool NewNotebookDialog::validateInputs()
|
||||
{
|
||||
bool valid = true;
|
||||
QString msg;
|
||||
@ -59,7 +56,7 @@ void NewNotebookDialog::validateInputs()
|
||||
|
||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||
: ScrollDialog::InformationLevel::Error);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool NewNotebookDialog::validateNameInput(QString &p_msg)
|
||||
@ -113,7 +110,7 @@ bool NewNotebookDialog::validateRootFolderInput(QString &p_msg)
|
||||
|
||||
void NewNotebookDialog::acceptedButtonClicked()
|
||||
{
|
||||
if (newNotebook()) {
|
||||
if (validateInputs() && newNotebook()) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +22,13 @@ namespace vnotex
|
||||
|
||||
NotebookInfoWidget *m_infoWidget = nullptr;
|
||||
|
||||
private slots:
|
||||
void validateInputs();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void setupNotebookInfoWidget(QWidget *p_parent = nullptr);
|
||||
|
||||
bool validateInputs();
|
||||
|
||||
bool validateNameInput(QString &p_msg);
|
||||
|
||||
// Create a new notebook.
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nodeinfowidget.h"
|
||||
#include <utils/widgetutils.h>
|
||||
#include <core/templatemgr.h>
|
||||
#include <snippet/snippetmgr.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -62,7 +63,6 @@ void NewNoteDialog::setupUI(const Node *p_node)
|
||||
}
|
||||
|
||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
||||
|
||||
setWindowTitle(tr("New Note"));
|
||||
}
|
||||
@ -70,11 +70,9 @@ void NewNoteDialog::setupUI(const Node *p_node)
|
||||
void NewNoteDialog::setupNodeInfoWidget(const Node *p_node, QWidget *p_parent)
|
||||
{
|
||||
m_infoWidget = new NodeInfoWidget(p_node, Node::Flag::Content, p_parent);
|
||||
connect(m_infoWidget, &NodeInfoWidget::inputEdited,
|
||||
this, &NewNoteDialog::validateInputs);
|
||||
}
|
||||
|
||||
void NewNoteDialog::validateInputs()
|
||||
bool NewNoteDialog::validateInputs()
|
||||
{
|
||||
bool valid = true;
|
||||
QString msg;
|
||||
@ -82,7 +80,7 @@ void NewNoteDialog::validateInputs()
|
||||
valid = valid && validateNameInput(msg);
|
||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||
: ScrollDialog::InformationLevel::Error);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool NewNoteDialog::validateNameInput(QString &p_msg)
|
||||
@ -90,8 +88,8 @@ bool NewNoteDialog::validateNameInput(QString &p_msg)
|
||||
p_msg.clear();
|
||||
|
||||
auto name = m_infoWidget->getName();
|
||||
if (name.isEmpty()) {
|
||||
p_msg = tr("Please specify a name for the note.");
|
||||
if (name.isEmpty() || !PathUtils::isLegalFileName(name)) {
|
||||
p_msg = tr("Please specify a valid name for the note.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -107,7 +105,7 @@ void NewNoteDialog::acceptedButtonClicked()
|
||||
{
|
||||
s_lastTemplate = m_templateComboBox->currentData().toString();
|
||||
|
||||
if (newNote()) {
|
||||
if (validateInputs() && newNote()) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
@ -151,8 +149,6 @@ void NewNoteDialog::initDefaultValues(const Node *p_node)
|
||||
QStringLiteral("md"));
|
||||
lineEdit->setText(defaultName);
|
||||
WidgetUtils::selectBaseName(lineEdit);
|
||||
|
||||
validateInputs();
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,6 +205,9 @@ void NewNoteDialog::setupTemplateComboBox(QWidget *p_parent)
|
||||
|
||||
QString NewNoteDialog::getTemplateContent() const
|
||||
{
|
||||
// TODO: parse snippets of the template.
|
||||
return m_templateContent;
|
||||
int cursorOffset = 0;
|
||||
return SnippetMgr::getInst().applySnippetBySymbol(m_templateContent,
|
||||
QString(),
|
||||
cursorOffset,
|
||||
SnippetMgr::generateOverrides(m_infoWidget->getName()));
|
||||
}
|
||||
|
@ -24,9 +24,6 @@ namespace vnotex
|
||||
protected:
|
||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void validateInputs();
|
||||
|
||||
private:
|
||||
void setupUI(const Node *p_node);
|
||||
|
||||
@ -34,6 +31,8 @@ namespace vnotex
|
||||
|
||||
void setupTemplateComboBox(QWidget *p_parent);
|
||||
|
||||
bool validateInputs();
|
||||
|
||||
bool validateNameInput(QString &p_msg);
|
||||
|
||||
bool newNote();
|
||||
|
88
src/widgets/dialogs/newsnippetdialog.cpp
Normal file
88
src/widgets/dialogs/newsnippetdialog.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include "newsnippetdialog.h"
|
||||
|
||||
#include "snippetinfowidget.h"
|
||||
|
||||
#include <snippet/snippetmgr.h>
|
||||
#include <core/exception.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
NewSnippetDialog::NewSnippetDialog(QWidget *p_parent)
|
||||
: ScrollDialog(p_parent)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
m_infoWidget->setFocus();
|
||||
}
|
||||
|
||||
void NewSnippetDialog::setupUI()
|
||||
{
|
||||
setupSnippetInfoWidget(this);
|
||||
setCentralWidget(m_infoWidget);
|
||||
|
||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
|
||||
setWindowTitle(tr("New Snippet"));
|
||||
}
|
||||
|
||||
void NewSnippetDialog::setupSnippetInfoWidget(QWidget *p_parent)
|
||||
{
|
||||
m_infoWidget = new SnippetInfoWidget(p_parent);
|
||||
}
|
||||
|
||||
bool NewSnippetDialog::validateInputs()
|
||||
{
|
||||
bool valid = true;
|
||||
QString msg;
|
||||
|
||||
valid = valid && validateNameInput(msg);
|
||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||
: ScrollDialog::InformationLevel::Error);
|
||||
return valid;
|
||||
}
|
||||
|
||||
void NewSnippetDialog::acceptedButtonClicked()
|
||||
{
|
||||
if (validateInputs() && newSnippet()) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
bool NewSnippetDialog::newSnippet()
|
||||
{
|
||||
auto snip = QSharedPointer<Snippet>::create(m_infoWidget->getName(),
|
||||
m_infoWidget->getContent(),
|
||||
m_infoWidget->getShortcut(),
|
||||
m_infoWidget->shouldIndentAsFirstLine(),
|
||||
m_infoWidget->getCursorMark(),
|
||||
m_infoWidget->getSelectionMark());
|
||||
Q_ASSERT(snip->isValid());
|
||||
try {
|
||||
SnippetMgr::getInst().addSnippet(snip);
|
||||
} catch (Exception &p_e) {
|
||||
QString msg = tr("Failed to add snippet (%1) (%2).")
|
||||
.arg(snip->getName(), p_e.what());
|
||||
qWarning() << msg;
|
||||
setInformationText(msg, ScrollDialog::InformationLevel::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NewSnippetDialog::validateNameInput(QString &p_msg)
|
||||
{
|
||||
p_msg.clear();
|
||||
|
||||
const auto name = m_infoWidget->getName();
|
||||
if (name.isEmpty()) {
|
||||
p_msg = tr("Please specify a name for the snippet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SnippetMgr::getInst().find(name, Qt::CaseInsensitive)) {
|
||||
p_msg = tr("Name conflicts with existing snippet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
34
src/widgets/dialogs/newsnippetdialog.h
Normal file
34
src/widgets/dialogs/newsnippetdialog.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef NEWSNIPPETDIALOG_H
|
||||
#define NEWSNIPPETDIALOG_H
|
||||
|
||||
#include "scrolldialog.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class SnippetInfoWidget;
|
||||
|
||||
class NewSnippetDialog : public ScrollDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NewSnippetDialog(QWidget *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void setupSnippetInfoWidget(QWidget *p_parent);
|
||||
|
||||
bool newSnippet();
|
||||
|
||||
bool validateNameInput(QString &p_msg);
|
||||
|
||||
bool validateInputs();
|
||||
|
||||
SnippetInfoWidget *m_infoWidget = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NEWSNIPPETDIALOG_H
|
@ -13,6 +13,7 @@
|
||||
#include "nodelabelwithupbutton.h"
|
||||
#include <utils/widgetutils.h>
|
||||
#include <buffer/filetypehelper.h>
|
||||
#include "../lineeditwithsnippet.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -69,7 +70,7 @@ void NodeInfoWidget::setupUI(const Node *p_parentNode, Node::Flags p_newNodeFlag
|
||||
|
||||
void NodeInfoWidget::setupNameLineEdit(QWidget *p_parent)
|
||||
{
|
||||
m_nameLineEdit = WidgetsFactory::createLineEdit(p_parent);
|
||||
m_nameLineEdit = WidgetsFactory::createLineEditWithSnippet(p_parent);
|
||||
auto validator = new QRegularExpressionValidator(QRegularExpression(PathUtils::c_fileNameRegularExpression),
|
||||
m_nameLineEdit);
|
||||
m_nameLineEdit->setValidator(validator);
|
||||
@ -107,7 +108,7 @@ QLineEdit *NodeInfoWidget::getNameLineEdit() const
|
||||
|
||||
QString NodeInfoWidget::getName() const
|
||||
{
|
||||
return getNameLineEdit()->text().trimmed();
|
||||
return m_nameLineEdit->evaluatedText().trimmed();
|
||||
}
|
||||
|
||||
const Notebook *NodeInfoWidget::getNotebook() const
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "notebook/node.h"
|
||||
#include <notebook/node.h>
|
||||
|
||||
class QLineEdit;
|
||||
class QLabel;
|
||||
@ -14,6 +14,7 @@ namespace vnotex
|
||||
{
|
||||
class Notebook;
|
||||
class NodeLabelWithUpButton;
|
||||
class LineEditWithSnippet;
|
||||
|
||||
class NodeInfoWidget : public QWidget
|
||||
{
|
||||
@ -50,13 +51,13 @@ namespace vnotex
|
||||
|
||||
void setNode(const Node *p_node);
|
||||
|
||||
Mode m_mode;
|
||||
Mode m_mode = Mode::Create;
|
||||
|
||||
QFormLayout *m_mainLayout = nullptr;
|
||||
|
||||
QComboBox *m_fileTypeComboBox = nullptr;
|
||||
|
||||
QLineEdit *m_nameLineEdit = nullptr;
|
||||
LineEditWithSnippet *m_nameLineEdit = nullptr;
|
||||
|
||||
NodeLabelWithUpButton *m_parentNodeLabel = nullptr;
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <utils/pathutils.h>
|
||||
#include "exception.h"
|
||||
#include <utils/widgetutils.h>
|
||||
#include "../lineeditwithsnippet.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -52,7 +53,7 @@ QGroupBox *NotebookInfoWidget::setupBasicInfoGroupBox(QWidget *p_parent)
|
||||
}
|
||||
|
||||
{
|
||||
m_nameLineEdit = WidgetsFactory::createLineEdit(box);
|
||||
m_nameLineEdit = WidgetsFactory::createLineEditWithSnippet(box);
|
||||
m_nameLineEdit->setPlaceholderText(tr("Name of notebook"));
|
||||
connect(m_nameLineEdit, &QLineEdit::textEdited,
|
||||
this, &NotebookInfoWidget::basicInfoEdited);
|
||||
@ -312,7 +313,7 @@ QComboBox *NotebookInfoWidget::getBackendComboBox() const
|
||||
|
||||
QString NotebookInfoWidget::getName() const
|
||||
{
|
||||
return getNameLineEdit()->text().trimmed();
|
||||
return m_nameLineEdit->evaluatedText().trimmed();
|
||||
}
|
||||
|
||||
QString NotebookInfoWidget::getDescription() const
|
||||
|
@ -11,6 +11,7 @@ class QGroupBox;
|
||||
namespace vnotex
|
||||
{
|
||||
class Notebook;
|
||||
class LineEditWithSnippet;
|
||||
|
||||
class NotebookInfoWidget : public QWidget
|
||||
{
|
||||
@ -92,7 +93,7 @@ namespace vnotex
|
||||
|
||||
const Notebook *m_notebook = nullptr;
|
||||
|
||||
QLineEdit *m_nameLineEdit = nullptr;
|
||||
LineEditWithSnippet *m_nameLineEdit = nullptr;
|
||||
|
||||
QLineEdit *m_descriptionLineEdit = nullptr;
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "notepropertiesdialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "notebook/notebook.h"
|
||||
#include "notebook/node.h"
|
||||
#include "../widgetsfactory.h"
|
||||
@ -32,19 +30,16 @@ void NotePropertiesDialog::setupUI()
|
||||
setCentralWidget(m_infoWidget);
|
||||
|
||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
||||
|
||||
setWindowTitle(m_node->getName() + QStringLiteral(" ") + tr("Properties"));
|
||||
setWindowTitle(tr("%1 Properties").arg(m_node->getName()));
|
||||
}
|
||||
|
||||
void NotePropertiesDialog::setupNodeInfoWidget(QWidget *p_parent)
|
||||
{
|
||||
m_infoWidget = new NodeInfoWidget(m_node, p_parent);
|
||||
connect(m_infoWidget, &NodeInfoWidget::inputEdited,
|
||||
this, &NotePropertiesDialog::validateInputs);
|
||||
}
|
||||
|
||||
void NotePropertiesDialog::validateInputs()
|
||||
bool NotePropertiesDialog::validateInputs()
|
||||
{
|
||||
bool valid = true;
|
||||
QString msg;
|
||||
@ -52,7 +47,7 @@ void NotePropertiesDialog::validateInputs()
|
||||
valid = valid && validateNameInput(msg);
|
||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||
: ScrollDialog::InformationLevel::Error);
|
||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool NotePropertiesDialog::validateNameInput(QString &p_msg)
|
||||
@ -60,8 +55,8 @@ bool NotePropertiesDialog::validateNameInput(QString &p_msg)
|
||||
p_msg.clear();
|
||||
|
||||
auto name = m_infoWidget->getName();
|
||||
if (name.isEmpty()) {
|
||||
p_msg = tr("Please specify a name for the note.");
|
||||
if (name.isEmpty() || !PathUtils::isLegalFileName(name)) {
|
||||
p_msg = tr("Please specify a valid name for the note.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -76,7 +71,7 @@ bool NotePropertiesDialog::validateNameInput(QString &p_msg)
|
||||
|
||||
void NotePropertiesDialog::acceptedButtonClicked()
|
||||
{
|
||||
if (saveNoteProperties()) {
|
||||
if (validateInputs() && saveNoteProperties()) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,6 @@ namespace vnotex
|
||||
protected:
|
||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void validateInputs();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
@ -29,6 +26,8 @@ namespace vnotex
|
||||
|
||||
bool saveNoteProperties();
|
||||
|
||||
bool validateInputs();
|
||||
|
||||
NodeInfoWidget *m_infoWidget = nullptr;
|
||||
|
||||
Node *m_node = nullptr;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QGroupBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QDebug>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <widgets/widgetsfactory.h>
|
||||
#include <core/sessionconfig.h>
|
||||
@ -79,6 +80,15 @@ QGroupBox *QuickAccessPage::setupFlashPageGroup()
|
||||
addSearchItem(label, m_flashPageInput->toolTip(), m_flashPageInput);
|
||||
connect(m_flashPageInput, &LocationInputWithBrowseButton::textChanged,
|
||||
this, &QuickAccessPage::pageIsChanged);
|
||||
connect(m_flashPageInput, &LocationInputWithBrowseButton::clicked,
|
||||
this, [this]() {
|
||||
auto filePath = QFileDialog::getOpenFileName(this,
|
||||
tr("Select Flash Page File"),
|
||||
QDir::homePath());
|
||||
if (!filePath.isEmpty()) {
|
||||
m_flashPageInput->setText(filePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return box;
|
||||
|
173
src/widgets/dialogs/snippetinfowidget.cpp
Normal file
173
src/widgets/dialogs/snippetinfowidget.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
#include "snippetinfowidget.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QComboBox>
|
||||
#include <QFormLayout>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include <widgets/widgetsfactory.h>
|
||||
#include <snippet/snippet.h>
|
||||
#include <snippet/snippetmgr.h>
|
||||
#include <utils/utils.h>
|
||||
#include <utils/pathutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
SnippetInfoWidget::SnippetInfoWidget(QWidget *p_parent)
|
||||
: QWidget(p_parent),
|
||||
m_mode(Mode::Create)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
SnippetInfoWidget::SnippetInfoWidget(const Snippet *p_snippet, QWidget *p_parent)
|
||||
: QWidget(p_parent),
|
||||
m_mode(Mode::Edit)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
setSnippet(p_snippet);
|
||||
}
|
||||
|
||||
void SnippetInfoWidget::setupUI()
|
||||
{
|
||||
auto mainLayout = new QFormLayout(this);
|
||||
|
||||
m_nameLineEdit = WidgetsFactory::createLineEdit(this);
|
||||
auto validator = new QRegularExpressionValidator(QRegularExpression(PathUtils::c_fileNameRegularExpression),
|
||||
m_nameLineEdit);
|
||||
m_nameLineEdit->setValidator(validator);
|
||||
connect(m_nameLineEdit, &QLineEdit::textEdited,
|
||||
this, &SnippetInfoWidget::inputEdited);
|
||||
mainLayout->addRow(tr("Name:"), m_nameLineEdit);
|
||||
|
||||
setFocusProxy(m_nameLineEdit);
|
||||
|
||||
setupTypeComboBox(this);
|
||||
mainLayout->addRow(tr("Type:"), m_typeComboBox);
|
||||
|
||||
setupShortcutComboBox(this);
|
||||
mainLayout->addRow(tr("Shortcut:"), m_shortcutComboBox);
|
||||
|
||||
m_cursorMarkLineEdit = WidgetsFactory::createLineEdit(this);
|
||||
m_cursorMarkLineEdit->setText(Snippet::c_defaultCursorMark);
|
||||
m_cursorMarkLineEdit->setToolTip(tr("A mark in the snippet content indicating the cursor position after the application"));
|
||||
connect(m_cursorMarkLineEdit, &QLineEdit::textEdited,
|
||||
this, &SnippetInfoWidget::inputEdited);
|
||||
mainLayout->addRow(tr("Cursor mark:"), m_cursorMarkLineEdit);
|
||||
|
||||
m_selectionMarkLineEdit = WidgetsFactory::createLineEdit(this);
|
||||
m_selectionMarkLineEdit->setText(Snippet::c_defaultSelectionMark);
|
||||
m_selectionMarkLineEdit->setToolTip(tr("A mark in the snippet content that will be replaced with the selected text before the application"));
|
||||
connect(m_selectionMarkLineEdit, &QLineEdit::textEdited,
|
||||
this, &SnippetInfoWidget::inputEdited);
|
||||
mainLayout->addRow(tr("Selection mark:"), m_selectionMarkLineEdit);
|
||||
|
||||
m_indentAsFirstLineCheckBox = WidgetsFactory::createCheckBox(tr("Indent as first line"), this);
|
||||
m_indentAsFirstLineCheckBox->setChecked(true);
|
||||
connect(m_indentAsFirstLineCheckBox, &QCheckBox::stateChanged,
|
||||
this, &SnippetInfoWidget::inputEdited);
|
||||
mainLayout->addRow(m_indentAsFirstLineCheckBox);
|
||||
|
||||
m_contentTextEdit = WidgetsFactory::createPlainTextEdit(this);
|
||||
connect(m_contentTextEdit, &QPlainTextEdit::textChanged,
|
||||
this, &SnippetInfoWidget::inputEdited);
|
||||
mainLayout->addRow(tr("Content:"), m_contentTextEdit);
|
||||
}
|
||||
|
||||
void SnippetInfoWidget::setupTypeComboBox(QWidget *p_parent)
|
||||
{
|
||||
m_typeComboBox = WidgetsFactory::createComboBox(p_parent);
|
||||
m_typeComboBox->addItem(tr("Text"), static_cast<int>(Snippet::Type::Text));
|
||||
if (m_mode == Mode::Edit) {
|
||||
m_typeComboBox->addItem(tr("Dynamic"), static_cast<int>(Snippet::Type::Dynamic));
|
||||
}
|
||||
connect(m_typeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, [this]() {
|
||||
emit inputEdited();
|
||||
});
|
||||
}
|
||||
|
||||
void SnippetInfoWidget::setupShortcutComboBox(QWidget *p_parent)
|
||||
{
|
||||
m_shortcutComboBox = WidgetsFactory::createComboBox(p_parent);
|
||||
if (m_mode == Mode::Create) {
|
||||
initShortcutComboBox();
|
||||
}
|
||||
connect(m_shortcutComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &SnippetInfoWidget::inputEdited);
|
||||
}
|
||||
|
||||
QString SnippetInfoWidget::getName() const
|
||||
{
|
||||
return m_nameLineEdit->text();
|
||||
}
|
||||
|
||||
Snippet::Type SnippetInfoWidget::getType() const
|
||||
{
|
||||
return static_cast<Snippet::Type>(m_typeComboBox->currentData().toInt());
|
||||
}
|
||||
|
||||
int SnippetInfoWidget::getShortcut() const
|
||||
{
|
||||
return m_shortcutComboBox->currentData().toInt();
|
||||
}
|
||||
|
||||
QString SnippetInfoWidget::getCursorMark() const
|
||||
{
|
||||
return m_cursorMarkLineEdit->text();
|
||||
}
|
||||
|
||||
QString SnippetInfoWidget::getSelectionMark() const
|
||||
{
|
||||
return m_selectionMarkLineEdit->text();
|
||||
}
|
||||
|
||||
bool SnippetInfoWidget::shouldIndentAsFirstLine() const
|
||||
{
|
||||
return m_indentAsFirstLineCheckBox->isChecked();
|
||||
}
|
||||
|
||||
QString SnippetInfoWidget::getContent() const
|
||||
{
|
||||
return m_contentTextEdit->toPlainText();
|
||||
}
|
||||
|
||||
void SnippetInfoWidget::setSnippet(const Snippet *p_snippet)
|
||||
{
|
||||
if (m_snippet == p_snippet) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_mode == Mode::Edit);
|
||||
m_snippet = p_snippet;
|
||||
initShortcutComboBox();
|
||||
if (m_snippet) {
|
||||
m_nameLineEdit->setText(m_snippet->getName());
|
||||
m_typeComboBox->setCurrentIndex(m_typeComboBox->findData(static_cast<int>(m_snippet->getType())));
|
||||
m_shortcutComboBox->setCurrentIndex(m_shortcutComboBox->findData(m_snippet->getShortcut()));
|
||||
m_cursorMarkLineEdit->setText(m_snippet->getCursorMark());
|
||||
m_selectionMarkLineEdit->setText(m_snippet->getSelectionMark());
|
||||
m_indentAsFirstLineCheckBox->setChecked(m_snippet->isIndentAsFirstLineEnabled());
|
||||
m_contentTextEdit->setPlainText(m_snippet->getContent());
|
||||
} else {
|
||||
m_nameLineEdit->clear();
|
||||
m_typeComboBox->setCurrentIndex(m_typeComboBox->findData(static_cast<int>(Snippet::Type::Text)));
|
||||
m_shortcutComboBox->setCurrentIndex(m_shortcutComboBox->findData(Snippet::InvalidShortcut));
|
||||
m_cursorMarkLineEdit->setText(Snippet::c_defaultCursorMark);
|
||||
m_selectionMarkLineEdit->setText(Snippet::c_defaultSelectionMark);
|
||||
m_indentAsFirstLineCheckBox->setChecked(true);
|
||||
m_contentTextEdit->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void SnippetInfoWidget::initShortcutComboBox()
|
||||
{
|
||||
m_shortcutComboBox->clear();
|
||||
m_shortcutComboBox->addItem(tr("None"), Snippet::InvalidShortcut);
|
||||
const auto shortcuts = SnippetMgr::getInst().getAvailableShortcuts(m_snippet ? m_snippet->getShortcut() : Snippet::InvalidShortcut);
|
||||
for (auto sh : shortcuts) {
|
||||
m_shortcutComboBox->addItem(Utils::intToString(sh, 2), sh);
|
||||
}
|
||||
}
|
73
src/widgets/dialogs/snippetinfowidget.h
Normal file
73
src/widgets/dialogs/snippetinfowidget.h
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef SNIPPETINFOWIDGET_H
|
||||
#define SNIPPETINFOWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <snippet/snippet.h>
|
||||
|
||||
class QLineEdit;
|
||||
class QComboBox;
|
||||
class QCheckBox;
|
||||
class QPlainTextEdit;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class SnippetInfoWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Mode { Create, Edit };
|
||||
|
||||
explicit SnippetInfoWidget(QWidget *p_parent = nullptr);
|
||||
|
||||
SnippetInfoWidget(const Snippet *p_snippet, QWidget *p_parent = nullptr);
|
||||
|
||||
QString getName() const;
|
||||
|
||||
Snippet::Type getType() const;
|
||||
|
||||
int getShortcut() const;
|
||||
|
||||
QString getCursorMark() const;
|
||||
|
||||
QString getSelectionMark() const;
|
||||
|
||||
bool shouldIndentAsFirstLine() const;
|
||||
|
||||
QString getContent() const;
|
||||
|
||||
signals:
|
||||
void inputEdited();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void setupTypeComboBox(QWidget *p_parent);
|
||||
|
||||
void setupShortcutComboBox(QWidget *p_parent);
|
||||
|
||||
void setSnippet(const Snippet *p_snippet);
|
||||
|
||||
void initShortcutComboBox();
|
||||
|
||||
Mode m_mode = Mode::Create;
|
||||
|
||||
const Snippet *m_snippet = nullptr;
|
||||
|
||||
QLineEdit *m_nameLineEdit = nullptr;
|
||||
|
||||
QComboBox *m_typeComboBox = nullptr;
|
||||
|
||||
QComboBox *m_shortcutComboBox = nullptr;
|
||||
|
||||
QLineEdit *m_cursorMarkLineEdit = nullptr;
|
||||
|
||||
QLineEdit *m_selectionMarkLineEdit = nullptr;
|
||||
|
||||
QCheckBox *m_indentAsFirstLineCheckBox = nullptr;
|
||||
|
||||
QPlainTextEdit *m_contentTextEdit = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SNIPPETINFOWIDGET_H
|
98
src/widgets/dialogs/snippetpropertiesdialog.cpp
Normal file
98
src/widgets/dialogs/snippetpropertiesdialog.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "snippetpropertiesdialog.h"
|
||||
|
||||
#include <snippet/snippet.h>
|
||||
#include <snippet/snippetmgr.h>
|
||||
#include <core/exception.h>
|
||||
|
||||
#include "snippetinfowidget.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
SnippetPropertiesDialog::SnippetPropertiesDialog(Snippet *p_snippet, QWidget *p_parent)
|
||||
: ScrollDialog(p_parent),
|
||||
m_snippet(p_snippet)
|
||||
{
|
||||
Q_ASSERT(m_snippet);
|
||||
setupUI();
|
||||
|
||||
m_infoWidget->setFocus();
|
||||
}
|
||||
|
||||
void SnippetPropertiesDialog::setupUI()
|
||||
{
|
||||
setupSnippetInfoWidget(this);
|
||||
setCentralWidget(m_infoWidget);
|
||||
|
||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
if (m_snippet->isReadOnly()) {
|
||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
||||
}
|
||||
|
||||
setWindowTitle(tr("%1 Properties").arg(m_snippet->getName()));
|
||||
}
|
||||
|
||||
void SnippetPropertiesDialog::setupSnippetInfoWidget(QWidget *p_parent)
|
||||
{
|
||||
m_infoWidget = new SnippetInfoWidget(m_snippet, p_parent);
|
||||
}
|
||||
|
||||
bool SnippetPropertiesDialog::validateInputs()
|
||||
{
|
||||
bool valid = true;
|
||||
QString msg;
|
||||
|
||||
valid = valid && validateNameInput(msg);
|
||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||
: ScrollDialog::InformationLevel::Error);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool SnippetPropertiesDialog::validateNameInput(QString &p_msg)
|
||||
{
|
||||
p_msg.clear();
|
||||
|
||||
auto name = m_infoWidget->getName();
|
||||
if (name.isEmpty()) {
|
||||
p_msg = tr("Please specify a name for the snippet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name.toLower() == m_snippet->getName().toLower()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SnippetMgr::getInst().find(name)) {
|
||||
p_msg = tr("Name conflicts with existing snippet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SnippetPropertiesDialog::acceptedButtonClicked()
|
||||
{
|
||||
if (validateInputs() && saveSnippetProperties()) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
bool SnippetPropertiesDialog::saveSnippetProperties()
|
||||
{
|
||||
auto snip = QSharedPointer<Snippet>::create(m_infoWidget->getName(),
|
||||
m_infoWidget->getContent(),
|
||||
m_infoWidget->getShortcut(),
|
||||
m_infoWidget->shouldIndentAsFirstLine(),
|
||||
m_infoWidget->getCursorMark(),
|
||||
m_infoWidget->getSelectionMark());
|
||||
Q_ASSERT(snip->isValid());
|
||||
try {
|
||||
SnippetMgr::getInst().updateSnippet(m_snippet->getName(), snip);
|
||||
} catch (Exception &p_e) {
|
||||
QString msg = tr("Failed to update snippet (%1) (%2).")
|
||||
.arg(snip->getName(), p_e.what());
|
||||
qWarning() << msg;
|
||||
setInformationText(msg, ScrollDialog::InformationLevel::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
37
src/widgets/dialogs/snippetpropertiesdialog.h
Normal file
37
src/widgets/dialogs/snippetpropertiesdialog.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef SNIPPETPROPERTIESDIALOG_H
|
||||
#define SNIPPETPROPERTIESDIALOG_H
|
||||
|
||||
#include "scrolldialog.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Snippet;
|
||||
class SnippetInfoWidget;
|
||||
|
||||
class SnippetPropertiesDialog : public ScrollDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SnippetPropertiesDialog(Snippet *p_snippet, QWidget *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void setupSnippetInfoWidget(QWidget *p_parent);
|
||||
|
||||
bool validateNameInput(QString &p_msg);
|
||||
|
||||
bool saveSnippetProperties();
|
||||
|
||||
bool validateInputs();
|
||||
|
||||
SnippetInfoWidget *m_infoWidget = nullptr;
|
||||
|
||||
Snippet *m_snippet = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SNIPPETPROPERTIESDIALOG_H
|
29
src/widgets/lineeditwithsnippet.cpp
Normal file
29
src/widgets/lineeditwithsnippet.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "lineeditwithsnippet.h"
|
||||
|
||||
#include <snippet/snippetmgr.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
LineEditWithSnippet::LineEditWithSnippet(QWidget *p_parent)
|
||||
: LineEdit(p_parent)
|
||||
{
|
||||
setTips();
|
||||
}
|
||||
|
||||
LineEditWithSnippet::LineEditWithSnippet(const QString &p_contents, QWidget *p_parent)
|
||||
: LineEdit(p_contents, p_parent)
|
||||
{
|
||||
setTips();
|
||||
}
|
||||
|
||||
void LineEditWithSnippet::setTips()
|
||||
{
|
||||
const auto tips = tr("Snippet is supported via %name%");
|
||||
setToolTip(tips);
|
||||
setPlaceholderText(tips);
|
||||
}
|
||||
|
||||
QString LineEditWithSnippet::evaluatedText() const
|
||||
{
|
||||
return SnippetMgr::getInst().applySnippetBySymbol(text());
|
||||
}
|
25
src/widgets/lineeditwithsnippet.h
Normal file
25
src/widgets/lineeditwithsnippet.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef LINEEDITWITHSNIPPET_H
|
||||
#define LINEEDITWITHSNIPPET_H
|
||||
|
||||
#include "lineedit.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// A line edit with snippet support.
|
||||
class LineEditWithSnippet : public LineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LineEditWithSnippet(QWidget *p_parent = nullptr);
|
||||
|
||||
LineEditWithSnippet(const QString &p_contents, QWidget *p_parent = nullptr);
|
||||
|
||||
// Get text with snippets evaluated.
|
||||
QString evaluatedText() const;
|
||||
|
||||
private:
|
||||
void setTips();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LINEEDITWITHSNIPPET_H
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <core/vnotex.h>
|
||||
#include <utils/iconutils.h>
|
||||
#include <utils/widgetutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -29,8 +30,7 @@ LocationList::LocationList(QWidget *p_parent)
|
||||
void LocationList::setupUI()
|
||||
{
|
||||
auto mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
WidgetUtils::setContentsMargins(mainLayout);
|
||||
|
||||
{
|
||||
setupTitleBar(QString(), this);
|
||||
@ -96,6 +96,7 @@ NavigationModeWrapper<QTreeWidget, QTreeWidgetItem> *LocationList::getNavigation
|
||||
void LocationList::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
||||
{
|
||||
m_titleBar = new TitleBar(p_title, true, TitleBar::Action::None, p_parent);
|
||||
m_titleBar->setActionButtonsAlwaysShown(true);
|
||||
|
||||
{
|
||||
auto clearBtn = m_titleBar->addActionButton(QStringLiteral("clear.svg"), tr("Clear"));
|
||||
@ -175,6 +176,6 @@ void LocationList::updateItemsCountLabel()
|
||||
if (cnt == 0) {
|
||||
m_titleBar->setInfoLabel("");
|
||||
} else {
|
||||
m_titleBar->setInfoLabel(tr("%n Item(s)", "", m_tree->topLevelItemCount()));
|
||||
m_titleBar->setInfoLabel(tr("%n Item(s)", "", cnt));
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "titletoolbar.h"
|
||||
#include "locationlist.h"
|
||||
#include "searchpanel.h"
|
||||
#include "snippetpanel.h"
|
||||
#include <notebook/notebook.h>
|
||||
#include "searchinfoprovider.h"
|
||||
#include <vtextedit/spellchecker.h>
|
||||
@ -84,14 +85,15 @@ void MainWindow::kickOffOnStart(const QStringList &p_paths)
|
||||
|
||||
VNoteX::getInst().initLoad();
|
||||
|
||||
setupSpellCheck();
|
||||
|
||||
// Do necessary stuffs before emitting this signal.
|
||||
emit mainWindowStarted();
|
||||
|
||||
emit layoutChanged();
|
||||
|
||||
demoWidget();
|
||||
|
||||
setupSpellCheck();
|
||||
|
||||
openFiles(p_paths);
|
||||
});
|
||||
}
|
||||
@ -206,6 +208,8 @@ void MainWindow::setupDocks()
|
||||
|
||||
setupSearchDock();
|
||||
|
||||
setupSnippetDock();
|
||||
|
||||
for (int i = 1; i < m_docks.size(); ++i) {
|
||||
tabifyDockWidget(m_docks[i - 1], m_docks[i]);
|
||||
}
|
||||
@ -295,6 +299,34 @@ void MainWindow::setupSearchPanel()
|
||||
m_searchPanel->setObjectName("SearchPanel.vnotex");
|
||||
}
|
||||
|
||||
void MainWindow::setupSnippetDock()
|
||||
{
|
||||
auto dock = new QDockWidget(tr("Snippets"), this);
|
||||
m_docks.push_back(dock);
|
||||
|
||||
dock->setObjectName(QStringLiteral("SnippetDock.vnotex"));
|
||||
dock->setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||
|
||||
setupSnippetPanel();
|
||||
dock->setWidget(m_snippetPanel);
|
||||
dock->setFocusProxy(m_snippetPanel);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, dock);
|
||||
}
|
||||
|
||||
void MainWindow::setupSnippetPanel()
|
||||
{
|
||||
m_snippetPanel = new SnippetPanel(this);
|
||||
m_snippetPanel->setObjectName("SnippetPanel.vnotex");
|
||||
connect(m_snippetPanel, &SnippetPanel::applySnippetRequested,
|
||||
this, [this](const QString &p_name) {
|
||||
auto viewWindow = m_viewArea->getCurrentViewWindow();
|
||||
if (viewWindow) {
|
||||
viewWindow->applySnippet(p_name);
|
||||
viewWindow->setFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::setupLocationListDock()
|
||||
{
|
||||
auto dock = new QDockWidget(tr("Location List"), this);
|
||||
@ -418,7 +450,7 @@ void MainWindow::saveStateAndGeometry()
|
||||
SessionConfig::MainWindowStateGeometry sg;
|
||||
sg.m_mainState = saveState();
|
||||
sg.m_mainGeometry = saveGeometry();
|
||||
sg.m_docksVisibilityBeforeExpand = m_docksVisibilityBeforeExpand;
|
||||
sg.m_visibleDocksBeforeExpand = m_visibleDocksBeforeExpand;
|
||||
|
||||
auto& sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||
sessionConfig.setMainWindowStateGeometry(sg);
|
||||
@ -439,12 +471,13 @@ void MainWindow::loadStateAndGeometry(bool p_stateOnly)
|
||||
}
|
||||
|
||||
if (!p_stateOnly) {
|
||||
m_docksVisibilityBeforeExpand = sg.m_docksVisibilityBeforeExpand;
|
||||
if (m_docksVisibilityBeforeExpand.isEmpty()) {
|
||||
// Init.
|
||||
m_docksVisibilityBeforeExpand.resize(m_docks.size());
|
||||
m_visibleDocksBeforeExpand = sg.m_visibleDocksBeforeExpand;
|
||||
if (m_visibleDocksBeforeExpand.isEmpty()) {
|
||||
// Init (or init again if there is no visible dock).
|
||||
for (int i = 0; i < m_docks.size(); ++i) {
|
||||
m_docksVisibilityBeforeExpand.setBit(i, m_docks[i]->isVisible());
|
||||
if (m_docks[i]->isVisible()) {
|
||||
m_visibleDocksBeforeExpand.push_back(m_docks[i]->objectName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -468,10 +501,14 @@ void MainWindow::setContentAreaExpanded(bool p_expanded)
|
||||
|
||||
if (p_expanded) {
|
||||
// Store the state and hide.
|
||||
m_visibleDocksBeforeExpand.clear();
|
||||
for (int i = 0; i < m_docks.size(); ++i) {
|
||||
m_docksVisibilityBeforeExpand[i] = m_docks[i]->isVisible();
|
||||
const auto objName = m_docks[i]->objectName();
|
||||
if (m_docks[i]->isVisible()) {
|
||||
m_visibleDocksBeforeExpand.push_back(objName);
|
||||
}
|
||||
|
||||
if (m_docks[i]->isFloating() || keepDocks.contains(m_docks[i]->objectName())) {
|
||||
if (m_docks[i]->isFloating() || keepDocks.contains(objName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -481,15 +518,15 @@ void MainWindow::setContentAreaExpanded(bool p_expanded)
|
||||
// Restore the state.
|
||||
bool hasVisible = false;
|
||||
for (int i = 0; i < m_docks.size(); ++i) {
|
||||
if (m_docks[i]->isFloating() || keepDocks.contains(m_docks[i]->objectName())) {
|
||||
const auto objName = m_docks[i]->objectName();
|
||||
if (m_docks[i]->isFloating() || keepDocks.contains(objName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_docksVisibilityBeforeExpand[i]) {
|
||||
hasVisible = true;
|
||||
}
|
||||
const bool visible = m_visibleDocksBeforeExpand.contains(objName);
|
||||
hasVisible = hasVisible || visible;
|
||||
|
||||
m_docks[i]->setVisible(m_docksVisibilityBeforeExpand[i]);
|
||||
m_docks[i]->setVisible(visible);
|
||||
}
|
||||
|
||||
if (!hasVisible) {
|
||||
@ -606,6 +643,9 @@ void MainWindow::setupShortcuts()
|
||||
|
||||
setupDockActivateShortcut(m_docks[DockIndex::LocationListDock],
|
||||
coreConfig.getShortcut(CoreConfig::Shortcut::LocationListDock));
|
||||
|
||||
setupDockActivateShortcut(m_docks[DockIndex::SnippetDock],
|
||||
coreConfig.getShortcut(CoreConfig::Shortcut::SnippetDock));
|
||||
}
|
||||
|
||||
void MainWindow::setupDockActivateShortcut(QDockWidget *p_dock, const QString &p_keys)
|
||||
|
@ -22,6 +22,7 @@ namespace vnotex
|
||||
class OutlineViewer;
|
||||
class LocationList;
|
||||
class SearchPanel;
|
||||
class SnippetPanel;
|
||||
|
||||
enum { RESTART_EXIT_CODE = 1000 };
|
||||
|
||||
@ -96,6 +97,7 @@ namespace vnotex
|
||||
NavigationDock = 0,
|
||||
OutlineDock,
|
||||
SearchDock,
|
||||
SnippetDock,
|
||||
LocationListDock
|
||||
};
|
||||
|
||||
@ -117,6 +119,10 @@ namespace vnotex
|
||||
|
||||
void setupLocationList();
|
||||
|
||||
void setupSnippetDock();
|
||||
|
||||
void setupSnippetPanel();
|
||||
|
||||
void setupNotebookExplorer(QWidget *p_parent = nullptr);
|
||||
|
||||
void setupDocks();
|
||||
@ -168,6 +174,8 @@ namespace vnotex
|
||||
|
||||
SearchPanel *m_searchPanel = nullptr;
|
||||
|
||||
SnippetPanel *m_snippetPanel = nullptr;
|
||||
|
||||
QVector<QDockWidget *> m_docks;
|
||||
|
||||
bool m_layoutReset = false;
|
||||
@ -184,7 +192,7 @@ namespace vnotex
|
||||
|
||||
QTimer *m_tipsTimer = nullptr;
|
||||
|
||||
QBitArray m_docksVisibilityBeforeExpand;
|
||||
QStringList m_visibleDocksBeforeExpand;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "editors/statuswidget.h"
|
||||
#include "editors/plantumlhelper.h"
|
||||
#include "editors/graphvizhelper.h"
|
||||
#include <snippet/snippetmgr.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -975,3 +976,16 @@ void MarkdownViewWindow::setupPreviewHelper()
|
||||
markdownEditorConfig.getPlantUmlCommand());
|
||||
GraphvizHelper::getInst().init(markdownEditorConfig.getGraphvizExe());
|
||||
}
|
||||
|
||||
void MarkdownViewWindow::applySnippet(const QString &p_name)
|
||||
{
|
||||
if (isReadMode() || m_editor->isReadOnly()) {
|
||||
qWarning() << "failed to apply snippet in read mode or to a read-only buffer" << p_name;
|
||||
return;
|
||||
}
|
||||
|
||||
m_editor->enterInsertModeIfApplicable();
|
||||
SnippetMgr::getInst().applySnippet(p_name,
|
||||
m_editor->getTextEdit(),
|
||||
SnippetMgr::generateOverrides(getBuffer()));
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ namespace vnotex
|
||||
|
||||
ViewWindowSession saveSession() const Q_DECL_OVERRIDE;
|
||||
|
||||
void applySnippet(const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
public slots:
|
||||
void handleEditorConfigChange() Q_DECL_OVERRIDE;
|
||||
|
||||
|
@ -105,6 +105,7 @@ TitleBar *NotebookExplorer::setupTitleBar(QWidget *p_parent)
|
||||
TitleBar::Action::Menu,
|
||||
p_parent);
|
||||
titleBar->setWhatsThis(tr("This title bar contains buttons and menu to manage notebooks and notes."));
|
||||
titleBar->setActionButtonsAlwaysShown(true);
|
||||
|
||||
{
|
||||
auto viewMenu = WidgetsFactory::createMenu(titleBar);
|
||||
|
@ -90,6 +90,7 @@ NavigationModeWrapper<QTreeWidget, QTreeWidgetItem> *OutlineViewer::getNavigatio
|
||||
TitleBar *OutlineViewer::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
||||
{
|
||||
auto titleBar = new TitleBar(p_title, false, TitleBar::Action::None, p_parent);
|
||||
titleBar->setActionButtonsAlwaysShown(true);
|
||||
|
||||
auto decreaseBtn = titleBar->addActionButton(QStringLiteral("decrease_outline_level.svg"), tr("Decrease Expansion Level"));
|
||||
connect(decreaseBtn, &QToolButton::clicked,
|
||||
|
@ -47,7 +47,7 @@ namespace vnotex
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SearchPanel(const QSharedPointer<ISearchInfoProvider> &p_provider, QWidget *p_parent = nullptr);
|
||||
SearchPanel(const QSharedPointer<ISearchInfoProvider> &p_provider, QWidget *p_parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void startSearch();
|
||||
|
232
src/widgets/snippetpanel.cpp
Normal file
232
src/widgets/snippetpanel.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
#include "snippetpanel.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QToolButton>
|
||||
#include <QListWidgetItem>
|
||||
|
||||
#include <utils/widgetutils.h>
|
||||
#include <snippet/snippetmgr.h>
|
||||
#include <core/vnotex.h>
|
||||
#include <core/exception.h>
|
||||
|
||||
#include "titlebar.h"
|
||||
#include "listwidget.h"
|
||||
#include "dialogs/newsnippetdialog.h"
|
||||
#include "dialogs/snippetpropertiesdialog.h"
|
||||
#include "dialogs/deleteconfirmdialog.h"
|
||||
#include "mainwindow.h"
|
||||
#include "messageboxhelper.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
SnippetPanel::SnippetPanel(QWidget *p_parent)
|
||||
: QFrame(p_parent)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void SnippetPanel::setupUI()
|
||||
{
|
||||
auto mainLayout = new QVBoxLayout(this);
|
||||
WidgetUtils::setContentsMargins(mainLayout);
|
||||
|
||||
{
|
||||
setupTitleBar(QString(), this);
|
||||
mainLayout->addWidget(m_titleBar);
|
||||
}
|
||||
|
||||
m_snippetList = new ListWidget(this);
|
||||
m_snippetList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_snippetList->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
connect(m_snippetList, &QListWidget::customContextMenuRequested,
|
||||
this, &SnippetPanel::handleContextMenuRequested);
|
||||
connect(m_snippetList, &QListWidget::itemActivated,
|
||||
this, &SnippetPanel::applySnippet);
|
||||
mainLayout->addWidget(m_snippetList);
|
||||
|
||||
setFocusProxy(m_snippetList);
|
||||
}
|
||||
|
||||
void SnippetPanel::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
||||
{
|
||||
m_titleBar = new TitleBar(p_title, true, TitleBar::Action::None, p_parent);
|
||||
m_titleBar->setActionButtonsAlwaysShown(true);
|
||||
|
||||
{
|
||||
auto newBtn = m_titleBar->addActionButton(QStringLiteral("add.svg"), tr("New Snippet"));
|
||||
connect(newBtn, &QToolButton::triggered,
|
||||
this, &SnippetPanel::newSnippet);
|
||||
}
|
||||
|
||||
{
|
||||
auto openFolderBtn = m_titleBar->addActionButton(QStringLiteral("open_folder.svg"), tr("Open Folder"));
|
||||
connect(openFolderBtn, &QToolButton::triggered,
|
||||
this, [this]() {
|
||||
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(SnippetMgr::getInst().getSnippetFolder()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SnippetPanel::newSnippet()
|
||||
{
|
||||
NewSnippetDialog dialog(VNoteX::getInst().getMainWindow());
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
updateSnippetList();
|
||||
}
|
||||
}
|
||||
|
||||
void SnippetPanel::updateItemsCountLabel()
|
||||
{
|
||||
const auto cnt = m_snippetList->count();
|
||||
if (cnt == 0) {
|
||||
m_titleBar->setInfoLabel("");
|
||||
} else {
|
||||
m_titleBar->setInfoLabel(tr("%n Item(s)", "", cnt));
|
||||
}
|
||||
}
|
||||
|
||||
void SnippetPanel::updateSnippetList()
|
||||
{
|
||||
m_snippetList->clear();
|
||||
|
||||
const auto &snippets = SnippetMgr::getInst().getSnippets();
|
||||
for (const auto &snippet : snippets) {
|
||||
auto item = new QListWidgetItem(m_snippetList);
|
||||
QString suffix;
|
||||
if (snippet->isReadOnly()) {
|
||||
suffix = QLatin1Char('*');
|
||||
}
|
||||
if (snippet->getShortcut() == Snippet::InvalidShortcut) {
|
||||
item->setText(snippet->getName() + suffix);
|
||||
} else {
|
||||
item->setText(tr("%1%2 [%3]").arg(snippet->getName(), suffix, snippet->getShortcutString()));
|
||||
}
|
||||
|
||||
item->setData(Qt::UserRole, snippet->getName());
|
||||
}
|
||||
|
||||
updateItemsCountLabel();
|
||||
}
|
||||
|
||||
void SnippetPanel::showEvent(QShowEvent *p_event)
|
||||
{
|
||||
QFrame::showEvent(p_event);
|
||||
|
||||
if (!m_listInitialized) {
|
||||
m_listInitialized = true;
|
||||
updateSnippetList();
|
||||
}
|
||||
}
|
||||
|
||||
void SnippetPanel::handleContextMenuRequested(QPoint p_pos)
|
||||
{
|
||||
QMenu menu(this);
|
||||
|
||||
auto item = m_snippetList->itemAt(p_pos);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int selectedCount = m_snippetList->selectedItems().size();
|
||||
if (selectedCount == 1) {
|
||||
menu.addAction(tr("&Apply"),
|
||||
&menu,
|
||||
[this]() {
|
||||
applySnippet(m_snippetList->currentItem());
|
||||
});
|
||||
}
|
||||
|
||||
menu.addAction(tr("&Delete"),
|
||||
this,
|
||||
&SnippetPanel::removeSelectedSnippets);
|
||||
|
||||
if (selectedCount == 1) {
|
||||
menu.addAction(tr("&Properties (Rename)"),
|
||||
&menu,
|
||||
[this]() {
|
||||
auto item = m_snippetList->currentItem();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto snippet = SnippetMgr::getInst().find(getSnippetName(item));
|
||||
if (!snippet) {
|
||||
qWarning() << "failed to find snippet for properties" << getSnippetName(item);
|
||||
return;
|
||||
}
|
||||
|
||||
SnippetPropertiesDialog dialog(snippet.data(), VNoteX::getInst().getMainWindow());
|
||||
if (dialog.exec()) {
|
||||
updateSnippetList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
menu.exec(m_snippetList->mapToGlobal(p_pos));
|
||||
}
|
||||
|
||||
QString SnippetPanel::getSnippetName(const QListWidgetItem *p_item)
|
||||
{
|
||||
return p_item->data(Qt::UserRole).toString();
|
||||
}
|
||||
|
||||
void SnippetPanel::removeSelectedSnippets()
|
||||
{
|
||||
const auto selectedItems = m_snippetList->selectedItems();
|
||||
if (selectedItems.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<ConfirmItemInfo> items;
|
||||
for (const auto &selectedItem : selectedItems) {
|
||||
const auto name = getSnippetName(selectedItem);
|
||||
items.push_back(ConfirmItemInfo(name,
|
||||
name,
|
||||
QString(),
|
||||
nullptr));
|
||||
}
|
||||
|
||||
DeleteConfirmDialog dialog(tr("Confirm Deletion"),
|
||||
tr("Delete these snippets permanently?"),
|
||||
tr("Files will be deleted permanently and could not be found even "
|
||||
"in operating system's recycle bin."),
|
||||
items,
|
||||
DeleteConfirmDialog::Flag::None,
|
||||
false,
|
||||
VNoteX::getInst().getMainWindow());
|
||||
|
||||
QStringList snippetsToDelete;
|
||||
if (dialog.exec()) {
|
||||
items = dialog.getConfirmedItems();
|
||||
for (const auto &item : items) {
|
||||
snippetsToDelete << item.m_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (snippetsToDelete.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &snippetName : snippetsToDelete) {
|
||||
try {
|
||||
SnippetMgr::getInst().removeSnippet(snippetName);
|
||||
} catch (Exception &p_e) {
|
||||
QString msg = tr("Failed to remove snippet (%1) (%2).").arg(snippetName, p_e.what());
|
||||
qCritical() << msg;
|
||||
MessageBoxHelper::notify(MessageBoxHelper::Critical, msg, VNoteX::getInst().getMainWindow());
|
||||
}
|
||||
}
|
||||
|
||||
updateSnippetList();
|
||||
}
|
||||
|
||||
void SnippetPanel::applySnippet(const QListWidgetItem *p_item)
|
||||
{
|
||||
if (!p_item) {
|
||||
return;
|
||||
}
|
||||
const auto name = getSnippetName(p_item);
|
||||
if (!name.isEmpty()) {
|
||||
emit applySnippetRequested(name);
|
||||
}
|
||||
}
|
53
src/widgets/snippetpanel.h
Normal file
53
src/widgets/snippetpanel.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef SNIPPETPANEL_H
|
||||
#define SNIPPETPANEL_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class TitleBar;
|
||||
|
||||
class SnippetPanel : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SnippetPanel(QWidget *p_parent = nullptr);
|
||||
|
||||
signals:
|
||||
void applySnippetRequested(const QString &p_name);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void newSnippet();
|
||||
|
||||
void handleContextMenuRequested(QPoint p_pos);
|
||||
|
||||
void removeSelectedSnippets();
|
||||
|
||||
void applySnippet(const QListWidgetItem *p_item);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void setupTitleBar(const QString &p_title, QWidget *p_parent = nullptr);
|
||||
|
||||
void updateItemsCountLabel();
|
||||
|
||||
void updateSnippetList();
|
||||
|
||||
QString getSnippetName(const QListWidgetItem *p_item);
|
||||
|
||||
TitleBar *m_titleBar = nullptr;
|
||||
|
||||
QListWidget *m_snippetList = nullptr;
|
||||
|
||||
bool m_listInitialized = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SNIPPETPANEL_H
|
@ -15,6 +15,7 @@
|
||||
#include <core/thememgr.h>
|
||||
#include "editors/statuswidget.h"
|
||||
#include <core/fileopenparameters.h>
|
||||
#include <snippet/snippetmgr.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -253,3 +254,16 @@ ViewWindowSession TextViewWindow::saveSession() const
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void TextViewWindow::applySnippet(const QString &p_name)
|
||||
{
|
||||
if (m_editor->isReadOnly()) {
|
||||
qWarning() << "failed to apply snippet to a read-only buffer" << p_name;
|
||||
return;
|
||||
}
|
||||
|
||||
m_editor->enterInsertModeIfApplicable();
|
||||
SnippetMgr::getInst().applySnippet(p_name,
|
||||
m_editor->getTextEdit(),
|
||||
SnippetMgr::generateOverrides(getBuffer()));
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ namespace vnotex
|
||||
|
||||
ViewWindowSession saveSession() const Q_DECL_OVERRIDE;
|
||||
|
||||
void applySnippet(const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
public slots:
|
||||
void handleEditorConfigChange() Q_DECL_OVERRIDE;
|
||||
|
||||
|
@ -180,7 +180,7 @@ QToolButton *TitleBar::addActionButton(const QString &p_iconName, const QString
|
||||
connect(p_menu, &QMenu::aboutToHide,
|
||||
this, [this]() {
|
||||
m_actionButtonsForcedShown = false;
|
||||
setActionButtonsVisible(false);
|
||||
setActionButtonsVisible(m_actionButtonsAlwaysShown);
|
||||
});
|
||||
return btn;
|
||||
}
|
||||
|
@ -72,13 +72,13 @@ namespace vnotex
|
||||
// Not all Workspace. Just all ViewSplits.
|
||||
QList<Buffer *> getAllBuffersInViewSplits() const;
|
||||
|
||||
ViewWindow *getCurrentViewWindow() const;
|
||||
|
||||
public slots:
|
||||
void openBuffer(Buffer *p_buffer, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||
|
||||
bool close(const Notebook *p_notebook, bool p_force);
|
||||
|
||||
ViewWindow *getCurrentViewWindow() const;
|
||||
|
||||
void focus();
|
||||
|
||||
// NavigationMode.
|
||||
|
@ -623,6 +623,16 @@ void ViewSplit::createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx) const
|
||||
WidgetUtils::addActionShortcutText(locateNodeAct,
|
||||
ConfigMgr::getInst().getCoreConfig().getShortcut(CoreConfig::Shortcut::LocateNode));
|
||||
}
|
||||
|
||||
// Pin To Quick Access.
|
||||
p_menu->addAction(tr("Pin To Quick Access"),
|
||||
[this, p_tabIdx]() {
|
||||
auto win = getViewWindow(p_tabIdx);
|
||||
if (win) {
|
||||
const QStringList files(win->getBuffer()->getPath());
|
||||
emit VNoteX::getInst().pinToQuickAccessRequested(files);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ViewSplit::closeTab(int p_idx)
|
||||
|
@ -84,6 +84,8 @@ namespace vnotex
|
||||
WindowFlags getWindowFlags() const;
|
||||
void setWindowFlags(WindowFlags p_flags);
|
||||
|
||||
virtual void applySnippet(const QString &p_name) = 0;
|
||||
|
||||
public slots:
|
||||
virtual void handleEditorConfigChange() = 0;
|
||||
|
||||
|
@ -51,6 +51,10 @@ void ViewWindowToolBarHelper::addActionShortcut(QAction *p_action,
|
||||
}
|
||||
}
|
||||
});
|
||||
QObject::connect(shortcut, &QShortcut::activatedAmbiguously,
|
||||
p_action, [p_action]() {
|
||||
qWarning() << "ViewWindow shortcut activated ambiguously" << p_action->text();
|
||||
});
|
||||
p_action->setText(QString("%1\t%2").arg(p_action->text(), shortcut->key().toString(QKeySequence::NativeText)));
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ SOURCES += \
|
||||
$$PWD/dialogs/legacynotebookutils.cpp \
|
||||
$$PWD/dialogs/linkinsertdialog.cpp \
|
||||
$$PWD/dialogs/newnotebookfromfolderdialog.cpp \
|
||||
$$PWD/dialogs/newsnippetdialog.cpp \
|
||||
$$PWD/dialogs/selectdialog.cpp \
|
||||
$$PWD/dialogs/selectionitemwidget.cpp \
|
||||
$$PWD/dialogs/settings/appearancepage.cpp \
|
||||
@ -25,6 +26,8 @@ SOURCES += \
|
||||
$$PWD/dialogs/settings/settingsdialog.cpp \
|
||||
$$PWD/dialogs/settings/texteditorpage.cpp \
|
||||
$$PWD/dialogs/settings/themepage.cpp \
|
||||
$$PWD/dialogs/snippetinfowidget.cpp \
|
||||
$$PWD/dialogs/snippetpropertiesdialog.cpp \
|
||||
$$PWD/dialogs/sortdialog.cpp \
|
||||
$$PWD/dialogs/tableinsertdialog.cpp \
|
||||
$$PWD/dragdropareaindicator.cpp \
|
||||
@ -47,6 +50,7 @@ SOURCES += \
|
||||
$$PWD/fullscreentoggleaction.cpp \
|
||||
$$PWD/lineedit.cpp \
|
||||
$$PWD/lineeditdelegate.cpp \
|
||||
$$PWD/lineeditwithsnippet.cpp \
|
||||
$$PWD/listwidget.cpp \
|
||||
$$PWD/locationinputwithbrowsebutton.cpp \
|
||||
$$PWD/locationlist.cpp \
|
||||
@ -60,6 +64,7 @@ SOURCES += \
|
||||
$$PWD/propertydefs.cpp \
|
||||
$$PWD/searchinfoprovider.cpp \
|
||||
$$PWD/searchpanel.cpp \
|
||||
$$PWD/snippetpanel.cpp \
|
||||
$$PWD/systemtrayhelper.cpp \
|
||||
$$PWD/textviewwindow.cpp \
|
||||
$$PWD/toolbarhelper.cpp \
|
||||
@ -112,6 +117,7 @@ HEADERS += \
|
||||
$$PWD/dialogs/legacynotebookutils.h \
|
||||
$$PWD/dialogs/linkinsertdialog.h \
|
||||
$$PWD/dialogs/newnotebookfromfolderdialog.h \
|
||||
$$PWD/dialogs/newsnippetdialog.h \
|
||||
$$PWD/dialogs/selectdialog.h \
|
||||
$$PWD/dialogs/selectionitemwidget.h \
|
||||
$$PWD/dialogs/settings/appearancepage.h \
|
||||
@ -124,6 +130,8 @@ HEADERS += \
|
||||
$$PWD/dialogs/settings/settingsdialog.h \
|
||||
$$PWD/dialogs/settings/texteditorpage.h \
|
||||
$$PWD/dialogs/settings/themepage.h \
|
||||
$$PWD/dialogs/snippetinfowidget.h \
|
||||
$$PWD/dialogs/snippetpropertiesdialog.h \
|
||||
$$PWD/dialogs/sortdialog.h \
|
||||
$$PWD/dialogs/tableinsertdialog.h \
|
||||
$$PWD/dragdropareaindicator.h \
|
||||
@ -146,6 +154,7 @@ HEADERS += \
|
||||
$$PWD/fullscreentoggleaction.h \
|
||||
$$PWD/lineedit.h \
|
||||
$$PWD/lineeditdelegate.h \
|
||||
$$PWD/lineeditwithsnippet.h \
|
||||
$$PWD/listwidget.h \
|
||||
$$PWD/locationinputwithbrowsebutton.h \
|
||||
$$PWD/locationlist.h \
|
||||
@ -160,6 +169,7 @@ HEADERS += \
|
||||
$$PWD/propertydefs.h \
|
||||
$$PWD/searchinfoprovider.h \
|
||||
$$PWD/searchpanel.h \
|
||||
$$PWD/snippetpanel.h \
|
||||
$$PWD/systemtrayhelper.h \
|
||||
$$PWD/textviewwindow.h \
|
||||
$$PWD/textviewwindowhelper.h \
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <QPlainTextEdit>
|
||||
#include <QRadioButton>
|
||||
|
||||
#include "lineedit.h"
|
||||
#include "lineeditwithsnippet.h"
|
||||
#include "combobox.h"
|
||||
|
||||
using namespace vnotex;
|
||||
@ -40,6 +40,16 @@ QLineEdit *WidgetsFactory::createLineEdit(const QString &p_contents, QWidget *p_
|
||||
return new LineEdit(p_contents, p_parent);
|
||||
}
|
||||
|
||||
LineEditWithSnippet *WidgetsFactory::createLineEditWithSnippet(QWidget *p_parent)
|
||||
{
|
||||
return new LineEditWithSnippet(p_parent);
|
||||
}
|
||||
|
||||
LineEditWithSnippet *WidgetsFactory::createLineEditWithSnippet(const QString &p_contents, QWidget *p_parent)
|
||||
{
|
||||
return new LineEditWithSnippet(p_contents, p_parent);
|
||||
}
|
||||
|
||||
QComboBox *WidgetsFactory::createComboBox(QWidget *p_parent)
|
||||
{
|
||||
auto comboBox = new ComboBox(p_parent);
|
||||
|
@ -16,6 +16,8 @@ class QRadioButton;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class LineEditWithSnippet;
|
||||
|
||||
class WidgetsFactory
|
||||
{
|
||||
public:
|
||||
@ -29,6 +31,10 @@ namespace vnotex
|
||||
|
||||
static QLineEdit *createLineEdit(const QString &p_contents, QWidget *p_parent = nullptr);
|
||||
|
||||
static LineEditWithSnippet *createLineEditWithSnippet(QWidget *p_parent = nullptr);
|
||||
|
||||
static LineEditWithSnippet *createLineEditWithSnippet(const QString &p_contents, QWidget *p_parent = nullptr);
|
||||
|
||||
static QComboBox *createComboBox(QWidget *p_parent = nullptr);
|
||||
|
||||
static QCheckBox *createCheckBox(const QString &p_text, QWidget *p_parent = nullptr);
|
||||
|
@ -19,6 +19,7 @@ include($$SRC_FOLDER/widgets/widgets.pri)
|
||||
include($$SRC_FOLDER/utils/utils.pri)
|
||||
include($$SRC_FOLDER/export/export.pri)
|
||||
include($$SRC_FOLDER/search/search.pri)
|
||||
include($$SRC_FOLDER/snippet/snippet.pri)
|
||||
|
||||
SOURCES += \
|
||||
test_notebook.cpp
|
||||
|
Loading…
x
Reference in New Issue
Block a user