diff --git a/src/resources/markdown-it.js b/src/resources/markdown-it.js
index 4a31f5dd..6bdd1bba 100644
--- a/src/resources/markdown-it.js
+++ b/src/resources/markdown-it.js
@@ -125,10 +125,10 @@ mdit.use(window.markdownitContainer, 'alert', {
},
render: function (tokens, idx) {
- let type = tokens[idx].info.trim().match(/^(alert-\S+)$/);
+ var type = tokens[idx].info.trim().match(/^(alert-\S+)$/);
if (tokens[idx].nesting === 1) {
// opening tag
- let alertClass = type[1];
+ var alertClass = type[1];
return '
';
} else {
// closing tag
diff --git a/src/resources/markdown_template.html b/src/resources/markdown_template.html
index 0850bb3d..80a4f8e3 100644
--- a/src/resources/markdown_template.html
+++ b/src/resources/markdown_template.html
@@ -25,7 +25,7 @@
-
+
diff --git a/src/resources/markdown_template.js b/src/resources/markdown_template.js
index af331ca6..1db0e68a 100644
--- a/src/resources/markdown_template.js
+++ b/src/resources/markdown_template.js
@@ -114,6 +114,10 @@ if (typeof handleMathjaxReady == 'undefined') {
var handleMathjaxReady = function() {};
}
+if (typeof VWebChannelPort == 'undefined') {
+ var VWebChannelPort = '12345';
+}
+
// Whether highlight special blocks like puml, flowchart.
var highlightSpecialBlocks = false;
@@ -176,54 +180,61 @@ var mute = function(muted) {
g_muteScroll = muted;
};
-new QWebChannel(qt.webChannelTransport,
- function(channel) {
- content = channel.objects.content;
+window.addEventListener('load', function() {
+ var baseUrl = 'ws://localhost:' + VWebChannelPort;
+ var socket = new WebSocket(baseUrl);
+ socket.onopen = function() {
+ console.log('WebSocket connected to ' + baseUrl);
+ new QWebChannel(socket,
+ function(channel) {
+ content = channel.objects.content;
- content.requestScrollToAnchor.connect(scrollToAnchor);
+ content.requestScrollToAnchor.connect(scrollToAnchor);
- content.requestMuted.connect(mute);
+ content.requestMuted.connect(mute);
- if (typeof highlightText == "function") {
- content.requestHighlightText.connect(highlightText);
- content.noticeReadyToHighlightText();
- }
+ if (typeof highlightText == "function") {
+ content.requestHighlightText.connect(highlightText);
+ content.noticeReadyToHighlightText();
+ }
- if (typeof htmlToText == "function") {
- content.requestHtmlToText.connect(htmlToText);
- }
+ if (typeof htmlToText == "function") {
+ content.requestHtmlToText.connect(htmlToText);
+ }
- if (typeof textToHtml == "function") {
- content.requestTextToHtml.connect(textToHtml);
- content.noticeReadyToTextToHtml();
- }
+ if (typeof textToHtml == "function") {
+ content.requestTextToHtml.connect(textToHtml);
+ content.noticeReadyToTextToHtml();
+ }
- if (typeof htmlContent == "function") {
- content.requestHtmlContent.connect(htmlContent);
- }
+ if (typeof htmlContent == "function") {
+ content.requestHtmlContent.connect(htmlContent);
+ }
- content.plantUMLResultReady.connect(handlePlantUMLResult);
- content.graphvizResultReady.connect(handleGraphvizResult);
+ content.plantUMLResultReady.connect(handlePlantUMLResult);
+ content.graphvizResultReady.connect(handleGraphvizResult);
- content.requestPreviewEnabled.connect(setPreviewEnabled);
+ content.requestPreviewEnabled.connect(setPreviewEnabled);
- content.requestPreviewCodeBlock.connect(previewCodeBlock);
+ content.requestPreviewCodeBlock.connect(previewCodeBlock);
- content.requestSetPreviewContent.connect(setPreviewContent);
- content.requestPerformSmartLivePreview.connect(performSmartLivePreview);
+ content.requestSetPreviewContent.connect(setPreviewContent);
+ content.requestPerformSmartLivePreview.connect(performSmartLivePreview);
- if (typeof updateHtml == "function") {
- updateHtml(content.html);
- content.htmlChanged.connect(updateHtml);
- }
+ if (typeof updateHtml == "function") {
+ updateHtml(content.html);
+ content.htmlChanged.connect(updateHtml);
+ }
- if (typeof updateText == "function") {
- content.textChanged.connect(updateText);
- content.updateText();
- }
+ if (typeof updateText == "function") {
+ content.textChanged.connect(updateText);
+ content.updateText();
+ }
- channelInitialized = true;
- });
+ channelInitialized = true;
+ });
+ }
+});
var VHighlightedAnchorClass = 'highlighted-anchor';
@@ -818,7 +829,7 @@ var renderPlantUMLOneOnline = function(code) {
++asyncJobsCount;
code.classList.add(plantUMLCodeClass + plantUMLIdx);
- let data = { index: plantUMLIdx,
+ var data = { index: plantUMLIdx,
setupView: !VPreviewMode
};
renderPlantUMLOnline(VPlantUMLServer,
@@ -1510,7 +1521,7 @@ var handleGraphvizResult = function(id, timeStamp, format, result) {
if (format == 'svg') {
obj = document.createElement('p');
obj.innerHTML = result;
- setupSVGToView(obj.children[0]);
+ setupSVGToView(obj.children[0], false);
} else {
obj = document.createElement('img');
obj.src = "data:image/" + format + ";base64, " + result;
diff --git a/src/resources/qwebchannel.js b/src/resources/qwebchannel.js
deleted file mode 100644
index 1d84b8e7..00000000
--- a/src/resources/qwebchannel.js
+++ /dev/null
@@ -1,430 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebChannel module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-"use strict";
-
-var QWebChannelMessageTypes = {
- signal: 1,
- propertyUpdate: 2,
- init: 3,
- idle: 4,
- debug: 5,
- invokeMethod: 6,
- connectToSignal: 7,
- disconnectFromSignal: 8,
- setProperty: 9,
- response: 10,
-};
-
-var QWebChannel = function(transport, initCallback)
-{
- if (typeof transport !== "object" || typeof transport.send !== "function") {
- console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +
- " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));
- return;
- }
-
- var channel = this;
- this.transport = transport;
-
- this.send = function(data)
- {
- if (typeof(data) !== "string") {
- data = JSON.stringify(data);
- }
- channel.transport.send(data);
- }
-
- this.transport.onmessage = function(message)
- {
- var data = message.data;
- if (typeof data === "string") {
- data = JSON.parse(data);
- }
- switch (data.type) {
- case QWebChannelMessageTypes.signal:
- channel.handleSignal(data);
- break;
- case QWebChannelMessageTypes.response:
- channel.handleResponse(data);
- break;
- case QWebChannelMessageTypes.propertyUpdate:
- channel.handlePropertyUpdate(data);
- break;
- default:
- console.error("invalid message received:", message.data);
- break;
- }
- }
-
- this.execCallbacks = {};
- this.execId = 0;
- this.exec = function(data, callback)
- {
- if (!callback) {
- // if no callback is given, send directly
- channel.send(data);
- return;
- }
- if (channel.execId === Number.MAX_VALUE) {
- // wrap
- channel.execId = Number.MIN_VALUE;
- }
- if (data.hasOwnProperty("id")) {
- console.error("Cannot exec message with property id: " + JSON.stringify(data));
- return;
- }
- data.id = channel.execId++;
- channel.execCallbacks[data.id] = callback;
- channel.send(data);
- };
-
- this.objects = {};
-
- this.handleSignal = function(message)
- {
- var object = channel.objects[message.object];
- if (object) {
- object.signalEmitted(message.signal, message.args);
- } else {
- console.warn("Unhandled signal: " + message.object + "::" + message.signal);
- }
- }
-
- this.handleResponse = function(message)
- {
- if (!message.hasOwnProperty("id")) {
- console.error("Invalid response message received: ", JSON.stringify(message));
- return;
- }
- channel.execCallbacks[message.id](message.data);
- delete channel.execCallbacks[message.id];
- }
-
- this.handlePropertyUpdate = function(message)
- {
- for (var i in message.data) {
- var data = message.data[i];
- var object = channel.objects[data.object];
- if (object) {
- object.propertyUpdate(data.signals, data.properties);
- } else {
- console.warn("Unhandled property update: " + data.object + "::" + data.signal);
- }
- }
- channel.exec({type: QWebChannelMessageTypes.idle});
- }
-
- this.debug = function(message)
- {
- channel.send({type: QWebChannelMessageTypes.debug, data: message});
- };
-
- channel.exec({type: QWebChannelMessageTypes.init}, function(data) {
- for (var objectName in data) {
- var object = new QObject(objectName, data[objectName], channel);
- }
- // now unwrap properties, which might reference other registered objects
- for (var objectName in channel.objects) {
- channel.objects[objectName].unwrapProperties();
- }
- if (initCallback) {
- initCallback(channel);
- }
- channel.exec({type: QWebChannelMessageTypes.idle});
- });
-};
-
-function QObject(name, data, webChannel)
-{
- this.__id__ = name;
- webChannel.objects[name] = this;
-
- // List of callbacks that get invoked upon signal emission
- this.__objectSignals__ = {};
-
- // Cache of all properties, updated when a notify signal is emitted
- this.__propertyCache__ = {};
-
- var object = this;
-
- // ----------------------------------------------------------------------
-
- this.unwrapQObject = function(response)
- {
- if (response instanceof Array) {
- // support list of objects
- var ret = new Array(response.length);
- for (var i = 0; i < response.length; ++i) {
- ret[i] = object.unwrapQObject(response[i]);
- }
- return ret;
- }
- if (!response
- || !response["__QObject*__"]
- || response.id === undefined) {
- return response;
- }
-
- var objectId = response.id;
- if (webChannel.objects[objectId])
- return webChannel.objects[objectId];
-
- if (!response.data) {
- console.error("Cannot unwrap unknown QObject " + objectId + " without data.");
- return;
- }
-
- var qObject = new QObject( objectId, response.data, webChannel );
- qObject.destroyed.connect(function() {
- if (webChannel.objects[objectId] === qObject) {
- delete webChannel.objects[objectId];
- // reset the now deleted QObject to an empty {} object
- // just assigning {} though would not have the desired effect, but the
- // below also ensures all external references will see the empty map
- // NOTE: this detour is necessary to workaround QTBUG-40021
- var propertyNames = [];
- for (var propertyName in qObject) {
- propertyNames.push(propertyName);
- }
- for (var idx in propertyNames) {
- delete qObject[propertyNames[idx]];
- }
- }
- });
- // here we are already initialized, and thus must directly unwrap the properties
- qObject.unwrapProperties();
- return qObject;
- }
-
- this.unwrapProperties = function()
- {
- for (var propertyIdx in object.__propertyCache__) {
- object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);
- }
- }
-
- function addSignal(signalData, isPropertyNotifySignal)
- {
- var signalName = signalData[0];
- var signalIndex = signalData[1];
- object[signalName] = {
- connect: function(callback) {
- if (typeof(callback) !== "function") {
- console.error("Bad callback given to connect to signal " + signalName);
- return;
- }
-
- object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
- object.__objectSignals__[signalIndex].push(callback);
-
- if (!isPropertyNotifySignal && signalName !== "destroyed") {
- // only required for "pure" signals, handled separately for properties in propertyUpdate
- // also note that we always get notified about the destroyed signal
- webChannel.exec({
- type: QWebChannelMessageTypes.connectToSignal,
- object: object.__id__,
- signal: signalIndex
- });
- }
- },
- disconnect: function(callback) {
- if (typeof(callback) !== "function") {
- console.error("Bad callback given to disconnect from signal " + signalName);
- return;
- }
- object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
- var idx = object.__objectSignals__[signalIndex].indexOf(callback);
- if (idx === -1) {
- console.error("Cannot find connection of signal " + signalName + " to " + callback.name);
- return;
- }
- object.__objectSignals__[signalIndex].splice(idx, 1);
- if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {
- // only required for "pure" signals, handled separately for properties in propertyUpdate
- webChannel.exec({
- type: QWebChannelMessageTypes.disconnectFromSignal,
- object: object.__id__,
- signal: signalIndex
- });
- }
- }
- };
- }
-
- /**
- * Invokes all callbacks for the given signalname. Also works for property notify callbacks.
- */
- function invokeSignalCallbacks(signalName, signalArgs)
- {
- var connections = object.__objectSignals__[signalName];
- if (connections) {
- connections.forEach(function(callback) {
- callback.apply(callback, signalArgs);
- });
- }
- }
-
- this.propertyUpdate = function(signals, propertyMap)
- {
- // update property cache
- for (var propertyIndex in propertyMap) {
- var propertyValue = propertyMap[propertyIndex];
- object.__propertyCache__[propertyIndex] = propertyValue;
- }
-
- for (var signalName in signals) {
- // Invoke all callbacks, as signalEmitted() does not. This ensures the
- // property cache is updated before the callbacks are invoked.
- invokeSignalCallbacks(signalName, signals[signalName]);
- }
- }
-
- this.signalEmitted = function(signalName, signalArgs)
- {
- invokeSignalCallbacks(signalName, signalArgs);
- }
-
- function addMethod(methodData)
- {
- var methodName = methodData[0];
- var methodIdx = methodData[1];
- object[methodName] = function() {
- var args = [];
- var callback;
- for (var i = 0; i < arguments.length; ++i) {
- if (typeof arguments[i] === "function")
- callback = arguments[i];
- else
- args.push(arguments[i]);
- }
-
- webChannel.exec({
- "type": QWebChannelMessageTypes.invokeMethod,
- "object": object.__id__,
- "method": methodIdx,
- "args": args
- }, function(response) {
- if (response !== undefined) {
- var result = object.unwrapQObject(response);
- if (callback) {
- (callback)(result);
- }
- }
- });
- };
- }
-
- function bindGetterSetter(propertyInfo)
- {
- var propertyIndex = propertyInfo[0];
- var propertyName = propertyInfo[1];
- var notifySignalData = propertyInfo[2];
- // initialize property cache with current value
- // NOTE: if this is an object, it is not directly unwrapped as it might
- // reference other QObject that we do not know yet
- object.__propertyCache__[propertyIndex] = propertyInfo[3];
-
- if (notifySignalData) {
- if (notifySignalData[0] === 1) {
- // signal name is optimized away, reconstruct the actual name
- notifySignalData[0] = propertyName + "Changed";
- }
- addSignal(notifySignalData, true);
- }
-
- Object.defineProperty(object, propertyName, {
- configurable: true,
- get: function () {
- var propertyValue = object.__propertyCache__[propertyIndex];
- if (propertyValue === undefined) {
- // This shouldn't happen
- console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__);
- }
-
- return propertyValue;
- },
- set: function(value) {
- if (value === undefined) {
- console.warn("Property setter for " + propertyName + " called with undefined value!");
- return;
- }
- object.__propertyCache__[propertyIndex] = value;
- webChannel.exec({
- "type": QWebChannelMessageTypes.setProperty,
- "object": object.__id__,
- "property": propertyIndex,
- "value": value
- });
- }
- });
-
- }
-
- // ----------------------------------------------------------------------
-
- data.methods.forEach(addMethod);
-
- data.properties.forEach(bindGetterSetter);
-
- data.signals.forEach(function(signal) { addSignal(signal, false); });
-
- for (var name in data.enums) {
- object[name] = data.enums[name];
- }
-}
-
-//required for use with nodejs
-if (typeof module === 'object') {
- module.exports = {
- QWebChannel: QWebChannel
- };
-}
diff --git a/src/resources/view_image.js b/src/resources/view_image.js
index 843eaf78..c9ac7de3 100644
--- a/src/resources/view_image.js
+++ b/src/resources/view_image.js
@@ -1,6 +1,6 @@
var imageViewDiv = document.getElementById('image-view-div');
-var viewImage = function(imgSrc, background = 'transparent') {
+var viewImage = function(imgSrc, background) {
viewBoxImageMouseDown = false;
imageViewDiv.style.display = 'block';
@@ -16,10 +16,10 @@ var viewImage = function(imgSrc, background = 'transparent') {
};
var viewIMG = function(imgNode) {
- viewImage(imgNode.src);
+ viewImage(imgNode.src, 'transparent');
};
-var viewSVG = function(svgNode, background = 'transparent') {
+var viewSVG = function(svgNode, background) {
var svg = svgNode.outerHTML.replace(/#/g, '%23').replace(/[\r\n]/g, '');
var src = 'data:image/svg+xml;utf8,' + svg;
@@ -173,14 +173,14 @@ var onSVGDoubleClick = function(forceBackground, e) {
viewSVG(this, style.backgroundColor);
}
} else {
- viewSVG(this);
+ viewSVG(this, 'transparent');
}
e.preventDefault();
}
};
-var setupSVGToView = function(node, forceBackground = false) {
+var setupSVGToView = function(node, forceBackground) {
if (!node || node.nodeName.toLowerCase() != 'svg') {
return;
}
diff --git a/src/src.pro b/src/src.pro
index 29429907..43be1e77 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -4,7 +4,7 @@
#
#-------------------------------------------------
-QT += core gui network svg printsupport webkitwidgets
+QT += core gui network svg printsupport webkitwidgets webchannel websockets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
@@ -156,7 +156,10 @@ SOURCES += main.cpp\
vtablehelper.cpp \
vtable.cpp \
dialog/vinserttabledialog.cpp \
- vpreviewpage.cpp
+ vpreviewpage.cpp \
+ vwebview.cpp \
+ websocketclientwrapper.cpp \
+ websockettransport.cpp
HEADERS += vmainwindow.h \
vdirectorytree.h \
@@ -297,7 +300,10 @@ HEADERS += vmainwindow.h \
vtablehelper.h \
vtable.h \
dialog/vinserttabledialog.h \
- vpreviewpage.h
+ vpreviewpage.h \
+ vwebview.h \
+ websocketclientwrapper.h \
+ websockettransport.h
RESOURCES += \
vnote.qrc \
diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp
index 3dae8d2e..5af96956 100644
--- a/src/utils/vutils.cpp
+++ b/src/utils/vutils.cpp
@@ -655,9 +655,9 @@ QString VUtils::generateSimpleHtmlTemplate(const QString &p_body)
return html.replace(HtmlHolder::c_bodyHolder, p_body);
}
-QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType)
+QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType, quint16 p_port)
{
- return generateHtmlTemplate(VNote::s_markdownTemplate, p_conType);
+ return generateHtmlTemplate(VNote::s_markdownTemplate, p_conType, p_port);
}
QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType,
@@ -675,11 +675,12 @@ QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType,
g_config->getCodeBlockCssStyleUrl(p_renderCodeBlockStyle),
p_isPDF);
- return generateHtmlTemplate(templ, p_conType, p_isPDF, p_wkhtmltopdf, p_addToc);
+ return generateHtmlTemplate(templ, p_conType, 0, p_isPDF, p_wkhtmltopdf, p_addToc);
}
QString VUtils::generateHtmlTemplate(const QString &p_template,
MarkdownConverterType p_conType,
+ quint16 p_port,
bool p_isPDF,
bool p_wkhtmltopdf,
bool p_addToc)
@@ -882,6 +883,8 @@ QString VUtils::generateHtmlTemplate(const QString &p_template,
extraFile += "\n";
#endif
+ extraFile += "\n";
+
QString htmlTemplate(p_template);
htmlTemplate.replace(HtmlHolder::c_JSHolder, jsFile);
if (!extraFile.isEmpty()) {
diff --git a/src/utils/vutils.h b/src/utils/vutils.h
index 24c155f7..62205f9a 100644
--- a/src/utils/vutils.h
+++ b/src/utils/vutils.h
@@ -186,7 +186,7 @@ public:
static DocType docTypeFromName(const QString &p_name);
// Generate HTML template.
- static QString generateHtmlTemplate(MarkdownConverterType p_conType);
+ static QString generateHtmlTemplate(MarkdownConverterType p_conType, quint16 p_port);
// @p_renderBg is the background name.
// @p_wkhtmltopdf: whether this template is used for wkhtmltopdf.
@@ -464,6 +464,7 @@ private:
static QString generateHtmlTemplate(const QString &p_template,
MarkdownConverterType p_conType,
+ quint16 p_port,
bool p_isPDF = false,
bool p_wkhtmltopdf = false,
bool p_addToc = false);
diff --git a/src/veditarea.cpp b/src/veditarea.cpp
index e48e2e83..9ad1864e 100644
--- a/src/veditarea.cpp
+++ b/src/veditarea.cpp
@@ -292,7 +292,7 @@ int VEditArea::openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_m
{
Q_ASSERT(windowIndex < splitter->count());
VEditWindow *win = getWindow(windowIndex);
- return win->openFile(p_file, OpenFileMode::Edit);
+ return win->openFile(p_file, p_mode);
}
void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus)
diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp
index 7b364606..8eb8c62a 100644
--- a/src/veditwindow.cpp
+++ b/src/veditwindow.cpp
@@ -150,7 +150,6 @@ int VEditWindow::insertEditTab(int p_index, VFile *p_file, QWidget *p_page)
int VEditWindow::openFile(VFile *p_file, OpenFileMode p_mode)
{
- qDebug() << "open" << p_file->getName();
// Find if it has been opened already
int idx = findTabByFile(p_file);
if (idx > -1) {
diff --git a/src/vfile.cpp b/src/vfile.cpp
index 874bc4cc..a01c521d 100644
--- a/src/vfile.cpp
+++ b/src/vfile.cpp
@@ -89,7 +89,7 @@ QUrl VFile::getBaseUrl() const
// Need to judge the path: Url, local file, resource file.
QUrl baseUrl;
// Use file path to make in page anchor work.
- QString filePath = fetchPath();
+ QString filePath = fetchBasePath();
QFileInfo pathInfo(filePath);
if (pathInfo.exists()) {
if (pathInfo.isNativePath()) {
diff --git a/src/vfilelist.cpp b/src/vfilelist.cpp
index 7f4b166e..ba15b8d0 100644
--- a/src/vfilelist.cpp
+++ b/src/vfilelist.cpp
@@ -561,18 +561,31 @@ void VFileList::contextMenuRequested(QPoint pos)
VNoteFile *file = getVFile(item);
if (file) {
if (file->getDocType() == DocType::Markdown) {
- QAction *openAct = new QAction(VIconUtils::menuIcon(":/resources/icons/editing.svg"),
- tr("Open"),
+ QAction *openInReadAct = new QAction(VIconUtils::menuIcon(":/resources/icons/reading.svg"),
+ tr("&Open In Read Mode"),
&menu);
- openAct->setToolTip(tr("Open and edit current note"));
- connect(openAct, &QAction::triggered,
+ openInReadAct->setToolTip(tr("Open current note in read mode"));
+ connect(openInReadAct, &QAction::triggered,
+ this, [this]() {
+ QListWidgetItem *item = fileList->currentItem();
+ if (item) {
+ emit fileClicked(getVFile(item), OpenFileMode::Read, true);
+ }
+ });
+ menu.addAction(openInReadAct);
+
+ QAction *openInEditAct = new QAction(VIconUtils::menuIcon(":/resources/icons/editing.svg"),
+ tr("Open In &Edit Mode"),
+ &menu);
+ openInEditAct->setToolTip(tr("Open current note in edit mode"));
+ connect(openInEditAct, &QAction::triggered,
this, [this]() {
QListWidgetItem *item = fileList->currentItem();
if (item) {
emit fileClicked(getVFile(item), OpenFileMode::Edit, true);
}
});
- menu.addAction(openAct);
+ menu.addAction(openInEditAct);
}
menu.addMenu(getOpenWithMenu());
@@ -811,8 +824,7 @@ void VFileList::activateItem(QListWidgetItem *p_item, bool p_restoreFocus)
// Qt seems not to update the QListWidget correctly. Manually force it to repaint.
fileList->update();
- // emit fileClicked(getVFile(p_item), g_config->getNoteOpenMode());
- emit fileClicked(getVFile(p_item), OpenFileMode::Edit);
+ emit fileClicked(getVFile(p_item), g_config->getNoteOpenMode());
if (p_restoreFocus) {
fileList->setFocus();
diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp
index 74e78a3f..28938196 100644
--- a/src/vmainwindow.cpp
+++ b/src/vmainwindow.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include "vmainwindow.h"
#include "vdirectorytree.h"
@@ -17,11 +18,11 @@
#include "dialog/vsettingsdialog.h"
#include "vcaptain.h"
#include "vedittab.h"
+#include "vwebview.h"
#include "vmdtab.h"
#include "vvimindicator.h"
#include "vvimcmdlineedit.h"
#include "vtabindicator.h"
-// #include "dialog/vupdater.h"
#include "vorphanfile.h"
#include "dialog/vorphanfileinfodialog.h"
#include "vsingleinstanceguard.h"
@@ -751,16 +752,22 @@ QToolBar *VMainWindow::initFileToolBar(QSize p_iconSize)
connect(deleteNoteAct, &QAction::triggered,
this, &VMainWindow::deleteCurNote);
- m_discardAct = new QAction(VIconUtils::menuIcon(":/resources/icons/discard_exit.svg"),
- tr("Discard Changes"),
+ m_editReadAct = new QAction(this);
+ connect(m_editReadAct, &QAction::triggered,
+ this, &VMainWindow::toggleEditReadMode);
+
+ m_discardExitAct = new QAction(VIconUtils::menuIcon(":/resources/icons/discard_exit.svg"),
+ tr("Discard Changes And Read"),
this);
- VUtils::fixTextWithCaptainShortcut(m_discardAct, "DiscardAndRead");
- m_discardAct->setStatusTip(tr("Discard changes"));
- connect(m_discardAct, &QAction::triggered,
+ VUtils::fixTextWithCaptainShortcut(m_discardExitAct, "DiscardAndRead");
+ m_discardExitAct->setStatusTip(tr("Discard changes and exit edit mode"));
+ connect(m_discardExitAct, &QAction::triggered,
this, [this]() {
m_editArea->readFile(true);
});
+ updateEditReadAct(nullptr);
+
saveNoteAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/save_note.svg"),
tr("Save"), this);
saveNoteAct->setStatusTip(tr("Save changes to current note"));
@@ -778,7 +785,8 @@ QToolBar *VMainWindow::initFileToolBar(QSize p_iconSize)
newNoteAct->setEnabled(false);
noteInfoAct->setEnabled(false);
deleteNoteAct->setEnabled(false);
- m_discardAct->setEnabled(false);
+ m_editReadAct->setEnabled(false);
+ m_discardExitAct->setEnabled(false);
saveNoteAct->setEnabled(false);
m_fileToolBar->addWidget(m_avatarBtn);
@@ -786,7 +794,8 @@ QToolBar *VMainWindow::initFileToolBar(QSize p_iconSize)
m_fileToolBar->addAction(newNoteAct);
m_fileToolBar->addAction(deleteNoteAct);
m_fileToolBar->addAction(noteInfoAct);
- m_fileToolBar->addAction(m_discardAct);
+ m_fileToolBar->addAction(m_editReadAct);
+ m_fileToolBar->addAction(m_discardExitAct);
m_fileToolBar->addAction(saveNoteAct);
return m_fileToolBar;
@@ -1042,7 +1051,6 @@ void VMainWindow::initFileMenu()
fileMenu->addSeparator();
- // Export as PDF.
/*
m_exportAct = new QAction(tr("E&xport"), this);
m_exportAct->setToolTip(tr("Export notes"));
@@ -2076,8 +2084,7 @@ void VMainWindow::updateActionsStateFromTab(const VEditTab *p_tab)
&& file->getType() == FileType::Orphan
&& dynamic_cast(file)->isSystemFile();
-
- m_discardAct->setEnabled(file && editMode && p_tab->isModified());
+ updateEditReadAct(p_tab);
saveNoteAct->setEnabled(file && editMode && file->isModifiable());
deleteNoteAct->setEnabled(file && file->getType() == FileType::Note);
@@ -2885,7 +2892,7 @@ bool VMainWindow::discardAndReadByCaptain(void *p_target, void *p_data)
Q_UNUSED(p_data);
VMainWindow *obj = static_cast(p_target);
if (obj->m_curTab) {
- obj->m_discardAct->trigger();
+ obj->m_discardExitAct->trigger();
obj->m_curTab->setFocus();
return false;
@@ -3265,6 +3272,47 @@ void VMainWindow::toggleEditReadMode()
}
}
+void VMainWindow::updateEditReadAct(const VEditTab *p_tab)
+{
+ static QIcon editIcon = VIconUtils::toolButtonIcon(":/resources/icons/edit_note.svg");
+ static QString editText;
+ static QIcon readIcon = VIconUtils::toolButtonIcon(":/resources/icons/save_exit.svg");
+ static QString readText;
+
+ if (editText.isEmpty()) {
+ QString keySeq = g_config->getShortcutKeySequence("EditReadNote");
+ QKeySequence seq(keySeq);
+ if (!seq.isEmpty()) {
+ QString shortcutText = VUtils::getShortcutText(keySeq);
+ editText = tr("Edit\t%1").arg(shortcutText);
+ readText = tr("Save Changes And Read\t%1").arg(shortcutText);
+
+ m_editReadAct->setShortcut(seq);
+ } else {
+ editText = tr("Edit");
+ readText = tr("Save Changes And Read");
+ }
+ }
+
+ if (!p_tab || !p_tab->isEditMode()) {
+ // Edit.
+ m_editReadAct->setIcon(editIcon);
+ m_editReadAct->setText(editText);
+ m_editReadAct->setStatusTip(tr("Edit current note"));
+
+ m_discardExitAct->setEnabled(false);
+ } else {
+ // Read.
+ m_editReadAct->setIcon(readIcon);
+ m_editReadAct->setText(readText);
+ m_editReadAct->setStatusTip(tr("Save changes and exit edit mode"));
+
+ m_discardExitAct->setEnabled(true);
+ }
+
+ m_editReadAct->setEnabled(p_tab);
+}
+
void VMainWindow::handleExportAct()
{
}
diff --git a/src/vmainwindow.h b/src/vmainwindow.h
index 48dfbe43..2da51331 100644
--- a/src/vmainwindow.h
+++ b/src/vmainwindow.h
@@ -319,6 +319,8 @@ private:
void initThemeMenu(QMenu *p_emnu);
+ void updateEditReadAct(const VEditTab *p_tab);
+
void initUniversalEntry();
void setMenuBarVisible(bool p_visible);
@@ -419,14 +421,21 @@ private:
QAction *deleteNoteAct;
+ // Toggle read and edit note.
+ QAction *m_editReadAct;
+
QAction *saveNoteAct;
- QAction *m_discardAct;
+ QAction *m_discardExitAct;
QAction *expandViewAct;
QAction *m_importNoteAct;
+ QAction *m_printAct;
+
+ QAction *m_exportAct;
+
QAction *m_findReplaceAct;
QAction *m_findNextAct;
diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp
index a10543f9..df578da8 100644
--- a/src/vmdtab.cpp
+++ b/src/vmdtab.cpp
@@ -1,13 +1,13 @@
#include
-// #include
#include
#include
// #include
+
#include "vmdtab.h"
#include "vdocument.h"
#include "vnote.h"
#include "utils/vutils.h"
-// #include "vpreviewpage.h"
+#include "vpreviewpage.h"
#include "pegmarkdownhighlighter.h"
#include "vconfigmanager.h"
#include "vmarkdownconverter.h"
@@ -29,12 +29,15 @@ extern VMainWindow *g_mainWin;
extern VConfigManager *g_config;
+const quint16 VMdTab::c_basePort = 10999;
+QSet VMdTab::s_usedPorts;
VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
OpenFileMode p_mode, QWidget *p_parent)
: VEditTab(p_file, p_editArea, p_parent),
m_editor(NULL),
- // m_webViewer(NULL),
+ m_webViewer(NULL),
+ m_port(getNextPort()),
m_document(NULL),
m_mdConType(g_config->getMdConverterType()),
m_enableHeadingSequence(false),
@@ -79,8 +82,6 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
m_livePreviewTimer->setInterval(500);
connect(m_livePreviewTimer, &QTimer::timeout,
this, [this]() {
- Q_ASSERT(false);
- /*
QString text = m_webViewer->selectedText().trimmed();
if (text.isEmpty()) {
return;
@@ -94,7 +95,6 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
info.m_startPos,
info.m_endPos);
}
- */
});
QTimer::singleShot(50, this, [this, p_mode]() {
@@ -106,6 +106,11 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
});
}
+VMdTab::~VMdTab()
+{
+ releasePort(m_port);
+}
+
void VMdTab::setupUI()
{
m_splitter = new QSplitter(this);
@@ -124,7 +129,6 @@ void VMdTab::setupUI()
void VMdTab::showFileReadMode()
{
- Q_ASSERT(false);
m_isEditMode = false;
// Will recover the header when web side is ready.
@@ -292,11 +296,13 @@ bool VMdTab::closeFile(bool p_forced)
Q_ASSERT(m_editor);
m_editor->reloadFile();
m_editor->endEdit();
+
+ showFileReadMode();
+ } else {
+ readFile();
}
- readFile();
-
- return !isModified();
+ return !m_isEditMode;
}
void VMdTab::editFile()
@@ -355,7 +361,7 @@ void VMdTab::readFile(bool p_discard)
m_editor->endEdit();
}
- showFileEditMode();
+ showFileReadMode();
}
bool VMdTab::saveFile()
@@ -436,8 +442,8 @@ void VMdTab::discardAndRead()
void VMdTab::setupMarkdownViewer()
{
- /*
m_webViewer = new VWebView(m_file, this);
+
connect(m_webViewer, &VWebView::editNote,
this, &VMdTab::editFile);
connect(m_webViewer, &VWebView::requestSavePage,
@@ -450,22 +456,24 @@ void VMdTab::setupMarkdownViewer()
VPreviewPage *page = new VPreviewPage(m_webViewer);
m_webViewer->setPage(page);
m_webViewer->setZoomFactor(g_config->getWebZoomFactor());
+ /*
connect(page->profile(), &QWebEngineProfile::downloadRequested,
this, &VMdTab::handleDownloadRequested);
connect(page, &QWebEnginePage::linkHovered,
this, &VMdTab::statusMessage);
+ */
// Avoid white flash before loading content.
// Setting Qt::transparent will force GrayScale antialias rendering.
page->setBackgroundColor(g_config->getBaseBackground());
- */
- m_document = new VDocument(m_file, this);
+ page->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
+
+ m_document = new VDocument(m_file, m_webViewer);
m_documentID = m_document->registerIdentifier();
- /*
- QWebChannel *channel = new QWebChannel(m_webViewer);
- channel->registerObject(QStringLiteral("content"), m_document);
+ m_webViewer->bindToChannel(m_port, QStringLiteral("content"), m_document);
+
connect(m_document, &VDocument::tocChanged,
this, &VMdTab::updateOutlineFromHtml);
connect(m_document, SIGNAL(headerChanged(const QString &)),
@@ -514,13 +522,10 @@ void VMdTab::setupMarkdownViewer()
emit statusUpdated(info);
});
- page->setWebChannel(channel);
-
- m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType),
+ m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType, m_port),
m_file->getBaseUrl());
m_splitter->addWidget(m_webViewer);
- */
}
void VMdTab::setupMarkdownEditor()
@@ -772,20 +777,18 @@ void VMdTab::nextMatch(const QString &p_text, uint p_options, bool p_forward)
void VMdTab::findTextInWebView(const QString &p_text, uint p_options,
bool /* p_peek */, bool p_forward)
{
- V_ASSERT(false);
+ V_ASSERT(m_webViewer);
- /*
- QWebEnginePage::FindFlags flags;
+ QWebPage::FindFlags flags;
if (p_options & FindOption::CaseSensitive) {
- flags |= QWebEnginePage::FindCaseSensitively;
+ flags |= QWebPage::FindCaseSensitively;
}
if (!p_forward) {
- flags |= QWebEnginePage::FindBackward;
+ flags |= QWebPage::FindBackward;
}
m_webViewer->findText(p_text, flags);
- */
}
QString VMdTab::getSelectedText() const
@@ -795,19 +798,15 @@ QString VMdTab::getSelectedText() const
QTextCursor cursor = m_editor->textCursor();
return cursor.selectedText();
} else {
- Q_ASSERT(false);
- // return m_webViewer->selectedText();
- return QString();
+ return m_webViewer->selectedText();
}
}
void VMdTab::clearSearchedWordHighlight()
{
- /*
if (m_webViewer) {
m_webViewer->findText("");
}
- */
if (m_editor) {
m_editor->clearSearchedWordHighlight();
@@ -816,7 +815,7 @@ void VMdTab::clearSearchedWordHighlight()
void VMdTab::handleWebKeyPressed(int p_key, bool p_ctrl, bool p_shift, bool p_meta)
{
- Q_ASSERT(false);
+ Q_ASSERT(m_webViewer);
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
bool macCtrl = p_meta;
#else
@@ -852,7 +851,7 @@ void VMdTab::handleWebKeyPressed(int p_key, bool p_ctrl, bool p_shift, bool p_me
case 48:
if (p_ctrl || macCtrl) {
// Recover zoom.
- // m_webViewer->setZoomFactor(1);
+ m_webViewer->setZoomFactor(1);
}
break;
@@ -910,7 +909,6 @@ void VMdTab::zoom(bool p_zoomIn, qreal p_step)
void VMdTab::zoomWebPage(bool p_zoomIn, qreal p_step)
{
Q_ASSERT(false);
- /*
V_ASSERT(m_webViewer);
qreal curFactor = m_webViewer->zoomFactor();
@@ -922,7 +920,11 @@ void VMdTab::zoomWebPage(bool p_zoomIn, qreal p_step)
}
m_webViewer->setZoomFactor(newFactor);
- */
+}
+
+VWebView *VMdTab::getWebViewer() const
+{
+ return m_webViewer;
}
MarkdownConverterType VMdTab::getMarkdownConverterType() const
@@ -934,8 +936,7 @@ void VMdTab::focusChild()
{
switch (m_mode) {
case Mode::Read:
- Q_ASSERT(false);
- // m_webViewer->setFocus();
+ m_webViewer->setFocus();
break;
case Mode::Edit:
@@ -943,11 +944,10 @@ void VMdTab::focusChild()
break;
case Mode::EditPreview:
- Q_ASSERT(false);
if (m_editor->isVisible()) {
m_editor->setFocus();
} else {
- // m_webViewer->setFocus();
+ m_webViewer->setFocus();
}
break;
@@ -1169,7 +1169,7 @@ void VMdTab::reload()
// Reload web viewer.
m_ready &= ~TabReady::ReadMode;
- // m_webViewer->reload();
+ m_webViewer->reload();
if (!m_isEditMode) {
VUtils::sleepWait(500);
@@ -1298,10 +1298,8 @@ void VMdTab::handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act)
{
// Reload the web view with new base URL.
m_headerFromEditMode = m_currentHeader;
- /*
- m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType),
+ m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType, m_port),
m_file->getBaseUrl());
- */
if (m_editor) {
m_editor->updateInitAndInsertedImages(p_isFile, p_act);
@@ -1449,8 +1447,7 @@ bool VMdTab::executeVimCommandInWebView(const QString &p_cmd)
msg = tr("Quit");
} else if (p_cmd == "nohlsearch" || p_cmd == "noh") {
// :nohlsearch, clear highlight search.
- Q_ASSERT(false);
- // m_webViewer->findText("");
+ m_webViewer->findText("");
} else {
validCommand = false;
}
@@ -1485,9 +1482,11 @@ void VMdTab::handleDownloadRequested(QWebEngineDownloadItem *p_item)
}
});
}
+*/
void VMdTab::handleSavePageRequested()
{
+ /*
Q_ASSERT(false);
static QString lastPath = g_config->getDocumentPathOrHomePath();
@@ -1517,8 +1516,8 @@ void VMdTab::handleSavePageRequested()
emit statusMessage(tr("Saving page to %1").arg(fileName));
// m_webViewer->page()->save(fileName, format);
+ */
}
-*/
VWordCountInfo VMdTab::fetchWordCountInfo(bool p_editMode) const
{
@@ -1544,8 +1543,7 @@ void VMdTab::setCurrentMode(Mode p_mode)
return;
}
- // qreal factor = m_webViewer->zoomFactor();
- qreal factor = 1.0;
+ qreal factor = m_webViewer->zoomFactor();
if (m_mode == Mode::Read) {
m_readWebViewState->m_zoomFactor = factor;
} else if (m_mode == Mode::EditPreview) {
@@ -1557,13 +1555,12 @@ void VMdTab::setCurrentMode(Mode p_mode)
switch (p_mode) {
case Mode::Read:
- Q_ASSERT(false);
if (m_editor) {
m_editor->hide();
}
- // m_webViewer->setInPreview(false);
- // m_webViewer->show();
+ m_webViewer->setInPreview(false);
+ m_webViewer->show();
// Fix the bug introduced by 051088be31dbffa3c04e2d382af15beec40d5fdb
// which replace QStackedLayout with QSplitter.
@@ -1573,7 +1570,7 @@ void VMdTab::setCurrentMode(Mode p_mode)
m_readWebViewState.reset(new WebViewState());
m_readWebViewState->m_zoomFactor = factor;
} else if (factor != m_readWebViewState->m_zoomFactor) {
- // m_webViewer->setZoomFactor(m_readWebViewState->m_zoomFactor);
+ m_webViewer->setZoomFactor(m_readWebViewState->m_zoomFactor);
}
m_document->setPreviewEnabled(false);
@@ -1581,7 +1578,7 @@ void VMdTab::setCurrentMode(Mode p_mode)
case Mode::Edit:
m_document->muteWebView(true);
- // m_webViewer->hide();
+ m_webViewer->hide();
m_editor->show();
QCoreApplication::sendPostedEvents();
@@ -1590,10 +1587,9 @@ void VMdTab::setCurrentMode(Mode p_mode)
case Mode::EditPreview:
Q_ASSERT(m_editor);
- Q_ASSERT(false);
m_document->muteWebView(true);
- // m_webViewer->setInPreview(true);
- // m_webViewer->show();
+ m_webViewer->setInPreview(true);
+ m_webViewer->show();
m_editor->show();
QCoreApplication::sendPostedEvents();
@@ -1617,7 +1613,7 @@ void VMdTab::setCurrentMode(Mode p_mode)
newSizes.append(b);
m_splitter->setSizes(newSizes);
} else if (factor != m_previewWebViewState->m_zoomFactor) {
- // m_webViewer->setZoomFactor(m_previewWebViewState->m_zoomFactor);
+ m_webViewer->setZoomFactor(m_previewWebViewState->m_zoomFactor);
}
m_document->setPreviewEnabled(true);
@@ -1670,10 +1666,9 @@ bool VMdTab::expandRestorePreviewArea()
return false;
}
- Q_ASSERT(false);
if (m_editor->isVisible()) {
m_editor->hide();
- // m_webViewer->setFocus();
+ m_webViewer->setFocus();
} else {
m_editor->show();
m_editor->setFocus();
@@ -1686,3 +1681,19 @@ bool VMdTab::previewExpanded() const
{
return (m_mode == Mode::EditPreview) && !m_editor->isVisible();
}
+
+quint16 VMdTab::getNextPort()
+{
+ auto port = c_basePort;
+ while (s_usedPorts.find(port) != s_usedPorts.end()) {
+ ++port;
+ }
+
+ s_usedPorts.insert(port);
+ return port;
+}
+
+void VMdTab::releasePort(quint16 p_port)
+{
+ s_usedPorts.remove(p_port);
+}
diff --git a/src/vmdtab.h b/src/vmdtab.h
index 51b8724e..ca7b407e 100644
--- a/src/vmdtab.h
+++ b/src/vmdtab.h
@@ -4,12 +4,14 @@
#include
#include
#include
+#include
+
#include "vedittab.h"
#include "vconstants.h"
#include "vmarkdownconverter.h"
#include "vconfigmanager.h"
-// class VWebView;
+class VWebView;
class VDocument;
class VMdEditor;
class VInsertSelector;
@@ -26,6 +28,8 @@ class VMdTab : public VEditTab
public:
VMdTab(VFile *p_file, VEditArea *p_editArea, OpenFileMode p_mode, QWidget *p_parent = 0);
+ ~VMdTab();
+
// Close current tab.
// @p_forced: if true, discard the changes.
bool closeFile(bool p_forced) Q_DECL_OVERRIDE;
@@ -69,7 +73,7 @@ public:
void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
- // VWebView *getWebViewer() const;
+ VWebView *getWebViewer() const;
VMdEditor *getEditor() const;
@@ -156,7 +160,7 @@ private slots:
// void handleDownloadRequested(QWebEngineDownloadItem *p_item);
// Handle save page request.
- // void handleSavePageRequested();
+ void handleSavePageRequested();
// Selection changed in web.
void handleWebSelectionChanged();
@@ -246,8 +250,12 @@ private:
bool previewExpanded() const;
+ static quint16 getNextPort();
+ static void releasePort(quint16 p_port);
+
VMdEditor *m_editor;
- // VWebView *m_webViewer;
+ VWebView *m_webViewer;
+ quint16 m_port;
VDocument *m_document;
MarkdownConverterType m_mdConType;
@@ -277,6 +285,10 @@ private:
VMathJaxInplacePreviewHelper *m_mathjaxPreviewHelper;
int m_documentID;
+
+ static const quint16 c_basePort;
+
+ static QSet s_usedPorts;
};
inline VMdEditor *VMdTab::getEditor()
diff --git a/src/vnote.qrc b/src/vnote.qrc
index 3566af0d..33249347 100644
--- a/src/vnote.qrc
+++ b/src/vnote.qrc
@@ -1,6 +1,5 @@
- resources/qwebchannel.js
utils/marked/marked.min.js
utils/highlightjs/highlight.pack.js
resources/vnote.ini
diff --git a/src/vpreviewpage.cpp b/src/vpreviewpage.cpp
index ddde027d..2c455bb4 100644
--- a/src/vpreviewpage.cpp
+++ b/src/vpreviewpage.cpp
@@ -18,24 +18,19 @@ bool VPreviewPage::acceptNavigationRequest(QWebFrame *p_frame,
const QNetworkRequest &p_request,
QWebPage::NavigationType p_type)
{
- Q_UNUSED(p_frame);
- Q_UNUSED(p_type);
-
- auto url = p_request.url();
- if (url.isLocalFile()) {
- QString filePath = url.toLocalFile();
- if (g_mainWin->tryOpenInternalFile(filePath)) {
- return false;
+ if (p_type == QWebPage::NavigationTypeLinkClicked) {
+ auto url = p_request.url();
+ if (url.isLocalFile()) {
+ QString filePath = url.toLocalFile();
+ if (g_mainWin->tryOpenInternalFile(filePath)) {
+ return false;
+ }
}
- } else if (p_frame) {
- return true;
- } else if (url.scheme() == "data") {
- // Qt 5.12 will trigger this when calling QWebEngineView.setHtml().
- return true;
+
+ QDesktopServices::openUrl(url);
}
- QDesktopServices::openUrl(url);
- return false;
+ return QWebPage::acceptNavigationRequest(p_frame, p_request, p_type);
}
void VPreviewPage::setBackgroundColor(const QColor &p_background)
diff --git a/src/vwebview.cpp b/src/vwebview.cpp
index ac12d30c..0dd8a01f 100644
--- a/src/vwebview.cpp
+++ b/src/vwebview.cpp
@@ -3,20 +3,25 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
#include
#include
#include
+#include
#include
+#include
+
#include "vfile.h"
#include "utils/vclipboardutils.h"
#include "utils/viconutils.h"
#include "vconfigmanager.h"
#include "utils/vwebutils.h"
#include "utils/vutils.h"
+#include "websocketclientwrapper.h"
+#include "websockettransport.h"
extern VConfigManager *g_config;
@@ -31,7 +36,8 @@ VWebView::VWebView(VFile *p_file, QWidget *p_parent)
m_file(p_file),
m_copyImageUrlActionHooked(false),
m_afterCopyImage(false),
- m_inPreview(false)
+ m_inPreview(false),
+ m_channel(nullptr)
{
setAcceptDrops(false);
@@ -55,7 +61,7 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
// and the URL as URLs. If the URL contains Chinese, OneNote or Word could not
// recognize it.
// We need to change it to only-space-encoded text.
- QAction *copyImageUrlAct = pageAction(QWebEnginePage::CopyImageUrlToClipboard);
+ QAction *copyImageUrlAct = pageAction(QWebPage::CopyImageUrlToClipboard);
if (actions.contains(copyImageUrlAct)) {
connect(copyImageUrlAct, &QAction::triggered,
this, &VWebView::handleCopyImageUrlAction);
@@ -94,7 +100,7 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
}
}
- QAction *savePageAct = new QAction(QWebEnginePage::tr("Save &Page"), menu);
+ QAction *savePageAct = new QAction(QWebPage::tr("Save &Page"), menu);
connect(savePageAct, &QAction::triggered,
this, &VWebView::requestSavePage);
menu->addAction(savePageAct);
@@ -103,7 +109,7 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
// Add Copy As menu.
{
- QAction *copyAct = pageAction(QWebEnginePage::Copy);
+ QAction *copyAct = pageAction(QWebPage::Copy);
if (actions.contains(copyAct) && !m_inPreview) {
initCopyAsMenu(copyAct, menu);
}
@@ -113,7 +119,7 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
// - the default one use the fully-encoded URL to fetch the image while
// Windows seems to not recognize it.
// - We need to remove the html to let it be recognized by some web pages.
- QAction *defaultCopyImageAct = pageAction(QWebEnginePage::CopyImageToClipboard);
+ QAction *defaultCopyImageAct = pageAction(QWebPage::CopyImageToClipboard);
if (actions.contains(defaultCopyImageAct)) {
QAction *copyImageAct = new QAction(defaultCopyImageAct->text(), menu);
copyImageAct->setToolTip(defaultCopyImageAct->toolTip());
@@ -156,9 +162,9 @@ void VWebView::copyImage()
{
#if defined(Q_OS_WIN)
Q_ASSERT(m_copyImageUrlActionHooked);
- // triggerPageAction(QWebEnginePage::CopyImageUrlToClipboard) will not really
+ // triggerPageAction(QWebPage::CopyImageUrlToClipboard) will not really
// trigger the corresponding action. It just do the stuff directly.
- QAction *copyImageUrlAct = pageAction(QWebEnginePage::CopyImageUrlToClipboard);
+ QAction *copyImageUrlAct = pageAction(QWebPage::CopyImageUrlToClipboard);
copyImageUrlAct->trigger();
QCoreApplication::processEvents();
@@ -189,7 +195,7 @@ void VWebView::copyImage()
m_afterCopyImage = true;
// Fall back.
- triggerPageAction(QWebEnginePage::CopyImageToClipboard);
+ triggerPageAction(QWebPage::CopyImageToClipboard);
}
void VWebView::handleCopyImageUrlAction()
@@ -230,9 +236,9 @@ void VWebView::hideUnusedActions(QMenu *p_menu)
// QWebEnginePage uses different actions of Back/Forward/Reload.
// [Woboq](https://code.woboq.org/qt5/qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp.html#1652)
// We tell these three actions by name.
- const QStringList actionNames({QWebEnginePage::tr("&Back"),
- QWebEnginePage::tr("&Forward"),
- QWebEnginePage::tr("&Reload")});
+ const QStringList actionNames({QWebPage::tr("&Back"),
+ QWebPage::tr("&Forward"),
+ QWebPage::tr("&Reload")});
const QList actions = p_menu->actions();
for (auto it : actions) {
@@ -242,15 +248,15 @@ void VWebView::hideUnusedActions(QMenu *p_menu)
}
// ViewSource.
- QAction *act = pageAction(QWebEnginePage::ViewSource);
- unusedActions.append(act);
+ // QAction *act = pageAction(QWebPage::ViewSource);
+ // unusedActions.append(act);
// DownloadImageToDisk.
- act = pageAction(QWebEnginePage::DownloadImageToDisk);
+ auto act = pageAction(QWebPage::DownloadImageToDisk);
unusedActions.append(act);
// DownloadLinkToDisk.
- act = pageAction(QWebEnginePage::DownloadLinkToDisk);
+ act = pageAction(QWebPage::DownloadLinkToDisk);
unusedActions.append(act);
for (auto it : unusedActions) {
@@ -394,7 +400,7 @@ void VWebView::handleCopyAsAction(QAction *p_act)
m_copyTarget = p_act->data().toString();
- triggerPageAction(QWebEnginePage::Copy);
+ triggerPageAction(QWebPage::Copy);
}
void VWebView::initCopyAllAsMenu(QMenu *p_menu)
@@ -427,13 +433,13 @@ void VWebView::handleCopyAllAsAction(QAction *p_act)
return;
}
- triggerPageAction(QWebEnginePage::SelectAll);
+ triggerPageAction(QWebPage::SelectAll);
m_copyTarget = p_act->data().toString();
- triggerPageAction(QWebEnginePage::Copy);
+ triggerPageAction(QWebPage::Copy);
- triggerPageAction(QWebEnginePage::Unselect);
+ // triggerPageAction(QWebPage::Unselect);
}
void VWebView::initPreviewTunnelMenu(QAction *p_before, QMenu *p_menu)
@@ -470,7 +476,6 @@ void VWebView::initPreviewTunnelMenu(QAction *p_before, QMenu *p_menu)
if (act->data().toInt() == config) {
act->setChecked(true);
}
-
connect(ag, &QActionGroup::triggered,
this, [](QAction *p_act) {
int data = p_act->data().toInt();
@@ -481,3 +486,23 @@ void VWebView::initPreviewTunnelMenu(QAction *p_before, QMenu *p_menu)
p_menu->insertMenu(p_before, subMenu);
}
+
+void VWebView::bindToChannel(quint16 p_port, const QString &p_name, QObject *p_object)
+{
+ Q_ASSERT(!m_channel);
+ auto server = new QWebSocketServer("Web View for VNote",
+ QWebSocketServer::NonSecureMode,
+ this);
+ quint16 port = p_port;
+ if (!server->listen(QHostAddress::LocalHost, port)) {
+ qWarning() << "fail to open web socket server on port" << port;
+ delete server;
+ return;
+ }
+
+ auto clientWrapper = new WebSocketClientWrapper(server, this);
+ m_channel = new QWebChannel(this);
+ connect(clientWrapper, &WebSocketClientWrapper::clientConnected,
+ m_channel, &QWebChannel::connectTo);
+ m_channel->registerObject(p_name, p_object);
+}
diff --git a/src/vwebview.h b/src/vwebview.h
index 190b90fb..fe60d2ce 100644
--- a/src/vwebview.h
+++ b/src/vwebview.h
@@ -7,6 +7,7 @@
class VFile;
class QMenu;
+class QWebChannel;
class VWebView : public QWebView
{
@@ -17,6 +18,8 @@ public:
void setInPreview(bool p_preview);
+ void bindToChannel(quint16 p_port, const QString &p_name, QObject *p_object);
+
signals:
void editNote();
@@ -73,6 +76,8 @@ private:
// Whether in preview mode.
bool m_inPreview;
+
+ QWebChannel *m_channel;
};
inline void VWebView::setInPreview(bool p_preview)
diff --git a/src/websocketclientwrapper.cpp b/src/websocketclientwrapper.cpp
new file mode 100644
index 00000000..8605f186
--- /dev/null
+++ b/src/websocketclientwrapper.cpp
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "websocketclientwrapper.h"
+#include "websockettransport.h"
+#include
+/*!
+ \brief Wraps connected QWebSockets clients in WebSocketTransport objects.
+ This code is all that is required to connect incoming WebSockets to the WebChannel. Any kind
+ of remote JavaScript client that supports WebSockets can thus receive messages and access the
+ published objects.
+*/
+/*!
+ Construct the client wrapper with the given parent.
+ All clients connecting to the QWebSocketServer will be automatically wrapped
+ in WebSocketTransport objects.
+*/
+WebSocketClientWrapper::WebSocketClientWrapper(QWebSocketServer *server, QObject *parent)
+ : QObject(parent)
+ , m_server(server)
+{
+ connect(server, &QWebSocketServer::newConnection,
+ this, &WebSocketClientWrapper::handleNewConnection);
+}
+/*!
+ Wrap an incoming WebSocket connection in a WebSocketTransport object.
+*/
+void WebSocketClientWrapper::handleNewConnection()
+{
+ emit clientConnected(new WebSocketTransport(m_server->nextPendingConnection()));
+}
diff --git a/src/websocketclientwrapper.h b/src/websocketclientwrapper.h
new file mode 100644
index 00000000..56bec059
--- /dev/null
+++ b/src/websocketclientwrapper.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef WEBSOCKETCLIENTWRAPPER_H
+#define WEBSOCKETCLIENTWRAPPER_H
+#include
+class WebSocketTransport;
+QT_BEGIN_NAMESPACE
+class QWebSocketServer;
+QT_END_NAMESPACE
+class WebSocketClientWrapper : public QObject
+{
+ Q_OBJECT
+public:
+ WebSocketClientWrapper(QWebSocketServer *server, QObject *parent = nullptr);
+signals:
+ void clientConnected(WebSocketTransport *client);
+private slots:
+ void handleNewConnection();
+private:
+ QWebSocketServer *m_server;
+};
+#endif // WEBSOCKETCLIENTWRAPPER_H
diff --git a/src/websockettransport.cpp b/src/websockettransport.cpp
new file mode 100644
index 00000000..ac2b07d1
--- /dev/null
+++ b/src/websockettransport.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "websockettransport.h"
+#include
+#include
+#include
+#include
+/*!
+ \brief QWebChannelAbstractSocket implementation that uses a QWebSocket internally.
+ The transport delegates all messages received over the QWebSocket over its
+ textMessageReceived signal. Analogously, all calls to sendTextMessage will
+ be send over the QWebSocket to the remote client.
+*/
+/*!
+ Construct the transport object and wrap the given socket.
+ The socket is also set as the parent of the transport object.
+*/
+WebSocketTransport::WebSocketTransport(QWebSocket *socket)
+: QWebChannelAbstractTransport(socket)
+, m_socket(socket)
+{
+ connect(socket, &QWebSocket::textMessageReceived,
+ this, &WebSocketTransport::textMessageReceived);
+ connect(socket, &QWebSocket::disconnected,
+ this, &WebSocketTransport::deleteLater);
+}
+/*!
+ Destroys the WebSocketTransport.
+*/
+WebSocketTransport::~WebSocketTransport()
+{
+ m_socket->deleteLater();
+}
+/*!
+ Serialize the JSON message and send it as a text message via the WebSocket to the client.
+*/
+void WebSocketTransport::sendMessage(const QJsonObject &message)
+{
+ QJsonDocument doc(message);
+ m_socket->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
+}
+/*!
+ Deserialize the stringified JSON messageData and emit messageReceived.
+*/
+void WebSocketTransport::textMessageReceived(const QString &messageData)
+{
+ QJsonParseError error;
+ QJsonDocument message = QJsonDocument::fromJson(messageData.toUtf8(), &error);
+ if (error.error) {
+ qWarning() << "Failed to parse text message as JSON object:" << messageData
+ << "Error is:" << error.errorString();
+ return;
+ } else if (!message.isObject()) {
+ qWarning() << "Received JSON message that is not an object: " << messageData;
+ return;
+ }
+ emit messageReceived(message.object(), this);
+}
diff --git a/src/websockettransport.h b/src/websockettransport.h
new file mode 100644
index 00000000..1a509873
--- /dev/null
+++ b/src/websockettransport.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef WEBSOCKETTRANSPORT_H
+#define WEBSOCKETTRANSPORT_H
+#include
+QT_BEGIN_NAMESPACE
+class QWebSocket;
+QT_END_NAMESPACE
+class WebSocketTransport : public QWebChannelAbstractTransport
+{
+ Q_OBJECT
+public:
+ explicit WebSocketTransport(QWebSocket *socket);
+ virtual ~WebSocketTransport();
+ void sendMessage(const QJsonObject &message) override;
+private slots:
+ void textMessageReceived(const QString &message);
+private:
+ QWebSocket *m_socket;
+};
+#endif // WEBSOCKETTRANSPORT_H