mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
History: support history
This commit is contained in:
parent
ef69ee435f
commit
cf988e6fa6
@ -1 +1 @@
|
||||
Subproject commit a15d859141506142a11cbf843a2d93124ea65bfa
|
||||
Subproject commit 47902164b0e11f5debfb40deb649d451d650660f
|
@ -105,7 +105,7 @@ void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenPar
|
||||
}
|
||||
|
||||
// Check if it is an internal node or not.
|
||||
auto node = loadNodeByPath(p_filePath);
|
||||
auto node = VNoteX::getInst().getNotebookMgr().loadNodeByPath(p_filePath);
|
||||
if (node) {
|
||||
if (node->hasContent()) {
|
||||
open(node.data(), p_paras);
|
||||
@ -187,16 +187,3 @@ void BufferMgr::addBuffer(Buffer *p_buffer)
|
||||
p_buffer->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
QSharedPointer<Node> BufferMgr::loadNodeByPath(const QString &p_path)
|
||||
{
|
||||
const auto ¬ebooks = VNoteX::getInst().getNotebookMgr().getNotebooks();
|
||||
for (const auto &nb : notebooks) {
|
||||
auto node = nb->loadNodeByPath(p_path);
|
||||
if (node) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -42,9 +42,6 @@ namespace vnotex
|
||||
|
||||
void addBuffer(Buffer *p_buffer);
|
||||
|
||||
// Try to load @p_path as a node if it is within one notebook.
|
||||
QSharedPointer<Node> loadNodeByPath(const QString &p_path);
|
||||
|
||||
QSharedPointer<NameBasedServer<IBufferFactory>> m_bufferServer;
|
||||
|
||||
// Managed by QObject.
|
||||
|
@ -17,6 +17,8 @@ SOURCES += \
|
||||
$$PWD/editorconfig.cpp \
|
||||
$$PWD/externalfile.cpp \
|
||||
$$PWD/file.cpp \
|
||||
$$PWD/historyitem.cpp \
|
||||
$$PWD/historymgr.cpp \
|
||||
$$PWD/htmltemplatehelper.cpp \
|
||||
$$PWD/logger.cpp \
|
||||
$$PWD/mainconfig.cpp \
|
||||
@ -43,6 +45,8 @@ HEADERS += \
|
||||
$$PWD/file.h \
|
||||
$$PWD/filelocator.h \
|
||||
$$PWD/fileopenparameters.h \
|
||||
$$PWD/historyitem.h \
|
||||
$$PWD/historymgr.h \
|
||||
$$PWD/htmltemplatehelper.h \
|
||||
$$PWD/location.h \
|
||||
$$PWD/logger.h \
|
||||
|
@ -51,6 +51,11 @@ void CoreConfig::init(const QJsonObject &p_app,
|
||||
loadNoteManagement(appObj, userObj);
|
||||
|
||||
m_recoverLastSessionOnStartEnabled = READBOOL(QStringLiteral("recover_last_session_on_start"));
|
||||
|
||||
m_historyMaxCount = READINT(QStringLiteral("history_max_count"));
|
||||
if (m_historyMaxCount < 0) {
|
||||
m_historyMaxCount = 100;
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject CoreConfig::toJson() const
|
||||
@ -61,6 +66,7 @@ QJsonObject CoreConfig::toJson() const
|
||||
obj[QStringLiteral("shortcuts")] = saveShortcuts();
|
||||
obj[QStringLiteral("toolbar_icon_size")] = m_toolBarIconSize;
|
||||
obj[QStringLiteral("recover_last_session_on_start")] = m_recoverLastSessionOnStartEnabled;
|
||||
obj[QStringLiteral("history_max_count")] = m_historyMaxCount;
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -162,3 +168,8 @@ void CoreConfig::setRecoverLastSessionOnStartEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_recoverLastSessionOnStartEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
int CoreConfig::getHistoryMaxCount() const
|
||||
{
|
||||
return m_historyMaxCount;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace vnotex
|
||||
SearchDock,
|
||||
SnippetDock,
|
||||
LocationListDock,
|
||||
HistoryDock,
|
||||
Search,
|
||||
NavigationMode,
|
||||
LocateNode,
|
||||
@ -61,6 +62,7 @@ namespace vnotex
|
||||
MoveOneSplitDown,
|
||||
MoveOneSplitUp,
|
||||
MoveOneSplitRight,
|
||||
OpenLastClosedFile,
|
||||
MaxShortcut
|
||||
};
|
||||
Q_ENUM(Shortcut)
|
||||
@ -92,6 +94,8 @@ namespace vnotex
|
||||
bool isRecoverLastSessionOnStartEnabled() const;
|
||||
void setRecoverLastSessionOnStartEnabled(bool p_enabled);
|
||||
|
||||
int getHistoryMaxCount() const;
|
||||
|
||||
private:
|
||||
friend class MainConfig;
|
||||
|
||||
@ -118,6 +122,9 @@ namespace vnotex
|
||||
// Whether recover last session on start.
|
||||
bool m_recoverLastSessionOnStartEnabled = true;
|
||||
|
||||
// Max count of the history items for each notebook and session config.
|
||||
int m_historyMaxCount = 100;
|
||||
|
||||
static QStringList s_availableLocales;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
28
src/core/historyitem.cpp
Normal file
28
src/core/historyitem.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "historyitem.h"
|
||||
|
||||
#include <utils/utils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
HistoryItem::HistoryItem(const QString &p_path, int p_lineNumber, const QDateTime &p_lastAccessedTimeUtc)
|
||||
: m_path(p_path),
|
||||
m_lineNumber(p_lineNumber),
|
||||
m_lastAccessedTimeUtc(p_lastAccessedTimeUtc)
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject HistoryItem::toJson() const
|
||||
{
|
||||
QJsonObject jobj;
|
||||
jobj[QStringLiteral("path")] = m_path;
|
||||
jobj[QStringLiteral("line_number")] = m_lineNumber;
|
||||
jobj[QStringLiteral("last_accessed_time")] = Utils::dateTimeStringUniform(m_lastAccessedTimeUtc);
|
||||
return jobj;
|
||||
}
|
||||
|
||||
void HistoryItem::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
m_path = p_jobj[QStringLiteral("path")].toString();
|
||||
m_lineNumber = p_jobj[QStringLiteral("line_number")].toInt();
|
||||
m_lastAccessedTimeUtc = Utils::dateTimeFromStringUniform(p_jobj[QStringLiteral("last_accessed_time")].toString());
|
||||
}
|
32
src/core/historyitem.h
Normal file
32
src/core/historyitem.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef HISTORYITEM_H
|
||||
#define HISTORYITEM_H
|
||||
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QDateTime>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
struct HistoryItem
|
||||
{
|
||||
HistoryItem() = default;
|
||||
|
||||
HistoryItem(const QString &p_path,
|
||||
int p_lineNumber,
|
||||
const QDateTime &p_lastAccessedTimeUtc);
|
||||
|
||||
QJsonObject toJson() const;
|
||||
|
||||
void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
// Relative path if it is a node within a notebook.
|
||||
QString m_path;
|
||||
|
||||
// 0-based.
|
||||
int m_lineNumber = -1;
|
||||
|
||||
QDateTime m_lastAccessedTimeUtc;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HISTORYITEM_H
|
182
src/core/historymgr.cpp
Normal file
182
src/core/historymgr.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
#include "historymgr.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "configmgr.h"
|
||||
#include "sessionconfig.h"
|
||||
#include "coreconfig.h"
|
||||
#include "vnotex.h"
|
||||
#include "notebookmgr.h"
|
||||
#include <notebook/notebook.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
bool HistoryItemFull::operator<(const HistoryItemFull &p_other) const
|
||||
{
|
||||
if (m_item.m_lastAccessedTimeUtc < p_other.m_item.m_lastAccessedTimeUtc) {
|
||||
return true;
|
||||
} else if (m_item.m_lastAccessedTimeUtc > p_other.m_item.m_lastAccessedTimeUtc) {
|
||||
return false;
|
||||
} else {
|
||||
return m_item.m_path < p_other.m_item.m_path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int HistoryMgr::s_maxHistoryCount = 100;
|
||||
|
||||
HistoryMgr::HistoryMgr()
|
||||
{
|
||||
s_maxHistoryCount = ConfigMgr::getInst().getCoreConfig().getHistoryMaxCount();
|
||||
|
||||
connect(&VNoteX::getInst().getNotebookMgr(), &NotebookMgr::notebooksUpdated,
|
||||
this, &HistoryMgr::loadHistory);
|
||||
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
static bool historyPtrCmp(const QSharedPointer<HistoryItemFull> &p_a, const QSharedPointer<HistoryItemFull> &p_b)
|
||||
{
|
||||
return *p_a < *p_b;
|
||||
}
|
||||
|
||||
void HistoryMgr::loadHistory()
|
||||
{
|
||||
m_history.clear();
|
||||
|
||||
// Load from session.
|
||||
{
|
||||
const auto &history = ConfigMgr::getInst().getSessionConfig().getHistory();
|
||||
for (const auto &item : history) {
|
||||
auto fullItem = QSharedPointer<HistoryItemFull>::create();
|
||||
fullItem->m_item = item;
|
||||
m_history.push_back(fullItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Load from notebooks.
|
||||
{
|
||||
const auto ¬ebooks = VNoteX::getInst().getNotebookMgr().getNotebooks();
|
||||
for (const auto &nb : notebooks) {
|
||||
const auto &history = nb->getHistory();
|
||||
for (const auto &item : history) {
|
||||
auto fullItem = QSharedPointer<HistoryItemFull>::create();
|
||||
fullItem->m_item = item;
|
||||
fullItem->m_notebookName = nb->getName();
|
||||
m_history.push_back(fullItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(m_history.begin(), m_history.end(), historyPtrCmp);
|
||||
|
||||
qDebug() << "loaded" << m_history.size() << "history items";
|
||||
|
||||
emit historyUpdated();
|
||||
}
|
||||
|
||||
const QVector<QSharedPointer<HistoryItemFull>> &HistoryMgr::getHistory() const
|
||||
{
|
||||
return m_history;
|
||||
}
|
||||
|
||||
void HistoryMgr::add(const QString &p_path,
|
||||
int p_lineNumber,
|
||||
ViewWindowMode p_mode,
|
||||
bool p_readOnly,
|
||||
Notebook *p_notebook)
|
||||
{
|
||||
if (p_path.isEmpty() || s_maxHistoryCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
HistoryItem item(p_path, p_lineNumber, QDateTime::currentDateTimeUtc());
|
||||
|
||||
if (p_notebook) {
|
||||
p_notebook->addHistory(item);
|
||||
} else {
|
||||
auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||
sessionConfig.addHistory(item);
|
||||
}
|
||||
|
||||
// Maintain the combined queue.
|
||||
{
|
||||
for (int i = m_history.size() - 1; i >= 0; --i) {
|
||||
if (m_history[i]->m_item.m_path == item.m_path) {
|
||||
// Erase it.
|
||||
m_history.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto fullItem = QSharedPointer<HistoryItemFull>::create();
|
||||
fullItem->m_item = item;
|
||||
if (p_notebook) {
|
||||
fullItem->m_notebookName = p_notebook->getName();
|
||||
}
|
||||
m_history.append(fullItem);
|
||||
}
|
||||
|
||||
// Update m_lastClosedFiles.
|
||||
{
|
||||
for (int i = m_lastClosedFiles.size() - 1; i >= 0; --i) {
|
||||
if (m_lastClosedFiles[i].m_path == p_path) {
|
||||
m_lastClosedFiles.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_lastClosedFiles.append(LastClosedFile());
|
||||
auto &file = m_lastClosedFiles.back();
|
||||
file.m_path = p_path;
|
||||
file.m_lineNumber = p_lineNumber;
|
||||
file.m_mode = p_mode;
|
||||
file.m_readOnly = p_readOnly;
|
||||
|
||||
if (m_lastClosedFiles.size() > 100) {
|
||||
m_lastClosedFiles.remove(0, m_lastClosedFiles.size() - 100);
|
||||
}
|
||||
}
|
||||
|
||||
emit historyUpdated();
|
||||
}
|
||||
|
||||
void HistoryMgr::insertHistoryItem(QVector<HistoryItem> &p_history, const HistoryItem &p_item)
|
||||
{
|
||||
for (int i = p_history.size() - 1; i >= 0; --i) {
|
||||
if (p_history[i].m_path == p_item.m_path) {
|
||||
// Erase it.
|
||||
p_history.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p_history.append(p_item);
|
||||
|
||||
if (p_history.size() > s_maxHistoryCount) {
|
||||
p_history.remove(0, p_history.size() - s_maxHistoryCount);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryMgr::clear()
|
||||
{
|
||||
ConfigMgr::getInst().getSessionConfig().clearHistory();
|
||||
|
||||
const auto ¬ebooks = VNoteX::getInst().getNotebookMgr().getNotebooks();
|
||||
for (const auto &nb : notebooks) {
|
||||
nb->clearHistory();
|
||||
}
|
||||
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
HistoryMgr::LastClosedFile HistoryMgr::popLastClosedFile()
|
||||
{
|
||||
if (m_lastClosedFiles.isEmpty()) {
|
||||
return LastClosedFile();
|
||||
}
|
||||
|
||||
auto file = m_lastClosedFiles.back();
|
||||
m_lastClosedFiles.pop_back();
|
||||
return file;
|
||||
}
|
81
src/core/historymgr.h
Normal file
81
src/core/historymgr.h
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef HISTORYMGR_H
|
||||
#define HISTORYMGR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "noncopyable.h"
|
||||
#include "historyitem.h"
|
||||
#include "global.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Notebook;
|
||||
|
||||
struct HistoryItemFull
|
||||
{
|
||||
bool operator<(const HistoryItemFull &p_other) const;
|
||||
|
||||
HistoryItem m_item;
|
||||
|
||||
QString m_notebookName;
|
||||
};
|
||||
|
||||
// Combine the history from all notebooks and from SessionConfig.
|
||||
// SessionConfig will store history about external files.
|
||||
// Also provide stack of files accessed during current session, which could be re-opened
|
||||
// via Ctrl+Shit+T.
|
||||
class HistoryMgr : public QObject, private Noncopyable
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct LastClosedFile
|
||||
{
|
||||
QString m_path;
|
||||
|
||||
int m_lineNumber = 0;
|
||||
|
||||
ViewWindowMode m_mode = ViewWindowMode::Read;
|
||||
|
||||
bool m_readOnly = false;
|
||||
};
|
||||
|
||||
static HistoryMgr &getInst()
|
||||
{
|
||||
static HistoryMgr inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
const QVector<QSharedPointer<HistoryItemFull>> &getHistory() const;
|
||||
|
||||
void add(const QString &p_path,
|
||||
int p_lineNumber,
|
||||
ViewWindowMode p_mode,
|
||||
bool p_readOnly,
|
||||
Notebook *p_notebook);
|
||||
|
||||
void clear();
|
||||
|
||||
LastClosedFile popLastClosedFile();
|
||||
|
||||
static void insertHistoryItem(QVector<HistoryItem> &p_history, const HistoryItem &p_item);
|
||||
|
||||
signals:
|
||||
void historyUpdated();
|
||||
|
||||
private:
|
||||
HistoryMgr();
|
||||
|
||||
void loadHistory();
|
||||
|
||||
// Sorted by last accessed time ascendingly.
|
||||
QVector<QSharedPointer<HistoryItemFull>> m_history;
|
||||
|
||||
QVector<LastClosedFile> m_lastClosedFiles;
|
||||
|
||||
static int s_maxHistoryCount;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HISTORYMGR_H
|
@ -57,6 +57,12 @@ namespace vnotex
|
||||
return m_revision;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
++m_revision;
|
||||
writeToSettings();
|
||||
}
|
||||
|
||||
protected:
|
||||
ConfigMgr *getMgr() const
|
||||
{
|
||||
|
@ -5,16 +5,18 @@
|
||||
#include <notebookconfigmgr/bundlenotebookconfigmgr.h>
|
||||
#include <notebookconfigmgr/notebookconfig.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <core/historymgr.h>
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
BundleNotebook::BundleNotebook(const NotebookParameters &p_paras,
|
||||
const QSharedPointer<NotebookConfig> &p_notebookConfig,
|
||||
QObject *p_parent)
|
||||
: Notebook(p_paras, p_parent)
|
||||
{
|
||||
auto configMgr = getBundleNotebookConfigMgr();
|
||||
auto config = configMgr->readNotebookConfig();
|
||||
m_nextNodeId = config->m_nextNodeId;
|
||||
m_nextNodeId = p_notebookConfig->m_nextNodeId;
|
||||
m_history = p_notebookConfig->m_history;
|
||||
}
|
||||
|
||||
BundleNotebookConfigMgr *BundleNotebook::getBundleNotebookConfigMgr() const
|
||||
@ -58,3 +60,24 @@ void BundleNotebook::remove()
|
||||
.arg(getRootFolderAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
const QVector<HistoryItem> &BundleNotebook::getHistory() const
|
||||
{
|
||||
return m_history;
|
||||
}
|
||||
|
||||
void BundleNotebook::addHistory(const HistoryItem &p_item)
|
||||
{
|
||||
HistoryItem item(p_item);
|
||||
item.m_path = getBackend()->getRelativePath(item.m_path);
|
||||
HistoryMgr::insertHistoryItem(m_history, p_item);
|
||||
|
||||
updateNotebookConfig();
|
||||
}
|
||||
|
||||
void BundleNotebook::clearHistory()
|
||||
{
|
||||
m_history.clear();
|
||||
|
||||
updateNotebookConfig();
|
||||
}
|
||||
|
@ -7,12 +7,14 @@
|
||||
namespace vnotex
|
||||
{
|
||||
class BundleNotebookConfigMgr;
|
||||
class NotebookConfig;
|
||||
|
||||
class BundleNotebook : public Notebook
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BundleNotebook(const NotebookParameters &p_paras,
|
||||
const QSharedPointer<NotebookConfig> &p_notebookConfig,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
ID getNextNodeId() const Q_DECL_OVERRIDE;
|
||||
@ -25,10 +27,16 @@ namespace vnotex
|
||||
|
||||
void remove() Q_DECL_OVERRIDE;
|
||||
|
||||
const QVector<HistoryItem> &getHistory() const Q_DECL_OVERRIDE;
|
||||
void addHistory(const HistoryItem &p_item) Q_DECL_OVERRIDE;
|
||||
void clearHistory() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
BundleNotebookConfigMgr *getBundleNotebookConfigMgr() const;
|
||||
|
||||
ID m_nextNodeId = 1;
|
||||
|
||||
QVector<HistoryItem> m_history;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -55,7 +55,8 @@ QSharedPointer<Notebook> BundleNotebookFactory::newNotebook(const NotebookParame
|
||||
|
||||
p_paras.m_notebookConfigMgr->createEmptySkeleton(p_paras);
|
||||
|
||||
auto notebook = QSharedPointer<BundleNotebook>::create(p_paras);
|
||||
auto nbConfig = BundleNotebookConfigMgr::readNotebookConfig(p_paras.m_notebookBackend);
|
||||
auto notebook = QSharedPointer<BundleNotebook>::create(p_paras, nbConfig);
|
||||
return notebook;
|
||||
}
|
||||
|
||||
@ -78,7 +79,7 @@ QSharedPointer<Notebook> BundleNotebookFactory::createNotebook(const NotebookMgr
|
||||
nbConfig->m_versionController,
|
||||
nbConfig->m_notebookConfigMgr);
|
||||
checkParameters(*paras);
|
||||
auto notebook = QSharedPointer<BundleNotebook>::create(*paras);
|
||||
auto notebook = QSharedPointer<BundleNotebook>::create(*paras, nbConfig);
|
||||
return notebook;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ namespace vnotex
|
||||
|
||||
ExternalNode(Node *p_parent, const QString &p_name, Type p_type);
|
||||
|
||||
// Get parent node.
|
||||
Node *getNode() const;
|
||||
|
||||
const QString &getName() const;
|
||||
|
@ -6,8 +6,9 @@
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "notebookparameters.h"
|
||||
#include "../global.h"
|
||||
#include <core/global.h>
|
||||
#include "node.h"
|
||||
#include <core/historyitem.h>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
@ -130,6 +131,10 @@ namespace vnotex
|
||||
|
||||
void reloadNodes();
|
||||
|
||||
virtual const QVector<HistoryItem> &getHistory() const = 0;
|
||||
virtual void addHistory(const HistoryItem &p_item) = 0;
|
||||
virtual void clearHistory() = 0;
|
||||
|
||||
static const QString c_defaultAttachmentFolder;
|
||||
|
||||
static const QString c_defaultImageFolder;
|
||||
|
@ -21,3 +21,9 @@ QString INotebookBackend::getFullPath(const QString &p_path) const
|
||||
constrainPath(p_path);
|
||||
return QDir(m_rootPath).filePath(p_path);
|
||||
}
|
||||
|
||||
QString INotebookBackend::getRelativePath(const QString &p_path) const
|
||||
{
|
||||
constrainPath(p_path);
|
||||
return PathUtils::relativePath(m_rootPath, p_path);
|
||||
}
|
||||
|
@ -65,6 +65,8 @@ namespace vnotex
|
||||
|
||||
QString getFullPath(const QString &p_path) const;
|
||||
|
||||
QString getRelativePath(const QString &p_path) const;
|
||||
|
||||
virtual bool exists(const QString &p_path) const = 0;
|
||||
|
||||
virtual bool existsFile(const QString &p_path) const = 0;
|
||||
|
@ -58,6 +58,8 @@ QJsonObject NotebookConfig::toJson() const
|
||||
jobj[NotebookConfig::c_configMgr] = m_notebookConfigMgr;
|
||||
jobj[NotebookConfig::c_nextNodeId] = QString::number(m_nextNodeId);
|
||||
|
||||
jobj[QStringLiteral("history")] = saveHistory();
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
@ -69,7 +71,7 @@ void NotebookConfig::fromJson(const QJsonObject &p_jobj)
|
||||
|| !p_jobj.contains(NotebookConfig::c_versionController)
|
||||
|| !p_jobj.contains(NotebookConfig::c_configMgr)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to read notebook configuration from JSON (%1)").arg(QJsonObjectToString(p_jobj)));
|
||||
QString("failed to read notebook configuration from JSON (%1)").arg(QJsonObjectToString(p_jobj)));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -90,6 +92,8 @@ void NotebookConfig::fromJson(const QJsonObject &p_jobj)
|
||||
m_nextNodeId = BundleNotebookConfigMgr::RootNodeId;
|
||||
}
|
||||
}
|
||||
|
||||
loadHistory(p_jobj);
|
||||
}
|
||||
|
||||
QSharedPointer<NotebookConfig> NotebookConfig::fromNotebook(const QString &p_version,
|
||||
@ -106,6 +110,25 @@ QSharedPointer<NotebookConfig> NotebookConfig::fromNotebook(const QString &p_ver
|
||||
config->m_versionController = p_notebook->getVersionController()->getName();
|
||||
config->m_notebookConfigMgr = p_notebook->getConfigMgr()->getName();
|
||||
config->m_nextNodeId = p_notebook->getNextNodeId();
|
||||
config->m_history = p_notebook->getHistory();
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
QJsonArray NotebookConfig::saveHistory() const
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const auto &item : m_history) {
|
||||
arr.append(item.toJson());
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
void NotebookConfig::loadHistory(const QJsonObject &p_jobj)
|
||||
{
|
||||
auto arr = p_jobj[QStringLiteral("history")].toArray();
|
||||
m_history.resize(arr.size());
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
m_history[i].fromJson(arr[i].toObject());
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,12 @@
|
||||
#include <QJsonObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QDateTime>
|
||||
#include <QVector>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "bundlenotebookconfigmgr.h"
|
||||
#include "global.h"
|
||||
#include <core/global.h>
|
||||
#include <core/historyitem.h>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
@ -45,6 +48,13 @@ namespace vnotex
|
||||
|
||||
ID m_nextNodeId = BundleNotebookConfigMgr::RootNodeId + 1;
|
||||
|
||||
QVector<HistoryItem> m_history;
|
||||
|
||||
private:
|
||||
QJsonArray saveHistory() const;
|
||||
|
||||
void loadHistory(const QJsonObject &p_jobj);
|
||||
|
||||
static const QString c_version;
|
||||
|
||||
static const QString c_name;
|
||||
|
@ -374,3 +374,15 @@ void NotebookMgr::addNotebook(const QSharedPointer<Notebook> &p_notebook)
|
||||
emit notebookUpdated(notebook);
|
||||
});
|
||||
}
|
||||
|
||||
QSharedPointer<Node> NotebookMgr::loadNodeByPath(const QString &p_path)
|
||||
{
|
||||
for (const auto &nb : m_notebooks) {
|
||||
auto node = nb->loadNodeByPath(p_path);
|
||||
if (node) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ namespace vnotex
|
||||
class INotebookBackendFactory;
|
||||
class INotebookFactory;
|
||||
class NotebookParameters;
|
||||
class Node;
|
||||
|
||||
class NotebookMgr : public QObject
|
||||
{
|
||||
@ -67,6 +68,9 @@ namespace vnotex
|
||||
|
||||
void removeNotebook(ID p_id);
|
||||
|
||||
// Try to load @p_path as a node if it is within one notebook.
|
||||
QSharedPointer<Node> loadNodeByPath(const QString &p_path);
|
||||
|
||||
public slots:
|
||||
void setCurrentNotebook(ID p_notebookId);
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "configmgr.h"
|
||||
#include "mainconfig.h"
|
||||
#include "historymgr.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -87,6 +88,8 @@ void SessionConfig::init()
|
||||
|
||||
loadNotebooks(sessionJobj);
|
||||
|
||||
loadHistory(sessionJobj);
|
||||
|
||||
if (MainConfig::isVersionChanged()) {
|
||||
doVersionSpecificOverride();
|
||||
}
|
||||
@ -208,6 +211,7 @@ QJsonObject SessionConfig::toJson() const
|
||||
writeByteArray(obj, QStringLiteral("viewarea_session"), m_viewAreaSession);
|
||||
writeByteArray(obj, QStringLiteral("notebook_explorer_session"), m_notebookExplorerSession);
|
||||
obj[QStringLiteral("external_programs")] = saveExternalPrograms();
|
||||
obj[QStringLiteral("history")] = saveHistory();
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -405,3 +409,38 @@ const QVector<SessionConfig::ExternalProgram> &SessionConfig::getExternalProgram
|
||||
{
|
||||
return m_externalPrograms;
|
||||
}
|
||||
|
||||
const QVector<HistoryItem> &SessionConfig::getHistory() const
|
||||
{
|
||||
return m_history;
|
||||
}
|
||||
|
||||
void SessionConfig::addHistory(const HistoryItem &p_item)
|
||||
{
|
||||
HistoryMgr::insertHistoryItem(m_history, p_item);
|
||||
update();
|
||||
}
|
||||
|
||||
void SessionConfig::clearHistory()
|
||||
{
|
||||
m_history.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
void SessionConfig::loadHistory(const QJsonObject &p_session)
|
||||
{
|
||||
auto arr = p_session[QStringLiteral("history")].toArray();
|
||||
m_history.resize(arr.size());
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
m_history[i].fromJson(arr[i].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray SessionConfig::saveHistory() const
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const auto &item : m_history) {
|
||||
arr.append(item.toJson());
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <export/exportdata.h>
|
||||
#include <search/searchdata.h>
|
||||
#include "historyitem.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
@ -123,6 +124,10 @@ namespace vnotex
|
||||
|
||||
const QVector<ExternalProgram> &getExternalPrograms() const;
|
||||
|
||||
const QVector<HistoryItem> &getHistory() const;
|
||||
void addHistory(const HistoryItem &p_item);
|
||||
void clearHistory();
|
||||
|
||||
private:
|
||||
void loadCore(const QJsonObject &p_session);
|
||||
|
||||
@ -142,6 +147,10 @@ namespace vnotex
|
||||
|
||||
void doVersionSpecificOverride();
|
||||
|
||||
void loadHistory(const QJsonObject &p_session);
|
||||
|
||||
QJsonArray saveHistory() const;
|
||||
|
||||
QString m_newNotebookDefaultRootFolderPath;
|
||||
|
||||
// Use root folder to identify a notebook uniquely.
|
||||
@ -175,6 +184,8 @@ namespace vnotex
|
||||
QStringList m_quickAccessFiles;
|
||||
|
||||
QVector<ExternalProgram> m_externalPrograms;
|
||||
|
||||
QVector<HistoryItem> m_history;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
"SearchDock" : "",
|
||||
"SnippetDock" : "Ctrl+G, S",
|
||||
"LocationListDock" : "Ctrl+G, C",
|
||||
"HistoryDock" : "",
|
||||
"Search" : "Ctrl+Alt+F",
|
||||
"NavigationMode" : "Ctrl+G, W",
|
||||
"LocateNode" : "Ctrl+G, D",
|
||||
@ -54,7 +55,8 @@
|
||||
"MoveOneSplitLeft" : "Ctrl+G, Shift+H",
|
||||
"MoveOneSplitDown" : "Ctrl+G, Shift+J",
|
||||
"MoveOneSplitUp" : "Ctrl+G, Shift+K",
|
||||
"MoveOneSplitRight" : "Ctrl+G, Shift+L"
|
||||
"MoveOneSplitRight" : "Ctrl+G, Shift+L",
|
||||
"OpenLastClosedFile" : "Ctrl+Shift+T"
|
||||
},
|
||||
"toolbar_icon_size" : 16,
|
||||
"note_management" : {
|
||||
@ -66,7 +68,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"recover_last_session_on_start" : true
|
||||
"recover_last_session_on_start" : true,
|
||||
"//comment" : "Max count of the history items for each notebook and session config",
|
||||
"history_max_count" : 100
|
||||
},
|
||||
"editor" : {
|
||||
"core": {
|
||||
|
@ -72,6 +72,15 @@ QString PathUtils::fileName(const QString &p_path)
|
||||
return fi.fileName();
|
||||
}
|
||||
|
||||
QString PathUtils::fileNameCheap(const QString &p_path)
|
||||
{
|
||||
int idx = p_path.lastIndexOf(QRegularExpression("[\\\\/]"));
|
||||
if (idx == -1) {
|
||||
return p_path;
|
||||
}
|
||||
return p_path.mid(idx + 1);
|
||||
}
|
||||
|
||||
QString PathUtils::normalizePath(const QString &p_path)
|
||||
{
|
||||
auto absPath = QDir::cleanPath(QDir(p_path).absolutePath());
|
||||
|
@ -38,6 +38,8 @@ namespace vnotex
|
||||
// Get file name of @p_path file/directory.
|
||||
static QString fileName(const QString &p_path);
|
||||
|
||||
static QString fileNameCheap(const QString &p_path);
|
||||
|
||||
static QString absolutePath(const QString &p_path)
|
||||
{
|
||||
return QDir(p_path).absolutePath();
|
||||
|
@ -296,6 +296,10 @@ QShortcut *WidgetUtils::createShortcut(const QString &p_shortcut,
|
||||
}
|
||||
|
||||
auto shortcut = new QShortcut(kseq, p_widget, nullptr, nullptr, p_context);
|
||||
if (shortcut->key().isEmpty()) {
|
||||
delete shortcut;
|
||||
return nullptr;
|
||||
}
|
||||
return shortcut;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ void GraphHelper::process(quint64 p_id,
|
||||
TimeStamp p_timeStamp,
|
||||
const QString &p_format,
|
||||
const QString &p_text,
|
||||
QObject *p_owner,
|
||||
const ResultCallback &p_callback)
|
||||
{
|
||||
Task task;
|
||||
@ -49,6 +50,7 @@ void GraphHelper::process(quint64 p_id,
|
||||
task.m_timeStamp = p_timeStamp;
|
||||
task.m_format = p_format;
|
||||
task.m_text = p_text;
|
||||
task.m_owner = p_owner;
|
||||
task.m_callback = p_callback;
|
||||
|
||||
m_tasks.enqueue(task);
|
||||
@ -126,10 +128,10 @@ void GraphHelper::finishOneTask(QProcess *p_process, int p_exitCode, QProcess::E
|
||||
QString data;
|
||||
if (task.m_format == QStringLiteral("svg")) {
|
||||
data = QString::fromLocal8Bit(outBa);
|
||||
task.m_callback(id, timeStamp, task.m_format, data);
|
||||
callbackOneTask(task, id, timeStamp, task.m_format, data);
|
||||
} else {
|
||||
data = QString::fromLocal8Bit(outBa.toBase64());
|
||||
task.m_callback(id, timeStamp, task.m_format, data);
|
||||
callbackOneTask(task, id, timeStamp, task.m_format, data);
|
||||
}
|
||||
|
||||
CacheItem item;
|
||||
@ -152,7 +154,7 @@ void GraphHelper::finishOneTask(QProcess *p_process, int p_exitCode, QProcess::E
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
task.m_callback(id, task.m_timeStamp, task.m_format, QString());
|
||||
callbackOneTask(task, id, task.m_timeStamp, task.m_format, QString());
|
||||
}
|
||||
|
||||
p_process->deleteLater();
|
||||
@ -169,7 +171,7 @@ void GraphHelper::finishOneTask(const QString &p_data)
|
||||
|
||||
qDebug() << "Graph task" << task.m_id << task.m_timeStamp << "finished by cache" << p_data.size();
|
||||
|
||||
task.m_callback(task.m_id, task.m_timeStamp, task.m_format, p_data);
|
||||
callbackOneTask(task, task.m_id, task.m_timeStamp, task.m_format, p_data);
|
||||
|
||||
m_taskOngoing = false;
|
||||
processOneTask();
|
||||
@ -199,3 +201,10 @@ void GraphHelper::checkValidProgram()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphHelper::callbackOneTask(const Task &p_task, quint64 p_id, TimeStamp p_timeStamp, const QString &p_format, const QString &p_data) const
|
||||
{
|
||||
if (p_task.m_owner) {
|
||||
p_task.m_callback(p_id, p_timeStamp, p_format, p_data);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QStringList>
|
||||
#include <QPair>
|
||||
#include <QQueue>
|
||||
#include <QPointer>
|
||||
|
||||
#include <core/noncopyable.h>
|
||||
#include <core/global.h>
|
||||
@ -23,6 +24,7 @@ namespace vnotex
|
||||
TimeStamp p_timeStamp,
|
||||
const QString &p_format,
|
||||
const QString &p_text,
|
||||
QObject *p_owner,
|
||||
const ResultCallback &p_callback);
|
||||
|
||||
protected:
|
||||
@ -55,6 +57,8 @@ namespace vnotex
|
||||
|
||||
QString m_text;
|
||||
|
||||
QPointer<QObject> m_owner;
|
||||
|
||||
ResultCallback m_callback;
|
||||
};
|
||||
|
||||
@ -76,6 +80,8 @@ namespace vnotex
|
||||
|
||||
void finishOneTask(const QString &p_data);
|
||||
|
||||
void callbackOneTask(const Task &p_task, quint64 p_id, TimeStamp p_timeStamp, const QString &p_format, const QString &p_data) const;
|
||||
|
||||
QQueue<Task> m_tasks;
|
||||
|
||||
bool m_taskOngoing = false;
|
||||
|
@ -390,6 +390,7 @@ void MarkdownViewerAdapter::renderGraph(quint64 p_id,
|
||||
p_index,
|
||||
p_format,
|
||||
p_text,
|
||||
this,
|
||||
[this](quint64 id, TimeStamp timeStamp, const QString &format, const QString &data) {
|
||||
emit graphRenderDataReady(id, timeStamp, format, data);
|
||||
});
|
||||
@ -398,6 +399,7 @@ void MarkdownViewerAdapter::renderGraph(quint64 p_id,
|
||||
p_index,
|
||||
p_format,
|
||||
p_text,
|
||||
this,
|
||||
[this](quint64 id, TimeStamp timeStamp, const QString &format, const QString &data) {
|
||||
emit graphRenderDataReady(id, timeStamp, format, data);
|
||||
});
|
||||
|
@ -262,6 +262,7 @@ void PreviewHelper::inplacePreviewCodeBlock(int p_blockPreviewIdx)
|
||||
m_codeBlockTimeStamp,
|
||||
QStringLiteral("svg"),
|
||||
vte::TextUtils::removeCodeBlockFence(blockData.m_text),
|
||||
this,
|
||||
[this](quint64 id, TimeStamp timeStamp, const QString &format, const QString &data) {
|
||||
handleLocalData(id, timeStamp, format, data, true);
|
||||
});
|
||||
@ -274,6 +275,7 @@ void PreviewHelper::inplacePreviewCodeBlock(int p_blockPreviewIdx)
|
||||
m_codeBlockTimeStamp,
|
||||
QStringLiteral("svg"),
|
||||
vte::TextUtils::removeCodeBlockFence(blockData.m_text),
|
||||
this,
|
||||
[this](quint64 id, TimeStamp timeStamp, const QString &format, const QString &data) {
|
||||
handleLocalData(id, timeStamp, format, data, false);
|
||||
});
|
||||
|
227
src/widgets/historypanel.cpp
Normal file
227
src/widgets/historypanel.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
#include "historypanel.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QToolButton>
|
||||
#include <QListWidgetItem>
|
||||
|
||||
#include <utils/widgetutils.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <core/vnotex.h>
|
||||
#include <core/exception.h>
|
||||
#include <core/configmgr.h>
|
||||
#include <core/historymgr.h>
|
||||
#include <core/notebookmgr.h>
|
||||
#include <core/fileopenparameters.h>
|
||||
|
||||
|
||||
#include "titlebar.h"
|
||||
#include "listwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "messageboxhelper.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
HistoryPanel::HistoryPanel(QWidget *p_parent)
|
||||
: QFrame(p_parent)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
updateSeparators();
|
||||
}
|
||||
|
||||
void HistoryPanel::setupUI()
|
||||
{
|
||||
auto mainLayout = new QVBoxLayout(this);
|
||||
WidgetUtils::setContentsMargins(mainLayout);
|
||||
|
||||
{
|
||||
setupTitleBar(QString(), this);
|
||||
mainLayout->addWidget(m_titleBar);
|
||||
}
|
||||
|
||||
m_historyList = new ListWidget(this);
|
||||
m_historyList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_historyList->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
connect(m_historyList, &QListWidget::customContextMenuRequested,
|
||||
this, &HistoryPanel::handleContextMenuRequested);
|
||||
connect(m_historyList, &QListWidget::itemActivated,
|
||||
this, &HistoryPanel::openItem);
|
||||
mainLayout->addWidget(m_historyList);
|
||||
|
||||
setFocusProxy(m_historyList);
|
||||
}
|
||||
|
||||
void HistoryPanel::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
||||
{
|
||||
m_titleBar = new TitleBar(p_title, false, TitleBar::Action::None, p_parent);
|
||||
m_titleBar->setActionButtonsAlwaysShown(true);
|
||||
|
||||
{
|
||||
auto clearBtn = m_titleBar->addActionButton(QStringLiteral("clear.svg"), tr("Clear"));
|
||||
connect(clearBtn, &QToolButton::triggered,
|
||||
this, &HistoryPanel::clearHistory);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryPanel::handleContextMenuRequested(const QPoint &p_pos)
|
||||
{
|
||||
auto item = m_historyList->itemAt(p_pos);
|
||||
if (!isValidItem(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu menu(this);
|
||||
|
||||
const int selectedCount = m_historyList->selectedItems().size();
|
||||
|
||||
menu.addAction(tr("&Open"),
|
||||
this,
|
||||
[this]() {
|
||||
const auto selectedItems = m_historyList->selectedItems();
|
||||
for (const auto &selectedItem : selectedItems) {
|
||||
openItem(selectedItem);
|
||||
}
|
||||
});
|
||||
|
||||
if (selectedCount == 1) {
|
||||
menu.addAction(tr("&Locate Node"),
|
||||
&menu,
|
||||
[this]() {
|
||||
auto item = m_historyList->currentItem();
|
||||
if (!isValidItem(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto node = VNoteX::getInst().getNotebookMgr().loadNodeByPath(getPath(item));
|
||||
if (node) {
|
||||
emit VNoteX::getInst().locateNodeRequested(node.data());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
menu.exec(m_historyList->mapToGlobal(p_pos));
|
||||
}
|
||||
|
||||
void HistoryPanel::openItem(const QListWidgetItem *p_item)
|
||||
{
|
||||
if (!isValidItem(p_item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit VNoteX::getInst().openFileRequested(getPath(p_item), QSharedPointer<FileOpenParameters>::create());
|
||||
}
|
||||
|
||||
void HistoryPanel::clearHistory()
|
||||
{
|
||||
int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Warning,
|
||||
tr("Clear all the history?"),
|
||||
QString(),
|
||||
QString(),
|
||||
VNoteX::getInst().getMainWindow());
|
||||
if (ret != QMessageBox::Ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
HistoryMgr::getInst().clear();
|
||||
}
|
||||
|
||||
bool HistoryPanel::isValidItem(const QListWidgetItem *p_item) const
|
||||
{
|
||||
return p_item && !ListWidget::isSeparatorItem(p_item);
|
||||
}
|
||||
|
||||
void HistoryPanel::updateHistoryList()
|
||||
{
|
||||
m_pendingUpdate = false;
|
||||
|
||||
m_historyList->clear();
|
||||
|
||||
const auto &history = HistoryMgr::getInst().getHistory();
|
||||
int itemIdx = history.size() - 1;
|
||||
for (int sepIdx = 0; sepIdx < m_separators.size() && itemIdx >= 0; ++sepIdx) {
|
||||
const auto &separator = m_separators[sepIdx];
|
||||
if (separator.m_dateUtc <= history[itemIdx]->m_item.m_lastAccessedTimeUtc) {
|
||||
// Insert this separator.
|
||||
auto sepItem = ListWidget::createSeparatorItem(separator.m_text);
|
||||
m_historyList->addItem(sepItem);
|
||||
|
||||
addItem(*history[itemIdx]);
|
||||
--itemIdx;
|
||||
|
||||
// Insert all qualified items.
|
||||
for (;
|
||||
itemIdx >= 0 && separator.m_dateUtc <= history[itemIdx]->m_item.m_lastAccessedTimeUtc;
|
||||
--itemIdx) {
|
||||
addItem(*history[itemIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemIdx >= 0) {
|
||||
// Older.
|
||||
auto sepItem = ListWidget::createSeparatorItem(tr(">>> Older"));
|
||||
m_historyList->addItem(sepItem);
|
||||
|
||||
for (; itemIdx >= 0; --itemIdx) {
|
||||
addItem(*history[itemIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryPanel::showEvent(QShowEvent *p_event)
|
||||
{
|
||||
QFrame::showEvent(p_event);
|
||||
|
||||
if (!m_initialized) {
|
||||
m_initialized = true;
|
||||
connect(&HistoryMgr::getInst(), &HistoryMgr::historyUpdated,
|
||||
this, &HistoryPanel::updateHistoryListIfProper);
|
||||
}
|
||||
|
||||
if (m_pendingUpdate) {
|
||||
updateHistoryList();
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryPanel::updateSeparators()
|
||||
{
|
||||
m_separators.resize(3);
|
||||
|
||||
// Mid-night of today.
|
||||
auto curDateTime = QDateTime::currentDateTime();
|
||||
curDateTime.setTime(QTime());
|
||||
|
||||
m_separators[0].m_text = tr(">>> Today");
|
||||
m_separators[0].m_dateUtc = curDateTime.toUTC();
|
||||
m_separators[1].m_text = tr(">>> Yesterday");
|
||||
m_separators[1].m_dateUtc = curDateTime.addDays(-1).toUTC();
|
||||
m_separators[2].m_text = tr(">>> Last 7 Days");
|
||||
m_separators[2].m_dateUtc = curDateTime.addDays(-7).toUTC();
|
||||
}
|
||||
|
||||
void HistoryPanel::updateHistoryListIfProper()
|
||||
{
|
||||
if (isVisible()) {
|
||||
updateHistoryList();
|
||||
} else {
|
||||
m_pendingUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryPanel::addItem(const HistoryItemFull &p_hisItem)
|
||||
{
|
||||
auto item = new QListWidgetItem(m_historyList);
|
||||
|
||||
item->setText(PathUtils::fileNameCheap(p_hisItem.m_item.m_path));
|
||||
item->setData(Qt::UserRole, p_hisItem.m_item.m_path);
|
||||
if (p_hisItem.m_notebookName.isEmpty()) {
|
||||
item->setToolTip(p_hisItem.m_item.m_path);
|
||||
} else {
|
||||
item->setToolTip(tr("[%1] %2").arg(p_hisItem.m_notebookName, p_hisItem.m_item.m_path));
|
||||
}
|
||||
}
|
||||
|
||||
QString HistoryPanel::getPath(const QListWidgetItem *p_item) const
|
||||
{
|
||||
return p_item->data(Qt::UserRole).toString();
|
||||
}
|
67
src/widgets/historypanel.h
Normal file
67
src/widgets/historypanel.h
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef HISTORYPANEL_H
|
||||
#define HISTORYPANEL_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QDateTime>
|
||||
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class TitleBar;
|
||||
struct HistoryItemFull;
|
||||
|
||||
class HistoryPanel : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HistoryPanel(QWidget *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void handleContextMenuRequested(const QPoint &p_pos);
|
||||
|
||||
void openItem(const QListWidgetItem *p_item);
|
||||
|
||||
void clearHistory();
|
||||
|
||||
private:
|
||||
struct SeparatorData
|
||||
{
|
||||
QString m_text;
|
||||
|
||||
QDateTime m_dateUtc;
|
||||
};
|
||||
|
||||
void setupUI();
|
||||
|
||||
void setupTitleBar(const QString &p_title, QWidget *p_parent = nullptr);
|
||||
|
||||
void updateHistoryList();
|
||||
|
||||
void updateHistoryListIfProper();
|
||||
|
||||
void updateSeparators();
|
||||
|
||||
void addItem(const HistoryItemFull &p_hisItem);
|
||||
|
||||
QString getPath(const QListWidgetItem *p_item) const;
|
||||
|
||||
bool isValidItem(const QListWidgetItem *p_item) const;
|
||||
|
||||
TitleBar *m_titleBar = nullptr;
|
||||
|
||||
QListWidget *m_historyList = nullptr;
|
||||
|
||||
bool m_initialized = false;
|
||||
|
||||
bool m_pendingUpdate = true;
|
||||
|
||||
QVector<SeparatorData> m_separators;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HISTORYPANEL_H
|
@ -41,3 +41,15 @@ QVector<QListWidgetItem *> ListWidget::getVisibleItems(const QListWidget *p_widg
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
QListWidgetItem *ListWidget::createSeparatorItem(const QString &p_text)
|
||||
{
|
||||
QListWidgetItem *item = new QListWidgetItem(p_text, nullptr, c_separatorType);
|
||||
item->setFlags(Qt::NoItemFlags);
|
||||
return item;
|
||||
}
|
||||
|
||||
bool ListWidget::isSeparatorItem(const QListWidgetItem *p_item)
|
||||
{
|
||||
return p_item->type() == c_separatorType;
|
||||
}
|
||||
|
@ -14,8 +14,15 @@ namespace vnotex
|
||||
|
||||
static QVector<QListWidgetItem *> getVisibleItems(const QListWidget *p_widget);
|
||||
|
||||
static QListWidgetItem *createSeparatorItem(const QString &p_text);
|
||||
|
||||
static bool isSeparatorItem(const QListWidgetItem *p_item);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
static const int c_separatorType = 2000;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "locationlist.h"
|
||||
#include "searchpanel.h"
|
||||
#include "snippetpanel.h"
|
||||
#include "historypanel.h"
|
||||
#include <notebook/notebook.h>
|
||||
#include "searchinfoprovider.h"
|
||||
#include <vtextedit/spellchecker.h>
|
||||
@ -217,6 +218,8 @@ void MainWindow::setupDocks()
|
||||
|
||||
setupOutlineDock();
|
||||
|
||||
setupHistoryDock();
|
||||
|
||||
setupSearchDock();
|
||||
|
||||
setupSnippetDock();
|
||||
@ -225,6 +228,7 @@ void MainWindow::setupDocks()
|
||||
tabifyDockWidget(m_docks[i - 1], m_docks[i]);
|
||||
}
|
||||
|
||||
// Following are non-tabfieid docks.
|
||||
setupLocationListDock();
|
||||
|
||||
for (auto dock : m_docks) {
|
||||
@ -338,6 +342,26 @@ void MainWindow::setupSnippetPanel()
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::setupHistoryDock()
|
||||
{
|
||||
auto dock = new QDockWidget(tr("History"), this);
|
||||
m_docks.push_back(dock);
|
||||
|
||||
dock->setObjectName(QStringLiteral("HistoryDock.vnotex"));
|
||||
dock->setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||
|
||||
setupHistoryPanel();
|
||||
dock->setWidget(m_historyPanel);
|
||||
dock->setFocusProxy(m_historyPanel);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, dock);
|
||||
}
|
||||
|
||||
void MainWindow::setupHistoryPanel()
|
||||
{
|
||||
m_historyPanel = new HistoryPanel(this);
|
||||
m_historyPanel->setObjectName("HistoryPanel.vnotex");
|
||||
}
|
||||
|
||||
void MainWindow::setupLocationListDock()
|
||||
{
|
||||
auto dock = new QDockWidget(tr("Location List"), this);
|
||||
@ -381,7 +405,10 @@ void MainWindow::setupNotebookExplorer(QWidget *p_parent)
|
||||
connect(&VNoteX::getInst(), &VNoteX::importLegacyNotebookRequested,
|
||||
m_notebookExplorer, &NotebookExplorer::importLegacyNotebook);
|
||||
connect(&VNoteX::getInst(), &VNoteX::locateNodeRequested,
|
||||
m_notebookExplorer, &NotebookExplorer::locateNode);
|
||||
this, [this](Node *p_node) {
|
||||
activateDock(m_docks[DockIndex::NavigationDock]);
|
||||
m_notebookExplorer->locateNode(p_node);
|
||||
});
|
||||
|
||||
auto notebookMgr = &VNoteX::getInst().getNotebookMgr();
|
||||
connect(notebookMgr, &NotebookMgr::notebooksUpdated,
|
||||
@ -646,6 +673,9 @@ void MainWindow::setupShortcuts()
|
||||
setupDockActivateShortcut(m_docks[DockIndex::OutlineDock],
|
||||
coreConfig.getShortcut(CoreConfig::Shortcut::OutlineDock));
|
||||
|
||||
setupDockActivateShortcut(m_docks[DockIndex::HistoryDock],
|
||||
coreConfig.getShortcut(CoreConfig::Shortcut::HistoryDock));
|
||||
|
||||
setupDockActivateShortcut(m_docks[DockIndex::SearchDock],
|
||||
coreConfig.getShortcut(CoreConfig::Shortcut::SearchDock));
|
||||
// Extra shortcut for SearchDock.
|
||||
|
@ -23,6 +23,7 @@ namespace vnotex
|
||||
class LocationList;
|
||||
class SearchPanel;
|
||||
class SnippetPanel;
|
||||
class HistoryPanel;
|
||||
|
||||
enum { RESTART_EXIT_CODE = 1000 };
|
||||
|
||||
@ -96,6 +97,7 @@ namespace vnotex
|
||||
{
|
||||
NavigationDock = 0,
|
||||
OutlineDock,
|
||||
HistoryDock,
|
||||
SearchDock,
|
||||
SnippetDock,
|
||||
LocationListDock
|
||||
@ -123,6 +125,10 @@ namespace vnotex
|
||||
|
||||
void setupSnippetPanel();
|
||||
|
||||
void setupHistoryDock();
|
||||
|
||||
void setupHistoryPanel();
|
||||
|
||||
void setupNotebookExplorer(QWidget *p_parent = nullptr);
|
||||
|
||||
void setupDocks();
|
||||
@ -176,6 +182,8 @@ namespace vnotex
|
||||
|
||||
SnippetPanel *m_snippetPanel = nullptr;
|
||||
|
||||
HistoryPanel *m_historyPanel = nullptr;
|
||||
|
||||
QVector<QDockWidget *> m_docks;
|
||||
|
||||
bool m_layoutReset = false;
|
||||
|
@ -135,15 +135,15 @@ void SnippetPanel::showEvent(QShowEvent *p_event)
|
||||
}
|
||||
}
|
||||
|
||||
void SnippetPanel::handleContextMenuRequested(QPoint p_pos)
|
||||
void SnippetPanel::handleContextMenuRequested(const QPoint &p_pos)
|
||||
{
|
||||
QMenu menu(this);
|
||||
|
||||
auto item = m_snippetList->itemAt(p_pos);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu menu(this);
|
||||
|
||||
const int selectedCount = m_snippetList->selectedItems().size();
|
||||
if (selectedCount == 1) {
|
||||
menu.addAction(tr("&Apply"),
|
||||
|
@ -25,7 +25,7 @@ namespace vnotex
|
||||
private slots:
|
||||
void newSnippet();
|
||||
|
||||
void handleContextMenuRequested(QPoint p_pos);
|
||||
void handleContextMenuRequested(const QPoint &p_pos);
|
||||
|
||||
void removeSelectedSnippets();
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <notebook/notebook.h>
|
||||
#include "editors/plantumlhelper.h"
|
||||
#include "editors/graphvizhelper.h"
|
||||
#include <core/historymgr.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -464,10 +465,23 @@ bool ViewArea::closeViewWindow(ViewWindow *p_win, bool p_force, bool p_removeSpl
|
||||
// Make it current ViewWindow.
|
||||
setCurrentViewWindow(p_win);
|
||||
|
||||
// Get info before close.
|
||||
const auto session = p_win->saveSession();
|
||||
Notebook *notebook = nullptr;
|
||||
if (p_win->getBuffer()) {
|
||||
auto node = p_win->getBuffer()->getNode();
|
||||
if (node) {
|
||||
notebook = node->getNotebook();
|
||||
}
|
||||
}
|
||||
|
||||
if (!p_win->aboutToClose(p_force)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update history.
|
||||
updateHistory(session, notebook);
|
||||
|
||||
// Remove the status widget.
|
||||
if (m_currentStatusWidget && p_win == getCurrentViewWindow()) {
|
||||
Q_ASSERT(m_currentStatusWidget == p_win->statusWidget());
|
||||
@ -890,6 +904,26 @@ void ViewArea::setupShortcuts()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// OpenLastClosedFile.
|
||||
{
|
||||
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OpenLastClosedFile), this);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
auto file = HistoryMgr::getInst().popLastClosedFile();
|
||||
if (file.m_path.isEmpty()) {
|
||||
VNoteX::getInst().showStatusMessageShort(tr("No recently closed file"));
|
||||
return;
|
||||
}
|
||||
auto paras = QSharedPointer<FileOpenParameters>::create();
|
||||
paras->m_lineNumber = file.m_lineNumber;
|
||||
paras->m_mode = file.m_mode;
|
||||
paras->m_readOnly = file.m_readOnly;
|
||||
emit VNoteX::getInst().openFileRequested(file.m_path, paras);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewArea::close(Node *p_node, bool p_force)
|
||||
@ -1441,3 +1475,12 @@ ViewArea::SplitType ViewArea::splitTypeOfDirection(Direction p_direction)
|
||||
return SplitType::Vertical;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewArea::updateHistory(const ViewWindowSession &p_session, Notebook *p_notebook) const
|
||||
{
|
||||
HistoryMgr::getInst().add(p_session.m_bufferPath,
|
||||
p_session.m_lineNumber,
|
||||
p_session.m_viewWindowMode,
|
||||
p_session.m_readOnly,
|
||||
p_notebook);
|
||||
}
|
||||
|
@ -228,6 +228,8 @@ namespace vnotex
|
||||
|
||||
void flashViewSplit(ViewSplit *p_split);
|
||||
|
||||
void updateHistory(const ViewWindowSession &p_session, Notebook *p_notebook) const;
|
||||
|
||||
static SplitType splitTypeOfDirection(Direction p_direction);
|
||||
|
||||
QLayout *m_mainLayout = nullptr;
|
||||
|
@ -49,6 +49,7 @@ SOURCES += \
|
||||
$$PWD/findandreplacewidget.cpp \
|
||||
$$PWD/floatingwidget.cpp \
|
||||
$$PWD/fullscreentoggleaction.cpp \
|
||||
$$PWD/historypanel.cpp \
|
||||
$$PWD/lineedit.cpp \
|
||||
$$PWD/lineeditdelegate.cpp \
|
||||
$$PWD/lineeditwithsnippet.cpp \
|
||||
@ -155,6 +156,7 @@ HEADERS += \
|
||||
$$PWD/findandreplacewidget.h \
|
||||
$$PWD/floatingwidget.h \
|
||||
$$PWD/fullscreentoggleaction.h \
|
||||
$$PWD/historypanel.h \
|
||||
$$PWD/lineedit.h \
|
||||
$$PWD/lineeditdelegate.h \
|
||||
$$PWD/lineeditwithsnippet.h \
|
||||
|
Loading…
x
Reference in New Issue
Block a user