refine copy HTML logics

This commit is contained in:
Le Tan 2018-01-09 20:20:32 +08:00
parent 9dad67739a
commit 14df9e6e90
13 changed files with 789 additions and 153 deletions

View File

@ -13,8 +13,12 @@
extern VConfigManager *g_config;
VCopyTextAsHtmlDialog::VCopyTextAsHtmlDialog(const QString &p_text, QWidget *p_parent)
: QDialog(p_parent), m_text(p_text)
extern VWebUtils *g_webUtils;
VCopyTextAsHtmlDialog::VCopyTextAsHtmlDialog(const QString &p_text,
const QString &p_copyTarget,
QWidget *p_parent)
: QDialog(p_parent), m_text(p_text), m_copyTarget(p_copyTarget)
{
setupUI();
}
@ -47,7 +51,7 @@ void VCopyTextAsHtmlDialog::setupUI()
mainLayout->addWidget(m_btnBox);
setLayout(mainLayout);
setWindowTitle(tr("Copy Text As HTML"));
setWindowTitle(tr("Copy Text As HTML (%1)").arg(m_copyTarget));
setHtmlVisible(false);
}
@ -61,16 +65,11 @@ void VCopyTextAsHtmlDialog::setHtmlVisible(bool p_visible)
void VCopyTextAsHtmlDialog::setConvertedHtml(const QUrl &p_baseUrl,
const QString &p_html)
{
QString html = QString("<html><body>%1</body></html>").arg(p_html);
m_htmlViewer->setHtml(html, p_baseUrl);
QString html = p_html;
m_htmlViewer->setHtml("<html><body>" + html + "</body></html>", p_baseUrl);
setHtmlVisible(true);
VWebUtils::translateColors(html);
// Fix image source.
if (g_config->getFixImageSrcInWebWhenCopied()) {
VWebUtils::fixImageSrcInHtml(p_baseUrl, html);
}
g_webUtils->alterHtmlAsTarget(p_baseUrl, html, m_copyTarget);
QClipboard *clipboard = QApplication::clipboard();
QMimeData *data = new QMimeData();

View File

@ -15,7 +15,9 @@ class VCopyTextAsHtmlDialog : public QDialog
{
Q_OBJECT
public:
VCopyTextAsHtmlDialog(const QString &p_text, QWidget *p_parent = nullptr);
VCopyTextAsHtmlDialog(const QString &p_text,
const QString &p_copyTarget,
QWidget *p_parent = nullptr);
void setConvertedHtml(const QUrl &p_baseUrl, const QString &p_html);
@ -37,6 +39,8 @@ private:
QDialogButtonBox *m_btnBox;
QString m_text;
QString m_copyTarget;
};
inline const QString &VCopyTextAsHtmlDialog::getText() const

View File

@ -193,17 +193,31 @@ custom_colors=White:#FFFFFF,LightGrey:#EEEEEE
; Location and configuration for Mathjax
mathjax_javascript=https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML
; Fix local relative image source when copied
fix_img_src_when_copied=true
; Styles to be removed when copied in read mode
; Styles to be removed when copied
; style1,style2,style3
styles_to_remove_when_copied=margin,margin-left,margin-right,padding,padding-left,padding-right
styles_to_remove_when_copied=
; Styles when transform <mark> to <span>
style_of_span_for_mark="background-color: #FFFF00;"
; CSS properties to embed as inline styles when copied in edit mode
; tag1:tag2:tag3$property1:property2:property3,tag4:tag5$property2:property3
; "all" for all tags not specified explicitly
styles_to_inline_when_copied=all$border:color:display:font-family:font-size:font-style:white-space:word-spacing:line-height:text-align:text-indent:padding-top:padding-bottom:margin-top:margin-bottom,code$font-family:font-size:line-height:color:display:overfow-x,li$line-height,a$color:vertical-align,pre$display:overflow-y:overflow-x:color:font-size:font-style:font-weight:letter-spacing:text-align:text-indent:word-spacing
styles_to_inline_when_copied=all$border:color:display:font-family:font-size:font-style:white-space:word-spacing:line-height:text-align:text-indent:padding-top:padding-bottom:margin-top:margin-bottom:background-color,code$font-family:font-size:line-height:color:display:overfow-x:background-color,li$line-height:background-color,a$color:vertical-align:background-color,pre$display:overflow-y:overflow-x:color:font-size:font-style:font-weight:letter-spacing:text-align:text-indent:word-spacing:background-color
; Define targets the copied content will be pasted into
; target_name$action1:action2:action3,targeet_name2$action2:action3
; Available actions:
; s - add surrounding tags <html><body>
; b(tag1|tag2) - remove background color of all tags except tag1 and tag2
; c(tag1|tag2) - translate colors using palette defined mapping except tag1 and tag2
; i - fix local relative <img src>
; m(tag1|tag2) - remove margin/margin-left/margin-right/padding/padding-left/padding-right of all tags except tag1 and tag2
; r - raw html with all styles removed
; a - transform <mark> to <span>
; x(tag1|tag2) - remove styles specified in [styles_to_inline_when_copied] of all tags except tag1 and tag2
; p - replace the background color of <pre> with that of its child <code>
copy_targets=WithoutBackground$s:b(mark):c:i:x,OneNote$s:b(mark):c:i:m:a:x,MicroSoftWord$s:p:b(mark|pre):c(pre):i:m:a:x,RawHTML$r
[shortcuts]
; Define shortcuts here, with each item in the form "operation=keysequence".

View File

@ -5,14 +5,79 @@
#include <QDebug>
#include "vpalette.h"
#include "vconfigmanager.h"
extern VPalette *g_palette;
extern VConfigManager *g_config;
VWebUtils::VWebUtils()
{
}
bool VWebUtils::fixImageSrcInHtml(const QUrl &p_baseUrl, QString &p_html)
void VWebUtils::init()
{
m_stylesToRemoveWhenCopied = g_config->getStylesToRemoveWhenCopied();
m_styleOfSpanForMark = g_config->getStyleOfSpanForMark();
m_tagReg = QRegExp("<([^>/\\s]+)([^>]*)>");
m_styleTagReg = QRegExp("<([^>\\s]+)([^>]*\\s)style=\"([^\">]+)\"([^>]*)>");
initCopyTargets(g_config->getCopyTargets());
}
void VWebUtils::initCopyTargets(const QStringList &p_str)
{
Q_ASSERT(m_copyTargets.isEmpty());
// cap(1): action;
// cap(3): arguments;
QRegExp actReg("([0-9a-zA-Z])(\\(([^\\)]*)\\))?");
for (auto const & str : p_str) {
auto vals = str.split('$');
if (vals.size() != 2) {
continue;
}
CopyTarget tar;
tar.m_name = vals[0];
if (tar.m_name.isEmpty()) {
continue;
}
auto acts = vals[1].split(':');
for (auto const & it : acts) {
if (it.isEmpty()) {
continue;
}
if (!actReg.exactMatch(it)) {
continue;
}
if (actReg.cap(1).size() != 1) {
continue;
}
CopyTargetAction act;
act.m_act = actReg.cap(1)[0];
if (!actReg.cap(3).isEmpty()) {
act.m_args = actReg.cap(3).toLower().split('|');
}
tar.m_actions.append(act);
}
m_copyTargets.append(tar);
}
qDebug() << "init" << m_copyTargets.size() << "copy targets";
}
bool VWebUtils::fixImageSrc(const QUrl &p_baseUrl, QString &p_html)
{
bool changed = false;
@ -61,15 +126,142 @@ bool VWebUtils::fixImageSrcInHtml(const QUrl &p_baseUrl, QString &p_html)
return changed;
}
bool VWebUtils::removeBackgroundColor(QString &p_html)
QStringList VWebUtils::getCopyTargetsName() const
{
QRegExp reg("(<[^>]+\\sstyle=[^>]*(\\s|\"))background(-color)?:[^;]+;([^>]*>)");
int size = p_html.size();
p_html.replace(reg, "\\1\\4");
return p_html.size() != size;
QStringList names;
for (auto const & it : m_copyTargets) {
names << it.m_name;
}
bool VWebUtils::translateColors(QString &p_html)
return names;
}
bool VWebUtils::alterHtmlAsTarget(const QUrl &p_baseUrl, QString &p_html, const QString &p_target) const
{
int idx = targetIndex(p_target);
if (idx == -1) {
return false;
}
bool altered = false;
for (auto const & act : m_copyTargets[idx].m_actions) {
if (const_cast<VWebUtils *>(this)->alterHtmlByTargetAction(p_baseUrl, p_html, act)) {
altered = true;
}
}
return altered;
}
int VWebUtils::targetIndex(const QString &p_target) const
{
if (p_target.isEmpty()) {
return -1;
}
for (int i = 0; i < m_copyTargets.size(); ++i) {
if (m_copyTargets[i].m_name == p_target) {
return i;
}
}
return -1;
}
bool VWebUtils::alterHtmlByTargetAction(const QUrl &p_baseUrl, QString &p_html, const CopyTargetAction &p_action)
{
bool altered = false;
switch (p_action.m_act.toLatin1()) {
case 's':
if (!p_html.startsWith("<html>")) {
p_html = "<html><body>" + p_html + "</body></html>";
altered = true;
}
break;
case 'b':
altered = removeBackgroundColor(p_html, p_action.m_args);
break;
case 'c':
altered = translateColors(p_html, p_action.m_args);
break;
case 'i':
altered = fixImageSrc(p_baseUrl, p_html);
break;
case 'm':
altered = removeMarginPadding(p_html, p_action.m_args);
break;
case 'x':
altered = removeStylesToRemoveWhenCopied(p_html, p_action.m_args);
break;
case 'r':
altered = removeAllStyles(p_html, p_action.m_args);
break;
case 'a':
altered = transformMarkToSpan(p_html);
break;
case 'p':
altered = replacePreBackgroundColorWithCode(p_html);
break;
default:
break;
}
return altered;
}
static int skipToTagEnd(const QString &p_html, int p_pos, const QString &p_tag)
{
QRegExp beginReg(QString("<%1 ").arg(p_tag));
QRegExp endReg(QString("</%1>").arg(p_tag));
int pos = p_pos;
int nBegin = p_html.indexOf(beginReg, pos);
int nEnd = p_html.indexOf(endReg, pos);
if (nBegin > -1 && nBegin < nEnd) {
// Nested tag.
pos = skipToTagEnd(p_html, nBegin + beginReg.matchedLength(), p_tag);
nEnd = p_html.indexOf(endReg, pos);
}
if (nEnd > -1) {
pos = nEnd + endReg.matchedLength();
}
return pos;
}
// @p_html is the style string.
static bool removeStylesInStyleString(QString &p_html, const QStringList &p_styles)
{
if (p_styles.isEmpty()) {
return false;
}
int size = p_html.size();
QRegExp reg(QString("(\\s|^)(%1):[^:]+;").arg(p_styles.join('|')));
p_html.remove(reg);
return size != p_html.size();
}
bool VWebUtils::removeBackgroundColor(QString &p_html, const QStringList &p_skipTags)
{
QStringList styles({"background", "background-color"});
return removeStyles(p_html, p_skipTags, styles);
}
bool VWebUtils::translateColors(QString &p_html, const QStringList &p_skipTags)
{
bool changed = false;
@ -78,20 +270,34 @@ bool VWebUtils::translateColors(QString &p_html)
return changed;
}
QRegExp tagReg("(<[^>]+\\sstyle=[^>]*>)");
// Won't mixed up with background-color.
QRegExp colorReg("(\\s|\")color:([^;]+);");
QRegExp colorReg("(\\s|^)color:([^;]+);");
int pos = 0;
while (pos < p_html.size()) {
int idx = p_html.indexOf(tagReg, pos);
if (idx == -1) {
int tagIdx = p_html.indexOf(m_tagReg, pos);
if (tagIdx == -1) {
break;
}
QString styleStr = tagReg.cap(1);
QString alteredStyleStr = styleStr;
QString tagName = m_tagReg.cap(1);
if (p_skipTags.contains(tagName.toLower())) {
// Skip this tag.
pos = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tagName);
continue;
}
pos = tagIdx;
int idx = p_html.indexOf(m_styleTagReg, pos);
if (idx == -1) {
break;
} else if (idx != tagIdx) {
pos = tagIdx + m_tagReg.matchedLength();
continue;
}
QString styleStr = m_styleTagReg.cap(3);
QString alteredStyleStr = styleStr;
int posb = 0;
while (posb < alteredStyleStr.size()) {
int idxb = alteredStyleStr.indexOf(colorReg, posb);
@ -108,19 +314,280 @@ bool VWebUtils::translateColors(QString &p_html)
// Replace the color.
QString newCol = it.value();
// Add one extra space between color and :.
// Should not add extra space before :.
QString newStr = QString("%1color: %2;").arg(colorReg.cap(1)).arg(newCol);
alteredStyleStr.replace(idxb, colorReg.matchedLength(), newStr);
posb = idxb + newStr.size();
changed = true;
}
pos = idx + tagReg.matchedLength();
if (changed) {
pos = pos + alteredStyleStr.size() - styleStr.size();
p_html.replace(idx, tagReg.matchedLength(), alteredStyleStr);
QString newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
.arg(m_styleTagReg.cap(2))
.arg(alteredStyleStr)
.arg(m_styleTagReg.cap(4));
p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
pos = idx + newTag.size();
} else {
pos = idx + m_styleTagReg.matchedLength();
}
}
return changed;
}
bool VWebUtils::removeMarginPadding(QString &p_html, const QStringList &p_skipTags)
{
QStringList styles({"margin", "margin-left", "margin-right",
"padding", "padding-left", "padding-right"});
return removeStyles(p_html, p_skipTags, styles);
}
bool VWebUtils::removeStyles(QString &p_html, const QStringList &p_skipTags, const QStringList &p_styles)
{
if (p_styles.isEmpty()) {
return false;
}
bool altered = false;
int pos = 0;
while (pos < p_html.size()) {
int tagIdx = p_html.indexOf(m_tagReg, pos);
if (tagIdx == -1) {
break;
}
QString tagName = m_tagReg.cap(1);
if (p_skipTags.contains(tagName.toLower())) {
// Skip this tag.
pos = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tagName);
continue;
}
pos = tagIdx;
int idx = p_html.indexOf(m_styleTagReg, pos);
if (idx == -1) {
break;
} else if (idx != tagIdx) {
pos = tagIdx + m_tagReg.matchedLength();
continue;
}
QString styleStr = m_styleTagReg.cap(3);
if (removeStylesInStyleString(styleStr, p_styles)) {
QString newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
.arg(m_styleTagReg.cap(2))
.arg(styleStr)
.arg(m_styleTagReg.cap(4));
p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
pos = idx + newTag.size();
altered = true;
} else {
pos = idx + m_styleTagReg.matchedLength();
}
}
return altered;
}
bool VWebUtils::removeStylesToRemoveWhenCopied(QString &p_html, const QStringList &p_skipTags)
{
return removeStyles(p_html, p_skipTags, m_stylesToRemoveWhenCopied);
}
bool VWebUtils::removeAllStyles(QString &p_html, const QStringList &p_skipTags)
{
bool altered = false;
int pos = 0;
while (pos < p_html.size()) {
int tagIdx = p_html.indexOf(m_tagReg, pos);
if (tagIdx == -1) {
break;
}
QString tagName = m_tagReg.cap(1);
if (p_skipTags.contains(tagName.toLower())) {
// Skip this tag.
pos = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tagName);
continue;
}
pos = tagIdx;
int idx = p_html.indexOf(m_styleTagReg, pos);
if (idx == -1) {
break;
} else if (idx != tagIdx) {
pos = tagIdx + m_tagReg.matchedLength();
continue;
}
QString newTag = QString("<%1%2%3>").arg(m_styleTagReg.cap(1))
.arg(m_styleTagReg.cap(2))
.arg(m_styleTagReg.cap(4));
p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
pos = idx + newTag.size();
altered = true;
}
return altered;
}
bool VWebUtils::transformMarkToSpan(QString &p_html)
{
bool altered = false;
int pos = 0;
while (pos < p_html.size()) {
int tagIdx = p_html.indexOf(m_tagReg, pos);
if (tagIdx == -1) {
break;
}
QString tagName = m_tagReg.cap(1);
if (tagName.toLower() != "mark") {
pos = tagIdx + m_tagReg.matchedLength();
continue;
}
pos = tagIdx;
int idx = p_html.indexOf(m_styleTagReg, pos);
if (idx == -1 || idx != tagIdx) {
// <mark> without "style".
QString newTag = QString("<span style=\"%1\" %2>").arg(m_styleOfSpanForMark)
.arg(m_tagReg.cap(2));
p_html.replace(tagIdx, m_tagReg.matchedLength(), newTag);
pos = tagIdx + newTag.size();
altered = true;
continue;
}
QString newTag = QString("<span%1style=\"%2\"%3>").arg(m_styleTagReg.cap(2))
.arg(m_styleTagReg.cap(3) + m_styleOfSpanForMark)
.arg(m_styleTagReg.cap(4));
p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
pos = idx + newTag.size();
altered = true;
}
if (altered) {
// Replace all </mark> with </span>.
p_html.replace("</mark>", "</span>");
}
return altered;
}
bool VWebUtils::replacePreBackgroundColorWithCode(QString &p_html)
{
if (p_html.isEmpty()) {
return false;
}
bool altered = false;
int pos = 0;
QRegExp bgReg("(\\s|^)(background(-color)?:[^;]+;)");
while (pos < p_html.size()) {
int tagIdx = p_html.indexOf(m_tagReg, pos);
if (tagIdx == -1) {
break;
}
QString tagName = m_tagReg.cap(1);
pos = tagIdx + m_tagReg.matchedLength();
if (tagName.toLower() != "pre") {
continue;
}
int preEnd = skipToTagEnd(p_html, pos, tagName);
HtmlTag nextTag = readNextTag(p_html, pos);
if (nextTag.m_name != "code"
|| nextTag.m_start >= preEnd
|| nextTag.m_style.isEmpty()) {
continue;
}
// Get the background style of <code>.
int idx = nextTag.m_style.indexOf(bgReg);
if (idx == -1) {
continue;
}
QString bgStyle = bgReg.cap(2);
pos = tagIdx;
idx = p_html.indexOf(m_styleTagReg, pos);
if (idx == -1 || idx != tagIdx) {
// <pre> without "style".
QString newTag = QString("<%1 style=\"%2\" %3>").arg(m_tagReg.cap(1))
.arg(bgStyle)
.arg(m_tagReg.cap(2));
p_html.replace(tagIdx, m_tagReg.matchedLength(), newTag);
pos = tagIdx + newTag.size();
altered = true;
continue;
}
QString newTag;
if (m_styleTagReg.cap(3).indexOf(bgReg) == -1) {
// No background style specified.
newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
.arg(m_styleTagReg.cap(2))
.arg(m_styleTagReg.cap(3) + bgStyle)
.arg(m_styleTagReg.cap(4));
} else {
// Replace background style.
newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
.arg(m_styleTagReg.cap(2))
.arg(m_styleTagReg.cap(3).replace(bgReg, " " + bgStyle))
.arg(m_styleTagReg.cap(4));
}
p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
pos = idx + newTag.size();
altered = true;
}
return altered;
}
VWebUtils::HtmlTag VWebUtils::readNextTag(const QString &p_html, int p_pos)
{
HtmlTag tag;
int tagIdx = p_html.indexOf(m_tagReg, p_pos);
if (tagIdx == -1) {
return tag;
}
tag.m_name = m_tagReg.cap(1);
tag.m_start = tagIdx;
tag.m_end = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tag.m_name);
int idx = p_html.indexOf(m_styleTagReg, tagIdx);
if (idx == -1 || idx != tagIdx) {
return tag;
}
tag.m_style = m_styleTagReg.cap(3);
return tag;
}

View File

@ -3,22 +3,114 @@
#include <QUrl>
#include <QString>
#include <QVector>
#include <QStringList>
#include <QRegExp>
class VWebUtils
{
public:
// Fix <img src> in @p_html.
static bool fixImageSrcInHtml(const QUrl &p_baseUrl, QString &p_html);
VWebUtils();
// Remove background color style in @p_html.
static bool removeBackgroundColor(QString &p_html);
void init();
// Translate color styles in @p_html using mappings from VPalette.
static bool translateColors(QString &p_html);
QStringList getCopyTargetsName() const;
// Alter @p_html using @p_target.
// Returns true if @p_html is modified.
bool alterHtmlAsTarget(const QUrl &p_baseUrl, QString &p_html, const QString &p_target) const;
private:
VWebUtils();
struct CopyTargetAction
{
QChar m_act;
QStringList m_args;
};
struct CopyTarget
{
QString m_name;
QVector<CopyTargetAction> m_actions;
};
struct HtmlTag
{
HtmlTag()
: m_start(-1), m_end(-1)
{
}
bool isNull()
{
return m_name.isEmpty();
}
QString m_name;
QString m_style;
int m_start;
int m_end;
};
void initCopyTargets(const QStringList &p_str);
// Return the index in m_copyTargets of @p_target.
int targetIndex(const QString &p_target) const;
bool alterHtmlByTargetAction(const QUrl &p_baseUrl, QString &p_html, const CopyTargetAction &p_action);
// Remove background color style in @p_html of all tags except @p_skipTags.
bool removeBackgroundColor(QString &p_html, const QStringList &p_skipTags);
// Translate color styles in @p_html using mappings from VPalette.
bool translateColors(QString &p_html, const QStringList &p_skipTags);
// Fix <img src> in @p_html.
bool fixImageSrc(const QUrl &p_baseUrl, QString &p_html);
// Remove margin/padding/margin-left/right/padding-left/right.
bool removeMarginPadding(QString &p_html, const QStringList &p_skipTags);
bool removeStyles(QString &p_html, const QStringList &p_skipTags, const QStringList &p_styles);
// Remove styles specified in [web]/styles_to_remove_when_copied.
bool removeStylesToRemoveWhenCopied(QString &p_html, const QStringList &p_skipTags);
// Remove all styles.
bool removeAllStyles(QString &p_html, const QStringList &p_skipTags);
// Transform <mark> to <span>.
bool transformMarkToSpan(QString &p_html);
// Replace the background color of <pre> with that of its child <code>.
bool replacePreBackgroundColorWithCode(QString &p_html);
VWebUtils::HtmlTag readNextTag(const QString &p_html, int p_pos);
QVector<CopyTarget> m_copyTargets;
// Custom styles to remove when copied.
QStringList m_stylesToRemoveWhenCopied;
// Style of <span> which is transformed from <mark>.
QString m_styleOfSpanForMark;
// Html start tag.
// Captured texts:
// 1. The tag name like 'code';
// 2. Text after tag name and before the end '>';
QRegExp m_tagReg;
// Html start tag with "style" defined.
// Captured texts:
// 1. The tag name like 'code';
// 2. Text before 'style=""';
// 3. Text inside 'style=""';
// 4. Text after 'style=""' and before '>';
QRegExp m_styleTagReg;
};
#endif // VWEBUTILS_H

View File

@ -285,12 +285,6 @@ void VConfigManager::initialize()
m_closeBeforeExternalEditor = getConfigFromSettings("global",
"close_before_external_editor").toBool();
m_fixImageSrcInWebWhenCopied = getConfigFromSettings("web",
"fix_img_src_when_copied").toBool();
m_stylesToRemoveWhenCopied = getConfigFromSettings("web",
"styles_to_remove_when_copied").toStringList();
m_stylesToInlineWhenCopied = getConfigFromSettings("web",
"styles_to_inline_when_copied").toStringList().join(",");
}

View File

@ -426,12 +426,15 @@ public:
bool getCloseBeforeExternalEditor() const;
bool getFixImageSrcInWebWhenCopied() const;
const QStringList &getStylesToRemoveWhenCopied() const;
QStringList getStylesToRemoveWhenCopied() const;
const QString &getStylesToInlineWhenCopied() const;
QString getStyleOfSpanForMark() const;
// Return [web]/copy_targets.
QStringList getCopyTargets() const;
private:
// Look up a config from user and default settings.
QVariant getConfigFromSettings(const QString &section, const QString &key) const;
@ -822,12 +825,6 @@ private:
// Whether user has reset the configurations.
bool m_hasReset;
// Whether fix the local relative image src in read mode when copied.
bool m_fixImageSrcInWebWhenCopied;
// Styles to be removed when copied in read mode.
QStringList m_stylesToRemoveWhenCopied;
// The string containing styles to inline when copied in edit mode.
QString m_stylesToInlineWhenCopied;
@ -1998,18 +1995,27 @@ inline bool VConfigManager::getCloseBeforeExternalEditor() const
return m_closeBeforeExternalEditor;
}
inline bool VConfigManager::getFixImageSrcInWebWhenCopied() const
inline QStringList VConfigManager::getStylesToRemoveWhenCopied() const
{
return m_fixImageSrcInWebWhenCopied;
}
return getConfigFromSettings("web",
"styles_to_remove_when_copied").toStringList();
inline const QStringList &VConfigManager::getStylesToRemoveWhenCopied() const
{
return m_stylesToRemoveWhenCopied;
}
inline const QString &VConfigManager::getStylesToInlineWhenCopied() const
{
return m_stylesToInlineWhenCopied;
}
inline QStringList VConfigManager::getCopyTargets() const
{
return getConfigFromSettings("web",
"copy_targets").toStringList();
}
inline QString VConfigManager::getStyleOfSpanForMark() const
{
return getConfigFromSettings("web",
"style_of_span_for_mark").toString();
}
#endif // VCONFIGMANAGER_H

View File

@ -36,14 +36,16 @@
#include "utils/viconutils.h"
#include "dialog/vtipsdialog.h"
VMainWindow *g_mainWin;
extern VConfigManager *g_config;
extern VPalette *g_palette;
VMainWindow *g_mainWin;
VNote *g_vnote;
VWebUtils *g_webUtils;
const int VMainWindow::c_sharedMemTimerInterval = 1000;
#if defined(QT_NO_DEBUG)
@ -62,9 +64,13 @@ VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent)
g_mainWin = this;
setWindowIcon(QIcon(":/resources/icons/vnote.ico"));
vnote = new VNote(this);
g_vnote = vnote;
m_webUtils.init();
g_webUtils = &m_webUtils;
if (g_config->getEnableCompactMode()) {
m_panelViewState = PanelViewState::CompactMode;
} else {

View File

@ -8,6 +8,7 @@
#include <QString>
#include "vfile.h"
#include "vedittab.h"
#include "utils/vwebutils.h"
class QLabel;
class QComboBox;
@ -376,6 +377,8 @@ private:
// Whether user request VNote to quit.
bool m_requestQuit;
VWebUtils m_webUtils;
// Interval of the shared memory timer in ms.
static const int c_sharedMemTimerInterval;
};

View File

@ -20,6 +20,9 @@
#include "vpreviewmanager.h"
#include "utils/viconutils.h"
#include "dialog/vcopytextashtmldialog.h"
#include "utils/vwebutils.h"
extern VWebUtils *g_webUtils;
extern VConfigManager *g_config;
@ -275,12 +278,7 @@ void VMdEditor::contextMenuEvent(QContextMenuEvent *p_event)
const QList<QAction *> actions = menu->actions();
if (textCursor().hasSelection()) {
QAction *copyAsHtmlAct = new QAction(tr("Copy As &HTML without Background"), menu);
copyAsHtmlAct->setToolTip(tr("Copy selected contents as HTML without background styles"));
connect(copyAsHtmlAct, &QAction::triggered,
this, &VMdEditor::handleCopyAsHtmlAction);
menu->insertAction(actions.isEmpty() ? NULL : actions[0], copyAsHtmlAct);
initCopyAsMenu(actions.isEmpty() ? NULL : actions.last(), menu);
} else {
QAction *saveExitAct = new QAction(VIconUtils::menuIcon(":/resources/icons/save_exit.svg"),
tr("&Save Changes And Read"),
@ -1042,7 +1040,7 @@ void VMdEditor::updateInitAndInsertedImages(bool p_fileChanged, UpdateAction p_a
}
}
void VMdEditor::handleCopyAsHtmlAction()
void VMdEditor::handleCopyAsAction(QAction *p_act)
{
QTextCursor cursor = textCursor();
Q_ASSERT(cursor.hasSelection());
@ -1051,7 +1049,7 @@ void VMdEditor::handleCopyAsHtmlAction()
Q_ASSERT(!text.isEmpty());
Q_ASSERT(!m_textToHtmlDialog);
m_textToHtmlDialog = new VCopyTextAsHtmlDialog(text, this);
m_textToHtmlDialog = new VCopyTextAsHtmlDialog(text, p_act->data().toString(), this);
// For Hoedown, we use marked.js to convert the text to have a general interface.
emit requestTextToHtml(text);
@ -1129,3 +1127,31 @@ void VMdEditor::zoomPage(bool p_zoomIn, int p_range)
m_mdHighlighter->rehighlight();
}
void VMdEditor::initCopyAsMenu(QAction *p_before, QMenu *p_menu)
{
QStringList targets = g_webUtils->getCopyTargetsName();
if (targets.isEmpty()) {
return;
}
QMenu *subMenu = new QMenu(tr("Copy HTML As"), p_menu);
subMenu->setToolTipsVisible(true);
for (auto const & target : targets) {
QAction *act = new QAction(target, subMenu);
act->setData(target);
act->setToolTip(tr("Copy selected content as HTML using rules specified by target %1").arg(target));
subMenu->addAction(act);
}
connect(subMenu, &QMenu::triggered,
this, &VMdEditor::handleCopyAsAction);
QAction *menuAct = p_menu->insertMenu(p_before, subMenu);
if (p_before) {
p_menu->removeAction(p_before);
p_menu->insertAction(menuAct, p_before);
p_menu->insertSeparator(menuAct);
}
}

View File

@ -211,7 +211,7 @@ private slots:
void updateCurrentHeader();
// Copy selected text as HTML.
void handleCopyAsHtmlAction();
void handleCopyAsAction(QAction *p_act);
private:
// Update the config of VTextEdit according to global configurations.
@ -232,6 +232,8 @@ private:
// We need to maintain the styles font size.
void zoomPage(bool p_zoomIn, int p_range = 1);
void initCopyAsMenu(QAction *p_before, QMenu *p_menu);
HGMarkdownHighlighter *m_mdHighlighter;
VCodeBlockHighlightHelper *m_cbHighlighter;

View File

@ -19,6 +19,8 @@
extern VConfigManager *g_config;
extern VWebUtils *g_webUtils;
// We set the property of the clipboard to mark that the URL copied in the
// clipboard has been altered.
static const QString c_ClipboardPropertyMark = "CopiedImageURLAltered";
@ -27,8 +29,6 @@ VWebView::VWebView(VFile *p_file, QWidget *p_parent)
: QWebEngineView(p_parent),
m_file(p_file),
m_copyImageUrlActionHooked(false),
m_needRemoveBackground(false),
m_fixImgSrc(g_config->getFixImageSrcInWebWhenCopied()),
m_afterCopyImage(false)
{
setAcceptDrops(false);
@ -75,16 +75,10 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
}
}
// Add Copy without Background action.
// Add Copy As menu.
QAction *copyAct = pageAction(QWebEnginePage::Copy);
if (actions.contains(copyAct)) {
QAction *copyWithoutBgAct = new QAction(tr("Copy &without Background"), menu);
copyWithoutBgAct->setToolTip(tr("Copy selected content without background styles"));
connect(copyWithoutBgAct, &QAction::triggered,
this, &VWebView::handleCopyWithoutBackgroundAction);
menu->insertAction(copyAct, copyWithoutBgAct);
menu->removeAction(copyAct);
menu->insertAction(copyWithoutBgAct, copyAct);
initCopyAsMenu(copyAct, menu);
}
// We need to replace the "Copy Image" action:
@ -101,14 +95,8 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
defaultCopyImageAct->setVisible(false);
}
// Add Copy All without Background action.
QAction *copyAllWithoutBgAct = new QAction(tr("Copy &All without Background"), menu);
copyAllWithoutBgAct->setToolTip(tr("Copy all contents without background styles"));
connect(copyAllWithoutBgAct, &QAction::triggered,
this, &VWebView::handleCopyAllWithoutBackgroundAction);
// Add it to the back.
menu->addSeparator();
menu->addAction(copyAllWithoutBgAct);
// Add Copy All As menu.
initCopyAllAsMenu(menu);
hideUnusedActions(menu);
@ -267,27 +255,10 @@ bool VWebView::removeStyles(QString &p_html)
return changed;
}
void VWebView::handleCopyWithoutBackgroundAction()
{
m_needRemoveBackground = true;
triggerPageAction(QWebEnginePage::Copy);
}
void VWebView::handleCopyAllWithoutBackgroundAction()
{
triggerPageAction(QWebEnginePage::SelectAll);
m_needRemoveBackground = true;
triggerPageAction(QWebEnginePage::Copy);
triggerPageAction(QWebEnginePage::Unselect);
}
void VWebView::handleClipboardChanged(QClipboard::Mode p_mode)
{
bool removeBackground = m_needRemoveBackground;
m_needRemoveBackground = false;
QString copyTarget = m_copyTarget;
m_copyTarget.clear();
bool afterCopyImage = m_afterCopyImage;
m_afterCopyImage = false;
@ -306,49 +277,22 @@ void VWebView::handleClipboardChanged(QClipboard::Mode p_mode)
if (afterCopyImage) {
removeHtmlFromImageData(clipboard, mimeData);
} else {
alterHtmlMimeData(clipboard, mimeData, removeBackground);
alterHtmlMimeData(clipboard, mimeData, copyTarget);
}
}
void VWebView::alterHtmlMimeData(QClipboard *p_clipboard,
const QMimeData *p_mimeData,
bool p_removeBackground)
const QString &p_copyTarget)
{
if (!p_mimeData->hasHtml() || p_mimeData->hasImage()) {
if (!p_mimeData->hasHtml()
|| p_mimeData->hasImage()
|| p_copyTarget.isEmpty()) {
return;
}
bool altered = false;
QString html = p_mimeData->html();
// Add surrounded tags.
if (!html.startsWith("<html>")) {
altered = true;
html = QString("<html><body>%1</body></html>").arg(html);
}
// Remove background color.
if (p_removeBackground) {
if (VWebUtils::removeBackgroundColor(html)) {
altered = true;
}
if (VWebUtils::translateColors(html)) {
altered = true;
}
}
// Fix local relative images.
if (m_fixImgSrc && VWebUtils::fixImageSrcInHtml(url(), html)) {
altered = true;
}
// Fix margin and padding.
if (removeStyles(html)) {
altered = true;
}
if (!altered) {
if (!g_webUtils->alterHtmlAsTarget(url(), html, p_copyTarget)) {
return;
}
@ -374,3 +318,79 @@ void VWebView::removeHtmlFromImageData(QClipboard *p_clipboard,
VClipboardUtils::setMimeDataToClipboard(p_clipboard, data, QClipboard::Clipboard);
}
}
void VWebView::initCopyAsMenu(QAction *p_after, QMenu *p_menu)
{
QStringList targets = g_webUtils->getCopyTargetsName();
if (targets.isEmpty()) {
return;
}
QMenu *subMenu = new QMenu(tr("Copy As"), p_menu);
subMenu->setToolTipsVisible(true);
for (auto const & target : targets) {
QAction *act = new QAction(target, subMenu);
act->setData(target);
act->setToolTip(tr("Copy selected content using rules specified by target %1").arg(target));
subMenu->addAction(act);
}
connect(subMenu, &QMenu::triggered,
this, &VWebView::handleCopyAsAction);
QAction *menuAct = p_menu->insertMenu(p_after, subMenu);
p_menu->removeAction(p_after);
p_menu->insertAction(menuAct, p_after);
}
void VWebView::handleCopyAsAction(QAction *p_act)
{
if (!p_act) {
return;
}
m_copyTarget = p_act->data().toString();
triggerPageAction(QWebEnginePage::Copy);
}
void VWebView::initCopyAllAsMenu(QMenu *p_menu)
{
QStringList targets = g_webUtils->getCopyTargetsName();
if (targets.isEmpty()) {
return;
}
QMenu *subMenu = new QMenu(tr("Copy All As"), p_menu);
subMenu->setToolTipsVisible(true);
for (auto const & target : targets) {
QAction *act = new QAction(target, subMenu);
act->setData(target);
act->setToolTip(tr("Copy all content using rules specified by target %1").arg(target));
subMenu->addAction(act);
}
connect(subMenu, &QMenu::triggered,
this, &VWebView::handleCopyAllAsAction);
p_menu->addSeparator();
p_menu->addMenu(subMenu);
}
void VWebView::handleCopyAllAsAction(QAction *p_act)
{
if (!p_act) {
return;
}
triggerPageAction(QWebEnginePage::SelectAll);
m_copyTarget = p_act->data().toString();
triggerPageAction(QWebEnginePage::Copy);
triggerPageAction(QWebEnginePage::Unselect);
}

View File

@ -26,9 +26,9 @@ private slots:
void handleCopyImageUrlAction();
void handleCopyWithoutBackgroundAction();
void handleCopyAsAction(QAction *p_act);
void handleCopyAllWithoutBackgroundAction();
void handleCopyAllAsAction(QAction *p_act);
// Copy the clicked image.
// Used to replace the default CopyImageToClipboard action.
@ -41,24 +41,27 @@ private:
void alterHtmlMimeData(QClipboard *p_clipboard,
const QMimeData *p_mimeData,
bool p_removeBackground);
const QString &p_copyTarget);
void removeHtmlFromImageData(QClipboard *p_clipboard,
const QMimeData *p_mimeData);
bool removeStyles(QString &p_html);
void initCopyAsMenu(QAction *p_after, QMenu *p_menu);
void initCopyAllAsMenu(QMenu *p_menu);
VFile *m_file;
// Whether this view has hooked the Copy Image Url action.
bool m_copyImageUrlActionHooked;
bool m_needRemoveBackground;
bool m_fixImgSrc;
// Whether it is after copy image action.
bool m_afterCopyImage;
// Target of Copy As.
QString m_copyTarget;
};
#endif // VWEBVIEW_H