mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support read mode via QWebView
This commit is contained in:
parent
c3a692d33d
commit
859fb51c07
@ -125,10 +125,10 @@ mdit.use(window.markdownitContainer, 'alert', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
render: function (tokens, idx) {
|
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) {
|
if (tokens[idx].nesting === 1) {
|
||||||
// opening tag
|
// opening tag
|
||||||
let alertClass = type[1];
|
var alertClass = type[1];
|
||||||
return '<div class="alert ' + alertClass + '" role="alert">';
|
return '<div class="alert ' + alertClass + '" role="alert">';
|
||||||
} else {
|
} else {
|
||||||
// closing tag
|
// closing tag
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="COMMON_CSS_PLACE_HOLDER">
|
<link rel="stylesheet" type="text/css" href="COMMON_CSS_PLACE_HOLDER">
|
||||||
<link rel="stylesheet" type="text/css" href="CSS_PLACE_HOLDER">
|
<link rel="stylesheet" type="text/css" href="CSS_PLACE_HOLDER">
|
||||||
<link rel="stylesheet" type="text/css" href="HIGHLIGHTJS_CSS_PLACE_HOLDER">
|
<link rel="stylesheet" type="text/css" href="HIGHLIGHTJS_CSS_PLACE_HOLDER">
|
||||||
<script src="qrc:/resources/qwebchannel.js"></script>
|
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
<script src="qrc:/utils/highlightjs/highlight.pack.js"></script>
|
<script src="qrc:/utils/highlightjs/highlight.pack.js"></script>
|
||||||
<script src="qrc:/utils/clipboard.js/clipboard.min.js"></script>
|
<script src="qrc:/utils/clipboard.js/clipboard.min.js"></script>
|
||||||
<!-- EXTRA_PLACE_HOLDER -->
|
<!-- EXTRA_PLACE_HOLDER -->
|
||||||
|
@ -114,6 +114,10 @@ if (typeof handleMathjaxReady == 'undefined') {
|
|||||||
var handleMathjaxReady = function() {};
|
var handleMathjaxReady = function() {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof VWebChannelPort == 'undefined') {
|
||||||
|
var VWebChannelPort = '12345';
|
||||||
|
}
|
||||||
|
|
||||||
// Whether highlight special blocks like puml, flowchart.
|
// Whether highlight special blocks like puml, flowchart.
|
||||||
var highlightSpecialBlocks = false;
|
var highlightSpecialBlocks = false;
|
||||||
|
|
||||||
@ -176,7 +180,12 @@ var mute = function(muted) {
|
|||||||
g_muteScroll = muted;
|
g_muteScroll = muted;
|
||||||
};
|
};
|
||||||
|
|
||||||
new QWebChannel(qt.webChannelTransport,
|
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) {
|
function(channel) {
|
||||||
content = channel.objects.content;
|
content = channel.objects.content;
|
||||||
|
|
||||||
@ -224,6 +233,8 @@ new QWebChannel(qt.webChannelTransport,
|
|||||||
|
|
||||||
channelInitialized = true;
|
channelInitialized = true;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var VHighlightedAnchorClass = 'highlighted-anchor';
|
var VHighlightedAnchorClass = 'highlighted-anchor';
|
||||||
|
|
||||||
@ -818,7 +829,7 @@ var renderPlantUMLOneOnline = function(code) {
|
|||||||
++asyncJobsCount;
|
++asyncJobsCount;
|
||||||
code.classList.add(plantUMLCodeClass + plantUMLIdx);
|
code.classList.add(plantUMLCodeClass + plantUMLIdx);
|
||||||
|
|
||||||
let data = { index: plantUMLIdx,
|
var data = { index: plantUMLIdx,
|
||||||
setupView: !VPreviewMode
|
setupView: !VPreviewMode
|
||||||
};
|
};
|
||||||
renderPlantUMLOnline(VPlantUMLServer,
|
renderPlantUMLOnline(VPlantUMLServer,
|
||||||
@ -1510,7 +1521,7 @@ var handleGraphvizResult = function(id, timeStamp, format, result) {
|
|||||||
if (format == 'svg') {
|
if (format == 'svg') {
|
||||||
obj = document.createElement('p');
|
obj = document.createElement('p');
|
||||||
obj.innerHTML = result;
|
obj.innerHTML = result;
|
||||||
setupSVGToView(obj.children[0]);
|
setupSVGToView(obj.children[0], false);
|
||||||
} else {
|
} else {
|
||||||
obj = document.createElement('img');
|
obj = document.createElement('img');
|
||||||
obj.src = "data:image/" + format + ";base64, " + result;
|
obj.src = "data:image/" + format + ";base64, " + result;
|
||||||
|
@ -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 <milian.wolff@kdab.com>
|
|
||||||
** 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
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
var imageViewDiv = document.getElementById('image-view-div');
|
var imageViewDiv = document.getElementById('image-view-div');
|
||||||
|
|
||||||
var viewImage = function(imgSrc, background = 'transparent') {
|
var viewImage = function(imgSrc, background) {
|
||||||
viewBoxImageMouseDown = false;
|
viewBoxImageMouseDown = false;
|
||||||
|
|
||||||
imageViewDiv.style.display = 'block';
|
imageViewDiv.style.display = 'block';
|
||||||
@ -16,10 +16,10 @@ var viewImage = function(imgSrc, background = 'transparent') {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var viewIMG = function(imgNode) {
|
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 svg = svgNode.outerHTML.replace(/#/g, '%23').replace(/[\r\n]/g, '');
|
||||||
var src = 'data:image/svg+xml;utf8,' + svg;
|
var src = 'data:image/svg+xml;utf8,' + svg;
|
||||||
|
|
||||||
@ -173,14 +173,14 @@ var onSVGDoubleClick = function(forceBackground, e) {
|
|||||||
viewSVG(this, style.backgroundColor);
|
viewSVG(this, style.backgroundColor);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
viewSVG(this);
|
viewSVG(this, 'transparent');
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var setupSVGToView = function(node, forceBackground = false) {
|
var setupSVGToView = function(node, forceBackground) {
|
||||||
if (!node || node.nodeName.toLowerCase() != 'svg') {
|
if (!node || node.nodeName.toLowerCase() != 'svg') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
12
src/src.pro
12
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
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
@ -156,7 +156,10 @@ SOURCES += main.cpp\
|
|||||||
vtablehelper.cpp \
|
vtablehelper.cpp \
|
||||||
vtable.cpp \
|
vtable.cpp \
|
||||||
dialog/vinserttabledialog.cpp \
|
dialog/vinserttabledialog.cpp \
|
||||||
vpreviewpage.cpp
|
vpreviewpage.cpp \
|
||||||
|
vwebview.cpp \
|
||||||
|
websocketclientwrapper.cpp \
|
||||||
|
websockettransport.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
@ -297,7 +300,10 @@ HEADERS += vmainwindow.h \
|
|||||||
vtablehelper.h \
|
vtablehelper.h \
|
||||||
vtable.h \
|
vtable.h \
|
||||||
dialog/vinserttabledialog.h \
|
dialog/vinserttabledialog.h \
|
||||||
vpreviewpage.h
|
vpreviewpage.h \
|
||||||
|
vwebview.h \
|
||||||
|
websocketclientwrapper.h \
|
||||||
|
websockettransport.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc \
|
vnote.qrc \
|
||||||
|
@ -655,9 +655,9 @@ QString VUtils::generateSimpleHtmlTemplate(const QString &p_body)
|
|||||||
return html.replace(HtmlHolder::c_bodyHolder, 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,
|
QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType,
|
||||||
@ -675,11 +675,12 @@ QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType,
|
|||||||
g_config->getCodeBlockCssStyleUrl(p_renderCodeBlockStyle),
|
g_config->getCodeBlockCssStyleUrl(p_renderCodeBlockStyle),
|
||||||
p_isPDF);
|
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,
|
QString VUtils::generateHtmlTemplate(const QString &p_template,
|
||||||
MarkdownConverterType p_conType,
|
MarkdownConverterType p_conType,
|
||||||
|
quint16 p_port,
|
||||||
bool p_isPDF,
|
bool p_isPDF,
|
||||||
bool p_wkhtmltopdf,
|
bool p_wkhtmltopdf,
|
||||||
bool p_addToc)
|
bool p_addToc)
|
||||||
@ -882,6 +883,8 @@ QString VUtils::generateHtmlTemplate(const QString &p_template,
|
|||||||
extraFile += "<script>var VOS = 'linux';</script>\n";
|
extraFile += "<script>var VOS = 'linux';</script>\n";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extraFile += "<script>var VWebChannelPort = '" + QString::number(p_port) + "';</script>\n";
|
||||||
|
|
||||||
QString htmlTemplate(p_template);
|
QString htmlTemplate(p_template);
|
||||||
htmlTemplate.replace(HtmlHolder::c_JSHolder, jsFile);
|
htmlTemplate.replace(HtmlHolder::c_JSHolder, jsFile);
|
||||||
if (!extraFile.isEmpty()) {
|
if (!extraFile.isEmpty()) {
|
||||||
|
@ -186,7 +186,7 @@ public:
|
|||||||
static DocType docTypeFromName(const QString &p_name);
|
static DocType docTypeFromName(const QString &p_name);
|
||||||
|
|
||||||
// Generate HTML template.
|
// 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_renderBg is the background name.
|
||||||
// @p_wkhtmltopdf: whether this template is used for wkhtmltopdf.
|
// @p_wkhtmltopdf: whether this template is used for wkhtmltopdf.
|
||||||
@ -464,6 +464,7 @@ private:
|
|||||||
|
|
||||||
static QString generateHtmlTemplate(const QString &p_template,
|
static QString generateHtmlTemplate(const QString &p_template,
|
||||||
MarkdownConverterType p_conType,
|
MarkdownConverterType p_conType,
|
||||||
|
quint16 p_port,
|
||||||
bool p_isPDF = false,
|
bool p_isPDF = false,
|
||||||
bool p_wkhtmltopdf = false,
|
bool p_wkhtmltopdf = false,
|
||||||
bool p_addToc = false);
|
bool p_addToc = false);
|
||||||
|
@ -292,7 +292,7 @@ int VEditArea::openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_m
|
|||||||
{
|
{
|
||||||
Q_ASSERT(windowIndex < splitter->count());
|
Q_ASSERT(windowIndex < splitter->count());
|
||||||
VEditWindow *win = getWindow(windowIndex);
|
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)
|
void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus)
|
||||||
|
@ -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)
|
int VEditWindow::openFile(VFile *p_file, OpenFileMode p_mode)
|
||||||
{
|
{
|
||||||
qDebug() << "open" << p_file->getName();
|
|
||||||
// Find if it has been opened already
|
// Find if it has been opened already
|
||||||
int idx = findTabByFile(p_file);
|
int idx = findTabByFile(p_file);
|
||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
|
@ -89,7 +89,7 @@ QUrl VFile::getBaseUrl() const
|
|||||||
// Need to judge the path: Url, local file, resource file.
|
// Need to judge the path: Url, local file, resource file.
|
||||||
QUrl baseUrl;
|
QUrl baseUrl;
|
||||||
// Use file path to make in page anchor work.
|
// Use file path to make in page anchor work.
|
||||||
QString filePath = fetchPath();
|
QString filePath = fetchBasePath();
|
||||||
QFileInfo pathInfo(filePath);
|
QFileInfo pathInfo(filePath);
|
||||||
if (pathInfo.exists()) {
|
if (pathInfo.exists()) {
|
||||||
if (pathInfo.isNativePath()) {
|
if (pathInfo.isNativePath()) {
|
||||||
|
@ -561,18 +561,31 @@ void VFileList::contextMenuRequested(QPoint pos)
|
|||||||
VNoteFile *file = getVFile(item);
|
VNoteFile *file = getVFile(item);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (file->getDocType() == DocType::Markdown) {
|
if (file->getDocType() == DocType::Markdown) {
|
||||||
QAction *openAct = new QAction(VIconUtils::menuIcon(":/resources/icons/editing.svg"),
|
QAction *openInReadAct = new QAction(VIconUtils::menuIcon(":/resources/icons/reading.svg"),
|
||||||
tr("Open"),
|
tr("&Open In Read Mode"),
|
||||||
&menu);
|
&menu);
|
||||||
openAct->setToolTip(tr("Open and edit current note"));
|
openInReadAct->setToolTip(tr("Open current note in read mode"));
|
||||||
connect(openAct, &QAction::triggered,
|
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]() {
|
this, [this]() {
|
||||||
QListWidgetItem *item = fileList->currentItem();
|
QListWidgetItem *item = fileList->currentItem();
|
||||||
if (item) {
|
if (item) {
|
||||||
emit fileClicked(getVFile(item), OpenFileMode::Edit, true);
|
emit fileClicked(getVFile(item), OpenFileMode::Edit, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
menu.addAction(openAct);
|
menu.addAction(openInEditAct);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.addMenu(getOpenWithMenu());
|
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.
|
// Qt seems not to update the QListWidget correctly. Manually force it to repaint.
|
||||||
fileList->update();
|
fileList->update();
|
||||||
// emit fileClicked(getVFile(p_item), g_config->getNoteOpenMode());
|
emit fileClicked(getVFile(p_item), g_config->getNoteOpenMode());
|
||||||
emit fileClicked(getVFile(p_item), OpenFileMode::Edit);
|
|
||||||
|
|
||||||
if (p_restoreFocus) {
|
if (p_restoreFocus) {
|
||||||
fileList->setFocus();
|
fileList->setFocus();
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <QPrinter>
|
#include <QPrinter>
|
||||||
#include <QPrintDialog>
|
#include <QPrintDialog>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QWebPage>
|
||||||
|
|
||||||
#include "vmainwindow.h"
|
#include "vmainwindow.h"
|
||||||
#include "vdirectorytree.h"
|
#include "vdirectorytree.h"
|
||||||
@ -17,11 +18,11 @@
|
|||||||
#include "dialog/vsettingsdialog.h"
|
#include "dialog/vsettingsdialog.h"
|
||||||
#include "vcaptain.h"
|
#include "vcaptain.h"
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
|
#include "vwebview.h"
|
||||||
#include "vmdtab.h"
|
#include "vmdtab.h"
|
||||||
#include "vvimindicator.h"
|
#include "vvimindicator.h"
|
||||||
#include "vvimcmdlineedit.h"
|
#include "vvimcmdlineedit.h"
|
||||||
#include "vtabindicator.h"
|
#include "vtabindicator.h"
|
||||||
// #include "dialog/vupdater.h"
|
|
||||||
#include "vorphanfile.h"
|
#include "vorphanfile.h"
|
||||||
#include "dialog/vorphanfileinfodialog.h"
|
#include "dialog/vorphanfileinfodialog.h"
|
||||||
#include "vsingleinstanceguard.h"
|
#include "vsingleinstanceguard.h"
|
||||||
@ -751,16 +752,22 @@ QToolBar *VMainWindow::initFileToolBar(QSize p_iconSize)
|
|||||||
connect(deleteNoteAct, &QAction::triggered,
|
connect(deleteNoteAct, &QAction::triggered,
|
||||||
this, &VMainWindow::deleteCurNote);
|
this, &VMainWindow::deleteCurNote);
|
||||||
|
|
||||||
m_discardAct = new QAction(VIconUtils::menuIcon(":/resources/icons/discard_exit.svg"),
|
m_editReadAct = new QAction(this);
|
||||||
tr("Discard Changes"),
|
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);
|
this);
|
||||||
VUtils::fixTextWithCaptainShortcut(m_discardAct, "DiscardAndRead");
|
VUtils::fixTextWithCaptainShortcut(m_discardExitAct, "DiscardAndRead");
|
||||||
m_discardAct->setStatusTip(tr("Discard changes"));
|
m_discardExitAct->setStatusTip(tr("Discard changes and exit edit mode"));
|
||||||
connect(m_discardAct, &QAction::triggered,
|
connect(m_discardExitAct, &QAction::triggered,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
m_editArea->readFile(true);
|
m_editArea->readFile(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateEditReadAct(nullptr);
|
||||||
|
|
||||||
saveNoteAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/save_note.svg"),
|
saveNoteAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/save_note.svg"),
|
||||||
tr("Save"), this);
|
tr("Save"), this);
|
||||||
saveNoteAct->setStatusTip(tr("Save changes to current note"));
|
saveNoteAct->setStatusTip(tr("Save changes to current note"));
|
||||||
@ -778,7 +785,8 @@ QToolBar *VMainWindow::initFileToolBar(QSize p_iconSize)
|
|||||||
newNoteAct->setEnabled(false);
|
newNoteAct->setEnabled(false);
|
||||||
noteInfoAct->setEnabled(false);
|
noteInfoAct->setEnabled(false);
|
||||||
deleteNoteAct->setEnabled(false);
|
deleteNoteAct->setEnabled(false);
|
||||||
m_discardAct->setEnabled(false);
|
m_editReadAct->setEnabled(false);
|
||||||
|
m_discardExitAct->setEnabled(false);
|
||||||
saveNoteAct->setEnabled(false);
|
saveNoteAct->setEnabled(false);
|
||||||
|
|
||||||
m_fileToolBar->addWidget(m_avatarBtn);
|
m_fileToolBar->addWidget(m_avatarBtn);
|
||||||
@ -786,7 +794,8 @@ QToolBar *VMainWindow::initFileToolBar(QSize p_iconSize)
|
|||||||
m_fileToolBar->addAction(newNoteAct);
|
m_fileToolBar->addAction(newNoteAct);
|
||||||
m_fileToolBar->addAction(deleteNoteAct);
|
m_fileToolBar->addAction(deleteNoteAct);
|
||||||
m_fileToolBar->addAction(noteInfoAct);
|
m_fileToolBar->addAction(noteInfoAct);
|
||||||
m_fileToolBar->addAction(m_discardAct);
|
m_fileToolBar->addAction(m_editReadAct);
|
||||||
|
m_fileToolBar->addAction(m_discardExitAct);
|
||||||
m_fileToolBar->addAction(saveNoteAct);
|
m_fileToolBar->addAction(saveNoteAct);
|
||||||
|
|
||||||
return m_fileToolBar;
|
return m_fileToolBar;
|
||||||
@ -1042,7 +1051,6 @@ void VMainWindow::initFileMenu()
|
|||||||
|
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
|
|
||||||
// Export as PDF.
|
|
||||||
/*
|
/*
|
||||||
m_exportAct = new QAction(tr("E&xport"), this);
|
m_exportAct = new QAction(tr("E&xport"), this);
|
||||||
m_exportAct->setToolTip(tr("Export notes"));
|
m_exportAct->setToolTip(tr("Export notes"));
|
||||||
@ -2076,8 +2084,7 @@ void VMainWindow::updateActionsStateFromTab(const VEditTab *p_tab)
|
|||||||
&& file->getType() == FileType::Orphan
|
&& file->getType() == FileType::Orphan
|
||||||
&& dynamic_cast<const VOrphanFile *>(file)->isSystemFile();
|
&& dynamic_cast<const VOrphanFile *>(file)->isSystemFile();
|
||||||
|
|
||||||
|
updateEditReadAct(p_tab);
|
||||||
m_discardAct->setEnabled(file && editMode && p_tab->isModified());
|
|
||||||
|
|
||||||
saveNoteAct->setEnabled(file && editMode && file->isModifiable());
|
saveNoteAct->setEnabled(file && editMode && file->isModifiable());
|
||||||
deleteNoteAct->setEnabled(file && file->getType() == FileType::Note);
|
deleteNoteAct->setEnabled(file && file->getType() == FileType::Note);
|
||||||
@ -2885,7 +2892,7 @@ bool VMainWindow::discardAndReadByCaptain(void *p_target, void *p_data)
|
|||||||
Q_UNUSED(p_data);
|
Q_UNUSED(p_data);
|
||||||
VMainWindow *obj = static_cast<VMainWindow *>(p_target);
|
VMainWindow *obj = static_cast<VMainWindow *>(p_target);
|
||||||
if (obj->m_curTab) {
|
if (obj->m_curTab) {
|
||||||
obj->m_discardAct->trigger();
|
obj->m_discardExitAct->trigger();
|
||||||
obj->m_curTab->setFocus();
|
obj->m_curTab->setFocus();
|
||||||
|
|
||||||
return false;
|
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()
|
void VMainWindow::handleExportAct()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -319,6 +319,8 @@ private:
|
|||||||
|
|
||||||
void initThemeMenu(QMenu *p_emnu);
|
void initThemeMenu(QMenu *p_emnu);
|
||||||
|
|
||||||
|
void updateEditReadAct(const VEditTab *p_tab);
|
||||||
|
|
||||||
void initUniversalEntry();
|
void initUniversalEntry();
|
||||||
|
|
||||||
void setMenuBarVisible(bool p_visible);
|
void setMenuBarVisible(bool p_visible);
|
||||||
@ -419,14 +421,21 @@ private:
|
|||||||
|
|
||||||
QAction *deleteNoteAct;
|
QAction *deleteNoteAct;
|
||||||
|
|
||||||
|
// Toggle read and edit note.
|
||||||
|
QAction *m_editReadAct;
|
||||||
|
|
||||||
QAction *saveNoteAct;
|
QAction *saveNoteAct;
|
||||||
|
|
||||||
QAction *m_discardAct;
|
QAction *m_discardExitAct;
|
||||||
|
|
||||||
QAction *expandViewAct;
|
QAction *expandViewAct;
|
||||||
|
|
||||||
QAction *m_importNoteAct;
|
QAction *m_importNoteAct;
|
||||||
|
|
||||||
|
QAction *m_printAct;
|
||||||
|
|
||||||
|
QAction *m_exportAct;
|
||||||
|
|
||||||
QAction *m_findReplaceAct;
|
QAction *m_findReplaceAct;
|
||||||
|
|
||||||
QAction *m_findNextAct;
|
QAction *m_findNextAct;
|
||||||
|
131
src/vmdtab.cpp
131
src/vmdtab.cpp
@ -1,13 +1,13 @@
|
|||||||
#include <QtWidgets>
|
#include <QtWidgets>
|
||||||
// #include <QWebChannel>
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
// #include <QWebEngineProfile>
|
// #include <QWebEngineProfile>
|
||||||
|
|
||||||
#include "vmdtab.h"
|
#include "vmdtab.h"
|
||||||
#include "vdocument.h"
|
#include "vdocument.h"
|
||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
// #include "vpreviewpage.h"
|
#include "vpreviewpage.h"
|
||||||
#include "pegmarkdownhighlighter.h"
|
#include "pegmarkdownhighlighter.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
@ -29,12 +29,15 @@ extern VMainWindow *g_mainWin;
|
|||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
const quint16 VMdTab::c_basePort = 10999;
|
||||||
|
QSet<quint16> VMdTab::s_usedPorts;
|
||||||
|
|
||||||
VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
||||||
OpenFileMode p_mode, QWidget *p_parent)
|
OpenFileMode p_mode, QWidget *p_parent)
|
||||||
: VEditTab(p_file, p_editArea, p_parent),
|
: VEditTab(p_file, p_editArea, p_parent),
|
||||||
m_editor(NULL),
|
m_editor(NULL),
|
||||||
// m_webViewer(NULL),
|
m_webViewer(NULL),
|
||||||
|
m_port(getNextPort()),
|
||||||
m_document(NULL),
|
m_document(NULL),
|
||||||
m_mdConType(g_config->getMdConverterType()),
|
m_mdConType(g_config->getMdConverterType()),
|
||||||
m_enableHeadingSequence(false),
|
m_enableHeadingSequence(false),
|
||||||
@ -79,8 +82,6 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
|||||||
m_livePreviewTimer->setInterval(500);
|
m_livePreviewTimer->setInterval(500);
|
||||||
connect(m_livePreviewTimer, &QTimer::timeout,
|
connect(m_livePreviewTimer, &QTimer::timeout,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
Q_ASSERT(false);
|
|
||||||
/*
|
|
||||||
QString text = m_webViewer->selectedText().trimmed();
|
QString text = m_webViewer->selectedText().trimmed();
|
||||||
if (text.isEmpty()) {
|
if (text.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@ -94,7 +95,6 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
|||||||
info.m_startPos,
|
info.m_startPos,
|
||||||
info.m_endPos);
|
info.m_endPos);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
|
|
||||||
QTimer::singleShot(50, this, [this, p_mode]() {
|
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()
|
void VMdTab::setupUI()
|
||||||
{
|
{
|
||||||
m_splitter = new QSplitter(this);
|
m_splitter = new QSplitter(this);
|
||||||
@ -124,7 +129,6 @@ void VMdTab::setupUI()
|
|||||||
|
|
||||||
void VMdTab::showFileReadMode()
|
void VMdTab::showFileReadMode()
|
||||||
{
|
{
|
||||||
Q_ASSERT(false);
|
|
||||||
m_isEditMode = false;
|
m_isEditMode = false;
|
||||||
|
|
||||||
// Will recover the header when web side is ready.
|
// Will recover the header when web side is ready.
|
||||||
@ -292,11 +296,13 @@ bool VMdTab::closeFile(bool p_forced)
|
|||||||
Q_ASSERT(m_editor);
|
Q_ASSERT(m_editor);
|
||||||
m_editor->reloadFile();
|
m_editor->reloadFile();
|
||||||
m_editor->endEdit();
|
m_editor->endEdit();
|
||||||
|
|
||||||
|
showFileReadMode();
|
||||||
|
} else {
|
||||||
|
readFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
readFile();
|
return !m_isEditMode;
|
||||||
|
|
||||||
return !isModified();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::editFile()
|
void VMdTab::editFile()
|
||||||
@ -355,7 +361,7 @@ void VMdTab::readFile(bool p_discard)
|
|||||||
m_editor->endEdit();
|
m_editor->endEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
showFileEditMode();
|
showFileReadMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdTab::saveFile()
|
bool VMdTab::saveFile()
|
||||||
@ -436,8 +442,8 @@ void VMdTab::discardAndRead()
|
|||||||
|
|
||||||
void VMdTab::setupMarkdownViewer()
|
void VMdTab::setupMarkdownViewer()
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
m_webViewer = new VWebView(m_file, this);
|
m_webViewer = new VWebView(m_file, this);
|
||||||
|
|
||||||
connect(m_webViewer, &VWebView::editNote,
|
connect(m_webViewer, &VWebView::editNote,
|
||||||
this, &VMdTab::editFile);
|
this, &VMdTab::editFile);
|
||||||
connect(m_webViewer, &VWebView::requestSavePage,
|
connect(m_webViewer, &VWebView::requestSavePage,
|
||||||
@ -450,22 +456,24 @@ void VMdTab::setupMarkdownViewer()
|
|||||||
VPreviewPage *page = new VPreviewPage(m_webViewer);
|
VPreviewPage *page = new VPreviewPage(m_webViewer);
|
||||||
m_webViewer->setPage(page);
|
m_webViewer->setPage(page);
|
||||||
m_webViewer->setZoomFactor(g_config->getWebZoomFactor());
|
m_webViewer->setZoomFactor(g_config->getWebZoomFactor());
|
||||||
|
/*
|
||||||
connect(page->profile(), &QWebEngineProfile::downloadRequested,
|
connect(page->profile(), &QWebEngineProfile::downloadRequested,
|
||||||
this, &VMdTab::handleDownloadRequested);
|
this, &VMdTab::handleDownloadRequested);
|
||||||
connect(page, &QWebEnginePage::linkHovered,
|
connect(page, &QWebEnginePage::linkHovered,
|
||||||
this, &VMdTab::statusMessage);
|
this, &VMdTab::statusMessage);
|
||||||
|
*/
|
||||||
|
|
||||||
// Avoid white flash before loading content.
|
// Avoid white flash before loading content.
|
||||||
// Setting Qt::transparent will force GrayScale antialias rendering.
|
// Setting Qt::transparent will force GrayScale antialias rendering.
|
||||||
page->setBackgroundColor(g_config->getBaseBackground());
|
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();
|
m_documentID = m_document->registerIdentifier();
|
||||||
|
|
||||||
/*
|
m_webViewer->bindToChannel(m_port, QStringLiteral("content"), m_document);
|
||||||
QWebChannel *channel = new QWebChannel(m_webViewer);
|
|
||||||
channel->registerObject(QStringLiteral("content"), m_document);
|
|
||||||
connect(m_document, &VDocument::tocChanged,
|
connect(m_document, &VDocument::tocChanged,
|
||||||
this, &VMdTab::updateOutlineFromHtml);
|
this, &VMdTab::updateOutlineFromHtml);
|
||||||
connect(m_document, SIGNAL(headerChanged(const QString &)),
|
connect(m_document, SIGNAL(headerChanged(const QString &)),
|
||||||
@ -514,13 +522,10 @@ void VMdTab::setupMarkdownViewer()
|
|||||||
emit statusUpdated(info);
|
emit statusUpdated(info);
|
||||||
});
|
});
|
||||||
|
|
||||||
page->setWebChannel(channel);
|
m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType, m_port),
|
||||||
|
|
||||||
m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType),
|
|
||||||
m_file->getBaseUrl());
|
m_file->getBaseUrl());
|
||||||
|
|
||||||
m_splitter->addWidget(m_webViewer);
|
m_splitter->addWidget(m_webViewer);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::setupMarkdownEditor()
|
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,
|
void VMdTab::findTextInWebView(const QString &p_text, uint p_options,
|
||||||
bool /* p_peek */, bool p_forward)
|
bool /* p_peek */, bool p_forward)
|
||||||
{
|
{
|
||||||
V_ASSERT(false);
|
V_ASSERT(m_webViewer);
|
||||||
|
|
||||||
/*
|
QWebPage::FindFlags flags;
|
||||||
QWebEnginePage::FindFlags flags;
|
|
||||||
if (p_options & FindOption::CaseSensitive) {
|
if (p_options & FindOption::CaseSensitive) {
|
||||||
flags |= QWebEnginePage::FindCaseSensitively;
|
flags |= QWebPage::FindCaseSensitively;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p_forward) {
|
if (!p_forward) {
|
||||||
flags |= QWebEnginePage::FindBackward;
|
flags |= QWebPage::FindBackward;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_webViewer->findText(p_text, flags);
|
m_webViewer->findText(p_text, flags);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VMdTab::getSelectedText() const
|
QString VMdTab::getSelectedText() const
|
||||||
@ -795,19 +798,15 @@ QString VMdTab::getSelectedText() const
|
|||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
return cursor.selectedText();
|
return cursor.selectedText();
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT(false);
|
return m_webViewer->selectedText();
|
||||||
// return m_webViewer->selectedText();
|
|
||||||
return QString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::clearSearchedWordHighlight()
|
void VMdTab::clearSearchedWordHighlight()
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
if (m_webViewer) {
|
if (m_webViewer) {
|
||||||
m_webViewer->findText("");
|
m_webViewer->findText("");
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
if (m_editor) {
|
if (m_editor) {
|
||||||
m_editor->clearSearchedWordHighlight();
|
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)
|
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)
|
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
|
||||||
bool macCtrl = p_meta;
|
bool macCtrl = p_meta;
|
||||||
#else
|
#else
|
||||||
@ -852,7 +851,7 @@ void VMdTab::handleWebKeyPressed(int p_key, bool p_ctrl, bool p_shift, bool p_me
|
|||||||
case 48:
|
case 48:
|
||||||
if (p_ctrl || macCtrl) {
|
if (p_ctrl || macCtrl) {
|
||||||
// Recover zoom.
|
// Recover zoom.
|
||||||
// m_webViewer->setZoomFactor(1);
|
m_webViewer->setZoomFactor(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -910,7 +909,6 @@ void VMdTab::zoom(bool p_zoomIn, qreal p_step)
|
|||||||
void VMdTab::zoomWebPage(bool p_zoomIn, qreal p_step)
|
void VMdTab::zoomWebPage(bool p_zoomIn, qreal p_step)
|
||||||
{
|
{
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
/*
|
|
||||||
V_ASSERT(m_webViewer);
|
V_ASSERT(m_webViewer);
|
||||||
|
|
||||||
qreal curFactor = m_webViewer->zoomFactor();
|
qreal curFactor = m_webViewer->zoomFactor();
|
||||||
@ -922,7 +920,11 @@ void VMdTab::zoomWebPage(bool p_zoomIn, qreal p_step)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_webViewer->setZoomFactor(newFactor);
|
m_webViewer->setZoomFactor(newFactor);
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
VWebView *VMdTab::getWebViewer() const
|
||||||
|
{
|
||||||
|
return m_webViewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
MarkdownConverterType VMdTab::getMarkdownConverterType() const
|
MarkdownConverterType VMdTab::getMarkdownConverterType() const
|
||||||
@ -934,8 +936,7 @@ void VMdTab::focusChild()
|
|||||||
{
|
{
|
||||||
switch (m_mode) {
|
switch (m_mode) {
|
||||||
case Mode::Read:
|
case Mode::Read:
|
||||||
Q_ASSERT(false);
|
m_webViewer->setFocus();
|
||||||
// m_webViewer->setFocus();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Mode::Edit:
|
case Mode::Edit:
|
||||||
@ -943,11 +944,10 @@ void VMdTab::focusChild()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Mode::EditPreview:
|
case Mode::EditPreview:
|
||||||
Q_ASSERT(false);
|
|
||||||
if (m_editor->isVisible()) {
|
if (m_editor->isVisible()) {
|
||||||
m_editor->setFocus();
|
m_editor->setFocus();
|
||||||
} else {
|
} else {
|
||||||
// m_webViewer->setFocus();
|
m_webViewer->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1169,7 +1169,7 @@ void VMdTab::reload()
|
|||||||
|
|
||||||
// Reload web viewer.
|
// Reload web viewer.
|
||||||
m_ready &= ~TabReady::ReadMode;
|
m_ready &= ~TabReady::ReadMode;
|
||||||
// m_webViewer->reload();
|
m_webViewer->reload();
|
||||||
|
|
||||||
if (!m_isEditMode) {
|
if (!m_isEditMode) {
|
||||||
VUtils::sleepWait(500);
|
VUtils::sleepWait(500);
|
||||||
@ -1298,10 +1298,8 @@ void VMdTab::handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act)
|
|||||||
{
|
{
|
||||||
// Reload the web view with new base URL.
|
// Reload the web view with new base URL.
|
||||||
m_headerFromEditMode = m_currentHeader;
|
m_headerFromEditMode = m_currentHeader;
|
||||||
/*
|
m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType, m_port),
|
||||||
m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType),
|
|
||||||
m_file->getBaseUrl());
|
m_file->getBaseUrl());
|
||||||
*/
|
|
||||||
|
|
||||||
if (m_editor) {
|
if (m_editor) {
|
||||||
m_editor->updateInitAndInsertedImages(p_isFile, p_act);
|
m_editor->updateInitAndInsertedImages(p_isFile, p_act);
|
||||||
@ -1449,8 +1447,7 @@ bool VMdTab::executeVimCommandInWebView(const QString &p_cmd)
|
|||||||
msg = tr("Quit");
|
msg = tr("Quit");
|
||||||
} else if (p_cmd == "nohlsearch" || p_cmd == "noh") {
|
} else if (p_cmd == "nohlsearch" || p_cmd == "noh") {
|
||||||
// :nohlsearch, clear highlight search.
|
// :nohlsearch, clear highlight search.
|
||||||
Q_ASSERT(false);
|
m_webViewer->findText("");
|
||||||
// m_webViewer->findText("");
|
|
||||||
} else {
|
} else {
|
||||||
validCommand = false;
|
validCommand = false;
|
||||||
}
|
}
|
||||||
@ -1485,9 +1482,11 @@ void VMdTab::handleDownloadRequested(QWebEngineDownloadItem *p_item)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void VMdTab::handleSavePageRequested()
|
void VMdTab::handleSavePageRequested()
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
static QString lastPath = g_config->getDocumentPathOrHomePath();
|
static QString lastPath = g_config->getDocumentPathOrHomePath();
|
||||||
|
|
||||||
@ -1517,8 +1516,8 @@ void VMdTab::handleSavePageRequested()
|
|||||||
emit statusMessage(tr("Saving page to %1").arg(fileName));
|
emit statusMessage(tr("Saving page to %1").arg(fileName));
|
||||||
|
|
||||||
// m_webViewer->page()->save(fileName, format);
|
// m_webViewer->page()->save(fileName, format);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
VWordCountInfo VMdTab::fetchWordCountInfo(bool p_editMode) const
|
VWordCountInfo VMdTab::fetchWordCountInfo(bool p_editMode) const
|
||||||
{
|
{
|
||||||
@ -1544,8 +1543,7 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// qreal factor = m_webViewer->zoomFactor();
|
qreal factor = m_webViewer->zoomFactor();
|
||||||
qreal factor = 1.0;
|
|
||||||
if (m_mode == Mode::Read) {
|
if (m_mode == Mode::Read) {
|
||||||
m_readWebViewState->m_zoomFactor = factor;
|
m_readWebViewState->m_zoomFactor = factor;
|
||||||
} else if (m_mode == Mode::EditPreview) {
|
} else if (m_mode == Mode::EditPreview) {
|
||||||
@ -1557,13 +1555,12 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
|
|
||||||
switch (p_mode) {
|
switch (p_mode) {
|
||||||
case Mode::Read:
|
case Mode::Read:
|
||||||
Q_ASSERT(false);
|
|
||||||
if (m_editor) {
|
if (m_editor) {
|
||||||
m_editor->hide();
|
m_editor->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
// m_webViewer->setInPreview(false);
|
m_webViewer->setInPreview(false);
|
||||||
// m_webViewer->show();
|
m_webViewer->show();
|
||||||
|
|
||||||
// Fix the bug introduced by 051088be31dbffa3c04e2d382af15beec40d5fdb
|
// Fix the bug introduced by 051088be31dbffa3c04e2d382af15beec40d5fdb
|
||||||
// which replace QStackedLayout with QSplitter.
|
// which replace QStackedLayout with QSplitter.
|
||||||
@ -1573,7 +1570,7 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
m_readWebViewState.reset(new WebViewState());
|
m_readWebViewState.reset(new WebViewState());
|
||||||
m_readWebViewState->m_zoomFactor = factor;
|
m_readWebViewState->m_zoomFactor = factor;
|
||||||
} else if (factor != m_readWebViewState->m_zoomFactor) {
|
} 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);
|
m_document->setPreviewEnabled(false);
|
||||||
@ -1581,7 +1578,7 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
|
|
||||||
case Mode::Edit:
|
case Mode::Edit:
|
||||||
m_document->muteWebView(true);
|
m_document->muteWebView(true);
|
||||||
// m_webViewer->hide();
|
m_webViewer->hide();
|
||||||
m_editor->show();
|
m_editor->show();
|
||||||
|
|
||||||
QCoreApplication::sendPostedEvents();
|
QCoreApplication::sendPostedEvents();
|
||||||
@ -1590,10 +1587,9 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
|
|
||||||
case Mode::EditPreview:
|
case Mode::EditPreview:
|
||||||
Q_ASSERT(m_editor);
|
Q_ASSERT(m_editor);
|
||||||
Q_ASSERT(false);
|
|
||||||
m_document->muteWebView(true);
|
m_document->muteWebView(true);
|
||||||
// m_webViewer->setInPreview(true);
|
m_webViewer->setInPreview(true);
|
||||||
// m_webViewer->show();
|
m_webViewer->show();
|
||||||
m_editor->show();
|
m_editor->show();
|
||||||
|
|
||||||
QCoreApplication::sendPostedEvents();
|
QCoreApplication::sendPostedEvents();
|
||||||
@ -1617,7 +1613,7 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
newSizes.append(b);
|
newSizes.append(b);
|
||||||
m_splitter->setSizes(newSizes);
|
m_splitter->setSizes(newSizes);
|
||||||
} else if (factor != m_previewWebViewState->m_zoomFactor) {
|
} 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);
|
m_document->setPreviewEnabled(true);
|
||||||
@ -1670,10 +1666,9 @@ bool VMdTab::expandRestorePreviewArea()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(false);
|
|
||||||
if (m_editor->isVisible()) {
|
if (m_editor->isVisible()) {
|
||||||
m_editor->hide();
|
m_editor->hide();
|
||||||
// m_webViewer->setFocus();
|
m_webViewer->setFocus();
|
||||||
} else {
|
} else {
|
||||||
m_editor->show();
|
m_editor->show();
|
||||||
m_editor->setFocus();
|
m_editor->setFocus();
|
||||||
@ -1686,3 +1681,19 @@ bool VMdTab::previewExpanded() const
|
|||||||
{
|
{
|
||||||
return (m_mode == Mode::EditPreview) && !m_editor->isVisible();
|
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);
|
||||||
|
}
|
||||||
|
20
src/vmdtab.h
20
src/vmdtab.h
@ -4,12 +4,14 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
|
|
||||||
// class VWebView;
|
class VWebView;
|
||||||
class VDocument;
|
class VDocument;
|
||||||
class VMdEditor;
|
class VMdEditor;
|
||||||
class VInsertSelector;
|
class VInsertSelector;
|
||||||
@ -26,6 +28,8 @@ class VMdTab : public VEditTab
|
|||||||
public:
|
public:
|
||||||
VMdTab(VFile *p_file, VEditArea *p_editArea, OpenFileMode p_mode, QWidget *p_parent = 0);
|
VMdTab(VFile *p_file, VEditArea *p_editArea, OpenFileMode p_mode, QWidget *p_parent = 0);
|
||||||
|
|
||||||
|
~VMdTab();
|
||||||
|
|
||||||
// Close current tab.
|
// Close current tab.
|
||||||
// @p_forced: if true, discard the changes.
|
// @p_forced: if true, discard the changes.
|
||||||
bool closeFile(bool p_forced) Q_DECL_OVERRIDE;
|
bool closeFile(bool p_forced) Q_DECL_OVERRIDE;
|
||||||
@ -69,7 +73,7 @@ public:
|
|||||||
|
|
||||||
void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
|
void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
// VWebView *getWebViewer() const;
|
VWebView *getWebViewer() const;
|
||||||
|
|
||||||
VMdEditor *getEditor() const;
|
VMdEditor *getEditor() const;
|
||||||
|
|
||||||
@ -156,7 +160,7 @@ private slots:
|
|||||||
// void handleDownloadRequested(QWebEngineDownloadItem *p_item);
|
// void handleDownloadRequested(QWebEngineDownloadItem *p_item);
|
||||||
|
|
||||||
// Handle save page request.
|
// Handle save page request.
|
||||||
// void handleSavePageRequested();
|
void handleSavePageRequested();
|
||||||
|
|
||||||
// Selection changed in web.
|
// Selection changed in web.
|
||||||
void handleWebSelectionChanged();
|
void handleWebSelectionChanged();
|
||||||
@ -246,8 +250,12 @@ private:
|
|||||||
|
|
||||||
bool previewExpanded() const;
|
bool previewExpanded() const;
|
||||||
|
|
||||||
|
static quint16 getNextPort();
|
||||||
|
static void releasePort(quint16 p_port);
|
||||||
|
|
||||||
VMdEditor *m_editor;
|
VMdEditor *m_editor;
|
||||||
// VWebView *m_webViewer;
|
VWebView *m_webViewer;
|
||||||
|
quint16 m_port;
|
||||||
VDocument *m_document;
|
VDocument *m_document;
|
||||||
MarkdownConverterType m_mdConType;
|
MarkdownConverterType m_mdConType;
|
||||||
|
|
||||||
@ -277,6 +285,10 @@ private:
|
|||||||
VMathJaxInplacePreviewHelper *m_mathjaxPreviewHelper;
|
VMathJaxInplacePreviewHelper *m_mathjaxPreviewHelper;
|
||||||
|
|
||||||
int m_documentID;
|
int m_documentID;
|
||||||
|
|
||||||
|
static const quint16 c_basePort;
|
||||||
|
|
||||||
|
static QSet<quint16> s_usedPorts;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline VMdEditor *VMdTab::getEditor()
|
inline VMdEditor *VMdTab::getEditor()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>resources/qwebchannel.js</file>
|
|
||||||
<file>utils/marked/marked.min.js</file>
|
<file>utils/marked/marked.min.js</file>
|
||||||
<file>utils/highlightjs/highlight.pack.js</file>
|
<file>utils/highlightjs/highlight.pack.js</file>
|
||||||
<file>resources/vnote.ini</file>
|
<file>resources/vnote.ini</file>
|
||||||
|
@ -18,24 +18,19 @@ bool VPreviewPage::acceptNavigationRequest(QWebFrame *p_frame,
|
|||||||
const QNetworkRequest &p_request,
|
const QNetworkRequest &p_request,
|
||||||
QWebPage::NavigationType p_type)
|
QWebPage::NavigationType p_type)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p_frame);
|
if (p_type == QWebPage::NavigationTypeLinkClicked) {
|
||||||
Q_UNUSED(p_type);
|
|
||||||
|
|
||||||
auto url = p_request.url();
|
auto url = p_request.url();
|
||||||
if (url.isLocalFile()) {
|
if (url.isLocalFile()) {
|
||||||
QString filePath = url.toLocalFile();
|
QString filePath = url.toLocalFile();
|
||||||
if (g_mainWin->tryOpenInternalFile(filePath)) {
|
if (g_mainWin->tryOpenInternalFile(filePath)) {
|
||||||
return false;
|
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)
|
void VPreviewPage::setBackgroundColor(const QColor &p_background)
|
||||||
|
@ -3,20 +3,25 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
#include <QContextMenuEvent>
|
#include <QContextMenuEvent>
|
||||||
#include <QWebEnginePage>
|
#include <QWebPage>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
#include <QWebChannel>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QWebSocketServer>
|
||||||
|
|
||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
#include "utils/vclipboardutils.h"
|
#include "utils/vclipboardutils.h"
|
||||||
#include "utils/viconutils.h"
|
#include "utils/viconutils.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "utils/vwebutils.h"
|
#include "utils/vwebutils.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
|
#include "websocketclientwrapper.h"
|
||||||
|
#include "websockettransport.h"
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
@ -31,7 +36,8 @@ VWebView::VWebView(VFile *p_file, QWidget *p_parent)
|
|||||||
m_file(p_file),
|
m_file(p_file),
|
||||||
m_copyImageUrlActionHooked(false),
|
m_copyImageUrlActionHooked(false),
|
||||||
m_afterCopyImage(false),
|
m_afterCopyImage(false),
|
||||||
m_inPreview(false)
|
m_inPreview(false),
|
||||||
|
m_channel(nullptr)
|
||||||
{
|
{
|
||||||
setAcceptDrops(false);
|
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
|
// and the URL as URLs. If the URL contains Chinese, OneNote or Word could not
|
||||||
// recognize it.
|
// recognize it.
|
||||||
// We need to change it to only-space-encoded text.
|
// We need to change it to only-space-encoded text.
|
||||||
QAction *copyImageUrlAct = pageAction(QWebEnginePage::CopyImageUrlToClipboard);
|
QAction *copyImageUrlAct = pageAction(QWebPage::CopyImageUrlToClipboard);
|
||||||
if (actions.contains(copyImageUrlAct)) {
|
if (actions.contains(copyImageUrlAct)) {
|
||||||
connect(copyImageUrlAct, &QAction::triggered,
|
connect(copyImageUrlAct, &QAction::triggered,
|
||||||
this, &VWebView::handleCopyImageUrlAction);
|
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,
|
connect(savePageAct, &QAction::triggered,
|
||||||
this, &VWebView::requestSavePage);
|
this, &VWebView::requestSavePage);
|
||||||
menu->addAction(savePageAct);
|
menu->addAction(savePageAct);
|
||||||
@ -103,7 +109,7 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
|
|||||||
|
|
||||||
// Add Copy As menu.
|
// Add Copy As menu.
|
||||||
{
|
{
|
||||||
QAction *copyAct = pageAction(QWebEnginePage::Copy);
|
QAction *copyAct = pageAction(QWebPage::Copy);
|
||||||
if (actions.contains(copyAct) && !m_inPreview) {
|
if (actions.contains(copyAct) && !m_inPreview) {
|
||||||
initCopyAsMenu(copyAct, menu);
|
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
|
// - the default one use the fully-encoded URL to fetch the image while
|
||||||
// Windows seems to not recognize it.
|
// Windows seems to not recognize it.
|
||||||
// - We need to remove the html to let it be recognized by some web pages.
|
// - 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)) {
|
if (actions.contains(defaultCopyImageAct)) {
|
||||||
QAction *copyImageAct = new QAction(defaultCopyImageAct->text(), menu);
|
QAction *copyImageAct = new QAction(defaultCopyImageAct->text(), menu);
|
||||||
copyImageAct->setToolTip(defaultCopyImageAct->toolTip());
|
copyImageAct->setToolTip(defaultCopyImageAct->toolTip());
|
||||||
@ -156,9 +162,9 @@ void VWebView::copyImage()
|
|||||||
{
|
{
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
Q_ASSERT(m_copyImageUrlActionHooked);
|
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.
|
// trigger the corresponding action. It just do the stuff directly.
|
||||||
QAction *copyImageUrlAct = pageAction(QWebEnginePage::CopyImageUrlToClipboard);
|
QAction *copyImageUrlAct = pageAction(QWebPage::CopyImageUrlToClipboard);
|
||||||
copyImageUrlAct->trigger();
|
copyImageUrlAct->trigger();
|
||||||
|
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
@ -189,7 +195,7 @@ void VWebView::copyImage()
|
|||||||
m_afterCopyImage = true;
|
m_afterCopyImage = true;
|
||||||
|
|
||||||
// Fall back.
|
// Fall back.
|
||||||
triggerPageAction(QWebEnginePage::CopyImageToClipboard);
|
triggerPageAction(QWebPage::CopyImageToClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VWebView::handleCopyImageUrlAction()
|
void VWebView::handleCopyImageUrlAction()
|
||||||
@ -230,9 +236,9 @@ void VWebView::hideUnusedActions(QMenu *p_menu)
|
|||||||
// QWebEnginePage uses different actions of Back/Forward/Reload.
|
// QWebEnginePage uses different actions of Back/Forward/Reload.
|
||||||
// [Woboq](https://code.woboq.org/qt5/qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp.html#1652)
|
// [Woboq](https://code.woboq.org/qt5/qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp.html#1652)
|
||||||
// We tell these three actions by name.
|
// We tell these three actions by name.
|
||||||
const QStringList actionNames({QWebEnginePage::tr("&Back"),
|
const QStringList actionNames({QWebPage::tr("&Back"),
|
||||||
QWebEnginePage::tr("&Forward"),
|
QWebPage::tr("&Forward"),
|
||||||
QWebEnginePage::tr("&Reload")});
|
QWebPage::tr("&Reload")});
|
||||||
|
|
||||||
const QList<QAction *> actions = p_menu->actions();
|
const QList<QAction *> actions = p_menu->actions();
|
||||||
for (auto it : actions) {
|
for (auto it : actions) {
|
||||||
@ -242,15 +248,15 @@ void VWebView::hideUnusedActions(QMenu *p_menu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ViewSource.
|
// ViewSource.
|
||||||
QAction *act = pageAction(QWebEnginePage::ViewSource);
|
// QAction *act = pageAction(QWebPage::ViewSource);
|
||||||
unusedActions.append(act);
|
// unusedActions.append(act);
|
||||||
|
|
||||||
// DownloadImageToDisk.
|
// DownloadImageToDisk.
|
||||||
act = pageAction(QWebEnginePage::DownloadImageToDisk);
|
auto act = pageAction(QWebPage::DownloadImageToDisk);
|
||||||
unusedActions.append(act);
|
unusedActions.append(act);
|
||||||
|
|
||||||
// DownloadLinkToDisk.
|
// DownloadLinkToDisk.
|
||||||
act = pageAction(QWebEnginePage::DownloadLinkToDisk);
|
act = pageAction(QWebPage::DownloadLinkToDisk);
|
||||||
unusedActions.append(act);
|
unusedActions.append(act);
|
||||||
|
|
||||||
for (auto it : unusedActions) {
|
for (auto it : unusedActions) {
|
||||||
@ -394,7 +400,7 @@ void VWebView::handleCopyAsAction(QAction *p_act)
|
|||||||
|
|
||||||
m_copyTarget = p_act->data().toString();
|
m_copyTarget = p_act->data().toString();
|
||||||
|
|
||||||
triggerPageAction(QWebEnginePage::Copy);
|
triggerPageAction(QWebPage::Copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VWebView::initCopyAllAsMenu(QMenu *p_menu)
|
void VWebView::initCopyAllAsMenu(QMenu *p_menu)
|
||||||
@ -427,13 +433,13 @@ void VWebView::handleCopyAllAsAction(QAction *p_act)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerPageAction(QWebEnginePage::SelectAll);
|
triggerPageAction(QWebPage::SelectAll);
|
||||||
|
|
||||||
m_copyTarget = p_act->data().toString();
|
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)
|
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) {
|
if (act->data().toInt() == config) {
|
||||||
act->setChecked(true);
|
act->setChecked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(ag, &QActionGroup::triggered,
|
connect(ag, &QActionGroup::triggered,
|
||||||
this, [](QAction *p_act) {
|
this, [](QAction *p_act) {
|
||||||
int data = p_act->data().toInt();
|
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);
|
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);
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
class VFile;
|
class VFile;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
|
class QWebChannel;
|
||||||
|
|
||||||
class VWebView : public QWebView
|
class VWebView : public QWebView
|
||||||
{
|
{
|
||||||
@ -17,6 +18,8 @@ public:
|
|||||||
|
|
||||||
void setInPreview(bool p_preview);
|
void setInPreview(bool p_preview);
|
||||||
|
|
||||||
|
void bindToChannel(quint16 p_port, const QString &p_name, QObject *p_object);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void editNote();
|
void editNote();
|
||||||
|
|
||||||
@ -73,6 +76,8 @@ private:
|
|||||||
|
|
||||||
// Whether in preview mode.
|
// Whether in preview mode.
|
||||||
bool m_inPreview;
|
bool m_inPreview;
|
||||||
|
|
||||||
|
QWebChannel *m_channel;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void VWebView::setInPreview(bool p_preview)
|
inline void VWebView::setInPreview(bool p_preview)
|
||||||
|
77
src/websocketclientwrapper.cpp
Normal file
77
src/websocketclientwrapper.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
|
||||||
|
** 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 <QWebSocketServer>
|
||||||
|
/*!
|
||||||
|
\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()));
|
||||||
|
}
|
69
src/websocketclientwrapper.h
Normal file
69
src/websocketclientwrapper.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
|
||||||
|
** 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 <QObject>
|
||||||
|
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
|
105
src/websockettransport.cpp
Normal file
105
src/websockettransport.cpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
|
||||||
|
** 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 <QDebug>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QWebSocket>
|
||||||
|
/*!
|
||||||
|
\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);
|
||||||
|
}
|
68
src/websockettransport.h
Normal file
68
src/websockettransport.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
|
||||||
|
** 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 <QWebChannelAbstractTransport>
|
||||||
|
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
|
Loading…
x
Reference in New Issue
Block a user