|  |  | @ -1,15 +1,15 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | #include "customDocument.h" |  |  |  | #include "customDocument.h" | 
			
		
	
		
		
			
				
					
					|  |  |  | #include <QDebug> |  |  |  | #include <QDebug> | 
			
		
	
		
		
			
				
					
					|  |  |  | #include <QGuiApplication> |  |  |  | #include <QGuiApplication>//应用程序 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #include <QTextCursor> |  |  |  | #include <QTextCursor>//文本光标 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #include <QMessageBox> |  |  |  | #include <QMessageBox>//消息框 | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | CustomDocument::CustomDocument(QWidget *parent) : QTextEdit(parent) |  |  |  | CustomDocument::CustomDocument(QWidget *parent) : QTextEdit(parent) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     installEventFilter(this); |  |  |  |     installEventFilter(this); // 安装事件过滤器,以便可以处理事件
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     viewport()->installEventFilter(this); |  |  |  |     viewport()->installEventFilter(this); // 安装视口的事件过滤器
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     setMouseTracking(true); |  |  |  |     setMouseTracking(true); // 开启鼠标追踪,以便可以捕获鼠标移动事件
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     setAttribute(Qt::WidgetAttribute::WA_Hover, true); |  |  |  |     setAttribute(Qt::WidgetAttribute::WA_Hover, true); // 设置悬停属性,以便可以处理鼠标悬停事件
 | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | /*!
 |  |  |  | /*!
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -25,16 +25,16 @@ CustomDocument::CustomDocument(QWidget *parent) : QTextEdit(parent) | 
			
		
	
		
		
			
				
					
					|  |  |  |  */ |  |  |  |  */ | 
			
		
	
		
		
			
				
					
					|  |  |  | void CustomDocument::setDocumentPadding(int left, int top, int right, int bottom) |  |  |  | void CustomDocument::setDocumentPadding(int left, int top, int right, int bottom) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     setViewportMargins(left, top, right, bottom); |  |  |  |     setViewportMargins(left, top, right, bottom);//设置视口的边距
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void CustomDocument::resizeEvent(QResizeEvent *event) |  |  |  | void CustomDocument::resizeEvent(QResizeEvent *event)//处理窗口变化
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     QTextEdit::resizeEvent(event); |  |  |  |     QTextEdit::resizeEvent(event); | 
			
		
	
		
		
			
				
					
					|  |  |  |     emit resized(); |  |  |  |     emit resized(); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void CustomDocument::mouseMoveEvent(QMouseEvent *event) |  |  |  | void CustomDocument::mouseMoveEvent(QMouseEvent *event)//处理鼠标移动事件
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     QTextEdit::mouseMoveEvent(event); |  |  |  |     QTextEdit::mouseMoveEvent(event); | 
			
		
	
		
		
			
				
					
					|  |  |  |     emit mouseMoved(); |  |  |  |     emit mouseMoved(); | 
			
		
	
	
		
		
			
				
					|  |  | @ -43,7 +43,9 @@ void CustomDocument::mouseMoveEvent(QMouseEvent *event) | 
			
		
	
		
		
			
				
					
					|  |  |  | bool CustomDocument::eventFilter(QObject *obj, QEvent *event) |  |  |  | bool CustomDocument::eventFilter(QObject *obj, QEvent *event) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     // qDebug() << event->type();
 |  |  |  |     // qDebug() << event->type();
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |     //分情况处理按键和鼠标状况:
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //先处理关注的事件,再处理不关注的事件
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //先处理不常见的事件——如按键。后处理常见的事件——如鼠标移动或松开
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (event->type() == QEvent::HoverMove) { |  |  |  |     if (event->type() == QEvent::HoverMove) { | 
			
		
	
		
		
			
				
					
					|  |  |  |         // if hovering and the control key is active, check whether the mouse is over a link
 |  |  |  |         // if hovering and the control key is active, check whether the mouse is over a link
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (QGuiApplication::keyboardModifiers() == Qt::ExtraButton24 |  |  |  |         if (QGuiApplication::keyboardModifiers() == Qt::ExtraButton24 | 
			
		
	
	
		
		
			
				
					|  |  | @ -52,7 +54,7 @@ bool CustomDocument::eventFilter(QObject *obj, QEvent *event) | 
			
		
	
		
		
			
				
					
					|  |  |  |         } else { |  |  |  |         } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |             viewport()->setCursor(Qt::IBeamCursor); |  |  |  |             viewport()->setCursor(Qt::IBeamCursor); | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } else if (event->type() == QEvent::KeyPress) { |  |  |  |     } else if (event->type() == QEvent::KeyPress) {//检测只按键的事件并做出响应
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         auto *keyEvent = static_cast<QKeyEvent *>(event); |  |  |  |         auto *keyEvent = static_cast<QKeyEvent *>(event); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (keyEvent->key() == Qt::Key_Control) { |  |  |  |         if (keyEvent->key() == Qt::Key_Control) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -69,7 +71,7 @@ bool CustomDocument::eventFilter(QObject *obj, QEvent *event) | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return true; |  |  |  |                 return true; | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } else if (event->type() == QEvent::MouseButtonRelease) { |  |  |  |     } else if (event->type() == QEvent::MouseButtonRelease) {//检测松鼠标的事件并做出响应
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         auto *mouseEvent = static_cast<QMouseEvent *>(event); |  |  |  |         auto *mouseEvent = static_cast<QMouseEvent *>(event); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -85,7 +87,7 @@ bool CustomDocument::eventFilter(QObject *obj, QEvent *event) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             return true; |  |  |  |             return true; | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } else if (event->type() == QEvent::KeyRelease) { |  |  |  |     } else if (event->type() == QEvent::KeyRelease) {//检测松按键的事件并做出响应
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         auto *keyEvent = static_cast<QKeyEvent *>(event); |  |  |  |         auto *keyEvent = static_cast<QKeyEvent *>(event); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         // reset cursor if control key was released
 |  |  |  |         // reset cursor if control key was released
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -103,6 +105,8 @@ bool CustomDocument::eventFilter(QObject *obj, QEvent *event) | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @param position |  |  |  |  * @param position | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @return url string |  |  |  |  * @return url string | 
			
		
	
		
		
			
				
					
					|  |  |  |  */ |  |  |  |  */ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //注释表明函数需要用到text和position变量,并且需要返回一个string类型的url
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //但是在实际操作过程中,作者用QString代替了String类型
 | 
			
		
	
		
		
			
				
					
					|  |  |  | QString CustomDocument::getMarkdownUrlAtPosition(const QString &text, int position) |  |  |  | QString CustomDocument::getMarkdownUrlAtPosition(const QString &text, int position) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     QString url; |  |  |  |     QString url; | 
			
		
	
	
		
		
			
				
					|  |  | @ -116,7 +120,7 @@ QString CustomDocument::getMarkdownUrlAtPosition(const QString &text, int positi | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         const int foundPositionStart = text.indexOf(linkText); |  |  |  |         const int foundPositionStart = text.indexOf(linkText); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (foundPositionStart >= 0) { |  |  |  |         if (foundPositionStart >= 0) {//谨慎处理url的长度引起的溢出问题,是健壮性的体现
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             // calculate end position of found linkText
 |  |  |  |             // calculate end position of found linkText
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             const int foundPositionEnd = foundPositionStart + linkText.size(); |  |  |  |             const int foundPositionEnd = foundPositionStart + linkText.size(); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -136,19 +140,31 @@ QString CustomDocument::getMarkdownUrlAtPosition(const QString &text, int positi | 
			
		
	
		
		
			
				
					
					|  |  |  |  * |  |  |  |  * | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @return QUrl |  |  |  |  * @return QUrl | 
			
		
	
		
		
			
				
					
					|  |  |  |  */ |  |  |  |  */ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //注释表明函数有返回值,且返回值是游标下面的url
 | 
			
		
	
		
		
			
				
					
					|  |  |  | QUrl CustomDocument::getUrlUnderMouse() |  |  |  | QUrl CustomDocument::getUrlUnderMouse() | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     // place a temp cursor at the mouse position
 |  |  |  |     // place a temp cursor at the mouse position
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //获取当前鼠标光标在全局坐标系中的位置
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //并将其映射到视图端口的局部坐标系中
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // 存储在 pos变量中。
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     auto pos = viewport()->mapFromGlobal(QCursor::pos()); |  |  |  |     auto pos = viewport()->mapFromGlobal(QCursor::pos()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //使用 cursorForPosition 方法创建一个 QTextCursor 对象,该对象位于 pos 指定的位置
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     QTextCursor cursor = cursorForPosition(pos); |  |  |  |     QTextCursor cursor = cursorForPosition(pos); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //获取当前 QTextCursor 的位置,即光标在文档中的索引位置
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     const int cursorPosition = cursor.position(); |  |  |  |     const int cursorPosition = cursor.position(); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // select the text of the current block
 |  |  |  |     // select the text of the current block
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //将 QTextCursor 移动到当前文本块的开始位置
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     cursor.movePosition(QTextCursor::StartOfBlock); |  |  |  |     cursor.movePosition(QTextCursor::StartOfBlock); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //计算光标在当前文本块中的索引位置,即光标位置与文本块开始位置的差值
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     const int indexInBlock = cursorPosition - cursor.position(); |  |  |  |     const int indexInBlock = cursorPosition - cursor.position(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //将 QTextCursor 移动到当前文本块的结束位置,并保持起始位置不变
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //即选择文本块中的所有文本
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); |  |  |  |     cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //先放到块的首段,再放到块的尾端,即选中文本块中所有文本,并且可以计算出当前游标的位置,即链接的位置
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // get the correct link from the selected text, or an empty URL if none found
 |  |  |  |     // get the correct link from the selected text, or an empty URL if none found
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //调用 getMarkdownUrlAtPosition 函数,传入选中的文本和光标在文本块中的索引位置,获取对应的 Markdown URL,并将其转换为 QUrl 对象返回。如果未找到 URL,则返回空的 QUrl 对象
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     return QUrl(getMarkdownUrlAtPosition(cursor.selectedText(), indexInBlock)); |  |  |  |     return QUrl(getMarkdownUrlAtPosition(cursor.selectedText(), indexInBlock)); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -158,30 +174,51 @@ QUrl CustomDocument::getUrlUnderMouse() | 
			
		
	
		
		
			
				
					
					|  |  |  | bool CustomDocument::openLinkAtCursorPosition() |  |  |  | bool CustomDocument::openLinkAtCursorPosition() | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     // find out which url in the selected text was clicked
 |  |  |  |     // find out which url in the selected text was clicked
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //先转化成QString变量
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     QUrl const url = getUrlUnderMouse(); |  |  |  |     QUrl const url = getUrlUnderMouse(); | 
			
		
	
		
		
			
				
					
					|  |  |  |     QString const urlString = url.toString(); |  |  |  |     QString const urlString = url.toString(); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |     //检查是否为本地文件类型
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     const bool isFileUrl = urlString.startsWith(QLatin1String("file://")); |  |  |  |     const bool isFileUrl = urlString.startsWith(QLatin1String("file://")); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |     //检查是否为遗留的附件URL?
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     const bool isLegacyAttachmentUrl = urlString.startsWith(QLatin1String("file://attachments")); |  |  |  |     const bool isLegacyAttachmentUrl = urlString.startsWith(QLatin1String("file://attachments")); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //检查是否为本地文件夹类型
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     const bool isLocalFilePath = urlString.startsWith(QLatin1String("/")); |  |  |  |     const bool isLocalFilePath = urlString.startsWith(QLatin1String("/")); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |     //定义一个标志变量,指示是否将本地文件路径转换为 URL。这里硬编码为 true
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     const bool convertLocalFilepathsToURLs = true; |  |  |  |     const bool convertLocalFilepathsToURLs = true; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |     //如果 URL 是有效的,并且通过 isValidUrl 函数验证,或者是文件 URL、本地文件路径或遗留附件 URL,则进入后续处理逻辑
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //其中url.isValid()是基本验证,是 QUrl 类的成员函数
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //用于检查 URL 是否符合基本的 URL 格式规范
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //它会验证 URL 是否包含有效的 scheme、host 和 path 等部分,但不会对 URL 的具体内容进行深入的验证
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //而isValidUrl是自定义的函数,可以对url内容进行核验
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //例如,isValidUrl 可能会检查 URL 是否指向一个有效的资源
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //或者是否符合特定的 URL 模式(如特定的域名或路径格式)
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //它还可以结合应用程序的上下文来判断 URL 是否有效,比如检查数据库中是否存在对应的记录等
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // 但是在下面的实现中,很显然作者并没有实现这样的功能,只是检查了基本格式,可能需要完善
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //两个检验程序依次递增,符合提高效率的要求
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //如不符合基本URL的格式规范,就不用调用自定义的函数进行搜索和审查,降低了对算力的要求。
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if ((url.isValid() && isValidUrl(urlString)) || isFileUrl || isLocalFilePath |  |  |  |     if ((url.isValid() && isValidUrl(urlString)) || isFileUrl || isLocalFilePath | 
			
		
	
		
		
			
				
					
					|  |  |  |         || isLegacyAttachmentUrl) { |  |  |  |         || isLegacyAttachmentUrl) { | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |         //如果 URL 的 scheme(协议)包含在 _ignoredClickUrlSchemata 集合中,则忽略该 URL 并返回 false
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         //可能是自定义内容的一部分,可以实现链接的自定义实现
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         //提高了用户的自定义能力,值得借鉴
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (_ignoredClickUrlSchemata.contains(url.scheme())) { |  |  |  |         if (_ignoredClickUrlSchemata.contains(url.scheme())) { | 
			
		
	
		
		
			
				
					
					|  |  |  |             qDebug() << __func__ << "ignored URL scheme:" << urlString; |  |  |  |             qDebug() << __func__ << "ignored URL scheme:" << urlString; | 
			
		
	
		
		
			
				
					
					|  |  |  |             return false; |  |  |  |             return false; | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         // ignore non-existent files
 |  |  |  |         // ignore non-existent files
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         //检查文件URL
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (isFileUrl) { |  |  |  |         if (isFileUrl) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             //从第8个字符开始提取
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             QString trimmed = urlString.mid(7); |  |  |  |             QString trimmed = urlString.mid(7); | 
			
		
	
		
		
			
				
					
					|  |  |  |             if (!QFile::exists(trimmed)) { |  |  |  |             if (!QFile::exists(trimmed)) { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 qDebug() << __func__ << ": File does not exist:" << urlString; |  |  |  |                 qDebug() << __func__ << ": File does not exist:" << urlString; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 // show a message box
 |  |  |  |                 // show a message box
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 //使用 QMessageBox::warning 显示一个警告信息框,告知用户文件不存在
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 //信息框的标题是 "File not found"
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 //内容是 "The file <strong>%1</strong> does not exist."
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 //其中 %1 被替换为实际的文件路径 trimmed,使用<strong> 标签以突出显示
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 //提示我们可以在输出的deBug中使用html等标签语言,增加信息的分辨度,以更快的进行筛选错误
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                 QMessageBox::warning( |  |  |  |                 QMessageBox::warning( | 
			
		
	
		
		
			
				
					
					|  |  |  |                         nullptr, tr("File not found"), |  |  |  |                         nullptr, tr("File not found"), | 
			
		
	
		
		
			
				
					
					|  |  |  |                         tr("The file <strong>%1</strong> does not exist.").arg(trimmed)); |  |  |  |                         tr("The file <strong>%1</strong> does not exist.").arg(trimmed)); | 
			
		
	
	
		
		
			
				
					|  |  | @ -196,7 +233,8 @@ bool CustomDocument::openLinkAtCursorPosition() | 
			
		
	
		
		
			
				
					
					|  |  |  |                                  tr("The file <strong>%1</strong> does not exist.").arg(urlString)); |  |  |  |                                  tr("The file <strong>%1</strong> does not exist.").arg(urlString)); | 
			
		
	
		
		
			
				
					
					|  |  |  |             return false; |  |  |  |             return false; | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |         //验证完毕后再统一集中打开链接,有利于对打开链接的种类进行集中调控
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         //在后期的纠错过程中,将打开部分的函数放在一起更有利于代码的纠错和DeBug
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (isLocalFilePath && convertLocalFilepathsToURLs) { |  |  |  |         if (isLocalFilePath && convertLocalFilepathsToURLs) { | 
			
		
	
		
		
			
				
					
					|  |  |  |             openUrl(QString("file://") + urlString); |  |  |  |             openUrl(QString("file://") + urlString); | 
			
		
	
		
		
			
				
					
					|  |  |  |         } else { |  |  |  |         } else { | 
			
		
	
	
		
		
			
				
					|  |  | @ -205,7 +243,7 @@ bool CustomDocument::openLinkAtCursorPosition() | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         return true; |  |  |  |         return true; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |     //查找到不是URL就返回false,其余均为true,以保证程序的顺利执行
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     return false; |  |  |  |     return false; | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -215,8 +253,20 @@ bool CustomDocument::openLinkAtCursorPosition() | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @param urlString |  |  |  |  * @param urlString | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @return |  |  |  |  * @return | 
			
		
	
		
		
			
				
					
					|  |  |  |  */ |  |  |  |  */ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //在注释中我们可以了解到函数的作用时间差是否为可达的路径
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //函数需要一个urString,返回某个值
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //实际来看并没有对函数的有效性进行充分验证,函数是失败的
 | 
			
		
	
		
		
			
				
					
					|  |  |  | bool CustomDocument::isValidUrl(const QString &urlString) |  |  |  | bool CustomDocument::isValidUrl(const QString &urlString) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //关于正则表达式
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //使用 QRegularExpression 类来创建一个正则表达式对象,并使用该对象的 match 方法来匹配 urlString
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //正则表达式中,^表示匹配的开始
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //\w+表示有一个或多个单词字符(字母、数字或下划线)
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // 这里应该表示的是http或https或file等之类的,我认为应该写的更详细一些,因为常用的协议就那么些
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // 两个反斜杠
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //.+表示匹配一个或多个任意字符
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // 这里表示的应该是主机及内部的文件路径,没有固定格式
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //总的来说,这里的匹配在我看来很糟糕,没有起到多少自定义的作用
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     const QRegularExpressionMatch match = QRegularExpression(R"(^\w+:\/\/.+)").match(urlString); |  |  |  |     const QRegularExpressionMatch match = QRegularExpression(R"(^\w+:\/\/.+)").match(urlString); | 
			
		
	
		
		
			
				
					
					|  |  |  |     return match.hasMatch(); |  |  |  |     return match.hasMatch(); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | @ -230,6 +280,7 @@ bool CustomDocument::isValidUrl(const QString &urlString) | 
			
		
	
		
		
			
				
					
					|  |  |  |  *   "/path/to/my/file/QOwnNotes.pdf" if the operating system supports that |  |  |  |  *   "/path/to/my/file/QOwnNotes.pdf" if the operating system supports that | 
			
		
	
		
		
			
				
					
					|  |  |  |  *  handler |  |  |  |  *  handler | 
			
		
	
		
		
			
				
					
					|  |  |  |  */ |  |  |  |  */ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //并没有对函数调用做出一定的检查,直接调用函数库打开链接
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void CustomDocument::openUrl(const QString &urlString) |  |  |  | void CustomDocument::openUrl(const QString &urlString) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     qDebug() << "CustomDocument " << __func__ << " - 'urlString': " << urlString; |  |  |  |     qDebug() << "CustomDocument " << __func__ << " - 'urlString': " << urlString; | 
			
		
	
	
		
		
			
				
					|  |  | @ -243,6 +294,7 @@ void CustomDocument::openUrl(const QString &urlString) | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @param text |  |  |  |  * @param text | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @return parsed urls |  |  |  |  * @return parsed urls | 
			
		
	
		
		
			
				
					
					|  |  |  |  */ |  |  |  |  */ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //主要作用是对输入的文本内的链接进行提取
 | 
			
		
	
		
		
			
				
					
					|  |  |  | QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &text) |  |  |  | QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString &text) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     QMap<QString, QString> urlMap; |  |  |  |     QMap<QString, QString> urlMap; | 
			
		
	
	
		
		
			
				
					|  |  | @ -251,6 +303,7 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString & | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // match urls like this: <http://mylink>
 |  |  |  |     // match urls like this: <http://mylink>
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     //    re = QRegularExpression("(<(.+?:\\/\\/.+?)>)");
 |  |  |  |     //    re = QRegularExpression("(<(.+?:\\/\\/.+?)>)");
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //匹配链接类型为<http://mylink>
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     regex = QRegularExpression(QStringLiteral("(<(.+?)>)")); |  |  |  |     regex = QRegularExpression(QStringLiteral("(<(.+?)>)")); | 
			
		
	
		
		
			
				
					
					|  |  |  |     iterator = regex.globalMatch(text); |  |  |  |     iterator = regex.globalMatch(text); | 
			
		
	
		
		
			
				
					
					|  |  |  |     while (iterator.hasNext()) { |  |  |  |     while (iterator.hasNext()) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -259,7 +312,7 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString & | 
			
		
	
		
		
			
				
					
					|  |  |  |         QString url = match.captured(2); |  |  |  |         QString url = match.captured(2); | 
			
		
	
		
		
			
				
					
					|  |  |  |         urlMap[linkText] = url; |  |  |  |         urlMap[linkText] = url; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |     //匹配链接类型为[链接文本](链接)
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     regex = QRegularExpression(R"((\[.*?\]\((.+?)\)))"); |  |  |  |     regex = QRegularExpression(R"((\[.*?\]\((.+?)\)))"); | 
			
		
	
		
		
			
				
					
					|  |  |  |     iterator = regex.globalMatch(text); |  |  |  |     iterator = regex.globalMatch(text); | 
			
		
	
		
		
			
				
					
					|  |  |  |     while (iterator.hasNext()) { |  |  |  |     while (iterator.hasNext()) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -270,6 +323,7 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString & | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // match urls like this: http://mylink
 |  |  |  |     // match urls like this: http://mylink
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //匹配裸链接
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     regex = QRegularExpression(R"(\b\w+?:\/\/[^\s]+[^\s>\)])"); |  |  |  |     regex = QRegularExpression(R"(\b\w+?:\/\/[^\s]+[^\s>\)])"); | 
			
		
	
		
		
			
				
					
					|  |  |  |     iterator = regex.globalMatch(text); |  |  |  |     iterator = regex.globalMatch(text); | 
			
		
	
		
		
			
				
					
					|  |  |  |     while (iterator.hasNext()) { |  |  |  |     while (iterator.hasNext()) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -279,6 +333,7 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString & | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // match urls like this: www.github.com
 |  |  |  |     // match urls like this: www.github.com
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //匹配以www开头的链接
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     regex = QRegularExpression(R"(\bwww\.[^\s]+\.[^\s]+\b)"); |  |  |  |     regex = QRegularExpression(R"(\bwww\.[^\s]+\.[^\s]+\b)"); | 
			
		
	
		
		
			
				
					
					|  |  |  |     iterator = regex.globalMatch(text); |  |  |  |     iterator = regex.globalMatch(text); | 
			
		
	
		
		
			
				
					
					|  |  |  |     while (iterator.hasNext()) { |  |  |  |     while (iterator.hasNext()) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -289,6 +344,7 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString & | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // match reference urls like this: [this url][1] with this later:
 |  |  |  |     // match reference urls like this: [this url][1] with this later:
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // [1]: http://domain
 |  |  |  |     // [1]: http://domain
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //匹配引用定义的链接
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     regex = QRegularExpression(R"((\[.*?\]\[(.+?)\]))"); |  |  |  |     regex = QRegularExpression(R"((\[.*?\]\[(.+?)\]))"); | 
			
		
	
		
		
			
				
					
					|  |  |  |     iterator = regex.globalMatch(text); |  |  |  |     iterator = regex.globalMatch(text); | 
			
		
	
		
		
			
				
					
					|  |  |  |     while (iterator.hasNext()) { |  |  |  |     while (iterator.hasNext()) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -309,6 +365,8 @@ QMap<QString, QString> CustomDocument::parseMarkdownUrlsFromText(const QString & | 
			
		
	
		
		
			
				
					
					|  |  |  |     return urlMap; |  |  |  |     return urlMap; | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //向上移动一个块
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //具体操作是,先删除本块内容,再移动到上一块的开始处,插入本块和换行,最后移动光标到指定位置
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void CustomDocument::moveBlockUp() |  |  |  | void CustomDocument::moveBlockUp() | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     QTextCursor cursor = textCursor(); |  |  |  |     QTextCursor cursor = textCursor(); | 
			
		
	
	
		
		
			
				
					|  |  | @ -342,6 +400,7 @@ void CustomDocument::moveBlockUp() | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //向下移动一个块,同理
 | 
			
		
	
		
		
			
				
					
					|  |  |  | void CustomDocument::moveBlockDown() |  |  |  | void CustomDocument::moveBlockDown() | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     QTextCursor cursor = textCursor(); |  |  |  |     QTextCursor cursor = textCursor(); | 
			
		
	
	
		
		
			
				
					|  |  | 
 |