mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support snippets
Shortcuts are not supported yet.
This commit is contained in:
parent
7131b483f3
commit
6ac33d2bd0
296
src/dialog/veditsnippetdialog.cpp
Normal file
296
src/dialog/veditsnippetdialog.cpp
Normal file
@ -0,0 +1,296 @@
|
||||
#include "veditsnippetdialog.h"
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "utils/vutils.h"
|
||||
#include "vlineedit.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "utils/vmetawordmanager.h"
|
||||
|
||||
extern VMetaWordManager *g_mwMgr;
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VEditSnippetDialog::VEditSnippetDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const QVector<VSnippet> &p_snippets,
|
||||
const VSnippet &p_snippet,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent),
|
||||
m_snippets(p_snippets),
|
||||
m_snippet(p_snippet)
|
||||
{
|
||||
setupUI(p_title, p_info);
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VEditSnippetDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
infoLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
// Name.
|
||||
m_nameEdit = new VLineEdit(m_snippet.getName());
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
m_nameEdit);
|
||||
m_nameEdit->setValidator(validator);
|
||||
|
||||
// Type.
|
||||
m_typeCB = new QComboBox();
|
||||
for (int i = 0; i < VSnippet::Type::Invalid; ++i) {
|
||||
m_typeCB->addItem(VSnippet::typeStr(static_cast<VSnippet::Type>(i)), i);
|
||||
}
|
||||
|
||||
int typeIdx = m_typeCB->findData((int)m_snippet.getType());
|
||||
Q_ASSERT(typeIdx > -1);
|
||||
m_typeCB->setCurrentIndex(typeIdx);
|
||||
|
||||
// Shortcut.
|
||||
m_shortcutCB = new QComboBox();
|
||||
m_shortcutCB->addItem(tr("None"), QChar());
|
||||
auto shortcuts = getAvailableShortcuts();
|
||||
for (auto it : shortcuts) {
|
||||
m_shortcutCB->addItem(it, it);
|
||||
}
|
||||
|
||||
QChar sh = m_snippet.getShortcut();
|
||||
if (sh.isNull()) {
|
||||
m_shortcutCB->setCurrentIndex(0);
|
||||
} else {
|
||||
int shortcutIdx = m_shortcutCB->findData(sh);
|
||||
m_shortcutCB->setCurrentIndex(shortcutIdx < 0 ? 0 : shortcutIdx);
|
||||
}
|
||||
|
||||
// Cursor mark.
|
||||
m_cursorMarkEdit = new QLineEdit(m_snippet.getCursorMark());
|
||||
m_cursorMarkEdit->setToolTip(tr("String in the content to mark the cursor position"));
|
||||
|
||||
// Selection mark.
|
||||
m_selectionMarkEdit = new QLineEdit(m_snippet.getSelectionMark());
|
||||
m_selectionMarkEdit->setToolTip(tr("String in the content to be replaced with selected text"));
|
||||
|
||||
// Content.
|
||||
m_contentEdit = new QTextEdit();
|
||||
setContentEditByType();
|
||||
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
topLayout->addRow(tr("Snippet &name:"), m_nameEdit);
|
||||
topLayout->addRow(tr("Snippet &type:"), m_typeCB);
|
||||
topLayout->addRow(tr("Shortc&ut:"), m_shortcutCB);
|
||||
topLayout->addRow(tr("Cursor &mark:"), m_cursorMarkEdit);
|
||||
topLayout->addRow(tr("&Selection mark:"), m_selectionMarkEdit);
|
||||
topLayout->addRow(tr("&Content:"), m_contentEdit);
|
||||
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
m_nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(p_title);
|
||||
|
||||
connect(m_nameEdit, &QLineEdit::textChanged,
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
|
||||
connect(m_typeCB, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
|
||||
connect(m_cursorMarkEdit, &QLineEdit::textChanged,
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
|
||||
connect(m_selectionMarkEdit, &QLineEdit::textChanged,
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
|
||||
connect(m_contentEdit, &QTextEdit::textChanged,
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
}
|
||||
|
||||
void VEditSnippetDialog::handleInputChanged()
|
||||
{
|
||||
bool showWarnLabel = false;
|
||||
QString name = m_nameEdit->getEvaluatedText();
|
||||
bool nameOk = !name.isEmpty();
|
||||
if (nameOk && name != m_snippet.getName()) {
|
||||
// Check if the name conflicts with existing snippet name.
|
||||
// Case-insensitive.
|
||||
QString lowerName = name.toLower();
|
||||
bool conflicted = false;
|
||||
for (auto const & item : m_snippets) {
|
||||
if (item.getName() == name) {
|
||||
conflicted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString warnText;
|
||||
if (conflicted) {
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
} else if (!VUtils::checkFileNameLegal(name)) {
|
||||
// Check if evaluated name contains illegal characters.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> contains illegal characters "
|
||||
"(after magic word evaluation).")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
}
|
||||
|
||||
if (!nameOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
QString cursorMark = m_cursorMarkEdit->text();
|
||||
bool cursorMarkOk = true;
|
||||
if (nameOk && !cursorMark.isEmpty()) {
|
||||
// Check if the mark appears more than once in the content.
|
||||
QString selectionMark = m_selectionMarkEdit->text();
|
||||
QString content = getContentEditByType();
|
||||
content = g_mwMgr->evaluate(content);
|
||||
QString warnText;
|
||||
if (content.count(cursorMark) > 1) {
|
||||
cursorMarkOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Cursor mark <span style=\"%2\">%3</span> occurs more than once "
|
||||
"in the content (after magic word evaluation). "
|
||||
"Please choose another mark.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(cursorMark);
|
||||
} else if ((cursorMark == selectionMark
|
||||
|| cursorMark.contains(selectionMark)
|
||||
|| selectionMark.contains(cursorMark))
|
||||
&& !selectionMark.isEmpty()) {
|
||||
cursorMarkOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Cursor mark <span style=\"%2\">%3</span> conflicts with selection mark. "
|
||||
"Please choose another mark.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(cursorMark);
|
||||
}
|
||||
|
||||
if (!cursorMarkOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(showWarnLabel);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(nameOk && cursorMarkOk);
|
||||
}
|
||||
|
||||
void VEditSnippetDialog::setContentEditByType()
|
||||
{
|
||||
switch (m_snippet.getType()) {
|
||||
case VSnippet::Type::PlainText:
|
||||
m_contentEdit->setPlainText(m_snippet.getContent());
|
||||
break;
|
||||
|
||||
case VSnippet::Type::Html:
|
||||
m_contentEdit->setHtml(m_snippet.getContent());
|
||||
break;
|
||||
|
||||
default:
|
||||
m_contentEdit->setPlainText(m_snippet.getContent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getContentEditByType() const
|
||||
{
|
||||
if (m_typeCB->currentIndex() == -1) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
switch (static_cast<VSnippet::Type>(m_typeCB->currentData().toInt())) {
|
||||
case VSnippet::Type::PlainText:
|
||||
return m_contentEdit->toPlainText();
|
||||
|
||||
case VSnippet::Type::Html:
|
||||
return m_contentEdit->toHtml();
|
||||
|
||||
default:
|
||||
return m_contentEdit->toPlainText();
|
||||
}
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getNameInput() const
|
||||
{
|
||||
return m_nameEdit->getEvaluatedText();
|
||||
}
|
||||
|
||||
VSnippet::Type VEditSnippetDialog::getTypeInput() const
|
||||
{
|
||||
return static_cast<VSnippet::Type>(m_typeCB->currentData().toInt());
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getCursorMarkInput() const
|
||||
{
|
||||
return m_cursorMarkEdit->text();
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getSelectionMarkInput() const
|
||||
{
|
||||
return m_selectionMarkEdit->text();
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getContentInput() const
|
||||
{
|
||||
return getContentEditByType();
|
||||
}
|
||||
|
||||
QChar VEditSnippetDialog::getShortcutInput() const
|
||||
{
|
||||
return m_shortcutCB->currentData().toChar();
|
||||
}
|
||||
|
||||
QVector<QChar> VEditSnippetDialog::getAvailableShortcuts() const
|
||||
{
|
||||
QVector<QChar> ret = VSnippet::getAllShortcuts();
|
||||
|
||||
QChar curCh = m_snippet.getShortcut();
|
||||
// Remove those have already been assigned to snippets.
|
||||
for (auto const & snip : m_snippets) {
|
||||
QChar ch = snip.getShortcut();
|
||||
if (ch.isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch != curCh) {
|
||||
ret.removeOne(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
66
src/dialog/veditsnippetdialog.h
Normal file
66
src/dialog/veditsnippetdialog.h
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef VEDITSNIPPETDIALOG_H
|
||||
#define VEDITSNIPPETDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVector>
|
||||
|
||||
#include "vsnippet.h"
|
||||
|
||||
class VLineEdit;
|
||||
class QLineEdit;
|
||||
class QLabel;
|
||||
class QDialogButtonBox;
|
||||
class QComboBox;
|
||||
class QTextEdit;
|
||||
|
||||
|
||||
class VEditSnippetDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VEditSnippetDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const QVector<VSnippet> &p_snippets,
|
||||
const VSnippet &p_snippet,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
QString getNameInput() const;
|
||||
|
||||
VSnippet::Type getTypeInput() const;
|
||||
|
||||
QString getCursorMarkInput() const;
|
||||
|
||||
QString getSelectionMarkInput() const;
|
||||
|
||||
QString getContentInput() const;
|
||||
|
||||
QChar getShortcutInput() const;
|
||||
|
||||
private slots:
|
||||
void handleInputChanged();
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title, const QString &p_info);
|
||||
|
||||
void setContentEditByType();
|
||||
|
||||
QString getContentEditByType() const;
|
||||
|
||||
QVector<QChar> getAvailableShortcuts() const;
|
||||
|
||||
VLineEdit *m_nameEdit;
|
||||
QComboBox *m_typeCB;
|
||||
QComboBox *m_shortcutCB;
|
||||
QLineEdit *m_cursorMarkEdit;
|
||||
QLineEdit *m_selectionMarkEdit;
|
||||
QTextEdit *m_contentEdit;
|
||||
|
||||
QLabel *m_warnLabel;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
const QVector<VSnippet> &m_snippets;
|
||||
|
||||
const VSnippet &m_snippet;
|
||||
};
|
||||
|
||||
#endif // VEDITSNIPPETDIALOG_H
|
7
src/resources/icons/add_snippet.svg
Normal file
7
src/resources/icons/add_snippet.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<polygon points="448,224 288,224 288,64 224,64 224,224 64,224 64,288 224,288 224,448 288,448 288,288 448,288 "/>
|
||||
</svg>
|
After Width: | Height: | Size: 597 B |
16
src/resources/icons/apply_snippet.svg
Normal file
16
src/resources/icons/apply_snippet.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<polygon points="198.011,159.22 163.968,193.337 420.064,450 454,415.883 "/>
|
||||
<rect x="182" y="62" width="32" height="64"/>
|
||||
<rect x="182" y="266" width="32" height="64"/>
|
||||
<rect x="274" y="178" width="64" height="32"/>
|
||||
<polygon points="303.941,112.143 281.314,89.465 236.06,134.82 258.687,157.498 "/>
|
||||
<polygon points="92.06,112.143 137.314,157.498 159.941,134.82 114.687,89.465 "/>
|
||||
<polygon points="92.06,279.141 114.687,301.816 159.941,256.462 137.314,233.784 "/>
|
||||
<rect x="58" y="178" width="64" height="32"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1006 B |
10
src/resources/icons/delete_snippet.svg
Normal file
10
src/resources/icons/delete_snippet.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path d="M341,128V99c0-19.1-14.5-35-34.5-35H205.4C185.5,64,171,79.9,171,99v29H80v32h9.2c0,0,5.4,0.6,8.2,3.4c2.8,2.8,3.9,9,3.9,9
|
||||
l19,241.7c1.5,29.4,1.5,33.9,36,33.9h199.4c34.5,0,34.5-4.4,36-33.8l19-241.6c0,0,1.1-6.3,3.9-9.1c2.8-2.8,8.2-3.4,8.2-3.4h9.2v-32
|
||||
h-91V128z M192,99c0-9.6,7.8-15,17.7-15h91.7c9.9,0,18.6,5.5,18.6,15v29H192V99z M183.5,384l-10.3-192h20.3L204,384H183.5z
|
||||
M267.1,384h-22V192h22V384z M328.7,384h-20.4l10.5-192h20.3L328.7,384z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 934 B |
10
src/resources/icons/locate_snippet.svg
Normal file
10
src/resources/icons/locate_snippet.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||
<path d="M437.334,144H256.006l-42.668-48H74.666C51.197,96,32,115.198,32,138.667v234.666C32,396.802,51.197,416,74.666,416h362.668
|
||||
C460.803,416,480,396.802,480,373.333V186.667C480,163.198,460.803,144,437.334,144z M448,373.333
|
||||
c0,5.782-4.885,10.667-10.666,10.667H74.666C68.884,384,64,379.115,64,373.333V176h373.334c5.781,0,10.666,4.885,10.666,10.667
|
||||
V373.333z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 840 B |
10
src/resources/icons/snippet_info.svg
Normal file
10
src/resources/icons/snippet_info.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<polygon points="288,448 288,192 192,192 192,208 224,208 224,448 192,448 192,464 320,464 320,448 "/>
|
||||
<path d="M255.8,144.5c26.6,0,48.2-21.6,48.2-48.2s-21.6-48.2-48.2-48.2c-26.6,0-48.2,21.6-48.2,48.2S229.2,144.5,255.8,144.5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 723 B |
16
src/resources/icons/snippets.svg
Normal file
16
src/resources/icons/snippets.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<polygon points="198.011,159.22 163.968,193.337 420.064,450 454,415.883 "/>
|
||||
<rect x="182" y="62" width="32" height="64"/>
|
||||
<rect x="182" y="266" width="32" height="64"/>
|
||||
<rect x="274" y="178" width="64" height="32"/>
|
||||
<polygon points="303.941,112.143 281.314,89.465 236.06,134.82 258.687,157.498 "/>
|
||||
<polygon points="92.06,112.143 137.314,157.498 159.941,134.82 114.687,89.465 "/>
|
||||
<polygon points="92.06,279.141 114.687,301.816 159.941,256.462 137.314,233.784 "/>
|
||||
<rect x="58" y="178" width="64" height="32"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1022 B |
13
src/src.pro
13
src/src.pro
@ -57,6 +57,7 @@ SOURCES += main.cpp\
|
||||
dialog/vselectdialog.cpp \
|
||||
vcaptain.cpp \
|
||||
vopenedlistmenu.cpp \
|
||||
vnavigationmode.cpp \
|
||||
vorphanfile.cpp \
|
||||
vcodeblockhighlighthelper.cpp \
|
||||
vwebview.cpp \
|
||||
@ -91,7 +92,11 @@ SOURCES += main.cpp\
|
||||
vpreviewmanager.cpp \
|
||||
vimageresourcemanager2.cpp \
|
||||
vtextdocumentlayout.cpp \
|
||||
vtextedit.cpp
|
||||
vtextedit.cpp \
|
||||
vsnippetlist.cpp \
|
||||
vsnippet.cpp \
|
||||
dialog/veditsnippetdialog.cpp \
|
||||
utils/vimnavigationforwidget.cpp
|
||||
|
||||
HEADERS += vmainwindow.h \
|
||||
vdirectorytree.h \
|
||||
@ -170,7 +175,11 @@ HEADERS += vmainwindow.h \
|
||||
vpreviewmanager.h \
|
||||
vimageresourcemanager2.h \
|
||||
vtextdocumentlayout.h \
|
||||
vtextedit.h
|
||||
vtextedit.h \
|
||||
vsnippetlist.h \
|
||||
vsnippet.h \
|
||||
dialog/veditsnippetdialog.h \
|
||||
utils/vimnavigationforwidget.h
|
||||
|
||||
RESOURCES += \
|
||||
vnote.qrc \
|
||||
|
69
src/utils/vimnavigationforwidget.cpp
Normal file
69
src/utils/vimnavigationforwidget.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "vimnavigationforwidget.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QCoreApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <QDebug>
|
||||
|
||||
#include "vutils.h"
|
||||
|
||||
VimNavigationForWidget::VimNavigationForWidget()
|
||||
{
|
||||
}
|
||||
|
||||
bool VimNavigationForWidget::injectKeyPressEventForVim(QWidget *p_widget,
|
||||
QKeyEvent *p_event,
|
||||
QWidget *p_escWidget)
|
||||
{
|
||||
Q_ASSERT(p_widget);
|
||||
|
||||
bool ret = false;
|
||||
int key = p_event->key();
|
||||
int modifiers = p_event->modifiers();
|
||||
if (!p_escWidget) {
|
||||
p_escWidget = p_widget;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case Qt::Key_BracketLeft:
|
||||
{
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
QKeyEvent *escEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(p_escWidget, escEvent);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_J:
|
||||
{
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
QKeyEvent *downEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(p_widget, downEvent);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_K:
|
||||
{
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
QKeyEvent *upEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(p_widget, upEvent);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
24
src/utils/vimnavigationforwidget.h
Normal file
24
src/utils/vimnavigationforwidget.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef VIMNAVIGATIONFORWIDGET_H
|
||||
#define VIMNAVIGATIONFORWIDGET_H
|
||||
|
||||
class QWidget;
|
||||
class QKeyEvent;
|
||||
|
||||
|
||||
// Provide simple Vim mode navigation for widgets.
|
||||
class VimNavigationForWidget
|
||||
{
|
||||
public:
|
||||
// Try to handle @p_event and inject proper event instead if it triggers
|
||||
// Vim operation.
|
||||
// Return true if @p_event is handled properly.
|
||||
// @p_escWidget: the widget to accept the ESC event.
|
||||
static bool injectKeyPressEventForVim(QWidget *p_widget,
|
||||
QKeyEvent *p_event,
|
||||
QWidget *p_escWidget = nullptr);
|
||||
|
||||
private:
|
||||
VimNavigationForWidget();
|
||||
};
|
||||
|
||||
#endif // VIMNAVIGATIONFORWIDGET_H
|
@ -61,7 +61,7 @@ QString VUtils::readFileFromDisk(const QString &filePath)
|
||||
{
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qWarning() << "fail to read file" << filePath;
|
||||
qWarning() << "fail to open file" << filePath << "to read";
|
||||
return QString();
|
||||
}
|
||||
QString fileText(file.readAll());
|
||||
@ -84,6 +84,34 @@ bool VUtils::writeFileToDisk(const QString &filePath, const QString &text)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VUtils::writeJsonToDisk(const QString &p_filePath, const QJsonObject &p_json)
|
||||
{
|
||||
QFile file(p_filePath);
|
||||
// We use Unix LF for config file.
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "fail to open file" << p_filePath << "to write";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonDocument doc(p_json);
|
||||
if (-1 == file.write(doc.toJson())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject VUtils::readJsonFromDisk(const QString &p_filePath)
|
||||
{
|
||||
QFile file(p_filePath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "fail to open file" << p_filePath << "to read";
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
return QJsonDocument::fromJson(file.readAll()).object();
|
||||
}
|
||||
|
||||
QRgb VUtils::QRgbFromString(const QString &str)
|
||||
{
|
||||
Q_ASSERT(str.length() == 6);
|
||||
@ -859,6 +887,12 @@ bool VUtils::deleteFile(const VNotebook *p_notebook,
|
||||
}
|
||||
}
|
||||
|
||||
bool VUtils::deleteFile(const QString &p_path)
|
||||
{
|
||||
QFile file(p_path);
|
||||
return file.remove();
|
||||
}
|
||||
|
||||
bool VUtils::deleteFile(const VOrphanFile *p_file,
|
||||
const QString &p_path,
|
||||
bool p_skipRecycleBin)
|
||||
@ -1004,3 +1038,12 @@ QString VUtils::validFilePathToOpen(const QString &p_file)
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool VUtils::isControlModifierForVim(int p_modifiers)
|
||||
{
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
|
||||
return p_modifiers == Qt::MetaModifier;
|
||||
#else
|
||||
return p_modifiers == Qt::ControlModifier;
|
||||
#endif
|
||||
}
|
||||
|
@ -72,9 +72,16 @@ class VUtils
|
||||
{
|
||||
public:
|
||||
static QString readFileFromDisk(const QString &filePath);
|
||||
|
||||
static bool writeFileToDisk(const QString &filePath, const QString &text);
|
||||
|
||||
static bool writeJsonToDisk(const QString &p_filePath, const QJsonObject &p_json);
|
||||
|
||||
static QJsonObject readJsonFromDisk(const QString &p_filePath);
|
||||
|
||||
// Transform FFFFFF string to QRgb
|
||||
static QRgb QRgbFromString(const QString &str);
|
||||
|
||||
static QString generateImageFileName(const QString &path, const QString &title,
|
||||
const QString &format = "png");
|
||||
|
||||
@ -226,6 +233,9 @@ public:
|
||||
const QString &p_path,
|
||||
bool p_skipRecycleBin = false);
|
||||
|
||||
// Delete file specified by @p_path.
|
||||
static bool deleteFile(const QString &p_path);
|
||||
|
||||
static QString displayDateTime(const QDateTime &p_dateTime);
|
||||
|
||||
// Check if file @p_name exists in @p_dir.
|
||||
@ -242,6 +252,9 @@ public:
|
||||
// Return empty if it is not valid.
|
||||
static QString validFilePathToOpen(const QString &p_file);
|
||||
|
||||
// See if @p_modifiers is Control which is different on macOs and Windows.
|
||||
static bool isControlModifierForVim(int p_modifiers);
|
||||
|
||||
// Regular expression for image link.
|
||||
// 
|
||||
// Captured texts (need to be trimmed):
|
||||
|
@ -24,16 +24,6 @@ const int VVim::SearchHistory::c_capacity = 50;
|
||||
|
||||
#define ADDKEY(x, y) case (x): {ch = (y); break;}
|
||||
|
||||
// See if @p_modifiers is Control which is different on macOs and Windows.
|
||||
static bool isControlModifier(int p_modifiers)
|
||||
{
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
|
||||
return p_modifiers == Qt::MetaModifier;
|
||||
#else
|
||||
return p_modifiers == Qt::ControlModifier;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns NULL QChar if invalid.
|
||||
static QChar keyToChar(int p_key, int p_modifiers)
|
||||
{
|
||||
@ -41,7 +31,7 @@ static QChar keyToChar(int p_key, int p_modifiers)
|
||||
return QChar('0' + (p_key - Qt::Key_0));
|
||||
} else if (p_key >= Qt::Key_A && p_key <= Qt::Key_Z) {
|
||||
if (p_modifiers == Qt::ShiftModifier
|
||||
|| isControlModifier(p_modifiers)) {
|
||||
|| VUtils::isControlModifierForVim(p_modifiers)) {
|
||||
return QChar('A' + (p_key - Qt::Key_A));
|
||||
} else {
|
||||
return QChar('a' + (p_key - Qt::Key_A));
|
||||
@ -99,7 +89,7 @@ static QString keyToString(int p_key, int p_modifiers)
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (isControlModifier(p_modifiers)) {
|
||||
if (VUtils::isControlModifierForVim(p_modifiers)) {
|
||||
return QString("^") + ch;
|
||||
} else {
|
||||
return ch;
|
||||
@ -473,7 +463,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
// Handle Insert mode key press.
|
||||
if (VimMode::Insert == m_mode) {
|
||||
if (key == Qt::Key_Escape
|
||||
|| (key == Qt::Key_BracketLeft && isControlModifier(modifiers))) {
|
||||
|| (key == Qt::Key_BracketLeft && VUtils::isControlModifierForVim(modifiers))) {
|
||||
// See if we need to cancel auto indent.
|
||||
bool cancelAutoIndent = false;
|
||||
if (p_autoIndentPos && *p_autoIndentPos > -1) {
|
||||
@ -510,14 +500,14 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
}
|
||||
|
||||
goto clear_accept;
|
||||
} else if (key == Qt::Key_R && isControlModifier(modifiers)) {
|
||||
} else if (key == Qt::Key_R && VUtils::isControlModifierForVim(modifiers)) {
|
||||
// Ctrl+R, insert the content of a register.
|
||||
m_pendingKeys.append(keyInfo);
|
||||
m_registerPending = true;
|
||||
goto accept;
|
||||
}
|
||||
|
||||
if (key == Qt::Key_O && isControlModifier(modifiers)) {
|
||||
if (key == Qt::Key_O && VUtils::isControlModifierForVim(modifiers)) {
|
||||
// Ctrl+O, enter normal mode, execute one command, then return to insert mode.
|
||||
m_insertModeAfterCommand = true;
|
||||
clearSelection();
|
||||
@ -823,7 +813,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
|
||||
m_editor->setTextCursorW(cursor);
|
||||
setMode(VimMode::Insert);
|
||||
} else if (isControlModifier(modifiers)) {
|
||||
} else if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
// Ctrl+I, jump to next location.
|
||||
if (!m_tokens.isEmpty()
|
||||
|| !checkMode(VimMode::Normal)) {
|
||||
@ -935,7 +925,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (isControlModifier(modifiers)) {
|
||||
} else if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
// Ctrl+O, jump to previous location.
|
||||
if (!m_tokens.isEmpty()
|
||||
|| !checkMode(VimMode::Normal)) {
|
||||
@ -1037,7 +1027,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
// Should be kept together with Qt::Key_PageUp.
|
||||
case Qt::Key_B:
|
||||
{
|
||||
if (isControlModifier(modifiers)) {
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
// Ctrl+B, page up, fall through.
|
||||
modifiers = Qt::NoModifier;
|
||||
} else if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) {
|
||||
@ -1095,7 +1085,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
bool toLower = modifiers == Qt::NoModifier;
|
||||
|
||||
if (isControlModifier(modifiers)) {
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
// Ctrl+U, HalfPageUp.
|
||||
if (!m_keys.isEmpty()) {
|
||||
// Not a valid sequence.
|
||||
@ -1200,7 +1190,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
|
||||
case Qt::Key_D:
|
||||
{
|
||||
if (isControlModifier(modifiers)) {
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
// Ctrl+D, HalfPageDown.
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (!m_keys.isEmpty()) {
|
||||
@ -1301,7 +1291,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
// Should be kept together with Qt::Key_Escape.
|
||||
case Qt::Key_BracketLeft:
|
||||
{
|
||||
if (isControlModifier(modifiers)) {
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
// fallthrough.
|
||||
} else if (modifiers == Qt::NoModifier) {
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
@ -1792,7 +1782,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
break;
|
||||
}
|
||||
|
||||
if (isControlModifier(modifiers)) {
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
// Redo.
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (!m_keys.isEmpty() || hasActionToken()) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "vmainwindow.h"
|
||||
#include "dialog/vconfirmdeletiondialog.h"
|
||||
#include "dialog/vsortdialog.h"
|
||||
#include "utils/vimnavigationforwidget.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
extern VMainWindow *g_mainWin;
|
||||
@ -462,49 +463,12 @@ void VAttachmentList::handleListItemCommitData(QWidget *p_itemEdit)
|
||||
|
||||
void VAttachmentList::keyPressEvent(QKeyEvent *p_event)
|
||||
{
|
||||
int key = p_event->key();
|
||||
int modifiers = p_event->modifiers();
|
||||
switch (key) {
|
||||
case Qt::Key_BracketLeft:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
QKeyEvent *escEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(this, escEvent);
|
||||
if (VimNavigationForWidget::injectKeyPressEventForVim(m_attachmentList,
|
||||
p_event,
|
||||
this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_J:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
QKeyEvent *downEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(m_attachmentList, downEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_K:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
QKeyEvent *upEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(m_attachmentList, upEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QWidget::keyPressEvent(p_event);
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,16 @@ const QString VConfigManager::c_defaultConfigFile = QString("vnote.ini");
|
||||
|
||||
const QString VConfigManager::c_sessionConfigFile = QString("session.ini");
|
||||
|
||||
const QString VConfigManager::c_snippetConfigFile = QString("snippet.json");
|
||||
|
||||
const QString VConfigManager::c_styleConfigFolder = QString("styles");
|
||||
|
||||
const QString VConfigManager::c_codeBlockStyleConfigFolder = QString("codeblock_styles");
|
||||
|
||||
const QString VConfigManager::c_templateConfigFolder = QString("templates");
|
||||
|
||||
const QString VConfigManager::c_snippetConfigFolder = QString("snippets");
|
||||
|
||||
const QString VConfigManager::c_defaultCssFile = QString(":/resources/styles/default.css");
|
||||
|
||||
const QString VConfigManager::c_defaultCodeBlockCssFile = QString(":/utils/highlightjs/styles/vnote.css");
|
||||
@ -483,6 +487,7 @@ bool VConfigManager::writeDirectoryConfig(const QString &path, const QJsonObject
|
||||
QString configFile = fetchDirConfigFilePath(path);
|
||||
|
||||
QFile config(configFile);
|
||||
// We use Unix LF for config file.
|
||||
if (!config.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "fail to open directory configuration file for write:"
|
||||
<< configFile;
|
||||
@ -734,17 +739,32 @@ QString VConfigManager::getConfigFilePath() const
|
||||
|
||||
QString VConfigManager::getStyleConfigFolder() const
|
||||
{
|
||||
return getConfigFolder() + QDir::separator() + c_styleConfigFolder;
|
||||
static QString path = QDir(getConfigFolder()).filePath(c_styleConfigFolder);
|
||||
return path;
|
||||
}
|
||||
|
||||
QString VConfigManager::getCodeBlockStyleConfigFolder() const
|
||||
{
|
||||
return getStyleConfigFolder() + QDir::separator() + c_codeBlockStyleConfigFolder;
|
||||
static QString path = QDir(getStyleConfigFolder()).filePath(c_codeBlockStyleConfigFolder);
|
||||
return path;
|
||||
}
|
||||
|
||||
QString VConfigManager::getTemplateConfigFolder() const
|
||||
{
|
||||
return getConfigFolder() + QDir::separator() + c_templateConfigFolder;
|
||||
static QString path = QDir(getConfigFolder()).filePath(c_templateConfigFolder);
|
||||
return path;
|
||||
}
|
||||
|
||||
QString VConfigManager::getSnippetConfigFolder() const
|
||||
{
|
||||
static QString path = QDir(getConfigFolder()).filePath(c_snippetConfigFolder);
|
||||
return path;
|
||||
}
|
||||
|
||||
QString VConfigManager::getSnippetConfigFilePath() const
|
||||
{
|
||||
static QString path = QDir(getSnippetConfigFolder()).filePath(c_snippetConfigFile);
|
||||
return path;
|
||||
}
|
||||
|
||||
QVector<QString> VConfigManager::getCssStyles() const
|
||||
|
@ -352,6 +352,11 @@ public:
|
||||
// Get the folder c_templateConfigFolder in the config folder.
|
||||
QString getTemplateConfigFolder() const;
|
||||
|
||||
// Get the folder c_snippetConfigFolder in the config folder.
|
||||
QString getSnippetConfigFolder() const;
|
||||
|
||||
QString getSnippetConfigFilePath() const;
|
||||
|
||||
// Read all available css files in c_styleConfigFolder.
|
||||
QVector<QString> getCssStyles() const;
|
||||
|
||||
@ -714,6 +719,9 @@ private:
|
||||
// The name of the config file for session information.
|
||||
static const QString c_sessionConfigFile;
|
||||
|
||||
// The name of the config file for snippets folder.
|
||||
static const QString c_snippetConfigFile;
|
||||
|
||||
// QSettings for the user configuration
|
||||
QSettings *userSettings;
|
||||
|
||||
@ -733,6 +741,9 @@ private:
|
||||
// The folder name of template files.
|
||||
static const QString c_templateConfigFolder;
|
||||
|
||||
// The folder name of snippet files.
|
||||
static const QString c_snippetConfigFolder;
|
||||
|
||||
// Default CSS file in resource system.
|
||||
static const QString c_defaultCssFile;
|
||||
|
||||
|
@ -50,6 +50,18 @@ namespace DirConfig
|
||||
static const QString c_modifiedTime = "modified_time";
|
||||
}
|
||||
|
||||
// Snippet Cofnig file items.
|
||||
namespace SnippetConfig
|
||||
{
|
||||
static const QString c_version = "version";
|
||||
static const QString c_snippets = "snippets";
|
||||
static const QString c_name = "name";
|
||||
static const QString c_type = "type";
|
||||
static const QString c_cursorMark = "cursor_mark";
|
||||
static const QString c_selectionMark = "selection_mark";
|
||||
static const QString c_shortcut = "shortcut";
|
||||
}
|
||||
|
||||
static const QString c_emptyHeaderName = "[EMPTY]";
|
||||
|
||||
enum class TextDecoration
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "vconfigmanager.h"
|
||||
#include "vmainwindow.h"
|
||||
#include "dialog/vsortdialog.h"
|
||||
#include "utils/vimnavigationforwidget.h"
|
||||
|
||||
extern VMainWindow *g_mainWin;
|
||||
|
||||
@ -910,6 +911,10 @@ void VDirectoryTree::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
void VDirectoryTree::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (VimNavigationForWidget::injectKeyPressEventForVim(this, event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int key = event->key();
|
||||
int modifiers = event->modifiers();
|
||||
|
||||
@ -924,32 +929,6 @@ void VDirectoryTree::keyPressEvent(QKeyEvent *event)
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_J:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
event->accept();
|
||||
QKeyEvent *downEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(this, downEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_K:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
event->accept();
|
||||
QKeyEvent *upEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(this, upEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Asterisk:
|
||||
{
|
||||
if (modifiers == Qt::ShiftModifier) {
|
||||
@ -1089,110 +1068,18 @@ void VDirectoryTree::expandSubTree(QTreeWidgetItem *p_item)
|
||||
}
|
||||
}
|
||||
|
||||
void VDirectoryTree::registerNavigation(QChar p_majorKey)
|
||||
{
|
||||
m_majorKey = p_majorKey;
|
||||
V_ASSERT(m_keyMap.empty());
|
||||
V_ASSERT(m_naviLabels.empty());
|
||||
}
|
||||
|
||||
void VDirectoryTree::showNavigation()
|
||||
{
|
||||
// Clean up.
|
||||
m_keyMap.clear();
|
||||
for (auto label : m_naviLabels) {
|
||||
delete label;
|
||||
}
|
||||
m_naviLabels.clear();
|
||||
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate labels for visible items.
|
||||
auto items = getVisibleItems();
|
||||
for (int i = 0; i < 26 && i < items.size(); ++i) {
|
||||
QChar key('a' + i);
|
||||
m_keyMap[key] = items[i];
|
||||
|
||||
QString str = QString(m_majorKey) + key;
|
||||
QLabel *label = new QLabel(str, this);
|
||||
label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
|
||||
label->move(visualItemRect(items[i]).topLeft());
|
||||
label->show();
|
||||
m_naviLabels.append(label);
|
||||
}
|
||||
}
|
||||
|
||||
void VDirectoryTree::hideNavigation()
|
||||
{
|
||||
m_keyMap.clear();
|
||||
for (auto label : m_naviLabels) {
|
||||
delete label;
|
||||
}
|
||||
m_naviLabels.clear();
|
||||
VNavigationMode::showNavigation(this);
|
||||
}
|
||||
|
||||
bool VDirectoryTree::handleKeyNavigation(int p_key, bool &p_succeed)
|
||||
{
|
||||
static bool secondKey = false;
|
||||
bool ret = false;
|
||||
p_succeed = false;
|
||||
QChar keyChar = VUtils::keyToChar(p_key);
|
||||
if (secondKey && !keyChar.isNull()) {
|
||||
secondKey = false;
|
||||
p_succeed = true;
|
||||
ret = true;
|
||||
auto it = m_keyMap.find(keyChar);
|
||||
if (it != m_keyMap.end()) {
|
||||
setCurrentItem(it.value());
|
||||
setFocus();
|
||||
}
|
||||
} else if (keyChar == m_majorKey) {
|
||||
// Major key pressed.
|
||||
// Need second key if m_keyMap is not empty.
|
||||
if (m_keyMap.isEmpty()) {
|
||||
p_succeed = true;
|
||||
} else {
|
||||
secondKey = true;
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem *> VDirectoryTree::getVisibleItems() const
|
||||
{
|
||||
QList<QTreeWidgetItem *> items;
|
||||
for (int i = 0; i < topLevelItemCount(); ++i) {
|
||||
QTreeWidgetItem *item = topLevelItem(i);
|
||||
if (!item->isHidden()) {
|
||||
items.append(item);
|
||||
if (item->isExpanded()) {
|
||||
items.append(getVisibleChildItems(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem *> VDirectoryTree::getVisibleChildItems(const QTreeWidgetItem *p_item) const
|
||||
{
|
||||
QList<QTreeWidgetItem *> items;
|
||||
if (p_item && !p_item->isHidden() && p_item->isExpanded()) {
|
||||
for (int i = 0; i < p_item->childCount(); ++i) {
|
||||
QTreeWidgetItem *child = p_item->child(i);
|
||||
if (!child->isHidden()) {
|
||||
items.append(child);
|
||||
if (child->isExpanded()) {
|
||||
items.append(getVisibleChildItems(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
return VNavigationMode::handleKeyNavigation(this,
|
||||
secondKey,
|
||||
p_key,
|
||||
p_succeed);
|
||||
}
|
||||
|
||||
int VDirectoryTree::getNewMagic()
|
||||
|
@ -29,9 +29,7 @@ public:
|
||||
const VNotebook *currentNotebook() const;
|
||||
|
||||
// Implementations for VNavigationMode.
|
||||
void registerNavigation(QChar p_majorKey) Q_DECL_OVERRIDE;
|
||||
void showNavigation() Q_DECL_OVERRIDE;
|
||||
void hideNavigation() Q_DECL_OVERRIDE;
|
||||
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
|
||||
|
||||
signals:
|
||||
@ -134,10 +132,6 @@ private:
|
||||
// Expand the currently-built subtree of @p_item according to VDirectory.isExpanded().
|
||||
void expandSubTree(QTreeWidgetItem *p_item);
|
||||
|
||||
QList<QTreeWidgetItem *> getVisibleItems() const;
|
||||
|
||||
QList<QTreeWidgetItem *> getVisibleChildItems(const QTreeWidgetItem *p_item) const;
|
||||
|
||||
// We use a map to save and restore current directory of each notebook.
|
||||
// Try to restore current directory after changing notebook.
|
||||
// Return false if no cache item found for current notebook.
|
||||
@ -180,11 +174,6 @@ private:
|
||||
// Reload content from disk.
|
||||
QAction *m_reloadAct;
|
||||
|
||||
// Navigation Mode.
|
||||
// Map second key to QTreeWidgetItem.
|
||||
QMap<QChar, QTreeWidgetItem *> m_keyMap;
|
||||
QVector<QLabel *> m_naviLabels;
|
||||
|
||||
static const QString c_infoShortcutSequence;
|
||||
static const QString c_copyShortcutSequence;
|
||||
static const QString c_cutShortcutSequence;
|
||||
|
@ -756,7 +756,8 @@ bool VEditArea::handleKeyNavigation(int p_key, bool &p_succeed)
|
||||
ret = true;
|
||||
auto it = m_keyMap.find(keyChar);
|
||||
if (it != m_keyMap.end()) {
|
||||
setCurrentWindow(splitter->indexOf(it.value()), true);
|
||||
setCurrentWindow(splitter->indexOf(static_cast<VEditWindow *>(it.value())),
|
||||
true);
|
||||
}
|
||||
} else if (keyChar == m_majorKey) {
|
||||
// Major key pressed.
|
||||
|
@ -205,11 +205,6 @@ private:
|
||||
|
||||
// Last closed files stack.
|
||||
QStack<VFileSessionInfo> m_lastClosedFiles;
|
||||
|
||||
// Navigation Mode.
|
||||
// Map second key to VEditWindow.
|
||||
QMap<QChar, VEditWindow *> m_keyMap;
|
||||
QVector<QLabel *> m_naviLabels;
|
||||
};
|
||||
|
||||
inline VEditWindow* VEditArea::getWindow(int windowIndex) const
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "veditoperations.h"
|
||||
#include "dialog/vinsertlinkdialog.h"
|
||||
#include "utils/vmetawordmanager.h"
|
||||
#include "utils/vvim.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
@ -915,3 +916,10 @@ void VEditor::updateConfig()
|
||||
{
|
||||
updateEditConfig();
|
||||
}
|
||||
|
||||
void VEditor::setVimMode(VimMode p_mode)
|
||||
{
|
||||
if (m_editOps) {
|
||||
m_editOps->setVimMode(p_mode);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ class VEditOperations;
|
||||
class QTimer;
|
||||
class QLabel;
|
||||
class VVim;
|
||||
enum class VimMode;
|
||||
|
||||
|
||||
enum class SelectionId {
|
||||
@ -136,6 +137,8 @@ public:
|
||||
// Update config according to global configurations.
|
||||
virtual void updateConfig();
|
||||
|
||||
void setVimMode(VimMode p_mode);
|
||||
|
||||
// Wrapper functions for QPlainTextEdit/QTextEdit.
|
||||
// Ends with W to distinguish it from the original interfaces.
|
||||
public:
|
||||
|
@ -124,3 +124,8 @@ bool VEditTab::tabHasFocus() const
|
||||
void VEditTab::insertLink()
|
||||
{
|
||||
}
|
||||
|
||||
void VEditTab::applySnippet(const VSnippet *p_snippet)
|
||||
{
|
||||
Q_UNUSED(p_snippet);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "vedittabinfo.h"
|
||||
|
||||
class VEditArea;
|
||||
class VSnippet;
|
||||
|
||||
// VEditTab is the base class of an edit tab inside VEditWindow.
|
||||
class VEditTab : public QWidget
|
||||
@ -91,6 +92,9 @@ public:
|
||||
// Called by evaluateMagicWordsByCaptain() to evaluate the magic words.
|
||||
virtual void evaluateMagicWords();
|
||||
|
||||
// Insert snippet @p_snippet.
|
||||
virtual void applySnippet(const VSnippet *p_snippet);
|
||||
|
||||
public slots:
|
||||
// Enter edit mode
|
||||
virtual void editFile() = 0;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "dialog/vconfirmdeletiondialog.h"
|
||||
#include "dialog/vsortdialog.h"
|
||||
#include "vmainwindow.h"
|
||||
#include "utils/vimnavigationforwidget.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
extern VNote *g_vnote;
|
||||
@ -825,50 +826,21 @@ void VFileList::pasteFiles(VDirectory *p_destDir,
|
||||
getNewMagic();
|
||||
}
|
||||
|
||||
void VFileList::keyPressEvent(QKeyEvent *event)
|
||||
void VFileList::keyPressEvent(QKeyEvent *p_event)
|
||||
{
|
||||
int key = event->key();
|
||||
int modifiers = event->modifiers();
|
||||
switch (key) {
|
||||
case Qt::Key_Return:
|
||||
{
|
||||
if (VimNavigationForWidget::injectKeyPressEventForVim(fileList,
|
||||
p_event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_event->key() == Qt::Key_Return) {
|
||||
QListWidgetItem *item = fileList->currentItem();
|
||||
if (item) {
|
||||
handleItemClicked(item);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case Qt::Key_J:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
event->accept();
|
||||
QKeyEvent *downEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(fileList, downEvent);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_K:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
event->accept();
|
||||
QKeyEvent *upEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(fileList, upEvent);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
QWidget::keyPressEvent(event);
|
||||
QWidget::keyPressEvent(p_event);
|
||||
}
|
||||
|
||||
void VFileList::focusInEvent(QFocusEvent * /* p_event */)
|
||||
@ -893,91 +865,15 @@ bool VFileList::locateFile(const VNoteFile *p_file)
|
||||
return false;
|
||||
}
|
||||
|
||||
void VFileList::registerNavigation(QChar p_majorKey)
|
||||
{
|
||||
m_majorKey = p_majorKey;
|
||||
V_ASSERT(m_keyMap.empty());
|
||||
V_ASSERT(m_naviLabels.empty());
|
||||
}
|
||||
|
||||
void VFileList::showNavigation()
|
||||
{
|
||||
// Clean up.
|
||||
m_keyMap.clear();
|
||||
for (auto label : m_naviLabels) {
|
||||
delete label;
|
||||
}
|
||||
m_naviLabels.clear();
|
||||
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate labels for visible items.
|
||||
auto items = getVisibleItems();
|
||||
int itemWidth = rect().width();
|
||||
for (int i = 0; i < 26 && i < items.size(); ++i) {
|
||||
QChar key('a' + i);
|
||||
m_keyMap[key] = items[i];
|
||||
|
||||
QString str = QString(m_majorKey) + key;
|
||||
QLabel *label = new QLabel(str, this);
|
||||
label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
|
||||
label->show();
|
||||
QRect rect = fileList->visualItemRect(items[i]);
|
||||
// Display the label at the end to show the file name.
|
||||
label->move(rect.x() + itemWidth - label->width(), rect.y());
|
||||
m_naviLabels.append(label);
|
||||
}
|
||||
}
|
||||
|
||||
void VFileList::hideNavigation()
|
||||
{
|
||||
m_keyMap.clear();
|
||||
for (auto label : m_naviLabels) {
|
||||
delete label;
|
||||
}
|
||||
m_naviLabels.clear();
|
||||
VNavigationMode::showNavigation(fileList);
|
||||
}
|
||||
|
||||
bool VFileList::handleKeyNavigation(int p_key, bool &p_succeed)
|
||||
{
|
||||
static bool secondKey = false;
|
||||
bool ret = false;
|
||||
p_succeed = false;
|
||||
QChar keyChar = VUtils::keyToChar(p_key);
|
||||
if (secondKey && !keyChar.isNull()) {
|
||||
secondKey = false;
|
||||
p_succeed = true;
|
||||
ret = true;
|
||||
auto it = m_keyMap.find(keyChar);
|
||||
if (it != m_keyMap.end()) {
|
||||
fileList->setCurrentItem(it.value(), QItemSelectionModel::ClearAndSelect);
|
||||
fileList->setFocus();
|
||||
}
|
||||
} else if (keyChar == m_majorKey) {
|
||||
// Major key pressed.
|
||||
// Need second key if m_keyMap is not empty.
|
||||
if (m_keyMap.isEmpty()) {
|
||||
p_succeed = true;
|
||||
} else {
|
||||
secondKey = true;
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<QListWidgetItem *> VFileList::getVisibleItems() const
|
||||
{
|
||||
QList<QListWidgetItem *> items;
|
||||
for (int i = 0; i < fileList->count(); ++i) {
|
||||
QListWidgetItem *item = fileList->item(i);
|
||||
if (!item->isHidden()) {
|
||||
items.append(item);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
return VNavigationMode::handleKeyNavigation(fileList, secondKey, p_key, p_succeed);
|
||||
}
|
||||
|
||||
int VFileList::getNewMagic()
|
||||
|
@ -49,9 +49,7 @@ public:
|
||||
QWidget *getContentWidget() const;
|
||||
|
||||
// Implementations for VNavigationMode.
|
||||
void registerNavigation(QChar p_majorKey) Q_DECL_OVERRIDE;
|
||||
void showNavigation() Q_DECL_OVERRIDE;
|
||||
void hideNavigation() Q_DECL_OVERRIDE;
|
||||
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
|
||||
|
||||
public slots:
|
||||
@ -104,7 +102,8 @@ private slots:
|
||||
void sortItems();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
|
||||
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
void focusInEvent(QFocusEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
@ -140,8 +139,6 @@ private:
|
||||
|
||||
inline QPointer<VNoteFile> getVFile(QListWidgetItem *p_item) const;
|
||||
|
||||
QList<QListWidgetItem *> getVisibleItems() const;
|
||||
|
||||
// Fill the info of @p_item according to @p_file.
|
||||
void fillItem(QListWidgetItem *p_item, const VNoteFile *p_file);
|
||||
|
||||
@ -174,11 +171,6 @@ private:
|
||||
QAction *m_openLocationAct;
|
||||
QAction *m_sortAct;
|
||||
|
||||
// Navigation Mode.
|
||||
// Map second key to QListWidgetItem.
|
||||
QMap<QChar, QListWidgetItem *> m_keyMap;
|
||||
QVector<QLabel *> m_naviLabels;
|
||||
|
||||
static const QString c_infoShortcutSequence;
|
||||
static const QString c_copyShortcutSequence;
|
||||
static const QString c_cutShortcutSequence;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "vbuttonwithwidget.h"
|
||||
#include "vattachmentlist.h"
|
||||
#include "vfilesessioninfo.h"
|
||||
#include "vsnippetlist.h"
|
||||
|
||||
VMainWindow *g_mainWin;
|
||||
|
||||
@ -111,6 +112,7 @@ void VMainWindow::registerCaptainAndNavigationTargets()
|
||||
m_captain->registerNavigationTarget(m_fileList);
|
||||
m_captain->registerNavigationTarget(editArea);
|
||||
m_captain->registerNavigationTarget(outline);
|
||||
m_captain->registerNavigationTarget(m_snippetList);
|
||||
|
||||
// Register Captain mode targets.
|
||||
m_captain->registerCaptainTarget(tr("AttachmentList"),
|
||||
@ -1185,7 +1187,6 @@ void VMainWindow::initDockWindows()
|
||||
toolDock = new QDockWidget(tr("Tools"), this);
|
||||
toolDock->setObjectName("ToolsDock");
|
||||
toolDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||
toolBox = new QToolBox(this);
|
||||
|
||||
// Outline tree.
|
||||
outline = new VOutline(this);
|
||||
@ -1196,8 +1197,18 @@ void VMainWindow::initDockWindows()
|
||||
connect(outline, &VOutline::outlineItemActivated,
|
||||
editArea, &VEditArea::scrollToHeader);
|
||||
|
||||
toolBox->addItem(outline, QIcon(":/resources/icons/outline.svg"), tr("Outline"));
|
||||
toolDock->setWidget(toolBox);
|
||||
// Snippets.
|
||||
m_snippetList = new VSnippetList(this);
|
||||
|
||||
m_toolBox = new QToolBox(this);
|
||||
m_toolBox->addItem(outline,
|
||||
QIcon(":/resources/icons/outline.svg"),
|
||||
tr("Outline"));
|
||||
m_toolBox->addItem(m_snippetList,
|
||||
QIcon(":/resources/icons/snippets.svg"),
|
||||
tr("Snippets"));
|
||||
|
||||
toolDock->setWidget(m_toolBox);
|
||||
addDockWidget(Qt::RightDockWidgetArea, toolDock);
|
||||
|
||||
QAction *toggleAct = toolDock->toggleViewAction();
|
||||
|
@ -37,6 +37,7 @@ class QSystemTrayIcon;
|
||||
class QShortcut;
|
||||
class VButtonWithWidget;
|
||||
class VAttachmentList;
|
||||
class VSnippetList;
|
||||
|
||||
enum class PanelViewState
|
||||
{
|
||||
@ -87,6 +88,8 @@ public:
|
||||
|
||||
VFile *getCurrentFile() const;
|
||||
|
||||
VEditTab *getCurrentTab() const;
|
||||
|
||||
signals:
|
||||
// Emit when editor related configurations were changed by user.
|
||||
void editorConfigUpdated();
|
||||
@ -294,8 +297,15 @@ private:
|
||||
VEditArea *editArea;
|
||||
|
||||
QDockWidget *toolDock;
|
||||
QToolBox *toolBox;
|
||||
|
||||
// Tool box in the dock widget.
|
||||
QToolBox *m_toolBox;
|
||||
|
||||
VOutline *outline;
|
||||
|
||||
// View and manage snippets.
|
||||
VSnippetList *m_snippetList;
|
||||
|
||||
VAvatar *m_avatar;
|
||||
VFindReplaceDialog *m_findReplaceDialog;
|
||||
VVimIndicator *m_vimIndicator;
|
||||
@ -396,4 +406,9 @@ inline VFile *VMainWindow::getCurrentFile() const
|
||||
return m_curFile;
|
||||
}
|
||||
|
||||
inline VEditTab *VMainWindow::getCurrentTab() const
|
||||
{
|
||||
return m_curTab;
|
||||
}
|
||||
|
||||
#endif // VMAINWINDOW_H
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "veditor.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "vtableofcontent.h"
|
||||
#include "veditoperations.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "utils/vutils.h"
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "vwebview.h"
|
||||
#include "vmdeditor.h"
|
||||
#include "vmainwindow.h"
|
||||
#include "vsnippet.h"
|
||||
|
||||
extern VMainWindow *g_mainWin;
|
||||
|
||||
@ -724,3 +725,19 @@ void VMdTab::evaluateMagicWords()
|
||||
getEditor()->evaluateMagicWords();
|
||||
}
|
||||
}
|
||||
|
||||
void VMdTab::applySnippet(const VSnippet *p_snippet)
|
||||
{
|
||||
if (isEditMode()
|
||||
&& m_file->isModifiable()
|
||||
&& p_snippet->getType() == VSnippet::Type::PlainText) {
|
||||
Q_ASSERT(m_editor);
|
||||
QTextCursor cursor = m_editor->textCursor();
|
||||
bool changed = p_snippet->apply(cursor);
|
||||
if (changed) {
|
||||
m_editor->setTextCursor(cursor);
|
||||
|
||||
m_editor->setVimMode(VimMode::Insert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ public:
|
||||
// Evaluate magic words.
|
||||
void evaluateMagicWords() Q_DECL_OVERRIDE;
|
||||
|
||||
void applySnippet(const VSnippet *p_snippet) Q_DECL_OVERRIDE;
|
||||
|
||||
public slots:
|
||||
// Enter edit mode.
|
||||
void editFile() Q_DECL_OVERRIDE;
|
||||
|
202
src/vnavigationmode.cpp
Normal file
202
src/vnavigationmode.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
#include "vnavigationmode.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QTreeWidget>
|
||||
|
||||
#include "vnote.h"
|
||||
#include "utils/vutils.h"
|
||||
|
||||
extern VNote *g_vnote;
|
||||
|
||||
VNavigationMode::VNavigationMode()
|
||||
{
|
||||
}
|
||||
|
||||
VNavigationMode::~VNavigationMode()
|
||||
{
|
||||
}
|
||||
|
||||
void VNavigationMode::registerNavigation(QChar p_majorKey)
|
||||
{
|
||||
m_majorKey = p_majorKey;
|
||||
Q_ASSERT(m_keyMap.empty());
|
||||
Q_ASSERT(m_naviLabels.empty());
|
||||
}
|
||||
|
||||
void VNavigationMode::hideNavigation()
|
||||
{
|
||||
clearNavigation();
|
||||
}
|
||||
|
||||
void VNavigationMode::showNavigation(QListWidget *p_widget)
|
||||
{
|
||||
clearNavigation();
|
||||
|
||||
if (!p_widget->isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate labels for visible items.
|
||||
auto items = getVisibleItems(p_widget);
|
||||
for (int i = 0; i < 26 && i < items.size(); ++i) {
|
||||
QChar key('a' + i);
|
||||
m_keyMap[key] = items[i];
|
||||
|
||||
QString str = QString(m_majorKey) + key;
|
||||
QLabel *label = new QLabel(str, p_widget);
|
||||
label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
|
||||
label->show();
|
||||
QRect rect = p_widget->visualItemRect(items[i]);
|
||||
// Display the label at the end to show the file name.
|
||||
label->move(rect.x() + p_widget->rect().width() - label->width() - 2,
|
||||
rect.y());
|
||||
m_naviLabels.append(label);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QListWidgetItem *> VNavigationMode::getVisibleItems(const QListWidget *p_widget) const
|
||||
{
|
||||
QList<QListWidgetItem *> items;
|
||||
for (int i = 0; i < p_widget->count(); ++i) {
|
||||
QListWidgetItem *item = p_widget->item(i);
|
||||
if (!item->isHidden()) {
|
||||
items.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
static QList<QTreeWidgetItem *> getVisibleChildItems(const QTreeWidgetItem *p_item)
|
||||
{
|
||||
QList<QTreeWidgetItem *> items;
|
||||
if (p_item && !p_item->isHidden() && p_item->isExpanded()) {
|
||||
for (int i = 0; i < p_item->childCount(); ++i) {
|
||||
QTreeWidgetItem *child = p_item->child(i);
|
||||
if (!child->isHidden()) {
|
||||
items.append(child);
|
||||
if (child->isExpanded()) {
|
||||
items.append(getVisibleChildItems(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem *> VNavigationMode::getVisibleItems(const QTreeWidget *p_widget) const
|
||||
{
|
||||
QList<QTreeWidgetItem *> items;
|
||||
for (int i = 0; i < p_widget->topLevelItemCount(); ++i) {
|
||||
QTreeWidgetItem *item = p_widget->topLevelItem(i);
|
||||
if (!item->isHidden()) {
|
||||
items.append(item);
|
||||
if (item->isExpanded()) {
|
||||
items.append(getVisibleChildItems(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
bool VNavigationMode::handleKeyNavigation(QListWidget *p_widget,
|
||||
bool &p_secondKey,
|
||||
int p_key,
|
||||
bool &p_succeed)
|
||||
{
|
||||
bool ret = false;
|
||||
p_succeed = false;
|
||||
QChar keyChar = VUtils::keyToChar(p_key);
|
||||
if (p_secondKey && !keyChar.isNull()) {
|
||||
p_secondKey = false;
|
||||
p_succeed = true;
|
||||
ret = true;
|
||||
auto it = m_keyMap.find(keyChar);
|
||||
if (it != m_keyMap.end()) {
|
||||
p_widget->setCurrentItem(static_cast<QListWidgetItem *>(it.value()),
|
||||
QItemSelectionModel::ClearAndSelect);
|
||||
p_widget->setFocus();
|
||||
}
|
||||
} else if (keyChar == m_majorKey) {
|
||||
// Major key pressed.
|
||||
// Need second key if m_keyMap is not empty.
|
||||
if (m_keyMap.isEmpty()) {
|
||||
p_succeed = true;
|
||||
} else {
|
||||
p_secondKey = true;
|
||||
}
|
||||
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VNavigationMode::showNavigation(QTreeWidget *p_widget)
|
||||
{
|
||||
clearNavigation();
|
||||
|
||||
if (!p_widget->isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate labels for visible items.
|
||||
auto items = getVisibleItems(p_widget);
|
||||
for (int i = 0; i < 26 && i < items.size(); ++i) {
|
||||
QChar key('a' + i);
|
||||
m_keyMap[key] = items[i];
|
||||
|
||||
QString str = QString(m_majorKey) + key;
|
||||
QLabel *label = new QLabel(str, p_widget);
|
||||
label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
|
||||
label->move(p_widget->visualItemRect(items[i]).topLeft());
|
||||
label->show();
|
||||
m_naviLabels.append(label);
|
||||
}
|
||||
}
|
||||
|
||||
void VNavigationMode::clearNavigation()
|
||||
{
|
||||
m_keyMap.clear();
|
||||
for (auto label : m_naviLabels) {
|
||||
delete label;
|
||||
}
|
||||
|
||||
m_naviLabels.clear();
|
||||
}
|
||||
|
||||
bool VNavigationMode::handleKeyNavigation(QTreeWidget *p_widget,
|
||||
bool &p_secondKey,
|
||||
int p_key,
|
||||
bool &p_succeed)
|
||||
{
|
||||
bool ret = false;
|
||||
p_succeed = false;
|
||||
QChar keyChar = VUtils::keyToChar(p_key);
|
||||
if (p_secondKey && !keyChar.isNull()) {
|
||||
p_secondKey = false;
|
||||
p_succeed = true;
|
||||
ret = true;
|
||||
auto it = m_keyMap.find(keyChar);
|
||||
if (it != m_keyMap.end()) {
|
||||
p_widget->setCurrentItem(static_cast<QTreeWidgetItem *>(it.value()));
|
||||
p_widget->setFocus();
|
||||
}
|
||||
} else if (keyChar == m_majorKey) {
|
||||
// Major key pressed.
|
||||
// Need second key if m_keyMap is not empty.
|
||||
if (m_keyMap.isEmpty()) {
|
||||
p_succeed = true;
|
||||
} else {
|
||||
p_secondKey = true;
|
||||
}
|
||||
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
@ -2,23 +2,63 @@
|
||||
#define VNAVIGATIONMODE_H
|
||||
|
||||
#include <QChar>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <QList>
|
||||
|
||||
class QLabel;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
|
||||
// Interface class for Navigation Mode in Captain Mode.
|
||||
class VNavigationMode
|
||||
{
|
||||
public:
|
||||
VNavigationMode() {};
|
||||
virtual ~VNavigationMode() {};
|
||||
VNavigationMode();
|
||||
|
||||
virtual ~VNavigationMode();
|
||||
|
||||
virtual void registerNavigation(QChar p_majorKey);
|
||||
|
||||
virtual void registerNavigation(QChar p_majorKey) = 0;
|
||||
virtual void showNavigation() = 0;
|
||||
virtual void hideNavigation() = 0;
|
||||
|
||||
virtual void hideNavigation();
|
||||
|
||||
// Return true if this object could consume p_key.
|
||||
// p_succeed indicates whether the keys hit a target successfully.
|
||||
virtual bool handleKeyNavigation(int p_key, bool &p_succeed) = 0;
|
||||
|
||||
protected:
|
||||
void clearNavigation();
|
||||
|
||||
void showNavigation(QListWidget *p_widget);
|
||||
|
||||
void showNavigation(QTreeWidget *p_widget);
|
||||
|
||||
bool handleKeyNavigation(QListWidget *p_widget,
|
||||
bool &p_secondKey,
|
||||
int p_key,
|
||||
bool &p_succeed);
|
||||
|
||||
bool handleKeyNavigation(QTreeWidget *p_widget,
|
||||
bool &p_secondKey,
|
||||
int p_key,
|
||||
bool &p_succeed);
|
||||
|
||||
QChar m_majorKey;
|
||||
|
||||
// Map second key to item.
|
||||
QMap<QChar, void *> m_keyMap;
|
||||
|
||||
QVector<QLabel *> m_naviLabels;
|
||||
|
||||
private:
|
||||
QList<QListWidgetItem *> getVisibleItems(const QListWidget *p_widget) const;
|
||||
|
||||
QList<QTreeWidgetItem *> getVisibleItems(const QTreeWidget *p_widget) const;
|
||||
};
|
||||
|
||||
#endif // VNAVIGATIONMODE_H
|
||||
|
@ -135,5 +135,11 @@
|
||||
<file>resources/icons/link.svg</file>
|
||||
<file>resources/icons/code_block.svg</file>
|
||||
<file>resources/icons/manage_template.svg</file>
|
||||
<file>resources/icons/snippets.svg</file>
|
||||
<file>resources/icons/add_snippet.svg</file>
|
||||
<file>resources/icons/locate_snippet.svg</file>
|
||||
<file>resources/icons/delete_snippet.svg</file>
|
||||
<file>resources/icons/snippet_info.svg</file>
|
||||
<file>resources/icons/apply_snippet.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "veditarea.h"
|
||||
#include "vnofocusitemdelegate.h"
|
||||
#include "vmainwindow.h"
|
||||
#include "utils/vimnavigationforwidget.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
@ -590,46 +591,10 @@ bool VNotebookSelector::handleKeyNavigation(int p_key, bool &p_succeed)
|
||||
|
||||
bool VNotebookSelector::handlePopupKeyPress(QKeyEvent *p_event)
|
||||
{
|
||||
int key = p_event->key();
|
||||
int modifiers = p_event->modifiers();
|
||||
switch (key) {
|
||||
case Qt::Key_BracketLeft:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
p_event->accept();
|
||||
hidePopup();
|
||||
if (VimNavigationForWidget::injectKeyPressEventForVim(m_listWidget,
|
||||
p_event)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_J:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
p_event->accept();
|
||||
QKeyEvent *downEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(m_listWidget, downEvent);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_K:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
p_event->accept();
|
||||
QKeyEvent *upEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(m_listWidget, upEvent);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -194,6 +194,7 @@ void VOpenedListMenu::keyPressEvent(QKeyEvent *p_event)
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -201,7 +202,7 @@ void VOpenedListMenu::keyPressEvent(QKeyEvent *p_event)
|
||||
{
|
||||
m_cmdTimer->stop();
|
||||
m_cmdNum = 0;
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
QList<QAction *> acts = actions();
|
||||
if (acts.size() == 0) {
|
||||
return;
|
||||
@ -230,6 +231,7 @@ void VOpenedListMenu::keyPressEvent(QKeyEvent *p_event)
|
||||
setActiveAction(act);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -237,11 +239,12 @@ void VOpenedListMenu::keyPressEvent(QKeyEvent *p_event)
|
||||
{
|
||||
m_cmdTimer->stop();
|
||||
m_cmdNum = 0;
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
if (VUtils::isControlModifierForVim(modifiers)) {
|
||||
QList<QAction *> acts = actions();
|
||||
if (acts.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = acts.size() - 1;
|
||||
QAction *act = activeAction();
|
||||
if (act) {
|
||||
@ -266,6 +269,7 @@ void VOpenedListMenu::keyPressEvent(QKeyEvent *p_event)
|
||||
setActiveAction(act);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -274,6 +278,7 @@ void VOpenedListMenu::keyPressEvent(QKeyEvent *p_event)
|
||||
m_cmdNum = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
QMenu::keyPressEvent(p_event);
|
||||
}
|
||||
|
||||
|
100
src/voutline.cpp
100
src/voutline.cpp
@ -221,108 +221,18 @@ void VOutline::keyPressEvent(QKeyEvent *event)
|
||||
QTreeWidget::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void VOutline::registerNavigation(QChar p_majorKey)
|
||||
{
|
||||
m_majorKey = p_majorKey;
|
||||
V_ASSERT(m_keyMap.empty());
|
||||
V_ASSERT(m_naviLabels.empty());
|
||||
}
|
||||
|
||||
void VOutline::showNavigation()
|
||||
{
|
||||
// Clean up.
|
||||
m_keyMap.clear();
|
||||
for (auto label : m_naviLabels) {
|
||||
delete label;
|
||||
}
|
||||
m_naviLabels.clear();
|
||||
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate labels for visible items.
|
||||
auto items = getVisibleItems();
|
||||
for (int i = 0; i < 26 && i < items.size(); ++i) {
|
||||
QChar key('a' + i);
|
||||
m_keyMap[key] = items[i];
|
||||
|
||||
QString str = QString(m_majorKey) + key;
|
||||
QLabel *label = new QLabel(str, this);
|
||||
label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
|
||||
label->move(visualItemRect(items[i]).topLeft());
|
||||
label->show();
|
||||
m_naviLabels.append(label);
|
||||
}
|
||||
}
|
||||
|
||||
void VOutline::hideNavigation()
|
||||
{
|
||||
m_keyMap.clear();
|
||||
for (auto label : m_naviLabels) {
|
||||
delete label;
|
||||
}
|
||||
m_naviLabels.clear();
|
||||
VNavigationMode::showNavigation(this);
|
||||
}
|
||||
|
||||
bool VOutline::handleKeyNavigation(int p_key, bool &p_succeed)
|
||||
{
|
||||
static bool secondKey = false;
|
||||
bool ret = false;
|
||||
p_succeed = false;
|
||||
QChar keyChar = VUtils::keyToChar(p_key);
|
||||
if (secondKey && !keyChar.isNull()) {
|
||||
secondKey = false;
|
||||
p_succeed = true;
|
||||
ret = true;
|
||||
auto it = m_keyMap.find(keyChar);
|
||||
if (it != m_keyMap.end()) {
|
||||
setCurrentItem(it.value());
|
||||
setFocus();
|
||||
}
|
||||
} else if (keyChar == m_majorKey) {
|
||||
// Major key pressed.
|
||||
// Need second key if m_keyMap is not empty.
|
||||
if (m_keyMap.isEmpty()) {
|
||||
p_succeed = true;
|
||||
} else {
|
||||
secondKey = true;
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem *> VOutline::getVisibleItems() const
|
||||
{
|
||||
QList<QTreeWidgetItem *> items;
|
||||
for (int i = 0; i < topLevelItemCount(); ++i) {
|
||||
QTreeWidgetItem *item = topLevelItem(i);
|
||||
if (!item->isHidden()) {
|
||||
items.append(item);
|
||||
if (item->isExpanded()) {
|
||||
items.append(getVisibleChildItems(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem *> VOutline::getVisibleChildItems(const QTreeWidgetItem *p_item) const
|
||||
{
|
||||
QList<QTreeWidgetItem *> items;
|
||||
if (p_item && !p_item->isHidden() && p_item->isExpanded()) {
|
||||
for (int i = 0; i < p_item->childCount(); ++i) {
|
||||
QTreeWidgetItem *child = p_item->child(i);
|
||||
if (!child->isHidden()) {
|
||||
items.append(child);
|
||||
if (child->isExpanded()) {
|
||||
items.append(getVisibleChildItems(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return items;
|
||||
return VNavigationMode::handleKeyNavigation(this,
|
||||
secondKey,
|
||||
p_key,
|
||||
p_succeed);
|
||||
}
|
||||
|
||||
const VTableOfContentItem *VOutline::getHeaderFromItem(QTreeWidgetItem *p_item) const
|
||||
|
@ -19,9 +19,7 @@ public:
|
||||
VOutline(QWidget *parent = 0);
|
||||
|
||||
// Implementations for VNavigationMode.
|
||||
void registerNavigation(QChar p_majorKey) Q_DECL_OVERRIDE;
|
||||
void showNavigation() Q_DECL_OVERRIDE;
|
||||
void hideNavigation() Q_DECL_OVERRIDE;
|
||||
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
|
||||
|
||||
signals:
|
||||
@ -64,9 +62,6 @@ private:
|
||||
|
||||
bool selectHeaderOne(QTreeWidgetItem *p_item, const VHeaderPointer &p_header);
|
||||
|
||||
QList<QTreeWidgetItem *> getVisibleItems() const;
|
||||
QList<QTreeWidgetItem *> getVisibleChildItems(const QTreeWidgetItem *p_item) const;
|
||||
|
||||
// Fill the info of @p_item.
|
||||
void fillItem(QTreeWidgetItem *p_item, const VTableOfContentItem &p_header);
|
||||
|
||||
@ -79,11 +74,6 @@ private:
|
||||
|
||||
// When true, won't emit outlineItemActivated().
|
||||
bool m_muted;
|
||||
|
||||
// Navigation Mode.
|
||||
// Map second key to QTreeWidgetItem.
|
||||
QMap<QChar, QTreeWidgetItem *> m_keyMap;
|
||||
QVector<QLabel *> m_naviLabels;
|
||||
};
|
||||
|
||||
#endif // VOUTLINE_H
|
||||
|
207
src/vsnippet.cpp
Normal file
207
src/vsnippet.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
#include "vsnippet.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include "vconstants.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "utils/veditutils.h"
|
||||
#include "utils/vmetawordmanager.h"
|
||||
|
||||
extern VMetaWordManager *g_mwMgr;
|
||||
|
||||
const QString VSnippet::c_defaultCursorMark = "@@";
|
||||
|
||||
const QString VSnippet::c_defaultSelectionMark = "$$";
|
||||
|
||||
QVector<QChar> VSnippet::s_allShortcuts;
|
||||
|
||||
VSnippet::VSnippet()
|
||||
: m_type(Type::PlainText),
|
||||
m_cursorMark(c_defaultCursorMark)
|
||||
{
|
||||
}
|
||||
|
||||
VSnippet::VSnippet(const QString &p_name,
|
||||
Type p_type,
|
||||
const QString &p_content,
|
||||
const QString &p_cursorMark,
|
||||
const QString &p_selectionMark,
|
||||
QChar p_shortcut)
|
||||
: m_name(p_name),
|
||||
m_type(p_type),
|
||||
m_content(p_content),
|
||||
m_cursorMark(p_cursorMark),
|
||||
m_selectionMark(p_selectionMark),
|
||||
m_shortcut(p_shortcut)
|
||||
{
|
||||
Q_ASSERT(m_selectionMark != m_cursorMark);
|
||||
}
|
||||
|
||||
bool VSnippet::update(const QString &p_name,
|
||||
Type p_type,
|
||||
const QString &p_content,
|
||||
const QString &p_cursorMark,
|
||||
const QString &p_selectionMark,
|
||||
QChar p_shortcut)
|
||||
{
|
||||
bool updated = false;
|
||||
if (m_name != p_name) {
|
||||
m_name = p_name;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (m_type != p_type) {
|
||||
m_type = p_type;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (m_content != p_content) {
|
||||
m_content = p_content;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (m_cursorMark != p_cursorMark) {
|
||||
m_cursorMark = p_cursorMark;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (m_selectionMark != p_selectionMark) {
|
||||
m_selectionMark = p_selectionMark;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (m_shortcut != p_shortcut) {
|
||||
m_shortcut = p_shortcut;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
qDebug() << "snippet" << m_name << "updated" << updated;
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
QString VSnippet::typeStr(VSnippet::Type p_type)
|
||||
{
|
||||
switch (p_type) {
|
||||
case Type::PlainText:
|
||||
return QObject::tr("PlainText");
|
||||
|
||||
case Type::Html:
|
||||
return QObject::tr("Html");
|
||||
|
||||
default:
|
||||
return QObject::tr("Invalid");
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject VSnippet::toJson() const
|
||||
{
|
||||
QJsonObject snip;
|
||||
snip[SnippetConfig::c_name] = m_name;
|
||||
snip[SnippetConfig::c_type] = (int)m_type;
|
||||
snip[SnippetConfig::c_cursorMark] = m_cursorMark;
|
||||
snip[SnippetConfig::c_selectionMark] = m_selectionMark;
|
||||
snip[SnippetConfig::c_shortcut] = m_shortcut.isNull() ? "" : QString(m_shortcut);
|
||||
|
||||
return snip;
|
||||
}
|
||||
|
||||
VSnippet VSnippet::fromJson(const QJsonObject &p_json)
|
||||
{
|
||||
QChar shortcut;
|
||||
QString shortcutStr = p_json[SnippetConfig::c_shortcut].toString();
|
||||
if (!shortcutStr.isEmpty() && isValidShortcut(shortcutStr[0])) {
|
||||
shortcut = shortcutStr[0];
|
||||
}
|
||||
|
||||
VSnippet snip(p_json[SnippetConfig::c_name].toString(),
|
||||
static_cast<VSnippet::Type>(p_json[SnippetConfig::c_type].toInt()),
|
||||
"",
|
||||
p_json[SnippetConfig::c_cursorMark].toString(),
|
||||
p_json[SnippetConfig::c_selectionMark].toString(),
|
||||
shortcut);
|
||||
|
||||
return snip;
|
||||
}
|
||||
|
||||
const QVector<QChar> &VSnippet::getAllShortcuts()
|
||||
{
|
||||
if (s_allShortcuts.isEmpty()) {
|
||||
// Init.
|
||||
char ch = 'a';
|
||||
while (true) {
|
||||
s_allShortcuts.append(ch);
|
||||
if (ch == 'z') {
|
||||
break;
|
||||
}
|
||||
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
|
||||
return s_allShortcuts;
|
||||
}
|
||||
|
||||
bool VSnippet::isValidShortcut(QChar p_char)
|
||||
{
|
||||
if (p_char >= 'a' && p_char <= 'z') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VSnippet::apply(QTextCursor &p_cursor) const
|
||||
{
|
||||
p_cursor.beginEditBlock();
|
||||
// Delete selected text.
|
||||
QString selection = VEditUtils::selectedText(p_cursor);
|
||||
p_cursor.removeSelectedText();
|
||||
|
||||
// Evaluate the content.
|
||||
QString content = g_mwMgr->evaluate(m_content);
|
||||
|
||||
// Find the cursor mark and break the content.
|
||||
QString secondPart;
|
||||
if (!m_cursorMark.isEmpty()) {
|
||||
QStringList parts = content.split(m_cursorMark, QString::SkipEmptyParts);
|
||||
Q_ASSERT(parts.size() < 3);
|
||||
|
||||
content = parts[0];
|
||||
if (parts.size() == 2) {
|
||||
secondPart = parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the selection mark.
|
||||
if (!m_selectionMark.isEmpty()) {
|
||||
content.replace(m_selectionMark, selection);
|
||||
}
|
||||
|
||||
int pos = p_cursor.position() + content.size();
|
||||
|
||||
if (!secondPart.isEmpty()) {
|
||||
secondPart.replace(m_selectionMark, selection);
|
||||
content += secondPart;
|
||||
}
|
||||
|
||||
// Insert it.
|
||||
switch (m_type) {
|
||||
case Type::Html:
|
||||
p_cursor.insertHtml(content);
|
||||
// TODO: set the position of the cursor.
|
||||
break;
|
||||
|
||||
case Type::PlainText:
|
||||
V_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
p_cursor.insertText(content);
|
||||
p_cursor.setPosition(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
p_cursor.endEditBlock();
|
||||
return true;
|
||||
}
|
113
src/vsnippet.h
Normal file
113
src/vsnippet.h
Normal file
@ -0,0 +1,113 @@
|
||||
#ifndef VSNIPPET_H
|
||||
#define VSNIPPET_H
|
||||
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QTextCursor>
|
||||
|
||||
|
||||
class VSnippet
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
PlainText = 0,
|
||||
Html,
|
||||
Invalid
|
||||
};
|
||||
|
||||
VSnippet();
|
||||
|
||||
VSnippet(const QString &p_name,
|
||||
Type p_type = Type::PlainText,
|
||||
const QString &p_content = QString(),
|
||||
const QString &p_cursorMark = c_defaultCursorMark,
|
||||
const QString &p_selectionMark = c_defaultSelectionMark,
|
||||
QChar p_shortcut = QChar());
|
||||
|
||||
// Return true if there is any update.
|
||||
bool update(const QString &p_name,
|
||||
Type p_type,
|
||||
const QString &p_content,
|
||||
const QString &p_cursorMark,
|
||||
const QString &p_selectionMark,
|
||||
QChar p_shortcut);
|
||||
|
||||
const QString &getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
VSnippet::Type getType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
const QString &getCursorMark() const
|
||||
{
|
||||
return m_cursorMark;
|
||||
}
|
||||
|
||||
const QString &getSelectionMark() const
|
||||
{
|
||||
return m_selectionMark;
|
||||
}
|
||||
|
||||
const QString &getContent() const
|
||||
{
|
||||
return m_content;
|
||||
}
|
||||
|
||||
QChar getShortcut() const
|
||||
{
|
||||
return m_shortcut;
|
||||
}
|
||||
|
||||
void setContent(const QString &p_content)
|
||||
{
|
||||
m_content = p_content;
|
||||
}
|
||||
|
||||
// Not including m_content.
|
||||
QJsonObject toJson() const;
|
||||
|
||||
// Apply this snippet via @p_cursor.
|
||||
bool apply(QTextCursor &p_cursor) const;
|
||||
|
||||
// Not including m_content.
|
||||
static VSnippet fromJson(const QJsonObject &p_json);
|
||||
|
||||
static QString typeStr(VSnippet::Type p_type);
|
||||
|
||||
static const QVector<QChar> &getAllShortcuts();
|
||||
|
||||
static bool isValidShortcut(QChar p_char);
|
||||
|
||||
private:
|
||||
// File name in the snippet folder.
|
||||
QString m_name;
|
||||
|
||||
Type m_type;
|
||||
|
||||
// Support magic word.
|
||||
QString m_content;
|
||||
|
||||
// String in the content that mark the position of the cursor after insertion.
|
||||
// If there is no such mark in the content, the cursor should be put at the
|
||||
// end of the insertion.
|
||||
QString m_cursorMark;
|
||||
|
||||
// Selection marks in the content will be replaced by selected text.
|
||||
QString m_selectionMark;
|
||||
|
||||
// Shortcut to apply this snippet.
|
||||
QChar m_shortcut;
|
||||
|
||||
static const QString c_defaultCursorMark;
|
||||
|
||||
static const QString c_defaultSelectionMark;
|
||||
|
||||
static QVector<QChar> s_allShortcuts;
|
||||
};
|
||||
|
||||
#endif // VSNIPPET_H
|
606
src/vsnippetlist.cpp
Normal file
606
src/vsnippetlist.cpp
Normal file
@ -0,0 +1,606 @@
|
||||
#include "vsnippetlist.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "vconfigmanager.h"
|
||||
#include "dialog/veditsnippetdialog.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "utils/vimnavigationforwidget.h"
|
||||
#include "dialog/vsortdialog.h"
|
||||
#include "dialog/vconfirmdeletiondialog.h"
|
||||
#include "vmainwindow.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
extern VMainWindow *g_mainWin;
|
||||
|
||||
const QString VSnippetList::c_infoShortcutSequence = "F2";
|
||||
|
||||
VSnippetList::VSnippetList(QWidget *p_parent)
|
||||
: QWidget(p_parent)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
initShortcuts();
|
||||
|
||||
initActions();
|
||||
|
||||
if (!readSnippetsFromConfig()) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to read snippets from <span style=\"%1\">%2</span>.")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(g_config->getSnippetConfigFolder()),
|
||||
"",
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
}
|
||||
|
||||
updateContent();
|
||||
}
|
||||
|
||||
void VSnippetList::setupUI()
|
||||
{
|
||||
m_addBtn = new QPushButton(QIcon(":/resources/icons/add_snippet.svg"), "");
|
||||
m_addBtn->setToolTip(tr("New Snippet"));
|
||||
m_addBtn->setProperty("FlatBtn", true);
|
||||
connect(m_addBtn, &QPushButton::clicked,
|
||||
this, &VSnippetList::newSnippet);
|
||||
|
||||
m_locateBtn = new QPushButton(QIcon(":/resources/icons/locate_snippet.svg"), "");
|
||||
m_locateBtn->setToolTip(tr("Open Folder"));
|
||||
m_locateBtn->setProperty("FlatBtn", true);
|
||||
connect(m_locateBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
makeSureFolderExist();
|
||||
QUrl url = QUrl::fromLocalFile(g_config->getSnippetConfigFolder());
|
||||
QDesktopServices::openUrl(url);
|
||||
});
|
||||
|
||||
m_numLabel = new QLabel();
|
||||
|
||||
QHBoxLayout *btnLayout = new QHBoxLayout;
|
||||
btnLayout->addWidget(m_addBtn);
|
||||
btnLayout->addWidget(m_locateBtn);
|
||||
btnLayout->addStretch();
|
||||
btnLayout->addWidget(m_numLabel);
|
||||
btnLayout->setContentsMargins(0, 0, 3, 0);
|
||||
|
||||
m_snippetList = new QListWidget();
|
||||
m_snippetList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_snippetList->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
m_snippetList->setEditTriggers(QAbstractItemView::SelectedClicked);
|
||||
connect(m_snippetList, &QListWidget::customContextMenuRequested,
|
||||
this, &VSnippetList::handleContextMenuRequested);
|
||||
connect(m_snippetList, &QListWidget::itemActivated,
|
||||
this, &VSnippetList::handleItemActivated);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(btnLayout);
|
||||
mainLayout->addWidget(m_snippetList);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
void VSnippetList::initActions()
|
||||
{
|
||||
m_applyAct = new QAction(QIcon(":/resources/icons/apply_snippet.svg"),
|
||||
tr("&Apply"),
|
||||
this);
|
||||
m_applyAct->setToolTip(tr("Insert this snippet in editor"));
|
||||
connect(m_applyAct, &QAction::triggered,
|
||||
this, [this]() {
|
||||
QListWidgetItem *item = m_snippetList->currentItem();
|
||||
handleItemActivated(item);
|
||||
});
|
||||
|
||||
m_infoAct = new QAction(QIcon(":/resources/icons/snippet_info.svg"),
|
||||
tr("&Info\t%1").arg(VUtils::getShortcutText(c_infoShortcutSequence)),
|
||||
this);
|
||||
m_infoAct->setToolTip(tr("View and edit snippet's information"));
|
||||
connect(m_infoAct, &QAction::triggered,
|
||||
this, &VSnippetList::snippetInfo);
|
||||
|
||||
m_deleteAct = new QAction(QIcon(":/resources/icons/delete_snippet.svg"),
|
||||
tr("&Delete"),
|
||||
this);
|
||||
m_deleteAct->setToolTip(tr("Delete selected snippets"));
|
||||
connect(m_deleteAct, &QAction::triggered,
|
||||
this, &VSnippetList::deleteSelectedItems);
|
||||
|
||||
m_sortAct = new QAction(QIcon(":/resources/icons/sort.svg"),
|
||||
tr("&Sort"),
|
||||
this);
|
||||
m_sortAct->setToolTip(tr("Sort snippets manually"));
|
||||
connect(m_sortAct, &QAction::triggered,
|
||||
this, &VSnippetList::sortItems);
|
||||
}
|
||||
|
||||
void VSnippetList::initShortcuts()
|
||||
{
|
||||
QShortcut *infoShortcut = new QShortcut(QKeySequence(c_infoShortcutSequence), this);
|
||||
infoShortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
connect(infoShortcut, &QShortcut::activated,
|
||||
this, &VSnippetList::snippetInfo);
|
||||
}
|
||||
|
||||
void VSnippetList::newSnippet()
|
||||
{
|
||||
QString defaultName = VUtils::getFileNameWithSequence(g_config->getSnippetConfigFolder(),
|
||||
"snippet");
|
||||
|
||||
VSnippet tmpSnippet(defaultName);
|
||||
QString info = tr("Magic words are supported in the content of the snippet.");
|
||||
VEditSnippetDialog dialog(tr("Create Snippet"),
|
||||
info,
|
||||
m_snippets,
|
||||
tmpSnippet,
|
||||
this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
makeSureFolderExist();
|
||||
VSnippet snippet(dialog.getNameInput(),
|
||||
dialog.getTypeInput(),
|
||||
dialog.getContentInput(),
|
||||
dialog.getCursorMarkInput(),
|
||||
dialog.getSelectionMarkInput());
|
||||
|
||||
QString errMsg;
|
||||
if (!addSnippet(snippet, &errMsg)) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to create snippet <span style=\"%1\">%2</span>.")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(snippet.getName()),
|
||||
errMsg,
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
|
||||
updateContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VSnippetList::handleContextMenuRequested(QPoint p_pos)
|
||||
{
|
||||
QListWidgetItem *item = m_snippetList->itemAt(p_pos);
|
||||
QMenu menu(this);
|
||||
menu.setToolTipsVisible(true);
|
||||
|
||||
if (item) {
|
||||
int itemCount = m_snippetList->selectedItems().size();
|
||||
if (itemCount == 1) {
|
||||
menu.addAction(m_applyAct);
|
||||
menu.addAction(m_infoAct);
|
||||
}
|
||||
|
||||
menu.addAction(m_deleteAct);
|
||||
}
|
||||
|
||||
m_snippetList->update();
|
||||
|
||||
if (m_snippets.size() > 1) {
|
||||
if (!menu.actions().isEmpty()) {
|
||||
menu.addSeparator();
|
||||
}
|
||||
|
||||
menu.addAction(m_sortAct);
|
||||
}
|
||||
|
||||
if (!menu.actions().isEmpty()) {
|
||||
menu.exec(m_snippetList->mapToGlobal(p_pos));
|
||||
}
|
||||
}
|
||||
|
||||
void VSnippetList::handleItemActivated(QListWidgetItem *p_item)
|
||||
{
|
||||
const VSnippet *snip = getSnippet(p_item);
|
||||
if (snip) {
|
||||
VEditTab *tab = g_mainWin->getCurrentTab();
|
||||
if (tab) {
|
||||
tab->applySnippet(snip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VSnippetList::deleteSelectedItems()
|
||||
{
|
||||
QVector<ConfirmItemInfo> items;
|
||||
const QList<QListWidgetItem *> selectedItems = m_snippetList->selectedItems();
|
||||
|
||||
if (selectedItems.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const & item : selectedItems) {
|
||||
items.push_back(ConfirmItemInfo(item->text(),
|
||||
item->text(),
|
||||
"",
|
||||
NULL));
|
||||
}
|
||||
|
||||
QString text = tr("Are you sure to delete these snippets?");
|
||||
QString info = tr("Click \"Cancel\" to leave them untouched.");
|
||||
VConfirmDeletionDialog dialog(tr("Confirm Deleting Snippets"),
|
||||
text,
|
||||
info,
|
||||
items,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
this);
|
||||
if (dialog.exec()) {
|
||||
items = dialog.getConfirmedItems();
|
||||
|
||||
QList<QString> names;
|
||||
for (auto const & item : items) {
|
||||
names.append(item.m_name);
|
||||
}
|
||||
|
||||
QString errMsg;
|
||||
if (!deleteSnippets(names, &errMsg)) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to delete snippets."),
|
||||
errMsg,
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
}
|
||||
|
||||
updateContent();
|
||||
}
|
||||
}
|
||||
|
||||
void VSnippetList::sortItems()
|
||||
{
|
||||
if (m_snippets.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
VSortDialog dialog(tr("Sort Snippets"),
|
||||
tr("Sort snippets in the configuration file."),
|
||||
this);
|
||||
QTreeWidget *tree = dialog.getTreeWidget();
|
||||
tree->clear();
|
||||
tree->setColumnCount(1);
|
||||
QStringList headers;
|
||||
headers << tr("Name");
|
||||
tree->setHeaderLabels(headers);
|
||||
|
||||
for (int i = 0; i < m_snippets.size(); ++i) {
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(tree, QStringList(m_snippets[i].getName()));
|
||||
|
||||
item->setData(0, Qt::UserRole, i);
|
||||
}
|
||||
|
||||
dialog.treeUpdated();
|
||||
|
||||
if (dialog.exec()) {
|
||||
QVector<QVariant> data = dialog.getSortedData();
|
||||
Q_ASSERT(data.size() == m_snippets.size());
|
||||
QVector<int> sortedIdx(data.size(), -1);
|
||||
for (int i = 0; i < data.size(); ++i) {
|
||||
sortedIdx[i] = data[i].toInt();
|
||||
}
|
||||
|
||||
QString errMsg;
|
||||
if (!sortSnippets(sortedIdx, &errMsg)) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to sort snippets."),
|
||||
errMsg,
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
}
|
||||
|
||||
updateContent();
|
||||
}
|
||||
}
|
||||
|
||||
void VSnippetList::snippetInfo()
|
||||
{
|
||||
if (m_snippetList->selectedItems().size() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem *item = m_snippetList->currentItem();
|
||||
VSnippet *snip = getSnippet(item);
|
||||
if (!snip) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString info = tr("Magic words are supported in the content of the snippet.");
|
||||
VEditSnippetDialog dialog(tr("Snippet Information"),
|
||||
info,
|
||||
m_snippets,
|
||||
*snip,
|
||||
this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QString errMsg;
|
||||
bool ret = true;
|
||||
if (snip->getName() != dialog.getNameInput()) {
|
||||
// Delete the original snippet file.
|
||||
if (!deleteSnippetFile(*snip, &errMsg)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (snip->update(dialog.getNameInput(),
|
||||
dialog.getTypeInput(),
|
||||
dialog.getContentInput(),
|
||||
dialog.getCursorMarkInput(),
|
||||
dialog.getSelectionMarkInput(),
|
||||
dialog.getShortcutInput())) {
|
||||
if (!writeSnippetFile(*snip, &errMsg)) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (!writeSnippetsToConfig()) {
|
||||
VUtils::addErrMsg(&errMsg,
|
||||
tr("Fail to write snippets configuration file."));
|
||||
ret = false;
|
||||
}
|
||||
|
||||
updateContent();
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to update information of snippet <span style=\"%1\">%2</span>.")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(snip->getName()),
|
||||
errMsg,
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VSnippetList::makeSureFolderExist() const
|
||||
{
|
||||
QString path = g_config->getSnippetConfigFolder();
|
||||
if (!QFileInfo::exists(path)) {
|
||||
QDir dir;
|
||||
dir.mkpath(path);
|
||||
}
|
||||
}
|
||||
|
||||
void VSnippetList::updateContent()
|
||||
{
|
||||
m_snippetList->clear();
|
||||
|
||||
for (int i = 0; i < m_snippets.size(); ++i) {
|
||||
const VSnippet &snip = m_snippets[i];
|
||||
QListWidgetItem *item = new QListWidgetItem(snip.getName());
|
||||
item->setToolTip(snip.getName());
|
||||
item->setData(Qt::UserRole, snip.getName());
|
||||
|
||||
m_snippetList->addItem(item);
|
||||
}
|
||||
|
||||
int cnt = m_snippetList->count();
|
||||
if (cnt > 0) {
|
||||
m_numLabel->setText(tr("%1 %2").arg(cnt)
|
||||
.arg(cnt > 1 ? tr("Snippets") : tr("Snippet")));
|
||||
m_snippetList->setFocus();
|
||||
} else {
|
||||
m_numLabel->setText("");
|
||||
m_addBtn->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
bool VSnippetList::addSnippet(const VSnippet &p_snippet, QString *p_errMsg)
|
||||
{
|
||||
if (!writeSnippetFile(p_snippet, p_errMsg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_snippets.push_back(p_snippet);
|
||||
|
||||
bool ret = true;
|
||||
if (!writeSnippetsToConfig()) {
|
||||
VUtils::addErrMsg(p_errMsg,
|
||||
tr("Fail to write snippets configuration file."));
|
||||
m_snippets.pop_back();
|
||||
ret = false;
|
||||
}
|
||||
|
||||
updateContent();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VSnippetList::writeSnippetsToConfig() const
|
||||
{
|
||||
makeSureFolderExist();
|
||||
|
||||
QJsonObject snippetJson;
|
||||
snippetJson[SnippetConfig::c_version] = "1";
|
||||
|
||||
QJsonArray snippetArray;
|
||||
for (int i = 0; i < m_snippets.size(); ++i) {
|
||||
snippetArray.append(m_snippets[i].toJson());
|
||||
}
|
||||
|
||||
snippetJson[SnippetConfig::c_snippets] = snippetArray;
|
||||
|
||||
return VUtils::writeJsonToDisk(g_config->getSnippetConfigFilePath(),
|
||||
snippetJson);
|
||||
}
|
||||
|
||||
bool VSnippetList::readSnippetsFromConfig()
|
||||
{
|
||||
m_snippets.clear();
|
||||
|
||||
if (!QFileInfo::exists(g_config->getSnippetConfigFilePath())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject snippets = VUtils::readJsonFromDisk(g_config->getSnippetConfigFilePath());
|
||||
if (snippets.isEmpty()) {
|
||||
qWarning() << "invalid snippets configuration file" << g_config->getSnippetConfigFilePath();
|
||||
return false;
|
||||
}
|
||||
|
||||
// [snippets] section.
|
||||
bool ret = true;
|
||||
QJsonArray snippetArray = snippets[SnippetConfig::c_snippets].toArray();
|
||||
for (int i = 0; i < snippetArray.size(); ++i) {
|
||||
VSnippet snip = VSnippet::fromJson(snippetArray[i].toObject());
|
||||
|
||||
// Read the content.
|
||||
QString filePath(QDir(g_config->getSnippetConfigFolder()).filePath(snip.getName()));
|
||||
QString content = VUtils::readFileFromDisk(filePath);
|
||||
if (content.isNull()) {
|
||||
qWarning() << "fail to read snippet" << snip.getName();
|
||||
ret = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
snip.setContent(content);
|
||||
m_snippets.push_back(snip);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VSnippetList::keyPressEvent(QKeyEvent *p_event)
|
||||
{
|
||||
if (VimNavigationForWidget::injectKeyPressEventForVim(m_snippetList,
|
||||
p_event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QWidget::keyPressEvent(p_event);
|
||||
}
|
||||
|
||||
void VSnippetList::showNavigation()
|
||||
{
|
||||
VNavigationMode::showNavigation(m_snippetList);
|
||||
}
|
||||
|
||||
bool VSnippetList::handleKeyNavigation(int p_key, bool &p_succeed)
|
||||
{
|
||||
static bool secondKey = false;
|
||||
return VNavigationMode::handleKeyNavigation(m_snippetList,
|
||||
secondKey,
|
||||
p_key,
|
||||
p_succeed);
|
||||
}
|
||||
|
||||
int VSnippetList::getSnippetIndex(QListWidgetItem *p_item) const
|
||||
{
|
||||
if (!p_item) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString name = p_item->data(Qt::UserRole).toString();
|
||||
for (int i = 0; i < m_snippets.size(); ++i) {
|
||||
if (m_snippets[i].getName() == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
VSnippet *VSnippetList::getSnippet(QListWidgetItem *p_item)
|
||||
{
|
||||
int idx = getSnippetIndex(p_item);
|
||||
if (idx == -1) {
|
||||
return NULL;
|
||||
} else {
|
||||
return &m_snippets[idx];
|
||||
}
|
||||
}
|
||||
|
||||
bool VSnippetList::writeSnippetFile(const VSnippet &p_snippet, QString *p_errMsg)
|
||||
{
|
||||
// Create and write to the snippet file.
|
||||
QString filePath = getSnippetFilePath(p_snippet);
|
||||
if (!VUtils::writeFileToDisk(filePath, p_snippet.getContent())) {
|
||||
VUtils::addErrMsg(p_errMsg,
|
||||
tr("Fail to add write the snippet file %1.")
|
||||
.arg(filePath));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString VSnippetList::getSnippetFilePath(const VSnippet &p_snippet) const
|
||||
{
|
||||
return QDir(g_config->getSnippetConfigFolder()).filePath(p_snippet.getName());
|
||||
}
|
||||
|
||||
bool VSnippetList::sortSnippets(const QVector<int> &p_sortedIdx, QString *p_errMsg)
|
||||
{
|
||||
V_ASSERT(p_sortedIdx.size() == m_snippets.size());
|
||||
|
||||
auto ori = m_snippets;
|
||||
|
||||
for (int i = 0; i < p_sortedIdx.size(); ++i) {
|
||||
m_snippets[i] = ori[p_sortedIdx[i]];
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
if (!writeSnippetsToConfig()) {
|
||||
VUtils::addErrMsg(p_errMsg,
|
||||
tr("Fail to write snippets configuration file."));
|
||||
m_snippets = ori;
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VSnippetList::deleteSnippets(const QList<QString> &p_snippets,
|
||||
QString *p_errMsg)
|
||||
{
|
||||
if (p_snippets.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
QSet<QString> targets = QSet<QString>::fromList(p_snippets);
|
||||
for (auto it = m_snippets.begin(); it != m_snippets.end();) {
|
||||
if (targets.contains(it->getName())) {
|
||||
// Remove it.
|
||||
if (!deleteSnippetFile(*it, p_errMsg)) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
it = m_snippets.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (!writeSnippetsToConfig()) {
|
||||
VUtils::addErrMsg(p_errMsg,
|
||||
tr("Fail to write snippets configuration file."));
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VSnippetList::deleteSnippetFile(const VSnippet &p_snippet, QString *p_errMsg)
|
||||
{
|
||||
QString filePath = getSnippetFilePath(p_snippet);
|
||||
if (!VUtils::deleteFile(filePath)) {
|
||||
VUtils::addErrMsg(p_errMsg,
|
||||
tr("Fail to remove snippet file %1.")
|
||||
.arg(filePath));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
98
src/vsnippetlist.h
Normal file
98
src/vsnippetlist.h
Normal file
@ -0,0 +1,98 @@
|
||||
#ifndef VSNIPPETLIST_H
|
||||
#define VSNIPPETLIST_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QPoint>
|
||||
|
||||
#include "vsnippet.h"
|
||||
#include "vnavigationmode.h"
|
||||
|
||||
class QPushButton;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QLabel;
|
||||
class QAction;
|
||||
class QKeyEvent;
|
||||
|
||||
|
||||
class VSnippetList : public QWidget, public VNavigationMode
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VSnippetList(QWidget *p_parent = nullptr);
|
||||
|
||||
// Implementations for VNavigationMode.
|
||||
void showNavigation() Q_DECL_OVERRIDE;
|
||||
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void newSnippet();
|
||||
|
||||
void handleContextMenuRequested(QPoint p_pos);
|
||||
|
||||
void handleItemActivated(QListWidgetItem *p_item);
|
||||
|
||||
void snippetInfo();
|
||||
|
||||
void deleteSelectedItems();
|
||||
|
||||
void sortItems();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void initActions();
|
||||
|
||||
void initShortcuts();
|
||||
|
||||
void makeSureFolderExist() const;
|
||||
|
||||
// Update list of snippets according to m_snippets.
|
||||
void updateContent();
|
||||
|
||||
// Add @p_snippet.
|
||||
bool addSnippet(const VSnippet &p_snippet, QString *p_errMsg = nullptr);
|
||||
|
||||
// Write m_snippets to config file.
|
||||
bool writeSnippetsToConfig() const;
|
||||
|
||||
// Read from config file to m_snippets.
|
||||
bool readSnippetsFromConfig();
|
||||
|
||||
// Get the snippet index in m_snippets of @p_item.
|
||||
int getSnippetIndex(QListWidgetItem *p_item) const;
|
||||
|
||||
VSnippet *getSnippet(QListWidgetItem *p_item);
|
||||
|
||||
// Write the content of @p_snippet to file.
|
||||
bool writeSnippetFile(const VSnippet &p_snippet, QString *p_errMsg = nullptr);
|
||||
|
||||
QString getSnippetFilePath(const VSnippet &p_snippet) const;
|
||||
|
||||
// Sort m_snippets according to @p_sortedIdx.
|
||||
bool sortSnippets(const QVector<int> &p_sortedIdx, QString *p_errMsg = nullptr);
|
||||
|
||||
bool deleteSnippets(const QList<QString> &p_snippets, QString *p_errMsg = nullptr);
|
||||
|
||||
bool deleteSnippetFile(const VSnippet &p_snippet, QString *p_errMsg = nullptr);
|
||||
|
||||
QPushButton *m_addBtn;
|
||||
QPushButton *m_locateBtn;
|
||||
QLabel *m_numLabel;
|
||||
QListWidget *m_snippetList;
|
||||
|
||||
QAction *m_applyAct;
|
||||
QAction *m_infoAct;
|
||||
QAction *m_deleteAct;
|
||||
QAction *m_sortAct;
|
||||
|
||||
QVector<VSnippet> m_snippets;
|
||||
|
||||
static const QString c_infoShortcutSequence;
|
||||
};
|
||||
|
||||
#endif // VSNIPPETLIST_H
|
Loading…
x
Reference in New Issue
Block a user