* KCMStyle
* Copyright (C) 2002 Karol Szwed <gallium@kde.org>
* Copyright (C) 2002 Daniel Molkentin <molkentin@kde.org>
* Copyright (C) 2007 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2009 by Davide Bettio <davide.bettio@kdemail.net>
* Portions Copyright (C) 2007 Paolo Capriotti <p.capriotti@gmail.com>
* Portions Copyright (C) 2007 Ivan Cukic <ivan.cukic+kde@gmail.com>
* Portions Copyright (C) 2008 by Petri Damsten <damu@iki.fi>
* Portions Copyright (C) 2000 TrollTech AS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kcmstyle.h"
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
#include <config-X11.h>
#endif
#include "styleconfdialog.h"
#include "ui_stylepreview.h"
#include <kaboutdata.h>
#include <kapplication.h>
#include <kcombobox.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kautostart.h>
#include <KDebug>
#include <KLibrary>
#include <KColorScheme>
#include <KStandardDirs>
#include <KNewStuff3/KNS3/DownloadDialog>
#include <KConfigGroup>
#include <QFile>
#include <QSettings>
#include <QAbstractItemView>
#include <QLabel>
#include <QPainter>
#include <QPixmapCache>
#include <QStyleFactory>
#include <QFormLayout>
#include <QStandardItemModel>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QStyle>
#include <KGlobal>
#include <KGlobalSettings>
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
#include <QX11Info>
#endif
#include "../krdb/krdb.h"
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
#include <X11/Xlib.h>
#endif
#undef Bool
#undef Below
#undef KeyPress
#undef KeyRelease
#include <kpluginfactory.h>
#include <kpluginloader.h>
#include <kdemacros.h>
K_PLUGIN_FACTORY(KCMStyleFactory, registerPlugin<KCMStyle>();)
K_EXPORT_PLUGIN(KCMStyleFactory("kcmstyle"))
extern "C"
{
Q_DECL_EXPORT void kcminit_style()
{
uint flags = KRdbExportQtSettings | KRdbExportQtColors | KRdbExportXftSettings | KRdbExportGtkTheme;
KConfig _config( QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals );
KConfigGroup config(&_config, "X11");
bool exportKDEColors = config.readEntry("exportKDEColors", true);
if (exportKDEColors)
flags |= KRdbExportColors;
runRdb( flags );
}
}
class StylePreview : public QWidget, public Ui::StylePreview
{
public:
StylePreview(QWidget *parent = 0)
: QWidget(parent)
{
setupUi(this);
QList<QWidget*> widgets = findChildren<QWidget*>();
foreach (QWidget* widget, widgets)
{
widget->installEventFilter(this);
widget->setFocusPolicy(Qt::NoFocus);
}
}
bool eventFilter( QObject* , QEvent* ev ) override
{
switch( ev->type() )
{
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::Enter:
case QEvent::Leave:
case QEvent::Wheel:
case QEvent::ContextMenu:
return true;
default:
break;
}
return false;
}
};
QString KCMStyle::defaultStyle()
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
return QStringLiteral("breeze");
#else
return QString();
#endif
}
KCMStyle::KCMStyle( QWidget* parent, const QVariantList& )
: KCModule( parent ), appliedStyle(NULL)
{
setQuickHelp( i18n("<h1>Style</h1>"
"This module allows you to modify the visual appearance "
"of user interface elements, such as the widget style "
"and effects."));
m_bStyleDirty= false;
m_bEffectsDirty = false;
KGlobal::dirs()->addResourceType("themes", "data", "kstyle/themes");
KAboutData *about =
new KAboutData( QStringLiteral("kcmstyle"), i18n("KDE Style Module"), QStringLiteral("1.0"),
QString(), KAboutLicense::GPL,
i18n("(c) 2002 Karol Szwed, Daniel Molkentin"));
about->addAuthor(i18n("Karol Szwed"), QString(), QStringLiteral("gallium@kde.org"));
about->addAuthor(i18n("Daniel Molkentin"), QString(), QStringLiteral("molkentin@kde.org"));
about->addAuthor(i18n("Ralf Nolden"), QString(), QStringLiteral("nolden@kde.org"));
setAboutData( about );
mainLayout = new QVBoxLayout( this );
mainLayout->setMargin(0);
tabWidget = new QTabWidget( this );
mainLayout->addWidget( tabWidget );
page1 = new QWidget;
page1Layout = new QVBoxLayout( page1 );
QWidget* gbWidgetStyle = new QWidget( page1 );
QVBoxLayout *widgetLayout = new QVBoxLayout(gbWidgetStyle);
gbWidgetStyleLayout = new QVBoxLayout;
widgetLayout->addLayout( gbWidgetStyleLayout );
gbWidgetStyleLayout->setAlignment( Qt::AlignTop );
hbLayout = new QHBoxLayout( );
hbLayout->setObjectName( QStringLiteral("hbLayout") );
QLabel* label=new QLabel(i18n("Widget style:"),this);
hbLayout->addWidget( label );
cbStyle = new KComboBox( gbWidgetStyle );
cbStyle->setObjectName( QStringLiteral("cbStyle") );
cbStyle->setEditable( false );
hbLayout->addWidget( cbStyle );
hbLayout->setStretchFactor( cbStyle, 1 );
label->setBuddy(cbStyle);
pbConfigStyle = new QPushButton( QIcon::fromTheme(QStringLiteral("configure")), i18n("Con&figure..."), gbWidgetStyle );
pbConfigStyle->setEnabled( false );
hbLayout->addWidget( pbConfigStyle );
gbWidgetStyleLayout->addLayout( hbLayout );
lblStyleDesc = new QLabel( gbWidgetStyle );
gbWidgetStyleLayout->addWidget( lblStyleDesc );
QGroupBox *gbPreview = new QGroupBox( i18n( "Preview" ), page1 );
QVBoxLayout *previewLayout = new QVBoxLayout(gbPreview);
previewLayout->setMargin( 0 );
stylePreview = new StylePreview( gbPreview );
gbPreview->layout()->addWidget( stylePreview );
page1Layout->addWidget( gbWidgetStyle );
page1Layout->addWidget( gbPreview );
page1Layout->addStretch();
connect( cbStyle, SIGNAL(activated(int)), this, SLOT(styleChanged()) );
connect( cbStyle, SIGNAL(activated(int)), this, SLOT(updateConfigButton()));
connect( pbConfigStyle, &QAbstractButton::clicked, this, &KCMStyle::styleSpecificConfig);
page2 = new QWidget;
fineTuningUi.setupUi(page2);
connect(cbStyle, SIGNAL(activated(int)), this, SLOT(setStyleDirty()));
connect(fineTuningUi.cbIconsOnButtons, &QAbstractButton::toggled, this, &KCMStyle::setEffectsDirty);
connect(fineTuningUi.cbIconsInMenus, &QAbstractButton::toggled, this, &KCMStyle::setEffectsDirty);
connect(fineTuningUi.comboToolbarIcons, SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
connect(fineTuningUi.comboSecondaryToolbarIcons, SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
addWhatsThis();
tabWidget->addTab(page1, i18nc("@title:tab", "&Applications"));
tabWidget->addTab(page2, i18nc("@title:tab", "&Fine Tuning"));
}
KCMStyle::~KCMStyle()
{
qDeleteAll(styleEntries);
delete appliedStyle;
}
void KCMStyle::updateConfigButton()
{
if (!styleEntries[currentStyle()] || styleEntries[currentStyle()]->configPage.isEmpty()) {
pbConfigStyle->setEnabled(false);
return;
}
pbConfigStyle->setEnabled( true );
}
void KCMStyle::styleSpecificConfig()
{
QString libname = styleEntries[currentStyle()]->configPage;
KLibrary library(libname);
if (!library.load()) {
KMessageBox::detailedError(this,
i18n("There was an error loading the configuration dialog for this style."),
library.errorString(),
i18n("Unable to Load Dialog"));
return;
}
KLibrary::void_function_ptr allocPtr = library.resolveFunction("allocate_kstyle_config");
if (!allocPtr)
{
KMessageBox::detailedError(this,
i18n("There was an error loading the configuration dialog for this style."),
library.errorString(),
i18n("Unable to Load Dialog"));
return;
}
StyleConfigDialog* dial = new StyleConfigDialog(this, styleEntries[currentStyle()]->name);
typedef QWidget*(* factoryRoutine)( QWidget* parent );
factoryRoutine factory = (factoryRoutine)(allocPtr);
QWidget* pluginConfig = factory( dial );
dial->setMainWidget( pluginConfig );
connect(pluginConfig, SIGNAL(changed(bool)), dial, SLOT(setDirty(bool)));
connect(dial, SIGNAL(defaults()), pluginConfig, SLOT(defaults()));
connect(dial, SIGNAL(save()), pluginConfig, SLOT(save()));
if (dial->exec() == QDialog::Accepted && dial->isDirty() ) {
switchStyle(currentStyle(), true);
KGlobalSettings::self()->emitChange(KGlobalSettings::StyleChanged);
setStyleDirty();
}
delete dial;
}
void KCMStyle::changeEvent( QEvent *event )
{
KCModule::changeEvent( event );
if ( event->type() == QEvent::PaletteChange ) {
switchStyle(currentStyle(), true);
}
}
void KCMStyle::load()
{
KConfig config( QStringLiteral("kdeglobals"), KConfig::FullConfig );
loadStyle( config );
loadEffects( config );
m_bStyleDirty= false;
m_bEffectsDirty = false;
updateConfigButton();
emit changed( false );
}
void KCMStyle::save()
{
if ( !(m_bStyleDirty | m_bEffectsDirty ) )
return;
KConfig _config(QStringLiteral("kdeglobals"), KConfig::NoGlobals);
KConfigGroup config(&_config, "KDE");
config.writeEntry( "ShowIconsOnPushButtons", fineTuningUi.cbIconsOnButtons->isChecked());
config.writeEntry( "ShowIconsInMenuItems", fineTuningUi.cbIconsInMenus->isChecked());
config.writeEntry("widgetStyle", currentStyle());
KConfigGroup toolbarStyleGroup(&_config, "Toolbar style");
toolbarStyleGroup.writeEntry("ToolButtonStyle",
toolbarButtonText(fineTuningUi.comboToolbarIcons->currentIndex()));
toolbarStyleGroup.writeEntry("ToolButtonStyleOtherToolbars",
toolbarButtonText(fineTuningUi.comboSecondaryToolbarIcons->currentIndex()));
_config.sync();
if (m_bStyleDirty || m_bEffectsDirty)
{
uint flags = KRdbExportQtSettings | KRdbExportGtkTheme;
KConfig _kconfig( QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals );
KConfigGroup kconfig(&_kconfig, "X11");
bool exportKDEColors = kconfig.readEntry("exportKDEColors", true);
if (exportKDEColors)
flags |= KRdbExportColors;
runRdb( flags );
}
if ( m_bStyleDirty )
KGlobalSettings::self()->emitChange(KGlobalSettings::StyleChanged);
if ( m_bEffectsDirty ) {
KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_STYLE);
KGlobalSettings::self()->emitChange(KGlobalSettings::ToolbarStyleChanged);
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
QDBusMessage message =
QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig"));
QDBusConnection::sessionBus().send(message);
#endif
}
m_bStyleDirty = false;
m_bEffectsDirty = false;
emit changed( false );
}
bool KCMStyle::findStyle( const QString& str, int& combobox_item )
{
StyleEntry* se = styleEntries[str.toLower()];
QString name = se ? se->name : str;
combobox_item = 0;
for( int i = 0; i < cbStyle->count(); i++ )
{
if ( cbStyle->itemText(i) == name )
{
combobox_item = i;
return true;
}
}
return false;
}
void KCMStyle::defaults()
{
int item = 0;
bool found;
found = findStyle( defaultStyle(), item );
if (!found)
found = findStyle( QStringLiteral("oxygen"), item );
if (!found)
found = findStyle( QStringLiteral("plastique"), item );
if (!found)
found = findStyle( QStringLiteral("windows"), item );
if (!found)
found = findStyle( QStringLiteral("platinum"), item );
if (!found)
found = findStyle( QStringLiteral("motif"), item );
cbStyle->setCurrentIndex( item );
m_bStyleDirty = true;
switchStyle( currentStyle() );
fineTuningUi.comboToolbarIcons->setCurrentIndex(toolbarButtonIndex(QStringLiteral("TextBesideIcon")));
fineTuningUi.comboSecondaryToolbarIcons->setCurrentIndex(toolbarButtonIndex(QStringLiteral("TextBesideIcon")));
fineTuningUi.cbIconsOnButtons->setChecked(true);
fineTuningUi.cbIconsInMenus->setChecked(true);
emit changed(true);
}
void KCMStyle::setEffectsDirty()
{
m_bEffectsDirty = true;
emit changed(true);
}
void KCMStyle::setStyleDirty()
{
m_bStyleDirty = true;
emit changed(true);
}
void KCMStyle::loadStyle( KConfig& config )
{
cbStyle->clear();
qDeleteAll(styleEntries);
styleEntries.clear();
QString strWidgetStyle;
QStringList list = KGlobal::dirs()->findAllResources("themes", QStringLiteral("*.themerc"),
KStandardDirs::Recursive |
KStandardDirs::NoDuplicates);
for (QStringList::iterator it = list.begin(); it != list.end(); ++it)
{
KConfig config( *it, KConfig::SimpleConfig);
if ( !(config.hasGroup("KDE") && config.hasGroup("Misc")) )
continue;
KConfigGroup configGroup = config.group("KDE");
strWidgetStyle = configGroup.readEntry("WidgetStyle");
if (strWidgetStyle.isNull())
continue;
StyleEntry* entry = new StyleEntry;
configGroup = config.group("Misc");
entry->name = configGroup.readEntry("Name");
entry->desc = configGroup.readEntry("Comment", i18n("No description available."));
entry->configPage = configGroup.readEntry("ConfigPage", QString());
configGroup = config.group("Desktop Entry");
entry->hidden = configGroup.readEntry("Hidden", false);
styleEntries.insert(strWidgetStyle.toLower(), entry);
}
QStringList allStyles = QStyleFactory::keys();
QStringList styles;
StyleEntry* entry;
for (QStringList::iterator it = allStyles.begin(); it != allStyles.end(); ++it)
{
QString id = (*it).toLower();
if ( (entry = styleEntries[id]) != 0 )
{
if (entry->hidden)
continue;
styles += entry->name;
nameToStyleKey[entry->name] = id;
}
else
{
styles += (*it);
nameToStyleKey[*it] = id;
}
}
styles.sort();
cbStyle->addItems( styles );
KConfigGroup configGroup = config.group( "KDE" );
QString defaultStyle = KCMStyle::defaultStyle();
QString cfgStyle = configGroup.readEntry( "widgetStyle", defaultStyle );
cfgStyle = cfgStyle.toLower();
int item = 0;
for( int i = 0; i < cbStyle->count(); i++ )
{
QString id = nameToStyleKey[cbStyle->itemText(i)];
item = i;
if ( id == cfgStyle )
break;
else if ( id.contains( cfgStyle ) )
break;
else if ( id.contains( QApplication::style()->metaObject()->className() ) )
break;
item = 0;
}
cbStyle->setCurrentIndex( item );
m_bStyleDirty = false;
switchStyle( currentStyle() );
}
QString KCMStyle::currentStyle()
{
return nameToStyleKey[cbStyle->currentText()];
}
void KCMStyle::styleChanged()
{
switchStyle( currentStyle() );
}
void KCMStyle::switchStyle(const QString& styleName, bool force)
{
if (!force && appliedStyle && appliedStyle->objectName() == styleName)
return;
QStyle* style = QStyleFactory::create(styleName);
if (!style)
return;
QPixmapCache::clear();
setStyleRecursive( stylePreview, style );
stylePreview->resize( stylePreview->sizeHint() );
delete appliedStyle;
appliedStyle = style;
StyleEntry* entry = styleEntries[ styleName ];
QString desc;
desc = i18n("Description: %1", entry ? entry->desc : i18n("No description available.") );
lblStyleDesc->setText( desc );
}
void KCMStyle::setStyleRecursive(QWidget* w, QStyle* s)
{
w->setPalette(QPalette());
QPalette newPalette(KGlobalSettings::createApplicationPalette());
s->polish( newPalette );
w->setPalette(newPalette);
w->setStyle(s);
const QObjectList children = w->children();
foreach (QObject* child, children)
{
if (child->isWidgetType())
setStyleRecursive((QWidget *) child, s);
}
}
QString KCMStyle::toolbarButtonText(int index)
{
switch (index) {
case 1:
return QStringLiteral("TextOnly");
case 2:
return QStringLiteral("TextBesideIcon");
case 3:
return QStringLiteral("TextUnderIcon");
default:
break;
}
return QStringLiteral("NoText");
}
int KCMStyle::toolbarButtonIndex(const QString &text)
{
if (text == QLatin1String("TextOnly")) {
return 1;
} else if (text == QLatin1String("TextBesideIcon")) {
return 2;
} else if (text == QLatin1String("TextUnderIcon")) {
return 3;
}
return 0;
}
QString KCMStyle::menuBarStyleText(int index)
{
switch (index) {
case 1:
return QStringLiteral("Decoration");
case 2:
return QStringLiteral("Widget");
}
return QStringLiteral("InApplication");
}
int KCMStyle::menuBarStyleIndex(const QString &text)
{
if (text == QLatin1String("Decoration")) {
return 1;
} else if (text == QLatin1String("Widget")) {
return 2;
}
return 0;
}
void KCMStyle::loadEffects( KConfig& config )
{
KConfigGroup configGroup = config.group("Toolbar style");
QString tbIcon = configGroup.readEntry("ToolButtonStyle", "TextBesideIcon");
fineTuningUi.comboToolbarIcons->setCurrentIndex(toolbarButtonIndex(tbIcon));
tbIcon = configGroup.readEntry("ToolButtonStyleOtherToolbars", "TextBesideIcon");
fineTuningUi.comboSecondaryToolbarIcons->setCurrentIndex(toolbarButtonIndex(tbIcon));
configGroup = config.group("KDE");
fineTuningUi.cbIconsOnButtons->setChecked(configGroup.readEntry("ShowIconsOnPushButtons", true));
fineTuningUi.cbIconsInMenus->setChecked(configGroup.readEntry("ShowIconsInMenuItems", true));
m_bEffectsDirty = false;
}
void KCMStyle::addWhatsThis()
{
cbStyle->setWhatsThis( i18n("Here you can choose from a list of"
" predefined widget styles (e.g. the way buttons are drawn) which"
" may or may not be combined with a theme (additional information"
" like a marble texture or a gradient).") );
stylePreview->setWhatsThis( i18n("This area shows a preview of the currently selected style "
"without having to apply it to the whole desktop.") );
page2->setWhatsThis( i18n("This page allows you to choose details about the widget style options") );
fineTuningUi.comboToolbarIcons->setWhatsThis( i18n( "<p><b>No Text:</b> Shows only icons on toolbar buttons. "
"Best option for low resolutions.</p>"
"<p><b>Text Only: </b>Shows only text on toolbar buttons.</p>"
"<p><b>Text Beside Icons: </b> Shows icons and text on toolbar buttons. "
"Text is aligned beside the icon.</p>"
"<b>Text Below Icons: </b> Shows icons and text on toolbar buttons. "
"Text is aligned below the icon.") );
fineTuningUi.cbIconsOnButtons->setWhatsThis( i18n( "If you enable this option, KDE Applications will "
"show small icons alongside some important buttons.") );
fineTuningUi.cbIconsInMenus->setWhatsThis( i18n( "If you enable this option, KDE Applications will "
"show small icons alongside most menu items.") );
}
#include "kcmstyle.moc"