mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +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;
|
using namespace vnotex;
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG
|
#ifndef QT_NO_DEBUG
|
||||||
#define VX_DEBUG_WEB
|
// #define VX_DEBUG_WEB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const QString ConfigMgr::c_orgName = "VNote";
|
const QString ConfigMgr::c_orgName = "VNote";
|
||||||
@ -388,6 +388,13 @@ QString ConfigMgr::getUserTemplateFolder() const
|
|||||||
return folderPath;
|
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
|
QString ConfigMgr::getUserOrAppFile(const QString &p_filePath) const
|
||||||
{
|
{
|
||||||
QFileInfo fi(p_filePath);
|
QFileInfo fi(p_filePath);
|
||||||
|
@ -93,6 +93,8 @@ namespace vnotex
|
|||||||
|
|
||||||
QString getUserTemplateFolder() const;
|
QString getUserTemplateFolder() const;
|
||||||
|
|
||||||
|
QString getUserSnippetFolder() const;
|
||||||
|
|
||||||
// If @p_filePath is absolute, just return it.
|
// If @p_filePath is absolute, just return it.
|
||||||
// Otherwise, first try to find it in user folder, then in app folder.
|
// Otherwise, first try to find it in user folder, then in app folder.
|
||||||
QString getUserOrAppFile(const QString &p_filePath) const;
|
QString getUserOrAppFile(const QString &p_filePath) const;
|
||||||
|
@ -24,6 +24,7 @@ namespace vnotex
|
|||||||
NavigationDock,
|
NavigationDock,
|
||||||
OutlineDock,
|
OutlineDock,
|
||||||
SearchDock,
|
SearchDock,
|
||||||
|
SnippetDock,
|
||||||
LocationListDock,
|
LocationListDock,
|
||||||
Search,
|
Search,
|
||||||
NavigationMode,
|
NavigationMode,
|
||||||
|
@ -216,7 +216,7 @@ QJsonObject SessionConfig::saveStateAndGeometry() const
|
|||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
writeByteArray(obj, QStringLiteral("main_window_state"), m_mainWindowStateGeometry.m_mainState);
|
writeByteArray(obj, QStringLiteral("main_window_state"), m_mainWindowStateGeometry.m_mainState);
|
||||||
writeByteArray(obj, QStringLiteral("main_window_geometry"), m_mainWindowStateGeometry.m_mainGeometry);
|
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;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ void SessionConfig::loadStateAndGeometry(const QJsonObject &p_session)
|
|||||||
const auto obj = p_session.value(QStringLiteral("state_geometry")).toObject();
|
const auto obj = p_session.value(QStringLiteral("state_geometry")).toObject();
|
||||||
m_mainWindowStateGeometry.m_mainState = readByteArray(obj, QStringLiteral("main_window_state"));
|
m_mainWindowStateGeometry.m_mainState = readByteArray(obj, QStringLiteral("main_window_state"));
|
||||||
m_mainWindowStateGeometry.m_mainGeometry = readByteArray(obj, QStringLiteral("main_window_geometry"));
|
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()
|
QByteArray SessionConfig::getViewAreaSessionAndClear()
|
||||||
|
@ -35,14 +35,14 @@ namespace vnotex
|
|||||||
{
|
{
|
||||||
return m_mainState == p_other.m_mainState
|
return m_mainState == p_other.m_mainState
|
||||||
&& m_mainGeometry == p_other.m_mainGeometry
|
&& m_mainGeometry == p_other.m_mainGeometry
|
||||||
&& m_docksVisibilityBeforeExpand == p_other.m_docksVisibilityBeforeExpand;
|
&& m_visibleDocksBeforeExpand == p_other.m_visibleDocksBeforeExpand;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray m_mainState;
|
QByteArray m_mainState;
|
||||||
|
|
||||||
QByteArray m_mainGeometry;
|
QByteArray m_mainGeometry;
|
||||||
|
|
||||||
QBitArray m_docksVisibilityBeforeExpand;
|
QStringList m_visibleDocksBeforeExpand;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum OpenGL
|
enum OpenGL
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"NavigationDock" : "Ctrl+G, A",
|
"NavigationDock" : "Ctrl+G, A",
|
||||||
"OutlineDock" : "Ctrl+G, U",
|
"OutlineDock" : "Ctrl+G, U",
|
||||||
"SearchDock" : "Ctrl+G, S",
|
"SearchDock" : "Ctrl+G, S",
|
||||||
|
"SnippetDock" : "",
|
||||||
"LocationListDock" : "Ctrl+G, L",
|
"LocationListDock" : "Ctrl+G, L",
|
||||||
"Search" : "Ctrl+Alt+F",
|
"Search" : "Ctrl+Alt+F",
|
||||||
"NavigationMode" : "Ctrl+G, W",
|
"NavigationMode" : "Ctrl+G, W",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# External Programs
|
# External Programs
|
||||||
VNote allows user to open notes with **external programs** via the `Open With` in the context menu of the node explorer.
|
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
|
```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);
|
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;
|
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 支持通过在节点浏览器上下文菜单中的 `打开方式` 来调用 **外部程序** 打开笔记。
|
VNote 支持通过在节点浏览器上下文菜单中的 `打开方式` 来调用 **外部程序** 打开笔记。
|
||||||
|
|
||||||
用户需要编辑会话配置来添加自定义外部程序。一个例子如下:
|
用户需要编辑会话配置(用户配置文件夹下的 `session.json` 文件)来添加自定义外部程序。一个例子如下:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -27,4 +27,4 @@ VNote 支持通过在节点浏览器上下文菜单中的 `打开方式` 来调
|
|||||||
1. 使用 `%1` 占位符,会被替换为真实的文件路径(自动加上双引号包裹);
|
1. 使用 `%1` 占位符,会被替换为真实的文件路径(自动加上双引号包裹);
|
||||||
3. `shortcut`: 分配给该外部程序的快捷键;
|
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/search/search.pri)
|
||||||
|
|
||||||
|
include($$PWD/snippet/snippet.pri)
|
||||||
|
|
||||||
include($$PWD/core/core.pri)
|
include($$PWD/core/core.pri)
|
||||||
|
|
||||||
include($$PWD/widgets/widgets.pri)
|
include($$PWD/widgets/widgets.pri)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#include "../core/exception.h"
|
#include "../core/exception.h"
|
||||||
#include "pathutils.h"
|
#include "pathutils.h"
|
||||||
@ -34,6 +35,11 @@ QString FileUtils::readTextFile(const QString &p_filePath)
|
|||||||
return text;
|
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)
|
void FileUtils::writeFile(const QString &p_filePath, const QByteArray &p_data)
|
||||||
{
|
{
|
||||||
QFile file(p_filePath);
|
QFile file(p_filePath);
|
||||||
@ -59,6 +65,11 @@ void FileUtils::writeFile(const QString &p_filePath, const QString &p_text)
|
|||||||
file.close();
|
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)
|
void FileUtils::renameFile(const QString &p_path, const QString &p_name)
|
||||||
{
|
{
|
||||||
Q_ASSERT(PathUtils::isLegalFileName(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)
|
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);
|
QFile file(p_filePath);
|
||||||
if (!file.remove()) {
|
if (!file.remove()) {
|
||||||
Exception::throwOne(Exception::Type::FailToRemoveFile,
|
Exception::throwOne(Exception::Type::FailToRemoveFile,
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
class QTemporaryFile;
|
class QTemporaryFile;
|
||||||
|
|
||||||
@ -19,10 +20,14 @@ namespace vnotex
|
|||||||
|
|
||||||
static QString readTextFile(const QString &p_filePath);
|
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 QByteArray &p_data);
|
||||||
|
|
||||||
static void writeFile(const QString &p_filePath, const QString &p_text);
|
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.
|
// Rename file or dir.
|
||||||
static void renameFile(const QString &p_path, const QString &p_name);
|
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");
|
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 bool fuzzyEqual(qreal p_a, qreal p_b);
|
||||||
|
|
||||||
static QString boolToString(bool p_val);
|
static QString boolToString(bool p_val);
|
||||||
|
|
||||||
|
static QString intToString(int p_val, int p_width = 0);
|
||||||
};
|
};
|
||||||
} // ns vnotex
|
} // ns vnotex
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ void FolderPropertiesDialog::setupUI()
|
|||||||
setCentralWidget(m_infoWidget);
|
setCentralWidget(m_infoWidget);
|
||||||
|
|
||||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
|
||||||
|
|
||||||
setWindowTitle(m_node->getName() + QStringLiteral(" ") + tr("Properties"));
|
setWindowTitle(m_node->getName() + QStringLiteral(" ") + tr("Properties"));
|
||||||
}
|
}
|
||||||
@ -38,11 +37,9 @@ void FolderPropertiesDialog::setupUI()
|
|||||||
void FolderPropertiesDialog::setupNodeInfoWidget(QWidget *p_parent)
|
void FolderPropertiesDialog::setupNodeInfoWidget(QWidget *p_parent)
|
||||||
{
|
{
|
||||||
m_infoWidget = new NodeInfoWidget(m_node, 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;
|
bool valid = true;
|
||||||
QString msg;
|
QString msg;
|
||||||
@ -50,7 +47,7 @@ void FolderPropertiesDialog::validateInputs()
|
|||||||
valid = valid && validateNameInput(msg);
|
valid = valid && validateNameInput(msg);
|
||||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||||
: ScrollDialog::InformationLevel::Error);
|
: ScrollDialog::InformationLevel::Error);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FolderPropertiesDialog::validateNameInput(QString &p_msg)
|
bool FolderPropertiesDialog::validateNameInput(QString &p_msg)
|
||||||
@ -74,7 +71,7 @@ bool FolderPropertiesDialog::validateNameInput(QString &p_msg)
|
|||||||
|
|
||||||
void FolderPropertiesDialog::acceptedButtonClicked()
|
void FolderPropertiesDialog::acceptedButtonClicked()
|
||||||
{
|
{
|
||||||
if (saveFolderProperties()) {
|
if (validateInputs() && saveFolderProperties()) {
|
||||||
accept();
|
accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,6 @@ namespace vnotex
|
|||||||
protected:
|
protected:
|
||||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void validateInputs();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI();
|
void setupUI();
|
||||||
|
|
||||||
@ -29,6 +26,8 @@ namespace vnotex
|
|||||||
|
|
||||||
bool saveFolderProperties();
|
bool saveFolderProperties();
|
||||||
|
|
||||||
|
bool validateInputs();
|
||||||
|
|
||||||
NodeInfoWidget *m_infoWidget = nullptr;
|
NodeInfoWidget *m_infoWidget = nullptr;
|
||||||
|
|
||||||
Node *m_node = nullptr;
|
Node *m_node = nullptr;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "notebookmgr.h"
|
#include "notebookmgr.h"
|
||||||
#include "../messageboxhelper.h"
|
#include "../messageboxhelper.h"
|
||||||
#include <utils/iconutils.h>
|
#include <utils/iconutils.h>
|
||||||
|
#include <utils/utils.h>
|
||||||
#include <utils/widgetutils.h>
|
#include <utils/widgetutils.h>
|
||||||
#include "../widgetsfactory.h"
|
#include "../widgetsfactory.h"
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
@ -212,12 +213,38 @@ void ManageNotebooksDialog::setChangesUnsaved(bool p_unsaved)
|
|||||||
setButtonEnabled(QDialogButtonBox::Reset, m_changesUnsaved);
|
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()
|
bool ManageNotebooksDialog::saveChangesToNotebook()
|
||||||
{
|
{
|
||||||
if (!m_changesUnsaved || !m_notebook) {
|
if (!m_changesUnsaved || !m_notebook) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!validateInputs()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_notebook->updateName(m_notebookInfoWidget->getName());
|
m_notebook->updateName(m_notebookInfoWidget->getName());
|
||||||
m_notebook->updateDescription(m_notebookInfoWidget->getDescription());
|
m_notebook->updateDescription(m_notebookInfoWidget->getDescription());
|
||||||
return true;
|
return true;
|
||||||
|
@ -51,6 +51,10 @@ namespace vnotex
|
|||||||
|
|
||||||
bool checkUnsavedChanges();
|
bool checkUnsavedChanges();
|
||||||
|
|
||||||
|
bool validateInputs();
|
||||||
|
|
||||||
|
bool validateNameInput(QString &p_msg);
|
||||||
|
|
||||||
QListWidget *m_notebookList = nullptr;
|
QListWidget *m_notebookList = nullptr;
|
||||||
|
|
||||||
NotebookInfoWidget *m_notebookInfoWidget = nullptr;
|
NotebookInfoWidget *m_notebookInfoWidget = nullptr;
|
||||||
|
@ -26,7 +26,6 @@ void NewFolderDialog::setupUI(const Node *p_node)
|
|||||||
setCentralWidget(m_infoWidget);
|
setCentralWidget(m_infoWidget);
|
||||||
|
|
||||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
|
||||||
|
|
||||||
setWindowTitle(tr("New Folder"));
|
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)
|
void NewFolderDialog::setupNodeInfoWidget(const Node *p_node, QWidget *p_parent)
|
||||||
{
|
{
|
||||||
m_infoWidget = new NodeInfoWidget(p_node, Node::Flag::Container, 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;
|
bool valid = true;
|
||||||
QString msg;
|
QString msg;
|
||||||
@ -46,7 +43,7 @@ void NewFolderDialog::validateInputs()
|
|||||||
valid = valid && validateNameInput(msg);
|
valid = valid && validateNameInput(msg);
|
||||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||||
: ScrollDialog::InformationLevel::Error);
|
: ScrollDialog::InformationLevel::Error);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewFolderDialog::validateNameInput(QString &p_msg)
|
bool NewFolderDialog::validateNameInput(QString &p_msg)
|
||||||
@ -69,7 +66,7 @@ bool NewFolderDialog::validateNameInput(QString &p_msg)
|
|||||||
|
|
||||||
void NewFolderDialog::acceptedButtonClicked()
|
void NewFolderDialog::acceptedButtonClicked()
|
||||||
{
|
{
|
||||||
if (newFolder()) {
|
if (validateInputs() && newFolder()) {
|
||||||
accept();
|
accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,6 @@ namespace vnotex
|
|||||||
protected:
|
protected:
|
||||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void validateInputs();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI(const Node *p_node);
|
void setupUI(const Node *p_node);
|
||||||
|
|
||||||
@ -32,6 +29,8 @@ namespace vnotex
|
|||||||
|
|
||||||
bool newFolder();
|
bool newFolder();
|
||||||
|
|
||||||
|
bool validateInputs();
|
||||||
|
|
||||||
NodeInfoWidget *m_infoWidget = nullptr;
|
NodeInfoWidget *m_infoWidget = nullptr;
|
||||||
|
|
||||||
QSharedPointer<Node> m_newNode;
|
QSharedPointer<Node> m_newNode;
|
||||||
|
@ -29,7 +29,6 @@ void NewNotebookDialog::setupUI()
|
|||||||
setCentralWidget(m_infoWidget);
|
setCentralWidget(m_infoWidget);
|
||||||
|
|
||||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
|
||||||
|
|
||||||
setWindowTitle(tr("New Notebook"));
|
setWindowTitle(tr("New Notebook"));
|
||||||
}
|
}
|
||||||
@ -39,8 +38,6 @@ void NewNotebookDialog::setupNotebookInfoWidget(QWidget *p_parent)
|
|||||||
m_infoWidget = new NotebookInfoWidget(NotebookInfoWidget::Create, p_parent);
|
m_infoWidget = new NotebookInfoWidget(NotebookInfoWidget::Create, p_parent);
|
||||||
connect(m_infoWidget, &NotebookInfoWidget::rootFolderEdited,
|
connect(m_infoWidget, &NotebookInfoWidget::rootFolderEdited,
|
||||||
this, &NewNotebookDialog::handleRootFolderPathChanged);
|
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.");
|
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;
|
bool valid = true;
|
||||||
QString msg;
|
QString msg;
|
||||||
@ -59,7 +56,7 @@ void NewNotebookDialog::validateInputs()
|
|||||||
|
|
||||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||||
: ScrollDialog::InformationLevel::Error);
|
: ScrollDialog::InformationLevel::Error);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewNotebookDialog::validateNameInput(QString &p_msg)
|
bool NewNotebookDialog::validateNameInput(QString &p_msg)
|
||||||
@ -113,7 +110,7 @@ bool NewNotebookDialog::validateRootFolderInput(QString &p_msg)
|
|||||||
|
|
||||||
void NewNotebookDialog::acceptedButtonClicked()
|
void NewNotebookDialog::acceptedButtonClicked()
|
||||||
{
|
{
|
||||||
if (newNotebook()) {
|
if (validateInputs() && newNotebook()) {
|
||||||
accept();
|
accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,13 @@ namespace vnotex
|
|||||||
|
|
||||||
NotebookInfoWidget *m_infoWidget = nullptr;
|
NotebookInfoWidget *m_infoWidget = nullptr;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void validateInputs();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI();
|
void setupUI();
|
||||||
|
|
||||||
void setupNotebookInfoWidget(QWidget *p_parent = nullptr);
|
void setupNotebookInfoWidget(QWidget *p_parent = nullptr);
|
||||||
|
|
||||||
|
bool validateInputs();
|
||||||
|
|
||||||
bool validateNameInput(QString &p_msg);
|
bool validateNameInput(QString &p_msg);
|
||||||
|
|
||||||
// Create a new notebook.
|
// Create a new notebook.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "nodeinfowidget.h"
|
#include "nodeinfowidget.h"
|
||||||
#include <utils/widgetutils.h>
|
#include <utils/widgetutils.h>
|
||||||
#include <core/templatemgr.h>
|
#include <core/templatemgr.h>
|
||||||
|
#include <snippet/snippetmgr.h>
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -62,7 +63,6 @@ void NewNoteDialog::setupUI(const Node *p_node)
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, false);
|
|
||||||
|
|
||||||
setWindowTitle(tr("New Note"));
|
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)
|
void NewNoteDialog::setupNodeInfoWidget(const Node *p_node, QWidget *p_parent)
|
||||||
{
|
{
|
||||||
m_infoWidget = new NodeInfoWidget(p_node, Node::Flag::Content, 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;
|
bool valid = true;
|
||||||
QString msg;
|
QString msg;
|
||||||
@ -82,7 +80,7 @@ void NewNoteDialog::validateInputs()
|
|||||||
valid = valid && validateNameInput(msg);
|
valid = valid && validateNameInput(msg);
|
||||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||||
: ScrollDialog::InformationLevel::Error);
|
: ScrollDialog::InformationLevel::Error);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewNoteDialog::validateNameInput(QString &p_msg)
|
bool NewNoteDialog::validateNameInput(QString &p_msg)
|
||||||
@ -90,8 +88,8 @@ bool NewNoteDialog::validateNameInput(QString &p_msg)
|
|||||||
p_msg.clear();
|
p_msg.clear();
|
||||||
|
|
||||||
auto name = m_infoWidget->getName();
|
auto name = m_infoWidget->getName();
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty() || !PathUtils::isLegalFileName(name)) {
|
||||||
p_msg = tr("Please specify a name for the note.");
|
p_msg = tr("Please specify a valid name for the note.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +105,7 @@ void NewNoteDialog::acceptedButtonClicked()
|
|||||||
{
|
{
|
||||||
s_lastTemplate = m_templateComboBox->currentData().toString();
|
s_lastTemplate = m_templateComboBox->currentData().toString();
|
||||||
|
|
||||||
if (newNote()) {
|
if (validateInputs() && newNote()) {
|
||||||
accept();
|
accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,8 +149,6 @@ void NewNoteDialog::initDefaultValues(const Node *p_node)
|
|||||||
QStringLiteral("md"));
|
QStringLiteral("md"));
|
||||||
lineEdit->setText(defaultName);
|
lineEdit->setText(defaultName);
|
||||||
WidgetUtils::selectBaseName(lineEdit);
|
WidgetUtils::selectBaseName(lineEdit);
|
||||||
|
|
||||||
validateInputs();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +205,9 @@ void NewNoteDialog::setupTemplateComboBox(QWidget *p_parent)
|
|||||||
|
|
||||||
QString NewNoteDialog::getTemplateContent() const
|
QString NewNoteDialog::getTemplateContent() const
|
||||||
{
|
{
|
||||||
// TODO: parse snippets of the template.
|
int cursorOffset = 0;
|
||||||
return m_templateContent;
|
return SnippetMgr::getInst().applySnippetBySymbol(m_templateContent,
|
||||||
|
QString(),
|
||||||
|
cursorOffset,
|
||||||
|
SnippetMgr::generateOverrides(m_infoWidget->getName()));
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,6 @@ namespace vnotex
|
|||||||
protected:
|
protected:
|
||||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void validateInputs();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI(const Node *p_node);
|
void setupUI(const Node *p_node);
|
||||||
|
|
||||||
@ -34,6 +31,8 @@ namespace vnotex
|
|||||||
|
|
||||||
void setupTemplateComboBox(QWidget *p_parent);
|
void setupTemplateComboBox(QWidget *p_parent);
|
||||||
|
|
||||||
|
bool validateInputs();
|
||||||
|
|
||||||
bool validateNameInput(QString &p_msg);
|
bool validateNameInput(QString &p_msg);
|
||||||
|
|
||||||
bool newNote();
|
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 "nodelabelwithupbutton.h"
|
||||||
#include <utils/widgetutils.h>
|
#include <utils/widgetutils.h>
|
||||||
#include <buffer/filetypehelper.h>
|
#include <buffer/filetypehelper.h>
|
||||||
|
#include "../lineeditwithsnippet.h"
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ void NodeInfoWidget::setupUI(const Node *p_parentNode, Node::Flags p_newNodeFlag
|
|||||||
|
|
||||||
void NodeInfoWidget::setupNameLineEdit(QWidget *p_parent)
|
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),
|
auto validator = new QRegularExpressionValidator(QRegularExpression(PathUtils::c_fileNameRegularExpression),
|
||||||
m_nameLineEdit);
|
m_nameLineEdit);
|
||||||
m_nameLineEdit->setValidator(validator);
|
m_nameLineEdit->setValidator(validator);
|
||||||
@ -107,7 +108,7 @@ QLineEdit *NodeInfoWidget::getNameLineEdit() const
|
|||||||
|
|
||||||
QString NodeInfoWidget::getName() const
|
QString NodeInfoWidget::getName() const
|
||||||
{
|
{
|
||||||
return getNameLineEdit()->text().trimmed();
|
return m_nameLineEdit->evaluatedText().trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Notebook *NodeInfoWidget::getNotebook() const
|
const Notebook *NodeInfoWidget::getNotebook() const
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "notebook/node.h"
|
#include <notebook/node.h>
|
||||||
|
|
||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
@ -14,6 +14,7 @@ namespace vnotex
|
|||||||
{
|
{
|
||||||
class Notebook;
|
class Notebook;
|
||||||
class NodeLabelWithUpButton;
|
class NodeLabelWithUpButton;
|
||||||
|
class LineEditWithSnippet;
|
||||||
|
|
||||||
class NodeInfoWidget : public QWidget
|
class NodeInfoWidget : public QWidget
|
||||||
{
|
{
|
||||||
@ -50,13 +51,13 @@ namespace vnotex
|
|||||||
|
|
||||||
void setNode(const Node *p_node);
|
void setNode(const Node *p_node);
|
||||||
|
|
||||||
Mode m_mode;
|
Mode m_mode = Mode::Create;
|
||||||
|
|
||||||
QFormLayout *m_mainLayout = nullptr;
|
QFormLayout *m_mainLayout = nullptr;
|
||||||
|
|
||||||
QComboBox *m_fileTypeComboBox = nullptr;
|
QComboBox *m_fileTypeComboBox = nullptr;
|
||||||
|
|
||||||
QLineEdit *m_nameLineEdit = nullptr;
|
LineEditWithSnippet *m_nameLineEdit = nullptr;
|
||||||
|
|
||||||
NodeLabelWithUpButton *m_parentNodeLabel = nullptr;
|
NodeLabelWithUpButton *m_parentNodeLabel = nullptr;
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <utils/pathutils.h>
|
#include <utils/pathutils.h>
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
#include <utils/widgetutils.h>
|
#include <utils/widgetutils.h>
|
||||||
|
#include "../lineeditwithsnippet.h"
|
||||||
|
|
||||||
using namespace vnotex;
|
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"));
|
m_nameLineEdit->setPlaceholderText(tr("Name of notebook"));
|
||||||
connect(m_nameLineEdit, &QLineEdit::textEdited,
|
connect(m_nameLineEdit, &QLineEdit::textEdited,
|
||||||
this, &NotebookInfoWidget::basicInfoEdited);
|
this, &NotebookInfoWidget::basicInfoEdited);
|
||||||
@ -312,7 +313,7 @@ QComboBox *NotebookInfoWidget::getBackendComboBox() const
|
|||||||
|
|
||||||
QString NotebookInfoWidget::getName() const
|
QString NotebookInfoWidget::getName() const
|
||||||
{
|
{
|
||||||
return getNameLineEdit()->text().trimmed();
|
return m_nameLineEdit->evaluatedText().trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString NotebookInfoWidget::getDescription() const
|
QString NotebookInfoWidget::getDescription() const
|
||||||
|
@ -11,6 +11,7 @@ class QGroupBox;
|
|||||||
namespace vnotex
|
namespace vnotex
|
||||||
{
|
{
|
||||||
class Notebook;
|
class Notebook;
|
||||||
|
class LineEditWithSnippet;
|
||||||
|
|
||||||
class NotebookInfoWidget : public QWidget
|
class NotebookInfoWidget : public QWidget
|
||||||
{
|
{
|
||||||
@ -92,7 +93,7 @@ namespace vnotex
|
|||||||
|
|
||||||
const Notebook *m_notebook = nullptr;
|
const Notebook *m_notebook = nullptr;
|
||||||
|
|
||||||
QLineEdit *m_nameLineEdit = nullptr;
|
LineEditWithSnippet *m_nameLineEdit = nullptr;
|
||||||
|
|
||||||
QLineEdit *m_descriptionLineEdit = nullptr;
|
QLineEdit *m_descriptionLineEdit = nullptr;
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#include "notepropertiesdialog.h"
|
#include "notepropertiesdialog.h"
|
||||||
|
|
||||||
#include <QtWidgets>
|
|
||||||
|
|
||||||
#include "notebook/notebook.h"
|
#include "notebook/notebook.h"
|
||||||
#include "notebook/node.h"
|
#include "notebook/node.h"
|
||||||
#include "../widgetsfactory.h"
|
#include "../widgetsfactory.h"
|
||||||
@ -32,19 +30,16 @@ void NotePropertiesDialog::setupUI()
|
|||||||
setCentralWidget(m_infoWidget);
|
setCentralWidget(m_infoWidget);
|
||||||
|
|
||||||
setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
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)
|
void NotePropertiesDialog::setupNodeInfoWidget(QWidget *p_parent)
|
||||||
{
|
{
|
||||||
m_infoWidget = new NodeInfoWidget(m_node, 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;
|
bool valid = true;
|
||||||
QString msg;
|
QString msg;
|
||||||
@ -52,7 +47,7 @@ void NotePropertiesDialog::validateInputs()
|
|||||||
valid = valid && validateNameInput(msg);
|
valid = valid && validateNameInput(msg);
|
||||||
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
setInformationText(msg, valid ? ScrollDialog::InformationLevel::Info
|
||||||
: ScrollDialog::InformationLevel::Error);
|
: ScrollDialog::InformationLevel::Error);
|
||||||
setButtonEnabled(QDialogButtonBox::Ok, valid);
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotePropertiesDialog::validateNameInput(QString &p_msg)
|
bool NotePropertiesDialog::validateNameInput(QString &p_msg)
|
||||||
@ -60,8 +55,8 @@ bool NotePropertiesDialog::validateNameInput(QString &p_msg)
|
|||||||
p_msg.clear();
|
p_msg.clear();
|
||||||
|
|
||||||
auto name = m_infoWidget->getName();
|
auto name = m_infoWidget->getName();
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty() || !PathUtils::isLegalFileName(name)) {
|
||||||
p_msg = tr("Please specify a name for the note.");
|
p_msg = tr("Please specify a valid name for the note.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +71,7 @@ bool NotePropertiesDialog::validateNameInput(QString &p_msg)
|
|||||||
|
|
||||||
void NotePropertiesDialog::acceptedButtonClicked()
|
void NotePropertiesDialog::acceptedButtonClicked()
|
||||||
{
|
{
|
||||||
if (saveNoteProperties()) {
|
if (validateInputs() && saveNoteProperties()) {
|
||||||
accept();
|
accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,6 @@ namespace vnotex
|
|||||||
protected:
|
protected:
|
||||||
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
void acceptedButtonClicked() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void validateInputs();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI();
|
void setupUI();
|
||||||
|
|
||||||
@ -29,6 +26,8 @@ namespace vnotex
|
|||||||
|
|
||||||
bool saveNoteProperties();
|
bool saveNoteProperties();
|
||||||
|
|
||||||
|
bool validateInputs();
|
||||||
|
|
||||||
NodeInfoWidget *m_infoWidget = nullptr;
|
NodeInfoWidget *m_infoWidget = nullptr;
|
||||||
|
|
||||||
Node *m_node = nullptr;
|
Node *m_node = nullptr;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
#include <widgets/widgetsfactory.h>
|
#include <widgets/widgetsfactory.h>
|
||||||
#include <core/sessionconfig.h>
|
#include <core/sessionconfig.h>
|
||||||
@ -79,6 +80,15 @@ QGroupBox *QuickAccessPage::setupFlashPageGroup()
|
|||||||
addSearchItem(label, m_flashPageInput->toolTip(), m_flashPageInput);
|
addSearchItem(label, m_flashPageInput->toolTip(), m_flashPageInput);
|
||||||
connect(m_flashPageInput, &LocationInputWithBrowseButton::textChanged,
|
connect(m_flashPageInput, &LocationInputWithBrowseButton::textChanged,
|
||||||
this, &QuickAccessPage::pageIsChanged);
|
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;
|
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 <core/vnotex.h>
|
||||||
#include <utils/iconutils.h>
|
#include <utils/iconutils.h>
|
||||||
|
#include <utils/widgetutils.h>
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -29,8 +30,7 @@ LocationList::LocationList(QWidget *p_parent)
|
|||||||
void LocationList::setupUI()
|
void LocationList::setupUI()
|
||||||
{
|
{
|
||||||
auto mainLayout = new QVBoxLayout(this);
|
auto mainLayout = new QVBoxLayout(this);
|
||||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
WidgetUtils::setContentsMargins(mainLayout);
|
||||||
mainLayout->setSpacing(0);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
setupTitleBar(QString(), this);
|
setupTitleBar(QString(), this);
|
||||||
@ -96,6 +96,7 @@ NavigationModeWrapper<QTreeWidget, QTreeWidgetItem> *LocationList::getNavigation
|
|||||||
void LocationList::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
void LocationList::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
||||||
{
|
{
|
||||||
m_titleBar = new TitleBar(p_title, true, TitleBar::Action::None, 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"));
|
auto clearBtn = m_titleBar->addActionButton(QStringLiteral("clear.svg"), tr("Clear"));
|
||||||
@ -175,6 +176,6 @@ void LocationList::updateItemsCountLabel()
|
|||||||
if (cnt == 0) {
|
if (cnt == 0) {
|
||||||
m_titleBar->setInfoLabel("");
|
m_titleBar->setInfoLabel("");
|
||||||
} else {
|
} 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 "titletoolbar.h"
|
||||||
#include "locationlist.h"
|
#include "locationlist.h"
|
||||||
#include "searchpanel.h"
|
#include "searchpanel.h"
|
||||||
|
#include "snippetpanel.h"
|
||||||
#include <notebook/notebook.h>
|
#include <notebook/notebook.h>
|
||||||
#include "searchinfoprovider.h"
|
#include "searchinfoprovider.h"
|
||||||
#include <vtextedit/spellchecker.h>
|
#include <vtextedit/spellchecker.h>
|
||||||
@ -84,14 +85,15 @@ void MainWindow::kickOffOnStart(const QStringList &p_paths)
|
|||||||
|
|
||||||
VNoteX::getInst().initLoad();
|
VNoteX::getInst().initLoad();
|
||||||
|
|
||||||
|
setupSpellCheck();
|
||||||
|
|
||||||
|
// Do necessary stuffs before emitting this signal.
|
||||||
emit mainWindowStarted();
|
emit mainWindowStarted();
|
||||||
|
|
||||||
emit layoutChanged();
|
emit layoutChanged();
|
||||||
|
|
||||||
demoWidget();
|
demoWidget();
|
||||||
|
|
||||||
setupSpellCheck();
|
|
||||||
|
|
||||||
openFiles(p_paths);
|
openFiles(p_paths);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -206,6 +208,8 @@ void MainWindow::setupDocks()
|
|||||||
|
|
||||||
setupSearchDock();
|
setupSearchDock();
|
||||||
|
|
||||||
|
setupSnippetDock();
|
||||||
|
|
||||||
for (int i = 1; i < m_docks.size(); ++i) {
|
for (int i = 1; i < m_docks.size(); ++i) {
|
||||||
tabifyDockWidget(m_docks[i - 1], m_docks[i]);
|
tabifyDockWidget(m_docks[i - 1], m_docks[i]);
|
||||||
}
|
}
|
||||||
@ -295,6 +299,34 @@ void MainWindow::setupSearchPanel()
|
|||||||
m_searchPanel->setObjectName("SearchPanel.vnotex");
|
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()
|
void MainWindow::setupLocationListDock()
|
||||||
{
|
{
|
||||||
auto dock = new QDockWidget(tr("Location List"), this);
|
auto dock = new QDockWidget(tr("Location List"), this);
|
||||||
@ -418,7 +450,7 @@ void MainWindow::saveStateAndGeometry()
|
|||||||
SessionConfig::MainWindowStateGeometry sg;
|
SessionConfig::MainWindowStateGeometry sg;
|
||||||
sg.m_mainState = saveState();
|
sg.m_mainState = saveState();
|
||||||
sg.m_mainGeometry = saveGeometry();
|
sg.m_mainGeometry = saveGeometry();
|
||||||
sg.m_docksVisibilityBeforeExpand = m_docksVisibilityBeforeExpand;
|
sg.m_visibleDocksBeforeExpand = m_visibleDocksBeforeExpand;
|
||||||
|
|
||||||
auto& sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
auto& sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||||
sessionConfig.setMainWindowStateGeometry(sg);
|
sessionConfig.setMainWindowStateGeometry(sg);
|
||||||
@ -439,12 +471,13 @@ void MainWindow::loadStateAndGeometry(bool p_stateOnly)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!p_stateOnly) {
|
if (!p_stateOnly) {
|
||||||
m_docksVisibilityBeforeExpand = sg.m_docksVisibilityBeforeExpand;
|
m_visibleDocksBeforeExpand = sg.m_visibleDocksBeforeExpand;
|
||||||
if (m_docksVisibilityBeforeExpand.isEmpty()) {
|
if (m_visibleDocksBeforeExpand.isEmpty()) {
|
||||||
// Init.
|
// Init (or init again if there is no visible dock).
|
||||||
m_docksVisibilityBeforeExpand.resize(m_docks.size());
|
|
||||||
for (int i = 0; i < m_docks.size(); ++i) {
|
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) {
|
if (p_expanded) {
|
||||||
// Store the state and hide.
|
// Store the state and hide.
|
||||||
|
m_visibleDocksBeforeExpand.clear();
|
||||||
for (int i = 0; i < m_docks.size(); ++i) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,15 +518,15 @@ void MainWindow::setContentAreaExpanded(bool p_expanded)
|
|||||||
// Restore the state.
|
// Restore the state.
|
||||||
bool hasVisible = false;
|
bool hasVisible = false;
|
||||||
for (int i = 0; i < m_docks.size(); ++i) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_docksVisibilityBeforeExpand[i]) {
|
const bool visible = m_visibleDocksBeforeExpand.contains(objName);
|
||||||
hasVisible = true;
|
hasVisible = hasVisible || visible;
|
||||||
}
|
|
||||||
|
|
||||||
m_docks[i]->setVisible(m_docksVisibilityBeforeExpand[i]);
|
m_docks[i]->setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasVisible) {
|
if (!hasVisible) {
|
||||||
@ -606,6 +643,9 @@ void MainWindow::setupShortcuts()
|
|||||||
|
|
||||||
setupDockActivateShortcut(m_docks[DockIndex::LocationListDock],
|
setupDockActivateShortcut(m_docks[DockIndex::LocationListDock],
|
||||||
coreConfig.getShortcut(CoreConfig::Shortcut::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)
|
void MainWindow::setupDockActivateShortcut(QDockWidget *p_dock, const QString &p_keys)
|
||||||
|
@ -22,6 +22,7 @@ namespace vnotex
|
|||||||
class OutlineViewer;
|
class OutlineViewer;
|
||||||
class LocationList;
|
class LocationList;
|
||||||
class SearchPanel;
|
class SearchPanel;
|
||||||
|
class SnippetPanel;
|
||||||
|
|
||||||
enum { RESTART_EXIT_CODE = 1000 };
|
enum { RESTART_EXIT_CODE = 1000 };
|
||||||
|
|
||||||
@ -96,6 +97,7 @@ namespace vnotex
|
|||||||
NavigationDock = 0,
|
NavigationDock = 0,
|
||||||
OutlineDock,
|
OutlineDock,
|
||||||
SearchDock,
|
SearchDock,
|
||||||
|
SnippetDock,
|
||||||
LocationListDock
|
LocationListDock
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,6 +119,10 @@ namespace vnotex
|
|||||||
|
|
||||||
void setupLocationList();
|
void setupLocationList();
|
||||||
|
|
||||||
|
void setupSnippetDock();
|
||||||
|
|
||||||
|
void setupSnippetPanel();
|
||||||
|
|
||||||
void setupNotebookExplorer(QWidget *p_parent = nullptr);
|
void setupNotebookExplorer(QWidget *p_parent = nullptr);
|
||||||
|
|
||||||
void setupDocks();
|
void setupDocks();
|
||||||
@ -168,6 +174,8 @@ namespace vnotex
|
|||||||
|
|
||||||
SearchPanel *m_searchPanel = nullptr;
|
SearchPanel *m_searchPanel = nullptr;
|
||||||
|
|
||||||
|
SnippetPanel *m_snippetPanel = nullptr;
|
||||||
|
|
||||||
QVector<QDockWidget *> m_docks;
|
QVector<QDockWidget *> m_docks;
|
||||||
|
|
||||||
bool m_layoutReset = false;
|
bool m_layoutReset = false;
|
||||||
@ -184,7 +192,7 @@ namespace vnotex
|
|||||||
|
|
||||||
QTimer *m_tipsTimer = nullptr;
|
QTimer *m_tipsTimer = nullptr;
|
||||||
|
|
||||||
QBitArray m_docksVisibilityBeforeExpand;
|
QStringList m_visibleDocksBeforeExpand;
|
||||||
};
|
};
|
||||||
} // ns vnotex
|
} // ns vnotex
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "editors/statuswidget.h"
|
#include "editors/statuswidget.h"
|
||||||
#include "editors/plantumlhelper.h"
|
#include "editors/plantumlhelper.h"
|
||||||
#include "editors/graphvizhelper.h"
|
#include "editors/graphvizhelper.h"
|
||||||
|
#include <snippet/snippetmgr.h>
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -975,3 +976,16 @@ void MarkdownViewWindow::setupPreviewHelper()
|
|||||||
markdownEditorConfig.getPlantUmlCommand());
|
markdownEditorConfig.getPlantUmlCommand());
|
||||||
GraphvizHelper::getInst().init(markdownEditorConfig.getGraphvizExe());
|
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;
|
ViewWindowSession saveSession() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void applySnippet(const QString &p_name) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleEditorConfigChange() Q_DECL_OVERRIDE;
|
void handleEditorConfigChange() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
@ -105,6 +105,7 @@ TitleBar *NotebookExplorer::setupTitleBar(QWidget *p_parent)
|
|||||||
TitleBar::Action::Menu,
|
TitleBar::Action::Menu,
|
||||||
p_parent);
|
p_parent);
|
||||||
titleBar->setWhatsThis(tr("This title bar contains buttons and menu to manage notebooks and notes."));
|
titleBar->setWhatsThis(tr("This title bar contains buttons and menu to manage notebooks and notes."));
|
||||||
|
titleBar->setActionButtonsAlwaysShown(true);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto viewMenu = WidgetsFactory::createMenu(titleBar);
|
auto viewMenu = WidgetsFactory::createMenu(titleBar);
|
||||||
|
@ -90,6 +90,7 @@ NavigationModeWrapper<QTreeWidget, QTreeWidgetItem> *OutlineViewer::getNavigatio
|
|||||||
TitleBar *OutlineViewer::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
TitleBar *OutlineViewer::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
||||||
{
|
{
|
||||||
auto titleBar = new TitleBar(p_title, false, TitleBar::Action::None, 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"));
|
auto decreaseBtn = titleBar->addActionButton(QStringLiteral("decrease_outline_level.svg"), tr("Decrease Expansion Level"));
|
||||||
connect(decreaseBtn, &QToolButton::clicked,
|
connect(decreaseBtn, &QToolButton::clicked,
|
||||||
|
@ -47,7 +47,7 @@ namespace vnotex
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit SearchPanel(const QSharedPointer<ISearchInfoProvider> &p_provider, QWidget *p_parent = nullptr);
|
SearchPanel(const QSharedPointer<ISearchInfoProvider> &p_provider, QWidget *p_parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void startSearch();
|
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 <core/thememgr.h>
|
||||||
#include "editors/statuswidget.h"
|
#include "editors/statuswidget.h"
|
||||||
#include <core/fileopenparameters.h>
|
#include <core/fileopenparameters.h>
|
||||||
|
#include <snippet/snippetmgr.h>
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -253,3 +254,16 @@ ViewWindowSession TextViewWindow::saveSession() const
|
|||||||
}
|
}
|
||||||
return session;
|
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;
|
ViewWindowSession saveSession() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void applySnippet(const QString &p_name) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleEditorConfigChange() Q_DECL_OVERRIDE;
|
void handleEditorConfigChange() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ QToolButton *TitleBar::addActionButton(const QString &p_iconName, const QString
|
|||||||
connect(p_menu, &QMenu::aboutToHide,
|
connect(p_menu, &QMenu::aboutToHide,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
m_actionButtonsForcedShown = false;
|
m_actionButtonsForcedShown = false;
|
||||||
setActionButtonsVisible(false);
|
setActionButtonsVisible(m_actionButtonsAlwaysShown);
|
||||||
});
|
});
|
||||||
return btn;
|
return btn;
|
||||||
}
|
}
|
||||||
|
@ -72,13 +72,13 @@ namespace vnotex
|
|||||||
// Not all Workspace. Just all ViewSplits.
|
// Not all Workspace. Just all ViewSplits.
|
||||||
QList<Buffer *> getAllBuffersInViewSplits() const;
|
QList<Buffer *> getAllBuffersInViewSplits() const;
|
||||||
|
|
||||||
|
ViewWindow *getCurrentViewWindow() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void openBuffer(Buffer *p_buffer, const QSharedPointer<FileOpenParameters> &p_paras);
|
void openBuffer(Buffer *p_buffer, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||||
|
|
||||||
bool close(const Notebook *p_notebook, bool p_force);
|
bool close(const Notebook *p_notebook, bool p_force);
|
||||||
|
|
||||||
ViewWindow *getCurrentViewWindow() const;
|
|
||||||
|
|
||||||
void focus();
|
void focus();
|
||||||
|
|
||||||
// NavigationMode.
|
// NavigationMode.
|
||||||
|
@ -623,6 +623,16 @@ void ViewSplit::createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx) const
|
|||||||
WidgetUtils::addActionShortcutText(locateNodeAct,
|
WidgetUtils::addActionShortcutText(locateNodeAct,
|
||||||
ConfigMgr::getInst().getCoreConfig().getShortcut(CoreConfig::Shortcut::LocateNode));
|
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)
|
void ViewSplit::closeTab(int p_idx)
|
||||||
|
@ -84,6 +84,8 @@ namespace vnotex
|
|||||||
WindowFlags getWindowFlags() const;
|
WindowFlags getWindowFlags() const;
|
||||||
void setWindowFlags(WindowFlags p_flags);
|
void setWindowFlags(WindowFlags p_flags);
|
||||||
|
|
||||||
|
virtual void applySnippet(const QString &p_name) = 0;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void handleEditorConfigChange() = 0;
|
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)));
|
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/legacynotebookutils.cpp \
|
||||||
$$PWD/dialogs/linkinsertdialog.cpp \
|
$$PWD/dialogs/linkinsertdialog.cpp \
|
||||||
$$PWD/dialogs/newnotebookfromfolderdialog.cpp \
|
$$PWD/dialogs/newnotebookfromfolderdialog.cpp \
|
||||||
|
$$PWD/dialogs/newsnippetdialog.cpp \
|
||||||
$$PWD/dialogs/selectdialog.cpp \
|
$$PWD/dialogs/selectdialog.cpp \
|
||||||
$$PWD/dialogs/selectionitemwidget.cpp \
|
$$PWD/dialogs/selectionitemwidget.cpp \
|
||||||
$$PWD/dialogs/settings/appearancepage.cpp \
|
$$PWD/dialogs/settings/appearancepage.cpp \
|
||||||
@ -25,6 +26,8 @@ SOURCES += \
|
|||||||
$$PWD/dialogs/settings/settingsdialog.cpp \
|
$$PWD/dialogs/settings/settingsdialog.cpp \
|
||||||
$$PWD/dialogs/settings/texteditorpage.cpp \
|
$$PWD/dialogs/settings/texteditorpage.cpp \
|
||||||
$$PWD/dialogs/settings/themepage.cpp \
|
$$PWD/dialogs/settings/themepage.cpp \
|
||||||
|
$$PWD/dialogs/snippetinfowidget.cpp \
|
||||||
|
$$PWD/dialogs/snippetpropertiesdialog.cpp \
|
||||||
$$PWD/dialogs/sortdialog.cpp \
|
$$PWD/dialogs/sortdialog.cpp \
|
||||||
$$PWD/dialogs/tableinsertdialog.cpp \
|
$$PWD/dialogs/tableinsertdialog.cpp \
|
||||||
$$PWD/dragdropareaindicator.cpp \
|
$$PWD/dragdropareaindicator.cpp \
|
||||||
@ -47,6 +50,7 @@ SOURCES += \
|
|||||||
$$PWD/fullscreentoggleaction.cpp \
|
$$PWD/fullscreentoggleaction.cpp \
|
||||||
$$PWD/lineedit.cpp \
|
$$PWD/lineedit.cpp \
|
||||||
$$PWD/lineeditdelegate.cpp \
|
$$PWD/lineeditdelegate.cpp \
|
||||||
|
$$PWD/lineeditwithsnippet.cpp \
|
||||||
$$PWD/listwidget.cpp \
|
$$PWD/listwidget.cpp \
|
||||||
$$PWD/locationinputwithbrowsebutton.cpp \
|
$$PWD/locationinputwithbrowsebutton.cpp \
|
||||||
$$PWD/locationlist.cpp \
|
$$PWD/locationlist.cpp \
|
||||||
@ -60,6 +64,7 @@ SOURCES += \
|
|||||||
$$PWD/propertydefs.cpp \
|
$$PWD/propertydefs.cpp \
|
||||||
$$PWD/searchinfoprovider.cpp \
|
$$PWD/searchinfoprovider.cpp \
|
||||||
$$PWD/searchpanel.cpp \
|
$$PWD/searchpanel.cpp \
|
||||||
|
$$PWD/snippetpanel.cpp \
|
||||||
$$PWD/systemtrayhelper.cpp \
|
$$PWD/systemtrayhelper.cpp \
|
||||||
$$PWD/textviewwindow.cpp \
|
$$PWD/textviewwindow.cpp \
|
||||||
$$PWD/toolbarhelper.cpp \
|
$$PWD/toolbarhelper.cpp \
|
||||||
@ -112,6 +117,7 @@ HEADERS += \
|
|||||||
$$PWD/dialogs/legacynotebookutils.h \
|
$$PWD/dialogs/legacynotebookutils.h \
|
||||||
$$PWD/dialogs/linkinsertdialog.h \
|
$$PWD/dialogs/linkinsertdialog.h \
|
||||||
$$PWD/dialogs/newnotebookfromfolderdialog.h \
|
$$PWD/dialogs/newnotebookfromfolderdialog.h \
|
||||||
|
$$PWD/dialogs/newsnippetdialog.h \
|
||||||
$$PWD/dialogs/selectdialog.h \
|
$$PWD/dialogs/selectdialog.h \
|
||||||
$$PWD/dialogs/selectionitemwidget.h \
|
$$PWD/dialogs/selectionitemwidget.h \
|
||||||
$$PWD/dialogs/settings/appearancepage.h \
|
$$PWD/dialogs/settings/appearancepage.h \
|
||||||
@ -124,6 +130,8 @@ HEADERS += \
|
|||||||
$$PWD/dialogs/settings/settingsdialog.h \
|
$$PWD/dialogs/settings/settingsdialog.h \
|
||||||
$$PWD/dialogs/settings/texteditorpage.h \
|
$$PWD/dialogs/settings/texteditorpage.h \
|
||||||
$$PWD/dialogs/settings/themepage.h \
|
$$PWD/dialogs/settings/themepage.h \
|
||||||
|
$$PWD/dialogs/snippetinfowidget.h \
|
||||||
|
$$PWD/dialogs/snippetpropertiesdialog.h \
|
||||||
$$PWD/dialogs/sortdialog.h \
|
$$PWD/dialogs/sortdialog.h \
|
||||||
$$PWD/dialogs/tableinsertdialog.h \
|
$$PWD/dialogs/tableinsertdialog.h \
|
||||||
$$PWD/dragdropareaindicator.h \
|
$$PWD/dragdropareaindicator.h \
|
||||||
@ -146,6 +154,7 @@ HEADERS += \
|
|||||||
$$PWD/fullscreentoggleaction.h \
|
$$PWD/fullscreentoggleaction.h \
|
||||||
$$PWD/lineedit.h \
|
$$PWD/lineedit.h \
|
||||||
$$PWD/lineeditdelegate.h \
|
$$PWD/lineeditdelegate.h \
|
||||||
|
$$PWD/lineeditwithsnippet.h \
|
||||||
$$PWD/listwidget.h \
|
$$PWD/listwidget.h \
|
||||||
$$PWD/locationinputwithbrowsebutton.h \
|
$$PWD/locationinputwithbrowsebutton.h \
|
||||||
$$PWD/locationlist.h \
|
$$PWD/locationlist.h \
|
||||||
@ -160,6 +169,7 @@ HEADERS += \
|
|||||||
$$PWD/propertydefs.h \
|
$$PWD/propertydefs.h \
|
||||||
$$PWD/searchinfoprovider.h \
|
$$PWD/searchinfoprovider.h \
|
||||||
$$PWD/searchpanel.h \
|
$$PWD/searchpanel.h \
|
||||||
|
$$PWD/snippetpanel.h \
|
||||||
$$PWD/systemtrayhelper.h \
|
$$PWD/systemtrayhelper.h \
|
||||||
$$PWD/textviewwindow.h \
|
$$PWD/textviewwindow.h \
|
||||||
$$PWD/textviewwindowhelper.h \
|
$$PWD/textviewwindowhelper.h \
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
|
|
||||||
#include "lineedit.h"
|
#include "lineeditwithsnippet.h"
|
||||||
#include "combobox.h"
|
#include "combobox.h"
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
@ -40,6 +40,16 @@ QLineEdit *WidgetsFactory::createLineEdit(const QString &p_contents, QWidget *p_
|
|||||||
return new LineEdit(p_contents, p_parent);
|
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)
|
QComboBox *WidgetsFactory::createComboBox(QWidget *p_parent)
|
||||||
{
|
{
|
||||||
auto comboBox = new ComboBox(p_parent);
|
auto comboBox = new ComboBox(p_parent);
|
||||||
|
@ -16,6 +16,8 @@ class QRadioButton;
|
|||||||
|
|
||||||
namespace vnotex
|
namespace vnotex
|
||||||
{
|
{
|
||||||
|
class LineEditWithSnippet;
|
||||||
|
|
||||||
class WidgetsFactory
|
class WidgetsFactory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -29,6 +31,10 @@ namespace vnotex
|
|||||||
|
|
||||||
static QLineEdit *createLineEdit(const QString &p_contents, QWidget *p_parent = nullptr);
|
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 QComboBox *createComboBox(QWidget *p_parent = nullptr);
|
||||||
|
|
||||||
static QCheckBox *createCheckBox(const QString &p_text, 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/utils/utils.pri)
|
||||||
include($$SRC_FOLDER/export/export.pri)
|
include($$SRC_FOLDER/export/export.pri)
|
||||||
include($$SRC_FOLDER/search/search.pri)
|
include($$SRC_FOLDER/search/search.pri)
|
||||||
|
include($$SRC_FOLDER/snippet/snippet.pri)
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
test_notebook.cpp
|
test_notebook.cpp
|
||||||
|
Loading…
x
Reference in New Issue
Block a user