File indexing completed on 2024-05-19 05:00:54

0001 
0002 // Own
0003 #include "kcmcss.h"
0004 
0005 // Qt
0006 #include <QCheckBox>
0007 #include <QDialogButtonBox>
0008 #include <QUrl>
0009 #include <QStandardPaths>
0010 
0011 // KDE
0012 #include <KColorButton>
0013 #include <KConfig>
0014 #include <KConfigGroup>
0015 #include <KUrlRequester>
0016 #include <KParts/Part>
0017 #include <KParts/OpenUrlArguments>
0018 #include <KParts/PartLoader>
0019 
0020 // Local
0021 #include "template.h"
0022 
0023 #include "ui_cssconfig.h"
0024 
0025 class CSSConfigWidget: public QWidget, public Ui::CSSConfigWidget
0026 {
0027 public:
0028     CSSConfigWidget(QWidget *parent) : QWidget(parent)
0029     {
0030         setupUi(this);
0031     }
0032 };
0033 
0034 CSSConfig::CSSConfig(QWidget *parent, const QVariantList &)
0035     : QWidget(parent)
0036     , configWidget(new CSSConfigWidget(this))
0037     , customDialogBase(new QDialog(this))
0038     , customDialog(new CSSCustomDialog(customDialogBase))
0039 {
0040     customDialogBase->setObjectName(QStringLiteral("customCSSDialog"));
0041     customDialogBase->setModal(true);
0042     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, customDialogBase);
0043     buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
0044     connect(buttonBox, &QDialogButtonBox::rejected, customDialogBase, &QDialog::reject);
0045 
0046     QVBoxLayout *vLayout = new QVBoxLayout(customDialogBase);
0047     vLayout->addWidget(customDialog);
0048     vLayout->addStretch(1);
0049     vLayout->addWidget(buttonBox);
0050 
0051     setToolTip(i18n("<h1>Konqueror Stylesheets</h1> This module allows you to apply your own color"
0052                       " and font settings to Konqueror by using"
0053                       " stylesheets (CSS). You can either specify"
0054                       " options or apply your own self-written"
0055                       " stylesheet by pointing to its location.<br />"
0056                       " Note that these settings will always have"
0057                       " precedence before all other settings made"
0058                       " by the site author. This can be useful to"
0059                       " visually impaired people or for web pages"
0060                       " that are unreadable due to bad design."));
0061 
0062     connect(configWidget->useDefault,    &QAbstractButton::clicked,    this, &CSSConfig::changed);
0063     connect(configWidget->useAccess,     &QAbstractButton::clicked,    this, &CSSConfig::changed);
0064     connect(configWidget->useUser,       &QAbstractButton::clicked,    this, &CSSConfig::changed);
0065     connect(configWidget->urlRequester,  &KUrlRequester::textChanged,  this, &CSSConfig::changed);
0066     connect(configWidget->customize,     &QAbstractButton::clicked,    this, &CSSConfig::slotCustomize);
0067     connect(customDialog,                &CSSCustomDialog::changed,    this, &CSSConfig::changed);
0068 
0069     QVBoxLayout *vbox = new QVBoxLayout(this);
0070     vbox->setContentsMargins(0, 0, 0, 0);
0071     vbox->addWidget(configWidget);
0072 }
0073 
0074 void CSSConfig::load()
0075 {
0076     QSignalBlocker block(customDialog);
0077 
0078     KConfig *c = new KConfig(QStringLiteral("kcmcssrc"), KConfig::NoGlobals);
0079     KConfigGroup group = c->group("Stylesheet");
0080     QString u = group.readEntry("Use", "default");
0081     configWidget->useDefault->setChecked(u == QLatin1String("default"));
0082     configWidget->useUser->setChecked(u == QLatin1String("user"));
0083     configWidget->useAccess->setChecked(u == QLatin1String("access"));
0084     configWidget->urlRequester->setUrl(QUrl::fromUserInput(group.readEntry("SheetName")));
0085 
0086     group = c->group("Font");
0087     customDialog->basefontsize->setEditText(QString::number(group.readEntry("BaseSize", 12)));
0088     customDialog->dontScale->setChecked(group.readEntry("DontScale", false));
0089 
0090     const QString fname(group.readEntry("Family", "Arial"));
0091     for (int i = 0; i < customDialog->fontFamily->count(); ++i) {
0092         if (customDialog->fontFamily->itemText(i) == fname) {
0093             customDialog->fontFamily->setCurrentIndex(i);
0094             break;
0095         }
0096     }
0097 
0098     customDialog->sameFamily->setChecked(group.readEntry("SameFamily", false));
0099 
0100     group = c->group("Colors");
0101     QString m = group.readEntry("Mode", "black-on-white");
0102     customDialog->blackOnWhite->setChecked(m == QLatin1String("black-on-white"));
0103     customDialog->whiteOnBlack->setChecked(m == QLatin1String("white-on-black"));
0104     customDialog->customColor->setChecked(m == QLatin1String("custom"));
0105 
0106     QColor white(Qt::white);
0107     QColor black(Qt::black);
0108     customDialog->backgroundColorButton->setColor(group.readEntry("BackColor", white));
0109     customDialog->foregroundColorButton->setColor(group.readEntry("ForeColor", black));
0110     customDialog->sameColor->setChecked(group.readEntry("SameColor", false));
0111 
0112     // Images
0113     group = c->group("Images");
0114     customDialog->hideImages->setChecked(group.readEntry("Hide", false));
0115     customDialog->hideBackground->setChecked(group.readEntry("HideBackground", true));
0116 
0117     delete c;
0118 }
0119 
0120 void CSSConfig::save()
0121 {
0122     // write to config file
0123     KConfig *c = new KConfig(QStringLiteral("kcmcssrc"), KConfig::NoGlobals);
0124     KConfigGroup group = c->group("Stylesheet");
0125     if (configWidget->useDefault->isChecked()) {
0126         group.writeEntry("Use", "default");
0127     }
0128     if (configWidget->useUser->isChecked()) {
0129         group.writeEntry("Use", "user");
0130     }
0131     if (configWidget->useAccess->isChecked()) {
0132         group.writeEntry("Use", "access");
0133     }
0134     group.writeEntry("SheetName", configWidget->urlRequester->url().url());
0135 
0136     group = c->group("Font");
0137     group.writeEntry("BaseSize", customDialog->basefontsize->currentText());
0138     group.writeEntry("DontScale", customDialog->dontScale->isChecked());
0139     group.writeEntry("SameFamily", customDialog->sameFamily->isChecked());
0140     group.writeEntry("Family", customDialog->fontFamily->currentText());
0141 
0142     group = c->group("Colors");
0143     if (customDialog->blackOnWhite->isChecked()) {
0144         group.writeEntry("Mode", "black-on-white");
0145     }
0146     if (customDialog->whiteOnBlack->isChecked()) {
0147         group.writeEntry("Mode", "white-on-black");
0148     }
0149     if (customDialog->customColor->isChecked()) {
0150         group.writeEntry("Mode", "custom");
0151     }
0152     group.writeEntry("BackColor", customDialog->backgroundColorButton->color());
0153     group.writeEntry("ForeColor", customDialog->foregroundColorButton->color());
0154     group.writeEntry("SameColor", customDialog->sameColor->isChecked());
0155 
0156     group = c->group("Images");
0157     group.writeEntry("Hide", customDialog->hideImages->isChecked());
0158     group.writeEntry("HideBackground", customDialog->hideBackground->isChecked());
0159 
0160     c->sync();
0161     delete c;
0162 
0163     // generate CSS template
0164     QString dest;
0165     const QString templ(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcmcss/template.css")));
0166     if (!templ.isEmpty()) {
0167         CSSTemplate css(templ);
0168         dest = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kcmcss/";
0169         QDir().mkpath(dest);
0170         dest += QLatin1String("override.css");
0171         css.expandToFile(dest, customDialog->cssDict());
0172     }
0173 
0174     // make konqueror use the right stylesheet
0175     c = new KConfig(QStringLiteral("konquerorrc"), KConfig::NoGlobals);
0176     group = c->group("HTML Settings");
0177     group.writeEntry("UserStyleSheetEnabled", !configWidget->useDefault->isChecked());
0178 
0179     if (configWidget->useUser->isChecked()) {
0180         group.writeEntry("UserStyleSheet", configWidget->urlRequester->url().url());
0181     }
0182     if (configWidget->useAccess->isChecked()) {
0183         group.writeEntry("UserStyleSheet", dest);
0184     }
0185 
0186     c->sync();
0187     delete c;
0188 }
0189 
0190 void CSSConfig::defaults()
0191 {
0192     configWidget->useDefault->setChecked(true);
0193     configWidget->useUser->setChecked(false);
0194     configWidget->useAccess->setChecked(false);
0195     configWidget->urlRequester->setUrl(QUrl());
0196 
0197     customDialog->basefontsize->setEditText(QString::number(12));
0198     customDialog->dontScale->setChecked(false);
0199 
0200     const QString fname(QStringLiteral("Arial"));
0201     for (int i = 0; i < customDialog->fontFamily->count(); ++i) {
0202         if (customDialog->fontFamily->itemText(i) == fname) {
0203             customDialog->fontFamily->setCurrentIndex(i);
0204             break;
0205         }
0206     }
0207 
0208     customDialog->sameFamily->setChecked(false);
0209     customDialog->blackOnWhite->setChecked(true);
0210     customDialog->whiteOnBlack->setChecked(false);
0211     customDialog->customColor->setChecked(false);
0212     customDialog->backgroundColorButton->setColor(Qt::white);
0213     customDialog->foregroundColorButton->setColor(Qt::black);
0214     customDialog->sameColor->setChecked(false);
0215 
0216     customDialog->hideImages->setChecked(false);
0217     customDialog->hideBackground->setChecked(true);
0218 }
0219 
0220 static QString px(int i, double scale)
0221 {
0222     QString px;
0223     px.setNum(static_cast<int>(i * scale));
0224     px += QLatin1String("px");
0225     return px;
0226 }
0227 
0228 QMap<QString, QString> CSSCustomDialog::cssDict()
0229 {
0230     QMap<QString, QString> dict;
0231 
0232     // Fontsizes ------------------------------------------------------
0233 
0234     int bfs = basefontsize->currentText().toInt();
0235     dict.insert(QStringLiteral("fontsize-base"), px(bfs, 1.0));
0236 
0237     if (dontScale->isChecked()) {
0238         dict.insert(QStringLiteral("fontsize-small-1"), px(bfs, 1.0));
0239         dict.insert(QStringLiteral("fontsize-large-1"), px(bfs, 1.0));
0240         dict.insert(QStringLiteral("fontsize-large-2"), px(bfs, 1.0));
0241         dict.insert(QStringLiteral("fontsize-large-3"), px(bfs, 1.0));
0242         dict.insert(QStringLiteral("fontsize-large-4"), px(bfs, 1.0));
0243         dict.insert(QStringLiteral("fontsize-large-5"), px(bfs, 1.0));
0244     } else {
0245         // TODO: use something harmonic here
0246         dict.insert(QStringLiteral("fontsize-small-1"), px(bfs, 0.8));
0247         dict.insert(QStringLiteral("fontsize-large-1"), px(bfs, 1.2));
0248         dict.insert(QStringLiteral("fontsize-large-2"), px(bfs, 1.4));
0249         dict.insert(QStringLiteral("fontsize-large-3"), px(bfs, 1.5));
0250         dict.insert(QStringLiteral("fontsize-large-4"), px(bfs, 1.6));
0251         dict.insert(QStringLiteral("fontsize-large-5"), px(bfs, 1.8));
0252     }
0253 
0254     // Colors --------------------------------------------------------
0255 
0256     if (customColor->isChecked()) {
0257         dict.insert(QStringLiteral("background-color"), backgroundColorButton->color().name());
0258         dict.insert(QStringLiteral("foreground-color"), foregroundColorButton->color().name());
0259     } else {
0260         const char *blackOnWhiteFG[2] = {"White", "Black"};
0261         bool bw = blackOnWhite->isChecked();
0262         dict.insert(QStringLiteral("foreground-color"), QLatin1String(blackOnWhiteFG[bw]));
0263         dict.insert(QStringLiteral("background-color"), QLatin1String(blackOnWhiteFG[!bw]));
0264     }
0265 
0266     const char *notImportant[2] = {"", "! important"};
0267     dict.insert(QStringLiteral("force-color"), QLatin1String(notImportant[sameColor->isChecked()]));
0268 
0269     // Fonts -------------------------------------------------------------
0270     dict.insert(QStringLiteral("font-family"), fontFamily->currentText());
0271     dict.insert(QStringLiteral("force-font"), QLatin1String(notImportant[sameFamily->isChecked()]));
0272 
0273     // Images
0274 
0275     const char *bgNoneImportant[2] = {"", "background-image : none ! important"};
0276     dict.insert(QStringLiteral("display-images"), QLatin1String(bgNoneImportant[hideImages->isChecked()]));
0277     dict.insert(QStringLiteral("display-background"), QLatin1String(bgNoneImportant[hideBackground->isChecked()]));
0278 
0279     return dict;
0280 }
0281 
0282 void CSSConfig::slotCustomize()
0283 {
0284     customDialog->slotPreview();
0285     customDialogBase->exec();
0286 }
0287 
0288 CSSCustomDialog::CSSCustomDialog(QWidget *parent)
0289     : QWidget(parent)
0290 {
0291     setupUi(this);
0292     connect(this,                   &CSSCustomDialog::changed,                  this, &CSSCustomDialog::slotPreview);
0293 
0294     connect(basefontsize,           QOverload<int>::of(&QComboBox::activated),  this, &CSSCustomDialog::changed);
0295     connect(basefontsize,           &QComboBox::editTextChanged,                this, &CSSCustomDialog::changed);
0296     connect(dontScale,              &QAbstractButton::clicked,                  this, &CSSCustomDialog::changed);
0297     connect(blackOnWhite,           &QAbstractButton::clicked,                  this, &CSSCustomDialog::changed);
0298     connect(whiteOnBlack,           &QAbstractButton::clicked,                  this, &CSSCustomDialog::changed);
0299     connect(customColor,            &QAbstractButton::clicked,                  this, &CSSCustomDialog::changed);
0300     connect(foregroundColorButton,  &KColorButton::changed,                     this, &CSSCustomDialog::changed);
0301     connect(backgroundColorButton,  &KColorButton::changed,                     this, &CSSCustomDialog::changed);
0302     connect(fontFamily,             QOverload<int>::of(&QComboBox::activated),  this, &CSSCustomDialog::changed);
0303     connect(fontFamily,             &QComboBox::editTextChanged,                this, &CSSCustomDialog::changed);
0304     connect(sameFamily,             &QAbstractButton::clicked,                  this, &CSSCustomDialog::changed);
0305     connect(sameColor,              &QAbstractButton::clicked,                  this, &CSSCustomDialog::changed);
0306     connect(hideImages,             &QAbstractButton::clicked,                  this, &CSSCustomDialog::changed);
0307     connect(hideBackground,         &QAbstractButton::clicked,                  this, &CSSCustomDialog::changed);
0308 
0309     //QStringList fonts;
0310     //KFontChooser::getFontList(fonts, 0);
0311     //fontFamily->addItems(fonts);
0312     part = KParts::PartLoader::instantiatePartForMimeType<KParts::ReadOnlyPart>(QStringLiteral("text/html"), nullptr, this).plugin;
0313     QVBoxLayout *l = new QVBoxLayout(previewBox);
0314     l->addWidget(part->widget());
0315 }
0316 
0317 static QUrl toDataUri(const QString &content, const QByteArray &contentType)
0318 {
0319     QByteArray data("data:");
0320     data += contentType;
0321     data += ";charset=utf-8;base64,";
0322     data += content.toUtf8().toBase64();
0323     return QUrl::fromEncoded(data);
0324 }
0325 
0326 void CSSCustomDialog::slotPreview()
0327 {
0328     const QString templ(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcmcss/template.css")));
0329 
0330     if (templ.isEmpty()) {
0331         return;
0332     }
0333 
0334     CSSTemplate css(templ);
0335 
0336     QString data(i18n("<html>\n<head>\n<style>\n<!--\n"
0337                       "%1"
0338                       "\n-->\n</style>\n</head>\n"
0339                       "<body>\n"
0340                       "<h1>Heading 1</h1>\n"
0341                       "<h2>Heading 2</h2>\n"
0342                       "<h3>Heading 3</h3>\n"
0343                       "\n"
0344                       "<p>User-defined stylesheets allow increased\n"
0345                       "accessibility for visually handicapped\n"
0346                       "people.</p>\n"
0347                       "\n"
0348                       "</body>\n"
0349                       "</html>\n", css.expandToString(cssDict())));
0350 
0351     KParts::OpenUrlArguments args(part->arguments());
0352     args.setReload(true); // Make sure the content is always freshly reloaded.
0353     part->setArguments(args);
0354     part->openUrl(toDataUri(data, "text/html"));
0355 }
0356