1
2 // Own
3 #include "kcmcss.h"
4
5 // Qt
6 #include <QCheckBox>
7 #include <QDialogButtonBox>
8 #include <QUrl>
9 #include <QStandardPaths>
10
11 // KDE
12 #include <kcolorbutton.h>
13 #include <kconfig.h>
14 #include <kconfiggroup.h>
15 #include <kurlrequester.h>
16 #include <kmimetypetrader.h>
17 #include <kparts/part.h>
18 #include <kparts/openurlarguments.h>
19
20 // Local
21 #include "template.h"
22
23 #include "ui_cssconfig.h"
24
25 class CSSConfigWidget: public QWidget, public Ui::CSSConfigWidget
26 {
27 public:
CSSConfigWidget(QWidget * parent)28 CSSConfigWidget(QWidget *parent) : QWidget(parent)
29 {
30 setupUi(this);
31 }
32 };
33
CSSConfig(QWidget * parent,const QVariantList &)34 CSSConfig::CSSConfig(QWidget *parent, const QVariantList &)
35 : QWidget(parent)
36 , configWidget(new CSSConfigWidget(this))
37 , customDialogBase(new QDialog(this))
38 , customDialog(new CSSCustomDialog(customDialogBase))
39 {
40 customDialogBase->setObjectName(QStringLiteral("customCSSDialog"));
41 customDialogBase->setModal(true);
42 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, customDialogBase);
43 buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
44 connect(buttonBox, &QDialogButtonBox::rejected, customDialogBase, &QDialog::reject);
45
46 QVBoxLayout *vLayout = new QVBoxLayout(customDialogBase);
47 vLayout->addWidget(customDialog);
48 vLayout->addStretch(1);
49 vLayout->addWidget(buttonBox);
50
51 setToolTip(i18n("<h1>Konqueror Stylesheets</h1> This module allows you to apply your own color"
52 " and font settings to Konqueror by using"
53 " stylesheets (CSS). You can either specify"
54 " options or apply your own self-written"
55 " stylesheet by pointing to its location.<br />"
56 " Note that these settings will always have"
57 " precedence before all other settings made"
58 " by the site author. This can be useful to"
59 " visually impaired people or for web pages"
60 " that are unreadable due to bad design."));
61
62 connect(configWidget->useDefault, &QAbstractButton::clicked, this, &CSSConfig::changed);
63 connect(configWidget->useAccess, &QAbstractButton::clicked, this, &CSSConfig::changed);
64 connect(configWidget->useUser, &QAbstractButton::clicked, this, &CSSConfig::changed);
65 connect(configWidget->urlRequester, &KUrlRequester::textChanged, this, &CSSConfig::changed);
66 connect(configWidget->customize, &QAbstractButton::clicked, this, &CSSConfig::slotCustomize);
67 connect(customDialog, &CSSCustomDialog::changed, this, &CSSConfig::changed);
68
69 QVBoxLayout *vbox = new QVBoxLayout(this);
70 vbox->setContentsMargins(0, 0, 0, 0);
71 vbox->addWidget(configWidget);
72 }
73
load()74 void CSSConfig::load()
75 {
76 QSignalBlocker block(customDialog);
77
78 KConfig *c = new KConfig(QStringLiteral("kcmcssrc"), KConfig::NoGlobals);
79 KConfigGroup group = c->group("Stylesheet");
80 QString u = group.readEntry("Use", "default");
81 configWidget->useDefault->setChecked(u == QLatin1String("default"));
82 configWidget->useUser->setChecked(u == QLatin1String("user"));
83 configWidget->useAccess->setChecked(u == QLatin1String("access"));
84 configWidget->urlRequester->setUrl(QUrl::fromUserInput(group.readEntry("SheetName")));
85
86 group = c->group("Font");
87 customDialog->basefontsize->setEditText(QString::number(group.readEntry("BaseSize", 12)));
88 customDialog->dontScale->setChecked(group.readEntry("DontScale", false));
89
90 const QString fname(group.readEntry("Family", "Arial"));
91 for (int i = 0; i < customDialog->fontFamily->count(); ++i) {
92 if (customDialog->fontFamily->itemText(i) == fname) {
93 customDialog->fontFamily->setCurrentIndex(i);
94 break;
95 }
96 }
97
98 customDialog->sameFamily->setChecked(group.readEntry("SameFamily", false));
99
100 group = c->group("Colors");
101 QString m = group.readEntry("Mode", "black-on-white");
102 customDialog->blackOnWhite->setChecked(m == QLatin1String("black-on-white"));
103 customDialog->whiteOnBlack->setChecked(m == QLatin1String("white-on-black"));
104 customDialog->customColor->setChecked(m == QLatin1String("custom"));
105
106 QColor white(Qt::white);
107 QColor black(Qt::black);
108 customDialog->backgroundColorButton->setColor(group.readEntry("BackColor", white));
109 customDialog->foregroundColorButton->setColor(group.readEntry("ForeColor", black));
110 customDialog->sameColor->setChecked(group.readEntry("SameColor", false));
111
112 // Images
113 group = c->group("Images");
114 customDialog->hideImages->setChecked(group.readEntry("Hide", false));
115 customDialog->hideBackground->setChecked(group.readEntry("HideBackground", true));
116
117 delete c;
118 }
119
save()120 void CSSConfig::save()
121 {
122 // write to config file
123 KConfig *c = new KConfig(QStringLiteral("kcmcssrc"), KConfig::NoGlobals);
124 KConfigGroup group = c->group("Stylesheet");
125 if (configWidget->useDefault->isChecked()) {
126 group.writeEntry("Use", "default");
127 }
128 if (configWidget->useUser->isChecked()) {
129 group.writeEntry("Use", "user");
130 }
131 if (configWidget->useAccess->isChecked()) {
132 group.writeEntry("Use", "access");
133 }
134 group.writeEntry("SheetName", configWidget->urlRequester->url().url());
135
136 group = c->group("Font");
137 group.writeEntry("BaseSize", customDialog->basefontsize->currentText());
138 group.writeEntry("DontScale", customDialog->dontScale->isChecked());
139 group.writeEntry("SameFamily", customDialog->sameFamily->isChecked());
140 group.writeEntry("Family", customDialog->fontFamily->currentText());
141
142 group = c->group("Colors");
143 if (customDialog->blackOnWhite->isChecked()) {
144 group.writeEntry("Mode", "black-on-white");
145 }
146 if (customDialog->whiteOnBlack->isChecked()) {
147 group.writeEntry("Mode", "white-on-black");
148 }
149 if (customDialog->customColor->isChecked()) {
150 group.writeEntry("Mode", "custom");
151 }
152 group.writeEntry("BackColor", customDialog->backgroundColorButton->color());
153 group.writeEntry("ForeColor", customDialog->foregroundColorButton->color());
154 group.writeEntry("SameColor", customDialog->sameColor->isChecked());
155
156 group = c->group("Images");
157 group.writeEntry("Hide", customDialog->hideImages->isChecked());
158 group.writeEntry("HideBackground", customDialog->hideBackground->isChecked());
159
160 c->sync();
161 delete c;
162
163 // generate CSS template
164 QString dest;
165 const QString templ(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcmcss/template.css")));
166 if (!templ.isEmpty()) {
167 CSSTemplate css(templ);
168 dest = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kcmcss/";
169 QDir().mkpath(dest);
170 dest += QLatin1String("override.css");
171 css.expandToFile(dest, customDialog->cssDict());
172 }
173
174 // make konqueror use the right stylesheet
175 c = new KConfig(QStringLiteral("konquerorrc"), KConfig::NoGlobals);
176 group = c->group("HTML Settings");
177 group.writeEntry("UserStyleSheetEnabled", !configWidget->useDefault->isChecked());
178
179 if (configWidget->useUser->isChecked()) {
180 group.writeEntry("UserStyleSheet", configWidget->urlRequester->url().url());
181 }
182 if (configWidget->useAccess->isChecked()) {
183 group.writeEntry("UserStyleSheet", dest);
184 }
185
186 c->sync();
187 delete c;
188 }
189
defaults()190 void CSSConfig::defaults()
191 {
192 configWidget->useDefault->setChecked(true);
193 configWidget->useUser->setChecked(false);
194 configWidget->useAccess->setChecked(false);
195 configWidget->urlRequester->setUrl(QUrl());
196
197 customDialog->basefontsize->setEditText(QString::number(12));
198 customDialog->dontScale->setChecked(false);
199
200 const QString fname(QStringLiteral("Arial"));
201 for (int i = 0; i < customDialog->fontFamily->count(); ++i) {
202 if (customDialog->fontFamily->itemText(i) == fname) {
203 customDialog->fontFamily->setCurrentIndex(i);
204 break;
205 }
206 }
207
208 customDialog->sameFamily->setChecked(false);
209 customDialog->blackOnWhite->setChecked(true);
210 customDialog->whiteOnBlack->setChecked(false);
211 customDialog->customColor->setChecked(false);
212 customDialog->backgroundColorButton->setColor(Qt::white);
213 customDialog->foregroundColorButton->setColor(Qt::black);
214 customDialog->sameColor->setChecked(false);
215
216 customDialog->hideImages->setChecked(false);
217 customDialog->hideBackground->setChecked(true);
218 }
219
px(int i,double scale)220 static QString px(int i, double scale)
221 {
222 QString px;
223 px.setNum(static_cast<int>(i * scale));
224 px += QLatin1String("px");
225 return px;
226 }
227
cssDict()228 QMap<QString, QString> CSSCustomDialog::cssDict()
229 {
230 QMap<QString, QString> dict;
231
232 // Fontsizes ------------------------------------------------------
233
234 int bfs = basefontsize->currentText().toInt();
235 dict.insert(QStringLiteral("fontsize-base"), px(bfs, 1.0));
236
237 if (dontScale->isChecked()) {
238 dict.insert(QStringLiteral("fontsize-small-1"), px(bfs, 1.0));
239 dict.insert(QStringLiteral("fontsize-large-1"), px(bfs, 1.0));
240 dict.insert(QStringLiteral("fontsize-large-2"), px(bfs, 1.0));
241 dict.insert(QStringLiteral("fontsize-large-3"), px(bfs, 1.0));
242 dict.insert(QStringLiteral("fontsize-large-4"), px(bfs, 1.0));
243 dict.insert(QStringLiteral("fontsize-large-5"), px(bfs, 1.0));
244 } else {
245 // TODO: use something harmonic here
246 dict.insert(QStringLiteral("fontsize-small-1"), px(bfs, 0.8));
247 dict.insert(QStringLiteral("fontsize-large-1"), px(bfs, 1.2));
248 dict.insert(QStringLiteral("fontsize-large-2"), px(bfs, 1.4));
249 dict.insert(QStringLiteral("fontsize-large-3"), px(bfs, 1.5));
250 dict.insert(QStringLiteral("fontsize-large-4"), px(bfs, 1.6));
251 dict.insert(QStringLiteral("fontsize-large-5"), px(bfs, 1.8));
252 }
253
254 // Colors --------------------------------------------------------
255
256 if (customColor->isChecked()) {
257 dict.insert(QStringLiteral("background-color"), backgroundColorButton->color().name());
258 dict.insert(QStringLiteral("foreground-color"), foregroundColorButton->color().name());
259 } else {
260 const char *blackOnWhiteFG[2] = {"White", "Black"};
261 bool bw = blackOnWhite->isChecked();
262 dict.insert(QStringLiteral("foreground-color"), QLatin1String(blackOnWhiteFG[bw]));
263 dict.insert(QStringLiteral("background-color"), QLatin1String(blackOnWhiteFG[!bw]));
264 }
265
266 const char *notImportant[2] = {"", "! important"};
267 dict.insert(QStringLiteral("force-color"), QLatin1String(notImportant[sameColor->isChecked()]));
268
269 // Fonts -------------------------------------------------------------
270 dict.insert(QStringLiteral("font-family"), fontFamily->currentText());
271 dict.insert(QStringLiteral("force-font"), QLatin1String(notImportant[sameFamily->isChecked()]));
272
273 // Images
274
275 const char *bgNoneImportant[2] = {"", "background-image : none ! important"};
276 dict.insert(QStringLiteral("display-images"), QLatin1String(bgNoneImportant[hideImages->isChecked()]));
277 dict.insert(QStringLiteral("display-background"), QLatin1String(bgNoneImportant[hideBackground->isChecked()]));
278
279 return dict;
280 }
281
slotCustomize()282 void CSSConfig::slotCustomize()
283 {
284 customDialog->slotPreview();
285 customDialogBase->exec();
286 }
287
CSSCustomDialog(QWidget * parent)288 CSSCustomDialog::CSSCustomDialog(QWidget *parent)
289 : QWidget(parent)
290 {
291 setupUi(this);
292 connect(this, &CSSCustomDialog::changed, this, &CSSCustomDialog::slotPreview);
293
294 connect(basefontsize, QOverload<int>::of(&QComboBox::activated), this, &CSSCustomDialog::changed);
295 connect(basefontsize, &QComboBox::editTextChanged, this, &CSSCustomDialog::changed);
296 connect(dontScale, &QAbstractButton::clicked, this, &CSSCustomDialog::changed);
297 connect(blackOnWhite, &QAbstractButton::clicked, this, &CSSCustomDialog::changed);
298 connect(whiteOnBlack, &QAbstractButton::clicked, this, &CSSCustomDialog::changed);
299 connect(customColor, &QAbstractButton::clicked, this, &CSSCustomDialog::changed);
300 connect(foregroundColorButton, &KColorButton::changed, this, &CSSCustomDialog::changed);
301 connect(backgroundColorButton, &KColorButton::changed, this, &CSSCustomDialog::changed);
302 connect(fontFamily, QOverload<int>::of(&QComboBox::activated), this, &CSSCustomDialog::changed);
303 connect(fontFamily, &QComboBox::editTextChanged, this, &CSSCustomDialog::changed);
304 connect(sameFamily, &QAbstractButton::clicked, this, &CSSCustomDialog::changed);
305 connect(sameColor, &QAbstractButton::clicked, this, &CSSCustomDialog::changed);
306 connect(hideImages, &QAbstractButton::clicked, this, &CSSCustomDialog::changed);
307 connect(hideBackground, &QAbstractButton::clicked, this, &CSSCustomDialog::changed);
308
309 //QStringList fonts;
310 //KFontChooser::getFontList(fonts, 0);
311 //fontFamily->addItems(fonts);
312 part = KMimeTypeTrader::createPartInstanceFromQuery<KParts::ReadOnlyPart>(QStringLiteral("text/html"), parent, this);
313 QVBoxLayout *l = new QVBoxLayout(previewBox);
314 l->addWidget(part->widget());
315 }
316
toDataUri(const QString & content,const QByteArray & contentType)317 static QUrl toDataUri(const QString &content, const QByteArray &contentType)
318 {
319 QByteArray data("data:");
320 data += contentType;
321 data += ";charset=utf-8;base64,";
322 data += content.toUtf8().toBase64();
323 return QUrl::fromEncoded(data);
324 }
325
slotPreview()326 void CSSCustomDialog::slotPreview()
327 {
328 const QString templ(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcmcss/template.css")));
329
330 if (templ.isEmpty()) {
331 return;
332 }
333
334 CSSTemplate css(templ);
335
336 QString data(i18n("<html>\n<head>\n<style>\n<!--\n"
337 "%1"
338 "\n-->\n</style>\n</head>\n"
339 "<body>\n"
340 "<h1>Heading 1</h1>\n"
341 "<h2>Heading 2</h2>\n"
342 "<h3>Heading 3</h3>\n"
343 "\n"
344 "<p>User-defined stylesheets allow increased\n"
345 "accessibility for visually handicapped\n"
346 "people.</p>\n"
347 "\n"
348 "</body>\n"
349 "</html>\n", css.expandToString(cssDict())));
350
351 KParts::OpenUrlArguments args(part->arguments());
352 args.setReload(true); // Make sure the content is always freshly reloaded.
353 part->setArguments(args);
354 part->openUrl(toDataUri(data, "text/html"));
355 }
356
357