Skip to content

Commit f094b24

Browse files
QuickAccess: change link address from hard link to VxURL (#2635)
Use the format '#signature:fileFullName' (VxURL) for QuickAccess instead of absolute file paths (though compatibility support for absolute paths is still maintained). Now each record in QuickAccess dynamically retrieves its absolute address when opened, which helps prevent broken links caused by file renaming or moving. Co-authored-by: schips <schips@dingtalk.com>
1 parent 32bbdcb commit f094b24

File tree

7 files changed

+258
-5
lines changed

7 files changed

+258
-5
lines changed

src/utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ target_sources(vnote PRIVATE
1313
processutils.cpp processutils.h
1414
urldragdroputils.cpp urldragdroputils.h
1515
utils.cpp utils.h
16+
vxurlutils.cpp vxurlutils.h
1617
webutils.cpp webutils.h
1718
widgetutils.cpp widgetutils.h
1819
)

src/utils/vxurlutils.cpp

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#include "vxurlutils.h"
2+
3+
#include <QFile>
4+
#include <QDir>
5+
#include <QDirIterator>
6+
#include <QTemporaryFile>
7+
8+
#include <core/exception.h>
9+
#include <core/global.h>
10+
11+
#include "pathutils.h"
12+
#include <QJsonArray>
13+
#include <QJsonDocument>
14+
#include <QJsonObject>
15+
#include <QJsonParseError>
16+
#include <QJsonValue>
17+
18+
using namespace vnotex;
19+
20+
QString VxUrlUtils::generateVxURL(const QString &p_signature, const QString &p_filePath)
21+
{
22+
return QString("#%1:%2").arg(p_signature, p_filePath);
23+
}
24+
25+
QString VxUrlUtils::getSignatureFromVxURL(const QString &p_vxUrl)
26+
{
27+
QString signature = p_vxUrl;
28+
// check if file is "#signature:fileFullName"
29+
if (signature.startsWith('#')) {
30+
signature = signature.mid(1); // remove '#'
31+
if (signature.contains(':')) {
32+
signature = signature.split(':').first(); // get 'signature'
33+
return signature;
34+
}
35+
} // if not 'signature', return original 'vxUrl'
36+
return p_vxUrl;
37+
}
38+
39+
QString VxUrlUtils::getFilePathFromVxURL(const QString &p_vxUrl) {
40+
QString filePath = p_vxUrl;
41+
// check if file is "#signature:fileFullName"
42+
if (p_vxUrl.startsWith('#')) {
43+
int colonPos = p_vxUrl.indexOf(':');
44+
if (colonPos != -1) {
45+
filePath = p_vxUrl.mid(colonPos + 1);
46+
filePath = PathUtils::fileName(filePath); // get 'filePath'
47+
return filePath;
48+
}
49+
} // if not 'filePath', return original 'vxUrl'
50+
return p_vxUrl;
51+
}
52+
53+
QString VxUrlUtils::getSignatureFromFilePath(const QString &p_filePath)
54+
{
55+
QFileInfo fileInfo(p_filePath);
56+
QString vxJsonPath;
57+
QString currentFileName;
58+
// get file's signature from vx.json
59+
if (fileInfo.isFile()) {
60+
QString dirPath = PathUtils::parentDirPath(p_filePath);
61+
vxJsonPath = PathUtils::concatenateFilePath(dirPath, "vx.json");
62+
currentFileName = PathUtils::fileName(p_filePath);
63+
} else if (fileInfo.isDir()) {
64+
vxJsonPath = PathUtils::concatenateFilePath(p_filePath, "vx.json");
65+
currentFileName = PathUtils::fileName(p_filePath);
66+
} else {
67+
return QString();
68+
}
69+
70+
QFile vxFile(vxJsonPath);
71+
if (!vxFile.open(QIODevice::ReadOnly)) {
72+
return QString();
73+
}
74+
75+
QByteArray data = vxFile.readAll();
76+
vxFile.close();
77+
78+
QJsonParseError parseError;
79+
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
80+
if (parseError.error != QJsonParseError::NoError) {
81+
return QString();
82+
}
83+
84+
QJsonObject obj = doc.object();
85+
QString signature;
86+
87+
if (obj.contains("files") && obj["files"].isArray()) {
88+
QJsonArray filesArray = obj["files"].toArray();
89+
for (const QJsonValue &fileVal : filesArray) {
90+
QJsonObject fileObj = fileVal.toObject();
91+
if (fileObj["name"].toString() == currentFileName) {
92+
signature = fileObj["signature"].toString();
93+
return signature;
94+
}
95+
}
96+
}
97+
98+
if (signature.isEmpty() && obj.contains("signature")) {
99+
signature = obj["signature"].toString();
100+
}
101+
102+
return signature;
103+
}
104+
105+
QString VxUrlUtils::getFilePathFromSignature(const QString &p_startPath, const QString &p_signature)
106+
{
107+
// Find the file with the specified signature in all vx.json files under the specified directory
108+
QDirIterator it(p_startPath, {"vx.json"}, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
109+
110+
while (it.hasNext()) {
111+
const QString vxPath = it.next();
112+
QFile vxFile(vxPath);
113+
if (!vxFile.open(QIODevice::ReadOnly)) {
114+
continue;
115+
}
116+
117+
const QByteArray data = vxFile.readAll();
118+
vxFile.close();
119+
120+
QJsonParseError parseError;
121+
const QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
122+
if (parseError.error != QJsonParseError::NoError) {
123+
continue;
124+
}
125+
126+
const QJsonObject json = doc.object();
127+
QString signature;
128+
QString fileName;
129+
130+
// Find signature in files array
131+
const auto filesArray = json.value("files").toArray();
132+
for (const auto &fileItem : filesArray) {
133+
const auto fileObj = fileItem.toObject();
134+
if (fileObj["signature"].toString() == p_signature) {
135+
fileName = fileObj["name"].toString();
136+
signature = p_signature;
137+
break;
138+
}
139+
}
140+
// If not found in files array, use directory signature
141+
if (signature.isEmpty()) {
142+
signature = json.value("signature").toString();
143+
}
144+
145+
if (!signature.isEmpty() && signature == p_signature) {
146+
const QString dirPath = QFileInfo(vxPath).absolutePath();
147+
const QString fullPath = PathUtils::concatenateFilePath(dirPath, fileName);
148+
return fullPath;
149+
}
150+
}
151+
152+
return QString();
153+
}

src/utils/vxurlutils.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#ifndef VXURLUTILS_H
2+
#define VXURLUTILS_H
3+
4+
#include <QByteArray>
5+
#include <QString>
6+
#include <QJsonObject>
7+
#include <QDir>
8+
9+
class QTemporaryFile;
10+
11+
namespace vnotex
12+
{
13+
class VxUrlUtils
14+
{
15+
public:
16+
VxUrlUtils() = delete;
17+
18+
// Generate vxUrl.
19+
static QString generateVxURL(const QString &p_signature, const QString &p_filePath);
20+
21+
// Get signature from vxUrl.
22+
static QString getSignatureFromVxURL(const QString &p_vxUrl);
23+
24+
// Get file path from vxUrl.
25+
static QString getFilePathFromVxURL(const QString &p_vxUrl);
26+
27+
// Get signature from file path.
28+
static QString getSignatureFromFilePath(const QString &p_filePath);
29+
30+
// Get file path from signature.
31+
static QString getFilePathFromSignature(const QString &p_startPath, const QString &p_signature);
32+
};
33+
} // ns vnotex
34+
35+
#endif // VXURLUTILS_H

src/widgets/notebooknodeexplorer.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "dialogs/viewtagsdialog.h"
2727
#include <utils/widgetutils.h>
2828
#include <utils/pathutils.h>
29+
#include <utils/vxurlutils.h>
2930
#include <utils/clipboardutils.h>
3031
#include "notebookmgr.h"
3132
#include "widgetsfactory.h"
@@ -1348,14 +1349,25 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent, boo
13481349
this, [this, p_master]() {
13491350
auto nodes = p_master ? getMasterSelectedNodesAndExternalNodes() : getSlaveSelectedNodesAndExternalNodes();
13501351
QStringList files;
1352+
QStringList vxUrls;
13511353
for (const auto &node : nodes.first) {
13521354
files.push_back(node->fetchAbsolutePath());
13531355
}
13541356
for (const auto &node : nodes.second) {
13551357
files.push_back(node->fetchAbsolutePath());
13561358
}
1359+
// find file signature and pinToQuickAccess as VxUrl.
1360+
for (const auto &file : files) {
1361+
QFileInfo fileInfo(file);
1362+
QString signature = VxUrlUtils::getSignatureFromFilePath(file);
1363+
1364+
if (!signature.isEmpty()) {
1365+
QString item = VxUrlUtils::generateVxURL(signature, file);
1366+
vxUrls.append(item);
1367+
}
1368+
}
13571369
if (!files.isEmpty()) {
1358-
emit VNoteX::getInst().pinToQuickAccessRequested(files);
1370+
emit VNoteX::getInst().pinToQuickAccessRequested(vxUrls);
13591371
}
13601372
});
13611373
break;

src/widgets/toolbarhelper.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <utils/widgetutils.h>
1919
#include <utils/docsutils.h>
2020
#include <utils/pathutils.h>
21+
#include <utils/vxurlutils.h>
2122
#include "fullscreentoggleaction.h"
2223
#include <core/configmgr.h>
2324
#include <core/coreconfig.h>
@@ -26,6 +27,7 @@
2627
#include <core/markdowneditorconfig.h>
2728
#include <core/fileopenparameters.h>
2829
#include <core/htmltemplatehelper.h>
30+
#include <core/notebookmgr.h>
2931
#include <core/exception.h>
3032
#include <task/taskmgr.h>
3133
#include <unitedentry/unitedentry.h>
@@ -493,7 +495,10 @@ void ToolBarHelper::updateQuickAccessMenu(QMenu *p_menu)
493495

494496
for (const auto &file : quickAccess) {
495497
auto act = new QWidgetAction(p_menu);
496-
auto widget = new LabelWithButtonsWidget(PathUtils::fileName(file), LabelWithButtonsWidget::Delete);
498+
QString displayName = PathUtils::fileName(file);
499+
QString displayFullName = VxUrlUtils::getFilePathFromVxURL(file);
500+
501+
auto widget = new LabelWithButtonsWidget(displayName, LabelWithButtonsWidget::Delete);
497502
p_menu->connect(widget, &LabelWithButtonsWidget::triggered,
498503
p_menu, [p_menu, act]() {
499504
const auto qaFile = act->data().toString();
@@ -506,7 +511,7 @@ void ToolBarHelper::updateQuickAccessMenu(QMenu *p_menu)
506511
// @act will own @widget.
507512
act->setDefaultWidget(widget);
508513
act->setData(file);
509-
act->setToolTip(file);
514+
act->setToolTip(displayFullName);
510515

511516
// Must call after setDefaultWidget().
512517
p_menu->addAction(act);
@@ -776,10 +781,45 @@ void ToolBarHelper::setupMenuButton(MainWindow *p_win, QToolBar *p_toolBar)
776781
}
777782

778783
void ToolBarHelper::activateQuickAccess(const QString &p_file)
784+
{
785+
if (p_file.startsWith('#')) {
786+
activateQuickAccessFromVxUrl(p_file);
787+
} else
788+
{
789+
activateQuickAccessFilePath(p_file);
790+
}
791+
}
792+
793+
void ToolBarHelper::activateQuickAccessFilePath(const QString &p_file)
779794
{
780795
const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
781796
auto paras = QSharedPointer<FileOpenParameters>::create();
782797
paras->m_mode = coreConfig.getDefaultOpenMode();
783798

784799
emit VNoteX::getInst().openFileRequested(p_file, paras);
785800
}
801+
802+
void ToolBarHelper::activateQuickAccessFromVxUrl(const QString &p_vx_url)
803+
{
804+
auto notebook = VNoteX::getInst().getNotebookMgr().getCurrentNotebook();
805+
if (!notebook) {
806+
return;
807+
}
808+
809+
// get 'signature' from format '#signature:filename'
810+
QString signature = VxUrlUtils::getSignatureFromVxURL(p_vx_url);
811+
812+
// get FilePath from Signature from currentNotebook
813+
const QString rootPath = notebook->getRootFolderAbsolutePath();
814+
const QString filePath = VxUrlUtils::getFilePathFromSignature(rootPath, signature);
815+
816+
const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
817+
auto paras = QSharedPointer<FileOpenParameters>::create();
818+
paras->m_mode = coreConfig.getDefaultOpenMode();
819+
820+
if (filePath.isEmpty()) {
821+
return;
822+
}
823+
824+
emit VNoteX::getInst().openFileRequested(filePath, paras);
825+
}

src/widgets/toolbarhelper.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ namespace vnotex
4949
static void setupMenuButton(MainWindow *p_win, QToolBar *p_toolBar);
5050

5151
static void activateQuickAccess(const QString &p_file);
52+
53+
static void activateQuickAccessFilePath(const QString &p_file);
54+
55+
static void activateQuickAccessFromVxUrl(const QString &p_vx_url);
56+
5257
};
5358
} // ns vnotex
5459

src/widgets/viewsplit.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <core/thememgr.h>
1919
#include <utils/iconutils.h>
2020
#include <utils/pathutils.h>
21+
#include <utils/vxurlutils.h>
2122
#include "widgetsfactory.h"
2223
#include <utils/widgetutils.h>
2324
#include <utils/urldragdroputils.h>
@@ -662,8 +663,14 @@ void ViewSplit::createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx)
662663
[this, p_tabIdx]() {
663664
auto win = getViewWindow(p_tabIdx);
664665
if (win) {
665-
const QStringList files(win->getBuffer()->getPath());
666-
emit VNoteX::getInst().pinToQuickAccessRequested(files);
666+
const QString filePath = win->getBuffer()->getPath();
667+
QString signature = VxUrlUtils::getSignatureFromFilePath(filePath);
668+
669+
if (!signature.isEmpty()) {
670+
QString quickAccessItem = VxUrlUtils::generateVxURL(signature, filePath);
671+
const QStringList files(quickAccessItem);
672+
emit VNoteX::getInst().pinToQuickAccessRequested(files);
673+
}
667674
}
668675
});
669676

0 commit comments

Comments
 (0)