diff --git a/src/dialog/vcopytextashtmldialog.cpp b/src/dialog/vcopytextashtmldialog.cpp index 5ba12fec..46ab0026 100644 --- a/src/dialog/vcopytextashtmldialog.cpp +++ b/src/dialog/vcopytextashtmldialog.cpp @@ -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("
%1").arg(p_html); - m_htmlViewer->setHtml(html, p_baseUrl); + QString html = p_html; + m_htmlViewer->setHtml("" + 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(); diff --git a/src/dialog/vcopytextashtmldialog.h b/src/dialog/vcopytextashtmldialog.h index 93d935b1..a4bad341 100644 --- a/src/dialog/vcopytextashtmldialog.h +++ b/src/dialog/vcopytextashtmldialog.h @@ -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 diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index 05f32c98..0279e6ed 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -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 to +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 +; 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 with that of its child
+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".
diff --git a/src/utils/vwebutils.cpp b/src/utils/vwebutils.cpp
index 98d14c44..2cdeb335 100644
--- a/src/utils/vwebutils.cpp
+++ b/src/utils/vwebutils.cpp
@@ -5,14 +5,79 @@
#include
#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;
+ }
+
+ return names;
}
-bool VWebUtils::translateColors(QString &p_html)
+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(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("")) {
+ p_html = "" + p_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 :.
- QString newStr = QString("%1color : %2;").arg(colorReg.cap(1)).arg(newCol);
+ // 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) {
+ // without "style".
+ QString newTag = QString("").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("").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 with
.
+ p_html.replace("", "");
+ }
+
+ 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 .
+ 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) {
+ // 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;
+}
diff --git a/src/utils/vwebutils.h b/src/utils/vwebutils.h
index 289e7680..e66c89a1 100644
--- a/src/utils/vwebutils.h
+++ b/src/utils/vwebutils.h
@@ -3,22 +3,114 @@
#include
#include
+#include
+#include
+#include
class VWebUtils
{
public:
- // Fix
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 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
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 to .
+ bool transformMarkToSpan(QString &p_html);
+
+ // Replace the background color of with that of its child .
+ bool replacePreBackgroundColorWithCode(QString &p_html);
+
+ VWebUtils::HtmlTag readNextTag(const QString &p_html, int p_pos);
+
+ QVector m_copyTargets;
+
+ // Custom styles to remove when copied.
+ QStringList m_stylesToRemoveWhenCopied;
+
+ // Style of which is transformed from .
+ 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
diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp
index 0e6956c9..7dd44d84 100644
--- a/src/vconfigmanager.cpp
+++ b/src/vconfigmanager.cpp
@@ -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(",");
}
diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h
index cb1a7b80..40a3cb84 100644
--- a/src/vconfigmanager.h
+++ b/src/vconfigmanager.h
@@ -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 §ion, 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
diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp
index eabd386e..158af2b3 100644
--- a/src/vmainwindow.cpp
+++ b/src/vmainwindow.cpp
@@ -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 {
diff --git a/src/vmainwindow.h b/src/vmainwindow.h
index 990a6c98..5a33cfb5 100644
--- a/src/vmainwindow.h
+++ b/src/vmainwindow.h
@@ -8,6 +8,7 @@
#include
#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;
};
diff --git a/src/vmdeditor.cpp b/src/vmdeditor.cpp
index 338e6e51..318c6386 100644
--- a/src/vmdeditor.cpp
+++ b/src/vmdeditor.cpp
@@ -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 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);
+ }
+}
diff --git a/src/vmdeditor.h b/src/vmdeditor.h
index 76428bc1..fbac42d2 100644
--- a/src/vmdeditor.h
+++ b/src/vmdeditor.h
@@ -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;
diff --git a/src/vwebview.cpp b/src/vwebview.cpp
index f047be56..3be1df13 100644
--- a/src/vwebview.cpp
+++ b/src/vwebview.cpp
@@ -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("")) {
- altered = true;
- html = QString("%1").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);
+}
+
diff --git a/src/vwebview.h b/src/vwebview.h
index 453e912f..2db5b046 100644
--- a/src/vwebview.h
+++ b/src/vwebview.h
@@ -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