Merge branch 'master' of github.com:martinrotter/rssguard

This commit is contained in:
Martin Rotter 2022-01-01 20:11:02 +01:00
commit 0d046d6b20
55 changed files with 2393 additions and 775 deletions

3
.gitignore vendored
View File

@ -24,4 +24,5 @@
*.autosave
*.user*
localization/*qm
*.TMP
*.TMP
resources/skins/*/*.map

View File

@ -229,11 +229,11 @@ version by clicking this popup notification.</source>
<name>ColorToolButton</name>
<message>
<source>Click me to change color!</source>
<translation type="unfinished"/>
<translation>Click me to change colour!</translation>
</message>
<message>
<source>Select new color</source>
<translation type="unfinished"/>
<translation>Select new colour</translation>
</message>
</context>
<context>
@ -3607,7 +3607,7 @@ List of supported readers:</source>
</message>
<message>
<source>OK-ish color</source>
<translation type="unfinished"/>
<translation>OK-ish colour</translation>
</message>
</context>
<context>
@ -4446,7 +4446,7 @@ Authors of this application are NOT responsible for lost data.</source>
</message>
<message>
<source>Fetch color from activated skin</source>
<translation type="unfinished"/>
<translation>Fetch colour from activated skin</translation>
</message>
</context>
<context>

File diff suppressed because it is too large Load Diff

View File

@ -82,6 +82,10 @@ win32 {
# Additionally link against Shell32.
LIBS *= Shell32.lib
static {
LIBS *= -lodbc32
}
}
static {

View File

@ -11,7 +11,7 @@ APP_URL = "https://github.com/martinrotter/rssguard"
APP_URL_ISSUES = "https://github.com/martinrotter/rssguard/issues"
APP_URL_ISSUES_NEW = "https://github.com/martinrotter/rssguard/issues/new"
APP_URL_DOCUMENTATION = "https://github.com/martinrotter/rssguard/blob/master/resources/docs/Documentation.md"
APP_USERAGENT = "RSS Guard/$$APP_VERSION (github.com/martinrotter/rssguard)"
APP_USERAGENT = "RSS Guard/$$APP_VERSION"
APP_DONATE_URL = "https://martinrotter.github.io/donate"
message($$MSG_PREFIX: Welcome RSS Guard qmake script.)

View File

@ -26,7 +26,7 @@
<url type="donation">https://github.com/sponsors/martinrotter</url>
<content_rating type="oars-1.1" />
<releases>
<release version="4.0.4" date="2021-11-26"/>
<release version="4.0.4" date="2021-12-22"/>
</releases>
<content_rating type="oars-1.0">
<content_attribute id="violence-cartoon">none</content_attribute>

View File

@ -167,6 +167,7 @@ Note that `MessageObject` attributes which can be synchronized with service are
| Method | `hostname()` | `String` | `utils.hostname()` | Returns name of your PC.
| Method | `fromXmlToJson(String)` | `String` | `utils.fromXmlToJson('<h1>hello</h1>')` | Converts `XML` string into `JSON`.
| Method | `parseDateTime(String)` | `Date` | `utils.parseDateTime('2020-02-24T08:00:00')` | Converts textual date/time representation into proper `Date` object.
| Method | `runExecutableGetOutput(String, String[])` | `String` | `utils.runExecutableGetOutput('cmd.exe', ['/c', 'dir'])` | Launches external executable with optional parameters, reads its standard output and returns the output when executable finishes.
#### Examples
Accept only messages/articles from "Bob", while also mark them "important":
@ -399,12 +400,15 @@ RSS Guard is distributed in two variants:
If you're not sure which version to use, **use the WebEngine-based RSS Guard**.
#### AdBlock
#### AdBlock <a id="adbl"></a>
[Web-based variant](#webb) of RSS Guard offers ad-blocking functionality via [Adblocker](https://github.com/cliqz-oss/adblocker). Adblocker offers similar performance to [uBlock Origin](https://github.com/gorhill/uBlock).
You need to have have [Node.js](https://nodejs.org) with [NPM](https://www.npmjs.com) (which is usually included in Node.js installer) installed to have ad-blocking in RSS Guard working. Also, the implementation requires additional [npm](https://www.npmjs.com) modules to be installed. You see the list of needed modules near the top of [this](https://github.com/martinrotter/rssguard/blob/master/resources/scripts/adblock/adblock-server.js) file.
If you want to enable AdBlock in RSS Guard you need to do this:
I understand that the above installation of needed dependencies is not trivial, but it is necessary evil to have up-to-date and modern implementation of AdBlock in RSS Guard. Previous, "C++"-based, implementation was buggy, quite slow, and hard to maintain.
1. Have [Node.js](https://nodejs.org) with [NPM](https://www.npmjs.com) (which is usually included in Node.js installer) installed. Also you need to have paths `node.exe` and `npm` added to your system `PATH` environment available.
2. The implementation requires additional [npm](https://www.npmjs.com) modules to be installed. You see the list of needed modules near the top of [this](https://github.com/martinrotter/rssguard/blob/master/resources/scripts/adblock/adblock-server.js) file.
I understand that the above installation is not trivial, but it is necessary evil to have up-to-date and modern implementation of AdBlock in RSS Guard. Previous, `C++`-based, implementation was buggy, slow, and hard to maintain.
You can find elaborate lists of AdBlock rules [here](https://easylist.to). You can just copy direct hyperlinks to those lists and paste them into the "Filter lists" text-box as shown below. Remember to always separate individual links with newlines. Same applies to "Custom filters", where you can insert individual filters, for example [filter](https://adblockplus.org/filter-cheatsheet) "idnes" to block all URLs with "idnes" in them.

View File

@ -24,6 +24,8 @@
<file>./graphics/Breeze/actions/22/edit-clear.svg</file>
<file>./graphics/Breeze/actions/22/edit-copy.svg</file>
<file>./graphics/Breeze/actions/32/edit-reset.svg</file>
<file>./graphics/Breeze/actions/22/edit-select-all.svg</file>
<file>./graphics/Breeze/actions/22/edit-select-none.svg</file>
<file>./graphics/Breeze/places/96/folder.svg</file>
<file>./graphics/Breeze/actions/22/format-indent-more.svg</file>
<file>./graphics/Breeze/actions/22/format-justify-fill.svg</file>
@ -89,6 +91,8 @@
<file>./graphics/Breeze Dark/actions/22/edit-clear.svg</file>
<file>./graphics/Breeze Dark/actions/22/edit-copy.svg</file>
<file>./graphics/Breeze Dark/actions/32/edit-reset.svg</file>
<file>./graphics/Breeze Dark/actions/22/edit-select-all.svg</file>
<file>./graphics/Breeze Dark/actions/22/edit-select-none.svg</file>
<file>./graphics/Breeze Dark/places/96/folder.svg</file>
<file>./graphics/Breeze Dark/actions/22/format-indent-more.svg</file>
<file>./graphics/Breeze Dark/actions/22/format-justify-fill.svg</file>
@ -153,6 +157,7 @@
<file>./graphics/Faenza/actions/64/down.png</file>
<file>./graphics/Faenza/actions/64/edit-clear.png</file>
<file>./graphics/Faenza/actions/64/edit-copy.png</file>
<file>./graphics/Faenza/actions/64/edit-select-all.png</file>
<file>./graphics/Faenza/emblems/64/emblem-downloads.png</file>
<file>./graphics/Faenza/emblems/64/emblem-system.png</file>
<file>./graphics/Faenza/places/64/folder.png</file>
@ -223,6 +228,7 @@
<file>./graphics/Numix/22/actions/download.svg</file>
<file>./graphics/Numix/22/actions/edit-clear.svg</file>
<file>./graphics/Numix/22/actions/edit-copy.svg</file>
<file>./graphics/Numix/22/actions/edit-select-all.svg</file>
<file>./graphics/Numix/22/emblems/emblem-downloads.svg</file>
<file>./graphics/Numix/22/emblems/emblem-system.svg</file>
<file>./graphics/Numix/22/places/folder.svg</file>

View File

@ -81,13 +81,20 @@
<file>skins/vergilius/metadata.xml</file>
<file>skins/vergilius/qt_style.qss</file>
<file>skins/nudus/html_adblocked.html</file>
<file>skins/nudus/html_enclosure_every.html</file>
<file>skins/nudus/html_enclosure_image.html</file>
<file>skins/nudus/html_single_message.html</file>
<file>skins/nudus/html_wrapper.html</file>
<file>skins/nudus/metadata.xml</file>
<file>skins/nudus/qt_style.qss</file>
<file>skins/nudus-base/html_adblocked.html</file>
<file>skins/nudus-base/html_enclosure_every.html</file>
<file>skins/nudus-base/html_enclosure_image.html</file>
<file>skins/nudus-base/html_single_message.html</file>
<file>skins/nudus-base/html_wrapper.html</file>
<file>skins/nudus-dark/html_wrapper.html</file>
<file>skins/nudus-dark/html_style.css</file>
<file>skins/nudus-dark/metadata.xml</file>
<file>skins/nudus-dark/qt_style.qss</file>
<file>skins/nudus-light/html_style.css</file>
<file>skins/nudus-light/metadata.xml</file>
<file>skins/nudus-light/qt_style.qss</file>
<file>initial_feeds/feeds-en.opml</file>

View File

@ -19,7 +19,7 @@
const fs = require('fs');
const tldts = require('tldts-experimental');
const adblock = require('@cliqz/adblocker')
const adblock = require('@cliqz/adblocker');
const http = require('http');
const cluster = require('cluster');

View File

@ -1,55 +0,0 @@
# Downloads full articles for RSS 2.0 feed and replaces original articles.
#
# Make sure to have all dependencies installed:
# pip3 install asyncio (if using parallel version of the script)
#
# You must provide raw RSS 2.0 UTF-8 feed XML data as input, for example with curl:
# curl 'http://rss.cnn.com/rss/edition.rss' | python ./scrape-rss2.py "4"
#
# You must provide three command line arguments:
# scrape-rss2.py [NUMBER-OF-PARALLEL-THREADS]
import json
import re
import sys
import time
import html
import urllib.request
import distutils.util
import xml.etree.ElementTree as ET
no_threads = int(sys.argv[1])
if no_threads > 1:
import asyncio
from concurrent.futures import ThreadPoolExecutor
sys.stdin.reconfigure(encoding='utf-8')
rss_data = sys.stdin.read()
rss_document = ET.fromstring(rss_data)
def process_article(article):
try:
link = "https://us-central1-technews-251304.cloudfunctions.net/article-parser?url=" + article.find("link").text
response = urllib.request.urlopen(link)
text = response.read().decode("utf-8")
js = json.loads(text)
if int(js["error"]) == 0:
article.find("description").text = js["data"]["content"]
except:
pass
# Scrape articles.
if no_threads > 1:
with ThreadPoolExecutor(max_workers = no_threads) as executor:
futures = []
for article in rss_document.findall(".//item"):
futures.append(executor.submit(process_article, article))
for future in futures:
future.result()
else:
for article in rss_document.findall(".//item"):
process_article(article)
print(ET.tostring(rss_document, encoding = "unicode"))

View File

@ -0,0 +1,96 @@
# Downloads full (HTML) articles for ATOM or RSS 2.0 feed and replaces original articles.
#
# Make sure to have all dependencies installed:
# pip3 install asyncio (if using parallel version of the script)
#
# You must provide raw ATOM or RSS 2.0 UTF-8 feed XML data as input, for example with curl:
# curl 'http://rss.cnn.com/rss/edition.rss' | python ./scrape-full-articles.py "4"
#
# You must provide three command line arguments:
# scrape-full-articles.py [NUMBER-OF-PARALLEL-THREADS]
import json
import sys
import urllib.request
import xml.etree.ElementTree as ET
# Globals.
atom_ns = {"atom": "http://www.w3.org/2005/Atom"}
article_parser_url = "https://us-central1-technews-251304.cloudfunctions.net/article-parser?url="
# Methods.
def process_article(article, is_rss, is_atom):
try:
# Extract link.
scraped_article = ""
if is_rss:
article_link = article.find("link").text
elif is_atom:
article_link = article.find("atom:link", atom_ns).attrib['href']
# Scrape with article-parser.
link = article_parser_url + article_link
response = urllib.request.urlopen(link)
text = response.read().decode("utf-8")
js = json.loads(text)
if int(js["error"]) == 0:
scraped_article = js["data"]["content"]
# Save scraped data.
if scraped_article:
if is_rss:
article.find("description").text = scraped_article
elif is_atom:
article.find("atom:content", atom_ns).text = scraped_article
except:
pass
def main():
no_threads = int(sys.argv[1]) if len(sys.argv) >= 2 else 1
if no_threads > 1:
import asyncio
from concurrent.futures import ThreadPoolExecutor
sys.stdin.reconfigure(encoding="utf-8")
#feed_data = urllib.request.urlopen("https://dilbert.com/feed").read()
feed_data = sys.stdin.read()
feed_document = ET.fromstring(feed_data)
# Determine feed type.
is_rss = feed_document.tag == "rss"
is_atom = feed_document.tag == "{http://www.w3.org/2005/Atom}feed"
if not is_rss and not is_atom:
sys.exit("Passed file is neither ATOM nor RSS 2.0 feed.")
# Extract articles.
if is_rss:
feed_articles = feed_document.findall(".//item")
elif is_atom:
feed_articles = feed_document.findall(".//atom:entry", atom_ns)
# Scrape articles.
if no_threads > 1:
with ThreadPoolExecutor(max_workers=no_threads) as executor:
futures = []
for article in feed_articles:
futures.append(
executor.submit(process_article, article, is_rss, is_atom))
for future in futures:
future.result()
else:
for article in feed_articles:
process_article(article, is_rss, is_atom)
print(ET.tostring(feed_document, encoding="unicode"))
if __name__ == '__main__':
main()

View File

@ -1,56 +0,0 @@
# Downloads full articles for RSS 2.0 feed and replaces original articles.
#
# Make sure to have all dependencies installed:
# pip3 install newspaper3k
# pip3 install asyncio (if using parallel version of the script)
#
# You must provide raw RSS 2.0 UTF-8 feed XML data as input, for example with curl:
# curl 'http://rss.cnn.com/rss/edition.rss' | python ./scrape-rss2.py "4"
#
# You must provide three command line arguments:
# scrape-rss2.py [NUMBER-OF-PARALLEL-THREADS]
import json
import re
import sys
import time
import html
import requests
import distutils.util
import xml.etree.ElementTree as ET
from newspaper import Article
no_threads = int(sys.argv[1])
if no_threads > 1:
import asyncio
from concurrent.futures import ThreadPoolExecutor
sys.stdin.reconfigure(encoding='utf-8')
rss_data = sys.stdin.read()
rss_document = ET.fromstring(rss_data)
def process_article(article):
try:
link = article.find("link").text
f = Article(link, keep_article_html = True)
f.download()
f.parse()
article.find("description").text = f.article_html
except:
pass
# Scrape articles.
if no_threads > 1:
with ThreadPoolExecutor(max_workers = no_threads) as executor:
futures = []
for article in rss_document.findall(".//item"):
futures.append(executor.submit(process_article, article))
for future in futures:
future.result()
else:
for article in rss_document.findall(".//item"):
process_article(article)
print(ET.tostring(rss_document, encoding = "unicode"))

View File

@ -1,16 +1,16 @@
#!/bin/bash
# Transka executable.
TRANSKA=./transka
TRANSKA="$(dirname "$0")/transka/transka"
# Get credentials.
read -p "Username: " USERNAME
read -e -p "Username: " -i "martinrotter" USERNAME
read -p "Password: " PASSWORD
# Setup parameters.
RESOURCE=../../../localization/rssguard_en.ts
RESOURCE="./localization/rssguard_en.ts"
CODES="cs da de en_GB es fi fr gl he id it ja lt nl pl pt_BR pt_PT ru sv uk zh_CN zh_TW"
TRANSLATION='../../../localization/rssguard_$CODE.ts'
TRANSLATION='./localization/rssguard_$CODE.ts'
declare PARAMS
@ -20,6 +20,4 @@ for CODE in $CODES; do
PARAMS+="-dt "$CODE" "$(eval echo $TRANSLATION)" "
done
cd ./transka
$TRANSKA $PARAMS
$TRANSKA $PARAMS

View File

@ -0,0 +1 @@
&#32;&#47;&nbsp;<a class="menc" href="%1"><span style="display:none"><!-- Cannot be removed -->%2</span><span style="text-transform: uppercase;">%3</span></a>

View File

@ -0,0 +1,16 @@
<div class="rssguard-mwrapper" dir="auto" id="%8">
<section class="rssguard-mhead">
<div style="float: right; margin: 10px;"><!-- Should it remain here??? -->%7</div>
<span class="msmall">%2</span>
<h1>%1<span class="mlinks">%6<span class="mwrapurl"><a href="%3">URL</a><span>&nbsp;&#47;&nbsp;</span></span></h1>
<span class="msmall">%5</span>
</section>
<hr>
<div class="rssguard-mbody">
%4
</div>
</div>

View File

@ -0,0 +1,432 @@
// ___________________________
// < I'm an expert in my field. >
// ---------------------------
// \ ^__^
// \ (oo)\_______
// (__)\ )\/\
// ||----w |
// || ||
//
// Variables
//
$base-unit: 10px !default;
//
// Styling
//
// Let the font be customised via RSS Guard settings
// Note: Font size there related **only** to that font alone, it is
// not absolute, and nothing else can be done from my side
// E.g. "Roboto 10" <-- something like this is send from RSS Guard side
* {
font-family: inherit;
}
//
// Reset some basic elements
//
body, h1, h2, h3, h4, h5, h6,
p, blockquote, pre, hr,
dl, dd, ol, ul, figure {
margin: 0;
padding: 0;
}
//
// Add some basic styling
//
body {
background-color: $cbg00;
box-sizing: border-box;
color: $cfg00;
//cursor: default;
-webkit-text-size-adjust: 100%;
-webkit-font-feature-settings: "kern" 1;
font-feature-settings: "kern" 1;
font-kerning: normal;
min-height: 100vh;
}
::selection {
background-color: $clink;
text-shadow: none;
}
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure,
details {
margin-bottom: $base-unit;
}
hr {
background-color: $cbor2;
border: none;
display: block;
height: 2px;
margin: $base-unit 0;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 600 !important;
}
h1 { font-size: 1.25rem !important; }
h2 { font-size: 1.20rem !important; }
h3 { font-size: 1.15rem !important; }
h4 { font-size: 1.1rem !important; }
h5 { font-size: 1rem !important; }
h6 { font-size: .95rem !important; }
b {
font-weight: bold !important;
}
i {
font-style: italic !important;
}
strong {
font-weight: 800 !important;
}
em {
font-style: oblique !important;
}
mark {
background-color: $cmark;
}
sub,
sup {
font-size: .8rem !important;
}
small {
font-size: .9rem !important;
}
abbr {
cursor: help;
font-style: italic !important;
font-weight: 100 !important;
}
q {
font-style: italic !important;
&::before {
content: '';
}
&::after {
content: '';
}
}
time {
font-weight: 450 !important;
}
var {
font-style: oblique !important;
font-weight: 500 !important;
}
a {
color: $clink;
&:hover {
text-decoration: none;
}
&:focus {
box-shadow: none;
outline: none;
}
}
cite {
font-style: italic !important;
font-weight: bold !important;
}
figure > img {
display: block;
}
figcaption {
font-size: .8rem !important;
}
blockquote {
border-left: .3em solid $cbor2;
margin-left: 0;
padding: 0 $base-unit;
&,
p {
color: $cfg11;
}
}
pre,
code {
border: 1px solid $cbor3;
border-radius: $radius-unit;
color: $cfg10;
// cursor: text;
}
code {
background-color: $ccode;
padding: 0 .25em;
word-break: break-word;
}
pre {
background-color: $ccodeblock;
overflow-x: auto;
padding: 7px 13px;
tab-size: 2;
// For <pre style='white-space:pre-wrap;'>
white-space: pre !important;
// For <pre style='white-space:pre-wrap;width:81ex'>
width: unset !important;
> code {
background-color: unset;
border: none;
color: unset;
padding-right: 0;
padding-left: 0;
tab-size: 2;
}
}
kbd {
background: $ccode;
border: 1px solid $cbor3;
border-bottom: 3px solid darken($cbor3, 3%);
border-radius: $radius-unit;
box-shadow:
0 2px 4px darken($cbg00, 6%),
inset 0 1px $cbg00
;
font-size: .9rem !important;
padding: .1em .4em .2em .4em;
}
select {
background-color: $ccodeblock;
border: 1px solid $cbor3;
border-radius: $radius-unit;
color: $cfg00;
padding: .04em .25em;
// Do not use max-width here
width: 100%;
&:focus {
box-shadow: none;
outline: none;
background-color: $cbg00;
}
> option {
background-color: $cbg00;
}
}
table {
border-collapse: collapse;
// `!important` is set to override something like <table width="900px">
width: 100% !important;
}
// Return this if something goes wrong, and return the JS override for dark theme
//table,
//th,
//td {
// color: $cfg00;
//}
li {
display: list-item;
}
ul,
ol {
padding-left: 1.5em;
}
ul {
list-style-type: disc;
li ul {
list-style-type: square;
}
}
ol {
list-style-type: decimal;
li ol {
list-style-type: lower-roman;
}
}
img {
// Let the width be defined (see .rssguard-mbody img), but keep aspect ratio
height: auto;
// `width auto` creates many problems even if set as a fallback
//width: auto;
}
details {
border: 1px solid $ccodeblock;
border-radius: $radius-unit;
padding: .5em .5em 0;
> summary {
background-color: $ccodeblock;
border-radius: $radius-unit * 0.9;
cursor: pointer;
margin: -.5em -.5em 0;
padding-left: .5em;
&:focus {
box-shadow: none;
outline: none;
}
}
& *:last-child {
margin-bottom: 0;
}
}
details[open] {
border-color: $cbor3;
padding: .5em;
> summary {
border-bottom: 1px solid $cbor3;
border-radius: ($radius-unit * 0.9) ($radius-unit * 0.9) 0 0;
margin-bottom: .5em;
}
}
iframe {
max-width: 100%;
height: auto;
width: auto;
}
a,
select,
summary {
&:focus {
background-color: $cbor3;
text-shadow: 0 -1px $cbg00;
}
}
:target {
outline: 1px solid $clink;
}
// m* == message*
body:hover .rssguard-mwrapper .rssguard-mhead .mwrapurl {
a,
span {
visibility: visible;
}
}
.rssguard-mwrapper .rssguard-mhead .mwrapurl a:focus {
&,
& + span {
visibility: visible !important;
}
}
.rssguard-mwrapper {
padding: $base-unit !important;
.rssguard-mhead {
.msmall,
.mlinks {
opacity: .8;
}
> h1 {
margin: 0;
}
.msmall {
font-size: .9em;
}
.mlinks {
.menc {
word-break: break-word;
}
.mwrapurl {
display: inline-flex;
a {
order: 1;
}
a,
span {
visibility: hidden;
}
}
}
}
.rssguard-mbody img {
// Needs to be `!important` when max-width is defined by image style
// <img src="https://....png" alt="alt" style="max-width: 100%;">
max-width: 450px !important;
// For cases when they both are set
max-height: unset !important;
@media only screen and (max-width: 800px) {
// `!important` to override `!important` that is set above
max-width: 100% !important;
}
}
}
//
// Other
//
// For articles without any html elements;
// If not applied to _all_, *must* be applied to links in mbody
// mbody == article body
.rssguard-mbody {
word-break: break-word;
}
// Fix at least some mess produced by above
table {
word-break: normal;
}

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>%1</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
%style%
</style>
</head>
<body>
%2
</body>
</html>

View File

@ -0,0 +1,505 @@
@charset "UTF-8";
* {
font-family: inherit;
}
body, h1, h2, h3, h4, h5, h6,
p, blockquote, pre, hr,
dl, dd, ol, ul, figure {
margin: 0;
padding: 0;
}
body {
background-color: #373A3D;
box-sizing: border-box;
color: #f5f5f5;
-webkit-text-size-adjust: 100%;
-webkit-font-feature-settings: "kern" 1;
font-feature-settings: "kern" 1;
font-kerning: normal;
min-height: 100vh;
}
::selection {
background-color: #8291AD;
text-shadow: none;
}
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure,
details {
margin-bottom: 10px;
}
hr {
background-color: #545556;
border: none;
display: block;
height: 2px;
margin: 10px 0;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 600 !important;
}
h1 {
font-size: 1.25rem !important;
}
h2 {
font-size: 1.20rem !important;
}
h3 {
font-size: 1.15rem !important;
}
h4 {
font-size: 1.1rem !important;
}
h5 {
font-size: 1rem !important;
}
h6 {
font-size: .95rem !important;
}
b {
font-weight: bold !important;
}
i {
font-style: italic !important;
}
strong {
font-weight: 800 !important;
}
em {
font-style: oblique !important;
}
mark {
background-color: #f8d08c66;
}
sub,
sup {
font-size: .8rem !important;
}
small {
font-size: .9rem !important;
}
abbr {
cursor: help;
font-style: italic !important;
font-weight: 100 !important;
}
q {
font-style: italic !important;
}
q::before {
content: '“';
}
q::after {
content: '”';
}
time {
font-weight: 450 !important;
}
var {
font-style: oblique !important;
font-weight: 500 !important;
}
a {
color: #8291AD;
}
a:hover {
text-decoration: none;
}
a:focus {
box-shadow: none;
outline: none;
}
cite {
font-style: italic !important;
font-weight: bold !important;
}
figure > img {
display: block;
}
figcaption {
font-size: .8rem !important;
}
blockquote {
border-left: 0.3em solid #545556;
margin-left: 0;
padding: 0 10px;
}
blockquote,
blockquote p {
color: #D8D8D8;
}
pre,
code {
border: 1px solid #282a2c;
border-radius: 0.3em;
color: #D8D8D8;
}
code {
background-color: rgba(33, 35, 39, 0.4);
padding: 0 .25em;
word-break: break-word;
}
pre {
background-color: rgba(33, 35, 39, 0.4);
overflow-x: auto;
padding: 7px 13px;
tab-size: 2;
white-space: pre !important;
width: unset !important;
}
pre > code {
background-color: unset;
border: none;
color: unset;
padding-right: 0;
padding-left: 0;
tab-size: 2;
}
kbd {
background: rgba(33, 35, 39, 0.4);
border: 1px solid #282a2c;
border-bottom: 3px solid #212224;
border-radius: 0.3em;
box-shadow: 0 2px 4px #282b2d, inset 0 1px #373A3D;
font-size: .9rem !important;
padding: .1em .4em .2em .4em;
}
select {
background-color: rgba(33, 35, 39, 0.4);
border: 1px solid #282a2c;
border-radius: 0.3em;
color: #f5f5f5;
padding: .04em .25em;
width: 100%;
}
select:focus {
box-shadow: none;
outline: none;
background-color: #373A3D;
}
select > option {
background-color: #373A3D;
}
table {
border-collapse: collapse;
width: 100% !important;
}
li {
display: list-item;
}
ul,
ol {
padding-left: 1.5em;
}
ul {
list-style-type: disc;
}
ul li ul {
list-style-type: square;
}
ol {
list-style-type: decimal;
}
ol li ol {
list-style-type: lower-roman;
}
img {
height: auto;
}
details {
border: 1px solid rgba(33, 35, 39, 0.4);
border-radius: 0.3em;
padding: .5em .5em 0;
}
details > summary {
background-color: rgba(33, 35, 39, 0.4);
border-radius: 0.27em;
cursor: pointer;
margin: -.5em -.5em 0;
padding-left: .5em;
}
details > summary:focus {
box-shadow: none;
outline: none;
}
details *:last-child {
margin-bottom: 0;
}
details[open] {
border-color: #282a2c;
padding: .5em;
}
details[open] > summary {
border-bottom: 1px solid #282a2c;
border-radius: 0.27em 0.27em 0 0;
margin-bottom: .5em;
}
iframe {
max-width: 100%;
height: auto;
width: auto;
}
a:focus,
select:focus,
summary:focus {
background-color: #282a2c;
text-shadow: 0 -1px #373A3D;
}
:target {
outline: 1px solid #8291AD;
}
body:hover .rssguard-mwrapper .rssguard-mhead .mwrapurl a,
body:hover .rssguard-mwrapper .rssguard-mhead .mwrapurl span {
visibility: visible;
}
.rssguard-mwrapper .rssguard-mhead .mwrapurl a:focus, .rssguard-mwrapper .rssguard-mhead .mwrapurl a:focus + span {
visibility: visible !important;
}
.rssguard-mwrapper {
padding: 10px !important;
}
.rssguard-mwrapper .rssguard-mhead .msmall,
.rssguard-mwrapper .rssguard-mhead .mlinks {
opacity: .8;
}
.rssguard-mwrapper .rssguard-mhead > h1 {
margin: 0;
}
.rssguard-mwrapper .rssguard-mhead .msmall {
font-size: .9em;
}
.rssguard-mwrapper .rssguard-mhead .mlinks .menc {
word-break: break-word;
}
.rssguard-mwrapper .rssguard-mhead .mlinks .mwrapurl {
display: inline-flex;
}
.rssguard-mwrapper .rssguard-mhead .mlinks .mwrapurl a {
order: 1;
}
.rssguard-mwrapper .rssguard-mhead .mlinks .mwrapurl a,
.rssguard-mwrapper .rssguard-mhead .mlinks .mwrapurl span {
visibility: hidden;
}
.rssguard-mwrapper .rssguard-mbody img {
max-width: 450px !important;
max-height: unset !important;
}
@media only screen and (max-width: 800px) {
.rssguard-mwrapper .rssguard-mbody img {
max-width: 100% !important;
}
}
.rssguard-mbody {
word-break: break-word;
}
table {
word-break: normal;
}
html::before,
html::after,
body::before,
body::after {
content: "";
background-color: #282a2c;
display: block;
position: fixed;
z-index: 5;
}
html::before {
height: 1px;
left: 0;
right: 0;
top: 0;
}
html::after {
width: 1px;
top: 0;
right: 0;
bottom: 0;
}
body::before {
height: 1px;
right: 0;
bottom: 0;
left: 0;
}
body::after {
width: 1px;
top: 0;
bottom: 0;
left: 0;
}
::-webkit-scrollbar {
height: 13px;
width: 14px;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
background-color: #37393c;
box-shadow: inset 1px 1px #323437;
}
::-webkit-scrollbar-corner {
border-radius: 0 0 0.3em 0;
}
::-webkit-scrollbar-thumb {
box-shadow: inset 1px 1px #565a5f, inset -1px -1px #565a5f, inset 0px 1px #565a5f, inset 0px -1px #565a5f, inset 1px 0px #565a5f, inset 1px -1px #565a5f, inset -1px 0px #565a5f, inset -1px 1px #565a5f;
}
::-webkit-scrollbar-thumb:horizontal {
background-image: linear-gradient(#414347 5%, #3c3e42);
min-width: 25px;
}
::-webkit-scrollbar-thumb:horizontal:hover {
background-image: linear-gradient(#43464a 25%, #3c3e42);
}
::-webkit-scrollbar-thumb:vertical {
background-image: linear-gradient(to right, #414347 5%, #3c3e42);
min-height: 25px;
}
::-webkit-scrollbar-thumb:vertical:hover {
background-image: linear-gradient(to right, #43464a 25%, #3c3e42);
}
::-webkit-scrollbar-thumb:active {
background-image: linear-gradient(#3c3e42, #3c3e42) !important;
}
:not(body)::-webkit-scrollbar-thumb:horizontal {
box-shadow: inset 1px 1px #565a5f, inset -1px -1px #565a5f, inset 0px 1px #565a5f, inset 0px -1px #565a5f, inset 1px 0px #565a5f, inset 1px -1px #565a5f, inset -1px 0px #565a5f, inset -1px 1px #565a5f, 1px 0px #282a2c, 1px 1px #282a2c, -1px 1px #282a2c, -1px 0px #282a2c;
}
:not(body)::-webkit-scrollbar-thumb:vertical {
box-shadow: inset 1px 1px #565a5f, inset -1px -1px #565a5f, inset 0px 1px #565a5f, inset 0px -1px #565a5f, inset 1px 0px #565a5f, inset 1px -1px #565a5f, inset -1px 0px #565a5f, inset -1px 1px #565a5f, 0px -1px #282a2c, 1px -1px #282a2c, 1px 1px #282a2c, 0px 1px #282a2c;
}
::-webkit-scrollbar-thumb:horizontal,
::-webkit-scrollbar-track:horizontal {
border-top: 1px solid #282a2c;
}
::-webkit-scrollbar-thumb:vertical,
::-webkit-scrollbar-track:vertical {
border-left: 1px solid #282a2c;
}
body::-webkit-scrollbar-thumb:horizontal, body::-webkit-scrollbar-thumb:vertical,
body::-webkit-scrollbar-track:horizontal,
body::-webkit-scrollbar-track:vertical {
border: 1px solid #282a2c;
}
body::-webkit-scrollbar-thumb:horizontal,
body::-webkit-scrollbar-track:horizontal {
border-top: none;
}
body::-webkit-scrollbar-thumb:vertical,
body::-webkit-scrollbar-track:vertical {
border-left: none;
}
body::-webkit-scrollbar-corner {
border: 1px solid #282a2c;
border-top: none;
border-left: none;
}
::-webkit-scrollbar-track:corner-present:horizontal,
::-webkit-scrollbar-thumb:corner-present:horizontal {
border-radius: 0 0 0 0.3em;
}
::-webkit-scrollbar-track:corner-present:vertical,
::-webkit-scrollbar-thumb:corner-present:vertical {
border-radius: 0 0.3em 0 0;
}
::-webkit-scrollbar-track:horizontal,
::-webkit-scrollbar-thumb:horizontal {
border-radius: 0 0 0.3em 0.3em;
}
::-webkit-scrollbar-track:vertical,
::-webkit-scrollbar-thumb:vertical {
border-radius: 0 0.3em 0.3em 0;
}
/* Please enable JS for additional font-colouring features */
:root {
--rssguard-red: 0;
--rssguard-green: 0;
--rssguard-blue: 0;
--rssguard-threshold: 0.5;
}
:root {
--rssguard-r: calc(var(--rssguard-red) * 0.2126);
--rssguard-g: calc(var(--rssguard-green) * 0.7152);
--rssguard-b: calc(var(--rssguard-blue) * 0.0722);
--rssguard-sum:
calc(
var(--rssguard-r) +
var(--rssguard-g) +
var(--rssguard-b)
);
--rssguard-perceived-lightness: calc(var(--rssguard-sum) / 255);
}
body,
::selection,
mark, code, pre, pre > code,
blockquote {
color: hsla(0, 0%, calc( ( var(--rssguard-perceived-lightness) - var(--rssguard-threshold) ) * -10000000% ), 0.9);
}
/*# sourceMappingURL=html_style.css.map */

View File

@ -0,0 +1,313 @@
@charset "utf-8";
$qtbg-base: #373A3D !default; // clr_basbg
$qtbg-button: #323437 !default; // clr_altbg // button bg (scrollbar, alt bg)
$qcselbg: #8291AD !default; // clr_selbg
//
// Emulate fusion colour processing (dark only)
//
//
// Scrollbar colours
//
//$qcbgbg: lighten($qtbg-button, 6%); // See toolbar bg
$bgscroll: lighten($qtbg-button, 2%) !default; // track and corner bg
$tr-border: darken($qtbg-button, 4%) !default; // track brdr
//
// Scrollbar thumb
//
// bg gradient
// Normal
$thscrlin: lighten($qtbg-button, 6%) !default;
$thscrlout: lighten($qtbg-button, 4%) !default;
// Hover
$thscrlhvin: lighten($qtbg-button, 7%) !default;
$thscrlhvout: $thscrlout;
// Light outline
$th-border: lighten($qtbg-button, 15%) !default;
//
// HTML palette (Colours)
//
$cbg00: $qtbg-base;
// Irrelevant, because fg is overridden by the switcher ~~~
$cfg00: #f5f5f5 !default;
$cfg10: #D8D8D8 !default;
$cfg11: $cfg10;
// ~~~
$cbor2: #545556 !default;
$ccodeblock: rgba(33, 35, 39, 0.4) !default;
$ccode: $ccodeblock;
$cbor3: $tr-border;
$cmark: #f8d08c66 !default; // 40% transparency
$clink: $qcselbg;
//
// Other
//
$radius-unit: .3em !default;
@import
"../nudus-base/html_style_base"
;
//
// Dark HTML-style has following additions:
//
//
// Border around viewport
// https://csswizardry.com/2010/12/simplified-page-borders-in-pure-css/
//
html::before,
html::after,
body::before,
body::after {
content: "";
background-color: $tr-border;
display: block;
position: fixed;
z-index: 5;
}
html::before {
height: 1px;
left: 0;
right: 0;
top: 0;
}
html::after {
width: 1px;
top: 0;
right: 0;
bottom: 0;
}
body::before {
height: 1px;
right: 0;
bottom: 0;
left: 0;
}
body::after {
width: 1px;
top: 0;
bottom: 0;
left: 0;
}
//
// Enhanced scrollbar
//
::-webkit-scrollbar {
height: 13px;
width: 14px;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
background-color: $bgscroll;
box-shadow: inset 1px 1px lighten($tr-border, 4%);
}
// Part where vertical and horizontal scrollbars meet
::-webkit-scrollbar-corner {
border-radius: 0 0 $radius-unit 0;
}
// TODO: Can be simplified to @include and function
$th-outline:
inset 1px 1px $th-border,
inset -1px -1px $th-border,
inset 0px 1px $th-border,
inset 0px -1px $th-border,
inset 1px 0px $th-border,
inset 1px -1px $th-border,
inset -1px 0px $th-border,
inset -1px 1px $th-border
;
::-webkit-scrollbar-thumb {
$th-min-unit: 25px;
box-shadow: $th-outline;
&:horizontal {
background-image: linear-gradient($thscrlin 5%, $thscrlout);
min-width: $th-min-unit;
&:hover {
background-image: linear-gradient($thscrlhvin 25%, $thscrlhvout);
}
}
&:vertical {
background-image: linear-gradient(to right, $thscrlin 5%, $thscrlout);
min-height: $th-min-unit;
&:hover {
background-image: linear-gradient(to right, $thscrlhvin 25%, $thscrlhvout);
}
}
&:active {
background-image: linear-gradient($thscrlout, $thscrlout) !important;
}
}
// Light and dark borders to outline the thumb
// Clockwise (x y)
:not(body)::-webkit-scrollbar-thumb {
&:horizontal {
box-shadow:
$th-outline,
1px 0px $tr-border,
1px 1px $tr-border,
-1px 1px $tr-border,
-1px 0px $tr-border
;
}
&:vertical {
box-shadow:
$th-outline,
0px -1px $tr-border,
1px -1px $tr-border,
1px 1px $tr-border,
0px 1px $tr-border
;
}
}
::-webkit-scrollbar-thumb,
::-webkit-scrollbar-track {
&:horizontal {
border-top: 1px solid $tr-border;
}
&:vertical {
border-left: 1px solid $tr-border;
}
}
// More complete borders for `body` scrollbar
body::-webkit-scrollbar-thumb,
body::-webkit-scrollbar-track {
&:horizontal,
&:vertical {
border: 1px solid $tr-border;
}
&:horizontal {
border-top: none;
}
&:vertical {
border-left: none;
}
}
body::-webkit-scrollbar-corner {
border: 1px solid $tr-border;
border-top: none;
border-left: none;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-thumb {
&:corner-present {
&:horizontal {
border-radius: 0 0 0 $radius-unit;
}
&:vertical {
border-radius: 0 $radius-unit 0 0;
}
}
&:horizontal {
border-radius: 0 0 $radius-unit $radius-unit;
}
&:vertical {
border-radius: 0 $radius-unit $radius-unit 0;
}
}
//
// Font colour switcher
// Thank you so much!!
// https://css-tricks.com/switch-font-color-for-different-backgrounds-with-css/
//
/* Please enable JS for additional font-colouring features */
:root {
// Default RGB values for background colour
--rssguard-red: 0;
--rssguard-green: 0;
--rssguard-blue: 0;
// The threshold at which colours are considered "light
// Range: decimals from 0 to 1, recommended 0.5 - 0.6
--rssguard-threshold: 0.5;
}
:root {
// Calculates perceived lightness using the sRGB Luma method
// Luma = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
--rssguard-r: calc(var(--rssguard-red) * 0.2126);
--rssguard-g: calc(var(--rssguard-green) * 0.7152);
--rssguard-b: calc(var(--rssguard-blue) * 0.0722);
--rssguard-sum:
calc(
var(--rssguard-r) +
var(--rssguard-g) +
var(--rssguard-b)
);
--rssguard-perceived-lightness: calc(var(--rssguard-sum) / 255);
}
// Shows either white or black colour depending on perceived lightness
body,
::selection,
mark, code, pre, pre > code,
blockquote {
color:
hsla(
0,
0%,
calc(
(
var(--rssguard-perceived-lightness) -
var(--rssguard-threshold)
) *
-10000000%
),
.9
);
}

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>%1</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
%style%
</style>
</head>
<body>
%2
<script>
const rssGuardroot = document.documentElement;
rssGuardBody = document.body;
let rssGuardColor = window.getComputedStyle(rssGuardBody).backgroundColor;
rssGuardMatch = rssGuardColor.match(/(\w+)\((\d+), (\d+), (\d+)\)/);
if (rssGuardMatch != null) {
rssGuardroot.style.setProperty('--rssguard-red', rssGuardMatch[2]);
rssGuardroot.style.setProperty('--rssguard-green', rssGuardMatch[3]);
rssGuardroot.style.setProperty('--rssguard-blue', rssGuardMatch[4]);
}
</script>
</body>
</html>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<skin version="0.1" base="nudus-base">
<author>
<name>akinokonomi</name>
</author>
<palette>
<color key="FgInteresting" >#85ACF6</color>
<color key="FgSelectedInteresting" >#D9E3F7</color>
<color key="FgError" >#DF5656</color>
<color key="FgSelectedError" >#910303</color>
<color key="Allright" >#44AA44</color>
</palette>
</skin>

View File

@ -0,0 +1,32 @@
QListWidget,
QScrollArea {
border: 1px solid palette(dark);
}
QPlainTextEdit:focus {
border: 1px solid palette(highlight);
}
QToolTip {
background-color: palette(window);
border: 1px solid palette(dark);
border-radius: 2px;
}
/* TODO: Fine for now, may be improved in future */
QProgressBar {
background-color: palette(highlight);
color: palette(window);
}
QSplitter::handle {
background: palette(dark);
}
/*
* For `qt5-styleplugins`: motif, cde, gtk2, etc.
*/
QStatusBar::item {
border: none;
}

View File

@ -0,0 +1,353 @@
@charset "UTF-8";
* {
font-family: inherit;
}
body, h1, h2, h3, h4, h5, h6,
p, blockquote, pre, hr,
dl, dd, ol, ul, figure {
margin: 0;
padding: 0;
}
body {
background-color: #FBFBFB;
box-sizing: border-box;
color: #000000;
-webkit-text-size-adjust: 100%;
-webkit-font-feature-settings: "kern" 1;
font-feature-settings: "kern" 1;
font-kerning: normal;
min-height: 100vh;
}
::selection {
background-color: #5D88D2;
text-shadow: none;
}
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure,
details {
margin-bottom: 10px;
}
hr {
background-color: #CFCFCF;
border: none;
display: block;
height: 2px;
margin: 10px 0;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 600 !important;
}
h1 {
font-size: 1.25rem !important;
}
h2 {
font-size: 1.20rem !important;
}
h3 {
font-size: 1.15rem !important;
}
h4 {
font-size: 1.1rem !important;
}
h5 {
font-size: 1rem !important;
}
h6 {
font-size: .95rem !important;
}
b {
font-weight: bold !important;
}
i {
font-style: italic !important;
}
strong {
font-weight: 800 !important;
}
em {
font-style: oblique !important;
}
mark {
background-color: #FFECCC;
}
sub,
sup {
font-size: .8rem !important;
}
small {
font-size: .9rem !important;
}
abbr {
cursor: help;
font-style: italic !important;
font-weight: 100 !important;
}
q {
font-style: italic !important;
}
q::before {
content: '“';
}
q::after {
content: '”';
}
time {
font-weight: 450 !important;
}
var {
font-style: oblique !important;
font-weight: 500 !important;
}
a {
color: #5D88D2;
}
a:hover {
text-decoration: none;
}
a:focus {
box-shadow: none;
outline: none;
}
cite {
font-style: italic !important;
font-weight: bold !important;
}
figure > img {
display: block;
}
figcaption {
font-size: .8rem !important;
}
blockquote {
border-left: 0.3em solid #CFCFCF;
margin-left: 0;
padding: 0 10px;
}
blockquote,
blockquote p {
color: #343434;
}
pre,
code {
border: 1px solid #DEDEDE;
border-radius: 0.1em;
color: #343434;
}
code {
background-color: #F1F1F1;
padding: 0 .25em;
word-break: break-word;
}
pre {
background-color: #F1F1F1;
overflow-x: auto;
padding: 7px 13px;
tab-size: 2;
white-space: pre !important;
width: unset !important;
}
pre > code {
background-color: unset;
border: none;
color: unset;
padding-right: 0;
padding-left: 0;
tab-size: 2;
}
kbd {
background: #F1F1F1;
border: 1px solid #DEDEDE;
border-bottom: 3px solid #d6d6d6;
border-radius: 0.1em;
box-shadow: 0 2px 4px #ececec, inset 0 1px #FBFBFB;
font-size: .9rem !important;
padding: .1em .4em .2em .4em;
}
select {
background-color: #F1F1F1;
border: 1px solid #DEDEDE;
border-radius: 0.1em;
color: #000000;
padding: .04em .25em;
width: 100%;
}
select:focus {
box-shadow: none;
outline: none;
background-color: #FBFBFB;
}
select > option {
background-color: #FBFBFB;
}
table {
border-collapse: collapse;
width: 100% !important;
}
li {
display: list-item;
}
ul,
ol {
padding-left: 1.5em;
}
ul {
list-style-type: disc;
}
ul li ul {
list-style-type: square;
}
ol {
list-style-type: decimal;
}
ol li ol {
list-style-type: lower-roman;
}
img {
height: auto;
}
details {
border: 1px solid #F1F1F1;
border-radius: 0.1em;
padding: .5em .5em 0;
}
details > summary {
background-color: #F1F1F1;
border-radius: 0.09em;
cursor: pointer;
margin: -.5em -.5em 0;
padding-left: .5em;
}
details > summary:focus {
box-shadow: none;
outline: none;
}
details *:last-child {
margin-bottom: 0;
}
details[open] {
border-color: #DEDEDE;
padding: .5em;
}
details[open] > summary {
border-bottom: 1px solid #DEDEDE;
border-radius: 0.09em 0.09em 0 0;
margin-bottom: .5em;
}
iframe {
max-width: 100%;
height: auto;
width: auto;
}
a:focus,
select:focus,
summary:focus {
background-color: #DEDEDE;
text-shadow: 0 -1px #FBFBFB;
}
:target {
outline: 1px solid #5D88D2;
}
body:hover .rssguard-mwrapper .rssguard-mhead .mwrapurl a,
body:hover .rssguard-mwrapper .rssguard-mhead .mwrapurl span {
visibility: visible;
}
.rssguard-mwrapper .rssguard-mhead .mwrapurl a:focus, .rssguard-mwrapper .rssguard-mhead .mwrapurl a:focus + span {
visibility: visible !important;
}
.rssguard-mwrapper {
padding: 10px !important;
}
.rssguard-mwrapper .rssguard-mhead .msmall,
.rssguard-mwrapper .rssguard-mhead .mlinks {
opacity: .8;
}
.rssguard-mwrapper .rssguard-mhead > h1 {
margin: 0;
}
.rssguard-mwrapper .rssguard-mhead .msmall {
font-size: .9em;
}
.rssguard-mwrapper .rssguard-mhead .mlinks .menc {
word-break: break-word;
}
.rssguard-mwrapper .rssguard-mhead .mlinks .mwrapurl {
display: inline-flex;
}
.rssguard-mwrapper .rssguard-mhead .mlinks .mwrapurl a {
order: 1;
}
.rssguard-mwrapper .rssguard-mhead .mlinks .mwrapurl a,
.rssguard-mwrapper .rssguard-mhead .mlinks .mwrapurl span {
visibility: hidden;
}
.rssguard-mwrapper .rssguard-mbody img {
max-width: 450px !important;
max-height: unset !important;
}
@media only screen and (max-width: 800px) {
.rssguard-mwrapper .rssguard-mbody img {
max-width: 100% !important;
}
}
.rssguard-mbody {
word-break: break-word;
}
table {
word-break: normal;
}
::selection {
color: #F1F1F1;
}
/*# sourceMappingURL=html_style.css.map */

View File

@ -0,0 +1,39 @@
@charset "utf-8";
//
// Colours
//
$cbg00: #FBFBFB !default; // Background // Qt5 fusion bg light toolbar-grey
$cfg00: #000000 !default;
//$cfg10: #A23542 !default; // TODO: fg for code
$cfg11: #343434 !default; // Lighter fg for blockquote
$cfg10: $cfg11;
$cbor2: #CFCFCF !default; // hr and blockquote border
$ccodeblock: #F1F1F1 !default; // bg for `pre > code` and `details > summ`
$ccode: $ccodeblock !default; // bg for `code`
$cbor3: #DEDEDE !default; // code/pre border
$cmark: #FFECCC !default;
$clink: #5D88D2 !default; // Else use steelblue
//
// Other
//
$radius-unit: .1em !default;
@import
"../nudus-base/html_style_base"
;
//
// Light style has following additions:
//
::selection {
color: #F1F1F1;
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<skin version="0.1" base="nudus-base">
<author>
<name>akinokonomi</name>
</author>
<palette>
<color key="FgInteresting" >#3A6FE4</color>
<color key="FgSelectedInteresting" >#F0F2FC</color>
<color key="FgError" >#E74343</color>
<color key="FgSelectedError" >#FFD7D7</color>
<color key="Allright" >#77dd77</color>
</palette>
</skin>

View File

@ -0,0 +1,14 @@
QPlainTextEdit:focus {
border: 1px solid palette(highlight);
}
QSplitter::handle {
background: palette(dark);
}
/*
* For `qt5-styleplugins`: motif, cde, gtk2, etc.
*/
QStatusBar::item {
border: none;
}

View File

@ -1 +0,0 @@
&#32;&#47;&nbsp;<a class="rssguard-menc" href="%1"><span style="display:none"><!-- Cannot be removed -->%2</span><span style="text-transform: uppercase;">%3</span></a>

View File

@ -1,12 +0,0 @@
<div class="rssguard-mwrapper" dir="auto">
<section class="rssguard-mhead">
<div style="float: right; margin: 10px;"><!-- Should it remain here??? -->%7</div>
<span class="rssguard-msmall">%2</span>
<h1>%1<span class="rssguard-mlinks">%6<span class="rssguard-murl">&nbsp;&#47;&nbsp;<a href="%3">URL</a></span></span></h1>
<span class="rssguard-msmall">%5</span>
</section>
<hr>
<div class="rssguard-mbody">
%4
</div>
</div>

View File

@ -1,287 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<!--
___________________________
< I'm an expert in my field. >
---------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
--rssguard-base-unit: 10px;
--rssguard-em-unit: .3em;
/* Qt5 fusion border grey */
--rssguard-grey01: #B8B8B8;
/* Some other grey */
--rssguard-grey02: #CFCFCF;
--rssguard-grey10: #343434;
--rssguard-grey20: #F1F1F1;
}
article, aside, details, div, dt,
figcaption, footer, form, header,
hgroup, html, main, nav, section, summary {
display: block;
}
body, h1, h2, h3, h4, h5, h6,
p, blockquote, pre, hr,
dl, dd, ol, ul, figure {
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure {
margin-bottom: var(--rssguard-base-unit);
}
body {
/*
* Another Qt grey;
* bg required for "newspaper mode"
*/
background-color: #FBFBFB;
box-sizing: border-box;
cursor: default;
font-kerning: normal;
/* redundant? */
-webkit-font-feature-settings: "kern" 1;
font-feature-settings: "kern" 1;
-webkit-text-size-adjust: 100%;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 600 !important;
}
h1 { font-size: 1.25em !important; }
h2 { font-size: 1.20em !important; }
h3 { font-size: 1.15em !important; }
h4 { font-size: 1.1em !important; }
h5 { font-size: 1em !important; }
h6 { font-size: .95em !important; }
b {
font-weight: bold !important;
}
i {
font-style: italic !important;
}
strong {
font-weight: 800 !important;
}
em {
font-style: oblique !important;
}
mark {
background-color: #FFECCC;
}
sub,
sup {
font-size: .8em !important;
}
small {
font-size: .9em !important;
}
q {
font-style: italic !important;
}
q::before {
content: '“';
}
q::after {
content: '”';
}
a {
color: steelblue;
}
a:hover {
text-decoration: none;
}
cite {
font-style: italic !important;
font-weight: bold !important;
}
figure > img {
display: block;
}
figcaption {
font-size: .8em !important;
}
blockquote {
color: var(--rssguard-grey10);
border-left: var(--rssguard-em-unit) solid var(--rssguard-grey02);
margin-left: 0;
padding: 0 var(--rssguard-base-unit);
}
pre,
code {
background-color: var(--rssguard-grey20);
cursor: text;
}
pre {
--rssguard-horiz: 13px;
border-radius: var(--rssguard-em-unit);
overflow-x: auto;
padding: 7px var(--rssguard-horiz);
white-space: pre !important;
width: calc(100wv - calc(var(--rssguard-horiz) * 2)) !important;
}
code {
border-radius: var(--rssguard-em-unit);
color: var(--rssguard-grey10);
padding: 0 .25em;
word-break: break-word;
}
pre code {
display: block;
padding: 0;
tab-size: 4;
}
table {
border-collapse: collapse;
width: 100%;
}
table,
th,
td {
border: 1px solid var(--rssguard-grey01);
}
li {
display: list-item;
}
ul,
ol {
padding-left: 1.5em;
}
ul {
display: block;
list-style-type: circle;
}
ul li ul {
list-style-type: square;
}
ol {
display: block;
list-style-type: decimal;
}
ol li ol {
list-style-type: lower-roman;
}
img {
/* redundant? */
display: inline-block;
height: auto;
width: auto;
}
details > summary {
border-radius: var(--rssguard-em-unit);
cursor: pointer;
}
details > summary:hover,
details[open] > summary {
background-color: var(--rssguard-grey20);
}
details > summary:focus {
box-shadow: none;
outline: none;
}
hr {
background-color: var(--rssguard-grey02);
border: none;
display: block;
height: 2px;
margin: var(--rssguard-base-unit) 0;
}
iframe {
max-width: 100%;
height: auto;
width: auto;
}
select {
max-width: 100%;
}
.rssguard-mwrapper {
padding: var(--rssguard-base-unit) !important;
}
.rssguard-mhead .rssguard-msmall,
.rssguard-mhead .rssguard-mlinks {
opacity: .8;
}
.rssguard-mhead h1 {
margin: 0;
}
.rssguard-msmall {
font-size: .9em;
}
.rssguard-mlinks a.rssguard-menc {
word-break: break-word;
}
.rssguard-murl {
visibility: hidden;
}
body:hover .rssguard-mwrapper .rssguard-murl {
visibility: visible;
}
@media only screen and (max-width: 800px) {
.rssguard-mbody img {
max-width: 100% !important;
}
}
.rssguard-mbody img {
max-width: 450px;
}
.rssguard-mbody {
/*
* For articles without html elements;
* Not sure if I want to apply this to *all*;
* otherwise *must* be applied to links in mbody
*/
word-break: break-word;
}
/* Fix at least some mess produced by above */
table {
word-break: normal;
}
</style>
<style>
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
font: inherit;
font-size: 100%;
}
html {
height: 100vh;
}
body {
height: 100vh;
}
</style>
<title>%1</title>
</head>
<body>
%2
</body>
</html>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<skin version="0.01">
<author>
<name>Maki Blackwell</name>
</author>
<palette>
<color key="FgInteresting">#3A4EE4</color>
<color key="FgSelectedInteresting">#ff66cc</color>
<color key="FgError">#4EE43A</color>
<color key="FgSelectedError">#ff99ff</color>
<color key="Allright">#00ff99</color>
</palette>
</skin>

View File

@ -1,11 +0,0 @@
QTextEdit {
selection-background-color: #4861f0;
}
QStatusBar::item {
border: none;
}
QSplitter::handle {
background: rgba(117, 117, 117, 0.5);
}

View File

@ -1,4 +1,4 @@
<div class="card filled fluid" dir="auto">
<div class="card filled fluid" dir="auto" id="%8">
<h5 class="doc section">%1 <a class="button small primary" href="%3">🔗URL</a> %6</h5>
<div class="doc section">
<div style="text-align: center;">

View File

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<skin version="0.24">
<skin version="0.25">
<author>
<name>Martin Rotter</name>
</author>
<palette>
<color key="FgInteresting">#3A4EE4</color>
<color key="FgSelectedInteresting">#ff66cc</color>
<color key="FgError">#4EE43A</color>
<color key="FgSelectedError">#ff99ff</color>
<color key="Allright">#00ff99</color>
<color key="FgInteresting" >#3A4EE4</color>
<color key="FgSelectedInteresting" >#F0F2FC</color>
<color key="FgError" >#DF5656</color>
<color key="FgSelectedError" >#FFD7D7</color>
<color key="Allright" >#77dd77</color>
</palette>
</skin>

View File

@ -198,7 +198,15 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right
bool FeedsProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const {
bool should_show = filterAcceptsRowInternal(source_row, source_parent);
qDebugNN << LOGSEC_CORE
<< "Filter accepts row"
<< QUOTE_W_SPACE(m_sourceModel->itemForIndex(m_sourceModel->index(source_row, 0, source_parent))->title())
<< "and filter result is:"
<< QUOTE_W_SPACE_DOT(should_show);
if (should_show && m_hiddenIndices.contains(QPair<int, QModelIndex>(source_row, source_parent))) {
qDebugNN << LOGSEC_CORE << "Item was previously hidden and now shows up, expand.";
const_cast<FeedsProxyModel*>(this)->m_hiddenIndices.removeAll(QPair<int, QModelIndex>(source_row, source_parent));
// Load status.
@ -213,10 +221,6 @@ bool FeedsProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source
}
bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const {
if (!m_showUnreadOnly) {
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
const QModelIndex idx = m_sourceModel->index(source_row, 0, source_parent);
if (!idx.isValid()) {
@ -225,18 +229,23 @@ bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex
const RootItem* item = m_sourceModel->itemForIndex(idx);
if (item->kind() != RootItem::Kind::Category && item->kind() != RootItem::Kind::Feed) {
if (item->kind() != RootItem::Kind::Category &&
item->kind() != RootItem::Kind::Feed &&
item->kind() != RootItem::Kind::Label) {
// Some items are always visible.
return true;
}
else if (item->isParentOf(m_selectedItem) /* || item->isChildOf(m_selectedItem)*/ || m_selectedItem == item) {
// Currently selected item and all its parents and children must be displayed.
return true;
if (!m_showUnreadOnly) {
// Take only regexp filtering into account.
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
else {
// NOTE: If item has < 0 of unread message it may mean, that the count
// of unread messages is not (yet) known, display that item too.
return item->countOfUnreadMessages() != 0;
return
item->countOfUnreadMessages() != 0 &&
QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
}

View File

@ -3,12 +3,14 @@
#include "core/filterutils.h"
#include "definitions/definitions.h"
#include "miscellaneous/iofactory.h"
#include "miscellaneous/textfactory.h"
#include <QDomDocument>
#include <QHostInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QProcess>
FilterUtils::FilterUtils(QObject* parent) : QObject(parent) {}
@ -84,3 +86,7 @@ QString FilterUtils::fromXmlToJson(const QString& xml) const {
QDateTime FilterUtils::parseDateTime(const QString& dat) const {
return TextFactory::parseDateTime(dat);
}
QString FilterUtils::runExecutableGetOutput(const QString& executable, const QStringList& arguments) const {
return IOFactory::startProcessGetOutput(executable, arguments);
}

View File

@ -22,6 +22,7 @@ class FilterUtils : public QObject {
// Parses string into date/time object.
Q_INVOKABLE QDateTime parseDateTime(const QString& dat) const;
Q_INVOKABLE QString runExecutableGetOutput(const QString& executable, const QStringList& arguments = {}) const;
};
#endif // FILTERUTILS_H

View File

@ -431,7 +431,9 @@ bool DatabaseQueries::purgeReadMessages(const QSqlDatabase& db) {
bool DatabaseQueries::purgeOldMessages(const QSqlDatabase& db, int older_than_days) {
QSqlQuery q(db);
const qint64 since_epoch = QDateTime::currentDateTimeUtc().addDays(-older_than_days).toMSecsSinceEpoch();
const qint64 since_epoch = older_than_days == 0
? QDateTime::currentDateTimeUtc().addYears(10).toMSecsSinceEpoch()
: QDateTime::currentDateTimeUtc().addDays(-older_than_days).toMSecsSinceEpoch();
q.setForwardOnly(true);
q.prepare(QSL("DELETE FROM Messages WHERE is_important = :is_important AND date_created < :date_created;"));

View File

@ -143,6 +143,12 @@
#define DEFAULT_ZOOM_FACTOR 1.0f
#define ZOOM_FACTOR_STEP 0.1f
#if defined(USE_WEBENGINE)
#define HTTP_COMPLETE_USERAGENT (QWebEngineProfile::defaultProfile()->httpUserAgent().toLocal8Bit() + QByteArrayLiteral(" ") + QByteArrayLiteral(APP_USERAGENT))
#else
#define HTTP_COMPLETE_USERAGENT (QByteArrayLiteral(APP_USERAGENT))
#endif
#define INTERNAL_URL_MESSAGE "http://rssguard.message"
#define INTERNAL_URL_BLANK "http://rssguard.blank"
#define INTERNAL_URL_ADBLOCKED "http://rssguard.adblocked"

View File

@ -36,8 +36,8 @@ FormMessageFiltersManager::FormMessageFiltersManager(FeedReader* reader, const Q
m_ui.m_treeFeeds->setIndentation(FEEDS_VIEW_INDENTATION);
m_ui.m_treeFeeds->setModel(m_feedsModel);
m_ui.m_btnCheckAll->setIcon(qApp->icons()->fromTheme(QSL("dialog-yes")));
m_ui.m_btnUncheckAll->setIcon(qApp->icons()->fromTheme(QSL("dialog-no")));
m_ui.m_btnCheckAll->setIcon(qApp->icons()->fromTheme(QSL("dialog-yes"), QSL("edit-select-all")));
m_ui.m_btnUncheckAll->setIcon(qApp->icons()->fromTheme(QSL("dialog-no"), QSL("edit-select-none")));
m_ui.m_btnAddNew->setIcon(qApp->icons()->fromTheme(QSL("list-add")));
m_ui.m_btnRemoveSelected->setIcon(qApp->icons()->fromTheme(QSL("list-remove")));
m_ui.m_btnBeautify->setIcon(qApp->icons()->fromTheme(QSL("format-justify-fill")));
@ -152,8 +152,14 @@ void FormMessageFiltersManager::removeSelectedFilter() {
return;
}
m_reader->removeMessageFilter(fltr);
delete m_ui.m_listFilters->currentItem();
if (MessageBox::show(this, QMessageBox::Icon::Question, tr("Are you sure?"),
tr("Do you really want to remove selected filter?"),
{}, fltr->name(),
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
QMessageBox::StandardButton::No) == QMessageBox::StandardButton::Yes) {
m_reader->removeMessageFilter(fltr);
delete m_ui.m_listFilters->currentItem();
}
}
void FormMessageFiltersManager::loadFilters() {

View File

@ -106,6 +106,14 @@ void FeedsView::saveExpandStates(RootItem* item) {
QModelIndex source_index = sourceModel()->indexForItem(it);
QModelIndex visible_index = model()->mapFromSource(source_index);
// TODO: Think.
/*
if (isRowHidden(visible_index.row(), visible_index.parent())) {
continue;
}
*/
settings->setValue(GROUP(CategoriesExpandStates),
setting_name,
isExpanded(visible_index));
@ -550,7 +558,9 @@ void FeedsView::focusInEvent(QFocusEvent* event) {
void FeedsView::expandItemDelayed(const QModelIndex& idx) {
QTimer::singleShot(100, this, [=] {
setExpanded(m_proxyModel->mapFromSource(idx), true);
QModelIndex pidx = m_proxyModel->mapFromSource(idx);
setExpanded(pidx, true);
});
}

View File

@ -39,7 +39,6 @@ SettingsGui::SettingsGui(Settings* settings, QWidget* parent) : SettingsPanel(se
// Setup skins.
m_ui->m_treeSkins->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents);
m_ui->m_treeSkins->header()->setSectionResizeMode(1, QHeaderView::ResizeMode::ResizeToContents);
m_ui->m_treeSkins->header()->setSectionResizeMode(2, QHeaderView::ResizeMode::ResizeToContents);
@ -238,7 +237,7 @@ void SettingsGui::loadSettings() {
clr_btn->setObjectName(QString::number(enumer.value(i)));
clr_btn->setColor(clr);
auto* lay = new QHBoxLayout(this);
auto* lay = new QHBoxLayout();
lay->addWidget(clr_btn);
lay->addWidget(rst_btn);
@ -251,7 +250,6 @@ void SettingsGui::loadSettings() {
m_ui->m_layoutCustomColors->setLayout(row,
QFormLayout::ItemRole::FieldRole,
lay);
}
onEndLoadSettings();

View File

@ -101,6 +101,8 @@ Application::Application(const QString& id, int& argc, char** argv)
#if defined(USE_WEBENGINE)
m_webFactory->urlIinterceptor()->load();
QWebEngineProfile::defaultProfile()->setHttpUserAgent(QString(HTTP_COMPLETE_USERAGENT));
connect(QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested, this, &Application::downloadRequested);
connect(m_webFactory->adBlock(), &AdBlockManager::processTerminated, this, &Application::onAdBlockFailure);

View File

@ -93,6 +93,32 @@ bool IOFactory::startProcessDetached(const QString& program, const QStringList&
return process.startDetached(nullptr);
}
QString IOFactory::startProcessGetOutput(const QString& executable,
const QStringList& arguments,
const QProcessEnvironment& pe) {
QProcess proc;
proc.setProgram(executable);
proc.setArguments(arguments);
QProcessEnvironment system_pe = QProcessEnvironment::systemEnvironment();
system_pe.insert(pe);
proc.setProcessEnvironment(system_pe);
proc.start();
if (proc.waitForFinished() &&
proc.exitStatus() == QProcess::ExitStatus::NormalExit &&
proc.exitCode() == EXIT_SUCCESS) {
return proc.readAllStandardOutput();
}
else {
QString err = proc.readAllStandardError().simplified();
return err;
}
}
QByteArray IOFactory::readFile(const QString& file_path) {
QFile input_file(file_path);
QByteArray input_data;
@ -121,6 +147,14 @@ void IOFactory::writeFile(const QString& file_path, const QByteArray& data) {
bool IOFactory::copyFile(const QString& source, const QString& destination) {
if (QFile::exists(destination)) {
QFile file(destination);
file.setPermissions(file.permissions() |
QFileDevice::WriteOwner |
QFileDevice::WriteUser |
QFileDevice::WriteGroup |
QFileDevice::WriteOther);
if (!QFile::remove(destination)) {
return false;
}

View File

@ -7,6 +7,7 @@
#include "definitions/definitions.h"
#include <QProcessEnvironment>
#include <QStandardPaths>
class IOFactory {
@ -31,6 +32,9 @@ class IOFactory {
const QStringList& arguments,
const QString& native_arguments = {},
const QString& working_directory = {});
static QString startProcessGetOutput(const QString& executable,
const QStringList& arguments = {},
const QProcessEnvironment& pe = {});
// Returns contents of a file.
// Throws exception when no such file exists.

View File

@ -8,6 +8,7 @@
#if !defined(Q_OS_OS2)
#include <QMediaPlayer>
#include <QSoundEffect>
#if QT_VERSION_MAJOR == 6
#include <QAudioOutput>
@ -36,50 +37,77 @@ void Notification::setSoundPath(const QString& sound_path) {
void Notification::playSound(Application* app) const {
if (!m_soundPath.isEmpty()) {
#if !defined(Q_OS_OS2)
QMediaPlayer* play = new QMediaPlayer(app);
if (m_soundPath.endsWith(QSL(".wav"), Qt::CaseSensitivity::CaseInsensitive)) {
qDebugNN << LOGSEC_CORE << "Using QSoundEffect to play notification sound.";
QSoundEffect* play = new QSoundEffect(app);
QObject::connect(play, &QSoundEffect::playingChanged, play, [play]() {
if (!play->isPlaying()) {
play->deleteLater();
}
});
if (m_soundPath.startsWith(QSL(":"))) {
play->setSource(QUrl(QSL("qrc") + m_soundPath));
}
else {
play->setSource(QUrl::fromLocalFile(
QDir::toNativeSeparators(app->replaceDataUserDataFolderPlaceholder(m_soundPath))));
}
play->setVolume(m_volume);
play->play();
}
else {
qDebugNN << LOGSEC_CORE << "Using QMediaPlayer to play notification sound.";
QMediaPlayer* play = new QMediaPlayer(app);
#if QT_VERSION_MAJOR == 6
QAudioOutput* out = new QAudioOutput(app);
QAudioOutput* out = new QAudioOutput(app);
play->setAudioOutput(out);
play->setAudioOutput(out);
QObject::connect(play, &QMediaPlayer::playbackStateChanged, play, [play, out](QMediaPlayer::PlaybackState state) {
if (state == QMediaPlayer::PlaybackState::StoppedState) {
out->deleteLater();
play->deleteLater();
}
});
if (m_soundPath.startsWith(QSL(":"))) {
play->setSource(QUrl(QSL("qrc") + m_soundPath));
QObject::connect(play, &QMediaPlayer::playbackStateChanged, play, [play, out](QMediaPlayer::PlaybackState state) {
if (state == QMediaPlayer::PlaybackState::StoppedState) {
out->deleteLater();
play->deleteLater();
}
});
else {
play->setSource(QUrl::fromLocalFile(QDir::toNativeSeparators(app->replaceDataUserDataFolderPlaceholder(m_soundPath))));
}
if (m_soundPath.startsWith(QSL(":"))) {
play->setSource(QUrl(QSL("qrc") + m_soundPath));
}
else {
play->setSource(QUrl::fromLocalFile(QDir::toNativeSeparators(app->replaceDataUserDataFolderPlaceholder(m_soundPath))));
}
play->audioOutput()->setVolume((m_volume * 1.0f) / 100.0f);
play->play();
play->audioOutput()->setVolume((m_volume * 1.0f) / 100.0f);
play->play();
#else
QObject::connect(play, &QMediaPlayer::stateChanged, play, [play](QMediaPlayer::State state) {
if (state == QMediaPlayer::State::StoppedState) {
play->deleteLater();
QObject::connect(play, &QMediaPlayer::stateChanged, play, [play](QMediaPlayer::State state) {
if (state == QMediaPlayer::State::StoppedState) {
play->deleteLater();
}
});
if (m_soundPath.startsWith(QSL(":"))) {
play->setMedia(QMediaContent(QUrl(QSL("qrc") + m_soundPath)));
}
else {
play->setMedia(QMediaContent(
QUrl::fromLocalFile(
QDir::toNativeSeparators(app->replaceDataUserDataFolderPlaceholder(m_soundPath)))));
}
});
if (m_soundPath.startsWith(QSL(":"))) {
play->setMedia(QMediaContent(QUrl(QSL("qrc") + m_soundPath)));
}
else {
play->setMedia(QMediaContent(
QUrl::fromLocalFile(
QDir::toNativeSeparators(app->replaceDataUserDataFolderPlaceholder(m_soundPath)))));
}
play->setVolume(m_volume);
play->play();
play->setVolume(m_volume);
play->play();
#endif
}
#endif
}
}

View File

@ -68,36 +68,40 @@ void SkinFactory::loadSkinFromData(const Skin& skin) {
qDebugNN << LOGSEC_GUI << "Activating dark palette for Fusion style.";
QPalette fusion_palette = qApp->palette();
QColor clr_bg(QSL("#2D2F32"));
QColor clr_maibg(QSL("#2D2F32"));
QColor clr_basbg(QSL("#373A3D"));
QColor clr_altbg(QSL("#323437"));
QColor clr_selbg(QSL("#8291AD"));
QColor clr_fg(QSL("#D8D8D8"));
QColor clr_brdr(QSL("#585C65"));
QColor clr_tooltip_brdr(QSL("#707580"));
QColor clr_link(QSL("#a1acc1"));
QColor clr_dis_fg(QSL("#727272"));
QColor clr_selfg(QSL("#FFFFFF"));
QColor clr_btnfg(QSL("#E7E7E7"));
QColor clr_dibfg(QSL("#A7A7A7"));
QColor clr_winfg(QSL("#D8D8D8"));
QColor clr_diwfg(QSL("#999999"));
QColor clr_brdbg(QSL("#202224")); // Use colour picker on dark brdr under list header for this one
QColor clr_wlink(QSL("#a1acc1"));
//
// Normal state.
//
// Backgrounds & bases.
fusion_palette.setColor(QPalette::ColorRole::Window, clr_bg);
fusion_palette.setColor(QPalette::ColorRole::Base, clr_bg);
fusion_palette.setColor(QPalette::ColorRole::Dark, clr_bg);
fusion_palette.setColor(QPalette::ColorRole::Window, clr_maibg);
fusion_palette.setColor(QPalette::ColorRole::Base, clr_basbg);
fusion_palette.setColor(QPalette::ColorRole::Dark, clr_brdbg);
fusion_palette.setColor(QPalette::ColorRole::AlternateBase, clr_altbg);
fusion_palette.setColor(QPalette::ColorRole::Button, clr_altbg);
fusion_palette.setColor(QPalette::ColorRole::Highlight, clr_selbg);
fusion_palette.setColor(QPalette::ColorRole::Button, clr_altbg);
fusion_palette.setColor(QPalette::ColorRole::Light, clr_altbg); // Bright
fusion_palette.setColor(QPalette::ColorRole::Highlight, clr_selbg);
// Texts.
fusion_palette.setColor(QPalette::ColorRole::WindowText, clr_fg);
fusion_palette.setColor(QPalette::ColorRole::ButtonText, clr_fg);
fusion_palette.setColor(QPalette::ColorRole::BrightText, clr_fg);
fusion_palette.setColor(QPalette::ColorRole::Text, clr_fg);
fusion_palette.setColor(QPalette::ColorRole::PlaceholderText, clr_fg);
fusion_palette.setColor(QPalette::ColorRole::Link, clr_link);
fusion_palette.setColor(QPalette::ColorRole::LinkVisited, clr_link);
fusion_palette.setColor(QPalette::ColorRole::HighlightedText, clr_fg);
fusion_palette.setColor(QPalette::ColorRole::ButtonText, clr_btnfg);
fusion_palette.setColor(QPalette::ColorRole::WindowText, clr_winfg);
fusion_palette.setColor(QPalette::ColorRole::BrightText, clr_basbg);
fusion_palette.setColor(QPalette::ColorRole::Text, clr_winfg); // Normal text
fusion_palette.setColor(QPalette::ColorRole::PlaceholderText, clr_dibfg);
fusion_palette.setColor(QPalette::ColorRole::Link, clr_wlink);
fusion_palette.setColor(QPalette::ColorRole::LinkVisited, clr_wlink);
fusion_palette.setColor(QPalette::ColorRole::HighlightedText, clr_selfg);
//
// Inactive state.
@ -112,29 +116,30 @@ void SkinFactory::loadSkinFromData(const Skin& skin) {
//
// Backgrounds & bases.
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Window, clr_altbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Base, clr_altbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Dark, clr_altbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Window, clr_maibg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Base, clr_basbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Dark, clr_brdbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::AlternateBase, clr_altbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Button, Qt::GlobalColor::red);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Highlight, clr_selbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Button, clr_altbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Light, clr_altbg); // Bright
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Highlight, clr_selbg);
// Texts.
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::WindowText, clr_dis_fg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::ButtonText, clr_dis_fg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::BrightText, clr_fg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Text, clr_dis_fg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::PlaceholderText, clr_fg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Link, clr_link);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::LinkVisited, clr_link);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::HighlightedText, clr_fg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::ButtonText, clr_dibfg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::WindowText, clr_diwfg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::BrightText, clr_basbg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Text, clr_diwfg); // Normal text
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::PlaceholderText, clr_dibfg);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Link, clr_wlink);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::LinkVisited, clr_wlink);
fusion_palette.setColor(QPalette::ColorGroup::Disabled, QPalette::ColorRole::HighlightedText, clr_selfg);
//
// Tooltips.
//
fusion_palette.setColor(QPalette::ColorGroup::All, QPalette::ColorRole::ToolTipBase, clr_bg);
fusion_palette.setColor(QPalette::ColorGroup::All, QPalette::ColorRole::ToolTipText, clr_fg);
fusion_palette.setColor(QPalette::ColorGroup::All, QPalette::ColorRole::ToolTipBase, clr_maibg);
fusion_palette.setColor(QPalette::ColorGroup::All, QPalette::ColorRole::ToolTipText, clr_winfg);
QToolTip::setPalette(fusion_palette);
qApp->setPalette(fusion_palette);

View File

@ -296,19 +296,20 @@ QProcess* AdBlockManager::startServer(int port) {
proc->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
auto pe = proc->processEnvironment();
QString default_node_path =
#if defined(Q_OS_WIN)
pe.value(QSL("APPDATA")) + QDir::separator() + QSL("npm") + QDir::separator() + QSL("node_modules");
#elif defined(Q_OS_LINUX)
QSL("/usr/lib/node_modules");
#elif defined(Q_OS_MACOS)
QSL("/usr/local/lib/node_modules");
#else
QSL("");
#endif
if (!pe.contains(QSL("NODE_PATH")) && !default_node_path.isEmpty()) {
pe.insert(QSL("NODE_PATH"), default_node_path);
if (!pe.contains(QSL("NODE_PATH"))) {
const QString system_node_prefix = IOFactory::startProcessGetOutput(
#if defined(Q_OS_WIN)
QSL("npm.cmd")
#else
QSL("npm")
#endif
, { QSL("root"), QSL("--quiet"), QSL("-g") }
);
if (!system_node_prefix.isEmpty()) {
pe.insert(QSL("NODE_PATH"), system_node_prefix.simplified());
}
}
proc->setProcessEnvironment(pe);

View File

@ -9,6 +9,10 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#if defined(USE_WEBENGINE)
#include <QWebEngineProfile>
#endif
BaseNetworkAccessManager::BaseNetworkAccessManager(QObject* parent)
: QNetworkAccessManager(parent) {
connect(this, &BaseNetworkAccessManager::sslErrors, this, &BaseNetworkAccessManager::onSslErrors);
@ -61,7 +65,7 @@ QNetworkReply* BaseNetworkAccessManager::createRequest(QNetworkAccessManager::Op
#endif
new_request.setRawHeader(HTTP_HEADERS_COOKIE, QSL("JSESSIONID= ").toLocal8Bit());
new_request.setRawHeader(HTTP_HEADERS_USER_AGENT, QSL(APP_USERAGENT).toLocal8Bit());
new_request.setRawHeader(HTTP_HEADERS_USER_AGENT, HTTP_COMPLETE_USERAGENT);
auto reply = QNetworkAccessManager::createRequest(op, new_request, outgoingData);
return reply;

View File

@ -14,6 +14,8 @@ class CookieJar : public QNetworkCookieJar {
virtual bool insertCookie(const QNetworkCookie& cookie);
virtual bool updateCookie(const QNetworkCookie& cookie);
virtual bool deleteCookie(const QNetworkCookie& cookie);
public:
static QList<QNetworkCookie> extractCookiesFromUrl(const QString& url);
private:

View File

@ -23,6 +23,8 @@
#include "miscellaneous/settings.h"
#include "network-web/urlinterceptor.h"
#include <QWebEngineProfile>
NetworkUrlInterceptor::NetworkUrlInterceptor(QObject* parent)
: QWebEngineUrlRequestInterceptor(parent), m_sendDnt(false) {}

View File

@ -315,6 +315,25 @@ void WebFactory::createMenu(QMenu* menu) {
actions << createEngineSettingsAction(tr("Allow geolocation on insecure origins"), QWebEngineSettings::WebAttribute::AllowGeolocationOnInsecureOrigins);
#endif
#if QT_VERSION >= 0x050A00 // Qt >= 5.10.0
actions << createEngineSettingsAction(tr("JS can activate windows"), QWebEngineSettings::WebAttribute::AllowWindowActivationFromJavaScript);
actions << createEngineSettingsAction(tr("Show scrollbars"), QWebEngineSettings::WebAttribute::ShowScrollBars);
#endif
#if QT_VERSION >= 0x050B00 // Qt >= 5.11.0
actions << createEngineSettingsAction(tr("Media playback with gestures"), QWebEngineSettings::WebAttribute::PlaybackRequiresUserGesture);
actions << createEngineSettingsAction(tr("WebRTC uses only public interfaces"), QWebEngineSettings::WebAttribute::WebRTCPublicInterfacesOnly);
actions << createEngineSettingsAction(tr("JS can paste from clipboard"), QWebEngineSettings::WebAttribute::JavascriptCanPaste);
#endif
#if QT_VERSION >= 0x050C00 // Qt >= 5.12.0
actions << createEngineSettingsAction(tr("DNS prefetch enabled"), QWebEngineSettings::WebAttribute::DnsPrefetchEnabled);
#endif
#if QT_VERSION >= 0x050D00 // Qt >= 5.13.0
actions << createEngineSettingsAction(tr("PDF viewer enabled"), QWebEngineSettings::WebAttribute::PdfViewerEnabled);
#endif
menu->addActions(actions);
}

View File

@ -30,6 +30,13 @@ int main(int argc, char* argv[]) {
QApplication::setDesktopFileName(APP_DESKTOP_ENTRY_FILE);
#endif
#if defined(QT_STATIC)
// NOTE: Add all used resources here.
Q_INIT_RESOURCE(icons);
Q_INIT_RESOURCE(sql);
Q_INIT_RESOURCE(rssguard);
#endif
// Ensure that ini format is used as application settings storage on macOS.
QSettings::setDefaultFormat(QSettings::IniFormat);